diff --git a/app/lattecorona.h b/app/lattecorona.h index b46d6ed7..46003022 100644 --- a/app/lattecorona.h +++ b/app/lattecorona.h @@ -1,253 +1,254 @@ /* * 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 LATTECORONA_H #define LATTECORONA_H // local #include #include "plasma/quick/configview.h" #include "layout/storage.h" #include "view/panelshadows_p.h" // Qt #include #include // Plasma #include // KDE #include namespace KDeclarative { class QmlObjectSharedEngine; } namespace Plasma { class Corona; class Containment; class Types; } namespace PlasmaQuick { class ConfigView; } namespace KActivities { class Consumer; } namespace KWayland { namespace Client { class PlasmaShell; } } namespace Latte { class CentralLayout; class ScreenPool; class GlobalShortcuts; class UniversalSettings; class View; namespace Indicator{ class Factory; } namespace Layout{ class GenericLayout; class Storage; } namespace Layouts{ class LaunchersSignals; class Manager; } namespace PlasmaExtended{ class ScreenGeometries; class ScreenPool; class Theme; } namespace WindowSystem{ class AbstractWindowInterface; } } namespace Latte { class Corona : public Plasma::Corona { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.LatteDock") public: Corona(bool defaultLayoutOnStartup = false, QString layoutNameOnStartUp = QString(), int userSetMemoryUsage = -1, QObject *parent = nullptr); virtual ~Corona(); int numScreens() const override; QRect screenGeometry(int id) const override; QRegion availableScreenRegion(int id) const override; QRect availableScreenRect(int id) const override; //! This is a very generic function in order to return the availableScreenRect of specific screen //! by calculating only the user specified visibility modes and edges. Empty QLists for both //! arguments mean that all choices are accepted in calculations. ignoreExternalPanels means that //! external panels should be not considered in the calculations QRect availableScreenRectWithCriteria(int id, QString forLayout = QString(), QList ignoreModes = QList(), QList ignoreEdges = QList(), bool ignoreExternalPanels = true, bool desktopUse = false) const; QRegion availableScreenRegionWithCriteria(int id, QString forLayout = QString(), QList ignoreModes = QList(), QList ignoreEdges = QList(), bool ignoreExternalPanels = true, bool desktopUse = false) const; int screenForContainment(const Plasma::Containment *containment) const override; KWayland::Client::PlasmaShell *waylandCoronaInterface() const; KActivities::Consumer *activitiesConsumer() const; GlobalShortcuts *globalShortcuts() const; ScreenPool *screenPool() const; UniversalSettings *universalSettings() const; Layouts::Manager *layoutsManager() const; Indicator::Factory *indicatorFactory() const; PlasmaExtended::ScreenPool *plasmaScreenPool() const; PlasmaExtended::Theme *themeExtended() const; WindowSystem::AbstractWindowInterface *wm() const; PanelShadows *dialogShadows() const; //! Needs to be called in order to import and load application properly after application //! finished all its exit operations void importFullConfiguration(const QString &file); //! these functions are used from context menu through containmentactions void quitApplication(); void switchToLayout(QString layout); void showSettingsWindow(int page); void setContextMenuView(int id); QStringList contextMenuData(); public slots: void aboutApplication(); void addViewForLayout(QString layoutName); void activateLauncherMenu(); void loadDefaultLayout() override; void setBackgroundFromBroadcast(QString activity, QString screenName, QString filename); void setBroadcastedBackgroundsEnabled(QString activity, QString screenName, bool enabled); void showAlternativesForApplet(Plasma::Applet *applet); void toggleHiddenState(QString layoutName, QString screenName, int screenEdge); //! values are separated with a "-" character void windowColorScheme(QString windowIdAndScheme); void updateDockItemBadge(QString identifier, QString value); void unload(); signals: void configurationShown(PlasmaQuick::ConfigView *configView); void viewLocationChanged(); void raiseViewsTemporaryChanged(); void availableScreenRectChangedFrom(Latte::View *origin); void availableScreenRegionChangedFrom(Latte::View *origin); + void verticalUnityViewHasFocus(); private slots: void alternativesVisibilityChanged(bool visible); void load(); void addOutput(QScreen *screen); void primaryOutputChanged(); void screenRemoved(QScreen *screen); void screenCountChanged(); void syncLatteViewsToScreens(); private: void cleanConfig(); void qmlRegisterTypes() const; void setupWaylandIntegration(); bool appletExists(uint containmentId, uint appletId) const; bool containmentExists(uint id) const; int primaryScreenId() const; QStringList containmentsIds(); QStringList appletsIds(); Layout::GenericLayout *layout(QString name) const; CentralLayout *centralLayout(QString name) const; private: bool m_activitiesStarting{true}; bool m_defaultLayoutOnStartup{false}; //! this is used to enforce loading the default layout on startup bool m_quitTimedEnded{false}; //! this is used on destructor in order to delay it and slide-out the views //!it can be used on startup to change memory usage from command line int m_userSetMemoryUsage{ -1}; int m_contextMenuViewId{-1}; QString m_layoutNameOnStartUp; QString m_importFullConfigurationFile; QList m_alternativesObjects; QTimer m_viewsScreenSyncTimer; KActivities::Consumer *m_activitiesConsumer; QPointer aboutDialog; ScreenPool *m_screenPool{nullptr}; UniversalSettings *m_universalSettings{nullptr}; GlobalShortcuts *m_globalShortcuts{nullptr}; Indicator::Factory *m_indicatorFactory{nullptr}; Layouts::Manager *m_layoutsManager{nullptr}; PlasmaExtended::ScreenGeometries *m_plasmaGeometries{nullptr}; PlasmaExtended::ScreenPool *m_plasmaScreenPool{nullptr}; PlasmaExtended::Theme *m_themeExtended{nullptr}; WindowSystem::AbstractWindowInterface *m_wm{nullptr}; PanelShadows *m_dialogShadows{nullptr}; KWayland::Client::PlasmaShell *m_waylandCorona{nullptr}; friend class GlobalShortcuts; friend class Layout::Storage; friend class Layouts::LaunchersSignals; friend class Layouts::Manager; }; } #endif // LATTECORONA_H diff --git a/app/view/positioner.cpp b/app/view/positioner.cpp index 181854ed..be00af73 100644 --- a/app/view/positioner.cpp +++ b/app/view/positioner.cpp @@ -1,940 +1,927 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "positioner.h" // local #include #include "effects.h" #include "view.h" #include "visibilitymanager.h" #include "../lattecorona.h" #include "../screenpool.h" #include "../settings/universalsettings.h" // Qt #include // KDE #include #include #include namespace Latte { namespace ViewPart { Positioner::Positioner(Latte::View *parent) : QObject(parent), m_view(parent) { m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(2000); connect(&m_screenSyncTimer, &QTimer::timeout, this, &Positioner::reconsiderScreen); //! under X11 it was identified that windows many times especially under screen changes //! don't end up at the correct position and size. This timer will enforce repositionings //! and resizes every 500ms if the window hasn't end up to correct values and until this //! is achieved m_validateGeometryTimer.setSingleShot(true); m_validateGeometryTimer.setInterval(500); connect(&m_validateGeometryTimer, &QTimer::timeout, this, &Positioner::syncGeometry); m_corona = qobject_cast(m_view->corona()); if (m_corona) { if (KWindowSystem::isPlatformX11()) { m_trackedWindowId = m_view->winId(); m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); connect(m_view, &Latte::View::forcedShown, this, [&]() { m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId); m_trackedWindowId = m_view->winId(); m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); }); } else { connect(m_view, &QWindow::windowTitleChanged, this, &Positioner::updateWaylandId); connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &Positioner::updateWaylandId); } ///// m_screenSyncTimer.setInterval(qMax(m_corona->universalSettings()->screenTrackerInterval() - 500, 1000)); connect(m_corona->universalSettings(), &UniversalSettings::screenTrackerIntervalChanged, this, [&]() { m_screenSyncTimer.setInterval(qMax(m_corona->universalSettings()->screenTrackerInterval() - 500, 1000)); }); connect(m_corona, &Latte::Corona::viewLocationChanged, this, [&]() { //! check if an edge has been freed for a primary dock //! from another screen if (m_view->onPrimary()) { m_screenSyncTimer.start(); } }); } init(); } Positioner::~Positioner() { m_inDelete = true; m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId); m_screenSyncTimer.stop(); m_validateGeometryTimer.stop(); } void Positioner::init() { //! connections connect(this, &Positioner::screenGeometryChanged, this, &Positioner::syncGeometry); connect(this, &Positioner::hideDockDuringLocationChangeStarted, this, &Positioner::updateInLocationAnimation); connect(this, &Positioner::hideDockDuringScreenChangeStarted, this, &Positioner::updateInLocationAnimation); connect(this, &Positioner::hideDockDuringMovingToLayoutStarted, this, &Positioner::updateInLocationAnimation); connect(this, &Positioner::showDockAfterLocationChangeFinished, this, &Positioner::updateInLocationAnimation); connect(this, &Positioner::showDockAfterScreenChangeFinished, this, &Positioner::updateInLocationAnimation); connect(this, &Positioner::showDockAfterMovingToLayoutFinished, this, &Positioner::updateInLocationAnimation); connect(this, &Positioner::showDockAfterLocationChangeFinished, this, &Positioner::syncLatteViews); connect(this, &Positioner::showDockAfterScreenChangeFinished, this, &Positioner::syncLatteViews); connect(this, &Positioner::showDockAfterMovingToLayoutFinished, this, &Positioner::syncLatteViews); connect(m_view, &Latte::View::onPrimaryChanged, this, &Positioner::syncLatteViews); connect(this, &Positioner::isStickedOnTopEdgeChanged, this, [&]() { if (m_view->formFactor() == Plasma::Types::Vertical) { syncGeometry(); } }); connect(this, &Positioner::isStickedOnBottomEdgeChanged, this, [&]() { if (m_view->formFactor() == Plasma::Types::Vertical) { syncGeometry(); } }); connect(m_view, &QQuickWindow::xChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::yChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::widthChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::heightChanged, this, &Positioner::validateDockGeometry); connect(m_view, &QQuickWindow::screenChanged, this, &Positioner::currentScreenChanged); connect(m_view, &QQuickWindow::screenChanged, this, &Positioner::screenChanged); connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::maxThicknessChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::maxLengthChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::offsetChanged, this, &Positioner::syncGeometry); connect(m_view, &Latte::View::locationChanged, this, [&]() { updateFormFactor(); syncGeometry(); }); connect(m_view, &Latte::View::normalThicknessChanged, this, [&]() { if (m_view->behaveAsPlasmaPanel()) { syncGeometry(); } }); connect(m_view, &Latte::View::screenEdgeMarginEnabledChanged, this, [&]() { if (m_view->inEditMode() ) { syncGeometry(); } }); connect(m_view, &Latte::View::screenEdgeMarginChanged, this, [&]() { if (m_view->screenEdgeMarginEnabled() && m_view->inEditMode()) { syncGeometry(); } }); connect(m_view->effects(), &Latte::ViewPart::Effects::drawShadowsChanged, this, [&]() { if (!m_view->behaveAsPlasmaPanel()) { syncGeometry(); } }); connect(m_view->effects(), &Latte::ViewPart::Effects::innerShadowChanged, this, [&]() { if (m_view->behaveAsPlasmaPanel()) { syncGeometry(); } }); connect(qGuiApp, &QGuiApplication::screenAdded, this, &Positioner::screenChanged); connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Positioner::screenChanged); connect(m_view, &Latte::View::visibilityChanged, this, &Positioner::initDelayedSignals); initSignalingForLocationChangeSliding(); } void Positioner::initDelayedSignals() { connect(m_view->visibility(), &ViewPart::VisibilityManager::isHiddenChanged, this, [&]() { if (m_view->behaveAsPlasmaPanel() && !m_view->visibility()->isHidden() && qAbs(m_slideOffset)>0) { //! ignore any checks to make sure the panel geometry is up-to-date immediateSyncGeometry(); } }); } void Positioner::updateWaylandId() { QString validTitle = m_view->validTitle(); if (validTitle.isEmpty()) { return; } Latte::WindowSystem::WindowId newId = m_corona->wm()->winIdFor("latte-dock", validTitle); if (m_trackedWindowId != newId) { if (!m_trackedWindowId.isNull()) { m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId); } m_trackedWindowId = newId; m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); } } int Positioner::currentScreenId() const { auto *latteCorona = qobject_cast(m_view->corona()); if (latteCorona) { return latteCorona->screenPool()->id(m_screenToFollowId); } return -1; } Latte::WindowSystem::WindowId Positioner::trackedWindowId() { return m_trackedWindowId; } QString Positioner::currentScreenName() const { return m_screenToFollowId; } void Positioner::syncLatteViews() { if (m_view->layout()) { //! This is needed in case the edge there are views that must be deleted //! after screen edges changes m_view->layout()->syncLatteViewsToScreens(); } } void Positioner::updateContainmentScreen() { if (m_view->containment()) { m_view->containment()->reactToScreenChange(); } } bool Positioner::setCurrentScreen(const QString id) { QScreen *nextScreen{qGuiApp->primaryScreen()}; if (id != "primary") { for (const auto scr : qGuiApp->screens()) { if (scr && scr->name() == id) { nextScreen = scr; break; } } } if (m_screenToFollow == nextScreen) { updateContainmentScreen(); return true; } if (nextScreen) { if (m_view->layout()) { m_goToScreen = nextScreen; //! asynchronous call in order to not crash from configwindow //! deletion from sliding out animation QTimer::singleShot(100, [this]() { emit hideDockDuringScreenChangeStarted(); }); } } return true; } //! this function updates the dock's associated screen. //! updateScreenId = true, update also the m_screenToFollowId //! updateScreenId = false, do not update the m_screenToFollowId //! that way an explicit dock can be shown in another screen when //! there isnt a tasks dock running in the system and for that //! dock its first origin screen is stored and that way when //! that screen is reconnected the dock will return to its original //! place void Positioner::setScreenToFollow(QScreen *scr, bool updateScreenId) { if (!scr || (scr && (m_screenToFollow == scr) && (m_view->screen() == scr))) { return; } qDebug() << "setScreenToFollow() called for screen:" << scr->name() << " update:" << updateScreenId; m_screenToFollow = scr; if (updateScreenId) { m_screenToFollowId = scr->name(); } qDebug() << "adapting to screen..."; m_view->setScreen(scr); updateContainmentScreen(); connect(scr, &QScreen::geometryChanged, this, &Positioner::screenGeometryChanged); syncGeometry(); m_view->updateAbsoluteGeometry(true); qDebug() << "setScreenToFollow() ended..."; emit screenGeometryChanged(); emit currentScreenChanged(); } //! the main function which decides if this dock is at the //! correct screen void Positioner::reconsiderScreen() { if (m_inDelete) { return; } qDebug() << "reconsiderScreen() called..."; qDebug() << " Delayer "; for (const auto scr : qGuiApp->screens()) { qDebug() << " D, found screen: " << scr->name(); } bool screenExists{false}; //!check if the associated screen is running for (const auto scr : qGuiApp->screens()) { if (m_screenToFollowId == scr->name() || (m_view->onPrimary() && scr == qGuiApp->primaryScreen())) { screenExists = true; } } qDebug() << "dock screen exists ::: " << screenExists; //! 1.a primary dock must be always on the primary screen if (m_view->onPrimary() && (m_screenToFollowId != qGuiApp->primaryScreen()->name() || m_screenToFollow != qGuiApp->primaryScreen() || m_view->screen() != qGuiApp->primaryScreen())) { //! case 1 qDebug() << "reached case 1: of updating dock primary screen..."; setScreenToFollow(qGuiApp->primaryScreen()); } else if (!m_view->onPrimary()) { //! 2.an explicit dock must be always on the correct associated screen //! there are cases that window manager misplaces the dock, this function //! ensures that this dock will return at its correct screen for (const auto scr : qGuiApp->screens()) { if (scr && scr->name() == m_screenToFollowId) { qDebug() << "reached case 2: updating the explicit screen for dock..."; setScreenToFollow(scr); break; } } } syncGeometry(); qDebug() << "reconsiderScreen() ended..."; } void Positioner::screenChanged(QScreen *scr) { m_screenSyncTimer.start(); //! this is needed in order to update the struts on screen change //! and even though the geometry has been set correctly the offsets //! of the screen must be updated to the new ones if (m_view->visibility() && m_view->visibility()->mode() == Latte::Types::AlwaysVisible) { m_view->updateAbsoluteGeometry(true); } } void Positioner::syncGeometry() { if (!(m_view->screen() && m_view->containment()) || m_inDelete || m_slideOffset!=0 || inSlideAnimation()) { return; } qDebug() << "syncGeometry() called..."; immediateSyncGeometry(); } void Positioner::immediateSyncGeometry() { bool found{false}; qDebug() << "immediateSyncGeometry() called..."; //! before updating the positioning and geometry of the dock //! we make sure that the dock is at the correct screen if (m_view->screen() != m_screenToFollow) { qDebug() << "Sync Geometry screens inconsistent!!!! "; if (m_screenToFollow) { qDebug() << "Sync Geometry screens inconsistent for m_screenToFollow:" << m_screenToFollow->name() << " dock screen:" << m_view->screen()->name(); } if (!m_screenSyncTimer.isActive()) { m_screenSyncTimer.start(); } } else { found = true; } //! if the dock isnt at the correct screen the calculations //! are not executed if (found) { //! compute the free screen rectangle for vertical panels only once //! this way the costly QRegion computations are calculated only once //! instead of two times (both inside the resizeWindow and the updatePosition) QRegion freeRegion;; QRect maximumRect; QRect availableScreenRect{m_view->screen()->geometry()}; if (m_view->formFactor() == Plasma::Types::Vertical) { QString layoutName = m_view->layout() ? m_view->layout()->name() : QString(); auto latteCorona = qobject_cast(m_view->corona()); int fixedScreen = m_view->onPrimary() ? latteCorona->screenPool()->primaryScreenId() : m_view->containment()->screen(); QList ignoreModes({Latte::Types::AutoHide, Latte::Types::SideBar}); QList ignoreEdges({Plasma::Types::LeftEdge, Plasma::Types::RightEdge}); if (m_isStickedOnTopEdge && m_isStickedOnBottomEdge) { //! dont send an empty edges array because that means include all screen edges in calculations ignoreEdges << Plasma::Types::TopEdge; ignoreEdges << Plasma::Types::BottomEdge; } else { if (m_isStickedOnTopEdge) { ignoreEdges << Plasma::Types::TopEdge; } if (m_isStickedOnBottomEdge) { ignoreEdges << Plasma::Types::BottomEdge; } } freeRegion = latteCorona->availableScreenRegionWithCriteria(fixedScreen, layoutName, ignoreModes, ignoreEdges); maximumRect = maximumNormalGeometry(); QRegion availableRegion = freeRegion.intersected(maximumRect); availableScreenRect = freeRegion.intersected(maximumRect).boundingRect(); float area = 0; //! it is used to choose which or the availableRegion rectangles will //! be the one representing dock geometry for (QRegion::const_iterator p_rect=availableRegion.begin(); p_rect!=availableRegion.end(); ++p_rect) { //! the area of each rectangle in calculated in squares of 50x50 //! this is a way to avoid enourmous numbers for area value float tempArea = (float)((*p_rect).width() * (*p_rect).height()) / 2500; if (tempArea > area) { availableScreenRect = (*p_rect); area = tempArea; } } validateTopBottomBorders(availableScreenRect, freeRegion); } else { m_view->effects()->setForceTopBorder(false); m_view->effects()->setForceBottomBorder(false); } m_view->effects()->updateEnabledBorders(); resizeWindow(availableScreenRect); updatePosition(availableScreenRect); qDebug() << "syncGeometry() calculations for screen: " << m_view->screen()->name() << " _ " << m_view->screen()->geometry(); qDebug() << "syncGeometry() calculations for edge: " << m_view->location(); } qDebug() << "syncGeometry() ended..."; // qDebug() << "dock geometry:" << qRectToStr(geometry()); } void Positioner::validateDockGeometry() { if (m_slideOffset==0 && m_view->geometry() != m_validGeometry) { m_validateGeometryTimer.start(); } } //! this is used mainly from vertical panels in order to //! to get the maximum geometry that can be used from the dock //! based on their alignment type and the location dock QRect Positioner::maximumNormalGeometry() { int xPos = 0; int yPos = m_view->screen()->geometry().y();; int maxHeight = m_view->screen()->geometry().height(); int maxWidth = m_view->normalThickness(); QRect maxGeometry; maxGeometry.setRect(0, 0, maxWidth, maxHeight); switch (m_view->location()) { case Plasma::Types::LeftEdge: xPos = m_view->screen()->geometry().x(); maxGeometry.setRect(xPos, yPos, maxWidth, maxHeight); break; case Plasma::Types::RightEdge: xPos = m_view->screen()->geometry().right() - maxWidth + 1; maxGeometry.setRect(xPos, yPos, maxWidth, maxHeight); break; default: //! bypass clang warnings break; } - //! this is needed in order to preserve that the top dock will be above - //! the others in case flag bypasswindowmanagerhint hasn't be set, - //! such a case is the AlwaysVisible mode - //! NO IDEA what this is trying to solve... It must be updated and checked - //! if this IS STILL NEEDED - if (m_view->location() == Plasma::Types::TopEdge - && m_view->visibility()->mode() != Latte::Types::WindowsCanCover - && m_view->visibility()->mode() != Latte::Types::WindowsAlwaysCover) { - KWindowSystem::setState(m_view->winId(), NET::KeepAbove); - } else { - // KWindowSystem::clearState(m_view->winId(), NET::KeepAbove); - } - return maxGeometry; } void Positioner::validateTopBottomBorders(QRect availableScreenRect, QRegion availableScreenRegion) { //! Check if the the top/bottom borders must be drawn also int edgeMargin = qMax(1, m_view->screenEdgeMargin()); if (availableScreenRect.top() != m_view->screenGeometry().top()) { //! check top border QRegion fitInRegion = QRect(m_view->screenGeometry().x(), availableScreenRect.y()-1, edgeMargin, 1); QRegion subtracted = fitInRegion.subtracted(availableScreenRegion); if (subtracted.isNull()) { //!FitIn rectangle fits TOTALLY in the free screen region and as such //!the top border should be drawn m_view->effects()->setForceTopBorder(true); } else { m_view->effects()->setForceTopBorder(false); } } else { m_view->effects()->setForceTopBorder(false); } if (availableScreenRect.bottom() != m_view->screenGeometry().bottom()) { //! check top border QRegion fitInRegion = QRect(m_view->screenGeometry().x(), availableScreenRect.bottom()+1, edgeMargin, 1); QRegion subtracted = fitInRegion.subtracted(availableScreenRegion); if (subtracted.isNull()) { //!FitIn rectangle fits TOTALLY in the free screen region and as such //!the BOTTOM border should be drawn m_view->effects()->setForceBottomBorder(true); } else { m_view->effects()->setForceBottomBorder(false); } } else { m_view->effects()->setForceBottomBorder(false); } } void Positioner::updatePosition(QRect availableScreenRect) { QRect screenGeometry{availableScreenRect}; QPoint position; position = {0, 0}; const auto gap = [&](int scr_length) -> int { return static_cast(scr_length * m_view->offset()); }; const auto gapCentered = [&](int scr_length) -> int { return static_cast(scr_length * ((1 - m_view->maxLength()) / 2) + scr_length * m_view->offset()); }; const auto gapReversed = [&](int scr_length) -> int { return static_cast(scr_length - (scr_length * m_view->maxLength()) - gap(scr_length)); }; int cleanThickness = m_view->normalThickness() - m_view->effects()->innerShadow(); int screenEdgeMargin = (m_view->behaveAsPlasmaPanel() && m_view->screenEdgeMarginEnabled()) ? m_view->screenEdgeMargin() : 0; if (m_view->behaveAsPlasmaPanel()) { //! Update screenEdgeMargin to take into account slide_offset that is used //! in real window animations for BEHAVEASPLASMAPANELS views if (m_view->location() == Plasma::Types::TopEdge || m_view->location() == Plasma::Types::LeftEdge) { screenEdgeMargin = screenEdgeMargin + qAbs(m_slideOffset); } else { screenEdgeMargin = screenEdgeMargin - qAbs(m_slideOffset); } } switch (m_view->location()) { case Plasma::Types::TopEdge: if (m_view->behaveAsPlasmaPanel()) { int y = screenGeometry.y() + screenEdgeMargin; if (m_view->alignment() == Latte::Types::Left) { position = {screenGeometry.x() + gap(screenGeometry.width()), y}; } else if (m_view->alignment() == Latte::Types::Right) { position = {screenGeometry.x() + gapReversed(screenGeometry.width()), y}; } else { position = {screenGeometry.x() + gapCentered(screenGeometry.width()), y}; } } else { position = {screenGeometry.x(), screenGeometry.y()}; } break; case Plasma::Types::BottomEdge: if (m_view->behaveAsPlasmaPanel()) { int y = screenGeometry.y() + screenGeometry.height() - cleanThickness - screenEdgeMargin; if (m_view->alignment() == Latte::Types::Left) { position = {screenGeometry.x() + gap(screenGeometry.width()), y}; } else if (m_view->alignment() == Latte::Types::Right) { position = {screenGeometry.x() + gapReversed(screenGeometry.width()), y}; } else { position = {screenGeometry.x() + gapCentered(screenGeometry.width()), y}; } } else { position = {screenGeometry.x(), screenGeometry.y() + screenGeometry.height() - m_view->height()}; } break; case Plasma::Types::RightEdge: if (m_view->behaveAsPlasmaPanel()) { int x = availableScreenRect.right() - cleanThickness + 1 - screenEdgeMargin; if (m_view->alignment() == Latte::Types::Top) { position = {x, availableScreenRect.y() + gap(availableScreenRect.height())}; } else if (m_view->alignment() == Latte::Types::Bottom) { position = {x, availableScreenRect.y() + gapReversed(availableScreenRect.height())}; } else { position = {x, availableScreenRect.y() + gapCentered(availableScreenRect.height())}; } } else { position = {availableScreenRect.right() - m_view->width() + 1, availableScreenRect.y()}; } break; case Plasma::Types::LeftEdge: if (m_view->behaveAsPlasmaPanel()) { int x = availableScreenRect.x() + screenEdgeMargin; if (m_view->alignment() == Latte::Types::Top) { position = {x, availableScreenRect.y() + gap(availableScreenRect.height())}; } else if (m_view->alignment() == Latte::Types::Bottom) { position = {x, availableScreenRect.y() + gapReversed(availableScreenRect.height())}; } else { position = {x, availableScreenRect.y() + gapCentered(availableScreenRect.height())}; } } else { position = {availableScreenRect.x(), availableScreenRect.y()}; } break; default: qWarning() << "wrong location, couldn't update the panel position" << m_view->location(); } if (m_slideOffset == 0) { //! update valid geometry in normal positioning m_validGeometry.setTopLeft(position); } else { //! when sliding in/out update only the relevant axis for the screen_edge in //! to not mess the calculations and the automatic geometry checkers that //! View::Positioner is using. if (m_view->formFactor() == Plasma::Types::Horizontal) { m_validGeometry.moveLeft(position.x()); } else { m_validGeometry.moveTop(position.y()); } } m_view->setPosition(position); if (m_view->surface()) { m_view->surface()->setPosition(position); } } int Positioner::slideOffset() const { return m_slideOffset; } void Positioner::setSlideOffset(int offset) { if (m_slideOffset == offset) { return; } m_slideOffset = offset; QPoint slidedTopLeft; if (m_view->location() == Plasma::Types::TopEdge) { int boundedY = qMax(m_view->screenGeometry().top() - (m_validGeometry.height() + 1), m_validGeometry.y() - qAbs(m_slideOffset)); slidedTopLeft = {m_validGeometry.x(), boundedY}; } else if (m_view->location() == Plasma::Types::BottomEdge) { int boundedY = qMin(m_view->screenGeometry().bottom() - 1, m_validGeometry.y() + qAbs(m_slideOffset)); slidedTopLeft = {m_validGeometry.x(), boundedY}; } else if (m_view->location() == Plasma::Types::RightEdge) { int boundedX = qMin(m_view->screenGeometry().right() - 1, m_validGeometry.x() + qAbs(m_slideOffset)); slidedTopLeft = {boundedX, m_validGeometry.y()}; } else if (m_view->location() == Plasma::Types::LeftEdge) { int boundedX = qMax(m_view->screenGeometry().left() - (m_validGeometry.width() + 1), m_validGeometry.x() - qAbs(m_slideOffset)); slidedTopLeft = {boundedX, m_validGeometry.y()}; } m_view->setPosition(slidedTopLeft); if (m_view->surface()) { m_view->surface()->setPosition(slidedTopLeft); } emit slideOffsetChanged(); } void Positioner::resizeWindow(QRect availableScreenRect) { QSize screenSize = m_view->screen()->size(); QSize size = (m_view->formFactor() == Plasma::Types::Vertical) ? QSize(m_view->maxThickness(), availableScreenRect.height()) : QSize(screenSize.width(), m_view->maxThickness()); if (m_view->formFactor() == Plasma::Types::Vertical) { //qDebug() << "MAXIMUM RECT :: " << maximumRect << " - AVAILABLE RECT :: " << availableRect; if (m_view->behaveAsPlasmaPanel()) { size.setWidth(m_view->normalThickness()); size.setHeight(static_cast(m_view->maxLength() * availableScreenRect.height())); } } else { if (m_view->behaveAsPlasmaPanel()) { size.setWidth(static_cast(m_view->maxLength() * screenSize.width())); size.setHeight(m_view->normalThickness()); } } m_validGeometry.setSize(size); m_view->setMinimumSize(size); m_view->setMaximumSize(size); m_view->resize(size); if (m_view->formFactor() == Plasma::Types::Horizontal) { emit windowSizeChanged(); } } void Positioner::updateFormFactor() { if (!m_view->containment()) return; switch (m_view->location()) { case Plasma::Types::TopEdge: case Plasma::Types::BottomEdge: m_view->containment()->setFormFactor(Plasma::Types::Horizontal); break; case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: m_view->containment()->setFormFactor(Plasma::Types::Vertical); break; default: qWarning() << "wrong location, couldn't update the panel position" << m_view->location(); } } void Positioner::initSignalingForLocationChangeSliding() { //! signals to handle the sliding-in/out during location changes connect(this, &Positioner::hideDockDuringLocationChangeStarted, this, &Positioner::onHideWindowsForSlidingOut); connect(m_view, &View::locationChanged, this, [&]() { if (m_goToLocation != Plasma::Types::Floating) { m_goToLocation = Plasma::Types::Floating; QTimer::singleShot(100, [this]() { m_view->effects()->setAnimationsBlocked(false); emit showDockAfterLocationChangeFinished(); m_view->showSettingsWindow(); emit edgeChanged(); }); } }); //! signals to handle the sliding-in/out during screen changes connect(this, &Positioner::hideDockDuringScreenChangeStarted, this, &Positioner::onHideWindowsForSlidingOut); connect(this, &Positioner::currentScreenChanged, this, [&]() { if (m_goToScreen) { m_goToScreen = nullptr; QTimer::singleShot(100, [this]() { m_view->effects()->setAnimationsBlocked(false); emit showDockAfterScreenChangeFinished(); m_view->showSettingsWindow(); emit edgeChanged(); }); } }); //! signals to handle the sliding-in/out during moving to another layout connect(this, &Positioner::hideDockDuringMovingToLayoutStarted, this, &Positioner::onHideWindowsForSlidingOut); connect(m_view, &View::layoutChanged, this, [&]() { if (!m_moveToLayout.isEmpty() && m_view->layout()) { m_moveToLayout = ""; QTimer::singleShot(100, [this]() { m_view->effects()->setAnimationsBlocked(false); emit showDockAfterMovingToLayoutFinished(); m_view->showSettingsWindow(); emit edgeChanged(); }); } }); //! ---- both cases ---- !// //! this is used for both location and screen change cases, this signal //! is send when the sliding-out animation has finished connect(this, &Positioner::hideDockDuringLocationChangeFinished, this, [&]() { m_view->effects()->setAnimationsBlocked(true); if (m_goToLocation != Plasma::Types::Floating) { m_view->setLocation(m_goToLocation); } else if (m_goToScreen) { setScreenToFollow(m_goToScreen); } else if (!m_moveToLayout.isEmpty()) { m_view->moveToLayout(m_moveToLayout); } }); } bool Positioner::inLocationAnimation() { return ((m_goToLocation != Plasma::Types::Floating) || (m_moveToLayout != "") || m_goToScreen); } bool Positioner::inSlideAnimation() const { return m_inSlideAnimation; } void Positioner::setInSlideAnimation(bool active) { if (m_inSlideAnimation == active) { return; } m_inSlideAnimation = active; emit inSlideAnimationChanged(); } bool Positioner::isStickedOnTopEdge() const { return m_isStickedOnTopEdge; } void Positioner::setIsStickedOnTopEdge(bool sticked) { if (m_isStickedOnTopEdge == sticked) { return; } m_isStickedOnTopEdge = sticked; emit isStickedOnTopEdgeChanged(); } bool Positioner::isStickedOnBottomEdge() const { return m_isStickedOnBottomEdge; } void Positioner::setIsStickedOnBottomEdge(bool sticked) { if (m_isStickedOnBottomEdge == sticked) { return; } m_isStickedOnBottomEdge = sticked; emit isStickedOnBottomEdgeChanged(); } void Positioner::updateInLocationAnimation() { bool inLocationAnimation = ((m_goToLocation != Plasma::Types::Floating) || (m_moveToLayout != "") || m_goToScreen); if (m_inLocationAnimation == inLocationAnimation) { return; } m_inLocationAnimation = inLocationAnimation; emit inLocationAnimationChanged(); } void Positioner::hideDockDuringLocationChange(int goToLocation) { m_goToLocation = static_cast(goToLocation); emit hideDockDuringLocationChangeStarted(); } void Positioner::hideDockDuringMovingToLayout(QString layoutName) { m_moveToLayout = layoutName; emit hideDockDuringMovingToLayoutStarted(); } } } diff --git a/app/view/view.cpp b/app/view/view.cpp index 4be66295..2ca5a481 100644 --- a/app/view/view.cpp +++ b/app/view/view.cpp @@ -1,1519 +1,1553 @@ /* * 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(); } 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 d71e0259..e0ce0d51 100644 --- a/app/view/view.h +++ b/app/view/view.h @@ -1,423 +1,427 @@ /* * 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 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 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 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_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