From d274b54277ae4cfc7b46978530e93498b20fcba7 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 14:48:18 +0100 Subject: [PATCH 01/43] TaskBar: forward declare --- plugin-taskbar/lxqttaskbar.cpp | 12 ++++++++++-- plugin-taskbar/lxqttaskbar.h | 18 +++++++++++------- plugin-taskbar/lxqttaskbarconfiguration.h | 4 ++-- plugin-taskbar/lxqttaskbarplugin.cpp | 10 +++++++++- plugin-taskbar/lxqttaskbarplugin.h | 10 ++++------ plugin-taskbar/lxqttaskbutton.cpp | 8 ++++---- plugin-taskbar/lxqttaskbutton.h | 2 +- plugin-taskbar/lxqttaskgroup.cpp | 2 ++ plugin-taskbar/lxqttaskgroup.h | 6 ++---- 9 files changed, 45 insertions(+), 27 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 5dfa289c9..c4fa01da2 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -28,6 +28,8 @@ * * END_COMMON_COPYRIGHT_HEADER */ +#include "lxqttaskbar.h" + #include #include #include @@ -39,11 +41,12 @@ #include #include +#include "ilxqtpanelplugin.h" +#include "pluginsettings.h" + #include #include -#include -#include "lxqttaskbar.h" #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" @@ -591,6 +594,11 @@ void LXQtTaskBar::realign() emit refreshIconGeometry(); } +ILXQtPanel *LXQtTaskBar::panel() const +{ + return mPlugin->panel(); +} + /************************************************ ************************************************/ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index 03e2bd0d2..d80d1ea13 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -32,30 +32,33 @@ #ifndef LXQTTASKBAR_H #define LXQTTASKBAR_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" - #include #include #include -#include + #include "../panel/ilxqtpanel.h" #include #include #include +class ILXQtPanel; +class ILXQtPanelPlugin; + class QSignalMapper; -class LXQtTaskButton; class LXQtTaskGroup; -class ElidedButtonStyle; class LeftAlignedTextStyle; namespace LXQt { class GridLayout; } +namespace GlobalKeyShortcut +{ +class Action; +} + class LXQtTaskBar : public QFrame { Q_OBJECT @@ -80,7 +83,8 @@ class LXQtTaskBar : public QFrame bool isIconByClass() const { return mIconByClass; } int wheelEventsAction() const { return mWheelEventsAction; } int wheelDeltaThreshold() const { return mWheelDeltaThreshold; } - inline ILXQtPanel * panel() const { return mPlugin->panel(); } + + ILXQtPanel * panel() const; inline ILXQtPanelPlugin * plugin() const { return mPlugin; } public slots: diff --git a/plugin-taskbar/lxqttaskbarconfiguration.h b/plugin-taskbar/lxqttaskbarconfiguration.h index e559508b7..930e9f441 100644 --- a/plugin-taskbar/lxqttaskbarconfiguration.h +++ b/plugin-taskbar/lxqttaskbarconfiguration.h @@ -29,8 +29,8 @@ #define LXQTTASKBARCONFIGURATION_H #include "../panel/lxqtpanelpluginconfigdialog.h" -#include "../panel/pluginsettings.h" -#include + +class PluginSettings; namespace Ui { class LXQtTaskbarConfiguration; diff --git a/plugin-taskbar/lxqttaskbarplugin.cpp b/plugin-taskbar/lxqttaskbarplugin.cpp index ae8af1403..759e6db46 100644 --- a/plugin-taskbar/lxqttaskbarplugin.cpp +++ b/plugin-taskbar/lxqttaskbarplugin.cpp @@ -28,6 +28,8 @@ #include "lxqttaskbarplugin.h" +#include "lxqttaskbar.h" + #include "lxqttaskbarconfiguration.h" LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupInfo): @@ -36,7 +38,6 @@ LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupI { mTaskBar = new LXQtTaskBar(this); - } @@ -45,11 +46,18 @@ LXQtTaskBarPlugin::~LXQtTaskBarPlugin() delete mTaskBar; } +QWidget *LXQtTaskBarPlugin::widget() { return mTaskBar; } + QDialog *LXQtTaskBarPlugin::configureDialog() { return new LXQtTaskbarConfiguration(settings()); } +void LXQtTaskBarPlugin::settingsChanged() +{ + mTaskBar->settingsChanged(); +} + void LXQtTaskBarPlugin::realign() { mTaskBar->realign(); diff --git a/plugin-taskbar/lxqttaskbarplugin.h b/plugin-taskbar/lxqttaskbarplugin.h index 9c3076990..9bc34cf8e 100644 --- a/plugin-taskbar/lxqttaskbarplugin.h +++ b/plugin-taskbar/lxqttaskbarplugin.h @@ -29,10 +29,8 @@ #ifndef LXQTTASKBARPLUGIN_H #define LXQTTASKBARPLUGIN_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" -#include "lxqttaskbar.h" -#include +#include "ilxqtpanelplugin.h" + class LXQtTaskBar; class LXQtTaskBarPlugin : public QObject, public ILXQtPanelPlugin @@ -45,10 +43,10 @@ class LXQtTaskBarPlugin : public QObject, public ILXQtPanelPlugin QString themeId() const { return QStringLiteral("TaskBar"); } virtual Flags flags() const { return HaveConfigDialog | NeedsHandle; } - QWidget *widget() { return mTaskBar; } + QWidget *widget(); QDialog *configureDialog(); - void settingsChanged() { mTaskBar->settingsChanged(); } + void settingsChanged(); void realign(); bool isSeparate() const { return true; } diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index bd6df460f..2f1fe3116 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -31,6 +31,8 @@ #include "lxqttaskgroup.h" #include "lxqttaskbar.h" +#include "ilxqtpanelplugin.h" + #include #include @@ -49,11 +51,8 @@ #include #include -#include "lxqttaskbutton.h" -#include "lxqttaskgroup.h" -#include "lxqttaskbar.h" - #include + // Necessary for closeApplication() #include @@ -61,6 +60,7 @@ #include #undef Bool + bool LXQtTaskButton::sDraggging = false; /************************************************ diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 69f3b41d8..73d0886d4 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -33,12 +33,12 @@ #include #include + #include "../panel/ilxqtpanel.h" class QPainter; class QPalette; class QMimeData; -class LXQtTaskGroup; class LXQtTaskBar; class LeftAlignedTextStyle : public QProxyStyle diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index e192baabf..a8aaa3f2a 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -32,6 +32,8 @@ #include "lxqttaskbar.h" #include "lxqtgrouppopup.h" +#include "ilxqtpanelplugin.h" + #include #include #include diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index f1e7e2469..3787f411f 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -31,11 +31,9 @@ #ifndef LXQTTASKGROUP_H #define LXQTTASKGROUP_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" - #include "lxqttaskbutton.h" -#include + +#include class QVBoxLayout; class ILXQtPanelPlugin; From 43ddc7cf20b1541258f25ba3a75b71cb7f80f2e0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:37:31 +0100 Subject: [PATCH 02/43] TaskBar: new ILXQtTaskbarAbstractBackend This is an abstract interface to operate windows and workspaces --- plugin-taskbar/CMakeLists.txt | 5 + .../ilxqttaskbarabstractbackend.cpp | 25 +++++ plugin-taskbar/ilxqttaskbarabstractbackend.h | 94 +++++++++++++++++++ plugin-taskbar/lxqttaskbartypes.h | 32 +++++++ 4 files changed, 156 insertions(+) create mode 100644 plugin-taskbar/ilxqttaskbarabstractbackend.cpp create mode 100644 plugin-taskbar/ilxqttaskbarabstractbackend.h create mode 100644 plugin-taskbar/lxqttaskbartypes.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 1d627f23e..9ca6321cc 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -7,6 +7,9 @@ set(HEADERS lxqttaskbarplugin.h lxqttaskgroup.h lxqtgrouppopup.h + + ilxqttaskbarabstractbackend.h + lxqttaskbartypes.h ) set(SOURCES @@ -16,6 +19,8 @@ set(SOURCES lxqttaskbarplugin.cpp lxqttaskgroup.cpp lxqtgrouppopup.cpp + + ilxqttaskbarabstractbackend.cpp ) set(UIS diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp b/plugin-taskbar/ilxqttaskbarabstractbackend.cpp new file mode 100644 index 000000000..d0959fb78 --- /dev/null +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.cpp @@ -0,0 +1,25 @@ +#include "ilxqttaskbarabstractbackend.h" + + +ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) + : QObject(parent) +{ + +} + +void ILXQtTaskbarAbstractBackend::moveApplicationToPrevNextDesktop(WId windowId, bool next) +{ + int count = getWorkspacesCount(); + if (count <= 1) + return; + + int targetWorkspace = getWindowWorkspace(windowId) + (next ? 1 : -1); + + // Wrap around + if (targetWorkspace > count) + targetWorkspace = 1; //TODO: are X11 desktops 1 based? + else if (targetWorkspace < 1) + targetWorkspace = count; + + setWindowOnWorkspace(windowId, targetWorkspace); +} diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/plugin-taskbar/ilxqttaskbarabstractbackend.h new file mode 100644 index 000000000..f37a4081b --- /dev/null +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.h @@ -0,0 +1,94 @@ +#ifndef ILXQTTASKBARABSTRACTBACKEND_H +#define ILXQTTASKBARABSTRACTBACKEND_H + +#include + +#include "lxqttaskbartypes.h" + +class QIcon; +class QScreen; + +//FIXME: add something like bool KWindowInfo::actionSupported(...) + +class ILXQtTaskbarAbstractBackend : public QObject +{ + Q_OBJECT + +public: + enum class WindowProperty + { + Title = 0, + Icon, + State, + Urgency, + WindowClass, + Workspace + }; + + explicit ILXQtTaskbarAbstractBackend(QObject *parent = nullptr); + + // Windows + virtual bool reloadWindows() = 0; + + virtual QVector getCurrentWindows() const = 0; + + virtual QString getWindowTitle(WId windowId) const = 0; + + virtual bool applicationDemandsAttention(WId windowId) const = 0; + + virtual QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const = 0; + + virtual QString getWindowClass(WId windowId) const = 0; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const = 0; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) = 0; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const = 0; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) = 0; + + virtual bool isWindowActive(WId windowId) const = 0; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) = 0; + + virtual bool closeWindow(WId windowId) = 0; + + virtual WId getActiveWindow() const = 0; + + // Workspaces + virtual int getWorkspacesCount() const = 0; + virtual QString getWorkspaceName(int idx) const = 0; + + virtual int getCurrentWorkspace() const = 0; + virtual bool setCurrentWorkspace(int idx) = 0; + + virtual int getWindowWorkspace(WId windowId) const = 0; + virtual bool setWindowOnWorkspace(WId windowId, int idx) = 0; + + virtual void moveApplicationToPrevNextDesktop(WId windowId, bool next); // Default implementation + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) = 0; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const = 0; + + // X11 Specific + virtual void moveApplication(WId windowId) = 0; + virtual void resizeApplication(WId windowId) = 0; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) = 0; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; + +#endif // ILXQTTASKBARABSTRACTBACKEND_H diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h new file mode 100644 index 000000000..d841fec74 --- /dev/null +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -0,0 +1,32 @@ +#ifndef LXQTTASKBARTYPES_H +#define LXQTTASKBARTYPES_H + +#include + +typedef quintptr WId; + +enum class LXQtTaskBarWindowState +{ + Hidden = 0, + FullScreen, + Minimized, + Maximized, + MaximizedVertically, + MaximizedHorizontally, + Normal, + RolledUp //Shaded +}; + +enum class LXQtTaskBarWindowLayer +{ + KeepBelow = 0, + Normal, + KeepAbove +}; + +enum class LXQtTaskBarWorkspace +{ + ShowOnAll = -1 +}; + +#endif // LXQTTASKBARTYPES_H From 223e8fc82dc882df69e9e85f61a4ce2b31051dce Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 16:37:03 +0100 Subject: [PATCH 03/43] ILXQtTaskbarAbstractBackend: add supportsAction() method - Move WindowProperty enum to lxqttaskbartypes.h --- plugin-taskbar/ilxqttaskbarabstractbackend.h | 15 +++---------- plugin-taskbar/lxqttaskbartypes.h | 22 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/plugin-taskbar/ilxqttaskbarabstractbackend.h index f37a4081b..932d64fc0 100644 --- a/plugin-taskbar/ilxqttaskbarabstractbackend.h +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.h @@ -8,25 +8,16 @@ class QIcon; class QScreen; -//FIXME: add something like bool KWindowInfo::actionSupported(...) - class ILXQtTaskbarAbstractBackend : public QObject { Q_OBJECT public: - enum class WindowProperty - { - Title = 0, - Icon, - State, - Urgency, - WindowClass, - Workspace - }; - explicit ILXQtTaskbarAbstractBackend(QObject *parent = nullptr); + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const = 0; + // Windows virtual bool reloadWindows() = 0; diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h index d841fec74..ccea42027 100644 --- a/plugin-taskbar/lxqttaskbartypes.h +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -5,6 +5,28 @@ typedef quintptr WId; +enum class LXQtTaskBarBackendAction +{ + Move = 0, + Resize, + Maximize, + MaximizeVertically, + MaximizeHorizontally, + Minimize, + RollUp, + FullScreen +}; + +enum class LXQtTaskBarWindowProperty +{ + Title = 0, + Icon, + State, + Urgency, + WindowClass, + Workspace +}; + enum class LXQtTaskBarWindowState { Hidden = 0, From f1bf02ab6f474083b4ab81e908dd7a1099886aeb Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:39:44 +0100 Subject: [PATCH 04/43] TaskBar: add X11 backend --- plugin-taskbar/CMakeLists.txt | 2 + plugin-taskbar/lxqttaskbarbackend_x11.cpp | 582 ++++++++++++++++++++++ plugin-taskbar/lxqttaskbarbackend_x11.h | 80 +++ 3 files changed, 664 insertions(+) create mode 100644 plugin-taskbar/lxqttaskbarbackend_x11.cpp create mode 100644 plugin-taskbar/lxqttaskbarbackend_x11.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 9ca6321cc..053381bef 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS ilxqttaskbarabstractbackend.h lxqttaskbartypes.h + lxqttaskbarbackend_x11.h ) set(SOURCES @@ -21,6 +22,7 @@ set(SOURCES lxqtgrouppopup.cpp ilxqttaskbarabstractbackend.cpp + lxqttaskbarbackend_x11.cpp ) set(UIS diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/plugin-taskbar/lxqttaskbarbackend_x11.cpp new file mode 100644 index 000000000..cc7759f4e --- /dev/null +++ b/plugin-taskbar/lxqttaskbarbackend_x11.cpp @@ -0,0 +1,582 @@ +#include "lxqttaskbarbackend_x11.h" + +#include +#include +#include + +// Necessary for closeApplication() +#include + +#include +#include + +#include + +//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum +#include +#undef Bool + +LXQtTaskbarX11Backend::LXQtTaskbarX11Backend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + auto *x11Application = qGuiApp->nativeInterface(); + Q_ASSERT_X(x11Application, "LXQtTaskbarX11Backend", "Constructed without X11 connection"); + m_X11Display = x11Application->display(); + m_xcbConnection = x11Application->connection(); + + connect(KX11Extras::self(), &KX11Extras::windowChanged, this, &LXQtTaskbarX11Backend::onWindowChanged); + connect(KX11Extras::self(), &KX11Extras::windowAdded, this, &LXQtTaskbarX11Backend::onWindowAdded); + connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &LXQtTaskbarX11Backend::onWindowRemoved); + + connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged); + + connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &ILXQtTaskbarAbstractBackend::activeWindowChanged); +} + +/************************************************ + * Model slots + ************************************************/ +void LXQtTaskbarX11Backend::onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2) +{ + if(!m_windows.contains(windowId)) + return; + + if(!acceptWindow(windowId)) + { + onWindowRemoved(windowId); + return; + } + + if (prop2.testFlag(NET::WM2WindowClass)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::WindowClass)); + } + + // window changed virtual desktop + if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Workspace)); + } + + if (prop.testFlag(NET::WMVisibleName) || prop.testFlag(NET::WMName)) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Title)); + + // XXX: we are setting window icon geometry -> don't need to handle NET::WMIconGeometry + // Icon of the button can be based on windowClass + if (prop.testFlag(NET::WMIcon) || prop2.testFlag(NET::WM2WindowClass)) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Icon)); + + bool update_urgency = false; + if (prop2.testFlag(NET::WM2Urgency)) + { + update_urgency = true; + } + + if (prop.testFlag(NET::WMState)) + { + update_urgency = true; + + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::State)); + } + + if (update_urgency) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Urgency)); +} + +void LXQtTaskbarX11Backend::onWindowAdded(WId windowId) +{ + if(m_windows.contains(windowId)) + return; + + if (!acceptWindow(windowId)) + return; + + addWindow_internal(windowId); +} + +void LXQtTaskbarX11Backend::onWindowRemoved(WId windowId) +{ + const int row = m_windows.indexOf(windowId); + if(row == -1) + return; + + m_windows.removeAt(row); + + emit windowRemoved(windowId); +} + +/************************************************ + * Model private functions + ************************************************/ +bool LXQtTaskbarX11Backend::acceptWindow(WId windowId) const +{ + QFlags ignoreList; + ignoreList |= NET::DesktopMask; + ignoreList |= NET::DockMask; + ignoreList |= NET::SplashMask; + ignoreList |= NET::ToolbarMask; + ignoreList |= NET::MenuMask; + ignoreList |= NET::PopupMenuMask; + ignoreList |= NET::NotificationMask; + + KWindowInfo info(windowId, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); + if (!info.valid()) + return false; + + if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) + return false; + + if (info.state() & NET::SkipTaskbar) + return false; + + // WM_TRANSIENT_FOR hint not set - normal window + WId transFor = info.transientFor(); + + WId appRootWindow = XDefaultRootWindow(m_X11Display); + + if (transFor == 0 || transFor == windowId || transFor == appRootWindow) + return true; + + info = KWindowInfo(transFor, NET::WMWindowType); + + QFlags normalFlag; + normalFlag |= NET::NormalMask; + normalFlag |= NET::DialogMask; + normalFlag |= NET::UtilityMask; + + return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); +} + +void LXQtTaskbarX11Backend::addWindow_internal(WId windowId, bool emitAdded) +{ + m_windows.append(windowId); + if(emitAdded) + emit windowAdded(windowId); +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskbarX11Backend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +{ + NET::Action x11Action; + + switch (action) + { + case LXQtTaskBarBackendAction::Move: + x11Action = NET::ActionMove; + break; + + case LXQtTaskBarBackendAction::Resize: + x11Action = NET::ActionResize; + break; + + case LXQtTaskBarBackendAction::Maximize: + x11Action = NET::ActionMax; + break; + + case LXQtTaskBarBackendAction::MaximizeVertically: + x11Action = NET::ActionMaxVert; + break; + + case LXQtTaskBarBackendAction::MaximizeHorizontally: + x11Action = NET::ActionMaxHoriz; + break; + + case LXQtTaskBarBackendAction::Minimize: + x11Action = NET::ActionMinimize; + break; + + case LXQtTaskBarBackendAction::RollUp: + x11Action = NET::ActionShade; + break; + + case LXQtTaskBarBackendAction::FullScreen: + x11Action = NET::ActionFullScreen; + break; + + default: + return false; + } + + KWindowInfo info(windowId, NET::Properties(), NET::WM2AllowedActions); + return info.actionSupported(x11Action); +} + +bool LXQtTaskbarX11Backend::reloadWindows() +{ + QVector oldWindows; + qSwap(oldWindows, m_windows); + + // Just add new windows to groups, deleting is up to the groups + const auto x11windows = KX11Extras::stackingOrder(); + for (auto const windowId: x11windows) + { + if (acceptWindow(windowId)) + { + bool emitAdded = !oldWindows.contains(windowId); + addWindow_internal(windowId, emitAdded); + } + } + + //emulate windowRemoved if known window not reported by KWindowSystem + for (auto i = oldWindows.begin(), i_e = oldWindows.end(); i != i_e; i++) + { + WId windowId = *i; + if (!m_windows.contains(windowId)) + { + //TODO: more efficient method? + emit windowRemoved(windowId); + } + } + + //TODO: refreshPlaceholderVisibility() + emit reloaded(); + + return true; +} + +QVector LXQtTaskbarX11Backend::getCurrentWindows() const +{ + return m_windows; +} + +QString LXQtTaskbarX11Backend::getWindowTitle(WId windowId) const +{ + KWindowInfo info(windowId, NET::WMVisibleName | NET::WMName); + QString title = info.visibleName().isEmpty() ? info.name() : info.visibleName(); + return title; +} + +bool LXQtTaskbarX11Backend::applicationDemandsAttention(WId windowId) const +{ + WId appRootWindow = XDefaultRootWindow(m_X11Display); + return NETWinInfo(m_xcbConnection, windowId, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency() + || KWindowInfo{windowId, NET::WMState}.hasState(NET::DemandsAttention); +} + +QIcon LXQtTaskbarX11Backend::getApplicationIcon(WId windowId, int devicePixels) const +{ + return KX11Extras::icon(windowId, devicePixels, devicePixels); +} + +QString LXQtTaskbarX11Backend::getWindowClass(WId windowId) const +{ + KWindowInfo info(windowId, NET::Properties(), NET::WM2WindowClass); + return QString::fromUtf8(info.windowClassClass()); +} + +LXQtTaskBarWindowLayer LXQtTaskbarX11Backend::getWindowLayer(WId windowId) const +{ + NET::States state = KWindowInfo(windowId, NET::WMState).state(); + if(state.testFlag(NET::KeepAbove)) + return LXQtTaskBarWindowLayer::KeepAbove; + else if(state.testFlag(NET::KeepBelow)) + return LXQtTaskBarWindowLayer::KeepBelow; + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarX11Backend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +{ + switch(layer) + { + case LXQtTaskBarWindowLayer::KeepAbove: + KX11Extras::clearState(windowId, NET::KeepBelow); + KX11Extras::setState(windowId, NET::KeepAbove); + break; + + case LXQtTaskBarWindowLayer::KeepBelow: + KX11Extras::clearState(windowId, NET::KeepAbove); + KX11Extras::setState(windowId, NET::KeepBelow); + break; + + default: + KX11Extras::clearState(windowId, NET::KeepBelow); + KX11Extras::clearState(windowId, NET::KeepAbove); + break; + } + + return true; +} + +LXQtTaskBarWindowState LXQtTaskbarX11Backend::getWindowState(WId windowId) const +{ + KWindowInfo info(windowId,NET::WMState | NET::XAWMState); + if(info.isMinimized()) + return LXQtTaskBarWindowState::Minimized; + + NET::States state = info.state(); + if(state.testFlag(NET::Hidden)) + return LXQtTaskBarWindowState::Hidden; + if(state.testFlag(NET::Max)) + return LXQtTaskBarWindowState::Maximized; + if(state.testFlag(NET::MaxHoriz)) + return LXQtTaskBarWindowState::MaximizedHorizontally; + if(state.testFlag(NET::MaxVert)) + return LXQtTaskBarWindowState::MaximizedVertically; + if(state.testFlag(NET::Shaded)) + return LXQtTaskBarWindowState::RolledUp; + if(state.testFlag(NET::FullScreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarX11Backend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + // NOTE: window activation is left to the caller + + NET::State x11State; + + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + if(set) + KX11Extras::minimizeWindow(windowId); + else + KX11Extras::unminimizeWindow(windowId); + return true; + } + case LXQtTaskBarWindowState::Maximized: + { + x11State = NET::Max; + break; + } + case LXQtTaskBarWindowState::MaximizedVertically: + { + x11State = NET::MaxVert; + break; + } + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + x11State = NET::MaxHoriz; + break; + } + case LXQtTaskBarWindowState::Normal: + { + x11State = NET::Max; //TODO: correct? + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + x11State = NET::Shaded; + break; + } + default: + return false; + } + + if(set) + KX11Extras::setState(windowId, x11State); + else + KX11Extras::clearState(windowId, x11State); + + return true; +} + +bool LXQtTaskbarX11Backend::isWindowActive(WId windowId) const +{ + return KX11Extras::activeWindow() == windowId; +} + +bool LXQtTaskbarX11Backend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + if (onCurrentWorkSpace && getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + { + setWindowOnWorkspace(windowId, getCurrentWorkspace()); + } + else + { + setCurrentWorkspace(getWindowWorkspace(windowId)); + } + + // bypass focus stealing prevention + KX11Extras::forceActiveWindow(windowId); + + // Clear urgency flag + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Urgency)); + + return true; +} + +bool LXQtTaskbarX11Backend::closeWindow(WId windowId) +{ + // FIXME: Why there is no such thing in KWindowSystem?? + NETRootInfo(m_xcbConnection, NET::CloseWindow).closeWindowRequest(windowId); + return true; +} + +WId LXQtTaskbarX11Backend::getActiveWindow() const +{ + return KX11Extras::activeWindow(); +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskbarX11Backend::getWorkspacesCount() const +{ + return KX11Extras::numberOfDesktops(); +} + +QString LXQtTaskbarX11Backend::getWorkspaceName(int idx) const +{ + return KX11Extras::desktopName(idx); +} + +int LXQtTaskbarX11Backend::getCurrentWorkspace() const +{ + return KX11Extras::currentDesktop(); +} + +bool LXQtTaskbarX11Backend::setCurrentWorkspace(int idx) +{ + if(KX11Extras::currentDesktop() == idx) + return true; + + KX11Extras::setCurrentDesktop(idx); + return true; +} + +int LXQtTaskbarX11Backend::getWindowWorkspace(WId windowId) const +{ + KWindowInfo info(windowId, NET::WMDesktop); + return info.desktop(); +} + +bool LXQtTaskbarX11Backend::setWindowOnWorkspace(WId windowId, int idx) +{ + KX11Extras::setOnDesktop(windowId, idx); + return true; +} + +void LXQtTaskbarX11Backend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& windowGeometry = KWindowInfo(windowId, NET::WMFrameExtents).frameGeometry(); + QList screens = QGuiApplication::screens(); + if (screens.size() > 1) + { + for (int i = 0; i < screens.size(); ++i) + { + QRect screenGeometry = screens[i]->geometry(); + if (screenGeometry.intersects(windowGeometry)) + { + int targetScreen = i + (next ? 1 : -1); + if (targetScreen < 0) + targetScreen += screens.size(); + else if (targetScreen >= screens.size()) + targetScreen -= screens.size(); + + QRect targetScreenGeometry = screens[targetScreen]->geometry(); + int X = windowGeometry.x() - screenGeometry.x() + targetScreenGeometry.x(); + int Y = windowGeometry.y() - screenGeometry.y() + targetScreenGeometry.y(); + NET::States state = KWindowInfo(windowId, NET::WMState).state(); + + // NW geometry | y/x | from panel + const int flags = 1 | (0b011 << 8) | (0b010 << 12); + KX11Extras::clearState(windowId, NET::MaxHoriz | NET::MaxVert | NET::Max | NET::FullScreen); + NETRootInfo(m_xcbConnection, NET::Properties(), NET::WM2MoveResizeWindow).moveResizeWindowRequest(windowId, flags, X, Y, 0, 0); + QTimer::singleShot(200, this, [this, windowId, state, raiseOnCurrentDesktop] + { + KX11Extras::setState(windowId, state); + raiseWindow(windowId, raiseOnCurrentDesktop); + }); + break; + } + } + } +} + +bool LXQtTaskbarX11Backend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + //TODO: old code was: + //return QApplication::desktop()->screenGeometry(parentTaskBar()).intersects(KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry()); + + if(!screen) + return true; + + QRect r = KWindowInfo(windowId, NET::WMFrameExtents).frameGeometry(); + return screen->geometry().intersects(r); +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskbarX11Backend::moveApplication(WId windowId) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& g = KWindowInfo(windowId, NET::WMGeometry).geometry(); + int X = g.center().x(); + int Y = g.center().y(); + QCursor::setPos(X, Y); + NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::Move); +} + +void LXQtTaskbarX11Backend::resizeApplication(WId windowId) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& g = KWindowInfo(windowId, NET::WMGeometry).geometry(); + int X = g.bottomRight().x(); + int Y = g.bottomRight().y(); + QCursor::setPos(X, Y); + NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::BottomRight); +} + +void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom) +{ + // NOTE: This function announces where the task icon is, + // such that X11 WMs can perform their related animations correctly. + + WId appRootWindow = XDefaultRootWindow(m_X11Display); + + NETWinInfo info(m_xcbConnection, + windowId, + appRootWindow, + NET::WMIconGeometry, + NET::Properties2()); + NETRect const curr = info.iconGeometry(); + + // see kwindowsystem -> NETWinInfo::setIconGeometry for the scale factor + const qreal scaleFactor = qApp->devicePixelRatio(); + int xPos = geom.x() * scaleFactor; + int yPos = geom.y() * scaleFactor; + int w = geom.width() * scaleFactor; + int h = geom.height() * scaleFactor; + if (xPos == curr.pos.x && yPos == curr.pos.y && w == curr.size.width && h == curr.size.height) + return; + NETRect nrect; + nrect.pos.x = geom.x(); + nrect.pos.y = geom.y(); + nrect.size.height = geom.height(); + nrect.size.width = geom.width(); + info.setIconGeometry(nrect); +} diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.h b/plugin-taskbar/lxqttaskbarbackend_x11.h new file mode 100644 index 000000000..26c721cf7 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarbackend_x11.h @@ -0,0 +1,80 @@ +#ifndef LXQTTASKBARBACKEND_X11_H +#define LXQTTASKBARBACKEND_X11_H + +#include "ilxqttaskbarabstractbackend.h" + +//TODO: make PIMPL to forward declare NET::Properties, Display, xcb_connection_t +#include + +typedef struct _XDisplay Display; +struct xcb_connection_t; + +class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarX11Backend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + +private slots: + void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); + void onWindowAdded(WId windowId); + void onWindowRemoved(WId windowId); + +private: + bool acceptWindow(WId windowId) const; + void addWindow_internal(WId windowId, bool emitAdded = true); + +private: + Display *m_X11Display; + xcb_connection_t *m_xcbConnection; + + QVector m_windows; +}; + +#endif // LXQTTASKBARBACKEND_X11_H From d0fe4b40b2c370640836e739aec2dae977b466d3 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:41:42 +0100 Subject: [PATCH 05/43] TaskBar: new LXQtTaskBarProxyModel This model will manage the tasks shown --- plugin-taskbar/CMakeLists.txt | 2 + plugin-taskbar/lxqttaskbarproxymodel.cpp | 257 +++++++++++++++++++++++ plugin-taskbar/lxqttaskbarproxymodel.h | 100 +++++++++ 3 files changed, 359 insertions(+) create mode 100644 plugin-taskbar/lxqttaskbarproxymodel.cpp create mode 100644 plugin-taskbar/lxqttaskbarproxymodel.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 053381bef..be434f6a6 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -11,6 +11,7 @@ set(HEADERS ilxqttaskbarabstractbackend.h lxqttaskbartypes.h lxqttaskbarbackend_x11.h + lxqttaskbarproxymodel.h ) set(SOURCES @@ -23,6 +24,7 @@ set(SOURCES ilxqttaskbarabstractbackend.cpp lxqttaskbarbackend_x11.cpp + lxqttaskbarproxymodel.cpp ) set(UIS diff --git a/plugin-taskbar/lxqttaskbarproxymodel.cpp b/plugin-taskbar/lxqttaskbarproxymodel.cpp new file mode 100644 index 000000000..e2f1d3941 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarproxymodel.cpp @@ -0,0 +1,257 @@ +#include "lxqttaskbarproxymodel.h" + +#include "ilxqttaskbarabstractbackend.h" + +#include + +LXQtTaskBarProxyModel::LXQtTaskBarProxyModel(QObject *parent) + : QAbstractListModel(parent) + , m_backend(nullptr) + , m_groupByWindowClass(false) +{ + +} + +int LXQtTaskBarProxyModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_items.count(); +} + +QVariant LXQtTaskBarProxyModel::data(const QModelIndex &idx, int role) const +{ + if (!idx.isValid() || idx.row() >= m_items.count()) + return QVariant(); + + const LXQtTaskBarProxyModelItem& item = m_items.at(idx.row()); + + switch (role) + { + case Qt::DisplayRole: + return item.windows.count() == 1 ? item.windows.first().title : item.windowClass; + default: + break; + } + + return QVariant(); +} + +QIcon LXQtTaskBarProxyModel::getWindowIcon(int itemRow, int windowIdxInGroup, int devicePixels) const +{ + if(!m_backend || itemRow < 0 || itemRow >= m_items.size() || windowIdxInGroup < 0) + return QIcon(); + + const LXQtTaskBarProxyModelItem& item = m_items.at(itemRow); + if(windowIdxInGroup >= item.windows.size()) + return QIcon(); + + const LXQtTaskBarProxyModelWindow& window = item.windows.at(windowIdxInGroup); + return m_backend->getApplicationIcon(window.windowId, devicePixels); +} + +void LXQtTaskBarProxyModel::onWindowAdded(WId windowId) +{ + QString windowClass = m_backend->getWindowClass(windowId); + bool willAddRow = !m_groupByWindowClass || !hasWindowClass(windowClass); + + if(willAddRow) + { + const int row = m_items.count(); + beginInsertRows(QModelIndex(), row, row); + } + + addWindow_internal(windowId); + + if(willAddRow) + endInsertRows(); +} + +void LXQtTaskBarProxyModel::onWindowRemoved(WId windowId) +{ + int row = -1; + int windowIdxInGroup = -1; + for(int i = 0; i < m_items.count(); i++) + { + windowIdxInGroup = m_items.at(i).indexOfWindow(windowId); + if(windowIdxInGroup != -1) + { + row = i; + break; + } + } + + if(row == -1) + return; + + LXQtTaskBarProxyModelItem& item = m_items[row]; + item.windows.removeAt(windowIdxInGroup); + + if(item.windows.isEmpty()) + { + // Remove the group + beginRemoveRows(QModelIndex(), row, row); + m_items.removeAt(row); + endRemoveRows(); + } +} + +void LXQtTaskBarProxyModel::onWindowPropertyChanged(WId windowId, int prop) +{ + int row = -1; + int windowIdxInGroup = -1; + for(int i = 0; i < m_items.count(); i++) + { + windowIdxInGroup = m_items.at(i).indexOfWindow(windowId); + if(windowIdxInGroup != -1) + { + row = i; + break; + } + } + + if(row == -1) + return; + + LXQtTaskBarProxyModelItem& item = m_items[row]; + LXQtTaskBarProxyModelWindow& window = item.windows[windowIdxInGroup]; + + switch (LXQtTaskBarWindowProperty(prop)) + { + case LXQtTaskBarWindowProperty::Title: + window.title = m_backend->getWindowTitle(window.windowId); + break; + + case LXQtTaskBarWindowProperty::Urgency: + window.demandsAttention = m_backend->applicationDemandsAttention(window.windowId); + break; + + case LXQtTaskBarWindowProperty::WindowClass: + { + // If window class is changed, window won't be part of same group + //TODO: optimize + onWindowRemoved(windowId); + onWindowAdded(windowId); + } + + default: + break; + } + + const QModelIndex idx = index(row); + emit dataChanged(idx, idx, {Qt::DisplayRole, Qt::DecorationRole}); +} + +void LXQtTaskBarProxyModel::addWindow_internal(WId windowId) +{ + LXQtTaskBarProxyModelWindow window; + window.windowId = windowId; + window.title = m_backend->getWindowTitle(window.windowId); + window.demandsAttention = m_backend->applicationDemandsAttention(window.windowId); + + QString windowClass = m_backend->getWindowClass(window.windowId); + + int row = -1; + if(m_groupByWindowClass) + { + // Find existing group + for(int i = 0; i < m_items.count(); i++) + { + if(m_items.at(i).windowClass == windowClass) + { + row = i; + break; + } + } + } + + if(row == -1) + { + // Create new group + LXQtTaskBarProxyModelItem item; + item.windowClass = windowClass; + m_items.append(item); + row = m_items.size() - 1; + } + + // Add window to group + LXQtTaskBarProxyModelItem& item = m_items[row]; + item.windows.append(window); +} + +bool LXQtTaskBarProxyModel::groupByWindowClass() const +{ + return m_groupByWindowClass; +} + +void LXQtTaskBarProxyModel::setGroupByWindowClass(bool newGroupByWindowClass) +{ + if(m_groupByWindowClass == newGroupByWindowClass) + return; + + m_groupByWindowClass = newGroupByWindowClass; + + if(m_backend && !m_items.isEmpty()) + { + beginResetModel(); + + m_items.clear(); + + // Reload current windows + const QVector windows = m_backend->getCurrentWindows(); + m_items.reserve(windows.size()); + + for(WId windowId : windows) + onWindowAdded(windowId); + + m_items.squeeze(); + + endResetModel(); + } + +} + +ILXQtTaskbarAbstractBackend *LXQtTaskBarProxyModel::backend() const +{ + return m_backend; +} + +void LXQtTaskBarProxyModel::setBackend(ILXQtTaskbarAbstractBackend *newBackend) +{ + beginResetModel(); + + m_items.clear(); + + if(m_backend) + { + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, + this, &LXQtTaskBarProxyModel::onWindowAdded); + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, + this, &LXQtTaskBarProxyModel::onWindowRemoved); + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, &LXQtTaskBarProxyModel::onWindowPropertyChanged); + } + + m_backend = newBackend; + + if(m_backend) + { + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, + this, &LXQtTaskBarProxyModel::onWindowAdded); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, + this, &LXQtTaskBarProxyModel::onWindowRemoved); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, &LXQtTaskBarProxyModel::onWindowPropertyChanged); + + // Reload current windows + const QVector windows = m_backend->getCurrentWindows(); + m_items.reserve(windows.size()); + + for(WId windowId : windows) + onWindowAdded(windowId); + + m_items.squeeze(); + } + + m_items.squeeze(); + + endResetModel(); +} diff --git a/plugin-taskbar/lxqttaskbarproxymodel.h b/plugin-taskbar/lxqttaskbarproxymodel.h new file mode 100644 index 000000000..be5799076 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarproxymodel.h @@ -0,0 +1,100 @@ +#ifndef LXQTTASKBARPROXYMODEL_H +#define LXQTTASKBARPROXYMODEL_H + +#include +#include + +#include "lxqttaskbartypes.h" + +class ILXQtTaskbarAbstractBackend; + +class LXQtTaskBarProxyModelWindow +{ +public: + LXQtTaskBarProxyModelWindow() = default; + + WId windowId; + QString title; + bool demandsAttention = false; +}; + +// Single window or group +class LXQtTaskBarProxyModelItem +{ +public: + LXQtTaskBarProxyModelItem() = default; + + QVector windows; + QString windowClass; + + inline bool demandsAttention() const + { + for(const LXQtTaskBarProxyModelWindow& w : windows) + { + if(w.demandsAttention) + return true; + } + + return false; + } + + int indexOfWindow(WId windowId) const + { + for(int i = 0; i < windows.size(); i++) + { + if(windows.at(i).windowId == windowId) + return i; + } + + return -1; + } +}; + +class LXQtTaskBarProxyModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit LXQtTaskBarProxyModel(QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override; + + QIcon getWindowIcon(int itemRow, int windowIdxInGroup, int devicePixels) const; + + ILXQtTaskbarAbstractBackend *backend() const; + void setBackend(ILXQtTaskbarAbstractBackend *newBackend); + + bool groupByWindowClass() const; + void setGroupByWindowClass(bool newGroupByWindowClass); + +private slots: + void onWindowAdded(WId windowId); + void onWindowRemoved(WId windowId); + void onWindowPropertyChanged(WId windowId, int prop); + +private: + void addWindow_internal(WId windowId); + + inline bool hasWindowClass(const QString& windowClass) const + { + for(const LXQtTaskBarProxyModelItem& item : m_items) + { + if(item.windowClass == windowClass) + return true; + } + + return false; + } + +private: + ILXQtTaskbarAbstractBackend *m_backend; + + QVector m_items; + + bool m_groupByWindowClass; +}; + +#endif // LXQTTASKBARPROXYMODEL_H From 3a842e7f7e3bc8cc7e26d7c463cd360a66780e47 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 19:00:46 +0100 Subject: [PATCH 06/43] TaskBar: use backend in LXQtTaskBar --- plugin-taskbar/lxqttaskbar.cpp | 14 +++++++++++--- plugin-taskbar/lxqttaskbar.h | 6 ++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index c4fa01da2..fc60496a3 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -54,6 +54,8 @@ #include #undef Bool +#include "lxqttaskbarbackend_x11.h" + using namespace LXQt; /************************************************ @@ -80,7 +82,8 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mWheelDeltaThreshold(300), mPlugin(plugin), mPlaceHolder(new QWidget(this)), - mStyle(new LeftAlignedTextStyle()) + mStyle(new LeftAlignedTextStyle()), + mBackend(nullptr) { setStyle(mStyle); mLayout = new LXQt::GridLayout(this); @@ -94,6 +97,10 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mPlaceHolder->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); mLayout->addWidget(mPlaceHolder); + // Create model + //TODO: use backend factory + mBackend = new LXQtTaskbarX11Backend(this); + QTimer::singleShot(0, this, &LXQtTaskBar::settingsChanged); setAcceptDrops(true); @@ -282,7 +289,7 @@ void LXQtTaskBar::groupBecomeEmptySlot() void LXQtTaskBar::addWindow(WId window) { // If grouping disabled group behaves like regular button - const QString group_id = mGroupingEnabled ? QString::fromUtf8(KWindowInfo(window, NET::Properties(), NET::WM2WindowClass).windowClassClass()) : QString::number(window); + const QString group_id = mGroupingEnabled ? mBackend->getWindowClass(window) : QString::number(window); LXQtTaskGroup *group = nullptr; auto i_group = mKnownWindows.find(window); @@ -534,7 +541,8 @@ void LXQtTaskBar::settingsChanged() if (iconByClassOld != mIconByClass) emit iconByClassChanged(); - refreshTaskList(); + refreshTaskList(); //TODO: remove + mBackend->reloadWindows(); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index d80d1ea13..c36bca7e0 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -50,6 +50,8 @@ class LXQtTaskGroup; class LeftAlignedTextStyle; +class ILXQtTaskbarAbstractBackend; + namespace LXQt { class GridLayout; } @@ -87,6 +89,8 @@ class LXQtTaskBar : public QFrame ILXQtPanel * panel() const; inline ILXQtPanelPlugin * plugin() const { return mPlugin; } + inline ILXQtTaskbarAbstractBackend *getBackend() const { return mBackend; } + public slots: void settingsChanged(); @@ -156,6 +160,8 @@ private slots: ILXQtPanelPlugin *mPlugin; QWidget *mPlaceHolder; LeftAlignedTextStyle *mStyle; + + ILXQtTaskbarAbstractBackend *mBackend; }; #endif // LXQTTASKBAR_H From b81220db79dead9005dd482c2e46b95d9115158e Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:30:07 +0100 Subject: [PATCH 07/43] TaskBar: initial use of backend in LXQtTaskButton --- plugin-taskbar/lxqttaskbutton.cpp | 169 +++--------------------------- plugin-taskbar/lxqttaskbutton.h | 7 +- plugin-taskbar/lxqttaskgroup.cpp | 28 ++--- 3 files changed, 35 insertions(+), 169 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 2f1fe3116..3d2ad0de0 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -51,6 +51,7 @@ #include #include +//TODO: remove #include // Necessary for closeApplication() @@ -61,6 +62,8 @@ #undef Bool +#include "ilxqttaskbarabstractbackend.h" + bool LXQtTaskButton::sDraggging = false; /************************************************ @@ -84,6 +87,7 @@ void LeftAlignedTextStyle::drawItemText(QPainter * painter, const QRect & rect, ************************************************/ LXQtTaskButton::LXQtTaskButton(const WId window, LXQtTaskBar * taskbar, QWidget *parent) : QToolButton(parent), + mBackend(taskbar->getBackend()), mWindow(window), mUrgencyHint(false), mOrigin(Qt::TopLeftCorner), @@ -144,8 +148,7 @@ LXQtTaskButton::~LXQtTaskButton() = default; ************************************************/ void LXQtTaskButton::updateText() { - KWindowInfo info(mWindow, NET::WMVisibleName | NET::WMName); - QString title = info.visibleName().isEmpty() ? info.name() : info.visibleName(); + QString title = mBackend->getWindowTitle(mWindow); setText(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); setToolTip(title); } @@ -168,57 +171,6 @@ void LXQtTaskButton::updateIcon() setIcon(ico.isNull() ? XdgIcon::defaultApplicationIcon() : ico); } -/************************************************ - - ************************************************/ -void LXQtTaskButton::refreshIconGeometry(QRect const & geom) -{ - // NOTE: This function announces where the task icon is, - // such that X11 WMs can perform their related animations correctly. - - WId appRootWindow = 0; - xcb_connection_t* x11conn = nullptr; - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - appRootWindow = XDefaultRootWindow(x11Application->display()); - x11conn = x11Application->connection(); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - return; - } - - - if (!x11conn) { - return; - } - - NETWinInfo info(x11conn, - windowId(), - appRootWindow, - NET::WMIconGeometry, - NET::Properties2()); - NETRect const curr = info.iconGeometry(); - - // see kwindowsystem -> NETWinInfo::setIconGeometry for the scale factor - const qreal scaleFactor = qApp->devicePixelRatio(); - int xPos = geom.x() * scaleFactor; - int yPos = geom.y() * scaleFactor; - int w = geom.width() * scaleFactor; - int h = geom.height() * scaleFactor; - if (xPos == curr.pos.x && yPos == curr.pos.y && w == curr.size.width && h == curr.size.height) - return; - NETRect nrect; - nrect.pos.x = geom.x(); - nrect.pos.y = geom.y(); - nrect.size.height = geom.height(); - nrect.size.width = geom.width(); - info.setIconGeometry(nrect); -} - /************************************************ ************************************************/ @@ -434,8 +386,7 @@ void LXQtTaskButton::mouseMoveEvent(QMouseEvent* event) ************************************************/ bool LXQtTaskButton::isApplicationHidden() const { - KWindowInfo info(mWindow, NET::WMState); - return (info.state() & NET::Hidden); + return false; //FIXME: unused } /************************************************ @@ -443,7 +394,7 @@ bool LXQtTaskButton::isApplicationHidden() const ************************************************/ bool LXQtTaskButton::isApplicationActive() const { - return KX11Extras::activeWindow() == mWindow; + return mBackend->isWindowActive(mWindow); } /************************************************ @@ -588,12 +539,12 @@ void LXQtTaskButton::moveApplicationToDesktop() return; bool ok; - int desk = act->data().toInt(&ok); + int idx = act->data().toInt(&ok); if (!ok) return; - KX11Extras::setOnDesktop(mWindow, desk); + mBackend->setWindowOnWorkspace(mWindow, idx); } /************************************************ @@ -601,17 +552,7 @@ void LXQtTaskButton::moveApplicationToDesktop() ************************************************/ void LXQtTaskButton::moveApplicationToPrevNextDesktop(bool next) { - int deskNum = KX11Extras::numberOfDesktops(); - if (deskNum <= 1) - return; - int targetDesk = KWindowInfo(mWindow, NET::WMDesktop).desktop() + (next ? 1 : -1); - // wrap around - if (targetDesk > deskNum) - targetDesk = 1; - else if (targetDesk < 1) - targetDesk = deskNum; - - KX11Extras::setOnDesktop(mWindow, targetDesk); + mBackend->moveApplicationToPrevNextDesktop(mWindow, next); } /************************************************ @@ -619,53 +560,7 @@ void LXQtTaskButton::moveApplicationToPrevNextDesktop(bool next) ************************************************/ void LXQtTaskButton::moveApplicationToPrevNextMonitor(bool next) { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& windowGeometry = KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry(); - QList screens = QGuiApplication::screens(); - if (screens.size() > 1){ - for (int i = 0; i < screens.size(); ++i) - { - QRect screenGeometry = screens[i]->geometry(); - if (screenGeometry.intersects(windowGeometry)) - { - int targetScreen = i + (next ? 1 : -1); - if (targetScreen < 0) - targetScreen += screens.size(); - else if (targetScreen >= screens.size()) - targetScreen -= screens.size(); - QRect targetScreenGeometry = screens[targetScreen]->geometry(); - int X = windowGeometry.x() - screenGeometry.x() + targetScreenGeometry.x(); - int Y = windowGeometry.y() - screenGeometry.y() + targetScreenGeometry.y(); - NET::States state = KWindowInfo(mWindow, NET::WMState).state(); - // NW geometry | y/x | from panel - const int flags = 1 | (0b011 << 8) | (0b010 << 12); - KX11Extras::clearState(mWindow, NET::MaxHoriz | NET::MaxVert | NET::Max | NET::FullScreen); - - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::Properties(), NET::WM2MoveResizeWindow).moveResizeWindowRequest(mWindow, flags, X, Y, 0, 0); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } - - QTimer::singleShot(200, this, [this, state] - { - KX11Extras::setState(mWindow, state); - raiseApplication(); - }); - break; - } - } - } + mBackend->moveApplicationToPrevNextMonitor(mWindow, next, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -673,26 +568,7 @@ void LXQtTaskButton::moveApplicationToPrevNextMonitor(bool next) ************************************************/ void LXQtTaskButton::moveApplication() { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& g = KWindowInfo(mWindow, NET::WMGeometry).geometry(); - int X = g.center().x(); - int Y = g.center().y(); - QCursor::setPos(X, Y); - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::WMMoveResize).moveResizeRequest(mWindow, X, Y, NET::Move); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->moveApplication(mWindow); } /************************************************ @@ -700,26 +576,7 @@ void LXQtTaskButton::moveApplication() ************************************************/ void LXQtTaskButton::resizeApplication() { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& g = KWindowInfo(mWindow, NET::WMGeometry).geometry(); - int X = g.bottomRight().x(); - int Y = g.bottomRight().y(); - QCursor::setPos(X, Y); - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::WMMoveResize).moveResizeRequest(mWindow, X, Y, NET::BottomRight); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->resizeApplication(mWindow); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 73d0886d4..9ccca36fa 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -41,6 +41,8 @@ class QPalette; class QMimeData; class LXQtTaskBar; +class ILXQtTaskbarAbstractBackend; + class LeftAlignedTextStyle : public QProxyStyle { using QProxyStyle::QProxyStyle; @@ -79,7 +81,6 @@ class LXQtTaskButton : public QToolButton LXQtTaskBar * parentTaskBar() const {return mParentTaskBar;} - void refreshIconGeometry(QRect const & geom); static QString mimeDataFormat() { return QLatin1String("lxqt/lxqttaskbutton"); } /*! \return true if this button received DragEnter event (and no DragLeave event yet) * */ @@ -121,6 +122,10 @@ public slots: inline ILXQtPanelPlugin * plugin() const { return mPlugin; } +protected: + //TODO: public getter instead? + ILXQtTaskbarAbstractBackend *mBackend; + private: void moveApplicationToPrevNextDesktop(bool next); void moveApplicationToPrevNextMonitor(bool next); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index a8aaa3f2a..0f3a19df8 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -43,9 +43,13 @@ #include #include +//TODO: remove #include //For nativeInterface() #include #include +#undef Bool + +#include "ilxqttaskbarabstractbackend.h" /************************************************ @@ -62,14 +66,14 @@ LXQtTaskGroup::LXQtTaskGroup(const QString &groupName, WId window, LXQtTaskBar * setObjectName(groupName); setText(groupName); - connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &LXQtTaskGroup::onDesktopChanged); - connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &LXQtTaskGroup::onActiveWindowChanged); - connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); - connect(parent, &LXQtTaskBar::refreshIconGeometry, this, &LXQtTaskGroup::refreshIconsGeometry); - connect(parent, &LXQtTaskBar::buttonStyleRefreshed, this, &LXQtTaskGroup::setToolButtonsStyle); - connect(parent, &LXQtTaskBar::showOnlySettingChanged, this, &LXQtTaskGroup::refreshVisibility); - connect(parent, &LXQtTaskBar::popupShown, this, &LXQtTaskGroup::groupPopupShown); + connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); + connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); + connect(parent, &LXQtTaskBar::refreshIconGeometry, this, &LXQtTaskGroup::refreshIconsGeometry); + connect(parent, &LXQtTaskBar::buttonStyleRefreshed, this, &LXQtTaskGroup::setToolButtonsStyle); + connect(parent, &LXQtTaskBar::showOnlySettingChanged, this, &LXQtTaskGroup::refreshVisibility); + connect(parent, &LXQtTaskBar::popupShown, this, &LXQtTaskGroup::groupPopupShown); + connect(mBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &LXQtTaskGroup::onDesktopChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::activeWindowChanged, this, &LXQtTaskGroup::onActiveWindowChanged); } /************************************************ @@ -102,7 +106,7 @@ void LXQtTaskGroup::contextMenuEvent(QContextMenuEvent *event) void LXQtTaskGroup::closeGroup() { for (LXQtTaskButton *button : std::as_const(mButtonHash) ) - if (button->isOnDesktop(KX11Extras::currentDesktop())) + if (button->isOnDesktop(mBackend->getCurrentWorkspace())) button->closeApplication(); } @@ -310,7 +314,7 @@ void LXQtTaskGroup::onClicked(bool) { if (visibleButtonsCount() > 1) { - setChecked(mButtonHash.contains(KX11Extras::activeWindow())); + setChecked(mButtonHash.contains(mBackend->getActiveWindow())); setPopupVisible(true); } } @@ -449,13 +453,13 @@ void LXQtTaskGroup::refreshIconsGeometry() if (mSingleButton) { - refreshIconGeometry(rect); + mBackend->refreshIconGeometry(windowId(), rect); return; } for(LXQtTaskButton *but : std::as_const(mButtonHash)) { - but->refreshIconGeometry(rect); + mBackend->refreshIconGeometry(but->windowId(), rect); but->setIconSize(QSize(plugin()->panel()->iconSize(), plugin()->panel()->iconSize())); } } From 9cd7198bae8a9abf4844d0bef8c788a5279f85ad Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:34:11 +0100 Subject: [PATCH 08/43] TaskBar: use backend for urgency hint --- plugin-taskbar/lxqttaskbutton.cpp | 14 +------------- plugin-taskbar/lxqttaskgroup.cpp | 23 +++++------------------ 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 3d2ad0de0..7febd5638 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -28,7 +28,6 @@ * END_COMMON_COPYRIGHT_HEADER */ #include "lxqttaskbutton.h" -#include "lxqttaskgroup.h" #include "lxqttaskbar.h" #include "ilxqtpanelplugin.h" @@ -121,18 +120,7 @@ LXQtTaskButton::LXQtTaskButton(const WId window, LXQtTaskBar * taskbar, QWidget mWheelDelta = 0; // forget previous wheel deltas }); - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - setUrgencyHint(NETWinInfo(x11Application->connection(), mWindow, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency() - || KWindowInfo{mWindow, NET::WMState}.hasState(NET::DemandsAttention)); - } - else - { - qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } - + setUrgencyHint(mBackend->applicationDemandsAttention(mWindow)); connect(LXQt::Settings::globalSettings(), &LXQt::GlobalSettings::iconThemeChanged, this, &LXQtTaskButton::updateIcon); connect(mParentTaskBar, &LXQtTaskBar::iconByClassChanged, this, &LXQtTaskButton::updateIcon); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 0f3a19df8..e4991c8a4 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -45,7 +45,6 @@ //TODO: remove #include //For nativeInterface() -#include #include #undef Bool @@ -670,30 +669,18 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (prop2.testFlag(NET::WM2Urgency)) { set_urgency = true; - if (auto *x11Application = qGuiApp->nativeInterface()) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - urgency = NETWinInfo(x11Application->connection(), window, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency(); - } + //FIXME: original code here did not consider "demand attention", was it intentional? + urgency = mBackend->applicationDemandsAttention(window); } if (prop.testFlag(NET::WMState)) { KWindowInfo info{window, NET::WMState}; - if(!set_urgency) - { - if (auto *x11Application = qGuiApp->nativeInterface()) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - urgency = NETWinInfo(x11Application->connection(), window, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency(); - } - } - // Force refresh urgency - //TODO: maybe do it in common place with NET::WM2Urgency - std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency || info.hasState(NET::DemandsAttention))); + if (!set_urgency) + urgency = mBackend->applicationDemandsAttention(window); + std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency)); set_urgency = false; - if (info.hasState(NET::SkipTaskbar)) onWindowRemoved(window); From 354e027f21eb37d876dfbebebb8eb7e0685cb7d4 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:36:29 +0100 Subject: [PATCH 09/43] TaskBar: use backend to set window layer --- plugin-taskbar/lxqttaskbutton.cpp | 37 +++++++++---------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 7febd5638..d324009ee 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -498,23 +498,7 @@ void LXQtTaskButton::setApplicationLayer() return; int layer = act->data().toInt(); - switch(layer) - { - case NET::KeepAbove: - KX11Extras::clearState(mWindow, NET::KeepBelow); - KX11Extras::setState(mWindow, NET::KeepAbove); - break; - - case NET::KeepBelow: - KX11Extras::clearState(mWindow, NET::KeepAbove); - KX11Extras::setState(mWindow, NET::KeepBelow); - break; - - default: - KX11Extras::clearState(mWindow, NET::KeepBelow); - KX11Extras::clearState(mWindow, NET::KeepAbove); - break; - } + mBackend->setWindowLayer(mWindow, LXQtTaskBarWindowLayer(layer)); } /************************************************ @@ -707,22 +691,23 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) QMenu* layerMenu = menu->addMenu(tr("&Layer")); - a = layerMenu->addAction(tr("Always on &top")); + LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); + // FIXME: There is no info.actionSupported(NET::ActionKeepAbove) - a->setEnabled(!(state & NET::KeepAbove)); - a->setData(NET::KeepAbove); + a = layerMenu->addAction(tr("Always on &top")); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepAbove); + a->setData(int(LXQtTaskBarWindowLayer::KeepAbove)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); a = layerMenu->addAction(tr("&Normal")); - a->setEnabled((state & NET::KeepAbove) || (state & NET::KeepBelow)); - // FIXME: There is no NET::KeepNormal, so passing 0 - a->setData(0); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::Normal); + a->setData(int(LXQtTaskBarWindowLayer::Normal)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); - a = layerMenu->addAction(tr("Always on &bottom")); // FIXME: There is no info.actionSupported(NET::ActionKeepBelow) - a->setEnabled(!(state & NET::KeepBelow)); - a->setData(NET::KeepBelow); + a = layerMenu->addAction(tr("Always on &bottom")); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepBelow); + a->setData(int(LXQtTaskBarWindowLayer::KeepBelow)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); /********** Kill menu **********/ From 290178eed8905b7eb1b9c0995f4dcf25c28a0507 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:53:02 +0100 Subject: [PATCH 10/43] TaskBar: use backend to close and raise window Also use it to get window icon --- plugin-taskbar/lxqttaskbutton.cpp | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index d324009ee..e59219d4c 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -154,7 +154,7 @@ void LXQtTaskButton::updateIcon() if (ico.isNull()) { int devicePixels = mIconSize * devicePixelRatioF(); - ico = KX11Extras::icon(mWindow, devicePixels, devicePixels); + ico = mBackend->getApplicationIcon(mWindow, devicePixels); } setIcon(ico.isNull() ? XdgIcon::defaultApplicationIcon() : ico); } @@ -390,21 +390,7 @@ bool LXQtTaskButton::isApplicationActive() const ************************************************/ void LXQtTaskButton::raiseApplication() { - KWindowInfo info(mWindow, NET::WMDesktop | NET::WMState | NET::XAWMState); - if (parentTaskBar()->raiseOnCurrentDesktop() && info.isMinimized()) - { - KX11Extras::setOnDesktop(mWindow, KX11Extras::currentDesktop()); - } - else - { - int winDesktop = info.desktop(); - if (KX11Extras::currentDesktop() != winDesktop) - KX11Extras::setCurrentDesktop(winDesktop); - } - // bypass focus stealing prevention - KX11Extras::forceActiveWindow(mWindow); - - setUrgencyHint(false); + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -476,16 +462,7 @@ void LXQtTaskButton::unShadeApplication() ************************************************/ void LXQtTaskButton::closeApplication() { - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - // FIXME: Why there is no such thing in KX11Extras?? - NETRootInfo(x11Application->connection(), NET::CloseWindow).closeWindowRequest(mWindow); - } - else - { - qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->closeWindow(mWindow); } /************************************************ From 78b5f7e0c65e3c3c27b99f948e9c9ed05f8d7f15 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:00:58 +0100 Subject: [PATCH 11/43] TaskBar: use backend to roll up (shade) windows --- plugin-taskbar/lxqttaskbutton.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index e59219d4c..3a63999ba 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -446,7 +446,7 @@ void LXQtTaskButton::deMaximizeApplication() ************************************************/ void LXQtTaskButton::shadeApplication() { - KX11Extras::setState(mWindow, NET::Shaded); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::RolledUp, true); } /************************************************ @@ -454,7 +454,7 @@ void LXQtTaskButton::shadeApplication() ************************************************/ void LXQtTaskButton::unShadeApplication() { - KX11Extras::clearState(mWindow, NET::Shaded); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::RolledUp, false); } /************************************************ From 74bcae45dba4fed2c419a4bb71b766815e2bed8d Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:09:37 +0100 Subject: [PATCH 12/43] TaskBar: use backend to minimize window --- plugin-taskbar/lxqttaskbutton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 3a63999ba..a77729bc4 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -398,7 +398,7 @@ void LXQtTaskButton::raiseApplication() ************************************************/ void LXQtTaskButton::minimizeApplication() { - KX11Extras::minimizeWindow(mWindow); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Minimized, true); } /************************************************ From a14a0fe553f329ff96e4951901a6f76a894d4811 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:10:08 +0100 Subject: [PATCH 13/43] TaskBar: use backend to de-maximize window --- plugin-taskbar/lxqttaskbutton.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index a77729bc4..27d430926 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -435,10 +435,10 @@ void LXQtTaskButton::maximizeApplication() ************************************************/ void LXQtTaskButton::deMaximizeApplication() { - KX11Extras::clearState(mWindow, NET::Max); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Maximized, false); - if (!isApplicationActive()) - raiseApplication(); + if(!mBackend->isWindowActive(mWindow)) + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ From d0d674e4b83a32ca1f5f39d92b574078728782c1 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:58:59 +0100 Subject: [PATCH 14/43] TaskBar: use backend to get window state --- plugin-taskbar/lxqttaskbutton.cpp | 35 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 27d430926..6f9c1d3d2 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -540,7 +540,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) } KWindowInfo info(mWindow, NET::Properties(), NET::WM2AllowedActions); - unsigned long state = KWindowInfo(mWindow, NET::WMState).state(); + NET::States state = KWindowInfo(mWindow, NET::WMState).state(); QMenu * menu = new QMenu(tr("Application")); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -624,42 +624,59 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) /********** State menu **********/ menu->addSeparator(); + LXQtTaskBarWindowState windowState = mBackend->getWindowState(mWindow); + a = menu->addAction(tr("Ma&ximize")); - a->setEnabled(info.actionSupported(NET::ActionMax) && (!(state & NET::Max) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMax) + && windowState != LXQtTaskBarWindowState::Maximized + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::Max); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) { a = menu->addAction(tr("Maximize vertically")); - a->setEnabled(info.actionSupported(NET::ActionMaxVert) && !((state & NET::MaxVert) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMaxVert) + && windowState != LXQtTaskBarWindowState::MaximizedVertically + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::MaxVert); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); - a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) && !((state & NET::MaxHoriz) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) + && windowState != LXQtTaskBarWindowState::MaximizedHorizontally + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::MaxHoriz); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } a = menu->addAction(tr("&Restore")); - a->setEnabled((state & NET::Hidden) || (state & NET::Max) || (state & NET::MaxHoriz) || (state & NET::MaxVert)); + a->setEnabled(windowState == LXQtTaskBarWindowState::Hidden + || windowState == LXQtTaskBarWindowState::Minimized + || windowState == LXQtTaskBarWindowState::Maximized + || windowState == LXQtTaskBarWindowState::MaximizedVertically + || windowState == LXQtTaskBarWindowState::MaximizedHorizontally); connect(a, &QAction::triggered, this, &LXQtTaskButton::deMaximizeApplication); a = menu->addAction(tr("Mi&nimize")); - a->setEnabled(info.actionSupported(NET::ActionMinimize) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionMinimize) + && windowState != LXQtTaskBarWindowState::Hidden + && windowState != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::minimizeApplication); - if (state & NET::Shaded) + if (windowState == LXQtTaskBarWindowState::RolledUp) { a = menu->addAction(tr("Roll down")); - a->setEnabled(info.actionSupported(NET::ActionShade) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionShade) + && windowState != LXQtTaskBarWindowState::Hidden + && windowState != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::unShadeApplication); } else { a = menu->addAction(tr("Roll up")); - a->setEnabled(info.actionSupported(NET::ActionShade) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionShade) + && windowState != LXQtTaskBarWindowState::Hidden); connect(a, &QAction::triggered, this, &LXQtTaskButton::shadeApplication); } From b4af0dfd683219b328942431e7574fe196136a4c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 18:26:53 +0100 Subject: [PATCH 15/43] TaskBar: use backend to set window state --- plugin-taskbar/lxqttaskbutton.cpp | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 6f9c1d3d2..b63b2aa3f 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -411,23 +411,10 @@ void LXQtTaskButton::maximizeApplication() return; int state = act->data().toInt(); - switch (state) - { - case NET::MaxHoriz: - KX11Extras::setState(mWindow, NET::MaxHoriz); - break; + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState(state), true); - case NET::MaxVert: - KX11Extras::setState(mWindow, NET::MaxVert); - break; - - default: - KX11Extras::setState(mWindow, NET::Max); - break; - } - - if (!isApplicationActive()) - raiseApplication(); + if(!mBackend->isWindowActive(mWindow)) + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -630,7 +617,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setEnabled(info.actionSupported(NET::ActionMax) && windowState != LXQtTaskBarWindowState::Maximized && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::Max); + a->setData(int(LXQtTaskBarWindowState::Maximized)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) @@ -639,14 +626,14 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setEnabled(info.actionSupported(NET::ActionMaxVert) && windowState != LXQtTaskBarWindowState::MaximizedVertically && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::MaxVert); + a->setData(int(LXQtTaskBarWindowState::MaximizedVertically)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) && windowState != LXQtTaskBarWindowState::MaximizedHorizontally && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::MaxHoriz); + a->setData(int(LXQtTaskBarWindowState::MaximizedHorizontally)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } @@ -747,7 +734,7 @@ bool LXQtTaskButton::isOnCurrentScreen() const bool LXQtTaskButton::isMinimized() const { - return KWindowInfo(mWindow,NET::WMState | NET::XAWMState).isMinimized(); + return mBackend->getWindowState(mWindow) == LXQtTaskBarWindowState::Minimized; } Qt::Corner LXQtTaskButton::origin() const From 6a28f7a88d71301cf36a11bab1b57a3e672923c8 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 13:27:46 +0100 Subject: [PATCH 16/43] TaskBar: port workspace usage to backend --- plugin-taskbar/lxqttaskbutton.cpp | 19 ++++++++----------- plugin-taskbar/lxqttaskgroup.cpp | 4 ++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index b63b2aa3f..ec5fca2d4 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -559,21 +559,21 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) */ /********** Desktop menu **********/ - int deskNum = KX11Extras::numberOfDesktops(); + int deskNum = mBackend->getWorkspacesCount(); if (deskNum > 1) { - int winDesk = KWindowInfo(mWindow, NET::WMDesktop).desktop(); + int winDesk = mBackend->getWindowWorkspace(mWindow); QMenu* deskMenu = menu->addMenu(tr("To &Desktop")); a = deskMenu->addAction(tr("&All Desktops")); - a->setData(NET::OnAllDesktops); - a->setEnabled(winDesk != NET::OnAllDesktops); + a->setData(int(LXQtTaskBarWorkspace::ShowOnAll)); + a->setEnabled(winDesk != int(LXQtTaskBarWorkspace::ShowOnAll)); connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplicationToDesktop); deskMenu->addSeparator(); for (int i = 1; i <= deskNum; ++i) { - auto deskName = KX11Extras::desktopName(i).trimmed(); + auto deskName = mBackend->getWorkspaceName(i).trimmed(); if (deskName.isEmpty()) a = deskMenu->addAction(tr("Desktop &%1").arg(i)); else @@ -584,7 +584,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplicationToDesktop); } - int curDesk = KX11Extras::currentDesktop(); + int curDesk = mBackend->getCurrentWorkspace(); a = menu->addAction(tr("&To Current Desktop")); a->setData(curDesk); a->setEnabled(curDesk != winDesk); @@ -720,16 +720,13 @@ void LXQtTaskButton::setUrgencyHint(bool set) ************************************************/ bool LXQtTaskButton::isOnDesktop(int desktop) const { - return KWindowInfo(mWindow, NET::WMDesktop).isOnDesktop(desktop); + return mBackend->getWindowWorkspace(mWindow) == desktop; } bool LXQtTaskButton::isOnCurrentScreen() const { QScreen *screen = parentTaskBar()->screen(); - QRect screenGeo = screen->geometry(); - QRect windowGeo = KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry(); - - return screenGeo.intersects(windowGeo); + return mBackend->isWindowOnScreen(screen, mWindow); } bool LXQtTaskButton::isMinimized() const diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index e4991c8a4..655e99638 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -392,7 +392,7 @@ void LXQtTaskGroup::refreshVisibility() const int showDesktop = taskbar->showDesktopNum(); for(LXQtTaskButton * btn : std::as_const(mButtonHash)) { - bool visible = taskbar->isShowOnlyOneDesktopTasks() ? btn->isOnDesktop(0 == showDesktop ? KX11Extras::currentDesktop() : showDesktop) : true; + bool visible = taskbar->isShowOnlyOneDesktopTasks() ? btn->isOnDesktop(0 == showDesktop ? mBackend->getCurrentWorkspace() : showDesktop) : true; visible &= taskbar->isShowOnlyCurrentScreenTasks() ? btn->isOnCurrentScreen() : true; visible &= taskbar->isShowOnlyMinimizedTasks() ? btn->isMinimized() : true; btn->setVisible(visible); @@ -649,7 +649,7 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) { if (parentTaskBar()->isShowOnlyOneDesktopTasks() - || parentTaskBar()->isShowOnlyCurrentScreenTasks()) + || parentTaskBar()->isShowOnlyCurrentScreenTasks()) { needsRefreshVisibility = true; } From 7fad2a1819a3b6a67a060e02f5bc78b36d5cecb6 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 18:33:09 +0100 Subject: [PATCH 17/43] TaskBar: remove X11 specific includes in lxqttaskbutton.cpp --- plugin-taskbar/lxqttaskbutton.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index ec5fca2d4..2126dca76 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -50,18 +50,9 @@ #include #include -//TODO: remove -#include - -// Necessary for closeApplication() -#include - -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool +#include "ilxqttaskbarabstractbackend.h" -#include "ilxqttaskbarabstractbackend.h" bool LXQtTaskButton::sDraggging = false; From d5a4751721cd22f491b48d54ae2692e22bd80be0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 15:59:25 +0100 Subject: [PATCH 18/43] TaskBar: remove X11 code from LXQtTaskBar and LXQtTaskGroup --- plugin-taskbar/lxqttaskbar.cpp | 99 ++++---------------------------- plugin-taskbar/lxqttaskbar.h | 9 +-- plugin-taskbar/lxqttaskgroup.cpp | 32 ++++------- plugin-taskbar/lxqttaskgroup.h | 5 +- 4 files changed, 28 insertions(+), 117 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index fc60496a3..1bf7781f7 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -50,10 +50,6 @@ #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool - #include "lxqttaskbarbackend_x11.h" using namespace LXQt; @@ -107,10 +103,9 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : connect(mSignalMapper, &QSignalMapper::mappedInt, this, &LXQtTaskBar::activateTask); QTimer::singleShot(0, this, &LXQtTaskBar::registerShortcuts); - connect(KX11Extras::self(), static_cast(&KX11Extras::windowChanged) - , this, &LXQtTaskBar::onWindowChanged); - connect(KX11Extras::self(), &KX11Extras::windowAdded, this, &LXQtTaskBar::onWindowAdded); - connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); } /************************************************ @@ -121,49 +116,6 @@ LXQtTaskBar::~LXQtTaskBar() delete mStyle; } -/************************************************ - - ************************************************/ -bool LXQtTaskBar::acceptWindow(WId window) const -{ - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::ToolbarMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::NotificationMask; - - KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); - if (!info.valid()) - return false; - - if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - return false; - - if (info.state() & NET::SkipTaskbar) - return false; - - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - - // WM_TRANSIENT_FOR hint not set - normal window - WId transFor = info.transientFor(); - if (transFor == 0 || transFor == window || transFor == appRootWindow) - return true; - - info = KWindowInfo(transFor, NET::WMWindowType); - - QFlags normalFlag; - normalFlag |= NET::NormalMask; - normalFlag |= NET::DialogMask; - normalFlag |= NET::UtilityMask; - - return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); -} - /************************************************ ************************************************/ @@ -328,7 +280,7 @@ void LXQtTaskBar::addWindow(WId window) if (mUngroupedNextToExisting) { - const QString window_class = QString::fromUtf8(KWindowInfo(window, NET::Properties(), NET::WM2WindowClass).windowClassClass()); + const QString window_class = mBackend->getWindowClass(window); int src_index = mLayout->count() - 1; int dst_index = src_index; for (int i = mLayout->count() - 2; 0 <= i; --i) @@ -336,7 +288,7 @@ void LXQtTaskBar::addWindow(WId window) LXQtTaskGroup * current_group = qobject_cast(mLayout->itemAt(i)->widget()); if (nullptr != current_group) { - const QString current_class = QString::fromUtf8(KWindowInfo((current_group->groupName()).toUInt(), NET::Properties(), NET::WM2WindowClass).windowClassClass()); + const QString current_class = mBackend->getWindowClass(current_group->groupName().toUInt()); if(current_class == window_class) { dst_index = i + 1; @@ -370,43 +322,14 @@ auto LXQtTaskBar::removeWindow(windowMap_t::iterator pos) -> windowMap_t::iterat /************************************************ ************************************************/ -void LXQtTaskBar::refreshTaskList() -{ - QList new_list; - // Just add new windows to groups, deleting is up to the groups - const auto wnds = KX11Extras::stackingOrder(); - for (auto const wnd: wnds) - { - if (acceptWindow(wnd)) - { - new_list << wnd; - addWindow(wnd); - } - } - - //emulate windowRemoved if known window not reported by KWindowSystem - for (auto i = mKnownWindows.begin(), i_e = mKnownWindows.end(); i != i_e; ) - { - if (0 > new_list.indexOf(i.key())) - { - i = removeWindow(i); - } else - ++i; - } - - refreshPlaceholderVisibility(); -} - -/************************************************ - - ************************************************/ -void LXQtTaskBar::onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2) +void LXQtTaskBar::onWindowChanged(WId window, int prop) { auto i = mKnownWindows.find(window); if (mKnownWindows.end() != i) { - if (!(*i)->onWindowChanged(window, prop, prop2) && acceptWindow(window)) - { // window is removed from a group because of class change, so we should add it again + if (!(*i)->onWindowChanged(window, LXQtTaskBarWindowProperty(prop))) + { + // window is removed from a group because of class change, so we should add it again addWindow(window); } } @@ -415,7 +338,7 @@ void LXQtTaskBar::onWindowChanged(WId window, NET::Properties prop, NET::Propert void LXQtTaskBar::onWindowAdded(WId window) { auto const pos = mKnownWindows.find(window); - if (mKnownWindows.end() == pos && acceptWindow(window)) + if (mKnownWindows.end() == pos) addWindow(window); } @@ -541,8 +464,8 @@ void LXQtTaskBar::settingsChanged() if (iconByClassOld != mIconByClass) emit iconByClassChanged(); - refreshTaskList(); //TODO: remove mBackend->reloadWindows(); + refreshPlaceholderVisibility(); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index c36bca7e0..9f94a2958 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -37,9 +37,6 @@ #include #include "../panel/ilxqtpanel.h" -#include -#include -#include class ILXQtPanel; class ILXQtPanelPlugin; @@ -107,13 +104,14 @@ public slots: virtual void dragMoveEvent(QDragMoveEvent * event); private slots: - void refreshTaskList(); void refreshButtonRotation(); void refreshPlaceholderVisibility(); void groupBecomeEmptySlot(); - void onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2); + + void onWindowChanged(WId window, int prop); void onWindowAdded(WId window); void onWindowRemoved(WId window); + void registerShortcuts(); void shortcutRegistered(); void activateTask(int pos); @@ -150,7 +148,6 @@ private slots: int mWheelEventsAction; int mWheelDeltaThreshold; - bool acceptWindow(WId window) const; void setButtonStyle(Qt::ToolButtonStyle buttonStyle); void wheelEvent(QWheelEvent* event); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 655e99638..30babc358 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -41,12 +41,6 @@ #include #include #include -#include - -//TODO: remove -#include //For nativeInterface() -#include -#undef Bool #include "ilxqttaskbarabstractbackend.h" @@ -622,8 +616,10 @@ void LXQtTaskGroup::wheelEvent(QWheelEvent* event) /************************************************ ************************************************/ -bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2) -{ // returns true if the class is preserved +bool LXQtTaskGroup::onWindowChanged(WId window, LXQtTaskBarWindowProperty prop) +{ + // Returns true if the class is preserved + bool needsRefreshVisibility{false}; QList buttons; if (mButtonHash.contains(window)) @@ -636,17 +632,16 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (!buttons.isEmpty()) { // if class is changed the window won't belong to our group any more - if (parentTaskBar()->isGroupingEnabled() && prop2.testFlag(NET::WM2WindowClass)) + if (parentTaskBar()->isGroupingEnabled() && prop == LXQtTaskBarWindowProperty::WindowClass) { - KWindowInfo info(window, NET::Properties(), NET::WM2WindowClass); - if (QString::fromUtf8(info.windowClassClass()) != mGroupName) + if (mBackend->getWindowClass(windowId()) != mGroupName) { onWindowRemoved(window); return false; } } // window changed virtual desktop - if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) + if (prop == LXQtTaskBarWindowProperty::Workspace) { if (parentTaskBar()->isShowOnlyOneDesktopTasks() || parentTaskBar()->isShowOnlyCurrentScreenTasks()) @@ -655,34 +650,29 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope } } - if (prop.testFlag(NET::WMVisibleName) || prop.testFlag(NET::WMName)) + if (prop == LXQtTaskBarWindowProperty::Title) std::for_each(buttons.begin(), buttons.end(), std::mem_fn(&LXQtTaskButton::updateText)); // XXX: we are setting window icon geometry -> don't need to handle NET::WMIconGeometry // Icon of the button can be based on windowClass - if (prop.testFlag(NET::WMIcon) || prop2.testFlag(NET::WM2WindowClass)) + if (prop == LXQtTaskBarWindowProperty::Icon) std::for_each(buttons.begin(), buttons.end(), std::mem_fn(&LXQtTaskButton::updateIcon)); bool set_urgency = false; bool urgency = false; - if (prop2.testFlag(NET::WM2Urgency)) + if (prop == LXQtTaskBarWindowProperty::Urgency) { set_urgency = true; //FIXME: original code here did not consider "demand attention", was it intentional? urgency = mBackend->applicationDemandsAttention(window); } - if (prop.testFlag(NET::WMState)) + if (prop == LXQtTaskBarWindowProperty::State) { - KWindowInfo info{window, NET::WMState}; - - // Force refresh urgency if (!set_urgency) urgency = mBackend->applicationDemandsAttention(window); std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency)); set_urgency = false; - if (info.hasState(NET::SkipTaskbar)) - onWindowRemoved(window); if (parentTaskBar()->isShowOnlyMinimizedTasks()) { diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index 3787f411f..d4868c5d2 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -33,7 +33,7 @@ #include "lxqttaskbutton.h" -#include +#include "lxqttaskbartypes.h" class QVBoxLayout; class ILXQtPanelPlugin; @@ -60,7 +60,8 @@ class LXQtTaskGroup: public LXQtTaskButton // if circular is true, then it will go around the list of buttons LXQtTaskButton * getNextPrevChildButton(bool next, bool circular); - bool onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2); + bool onWindowChanged(WId window, LXQtTaskBarWindowProperty prop); + void setAutoRotation(bool value, ILXQtPanel::Position position); Qt::ToolButtonStyle popupButtonStyle() const; void setToolButtonsStyle(Qt::ToolButtonStyle style); From 649e85adaf62159a504eb9345fc576721b3cfe43 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 16:38:00 +0100 Subject: [PATCH 19/43] TaskBar: LXQtTaskButton remove X11 specific code --- plugin-taskbar/lxqttaskbutton.cpp | 68 ++++++++++++++++--------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 2126dca76..fa588455e 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -140,7 +140,7 @@ void LXQtTaskButton::updateIcon() QIcon ico; if (mParentTaskBar->isIconByClass()) { - ico = XdgIcon::fromTheme(QString::fromUtf8(KWindowInfo{mWindow, NET::Properties(), NET::WM2WindowClass}.windowClassClass()).toLower()); + ico = XdgIcon::fromTheme(mBackend->getWindowClass(mWindow).toLower()); } if (ico.isNull()) { @@ -517,8 +517,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) return; } - KWindowInfo info(mWindow, NET::Properties(), NET::WM2AllowedActions); - NET::States state = KWindowInfo(mWindow, NET::WMState).state(); + const LXQtTaskBarWindowState state = mBackend->getWindowState(mWindow); QMenu * menu = new QMenu(tr("Application")); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -587,74 +586,79 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); a = menu->addAction(tr("Move To N&ext Monitor")); connect(a, &QAction::triggered, this, [this] { moveApplicationToPrevNextMonitor(true); }); - a->setEnabled(info.actionSupported(NET::ActionMove) && (!(state & NET::FullScreen) || ((state & NET::FullScreen) && info.actionSupported(NET::ActionFullScreen)))); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Move) && + (state != LXQtTaskBarWindowState::FullScreen + || ((state == LXQtTaskBarWindowState::FullScreen) && mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::FullScreen)))); a = menu->addAction(tr("Move To &Previous Monitor")); connect(a, &QAction::triggered, this, [this] { moveApplicationToPrevNextMonitor(false); }); } + menu->addSeparator(); a = menu->addAction(tr("&Move")); - a->setEnabled(info.actionSupported(NET::ActionMove) && !(state & NET::Max) && !(state & NET::FullScreen)); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Move) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::FullScreen); connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplication); a = menu->addAction(tr("Resi&ze")); - a->setEnabled(info.actionSupported(NET::ActionResize) && !(state & NET::Max) && !(state & NET::FullScreen)); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Resize) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::FullScreen); connect(a, &QAction::triggered, this, &LXQtTaskButton::resizeApplication); /********** State menu **********/ menu->addSeparator(); - LXQtTaskBarWindowState windowState = mBackend->getWindowState(mWindow); - a = menu->addAction(tr("Ma&ximize")); - a->setEnabled(info.actionSupported(NET::ActionMax) - && windowState != LXQtTaskBarWindowState::Maximized - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Maximize) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::Maximized)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) { a = menu->addAction(tr("Maximize vertically")); - a->setEnabled(info.actionSupported(NET::ActionMaxVert) - && windowState != LXQtTaskBarWindowState::MaximizedVertically - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::MaximizeVertically) + && state != LXQtTaskBarWindowState::MaximizedVertically + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::MaximizedVertically)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); - a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) - && windowState != LXQtTaskBarWindowState::MaximizedHorizontally - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::MaximizeHorizontally) + && state != LXQtTaskBarWindowState::MaximizedHorizontally + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::MaximizedHorizontally)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } a = menu->addAction(tr("&Restore")); - a->setEnabled(windowState == LXQtTaskBarWindowState::Hidden - || windowState == LXQtTaskBarWindowState::Minimized - || windowState == LXQtTaskBarWindowState::Maximized - || windowState == LXQtTaskBarWindowState::MaximizedVertically - || windowState == LXQtTaskBarWindowState::MaximizedHorizontally); + a->setEnabled(state == LXQtTaskBarWindowState::Hidden + || state == LXQtTaskBarWindowState::Minimized + || state == LXQtTaskBarWindowState::Maximized + || state == LXQtTaskBarWindowState::MaximizedVertically + || state == LXQtTaskBarWindowState::MaximizedHorizontally); connect(a, &QAction::triggered, this, &LXQtTaskButton::deMaximizeApplication); a = menu->addAction(tr("Mi&nimize")); - a->setEnabled(info.actionSupported(NET::ActionMinimize) - && windowState != LXQtTaskBarWindowState::Hidden - && windowState != LXQtTaskBarWindowState::Minimized); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Minimize) + && state != LXQtTaskBarWindowState::Hidden + && state != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::minimizeApplication); - if (windowState == LXQtTaskBarWindowState::RolledUp) + if (state == LXQtTaskBarWindowState::RolledUp) { a = menu->addAction(tr("Roll down")); - a->setEnabled(info.actionSupported(NET::ActionShade) - && windowState != LXQtTaskBarWindowState::Hidden - && windowState != LXQtTaskBarWindowState::Minimized); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::RollUp) + && state != LXQtTaskBarWindowState::Hidden + && state != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::unShadeApplication); } else { a = menu->addAction(tr("Roll up")); - a->setEnabled(info.actionSupported(NET::ActionShade) - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::RollUp) + && state != LXQtTaskBarWindowState::Hidden); connect(a, &QAction::triggered, this, &LXQtTaskButton::shadeApplication); } @@ -665,7 +669,6 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); - // FIXME: There is no info.actionSupported(NET::ActionKeepAbove) a = layerMenu->addAction(tr("Always on &top")); a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepAbove); a->setData(int(LXQtTaskBarWindowLayer::KeepAbove)); @@ -676,7 +679,6 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setData(int(LXQtTaskBarWindowLayer::Normal)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); - // FIXME: There is no info.actionSupported(NET::ActionKeepBelow) a = layerMenu->addAction(tr("Always on &bottom")); a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepBelow); a->setData(int(LXQtTaskBarWindowLayer::KeepBelow)); From 99747610a3ba5d49f9991c4ccb34f537e953ac91 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 23:40:21 +0100 Subject: [PATCH 20/43] TaskBar: set context menu parent --- plugin-taskbar/lxqttaskbutton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index fa588455e..08bc50a1e 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -519,7 +519,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) const LXQtTaskBarWindowState state = mBackend->getWindowState(mWindow); - QMenu * menu = new QMenu(tr("Application")); + QMenu * menu = new QMenu(tr("Application"), this); menu->setAttribute(Qt::WA_DeleteOnClose); QAction* a; From b0e54b87c7a9800cf9786b97916b48e9eb0c4c23 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 18 Feb 2024 00:01:15 +0100 Subject: [PATCH 21/43] LXQtPanel: rework calculatePopupWindowPos() - Don't rely on global screen coordinates - This will be needed for future Wayland port, Where we don't have global screen coordinates - Keep compatible behavior on X11 --- panel/lxqtpanel.cpp | 56 +++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index a1fb68f35..ead1b276b 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -1407,48 +1407,60 @@ Plugin* LXQtPanel::findPlugin(const ILXQtPanelPlugin* iPlugin) const ************************************************/ QRect LXQtPanel::calculatePopupWindowPos(QPoint const & absolutePos, QSize const & windowSize) const { - int x = absolutePos.x(), y = absolutePos.y(); + QPoint localPos = mapFromGlobal(absolutePos); + int x = localPos.x(), y = localPos.y(); switch (position()) { case ILXQtPanel::PositionTop: - y = mGeometry.bottom(); + y = geometry().height(); break; case ILXQtPanel::PositionBottom: - y = mGeometry.top() - windowSize.height(); + y = 0 - windowSize.height(); break; case ILXQtPanel::PositionLeft: - x = mGeometry.right(); + x = geometry().right(); break; case ILXQtPanel::PositionRight: - x = mGeometry.left() - windowSize.width(); + x = geometry().left() - windowSize.width(); break; } QRect res(QPoint(x, y), windowSize); - QRect panelScreen; - const auto screens = QApplication::screens(); - if (mActualScreenNum < screens.size()) - panelScreen = screens.at(mActualScreenNum)->geometry(); - // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a - // multihead setup with different resolutions. In this case, the size of the work area is limited - // by the smallest monitor and may be much smaller than the current screen and we will place the - // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. - if (res.right() > panelScreen.right()) - res.moveRight(panelScreen.right()); + // Map to global coordinates + res = QRect(mapToGlobal(res.topLeft()), mapToGlobal(res.bottomRight())); + + if(qGuiApp->nativeInterface()) + { + //On X11 we clamp rects inside screen area. + //NOTE: On Wayland it's done by compositor - if (res.bottom() > panelScreen.bottom()) - res.moveBottom(panelScreen.bottom()); + QRect panelScreen; + const auto screens = QApplication::screens(); + if (mActualScreenNum < screens.size()) + panelScreen = screens.at(mActualScreenNum)->geometry(); - if (res.left() < panelScreen.left()) - res.moveLeft(panelScreen.left()); + // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a + // multihead setup with different resolutions. In this case, the size of the work area is limited + // by the smallest monitor and may be much smaller than the current screen and we will place the + // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. - if (res.top() < panelScreen.top()) - res.moveTop(panelScreen.top()); + if (res.right() > panelScreen.right()) + res.moveRight(panelScreen.right()); + + if (res.bottom() > panelScreen.bottom()) + res.moveBottom(panelScreen.bottom() - mGeometry.top()); + + if (res.left() < panelScreen.left()) + res.moveLeft(panelScreen.left()); + + if (res.top() < panelScreen.top()) + res.moveTop(panelScreen.top() - mGeometry.top()); + } return res; } @@ -1470,7 +1482,7 @@ QRect LXQtPanel::calculatePopupWindowPos(const ILXQtPanelPlugin *plugin, const Q } // Note: assuming there are not contentMargins around the "BackgroundWidget" (LXQtPanelWidget) - return calculatePopupWindowPos(mGeometry.topLeft() + panel_plugin->geometry().topLeft(), windowSize); + return calculatePopupWindowPos(mapToGlobal(panel_plugin->geometry().topLeft()), windowSize); } From 8fd51efdfd11bbdc11d13a92dddf689ea50f8631 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 18 Feb 2024 13:13:24 +0100 Subject: [PATCH 22/43] LXQtPanel: avoid QCursor::pos() usage --- panel/lxqtpanel.cpp | 6 +++--- panel/lxqtpanel.h | 3 ++- panel/plugin.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index ead1b276b..f0897fede 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -1255,7 +1255,7 @@ bool LXQtPanel::event(QEvent *event) switch (event->type()) { case QEvent::ContextMenu: - showPopupMenu(); + showPopupMenu(static_cast(event)->globalPos()); break; case QEvent::LayoutRequest: @@ -1320,7 +1320,7 @@ void LXQtPanel::showEvent(QShowEvent *event) /************************************************ ************************************************/ -void LXQtPanel::showPopupMenu(Plugin *plugin) +void LXQtPanel::showPopupMenu(const QPoint& cursorPos, Plugin *plugin) { PopupMenu * menu = new PopupMenu(tr("Panel"), this); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -1388,7 +1388,7 @@ void LXQtPanel::showPopupMenu(Plugin *plugin) * sometimes wrongly (it seems that this bug is somehow connected to misinterpretation * of QDesktopWidget::availableGeometry) */ - menu->setGeometry(calculatePopupWindowPos(QCursor::pos(), menu->sizeHint())); + menu->setGeometry(calculatePopupWindowPos(cursorPos, menu->sizeHint())); willShowWindow(menu); menu->show(); } diff --git a/panel/lxqtpanel.h b/panel/lxqtpanel.h index c1930c1bf..ed952dbdb 100644 --- a/panel/lxqtpanel.h +++ b/panel/lxqtpanel.h @@ -139,10 +139,11 @@ class LXQT_PANEL_API LXQtPanel : public QFrame, public ILXQtPanel * is given as parameter, the menu will be divided in two groups: * plugin-specific options and panel-related options. As these two are * shown together, this menu has to be created by LXQtPanel. + * @param cursorPos The global cursor pos * @param plugin The plugin whose menu options will be included in the * context menu. */ - void showPopupMenu(Plugin *plugin = 0); + void showPopupMenu(const QPoint &cursorPos, Plugin *plugin = nullptr); // ILXQtPanel overrides ........ ILXQtPanel::Position position() const override { return mPosition; } diff --git a/panel/plugin.cpp b/panel/plugin.cpp index eb22ed431..4844bd528 100644 --- a/panel/plugin.cpp +++ b/panel/plugin.cpp @@ -380,9 +380,9 @@ void Plugin::saveSettings() /************************************************ ************************************************/ -void Plugin::contextMenuEvent(QContextMenuEvent * /*event*/) +void Plugin::contextMenuEvent(QContextMenuEvent * event) { - mPanel->showPopupMenu(this); + mPanel->showPopupMenu(event->globalPos(), this); } From be23ae5cb184388d65d7973bde8320723e8c7887 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:10:58 +0100 Subject: [PATCH 23/43] ILXQtTaskbarAbstractBackend: new Geometry window property This new window propery flag is needed to notify geometry changes --- plugin-taskbar/lxqttaskbarbackend_x11.cpp | 5 +++++ plugin-taskbar/lxqttaskbartypes.h | 1 + 2 files changed, 6 insertions(+) diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/plugin-taskbar/lxqttaskbarbackend_x11.cpp index cc7759f4e..bcb83bdde 100644 --- a/plugin-taskbar/lxqttaskbarbackend_x11.cpp +++ b/plugin-taskbar/lxqttaskbarbackend_x11.cpp @@ -48,6 +48,11 @@ void LXQtTaskbarX11Backend::onWindowChanged(WId windowId, NET::Properties prop, return; } + if (prop.testFlag(NET::WMGeometry)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Geometry)); + } + if (prop2.testFlag(NET::WM2WindowClass)) { emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::WindowClass)); diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h index ccea42027..9e12092bb 100644 --- a/plugin-taskbar/lxqttaskbartypes.h +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -22,6 +22,7 @@ enum class LXQtTaskBarWindowProperty Title = 0, Icon, State, + Geometry, Urgency, WindowClass, Workspace From e334ec065350a9914dd5d9f97ca8a56f770df729 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 12:34:05 +0100 Subject: [PATCH 24/43] Move ILXQtTaskbarAbstractBackend to panel directory - It is now a global instance --- panel/CMakeLists.txt | 15 +++++++++ panel/backends/CMakeLists.txt | 1 + .../backends}/ilxqttaskbarabstractbackend.cpp | 2 +- .../backends}/ilxqttaskbarabstractbackend.h | 0 .../backends}/lxqttaskbartypes.h | 0 panel/backends/xcb/CMakeLists.txt | 1 + .../backends/xcb}/lxqttaskbarbackend_x11.cpp | 0 .../backends/xcb}/lxqttaskbarbackend_x11.h | 2 +- panel/lxqtpanelapplication.cpp | 31 +++++++++++++++---- panel/lxqtpanelapplication.h | 4 +++ panel/lxqtpanelapplication_p.h | 3 ++ plugin-taskbar/CMakeLists.txt | 5 --- plugin-taskbar/lxqttaskbar.cpp | 9 +++--- plugin-taskbar/lxqttaskbarproxymodel.cpp | 2 +- plugin-taskbar/lxqttaskbarproxymodel.h | 2 +- plugin-taskbar/lxqttaskbutton.cpp | 2 +- plugin-taskbar/lxqttaskgroup.cpp | 2 +- plugin-taskbar/lxqttaskgroup.h | 2 +- 18 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 panel/backends/CMakeLists.txt rename {plugin-taskbar => panel/backends}/ilxqttaskbarabstractbackend.cpp (90%) rename {plugin-taskbar => panel/backends}/ilxqttaskbarabstractbackend.h (100%) rename {plugin-taskbar => panel/backends}/lxqttaskbartypes.h (100%) create mode 100644 panel/backends/xcb/CMakeLists.txt rename {plugin-taskbar => panel/backends/xcb}/lxqttaskbarbackend_x11.cpp (100%) rename {plugin-taskbar => panel/backends/xcb}/lxqttaskbarbackend_x11.h (98%) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 556d2212f..83d813772 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -1,5 +1,8 @@ set(PROJECT lxqt-panel) +# TODO +add_subdirectory(backends) + set(PRIV_HEADERS panelpluginsmodel.h windownotifier.h @@ -18,6 +21,11 @@ set(PRIV_HEADERS config/configstyling.h config/configpluginswidget.h config/addplugindialog.h + + backends/ilxqttaskbarabstractbackend.h + backends/lxqttaskbartypes.h + + backends/xcb/lxqttaskbarbackend_x11.h ) # using LXQt namespace in the public headers. @@ -26,6 +34,9 @@ set(PUB_HEADERS pluginsettings.h ilxqtpanelplugin.h ilxqtpanel.h + + backends/ilxqttaskbarabstractbackend.h + backends/lxqttaskbartypes.h ) set(SOURCES @@ -45,6 +56,10 @@ set(SOURCES config/configstyling.cpp config/configpluginswidget.cpp config/addplugindialog.cpp + + backends/ilxqttaskbarabstractbackend.cpp + + backends/xcb/lxqttaskbarbackend_x11.cpp ) set(UI diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt new file mode 100644 index 000000000..8f34a3c67 --- /dev/null +++ b/panel/backends/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(xcb) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp similarity index 90% rename from plugin-taskbar/ilxqttaskbarabstractbackend.cpp rename to panel/backends/ilxqttaskbarabstractbackend.cpp index d0959fb78..acf33f464 100644 --- a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -1,4 +1,4 @@ -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h similarity index 100% rename from plugin-taskbar/ilxqttaskbarabstractbackend.h rename to panel/backends/ilxqttaskbarabstractbackend.h diff --git a/plugin-taskbar/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h similarity index 100% rename from plugin-taskbar/lxqttaskbartypes.h rename to panel/backends/lxqttaskbartypes.h diff --git a/panel/backends/xcb/CMakeLists.txt b/panel/backends/xcb/CMakeLists.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/panel/backends/xcb/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp similarity index 100% rename from plugin-taskbar/lxqttaskbarbackend_x11.cpp rename to panel/backends/xcb/lxqttaskbarbackend_x11.cpp diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h similarity index 98% rename from plugin-taskbar/lxqttaskbarbackend_x11.h rename to panel/backends/xcb/lxqttaskbarbackend_x11.h index 26c721cf7..2797a7da8 100644 --- a/plugin-taskbar/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -1,7 +1,7 @@ #ifndef LXQTTASKBARBACKEND_X11_H #define LXQTTASKBARBACKEND_X11_H -#include "ilxqttaskbarabstractbackend.h" +#include "../ilxqttaskbarabstractbackend.h" //TODO: make PIMPL to forward declare NET::Properties, Display, xcb_connection_t #include diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 430ea6f35..2b1980d85 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -25,22 +25,35 @@ * * END_COMMON_COPYRIGHT_HEADER */ - #include "lxqtpanelapplication.h" #include "lxqtpanelapplication_p.h" -#include "lxqtpanel.h" + #include "config/configpaneldialog.h" -#include -#include -#include +#include "lxqtpanel.h" + +#include #include +#include #include -#include +#include +#include + +#include "backends/xcb/lxqttaskbarbackend_x11.h" + +ILXQtTaskbarAbstractBackend *createWMBackend() +{ + if(qGuiApp->nativeInterface()) + return new LXQtTaskbarX11Backend; + + Q_ASSERT_X(false, "createWMBackend()", "Only X11 supported!"); + return nullptr; +} LXQtPanelApplicationPrivate::LXQtPanelApplicationPrivate(LXQtPanelApplication *q) : mSettings(nullptr), q_ptr(q) { + mWMBackend = createWMBackend(); } @@ -290,6 +303,12 @@ bool LXQtPanelApplication::isPluginSingletonAndRunning(QString const & pluginId) return false; } +ILXQtTaskbarAbstractBackend *LXQtPanelApplication::getWMBackend() const +{ + Q_D(const LXQtPanelApplication); + return d->mWMBackend; +} + // See LXQtPanelApplication::LXQtPanelApplication for why this isn't good. void LXQtPanelApplication::setIconTheme(const QString &iconTheme) { diff --git a/panel/lxqtpanelapplication.h b/panel/lxqtpanelapplication.h index a672e7e46..15c912884 100644 --- a/panel/lxqtpanelapplication.h +++ b/panel/lxqtpanelapplication.h @@ -37,6 +37,8 @@ class QScreen; class LXQtPanel; class LXQtPanelApplicationPrivate; +class ILXQtTaskbarAbstractBackend; + /*! * \brief The LXQtPanelApplication class inherits from LXQt::Application and * is therefore the QApplication that we will create and execute in our @@ -89,6 +91,8 @@ class LXQtPanelApplication : public LXQt::Application */ bool isPluginSingletonAndRunning(QString const & pluginId) const; + ILXQtTaskbarAbstractBackend* getWMBackend() const; + public slots: /*! * \brief Adds a new LXQtPanel which consists of the following steps: diff --git a/panel/lxqtpanelapplication_p.h b/panel/lxqtpanelapplication_p.h index 4dd26182c..db924bf62 100644 --- a/panel/lxqtpanelapplication_p.h +++ b/panel/lxqtpanelapplication_p.h @@ -27,6 +27,8 @@ namespace LXQt { class Settings; } +class ILXQtTaskbarAbstractBackend; + class LXQtPanelApplicationPrivate { Q_DECLARE_PUBLIC(LXQtPanelApplication) public: @@ -35,6 +37,7 @@ class LXQtPanelApplicationPrivate { ~LXQtPanelApplicationPrivate() {}; LXQt::Settings *mSettings; + ILXQtTaskbarAbstractBackend *mWMBackend; ILXQtPanel::Position computeNewPanelPosition(const LXQtPanel *p, const int screenNum); diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index be434f6a6..74a00d6e6 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -8,9 +8,6 @@ set(HEADERS lxqttaskgroup.h lxqtgrouppopup.h - ilxqttaskbarabstractbackend.h - lxqttaskbartypes.h - lxqttaskbarbackend_x11.h lxqttaskbarproxymodel.h ) @@ -22,8 +19,6 @@ set(SOURCES lxqttaskgroup.cpp lxqtgrouppopup.cpp - ilxqttaskbarabstractbackend.cpp - lxqttaskbarbackend_x11.cpp lxqttaskbarproxymodel.cpp ) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 1bf7781f7..c306d796e 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -50,7 +50,8 @@ #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" -#include "lxqttaskbarbackend_x11.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" +#include "../panel/lxqtpanelapplication.h" using namespace LXQt; @@ -93,9 +94,9 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mPlaceHolder->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); mLayout->addWidget(mPlaceHolder); - // Create model - //TODO: use backend factory - mBackend = new LXQtTaskbarX11Backend(this); + // Get backend + LXQtPanelApplication *a = static_cast(qApp); + mBackend = a->getWMBackend(); QTimer::singleShot(0, this, &LXQtTaskBar::settingsChanged); setAcceptDrops(true); diff --git a/plugin-taskbar/lxqttaskbarproxymodel.cpp b/plugin-taskbar/lxqttaskbarproxymodel.cpp index e2f1d3941..fdcdfa4d4 100644 --- a/plugin-taskbar/lxqttaskbarproxymodel.cpp +++ b/plugin-taskbar/lxqttaskbarproxymodel.cpp @@ -1,6 +1,6 @@ #include "lxqttaskbarproxymodel.h" -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" #include diff --git a/plugin-taskbar/lxqttaskbarproxymodel.h b/plugin-taskbar/lxqttaskbarproxymodel.h index be5799076..8bbb5ec49 100644 --- a/plugin-taskbar/lxqttaskbarproxymodel.h +++ b/plugin-taskbar/lxqttaskbarproxymodel.h @@ -4,7 +4,7 @@ #include #include -#include "lxqttaskbartypes.h" +#include "../panel/backends/lxqttaskbartypes.h" class ILXQtTaskbarAbstractBackend; diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 08bc50a1e..019b45199 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -50,7 +50,7 @@ #include #include -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 30babc358..c6075df89 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -42,7 +42,7 @@ #include #include -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" /************************************************ diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index d4868c5d2..31ab784fb 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -33,7 +33,7 @@ #include "lxqttaskbutton.h" -#include "lxqttaskbartypes.h" +#include "../panel/backends/lxqttaskbartypes.h" class QVBoxLayout; class ILXQtPanelPlugin; From 9bbf4fa3d1e95bff52105038574a1ff2550a1bc3 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:17:55 +0100 Subject: [PATCH 25/43] ILXQtTaskbarAbstractBackend: new isAreaOverlapped() method --- panel/backends/ilxqttaskbarabstractbackend.h | 3 ++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 32 +++++++++++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.h | 3 ++ 3 files changed, 38 insertions(+) diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index 932d64fc0..fb49d7b52 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -65,6 +65,9 @@ class ILXQtTaskbarAbstractBackend : public QObject virtual void refreshIconGeometry(WId windowId, const QRect &geom) = 0; + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const = 0; + signals: void reloaded(); diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index bcb83bdde..1d29148c8 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -585,3 +585,35 @@ void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom nrect.size.width = geom.width(); info.setIconGeometry(nrect); } + +bool LXQtTaskbarX11Backend::isAreaOverlapped(const QRect &area) const +{ + //TODO: reuse our m_windows cache? + QFlags ignoreList; + ignoreList |= NET::DesktopMask; + ignoreList |= NET::DockMask; + ignoreList |= NET::SplashMask; + ignoreList |= NET::MenuMask; + ignoreList |= NET::PopupMenuMask; + ignoreList |= NET::DropdownMenuMask; + ignoreList |= NET::TopMenuMask; + ignoreList |= NET::NotificationMask; + + const auto wIds = KX11Extras::stackingOrder(); + for (auto const wId : wIds) + { + KWindowInfo info(wId, NET::WMWindowType | NET::WMState | NET::WMFrameExtents | NET::WMDesktop); + if (info.valid() + // skip windows that are on other desktops + && info.isOnCurrentDesktop() + // skip shaded, minimized or hidden windows + && !(info.state() & (NET::Shaded | NET::Hidden)) + // check against the list of ignored types + && !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) + { + if (info.frameGeometry().intersects(area)) + return true; + } + } + return false; +} diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 2797a7da8..42e84b146 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -61,6 +61,9 @@ class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + private slots: void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); void onWindowAdded(WId windowId); From b9c8dfd7838d3b0c118b2c89d74023449575ebe2 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:11:23 +0100 Subject: [PATCH 26/43] LXQtPanel: use less KX11Extras and more ILXQtTaskbarAbstractBackend --- panel/lxqtpanel.cpp | 83 +++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index f0897fede..3735bd068 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -51,7 +51,9 @@ #include #include #include -#include + +#include "backends/ilxqttaskbarabstractbackend.h" + #include @@ -276,18 +278,21 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg QTimer::singleShot(PANEL_HIDE_FIRST_TIME, this, SLOT(hidePanel())); } - connect(KX11Extras::self(), &KX11Extras::windowAdded, this, [this] { + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, [this] { if (mHidable && mHideOnOverlap && !mHidden) { mShowDelayTimer.stop(); hidePanel(); } }); - connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, [this] { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, [this] { if (mHidable && mHideOnOverlap && mHidden && !isPanelOverlapped()) mShowDelayTimer.start(); }); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, [this] { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, [this] { if (mHidable && mHideOnOverlap) { if (!mHidden) @@ -299,12 +304,12 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg mShowDelayTimer.start(); } }); - connect(KX11Extras::self(), - static_cast(&KX11Extras::windowChanged), - this, [this] (WId /* id */, NET::Properties prop, NET::Properties2) { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, [this] (WId /* id */, int prop) + { if (mHidable && mHideOnOverlap // when a window is moved, resized, shaded, or minimized - && (prop.testFlag(NET::WMGeometry) || prop.testFlag(NET::WMState))) + && (prop == int(LXQtTaskBarWindowProperty::Geometry) || prop == int(LXQtTaskBarWindowProperty::State))) { if (!mHidden) { @@ -453,7 +458,8 @@ LXQtPanel::~LXQtPanel() void LXQtPanel::show() { QWidget::show(); - KX11Extras::setOnDesktop(effectiveWinId(), NET::OnAllDesktops); + if(qGuiApp->nativeInterface()) //TODO: cache in bool isPlatformX11 + KX11Extras::setOnDesktop(effectiveWinId(), NET::OnAllDesktops); } @@ -1264,22 +1270,25 @@ bool LXQtPanel::event(QEvent *event) case QEvent::WinIdChange: { - // qDebug() << "WinIdChange" << hex << effectiveWinId(); - if(effectiveWinId() == 0) - break; + if(qGuiApp->nativeInterface()) + { + // qDebug() << "WinIdChange" << hex << effectiveWinId(); + if(effectiveWinId() == 0) + break; - // Sometimes Qt needs to re-create the underlying window of the widget and - // the winId() may be changed at runtime. So we need to reset all X11 properties - // when this happens. + // Sometimes Qt needs to re-create the underlying window of the widget and + // the winId() may be changed at runtime. So we need to reset all X11 properties + // when this happens. qDebug() << "WinIdChange" << Qt::hex << effectiveWinId() << "handle" << windowHandle() << windowHandle()->screen(); - // Qt::WA_X11NetWmWindowTypeDock becomes ineffective in Qt 5 - // See QTBUG-39887: https://bugreports.qt-project.org/browse/QTBUG-39887 - // Let's use KWindowSystem for that - KX11Extras::setType(effectiveWinId(), NET::Dock); + // Qt::WA_X11NetWmWindowTypeDock becomes ineffective in Qt 5 + // See QTBUG-39887: https://bugreports.qt-project.org/browse/QTBUG-39887 + // Let's use KWindowSystem for that + KX11Extras::setType(effectiveWinId(), NET::Dock); - updateWmStrut(); // reserve screen space for the panel - KX11Extras::setOnAllDesktops(effectiveWinId(), true); + updateWmStrut(); // reserve screen space for the panel + KX11Extras::setOnAllDesktops(effectiveWinId(), true); + } break; } case QEvent::DragEnter: @@ -1565,33 +1574,11 @@ void LXQtPanel::userRequestForDeletion() bool LXQtPanel::isPanelOverlapped() const { - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::DropdownMenuMask; - ignoreList |= NET::TopMenuMask; - ignoreList |= NET::NotificationMask; - - const auto wIds = KX11Extras::stackingOrder(); - for (auto const wId : wIds) - { - KWindowInfo info(wId, NET::WMWindowType | NET::WMState | NET::WMFrameExtents | NET::WMDesktop); - if (info.valid() - // skip windows that are on other desktops - && info.isOnCurrentDesktop() - // skip shaded, minimized or hidden windows - && !(info.state() & (NET::Shaded | NET::Hidden)) - // check against the list of ignored types - && !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - { - if (info.frameGeometry().intersects(mGeometry)) - return true; - } - } - return false; + LXQtPanelApplication *a = reinterpret_cast(qApp); + + //TODO: calculate geometry on wayland + QRect area = mGeometry; + return a->getWMBackend()->isAreaOverlapped(area); } void LXQtPanel::showPanel(bool animate) From 145d59806280feaa82d301ea1c6b7146f49ea18a Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 20:32:50 +0100 Subject: [PATCH 27/43] ILXQtTaskbarAbstractBackend: add dummy implementation This will be used to avoid crashing panel in case no backend could be created. A warning message will be printed in this case. --- panel/CMakeLists.txt | 2 + panel/backends/lxqttaskbardummybackend.cpp | 156 +++++++++++++++++++++ panel/backends/lxqttaskbardummybackend.h | 82 +++++++++++ panel/lxqtpanelapplication.cpp | 10 +- 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 panel/backends/lxqttaskbardummybackend.cpp create mode 100644 panel/backends/lxqttaskbardummybackend.h diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 83d813772..f088fe33a 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -25,6 +25,7 @@ set(PRIV_HEADERS backends/ilxqttaskbarabstractbackend.h backends/lxqttaskbartypes.h + backends/lxqttaskbardummybackend.h backends/xcb/lxqttaskbarbackend_x11.h ) @@ -59,6 +60,7 @@ set(SOURCES backends/ilxqttaskbarabstractbackend.cpp + backends/lxqttaskbardummybackend.cpp backends/xcb/lxqttaskbarbackend_x11.cpp ) diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp new file mode 100644 index 000000000..f91fa75bf --- /dev/null +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -0,0 +1,156 @@ +#include "lxqttaskbardummybackend.h" + +#include + +LXQtTaskBarDummyBackend::LXQtTaskBarDummyBackend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskBarDummyBackend::supportsAction(WId, LXQtTaskBarBackendAction) const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::reloadWindows() +{ + return false; +} + +QVector LXQtTaskBarDummyBackend::getCurrentWindows() const +{ + return {}; +} + +QString LXQtTaskBarDummyBackend::getWindowTitle(WId) const +{ + return QString(); +} + +bool LXQtTaskBarDummyBackend::applicationDemandsAttention(WId) const +{ + return false; +} + +QIcon LXQtTaskBarDummyBackend::getApplicationIcon(WId, int) const +{ + return QIcon(); +} + +QString LXQtTaskBarDummyBackend::getWindowClass(WId) const +{ + return QString(); +} + +LXQtTaskBarWindowLayer LXQtTaskBarDummyBackend::getWindowLayer(WId) const +{ + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskBarDummyBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) +{ + return false; +} + +LXQtTaskBarWindowState LXQtTaskBarDummyBackend::getWindowState(WId) const +{ + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskBarDummyBackend::setWindowState(WId, LXQtTaskBarWindowState, bool) +{ + return false; +} + +bool LXQtTaskBarDummyBackend::isWindowActive(WId) const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::raiseWindow(WId, bool) +{ + return false; +} + +bool LXQtTaskBarDummyBackend::closeWindow(WId) +{ + return false; +} + +WId LXQtTaskBarDummyBackend::getActiveWindow() const +{ + return 0; +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskBarDummyBackend::getWorkspacesCount() const +{ + return 1; // Fake 1 workspace +} + +QString LXQtTaskBarDummyBackend::getWorkspaceName(int) const +{ + return QString(); +} + +int LXQtTaskBarDummyBackend::getCurrentWorkspace() const +{ + return 0; +} + +bool LXQtTaskBarDummyBackend::setCurrentWorkspace(int) +{ + return false; +} + +int LXQtTaskBarDummyBackend::getWindowWorkspace(WId) const +{ + return 0; +} + +bool LXQtTaskBarDummyBackend::setWindowOnWorkspace(WId, int) +{ + return false; +} + +void LXQtTaskBarDummyBackend::moveApplicationToPrevNextMonitor(WId, bool, bool) +{ + //No-op +} + +bool LXQtTaskBarDummyBackend::isWindowOnScreen(QScreen *, WId) const +{ + return false; +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskBarDummyBackend::moveApplication(WId) +{ + //No-op +} + +void LXQtTaskBarDummyBackend::resizeApplication(WId) +{ + //No-op +} + +void LXQtTaskBarDummyBackend::refreshIconGeometry(WId, QRect const &) +{ + //No-op +} + +bool LXQtTaskBarDummyBackend::isAreaOverlapped(const QRect &) const +{ + return false; +} + diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h new file mode 100644 index 000000000..535de303b --- /dev/null +++ b/panel/backends/lxqttaskbardummybackend.h @@ -0,0 +1,82 @@ +#ifndef LXQTTASKBARDUMMYBACKEND_H +#define LXQTTASKBARDUMMYBACKEND_H + +#include "ilxqttaskbarabstractbackend.h" + +class LXQtTaskBarDummyBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskBarDummyBackend(QObject *parent = nullptr); + + // Backend + bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + bool reloadWindows() override; + + QVector getCurrentWindows() const override; + + QString getWindowTitle(WId windowId) const override; + + bool applicationDemandsAttention(WId windowId) const override; + + QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const override; + + QString getWindowClass(WId windowId) const override; + + LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + LXQtTaskBarWindowState getWindowState(WId windowId) const override; + bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) override; + + bool isWindowActive(WId windowId) const override; + bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + bool closeWindow(WId windowId) override; + + WId getActiveWindow() const override; + + // Workspaces + int getWorkspacesCount() const override; + QString getWorkspaceName(int idx) const override; + + int getCurrentWorkspace() const override; + bool setCurrentWorkspace(int idx) override; + + int getWindowWorkspace(WId windowId) const override; + bool setWindowOnWorkspace(WId windowId, int idx) override; + + void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + void moveApplication(WId windowId) override; + void resizeApplication(WId windowId) override; + + void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + bool isAreaOverlapped(const QRect& area) const override; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; + +#endif // LXQTTASKBARDUMMYBACKEND_H diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 2b1980d85..c3b666620 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -38,6 +38,7 @@ #include #include +#include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" ILXQtTaskbarAbstractBackend *createWMBackend() @@ -45,8 +46,13 @@ ILXQtTaskbarAbstractBackend *createWMBackend() if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; - Q_ASSERT_X(false, "createWMBackend()", "Only X11 supported!"); - return nullptr; + qWarning() << "\n" + << "ERROR: Could not create a backend for window managment operations.\n" + << "Only X11 supported!\n" + << "Falling back to dummy backend. Some functions will not be available.\n" + << "\n"; + + return new LXQtTaskBarDummyBackend; } LXQtPanelApplicationPrivate::LXQtPanelApplicationPrivate(LXQtPanelApplication *q) From a3b7c022e8808102f05cd49ec582d0c96b50fe1e Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 28 Feb 2024 20:32:04 +0100 Subject: [PATCH 28/43] LXQtMainMenu: indent header include --- plugin-mainmenu/lxqtmainmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-mainmenu/lxqtmainmenu.cpp b/plugin-mainmenu/lxqtmainmenu.cpp index 1db7f911f..0f55cdfbd 100644 --- a/plugin-mainmenu/lxqtmainmenu.cpp +++ b/plugin-mainmenu/lxqtmainmenu.cpp @@ -54,7 +54,7 @@ #include #include #include -#include + #include #endif #define DEFAULT_SHORTCUT "Alt+F1" From b199b7716178bf8fc076905c44d8fe44507ecdb9 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:03 +0100 Subject: [PATCH 29/43] ILXQtTaskbarAbstractBackend: new showDesktop() function --- panel/backends/ilxqttaskbarabstractbackend.h | 8 ++++++++ panel/backends/lxqttaskbardummybackend.cpp | 10 ++++++++++ panel/backends/lxqttaskbardummybackend.h | 4 ++++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 11 +++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.h | 4 ++++ 5 files changed, 37 insertions(+) diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index fb49d7b52..7daecfe60 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -68,6 +68,14 @@ class ILXQtTaskbarAbstractBackend : public QObject // Panel internal virtual bool isAreaOverlapped(const QRect& area) const = 0; + // Show Destop TODO: split in multiple interfeces, this is becoming big + // NOTE: KWindowSystem already has these functions + // However on Wayland they are only compatible with KWin + // because internally it uses org_kde_plasma_window_management protocol + // We make this virtual so it can be implemented also for other compositors + virtual bool isShowingDesktop() const = 0; + virtual bool showDesktop(bool value) = 0; + signals: void reloaded(); diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp index f91fa75bf..ceaf3c334 100644 --- a/panel/backends/lxqttaskbardummybackend.cpp +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -154,3 +154,13 @@ bool LXQtTaskBarDummyBackend::isAreaOverlapped(const QRect &) const return false; } +bool LXQtTaskBarDummyBackend::isShowingDesktop() const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::showDesktop(bool) +{ + return false; +} + diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h index 535de303b..d4e1ca4bc 100644 --- a/panel/backends/lxqttaskbardummybackend.h +++ b/panel/backends/lxqttaskbardummybackend.h @@ -62,6 +62,10 @@ class LXQtTaskBarDummyBackend : public ILXQtTaskbarAbstractBackend // Panel internal bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + bool isShowingDesktop() const override; + bool showDesktop(bool value) override; + signals: void reloaded(); diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index 1d29148c8..822afed51 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -617,3 +617,14 @@ bool LXQtTaskbarX11Backend::isAreaOverlapped(const QRect &area) const } return false; } + +bool LXQtTaskbarX11Backend::isShowingDesktop() const +{ + return KWindowSystem::showingDesktop(); +} + +bool LXQtTaskbarX11Backend::showDesktop(bool value) +{ + KWindowSystem::setShowingDesktop(value); + return true; +} diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 42e84b146..8106cda9f 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -64,6 +64,10 @@ class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend // Panel internal virtual bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + private slots: void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); void onWindowAdded(WId windowId); From f59269ce897466ed23cd65237d466f8f9f6e0899 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:28 +0100 Subject: [PATCH 30/43] ShowDesktop: use ILXQtTaskbarAbstractBackend --- plugin-showdesktop/showdesktop.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugin-showdesktop/showdesktop.cpp b/plugin-showdesktop/showdesktop.cpp index 871da62ea..fb69f6067 100644 --- a/plugin-showdesktop/showdesktop.cpp +++ b/plugin-showdesktop/showdesktop.cpp @@ -29,11 +29,12 @@ #include #include #include -#include -#include #include "showdesktop.h" #include "../panel/pluginsettings.h" +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" + #define DEFAULT_SHORTCUT "Control+Alt+D" ShowDesktop::ShowDesktop(const ILXQtPanelPluginStartupInfo &startupInfo) : @@ -69,7 +70,9 @@ void ShowDesktop::shortcutRegistered() void ShowDesktop::toggleShowingDesktop() { - KWindowSystem::setShowingDesktop(!KWindowSystem::showingDesktop()); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + wmBackend->showDesktop(!wmBackend->isShowingDesktop()); } #undef DEFAULT_SHORTCUT From f1a26d4138398a841ee526e13cae825b6bb280f8 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:27:28 +0100 Subject: [PATCH 31/43] DesktopSwitch: port to ILXQtTaskbarAbstractBackend - Clarify desktop index range --- .../backends/ilxqttaskbarabstractbackend.cpp | 2 +- panel/backends/ilxqttaskbarabstractbackend.h | 1 + plugin-desktopswitch/desktopswitch.cpp | 141 ++++++++---------- plugin-desktopswitch/desktopswitch.h | 34 ++--- 4 files changed, 79 insertions(+), 99 deletions(-) diff --git a/panel/backends/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp index acf33f464..137728263 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -17,7 +17,7 @@ void ILXQtTaskbarAbstractBackend::moveApplicationToPrevNextDesktop(WId windowId, // Wrap around if (targetWorkspace > count) - targetWorkspace = 1; //TODO: are X11 desktops 1 based? + targetWorkspace = 1; //Ids are 1-based else if (targetWorkspace < 1) targetWorkspace = count; diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index 7daecfe60..fe3368363 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -45,6 +45,7 @@ class ILXQtTaskbarAbstractBackend : public QObject virtual WId getActiveWindow() const = 0; // Workspaces + // NOTE: indexes are 1-based, 0 means "Show on All desktops" virtual int getWorkspacesCount() const = 0; virtual QString getWorkspaceName(int idx) const = 0; diff --git a/plugin-desktopswitch/desktopswitch.cpp b/plugin-desktopswitch/desktopswitch.cpp index 4bd05e08f..67f6e56eb 100644 --- a/plugin-desktopswitch/desktopswitch.cpp +++ b/plugin-desktopswitch/desktopswitch.cpp @@ -25,6 +25,7 @@ * * END_COMMON_COPYRIGHT_HEADER */ +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" #include @@ -40,29 +43,22 @@ #include "desktopswitchbutton.h" #include "desktopswitchconfiguration.h" -#include -#include -#include - -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool - static const QString DEFAULT_SHORTCUT_TEMPLATE(QStringLiteral("Control+F%1")); DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) : QObject(), ILXQtPanelPlugin(startupInfo), m_pSignalMapper(new QSignalMapper(this)), - m_desktopCount(KX11Extras::numberOfDesktops()), + m_desktopCount(0), mRows(-1), mShowOnlyActive(false), - mDesktops(nullptr), mLabelType(static_cast(-1)) { - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - mDesktops.reset(new NETRootInfo(x11Application->connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopNames, NET::WM2DesktopLayout)); + LXQtPanelApplication *a = reinterpret_cast(qApp); + mBackend = a->getWMBackend(); + + + m_desktopCount = mBackend->getWorkspacesCount(); m_buttons = new QButtonGroup(this); @@ -74,17 +70,16 @@ DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) : settingsChanged(); - onCurrentDesktopChanged(KX11Extras::currentDesktop()); + onCurrentDesktopChanged(mBackend->getCurrentWorkspace()); QTimer::singleShot(0, this, SLOT(registerShortcuts())); connect(m_buttons, &QButtonGroup::idClicked, this, &DesktopSwitch::setDesktop); - connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &DesktopSwitch::onNumberOfDesktopsChanged); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &DesktopSwitch::onCurrentDesktopChanged); - connect(KX11Extras::self(), &KX11Extras::desktopNamesChanged, this, &DesktopSwitch::onDesktopNamesChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::workspacesCountChanged, this, &DesktopSwitch::onNumberOfDesktopsChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &DesktopSwitch::onCurrentDesktopChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::workspaceNameChanged, this, &DesktopSwitch::onDesktopNamesChanged); - connect(KX11Extras::self(), static_cast(&KX11Extras::windowChanged), - this, &DesktopSwitch::onWindowChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &DesktopSwitch::onWindowChanged); } void DesktopSwitch::registerShortcuts() @@ -126,19 +121,18 @@ void DesktopSwitch::shortcutRegistered() } } -void DesktopSwitch::onWindowChanged(WId id, NET::Properties properties, NET::Properties2 /*properties2*/) +void DesktopSwitch::onWindowChanged(WId id, int prop) { - if (properties.testFlag(NET::WMState) && isWindowHighlightable(id)) + if (prop == int(LXQtTaskBarWindowProperty::State) && isWindowHighlightable(id)) { - KWindowInfo info = KWindowInfo(id, NET::WMDesktop | NET::WMState); - int desktop = info.desktop(); - if (!info.valid() || info.onAllDesktops()) + int desktop = mBackend->getWindowWorkspace(id); + if (desktop == int(LXQtTaskBarWorkspace::ShowOnAll)) return; else { DesktopSwitchButton *button = static_cast(m_buttons->button(desktop - 1)); if(button) - button->setUrgencyHint(id, info.hasState(NET::DemandsAttention)); + button->setUrgencyHint(id, mBackend->applicationDemandsAttention(id)); } } } @@ -148,7 +142,7 @@ void DesktopSwitch::refresh() const QList btns = m_buttons->buttons(); int i = 0; - const int current_desktop = KX11Extras::currentDesktop(); + const int current_desktop = mBackend->getCurrentWorkspace(); const int current_cnt = btns.count(); const int border = qMin(btns.count(), m_desktopCount); //update existing buttons @@ -156,9 +150,9 @@ void DesktopSwitch::refresh() { DesktopSwitchButton * button = qobject_cast(btns[i]); button->update(i, mLabelType, - KX11Extras::desktopName(i + 1).isEmpty() ? + mBackend->getWorkspaceName(i + 1).isEmpty() ? tr("Desktop %1").arg(i + 1) : - KX11Extras::desktopName(i + 1)); + mBackend->getWorkspaceName(i + 1)); button->setVisible(!mShowOnlyActive || i + 1 == current_desktop); } @@ -167,9 +161,9 @@ void DesktopSwitch::refresh() for ( ; i < m_desktopCount; ++i) { b = new DesktopSwitchButton(&mWidget, i, mLabelType, - KX11Extras::desktopName(i+1).isEmpty() ? + mBackend->getWorkspaceName(i+1).isEmpty() ? tr("Desktop %1").arg(i+1) : - KX11Extras::desktopName(i+1)); + mBackend->getWorkspaceName(i+1)); mWidget.layout()->addWidget(b); m_buttons->addButton(b, i); b->setVisible(!mShowOnlyActive || i + 1 == current_desktop); @@ -185,56 +179,23 @@ void DesktopSwitch::refresh() } } -bool DesktopSwitch::isWindowHighlightable(WId window) +bool DesktopSwitch::isWindowHighlightable(WId) { - // this method was borrowed from the taskbar plugin - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::ToolbarMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::NotificationMask; - - KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); - if (!info.valid()) - return false; - - if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - return false; - - if (info.state() & NET::SkipTaskbar) - return false; - - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - - // WM_TRANSIENT_FOR hint not set - normal window - WId transFor = info.transientFor(); - if (transFor == 0 || transFor == window || transFor == appRootWindow) - return true; - - info = KWindowInfo(transFor, NET::WMWindowType); - - QFlags normalFlag; - normalFlag |= NET::NormalMask; - normalFlag |= NET::DialogMask; - normalFlag |= NET::UtilityMask; - - return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); + // Backend should emit signals only for higlightable windows and ignore others + // TODO: check + return true; } DesktopSwitch::~DesktopSwitch() = default; void DesktopSwitch::setDesktop(int desktop) { - KX11Extras::setCurrentDesktop(desktop + 1); + mBackend->setCurrentWorkspace(desktop + 1); } -void DesktopSwitch::onNumberOfDesktopsChanged(int count) +void DesktopSwitch::onNumberOfDesktopsChanged() { + int count = mBackend->getWorkspacesCount(); qDebug() << "Desktop count changed from" << m_desktopCount << "to" << count; m_desktopCount = count; refresh(); @@ -293,11 +254,11 @@ void DesktopSwitch::settingsChanged() int columns = static_cast(ceil(static_cast(m_desktopCount) / mRows)); if (panel()->isHorizontal()) { - mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + //mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); } else { - mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + //mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); } realign(); // in case it isn't called when the desktop layout changes } @@ -345,9 +306,12 @@ void DesktopSwitchWidget::wheelEvent(QWheelEvent *e) if(abs(m_mouseWheelThresholdCounter) < 100) return; - int max = KX11Extras::numberOfDesktops(); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + int max = wmBackend->getWorkspacesCount(); int delta = rotationSteps < 0 ? 1 : -1; - int current = KX11Extras::currentDesktop() + delta; + int current = wmBackend->getCurrentWorkspace() + delta; if (current > max){ current = 1; @@ -356,5 +320,32 @@ void DesktopSwitchWidget::wheelEvent(QWheelEvent *e) current = max; m_mouseWheelThresholdCounter = 0; - KX11Extras::setCurrentDesktop(current); + wmBackend->setCurrentWorkspace(current); +} + +ILXQtPanelPlugin *DesktopSwitchPluginLibrary::instance(const ILXQtPanelPluginStartupInfo &startupInfo) const +{ + return new DesktopSwitch{startupInfo}; + + //TODO: detect dummy backend and show unsupported message? + // Or instead remove it and make just a message box at application start + //return new DesktopSwitchUnsupported{startupInfo}; +} + +DesktopSwitchUnsupported::DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo) + : ILXQtPanelPlugin(startupInfo) + , mLabel(new QLabel(tr("n/a"))) +{ + mLabel->setToolTip(tr("DesktopSwitch is unsupported on current platform: %1").arg(QGuiApplication::platformName())); +} + +DesktopSwitchUnsupported::~DesktopSwitchUnsupported() +{ + delete mLabel; + mLabel = nullptr; +} + +QWidget *DesktopSwitchUnsupported::widget() +{ + return mLabel; } diff --git a/plugin-desktopswitch/desktopswitch.h b/plugin-desktopswitch/desktopswitch.h index 93a930dc2..b57b44c32 100644 --- a/plugin-desktopswitch/desktopswitch.h +++ b/plugin-desktopswitch/desktopswitch.h @@ -30,21 +30,19 @@ #define DESKTOPSWITCH_H #include "../panel/ilxqtpanelplugin.h" -#include #include -#include -#include -#include #include "desktopswitchbutton.h" +class QLabel; class QSignalMapper; class QButtonGroup; -class NETRootInfo; namespace LXQt { class GridLayout; } +class ILXQtTaskbarAbstractBackend; + class DesktopSwitchWidget: public QFrame { Q_OBJECT @@ -85,7 +83,7 @@ class DesktopSwitch : public QObject, public ILXQtPanelPlugin LXQt::GridLayout *mLayout; int mRows; bool mShowOnlyActive; - std::unique_ptr mDesktops; + ILXQtTaskbarAbstractBackend *mBackend; DesktopSwitchButton::LabelType mLabelType; void refresh(); @@ -93,31 +91,27 @@ class DesktopSwitch : public QObject, public ILXQtPanelPlugin private slots: void setDesktop(int desktop); - void onNumberOfDesktopsChanged(int); + void onNumberOfDesktopsChanged(); void onCurrentDesktopChanged(int); void onDesktopNamesChanged(); virtual void settingsChanged(); void registerShortcuts(); void shortcutRegistered(); - void onWindowChanged(WId id, NET::Properties properties, NET::Properties2 properties2); + void onWindowChanged(WId id, int prop); }; class DesktopSwitchUnsupported : public QObject, public ILXQtPanelPlugin { Q_OBJECT public: - DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo) - : ILXQtPanelPlugin(startupInfo) - , mLabel{tr("n/a")} - { - mLabel.setToolTip(tr("DesktopSwitch is unsupported on current platform: %1").arg(QGuiApplication::platformName())); - } + DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo); + ~DesktopSwitchUnsupported(); QString themeId() const { return QStringLiteral("DesktopSwitchUnsupported"); } - QWidget *widget() { return &mLabel; } + QWidget *widget(); bool isSeparate() const { return true; } private: - QLabel mLabel; + QLabel *mLabel; }; class DesktopSwitchPluginLibrary: public QObject, public ILXQtPanelPluginLibrary @@ -126,13 +120,7 @@ class DesktopSwitchPluginLibrary: public QObject, public ILXQtPanelPluginLibrary // Q_PLUGIN_METADATA(IID "lxqt.org/Panel/PluginInterface/3.0") Q_INTERFACES(ILXQtPanelPluginLibrary) public: - ILXQtPanelPlugin *instance(const ILXQtPanelPluginStartupInfo &startupInfo) const - { - if (QGuiApplication::platformName() == QStringLiteral("xcb")) - return new DesktopSwitch{startupInfo}; - else - return new DesktopSwitchUnsupported{startupInfo}; - } + ILXQtPanelPlugin *instance(const ILXQtPanelPluginStartupInfo &startupInfo) const; }; #endif From 72a75e04c0d821ed076ab93b9fd394498b1baecf Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:39:46 +0100 Subject: [PATCH 32/43] LXQtTaskbarConfiguration: port to ILXQtTaskBarAbstractBackend --- plugin-taskbar/lxqttaskbarconfiguration.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbarconfiguration.cpp b/plugin-taskbar/lxqttaskbarconfiguration.cpp index b346bb69d..0dd528e51 100644 --- a/plugin-taskbar/lxqttaskbarconfiguration.cpp +++ b/plugin-taskbar/lxqttaskbarconfiguration.cpp @@ -29,7 +29,9 @@ #include "lxqttaskbarconfiguration.h" #include "ui_lxqttaskbarconfiguration.h" -#include + +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" LXQtTaskbarConfiguration::LXQtTaskbarConfiguration(PluginSettings *settings, QWidget *parent): LXQtPanelPluginConfigDialog(settings, parent), @@ -52,11 +54,14 @@ LXQtTaskbarConfiguration::LXQtTaskbarConfiguration(PluginSettings *settings, QWi ui->wheelEventsActionCB->addItem(tr("Scroll up to move to next desktop, down to previous"), 4); ui->wheelEventsActionCB->addItem(tr("Scroll up to move to previous desktop, down to next"), 5); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + ui->showDesktopNumCB->addItem(tr("Current"), 0); //Note: in KWindowSystem desktops are numbered from 1..N - const int desk_cnt = KX11Extras::numberOfDesktops(); + const int desk_cnt = wmBackend->getWorkspacesCount(); for (int i = 1; desk_cnt >= i; ++i) - ui->showDesktopNumCB->addItem(QString(QStringLiteral("%1 - %2")).arg(i).arg(KX11Extras::desktopName(i)), i); + ui->showDesktopNumCB->addItem(QString(QStringLiteral("%1 - %2")).arg(i).arg(wmBackend->getWorkspaceName(i)), i); loadSettings(); ui->ungroupedNextToExistingCB->setEnabled(!(ui->groupingGB->isChecked())); From 05fc7422203a2d12c5869390bb645627132d6cd5 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 14:07:05 +0100 Subject: [PATCH 33/43] DesktopSwitchConfiguration: port to ILXQtTaskBarAbstractBackend TODO TODO: this will disable changing desktop names --- .../desktopswitchconfiguration.cpp | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/plugin-desktopswitch/desktopswitchconfiguration.cpp b/plugin-desktopswitch/desktopswitchconfiguration.cpp index 943dbdd6b..a6edb41a6 100644 --- a/plugin-desktopswitch/desktopswitchconfiguration.cpp +++ b/plugin-desktopswitch/desktopswitchconfiguration.cpp @@ -26,8 +26,9 @@ #include "desktopswitchconfiguration.h" #include "ui_desktopswitchconfiguration.h" -#include -#include + +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" DesktopSwitchConfiguration::DesktopSwitchConfiguration(PluginSettings *settings, QWidget *parent) : LXQtPanelPluginConfigDialog(settings, parent), @@ -64,18 +65,25 @@ void DesktopSwitchConfiguration::loadSettings() void DesktopSwitchConfiguration::loadDesktopsNames() { - int n = KX11Extras::numberOfDesktops(); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + int n = wmBackend->getWorkspacesCount(); for (int i = 1; i <= n; i++) { - QLineEdit *edit = new QLineEdit(KX11Extras::desktopName(i), this); + QLineEdit *edit = new QLineEdit(wmBackend->getWorkspaceName(i), this); ((QFormLayout *) ui->namesGroupBox->layout())->addRow(tr("Desktop %1:").arg(i), edit); + //TODO: on Wayland we cannot set desktop names in a standart way + // On KWin we could use DBus org.kde.KWin as done by kcm_kwin_virtualdesktops + edit->setReadOnly(true); + // C++11 rocks! - QTimer *timer = new QTimer(this); - timer->setInterval(400); - timer->setSingleShot(true); - connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); - connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); + //QTimer *timer = new QTimer(this); + //timer->setInterval(400); + //timer->setSingleShot(true); + //connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); + //connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); } } From 8b78adda8784fe52e3d5a4154cf71aef71f018ab Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:41:16 +0100 Subject: [PATCH 34/43] TaskBar: consider initial windows --- plugin-taskbar/lxqttaskbar.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index c306d796e..43090c396 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -107,6 +107,13 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); + + // Consider already fetched windows + const auto initialWindows = mBackend->getCurrentWindows(); + for(WId windowId : initialWindows) + { + onWindowAdded(windowId); + } } /************************************************ From 099082925e01f7c7752d0a62560a6b5a585c3504 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 10 May 2024 10:30:00 +0200 Subject: [PATCH 35/43] DesktopSwitch: remove unused isWindowHighlightable() --- plugin-desktopswitch/desktopswitch.cpp | 9 +-------- plugin-desktopswitch/desktopswitch.h | 1 - 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/plugin-desktopswitch/desktopswitch.cpp b/plugin-desktopswitch/desktopswitch.cpp index 67f6e56eb..8f46fd271 100644 --- a/plugin-desktopswitch/desktopswitch.cpp +++ b/plugin-desktopswitch/desktopswitch.cpp @@ -123,7 +123,7 @@ void DesktopSwitch::shortcutRegistered() void DesktopSwitch::onWindowChanged(WId id, int prop) { - if (prop == int(LXQtTaskBarWindowProperty::State) && isWindowHighlightable(id)) + if (prop == int(LXQtTaskBarWindowProperty::State)) { int desktop = mBackend->getWindowWorkspace(id); if (desktop == int(LXQtTaskBarWorkspace::ShowOnAll)) @@ -179,13 +179,6 @@ void DesktopSwitch::refresh() } } -bool DesktopSwitch::isWindowHighlightable(WId) -{ - // Backend should emit signals only for higlightable windows and ignore others - // TODO: check - return true; -} - DesktopSwitch::~DesktopSwitch() = default; void DesktopSwitch::setDesktop(int desktop) diff --git a/plugin-desktopswitch/desktopswitch.h b/plugin-desktopswitch/desktopswitch.h index b57b44c32..182eb0ced 100644 --- a/plugin-desktopswitch/desktopswitch.h +++ b/plugin-desktopswitch/desktopswitch.h @@ -87,7 +87,6 @@ class DesktopSwitch : public QObject, public ILXQtPanelPlugin DesktopSwitchButton::LabelType mLabelType; void refresh(); - bool isWindowHighlightable(WId window); private slots: void setDesktop(int desktop); From 1b7dadcbf0c26f4371389400e35bcfcdbae41e17 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 10 May 2024 10:36:01 +0200 Subject: [PATCH 36/43] DesktopSwitch: show unsupported widget if backend does not have switch capability --- panel/backends/lxqttaskbartypes.h | 3 ++- panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 3 +++ plugin-desktopswitch/desktopswitch.cpp | 9 +++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/panel/backends/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h index 9e12092bb..656591fbc 100644 --- a/panel/backends/lxqttaskbartypes.h +++ b/panel/backends/lxqttaskbartypes.h @@ -14,7 +14,8 @@ enum class LXQtTaskBarBackendAction MaximizeHorizontally, Minimize, RollUp, - FullScreen + FullScreen, + DesktopSwitch }; enum class LXQtTaskBarWindowProperty diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index 822afed51..53ae6e5b0 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -202,6 +202,9 @@ bool LXQtTaskbarX11Backend::supportsAction(WId windowId, LXQtTaskBarBackendActio x11Action = NET::ActionFullScreen; break; + case LXQtTaskBarBackendAction::DesktopSwitch: + return true; + default: return false; } diff --git a/plugin-desktopswitch/desktopswitch.cpp b/plugin-desktopswitch/desktopswitch.cpp index 8f46fd271..9484dc82e 100644 --- a/plugin-desktopswitch/desktopswitch.cpp +++ b/plugin-desktopswitch/desktopswitch.cpp @@ -318,11 +318,12 @@ void DesktopSwitchWidget::wheelEvent(QWheelEvent *e) ILXQtPanelPlugin *DesktopSwitchPluginLibrary::instance(const ILXQtPanelPluginStartupInfo &startupInfo) const { - return new DesktopSwitch{startupInfo}; + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a ? a->getWMBackend() : nullptr; + if(!wmBackend || !wmBackend->supportsAction(0, LXQtTaskBarBackendAction::DesktopSwitch)) + return new DesktopSwitchUnsupported{startupInfo}; - //TODO: detect dummy backend and show unsupported message? - // Or instead remove it and make just a message box at application start - //return new DesktopSwitchUnsupported{startupInfo}; + return new DesktopSwitch{startupInfo}; } DesktopSwitchUnsupported::DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo) From 79ace78aae03adbc86117ead8b8348e213e04f10 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 10 May 2024 10:48:29 +0200 Subject: [PATCH 37/43] ILXQtTaskbarAbstractBackend: add setDesktopLayout() function - Use it in DesktopSwitch::settingsChanged() --- panel/backends/ilxqttaskbarabstractbackend.h | 2 ++ panel/backends/lxqttaskbardummybackend.cpp | 5 +++++ panel/backends/lxqttaskbardummybackend.h | 2 ++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 19 +++++++++++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.h | 2 ++ plugin-desktopswitch/desktopswitch.cpp | 11 +++-------- 6 files changed, 33 insertions(+), 8 deletions(-) diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index fe3368363..44840671a 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -60,6 +60,8 @@ class ILXQtTaskbarAbstractBackend : public QObject virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const = 0; + virtual bool setDesktopLayout(Qt::Orientation orientation, int rows, int columns, bool rightToLeft) = 0; + // X11 Specific virtual void moveApplication(WId windowId) = 0; virtual void resizeApplication(WId windowId) = 0; diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp index ceaf3c334..79671253b 100644 --- a/panel/backends/lxqttaskbardummybackend.cpp +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -131,6 +131,11 @@ bool LXQtTaskBarDummyBackend::isWindowOnScreen(QScreen *, WId) const return false; } +bool setDesktopLayout(Qt::Orientation, int, int, bool) +{ + return false; +} + /************************************************ * X11 Specific ************************************************/ diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h index d4e1ca4bc..15506838f 100644 --- a/panel/backends/lxqttaskbardummybackend.h +++ b/panel/backends/lxqttaskbardummybackend.h @@ -53,6 +53,8 @@ class LXQtTaskBarDummyBackend : public ILXQtTaskbarAbstractBackend bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + virtual bool setDesktopLayout(Qt::Orientation orientation, int rows, int columns, bool rightToLeft) override; + // X11 Specific void moveApplication(WId windowId) override; void resizeApplication(WId windowId) override; diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index 53ae6e5b0..d09de3113 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -520,6 +520,25 @@ bool LXQtTaskbarX11Backend::isWindowOnScreen(QScreen *screen, WId windowId) cons return screen->geometry().intersects(r); } +bool LXQtTaskbarX11Backend::setDesktopLayout(Qt::Orientation orientation, int rows, int columns, bool rightToLeft) +{ + NETRootInfo mDesktops(m_xcbConnection, NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopNames, NET::WM2DesktopLayout); + + if (orientation == Qt::Horizontal) + { + mDesktops.setDesktopLayout(NET::OrientationHorizontal, + columns, rows, + rightToLeft ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + } + else + { + mDesktops.setDesktopLayout(NET::OrientationHorizontal, + rows, columns, + rightToLeft ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + } + return true; +} + /************************************************ * X11 Specific ************************************************/ diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 8106cda9f..2478b3fff 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -55,6 +55,8 @@ class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + virtual bool setDesktopLayout(Qt::Orientation orientation, int rows, int columns, bool rightToLeft) override; + // X11 Specific virtual void moveApplication(WId windowId) override; virtual void resizeApplication(WId windowId) override; diff --git a/plugin-desktopswitch/desktopswitch.cpp b/plugin-desktopswitch/desktopswitch.cpp index 9484dc82e..435250ba8 100644 --- a/plugin-desktopswitch/desktopswitch.cpp +++ b/plugin-desktopswitch/desktopswitch.cpp @@ -245,14 +245,9 @@ void DesktopSwitch::settingsChanged() // "DesktopSwitch::realign()". Therefore, the desktop layout should not be changed // inside the latter method. int columns = static_cast(ceil(static_cast(m_desktopCount) / mRows)); - if (panel()->isHorizontal()) - { - //mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); - } - else - { - //mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); - } + mBackend->setDesktopLayout(panel()->isHorizontal() ? Qt::Horizontal : Qt::Vertical, + mRows, columns, mWidget.isRightToLeft()); + realign(); // in case it isn't called when the desktop layout changes } if (need_refresh) From 5e8966ed39a3bbfe1be7b265c5012ccf893b81de Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 10 May 2024 10:57:50 +0200 Subject: [PATCH 38/43] LXQtTaskbarX11Backend: fix re-add window after property changes --- panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index d09de3113..fe0452776 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -40,10 +40,18 @@ LXQtTaskbarX11Backend::LXQtTaskbarX11Backend(QObject *parent) void LXQtTaskbarX11Backend::onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2) { if(!m_windows.contains(windowId)) + { + // If already known window changes its property in a way + // it's now acceptable, add it again to taskbar + if(acceptWindow(windowId)) + onWindowAdded(windowId); return; + } if(!acceptWindow(windowId)) { + // If already known window changes its property in a way + // it's not anymore accepted, remove it from taskbar onWindowRemoved(windowId); return; } From 0e79998453421c8efc29609f179de19a738d8d15 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 10 May 2024 15:00:38 +0200 Subject: [PATCH 39/43] LXQtTaskBarDummyBackend: fix compilation --- panel/backends/lxqttaskbardummybackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp index 79671253b..15e7e1149 100644 --- a/panel/backends/lxqttaskbardummybackend.cpp +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -131,7 +131,7 @@ bool LXQtTaskBarDummyBackend::isWindowOnScreen(QScreen *, WId) const return false; } -bool setDesktopLayout(Qt::Orientation, int, int, bool) +bool LXQtTaskBarDummyBackend::setDesktopLayout(Qt::Orientation, int, int, bool) { return false; } From 09f006f6dc0fa7905a843bd219131c6e79e86665 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 31 May 2024 12:35:41 +0200 Subject: [PATCH 40/43] LXQtTaskBarPlugin: fix headers path --- plugin-taskbar/lxqttaskbar.cpp | 4 ++-- plugin-taskbar/lxqttaskbarplugin.h | 2 +- plugin-taskbar/lxqttaskbutton.cpp | 2 +- plugin-taskbar/lxqttaskgroup.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 43090c396..fb41ae7e0 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -41,8 +41,8 @@ #include #include -#include "ilxqtpanelplugin.h" -#include "pluginsettings.h" +#include "../panel/ilxqtpanelplugin.h" +#include "../panel/pluginsettings.h" #include #include diff --git a/plugin-taskbar/lxqttaskbarplugin.h b/plugin-taskbar/lxqttaskbarplugin.h index 9bc34cf8e..8566b7c2b 100644 --- a/plugin-taskbar/lxqttaskbarplugin.h +++ b/plugin-taskbar/lxqttaskbarplugin.h @@ -29,7 +29,7 @@ #ifndef LXQTTASKBARPLUGIN_H #define LXQTTASKBARPLUGIN_H -#include "ilxqtpanelplugin.h" +#include "../panel/ilxqtpanelplugin.h" class LXQtTaskBar; diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 019b45199..c86e6af5f 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -30,7 +30,7 @@ #include "lxqttaskbutton.h" #include "lxqttaskbar.h" -#include "ilxqtpanelplugin.h" +#include "../panel/ilxqtpanelplugin.h" #include diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index c6075df89..8317c034f 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -32,7 +32,7 @@ #include "lxqttaskbar.h" #include "lxqtgrouppopup.h" -#include "ilxqtpanelplugin.h" +#include "../panel/ilxqtpanelplugin.h" #include #include From 3fe36aff02d6fe178c14e723a5bc9e568715929d Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 31 May 2024 12:41:15 +0200 Subject: [PATCH 41/43] Revert "LXQtPanel: rework calculatePopupWindowPos()" This reverts commit b0e54b87c7a9800cf9786b97916b48e9eb0c4c23. --- panel/lxqtpanel.cpp | 56 ++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index 3735bd068..f4144ff41 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -1416,60 +1416,48 @@ Plugin* LXQtPanel::findPlugin(const ILXQtPanelPlugin* iPlugin) const ************************************************/ QRect LXQtPanel::calculatePopupWindowPos(QPoint const & absolutePos, QSize const & windowSize) const { - QPoint localPos = mapFromGlobal(absolutePos); - int x = localPos.x(), y = localPos.y(); + int x = absolutePos.x(), y = absolutePos.y(); switch (position()) { case ILXQtPanel::PositionTop: - y = geometry().height(); + y = mGeometry.bottom(); break; case ILXQtPanel::PositionBottom: - y = 0 - windowSize.height(); + y = mGeometry.top() - windowSize.height(); break; case ILXQtPanel::PositionLeft: - x = geometry().right(); + x = mGeometry.right(); break; case ILXQtPanel::PositionRight: - x = geometry().left() - windowSize.width(); + x = mGeometry.left() - windowSize.width(); break; } QRect res(QPoint(x, y), windowSize); - // Map to global coordinates - res = QRect(mapToGlobal(res.topLeft()), mapToGlobal(res.bottomRight())); - - if(qGuiApp->nativeInterface()) - { - //On X11 we clamp rects inside screen area. - //NOTE: On Wayland it's done by compositor - - QRect panelScreen; - const auto screens = QApplication::screens(); - if (mActualScreenNum < screens.size()) - panelScreen = screens.at(mActualScreenNum)->geometry(); - - // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a - // multihead setup with different resolutions. In this case, the size of the work area is limited - // by the smallest monitor and may be much smaller than the current screen and we will place the - // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. - - if (res.right() > panelScreen.right()) - res.moveRight(panelScreen.right()); + QRect panelScreen; + const auto screens = QApplication::screens(); + if (mActualScreenNum < screens.size()) + panelScreen = screens.at(mActualScreenNum)->geometry(); + // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a + // multihead setup with different resolutions. In this case, the size of the work area is limited + // by the smallest monitor and may be much smaller than the current screen and we will place the + // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. + if (res.right() > panelScreen.right()) + res.moveRight(panelScreen.right()); - if (res.bottom() > panelScreen.bottom()) - res.moveBottom(panelScreen.bottom() - mGeometry.top()); + if (res.bottom() > panelScreen.bottom()) + res.moveBottom(panelScreen.bottom()); - if (res.left() < panelScreen.left()) - res.moveLeft(panelScreen.left()); + if (res.left() < panelScreen.left()) + res.moveLeft(panelScreen.left()); - if (res.top() < panelScreen.top()) - res.moveTop(panelScreen.top() - mGeometry.top()); - } + if (res.top() < panelScreen.top()) + res.moveTop(panelScreen.top()); return res; } @@ -1491,7 +1479,7 @@ QRect LXQtPanel::calculatePopupWindowPos(const ILXQtPanelPlugin *plugin, const Q } // Note: assuming there are not contentMargins around the "BackgroundWidget" (LXQtPanelWidget) - return calculatePopupWindowPos(mapToGlobal(panel_plugin->geometry().topLeft()), windowSize); + return calculatePopupWindowPos(mGeometry.topLeft() + panel_plugin->geometry().topLeft(), windowSize); } From 76dad956b9140fdce31a8895edb190905df84b6c Mon Sep 17 00:00:00 2001 From: Tsu Jan Date: Thu, 6 Jun 2024 17:33:32 +0330 Subject: [PATCH 42/43] Removed `mDesktops` after rebasing --- plugin-desktopswitch/desktopswitch.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin-desktopswitch/desktopswitch.cpp b/plugin-desktopswitch/desktopswitch.cpp index 9dd54cf24..ea2a0fce3 100644 --- a/plugin-desktopswitch/desktopswitch.cpp +++ b/plugin-desktopswitch/desktopswitch.cpp @@ -52,7 +52,6 @@ DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) : m_desktopCount(0), mRows(-1), mShowOnlyActive(false), - mDesktops(nullptr), mLabelType(DesktopSwitchButton::LABEL_TYPE_INVALID) { LXQtPanelApplication *a = reinterpret_cast(qApp); From 84ad2be362516e14dca6b61ee5171e326f277c3a Mon Sep 17 00:00:00 2001 From: Tsu Jan Date: Thu, 6 Jun 2024 19:32:31 +0330 Subject: [PATCH 43/43] Reverted X11 codes of `DesktopSwitchConfiguration::loadDesktopsNames` --- .../desktopswitchconfiguration.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/plugin-desktopswitch/desktopswitchconfiguration.cpp b/plugin-desktopswitch/desktopswitchconfiguration.cpp index a6edb41a6..33781f7d8 100644 --- a/plugin-desktopswitch/desktopswitchconfiguration.cpp +++ b/plugin-desktopswitch/desktopswitchconfiguration.cpp @@ -30,6 +30,9 @@ #include "../panel/lxqtpanelapplication.h" #include "../panel/backends/ilxqttaskbarabstractbackend.h" +#include +#include + DesktopSwitchConfiguration::DesktopSwitchConfiguration(PluginSettings *settings, QWidget *parent) : LXQtPanelPluginConfigDialog(settings, parent), ui(new Ui::DesktopSwitchConfiguration) @@ -76,14 +79,19 @@ void DesktopSwitchConfiguration::loadDesktopsNames() //TODO: on Wayland we cannot set desktop names in a standart way // On KWin we could use DBus org.kde.KWin as done by kcm_kwin_virtualdesktops - edit->setReadOnly(true); - - // C++11 rocks! - //QTimer *timer = new QTimer(this); - //timer->setInterval(400); - //timer->setSingleShot(true); - //connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); - //connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); + if(qGuiApp->nativeInterface()) + { + // C++11 rocks! + QTimer *timer = new QTimer(this); + timer->setInterval(400); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); + connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); + } + else + { + edit->setReadOnly(true); + } } }