diff --git a/app/view/containmentinterface.cpp b/app/view/containmentinterface.cpp index 64e2ba5e..a72c591b 100644 --- a/app/view/containmentinterface.cpp +++ b/app/view/containmentinterface.cpp @@ -1,556 +1,552 @@ /* * Copyright 2019 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 "containmentinterface.h" // local #include "view.h" #include "../lattecorona.h" #include "../layout/genericlayout.h" #include "../settings/universalsettings.h" // Qt #include // Plasma #include #include #include // KDE #include #include namespace Latte { namespace ViewPart { ContainmentInterface::ContainmentInterface(Latte::View *parent) : QObject(parent), m_view(parent) { m_corona = qobject_cast(m_view->corona()); m_appletsExpandedConnectionsTimer.setInterval(2000); m_appletsExpandedConnectionsTimer.setSingleShot(true); connect(&m_appletsExpandedConnectionsTimer, &QTimer::timeout, this, &ContainmentInterface::updateAppletIsExpandedTracking); connect(m_view, &View::containmentChanged , this, [&]() { if (m_view->containment()) { connect(m_view->containment(), &Plasma::Containment::appletAdded, this, &ContainmentInterface::updateAppletIsExpandedTracking); m_appletsExpandedConnectionsTimer.start(); } }); } ContainmentInterface::~ContainmentInterface() { } void ContainmentInterface::identifyMainItem() { if (m_mainItem) { return; } if (QQuickItem *graphicItem = m_view->containment()->property("_plasma_graphicObject").value()) { const auto &childItems = graphicItem->childItems(); for (QQuickItem *item : childItems) { if (item->objectName() == "containmentViewLayout" ) { m_mainItem = item; identifyMethods(); return; } } } } void ContainmentInterface::identifyMethods() { int aeIndex = m_mainItem->metaObject()->indexOfMethod("activateEntryAtIndex(QVariant)"); int niIndex = m_mainItem->metaObject()->indexOfMethod("newInstanceForEntryAtIndex(QVariant)"); int sbIndex = m_mainItem->metaObject()->indexOfMethod("setShowAppletShortcutBadges(QVariant,QVariant,QVariant,QVariant)"); int afiIndex = m_mainItem->metaObject()->indexOfMethod("appletIdForIndex(QVariant)"); m_activateEntryMethod = m_mainItem->metaObject()->method(aeIndex); m_appletIdForIndexMethod = m_mainItem->metaObject()->method(afiIndex); m_newInstanceMethod = m_mainItem->metaObject()->method(niIndex); m_showShortcutsMethod = m_mainItem->metaObject()->method(sbIndex); } bool ContainmentInterface::applicationLauncherHasGlobalShortcut() const { if (!containsApplicationLauncher()) { return false; } uint launcherAppletId = applicationLauncherId(); const auto applets = m_view->containment()->applets(); for (auto applet : applets) { if (applet->id() == launcherAppletId) { return !applet->globalShortcut().isEmpty(); } } return false; } bool ContainmentInterface::applicationLauncherInPopup() const { if (!containsApplicationLauncher()) { return false; } uint launcherAppletId = applicationLauncherId(); QString launcherPluginId; const auto applets = m_view->containment()->applets(); for (auto applet : applets) { if (applet->id() == launcherAppletId) { launcherPluginId = applet->kPackage().metadata().pluginId(); } } return launcherPluginId != "org.kde.plasma.kickerdash"; } bool ContainmentInterface::containsApplicationLauncher() const { return (applicationLauncherId() >= 0); } bool ContainmentInterface::isCapableToShowShortcutBadges() { identifyMainItem(); if (!m_view->latteTasksArePresent() && m_view->tasksPresent()) { return false; } return m_showShortcutsMethod.isValid(); } int ContainmentInterface::applicationLauncherId() const { const auto applets = m_view->containment()->applets(); auto launcherId{-1}; for (auto applet : applets) { const auto provides = applet->kPackage().metadata().value(QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) { if (!applet->globalShortcut().isEmpty()) { return applet->id(); } else if (launcherId == -1) { launcherId = applet->id(); } } } return launcherId; } bool ContainmentInterface::updateBadgeForLatteTask(const QString identifier, const QString value) { if (!m_view->latteTasksArePresent()) { return false; } const auto &applets = m_view->containment()->applets(); for (auto *applet : applets) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("updateBadge(QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, identifier), Q_ARG(QVariant, value))) { return true; } } } } } } return false; } bool ContainmentInterface::activatePlasmaTask(const int index) { bool containsPlasmaTaskManager{m_view->tasksPresent() && !m_view->latteTasksArePresent()}; if (!containsPlasmaTaskManager) { return false; } const auto &applets = m_view->containment()->applets(); for (auto *applet : applets) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } KPluginMetaData meta = applet->kPackage().metadata(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex{metaObject->indexOfMethod("activateTaskAtIndex(QVariant)")}; if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, index - 1))) { showShortcutBadges(false, true); return true; } } } } } } return false; } bool ContainmentInterface::newInstanceForPlasmaTask(const int index) { bool containsPlasmaTaskManager{m_view->tasksPresent() && !m_view->latteTasksArePresent()}; if (!containsPlasmaTaskManager) { return false; } const auto &applets = m_view->containment()->applets(); for (auto *applet : applets) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } KPluginMetaData meta = applet->kPackage().metadata(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { int methodIndex{metaObject->indexOfMethod("ewInstanceForTaskAtIndex(QVariant)")}; if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, index - 1))) { showShortcutBadges(false, true); return true; } } } } } } return false; } bool ContainmentInterface::activateEntry(const int index) { identifyMainItem(); if (!m_activateEntryMethod.isValid()) { return false; } return m_activateEntryMethod.invoke(m_mainItem, Q_ARG(QVariant, index)); } bool ContainmentInterface::newInstanceForEntry(const int index) { identifyMainItem(); if (!m_newInstanceMethod.isValid()) { return false; } return m_newInstanceMethod.invoke(m_mainItem, Q_ARG(QVariant, index)); } bool ContainmentInterface::hideShortcutBadges() { identifyMainItem(); if (!m_showShortcutsMethod.isValid()) { return false; } return m_showShortcutsMethod.invoke(m_mainItem, Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, -1)); } bool ContainmentInterface::showOnlyMeta() { if (!m_corona->universalSettings()->kwin_metaForwardedToLatte()) { return false; } return showShortcutBadges(false, true); } bool ContainmentInterface::showShortcutBadges(const bool showLatteShortcuts, const bool showMeta) { identifyMainItem(); if (!m_showShortcutsMethod.isValid()) { return false; } int appLauncherId = m_corona->universalSettings()->kwin_metaForwardedToLatte() && showMeta ? applicationLauncherId() : -1; return m_showShortcutsMethod.invoke(m_mainItem, Q_ARG(QVariant, showLatteShortcuts), Q_ARG(QVariant, true), Q_ARG(QVariant, showMeta), Q_ARG(QVariant, appLauncherId)); } int ContainmentInterface::appletIdForIndex(const int index) { identifyMainItem(); if (!m_appletIdForIndexMethod.isValid()) { return false; } QVariant appletId{-1}; m_appletIdForIndexMethod.invoke(m_mainItem, Q_RETURN_ARG(QVariant, appletId), Q_ARG(QVariant, index)); return appletId.toInt(); } void ContainmentInterface::deactivateApplets() { if (!m_view->containment()) { return; } for (const auto applet : m_view->containment()->applets()) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { ai->setExpanded(false); } } } bool ContainmentInterface::appletIsExpandable(const int id) { if (!m_view->containment()) { return false; } for (const auto applet : m_view->containment()->applets()) { if (applet->id() == (uint)id) { if (m_view->layout() && m_view->layout()->isInternalContainment(applet)) { return true; } PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { return (ai->fullRepresentation() != nullptr && ai->preferredRepresentation() != ai->fullRepresentation()); } } } return false; } bool ContainmentInterface::hasExpandedApplet() const { return m_expandedAppletIds.count() > 0; } void ContainmentInterface::addExpandedApplet(const int &id) { if (m_expandedAppletIds.contains(id) && appletIsExpandable(id)) { return; } bool isExpanded = hasExpandedApplet(); m_expandedAppletIds << id; if (isExpanded != hasExpandedApplet()) { emit hasExpandedAppletChanged(); } emit expandedAppletStateChanged(); } void ContainmentInterface::removeExpandedApplet(const int &id) { if (!m_expandedAppletIds.contains(id)) { return; } bool isExpanded = hasExpandedApplet(); m_expandedAppletIds.removeAll(id); if (isExpanded != hasExpandedApplet()) { emit hasExpandedAppletChanged(); } emit expandedAppletStateChanged(); } void ContainmentInterface::on_appletExpandedChanged() { PlasmaQuick::AppletQuickItem *appletItem = static_cast(QObject::sender()); if (appletItem) { if (appletItem->isExpanded()) { addExpandedApplet(appletItem->applet()->id()); } else { removeExpandedApplet(appletItem->applet()->id()); } } } bool ContainmentInterface::appletIsExpanded(const int id) { return m_expandedAppletIds.contains(id); } void ContainmentInterface::toggleAppletExpanded(const int id) { if (!m_view->containment()) { return; } for (const auto applet : m_view->containment()->applets()) { if (applet->id() == (uint)id && !m_view->layout()->isInternalContainment(applet)/*block for internal containments*/) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { - if (!ai->isActivationTogglesExpanded()) { - ai->setActivationTogglesExpanded(true); - } - - emit applet->activated(); + ai->setExpanded(!ai->isExpanded()); } } } } void ContainmentInterface::updateAppletIsExpandedTracking() { if (!m_view->containment()) { return; } for (const auto applet : m_view->containment()->applets()) { if (m_view->layout() && m_view->layout()->isInternalContainment(applet)) { //! internal containment case Plasma::Containment *internalC = m_view->layout()->internalContainmentOf(applet); PlasmaQuick::AppletQuickItem *contAi = applet->property("_plasma_graphicObject").value(); if (contAi && !m_appletsExpandedConnections.contains(contAi)) { m_appletsExpandedConnections[contAi] = connect(contAi, &PlasmaQuick::AppletQuickItem::expandedChanged, this, &ContainmentInterface::on_appletExpandedChanged); connect(contAi, &QObject::destroyed, this, [&, contAi](){ m_appletsExpandedConnections.remove(contAi); removeExpandedApplet(contAi->applet()->id()); }); } for (const auto internalApplet : internalC->applets()) { PlasmaQuick::AppletQuickItem *ai = internalApplet->property("_plasma_graphicObject").value(); if (ai && !m_appletsExpandedConnections.contains(ai) ){ m_appletsExpandedConnections[ai] = connect(ai, &PlasmaQuick::AppletQuickItem::expandedChanged, this, &ContainmentInterface::on_appletExpandedChanged); connect(ai, &QObject::destroyed, this, [&, ai](){ m_appletsExpandedConnections.remove(ai); removeExpandedApplet(ai->applet()->id()); }); } } } else { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai && !m_appletsExpandedConnections.contains(ai)) { m_appletsExpandedConnections[ai] = connect(ai, &PlasmaQuick::AppletQuickItem::expandedChanged, this, &ContainmentInterface::on_appletExpandedChanged); connect(ai, &QObject::destroyed, this, [&, ai](){ m_appletsExpandedConnections.remove(ai); removeExpandedApplet(ai->applet()->id()); }); } } } } } } diff --git a/app/view/visibilitymanager.cpp b/app/view/visibilitymanager.cpp index 0621687e..672cdacf 100644 --- a/app/view/visibilitymanager.cpp +++ b/app/view/visibilitymanager.cpp @@ -1,977 +1,986 @@ /* * 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 "view.h" #include "helpers/floatinggapwindow.h" #include "helpers/screenedgeghostwindow.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::hidingIsBlockedChanged, this, &VisibilityManager::on_hidingIsBlockedChanged); connect(this, &VisibilityManager::slideOutFinished, this, &VisibilityManager::updateHiddenState); connect(this, &VisibilityManager::slideInFinished, 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::behaveAsPlasmaPanelChanged , this, &VisibilityManager::updateKWinEdgesSupport); connect(m_latteView, &Latte::View::byPassWMChanged, this, &VisibilityManager::updateKWinEdgesSupport); connect(m_latteView, &Latte::View::inEditModeChanged, this, &VisibilityManager::initViewFlags); connect(m_latteView, &Latte::View::absoluteGeometryChanged, this, [&]() { if (m_mode == Types::AlwaysVisible && m_latteView->screen()) { updateStrutsBasedOnLayoutsAndActivities(); } }); connect(m_latteView, &Latte::View::screenEdgeMarginEnabledChanged, this, [&]() { if (!m_latteView->screenEdgeMarginEnabled()) { deleteFloatingGapWindow(); } }); 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 (!hidingIsBlocked() && !m_isHidden && !m_isBelowLayer && !m_dragEnter) { if (m_latteView->isFloatingWindow()) { //! first check if mouse is inside the floating gap checkMouseInFloatingArea(); } else { //! immediate call emit mustBeHide(); } } }); restoreConfig(); } VisibilityManager::~VisibilityManager() { qDebug() << "VisibilityManager deleting..."; m_wm->removeViewStruts(*m_latteView); if (m_edgeGhostWindow) { m_edgeGhostWindow->deleteLater(); } if (m_floatingGapWindow) { m_floatingGapWindow->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; case Types::SideBar: m_connections[base] = connect(m_latteView, &Latte::View::inEditModeChanged, this, [&]() { if (!m_latteView->inEditMode()) { //! Give the time to View to change from !behaveAsPlasmaPanel to behaveAsPlasmaPanel //! if this is needed when changing to !inEditMode QTimer::singleShot(100, [this]() { toggleHiddenState(); }); } }); toggleHiddenState(); 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()->inLocationAnimation() && 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 (hidingIsBlocked() && isHidden) { qWarning() << "isHidden property is blocked, ignoring update"; return; } m_isHidden = isHidden; updateGhostWindowState(); emit isHiddenChanged(); } bool VisibilityManager::hidingIsBlocked() const { return (m_blockHidingEvents.count() > 0); } void VisibilityManager::addBlockHidingEvent(const QString &type) { if (m_blockHidingEvents.contains(type) || type.isEmpty()) { return; } //qDebug() << " adding block hiding event :: " << type; bool prevHidingIsBlocked = hidingIsBlocked(); m_blockHidingEvents << type; if (prevHidingIsBlocked != hidingIsBlocked()) { emit hidingIsBlockedChanged(); } } void VisibilityManager::removeBlockHidingEvent(const QString &type) { if (!m_blockHidingEvents.contains(type) || type.isEmpty()) { return; } //qDebug() << " remove block hiding event :: " << type; bool prevHidingIsBlocked = hidingIsBlocked(); m_blockHidingEvents.removeAll(type); if (prevHidingIsBlocked != hidingIsBlocked()) { emit hidingIsBlockedChanged(); } } void VisibilityManager::on_hidingIsBlockedChanged() { if (hidingIsBlocked()) { m_timerHide.stop(); if (m_isHidden) { emit mustBeShown(); } } else { updateHiddenState(); } } 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()->inLocationAnimation() && m_latteView->layout()->isCurrent())); if (inCurrentLayout) { if (m_mode == Latte::Types::WindowsCanCover) { m_wm->setActiveEdge(m_edgeGhostWindow, m_isBelowLayer && !m_containsMouse); } else { bool activated = (m_isHidden && !windowContainsMouse()); m_wm->setActiveEdge(m_edgeGhostWindow, activated); } } else { m_wm->setActiveEdge(m_edgeGhostWindow, false); } } } void VisibilityManager::hide() { if (KWindowSystem::isPlatformX11()) { m_lastMask = m_latteView->mask(); m_latteView->setMask(QRect(-1, -1, 1, 1)); } else { //! wayland case m_latteView->hide(); } } void VisibilityManager::show() { if (KWindowSystem::isPlatformX11()) { if (m_latteView->mask() == QRect(-1, -1 , 1, 1)) { m_latteView->setMask(m_lastMask); } } else { //! wayland case m_latteView->show(); } } void VisibilityManager::raiseView(bool raise) { if (hidingIsBlocked() || m_mode == Latte::Types::SideBar) 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(); }); } +bool VisibilityManager::isValidMode() const +{ + return (m_mode != Types::None && m_mode != Types::NormalWindow); +} + void VisibilityManager::toggleHiddenState() { if (!m_latteView->inEditMode()) { if (m_mode == Latte::Types::SideBar) { + if (m_blockHidingEvents.contains(Q_FUNC_INFO)) { + removeBlockHidingEvent(Q_FUNC_INFO); + } + if (m_isHidden) { emit mustBeShown(); } else { emit mustBeHide(); } } else { if (!m_blockHidingEvents.contains(Q_FUNC_INFO)) { addBlockHidingEvent(Q_FUNC_INFO); } else { removeBlockHidingEvent(Q_FUNC_INFO); } } } } void VisibilityManager::updateHiddenState() { if (m_dragEnter) return; switch (m_mode) { case Types::AutoHide: case Types::WindowsCanCover: raiseView(m_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); } if (m_floatingGapWindow) { m_wm->setWindowOnActivities(*m_floatingGapWindow, 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); return; } 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(); } bool VisibilityManager::windowContainsMouse() { return m_containsMouse || (m_edgeGhostWindow && m_edgeGhostWindow->containsMouse()); } void VisibilityManager::checkMouseInFloatingArea() { if (m_latteView->isFloatingWindow()) { if (!m_floatingGapWindow) { createFloatingGapWindow(); } m_floatingGapWindow->callAsyncContainsMouse(); } } 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 && m_mode != Latte::Types::SideBar) { 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 || m_latteView->behaveAsPlasmaPanel()) { createEdgeGhostWindow(); if (m_latteView->isFloatingWindow()) { createFloatingGapWindow(); } } else if (!m_enableKWinEdgesFromUser) { deleteEdgeGhostWindow(); deleteFloatingGapWindow(); } } else if (m_mode == Types::WindowsCanCover) { createEdgeGhostWindow(); } else { deleteEdgeGhostWindow(); deleteFloatingGapWindow(); } } 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()->inLocationAnimation() && 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(); } } void VisibilityManager::createFloatingGapWindow() { if (!m_floatingGapWindow) { m_floatingGapWindow = new FloatingGapWindow(m_latteView); connect(m_floatingGapWindow, &FloatingGapWindow::asyncContainsMouseChanged, this, [ = ](bool contains) { if (contains) { if (m_latteView->isFloatingWindow() && !m_isHidden) { //! immediate call after contains mouse checks for mouse in sensitive floating areas updateHiddenState(); } } else { if (m_latteView->isFloatingWindow() && !m_isHidden) { //! immediate call after contains mouse checks for mouse in sensitive floating areas emit mustBeHide(); } } }); } } void VisibilityManager::deleteFloatingGapWindow() { if (m_floatingGapWindow) { m_floatingGapWindow->deleteLater(); m_floatingGapWindow = nullptr; } } bool VisibilityManager::supportsFloatingGap() const { return (m_floatingGapWindow != nullptr); } //! END: VisibilityManager implementation } } diff --git a/app/view/visibilitymanager.h b/app/view/visibilitymanager.h index 4300c92f..345a5589 100644 --- a/app/view/visibilitymanager.h +++ b/app/view/visibilitymanager.h @@ -1,223 +1,225 @@ /* * 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 . */ #ifndef VISIBILITYMANAGER_H #define VISIBILITYMANAGER_H #include // local #include "../plasma/quick/containmentview.h" #include "../../liblatte2/types.h" // Qt #include #include // Plasma #include namespace Latte { class Corona; class View; namespace ViewPart { class FloatingGapWindow; class ScreenEdgeGhostWindow; } namespace WindowSystem { class AbstractWindowInterface; } } namespace Latte { namespace ViewPart { class VisibilityManager : public QObject { Q_OBJECT Q_PROPERTY(bool hidingIsBlocked READ hidingIsBlocked NOTIFY hidingIsBlockedChanged) Q_PROPERTY(Latte::Types::Visibility mode READ mode WRITE setMode NOTIFY modeChanged) Q_PROPERTY(bool raiseOnDesktop READ raiseOnDesktop WRITE setRaiseOnDesktop NOTIFY raiseOnDesktopChanged) Q_PROPERTY(bool raiseOnActivity READ raiseOnActivity WRITE setRaiseOnActivity NOTIFY raiseOnActivityChanged) Q_PROPERTY(bool isHidden READ isHidden WRITE setIsHidden NOTIFY isHiddenChanged) Q_PROPERTY(bool isBelowLayer READ isBelowLayer NOTIFY isBelowLayerChanged) Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged) //! KWin Edges Support Options Q_PROPERTY(bool enableKWinEdges READ enableKWinEdges WRITE setEnableKWinEdges NOTIFY enableKWinEdgesChanged) Q_PROPERTY(bool supportsKWinEdges READ supportsKWinEdges NOTIFY supportsKWinEdgesChanged) Q_PROPERTY(int timerShow READ timerShow WRITE setTimerShow NOTIFY timerShowChanged) Q_PROPERTY(int timerHide READ timerHide WRITE setTimerHide NOTIFY timerHideChanged) public: explicit VisibilityManager(PlasmaQuick::ContainmentView *view); virtual ~VisibilityManager(); Latte::Types::Visibility mode() const; void setMode(Latte::Types::Visibility mode); void applyActivitiesToHiddenWindows(const QStringList &activities); bool raiseOnDesktop() const; void setRaiseOnDesktop(bool enable); bool raiseOnActivity() const; void setRaiseOnActivity(bool enable); bool isBelowLayer() const; bool isHidden() const; void setIsHidden(bool isHidden); bool hidingIsBlocked() const; bool containsMouse() const; int timerShow() const; void setTimerShow(int msec); int timerHide() const; void setTimerHide(int msec); //! KWin Edges Support functions bool enableKWinEdges() const; void setEnableKWinEdges(bool enable); bool supportsKWinEdges() const; //! Used mostly to show / hide SideBars void toggleHiddenState(); public slots: Q_INVOKABLE void hide(); Q_INVOKABLE void show(); Q_INVOKABLE void setViewOnBackLayer(); Q_INVOKABLE void setViewOnFrontLayer(); Q_INVOKABLE void addBlockHidingEvent(const QString &type); Q_INVOKABLE void removeBlockHidingEvent(const QString &type); void initViewFlags(); signals: void mustBeShown(); void mustBeHide(); void slideOutFinished(); void slideInFinished(); void modeChanged(); void raiseOnDesktopChanged(); void raiseOnActivityChanged(); void isBelowLayerChanged(); void isHiddenChanged(); void hidingIsBlockedChanged(); void containsMouseChanged(); void timerShowChanged(); void timerHideChanged(); //! KWin Edges Support signals void enableKWinEdgesChanged(); void supportsKWinEdgesChanged(); private slots: void saveConfig(); void restoreConfig(); void setIsBelowLayer(bool below); void on_hidingIsBlockedChanged(); //! KWin Edges Support functions void updateKWinEdgesSupport(); private: void setContainsMouse(bool contains); void raiseView(bool raise); void raiseViewTemporarily(); //! KWin Edges Support functions void createEdgeGhostWindow(); void deleteEdgeGhostWindow(); void updateGhostWindowState(); //! Floating Gap Support functions void createFloatingGapWindow(); void deleteFloatingGapWindow(); bool supportsFloatingGap() const; void updateStrutsBasedOnLayoutsAndActivities(bool forceUpdate = false); void viewEventManager(QEvent *ev); void checkMouseInFloatingArea(); bool windowContainsMouse(); QRect acceptableStruts(); private slots: void dodgeAllWindows(); void dodgeActive(); void dodgeMaximized(); void updateHiddenState(); + bool isValidMode() const; + private: WindowSystem::AbstractWindowInterface *m_wm; Types::Visibility m_mode{Types::None}; std::array m_connections; QTimer m_timerShow; QTimer m_timerHide; QTimer m_timerStartUp; bool m_isBelowLayer{false}; bool m_isHidden{false}; bool m_dragEnter{false}; bool m_containsMouse{false}; bool m_raiseTemporarily{false}; bool m_raiseOnDesktopChange{false}; bool m_raiseOnActivityChange{false}; bool m_hideNow{false}; QStringList m_blockHidingEvents; QRect m_publishedStruts; QRegion m_lastMask; //! KWin Edges bool m_enableKWinEdgesFromUser{true}; std::array m_connectionsKWinEdges; ScreenEdgeGhostWindow *m_edgeGhostWindow{nullptr}; //! Floating Gap FloatingGapWindow *m_floatingGapWindow{nullptr}; Latte::Corona *m_corona{nullptr}; Latte::View *m_latteView{nullptr}; }; } } #endif // VISIBILITYMANAGER_H