diff --git a/app/view/view.cpp b/app/view/view.cpp index 2ca5a481..b5eabc3e 100644 --- a/app/view/view.cpp +++ b/app/view/view.cpp @@ -1,1553 +1,1568 @@ /* * 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 "view.h" // local #include "contextmenu.h" #include "effects.h" #include "positioner.h" #include "visibilitymanager.h" #include "settings/primaryconfigview.h" #include "settings/secondaryconfigview.h" #include "../apptypes.h" #include "../lattecorona.h" #include "../indicator/factory.h" #include "../layout/genericlayout.h" #include "../layouts/manager.h" #include "../plasma/extended/theme.h" #include "../screenpool.h" #include "../settings/universalsettings.h" #include "../shortcuts/globalshortcuts.h" #include "../shortcuts/shortcutstracker.h" // Qt #include #include #include #include #include #include #include // KDe #include #include #include #include #include // Plasma #include #include #include #define BLOCKHIDINGDRAGTYPE "View::ContainsDrag()" #define BLOCKHIDINGNEEDSATTENTIONTYPE "View::Containment::NeedsAttentionState()" #define BLOCKHIDINGREQUESTSINPUTTYPE "View::Containment::RequestsInputState()" namespace Latte { //! both alwaysVisible and byPassWM are passed through corona because //! during the view window creation containment hasn't been set, but these variables //! are needed in order for window flags to be set correctly View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassWM) : PlasmaQuick::ContainmentView(corona), m_contextMenu(new ViewPart::ContextMenu(this)), m_effects(new ViewPart::Effects(this)), m_interface(new ViewPart::ContainmentInterface(this)) { //! needs to be created after Effects because it catches some of its signals //! and avoid a crash from View::winId() at the same time m_positioner = new ViewPart::Positioner(this); // setTitle(corona->kPackage().metadata().name()); setIcon(qGuiApp->windowIcon()); setResizeMode(QuickViewSharedEngine::SizeRootObjectToView); setColor(QColor(Qt::transparent)); setDefaultAlphaBuffer(true); setClearBeforeRendering(true); const auto flags = Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus; if (byPassWM) { setFlags(flags | Qt::BypassWindowManagerHint); } else { setFlags(flags); } if (targetScreen) m_positioner->setScreenToFollow(targetScreen); else m_positioner->setScreenToFollow(qGuiApp->primaryScreen()); m_releaseGrabTimer.setInterval(400); m_releaseGrabTimer.setSingleShot(true); connect(&m_releaseGrabTimer, &QTimer::timeout, this, &View::releaseGrab); connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::updateTransientWindowsTracking); connect(this, &View::containmentChanged , this, [ &, byPassWM]() { qDebug() << "dock view c++ containment changed 1..."; if (!this->containment()) return; qDebug() << "dock view c++ containment changed 2..."; setTitle(validTitle()); //! First load default values from file restoreConfig(); //! Afterwards override that values in case during creation something different is needed setByPassWM(byPassWM); //! Check the screen assigned to this dock reconsiderScreen(); //! needs to be created before visibility creation because visibility uses it if (!m_windowsTracker) { m_windowsTracker = new ViewPart::WindowsTracker(this); emit windowsTrackerChanged(); } if (!m_visibility) { m_visibility = new ViewPart::VisibilityManager(this); connect(m_visibility, &ViewPart::VisibilityManager::isHiddenChanged, this, [&]() { if (m_visibility->isHidden()) { m_interface->deactivateApplets(); } }); connect(m_visibility, &ViewPart::VisibilityManager::containsMouseChanged, this, &View::updateTransientWindowsTracking); emit visibilityChanged(); } if (!m_indicator) { m_indicator = new ViewPart::Indicator(this); emit indicatorChanged(); } connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus))); }, Qt::DirectConnection); m_corona = qobject_cast(this->corona()); if (m_corona) { connect(m_corona, &Latte::Corona::viewLocationChanged, this, &View::dockLocationChanged); } } View::~View() { m_inDelete = true; //! clear Layout connections m_visibleHackTimer1.stop(); m_visibleHackTimer2.stop(); for (auto &c : connectionsLayout) { disconnect(c); } //! unload indicators if (m_indicator) { m_indicator->unloadIndicators(); } disconnectSensitiveSignals(); disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus))); qDebug() << "dock view deleting..."; //! this disconnect does not free up connections correctly when //! latteView is deleted. A crash for this example is the following: //! switch to Alternative Session and disable compositing, //! the signal creating the crash was probably from deleted //! windows. //! this->disconnect(); if (m_configView) { delete m_configView; } if (m_contextMenu) { delete m_contextMenu; } //needs to be deleted before Effects because it catches some of its signals if (m_positioner) { delete m_positioner; } if (m_effects) { delete m_effects; } if (m_indicator) { delete m_indicator; } if (m_interface) { delete m_interface; } if (m_visibility) { delete m_visibility; } if (m_windowsTracker) { delete m_windowsTracker; } } void View::init(Plasma::Containment *plasma_containment) { connect(this, &QQuickWindow::xChanged, this, &View::xChanged); connect(this, &QQuickWindow::xChanged, this, &View::updateAbsoluteGeometry); connect(this, &QQuickWindow::yChanged, this, &View::yChanged); connect(this, &QQuickWindow::yChanged, this, &View::updateAbsoluteGeometry); connect(this, &QQuickWindow::widthChanged, this, &View::widthChanged); connect(this, &QQuickWindow::widthChanged, this, &View::updateAbsoluteGeometry); connect(this, &QQuickWindow::heightChanged, this, &View::heightChanged); connect(this, &QQuickWindow::heightChanged, this, &View::updateAbsoluteGeometry); connect(this, &View::activitiesChanged, this, &View::applyActivitiesToWindows); connect(this, &View::localGeometryChanged, this, [&]() { updateAbsoluteGeometry(); }); connect(this, &View::screenEdgeMarginEnabledChanged, this, [&]() { updateAbsoluteGeometry(); }); //! used in order to disconnect it when it should NOT be called because it creates crashes connect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom); connect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom); connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot); connect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop); connect(this, &View::byPassWMChanged, this, &View::saveConfig); connect(this, &View::isPreferredForShortcutsChanged, this, &View::saveConfig); connect(this, &View::onPrimaryChanged, this, &View::saveConfig); connect(this, &View::typeChanged, this, &View::saveConfig); connect(this, &View::normalThicknessChanged, this, [&]() { emit availableScreenRectChangedFrom(this); }); connect(m_effects, &ViewPart::Effects::innerShadowChanged, this, [&]() { emit availableScreenRectChangedFrom(this); }); connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &View::hideWindowsForSlidingOut); connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &View::screenGeometryChanged); connect(m_positioner, &ViewPart::Positioner::windowSizeChanged, this, [&]() { emit availableScreenRectChangedFrom(this); }); connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged); connect(m_interface, &ViewPart::ContainmentInterface::hasExpandedAppletChanged, this, &View::verticalUnityViewHasFocus); //! View sends this signal in order to avoid crashes from ViewPart::Indicator when the view is recreated connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorChanged, this, [&](const QString &indicatorId) { emit indicatorPluginChanged(indicatorId); }); connect(this, &View::indicatorPluginChanged, this, [&](const QString &indicatorId) { if (m_indicator && m_indicator->isCustomIndicator() && m_indicator->type() == indicatorId) { reloadSource(); } }); connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorRemoved, this, &View::indicatorPluginRemoved); //! Assign app interfaces in be accessible through containment graphic item QQuickItem *containmentGraphicItem = qobject_cast(plasma_containment->property("_plasma_graphicObject").value()); if (containmentGraphicItem) { containmentGraphicItem->setProperty("_latte_globalShortcuts_object", QVariant::fromValue(m_corona->globalShortcuts()->shortcutsTracker())); containmentGraphicItem->setProperty("_latte_layoutsManager_object", QVariant::fromValue(m_corona->layoutsManager())); containmentGraphicItem->setProperty("_latte_themeExtended_object", QVariant::fromValue(m_corona->themeExtended())); containmentGraphicItem->setProperty("_latte_universalSettings_object", QVariant::fromValue(m_corona->universalSettings())); containmentGraphicItem->setProperty("_latte_view_object", QVariant::fromValue(this)); } setSource(corona()->kPackage().filePath("lattedockui")); m_positioner->syncGeometry(); qDebug() << "SOURCE:" << source(); } void View::reloadSource() { if (m_layout && containment()) { if (settingsWindowIsShown()) { m_configView->deleteLater(); } engine()->clearComponentCache(); m_layout->recreateView(containment(), settingsWindowIsShown()); } } bool View::inDelete() const { return m_inDelete; } bool View::inReadyState() const { return (m_layout != nullptr); } void View::disconnectSensitiveSignals() { m_initLayoutTimer.stop(); disconnect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom); disconnect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom); disconnect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot); disconnect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop); setLayout(nullptr); } void View::availableScreenRectChangedFromSlot(View *origin) { if (m_inDelete || origin == this) return; if (formFactor() == Plasma::Types::Vertical) { m_positioner->syncGeometry(); } } void View::setupWaylandIntegration() { if (m_shellSurface) return; if (Latte::Corona *c = qobject_cast(corona())) { using namespace KWayland::Client; PlasmaShell *interface {c->waylandCoronaInterface()}; if (!interface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; m_shellSurface = interface->createSurface(s, this); qDebug() << "WAYLAND dock window surface was created..."; if (m_visibility) { m_visibility->initViewFlags(); } } } KWayland::Client::PlasmaShellSurface *View::surface() { return m_shellSurface; } //! the main function which decides if this dock is at the //! correct screen void View::reconsiderScreen() { m_positioner->reconsiderScreen(); } void View::copyView() { m_layout->copyView(containment()); } void View::removeView() { if (m_layout && m_layout->viewsCount() > 1) { m_inDelete = true; QAction *removeAct = this->containment()->actions()->action(QStringLiteral("remove")); if (removeAct) { removeAct->trigger(); } } } bool View::settingsWindowIsShown() { auto configView = qobject_cast(m_configView); return (configView != nullptr); } void View::showSettingsWindow() { if (!settingsWindowIsShown()) { emit m_visibility->mustBeShown(); showConfigurationInterface(containment()); applyActivitiesToWindows(); } } PlasmaQuick::ConfigView *View::configView() { return m_configView; } void View::showConfigurationInterface(Plasma::Applet *applet) { if (!applet || !applet->containment()) return; Plasma::Containment *c = qobject_cast(applet); if (m_configView && c && c->isContainment() && c == this->containment()) { if (m_configView->isVisible()) { m_configView->hide(); } else { m_configView->show(); } return; } else if (m_configView) { if (m_configView->applet() == applet) { m_configView->show(); if (KWindowSystem::isPlatformX11()) { m_configView->requestActivate(); } return; } else { m_configView->hide(); } } bool delayConfigView = false; if (c && containment() && c->isContainment() && c->id() == this->containment()->id()) { m_configView = new ViewPart::PrimaryConfigView(c, this); delayConfigView = true; } else { m_configView = new PlasmaQuick::ConfigView(applet); } m_configView.data()->init(); if (!delayConfigView) { m_configView->show(); } else { //add a timer for showing the configuration window the first time it is //created in order to give the containment's layouts the time to //calculate the window's height QTimer::singleShot(150, [this]() { if (m_configView) { m_configView->show(); } }); } } QRect View::localGeometry() const { return m_localGeometry; } void View::setLocalGeometry(const QRect &geometry) { if (m_localGeometry == geometry) { return; } m_localGeometry = geometry; emit localGeometryChanged(); } QString View::validTitle() const { if (!containment()) { return QString(); } return QString("#view#" + QString::number(containment()->id())); } void View::updateAbsoluteGeometry(bool bypassChecks) { //! there was a -1 in height and width here. The reason of this //! if I remember correctly was related to multi-screen but I cant //! remember exactly the reason, something related to right edge in //! multi screen environment. BUT this was breaking the entire AlwaysVisible //! experience with struts. Removing them in order to restore correct //! behavior and keeping this comment in order to check for //! multi-screen breakage QRect absGeometry = m_localGeometry; absGeometry.moveLeft(x() + m_localGeometry.x()); absGeometry.moveTop(y() + m_localGeometry.y()); if (behaveAsPlasmaPanel()) { int currentScreenEdgeMargin = m_screenEdgeMarginEnabled ? qMax(0, m_screenEdgeMargin) : 0; if (location() == Plasma::Types::BottomEdge) { absGeometry.moveTop(screenGeometry().bottom() - currentScreenEdgeMargin - m_normalThickness); } else if (location() == Plasma::Types::TopEdge) { absGeometry.moveTop(screenGeometry().top() + currentScreenEdgeMargin); } else if (location() == Plasma::Types::LeftEdge) { absGeometry.moveLeft(screenGeometry().left() + currentScreenEdgeMargin); } else if (location() == Plasma::Types::RightEdge) { absGeometry.moveLeft(screenGeometry().right() - currentScreenEdgeMargin - m_normalThickness); } } if (m_absoluteGeometry == absGeometry && !bypassChecks) { return; } if (m_absoluteGeometry != absGeometry) { m_absoluteGeometry = absGeometry; emit absoluteGeometryChanged(m_absoluteGeometry); } //! this is needed in order to update correctly the screenGeometries if (visibility() && corona() && visibility()->mode() == Types::AlwaysVisible) { //! main use of BYPASSCKECKS is from Positioner when the view changes screens emit availableScreenRectChangedFrom(this); emit availableScreenRegionChangedFrom(this); } } void View::statusChanged(Plasma::Types::ItemStatus status) { if (!containment()) { return; } if (status == Plasma::Types::NeedsAttentionStatus) { m_visibility->addBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE); m_visibility->initViewFlags(); } else if (status == Plasma::Types::AcceptingInputStatus) { m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE); setFlags(flags() & ~Qt::WindowDoesNotAcceptFocus); KWindowSystem::forceActiveWindow(winId()); } else { updateTransientWindowsTracking(); m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE); m_visibility->initViewFlags(); } } void View::addTransientWindow(QWindow *window) { if (!m_transientWindows.contains(window)) { m_transientWindows.append(window); QString winPtrStr = "0x" + QString::number((qulonglong)window,16); m_visibility->addBlockHidingEvent(winPtrStr); connect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow); } } void View::removeTransientWindow(const bool &visible) { QWindow *window = static_cast(QObject::sender()); if (window && !visible) { QString winPtrStr = "0x" + QString::number((qulonglong)window,16); m_visibility->removeBlockHidingEvent(winPtrStr); disconnect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow); m_transientWindows.removeAll(window); updateTransientWindowsTracking(); } } void View::updateTransientWindowsTracking() { for(QWindow *window: qApp->topLevelWindows()) { if (window->transientParent() == this){ if (window->isVisible()) { addTransientWindow(window); break; } } } } Types::ViewType View::type() const { return m_type; } void View::setType(Types::ViewType type) { if (m_type == type) { return; } m_type = type; emit typeChanged(); } bool View::alternativesIsShown() const { return m_alternativesIsShown; } void View::setAlternativesIsShown(bool show) { if (m_alternativesIsShown == show) { return; } m_alternativesIsShown = show; emit alternativesIsShownChanged(); } bool View::containsDrag() const { return m_containsDrag; } void View::setContainsDrag(bool contains) { if (m_containsDrag == contains) { return; } m_containsDrag = contains; if (m_containsDrag) { m_visibility->addBlockHidingEvent(BLOCKHIDINGDRAGTYPE); } else { m_visibility->removeBlockHidingEvent(BLOCKHIDINGDRAGTYPE); } emit containsDragChanged(); } bool View::containsMouse() const { return m_containsMouse; } bool View::contextMenuIsShown() const { if (!m_contextMenu) { return false; } return m_contextMenu->menu(); } int View::currentThickness() const { if (formFactor() == Plasma::Types::Vertical) { return m_effects->mask().isNull() ? width() : m_effects->mask().width() - m_effects->innerShadow(); } else { return m_effects->mask().isNull() ? height() : m_effects->mask().height() - m_effects->innerShadow(); } } int View::normalThickness() const { return m_normalThickness; } void View::setNormalThickness(int thickness) { if (m_normalThickness == thickness) { return; } m_normalThickness = thickness; emit normalThicknessChanged(); } +int View::headThicknessGap() const +{ + return m_headThicknessGap; +} + +void View::setHeadThicknessGap(int thickness) +{ + if (m_headThicknessGap == thickness) { + return; + } + + m_headThicknessGap = thickness; + emit headThicknessGapChanged(); +} + bool View::byPassWM() const { return m_byPassWM; } void View::setByPassWM(bool bypass) { if (m_byPassWM == bypass) { return; } m_byPassWM = bypass; emit byPassWMChanged(); } bool View::behaveAsPlasmaPanel() const { return m_behaveAsPlasmaPanel; } void View::setBehaveAsPlasmaPanel(bool behavior) { if (m_behaveAsPlasmaPanel == behavior) { return; } m_behaveAsPlasmaPanel = behavior; emit behaveAsPlasmaPanelChanged(); } bool View::inEditMode() const { return m_inEditMode; } void View::setInEditMode(bool edit) { if (m_inEditMode == edit) { return; } m_inEditMode = edit; emit inEditModeChanged(); } bool View::isFloatingWindow() const { return m_behaveAsPlasmaPanel && m_screenEdgeMarginEnabled && (m_screenEdgeMargin>0); } bool View::isPreferredForShortcuts() const { return m_isPreferredForShortcuts; } void View::setIsPreferredForShortcuts(bool preferred) { if (m_isPreferredForShortcuts == preferred) { return; } m_isPreferredForShortcuts = preferred; emit isPreferredForShortcutsChanged(); if (m_isPreferredForShortcuts && m_layout) { emit m_layout->preferredViewForShortcutsChanged(this); } } bool View::latteTasksArePresent() const { return m_latteTasksArePresent; } void View::setLatteTasksArePresent(bool present) { if (m_latteTasksArePresent == present) { return; } m_latteTasksArePresent = present; emit latteTasksArePresentChanged(); } bool View::inSettingsAdvancedMode() const { if (m_configView) { auto configView = qobject_cast(m_configView); if (configView) { return configView->inAdvancedMode(); } } return false; } bool View::isTouchingBottomViewAndIsBusy() const { return m_isTouchingBottomViewAndIsBusy; } void View::setIsTouchingBottomViewAndIsBusy(bool touchAndBusy) { if (m_isTouchingBottomViewAndIsBusy == touchAndBusy) { return; } m_isTouchingBottomViewAndIsBusy = touchAndBusy; emit isTouchingBottomViewAndIsBusyChanged(); } bool View::isTouchingTopViewAndIsBusy() const { return m_isTouchingTopViewAndIsBusy; } void View::setIsTouchingTopViewAndIsBusy(bool touchAndBusy) { if (m_isTouchingTopViewAndIsBusy == touchAndBusy) { return; } m_isTouchingTopViewAndIsBusy = touchAndBusy; emit isTouchingTopViewAndIsBusyChanged(); } void View::preferredViewForShortcutsChangedSlot(Latte::View *view) { if (view != this) { setIsPreferredForShortcuts(false); } } bool View::onPrimary() const { return m_onPrimary; } void View::setOnPrimary(bool flag) { if (m_onPrimary == flag) { return; } m_onPrimary = flag; emit onPrimaryChanged(); } float View::maxLength() const { return m_maxLength; } void View::setMaxLength(float length) { if (m_maxLength == length) { return; } m_maxLength = length; emit maxLengthChanged(); } int View::editThickness() const { return m_editThickness; } void View::setEditThickness(int thickness) { if (m_editThickness == thickness) { return; } m_editThickness = thickness; emit editThicknessChanged(); } int View::maxThickness() const { return m_maxThickness; } void View::setMaxThickness(int thickness) { if (m_maxThickness == thickness) return; m_maxThickness = thickness; emit maxThicknessChanged(); } int View::alignment() const { return m_alignment; } void View::setAlignment(int alignment) { Types::Alignment align = static_cast(alignment); if (m_alignment == alignment) { return; } m_alignment = align; emit alignmentChanged(); } QRect View::absoluteGeometry() const { return m_absoluteGeometry; } QRect View::screenGeometry() const { if (this->screen()) { QRect geom = this->screen()->geometry(); return geom; } return QRect(); } float View::offset() const { return m_offset; } void View::setOffset(float offset) { if (m_offset == offset) { return; } m_offset = offset; emit offsetChanged(); } bool View::screenEdgeMarginEnabled() const { return m_screenEdgeMarginEnabled; } void View::setScreenEdgeMarginEnabled(bool enabled) { if (m_screenEdgeMarginEnabled == enabled) { return; } m_screenEdgeMarginEnabled = enabled; emit screenEdgeMarginEnabledChanged(); } int View::screenEdgeMargin() const { return m_screenEdgeMargin; } void View::setScreenEdgeMargin(int margin) { if (m_screenEdgeMargin == margin) { return; } m_screenEdgeMargin = margin; emit screenEdgeMarginChanged(); } int View::fontPixelSize() const { return m_fontPixelSize; } void View::setFontPixelSize(int size) { if (m_fontPixelSize == size) { return; } m_fontPixelSize = size; emit fontPixelSizeChanged(); } bool View::isOnAllActivities() const { return m_activities.isEmpty() || m_activities[0] == "0"; } bool View::isOnActivity(const QString &activity) const { return isOnAllActivities() || m_activities.contains(activity); } QStringList View::activities() const { QStringList running; QStringList runningAll = m_corona->activitiesConsumer()->runningActivities(); for(int i=0; isetWindowOnActivities(*this, runningActivities); if (m_configView) { m_windowsTracker->setWindowOnActivities(*m_configView, runningActivities); auto configView = qobject_cast(m_configView); if (configView && configView->secondaryWindow()) { m_windowsTracker->setWindowOnActivities(*configView->secondaryWindow(), runningActivities); } } if (m_visibility->supportsKWinEdges()) { m_visibility->applyActivitiesToHiddenWindows(runningActivities); } } } Layout::GenericLayout *View::layout() const { return m_layout; } void View::setLayout(Layout::GenericLayout *layout) { if (m_layout == layout) { return; } // clear mode for (auto &c : connectionsLayout) { disconnect(c); } m_layout = layout; if (m_layout) { connectionsLayout << connect(containment(), &Plasma::Applet::destroyedChanged, m_layout, &Layout::GenericLayout::destroyedChanged); connectionsLayout << connect(containment(), &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::viewLocationChanged); connectionsLayout << connect(containment(), &Plasma::Containment::appletAlternativesRequested, m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection); if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { connectionsLayout << connect(containment(), &Plasma::Containment::appletCreated, m_layout, &Layout::GenericLayout::appletCreated); } connectionsLayout << connect(m_positioner, &Latte::ViewPart::Positioner::edgeChanged, m_layout, &Layout::GenericLayout::viewEdgeChanged); //! Sometimes the activity isnt completely ready, by adding a delay //! we try to catch up m_initLayoutTimer.setInterval(100); m_initLayoutTimer.setSingleShot(true); connectionsLayout << connect(&m_initLayoutTimer, &QTimer::timeout, this, [&]() { if (m_layout && m_visibility) { setActivities(m_layout->appliedActivities()); qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_layout->name() << " - activities: " << m_activities; } }); m_initLayoutTimer.start(); connectionsLayout << connect(m_layout, &Layout::GenericLayout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot); connectionsLayout << connect(m_layout, &Layout::GenericLayout::lastConfigViewForChanged, this, &View::configViewCreatedFor); Latte::Corona *latteCorona = qobject_cast(this->corona()); connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() { if (m_layout && m_visibility) { setActivities(m_layout->appliedActivities()); applyActivitiesToWindows(); qDebug() << "DOCK VIEW FROM LAYOUT (currentActivityChanged) ::: " << m_layout->name() << " - activities: " << m_activities; } }); if (latteCorona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (m_layout && m_visibility) { setActivities(m_layout->appliedActivities()); qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_layout->name() << " - activities: " << m_activities; } }); connectionsLayout << connect(m_layout, &Layout::GenericLayout::activitiesChanged, this, [&]() { if (m_layout) { setActivities(m_layout->appliedActivities()); } }); connectionsLayout << connect(latteCorona->layoutsManager(), &Layouts::Manager::layoutsChanged, this, [&]() { if (m_layout) { setActivities(m_layout->appliedActivities()); } }); //! BEGIN OF KWIN HACK //! IMPORTANT ::: Fixing KWin Faulty Behavior that KWin hides ALL Views when an Activity stops //! with no reason!! m_visibleHackTimer1.setInterval(400); m_visibleHackTimer2.setInterval(2500); m_visibleHackTimer1.setSingleShot(true); m_visibleHackTimer2.setSingleShot(true); connectionsLayout << connect(this, &QWindow::visibleChanged, this, [&]() { if (m_layout && !inDelete() & !isVisible()) { m_visibleHackTimer1.start(); m_visibleHackTimer2.start(); } }); connectionsLayout << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() { applyActivitiesToWindows(); emit activitiesChanged(); if (m_layout && !inDelete() & !isVisible()) { show(); //qDebug() << "View:: Enforce reshow from timer 1..."; emit forcedShown(); } else if (m_layout && isVisible()){ m_inDelete = false; //qDebug() << "View:: No needed reshow from timer 1..."; } }); connectionsLayout << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() { applyActivitiesToWindows(); emit activitiesChanged(); if (m_layout && !inDelete() & !isVisible()) { show(); //qDebug() << "View:: Enforce reshow from timer 1..."; emit forcedShown(); } else if (m_layout && isVisible()){ m_inDelete = false; //qDebug() << "View:: No needed reshow from timer 1..."; } }); //! END OF KWIN HACK } emit layoutChanged(); } else { m_activities.clear(); } } void View::moveToLayout(QString layoutName) { if (!m_layout) { return; } QList containments = m_layout->unassignFromLayout(this); Latte::Corona *latteCorona = qobject_cast(this->corona()); if (latteCorona && containments.size() > 0) { Layout::GenericLayout *newLayout = latteCorona->layoutsManager()->synchronizer()->layout(layoutName); if (newLayout) { newLayout->assignToLayout(this, containments); } } } void View::configViewCreatedFor(Latte::View *view) { if (view!=this && m_configView) { //! for each layout only one dock should show its configuration windows //! otherwise we could reach a point that because a settings window //! is below another Latte View its options are not reachable auto configDialog = qobject_cast(m_configView); if (configDialog) { configDialog->hideConfigWindow(); } } } void View::hideWindowsForSlidingOut() { if (m_configView) { auto configDialog = qobject_cast(m_configView); if (configDialog) { configDialog->hideConfigWindow(); } } } //! remove latte tasks plasmoid void View::removeTasksPlasmoid() { if (!tasksPresent() || !containment()) { return; } for (const Plasma::Applet *applet : containment()->applets()) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->trigger(); //! remove only the first found return; } } } } //! check if the tasks plasmoid exist in the dock bool View::tasksPresent() { if (!this->containment()) { return false; } for (const Plasma::Applet *applet : this->containment()->applets()) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { return true; } } return false; } //!check if the plasmoid with _name_ exists in the midedata bool View::mimeContainsPlasmoid(QMimeData *mimeData, QString name) { if (!mimeData) { return false; } if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) { QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename")); const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); for (const QString &appletName : appletNames) { if (appletName == name) return true; } } return false; } ViewPart::Effects *View::effects() const { return m_effects; } ViewPart::Indicator *View::indicator() const { return m_indicator; } ViewPart::ContainmentInterface *View::extendedInterface() const { return m_interface; } ViewPart::Positioner *View::positioner() const { return m_positioner; } ViewPart::VisibilityManager *View::visibility() const { return m_visibility; } ViewPart::WindowsTracker *View::windowsTracker() const { return m_windowsTracker; } bool View::event(QEvent *e) { if (!m_inDelete) { emit eventTriggered(e); switch (e->type()) { case QEvent::Enter: m_containsMouse = true; if (m_configView) { ViewPart::PrimaryConfigView *primaryConfigView = qobject_cast(m_configView); if (primaryConfigView) { if (primaryConfigView->secondaryWindow()) { ViewPart::SecondaryConfigView *secConfigView = qobject_cast(primaryConfigView->secondaryWindow()); if (secConfigView) { secConfigView->requestActivate(); } } primaryConfigView->requestActivate(); } } break; case QEvent::Leave: m_containsMouse = false; setContainsDrag(false); engine()->trimComponentCache(); break; case QEvent::DragEnter: setContainsDrag(true); break; case QEvent::DragLeave: case QEvent::Drop: setContainsDrag(false); break; case QEvent::MouseButtonPress: if (auto mouseEvent = dynamic_cast(e)) { emit mousePressed(mouseEvent->pos(), mouseEvent->button()); } break; case QEvent::MouseButtonRelease: if (auto mouseEvent = dynamic_cast(e)) { emit mouseReleased(mouseEvent->pos(), mouseEvent->button()); } break; /* case QEvent::DragMove: qDebug() << "DRAG MOVING>>>>>>"; break;*/ case QEvent::PlatformSurface: if (auto pe = dynamic_cast(e)) { switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: setupWaylandIntegration(); if (m_shellSurface) { m_positioner->syncGeometry(); m_effects->updateShadows(); } break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; qDebug() << "WAYLAND dock window surface was deleted..."; m_effects->clearShadows(); } break; } } break; case QEvent::Show: if (m_visibility) { m_visibility->initViewFlags(); } break; case QEvent::Wheel: if (auto wheelEvent = dynamic_cast(e)) { #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) QPoint position = QPoint(wheelEvent->x(), wheelEvent->y()); #else QPoint position = wheelEvent->position().toPoint(); #endif emit wheelScrolled(position, wheelEvent->angleDelta(), wheelEvent->buttons()); } break; default: break; } } return ContainmentView::event(e); } //! release grab and restore mouse state void View::unblockMouse(int x, int y) { setMouseGrabEnabled(false); m_releaseGrab_x = x; m_releaseGrab_y = y; m_releaseGrabTimer.start(); } void View::releaseGrab() { //! ungrab mouse if (mouseGrabberItem()) { mouseGrabberItem()->ungrabMouse(); } //! properly release grabbed mouse in order to inform all views setMouseGrabEnabled(true); setMouseGrabEnabled(false); //! Send a fake QEvent::Leave to inform applets for mouse leaving the view QHoverEvent e(QEvent::Leave, QPoint(-5,-5), QPoint(m_releaseGrab_x, m_releaseGrab_y)); QCoreApplication::instance()->sendEvent(this, &e); } QVariantList View::containmentActions() { QVariantList actions; /*if (containment()->corona()->immutability() != Plasma::Types::Mutable) { return actions; }*/ //FIXME: the trigger string it should be better to be supported this way //const QString trigger = Plasma::ContainmentActions::eventToString(event); const QString trigger = "RightButton;NoModifier"; Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger); if (!plugin) { return actions; } if (plugin->containment() != this->containment()) { plugin->setContainment(this->containment()); // now configure it KConfigGroup cfg(this->containment()->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(this->containment()->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } for (QAction *ac : plugin->contextualActions()) { actions << QVariant::fromValue(ac); } return actions; } bool View::isHighestPriorityView() { if (m_layout) { return this == m_layout->highestPriorityView(); } return false; } //! BEGIN: WORKAROUND order to force top panels always on top and above left/right panels void View::topViewAlwaysOnTop() { if (!m_visibility) { return; } if (location() == Plasma::Types::TopEdge && m_visibility->mode() != Latte::Types::WindowsCanCover && m_visibility->mode() != Latte::Types::WindowsAlwaysCover) { //! this is needed in order to preserve that the top dock will be above others. //! Unity layout paradigm is a good example for this. The top panel shadow //! should be always on top compared to left panel m_visibility->setViewOnFrontLayer(); } } void View::verticalUnityViewHasFocus() { if (formFactor() == Plasma::Types::Vertical && (y() != screenGeometry().y()) && ( (m_alignment == Latte::Types::Justify && m_maxLength == 1.0) ||(m_alignment == Latte::Types::Top && m_offset == 0.0) )) { emit m_corona->verticalUnityViewHasFocus(); } } //! END: WORKAROUND //!BEGIN overriding context menus behavior void View::mousePressEvent(QMouseEvent *event) { bool result = m_contextMenu->mousePressEvent(event); if (result) { PlasmaQuick::ContainmentView::mousePressEvent(event); updateTransientWindowsTracking(); } verticalUnityViewHasFocus(); } //!END overriding context menus behavior //!BEGIN configuration functions void View::saveConfig() { if (!this->containment()) return; auto config = this->containment()->config(); config.writeEntry("onPrimary", onPrimary()); config.writeEntry("byPassWM", byPassWM()); config.writeEntry("isPreferredForShortcuts", isPreferredForShortcuts()); config.writeEntry("viewType", (int)m_type); config.sync(); } void View::restoreConfig() { if (!this->containment()) return; auto config = this->containment()->config(); m_onPrimary = config.readEntry("onPrimary", true); m_byPassWM = config.readEntry("byPassWM", false); m_isPreferredForShortcuts = config.readEntry("isPreferredForShortcuts", false); //! Send changed signals at the end in order to be sure that saveConfig //! wont rewrite default/invalid values emit onPrimaryChanged(); emit byPassWMChanged(); } //!END configuration functions } //!END namespace diff --git a/app/view/view.h b/app/view/view.h index e0ce0d51..c29e9cc1 100644 --- a/app/view/view.h +++ b/app/view/view.h @@ -1,427 +1,433 @@ /* * 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 VIEW_H #define VIEW_H // local #include #include "containmentinterface.h" #include "effects.h" #include "positioner.h" #include "visibilitymanager.h" #include "indicator/indicator.h" #include "settings/primaryconfigview.h" #include "windowstracker/windowstracker.h" #include "../shortcuts/globalshortcuts.h" #include "../layout/genericlayout.h" #include "../plasma/quick/containmentview.h" #include "../plasma/quick/configview.h" // C++ #include // Qt #include #include #include #include #include #include #include namespace Plasma { class Types; class Corona; class Containment; } namespace PlasmaQuick { class AppletQuickItem; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { class Corona; class GenericLayout; namespace ViewPart { class ContextMenu; } } namespace Latte { class View : public PlasmaQuick::ContainmentView { Q_OBJECT Q_PROPERTY(Latte::Types::ViewType type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(bool alternativesIsShown READ alternativesIsShown NOTIFY alternativesIsShownChanged) Q_PROPERTY(bool behaveAsPlasmaPanel READ behaveAsPlasmaPanel WRITE setBehaveAsPlasmaPanel NOTIFY behaveAsPlasmaPanelChanged) Q_PROPERTY(bool byPassWM READ byPassWM WRITE setByPassWM NOTIFY byPassWMChanged) Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged) Q_PROPERTY(bool contextMenuIsShown READ contextMenuIsShown NOTIFY contextMenuIsShownChanged) Q_PROPERTY(bool inSettingsAdvancedMode READ inSettingsAdvancedMode NOTIFY inSettingsAdvancedModeChanged) //! Because Latte uses animations, changing to edit mode it may be different than //! when the isUserConfiguring changes value Q_PROPERTY(bool inEditMode READ inEditMode WRITE setInEditMode NOTIFY inEditModeChanged) Q_PROPERTY(bool isPreferredForShortcuts READ isPreferredForShortcuts WRITE setIsPreferredForShortcuts NOTIFY isPreferredForShortcutsChanged) Q_PROPERTY(bool latteTasksArePresent READ latteTasksArePresent WRITE setLatteTasksArePresent NOTIFY latteTasksArePresentChanged) Q_PROPERTY(bool onPrimary READ onPrimary WRITE setOnPrimary NOTIFY onPrimaryChanged) Q_PROPERTY(bool screenEdgeMarginEnabled READ screenEdgeMarginEnabled WRITE setScreenEdgeMarginEnabled NOTIFY screenEdgeMarginEnabledChanged) //! values to be used from Smart surrounding Views Q_PROPERTY(bool isTouchingBottomViewAndIsBusy READ isTouchingBottomViewAndIsBusy WRITE setIsTouchingBottomViewAndIsBusy NOTIFY isTouchingBottomViewAndIsBusyChanged) Q_PROPERTY(bool isTouchingTopViewAndIsBusy READ isTouchingTopViewAndIsBusy WRITE setIsTouchingTopViewAndIsBusy NOTIFY isTouchingTopViewAndIsBusyChanged) Q_PROPERTY(int alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged) Q_PROPERTY(int fontPixelSize READ fontPixelSize WRITE setFontPixelSize NOTIFY fontPixelSizeChanged) Q_PROPERTY(int x READ x NOTIFY xChanged) Q_PROPERTY(int y READ y NOTIFY yChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(int height READ height NOTIFY heightChanged) Q_PROPERTY(int editThickness READ editThickness WRITE setEditThickness NOTIFY editThicknessChanged) Q_PROPERTY(int maxThickness READ maxThickness WRITE setMaxThickness NOTIFY maxThicknessChanged) Q_PROPERTY(int normalThickness READ normalThickness WRITE setNormalThickness NOTIFY normalThicknessChanged) + Q_PROPERTY(int headThicknessGap READ headThicknessGap WRITE setHeadThicknessGap NOTIFY headThicknessGapChanged) Q_PROPERTY(int screenEdgeMargin READ screenEdgeMargin WRITE setScreenEdgeMargin NOTIFY screenEdgeMarginChanged) Q_PROPERTY(float maxLength READ maxLength WRITE setMaxLength NOTIFY maxLengthChanged) Q_PROPERTY(float offset READ offset WRITE setOffset NOTIFY offsetChanged) Q_PROPERTY(Latte::Layout::GenericLayout *layout READ layout WRITE setLayout NOTIFY layoutChanged) Q_PROPERTY(Latte::ViewPart::Effects *effects READ effects NOTIFY effectsChanged) Q_PROPERTY(Latte::ViewPart::ContainmentInterface *extendedInterface READ extendedInterface NOTIFY extendedInterfaceChanged) Q_PROPERTY(Latte::ViewPart::Indicator *indicator READ indicator NOTIFY indicatorChanged) Q_PROPERTY(Latte::ViewPart::Positioner *positioner READ positioner NOTIFY positionerChanged) Q_PROPERTY(Latte::ViewPart::VisibilityManager *visibility READ visibility NOTIFY visibilityChanged) Q_PROPERTY(Latte::ViewPart::WindowsTracker *windowsTracker READ windowsTracker NOTIFY windowsTrackerChanged) Q_PROPERTY(QRect absoluteGeometry READ absoluteGeometry NOTIFY absoluteGeometryChanged) Q_PROPERTY(QRect localGeometry READ localGeometry WRITE setLocalGeometry NOTIFY localGeometryChanged) Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) public: View(Plasma::Corona *corona, QScreen *targetScreen = nullptr, bool byPassWM = false); virtual ~View(); void init(Plasma::Containment *plasma_containment = nullptr); Types::ViewType type() const; void setType(Types::ViewType type); bool alternativesIsShown() const; void setAlternativesIsShown(bool show); bool inDelete() const; bool inReadyState() const; bool onPrimary() const; void setOnPrimary(bool flag); int currentThickness() const; bool behaveAsPlasmaPanel() const; void setBehaveAsPlasmaPanel(bool behavior); bool containsDrag() const; bool containsMouse() const; bool contextMenuIsShown() const; bool byPassWM() const; void setByPassWM(bool bypass); bool inEditMode() const; void setInEditMode(bool edit); bool isFloatingWindow() const; bool isPreferredForShortcuts() const; void setIsPreferredForShortcuts(bool preferred); bool latteTasksArePresent() const; void setLatteTasksArePresent(bool present); bool inSettingsAdvancedMode() const; bool isTouchingBottomViewAndIsBusy() const; void setIsTouchingBottomViewAndIsBusy(bool touchAndBusy); bool isTouchingTopViewAndIsBusy() const; void setIsTouchingTopViewAndIsBusy(bool touchAndBusy); bool screenEdgeMarginEnabled() const; void setScreenEdgeMarginEnabled(bool enabled); int fontPixelSize() const; void setFontPixelSize(int size); int editThickness() const; void setEditThickness(int thickness); int maxThickness() const; void setMaxThickness(int thickness); int normalThickness() const; void setNormalThickness(int thickness); + int headThicknessGap() const; + void setHeadThicknessGap(int thickness); + int screenEdgeMargin() const; void setScreenEdgeMargin(int margin); int alignment() const; void setAlignment(int alignment); float maxLength() const; void setMaxLength(float length); float offset() const; void setOffset(float offset); QRect absoluteGeometry() const; QRect screenGeometry() const; QRect localGeometry() const; void setLocalGeometry(const QRect &geometry); QString validTitle() const; bool isOnActivity(const QString &activity) const; bool isOnAllActivities() const; QStringList activities() const; void setActivities(const QStringList &ids); bool settingsWindowIsShown(); void showSettingsWindow(); PlasmaQuick::ConfigView *configView(); ViewPart::Effects *effects() const; ViewPart::ContainmentInterface *extendedInterface() const; ViewPart::Indicator *indicator() const; ViewPart::Positioner *positioner() const; ViewPart::VisibilityManager *visibility() const; ViewPart::WindowsTracker *windowsTracker() const; Layout::GenericLayout *layout() const; void setLayout(Layout::GenericLayout *layout); KWayland::Client::PlasmaShellSurface *surface(); //! release grab and restore mouse state void unblockMouse(int x, int y); void reconsiderScreen(); //! these are signals that create crashes, such a example is the availableScreenRectChanged from corona //! when its containment is destroyed void disconnectSensitiveSignals(); public slots: Q_INVOKABLE void copyView(); Q_INVOKABLE void removeView(); Q_INVOKABLE QVariantList containmentActions(); Q_INVOKABLE void moveToLayout(QString layoutName); Q_INVOKABLE void removeTasksPlasmoid(); Q_INVOKABLE bool mimeContainsPlasmoid(QMimeData *mimeData, QString name); Q_INVOKABLE bool tasksPresent(); void updateAbsoluteGeometry(bool bypassChecks = false); Q_INVOKABLE bool isHighestPriorityView(); protected slots: void showConfigurationInterface(Plasma::Applet *applet) override; protected: bool event(QEvent *ev) override; void mousePressEvent(QMouseEvent *event) override; signals: void eventTriggered(QEvent *ev); void mousePressed(const QPoint pos, const int button); void mouseReleased(const QPoint pos, const int button); void wheelScrolled(const QPoint pos, const QPoint angleDelta, const int buttons); void activitiesChanged(); void alternativesIsShownChanged(); void alignmentChanged(); void behaveAsPlasmaPanelChanged(); void byPassWMChanged(); void configWindowGeometryChanged(); // is called from config windows void containsDragChanged(); void contextMenuIsShownChanged(); void dockLocationChanged(); void editThicknessChanged(); void effectsChanged(); void extendedInterfaceChanged(); void fontPixelSizeChanged(); void forcedShown(); //[workaround] forced shown to avoid a KWin issue that hides windows when closing activities void widthChanged(); + void headThicknessGapChanged(); void heightChanged(); void inEditModeChanged(); void indicatorChanged(); void inSettingsAdvancedModeChanged(); void isPreferredForShortcutsChanged(); void isTouchingBottomViewAndIsBusyChanged(); void isTouchingTopViewAndIsBusyChanged(); void latteTasksArePresentChanged(); void layoutChanged(); void localGeometryChanged(); void maxLengthChanged(); void maxThicknessChanged(); void normalThicknessChanged(); void offsetChanged(); void onPrimaryChanged(); void positionerChanged(); void screenEdgeMarginChanged(); void screenEdgeMarginEnabledChanged(); void screenGeometryChanged(); void typeChanged(); void visibilityChanged(); void windowsTrackerChanged(); void xChanged(); void yChanged(); void absoluteGeometryChanged(const QRect &geometry); void indicatorPluginChanged(const QString &indicatorId); void indicatorPluginRemoved(const QString &indicatorId); //! are used to trigger the Corona relevant signals and in that //! way we can disable any such signaling all together, e.g. through disconnectSensitiveSignals() void availableScreenRectChangedFrom(Latte::View *origin); void availableScreenRegionChangedFrom(Latte::View *origin); private slots: void applyActivitiesToWindows(); void availableScreenRectChangedFromSlot(View *origin); void configViewCreatedFor(Latte::View *view); void hideWindowsForSlidingOut(); void preferredViewForShortcutsChangedSlot(Latte::View *view); void releaseGrab(); void reloadSource(); void updateTransientWindowsTracking(); void statusChanged(Plasma::Types::ItemStatus); void addTransientWindow(QWindow *window); void removeTransientWindow(const bool &visible); //! workaround in order for top panels to be always on top void topViewAlwaysOnTop(); void verticalUnityViewHasFocus(); void restoreConfig(); void saveConfig(); private: void initSignalingForLocationChangeSliding(); void setupWaylandIntegration(); void updateAppletContainsMethod(); void setContainsDrag(bool contains); private: Plasma::Containment *containmentById(uint id); bool m_alternativesIsShown{false}; bool m_behaveAsPlasmaPanel{false}; bool m_byPassWM{true}; bool m_containsDrag{false}; bool m_containsMouse{false}; bool m_inDelete{false}; bool m_inEditMode{false}; bool m_isPreferredForShortcuts{false}; bool m_latteTasksArePresent{false}; bool m_onPrimary{true}; bool m_screenEdgeMarginEnabled{false}; bool m_isTouchingBottomViewAndIsBusy{false}; bool m_isTouchingTopViewAndIsBusy{false}; int m_fontPixelSize{ -1}; int m_editThickness{24}; int m_maxThickness{24}; int m_normalThickness{24}; + int m_headThicknessGap{0}; int m_screenEdgeMargin{-1}; float m_maxLength{1}; float m_offset{0}; Types::Alignment m_alignment{Types::Center}; Types::ViewType m_type{Types::DockView}; QRect m_localGeometry; QRect m_absoluteGeometry; QStringList m_activities; //! HACK: In order to avoid crashes when the View is added and removed //! immediately during startup QTimer m_initLayoutTimer; //! 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; QTimer m_releaseGrabTimer; int m_releaseGrab_x; int m_releaseGrab_y; Layout::GenericLayout *m_layout{nullptr}; QPointer m_configView; QPointer m_contextMenu; QPointer m_effects; QPointer m_indicator; QPointer m_interface; QPointer m_positioner; QPointer m_visibility; QPointer m_windowsTracker; //! Connections to release and bound for the assigned layout QList connectionsLayout; //! track transientWindows QList m_transientWindows; QPointer m_corona; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif diff --git a/app/view/visibilitymanager.cpp b/app/view/visibilitymanager.cpp index 14944ed0..29739b0b 100644 --- a/app/view/visibilitymanager.cpp +++ b/app/view/visibilitymanager.cpp @@ -1,986 +1,1026 @@ /* * 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 "../apptypes.h" #include "../lattecorona.h" #include "../screenpool.h" #include "../layouts/manager.h" #include "../wm/abstractwindowinterface.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(); } }); + //! Frame Extents + connect(m_latteView, &Latte::View::headThicknessGapChanged , this, &VisibilityManager::on_publishFrameExtents); + connect(m_latteView, &Latte::View::locationChanged , this, &VisibilityManager::publishFrameExtents); + 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(); } } }); + m_timerPublishFrameExtents.setInterval(2000); + m_timerPublishFrameExtents.setSingleShot(true); + connect(&m_timerPublishFrameExtents, &QTimer::timeout, this, &VisibilityManager::publishFrameExtents); + 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() == MemoryUsage::MultipleLayouts) { updateStrutsBasedOnLayoutsAndActivities(true); } }); m_connections[base+2] = connect(m_latteView, &Latte::View::activitiesChanged, this, [&]() { if (m_corona && m_corona->layoutsManager()->memoryUsage() == MemoryUsage::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() == MemoryUsage::MultipleLayouts && m_latteView->layout() && !m_latteView->positioner()->inLocationAnimation() && m_latteView->layout()->isCurrent()); if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::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(); } } +void VisibilityManager::on_publishFrameExtents() +{ + if (!m_timerPublishFrameExtents.isActive()) { + m_timerPublishFrameExtents.start(); + } +} + +void VisibilityManager::publishFrameExtents() +{ + if (m_frameExtentsHeadThicknessGap != m_latteView->headThicknessGap() + || m_frameExtentsLocation != m_latteView->location()) { + + m_frameExtentsHeadThicknessGap = m_latteView->headThicknessGap(); + m_frameExtentsLocation = m_latteView->location(); + + QMargins frameExtents; + + if (m_latteView->location() == Plasma::Types::LeftEdge) { + frameExtents.setRight(m_frameExtentsHeadThicknessGap); + } else if (m_latteView->location() == Plasma::Types::TopEdge) { + frameExtents.setBottom(m_frameExtentsHeadThicknessGap); + } else if (m_latteView->location() == Plasma::Types::RightEdge) { + frameExtents.setLeft(m_frameExtentsHeadThicknessGap); + } else { + frameExtents.setTop(m_frameExtentsHeadThicknessGap); + } + + qDebug() << " -> Frame Extents :: " << m_frameExtentsLocation << " __ " << " extents :: " << frameExtents; + m_wm->setFrameExtents(m_latteView, frameExtents); + } +} + 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() == MemoryUsage::SingleLayout || (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::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 = (Types::Visibility)(m_latteView->containment()->config().readEntry("visibility", (int)(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; } Types::Visibility fMode = (Types::Visibility)(m_latteView->containment()->config().readEntry("visibility", (int)(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() == MemoryUsage::SingleLayout || (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::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 01f7e3c5..b5d6962c 100644 --- a/app/view/visibilitymanager.h +++ b/app/view/visibilitymanager.h @@ -1,226 +1,233 @@ /* * 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 #include "../plasma/quick/containmentview.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(); + void on_publishFrameExtents(); //! delayed + void publishFrameExtents(); //! direct + //! 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; + QTimer m_timerPublishFrameExtents; 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}; + int m_frameExtentsHeadThicknessGap{0}; + Plasma::Types::Location m_frameExtentsLocation{Plasma::Types::BottomEdge}; + 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 diff --git a/app/wm/abstractwindowinterface.h b/app/wm/abstractwindowinterface.h index 7e9a7ba1..dbf5a30d 100644 --- a/app/wm/abstractwindowinterface.h +++ b/app/wm/abstractwindowinterface.h @@ -1,200 +1,202 @@ /* * 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 ABSTRACTWINDOWINTERFACE_H #define ABSTRACTWINDOWINTERFACE_H // local #include #include "schemecolors.h" #include "tasktools.h" #include "windowinfowrap.h" #include "tracker/windowstracker.h" // C++ #include #include // Qt #include #include #include #include #include #include #include #include #include // KDE #include #include // Plasma #include namespace Latte { class Corona; namespace WindowSystem { namespace Tracker { class Schemes; class Windows; } } } namespace Latte { namespace WindowSystem { class AbstractWindowInterface : public QObject { Q_OBJECT public: enum class Slide { None, Top, Left, Bottom, Right, }; explicit AbstractWindowInterface(QObject *parent = nullptr); virtual ~AbstractWindowInterface(); virtual void setViewExtraFlags(QObject *view,bool isPanelWindow = true, Latte::Types::Visibility mode = Latte::Types::WindowsGoBelow) = 0; virtual void setViewStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) = 0; virtual void setWindowOnActivities(QWindow &window, const QStringList &activities) = 0; virtual void removeViewStruts(QWindow &view) = 0; virtual WindowId activeWindow() = 0; virtual WindowInfoWrap requestInfo(WindowId wid) = 0; virtual WindowInfoWrap requestInfoActive() = 0; virtual void skipTaskBar(const QDialog &dialog) = 0; virtual void slideWindow(QWindow &view, Slide location) = 0; virtual void enableBlurBehind(QWindow &view) = 0; virtual void setActiveEdge(QWindow *view, bool active) = 0; virtual void requestActivate(WindowId wid) = 0; virtual void requestClose(WindowId wid) = 0; virtual void requestMoveWindow(WindowId wid, QPoint from) = 0; virtual void requestToggleIsOnAllDesktops(WindowId wid) = 0; virtual void requestToggleKeepAbove(WindowId wid) = 0; virtual void requestToggleMinimized(WindowId wid) = 0; virtual void requestToggleMaximized(WindowId wid) = 0; virtual void setKeepAbove(WindowId wid, bool active) = 0; virtual void setKeepBelow(WindowId wid, bool active) = 0; virtual bool windowCanBeDragged(WindowId wid) = 0; virtual bool windowCanBeMaximized(WindowId wid) = 0; virtual QIcon iconFor(WindowId wid) = 0; virtual WindowId winIdFor(QString appId, QRect geometry) = 0; virtual WindowId winIdFor(QString appId, QString title) = 0; virtual AppData appDataFor(WindowId wid) = 0; bool inCurrentDesktopActivity(const WindowInfoWrap &winfo); bool hasBlockedTracking(const WindowId &wid) const; QString currentDesktop(); QString currentActivity(); virtual void registerIgnoredWindow(WindowId wid); virtual void unregisterIgnoredWindow(WindowId wid); void registerPlasmaIgnoredWindow(WindowId wid); void unregisterPlasmaIgnoredWindow(WindowId wid); void registerWhitelistedWindow(WindowId wid); void unregisterWhitelistedWindow(WindowId wid); void switchToNextActivity(); void switchToPreviousActivity(); virtual void switchToNextVirtualDesktop() = 0; virtual void switchToPreviousVirtualDesktop() = 0; + virtual void setFrameExtents(QWindow *view, const QMargins &margins) = 0; + Latte::Corona *corona(); Tracker::Schemes *schemesTracker(); Tracker::Windows *windowsTracker() const; signals: void activeWindowChanged(WindowId wid); void windowChanged(WindowId winfo); void windowAdded(WindowId wid); void windowRemoved(WindowId wid); void currentDesktopChanged(); void currentActivityChanged(); void latteWindowAdded(); protected: QString m_currentDesktop; QString m_currentActivity; //! windows that must be ignored from tracking, a good example are Latte::Views and //! their Configuration windows QList m_ignoredWindows; //! identified plasma panels QList m_plasmaIgnoredWindows; //! identified whitelisted windows that can be tracked e.g. plasma widgets explorer and activities QList m_whitelistedWindows; QPointer m_activities; //! Sending too fast plenty of signals for the same window //! has no reason and can create HIGH CPU usage. This Timer //! can delay the batch sending of signals for the same window WindowId m_windowChangedWaiting; QTimer m_windowWaitingTimer; //! Plasma taskmanager rules ile KSharedConfig::Ptr rulesConfig; void considerWindowChanged(WindowId wid); bool isIgnored(const WindowId &wid) const; bool isRegisteredPlasmaIgnoredWindow(const WindowId &wid) const; bool isWhitelistedWindow(const WindowId &wid) const; bool isFullScreenWindow(const QRect &wGeometry) const; bool isPlasmaPanel(const QRect &wGeometry) const; bool isSidepanel(const QRect &wGeometry) const; private slots: void windowRemovedSlot(WindowId wid); private: Latte::Corona *m_corona; Tracker::Schemes *m_schemesTracker; Tracker::Windows *m_windowsTracker; }; } } #endif // ABSTRACTWINDOWINTERFACE_H diff --git a/app/wm/waylandinterface.cpp b/app/wm/waylandinterface.cpp index 74e5b958..d3dadede 100644 --- a/app/wm/waylandinterface.cpp +++ b/app/wm/waylandinterface.cpp @@ -1,918 +1,923 @@ /* * 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 "waylandinterface.h" // local #include #include "view/positioner.h" #include "view/view.h" #include "view/helpers/screenedgeghostwindow.h" #include "../lattecorona.h" // Qt #include #include #include #include #include // KDE #include #include #include #if KF5_VERSION_MINOR >= 52 #include #endif // X11 #include using namespace KWayland::Client; namespace Latte { class Private::GhostWindow : public QRasterWindow { Q_OBJECT public: WindowSystem::WindowId m_winId; GhostWindow(WindowSystem::WaylandInterface *waylandInterface) : m_waylandInterface(waylandInterface) { setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); connect(m_waylandInterface, &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &GhostWindow::identifyWinId); setupWaylandIntegration(); show(); } ~GhostWindow() { m_waylandInterface->unregisterIgnoredWindow(m_winId); delete m_shellSurface; } void setGeometry(const QRect &rect) { if (geometry() == rect) { return; } m_validGeometry = rect; setMinimumSize(rect.size()); setMaximumSize(rect.size()); resize(rect.size()); m_shellSurface->setPosition(rect.topLeft()); } void setupWaylandIntegration() { using namespace KWayland::Client; if (m_shellSurface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; m_shellSurface = m_waylandInterface->waylandCoronaInterface()->createSurface(s, this); qDebug() << "wayland ghost window surface was created..."; m_shellSurface->setSkipTaskbar(true); m_shellSurface->setPanelTakesFocus(false); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); } KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; WindowSystem::WaylandInterface *m_waylandInterface{nullptr}; //! geometry() function under wayland does not return nice results QRect m_validGeometry; public slots: void identifyWinId() { if (m_winId.isNull()) { m_winId = m_waylandInterface->winIdFor("latte-dock", m_validGeometry); m_waylandInterface->registerIgnoredWindow(m_winId); } } }; namespace WindowSystem { WaylandInterface::WaylandInterface(QObject *parent) : AbstractWindowInterface(parent) { m_corona = qobject_cast(parent); } WaylandInterface::~WaylandInterface() { } void WaylandInterface::init() { } void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement) { if (m_windowManagement == windowManagement) { return; } m_windowManagement = windowManagement; connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, &WaylandInterface::windowCreatedProxy); connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, this, [&]() noexcept { auto w = m_windowManagement->activeWindow(); if (!w || (w && (!m_ignoredWindows.contains(w->internalId()))) ) { emit activeWindowChanged(w ? w->internalId() : 0); } }, Qt::QueuedConnection); } #if KF5_VERSION_MINOR >= 52 void WaylandInterface::initVirtualDesktopManagement(KWayland::Client::PlasmaVirtualDesktopManagement *virtualDesktopManagement) { if (m_virtualDesktopManagement == virtualDesktopManagement) { return; } m_virtualDesktopManagement = virtualDesktopManagement; connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopCreated, this, [this](const QString &id, quint32 position) { addDesktop(id, position); }); connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved, this, [this](const QString &id) { m_desktops.removeAll(id); if (m_currentDesktop == id) { setCurrentDesktop(QString()); } }); } void WaylandInterface::addDesktop(const QString &id, quint32 position) { if (m_desktops.contains(id)) { return; } m_desktops.append(id); const KWayland::Client::PlasmaVirtualDesktop *desktop = m_virtualDesktopManagement->getVirtualDesktop(id); QObject::connect(desktop, &KWayland::Client::PlasmaVirtualDesktop::activated, this, [desktop, this]() { setCurrentDesktop(desktop->id()); } ); if (desktop->isActive()) { setCurrentDesktop(id); } } void WaylandInterface::setCurrentDesktop(QString desktop) { if (m_currentDesktop == desktop) { return; } m_currentDesktop = desktop; emit currentDesktopChanged(); } #endif KWayland::Client::PlasmaShell *WaylandInterface::waylandCoronaInterface() const { return m_corona->waylandCoronaInterface(); } //! Register Latte Ignored Windows in order to NOT be tracked void WaylandInterface::registerIgnoredWindow(WindowId wid) { if (!wid.isNull() && !m_ignoredWindows.contains(wid)) { m_ignoredWindows.append(wid); KWayland::Client::PlasmaWindow *w = windowFor(wid); if (w) { untrackWindow(w); } emit windowChanged(wid); } } void WaylandInterface::unregisterIgnoredWindow(WindowId wid) { if (m_ignoredWindows.contains(wid)) { m_ignoredWindows.removeAll(wid); emit windowRemoved(wid); } } void WaylandInterface::setViewExtraFlags(QObject *view, bool isPanelWindow, Latte::Types::Visibility mode) { KWayland::Client::PlasmaShellSurface *surface = qobject_cast(view); Latte::View *latteView = qobject_cast(view); if (latteView) { surface = latteView->surface(); } if (!surface) { return; } surface->setSkipTaskbar(true); #if KF5_VERSION_MINOR >= 47 surface->setSkipSwitcher(true); #endif bool atBottom{!isPanelWindow && (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover)}; if (isPanelWindow) { surface->setRole(PlasmaShellSurface::Role::Panel); surface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); } else { surface->setRole(PlasmaShellSurface::Role::Normal); } if (latteView) { WindowId winId = latteView->positioner()->trackedWindowId(); auto w = windowFor(winId); if (w && !w->isOnAllDesktops()) { requestToggleIsOnAllDesktops(winId); } //! Layer to be applied if (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover) { setKeepBelow(winId, true); } else if (mode == Latte::Types::NormalWindow) { setKeepBelow(winId, false); setKeepAbove(winId, false); } else { setKeepAbove(winId, true); } } if (atBottom){ //! trying to workaround WM behavior in order //! 1. View at the end MUST NOT HAVE FOCUSABILITY (issue example: clicking a single active task is not minimized) //! 2. View at the end MUST BE AT THE BOTTOM of windows stack QTimer::singleShot(50, [this, surface]() { surface->setRole(PlasmaShellSurface::Role::ToolTip); }); } } void WaylandInterface::setViewStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location) { if (!m_ghostWindows.contains(view.winId())) { m_ghostWindows[view.winId()] = new Private::GhostWindow(this); } auto w = m_ghostWindows[view.winId()]; switch (location) { case Plasma::Types::TopEdge: case Plasma::Types::BottomEdge: w->setGeometry({rect.x() + rect.width() / 2, rect.y(), 1, rect.height()}); break; case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: w->setGeometry({rect.x(), rect.y() + rect.height() / 2, rect.width(), 1}); break; default: break; } } void WaylandInterface::switchToNextVirtualDesktop() { #if KF5_VERSION_MINOR >= 52 if (!m_virtualDesktopManagement || m_desktops.count() <= 1) { return; } int curPos = m_desktops.indexOf(m_currentDesktop); int nextPos = curPos + 1; if (curPos == m_desktops.count()-1) { nextPos = 0; } KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]); if (desktopObj) { desktopObj->requestActivate(); } #endif } void WaylandInterface::switchToPreviousVirtualDesktop() { #if KF5_VERSION_MINOR >= 52 if (!m_virtualDesktopManagement || m_desktops.count() <= 1) { return; } int curPos = m_desktops.indexOf(m_currentDesktop); int nextPos = curPos - 1; if (curPos == 0) { nextPos = m_desktops.count()-1; } KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]); if (desktopObj) { desktopObj->requestActivate(); } #endif } void WaylandInterface::setWindowOnActivities(QWindow &window, const QStringList &activities) { //! needs to updated to wayland case // KWindowSystem::setOnActivities(view.winId(), activities); } void WaylandInterface::removeViewStruts(QWindow &view) { delete m_ghostWindows.take(view.winId()); } WindowId WaylandInterface::activeWindow() { if (!m_windowManagement) { return 0; } auto wid = m_windowManagement->activeWindow(); return wid ? wid->internalId() : 0; } void WaylandInterface::skipTaskBar(const QDialog &dialog) { KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); } void WaylandInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) { auto slideLocation = KWindowEffects::NoEdge; switch (location) { case Slide::Top: slideLocation = KWindowEffects::TopEdge; break; case Slide::Bottom: slideLocation = KWindowEffects::BottomEdge; break; case Slide::Left: slideLocation = KWindowEffects::LeftEdge; break; case Slide::Right: slideLocation = KWindowEffects::RightEdge; break; default: break; } KWindowEffects::slideWindow(view.winId(), slideLocation, -1); } void WaylandInterface::enableBlurBehind(QWindow &view) { KWindowEffects::enableBlurBehind(view.winId()); } void WaylandInterface::setActiveEdge(QWindow *view, bool active) { ViewPart::ScreenEdgeGhostWindow *window = qobject_cast(view); if (!window) { return; } if (window->parentView()->surface() && window->parentView()->visibility() && (window->parentView()->visibility()->mode() == Types::DodgeActive || window->parentView()->visibility()->mode() == Types::DodgeMaximized || window->parentView()->visibility()->mode() == Types::DodgeAllWindows || window->parentView()->visibility()->mode() == Types::AutoHide)) { if (active) { window->showWithMask(); window->surface()->requestHideAutoHidingPanel(); } else { window->hideWithMask(); window->surface()->requestShowAutoHidingPanel(); } } } +void WaylandInterface::setFrameExtents(QWindow *view, const QMargins &extents) +{ + //! do nothing yet until there is a wayland way to provide this +} + WindowInfoWrap WaylandInterface::requestInfoActive() { if (!m_windowManagement) { return {}; } auto w = m_windowManagement->activeWindow(); if (!w) return {}; return requestInfo(w->internalId()); } WindowInfoWrap WaylandInterface::requestInfo(WindowId wid) { WindowInfoWrap winfoWrap; auto w = windowFor(wid); if (w && isValidWindow(w)) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setParentId(w->parentWindow() ? w->parentWindow()->internalId() : 0); winfoWrap.setIsActive(w->isActive()); winfoWrap.setIsMinimized(w->isMinimized()); winfoWrap.setIsMaxVert(w->isMaximized()); winfoWrap.setIsMaxHoriz(w->isMaximized()); winfoWrap.setIsFullscreen(w->isFullscreen()); winfoWrap.setIsShaded(w->isShaded()); winfoWrap.setIsOnAllDesktops(w->isOnAllDesktops()); winfoWrap.setIsOnAllActivities(true); winfoWrap.setIsKeepAbove(w->isKeepAbove()); winfoWrap.setIsKeepBelow(w->isKeepBelow()); winfoWrap.setGeometry(w->geometry()); #if KF5_VERSION_MINOR >= 47 winfoWrap.setHasSkipSwitcher(w->skipSwitcher()); #endif winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); //! BEGIN:Window Abilities winfoWrap.setIsClosable(w->isCloseable()); winfoWrap.setIsFullScreenable(w->isFullscreenable()); winfoWrap.setIsMaximizable(w->isMaximizeable()); winfoWrap.setIsMinimizable(w->isMinimizeable()); winfoWrap.setIsMovable(w->isMovable()); winfoWrap.setIsResizable(w->isResizable()); winfoWrap.setIsShadeable(w->isShadeable()); winfoWrap.setIsVirtualDesktopsChangeable(w->isVirtualDesktopChangeable()); //! END:Window Abilities winfoWrap.setDisplay(w->title()); #if KF5_VERSION_MINOR >= 52 winfoWrap.setDesktops(w->plasmaVirtualDesktops()); #endif winfoWrap.setActivities(QStringList()); } else { winfoWrap.setIsValid(false); } return winfoWrap; } AppData WaylandInterface::appDataFor(WindowId wid) { auto window = windowFor(wid); if (window) { const AppData &data = appDataFromUrl(windowUrlFromMetadata(window->appId(), window->pid(), rulesConfig)); return data; } AppData empty; return empty; } KWayland::Client::PlasmaWindow *WaylandInterface::windowFor(WindowId wid) { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); if (it == m_windowManagement->windows().constEnd()) { return nullptr; } return *it; } QIcon WaylandInterface::iconFor(WindowId wid) { auto window = windowFor(wid); if (window) { return window->icon(); } return QIcon(); } WindowId WaylandInterface::winIdFor(QString appId, QString title) { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&appId, &title](PlasmaWindow * w) noexcept { return w->isValid() && w->appId() == appId && w->title().startsWith(title); }); if (it == m_windowManagement->windows().constEnd()) { return QVariant(); } return (*it)->internalId(); } WindowId WaylandInterface::winIdFor(QString appId, QRect geometry) { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&appId, &geometry](PlasmaWindow * w) noexcept { return w->isValid() && w->appId() == appId && w->geometry() == geometry; }); if (it == m_windowManagement->windows().constEnd()) { return QVariant(); } return (*it)->internalId(); } bool WaylandInterface::windowCanBeDragged(WindowId wid) { auto w = windowFor(wid); if (w && isValidWindow(w)) { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && w->isMovable() && !winfo.isMinimized() && inCurrentDesktopActivity(winfo)); } return false; } bool WaylandInterface::windowCanBeMaximized(WindowId wid) { auto w = windowFor(wid); if (w && isValidWindow(w)) { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && w->isMaximizeable() && !winfo.isMinimized() && inCurrentDesktopActivity(winfo)); } return false; } void WaylandInterface::requestActivate(WindowId wid) { auto w = windowFor(wid); if (w) { w->requestActivate(); } } void WaylandInterface::requestClose(WindowId wid) { auto w = windowFor(wid); if (w) { w->requestClose(); } } void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from) { WindowInfoWrap wInfo = requestInfo(wid); if (windowCanBeDragged(wid) && inCurrentDesktopActivity(wInfo)) { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestMove(); } } } void WaylandInterface::requestToggleIsOnAllDesktops(WindowId wid) { #if KF5_VERSION_MINOR >= 52 auto w = windowFor(wid); if (w && isValidWindow(w) && m_desktops.count() > 1) { if (w->isOnAllDesktops()) { w->requestEnterVirtualDesktop(m_currentDesktop); } else { const QStringList &now = w->plasmaVirtualDesktops(); foreach (const QString &desktop, now) { w->requestLeaveVirtualDesktop(desktop); } } } #endif } void WaylandInterface::requestToggleKeepAbove(WindowId wid) { auto w = windowFor(wid); if (w) { w->requestToggleKeepAbove(); } } void WaylandInterface::setKeepAbove(WindowId wid, bool active) { auto w = windowFor(wid); if (w) { if (active) { setKeepBelow(wid, false); } if ((w->isKeepAbove() && active) || (!w->isKeepAbove() && !active)) { return; } w->requestToggleKeepAbove(); } } void WaylandInterface::setKeepBelow(WindowId wid, bool active) { auto w = windowFor(wid); if (w) { if (active) { setKeepAbove(wid, false); } if ((w->isKeepBelow() && active) || (!w->isKeepBelow() && !active)) { return; } w->requestToggleKeepBelow(); } } void WaylandInterface::requestToggleMinimized(WindowId wid) { auto w = windowFor(wid); WindowInfoWrap wInfo = requestInfo(wid); if (w && isValidWindow(w) && inCurrentDesktopActivity(wInfo)) { #if KF5_VERSION_MINOR >= 52 if (!m_currentDesktop.isEmpty()) { w->requestEnterVirtualDesktop(m_currentDesktop); } #endif w->requestToggleMinimized(); } } void WaylandInterface::requestToggleMaximized(WindowId wid) { auto w = windowFor(wid); WindowInfoWrap wInfo = requestInfo(wid); if (w && isValidWindow(w) && windowCanBeMaximized(wid) && inCurrentDesktopActivity(wInfo)) { #if KF5_VERSION_MINOR >= 52 if (!m_currentDesktop.isEmpty()) { w->requestEnterVirtualDesktop(m_currentDesktop); } #endif w->requestToggleMaximized(); } } bool WaylandInterface::isPlasmaPanel(const KWayland::Client::PlasmaWindow *w) const { if (!w || (w->appId() != QLatin1String("org.kde.plasmashell"))) { return false; } return AbstractWindowInterface::isPlasmaPanel(w->geometry()); } bool WaylandInterface::isFullScreenWindow(const KWayland::Client::PlasmaWindow *w) const { if (!w) { return false; } return w->isFullscreen() || AbstractWindowInterface::isFullScreenWindow(w->geometry()); } bool WaylandInterface::isSidepanel(const KWayland::Client::PlasmaWindow *w) const { if (!w) { return false; } return AbstractWindowInterface::isSidepanel(w->geometry()); } bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w) { if (!w || !w->isValid()) { return false; } if (windowsTracker()->isValidFor(w->internalId())) { return true; } return isAcceptableWindow(w); } bool WaylandInterface::isAcceptableWindow(const KWayland::Client::PlasmaWindow *w) { if (!w || !w->isValid()) { return false; } //! ignored windows that are not tracked if (hasBlockedTracking(w->internalId())) { return false; } //! whitelisted/approved windows if (isWhitelistedWindow(w->internalId())) { return true; } //! Window Checks bool hasSkipTaskbar = w->skipTaskbar(); bool isSkipped = hasSkipTaskbar; #if KF5_VERSION_MINOR >= 47 bool hasSkipSwitcher = w->skipSwitcher(); isSkipped = hasSkipTaskbar && hasSkipSwitcher; #endif if (isSkipped && ((w->appId() == QLatin1String("yakuake") || (w->appId() == QLatin1String("krunner"))) )) { registerWhitelistedWindow(w->internalId()); } else if (w->appId() == QLatin1String("org.kde.plasmashell")) { if (isSkipped && isSidepanel(w)) { registerWhitelistedWindow(w->internalId()); return true; } else if (isPlasmaPanel(w) || isFullScreenWindow(w)) { registerPlasmaIgnoredWindow(w->internalId()); return false; } } else if ((w->appId() == QLatin1String("latte-dock")) || (w->appId().startsWith(QLatin1String("ksmserver")))) { if (isFullScreenWindow(w)) { registerIgnoredWindow(w->internalId()); return false; } } return !isSkipped; } void WaylandInterface::updateWindow() { PlasmaWindow *pW = qobject_cast(QObject::sender()); if (isValidWindow(pW)) { considerWindowChanged(pW->internalId()); } } void WaylandInterface::windowUnmapped() { PlasmaWindow *pW = qobject_cast(QObject::sender()); if (pW) { untrackWindow(pW); emit windowRemoved(pW->internalId()); } } void WaylandInterface::trackWindow(KWayland::Client::PlasmaWindow *w) { if (!w) { return; } connect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow); #if KF5_VERSION_MINOR >= 52 connect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow); #else connect(w, &PlasmaWindow::virtualDesktopChanged, this, &WaylandInterface::updateWindow); #endif connect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped); } void WaylandInterface::untrackWindow(KWayland::Client::PlasmaWindow *w) { if (!w) { return; } disconnect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow); #if KF5_VERSION_MINOR >= 52 disconnect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow); #else disconnect(w, &PlasmaWindow::virtualDesktopChanged, this, &WaylandInterface::updateWindow); #endif disconnect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped); } void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w) { if (!isAcceptableWindow(w)) { return; } trackWindow(w); emit windowAdded(w->internalId()); if (w->appId() == "latte-dock") { emit latteWindowAdded(); } } } } #include "waylandinterface.moc" diff --git a/app/wm/waylandinterface.h b/app/wm/waylandinterface.h index 2752dc1a..bfb65aa1 100644 --- a/app/wm/waylandinterface.h +++ b/app/wm/waylandinterface.h @@ -1,155 +1,157 @@ /* * 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 WAYLANDINTERFACE_H #define WAYLANDINTERFACE_H // local #include #include "abstractwindowinterface.h" #include "windowinfowrap.h" // Qt #include #include // KDE #include #include #include #include #include #include #include namespace Latte { class Corona; namespace Private { //! this class is used to create the struts inside wayland class GhostWindow; } } namespace Latte { namespace WindowSystem { class WaylandInterface : public AbstractWindowInterface { Q_OBJECT public: explicit WaylandInterface(QObject *parent = nullptr); ~WaylandInterface() override; void setViewExtraFlags(QObject *view, bool isPanelWindow = true, Latte::Types::Visibility mode = Latte::Types::WindowsGoBelow) override; void setViewStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) override; void setWindowOnActivities(QWindow &view, const QStringList &activities) override; void removeViewStruts(QWindow &view) override; WindowId activeWindow() override; WindowInfoWrap requestInfo(WindowId wid) override; WindowInfoWrap requestInfoActive() override; void skipTaskBar(const QDialog &dialog) override; void slideWindow(QWindow &view, Slide location) override; void enableBlurBehind(QWindow &view) override; void requestActivate(WindowId wid) override; void requestClose(WindowId wid) override; void requestMoveWindow(WindowId wid, QPoint from) override; void requestToggleIsOnAllDesktops(WindowId wid) override; void requestToggleKeepAbove(WindowId wid) override; void requestToggleMinimized(WindowId wid) override; void requestToggleMaximized(WindowId wid) override; void setKeepAbove(WindowId wid, bool active) override; void setKeepBelow(WindowId wid, bool active) override; bool windowCanBeDragged(WindowId wid) override; bool windowCanBeMaximized(WindowId wid) override; QIcon iconFor(WindowId wid) override; WindowId winIdFor(QString appId, QRect geometry) override; WindowId winIdFor(QString appId, QString title) override; AppData appDataFor(WindowId wid) override; void setActiveEdge(QWindow *view, bool active) override; void switchToNextVirtualDesktop() override; void switchToPreviousVirtualDesktop() override; + void setFrameExtents(QWindow *view, const QMargins &margins) override; + void registerIgnoredWindow(WindowId wid) override; void unregisterIgnoredWindow(WindowId wid) override; void initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement); #if KF5_VERSION_MINOR >= 52 //! VirtualDesktopsSupport void initVirtualDesktopManagement(KWayland::Client::PlasmaVirtualDesktopManagement *virtualDesktopManagement); #endif private slots: void updateWindow(); void windowUnmapped(); private: void init(); bool isAcceptableWindow(const KWayland::Client::PlasmaWindow *w); bool isValidWindow(const KWayland::Client::PlasmaWindow *w); bool isFullScreenWindow(const KWayland::Client::PlasmaWindow *w) const; bool isPlasmaPanel(const KWayland::Client::PlasmaWindow *w) const; bool isSidepanel(const KWayland::Client::PlasmaWindow *w) const; void windowCreatedProxy(KWayland::Client::PlasmaWindow *w); void trackWindow(KWayland::Client::PlasmaWindow *w); void untrackWindow(KWayland::Client::PlasmaWindow *w); KWayland::Client::PlasmaWindow *windowFor(WindowId wid); KWayland::Client::PlasmaShell *waylandCoronaInterface() const; #if KF5_VERSION_MINOR >= 52 //! VirtualDesktopsSupport void setCurrentDesktop(QString desktop); void addDesktop(const QString &id, quint32 position); #endif private: friend class Private::GhostWindow; mutable QMap m_ghostWindows; KWayland::Client::PlasmaWindowManagement *m_windowManagement{nullptr}; #if KF5_VERSION_MINOR >= 52 //! VirtualDesktopsSupport KWayland::Client::PlasmaVirtualDesktopManagement *m_virtualDesktopManagement{nullptr}; QStringList m_desktops; #endif Latte::Corona *m_corona{nullptr}; }; } } #endif // WAYLANDINTERFACE_H diff --git a/app/wm/xwindowinterface.cpp b/app/wm/xwindowinterface.cpp index c55dca9c..9686ff25 100644 --- a/app/wm/xwindowinterface.cpp +++ b/app/wm/xwindowinterface.cpp @@ -1,740 +1,764 @@ /* * 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 "xwindowinterface.h" // local #include #include "tasktools.h" #include "view/view.h" #include "view/helpers/screenedgeghostwindow.h" // Qt #include #include #include // KDE #include #include #include #include // X11 #include #include namespace Latte { namespace WindowSystem { XWindowInterface::XWindowInterface(QObject *parent) : AbstractWindowInterface(parent) { m_currentDesktop = QString(KWindowSystem::self()->currentDesktop()); connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AbstractWindowInterface::activeWindowChanged); connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, this, &AbstractWindowInterface::windowRemoved); connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &XWindowInterface::windowAddedProxy); connect(KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, [&](int desktop) { m_currentDesktop = QString(desktop); emit currentDesktopChanged(); }); connect(KWindowSystem::self() , static_cast (&KWindowSystem::windowChanged) , this, &XWindowInterface::windowChangedProxy); for(auto wid : KWindowSystem::self()->windows()) { windowAddedProxy(wid); } } XWindowInterface::~XWindowInterface() { } void XWindowInterface::setViewExtraFlags(QObject *view,bool isPanelWindow, Latte::Types::Visibility mode) { WId winId = -1; QQuickView *quickView = qobject_cast(view); if (quickView) { winId = quickView->winId(); } if (!quickView) { QQuickWindow *quickWindow = qobject_cast(view); if (quickWindow) { winId = quickWindow->winId(); } } NETWinInfo winfo(QX11Info::connection() , static_cast(winId) , static_cast(winId) , 0, 0); winfo.setAllowedActions(NET::ActionChangeDesktop); if (isPanelWindow) { KWindowSystem::setType(winId, NET::Dock); } else { KWindowSystem::setType(winId, NET::Normal); } #if KF5_VERSION_MINOR >= 45 KWindowSystem::setState(winId, NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher); #else KWindowSystem::setState(winId, NET::SkipTaskbar | NET::SkipPager); #endif KWindowSystem::setOnAllDesktops(winId, true); //! Layer to be applied if (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover) { setKeepBelow(winId, true); } else if (mode == Latte::Types::NormalWindow) { setKeepBelow(winId, false); setKeepAbove(winId, false); } else { setKeepAbove(winId, true); } } void XWindowInterface::setViewStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) { NETExtendedStrut strut; const auto screen = view.screen(); const QRect currentScreen {screen->geometry()}; const QRect wholeScreen {{0, 0}, screen->virtualSize()}; switch (location) { case Plasma::Types::TopEdge: { const int topOffset {screen->geometry().top()}; strut.top_width = rect.height() + topOffset; strut.top_start = rect.x(); strut.top_end = rect.x() + rect.width() - 1; break; } case Plasma::Types::BottomEdge: { const int bottomOffset {wholeScreen.bottom() - currentScreen.bottom()}; strut.bottom_width = rect.height() + bottomOffset; strut.bottom_start = rect.x(); strut.bottom_end = rect.x() + rect.width() - 1; break; } case Plasma::Types::LeftEdge: { const int leftOffset = {screen->geometry().left()}; strut.left_width = rect.width() + leftOffset; strut.left_start = rect.y(); strut.left_end = rect.y() + rect.height() - 1; break; } case Plasma::Types::RightEdge: { const int rightOffset = {wholeScreen.right() - currentScreen.right()}; strut.right_width = rect.width() + rightOffset; strut.right_start = rect.y(); strut.right_end = rect.y() + rect.height() - 1; break; } default: qWarning() << "wrong location:" << location; return; } KWindowSystem::setExtendedStrut(view.winId(), strut.left_width, strut.left_start, strut.left_end, strut.right_width, strut.right_start, strut.right_end, strut.top_width, strut.top_start, strut.top_end, strut.bottom_width, strut.bottom_start, strut.bottom_end ); } void XWindowInterface::switchToNextVirtualDesktop() { int desktops = KWindowSystem::numberOfDesktops(); if (desktops <= 1) { return; } int curPos = KWindowSystem::currentDesktop(); int nextPos = curPos + 1; if (curPos == desktops) { nextPos = 1; } KWindowSystem::setCurrentDesktop(nextPos); } void XWindowInterface::switchToPreviousVirtualDesktop() { int desktops = KWindowSystem::numberOfDesktops(); if (desktops <= 1) { return; } int curPos = KWindowSystem::currentDesktop(); int nextPos = curPos - 1; if (curPos == 1) { nextPos = desktops; } KWindowSystem::setCurrentDesktop(nextPos); } void XWindowInterface::setWindowOnActivities(QWindow &window, const QStringList &activities) { KWindowSystem::setOnActivities(window.winId(), activities); } void XWindowInterface::removeViewStruts(QWindow &view) { KWindowSystem::setStrut(view.winId(), 0, 0, 0, 0); } WindowId XWindowInterface::activeWindow() { return KWindowSystem::self()->activeWindow(); } void XWindowInterface::skipTaskBar(const QDialog &dialog) { KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); } void XWindowInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) { auto slideLocation = KWindowEffects::NoEdge; switch (location) { case Slide::Top: slideLocation = KWindowEffects::TopEdge; break; case Slide::Bottom: slideLocation = KWindowEffects::BottomEdge; break; case Slide::Left: slideLocation = KWindowEffects::LeftEdge; break; case Slide::Right: slideLocation = KWindowEffects::RightEdge; break; default: break; } KWindowEffects::slideWindow(view.winId(), slideLocation, -1); } void XWindowInterface::enableBlurBehind(QWindow &view) { KWindowEffects::enableBlurBehind(view.winId()); } void XWindowInterface::setActiveEdge(QWindow *view, bool active) { ViewPart::ScreenEdgeGhostWindow *window = qobject_cast(view); if (!window) { return; } xcb_connection_t *c = QX11Info::connection(); const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"); xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData()); QScopedPointer atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); if (!atom) { return; } if (!active) { xcb_delete_property(c, window->winId(), atom->atom); window->hideWithMask(); return; } window->showWithMask(); uint32_t value = 0; switch (window->location()) { case Plasma::Types::TopEdge: value = 0; break; case Plasma::Types::RightEdge: value = 1; break; case Plasma::Types::BottomEdge: value = 2; break; case Plasma::Types::LeftEdge: value = 3; break; case Plasma::Types::Floating: default: value = 4; break; } int hideType = 0; value |= hideType << 8; xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, 1, &value); } #if KF5_VERSION_MINOR >= 65 QRect XWindowInterface::visibleGeometry(const WindowId &wid, const QRect &frameGeometry) const { - NETWinInfo ni(QX11Info::connection(), wid.toUInt(), QX11Info::appRootWindow(), 0, NET::WM2GTKFrameExtents); NETStrut struts = ni.gtkFrameExtents(); QMargins margins(struts.left, struts.top, struts.right, struts.bottom); QRect visibleGeometry = frameGeometry; if (!margins.isNull()) { visibleGeometry -= margins; } return visibleGeometry; } #endif + +void XWindowInterface::setFrameExtents(QWindow *view, const QMargins &margins) +{ + if (!view) { + return; + } + +#if KF5_VERSION_MINOR >= 65 + NETWinInfo ni(QX11Info::connection(), view->winId(), QX11Info::appRootWindow(), 0, NET::WM2GTKFrameExtents); + + NETStrut struts; + struts.left = margins.left(); + struts.top = margins.top(); + struts.right = margins.right(); + struts.bottom = margins.bottom(); + + ni.setGtkFrameExtents(struts); + + NETStrut applied = ni.gtkFrameExtents(); + QMargins amargins(applied.left, applied.top, applied.right, applied.bottom); + qDebug() << " window applied extents :: " << amargins; +#endif +} + + WindowInfoWrap XWindowInterface::requestInfoActive() { return requestInfo(KWindowSystem::activeWindow()); } WindowInfoWrap XWindowInterface::requestInfo(WindowId wid) { const KWindowInfo winfo{wid.value(), NET::WMFrameExtents | NET::WMWindowType | NET::WMGeometry | NET::WMDesktop | NET::WMState | NET::WMName | NET::WMVisibleName, NET::WM2WindowClass | NET::WM2Activities | NET::WM2AllowedActions | NET::WM2TransientFor}; WindowInfoWrap winfoWrap; if (!winfo.valid()) { winfoWrap.setIsValid(false); } else if (isValidWindow(wid)) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setParentId(winfo.transientFor()); winfoWrap.setIsActive(KWindowSystem::activeWindow() == wid.value()); winfoWrap.setIsMinimized(winfo.hasState(NET::Hidden)); winfoWrap.setIsMaxVert(winfo.hasState(NET::MaxVert)); winfoWrap.setIsMaxHoriz(winfo.hasState(NET::MaxHoriz)); winfoWrap.setIsFullscreen(winfo.hasState(NET::FullScreen)); winfoWrap.setIsShaded(winfo.hasState(NET::Shaded)); winfoWrap.setIsOnAllDesktops(winfo.onAllDesktops()); winfoWrap.setIsOnAllActivities(winfo.activities().empty()); #if KF5_VERSION_MINOR >= 65 winfoWrap.setGeometry(visibleGeometry(wid, winfo.frameGeometry())); #else winfoWrap.setGeometry(winfo.frameGeometry()); #endif winfoWrap.setIsKeepAbove(winfo.hasState(NET::KeepAbove)); winfoWrap.setIsKeepBelow(winfo.hasState(NET::KeepBelow)); winfoWrap.setHasSkipPager(winfo.hasState(NET::SkipPager)); #if KF5_VERSION_MINOR >= 45 winfoWrap.setHasSkipSwitcher(winfo.hasState(NET::SkipSwitcher)); #endif winfoWrap.setHasSkipTaskbar(winfo.hasState(NET::SkipTaskbar)); //! BEGIN:Window Abilities winfoWrap.setIsClosable(winfo.actionSupported(NET::ActionClose)); winfoWrap.setIsFullScreenable(winfo.actionSupported(NET::ActionFullScreen)); winfoWrap.setIsMaximizable(winfo.actionSupported(NET::ActionMax)); winfoWrap.setIsMinimizable(winfo.actionSupported(NET::ActionMinimize)); winfoWrap.setIsMovable(winfo.actionSupported(NET::ActionMove)); winfoWrap.setIsResizable(winfo.actionSupported(NET::ActionResize)); winfoWrap.setIsShadeable(winfo.actionSupported(NET::ActionShade)); winfoWrap.setIsVirtualDesktopsChangeable(winfo.actionSupported(NET::ActionChangeDesktop)); //! END:Window Abilities winfoWrap.setDisplay(winfo.visibleName()); winfoWrap.setDesktops({QString(winfo.desktop())}); winfoWrap.setActivities(winfo.activities()); } return winfoWrap; } AppData XWindowInterface::appDataFor(WindowId wid) { return appDataFromUrl(windowUrl(wid)); } QUrl XWindowInterface::windowUrl(WindowId wid) { const KWindowInfo info(wid.value(), 0, NET::WM2WindowClass | NET::WM2DesktopFileName); QString desktopFile = QString::fromUtf8(info.desktopFileName()); if (!desktopFile.isEmpty()) { KService::Ptr service = KService::serviceByStorageId(desktopFile); if (service) { const QString &menuId = service->menuId(); // applications: URLs are used to refer to applications by their KService::menuId // (i.e. .desktop file name) rather than the absolute path to a .desktop file. if (!menuId.isEmpty()) { return QUrl(QStringLiteral("applications:") + menuId); } return QUrl::fromLocalFile(service->entryPath()); } if (!desktopFile.endsWith(QLatin1String(".desktop"))) { desktopFile.append(QLatin1String(".desktop")); } if (KDesktopFile::isDesktopFile(desktopFile) && QFile::exists(desktopFile)) { return QUrl::fromLocalFile(desktopFile); } } return windowUrlFromMetadata(info.windowClassClass(), NETWinInfo(QX11Info::connection(), wid.value(), QX11Info::appRootWindow(), NET::WMPid, NET::Properties2()).pid(), rulesConfig, info.windowClassName()); } bool XWindowInterface::windowCanBeDragged(WindowId wid) { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && !winfo.isMinimized() && winfo.isMovable() && inCurrentDesktopActivity(winfo)); } bool XWindowInterface::windowCanBeMaximized(WindowId wid) { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && !winfo.isMinimized() && winfo.isMaximizable() && inCurrentDesktopActivity(winfo)); } void XWindowInterface::requestActivate(WindowId wid) { KWindowSystem::activateWindow(wid.toInt()); } QIcon XWindowInterface::iconFor(WindowId wid) { QIcon icon; icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeSmall, KIconLoader::SizeSmall, false)); icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium, false)); icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeMedium, KIconLoader::SizeMedium, false)); icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeLarge, KIconLoader::SizeLarge, false)); return icon; } WindowId XWindowInterface::winIdFor(QString appId, QRect geometry) { return activeWindow(); } WindowId XWindowInterface::winIdFor(QString appId, QString title) { return activeWindow(); } void XWindowInterface::requestClose(WindowId wid) { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid()) { return; } NETRootInfo ri(QX11Info::connection(), NET::CloseWindow); ri.closeWindowRequest(wInfo.wid().toUInt()); } void XWindowInterface::requestMoveWindow(WindowId wid, QPoint from) { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || !inCurrentDesktopActivity(wInfo)) { return; } int borderX = wInfo.geometry().width() > 120 ? 60 : 10; int borderY{10}; //! find min/max values for x,y based on active window geometry int minX = wInfo.geometry().x() + borderX; int maxX = wInfo.geometry().x() + wInfo.geometry().width() - borderX; int minY = wInfo.geometry().y() + borderY; int maxY = wInfo.geometry().y() + wInfo.geometry().height() - borderY; //! set the point from which this window will be moved, //! make sure that it is in window boundaries int validX = qBound(minX, from.x(), maxX); int validY = qBound(minY, from.y(), maxY); NETRootInfo ri(QX11Info::connection(), NET::WMMoveResize); ri.moveResizeRequest(wInfo.wid().toUInt(), validX, validY, NET::Move); } void XWindowInterface::requestToggleIsOnAllDesktops(WindowId wid) { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid()) { return; } if (KWindowSystem::numberOfDesktops() <= 1) { return; } if (wInfo.isOnAllDesktops()) { KWindowSystem::setOnDesktop(wid.toUInt(), KWindowSystem::currentDesktop()); KWindowSystem::forceActiveWindow(wid.toUInt()); } else { KWindowSystem::setOnAllDesktops(wid.toUInt(), true); } } void XWindowInterface::requestToggleKeepAbove(WindowId wid) { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid()) { return; } NETWinInfo ni(QX11Info::connection(), wid.toUInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); if (wInfo.isKeepAbove()) { ni.setState(NET::States(), NET::StaysOnTop); } else { ni.setState(NET::StaysOnTop, NET::StaysOnTop); } } void XWindowInterface::setKeepAbove(WindowId wid, bool active) { if (wid.toUInt() <= 0) { return; } if (active) { KWindowSystem::setState(wid.toUInt(), NET::KeepAbove); KWindowSystem::clearState(wid.toUInt(), NET::KeepBelow); } else { KWindowSystem::clearState(wid.toUInt(), NET::KeepAbove); } } void XWindowInterface::setKeepBelow(WindowId wid, bool active) { if (wid.toUInt() <= 0) { return; } if (active) { KWindowSystem::setState(wid.toUInt(), NET::KeepBelow); KWindowSystem::clearState(wid.toUInt(), NET::KeepAbove); } else { KWindowSystem::clearState(wid.toUInt(), NET::KeepBelow); } } void XWindowInterface::requestToggleMinimized(WindowId wid) { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || !inCurrentDesktopActivity(wInfo)) { return; } if (wInfo.isMinimized()) { bool onCurrent = wInfo.isOnDesktop(m_currentDesktop); KWindowSystem::unminimizeWindow(wid.toUInt()); if (onCurrent) { KWindowSystem::forceActiveWindow(wid.toUInt()); } } else { KWindowSystem::minimizeWindow(wid.toUInt()); } } void XWindowInterface::requestToggleMaximized(WindowId wid) { WindowInfoWrap wInfo = requestInfo(wid); if (!windowCanBeMaximized(wid) || !inCurrentDesktopActivity(wInfo)) { return; } bool restore = wInfo.isMaxHoriz() && wInfo.isMaxVert(); if (wInfo.isMinimized()) { KWindowSystem::unminimizeWindow(wid.toUInt()); } NETWinInfo ni(QX11Info::connection(), wid.toInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); if (restore) { ni.setState(NET::States(), NET::Max); } else { ni.setState(NET::Max, NET::Max); } } bool XWindowInterface::isValidWindow(WindowId wid) { if (windowsTracker()->isValidFor(wid)) { return true; } return isAcceptableWindow(wid); } bool XWindowInterface::isAcceptableWindow(WindowId wid) { const KWindowInfo info(wid.toUInt(), NET::WMGeometry | NET::WMState, NET::WM2WindowClass); const auto winClass = QString(info.windowClassName()); //! ignored windows do not trackd if (hasBlockedTracking(wid)) { return false; } //! whitelisted/approved windows if (isWhitelistedWindow(wid)) { return true; } //! Window Checks bool hasSkipTaskbar = info.hasState(NET::SkipTaskbar); bool hasSkipPager = info.hasState(NET::SkipPager); bool isSkipped = hasSkipTaskbar && hasSkipPager; if (isSkipped && ((winClass == QLatin1String("yakuake") || (winClass == QLatin1String("krunner"))) )) { registerWhitelistedWindow(wid); } else if (winClass == QLatin1String("plasmashell")) { if (isSkipped && isSidepanel(info.geometry())) { registerWhitelistedWindow(wid); return true; } else if (isPlasmaPanel(info.geometry()) || isFullScreenWindow(info.geometry())) { registerPlasmaIgnoredWindow(wid); return false; } } else if ((winClass == QLatin1String("latte-dock")) || (winClass == QLatin1String("ksmserver"))) { if (isFullScreenWindow(info.geometry())) { registerIgnoredWindow(wid); return false; } } return !isSkipped; } void XWindowInterface::windowAddedProxy(WId wid) { if (!isAcceptableWindow(wid)) { return; } emit windowAdded(wid); considerWindowChanged(wid); } void XWindowInterface::windowChangedProxy(WId wid, NET::Properties prop1, NET::Properties2 prop2) { if (!isValidWindow(wid)) { return; } //! accept only NET::Properties events, //! ignore when the user presses a key, or a window is sending X events etc. //! without needing to (e.g. Firefox, https://bugzilla.mozilla.org/show_bug.cgi?id=1389953) //! NET::WM2UserTime, NET::WM2IconPixmap etc.... if (prop1 == 0 && !(prop2 & (NET::WM2Activities | NET::WM2TransientFor))) { return; } //! accept only the following NET:Properties changed signals //! NET::WMState, NET::WMGeometry, NET::ActiveWindow if ( !(prop1 & NET::WMState) && !(prop1 & NET::WMGeometry) && !(prop1 & NET::ActiveWindow) && !(prop1 & NET::WMDesktop) && !(prop1 & (NET::WMName | NET::WMVisibleName) && !(prop2 & NET::WM2TransientFor) && !(prop2 & NET::WM2Activities)) ) { return; } considerWindowChanged(wid); } } } diff --git a/app/wm/xwindowinterface.h b/app/wm/xwindowinterface.h index f6965ae6..fba869b4 100644 --- a/app/wm/xwindowinterface.h +++ b/app/wm/xwindowinterface.h @@ -1,106 +1,108 @@ /* * 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 XWINDOWINTERFACE_H #define XWINDOWINTERFACE_H // local #include #include "abstractwindowinterface.h" #include "windowinfowrap.h" // Qt #include // KDE #include #include namespace Latte { namespace WindowSystem { class XWindowInterface : public AbstractWindowInterface { Q_OBJECT public: explicit XWindowInterface(QObject *parent = nullptr); ~XWindowInterface() override; void setViewExtraFlags(QObject *view, bool isPanelWindow = true, Latte::Types::Visibility mode = Latte::Types::WindowsGoBelow) override; void setViewStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location) override; void setWindowOnActivities(QWindow &window, const QStringList &activities) override; void removeViewStruts(QWindow &view) override; WindowId activeWindow() override; WindowInfoWrap requestInfo(WindowId wid) override; WindowInfoWrap requestInfoActive() override; void skipTaskBar(const QDialog &dialog) override; void slideWindow(QWindow &view, Slide location) override; void enableBlurBehind(QWindow &view) override; void requestActivate(WindowId wid) override; void requestClose(WindowId wid) override; void requestMoveWindow(WindowId wid, QPoint from) override; void requestToggleIsOnAllDesktops(WindowId wid) override; void requestToggleKeepAbove(WindowId wid) override; void requestToggleMinimized(WindowId wid) override; void requestToggleMaximized(WindowId wid) override; void setKeepAbove(WindowId wid, bool active) override; void setKeepBelow(WindowId wid, bool active) override; bool windowCanBeDragged(WindowId wid) override; bool windowCanBeMaximized(WindowId wid) override; QIcon iconFor(WindowId wid) override; WindowId winIdFor(QString appId, QRect geometry) override; WindowId winIdFor(QString appId, QString title) override; AppData appDataFor(WindowId wid) override; void setActiveEdge(QWindow *view, bool active) override; void switchToNextVirtualDesktop() override; void switchToPreviousVirtualDesktop() override; + void setFrameExtents(QWindow *view, const QMargins &margins) override; + private: bool isAcceptableWindow(WindowId wid); bool isValidWindow(WindowId wid); #if KF5_VERSION_MINOR >= 65 QRect visibleGeometry(const WindowId &wid, const QRect &frameGeometry) const; #endif void windowAddedProxy(WId wid); void windowChangedProxy(WId wid, NET::Properties prop1, NET::Properties2 prop2); QUrl windowUrl(WindowId wid); }; } } #endif // XWINDOWINTERFACE_H diff --git a/containment/package/contents/ui/VisibilityManager.qml b/containment/package/contents/ui/VisibilityManager.qml index d0787ec9..6f71a38a 100644 --- a/containment/package/contents/ui/VisibilityManager.qml +++ b/containment/package/contents/ui/VisibilityManager.qml @@ -1,1021 +1,1033 @@ /* * 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 . */ import QtQuick 2.1 import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.latte.core 0.2 as LatteCore import org.kde.latte.private.containment 0.1 as LatteContainment Item{ id: manager anchors.fill: parent property QtObject window property bool debugManager: Qt.application.arguments.indexOf("--mask") >= 0 property bool blockUpdateMask: false property bool inForceHiding: false //is used when the docks are forced in hiding e.g. when changing layouts property bool normalState : false // this is being set from updateMaskArea property bool previousNormalState : false // this is only for debugging purposes property bool panelIsBiggerFromIconSize: root.useThemePanel && (background.totals.visualThickness >= (metrics.iconSize + metrics.margin.thickness)) property bool maskIsFloating: !root.behaveAsPlasmaPanel && !root.editMode && screenEdgeMarginEnabled && !plasmoid.configuration.fittsLawIsRequested && !inSlidingIn && !inSlidingOut property int maskFloatedGap: maskIsFloating ? Math.max(0, metrics.margin.screenEdge - background.shadows.headThickness) : 0 property int animationSpeed: LatteCore.WindowSystem.compositingActive ? (editModeVisual.inEditMode ? editModeVisual.speed * 0.8 : animations.speedFactor.normal * 1.62 * animations.duration.large) : 0 property bool inLocationAnimation: latteView && latteView.positioner && latteView.positioner.inLocationAnimation property bool inSlidingIn: false //necessary because of its init structure property alias inSlidingOut: slidingAnimationAutoHiddenOut.running property bool inTempHiding: false property bool inScreenEdgeInternalWindowSliding: root.behaveAsDockWithMask && hideThickScreenGap property int length: root.isVertical ? Screen.height : Screen.width //screenGeometry.height : screenGeometry.width property int slidingOutToPos: { if (root.behaveAsPlasmaPanel) { var edgeMargin = screenEdgeMarginEnabled ? plasmoid.configuration.screenEdgeMargin : 0 root.isHorizontal ? root.height + edgeMargin - 1 : root.width + edgeMargin - 1; } else { var topOrLeftEdge = ((plasmoid.location===PlasmaCore.Types.LeftEdge)||(plasmoid.location===PlasmaCore.Types.TopEdge)); return (topOrLeftEdge ? -thicknessNormal : thicknessNormal); } } property int finalScreenEdgeMargin: { //! is used for window geometry calculations if (!screenEdgeMarginEnabled || (hideThickScreenGap && metrics.margin.screenEdge === 0)) { /*window geometry is updated after the local screen margin animation was zeroed*/ return 0; } return plasmoid.configuration.screenEdgeMargin; } property int thicknessAutoHidden: LatteCore.WindowSystem.compositingActive ? 2 : 1 - property int thicknessMid: finalScreenEdgeMargin + (1 + (0.65 * (parabolic.factor.maxZoom-1)))*(metrics.totals.thickness+extraThickMask) //needed in some animations - property int thicknessNormal: finalScreenEdgeMargin + Math.max(metrics.totals.thickness + extraThickMask + 1, background.thickness + background.shadows.headThickness) + property int thicknessMid: finalScreenEdgeMargin + (1 + (0.65 * (parabolic.factor.maxZoom-1)))*(metrics.totals.thickness+extraZoomThickMask) //needed in some animations + property int thicknessNormal: finalScreenEdgeMargin + Math.max(metrics.totals.thickness + extraNormalThickMask + 1, background.thickness + background.shadows.headThickness) - property int thicknessZoom: finalScreenEdgeMargin + ((metrics.totals.thickness+extraThickMask) * parabolic.factor.maxZoom) + 2 + property int thicknessZoom: finalScreenEdgeMargin + ((metrics.totals.thickness+extraZoomThickMask) * parabolic.factor.maxZoom) + 2 //it is used to keep thickness solid e.g. when iconSize changes from auto functions - property int thicknessMidOriginal: finalScreenEdgeMargin + Math.max(thicknessNormalOriginal,extraThickMask + (1 + (0.65 * (parabolic.factor.maxZoom-1)))*(metrics.maxIconSize+metrics.margin.maxThickness)) //needed in some animations + property int thicknessMidOriginal: finalScreenEdgeMargin + Math.max(thicknessNormalOriginal,extraNormalThickMask + (1 + (0.65 * (parabolic.factor.maxZoom-1)))*(metrics.maxIconSize+metrics.margin.maxThickness)) //needed in some animations property int thicknessNormalOriginal: finalScreenEdgeMargin + metrics.maxIconSize + (metrics.margin.maxThickness * 2) //this way we always have the same thickness published at all states /*property int thicknessNormalOriginal: !root.behaveAsPlasmaPanel || root.editMode ? thicknessNormalOriginalValue : background.thickness + background.shadows.headThickness*/ - property int thicknessNormalOriginalValue: finalScreenEdgeMargin + metrics.maxIconSize + (metrics.margin.maxThickness * 2) + extraThickMask + 1 - property int thicknessZoomOriginal: finalScreenEdgeMargin + Math.max( ((metrics.maxIconSize+(metrics.margin.maxThickness * 2)) * parabolic.factor.maxZoom) + extraThickMask + 2, + property int thicknessNormalOriginalValue: finalScreenEdgeMargin + metrics.maxIconSize + (metrics.margin.maxThickness * 2) + extraNormalThickMask + 1 + property int thicknessZoomOriginal: finalScreenEdgeMargin + Math.max( ((metrics.maxIconSize+(metrics.margin.maxThickness * 2)) * parabolic.factor.maxZoom) + extraZoomThickMask + 2, background.thickness + background.shadows.headThickness, (LatteCore.WindowSystem.compositingActive ? thicknessEditMode + root.editShadow : thicknessEditMode)) //! is used from Panel in edit mode in order to provide correct masking property int thicknessEditMode: thicknessNormalOriginalValue + editModeVisual.settingsThickness //! when Latte behaves as Plasma panel property int thicknessAsPanel: metrics.totals.thickness //! is used to increase the mask thickness - readonly property int marginBetweenContentsAndRuler: root.editMode ? 10 : 0 - property int extraThickMask: marginBetweenContentsAndRuler + Math.max(indicatorsExtraThickMask, shadowsExtraThickMask) + readonly property int marginBetweenContentsAndRuler: 10 + property int extraNormalThickMask: Math.max(indicatorsExtraThickMask, shadowsExtraThickMask) + property int extraZoomThickMask: Math.max(marginBetweenContentsAndRuler, indicatorsExtraThickMask, shadowsExtraThickMask) //! this is set from indicators when they need extra thickness mask size readonly property int indicatorsExtraThickMask: indicators.info.extraMaskThickness property int shadowsExtraThickMask: { if (LatteCore.WindowSystem.isPlatformWayland) { return 0; } //! 45% of max shadow size in px. var shadowMaxNeededMargin = 0.45 * root.appShadowSizeOriginal; var shadowOpacity = (plasmoid.configuration.shadowOpacity) / 100; //! +40% of shadow opacity in percentage shadowOpacity = shadowOpacity + shadowOpacity*0.4; //! This way we are trying to calculate how many pixels are needed in order for the shadow //! to be drawn correctly without being cut of from View::mask() under X11 shadowMaxNeededMargin = (shadowMaxNeededMargin * shadowOpacity); //! give some more space when items shadows are enabled and extremely big if (root.enableShadows && metrics.margin.maxThickness < shadowMaxNeededMargin) { return shadowMaxNeededMargin - metrics.margin.maxThickness; } return 0; } property Item applets: null Binding{ target: latteView property:"maxThickness" //! prevents updating window geometry during closing window in wayland and such fixes a crash when: latteView && !inTempHiding && !inForceHiding && !inScreenEdgeInternalWindowSliding value: root.behaveAsPlasmaPanel && !root.editMode ? thicknessAsPanel : thicknessZoomOriginal } property bool validIconSize: (metrics.iconSize===metrics.maxIconSize || metrics.iconSize === autosize.iconSize) property bool inPublishingState: validIconSize && !inSlidingIn && !inSlidingOut && !inTempHiding && !inForceHiding Binding{ target: latteView property:"normalThickness" when: latteView && inPublishingState value: root.behaveAsPlasmaPanel && !root.editMode ? thicknessAsPanel : thicknessNormalOriginal } Binding{ target: latteView property:"editThickness" when: latteView value: thicknessEditMode } + Binding { + target: latteView + property: "headThicknessGap" + when: latteView && !inTempHiding && !inForceHiding && !inScreenEdgeInternalWindowSliding && inPublishingState + value: { + if (root.behaveAsPlasmaPanel) { + return 0; + } + + return thicknessZoomOriginal - thicknessNormalOriginalValue; + } + } + Binding{ target: latteView property: "type" when: latteView value: root.viewType } Binding{ target: latteView property: "behaveAsPlasmaPanel" when: latteView value: root.editMode ? false : root.behaveAsPlasmaPanel } Binding{ target: latteView property: "fontPixelSize" when: theme value: theme.defaultFont.pixelSize } Binding{ target: latteView property:"inEditMode" when: latteView value: root.editMode } Binding{ target: latteView property:"latteTasksArePresent" when: latteView value: latteApplet !== null } Binding{ target: latteView property: "maxLength" when: latteView value: maxLengthPerCentage/100 } Binding{ target: latteView property: "offset" when: latteView value: plasmoid.configuration.offset/100 } Binding{ target: latteView property: "screenEdgeMargin" when: latteView value: plasmoid.configuration.screenEdgeMargin } Binding{ target: latteView property: "screenEdgeMarginEnabled" when: latteView value: root.screenEdgeMarginEnabled && !root.hideThickScreenGap } Binding{ target: latteView property: "alignment" when: latteView value: root.panelAlignment } Binding{ target: latteView property: "isTouchingTopViewAndIsBusy" when: latteView value: { var isTouchingTopScreenEdge = (latteView.y === latteView.screenGeometry.y); var hasTopBorder = ((latteView.effects && (latteView.effects.enabledBorders & PlasmaCore.FrameSvg.TopBorder)) > 0); return root.isVertical && !latteView.visibility.isHidden && !isTouchingTopScreenEdge && !hasTopBorder && background.isShown; } } Binding{ target: latteView property: "isTouchingBottomViewAndIsBusy" when: latteView value: { var latteBottom = latteView.y + latteView.height; var screenBottom = latteView.screenGeometry.y + latteView.screenGeometry.height; var isTouchingBottomScreenEdge = (latteBottom === screenBottom); var hasBottomBorder = ((latteView.effects && (latteView.effects.enabledBorders & PlasmaCore.FrameSvg.BottomBorder)) > 0); return root.isVertical && !latteView.visibility.isHidden && !isTouchingBottomScreenEdge && !hasBottomBorder && background.isShown; } } //! View::Effects bindings Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "backgroundOpacity" when: latteView && latteView.effects value: background.currentOpacity } Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "drawEffects" when: latteView && latteView.effects value: LatteCore.WindowSystem.compositingActive && !root.inConfigureAppletsMode && (((root.blurEnabled && root.useThemePanel) || (root.blurEnabled && root.forceSolidPanel && LatteCore.WindowSystem.compositingActive)) && (!root.inStartup || inForceHiding || inTempHiding)) } Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "drawShadows" when: latteView && latteView.effects value: root.drawShadowsExternal && (!root.inStartup || inForceHiding || inTempHiding) && !(latteView && latteView.visibility.isHidden) } Binding{ target: latteView && latteView.effects ? latteView.effects : null property:"editShadow" when: latteView && latteView.effects value: root.editShadow } Binding{ target: latteView && latteView.effects ? latteView.effects : null property:"innerShadow" when: latteView && latteView.effects value: { if (editModeVisual.editAnimationEnded && !root.behaveAsPlasmaPanel) { return root.editShadow; } else { return background.shadows.headThickness; } } } Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "settingsMaskSubtracted" when: latteView && latteView.effects value: { if (LatteCore.WindowSystem.compositingActive && root.editMode && editModeVisual.editAnimationEnded && (animations.needBothAxis.count === 0 || parabolic.factor.maxZoom===1) ) { return true; } else { return false; } } } //! View::Positioner bindings Binding{ target: latteView && latteView.positioner ? latteView.positioner : null property: "isStickedOnTopEdge" when: latteView && latteView.positioner value: plasmoid.configuration.isStickedOnTopEdge } Binding{ target: latteView && latteView.positioner ? latteView.positioner : null property: "isStickedOnBottomEdge" when: latteView && latteView.positioner value: plasmoid.configuration.isStickedOnBottomEdge } //! View::WindowsTracker bindings Binding{ target: latteView && latteView.windowsTracker ? latteView.windowsTracker : null property: "enabled" when: latteView && latteView.windowsTracker && latteView.visibility value: (latteView && latteView.visibility && !(latteView.visibility.mode === LatteCore.Types.AlwaysVisible /* Visibility */ || latteView.visibility.mode === LatteCore.Types.WindowsGoBelow || latteView.visibility.mode === LatteCore.Types.AutoHide)) || applets.require.windowsTrackingCount > 0 /*Applets Need Windows Tracking */ || root.dragActiveWindowEnabled /*Dragging Active Window(Empty Areas)*/ || ((root.backgroundOnlyOnMaximized /*Dynamic Background */ || plasmoid.configuration.solidBackgroundForMaximized || root.disablePanelShadowMaximized || root.windowColors !== LatteContainment.Types.NoneWindowColors)) || (root.screenEdgeMarginsEnabled /*Dynamic Screen Edge Margin*/ && plasmoid.configuration.hideScreenGapForMaximized) } Connections{ target:root onEditModeChanged: { if (root.editMode) { visibilityManager.updateMaskArea(); } } } Connections{ target: background.totals onVisualLengthChanged: updateMaskArea(); onVisualThicknessChanged: updateMaskArea(); } Connections{ target: background.shadows onHeadThicknessChanged: updateMaskArea(); } Connections{ target: latteView ? latteView : null onXChanged: updateMaskArea(); onYChanged: updateMaskArea() onWidthChanged: updateMaskArea(); onHeightChanged: updateMaskArea(); } Connections{ target: animations.needBothAxis onCountChanged: updateMaskArea(); } Connections{ target: animations.needLength onCountChanged: updateMaskArea(); } Connections{ target: animations.needThickness onCountChanged: updateMaskArea(); } Connections{ target: layoutsManager onCurrentLayoutIsSwitching: { if (LatteCore.WindowSystem.compositingActive && latteView && latteView.layout && latteView.layout.name === layoutName) { manager.inTempHiding = true; manager.inForceHiding = true; parabolic.sglClearZoom(); manager.slotMustBeHide(); } } } Connections{ target: themeExtended ? themeExtended : null onRoundnessChanged: latteView.effects.forceMaskRedraw(); onThemeChanged: latteView.effects.forceMaskRedraw(); } onMaskIsFloatingChanged: updateMaskArea(); onNormalStateChanged: { if (normalState) { autosize.updateIconSize(); root.updateSizeForAppletsInFill(); } } - onThicknessZoomOriginalChanged: { - updateMaskArea(); - } + onThicknessZoomOriginalChanged: updateMaskArea(); function slotContainsMouseChanged() { if(latteView.visibility.containsMouse && latteView.visibility.mode !== LatteCore.Types.SideBar) { updateMaskArea(); if (slidingAnimationAutoHiddenOut.running && !inTempHiding && !inForceHiding) { slotMustBeShown(); } } } function slotMustBeShown() { //! WindowsCanCover case if (latteView && latteView.visibility.mode === LatteCore.Types.WindowsCanCover) { latteView.visibility.setViewOnFrontLayer(); return; } //! Normal Dodge/AutoHide case if (!slidingAnimationAutoHiddenIn.running && !inTempHiding && !inForceHiding){ slidingAnimationAutoHiddenIn.init(); } } function slotMustBeHide() { if (latteView && latteView.visibility.mode === LatteCore.Types.WindowsCanCover) { latteView.visibility.setViewOnBackLayer(); return; } //! prevent sliding-in on startup if the dodge modes have sent a hide signal if (inStartupTimer.running && root.inStartup) { root.inStartup = false; } //! Normal Dodge/AutoHide case if((!slidingAnimationAutoHiddenOut.running && !latteView.visibility.blockHiding && (!latteView.visibility.containsMouse || latteView.visibility.mode === LatteCore.Types.SideBar)) || inForceHiding) { slidingAnimationAutoHiddenOut.init(); } } //! functions used for sliding out/in during location/screen changes function slotHideDockDuringLocationChange() { inTempHiding = true; blockUpdateMask = true; if(!slidingAnimationAutoHiddenOut.running) { slidingAnimationAutoHiddenOut.init(); } } function slotShowDockAfterLocationChange() { slidingAnimationAutoHiddenIn.init(); } function sendHideDockDuringLocationChangeFinished(){ blockUpdateMask = false; latteView.positioner.hideDockDuringLocationChangeFinished(); } function sendSlidingOutAnimationEnded() { latteView.visibility.hide(); latteView.visibility.isHidden = true; if (visibilityManager.debugManager) { console.log("hiding animation ended..."); } sendHideDockDuringLocationChangeFinished(); } ///test maskArea function updateMaskArea() { if (!latteView || blockUpdateMask) { return; } var localX = 0; var localY = 0; normalState = ((animations.needBothAxis.count === 0) && (animations.needLength.count === 0)) || (latteView.visibility.isHidden && !latteView.visibility.containsMouse && animations.needThickness.count === 0); // debug maskArea criteria if (debugManager) { console.log(animations.needBothAxis.count + ", " + animations.needLength.count + ", " + animations.needThickness.count + ", " + latteView.visibility.isHidden); if (previousNormalState !== normalState) { console.log("normal state changed to:" + normalState); previousNormalState = normalState; } } var tempLength = root.isHorizontal ? width : height; var tempThickness = root.isHorizontal ? height : width; var noCompositingEdit = !LatteCore.WindowSystem.compositingActive && root.editMode; if (LatteCore.WindowSystem.compositingActive || noCompositingEdit) { if (normalState) { //console.log("entered normal state..."); //count panel length //used when !compositing and in editMode if (noCompositingEdit) { tempLength = root.isHorizontal ? root.width : root.height; } else { tempLength = background.totals.visualLength; } tempThickness = thicknessNormal; if (animations.needThickness.count > 0) { tempThickness = LatteCore.WindowSystem.compositingActive ? thicknessZoom : thicknessNormal; } if (maskIsFloating) { tempThickness = tempThickness - maskFloatedGap; } if (latteView.visibility.isHidden && !slidingAnimationAutoHiddenOut.running ) { tempThickness = thicknessAutoHidden; } //configure x,y based on plasmoid position and root.panelAlignment(Alignment) if ((plasmoid.location === PlasmaCore.Types.BottomEdge) || (plasmoid.location === PlasmaCore.Types.TopEdge)) { if (plasmoid.location === PlasmaCore.Types.BottomEdge) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localY = latteView.height + tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localY = latteView.height - tempThickness - maskFloatedGap; } else { localY = latteView.height - tempThickness; } } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localY = -tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localY = maskFloatedGap; } else { localY = 0; } } if (noCompositingEdit) { localX = 0; } else if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { localX = (latteView.width/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Left) { localX = background.offset; } else if (root.panelAlignment === LatteCore.Types.Center) { localX = (latteView.width/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Right) { localX = latteView.width - tempLength - background.offset; } } else if ((plasmoid.location === PlasmaCore.Types.LeftEdge) || (plasmoid.location === PlasmaCore.Types.RightEdge)){ if (plasmoid.location === PlasmaCore.Types.LeftEdge) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localX = -tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localX = maskFloatedGap; } else { localX = 0; } } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localX = latteView.width + tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localX = latteView.width - tempThickness - maskFloatedGap; } else { localX = latteView.width - tempThickness; } } if (noCompositingEdit) { localY = 0; } else if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { localY = (latteView.height/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Top) { localY = background.offset; } else if (root.panelAlignment === LatteCore.Types.Center) { localY = (latteView.height/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Bottom) { localY = latteView.height - tempLength - background.offset; } } if (latteView.visibility.isHidden && latteView && latteView.visibility.mode === LatteCore.Types.SideBar) { //!hide completely localX = -1; localY = -1; tempThickness = 1; tempLength = 1; } } else { // !inNormalState if(root.isHorizontal) tempLength = Screen.width; //screenGeometry.width; else tempLength = Screen.height; //screenGeometry.height; //grow only on length and not thickness var onlyLengthAnimation = (animations.needLength.count>0 && animations.needBothAxis.count === 0); if(onlyLengthAnimation) { //this is used to fix a bug with shadow showing when the animation of edit mode //is triggered tempThickness = editModeVisual.editAnimationEnded ? thicknessEditMode + root.editShadow : thicknessEditMode if (latteView.visibility.isHidden && !slidingAnimationAutoHiddenOut.running ) { tempThickness = thicknessAutoHidden; } else if (animations.needThickness.count > 0) { tempThickness = thicknessZoomOriginal; } } else{ //use all thickness space if (latteView.visibility.isHidden && !slidingAnimationAutoHiddenOut.running ) { tempThickness = LatteCore.WindowSystem.compositingActive ? thicknessAutoHidden : thicknessNormalOriginal; } else { tempThickness = !maskIsFloating ? thicknessZoomOriginal : thicknessZoomOriginal - maskFloatedGap; } } //configure the x,y position based on thickness if(plasmoid.location === PlasmaCore.Types.RightEdge) { localX = !maskIsFloating ? latteView.width - tempThickness : latteView.width - tempThickness - maskFloatedGap; if (localX < 0) { tempThickness = tempThickness + localX; localX = 0; } } else if (plasmoid.location === PlasmaCore.Types.BottomEdge) { localY = !maskIsFloating ? latteView.height - tempThickness : latteView.height - tempThickness - maskFloatedGap; if (localY < 0) { tempThickness = tempThickness + localY; localY = 0; } } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { localY = !maskIsFloating ? 0 : maskFloatedGap; } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { localX = !maskIsFloating ? 0 : maskFloatedGap; } } } // end of compositing calculations var maskArea = latteView.effects.mask; if (LatteCore.WindowSystem.compositingActive) { var maskLength = maskArea.width; //in Horizontal if (root.isVertical) { maskLength = maskArea.height; } var maskThickness = maskArea.height; //in Horizontal if (root.isVertical) { maskThickness = maskArea.width; } } else if (!noCompositingEdit){ //! no compositing case var overridesHidden = latteView.visibility.isHidden && !latteView.visibility.supportsKWinEdges; if (!overridesHidden) { localX = latteView.effects.rect.x; localY = latteView.effects.rect.y; } else { if (plasmoid.location === PlasmaCore.Types.BottomEdge) { localX = latteView.effects.rect.x; localY = root.height - thicknessAutoHidden; } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { localX = latteView.effects.rect.x; localY = 0; } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { localX = 0; localY = latteView.effects.rect.y; } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { localX = root.width - thicknessAutoHidden; localY = latteView.effects.rect.y; } } if (root.isHorizontal) { tempThickness = overridesHidden ? thicknessAutoHidden : latteView.effects.rect.height; tempLength = latteView.effects.rect.width; } else { tempThickness = overridesHidden ? thicknessAutoHidden : latteView.effects.rect.width; tempLength = latteView.effects.rect.height; } } // console.log("Not updating mask..."); if( maskArea.x !== localX || maskArea.y !== localY || maskLength !== tempLength || maskThickness !== tempThickness) { // console.log("Updating mask..."); var newMaskArea = Qt.rect(-1,-1,0,0); newMaskArea.x = localX; newMaskArea.y = localY; if (isHorizontal) { newMaskArea.width = tempLength; newMaskArea.height = tempThickness; } else { newMaskArea.width = tempThickness; newMaskArea.height = tempLength; } if (!LatteCore.WindowSystem.compositingActive) { latteView.effects.mask = newMaskArea; } else { if (latteView.behaveAsPlasmaPanel && !root.editMode) { latteView.effects.mask = Qt.rect(0,0,root.width,root.height); } else { latteView.effects.mask = newMaskArea; } } } var validIconSize = (metrics.iconSize===metrics.maxIconSize || metrics.iconSize === autosize.iconSize); //console.log("reached updating geometry ::: "+dock.maskArea); if(inPublishingState && !latteView.visibility.isHidden && (normalState || root.editMode)) { //! Important: Local Geometry must not be updated when view ISHIDDEN //! because it breaks Dodge(s) modes in such case var localGeometry = Qt.rect(0, 0, root.width, root.height); //the shadows size must be removed from the maskArea //before updating the localDockGeometry if (!latteView.behaveAsPlasmaPanel || root.editMode) { var cleanThickness = metrics.totals.thickness; var edgeMargin = finalScreenEdgeMargin; if (plasmoid.location === PlasmaCore.Types.TopEdge) { localGeometry.x = latteView.effects.rect.x; // from effects area localGeometry.width = latteView.effects.rect.width; // from effects area localGeometry.y = edgeMargin; localGeometry.height = cleanThickness ; } else if (plasmoid.location === PlasmaCore.Types.BottomEdge) { localGeometry.x = latteView.effects.rect.x; // from effects area localGeometry.width = latteView.effects.rect.width; // from effects area localGeometry.y = root.height - cleanThickness - edgeMargin; localGeometry.height = cleanThickness; } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { localGeometry.y = latteView.effects.rect.y; // from effects area localGeometry.height = latteView.effects.rect.height; // from effects area localGeometry.x = edgeMargin; localGeometry.width = cleanThickness; } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { localGeometry.y = latteView.effects.rect.y; // from effects area localGeometry.height = latteView.effects.rect.height; // from effects area localGeometry.x = root.width - cleanThickness - edgeMargin; localGeometry.width = cleanThickness; } //set the boundaries for latteView local geometry //qBound = qMax(min, qMin(value, max)). localGeometry.x = Math.max(0, Math.min(localGeometry.x, latteView.width)); localGeometry.y = Math.max(0, Math.min(localGeometry.y, latteView.height)); localGeometry.width = Math.min(localGeometry.width, latteView.width); localGeometry.height = Math.min(localGeometry.height, latteView.height); } //console.log("update geometry ::: "+localGeometry); latteView.localGeometry = localGeometry; } } Loader{ anchors.fill: parent active: root.debugMode sourceComponent: Item{ anchors.fill:parent Rectangle{ id: windowBackground anchors.fill: parent border.color: "red" border.width: 1 color: "transparent" } Rectangle{ x: latteView ? latteView.effects.mask.x : -1 y: latteView ? latteView.effects.mask.y : -1 height: latteView ? latteView.effects.mask.height : 0 width: latteView ? latteView.effects.mask.width : 0 border.color: "green" border.width: 1 color: "transparent" } } } /***Hiding/Showing Animations*****/ //////////////// Animations - Slide In - Out SequentialAnimation{ id: slidingAnimationAutoHiddenOut ScriptAction{ script: { root.isHalfShown = true; } } PropertyAnimation { target: !root.behaveAsPlasmaPanel ? layoutsContainer : latteView.positioner property: !root.behaveAsPlasmaPanel ? (root.isVertical ? "x" : "y") : "slideOffset" to: { if (root.behaveAsPlasmaPanel) { return slidingOutToPos; } if (LatteCore.WindowSystem.compositingActive) { return slidingOutToPos; } else { if ((plasmoid.location===PlasmaCore.Types.LeftEdge)||(plasmoid.location===PlasmaCore.Types.TopEdge)) { return slidingOutToPos + 1; } else { return slidingOutToPos - 1; } } } duration: manager.animationSpeed easing.type: Easing.InQuad } ScriptAction{ script: { latteView.visibility.isHidden = true; if (root.behaveAsPlasmaPanel && latteView.positioner.slideOffset !== 0) { //! hide real panels when they slide-out latteView.visibility.hide(); } } } onStarted: { if (manager.debugManager) { console.log("hiding animation started..."); } } onStopped: { //! Trying to move the ending part of the signals at the end of editing animation if (!manager.inTempHiding) { manager.updateMaskArea(); } else { if (!editModeVisual.inEditMode) { manager.sendSlidingOutAnimationEnded(); } } latteView.visibility.slideOutFinished(); } function init() { if (manager.inLocationAnimation || !latteView.visibility.blockHiding) { start(); } } } SequentialAnimation{ id: slidingAnimationAutoHiddenIn PauseAnimation{ duration: manager.inTempHiding && animations.active ? 500 : 0 } PropertyAnimation { target: !root.behaveAsPlasmaPanel ? layoutsContainer : latteView.positioner property: !root.behaveAsPlasmaPanel ? (root.isVertical ? "x" : "y") : "slideOffset" to: 0 duration: manager.animationSpeed easing.type: Easing.OutQuad } ScriptAction{ script: { root.isHalfShown = false; root.inStartup = false; } } onStarted: { latteView.visibility.show(); if (manager.debugManager) { console.log("showing animation started..."); } } onStopped: { inSlidingIn = false; if (manager.inTempHiding) { manager.inTempHiding = false; autosize.updateIconSize(); } manager.inTempHiding = false; autosize.updateIconSize(); if (manager.debugManager) { console.log("showing animation ended..."); } latteView.visibility.slideInFinished(); //! this is needed in order to update dock absolute geometry correctly in the end AND //! when a floating dock is sliding-in through masking techniques updateMaskArea(); } function init() { inSlidingIn = true; if (slidingAnimationAutoHiddenOut.running) { slidingAnimationAutoHiddenOut.stop(); } latteView.visibility.isHidden = false; updateMaskArea(); start(); } } //! Slides Animations for FLOATING+BEHAVEASPLASMAPANEL when //! HIDETHICKSCREENCAP dynamically is enabled/disabled SequentialAnimation{ id: slidingInRealFloating PropertyAnimation { target: latteView.positioner property: "slideOffset" to: 0 duration: manager.animationSpeed easing.type: Easing.OutQuad } } SequentialAnimation{ id: slidingOutRealFloating PropertyAnimation { target: latteView.positioner property: "slideOffset" to: plasmoid.configuration.screenEdgeMargin duration: manager.animationSpeed easing.type: Easing.InQuad } } Connections { target: root onHideThickScreenGapChanged: { if (root.behaveAsPlasmaPanel && !latteView.visibility.isHidden && !inSlidingIn && !inSlidingOut && !inStartup) { if (hideThickScreenGap) { latteView.positioner.inSlideAnimation = true; slidingInRealFloating.stop(); slidingOutRealFloating.start(); } else { slidingOutRealFloating.stop(); slidingInRealFloating.start(); latteView.positioner.inSlideAnimation = false; } } } } }