1
0
mirror of https://github.com/KDE/latte-dock.git synced 2024-12-24 17:33:50 +03:00

REFACTOR:Introduce WM:WindowsTracker

--this is a single and very important class
that handles all windows tracking and management
for ALL VIEWS at ALL ACTIVE LAYOUTS
This commit is contained in:
Michail Vourlakos 2019-05-11 21:10:02 +03:00
parent 945c6e50e2
commit a5168c3a2a
7 changed files with 679 additions and 406 deletions

View File

@ -25,6 +25,7 @@
#include "../lattecorona.h"
#include "../layouts/manager.h"
#include "../wm/schemecolors.h"
#include "../wm/windowstracker.h"
#include "../../liblatte2/types.h"
namespace Latte {
@ -38,395 +39,113 @@ WindowsTracker::WindowsTracker(Latte::View *parent)
m_corona = qobject_cast<Latte::Corona *>(m_latteView->corona());
m_wm = m_corona->wm();
init();
m_wm->windowsTracker()->addView(m_latteView);
}
WindowsTracker::~WindowsTracker()
{
qDebug() << "WindowsTracker removing...";
m_wm->windowsTracker()->removeView(m_latteView);
}
void WindowsTracker::init()
{
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::enabledChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit enabledChanged();
}
});
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::activeWindowMaximizedChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit activeWindowMaximizedChanged();
}
});
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::activeWindowTouchingChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit activeWindowTouchingChanged();
}
});
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::existsWindowActiveChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit existsWindowActiveChanged();
}
});
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::existsWindowMaximizedChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit existsWindowMaximizedChanged();
}
});
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::existsWindowTouchingChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit existsWindowTouchingChanged();
}
});
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::activeWindowSchemeChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit activeWindowSchemeChanged();
}
});
connect(m_wm->windowsTracker(), &WindowSystem::WindowsTracker::touchingWindowSchemeChanged, this, [&](const Latte::View *view) {
if (m_latteView == view) {
emit touchingWindowSchemeChanged();
}
});
}
bool WindowsTracker::activeWindowMaximized() const
{
return m_activeWindowIsMaximizedFlag;
}
void WindowsTracker::setActiveWindowMaximized(bool activeMaximized)
{
if (m_activeWindowIsMaximizedFlag == activeMaximized) {
return;
}
m_activeWindowIsMaximizedFlag = activeMaximized;
emit activeWindowMaximizedChanged();
return m_wm->windowsTracker()->activeWindowMaximized(m_latteView);
}
bool WindowsTracker::activeWindowTouching() const
{
return m_activeWindowIsTouchingFlag;
}
void WindowsTracker::setActiveWindowTouching(bool activeTouching)
{
if (m_activeWindowIsTouchingFlag == activeTouching) {
return;
}
m_activeWindowIsTouchingFlag = activeTouching;
emit activeWindowTouchingChanged();
return m_wm->windowsTracker()->activeWindowTouching(m_latteView);
}
bool WindowsTracker::existsWindowActive() const
{
return m_windowIsActiveFlag;
}
void WindowsTracker::setExistsWindowActive(bool windowActive)
{
if (m_windowIsActiveFlag == windowActive) {
return;
}
m_windowIsActiveFlag = windowActive;
emit existsWindowActiveChanged();
return m_wm->windowsTracker()->existsWindowActive(m_latteView);
}
bool WindowsTracker::existsWindowMaximized() const
{
return m_windowIsMaximizedFlag;
}
void WindowsTracker::setExistsWindowMaximized(bool windowMaximized)
{
if (m_windowIsMaximizedFlag == windowMaximized) {
return;
}
m_windowIsMaximizedFlag = windowMaximized;
emit existsWindowMaximizedChanged();
return m_wm->windowsTracker()->existsWindowMaximized(m_latteView);
}
bool WindowsTracker::existsWindowTouching() const
{
return m_windowIsTouchingFlag;
}
void WindowsTracker::setExistsWindowTouching(bool windowTouching)
{
if (m_windowIsTouchingFlag == windowTouching) {
return;
}
m_windowIsTouchingFlag = windowTouching;
emit existsWindowTouchingChanged();
return m_wm->windowsTracker()->existsWindowTouching(m_latteView);
}
WindowSystem::SchemeColors *WindowsTracker::activeWindowScheme() const
{
return m_activeScheme;
}
void WindowsTracker::setActiveWindowScheme(WindowSystem::SchemeColors *scheme)
{
if (m_activeScheme == scheme) {
return;
}
m_activeScheme = scheme;
emit activeWindowSchemeChanged();
return m_wm->windowsTracker()->activeWindowScheme(m_latteView);
}
WindowSystem::SchemeColors *WindowsTracker::touchingWindowScheme() const
{
return m_touchingScheme;
}
void WindowsTracker::setTouchingWindowScheme(WindowSystem::SchemeColors *scheme)
{
if (m_touchingScheme == scheme) {
return;
}
m_touchingScheme = scheme;
emit touchingWindowSchemeChanged();
return m_wm->windowsTracker()->touchingWindowScheme(m_latteView);
}
bool WindowsTracker::enabled() const
{
return m_enabled;
return m_wm->windowsTracker()->enabled(m_latteView);
}
void WindowsTracker::setEnabled(bool active)
{
if (m_enabled == active) {
return;
}
m_enabled = active;
if (m_enabled) {
m_windows.clear();
for (const auto &wid : m_wm->windows()) {
m_windows.insert(wid, m_wm->requestInfo(wid));
}
m_connections[0] = connect(m_corona, &Plasma::Corona::availableScreenRectChanged,
this, &WindowsTracker::updateAvailableScreenGeometry);
m_connections[1] = connect(m_wm, &WindowSystem::AbstractWindowInterface::windowChanged, this, [&](WindowSystem::WindowId wid) {
m_windows[wid] = m_wm->requestInfo(wid);
updateFlags();
});
m_connections[2] = connect(m_wm, &WindowSystem::AbstractWindowInterface::windowRemoved, this, [&](WindowSystem::WindowId wid) {
m_windows.remove(wid);
});
m_connections[3] = connect(m_wm, &WindowSystem::AbstractWindowInterface::windowAdded, this, [&](WindowSystem::WindowId wid) {
m_windows.insert(wid, m_wm->requestInfo(wid));
updateFlags();
});
m_connections[4] = connect(m_wm, &WindowSystem::AbstractWindowInterface::activeWindowChanged, this, [&](WindowSystem::WindowId wid) {
if (m_windows.contains(m_lastActiveWindowWid)) {
m_windows[m_lastActiveWindowWid] = m_wm->requestInfo(m_lastActiveWindowWid);
}
m_windows[wid] = m_wm->requestInfo(wid);
updateFlags();
});
m_connections[5] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentDesktopChanged, this, [&] {
updateFlags();
});
m_connections[6] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentActivityChanged, this, [&] {
if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
//! this is needed in MultipleLayouts because there is a chance that multiple
//! layouts are providing different available screen geometries in different Activities
updateAvailableScreenGeometry();
}
updateFlags();
});
updateAvailableScreenGeometry();
updateFlags();
} else {
// clear mode
for (auto &c : m_connections) {
disconnect(c);
}
m_windows.clear();
setActiveWindowTouching(false);
setExistsWindowMaximized(false);
setExistsWindowTouching(false);
}
emit enabledChanged();
m_wm->windowsTracker()->setEnabled(m_latteView, active);
}
void WindowsTracker::updateAvailableScreenGeometry()
{
if (!m_latteView || !m_latteView->containment()) {
return;
}
int currentScrId = m_latteView->positioner()->currentScreenId();
QRect tempAvailableScreenGeometry = m_corona->availableScreenRectWithCriteria(currentScrId, {Types::AlwaysVisible}, {});
if (tempAvailableScreenGeometry != m_availableScreenGeometry) {
m_availableScreenGeometry = tempAvailableScreenGeometry;
updateFlags();
}
}
void WindowsTracker::updateFlags()
{
bool foundActive{false};
bool foundActiveInCurScreen{false};
bool foundActiveTouchInCurScreen{false};
bool foundTouchInCurScreen{false};
bool foundMaximizedInCurScreen{false};
//! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0),
//! maybe a garbage collector here is a good idea!!!
bool existsFaultyWindow{false};
WindowSystem::WindowId maxWinId;
WindowSystem::WindowId activeWinId;
WindowSystem::WindowId touchWinId;
WindowSystem::WindowId activeTouchWinId;
for (const auto &winfo : m_windows) {
if (winfo.isPlasmaDesktop() || !inCurrentDesktopActivity(winfo)) {
continue;
}
if (isActive(winfo)) {
foundActive = true;
m_lastActiveWindowWid = winfo.wid();
}
if (isActiveInCurrentScreen(winfo)) {
foundActiveInCurScreen = true;
activeWinId = winfo.wid();
}
if (isTouchingViewEdge(winfo) || isTouchingView(winfo)) {
if (winfo.isActive()) {
foundActiveTouchInCurScreen = true;
activeTouchWinId = winfo.wid();
if (isMaximizedInCurrentScreen(winfo)) {
//! active maximized windows have higher priority than the rest maximized windows
foundMaximizedInCurScreen = true;
maxWinId = winfo.wid();
}
} else {
foundTouchInCurScreen = true;
touchWinId = winfo.wid();
}
if (!foundMaximizedInCurScreen && isMaximizedInCurrentScreen(winfo)) {
foundMaximizedInCurScreen = true;
maxWinId = winfo.wid();
}
}
if (!existsFaultyWindow && winfo.geometry() == QRect(0, 0, 0, 0)) {
existsFaultyWindow = true;
}
//qDebug() << "window geometry ::: " << winfo.geometry();
}
if (existsFaultyWindow) {
cleanupFaultyWindows();
}
//! HACK: KWin Effects such as ShowDesktop have no way to be identified and as such
//! create issues with identifying properly touching and maximized windows. BUT when
//! they are enabled then NO ACTIVE window is found. This is a way to identify these
//! effects trigerring and disable the touch flags.
//! BUG: 404483
//! Disabled because it has fault identifications, e.g. when a window is maximized and
//! Latte or Plasma are showing their View settings
//foundMaximizedInCurScreen = foundMaximizedInCurScreen && foundActive;
//foundTouchInCurScreen = foundTouchInCurScreen && foundActive;
//! assign flags
setExistsWindowActive(foundActiveInCurScreen);
setActiveWindowTouching(foundActiveTouchInCurScreen);
setActiveWindowMaximized(maxWinId.toInt()>0 && (maxWinId == activeTouchWinId));
setExistsWindowMaximized(foundMaximizedInCurScreen);
setExistsWindowTouching(foundTouchInCurScreen || foundActiveTouchInCurScreen);
//! update color schemes for active and touching windows
setActiveWindowScheme(foundActiveInCurScreen ? m_wm->schemeForWindow(activeWinId) : nullptr);
if (foundActiveTouchInCurScreen) {
setTouchingWindowScheme(m_wm->schemeForWindow(activeTouchWinId));
} else if (foundMaximizedInCurScreen) {
setTouchingWindowScheme(m_wm->schemeForWindow(maxWinId));
} else if (foundTouchInCurScreen) {
setTouchingWindowScheme(m_wm->schemeForWindow(touchWinId));
} else {
setTouchingWindowScheme(nullptr);
}
}
bool WindowsTracker::inCurrentDesktopActivity(const WindowSystem::WindowInfoWrap &winfo)
{
return (winfo.isValid() && m_wm->isOnCurrentDesktop(winfo.wid()) && m_wm->isOnCurrentActivity(winfo.wid()));
}
bool WindowsTracker::intersects(const WindowSystem::WindowInfoWrap &winfo)
{
return (!winfo.isMinimized() && !winfo.isShaded() && winfo.geometry().intersects(m_latteView->absoluteGeometry()));
}
bool WindowsTracker::isActive(const WindowSystem::WindowInfoWrap &winfo)
{
return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized());
}
bool WindowsTracker::isActiveInCurrentScreen(const WindowSystem::WindowInfoWrap &winfo)
{
return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized()
&& m_availableScreenGeometry.contains(winfo.geometry().center()));
}
bool WindowsTracker::isMaximizedInCurrentScreen(const WindowSystem::WindowInfoWrap &winfo)
{
auto viewIntersectsMaxVert = [&]() noexcept -> bool {
return ((winfo.isMaxVert()
|| (m_latteView->screen() && m_latteView->screen()->availableSize().height() <= winfo.geometry().height()))
&& intersects(winfo));
};
auto viewIntersectsMaxHoriz = [&]() noexcept -> bool {
return ((winfo.isMaxHoriz()
|| (m_latteView->screen() && m_latteView->screen()->availableSize().width() <= winfo.geometry().width()))
&& intersects(winfo));
};
//! updated implementation to identify the screen that the maximized window is present
//! in order to avoid: https://bugs.kde.org/show_bug.cgi?id=397700
return (winfo.isValid() && !winfo.isMinimized()
&& (winfo.isMaximized() || viewIntersectsMaxVert() || viewIntersectsMaxHoriz())
&& m_availableScreenGeometry.contains(winfo.geometry().center()));
}
bool WindowsTracker::isTouchingView(const WindowSystem::WindowInfoWrap &winfo)
{
return (winfo.isValid() && intersects(winfo));
}
bool WindowsTracker::isTouchingViewEdge(const WindowSystem::WindowInfoWrap &winfo)
{
if (winfo.isValid() && !winfo.isMinimized()) {
bool touchingViewEdge{false};
QRect screenGeometry = m_latteView->screenGeometry();
bool inCurrentScreen{screenGeometry.contains(winfo.geometry().topLeft()) || screenGeometry.contains(winfo.geometry().bottomRight())};
if (inCurrentScreen) {
if (m_latteView->location() == Plasma::Types::TopEdge) {
touchingViewEdge = (winfo.geometry().y() == m_availableScreenGeometry.y());
} else if (m_latteView->location() == Plasma::Types::BottomEdge) {
touchingViewEdge = (winfo.geometry().bottom() == m_availableScreenGeometry.bottom());
} else if (m_latteView->location() == Plasma::Types::LeftEdge) {
touchingViewEdge = (winfo.geometry().x() == m_availableScreenGeometry.x());
} else if (m_latteView->location() == Plasma::Types::RightEdge) {
touchingViewEdge = (winfo.geometry().right() == m_availableScreenGeometry.right());
}
}
return touchingViewEdge;
}
return false;
}
void WindowsTracker::cleanupFaultyWindows()
{
for (const auto &key : m_windows.keys()) {
auto winfo = m_windows[key];
//! garbage windows removing
if (winfo.geometry() == QRect(0, 0, 0, 0)) {
//qDebug() << "Faulty Geometry ::: " << winfo.wid();
m_windows.remove(key);
}
}
}
//! Window Functions
void WindowsTracker::setWindowOnActivities(QWindow &window, const QStringList &activities)
@ -436,13 +155,7 @@ void WindowsTracker::setWindowOnActivities(QWindow &window, const QStringList &a
void WindowsTracker::requestToggleMaximizeForActiveWindow()
{
WindowSystem::WindowInfoWrap actInfo;
if (m_windows.contains(m_lastActiveWindowWid)) {
actInfo = m_windows[m_lastActiveWindowWid];
} else {
actInfo = m_wm->requestInfoActive();
}
WindowSystem::WindowInfoWrap actInfo = m_wm->windowsTracker()->lastActiveWindowInfo(m_latteView);
//active window can be toggled only when it is in the same screen
if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) {
@ -452,13 +165,7 @@ void WindowsTracker::requestToggleMaximizeForActiveWindow()
void WindowsTracker::requestMoveActiveWindow(int localX, int localY)
{
WindowSystem::WindowInfoWrap actInfo;
if (m_windows.contains(m_lastActiveWindowWid)) {
actInfo = m_windows[m_lastActiveWindowWid];
} else {
actInfo = m_wm->requestInfoActive();
}
WindowSystem::WindowInfoWrap actInfo = m_wm->windowsTracker()->lastActiveWindowInfo(m_latteView);
//active window can be dragged only when it is in the same screen
if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) {
@ -478,13 +185,7 @@ void WindowsTracker::requestMoveActiveWindow(int localX, int localY)
bool WindowsTracker::activeWindowCanBeDragged()
{
WindowSystem::WindowInfoWrap actInfo;
if (m_windows.contains(m_lastActiveWindowWid)) {
actInfo = m_windows[m_lastActiveWindowWid];
} else {
actInfo = m_wm->requestInfoActive();
}
WindowSystem::WindowInfoWrap actInfo = m_wm->windowsTracker()->lastActiveWindowInfo(m_latteView);
//active window can be dragged only when it is in the same screen
if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) {

View File

@ -22,7 +22,6 @@
// local
#include "../wm/abstractwindowinterface.h"
#include "../wm/windowinfowrap.h"
// Qt
#include <QObject>
@ -86,50 +85,13 @@ public slots:
Q_INVOKABLE bool activeWindowCanBeDragged();
private:
void setActiveWindowMaximized(bool activeMaximized);
void setActiveWindowTouching(bool activeTouching);
void setExistsWindowActive(bool windowActive);
void setExistsWindowMaximized(bool windowMaximized);
void setExistsWindowTouching(bool windowTouching);
void setActiveWindowScheme(WindowSystem::SchemeColors *scheme);
void setTouchingWindowScheme(WindowSystem::SchemeColors *scheme);
void updateAvailableScreenGeometry();
void updateFlags();
//! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0),
//! this is a garbage collector to collect such windows in order to not break the windows array validity.
void cleanupFaultyWindows();
bool intersects(const WindowSystem::WindowInfoWrap &winfo);
bool inCurrentDesktopActivity(const WindowSystem::WindowInfoWrap &winfo);
bool isActive(const WindowSystem::WindowInfoWrap &winfo);
bool isActiveInCurrentScreen(const WindowSystem::WindowInfoWrap &winfo);
bool isMaximizedInCurrentScreen(const WindowSystem::WindowInfoWrap &winfo);
bool isTouchingViewEdge(const WindowSystem::WindowInfoWrap &winfo);
bool isTouchingView(const WindowSystem::WindowInfoWrap &winfo);
void init();
private:
bool m_enabled{false};
bool m_activeWindowIsMaximizedFlag{false};
bool m_activeWindowIsTouchingFlag{false};
bool m_windowIsActiveFlag{false};
bool m_windowIsTouchingFlag{false};
bool m_windowIsMaximizedFlag{false};
QRect m_availableScreenGeometry;
WindowSystem::WindowId m_lastActiveWindowWid;
std::array<QMetaObject::Connection, 7> m_connections;
QMap<WindowSystem::WindowId, WindowSystem::WindowInfoWrap> m_windows;
Latte::Corona *m_corona{nullptr};
Latte::View *m_latteView{nullptr};
WindowSystem::AbstractWindowInterface *m_wm;
WindowSystem::SchemeColors *m_activeScheme{nullptr};
WindowSystem::SchemeColors *m_touchingScheme{nullptr};
};
}

View File

@ -4,6 +4,7 @@ set(lattedock-app_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/schemecolors.cpp
${CMAKE_CURRENT_SOURCE_DIR}/waylandinterface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/windowinfowrap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/windowstracker.cpp
${CMAKE_CURRENT_SOURCE_DIR}/xwindowinterface.cpp
PARENT_SCOPE
)

View File

@ -20,6 +20,10 @@
#include "abstractwindowinterface.h"
// local
#include "windowstracker.h"
#include "../lattecorona.h"
// Qt
#include <QObject>
#include <QDir>
@ -35,6 +39,9 @@ namespace WindowSystem {
AbstractWindowInterface::AbstractWindowInterface(QObject *parent)
: QObject(parent)
{
m_corona = qobject_cast<Latte::Corona *>(parent);
m_windowsTracker = new WindowsTracker(this);
updateDefaultScheme();
connect(this, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) {
@ -66,6 +73,8 @@ AbstractWindowInterface::~AbstractWindowInterface()
m_schemes.take("kdeglobals");
qDeleteAll(m_schemes);
m_schemes.clear();
m_windowsTracker->deleteLater();
}
//! Scheme support for windows
@ -89,6 +98,11 @@ void AbstractWindowInterface::updateDefaultScheme()
}
}
Latte::Corona *AbstractWindowInterface::corona()
{
return m_corona;
}
SchemeColors *AbstractWindowInterface::schemeForWindow(WindowId wid)
{
if (!m_windowScheme.contains(wid)) {
@ -100,6 +114,11 @@ SchemeColors *AbstractWindowInterface::schemeForWindow(WindowId wid)
return nullptr;
}
WindowsTracker *AbstractWindowInterface::windowsTracker()
{
return m_windowsTracker;
}
void AbstractWindowInterface::setColorSchemeForWindow(WindowId wid, QString scheme)
{
if (scheme == "kdeglobals" && !m_windowScheme.contains(wid)) {

View File

@ -47,6 +47,13 @@
// Plasma
#include <Plasma>
namespace Latte {
class Corona;
namespace WindowSystem {
class WindowsTracker;
}
}
namespace Latte {
namespace WindowSystem {
@ -94,7 +101,9 @@ public:
virtual bool windowCanBeDragged(WindowId wid) const = 0;
virtual WindowId winIdFor(QString appId, QRect geometry) const = 0;
Latte::Corona *corona();
SchemeColors *schemeForWindow(WindowId wId);
WindowsTracker *windowsTracker();
void setColorSchemeForWindow(WindowId wId, QString scheme);
signals:
@ -118,6 +127,9 @@ private:
//! window id and its corresponding scheme file
QMap<WindowId, QString> m_windowScheme;
Latte::Corona *m_corona;
WindowsTracker *m_windowsTracker;
};
}

View File

@ -18,3 +18,492 @@
*/
#include "windowstracker.h"
// local
#include "abstractwindowinterface.h"
#include "schemecolors.h"
#include "../lattecorona.h"
#include "../layouts/manager.h"
#include "../view/view.h"
#include "../view/positioner.h"
#include "../../liblatte2/types.h"
namespace Latte {
namespace WindowSystem {
WindowsTracker::WindowsTracker(AbstractWindowInterface *parent)
: QObject(parent)
{
m_wm = parent;
init();
}
WindowsTracker::~WindowsTracker()
{
}
void WindowsTracker::init()
{
connect(m_wm->corona(), &Plasma::Corona::availableScreenRectChanged, this, &WindowsTracker::updateAvailableScreenGeometries);
connect(m_wm, &AbstractWindowInterface::windowChanged, this, [&](WindowId wid) {
m_windows[wid] = m_wm->requestInfo(wid);
updateViewsHints();
});
connect(m_wm, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) {
m_windows.remove(wid);
updateViewsHints();
});
connect(m_wm, &AbstractWindowInterface::windowAdded, this, [&](WindowId wid) {
if (!m_windows.contains(wid)) {
m_windows.insert(wid, m_wm->requestInfo(wid));
}
updateViewsHints();
});
connect(m_wm, &AbstractWindowInterface::activeWindowChanged, this, [&](WindowId wid) {
for (const auto view : m_views.keys()) {
WindowId lastWinId = m_views[view].lastActiveWindow;
if (m_windows.contains(lastWinId)) {
m_windows[lastWinId] = m_wm->requestInfo(lastWinId);
}
}
m_windows[wid] = m_wm->requestInfo(wid);
updateViewsHints();
});
connect(m_wm, &AbstractWindowInterface::currentDesktopChanged, this, [&] {
updateViewsHints();
});
connect(m_wm, &AbstractWindowInterface::currentActivityChanged, this, [&] {
if (m_wm->corona()->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
//! this is needed in MultipleLayouts because there is a chance that multiple
//! layouts are providing different available screen geometries in different Activities
updateAvailableScreenGeometries();
}
updateViewsHints();
});
}
void WindowsTracker::addView(Latte::View *view)
{
if (m_views.contains(view)) {
return;
}
ViewHints hints;
m_views[view] = hints;
updateAvailableScreenGeometries();
updateHints(view);
}
void WindowsTracker::removeView(Latte::View *view)
{
if (!m_views.contains(view)) {
return;
}
m_views.remove(view);
}
//! Views Properties And Hints
bool WindowsTracker::enabled(Latte::View *view)
{
if (!m_views.contains(view)) {
return false;
}
return m_views[view].enabled;
}
void WindowsTracker::setEnabled(Latte::View *view, const bool enabled)
{
if (!m_views.contains(view) || m_views[view].enabled == enabled) {
return;
}
m_views[view].enabled = enabled;
if (enabled) {
updateHints(view);
} else {
//! INITIALIZE ALL HINTS !!!
}
emit enabledChanged(view);
}
bool WindowsTracker::activeWindowMaximized(Latte::View *view) const
{
if (!m_views.contains(view)) {
return false;
}
return m_views[view].activeWindowMaximized;
}
void WindowsTracker::setActiveWindowMaximized(Latte::View *view, bool activeMaximized)
{
if (!m_views.contains(view) || m_views[view].activeWindowMaximized == activeMaximized) {
return;
}
m_views[view].activeWindowMaximized = activeMaximized;
emit activeWindowMaximizedChanged(view);
}
bool WindowsTracker::activeWindowTouching(Latte::View *view) const
{
if (!m_views.contains(view)) {
return false;
}
return m_views[view].activeWindowTouching;
}
void WindowsTracker::setActiveWindowTouching(Latte::View *view, bool activeTouching)
{
if (!m_views.contains(view) || m_views[view].activeWindowTouching == activeTouching) {
return;
}
m_views[view].activeWindowTouching = activeTouching;
emit activeWindowTouchingChanged(view);
}
bool WindowsTracker::existsWindowActive(Latte::View *view) const
{
if (!m_views.contains(view)) {
return false;
}
return m_views[view].existsWindowActive;
}
void WindowsTracker::setExistsWindowActive(Latte::View *view, bool windowActive)
{
if (!m_views.contains(view) || m_views[view].existsWindowActive == windowActive) {
return;
}
m_views[view].existsWindowActive = windowActive;
emit existsWindowActiveChanged(view);
}
bool WindowsTracker::existsWindowMaximized(Latte::View *view) const
{
if (!m_views.contains(view)) {
return false;
}
return m_views[view].existsWindowMaximized;
}
void WindowsTracker::setExistsWindowMaximized(Latte::View *view, bool windowMaximized)
{
if (!m_views.contains(view) || m_views[view].existsWindowMaximized == windowMaximized) {
return;
}
m_views[view].existsWindowMaximized = windowMaximized;
emit existsWindowMaximizedChanged(view);
}
bool WindowsTracker::existsWindowTouching(Latte::View *view) const
{
if (!m_views.contains(view)) {
return false;
}
return m_views[view].existsWindowTouching;
}
void WindowsTracker::setExistsWindowTouching(Latte::View *view, bool windowTouching)
{
if (!m_views.contains(view) || m_views[view].existsWindowTouching == windowTouching) {
return;
}
m_views[view].existsWindowTouching = windowTouching;
emit existsWindowTouchingChanged(view);
}
SchemeColors *WindowsTracker::activeWindowScheme(Latte::View *view) const
{
if (!m_views.contains(view)) {
return nullptr;
}
return m_views[view].activeWindowScheme;
}
void WindowsTracker::setActiveWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme)
{
if (!m_views.contains(view) || m_views[view].activeWindowScheme == scheme) {
return;
}
m_views[view].activeWindowScheme = scheme;
emit activeWindowSchemeChanged(view);
}
SchemeColors *WindowsTracker::touchingWindowScheme(Latte::View *view) const
{
if (!m_views.contains(view)) {
return nullptr;
}
return m_views[view].touchingWindowScheme;
}
void WindowsTracker::setTouchingWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme)
{
if (!m_views.contains(view) || m_views[view].touchingWindowScheme == scheme) {
return;
}
m_views[view].touchingWindowScheme = scheme;
emit touchingWindowSchemeChanged(view);
}
WindowInfoWrap WindowsTracker::lastActiveWindowInfo(Latte::View *view)
{
WindowInfoWrap info;
if (!m_views.contains(view)) {
return info;
}
if (!m_windows.contains(m_views[view].lastActiveWindow)) {
m_views[view].lastActiveWindow = info.wid();
return info;
}
return m_wm->requestInfo(m_views[view].lastActiveWindow);
}
//! Windows Criteria Functions
bool WindowsTracker::inCurrentDesktopActivity(const WindowInfoWrap &winfo)
{
return (winfo.isValid() && m_wm->isOnCurrentDesktop(winfo.wid()) && m_wm->isOnCurrentActivity(winfo.wid()));
}
bool WindowsTracker::intersects(Latte::View *view, const WindowInfoWrap &winfo)
{
return (!winfo.isMinimized() && !winfo.isShaded() && winfo.geometry().intersects(view->absoluteGeometry()));
}
bool WindowsTracker::isActive(const WindowInfoWrap &winfo)
{
return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized());
}
bool WindowsTracker::isActiveInViewScreen(Latte::View *view, const WindowInfoWrap &winfo)
{
return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized()
&& m_views[view].availableScreenGeometry.contains(winfo.geometry().center()));
}
bool WindowsTracker::isMaximizedInViewScreen(Latte::View *view, const WindowInfoWrap &winfo)
{
auto viewIntersectsMaxVert = [&]() noexcept -> bool {
return ((winfo.isMaxVert()
|| (view->screen() && view->screen()->availableSize().height() <= winfo.geometry().height()))
&& intersects(view, winfo));
};
auto viewIntersectsMaxHoriz = [&]() noexcept -> bool {
return ((winfo.isMaxHoriz()
|| (view->screen() && view->screen()->availableSize().width() <= winfo.geometry().width()))
&& intersects(view, winfo));
};
//! updated implementation to identify the screen that the maximized window is present
//! in order to avoid: https://bugs.kde.org/show_bug.cgi?id=397700
return (winfo.isValid() && !winfo.isMinimized()
&& (winfo.isMaximized() || viewIntersectsMaxVert() || viewIntersectsMaxHoriz())
&& m_views[view].availableScreenGeometry.contains(winfo.geometry().center()));
}
bool WindowsTracker::isTouchingView(Latte::View *view, const WindowSystem::WindowInfoWrap &winfo)
{
return (winfo.isValid() && intersects(view, winfo));
}
bool WindowsTracker::isTouchingViewEdge(Latte::View *view, const WindowInfoWrap &winfo)
{
if (winfo.isValid() && !winfo.isMinimized()) {
bool touchingViewEdge{false};
QRect screenGeometry = view->screenGeometry();
QRect availableScreenGeometry = m_views[view].availableScreenGeometry;
bool inCurrentScreen{screenGeometry.contains(winfo.geometry().topLeft()) || screenGeometry.contains(winfo.geometry().bottomRight())};
if (inCurrentScreen) {
if (view->location() == Plasma::Types::TopEdge) {
touchingViewEdge = (winfo.geometry().y() == availableScreenGeometry.y());
} else if (view->location() == Plasma::Types::BottomEdge) {
touchingViewEdge = (winfo.geometry().bottom() == availableScreenGeometry.bottom());
} else if (view->location() == Plasma::Types::LeftEdge) {
touchingViewEdge = (winfo.geometry().x() == availableScreenGeometry.x());
} else if (view->location() == Plasma::Types::RightEdge) {
touchingViewEdge = (winfo.geometry().right() == availableScreenGeometry.right());
}
}
return touchingViewEdge;
}
return false;
}
void WindowsTracker::cleanupFaultyWindows()
{
for (const auto &key : m_windows.keys()) {
auto winfo = m_windows[key];
//! garbage windows removing
if (winfo.geometry() == QRect(0, 0, 0, 0)) {
//qDebug() << "Faulty Geometry ::: " << winfo.wid();
m_windows.remove(key);
}
}
}
void WindowsTracker::updateAvailableScreenGeometries()
{
for (const auto view : m_views.keys()) {
if (m_views[view].enabled) {
int currentScrId = view->positioner()->currentScreenId();
QRect tempAvailableScreenGeometry = m_wm->corona()->availableScreenRectWithCriteria(currentScrId, {Types::AlwaysVisible}, {});
if (tempAvailableScreenGeometry != m_views[view].availableScreenGeometry) {
m_views[view].availableScreenGeometry = tempAvailableScreenGeometry;
updateHints(view);
}
}
}
}
void WindowsTracker::updateViewsHints()
{
for (const auto view : m_views.keys()) {
if (m_views[view].enabled) {
updateHints(view);
}
}
}
void WindowsTracker::updateHints(Latte::View *view)
{
if (!m_views.contains(view)) {
return;
}
bool foundActive{false};
bool foundActiveInCurScreen{false};
bool foundActiveTouchInCurScreen{false};
bool foundTouchInCurScreen{false};
bool foundMaximizedInCurScreen{false};
//! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0),
//! maybe a garbage collector here is a good idea!!!
bool existsFaultyWindow{false};
WindowId maxWinId;
WindowId activeWinId;
WindowId touchWinId;
WindowId activeTouchWinId;
for (const auto &winfo : m_windows) {
if (winfo.isPlasmaDesktop() || !inCurrentDesktopActivity(winfo)) {
continue;
}
if (isActive(winfo)) {
foundActive = true;
}
if (isActiveInViewScreen(view, winfo)) {
m_views[view].lastActiveWindow = winfo.wid();
foundActiveInCurScreen = true;
activeWinId = winfo.wid();
}
if (isTouchingViewEdge(view, winfo) || isTouchingView(view, winfo)) {
if (winfo.isActive()) {
foundActiveTouchInCurScreen = true;
activeTouchWinId = winfo.wid();
if (isMaximizedInViewScreen(view, winfo)) {
//! active maximized windows have higher priority than the rest maximized windows
foundMaximizedInCurScreen = true;
maxWinId = winfo.wid();
}
} else {
foundTouchInCurScreen = true;
touchWinId = winfo.wid();
}
if (!foundMaximizedInCurScreen && isMaximizedInViewScreen(view, winfo)) {
foundMaximizedInCurScreen = true;
maxWinId = winfo.wid();
}
}
if (!existsFaultyWindow && winfo.geometry() == QRect(0, 0, 0, 0)) {
existsFaultyWindow = true;
}
//qDebug() << "window geometry ::: " << winfo.geometry();
}
if (existsFaultyWindow) {
cleanupFaultyWindows();
}
//! HACK: KWin Effects such as ShowDesktop have no way to be identified and as such
//! create issues with identifying properly touching and maximized windows. BUT when
//! they are enabled then NO ACTIVE window is found. This is a way to identify these
//! effects trigerring and disable the touch flags.
//! BUG: 404483
//! Disabled because it has fault identifications, e.g. when a window is maximized and
//! Latte or Plasma are showing their View settings
//foundMaximizedInCurScreen = foundMaximizedInCurScreen && foundActive;
//foundTouchInCurScreen = foundTouchInCurScreen && foundActive;
//! assign flags
setExistsWindowActive(view, foundActiveInCurScreen);
setActiveWindowTouching(view, foundActiveTouchInCurScreen);
setActiveWindowMaximized(view, (maxWinId.toInt()>0 && (maxWinId == activeTouchWinId)));
setExistsWindowMaximized(view, foundMaximizedInCurScreen);
setExistsWindowTouching(view, (foundTouchInCurScreen || foundActiveTouchInCurScreen));
//! update color schemes for active and touching windows
setActiveWindowScheme(view, (foundActiveInCurScreen ? m_wm->schemeForWindow(activeWinId) : nullptr));
if (foundActiveTouchInCurScreen) {
setTouchingWindowScheme(view, m_wm->schemeForWindow(activeTouchWinId));
} else if (foundMaximizedInCurScreen) {
setTouchingWindowScheme(view, m_wm->schemeForWindow(maxWinId));
} else if (foundTouchInCurScreen) {
setTouchingWindowScheme(view, m_wm->schemeForWindow(touchWinId));
} else {
setTouchingWindowScheme(view, nullptr);
}
}
}
}

View File

@ -20,16 +20,105 @@
#ifndef WINDOWSYSTEMWINDOWSTRACKER_H
#define WINDOWSYSTEMWINDOWSTRACKER_H
// local
#include "windowinfowrap.h"
// Qt
#include <QObject>
#include <QHash>
#include <QMap>
namespace Latte {
class View;
namespace WindowSystem {
class AbstractWindowInterface;
class SchemeColors;
}
}
namespace Latte {
namespace WindowSystem {
struct ViewHints {
bool enabled{false};
bool activeWindowMaximized{false};
bool activeWindowTouching{false};
bool existsWindowActive{false};
bool existsWindowMaximized{false};
bool existsWindowTouching{false};
QRect availableScreenGeometry;
WindowId lastActiveWindow;
SchemeColors *activeWindowScheme{nullptr};
SchemeColors *touchingWindowScheme{nullptr};
};
class WindowsTracker : public QObject {
Q_OBJECT
public:
WindowsTracker(AbstractWindowInterface *parent);
~WindowsTracker() override;
void addView(Latte::View *view);
void removeView(Latte::View *view);
bool enabled(Latte::View *view);
void setEnabled(Latte::View *view, const bool enabled);
bool activeWindowMaximized(Latte::View *view) const;
bool activeWindowTouching(Latte::View *view) const;
bool existsWindowActive(Latte::View *view) const;
bool existsWindowMaximized(Latte::View *view) const;
bool existsWindowTouching(Latte::View *view) const;
SchemeColors *activeWindowScheme(Latte::View *view) const;
SchemeColors *touchingWindowScheme(Latte::View *view) const;
WindowInfoWrap lastActiveWindowInfo(Latte::View *view);
signals:
void enabledChanged(const Latte::View *view);
void activeWindowMaximizedChanged(const Latte::View *view);
void activeWindowTouchingChanged(const Latte::View *view);
void existsWindowActiveChanged(const Latte::View *view);
void existsWindowMaximizedChanged(const Latte::View *view);
void existsWindowTouchingChanged(const Latte::View *view);
void activeWindowSchemeChanged(const Latte::View *view);
void touchingWindowSchemeChanged(const Latte::View *view);
private slots:
void updateAvailableScreenGeometries();
private:
void init();
void cleanupFaultyWindows();
void updateViewsHints();
void updateHints(Latte::View *view);
void setActiveWindowMaximized(Latte::View *view, bool activeMaximized);
void setActiveWindowTouching(Latte::View *view, bool activeTouching);
void setExistsWindowActive(Latte::View *view, bool windowActive);
void setExistsWindowMaximized(Latte::View *view, bool windowMaximized);
void setExistsWindowTouching(Latte::View *view, bool windowTouching);
void setActiveWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme);
void setTouchingWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme);
bool inCurrentDesktopActivity(const WindowInfoWrap &winfo);
bool intersects(Latte::View *view, const WindowInfoWrap &winfo);
bool isActive(const WindowInfoWrap &winfo);
bool isActiveInViewScreen(Latte::View *view, const WindowInfoWrap &winfo);
bool isMaximizedInViewScreen(Latte::View *view, const WindowInfoWrap &winfo);
bool isTouchingView(Latte::View *view, const WindowSystem::WindowInfoWrap &winfo);
bool isTouchingViewEdge(Latte::View *view, const WindowInfoWrap &winfo);
private:
AbstractWindowInterface *m_wm;
QHash<Latte::View *, ViewHints> m_views;
QMap<WindowId, WindowInfoWrap > m_windows;
};
}
}
#endif