Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
# Created by Mike Wille, SLSE, 2016-11-01
|
||||
#
|
||||
|
||||
|
||||
# LtsMultiMonitor
|
||||
SET(LtsMultiMonitor_SRCS
|
||||
lts_multi_monitor_export.h
|
||||
lts_multi_monitor.cpp
|
||||
lts_dummy_widget.cpp
|
||||
lts_screen_container.cpp
|
||||
lts_multi_monitor_selector_base.cpp
|
||||
lts_screen_base.cpp
|
||||
lts_magic_corner.h
|
||||
lts_magic_corner.cpp
|
||||
|
||||
# normal screen
|
||||
lts_multi_monitor_selector_normal.cpp
|
||||
lts_screen_normal.cpp
|
||||
|
||||
# ultra wide screen
|
||||
lts_multi_monitor_selector_wide.cpp
|
||||
lts_screen_wide.cpp
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY(WS LtsMultiMonitor SHARED ${LtsMultiMonitor_SRCS})
|
||||
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(LtsMultiMonitor
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(LtsMultiMonitor
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
Qt5::Widgets
|
||||
PRIVATE
|
||||
Qt5::Gui
|
||||
LtsWidgets
|
||||
LtsSessionData
|
||||
LtsUtilsCore
|
||||
LtsUtilsGuiDds
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(LtsMultiMonitor)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#include "lts_dummy_widget.h"
|
||||
|
||||
#include <LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h>
|
||||
|
||||
#include <QIcon>
|
||||
//#include <QLabel>
|
||||
#include <QEvent>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
|
||||
FullscreenVideoDummyWidget::FullscreenVideoDummyWidget()
|
||||
{
|
||||
setWindowIcon(LtsUtils::iconFromSvg(QStringLiteral(":/style/mobile_video.svg")));
|
||||
setWindowTitle(tr("Fullscreen Video Helper"));
|
||||
|
||||
// QLabel *x = new QLabel;
|
||||
// x->setPixmap(windowIcon().pixmap(QSize(100, 100)));
|
||||
// x->show();
|
||||
}
|
||||
|
||||
|
||||
void FullscreenVideoDummyWidget::hideVideo()
|
||||
{
|
||||
qWarning() << "FullscreenVideoDummyWidget::hideVideo()";
|
||||
emit signalHideVideo();
|
||||
}
|
||||
|
||||
void FullscreenVideoDummyWidget::showVideo()
|
||||
{
|
||||
emit signalShowVideo();
|
||||
}
|
||||
|
||||
|
||||
bool FullscreenVideoDummyWidget::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::Type::Show)
|
||||
assert(false);
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsMultiMonitor/lts_multi_monitor_export.h>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
class QEvent;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
|
||||
class LTSMULTIMONITOR_EXPORT SecondaryWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SecondaryWidget() = default;
|
||||
virtual ~SecondaryWidget() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The FullscreenVideoDummyWidget class provides a widget which obtains a whole screen in the MMM.
|
||||
* A foreign application's window can be placed on this position. If the dummy widget gets
|
||||
* replaced by another widget of the OpApp, then the FullscreenVideoDummyWidget gets destroyed and the
|
||||
* foreign application's window *must* be closed.
|
||||
* Do not destroy the FullscreenVideoDummyWidget yourself unless you want to free the screen space in the
|
||||
* MMM again.
|
||||
*
|
||||
* Listen to signalWidgetPlaced and set your foreign application's window to globalGeometry.
|
||||
* Listen to destroyed to close your foreign application's window as that window was replaced
|
||||
* by the MMM.
|
||||
*
|
||||
* Does not work in tablet mode.
|
||||
*/
|
||||
class LTSMULTIMONITOR_EXPORT FullscreenVideoDummyWidget : public SecondaryWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FullscreenVideoDummyWidget();
|
||||
|
||||
void hideVideo();
|
||||
void showVideo();
|
||||
|
||||
protected:
|
||||
bool event(QEvent *);
|
||||
|
||||
signals:
|
||||
void signalWidgetPlaced(const QRect &globalGeometry);
|
||||
void signalHideVideo();
|
||||
void signalShowVideo();
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
#include "lts_magic_corner.h"
|
||||
|
||||
#include <LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h>
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
||||
#define SMOL 48
|
||||
#define SWOLE 100
|
||||
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
LtsMagicCorner::LtsMagicCorner(const QRect &neogeo)
|
||||
: m_neogeo(/*QRect(500, 500, 50, 50)*/neogeo)
|
||||
, m_shrinkTimer(new QTimer(this))
|
||||
, m_label(new QLabel(this))
|
||||
{
|
||||
//setStyleSheet(QStringLiteral("background-color: red"));
|
||||
|
||||
m_shrinkTimer->setInterval(1500ms);
|
||||
m_shrinkTimer->setSingleShot(true);
|
||||
|
||||
connect(m_shrinkTimer, &QTimer::timeout,
|
||||
this, &LtsMagicCorner::makeSmol);
|
||||
|
||||
makeSmol();
|
||||
|
||||
//#warning !!!!!!!!!!!!!!!!!!!!!!!
|
||||
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
|
||||
setMouseTracking(true);
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
setWindowFlag(Qt::WindowType::WindowStaysOnTopHint);
|
||||
setWindowFlag(Qt::WindowType::X11BypassWindowManagerHint); // hide from tab list
|
||||
activateWindow();
|
||||
|
||||
|
||||
// qWarning() << "w geo #1" << geometry();
|
||||
auto *t = new QTimer(this);
|
||||
connect(t, &QTimer::timeout,
|
||||
this, [this]()
|
||||
{
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
raise();
|
||||
|
||||
// #warning !!!!!!!!!!!!!!!!!!!!!!!
|
||||
//qWarning() << "w geo " << geometry();
|
||||
});
|
||||
t->setInterval(750ms);
|
||||
t->start();
|
||||
|
||||
|
||||
m_label->setMinimumSize(SWOLE, SWOLE);
|
||||
m_label->setMaximumSize(SWOLE, SWOLE);
|
||||
m_label->setGeometry(0, 0, SWOLE, SWOLE);
|
||||
m_label->setText(QStringLiteral("<unknown>"));
|
||||
m_label->setAlignment(Qt::AlignmentFlag::AlignCenter);
|
||||
|
||||
// auto *theLayout = new QHBoxLayout;
|
||||
// theLayout->setContentsMargins(0, 0, 0, 0);
|
||||
// theLayout->addWidget(m_label);
|
||||
|
||||
// setLayout(theLayout);
|
||||
}
|
||||
|
||||
|
||||
void LtsMagicCorner::setActiveWidgetType(LtsScreenBase::WidgetType widgetType)
|
||||
{
|
||||
m_activeWidgetType = widgetType;
|
||||
if (widgetType == LtsScreenBase::WidgetType::MainWidget)
|
||||
m_label->setPixmap(m_mainPixmap);
|
||||
else
|
||||
m_label->setPixmap(m_secondaryPixmap);
|
||||
}
|
||||
|
||||
void LtsMagicCorner::setFreezed(bool freezed)
|
||||
{
|
||||
setVisible(!freezed);
|
||||
}
|
||||
|
||||
|
||||
void LtsMagicCorner::setIcon(const QIcon &icon, LtsScreenBase::WidgetType widgetType)
|
||||
{
|
||||
QPixmap p(LtsUtils::pixmapFromIcon(icon));
|
||||
|
||||
if (widgetType == LtsScreenBase::WidgetType::MainWidget)
|
||||
m_mainPixmap = p;
|
||||
else
|
||||
m_secondaryPixmap = p;
|
||||
|
||||
if (widgetType != m_activeWidgetType)
|
||||
m_label->setPixmap(p);
|
||||
|
||||
//m_label->show();
|
||||
|
||||
// auto *x = new QLabel;
|
||||
// x->setText(QStringLiteral("<empty>"));
|
||||
// x->setPixmap(p);
|
||||
// x->setMinimumSize(100, 100);
|
||||
// x->setMaximumSize(100, 100);
|
||||
// x->activateWindow();
|
||||
// x->show();
|
||||
|
||||
//qWarning() << "i was here for" << widgetType;
|
||||
}
|
||||
|
||||
|
||||
void LtsMagicCorner::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
m_mousePressed = (event->button() == Qt::MouseButton::LeftButton);
|
||||
}
|
||||
|
||||
|
||||
void LtsMagicCorner::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!m_mousePressed)
|
||||
return;
|
||||
|
||||
m_mousePressed = false;
|
||||
|
||||
if (event->button() != Qt::MouseButton::LeftButton)
|
||||
return;
|
||||
|
||||
m_shrinkTimer->start();
|
||||
emit toggle();
|
||||
}
|
||||
|
||||
|
||||
void LtsMagicCorner::makeSmol()
|
||||
{
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
setGeometry(m_neogeo.x(), m_neogeo.y(), SMOL, SMOL);
|
||||
setMinimumSize(SMOL, SMOL);
|
||||
setMaximumSize(SMOL, SMOL);
|
||||
m_label->hide();
|
||||
}
|
||||
|
||||
|
||||
void LtsMagicCorner::makeSwole()
|
||||
{
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
setAttribute(Qt::WA_TranslucentBackground, false);
|
||||
setAttribute(Qt::WA_NoSystemBackground, false); // NOTE: set by activating WA_TranslucentBackground
|
||||
setGeometry(m_neogeo.x(), m_neogeo.y(), SWOLE, SWOLE);
|
||||
setMinimumSize(SWOLE, SWOLE);
|
||||
setMaximumSize(SWOLE, SWOLE);
|
||||
m_label->show();
|
||||
m_shrinkTimer->start();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void LtsMagicCorner::mouseMoveEvent(QMouseEvent */*event*/)
|
||||
{
|
||||
// see https://devhub.defence-elec.de/confluence/x/yLjeDg 13.1
|
||||
emit signalMakeAllSwole(); // tell all corners to reveal themselves (=> LtsScreenX => LtsMultiMonitor => (all!) LtsScreenX => LtsMagicCorner)
|
||||
}
|
||||
|
||||
} // end of namespace LtsMultiMonitor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "lts_screen_base.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPixmap>
|
||||
|
||||
|
||||
class QMouseEvent;
|
||||
class QPaintEvent;
|
||||
class QLabel;
|
||||
class QTimer;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LtsMagicCorner : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LtsMagicCorner(const QRect &neogeo);
|
||||
|
||||
void setIcon(const QIcon &icon, LtsScreenBase::WidgetType widgetType);
|
||||
void setActiveWidgetType(LtsScreenBase::WidgetType widgetType);
|
||||
//void setLastIcon(const QIcon &icon);
|
||||
//bool hasIcon() const;
|
||||
void setFreezed(bool freezed);
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
signals:
|
||||
void toggle();
|
||||
void signalMakeAllSwole();
|
||||
|
||||
private slots:
|
||||
void makeSmol();
|
||||
|
||||
public slots:
|
||||
void makeSwole();
|
||||
|
||||
private:
|
||||
QRect m_neogeo;
|
||||
QTimer *m_shrinkTimer;
|
||||
bool m_mousePressed { false };
|
||||
bool m_freezed = false;
|
||||
QLabel *m_label;
|
||||
QPixmap m_mainPixmap;
|
||||
QPixmap m_secondaryPixmap;
|
||||
LtsScreenBase::WidgetType m_activeWidgetType;
|
||||
};
|
||||
} // end of namespace LtsMultiMonitor
|
||||
+702
@@ -0,0 +1,702 @@
|
||||
// NOTE: removing a screen after the OpApp was started results in an invisible screen
|
||||
// NOTE: adding and removing a screen after the OpApp was started in tablet mode will make mmm do weird things
|
||||
|
||||
#include "lts_multi_monitor.h"
|
||||
#include "lts_multi_monitor_selector_normal.h"
|
||||
#include "lts_multi_monitor_selector_wide.h"
|
||||
#include "lts_screen_normal.h"
|
||||
#include "lts_screen_wide.h"
|
||||
#include "lts_dummy_widget.h"
|
||||
#include "lts_screen_container.h"
|
||||
|
||||
#include <LtsWidgets/lts_message_dialog.h>
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
#include <QRect>
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
static LtsMultiMonitor *s_ltsMultiMonitor = nullptr;
|
||||
|
||||
|
||||
LtsMultiMonitor *LtsMultiMonitor::getInstance()
|
||||
{
|
||||
if (!s_ltsMultiMonitor)
|
||||
{
|
||||
s_ltsMultiMonitor = new LtsMultiMonitor;
|
||||
qAddPostRoutine(&cleanupLtsMultiMonitor);
|
||||
}
|
||||
return s_ltsMultiMonitor;
|
||||
}
|
||||
|
||||
void LtsMultiMonitor::cleanupLtsMultiMonitor()
|
||||
{
|
||||
delete s_ltsMultiMonitor;
|
||||
s_ltsMultiMonitor = nullptr;
|
||||
}
|
||||
|
||||
|
||||
LtsMultiMonitor::LtsMultiMonitor()
|
||||
: QObject(nullptr)
|
||||
, m_mainScreen(nullptr)
|
||||
, m_active(false)
|
||||
, m_initialized(false)
|
||||
, m_skipScreenId(-1)
|
||||
, m_subSkipScreenId(-1)
|
||||
// , m_dummy(new QWidget())
|
||||
, m_overlapBaseLayouts(false)
|
||||
, m_debugMargins(false)
|
||||
{}
|
||||
|
||||
|
||||
LtsMultiMonitor::~LtsMultiMonitor()
|
||||
{
|
||||
// if (m_superParent)
|
||||
// m_superParent->deleteLater();
|
||||
|
||||
for (auto i = m_screens.begin(); i != m_screens.end(); ++i)
|
||||
{
|
||||
LtsScreenBase *screen = *i;
|
||||
disconnect(screen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied);
|
||||
screen->deleteLater();
|
||||
}
|
||||
|
||||
//qDeleteAll(m_screens);
|
||||
//delete m_dummy;
|
||||
//m_dummy->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void LtsMultiMonitor::screenDied()
|
||||
{
|
||||
// qWarning() << "whoops! a screen died!";
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
|
||||
void LtsMultiMonitor::init()
|
||||
{
|
||||
// TODO: reinit if device is a tablet, to refresh screen count for attaching screens live
|
||||
if (LtsSessionData::LtsSessionData::getInstance()->isTablet())
|
||||
{
|
||||
m_overlapBaseLayouts = true;
|
||||
m_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_initialized || !m_active)
|
||||
return;
|
||||
|
||||
QList<QScreen *> screenList = QGuiApplication::screens();
|
||||
const QScreen &mainScreen = *QGuiApplication::primaryScreen();
|
||||
|
||||
// qWarning() << "skip screen" << m_skipScreenId << "part" << m_subSkipScreenId;
|
||||
|
||||
//for (int i = 0; i < screenList.count(); i++)
|
||||
for (auto i = screenList.constBegin(); i != screenList.constEnd(); ++i)
|
||||
{
|
||||
const QScreen &screen = **i;//screenList.at(i);
|
||||
|
||||
// only add screen if it was not already added, by its serial number
|
||||
bool found = false;
|
||||
|
||||
for (LtsScreenBase *ltsScreen : qAsConst(m_screens))
|
||||
{
|
||||
if (ltsScreen->serialNumber() == screen.name())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
// more like "possible" screen id as there can be some continues, etc.
|
||||
int screenId = m_nextScreenId++;
|
||||
// qWarning() << "screen id" << screenId;
|
||||
|
||||
QRect screenGeo(screen.geometry());
|
||||
|
||||
if (screenId == m_skipScreenId) // do not create LtsScreenBase where AVM is (or at least limit the "sub screens")
|
||||
{
|
||||
if (m_subSkipScreenId == 0)
|
||||
{
|
||||
// left left side of screen empty
|
||||
QRect newScreenGeo(screenGeo);
|
||||
|
||||
newScreenGeo.moveLeft(screenGeo.x() + (screenGeo.width() / 2)/* + 1*/);
|
||||
newScreenGeo.setWidth(screenGeo.width() / 2);
|
||||
|
||||
screenGeo = newScreenGeo;
|
||||
}
|
||||
else if (m_subSkipScreenId == 1)
|
||||
{
|
||||
// left right side of screen empty
|
||||
QRect newScreenGeo(screenGeo);
|
||||
|
||||
newScreenGeo.setWidth(screenGeo.width() / 2); // starts to malfunction in old gnome starting from 1.78 ...
|
||||
|
||||
screenGeo = newScreenGeo;
|
||||
}
|
||||
else // completely skip LtsScreenBase creation if no sub screen is set
|
||||
{
|
||||
// qWarning() << "skipping" << screenId;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// qWarning() << "screenGeo" << screenGeo;
|
||||
|
||||
bool isUltraWideView = (screenGeo.width() > 3000);
|
||||
|
||||
LtsScreenBase *newScreen;
|
||||
if (isUltraWideView)
|
||||
{
|
||||
newScreen = new LtsScreenWide(screenGeo, screenId, m_debugMargins, screen.name());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto *newScreenNormal = new LtsScreenNormal(std::move(screenGeo), screenId, m_debugMargins, screen.name());
|
||||
newScreen = newScreenNormal;
|
||||
|
||||
// see https://devhub.defence-elec.de/confluence/x/yLjeDg 13.1
|
||||
connect(newScreenNormal, &LtsScreenNormal::signalMakeAllSwole,
|
||||
this, &LtsMultiMonitor::signalMakeAllSwole);
|
||||
connect(this, &LtsMultiMonitor::signalMakeAllSwole,
|
||||
newScreenNormal, &LtsScreenNormal::signalMakeSwole);
|
||||
}
|
||||
|
||||
connect(newScreen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied);
|
||||
|
||||
if ((&mainScreen) == (&screen))
|
||||
m_mainScreen = newScreen;
|
||||
|
||||
m_screens.append(newScreen);
|
||||
}
|
||||
if (m_screens.isEmpty())
|
||||
{
|
||||
QMessageBox::critical(nullptr, QString(), tr("No screens found - cannot start application."));
|
||||
abort();
|
||||
}
|
||||
|
||||
if (m_mainScreen == nullptr)
|
||||
m_mainScreen = m_screens.first();
|
||||
|
||||
m_initialized = true;
|
||||
emit signalScreenCountChanged(screenCount());
|
||||
}
|
||||
|
||||
|
||||
int LtsMultiMonitor::screenCount() const
|
||||
{
|
||||
return m_screens.count();
|
||||
}
|
||||
|
||||
|
||||
bool LtsMultiMonitor::cancelAndProcessIfInactive(QWidget *widget) const
|
||||
{
|
||||
if (m_active)
|
||||
return false;
|
||||
|
||||
widget->activateWindow(); // no mmm => show as discrete window
|
||||
widget->show();
|
||||
|
||||
place(widget);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void LtsMultiMonitor::place(QWidget *widget)
|
||||
{
|
||||
/* tell the blw that it was now sucessfully placed and is shown / ready to use
|
||||
* e.g. for autostarting a replay only if it is already visible, so the user
|
||||
* doesn't miss out on the replay's beginning
|
||||
*
|
||||
* -- as this method returns here, we have to emit 'successfullyPlaced' here,
|
||||
* as the widget is now visible as its own widget
|
||||
*/
|
||||
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
|
||||
if (blw != nullptr)
|
||||
emit blw->signalSuccessfullyPlaced();
|
||||
}
|
||||
|
||||
|
||||
bool LtsMultiMonitor::putScreen(QWidget *widget, int screenId, int screenPart)
|
||||
{
|
||||
Q_ASSERT(widget != nullptr);
|
||||
if (widget == nullptr)
|
||||
return false; // fail-safe
|
||||
|
||||
if (cancelAndProcessIfInactive(widget))
|
||||
return true;
|
||||
|
||||
init();
|
||||
|
||||
if (overlapBaseLayouts(widget)) // NOTE: call 'init' before 'overlapBaseLayouts'
|
||||
return true;
|
||||
|
||||
|
||||
bool isSecondary = isWidgetSecondary(widget);
|
||||
qWarning() << "putScreen: isSecondary" << isSecondary;
|
||||
|
||||
for (LtsScreenBase *screen : qAsConst(m_screens))
|
||||
{
|
||||
if (screen->screenId() != screenId)
|
||||
continue;
|
||||
|
||||
screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget);
|
||||
|
||||
try
|
||||
{
|
||||
screen->putWidget(widget, screenPart);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
screen->revertSwitch();
|
||||
// qWarning() << "LtsMultiMonitor:::putScreen: cannot put widget on screen "<<screenId<<" at part "<<screenPart<<"! Using selectScreen()!";
|
||||
return selectScreen(widget);
|
||||
}
|
||||
|
||||
screen->check();
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
place(widget);
|
||||
screen->finalizeSwitch();
|
||||
return true;
|
||||
}
|
||||
|
||||
// qWarning() << "LtsMultiMonitor:::putScreen: screen"<<screenId<<"not available! Using selectScreen()!";
|
||||
return selectScreen(widget);
|
||||
}
|
||||
|
||||
bool LtsMultiMonitor::putScreenAutomatically(QWidget *widget)
|
||||
{
|
||||
if (widget == nullptr)
|
||||
return false;
|
||||
|
||||
if (cancelAndProcessIfInactive(widget))
|
||||
return true;
|
||||
|
||||
init();
|
||||
|
||||
if (overlapBaseLayouts(widget)) // NOTE: call 'init' before 'overlapBaseLayouts'
|
||||
return true;
|
||||
|
||||
for (LtsScreenBase *screen : qAsConst(m_screens))
|
||||
{
|
||||
if (screen->screenId() == m_skipScreenId || !screen->isEmpty())
|
||||
continue;
|
||||
|
||||
screen->switchToTemp(LtsScreenBase::WidgetType::SecondaryWidget);
|
||||
|
||||
try //first try on part 1
|
||||
{
|
||||
screen->putWidget(widget, 1);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
try //if part 1 failed, try part 2
|
||||
{
|
||||
screen->putWidget(widget, 2);
|
||||
}
|
||||
catch (...) //if part 2 failed, try next screen
|
||||
{
|
||||
screen->revertSwitch();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
screen->check();
|
||||
place(widget);
|
||||
screen->finalizeSwitch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool LtsMultiMonitor::isOverlapBaseLayouts() const
|
||||
{
|
||||
return (m_overlapBaseLayouts /*&& m_screens.count() == 1*/); // TODO: reactivate if mmm works with external monitors on a tablet
|
||||
}
|
||||
|
||||
|
||||
void LtsMultiMonitor::freezeMagicCorner(int screenId)
|
||||
{
|
||||
|
||||
for (auto *screen : qAsConst(m_screens))
|
||||
{
|
||||
|
||||
if (screen->screenId() == screenId)
|
||||
{
|
||||
LtsScreenNormal* normalScreen = qobject_cast<LtsScreenNormal*>(screen);
|
||||
if(normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget())
|
||||
{
|
||||
normalScreen->hideMagicCorner(true);
|
||||
if(normalScreen->child()->activeWidgetType() == LtsScreenBase::WidgetType::MainWidget)
|
||||
normalScreen->child()->toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LtsMultiMonitor::unfreezeMagicCorner(int screenId)
|
||||
{
|
||||
|
||||
for (auto *screen : qAsConst(m_screens))
|
||||
{
|
||||
|
||||
if (screen->screenId() == screenId)
|
||||
{
|
||||
LtsScreenNormal* normalScreen = qobject_cast<LtsScreenNormal*>(screen);
|
||||
if(normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget())
|
||||
normalScreen->hideMagicCorner(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LtsMultiMonitor::overlapBaseLayouts(QWidget *widget)
|
||||
{
|
||||
if (!isOverlapBaseLayouts())
|
||||
return false;
|
||||
|
||||
/* only overlap base layouts if the main screen is the only screen - if
|
||||
* another screen is connected to the tablet, the new screen should be
|
||||
* selectable, too */
|
||||
|
||||
Q_ASSERT(qobject_cast<FullscreenVideoDummyWidget *>(widget) == nullptr);
|
||||
if (qobject_cast<FullscreenVideoDummyWidget *>(widget) != nullptr)
|
||||
{
|
||||
// qWarning() << "tried to put a dummy widget on a tablet device... - ignore";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// FullscreenVideoDummyWidget *dummy = qobject_cast<FullscreenVideoDummyWidget *>(widget);
|
||||
// if (dummy != nullptr)
|
||||
// {
|
||||
// widget->setParent(m_dummy);
|
||||
// emit dummy->signalWidgetPlaced(QRect());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// widget->show(); // as discrete window
|
||||
// place(widget);
|
||||
// }
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
widget->winId(); // make sure windowHandle() is not a nullptr
|
||||
QWindowsWindowFunctions::setHasBorderInFullScreen(widget->windowHandle(), true);
|
||||
#endif
|
||||
|
||||
widget->activateWindow();
|
||||
widget->showFullScreen();
|
||||
|
||||
place(widget);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LtsMultiMonitor::selectAndSwap(QWidget *widget)
|
||||
{
|
||||
if (!m_active)
|
||||
return false; // no swap if mmm not active
|
||||
|
||||
Q_ASSERT(!isOverlapBaseLayouts()); // swap button not allowed on tablet
|
||||
if (isOverlapBaseLayouts())
|
||||
return false; // don't allow swap if overlay mode is active (as that would make no sense)
|
||||
|
||||
|
||||
// look for the screen which contains the widget
|
||||
LtsScreenContainer *oldContainer = nullptr; // TODO C++17: std::optional
|
||||
for (LtsScreenBase *_screen : qAsConst(m_screens))
|
||||
{
|
||||
try
|
||||
{
|
||||
oldContainer = _screen->containerOfWidget(widget);
|
||||
}
|
||||
catch (...){}
|
||||
}
|
||||
|
||||
if (oldContainer == nullptr)
|
||||
{
|
||||
// qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
LtsWidgets::LtsMessageDialog::showMessage(widget, tr("Could not determine screen id for this swap!<br>Swapping not possible, please try again later."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// qWarning() << "oldContainer" << (quintptr)oldContainer;
|
||||
|
||||
|
||||
return selectScreen(widget, oldContainer); // NOTE: the screen later TAKES the widget from the clicked container and puts it into the OLD container given
|
||||
}
|
||||
|
||||
|
||||
LtsScreenBase *LtsMultiMonitor::widgetsScreen(QWidget *widget)
|
||||
{
|
||||
for (LtsScreenBase *screen : qAsConst(m_screens))
|
||||
if (screen->hasWidget(widget))
|
||||
return screen;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool LtsMultiMonitor::isWidgetSecondary(QWidget *widget)
|
||||
{
|
||||
#ifndef GUEZ_MODE
|
||||
return false;
|
||||
#endif
|
||||
|
||||
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
|
||||
auto *secondaryWidget = qobject_cast<SecondaryWidget *>(widget); // full screen video & script editor's screen widget
|
||||
|
||||
bool isSecondaryWidget = ((blw != nullptr && blw->isSecondaryWidget()) || secondaryWidget != nullptr);
|
||||
|
||||
// if (isSec)
|
||||
|
||||
return isSecondaryWidget;
|
||||
}
|
||||
|
||||
|
||||
class StupidDeleter
|
||||
{
|
||||
public:
|
||||
explicit StupidDeleter(LtsScreenBase *screen)
|
||||
: m_screen(screen)
|
||||
{}
|
||||
|
||||
~StupidDeleter()
|
||||
{
|
||||
//qWarning() << "####### Deleter";
|
||||
if (m_screen != nullptr)
|
||||
{
|
||||
//qWarning() << "####### REVERTED";
|
||||
m_screen->revertSwitch();
|
||||
}
|
||||
//else qWarning() << "####### NOT REVERTED !!!!";
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
//qWarning() << "####### RESET";
|
||||
m_screen = nullptr;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
LtsScreenBase *m_screen;
|
||||
};
|
||||
|
||||
|
||||
bool LtsMultiMonitor::selectScreen(QWidget *widget, LtsScreenContainer *swap, bool putOnMainIfFree, bool ltsScreenNormalOnly, const QVector<QWidget *> &irreplacableWidgets)
|
||||
{
|
||||
Q_ASSERT(widget != nullptr);
|
||||
if (widget == nullptr)
|
||||
return false; // fail-safe
|
||||
|
||||
if (cancelAndProcessIfInactive(widget))
|
||||
return true;
|
||||
|
||||
init();
|
||||
|
||||
if (overlapBaseLayouts(widget)) // NOTE: call 'init' before 'overlapBaseLayouts'
|
||||
return true;
|
||||
|
||||
bool isSecondary = isWidgetSecondary(widget);
|
||||
//qWarning() << "selectScreen: isSecondary" << isSecondary;
|
||||
|
||||
if (!isSecondary) // the allEmpty stuff is only for main widgets!!
|
||||
{
|
||||
bool allEmpty = putOnMainIfFree; // initial value
|
||||
for (LtsScreenBase *screen : qAsConst(m_screens))
|
||||
{
|
||||
screen->switchToTemp(LtsScreenBase::WidgetType::MainWidget);
|
||||
bool isEmpty = screen->isEmpty();
|
||||
screen->revertSwitch();
|
||||
|
||||
if (isEmpty)
|
||||
continue;
|
||||
|
||||
allEmpty = false;
|
||||
break;
|
||||
}
|
||||
|
||||
//qWarning() << "allEmpty" << allEmpty;
|
||||
|
||||
if (allEmpty)
|
||||
{
|
||||
/* if ALL screens in the entire mmm are empty, then just put the widget on
|
||||
* the main screen */
|
||||
|
||||
m_mainScreen->switchToTemp(LtsScreenBase::WidgetType::MainWidget);
|
||||
|
||||
try
|
||||
{
|
||||
m_mainScreen->putWidget(widget);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
qWarning() << "failed - reverting";
|
||||
m_mainScreen->revertSwitch();
|
||||
|
||||
Q_ASSERT_X(false, __FUNCTION__, "screen promised to be empty but 'putWidgetSomewhere' method failed regardless");
|
||||
// qWarning() << "woopsie";
|
||||
|
||||
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
|
||||
if (blw != nullptr)
|
||||
emit blw->signalClose();
|
||||
else
|
||||
widget->deleteLater();
|
||||
|
||||
return false; // fail-safe
|
||||
}
|
||||
|
||||
m_mainScreen->check();
|
||||
place(widget);
|
||||
m_mainScreen->finalizeSwitch();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto *selectionProcessWatcher = new QObject;
|
||||
|
||||
// finally show selectors
|
||||
bool minOnce = false;
|
||||
|
||||
for (auto i = m_screens.begin(); i != m_screens.end(); ++i)
|
||||
{
|
||||
LtsScreenBase *screen = *i;
|
||||
|
||||
screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget);
|
||||
|
||||
StupidDeleter _(screen);
|
||||
|
||||
if (screen->isIrreplaceable()) // do not create mms for a screen that is fully irreplaceable
|
||||
{
|
||||
// qWarning() << "screen marked as irreplacable, skipping"<<screen;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
for (QWidget *widget : qAsConst(irreplacableWidgets))
|
||||
{
|
||||
// qWarning() << "does screen have" << widget<<screen;
|
||||
if (screen->hasWidget(widget))
|
||||
{
|
||||
// qWarning() << "yes, skipping!";
|
||||
skip = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// qWarning() << "no, selection available";
|
||||
}
|
||||
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
MultiMonitorSelectorBase *mms;
|
||||
if (qobject_cast<LtsScreenNormal *>(screen) != nullptr)
|
||||
{
|
||||
auto *screenNormal = static_cast<LtsScreenNormal *>(screen);
|
||||
mms = new MultiMonitorSelectorNormal(screenNormal, isSecondary, irreplacableWidgets);
|
||||
}
|
||||
else if (qobject_cast<LtsScreenWide *>(screen) != nullptr && !ltsScreenNormalOnly)
|
||||
{
|
||||
auto *screenWide = static_cast<LtsScreenWide *>(screen);
|
||||
mms = new MultiMonitorSelectorWide(screenWide, nullptr, irreplacableWidgets);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
|
||||
QPointer<QWidget> widgetPtr(widget);
|
||||
|
||||
|
||||
connect(widget, &QWidget::destroyed,
|
||||
mms, &MultiMonitorSelectorBase::close);
|
||||
connect(mms, &MultiMonitorSelectorBase::rejected,//signalAbort,
|
||||
this, [widgetPtr, swap]()
|
||||
{
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
if (!widgetPtr || swap != nullptr) // don't delete widget if it was a swap, just let it live where it currently is living
|
||||
return;
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
//qWarning() << "###### REJECTED!!!!";
|
||||
|
||||
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widgetPtr.data());
|
||||
if (blw != nullptr)
|
||||
emit blw->signalClose();
|
||||
else
|
||||
widgetPtr->deleteLater();
|
||||
});
|
||||
connect(mms, &MultiMonitorSelectorBase::signalSelected,
|
||||
this, [this, mms, widgetPtr]()
|
||||
{
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
for (LtsScreenBase *screen : qAsConst(m_screens))
|
||||
screen->check();
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
if (widgetPtr)
|
||||
place(widgetPtr.data());
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
mms->deleteLater();
|
||||
emit signalSelected();
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
});
|
||||
connect(mms, &MultiMonitorSelectorBase::destroyed,
|
||||
selectionProcessWatcher, &QObject::deleteLater);
|
||||
connect(selectionProcessWatcher, &QObject::destroyed,
|
||||
mms, &MultiMonitorSelectorBase::close);
|
||||
|
||||
mms->setSwap(swap);
|
||||
mms->setWidget(widget);
|
||||
mms->show();
|
||||
_.reset();
|
||||
minOnce = true;
|
||||
}
|
||||
|
||||
if (!minOnce)
|
||||
{
|
||||
selectionProcessWatcher->deleteLater();
|
||||
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
|
||||
if (blw != nullptr)
|
||||
emit blw->signalClose();
|
||||
else
|
||||
widget->deleteLater();
|
||||
|
||||
LtsWidgets::LtsMessageDialog::showMessage(m_mainScreen->screenDialog(), tr("There are no free screens available to select from."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include "lts_multi_monitor_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LtsScreenBase;
|
||||
class MultiMonitorSelectorBase;
|
||||
class LtsScreenContainer;
|
||||
|
||||
|
||||
class LTSMULTIMONITOR_EXPORT LtsMultiMonitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static LtsMultiMonitor *getInstance();
|
||||
|
||||
/**
|
||||
* @brief selectScreen - Behaviour is undefined for widgets that are already placed on a screen.
|
||||
* @param widget to put on the screen
|
||||
* @param forceSelection selectScreen puts 'widget' on the main screen, if it's not currently occupied
|
||||
* by another widget - set to true if the user should select which screen 'widget' should be put on
|
||||
* instead of putting it on the main screen if it's not already occupied
|
||||
* @param swap
|
||||
* @return true if widget was in fact put on the screen - false if there is no screen free or the selection was aborted by the user
|
||||
*/
|
||||
bool selectScreen(QWidget *widget, LtsScreenContainer *swap = nullptr, bool putOnMainIfFree = true, bool ltsScreenNormalOnly = false, const QVector<QWidget *> &irreplacableWidgets = {}); // WARNING: widget might be deleted after calling this (e.g. user closed the widget) - do not access widget after calling this!
|
||||
bool selectAndSwap(QWidget *widget);
|
||||
|
||||
|
||||
void setEnableMultiMonitorMode() { m_active = true; }
|
||||
void setDebugMultiMonitorMode() { m_debugMargins = true; }
|
||||
bool isActive() const { return m_active; }
|
||||
|
||||
LtsScreenBase *widgetsScreen(QWidget *widget);
|
||||
|
||||
/**
|
||||
* @brief putScreen is called only by LtsSession if a screen id preset for
|
||||
* a log-off-saved app was available
|
||||
* @param widget
|
||||
* @param screenId
|
||||
* @param screenPart
|
||||
* @return
|
||||
*/
|
||||
|
||||
void freezeMagicCorner(int screenId);
|
||||
void unfreezeMagicCorner(int screenId);
|
||||
|
||||
bool putScreen(QWidget *widget, int screenId, int screenPart);
|
||||
bool putScreenAutomatically(QWidget *widget);
|
||||
static void place(QWidget *widget);
|
||||
void setSkipScreen(int screenId, int subScreenId = -1) { m_skipScreenId = screenId; m_subSkipScreenId = subScreenId; }
|
||||
LtsScreenBase *mainScreen() const { return m_mainScreen; }
|
||||
int screenCount() const; // NOTE: screen count is 0 if mmm is inactive
|
||||
|
||||
private slots:
|
||||
void screenDied();
|
||||
|
||||
private:
|
||||
LtsMultiMonitor();
|
||||
~LtsMultiMonitor();
|
||||
static void cleanupLtsMultiMonitor();
|
||||
void init();
|
||||
bool cancelAndProcessIfInactive(QWidget *widget) const;
|
||||
static bool isWidgetSecondary(QWidget *widget) ;
|
||||
|
||||
/**
|
||||
* @brief overlapBaseLayouts // NOTE: call 'init' before 'overlapBaseLayouts'
|
||||
* @param widget
|
||||
* @return
|
||||
*/
|
||||
bool overlapBaseLayouts(QWidget *widget);
|
||||
bool isOverlapBaseLayouts() const;
|
||||
|
||||
QVector<LtsScreenBase *> m_screens;
|
||||
int m_nextScreenId { 0 };
|
||||
LtsScreenBase *m_mainScreen; // TODO C++17: std::optional
|
||||
bool m_active;
|
||||
bool m_initialized;
|
||||
int m_skipScreenId;
|
||||
int m_subSkipScreenId;
|
||||
//QWidget *m_dummy;
|
||||
bool m_overlapBaseLayouts;
|
||||
bool m_debugMargins;
|
||||
|
||||
signals:
|
||||
void signalQuitLoop();
|
||||
void signalSelected();
|
||||
void signalScreenCountChanged(int);
|
||||
void signalMakeAllSwole();
|
||||
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Export definitions
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifndef LTSMULTIMONITOR_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define LTSMULTIMONITOR_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_LTSMULTIMONITOR_LIB)
|
||||
# define LTSMULTIMONITOR_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define LTSMULTIMONITOR_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,419 @@
|
||||
// TODO: hdr
|
||||
|
||||
#include "lts_multi_monitor_selector_base.h"
|
||||
#include "lts_multi_monitor.h"
|
||||
#include "lts_screen_base.h"
|
||||
#include "lts_screen_container.h"
|
||||
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
#include <LtsUtilsCore/promise.h>
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QCloseEvent>
|
||||
#include <QStyle>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QColor>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
int MultiMonitorSelectorBase::s_margin = 10;
|
||||
int MultiMonitorSelectorBase::s_offset = 6;
|
||||
int MultiMonitorSelectorBase::s_buttonSize = 20;
|
||||
|
||||
|
||||
MultiMonitorSelectorBase::MultiMonitorSelectorBase(LtsScreenBase *screen, const QVector<QWidget *> &irreplacableWidgets, QWidget */*superParent*/)
|
||||
: QDialog(nullptr)
|
||||
, m_irreplacableWidgets(irreplacableWidgets)
|
||||
, m_screen(screen)
|
||||
, m_screenIsIrreplaceable(screen->isIrreplaceable())
|
||||
{
|
||||
setGeometry(screen->screenRect());
|
||||
setMouseTracking(true);
|
||||
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint /* | Qt::Popup*/);
|
||||
setAttribute(Qt::WA_TranslucentBackground); // transparent window (FIXME: os and mood dependent)
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
|
||||
bool MultiMonitorSelectorBase::isNullSwap(LtsScreenContainer *container)
|
||||
{
|
||||
if (container == m_swap) // if the user clicked the same container the widget comes from, simply ignore
|
||||
{
|
||||
// qWarning() << "null-swap requested";
|
||||
return true;
|
||||
}
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorBase::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
qWarning() << "closeEvent";
|
||||
|
||||
if (!m_selected && !m_reverted)
|
||||
{
|
||||
qWarning() << "closeEvent: not selected and not reverted! reverting!";
|
||||
m_screen->revertSwitch();
|
||||
}
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
//QDialog::closeEvent(event);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
|
||||
bool MultiMonitorSelectorBase::killOrSwapOldBLW(LtsScreenContainer *container /* clicked on container of this screen */) noexcept(false)
|
||||
{
|
||||
// return true if nothing has to be done anymore
|
||||
// return false if things have to be done by the caller
|
||||
// throw to cancel !
|
||||
|
||||
//qWarning() << "container" << (quintptr)container << "m_swap" << (quintptr)m_swap;
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
// same container was selected where the widget is currently already on? do nothing
|
||||
if (isNullSwap(container))
|
||||
return true; // just pretend we did something
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
|
||||
// swap available? then swap instead of closing / deleting it
|
||||
if (m_swap != nullptr)
|
||||
{
|
||||
QWidget *oldSwapWidget;
|
||||
try
|
||||
{
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
oldSwapWidget = m_swap->stealWidget();
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
throw;
|
||||
}
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
|
||||
// the swap TARGET container doesn't need to be occupied - just put it there (swapping the current app to a free screen (container)):
|
||||
if (!container->isEmpty())
|
||||
{
|
||||
// NOTE: FIRST get old widget THEN add it to the container - otherwise you would SET it before stealing it
|
||||
QWidget *oldContainerWidget;
|
||||
try
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
oldContainerWidget = container->stealWidget();
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// TODO: bail!
|
||||
throw;
|
||||
}
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
m_swap->setWidget(oldContainerWidget);
|
||||
}
|
||||
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
container->setWidget(oldSwapWidget); // put the widget, which is going to be replaced / which was clicked on, into the old container, where the new widget came from
|
||||
|
||||
|
||||
if(m_swap->activeWidgetType() == LtsScreenBase::WidgetType::SecondaryWidget && !m_swap->hasSecondaryWidget() && m_swap->hasMainWidget())
|
||||
m_swap->toggle();
|
||||
else if(m_swap->activeWidgetType() == LtsScreenBase::WidgetType::MainWidget && !m_swap->hasMainWidget() && m_swap->hasSecondaryWidget())
|
||||
m_swap->toggle();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// no swap => kill old widget
|
||||
QWidget *oldWidget;
|
||||
try
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
oldWidget = container->widget(); // DO NOT STEAL! blw might have unsaved changes! only steal if no changes! stealWidget();
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
//qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
return false; // nothing to swap, nothing to kill, container is ready to go
|
||||
}
|
||||
|
||||
|
||||
// at this point no swap is available and container has a widget => close / delete
|
||||
auto *oldBLW = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(oldWidget);
|
||||
|
||||
if (oldBLW == nullptr)
|
||||
{
|
||||
container->stealWidget()->deleteLater();
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
if (!oldBLW->slotClose())
|
||||
{
|
||||
// qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
throw "MultiMonitorSelectorBase::killOrSwapOldBLW: failed to close old blw";
|
||||
}
|
||||
|
||||
container->stealWidget();
|
||||
return false;
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorBase::setWidget(QWidget *widget)
|
||||
{
|
||||
m_widget = widget;
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorBase::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
m_cursorPos = event->pos();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorBase::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
const QRect abortButtonRect(QRect(centeredRect().topRight() + QPoint(s_buttonSize, s_buttonSize), QSize(s_buttonSize, s_buttonSize)));
|
||||
auto pos = event->pos();
|
||||
|
||||
Q_ASSERT(m_widget != nullptr);
|
||||
|
||||
|
||||
if (abortButtonRect.contains(pos) || m_widget == nullptr) // X
|
||||
{
|
||||
qWarning() << "X pressed!";
|
||||
m_reverted = true;
|
||||
m_screen->revertSwitch();
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
if (processSelection(pos))
|
||||
{
|
||||
qWarning() << "process!";
|
||||
m_screen->finalizeSwitch();
|
||||
selected();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorBase::selected()
|
||||
{
|
||||
m_widget = nullptr;
|
||||
m_selected = true;
|
||||
emit signalSelected();
|
||||
}
|
||||
|
||||
|
||||
QRect MultiMonitorSelectorBase::centeredRect() const
|
||||
{
|
||||
// use cache
|
||||
if (m_cachedCenteredRect.isValid())
|
||||
return m_cachedCenteredRect;
|
||||
|
||||
// create cache
|
||||
const QSize thisSize(size());
|
||||
const QSize newSize(thisSize.width() * 0.20, thisSize.height() * 0.20);
|
||||
m_cachedCenteredRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, newSize, rect()); // find center
|
||||
|
||||
return m_cachedCenteredRect;
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorBase::setColors(QPainter &p, const QRect checkSelection, bool occupied, bool countEdge)
|
||||
{
|
||||
if (occupied)
|
||||
{
|
||||
p.setBrush(QColor("silver"));
|
||||
p.setPen(QPen(QColor("white"), 2));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (checkSelection.contains(m_cursorPos, countEdge)) // edge does not count
|
||||
{
|
||||
// selected
|
||||
p.setBrush(QColor("grey"));
|
||||
p.setPen(QPen(QColor("white"), 2));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// unselected
|
||||
p.setBrush(QColor("black"));
|
||||
p.setPen(QPen(p.brush(), 1));
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorBase::drawBase(QPainter &p, bool drawSelector, bool occupied, bool isSecondary, bool isInactiveWidgetOccupied)
|
||||
{
|
||||
bool showReplacementIndicator = true;
|
||||
#ifndef GUEZ_MODE
|
||||
isSecondary = false; // vorsichtshalber
|
||||
showReplacementIndicator = false;
|
||||
#endif
|
||||
|
||||
// prepare painter
|
||||
p.save();
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setRenderHint(QPainter::HighQualityAntialiasing, true);
|
||||
|
||||
|
||||
// paint transparent background across whole screen (transculent, dark grey)
|
||||
p.setBrush(QColor(53, 53, 53, 150));
|
||||
p.drawRect(geometry());
|
||||
|
||||
|
||||
// prepare painting window rects
|
||||
const QRect centered(centeredRect()); // the small chooser-rect
|
||||
p.setBrush(QColor("silver"));
|
||||
p.setPen(QPen(QColor("black"), 0.5));
|
||||
|
||||
|
||||
// draw as stacked to indicate that the new secondary widget will be stacked above the active main widget
|
||||
if (isSecondary && isInactiveWidgetOccupied)
|
||||
{
|
||||
const QRect r(centered.adjusted(12, 12, 12, 12));
|
||||
setColors(p, r, true);
|
||||
p.drawRoundedRect(r, 7, 7); // draw selector window
|
||||
}
|
||||
|
||||
|
||||
// paint selector
|
||||
if (drawSelector)
|
||||
{
|
||||
if (!m_screenIsIrreplaceable)
|
||||
{
|
||||
// NOTE: no need to check for irreplacibility here as there is no child to check :)
|
||||
setColors(p, centered, occupied); // to use the selector window as the selector itself
|
||||
}
|
||||
|
||||
p.drawRoundedRect(centered, 7, 7);
|
||||
}
|
||||
|
||||
|
||||
const int cX = centered.x();
|
||||
const int cW = centered.width();
|
||||
const int cY = centered.y();
|
||||
const int cH = centered.height();
|
||||
|
||||
|
||||
// draw marker for a secondary window (e.g. 'H', German for 'Hilfsansicht')
|
||||
if (isSecondary)
|
||||
{
|
||||
QPen pen(p.pen());
|
||||
QColor pc(pen.color());
|
||||
const QColor c(255 - pc.red(), 255 - pc.green(), 255 - pc.blue()); // invert for readability
|
||||
pen.setColor(c);
|
||||
p.setPen(pen);
|
||||
|
||||
QFont f(p.font());
|
||||
f.setBold(true);
|
||||
f.setPixelSize(cH / 2);
|
||||
p.setFont(f);
|
||||
|
||||
const QString text(tr("S", "Secondary (German: 'H' for 'Hilfsansicht')"));
|
||||
|
||||
p.drawText(centered, Qt::AlignCenter, text);
|
||||
}
|
||||
|
||||
|
||||
// draw abort "X" button
|
||||
const QRect abortButtonRect(QRect(centered.topRight() + QPoint(s_buttonSize, s_buttonSize), QSize(s_buttonSize, s_buttonSize)));
|
||||
const QPoint topRight(abortButtonRect.topLeft() /* :D */);
|
||||
setColors(p, abortButtonRect, false, false);
|
||||
|
||||
QPen pen(p.pen());
|
||||
pen.setWidth(5.0);
|
||||
p.setPen(pen);
|
||||
|
||||
p.drawLines(QVector<QLine>()
|
||||
<< QLine(topRight , topRight + QPoint(s_buttonSize, s_buttonSize))
|
||||
<< QLine(topRight + QPoint(0, s_buttonSize), topRight + QPoint(s_buttonSize, 0))
|
||||
);
|
||||
|
||||
|
||||
// draw the second rect - but only if this selection would replace an existing app / widget
|
||||
if (showReplacementIndicator && occupied)
|
||||
{
|
||||
p.setBrush(QColor("silver"));
|
||||
p.setPen(QPen(QColor("white"), 2));
|
||||
|
||||
const QRect secondRect(centered.adjusted(cW + 70, 0/*centered.height() + 20*/, cW + 70, 0/*centered.height() + 20*/));
|
||||
p.drawRoundedRect(secondRect, 7, 7);
|
||||
|
||||
// arrow line
|
||||
p.drawLine(cX + cW + 15, cY + (cH / 2),
|
||||
cX + cW + 55, cY + (cH / 2));
|
||||
|
||||
// the arrow
|
||||
p.drawPolygon(QPolygon(
|
||||
{
|
||||
QPoint(cX + cW + 56, cY + (cH / 2)), // spitze
|
||||
QPoint(cX + cW + 46, cY + (cH / 2) - 10),
|
||||
QPoint(cX + cW + 46, cY + (cH / 2) + 10),
|
||||
}));
|
||||
|
||||
const int srX = secondRect.x();
|
||||
const int srY = secondRect.y();
|
||||
const int srH = secondRect.height();
|
||||
const int diameter = srH - 50;
|
||||
const int widthByTwo = (secondRect.width() / 2);
|
||||
const int diameterByTwo = (diameter / 2);
|
||||
const int lineX1 = srX + widthByTwo - diameterByTwo;
|
||||
const int lineY1 = srY + 25;
|
||||
// red circle
|
||||
p.drawEllipse(lineX1, lineY1, diameter, diameter);
|
||||
|
||||
// draw 'S' - only if secondary
|
||||
if (isSecondary)
|
||||
{
|
||||
QFont f(p.font());
|
||||
f.setBold(true);
|
||||
f.setPixelSize(srH / 2);
|
||||
p.setFont(f);
|
||||
|
||||
const QString text(tr("S", "Secondary (German: 'H' for 'Hilfsansicht')"));
|
||||
|
||||
p.drawText(secondRect, Qt::AlignCenter, text);
|
||||
}
|
||||
|
||||
// red cross inside circle
|
||||
const int lineX2 = lineX1 + diameter;
|
||||
const int lineY2 = lineY1 + diameter;
|
||||
p.drawLine(lineX1, lineY1, lineX2, lineY2);
|
||||
p.drawLine(lineX2, lineY1, lineX1, lineY2);
|
||||
}
|
||||
|
||||
|
||||
p.restore();
|
||||
}
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMap>
|
||||
|
||||
|
||||
class QScreen;
|
||||
class QPainter;
|
||||
class QPaintEvent;
|
||||
class QMouseEvent;
|
||||
class QCloseEvent;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LtsScreenBase;
|
||||
class LtsScreenContainer;
|
||||
|
||||
class MultiMonitorSelectorBase : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MultiMonitorSelectorBase(LtsScreenBase *screen, const QVector<QWidget *> &irreplacableWidgets, QWidget */*superParent*/);
|
||||
virtual ~MultiMonitorSelectorBase() {}
|
||||
|
||||
void setSwap(LtsScreenContainer *swap) { m_swap = swap; }
|
||||
void setWidget(QWidget *widget);
|
||||
LtsScreenBase *screen() const { return m_screen; }
|
||||
|
||||
signals:
|
||||
void signalSelected();
|
||||
|
||||
protected:
|
||||
static int s_offset;
|
||||
static int s_margin;
|
||||
static int s_buttonSize;
|
||||
|
||||
LtsScreenContainer *m_swap { nullptr };
|
||||
QWidget *m_widget { nullptr };
|
||||
mutable QRect m_cachedCenteredRect;
|
||||
QVector<QWidget *> m_irreplacableWidgets;
|
||||
|
||||
void drawBase(QPainter &p, bool drawSelector = true, bool occupied = false, bool isSecondary = false, bool isInactiveWidgetOccupied = false);
|
||||
bool killOrSwapOldBLW(LtsScreenContainer *container) noexcept(false); // returns true if nothing has to be done for the caller anymore
|
||||
bool isNullSwap(LtsScreenContainer *container);
|
||||
QRect centeredRect() const;
|
||||
void setColors(QPainter &p, const QRect checkSelection, bool occupied = false, bool countEdge = false);
|
||||
void selected();
|
||||
virtual bool processSelection(const QPoint &selection) = 0;
|
||||
|
||||
private:
|
||||
QPoint m_cursorPos;
|
||||
bool m_selected { false }; // to differ between user made a selection or user aborted
|
||||
LtsScreenBase *m_screen;
|
||||
bool m_screenIsIrreplaceable;
|
||||
bool m_reverted { false };
|
||||
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
@@ -0,0 +1,100 @@
|
||||
// TODO: hdr
|
||||
|
||||
#include "lts_multi_monitor.h"
|
||||
#include "lts_multi_monitor_selector_normal.h"
|
||||
#include "lts_screen_container.h"
|
||||
#include "lts_screen_normal.h"
|
||||
#include "lts_dummy_widget.h"
|
||||
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QStyle>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QColor>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
|
||||
MultiMonitorSelectorNormal::MultiMonitorSelectorNormal(LtsScreenNormal *screen, bool isSecondary, const QVector<QWidget *> &irreplacableWidgets)
|
||||
: MultiMonitorSelectorBase(screen, irreplacableWidgets, nullptr)
|
||||
, m_screen(screen)
|
||||
, m_screenIsEmpty(screen->isEmpty())
|
||||
, m_isSecondary(isSecondary)
|
||||
, m_isInactiveWidgetOccupied(screen->child()->isInactiveWidgetOccupied())
|
||||
{
|
||||
//qWarning() << "ctor" << (quintptr)this;
|
||||
}
|
||||
|
||||
|
||||
MultiMonitorSelectorNormal::~MultiMonitorSelectorNormal()
|
||||
{
|
||||
//qWarning() << "dtor" << (quintptr)this;
|
||||
}
|
||||
|
||||
|
||||
bool MultiMonitorSelectorNormal::processSelection(const QPoint &selection)
|
||||
{
|
||||
//qWarning() << "proc" << (quintptr)this;
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
if (!centeredRect().contains(selection))
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_screen->isIrreplaceable())
|
||||
{
|
||||
// qWarning() << "user tried to replace a screen that is irreplacable";
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (m_irreplacableWidgets.contains(m_screen->child()->widget()))
|
||||
return false;
|
||||
}
|
||||
catch(...){}
|
||||
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
// if selected while occupied, delete (or directly swap if swap wa set)
|
||||
try
|
||||
{
|
||||
if (killOrSwapOldBLW(m_screen->child()))
|
||||
{
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false; // error
|
||||
}
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
// there is no old widget if all are empty, so just put the widget and done
|
||||
m_screen->putWidgetUnmanaged(m_widget);
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorNormal::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
drawBase(p, true, !m_screenIsEmpty, m_isSecondary, m_isInactiveWidgetOccupied);
|
||||
}
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "lts_multi_monitor_selector_base.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMap>
|
||||
|
||||
|
||||
class QScreen;
|
||||
class QPaintEvent;
|
||||
class QMouseEvent;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LtsScreenBase;
|
||||
class LtsScreenNormal;
|
||||
|
||||
class MultiMonitorSelectorNormal : public MultiMonitorSelectorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MultiMonitorSelectorNormal(LtsScreenNormal *screen, bool isSecondary, const QVector<QWidget *> &irreplacableWidgets = {});
|
||||
~MultiMonitorSelectorNormal();
|
||||
|
||||
private:
|
||||
LtsScreenNormal *m_screen;
|
||||
bool m_screenIsEmpty;
|
||||
bool m_isSecondary;
|
||||
bool m_isInactiveWidgetOccupied;
|
||||
|
||||
bool processSelection(const QPoint &selection) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
@@ -0,0 +1,335 @@
|
||||
// TODO: hdr
|
||||
|
||||
#include "lts_multi_monitor.h"
|
||||
#include "lts_multi_monitor_selector_wide.h"
|
||||
#include "lts_screen_container.h"
|
||||
#include "lts_screen_wide.h"
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QStyle>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QColor>
|
||||
|
||||
//#include <QDebug>
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
|
||||
MultiMonitorSelectorWide::MultiMonitorSelectorWide(LtsScreenWide *screen, QWidget */*superParent*/, const QVector<QWidget *> &irreplacableWidgets)
|
||||
: MultiMonitorSelectorBase(screen, irreplacableWidgets, nullptr /*, superParent*/)
|
||||
, m_screen(screen)
|
||||
{}
|
||||
|
||||
|
||||
bool MultiMonitorSelectorWide::isDrawOnlyOneSelector()
|
||||
{
|
||||
/* [*1] if a swap was rquested and the origin widget of the swap if on a wide screen and
|
||||
* ONLY main container is occupied, then draw only ONE selector, as otherwise left
|
||||
* or right could be selected but only one widget would be on the screen. the correct
|
||||
* behaviour would be to have only the main container if only one widget is on the
|
||||
* wide screen. */
|
||||
|
||||
bool res = ((m_screen->childMain()->isEmpty() && m_screen->childLeft()->isEmpty() && m_screen->childRight()->isEmpty())
|
||||
|| (!m_screen->childMain()->isEmpty() && m_swap != nullptr && m_screen->childMain() == m_swap)); // draw ONE selector if completely empty (or *1)
|
||||
|
||||
/*qWarning() << "isDrawOnlyOneSelector: occupied? "
|
||||
<< "- main:" << !m_screen->childMain()->isEmpty()
|
||||
<< "- left:" << !m_screen->childLeft()->isEmpty()
|
||||
<< "- right:" << !m_screen->childRight()->isEmpty()
|
||||
<< "result" << res;*/
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool MultiMonitorSelectorWide::processSelection(const QPoint &selection)
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
/*
|
||||
* if the whole screen is occupied, its child needs to be placed either on
|
||||
* the left (if the right container was selected), or the right container
|
||||
* (if the left container was selected) as there are now two children:
|
||||
* the original child which moved from the main container to the left/right
|
||||
* container and the newly added child which gets added in thie method here
|
||||
*/
|
||||
|
||||
LtsScreenContainer *main = m_screen->childMain();
|
||||
LtsScreenContainer *left = m_screen->childLeft();
|
||||
LtsScreenContainer *right = m_screen->childRight();
|
||||
|
||||
bool singleSelector = isDrawOnlyOneSelector();
|
||||
|
||||
if (singleSelector)
|
||||
{
|
||||
if (main->isIrreplaceable())
|
||||
{
|
||||
// qWarning() << "user tried to replace a screen that is irreplacable";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (m_irreplacableWidgets.contains(main->widget()))
|
||||
return false;
|
||||
}
|
||||
catch(...){}
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
if (isNullSwap(m_screen->childMain()))
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return true; // just say we swapped
|
||||
}
|
||||
|
||||
if (centeredRect().contains(selection))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (killOrSwapOldBLW(m_screen->childMain()))
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
return false;
|
||||
}
|
||||
|
||||
// there is no old widget if all are empty, so just put the widget and done
|
||||
m_screen->putWidgetUnmanagedMain(m_widget);
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool leftContains = leftSide().contains(selection);
|
||||
bool rightContains = rightSide().contains(selection);
|
||||
|
||||
|
||||
// if there's a child on main and left/right was selected
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
if (leftContains)
|
||||
{
|
||||
if (left->isIrreplaceable())
|
||||
{
|
||||
//qWarning() << "user tried to replace a screen that is irreplacable";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (m_irreplacableWidgets.contains(left->widget()))
|
||||
return false;
|
||||
}
|
||||
catch(...){}
|
||||
|
||||
|
||||
// if left or right is selected while main is occupied, main has to be moved to right
|
||||
try
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
QWidget *oldWidget = main->stealWidget();
|
||||
// if code goes here, it is guaranteed that left AND right are empty, as main is occupied and main does occupy all the space
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
m_screen->putWidgetUnmanagedRight(oldWidget);
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
// kill old widget that was on left before (or swap it if a swap was requested)
|
||||
try
|
||||
{
|
||||
if (killOrSwapOldBLW(left))
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
m_screen->putWidgetUnmanagedLeft(m_widget);
|
||||
return true;
|
||||
}
|
||||
else if (rightContains)
|
||||
{
|
||||
if (right->isIrreplaceable())
|
||||
{
|
||||
// qWarning() << "user tried to replace a screen that is irreplacable";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (m_irreplacableWidgets.contains(right->widget()))
|
||||
return false;
|
||||
}
|
||||
catch(...){}
|
||||
|
||||
|
||||
// if left or right is selected while main is occupied, main has to be moved to right
|
||||
try
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
QWidget *oldWidget = main->stealWidget();
|
||||
// if code goes here, it is guaranteed that left AND right are empty, as main is occupied and main does occupy all the space
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
m_screen->putWidgetUnmanagedLeft(oldWidget);
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
// kill old widget that was on right before (or swap it if a swap was requested)
|
||||
try
|
||||
{
|
||||
if (killOrSwapOldBLW(right))
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
return false;
|
||||
}
|
||||
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
m_screen->putWidgetUnmanagedRight(m_widget);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QRect MultiMonitorSelectorWide::leftSide() const
|
||||
{
|
||||
QRect centered = centeredRect();
|
||||
|
||||
return QRect(centered.x() + s_margin,
|
||||
centered.y() + s_margin,
|
||||
(centered.width() / 2) - (s_margin*2),
|
||||
centered.height() - (s_margin*2));
|
||||
}
|
||||
|
||||
|
||||
QRect MultiMonitorSelectorWide::rightSide() const
|
||||
{
|
||||
QRect centered = centeredRect();
|
||||
|
||||
return QRect(centered.x() + (centered.width() / 2) + s_margin,
|
||||
centered.y() + s_margin,
|
||||
(centered.width() / 2) - (s_margin*2),
|
||||
centered.height() - (s_margin*2));
|
||||
}
|
||||
|
||||
|
||||
void MultiMonitorSelectorWide::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.save();
|
||||
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setRenderHint(QPainter::HighQualityAntialiasing, true);
|
||||
|
||||
const QRect centered(centeredRect()); // the small chooser-rect
|
||||
|
||||
bool drawOnlyOneSelector = isDrawOnlyOneSelector();
|
||||
drawBase(p, drawOnlyOneSelector, false /* never occupied - NOTE: doesn't sound like it makes sense but it DOES! */);
|
||||
|
||||
|
||||
// draw both selectors
|
||||
if (!drawOnlyOneSelector) // draw both selectors
|
||||
{
|
||||
p.setBrush(QColor("silver"));
|
||||
p.setPen(QPen(QColor("black"), 0.5));
|
||||
p.drawRoundedRect(centered, 7, 7);
|
||||
|
||||
|
||||
bool leftIrreplacable = false;
|
||||
try
|
||||
{
|
||||
leftIrreplacable = m_irreplacableWidgets.contains(m_screen->childLeft()->widget());
|
||||
}
|
||||
catch(...){}
|
||||
|
||||
if (!m_screen->childLeft()->isIrreplaceable() && !leftIrreplacable)
|
||||
{
|
||||
// draw left side
|
||||
const QRect leftSide(centered.x() + s_margin,
|
||||
centered.y() + s_margin,
|
||||
(centered.width() / 2) - (s_margin*2),
|
||||
centered.height() - (s_margin*2));
|
||||
|
||||
setColors(p, leftSide, !m_screen->childLeft()->isEmpty());
|
||||
|
||||
p.drawRoundedRect(leftSide, 6, 6);
|
||||
}
|
||||
|
||||
|
||||
bool rightIrreplacable = false;
|
||||
try
|
||||
{
|
||||
rightIrreplacable = m_irreplacableWidgets.contains(m_screen->childRight()->widget());
|
||||
}
|
||||
catch(...){}
|
||||
|
||||
if (!m_screen->childRight()->isIrreplaceable() && !rightIrreplacable)
|
||||
{
|
||||
// draw right side
|
||||
const QRect rightSide(centered.x() + (centered.width() / 2) + s_margin,
|
||||
centered.y() + s_margin,
|
||||
(centered.width() / 2) - (s_margin*2),
|
||||
centered.height() - (s_margin*2));
|
||||
|
||||
setColors(p, rightSide, !m_screen->childRight()->isEmpty());
|
||||
|
||||
p.drawRoundedRect(rightSide, 6, 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// draw abort "X" button
|
||||
static int buttonSize = 20;
|
||||
const QRect abortButtonRect(QRect(centered.topRight() + QPoint(buttonSize, buttonSize), QSize(buttonSize, buttonSize)));
|
||||
const QPoint topRight(abortButtonRect.topLeft() /* :D */);
|
||||
setColors(p, abortButtonRect, false, false);
|
||||
|
||||
// meh ...:
|
||||
QPen pen(p.pen());
|
||||
pen.setWidth(5.0);
|
||||
p.setPen(pen);
|
||||
|
||||
p.drawLines(QVector<QLine>()
|
||||
<< QLine(topRight , topRight + QPoint(buttonSize, buttonSize))
|
||||
<< QLine(topRight + QPoint(0, buttonSize), topRight + QPoint(buttonSize, 0))
|
||||
);
|
||||
|
||||
|
||||
p.restore();
|
||||
}
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "LtsMultiMonitor/lts_multi_monitor_selector_base.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMap>
|
||||
|
||||
|
||||
class QScreen;
|
||||
class QPaintEvent;
|
||||
class QMouseEvent;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LtsScreenWide;
|
||||
class LtsScreenBase;
|
||||
|
||||
class MultiMonitorSelectorWide : public MultiMonitorSelectorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MultiMonitorSelectorWide(LtsScreenWide *screen, QWidget *superParent, const QVector<QWidget *> &irreplacableWidgets);
|
||||
|
||||
private:
|
||||
LtsScreenWide *m_screen;
|
||||
|
||||
bool isDrawOnlyOneSelector();
|
||||
QRect leftSide() const;
|
||||
QRect rightSide() const;
|
||||
bool processSelection(const QPoint &selection) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#include "lts_screen_base.h"
|
||||
#include "lts_dummy_widget.h"
|
||||
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
#include <LtsUtilsCore/promise.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSplitter>
|
||||
#include <QHBoxLayout>
|
||||
#include <QCloseEvent>
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
|
||||
#endif
|
||||
|
||||
|
||||
class NoCloseWidget : public QWidget
|
||||
{
|
||||
void closeEvent(QCloseEvent *event) override
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
LtsScreenBase::LtsScreenBase(QRect screenGeo, int screenId, bool debugMargins, const QString &serial)
|
||||
: m_screenDialog(new NoCloseWidget())
|
||||
, m_screenId(screenId)
|
||||
, m_serial(serial)
|
||||
, m_screenRect(screenGeo)
|
||||
{
|
||||
connect(this, &LtsScreenBase::destroyed,
|
||||
m_screenDialog, &QWidget::deleteLater);
|
||||
|
||||
if (debugMargins)
|
||||
{
|
||||
qWarning() << "LtsScreenBase: screen rect adjusted for dev machines!";
|
||||
m_screenRect.adjust(+50, +50, -100, -100);
|
||||
}
|
||||
|
||||
// NOTE: directly put the child on the m_screenDialog
|
||||
m_screenDialog->setWindowFlags(/*Qt::Dialog | */Qt::FramelessWindowHint);
|
||||
m_screenDialog->setGeometry(m_screenRect);
|
||||
m_screenDialog->setMinimumSize(m_screenRect.width(), m_screenRect.height());
|
||||
m_screenDialog->setMaximumSize(m_screenRect.width(), m_screenRect.height());
|
||||
m_screenDialog->setContentsMargins(0, 0, 0, 0);
|
||||
m_screenDialog->setWindowFlag(Qt::WindowType::WindowStaysOnBottomHint); // external applications like AVS and others should be able to overlap!
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
m_screenDialog->winId(); // make sure windowHandle() is not a nullptr
|
||||
QWindowsWindowFunctions::setHasBorderInFullScreen(m_screenDialog->windowHandle(), true);
|
||||
#endif
|
||||
|
||||
// make the screen transparent
|
||||
//m_screenDialog->setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
|
||||
|
||||
// FIXME: unfortunately OpenGL, used for bgv, doesn't render on transculent windows, so we can't use it ...
|
||||
//m_screenDialog->setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
|
||||
|
||||
//m_screenDialog->setAttribute(Qt::WA_PaintOnScreen); // workaround for Qt < 5.3>
|
||||
|
||||
m_screenDialog->activateWindow();
|
||||
m_screenDialog->show();//FullScreen(); // to apply the border
|
||||
}
|
||||
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "lts_multi_monitor_export.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QRect>
|
||||
|
||||
class QSplitter;
|
||||
class QHBoxLayout;
|
||||
|
||||
namespace LtsUtils { class Promise; }
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LtsScreenContainer;
|
||||
|
||||
// controller class, manages the underlying screen widget
|
||||
// hides from System Menu + Parent for QDialogs
|
||||
class LTSMULTIMONITOR_EXPORT LtsScreenBase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum WidgetType
|
||||
{
|
||||
MainWidget,
|
||||
SecondaryWidget,
|
||||
None,
|
||||
};
|
||||
|
||||
LtsScreenBase(QRect screenGeo, int screenId, bool debugMargins, const QString &serial);
|
||||
|
||||
const QRect &screenRect() const { return m_screenRect; }
|
||||
int screenId() const { return m_screenId; }
|
||||
const QString serialNumber() const { return m_serial; }
|
||||
QWidget *screenDialog() const { return m_screenDialog; }
|
||||
|
||||
virtual int screenType() const = 0;
|
||||
virtual int containerIdOfWidget(QWidget *widget) const noexcept(false) = 0;
|
||||
virtual LtsScreenContainer *putWidget(QWidget *widget, int screenPart = -1 /* unspecified */) noexcept(false) = 0;
|
||||
virtual LtsScreenContainer *containerOfWidget(QWidget *widget) noexcept(false) = 0;
|
||||
virtual bool isEmpty() const = 0;
|
||||
virtual bool isIrreplaceable() const = 0;
|
||||
virtual void check() {}
|
||||
virtual bool hasWidget(QWidget *widget) const = 0; // NOTE: ignores active widget - checks for main & secondary
|
||||
virtual void switchToTemp(WidgetType /*widgetType*/) {}
|
||||
virtual void finalizeSwitch() {}
|
||||
virtual void revertSwitch() {}
|
||||
|
||||
protected:
|
||||
QWidget *m_screenDialog;
|
||||
|
||||
private:
|
||||
int m_screenId;
|
||||
QString m_serial;
|
||||
QRect m_screenRect;
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+555
@@ -0,0 +1,555 @@
|
||||
#include "lts_screen_container.h"
|
||||
#include "lts_dummy_widget.h"
|
||||
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QResizeEvent>
|
||||
#include <QRect>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
QString LtsScreenContainer::geoStr() const
|
||||
{
|
||||
return QStringLiteral("%1:%2:")
|
||||
.arg(QString::number(m_screenGeo.x()).rightJustified(4, QLatin1Char(' ')))
|
||||
.arg(QString::number(m_screenGeo.y()).rightJustified(4, QLatin1Char(' ')))
|
||||
;
|
||||
}
|
||||
|
||||
LtsScreenContainer::LtsScreenContainer(QRect screenGeo)
|
||||
: m_layout(new QHBoxLayout)
|
||||
, m_screenGeo(screenGeo)
|
||||
{
|
||||
//m_layout->setContentsMargins(300, 300, 300, 300);
|
||||
m_layout->setMargin(0);
|
||||
m_layout->setSpacing(0);
|
||||
m_layout->setStretch(0, 999); // ?????
|
||||
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
vanish();
|
||||
setLayout(m_layout);
|
||||
|
||||
|
||||
|
||||
QTimer *t = new QTimer(this);
|
||||
t->setInterval(3000);
|
||||
#ifdef LTS_DEBUG
|
||||
t->start();
|
||||
#endif
|
||||
connect(t, &QTimer::timeout,
|
||||
this, [this, screenGeo]()
|
||||
{
|
||||
QString old(QStringLiteral("<#!UNKNOWN!#>"));
|
||||
|
||||
if (m_oldActiveWidget == nullptr)
|
||||
old = QStringLiteral("null");
|
||||
else if (m_oldActiveWidget == &m_mainWidget)
|
||||
old = QStringLiteral("MAIN");
|
||||
else if (m_oldActiveWidget == &m_secondaryWidget)
|
||||
old = QStringLiteral("SECONDARY");
|
||||
|
||||
//qWarning() << geoStr() << "OLD = " << old << "main" << m_mainWidget << " - sec" << m_secondaryWidget;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenContainer::isInactiveWidgetOccupied() const
|
||||
{
|
||||
if (m_activeWidget == &m_mainWidget && m_secondaryWidget != nullptr)
|
||||
return true;
|
||||
if (m_activeWidget == &m_secondaryWidget && m_mainWidget != nullptr)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QIcon LtsScreenContainer::mainWindowIcon() const
|
||||
{
|
||||
if (m_mainWidget != nullptr)
|
||||
return m_mainWidget->windowIcon();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
QIcon LtsScreenContainer::secondaryWindowIcon() const
|
||||
{
|
||||
if (m_secondaryWidget != nullptr)
|
||||
return m_secondaryWidget->windowIcon();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenContainer::mainAndSecondaryOccupied() const
|
||||
{
|
||||
return (m_mainWidget != nullptr && m_secondaryWidget != nullptr);
|
||||
}
|
||||
|
||||
|
||||
LtsScreenBase::WidgetType LtsScreenContainer::activeWidgetType() const
|
||||
{
|
||||
if (m_activeWidget == &m_mainWidget)
|
||||
return LtsScreenBase::WidgetType::MainWidget;
|
||||
if (m_activeWidget == &m_secondaryWidget)
|
||||
return LtsScreenBase::WidgetType::SecondaryWidget;
|
||||
|
||||
assert(false);
|
||||
return LtsScreenBase::WidgetType::None;
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::toggle()
|
||||
{
|
||||
//qWarning() << geoStr() << "SC TOGGLE";
|
||||
switchToTemp((*m_activeWidget == m_mainWidget) ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget);
|
||||
|
||||
QWidget *w = activeWidget();
|
||||
|
||||
assert(w != nullptr);
|
||||
if (w == nullptr) // bail for prod ...
|
||||
{
|
||||
revertSwitch();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tryPutDummyUnmanaged(w))
|
||||
w->show(); // if not fullscreen video, show the widget - don't show fullscreen video dummy widget as it's just an invisible helper
|
||||
|
||||
finalizeSwitch();
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenContainer::isIrreplaceable() const
|
||||
{
|
||||
if (m_activeWidget == nullptr)
|
||||
return false;
|
||||
|
||||
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(activeWidget());
|
||||
if (blw == nullptr) // can't be irreplacable if it's not an blw
|
||||
return false;
|
||||
|
||||
return blw->isIrreplaceable();
|
||||
}
|
||||
|
||||
|
||||
QWidget *LtsScreenContainer::activeWidget() const
|
||||
{
|
||||
assert(m_activeWidget != nullptr);
|
||||
|
||||
// if (m_mainWidget != nullptr && m_mainWidget->isVisible())
|
||||
// return m_mainWidget;
|
||||
// if (m_secondaryWidget != nullptr && m_secondaryWidget->isVisible())
|
||||
// return m_secondaryWidget;
|
||||
// return nullptr;
|
||||
return *m_activeWidget;
|
||||
}
|
||||
|
||||
|
||||
QWidget **LtsScreenContainer::activeWidgetPtr()
|
||||
{
|
||||
assert(m_activeWidget != nullptr);
|
||||
|
||||
// if (m_mainWidget != nullptr && m_mainWidget->isVisible())
|
||||
// return &m_mainWidget;
|
||||
// if (m_secondaryWidget != nullptr && m_secondaryWidget->isVisible())
|
||||
// return &m_secondaryWidget;
|
||||
// return nullptr;
|
||||
return m_activeWidget;
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::vanish()
|
||||
{
|
||||
return;
|
||||
|
||||
|
||||
|
||||
//setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
//setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
//setAttribute(Qt::WA_PaintOnScreen); // workaround for Qt < 5.3
|
||||
|
||||
|
||||
|
||||
// qWarning() << "m_layout" << (quintptr)m_layout;
|
||||
m_layout->setParent(nullptr);
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::reappear()
|
||||
{
|
||||
return;
|
||||
|
||||
|
||||
|
||||
//setAttribute(Qt::WA_NoSystemBackground, false);
|
||||
setAttribute(Qt::WA_TranslucentBackground, false);
|
||||
//setAttribute(Qt::WA_PaintOnScreen, false); // workaround for Qt < 5.3
|
||||
|
||||
|
||||
|
||||
// qWarning() << "m_layout" << (quintptr)m_layout;
|
||||
if (m_layout->parent() == nullptr) // FIXME: does Qt optimize this?
|
||||
setLayout(m_layout);
|
||||
}
|
||||
|
||||
|
||||
//void LtsScreenContainer::slotOverlayDestroyed()
|
||||
//{
|
||||
// qWarning() << "!!! mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
// if (sender() == static_cast<QWidget *>(m_widget)) // only show if it's REALLY that widget that was destroyed
|
||||
// {
|
||||
// qWarning() << "!!! mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
// vanish();
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
QWidget *LtsScreenContainer::stealWidget() noexcept(false)
|
||||
{
|
||||
//qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
QWidget **widgetPtr = activeWidgetPtr();
|
||||
QWidget *widget = *widgetPtr;
|
||||
|
||||
if (widget == nullptr)
|
||||
{
|
||||
//qWarning() << "!!! mep" << __FILE__ << __LINE__;
|
||||
throw "stealWidget: no widget to steal";
|
||||
}
|
||||
|
||||
//qWarning() << "stealWidget: stealing widget from" << (quintptr)this;
|
||||
|
||||
//vanish();
|
||||
widget->setParent(nullptr);
|
||||
vanish();
|
||||
|
||||
// do NOT send a signal if the widget was stolen instead of closed, etc.
|
||||
disconnect(widget, &QWidget::destroyed,
|
||||
this, &LtsScreenContainer::slotChildDestroyed);
|
||||
|
||||
// FullscreenVideoDummyWidget *dummy = qobject_cast<FullscreenVideoDummyWidget *>(m_widget);
|
||||
// if (dummy != nullptr)
|
||||
// {
|
||||
// disconnect(dummy, &FullscreenVideoDummyWidget::destroyed,
|
||||
// this, &LtsScreenContainer::slotOverlayDestroyed);
|
||||
// }
|
||||
|
||||
QWidget *oldWidget = widget;
|
||||
*widgetPtr = nullptr;
|
||||
//emit check();
|
||||
return oldWidget;
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenContainer::hasWidget(QWidget *widget) const
|
||||
{
|
||||
if (m_mainWidget != nullptr && m_mainWidget == widget)
|
||||
return true;
|
||||
if (m_secondaryWidget != nullptr && m_secondaryWidget == widget)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenContainer::isEmpty() const
|
||||
{
|
||||
QWidget **currentWidgetPtr = m_activeWidget;
|
||||
if (currentWidgetPtr == nullptr)
|
||||
return true;
|
||||
return (*currentWidgetPtr == nullptr);
|
||||
}
|
||||
|
||||
bool LtsScreenContainer::hasMainWidget()
|
||||
{
|
||||
return m_mainWidget != nullptr;
|
||||
}
|
||||
|
||||
bool LtsScreenContainer::hasSecondaryWidget()
|
||||
{
|
||||
return m_secondaryWidget != nullptr;
|
||||
}
|
||||
|
||||
bool LtsScreenContainer::tryPutDummyUnmanaged(QWidget *widget)
|
||||
{
|
||||
auto *dummy = qobject_cast<FullscreenVideoDummyWidget *>(widget);
|
||||
|
||||
if (dummy == nullptr)
|
||||
return false;
|
||||
|
||||
// qWarning() << "dummy" << (quintptr)dummy;
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
|
||||
//QRect r(dummy->mapToGlobal(dummy->geometry().topLeft()), dummy->size());
|
||||
QRect r(mapToGlobal(rect().topLeft()), rect().size());
|
||||
//qWarning() << geoStr() << "LtsScreenContainer::tryPutDummyUnmanaged => dummy->signalWidgetPlaced";
|
||||
emit dummy->signalWidgetPlaced(r);
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
//vanish(); // get out of the video's way [not needed as containers are always stay-on-bottom
|
||||
// QWidget **widgetPtr = activeWidgetPtr();
|
||||
// *widgetPtr = widget;
|
||||
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
emit signalSetCornerIcons();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::switchToTemp(LtsScreenBase::WidgetType widgetType)
|
||||
{
|
||||
// fprintf(stderr, "switchToTemp: (%s)\n", geoStr().toStdString().c_str());
|
||||
|
||||
Q_ASSERT(widgetType != LtsScreenBase::WidgetType::None);
|
||||
|
||||
if ((m_activeWidget == &m_mainWidget && widgetType == LtsScreenBase::WidgetType::MainWidget) ||
|
||||
(m_activeWidget == &m_secondaryWidget && widgetType == LtsScreenBase::WidgetType::SecondaryWidget))
|
||||
{
|
||||
// fprintf(stderr, "switchToTemp: null switch (%s)\n", geoStr().toStdString().c_str());
|
||||
//qWarning() << geoStr() << "switchToTemp: null switch!! widgetType:" << (int)widgetType;
|
||||
m_nullSwitchActive = true;
|
||||
return; // do nothing if already on requested widget
|
||||
}
|
||||
|
||||
if (widgetType == LtsScreenBase::WidgetType::MainWidget)
|
||||
m_oldActiveWidget = &m_secondaryWidget;
|
||||
else if (widgetType == LtsScreenBase::WidgetType::SecondaryWidget)
|
||||
m_oldActiveWidget = &m_mainWidget;
|
||||
else
|
||||
Q_ASSERT(false);
|
||||
|
||||
//m_oldActiveWidget = m_activeWidget; // can still be 0ptr (e.g. at first start)
|
||||
|
||||
// qWarning() << geoStr() << "switchToTemp:" << ((widgetType == LtsScreenBase::WidgetType::MainWidget) ? QStringLiteral("Main") : QStringLiteral("Secondary")) << "m_activeWidget before" << m_activeWidget << "(which means that m_oldActiveWidget is now exactly that)";
|
||||
m_activeWidget = (widgetType == LtsScreenBase::WidgetType::SecondaryWidget ? &m_secondaryWidget : &m_mainWidget);
|
||||
// qWarning() << geoStr() << "switchToTemp: m_activeWidget now" << m_activeWidget;
|
||||
// fprintf(stderr, "switchToTemp: m_activeWidget is now %d, %lld\n", (int)widgetType, (qintptr)m_activeWidget);
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::finalizeSwitch()
|
||||
{
|
||||
// fprintf(stderr, "finalizeSwitch: (%s)\n", geoStr().toStdString().c_str());
|
||||
|
||||
QWidget **oldActiveWidget = m_oldActiveWidget;
|
||||
m_oldActiveWidget = nullptr;
|
||||
//qWarning() << geoStr() << "finalizeSwitch: m_oldActiveWidget set to nullptr !";
|
||||
|
||||
if (m_nullSwitchActive)
|
||||
{
|
||||
// qWarning() << geoStr() << "finalizeSwitch: was null switch!";
|
||||
m_nullSwitchActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldActiveWidget == nullptr)
|
||||
{
|
||||
// qWarning() << geoStr() << "finalizeSwitch: m_oldActiveWidget == nullptr";
|
||||
return;
|
||||
}
|
||||
|
||||
if (*oldActiveWidget == nullptr)
|
||||
{
|
||||
// qWarning() << geoStr() << "finalizeSwitch: *m_oldActiveWidget == nullptr";
|
||||
return;
|
||||
}
|
||||
|
||||
// not needed anymore because of check for null switch (?)
|
||||
// if (*oldActiveWidget == *m_activeWidget)
|
||||
// {
|
||||
// qWarning() << "finalizeSwitch: (*m_oldActiveWidget == *m_activeWidget) - old:" << (quintptr)*oldActiveWidget << "active:" << (quintptr)*m_activeWidget << *m_activeWidget << (*m_activeWidget)->windowTitle();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// qWarning() << geoStr() << "finalizeSwitch: (info): old:" << (quintptr)*oldActiveWidget << "active" << (quintptr)*m_activeWidget << *m_activeWidget;
|
||||
|
||||
// if the active widget changed now, hide the old widget before showing the new one
|
||||
QWidget *widget = *oldActiveWidget;
|
||||
auto *dummy = qobject_cast<FullscreenVideoDummyWidget *>(widget);
|
||||
if (dummy != nullptr)
|
||||
{
|
||||
// qWarning() << geoStr() << "LtsScreenContainer::finalizeSwitch() => dummy->hideVideo()";
|
||||
dummy->hideVideo(); // widget is always hidden, so a simple hide would do nothing - special case here
|
||||
}
|
||||
else // in any other case it's sufficient to just hide the widget
|
||||
{
|
||||
// qWarning() << geoStr() << "LtsScreenContainer::finalizeSwitch() => widget->hide()";
|
||||
widget->hide();
|
||||
}
|
||||
|
||||
QWidget *widget2 = *m_activeWidget;
|
||||
auto *dummy2 = qobject_cast<FullscreenVideoDummyWidget *>(widget2);
|
||||
if (dummy2 != nullptr)
|
||||
{
|
||||
dummy2->showVideo();
|
||||
}
|
||||
|
||||
emit signalSetCornerIcons();
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::revertSwitch()
|
||||
{
|
||||
// fprintf(stderr, "revertSwitch: (%s)\n", geoStr().toStdString().c_str());
|
||||
|
||||
QWidget **oldActiveWidgetBackup = m_oldActiveWidget;
|
||||
m_oldActiveWidget = nullptr;
|
||||
// qWarning() << geoStr() << "revertSwitch: m_oldActiveWidget set to nullptr !";
|
||||
|
||||
if (m_nullSwitchActive)
|
||||
{
|
||||
// qWarning() << geoStr() << "revertSwitch: was null switch!";
|
||||
m_nullSwitchActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//qWarning() << geoStr() << "revertSwitch: REVERT #1";
|
||||
//assert(m_oldActiveWidget != nullptr);
|
||||
if (oldActiveWidgetBackup == nullptr)
|
||||
return;
|
||||
// qWarning() << geoStr() << "revertSwitch: REVERT #2";
|
||||
m_activeWidget = oldActiveWidgetBackup;
|
||||
// qWarning() << geoStr() << "revertSwitch: m_activeWidget set to oldActiveWidgetBackup" << oldActiveWidgetBackup;
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::setWidget(QWidget *widget)
|
||||
{
|
||||
// fprintf(stderr, "setWidget: (%s)\n", geoStr().toStdString().c_str());
|
||||
|
||||
// NOTE: switch to main widget or secondary widget has to be done by the selector prior to calling this method
|
||||
|
||||
QWidget **widgetPtr = activeWidgetPtr();
|
||||
|
||||
qWarning() << geoStr() << "setWidget: m_activeWidget" << m_activeWidget;
|
||||
|
||||
QWidget *currentWidget = *widgetPtr;
|
||||
|
||||
Q_ASSERT(currentWidget == nullptr); // don't set a widget if there is already one (LtsMultiMonitorSelector*'s responsibility to handle this correctly)
|
||||
// if (m_widget != nullptr)
|
||||
// qWarning() << "LtsScreenContainer::setWidget: m_widget is not empty!!!";
|
||||
|
||||
//qWarning() << (quintptr)widget << "hier" << __LINE__;
|
||||
|
||||
*widgetPtr = widget;
|
||||
reappear();
|
||||
|
||||
connect(widget, &QWidget::destroyed,
|
||||
this, &LtsScreenContainer::slotChildDestroyed,
|
||||
Qt::UniqueConnection);
|
||||
|
||||
if (tryPutDummyUnmanaged(widget))
|
||||
return;
|
||||
|
||||
//qWarning() << "hier" << __LINE__;
|
||||
|
||||
//m_widget->setAutoFillBackground(true); // as the background itself is transparent
|
||||
|
||||
// qWarning() << "!!>>> B1" << m_widget << m_widget->parent();
|
||||
m_layout->addWidget(widget);
|
||||
// qWarning() << "!!>>> B2" << m_widget << m_widget->parent();
|
||||
|
||||
// qWarning() << "!!>>> B4" << m_widget << m_widget->parent();
|
||||
// QMetaObject::invokeMethod(this, [currentWidget]
|
||||
// {
|
||||
// qWarning() << "!!>>> 4F" << w << w->parent();
|
||||
// });
|
||||
|
||||
widget->show();
|
||||
|
||||
emit signalSetCornerIcons();
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenContainer::slotChildDestroyed()
|
||||
{
|
||||
if (sender() == static_cast<QObject *>(m_mainWidget))
|
||||
{
|
||||
m_mainWidget = nullptr;
|
||||
qWarning() << geoStr() << "m_mainWidget set to nullptr!";
|
||||
|
||||
if (*m_activeWidget == m_mainWidget) // switch to sec if not already sec
|
||||
{
|
||||
qWarning() << "slotChildDestroyed: switching to sec";
|
||||
switchToTemp(LtsScreenBase::WidgetType::SecondaryWidget);
|
||||
|
||||
QWidget *widget = activeWidget();
|
||||
|
||||
if (widget != nullptr)
|
||||
{
|
||||
auto *dummy = qobject_cast<FullscreenVideoDummyWidget *>(widget);
|
||||
if (dummy != nullptr)
|
||||
{
|
||||
QRect r(mapToGlobal(rect().topLeft()), rect().size());
|
||||
qWarning() << geoStr() << "LtsScreenContainer::slotChildDestroyed => (main) dummy->signalWidgetPlaced";
|
||||
emit dummy->signalWidgetPlaced(r);
|
||||
}
|
||||
else // in any other case it's sufficient to just hide the widget
|
||||
{
|
||||
widget->show();
|
||||
}
|
||||
}
|
||||
|
||||
finalizeSwitch();
|
||||
}
|
||||
}
|
||||
else if (sender() == static_cast<QObject *>(m_secondaryWidget))
|
||||
{
|
||||
m_secondaryWidget = nullptr;
|
||||
qWarning() << geoStr() << "m_secondaryWidget set to nullptr!";
|
||||
|
||||
if (*m_activeWidget == m_secondaryWidget) // switch to main if not already main
|
||||
{
|
||||
qWarning() << "slotChildDestroyed: switching to main";
|
||||
switchToTemp(LtsScreenBase::WidgetType::MainWidget);
|
||||
|
||||
QWidget *widget = activeWidget();
|
||||
|
||||
if (widget != nullptr)
|
||||
{
|
||||
auto *dummy = qobject_cast<FullscreenVideoDummyWidget *>(widget);
|
||||
if (dummy != nullptr)
|
||||
{
|
||||
QRect r(mapToGlobal(rect().topLeft()), rect().size());
|
||||
qWarning() << geoStr() << "LtsScreenContainer::slotChildDestroyed => (sec) dummy->signalWidgetPlaced";
|
||||
emit dummy->signalWidgetPlaced(r);
|
||||
}
|
||||
else // in any other case it's sufficient to just hide the widget
|
||||
{
|
||||
widget->show();
|
||||
}
|
||||
}
|
||||
|
||||
finalizeSwitch();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << geoStr() << "slotChildDestroyed NOT received from any known widget !!";
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
vanish();
|
||||
emit check();
|
||||
}
|
||||
|
||||
|
||||
QWidget *LtsScreenContainer::widget() noexcept(false)
|
||||
{
|
||||
QWidget **widgetPtr = m_activeWidget;
|
||||
if (widgetPtr != nullptr && *widgetPtr != nullptr)
|
||||
return *widgetPtr;
|
||||
throw "no widget in container";
|
||||
}
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "lts_screen_base.h" // enum WidgetType
|
||||
|
||||
#include <lts_multi_monitor_export.h> // TODO: export needed?
|
||||
|
||||
#include <QWidget>
|
||||
#include <QRect>
|
||||
|
||||
|
||||
class QHBoxLayout;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LTSMULTIMONITOR_EXPORT LtsScreenContainer : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LtsScreenContainer(QRect screenGeo);
|
||||
|
||||
bool isIrreplaceable() const;
|
||||
bool isEmpty() const;
|
||||
bool hasMainWidget();
|
||||
bool hasSecondaryWidget();
|
||||
void setWidget(QWidget *widget);
|
||||
bool hasWidget(QWidget *widget) const;
|
||||
QWidget *widget() noexcept(false);
|
||||
QWidget *stealWidget() noexcept(false);
|
||||
void switchToTemp(LtsScreenBase::WidgetType widgetType);
|
||||
void finalizeSwitch();
|
||||
void revertSwitch();
|
||||
bool mainAndSecondaryOccupied() const;
|
||||
QIcon mainWindowIcon() const;
|
||||
QIcon secondaryWindowIcon() const;
|
||||
LtsScreenBase::WidgetType activeWidgetType() const;
|
||||
bool isInactiveWidgetOccupied() const;
|
||||
|
||||
signals:
|
||||
void check();
|
||||
void signalSetCornerIcons();
|
||||
|
||||
private:
|
||||
QHBoxLayout *m_layout { nullptr };
|
||||
QWidget *m_mainWidget { nullptr }; // TODO C++17: to optional :(
|
||||
QWidget *m_secondaryWidget { nullptr }; // TODO C++17: to optional :(
|
||||
QWidget **m_activeWidget { nullptr };
|
||||
QWidget **m_oldActiveWidget { nullptr };
|
||||
bool m_nullSwitchActive { false };
|
||||
QRect m_screenGeo;
|
||||
|
||||
QString geoStr() const;
|
||||
bool tryPutDummyUnmanaged(QWidget *widget);
|
||||
void vanish();
|
||||
void reappear();
|
||||
QWidget *activeWidget() const;
|
||||
QWidget **activeWidgetPtr();
|
||||
|
||||
public slots:
|
||||
void toggle();
|
||||
|
||||
private slots:
|
||||
//void slotOverlayDestroyed();
|
||||
void slotChildDestroyed();
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
#include "lts_screen_normal.h"
|
||||
#include "lts_dummy_widget.h"
|
||||
#include "lts_screen_container.h"
|
||||
#include "lts_magic_corner.h"
|
||||
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSplitter>
|
||||
#include <QHBoxLayout>
|
||||
//#include <QTimer>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
|
||||
LtsScreenNormal::LtsScreenNormal(QRect screenGeo, int screenId, bool debugMargins, const QString &serial)
|
||||
: LtsScreenBase(screenGeo, screenId, debugMargins, serial)
|
||||
, m_childContainer(new LtsScreenContainer(screenGeo))
|
||||
, m_corner(new LtsMagicCorner(screenGeo))
|
||||
{
|
||||
// force check if widget in container was deleted:
|
||||
connect(m_childContainer, &LtsScreenContainer::check,
|
||||
this, &LtsScreenNormal::check);
|
||||
|
||||
auto *theLayout = new QHBoxLayout;
|
||||
|
||||
theLayout->setContentsMargins(0, 0, 0, 0);
|
||||
theLayout->setMargin(0);
|
||||
theLayout->setSpacing(0);
|
||||
theLayout->setStretch(0, 999); // ?????
|
||||
theLayout->addWidget(m_childContainer);
|
||||
|
||||
m_screenDialog->setLayout(theLayout);
|
||||
|
||||
m_corner->hide();
|
||||
connect(m_corner, &LtsMagicCorner::toggle,
|
||||
this, [this]()
|
||||
{
|
||||
m_childContainer->toggle();
|
||||
m_corner->setActiveWidgetType(m_childContainer->activeWidgetType());
|
||||
setCornerIcons();
|
||||
});
|
||||
|
||||
connect(m_corner, &LtsMagicCorner::signalMakeAllSwole,
|
||||
this, &LtsScreenNormal::signalMakeAllSwole); // =>
|
||||
connect(this, &LtsScreenNormal::signalMakeSwole,
|
||||
m_corner, &LtsMagicCorner::makeSwole); // <=
|
||||
|
||||
connect(m_childContainer, &LtsScreenContainer::signalSetCornerIcons,
|
||||
this, &LtsScreenNormal::setCornerIcons);
|
||||
}
|
||||
|
||||
|
||||
LtsScreenNormal::~LtsScreenNormal()
|
||||
{
|
||||
m_corner->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenNormal::setCornerIcons()
|
||||
{
|
||||
m_corner->setActiveWidgetType(m_childContainer->activeWidgetType());
|
||||
m_corner->setIcon(m_childContainer->mainWindowIcon(), LtsScreenBase::WidgetType::MainWidget);
|
||||
m_corner->setIcon(m_childContainer->secondaryWindowIcon(), LtsScreenBase::WidgetType::SecondaryWidget);
|
||||
//qWarning() << "setCornerIcons done";
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenNormal::check()
|
||||
{
|
||||
m_childContainer->mainAndSecondaryOccupied() ? m_corner->show() : m_corner->hide();
|
||||
}
|
||||
|
||||
|
||||
LtsScreenContainer *LtsScreenNormal::containerOfWidget(QWidget *widget)
|
||||
{
|
||||
if (m_childContainer->hasWidget(widget))
|
||||
return m_childContainer;
|
||||
throw "widget is not present on this container";
|
||||
}
|
||||
|
||||
void LtsScreenNormal::hideMagicCorner(bool hide)
|
||||
{
|
||||
m_corner->setFreezed(hide);
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenNormal::isIrreplaceable() const
|
||||
{
|
||||
return m_childContainer->isIrreplaceable();
|
||||
}
|
||||
|
||||
|
||||
int LtsScreenNormal::containerIdOfWidget(QWidget *widget) const noexcept(false)
|
||||
{
|
||||
if (m_childContainer->hasWidget(widget))
|
||||
return 0;
|
||||
throw "widget not on this screen";
|
||||
}
|
||||
|
||||
|
||||
LtsScreenContainer *LtsScreenNormal::putWidget(QWidget *widget, int /*screenPart normal screen has no parts*/) noexcept(false)
|
||||
{
|
||||
if (!isEmpty())
|
||||
throw "cannot put widget as container is non-empty"; // can't put something on this screen as the container is already occupied
|
||||
putWidgetUnmanaged(widget);
|
||||
return m_childContainer;
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenNormal::hasWidget(QWidget *widget) const
|
||||
{
|
||||
return (m_childContainer->hasWidget(widget));
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenNormal::putWidgetUnmanaged(QWidget *widget)
|
||||
{
|
||||
//qWarning() << "putWidgetUnmanaged" << /*(quintptr)*/widget;
|
||||
|
||||
// QTimer *t = new QTimer;
|
||||
// t->setInterval(0);
|
||||
// QPointer<QWidget> wptr(widget);
|
||||
// connect(t, &QTimer::timeout,
|
||||
// this, [t, widget, wptr]()
|
||||
// {
|
||||
// if (wptr)
|
||||
// qWarning() << widget << "parent" << wptr->parent();
|
||||
// else
|
||||
// {
|
||||
// qWarning() << (quintptr)widget << "ded!!!!!!";
|
||||
// t->stop();
|
||||
// t->deleteLater();
|
||||
// }
|
||||
// });
|
||||
// t->start();
|
||||
|
||||
|
||||
|
||||
// the problem: if switched from main to secondary, there is no widget for setCornerIcon to take
|
||||
|
||||
|
||||
|
||||
// bool setCornerIconLater = m_corner->hasIcon();//m_childContainer->isEmpty();
|
||||
|
||||
// if (!setCornerIconLater)
|
||||
// {
|
||||
// qWarning() << "setCornerIcon FIRST";
|
||||
// setCornerIcon(widget); // get ACTIVE widget before toggeling, as we need to show the app that we get AFTER clicking the corner
|
||||
// }
|
||||
|
||||
m_childContainer->setWidget(widget);
|
||||
|
||||
// if (setCornerIconLater)
|
||||
// {
|
||||
// qWarning() << "setCornerIcon LAST";
|
||||
setCornerIcons(); // get ACTIVE widget AFTER toggeling, as there was no widget before
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// setCornerIcon(); // get ACTIVE widget before toggeling, as we need to show the app that we get AFTER clicking the corner
|
||||
// m_childContainer->setWidget(widget);
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenNormal::slotOverlayDestroyed()
|
||||
{
|
||||
m_screenDialog->show();
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenNormal::isEmpty() const
|
||||
{
|
||||
return m_childContainer->isEmpty();
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenNormal::switchToTemp(LtsScreenBase::WidgetType widgetType)
|
||||
{
|
||||
m_childContainer->switchToTemp(widgetType);
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenNormal::finalizeSwitch()
|
||||
{
|
||||
m_childContainer->finalizeSwitch();
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenNormal::revertSwitch()
|
||||
{
|
||||
m_childContainer->revertSwitch();
|
||||
}
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "lts_screen_base.h"
|
||||
|
||||
#include <LtsMultiMonitor/lts_multi_monitor_export.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QRect>
|
||||
|
||||
|
||||
class QSplitter;
|
||||
class QHBoxLayout;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
class LtsMagicCorner;
|
||||
|
||||
|
||||
class LTSMULTIMONITOR_EXPORT LtsScreenNormal : public LtsScreenBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LtsScreenNormal(QRect screenGeo, int screenId, bool debugMargins, const QString &serial);
|
||||
~LtsScreenNormal();
|
||||
int screenType() const override { return 0; }
|
||||
int containerIdOfWidget(QWidget *widget) const noexcept(false) override;
|
||||
LtsScreenContainer *child() const { return m_childContainer; }
|
||||
LtsScreenContainer *putWidget(QWidget *widget, int) noexcept(false) override;
|
||||
LtsScreenContainer *containerOfWidget(QWidget *widget) noexcept(false) override;
|
||||
void hideMagicCorner(bool hide);
|
||||
bool isEmpty() const override;
|
||||
bool isIrreplaceable() const override; // only works for BLWs
|
||||
void putWidgetUnmanaged(QWidget *widget);
|
||||
bool hasWidget(QWidget *widget) const override;
|
||||
void switchToTemp(WidgetType widgetType) override;
|
||||
void finalizeSwitch() override;
|
||||
void revertSwitch() override;
|
||||
void check() override;
|
||||
|
||||
private:
|
||||
LtsScreenContainer *m_childContainer { nullptr };
|
||||
LtsMagicCorner *m_corner;
|
||||
//LtsScreenBase::WidgetType m_widgetType { LtsScreenBase::WidgetType::None };
|
||||
|
||||
void swapLeftRight();
|
||||
bool tryPutDummyUnmanaged(QWidget *widget);
|
||||
|
||||
signals:
|
||||
void signalMakeAllSwole(); // =>
|
||||
void signalMakeSwole(); // <=
|
||||
|
||||
private slots:
|
||||
void slotOverlayDestroyed();
|
||||
void setCornerIcons();
|
||||
};
|
||||
} // namespace LtsMultiMonitor
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
#include "lts_screen_wide.h"
|
||||
#include "lts_dummy_widget.h"
|
||||
#include "lts_screen_container.h"
|
||||
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSplitter>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTimer>
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
|
||||
#endif
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
|
||||
LtsScreenWide::LtsScreenWide(const QRect &screenGeo, int screenId, bool debugMargins, const QString &serial)
|
||||
: LtsScreenBase(screenGeo, screenId, debugMargins, serial)
|
||||
, m_childContainerMain(new LtsScreenContainer(screenGeo))
|
||||
, m_childContainerLeft(new LtsScreenContainer(screenGeo))
|
||||
, m_childContainerRight(new LtsScreenContainer(screenGeo))
|
||||
{
|
||||
// force check if widget in container was deleted:
|
||||
connect(m_childContainerMain, &LtsScreenContainer::check,
|
||||
this, &LtsScreenWide::check);
|
||||
connect(m_childContainerLeft, &LtsScreenContainer::check,
|
||||
this, &LtsScreenWide::check);
|
||||
connect(m_childContainerRight, &LtsScreenContainer::check,
|
||||
this, &LtsScreenWide::check);
|
||||
|
||||
auto *theLayout = new QHBoxLayout;
|
||||
|
||||
theLayout->setContentsMargins(0, 0, 0, 0);
|
||||
theLayout->setMargin(0);
|
||||
theLayout->setSpacing(0);
|
||||
theLayout->setStretch(0, 9999); // ?????
|
||||
|
||||
m_splitter = new QSplitter;
|
||||
m_splitter->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_splitter->addWidget(m_childContainerLeft);
|
||||
m_splitter->addWidget(m_childContainerRight);
|
||||
|
||||
theLayout->addWidget(m_splitter);
|
||||
theLayout->addWidget(m_childContainerMain);
|
||||
|
||||
m_screenDialog->setLayout(theLayout);
|
||||
|
||||
m_splitter->hide();
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenWide::putWidgetUnmanagedMain(QWidget *widget)
|
||||
{
|
||||
//qWarning() << "wide: put main";
|
||||
m_childContainerMain->setWidget(widget);
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenWide::putWidgetUnmanagedRight(QWidget *widget)
|
||||
{
|
||||
//qWarning() << "wide: put right";
|
||||
m_childContainerRight->setWidget(widget);
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenWide::putWidgetUnmanagedLeft(QWidget *widget)
|
||||
{
|
||||
//qWarning() << "wide: put left";
|
||||
m_childContainerLeft->setWidget(widget);
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenWide::hasWidget(QWidget *widget) const
|
||||
{
|
||||
return (m_childContainerMain->hasWidget(widget) ||
|
||||
m_childContainerLeft->hasWidget(widget) ||
|
||||
m_childContainerRight->hasWidget(widget));
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenWide::isIrreplaceable() const
|
||||
{
|
||||
return (childLeft()->isIrreplaceable() && childRight()->isIrreplaceable() && childMain()->isIrreplaceable());
|
||||
}
|
||||
|
||||
|
||||
bool LtsScreenWide::isEmpty() const
|
||||
{
|
||||
return (m_childContainerLeft->isEmpty() && m_childContainerRight->isEmpty() && m_childContainerMain->isEmpty());
|
||||
}
|
||||
|
||||
|
||||
int LtsScreenWide::containerIdOfWidget(QWidget *widget) const noexcept(false)
|
||||
{
|
||||
if (m_childContainerMain->hasWidget(widget))
|
||||
return 0;
|
||||
if (m_childContainerLeft->hasWidget(widget))
|
||||
return 1;
|
||||
if (m_childContainerRight->hasWidget(widget))
|
||||
return 2;
|
||||
throw "widget not on this screen";
|
||||
}
|
||||
|
||||
|
||||
LtsScreenContainer *LtsScreenWide::containerOfWidget(QWidget *widget) noexcept(false)
|
||||
{
|
||||
if (m_childContainerMain->hasWidget(widget))
|
||||
return m_childContainerMain;
|
||||
if (m_childContainerLeft->hasWidget(widget))
|
||||
return m_childContainerLeft;
|
||||
if (m_childContainerRight->hasWidget(widget))
|
||||
return m_childContainerRight;
|
||||
throw "widget is not present on this container";
|
||||
}
|
||||
|
||||
|
||||
LtsScreenContainer *LtsScreenWide::putWidget(QWidget *widget, int screenPart) noexcept(false)
|
||||
{
|
||||
if (screenPart == -1)
|
||||
{
|
||||
if (!isEmpty())
|
||||
throw "cannot put widget as at least one container is occupied and screen part was not specified";
|
||||
|
||||
putWidgetUnmanagedMain(widget);
|
||||
return m_childContainerMain;
|
||||
}
|
||||
|
||||
switch (screenPart)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (!m_childContainerMain->isEmpty())
|
||||
throw "cannot put widget as main container is already occupied";
|
||||
|
||||
putWidgetUnmanagedMain(widget);
|
||||
return m_childContainerMain;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
if (!m_childContainerLeft->isEmpty())
|
||||
throw "cannot put widget as left container is already occupied";
|
||||
|
||||
/* if main is currently occupied but left / right is requested, then put
|
||||
* the widget from main to the other side respective (as main means left
|
||||
* & right is empty, there is no need to check left & right occupancy) */
|
||||
if (!m_childContainerMain->isEmpty())
|
||||
putWidgetUnmanagedRight(m_childContainerMain->stealWidget());
|
||||
|
||||
putWidgetUnmanagedLeft(widget);
|
||||
return m_childContainerLeft;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
if (!m_childContainerRight->isEmpty())
|
||||
throw "cannot put widget as right container is already occupied";
|
||||
|
||||
/* if main is currently occupied but left / right is requested, then put
|
||||
* the widget from main to the other side respective (as main means left
|
||||
* & right is empty, there is no need to check left & right occupancy) */
|
||||
if (!m_childContainerMain->isEmpty())
|
||||
putWidgetUnmanagedLeft(m_childContainerMain->stealWidget());
|
||||
|
||||
putWidgetUnmanagedRight(widget);
|
||||
return m_childContainerRight;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw "unknown screen part id";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LtsScreenWide::check()
|
||||
{
|
||||
/*qWarning() << "check @ " << (quintptr)this;
|
||||
|
||||
qWarning() << "check: occupied? "
|
||||
<< "- main:" << !childMain()->isEmpty()
|
||||
<< "- left:" << !childLeft()->isEmpty()
|
||||
<< "- right:" << !childRight()->isEmpty();*/
|
||||
|
||||
|
||||
// if main is occupied but left and / or right is occupied, too, then assert:
|
||||
Q_ASSERT( (!m_childContainerMain->isEmpty() && m_childContainerLeft->isEmpty() && m_childContainerRight->isEmpty()) || m_childContainerMain->isEmpty() );
|
||||
|
||||
|
||||
// if main is empty AND either left or right (but not both) are empty, widget must move from left/right to main!!!
|
||||
if (m_childContainerMain->isEmpty())
|
||||
{
|
||||
if (m_childContainerLeft->isEmpty() && !m_childContainerRight->isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
QWidget *widget = m_childContainerRight->stealWidget();
|
||||
m_childContainerMain->setWidget(widget);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//qWarning() << "oops#2";
|
||||
//Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
else if (!m_childContainerLeft->isEmpty() && m_childContainerRight->isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
QWidget *widget = m_childContainerLeft->stealWidget();
|
||||
m_childContainerMain->setWidget(widget);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//qWarning() << "oops#1";
|
||||
//Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now do the visibility checks
|
||||
if (!m_childContainerMain->isEmpty())
|
||||
{
|
||||
//qWarning() << "wide: check main";
|
||||
m_childContainerMain->show();
|
||||
m_splitter->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//qWarning() << "wide: check left & right";
|
||||
m_childContainerMain->hide();
|
||||
m_splitter->setVisible(!m_childContainerLeft->isEmpty() || !m_childContainerRight->isEmpty());
|
||||
m_childContainerLeft->setVisible(!m_childContainerLeft->isEmpty());
|
||||
m_childContainerRight->setVisible(!m_childContainerRight->isEmpty());
|
||||
}
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "lts_screen_base.h"
|
||||
|
||||
#include <LtsMultiMonitor/lts_multi_monitor_export.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QRect>
|
||||
|
||||
|
||||
class QSplitter;
|
||||
class QHBoxLayout;
|
||||
|
||||
|
||||
namespace LtsMultiMonitor
|
||||
{
|
||||
|
||||
class LTSMULTIMONITOR_EXPORT LtsScreenWide : public LtsScreenBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LtsScreenWide(const QRect &screenGeo, int screenId, bool debugMargins, const QString &serial);
|
||||
|
||||
int screenType() const override { return 1; }
|
||||
LtsScreenContainer *containerOfWidget(QWidget *widget) noexcept(false) override;
|
||||
bool isEmpty() const override;
|
||||
LtsScreenContainer *childLeft() const { return m_childContainerLeft; }
|
||||
LtsScreenContainer *childRight() const { return m_childContainerRight; }
|
||||
LtsScreenContainer *childMain() const { return m_childContainerMain; }
|
||||
void putWidgetUnmanagedMain(QWidget *widget);
|
||||
void putWidgetUnmanagedLeft(QWidget *widget);
|
||||
void putWidgetUnmanagedRight(QWidget *widget);
|
||||
bool isIrreplaceable() const override;
|
||||
bool hasWidget(QWidget *widget) const override;
|
||||
LtsScreenContainer *putWidget(QWidget *widget, int screenPart) noexcept(false) override; // NOTE: doesn't put if part occupied
|
||||
int containerIdOfWidget(QWidget *widget) const noexcept(false) override;
|
||||
|
||||
public slots:
|
||||
void check() override;
|
||||
|
||||
private:
|
||||
LtsScreenContainer *m_childContainerMain;
|
||||
LtsScreenContainer *m_childContainerLeft;
|
||||
LtsScreenContainer *m_childContainerRight;
|
||||
QSplitter *m_splitter;
|
||||
|
||||
void swapLeftRight();
|
||||
};
|
||||
|
||||
} // namespace LtsMultiMonitor
|
||||
Reference in new issue
Block a user