Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+49
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+59
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+51
View File
@@ -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