diff --git a/app/view/screenedgeghostwindow.cpp b/app/view/screenedgeghostwindow.cpp index 48300dbe..6c924256 100644 --- a/app/view/screenedgeghostwindow.cpp +++ b/app/view/screenedgeghostwindow.cpp @@ -1,350 +1,356 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "screenedgeghostwindow.h" // local #include "view.h" // Qt #include #include #include #include // KDE #include #include #include // X11 #include namespace Latte { namespace ViewPart { ScreenEdgeGhostWindow::ScreenEdgeGhostWindow(Latte::View *view) : m_latteView(view) { m_corona = qobject_cast(view->corona()); bool debugEdge = (qApp->arguments().contains("-d") && qApp->arguments().contains("--kwinedges")); setColor(debugEdge ? QColor("purple") : QColor(Qt::transparent)); setDefaultAlphaBuffer(true); setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); m_fixGeometryTimer.setSingleShot(true); m_fixGeometryTimer.setInterval(500); connect(&m_fixGeometryTimer, &QTimer::timeout, this, &ScreenEdgeGhostWindow::fixGeometry); //! this timer is used in order to avoid fast enter/exit signals during first //! appearing after edge activation m_delayedMouseTimer.setSingleShot(true); m_delayedMouseTimer.setInterval(50); connect(&m_delayedMouseTimer, &QTimer::timeout, this, [this]() { if (m_delayedContainsMouse) { setContainsMouse(true); } else { setContainsMouse(false); } }); connect(this, &QQuickView::xChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::yChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::widthChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::heightChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(m_latteView, &Latte::View::absoluteGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); connect(m_latteView, &Latte::View::screenGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); connect(m_latteView, &Latte::View::locationChanged, this, &ScreenEdgeGhostWindow::updateGeometry); connect(m_latteView, &QQuickView::screenChanged, this, [this]() { setScreen(m_latteView->screen()); updateGeometry(); }); if (!KWindowSystem::isPlatformWayland()) { //! IMPORTANT!!! ::: This fixes a bug when closing an Activity all views from all Activities are //! disappearing! With this code parts they reappear!!! m_visibleHackTimer1.setInterval(400); m_visibleHackTimer2.setInterval(2500); m_visibleHackTimer1.setSingleShot(true); m_visibleHackTimer2.setSingleShot(true); connectionsHack << connect(this, &QWindow::visibleChanged, this, [&]() { if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) { m_visibleHackTimer1.start(); m_visibleHackTimer2.start(); } else if (!m_inDelete) { //! For some reason when the window is hidden in the edge under X11 afterwards //! is losing its window flags m_corona->wm()->setViewExtraFlags(this); } }); connectionsHack << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() { if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) { show(); emit forcedShown(); //qDebug() << "Ghost Edge:: Enforce reshow from timer 1..."; } else { //qDebug() << "Ghost Edge:: No needed reshow from timer 1..."; } }); connectionsHack << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() { if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) { show(); emit forcedShown(); //qDebug() << "Ghost Edge:: Enforce reshow from timer 2..."; } else { //qDebug() << "Ghost Edge:: No needed reshow from timer 2..."; } }); connectionsHack << connect(this, &ScreenEdgeGhostWindow::forcedShown, this, [&]() { m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId); m_trackedWindowId = winId(); m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); }); } setupWaylandIntegration(); if (KWindowSystem::isPlatformX11()) { m_trackedWindowId = winId(); m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); } else { connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, [&]() { if (m_trackedWindowId.isNull()) { m_trackedWindowId = m_corona->wm()->winIdFor("latte-dock", geometry()); m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); } }); } setScreen(m_latteView->screen()); show(); updateGeometry(); hideWithMask(); } ScreenEdgeGhostWindow::~ScreenEdgeGhostWindow() { m_inDelete = true; m_corona->wm()->unregisterIgnoredWindow(KWindowSystem::isPlatformX11() ? winId() : m_trackedWindowId); m_latteView = nullptr; // clear mode m_visibleHackTimer1.stop(); m_visibleHackTimer2.stop(); for (auto &c : connectionsHack) { disconnect(c); } if (m_shellSurface) { delete m_shellSurface; } } int ScreenEdgeGhostWindow::location() { return (int)m_latteView->location(); } +int ScreenEdgeGhostWindow::thickness() const +{ + return m_thickness; +} + Latte::View *ScreenEdgeGhostWindow::parentView() { return m_latteView; } KWayland::Client::PlasmaShellSurface *ScreenEdgeGhostWindow::surface() { return m_shellSurface; } void ScreenEdgeGhostWindow::updateGeometry() { if (m_latteView->positioner()->slideOffset() != 0) { return; } QRect newGeometry; - int thickness{2}; if (KWindowSystem::compositingActive()) { - thickness = 6; + m_thickness = 6; + } else { + m_thickness = 2; } int length{30}; int lengthDifference{0}; if (m_latteView->formFactor() == Plasma::Types::Horizontal) { //! set minimum length to be 25% of screen width length = qMax(m_latteView->screenGeometry().width()/4,qMin(m_latteView->absoluteGeometry().width(), m_latteView->screenGeometry().width() - 1)); lengthDifference = qMax(0,length - m_latteView->absoluteGeometry().width()); } else { //! set minimum length to be 25% of screen height length = qMax(m_latteView->screenGeometry().height()/4,qMin(m_latteView->absoluteGeometry().height(), m_latteView->screenGeometry().height() - 1)); lengthDifference = qMax(0,length - m_latteView->absoluteGeometry().height()); } if (m_latteView->location() == Plasma::Types::BottomEdge) { int xF = qMax(m_latteView->screenGeometry().left(), m_latteView->absoluteGeometry().left() - lengthDifference); newGeometry.setX(xF); - newGeometry.setY(m_latteView->screenGeometry().bottom() - thickness); + newGeometry.setY(m_latteView->screenGeometry().bottom() - m_thickness); } else if (m_latteView->location() == Plasma::Types::TopEdge) { int xF = qMax(m_latteView->screenGeometry().left(), m_latteView->absoluteGeometry().left() - lengthDifference); newGeometry.setX(xF); newGeometry.setY(m_latteView->screenGeometry().top()); } else if (m_latteView->location() == Plasma::Types::LeftEdge) { int yF = qMax(m_latteView->screenGeometry().top(), m_latteView->absoluteGeometry().top() - lengthDifference); newGeometry.setX(m_latteView->screenGeometry().left()); newGeometry.setY(yF); } else if (m_latteView->location() == Plasma::Types::RightEdge) { int yF = qMax(m_latteView->screenGeometry().top(), m_latteView->absoluteGeometry().top() - lengthDifference); - newGeometry.setX(m_latteView->screenGeometry().right() - thickness); + newGeometry.setX(m_latteView->screenGeometry().right() - m_thickness); newGeometry.setY(yF); } if (m_latteView->formFactor() == Plasma::Types::Horizontal) { newGeometry.setWidth(length); - newGeometry.setHeight(thickness + 1); + newGeometry.setHeight(m_thickness + 1); } else { - newGeometry.setWidth(thickness + 1); + newGeometry.setWidth(m_thickness + 1); newGeometry.setHeight(length); } m_calculatedGeometry = newGeometry; fixGeometry(); } void ScreenEdgeGhostWindow::fixGeometry() { if (!m_calculatedGeometry.isEmpty() && (m_calculatedGeometry.x() != x() || m_calculatedGeometry.y() != y() || m_calculatedGeometry.width() != width() || m_calculatedGeometry.height() != height())) { setMinimumSize(m_calculatedGeometry.size()); setMaximumSize(m_calculatedGeometry.size()); resize(m_calculatedGeometry.size()); setPosition(m_calculatedGeometry.x(), m_calculatedGeometry.y()); if (m_shellSurface) { m_shellSurface->setPosition(m_calculatedGeometry.topLeft()); } } } void ScreenEdgeGhostWindow::startGeometryTimer() { m_fixGeometryTimer.start(); } void ScreenEdgeGhostWindow::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) { // already setup return; } if (m_corona) { using namespace KWayland::Client; PlasmaShell *interface = m_corona->waylandCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland screen edge ghost window surface was created..."; m_shellSurface = interface->createSurface(s, this); m_corona->wm()->setViewExtraFlags(m_shellSurface); m_shellSurface->setPanelTakesFocus(false); } } bool ScreenEdgeGhostWindow::containsMouse() const { return m_containsMouse; } void ScreenEdgeGhostWindow::setContainsMouse(bool contains) { if (m_containsMouse == contains) { return; } m_containsMouse = contains; emit containsMouseChanged(contains); } bool ScreenEdgeGhostWindow::event(QEvent *e) { if (e->type() == QEvent::DragEnter || e->type() == QEvent::DragMove) { if (!m_containsMouse) { m_delayedContainsMouse = false; m_delayedMouseTimer.stop(); setContainsMouse(true); emit dragEntered(); } } else if (e->type() == QEvent::Enter) { m_delayedContainsMouse = true; if (!m_delayedMouseTimer.isActive()) { m_delayedMouseTimer.start(); } } else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave) { m_delayedContainsMouse = false; if (!m_delayedMouseTimer.isActive()) { m_delayedMouseTimer.start(); } } else if (e->type() == QEvent::Show) { m_corona->wm()->setViewExtraFlags(this); } return QQuickView::event(e); } void ScreenEdgeGhostWindow::hideWithMask() { //! old values: 0,0,1,1 were blocking the top-left corner of the window QRect maskGeometry{-2, 0, 1, 1}; setMask(maskGeometry); } void ScreenEdgeGhostWindow::showWithMask() { setMask(QRect()); } } } diff --git a/app/view/screenedgeghostwindow.h b/app/view/screenedgeghostwindow.h index 3907a347..3499f475 100644 --- a/app/view/screenedgeghostwindow.h +++ b/app/view/screenedgeghostwindow.h @@ -1,126 +1,130 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SCREENEDGEGHOSTWINDOW_H #define SCREENEDGEGHOSTWINDOW_H // local #include "../lattecorona.h" #include "../wm/windowinfowrap.h" // Qt #include #include #include namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { class Corona; class View; } namespace Latte { namespace ViewPart { //! What is the importance of this class? //! //! Plasma is activating the screen edges for the main panel window //! unfortunately this isn't possible for the Latte case. //! When a window is hidden at an edge it becomes NOT visible //! unfortunately that means that all the animations are //! stopped (Qt behaviour) and that creates confusion to the user after the window //! reappears because various animations are played (adding-removing tasks/launchers) //! that aren't relevant any more. //! //! In order to workaround the above behaviour Latte is using a //! fake window to communicate with KWin and the MAIN Latte::View window //! continues to use only mask technique to hide //! //! KDE BUGS: https://bugs.kde.org/show_bug.cgi?id=382219 //! https://bugs.kde.org/show_bug.cgi?id=392464 class ScreenEdgeGhostWindow : public QQuickView { Q_OBJECT public: ScreenEdgeGhostWindow(Latte::View *view); ~ScreenEdgeGhostWindow() override; bool containsMouse() const; int location(); + int thickness() const; void hideWithMask(); void showWithMask(); Latte::View *parentView(); KWayland::Client::PlasmaShellSurface *surface(); signals: void containsMouseChanged(bool contains); void dragEntered(); void forcedShown(); //[workaround] forced shown to avoid a KWin issue that hides windows when activities are stopped protected: bool event(QEvent *ev) override; private slots: void startGeometryTimer(); void updateGeometry(); void fixGeometry(); private: void setContainsMouse(bool contains); void setupWaylandIntegration(); private: bool m_delayedContainsMouse{false}; bool m_containsMouse{false}; bool m_inDelete{false}; + + int m_thickness{2}; + QRect m_calculatedGeometry; QTimer m_delayedMouseTimer; QTimer m_fixGeometryTimer; //! HACK: Timers in order to handle KWin faulty //! behavior that hides Views when closing Activities //! with no actual reason QTimer m_visibleHackTimer1; QTimer m_visibleHackTimer2; //! Connections for the KWin visibility hack QList connectionsHack; Latte::View *m_latteView{nullptr}; QPointer m_corona; Latte::WindowSystem::WindowId m_trackedWindowId; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } } #endif diff --git a/app/view/visibilitymanager.cpp b/app/view/visibilitymanager.cpp index 4c1634d6..036dc225 100644 --- a/app/view/visibilitymanager.cpp +++ b/app/view/visibilitymanager.cpp @@ -1,840 +1,842 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "visibilitymanager.h" // local #include "positioner.h" #include "screenedgeghostwindow.h" #include "view.h" #include "windowstracker/currentscreentracker.h" #include "../lattecorona.h" #include "../screenpool.h" #include "../layouts/manager.h" #include "../wm/abstractwindowinterface.h" #include "../../liblatte2/extras.h" // Qt #include // KDE #include #include #include //! Hide Timer can create cases that when it is low it does not allow the //! view to be show. For example !compositing+kwin_edges+hide inteval<50ms const int HIDEMINIMUMINTERVAL = 50; namespace Latte { namespace ViewPart { //! BEGIN: VisiblityManager implementation VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view) : QObject(view) { qDebug() << "VisibilityManager creating..."; m_latteView = qobject_cast(view); m_corona = qobject_cast(view->corona()); m_wm = m_corona->wm(); connect(this, &VisibilityManager::slideInFinished, this, &VisibilityManager::updateHiddenState); connect(this, &VisibilityManager::slideOutFinished, this, &VisibilityManager::updateHiddenState); connect(this, &VisibilityManager::enableKWinEdgesChanged, this, &VisibilityManager::updateKWinEdgesSupport); connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::updateKWinEdgesSupport); if (m_latteView) { connect(m_latteView, &Latte::View::eventTriggered, this, &VisibilityManager::viewEventManager); connect(m_latteView, &Latte::View::byPassWMChanged, this, &VisibilityManager::updateKWinEdgesSupport); connect(m_latteView, &Latte::View::absoluteGeometryChanged, this, [&]() { if (m_mode == Types::AlwaysVisible && m_latteView->screen()) { updateStrutsBasedOnLayoutsAndActivities(); } }); connect(m_latteView, &Latte::View::inEditModeChanged, this, &VisibilityManager::initViewFlags); connect(this, &VisibilityManager::modeChanged, this, [&]() { emit m_latteView->availableScreenRectChangedFrom(m_latteView); }); } m_timerStartUp.setInterval(5000); m_timerStartUp.setSingleShot(true); m_timerShow.setSingleShot(true); m_timerHide.setSingleShot(true); connect(&m_timerShow, &QTimer::timeout, this, [&]() { if (m_isHidden || m_isBelowLayer) { // qDebug() << "must be shown"; emit mustBeShown(); } }); connect(&m_timerHide, &QTimer::timeout, this, [&]() { if (!m_blockHiding && !m_isHidden && !m_isBelowLayer && !m_dragEnter) { // qDebug() << "must be hide"; emit mustBeHide(); } }); restoreConfig(); } VisibilityManager::~VisibilityManager() { qDebug() << "VisibilityManager deleting..."; m_wm->removeViewStruts(*m_latteView); if (m_edgeGhostWindow) { m_edgeGhostWindow->deleteLater(); } } Types::Visibility VisibilityManager::mode() const { return m_mode; } void VisibilityManager::initViewFlags() { if ((m_mode == Types::WindowsCanCover || m_mode == Types::WindowsAlwaysCover) && (!m_latteView->inEditMode())) { setViewOnBackLayer(); } else { setViewOnFrontLayer(); } } void VisibilityManager::setViewOnBackLayer() { m_wm->setViewExtraFlags(m_latteView, false, Types::WindowsAlwaysCover); setIsBelowLayer(true); } void VisibilityManager::setViewOnFrontLayer() { m_wm->setViewExtraFlags(m_latteView, true); setIsBelowLayer(false); } void VisibilityManager::setMode(Latte::Types::Visibility mode) { if (m_mode == mode) return; Q_ASSERT_X(mode != Types::None, staticMetaObject.className(), "set visibility to Types::None"); // clear mode for (auto &c : m_connections) { disconnect(c); } int base{0}; m_publishedStruts = QRect(); if (m_mode == Types::AlwaysVisible) { //! remove struts for old always visible mode m_wm->removeViewStruts(*m_latteView); } m_timerShow.stop(); m_timerHide.stop(); m_mode = mode; initViewFlags(); if (mode != Types::AlwaysVisible && mode != Types::WindowsGoBelow) { m_connections[0] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentDesktopChanged, this, [&] { if (m_raiseOnDesktopChange) { raiseViewTemporarily(); } }); m_connections[1] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentActivityChanged, this, [&]() { if (m_raiseOnActivityChange) { raiseViewTemporarily(); } else { updateHiddenState(); } }); base = 2; } switch (m_mode) { case Types::AlwaysVisible: { if (m_latteView->containment() && m_latteView->screen()) { updateStrutsBasedOnLayoutsAndActivities(); } m_connections[base] = connect(m_latteView, &Latte::View::normalThicknessChanged, this, [&]() { updateStrutsBasedOnLayoutsAndActivities(); }); m_connections[base+1] = connect(m_corona->layoutsManager(), &Layouts::Manager::currentLayoutNameChanged, this, [&]() { if (m_corona && m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { updateStrutsBasedOnLayoutsAndActivities(true); } }); m_connections[base+2] = connect(m_latteView, &Latte::View::activitiesChanged, this, [&]() { if (m_corona && m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { updateStrutsBasedOnLayoutsAndActivities(true); } }); raiseView(true); break; } case Types::AutoHide: { m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() { raiseView(m_containsMouse); }); raiseView(m_containsMouse); break; } case Types::DodgeActive: { m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged , this, &VisibilityManager::dodgeActive); m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::activeWindowTouchingChanged , this, &VisibilityManager::dodgeActive); dodgeActive(); break; } case Types::DodgeMaximized: { m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged , this, &VisibilityManager::dodgeMaximized); m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::activeWindowMaximizedChanged , this, &VisibilityManager::dodgeMaximized); dodgeMaximized(); break; } case Types::DodgeAllWindows: { m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged , this, &VisibilityManager::dodgeAllWindows); m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::existsWindowTouchingChanged , this, &VisibilityManager::dodgeAllWindows); dodgeAllWindows(); break; } case Types::WindowsGoBelow: break; case Types::WindowsCanCover: m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() { if (m_containsMouse) { emit mustBeShown(); } else { raiseView(false); } }); raiseView(m_containsMouse); break; case Types::WindowsAlwaysCover: break; default: break; } m_latteView->containment()->config().writeEntry("visibility", static_cast(m_mode)); updateKWinEdgesSupport(); emit modeChanged(); } void VisibilityManager::updateStrutsBasedOnLayoutsAndActivities(bool forceUpdate) { bool multipleLayoutsAndCurrent = (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts && m_latteView->layout() && !m_latteView->positioner()->inLocationChangeAnimation() && m_latteView->layout()->isCurrent()); if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout || multipleLayoutsAndCurrent) { QRect computedStruts = acceptableStruts(); if (m_publishedStruts != computedStruts || forceUpdate) { //! Force update is needed when very important events happen in DE and there is a chance //! that previously even though struts where sent the DE did not accept them. //! Such a case is when STOPPING an Activity and windows faulty become invisible even //! though they should not. In such case setting struts when the windows are hidden //! the struts do not take any effect m_publishedStruts = computedStruts; m_wm->setViewStruts(*m_latteView, m_publishedStruts, m_latteView->location()); } } else { m_publishedStruts = QRect(); m_wm->removeViewStruts(*m_latteView); } } QRect VisibilityManager::acceptableStruts() { QRect calcs; int screenEdgeMargin = (m_latteView->behaveAsPlasmaPanel() && m_latteView->screenEdgeMarginEnabled()) ? m_latteView->screenEdgeMargin() : 0; int shownThickness = m_latteView->normalThickness() + screenEdgeMargin; switch (m_latteView->location()) { case Plasma::Types::TopEdge: { calcs = QRect(m_latteView->x(), m_latteView->y(), m_latteView->width(), shownThickness); break; } case Plasma::Types::BottomEdge: { int y = m_latteView->y() + m_latteView->height() - shownThickness; calcs = QRect(m_latteView->x(), y, m_latteView->width(), shownThickness); break; } case Plasma::Types::LeftEdge: { calcs = QRect(m_latteView->x(), m_latteView->y(), shownThickness, m_latteView->height()); break; } case Plasma::Types::RightEdge: { int x = m_latteView->x() + m_latteView->width() - shownThickness; calcs = QRect(x, m_latteView->y(), shownThickness, m_latteView->height()); break; } } return calcs; } bool VisibilityManager::raiseOnDesktop() const { return m_raiseOnDesktopChange; } void VisibilityManager::setRaiseOnDesktop(bool enable) { if (enable == m_raiseOnDesktopChange) return; m_raiseOnDesktopChange = enable; emit raiseOnDesktopChanged(); } bool VisibilityManager::raiseOnActivity() const { return m_raiseOnActivityChange; } void VisibilityManager::setRaiseOnActivity(bool enable) { if (enable == m_raiseOnActivityChange) return; m_raiseOnActivityChange = enable; emit raiseOnActivityChanged(); } bool VisibilityManager::isBelowLayer() const { return m_isBelowLayer; } void VisibilityManager::setIsBelowLayer(bool below) { if (m_isBelowLayer == below) { return; } m_isBelowLayer = below; updateGhostWindowState(); emit isBelowLayerChanged(); } bool VisibilityManager::isHidden() const { return m_isHidden; } void VisibilityManager::setIsHidden(bool isHidden) { if (m_isHidden == isHidden) return; if (m_blockHiding && isHidden) { qWarning() << "isHidden property is blocked, ignoring update"; return; } m_isHidden = isHidden; updateGhostWindowState(); emit isHiddenChanged(); } bool VisibilityManager::blockHiding() const { return m_blockHiding; } void VisibilityManager::setBlockHiding(bool blockHiding) { if (m_blockHiding == blockHiding) { return; } m_blockHiding = blockHiding; // qDebug() << "blockHiding:" << blockHiding; if (m_blockHiding) { m_timerHide.stop(); if (m_isHidden) { emit mustBeShown(); } } else { updateHiddenState(); } emit blockHidingChanged(); } int VisibilityManager::timerShow() const { return m_timerShow.interval(); } void VisibilityManager::setTimerShow(int msec) { if (m_timerShow.interval() == msec) { return; } m_timerShow.setInterval(msec); emit timerShowChanged(); } int VisibilityManager::timerHide() const { return m_timerHide.interval(); } void VisibilityManager::setTimerHide(int msec) { int interval = qMax(HIDEMINIMUMINTERVAL, msec); if (m_timerHide.interval() == interval) { return; } m_timerHide.setInterval(interval); emit timerHideChanged(); } bool VisibilityManager::supportsKWinEdges() const { return (m_edgeGhostWindow != nullptr); } void VisibilityManager::updateGhostWindowState() { if (supportsKWinEdges()) { bool inCurrentLayout = (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout || (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts && m_latteView->layout() && !m_latteView->positioner()->inLocationChangeAnimation() && m_latteView->layout()->isCurrent())); if (inCurrentLayout) { if (m_mode == Latte::Types::WindowsCanCover) { m_wm->setActiveEdge(m_edgeGhostWindow, m_isBelowLayer && !m_containsMouse); } else { - bool viewIsFloatingAndMouseOnEdge = + bool viewIsFloatingAndContainsMouse = m_latteView->behaveAsPlasmaPanel() && m_latteView->screenEdgeMarginEnabled() && m_latteView->screenEdgeMargin()>0 - && m_edgeGhostWindow->containsMouse(); + && (m_edgeGhostWindow->containsMouse() || m_containsMouse); - m_wm->setActiveEdge(m_edgeGhostWindow, (m_isHidden && !m_containsMouse) || viewIsFloatingAndMouseOnEdge); + bool activated = (m_isHidden && !m_containsMouse) || viewIsFloatingAndContainsMouse; + + m_wm->setActiveEdge(m_edgeGhostWindow, activated); } } else { m_wm->setActiveEdge(m_edgeGhostWindow, false); } } } void VisibilityManager::hide() { if (KWindowSystem::isPlatformX11()) { m_latteView->hide(); } } void VisibilityManager::show() { if (KWindowSystem::isPlatformX11()) { m_latteView->show(); } } void VisibilityManager::raiseView(bool raise) { if (m_blockHiding) return; if (raise) { m_timerHide.stop(); if (!m_timerShow.isActive()) { m_timerShow.start(); } } else if (!m_dragEnter) { m_timerShow.stop(); if (m_hideNow) { m_hideNow = false; emit mustBeHide(); } else if (!m_timerHide.isActive()) { m_timerHide.start(); } } } void VisibilityManager::raiseViewTemporarily() { if (m_raiseTemporarily) return; m_raiseTemporarily = true; m_timerHide.stop(); m_timerShow.stop(); if (m_isHidden) emit mustBeShown(); QTimer::singleShot(qBound(1800, 2 * m_timerHide.interval(), 3000), this, [&]() { m_raiseTemporarily = false; m_hideNow = true; updateHiddenState(); }); } void VisibilityManager::updateHiddenState() { if (m_dragEnter) return; switch (m_mode) { case Types::AutoHide: case Types::WindowsCanCover: raiseView(m_containsMouse || (m_edgeGhostWindow && m_edgeGhostWindow->containsMouse())); break; case Types::DodgeActive: dodgeActive(); break; case Types::DodgeMaximized: dodgeMaximized(); break; case Types::DodgeAllWindows: dodgeAllWindows(); break; default: break; } } void VisibilityManager::applyActivitiesToHiddenWindows(const QStringList &activities) { if (m_edgeGhostWindow) { m_wm->setWindowOnActivities(*m_edgeGhostWindow, activities); } } void VisibilityManager::dodgeActive() { if (m_raiseTemporarily) return; //!don't send false raiseView signal when containing mouse if (m_containsMouse) { raiseView(true); return; } raiseView(!m_latteView->windowsTracker()->currentScreen()->activeWindowTouching()); } void VisibilityManager::dodgeMaximized() { if (m_raiseTemporarily) return; //!don't send false raiseView signal when containing mouse if (m_containsMouse) { raiseView(true); return; } raiseView(!m_latteView->windowsTracker()->currentScreen()->activeWindowMaximized()); } void VisibilityManager::dodgeAllWindows() { if (m_raiseTemporarily) return; if (m_containsMouse) { raiseView(true); } bool windowIntersects{m_latteView->windowsTracker()->currentScreen()->activeWindowTouching() || m_latteView->windowsTracker()->currentScreen()->existsWindowTouching()}; raiseView(!windowIntersects); } void VisibilityManager::saveConfig() { if (!m_latteView->containment()) return; auto config = m_latteView->containment()->config(); config.writeEntry("enableKWinEdges", m_enableKWinEdgesFromUser); config.writeEntry("timerShow", m_timerShow.interval()); config.writeEntry("timerHide", m_timerHide.interval()); config.writeEntry("raiseOnDesktopChange", m_raiseOnDesktopChange); config.writeEntry("raiseOnActivityChange", m_raiseOnActivityChange); m_latteView->containment()->configNeedsSaving(); } void VisibilityManager::restoreConfig() { if (!m_latteView || !m_latteView->containment()){ return; } auto config = m_latteView->containment()->config(); m_timerShow.setInterval(config.readEntry("timerShow", 0)); m_timerHide.setInterval(qMax(HIDEMINIMUMINTERVAL, config.readEntry("timerHide", 700))); emit timerShowChanged(); emit timerHideChanged(); m_enableKWinEdgesFromUser = config.readEntry("enableKWinEdges", true); emit enableKWinEdgesChanged(); setRaiseOnDesktop(config.readEntry("raiseOnDesktopChange", false)); setRaiseOnActivity(config.readEntry("raiseOnActivityChange", false)); auto storedMode = static_cast(m_latteView->containment()->config().readEntry("visibility", static_cast(Types::DodgeActive))); if (storedMode == Types::AlwaysVisible) { qDebug() << "Loading visibility mode: Always Visible , on startup..."; setMode(Types::AlwaysVisible); } else { connect(&m_timerStartUp, &QTimer::timeout, this, [&]() { if (!m_latteView || !m_latteView->containment()) { return; } auto fMode = static_cast(m_latteView->containment()->config().readEntry("visibility", static_cast(Types::DodgeActive))); qDebug() << "Loading visibility mode:" << fMode << " on startup..."; setMode(fMode); }); connect(m_latteView->containment(), &Plasma::Containment::userConfiguringChanged , this, [&](bool configuring) { if (configuring && m_timerStartUp.isActive()) m_timerStartUp.start(100); }); m_timerStartUp.start(); } connect(m_latteView->containment(), &Plasma::Containment::userConfiguringChanged , this, [&](bool configuring) { if (!configuring) { saveConfig(); } }); } bool VisibilityManager::containsMouse() const { return m_containsMouse; } void VisibilityManager::setContainsMouse(bool contains) { if (m_containsMouse == contains) { return; } m_containsMouse = contains; emit containsMouseChanged(); if (contains && m_mode != Types::AlwaysVisible) { raiseView(true); } } void VisibilityManager::viewEventManager(QEvent *ev) { switch (ev->type()) { case QEvent::Enter: setContainsMouse(true); break; case QEvent::Leave: m_dragEnter = false; setContainsMouse(false); break; case QEvent::DragEnter: m_dragEnter = true; if (m_isHidden) { emit mustBeShown(); } break; case QEvent::DragLeave: case QEvent::Drop: m_dragEnter = false; updateHiddenState(); break; default: break; } } //! KWin Edges Support functions bool VisibilityManager::enableKWinEdges() const { return m_enableKWinEdgesFromUser; } void VisibilityManager::setEnableKWinEdges(bool enable) { if (m_enableKWinEdgesFromUser == enable) { return; } m_enableKWinEdgesFromUser = enable; emit enableKWinEdgesChanged(); } void VisibilityManager::updateKWinEdgesSupport() { if ((m_mode == Types::AutoHide || m_mode == Types::DodgeActive || m_mode == Types::DodgeAllWindows || m_mode == Types::DodgeMaximized) && !m_latteView->byPassWM()) { if (m_enableKWinEdgesFromUser) { createEdgeGhostWindow(); } else if (!m_enableKWinEdgesFromUser) { deleteEdgeGhostWindow(); } } else if (m_mode == Types::WindowsCanCover) { createEdgeGhostWindow(); } else { deleteEdgeGhostWindow(); } } void VisibilityManager::createEdgeGhostWindow() { if (!m_edgeGhostWindow) { m_edgeGhostWindow = new ScreenEdgeGhostWindow(m_latteView); connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::containsMouseChanged, this, [ = ](bool contains) { if (contains) { raiseView(true); } else { m_timerShow.stop(); updateGhostWindowState(); } }); connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::dragEntered, this, [&]() { if (m_isHidden) { emit mustBeShown(); } }); m_connectionsKWinEdges[0] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentActivityChanged, this, [&]() { bool inCurrentLayout = (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout || (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts && m_latteView->layout() && !m_latteView->positioner()->inLocationChangeAnimation() && m_latteView->layout()->isCurrent())); if (m_edgeGhostWindow) { if (inCurrentLayout) { m_wm->setActiveEdge(m_edgeGhostWindow, m_isHidden); } else { m_wm->setActiveEdge(m_edgeGhostWindow, false); } } }); emit supportsKWinEdgesChanged(); } } void VisibilityManager::deleteEdgeGhostWindow() { if (m_edgeGhostWindow) { m_edgeGhostWindow->deleteLater(); m_edgeGhostWindow = nullptr; for (auto &c : m_connectionsKWinEdges) { disconnect(c); } emit supportsKWinEdgesChanged(); } } //! END: VisibilityManager implementation } }