diff --git a/app/lattecorona.cpp b/app/lattecorona.cpp index 1faf51b5..26e5504e 100644 --- a/app/lattecorona.cpp +++ b/app/lattecorona.cpp @@ -1,1271 +1,1270 @@ /* * 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 "lattecorona.h" // local #include #include "alternativeshelper.h" #include "apptypes.h" #include "lattedockadaptor.h" #include "screenpool.h" #include "declarativeimports/interfaces.h" #include "indicator/factory.h" #include "layout/centrallayout.h" #include "layout/genericlayout.h" #include "layout/sharedlayout.h" #include "layouts/importer.h" #include "layouts/manager.h" #include "layouts/synchronizer.h" #include "layouts/launcherssignals.h" #include "shortcuts/globalshortcuts.h" #include "package/lattepackage.h" #include "plasma/extended/backgroundcache.h" #include "plasma/extended/backgroundtracker.h" #include "plasma/extended/screengeometries.h" #include "plasma/extended/screenpool.h" #include "plasma/extended/theme.h" #include "settings/universalsettings.h" #include "settings/dialogs/settingsdialog.h" #include "view/view.h" #include "view/windowstracker/windowstracker.h" #include "view/windowstracker/allscreenstracker.h" #include "view/windowstracker/currentscreentracker.h" #include "wm/abstractwindowinterface.h" #include "wm/schemecolors.h" #include "wm/waylandinterface.h" #include "wm/xwindowinterface.h" #include "wm/tracker/lastactivewindow.h" #include "wm/tracker/schemes.h" #include "wm/tracker/windowstracker.h" // Qt #include #include #include #include #include #include #include #include #include #include // Plasma #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Latte { Corona::Corona(bool defaultLayoutOnStartup, QString layoutNameOnStartUp, int userSetMemoryUsage, QObject *parent) : Plasma::Corona(parent), m_defaultLayoutOnStartup(defaultLayoutOnStartup), m_userSetMemoryUsage(userSetMemoryUsage), m_layoutNameOnStartUp(layoutNameOnStartUp), m_activitiesConsumer(new KActivities::Consumer(this)), m_screenPool(new ScreenPool(KSharedConfig::openConfig(), this)), m_indicatorFactory(new Indicator::Factory(this)), m_universalSettings(new UniversalSettings(KSharedConfig::openConfig(), this)), m_globalShortcuts(new GlobalShortcuts(this)), m_plasmaScreenPool(new PlasmaExtended::ScreenPool(this)), m_themeExtended(new PlasmaExtended::Theme(KSharedConfig::openConfig(), this)), m_layoutsManager(new Layouts::Manager(this)), m_plasmaGeometries(new PlasmaExtended::ScreenGeometries(this)), m_dialogShadows(new PanelShadows(this, QStringLiteral("dialogs/background"))) { //! create the window manager if (KWindowSystem::isPlatformWayland()) { m_wm = new WindowSystem::WaylandInterface(this); } else { m_wm = new WindowSystem::XWindowInterface(this); } setupWaylandIntegration(); KPackage::Package package(new Latte::Package(this)); m_screenPool->load(); if (!package.isValid()) { qWarning() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is invalid!"; return; } else { qDebug() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is valid!"; } setKPackage(package); //! universal settings / extendedtheme must be loaded after the package has been set m_universalSettings->load(); m_themeExtended->load(); qmlRegisterTypes(); if (m_activitiesConsumer && (m_activitiesConsumer->serviceStatus() == KActivities::Consumer::Running)) { load(); } connect(m_activitiesConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_viewsScreenSyncTimer.setSingleShot(true); m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); connect(&m_viewsScreenSyncTimer, &QTimer::timeout, this, &Corona::syncLatteViewsToScreens); connect(m_universalSettings, &UniversalSettings::screenTrackerIntervalChanged, this, [this]() { m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); }); //! Dbus adaptor initialization new LatteDockAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Latte"), this); } Corona::~Corona() { //! BEGIN: Give the time to slide-out views when closing m_layoutsManager->synchronizer()->hideAllViews(); //! Don't delay the destruction under wayland in any case //! because it creates a crash with kwin effects //! https://bugs.kde.org/show_bug.cgi?id=392890 if (!KWindowSystem::isPlatformWayland()) { QTimer::singleShot(400, [this]() { m_quitTimedEnded = true; }); while (!m_quitTimedEnded) { QGuiApplication::processEvents(QEventLoop::AllEvents, 50); } } //! END: slide-out views when closing m_viewsScreenSyncTimer.stop(); if (m_layoutsManager->memoryUsage() == MemoryUsage::SingleLayout) { cleanConfig(); } qDebug() << "Latte Corona - unload: containments ..."; m_layoutsManager->unload(); m_plasmaGeometries->deleteLater(); m_wm->deleteLater(); m_dialogShadows->deleteLater(); m_globalShortcuts->deleteLater(); m_layoutsManager->deleteLater(); m_screenPool->deleteLater(); m_universalSettings->deleteLater(); m_plasmaScreenPool->deleteLater(); m_themeExtended->deleteLater(); m_indicatorFactory->deleteLater(); disconnect(m_activitiesConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); delete m_activitiesConsumer; qDebug() << "Latte Corona - deleted..."; if (!m_importFullConfigurationFile.isEmpty()) { //!NOTE: Restart latte to import the new configuration QString importCommand = "latte-dock --import-full \"" + m_importFullConfigurationFile + "\""; qDebug() << "Executing Import Full Configuration command : " << importCommand; QProcess::startDetached(importCommand); } } void Corona::load() { if (m_activitiesConsumer && (m_activitiesConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) { m_activitiesStarting = false; disconnect(m_activitiesConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_layoutsManager->load(); connect(this, &Corona::availableScreenRectChangedFrom, this, &Plasma::Corona::availableScreenRectChanged); connect(this, &Corona::availableScreenRegionChangedFrom, this, &Plasma::Corona::availableScreenRegionChanged); connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Corona::primaryOutputChanged, Qt::UniqueConnection); connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &Corona::screenCountChanged); QString assignedLayout = m_layoutsManager->synchronizer()->shouldSwitchToLayout(m_activitiesConsumer->currentActivity()); QString loadLayoutName = ""; if (!m_defaultLayoutOnStartup && m_layoutNameOnStartUp.isEmpty()) { if (!assignedLayout.isEmpty() && assignedLayout != m_universalSettings->currentLayoutName()) { loadLayoutName = assignedLayout; } else { loadLayoutName = m_universalSettings->currentLayoutName(); } if (!m_layoutsManager->synchronizer()->layoutExists(loadLayoutName)) { loadLayoutName = m_layoutsManager->defaultLayoutName(); m_layoutsManager->importDefaultLayout(false); } } else if (m_defaultLayoutOnStartup) { loadLayoutName = m_layoutsManager->importer()->uniqueLayoutName(m_layoutsManager->defaultLayoutName()); m_layoutsManager->importDefaultLayout(true); } else { loadLayoutName = m_layoutNameOnStartUp; } if (m_userSetMemoryUsage != -1 && !KWindowSystem::isPlatformWayland()) { MemoryUsage::LayoutsMemory usage = static_cast(m_userSetMemoryUsage); m_universalSettings->setLayoutsMemoryUsage(usage); } if (KWindowSystem::isPlatformWayland()) { m_universalSettings->setLayoutsMemoryUsage(MemoryUsage::SingleLayout); } m_layoutsManager->loadLayoutOnStartup(loadLayoutName); //! load screens signals such screenGeometryChanged in order to support //! plasmoid.screenGeometry properly for (QScreen *screen : qGuiApp->screens()) { addOutput(screen); } connect(qGuiApp, &QGuiApplication::screenAdded, this, &Corona::addOutput, Qt::UniqueConnection); connect(qGuiApp, &QGuiApplication::screenRemoved, this, &Corona::screenRemoved, Qt::UniqueConnection); } } void Corona::unload() { qDebug() << "unload: removing containments..."; while (!containments().isEmpty()) { //deleting a containment will remove it from the list due to QObject::destroyed connect in Corona //this form doesn't crash, while qDeleteAll(containments()) does delete containments().first(); } } void Corona::setupWaylandIntegration() { if (!KWindowSystem::isPlatformWayland()) { return; } using namespace KWayland::Client; auto connection = ConnectionThread::fromApplication(this); if (!connection) { return; } Registry *registry{new Registry(this)}; registry->create(connection); connect(registry, &Registry::plasmaShellAnnounced, this , [this, registry](quint32 name, quint32 version) { m_waylandCorona = registry->createPlasmaShell(name, version, this); }); QObject::connect(registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, [this, registry](quint32 name, quint32 version) { KWayland::Client::PlasmaWindowManagement *pwm = registry->createPlasmaWindowManagement(name, version, this); WindowSystem::WaylandInterface *wI = qobject_cast(m_wm); if (wI) { wI->initWindowManagement(pwm); } }); #if KF5_VERSION_MINOR >= 52 QObject::connect(registry, &KWayland::Client::Registry::plasmaVirtualDesktopManagementAnnounced, [this, registry] (quint32 name, quint32 version) { KWayland::Client::PlasmaVirtualDesktopManagement *vdm = registry->createPlasmaVirtualDesktopManagement(name, version, this); WindowSystem::WaylandInterface *wI = qobject_cast(m_wm); if (wI) { wI->initVirtualDesktopManagement(vdm); } }); #endif registry->setup(); connection->roundtrip(); } KWayland::Client::PlasmaShell *Corona::waylandCoronaInterface() const { return m_waylandCorona; } void Corona::cleanConfig() { auto containmentsEntries = config()->group("Containments"); bool changed = false; for(const auto &cId : containmentsEntries.groupList()) { if (!containmentExists(cId.toUInt())) { //cleanup obsolete containments containmentsEntries.group(cId).deleteGroup(); changed = true; qDebug() << "obsolete containment configuration deleted:" << cId; } else { //cleanup obsolete applets of running containments auto appletsEntries = containmentsEntries.group(cId).group("Applets"); for(const auto &appletId : appletsEntries.groupList()) { if (!appletExists(cId.toUInt(), appletId.toUInt())) { appletsEntries.group(appletId).deleteGroup(); changed = true; qDebug() << "obsolete applet configuration deleted:" << appletId; } } } } if (changed) { config()->sync(); qDebug() << "configuration file cleaned..."; } } bool Corona::containmentExists(uint id) const { for(const auto containment : containments()) { if (id == containment->id()) { return true; } } return false; } bool Corona::appletExists(uint containmentId, uint appletId) const { Plasma::Containment *containment = nullptr; for(const auto cont : containments()) { if (containmentId == cont->id()) { containment = cont; break; } } if (!containment) { return false; } for(const auto applet : containment->applets()) { if (applet->id() == appletId) { return true; } } return false; } KActivities::Consumer *Corona::activitiesConsumer() const { return m_activitiesConsumer; } PanelShadows *Corona::dialogShadows() const { return m_dialogShadows; } GlobalShortcuts *Corona::globalShortcuts() const { return m_globalShortcuts; } ScreenPool *Corona::screenPool() const { return m_screenPool; } UniversalSettings *Corona::universalSettings() const { return m_universalSettings; } WindowSystem::AbstractWindowInterface *Corona::wm() const { return m_wm; } Indicator::Factory *Corona::indicatorFactory() const { return m_indicatorFactory; } Layouts::Manager *Corona::layoutsManager() const { return m_layoutsManager; } PlasmaExtended::ScreenPool *Corona::plasmaScreenPool() const { return m_plasmaScreenPool; } PlasmaExtended::Theme *Corona::themeExtended() const { return m_themeExtended; } int Corona::numScreens() const { return qGuiApp->screens().count(); } QRect Corona::screenGeometry(int id) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->hasId(id)) { screenName = m_screenPool->connector(id); } for(const auto scr : screens) { if (scr->name() == screenName) { screen = scr; break; } } return screen->geometry(); } CentralLayout *Corona::centralLayout(QString name) const { CentralLayout *result{nullptr}; if (name.isEmpty()) { result = m_layoutsManager->currentLayout(); } else { CentralLayout *tempCentral = m_layoutsManager->synchronizer()->centralLayout(name); if (!tempCentral) { //! Identify best active layout to be used for metrics calculations. //! Active layouts are always take into account their shared layouts for their metrics SharedLayout *sharedLayout = m_layoutsManager->synchronizer()->sharedLayout(name); if (sharedLayout) { tempCentral = sharedLayout->currentCentralLayout(); } } if (tempCentral) { result = tempCentral; } } return result; } Layout::GenericLayout *Corona::layout(QString name) const { Layout::GenericLayout *result{nullptr}; if (name.isEmpty()) { result = m_layoutsManager->currentLayout(); } else { result = m_layoutsManager->synchronizer()->layout(name); if (!result) { result = m_layoutsManager->currentLayout(); } } return result; } QRegion Corona::availableScreenRegion(int id) const { return availableScreenRegionWithCriteria(id); } QRegion Corona::availableScreenRegionWithCriteria(int id, QString forLayout, QList ignoreModes, QList ignoreEdges, bool ignoreExternalPanels, bool desktopUse) const { const QScreen *screen = m_screenPool->screenForId(id); CentralLayout *layout = centralLayout(forLayout); if (!screen) { return {}; } QRegion available = ignoreExternalPanels ? screen->geometry() : screen->availableGeometry(); if (!layout) { return available; } //! blacklist irrelevant visibility modes if (!ignoreModes.contains(Latte::Types::None)) { ignoreModes << Latte::Types::None; } if (!ignoreModes.contains(Latte::Types::NormalWindow)) { ignoreModes << Latte::Types::NormalWindow; } bool allEdges = ignoreEdges.isEmpty(); QList views = layout->latteViews(); for (const auto *view : views) { if (view && view->containment() && view->screen() == screen && ((allEdges || !ignoreEdges.contains(view->location())) && (view->visibility() && !ignoreModes.contains(view->visibility()->mode())))) { int realThickness = view->normalThickness(); int x = 0; int y = 0; int w = 0; int h = 0; switch (view->formFactor()) { case Plasma::Types::Horizontal: if (view->behaveAsPlasmaPanel()) { w = view->width(); x = view->x(); } else { w = view->maxLength() * view->width(); int offsetW = view->offset() * view->width(); switch (view->alignment()) { case Latte::Types::Left: x = view->x() + offsetW; break; case Latte::Types::Center: case Latte::Types::Justify: x = (view->geometry().center().x() - w/2) + offsetW; break; case Latte::Types::Right: x = view->geometry().right() - w - offsetW; break; } } break; case Plasma::Types::Vertical: if (view->behaveAsPlasmaPanel()) { h = view->height(); y = view->y(); } else { h = view->maxLength() * view->height(); int offsetH = view->offset() * view->height(); switch (view->alignment()) { case Latte::Types::Top: y = view->y() + offsetH; break; case Latte::Types::Center: case Latte::Types::Justify: y = (view->geometry().center().y() - h/2) + offsetH; break; case Latte::Types::Bottom: y = view->geometry().bottom() - h - offsetH; break; } } break; } // Usually availableScreenRect is used by the desktop, // but Latte don't have desktop, then here just // need calculate available space for top and bottom location, // because the left and right are those who dodge others views switch (view->location()) { case Plasma::Types::TopEdge: if (view->behaveAsPlasmaPanel()) { QRect viewGeometry = view->geometry(); if (desktopUse) { //! ignore any real window slide outs in all cases viewGeometry.moveTop(view->screen()->geometry().top() + view->screenEdgeMargin()); } available -= viewGeometry; } else { y = view->y(); available -= QRect(x, y, w, realThickness); } break; case Plasma::Types::BottomEdge: if (view->behaveAsPlasmaPanel()) { QRect viewGeometry = view->geometry(); if (desktopUse) { //! ignore any real window slide outs in all cases viewGeometry.moveTop(view->screen()->geometry().bottom() - view->screenEdgeMargin() - viewGeometry.height()); } available -= viewGeometry; } else { y = view->geometry().bottom() - realThickness + 1; available -= QRect(x, y, w, realThickness); } break; case Plasma::Types::LeftEdge: if (view->behaveAsPlasmaPanel()) { QRect viewGeometry = view->geometry(); if (desktopUse) { //! ignore any real window slide outs in all cases viewGeometry.moveLeft(view->screen()->geometry().left() + view->screenEdgeMargin()); } available -= viewGeometry; } else { x = view->x(); available -= QRect(x, y, realThickness, h); } break; case Plasma::Types::RightEdge: if (view->behaveAsPlasmaPanel()) { QRect viewGeometry = view->geometry(); if (desktopUse) { //! ignore any real window slide outs in all cases viewGeometry.moveLeft(view->screen()->geometry().right() - view->screenEdgeMargin() - viewGeometry.width()); } available -= viewGeometry; } else { x = view->geometry().right() - realThickness + 1; available -= QRect(x, y, realThickness, h); } break; default: //! bypass clang warnings break; } } } /*qDebug() << "::::: FREE AREAS :::::"; for (int i = 0; i < available.rectCount(); ++i) { qDebug() << available.rects().at(i); } qDebug() << "::::: END OF FREE AREAS :::::";*/ return available; } QRect Corona::availableScreenRect(int id) const { return availableScreenRectWithCriteria(id); } QRect Corona::availableScreenRectWithCriteria(int id, QString forLayout, QList ignoreModes, QList ignoreEdges, bool ignoreExternalPanels, bool desktopUse) const { const QScreen *screen = m_screenPool->screenForId(id); CentralLayout *layout = centralLayout(forLayout); if (!screen) { return {}; } QRect available = ignoreExternalPanels ? screen->geometry() : screen->availableGeometry(); if (!layout) { return available; } //! blacklist irrelevant visibility modes if (!ignoreModes.contains(Latte::Types::None)) { ignoreModes << Latte::Types::None; } if (!ignoreModes.contains(Latte::Types::NormalWindow)) { ignoreModes << Latte::Types::NormalWindow; } bool allEdges = ignoreEdges.isEmpty(); QList views = layout->latteViews(); for (const auto *view : views) { if (view && view->containment() && view->screen() == screen && ((allEdges || !ignoreEdges.contains(view->location())) && (view->visibility() && !ignoreModes.contains(view->visibility()->mode())))) { int appliedThickness = view->behaveAsPlasmaPanel() ? view->screenEdgeMargin() + view->normalThickness() : view->normalThickness(); // Usually availableScreenRect is used by the desktop, // but Latte don't have desktop, then here just // need calculate available space for top and bottom location, // because the left and right are those who dodge others docks switch (view->location()) { case Plasma::Types::TopEdge: if (view->behaveAsPlasmaPanel() && desktopUse) { //! ignore any real window slide outs in all cases available.setTop(qMax(available.top(), view->screen()->geometry().top() + appliedThickness)); } else { available.setTop(qMax(available.top(), view->y() + appliedThickness)); } break; case Plasma::Types::BottomEdge: if (view->behaveAsPlasmaPanel() && desktopUse) { //! ignore any real window slide outs in all cases available.setBottom(qMin(available.bottom(), view->screen()->geometry().bottom() - appliedThickness)); } else { available.setBottom(qMin(available.bottom(), view->y() + view->height() - appliedThickness)); } break; case Plasma::Types::LeftEdge: if (view->behaveAsPlasmaPanel() && desktopUse) { //! ignore any real window slide outs in all cases available.setLeft(qMax(available.left(), view->screen()->geometry().left() + appliedThickness)); } else { available.setLeft(qMax(available.left(), view->x() + appliedThickness)); } break; case Plasma::Types::RightEdge: if (view->behaveAsPlasmaPanel() && desktopUse) { //! ignore any real window slide outs in all cases available.setRight(qMin(available.right(), view->screen()->geometry().right() - appliedThickness)); } else { available.setRight(qMin(available.right(), view->x() + view->width() - appliedThickness)); } break; default: //! bypass clang warnings break; } } } return available; } void Corona::addOutput(QScreen *screen) { Q_ASSERT(screen); int id = m_screenPool->id(screen->name()); if (id == -1) { int newId = m_screenPool->firstAvailableId(); m_screenPool->insertScreenMapping(newId, screen->name()); } connect(screen, &QScreen::geometryChanged, this, [ = ]() { const int id = m_screenPool->id(screen->name()); if (id >= 0) { emit screenGeometryChanged(id); emit availableScreenRegionChanged(); emit availableScreenRectChanged(); } }); emit availableScreenRectChanged(); emit screenAdded(m_screenPool->id(screen->name())); screenCountChanged(); } void Corona::primaryOutputChanged() { m_viewsScreenSyncTimer.start(); } void Corona::screenRemoved(QScreen *screen) { screenCountChanged(); } void Corona::screenCountChanged() { m_viewsScreenSyncTimer.start(); } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) void Corona::syncLatteViewsToScreens() { m_layoutsManager->synchronizer()->syncLatteViewsToScreens(); } int Corona::primaryScreenId() const { return m_screenPool->id(qGuiApp->primaryScreen()->name()); } void Corona::quitApplication() { //! this code must be called asynchronously because it is called //! also from qml (Settings window). QTimer::singleShot(300, [this]() { m_layoutsManager->hideLatteSettingsDialog(); m_layoutsManager->synchronizer()->hideAllViews(); }); //! give the time for the views to hide themselves QTimer::singleShot(800, [this]() { qGuiApp->quit(); }); } void Corona::aboutApplication() { if (aboutDialog) { aboutDialog->hide(); aboutDialog->deleteLater(); } aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData()); connect(aboutDialog.data(), &QDialog::finished, aboutDialog.data(), &QObject::deleteLater); m_wm->skipTaskBar(*aboutDialog); m_wm->setKeepAbove(aboutDialog->winId(), true); aboutDialog->show(); } int Corona::screenForContainment(const Plasma::Containment *containment) const { //FIXME: indexOf is not a proper way to support multi-screen // as for environment to environment the indexes change // also there is the following issue triggered // from latteView adaptToScreen() // // in a multi-screen environment that // primary screen is not set to 0 it was // created an endless showing loop at // startup (catch-up race) between // screen:0 and primaryScreen //case in which this containment is child of an applet, hello systray :) if (Plasma::Applet *parentApplet = qobject_cast(containment->parent())) { if (Plasma::Containment *cont = parentApplet->containment()) { return screenForContainment(cont); } else { return -1; } } Plasma::Containment *c = const_cast(containment); Latte::View *view = m_layoutsManager->synchronizer()->viewForContainment(c); if (view && view->screen()) { return m_screenPool->id(view->screen()->name()); } //Failed? fallback on lastScreen() //lastScreen() is the correct screen for panels //It is also correct for desktops *that have the correct activity()* //a containment with lastScreen() == 0 but another activity, //won't be associated to a screen // qDebug() << "ShellCorona screenForContainment: " << containment << " Last screen is " << containment->lastScreen(); for (auto screen : qGuiApp->screens()) { // containment->lastScreen() == m_screenPool->id(screen->name()) to check if the lastScreen refers to a screen that exists/it's known if (containment->lastScreen() == m_screenPool->id(screen->name()) && (containment->activity() == m_activitiesConsumer->currentActivity() || containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment)) { return containment->lastScreen(); } } return -1; } void Corona::showAlternativesForApplet(Plasma::Applet *applet) { const QString alternativesQML = kPackage().filePath("appletalternativesui"); if (alternativesQML.isEmpty()) { return; } Latte::View *latteView = m_layoutsManager->synchronizer()->viewForContainment(applet->containment()); KDeclarative::QmlObjectSharedEngine *qmlObj{nullptr}; if (latteView) { latteView->setAlternativesIsShown(true); qmlObj = new KDeclarative::QmlObjectSharedEngine(latteView); } else { qmlObj = new KDeclarative::QmlObjectSharedEngine(this); } qmlObj->setInitializationDelayed(true); qmlObj->setSource(QUrl::fromLocalFile(alternativesQML)); AlternativesHelper *helper = new AlternativesHelper(applet, qmlObj); qmlObj->rootContext()->setContextProperty(QStringLiteral("alternativesHelper"), helper); m_alternativesObjects << qmlObj; qmlObj->completeInitialization(); //! Alternative dialog signals connect(helper, &QObject::destroyed, this, [latteView]() { latteView->setAlternativesIsShown(false); }); connect(qmlObj->rootObject(), SIGNAL(visibleChanged(bool)), this, SLOT(alternativesVisibilityChanged(bool))); connect(applet, &Plasma::Applet::destroyedChanged, this, [this, qmlObj](bool destroyed) { if (!destroyed) { return; } QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObjectSharedEngine *obj = it.next(); if (obj == qmlObj) { it.remove(); obj->deleteLater(); } } }); } void Corona::alternativesVisibilityChanged(bool visible) { if (visible) { return; } QObject *root = sender(); QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObjectSharedEngine *obj = it.next(); if (obj->rootObject() == root) { it.remove(); obj->deleteLater(); } } } void Corona::addViewForLayout(QString layoutName) { qDebug() << "loading default layout"; //! Setting mutable for create a containment setImmutability(Plasma::Types::Mutable); QVariantList args; auto defaultContainment = createContainmentDelayed("org.kde.latte.containment", args); defaultContainment->setContainmentType(Plasma::Types::PanelContainment); defaultContainment->init(); if (!defaultContainment || !defaultContainment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } auto config = defaultContainment->config(); defaultContainment->restore(config); using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; Layout::GenericLayout *currentLayout = m_layoutsManager->synchronizer()->layout(layoutName); if (currentLayout) { edges = currentLayout->freeEdges(defaultContainment->screen()); } if ((edges.count() > 0)) { defaultContainment->setLocation(edges.at(0)); } else { defaultContainment->setLocation(Plasma::Types::BottomEdge); } if (m_layoutsManager->memoryUsage() == MemoryUsage::MultipleLayouts) { config.writeEntry("layoutId", layoutName); } defaultContainment->updateConstraints(Plasma::Types::StartupCompletedConstraint); defaultContainment->save(config); requestConfigSync(); defaultContainment->flushPendingConstraintsEvents(); emit containmentAdded(defaultContainment); emit containmentCreated(defaultContainment); defaultContainment->createApplet(QStringLiteral("org.kde.latte.plasmoid")); - defaultContainment->createApplet(QStringLiteral("org.kde.plasma.analogclock")); } void Corona::loadDefaultLayout() { addViewForLayout(m_layoutsManager->currentLayoutName()); } QStringList Corona::containmentsIds() { QStringList ids; for(const auto containment : containments()) { ids << QString::number(containment->id()); } return ids; } QStringList Corona::appletsIds() { QStringList ids; for(const auto containment : containments()) { auto applets = containment->config().group("Applets"); ids << applets.groupList(); } return ids; } //! Activate launcher menu through dbus interface void Corona::activateLauncherMenu() { m_globalShortcuts->activateLauncherMenu(); } void Corona::windowColorScheme(QString windowIdAndScheme) { int firstSlash = windowIdAndScheme.indexOf("-"); QString windowIdStr = windowIdAndScheme.mid(0, firstSlash); QString schemeStr = windowIdAndScheme.mid(firstSlash + 1); if (KWindowSystem::isPlatformWayland()) { QTimer::singleShot(200, [this, schemeStr]() { //! [Wayland Case] - give the time to be informed correctly for the active window id //! otherwise the active window id may not be the same with the one trigerred //! the color scheme dbus signal QString windowIdStr = m_wm->activeWindow().toString(); m_wm->schemesTracker()->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); }); } else { m_wm->schemesTracker()->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); } } //! update badge for specific view item void Corona::updateDockItemBadge(QString identifier, QString value) { m_globalShortcuts->updateViewItemBadge(identifier, value); } void Corona::switchToLayout(QString layout) { if ((layout.startsWith("file:/") || layout.startsWith("/")) && layout.endsWith(".layout.latte")) { //! Import and load runtime a layout through dbus interface //! It can be used from external programs that want to update runtime //! the Latte shown layout QString layoutPath = layout; //! cleanup layout path if (layoutPath.startsWith("file:///")) { layoutPath = layout.remove("file://"); } else if (layoutPath.startsWith("file://")) { layoutPath = layout.remove("file:/"); } //! check out layoutpath existence if (QFileInfo(layoutPath).exists()) { qDebug() << " Layout is going to be imported and loaded from file :: " << layoutPath; QString importedLayout = m_layoutsManager->importer()->importLayoutHelper(layoutPath); if (importedLayout.isEmpty()) { qDebug() << i18n("The layout cannot be imported from file :: ") << layoutPath; } else { m_layoutsManager->synchronizer()->loadLayouts(); m_layoutsManager->switchToLayout(importedLayout); } } else { qDebug() << " Layout from missing file can not be imported and loaded :: " << layoutPath; } } else { m_layoutsManager->switchToLayout(layout); } } void Corona::showSettingsWindow(int page) { Settings::Dialog::ConfigurationPage p = Settings::Dialog::LayoutPage; if (page >= Settings::Dialog::LayoutPage && page <= Settings::Dialog::PreferencesPage) { p = static_cast(page); } m_layoutsManager->showLatteSettingsDialog(p); } void Corona::setContextMenuView(int id) { //! set context menu view id m_contextMenuViewId = id; } QStringList Corona::contextMenuData() { QStringList data; Types::ViewType viewType{Types::DockView}; Latte::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); if (currentLayout) { viewType = currentLayout->latteViewType(m_contextMenuViewId); } data << QString::number((int)m_layoutsManager->memoryUsage()); data << m_layoutsManager->currentLayoutName(); data << QString::number((int)viewType); for(const auto &layoutName : m_layoutsManager->menuLayouts()) { if (m_layoutsManager->synchronizer()->centralLayout(layoutName)) { data << QString("1," + layoutName); } else { data << QString("0," + layoutName); } } //! reset context menu view id m_contextMenuViewId = -1; return data; } void Corona::setBackgroundFromBroadcast(QString activity, QString screenName, QString filename) { if (filename.startsWith("file://")) { filename = filename.remove(0,7); } PlasmaExtended::BackgroundCache::self()->setBackgroundFromBroadcast(activity, screenName, filename); } void Corona::setBroadcastedBackgroundsEnabled(QString activity, QString screenName, bool enabled) { PlasmaExtended::BackgroundCache::self()->setBroadcastedBackgroundsEnabled(activity, screenName, enabled); } void Corona::toggleHiddenState(QString layoutName, QString screenName, int screenEdge) { Layout::GenericLayout *gLayout = layout(layoutName); if (gLayout) { gLayout->toggleHiddenState(screenName, (Plasma::Types::Location)screenEdge); } } void Corona::importFullConfiguration(const QString &file) { m_importFullConfigurationFile = file; quitApplication(); } inline void Corona::qmlRegisterTypes() const { qmlRegisterUncreatableMetaObject(Latte::Settings::staticMetaObject, "org.kde.latte.private.app", // import statement 0, 1, // major and minor version of the import "Settings", // name in QML "Error: only enums of latte app settings"); qmlRegisterType("org.kde.latte.private.app", 0, 1, "BackgroundTracker"); qmlRegisterType("org.kde.latte.private.app", 0, 1, "Interfaces"); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); #else qmlRegisterAnonymousType("latte-dock", 1); qmlRegisterAnonymousType("latte-dock", 1); qmlRegisterAnonymousType("latte-dock", 1); qmlRegisterAnonymousType("latte-dock", 1); qmlRegisterAnonymousType("latte-dock", 1); qmlRegisterAnonymousType("latte-dock", 1); qmlRegisterAnonymousType("latte-dock", 1); qmlRegisterAnonymousType("latte-dock", 1); #endif } } diff --git a/app/view/settings/primaryconfigview.cpp b/app/view/settings/primaryconfigview.cpp index 861a7fee..56ebd0ba 100644 --- a/app/view/settings/primaryconfigview.cpp +++ b/app/view/settings/primaryconfigview.cpp @@ -1,864 +1,863 @@ /* * 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 "primaryconfigview.h" // local #include #include "secondaryconfigview.h" #include "../effects.h" #include "../panelshadows_p.h" #include "../view.h" #include "../../lattecorona.h" #include "../../layouts/manager.h" #include "../../layout/genericlayout.h" #include "../../settings/universalsettings.h" #include "../../shortcuts/globalshortcuts.h" #include "../../shortcuts/shortcutstracker.h" #include "../../wm/abstractwindowinterface.h" // Qt #include #include #include #include // KDE #include #include #include #include #include #include // Plasma #include namespace Latte { namespace ViewPart { PrimaryConfigView::PrimaryConfigView(Plasma::Containment *containment, Latte::View *view, QWindow *parent) : PlasmaQuick::ConfigView(containment, parent), m_latteView(view) { m_corona = qobject_cast(m_latteView->containment()->corona()); setupWaylandIntegration(); setTitle(validTitle()); if (KWindowSystem::isPlatformX11()) { m_corona->wm()->registerIgnoredWindow(winId()); } else { connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &PrimaryConfigView::updateWaylandId); } setScreen(m_latteView->screen()); if (containment) { setIcon(qGuiApp->windowIcon()); } m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(100); connect(this, &QQuickView::widthChanged, this, &PrimaryConfigView::updateEffects); connect(this, &QQuickView::heightChanged, this, &PrimaryConfigView::updateEffects); connect(this, &PrimaryConfigView::availableScreenGeometryChanged, this, &PrimaryConfigView::syncGeometry); connect(this, &PrimaryConfigView::inAdvancedModeChanged, this, &PrimaryConfigView::saveConfig); connect(this, &PrimaryConfigView::inAdvancedModeChanged, this, &PrimaryConfigView::updateShowInlineProperties); connect(this, &PrimaryConfigView::inAdvancedModeChanged, this, &PrimaryConfigView::syncGeometry); connect(this, &PrimaryConfigView::inAdvancedModeChanged, m_latteView, &Latte::View::inSettingsAdvancedModeChanged); connect(this, &QQuickView::statusChanged, [&](QQuickView::Status status) { if (status == QQuickView::Ready) { updateEffects(); } }); connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { setScreen(m_latteView->screen()); setFlags(wFlags()); if (KWindowSystem::isPlatformX11()) { m_corona->wm()->setViewExtraFlags(this, false, Latte::Types::NormalWindow); } syncGeometry(); syncSlideEffect(); }); connections << connect(m_latteView, &View::hiddenConfigurationWindowsAreDeletedChanged, this, &PrimaryConfigView::onHiddenConfigurationWindowsAreDeletedChanged); connections << connect(m_latteView->visibility(), &VisibilityManager::modeChanged, this, &PrimaryConfigView::syncGeometry); connections << connect(containment, &Plasma::Containment::immutabilityChanged, this, &PrimaryConfigView::immutabilityChanged); m_thicknessSyncTimer.setSingleShot(true); m_thicknessSyncTimer.setInterval(200); connections << connect(&m_thicknessSyncTimer, &QTimer::timeout, this, [this]() { syncGeometry(); }); connections << connect(m_latteView, &Latte::View::normalThicknessChanged, [&]() { m_thicknessSyncTimer.start(); }); if (m_corona) { connections << connect(m_corona, &Latte::Corona::raiseViewsTemporaryChanged, this, &PrimaryConfigView::raiseDocksTemporaryChanged); connections << connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &PrimaryConfigView::updateAvailableScreenGeometry); } } PrimaryConfigView::~PrimaryConfigView() { qDebug() << "ConfigView deleting ..."; if (m_latteView->indicator()) { //! destroy indicator config ui when the configuration window is closed m_latteView->indicator()->releaseConfigUi(); } m_corona->dialogShadows()->removeWindow(this); m_corona->wm()->unregisterIgnoredWindow(KWindowSystem::isPlatformX11() ? winId() : m_waylandWindowId); if (m_secConfigView) { m_secConfigView->deleteLater(); } for (const auto &var : connections) { QObject::disconnect(var); } } void PrimaryConfigView::init() { qDebug() << "dock config view : initialization started..."; m_originalByPassWM = m_latteView->byPassWM(); m_originalMode = m_latteView->visibility()->mode(); loadConfig(); //! inform view about the current settings level emit m_latteView->inSettingsAdvancedModeChanged(); setDefaultAlphaBuffer(true); setColor(Qt::transparent); m_corona->dialogShadows()->addWindow(this); rootContext()->setContextProperty(QStringLiteral("latteView"), m_latteView); rootContext()->setContextProperty(QStringLiteral("shortcutsEngine"), m_corona->globalShortcuts()->shortcutsTracker()); rootContext()->setContextProperty(QStringLiteral("viewConfig"), this); if (m_corona) { rootContext()->setContextProperty(QStringLiteral("universalSettings"), m_corona->universalSettings()); rootContext()->setContextProperty(QStringLiteral("layoutsManager"), m_corona->layoutsManager()); } KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); #if KF5_VERSION_MINOR >= 45 kdeclarative.setupContext(); kdeclarative.setupEngine(engine()); #else kdeclarative.setupBindings(); #endif QByteArray tempFilePath = "lattedockconfigurationui"; updateEnabledBorders(); updateAvailableScreenGeometry(); auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath)); setSource(source); syncGeometry(); syncSlideEffect(); qDebug() << "dock config view : initialization ended..."; } inline Qt::WindowFlags PrimaryConfigView::wFlags() const { return (flags() | Qt::FramelessWindowHint) & ~Qt::WindowDoesNotAcceptFocus; } QString PrimaryConfigView::validTitle() const { return QString("#primaryconfig#" + QString::number(m_latteView->containment()->id())); } QQuickView *PrimaryConfigView::secondaryWindow() { return m_secConfigView; } void PrimaryConfigView::showSecondaryWindow() { if (!m_secConfigView) { m_secConfigView = new SecondaryConfigView(m_latteView, this); m_secConfigView->init(); } else if (m_secConfigView && !m_latteView->hiddenConfigurationWindowsAreDeleted() && !m_secConfigView->isVisible()){ m_secConfigView->show(); } } void PrimaryConfigView::hideSecondaryWindow() { if (m_secConfigView) { if (m_latteView->hiddenConfigurationWindowsAreDeleted()) { auto secWindow = m_secConfigView; m_secConfigView = nullptr; secWindow->deleteLater(); } else { m_secConfigView->hide(); } if (KWindowSystem::isPlatformX11() && m_latteView->effects()) { //! this is needed in order for subtracked mask of secondary window to //! be released properly when changing for Advanced to Basic mode. //! Under wayland this is not needed because masks do not break any visuals. m_latteView->effects()->updateMask(); } } } void PrimaryConfigView::onHiddenConfigurationWindowsAreDeletedChanged() { if (m_latteView && m_latteView->hiddenConfigurationWindowsAreDeleted() && !isVisible()) { deleteLater(); } } void PrimaryConfigView::updateAvailableScreenGeometry(View *origin) { int currentScrId = m_latteView->positioner()->currentScreenId(); QList ignoreModes{Latte::Types::SideBar}; if (m_latteView->visibility() && m_latteView->visibility()->mode() == Latte::Types::SideBar) { ignoreModes.removeAll(Latte::Types::SideBar); } m_availableScreenGeometry = m_corona->availableScreenRectWithCriteria(currentScrId, QString(), ignoreModes, {}, false); emit availableScreenGeometryChanged(); } QRect PrimaryConfigView::availableScreenGeometry() const { return m_availableScreenGeometry; } QRect PrimaryConfigView::geometryWhenVisible() const { return m_geometryWhenVisible; } void PrimaryConfigView::requestActivate() { if (KWindowSystem::isPlatformWayland() && m_shellSurface) { updateWaylandId(); m_corona->wm()->requestActivate(m_waylandWindowId); } else { QQuickView::requestActivate(); } } void PrimaryConfigView::syncGeometry() { if (!m_latteView || !m_latteView->layout() || !m_latteView->containment() || !rootObject()) { return; } const QSize size(rootObject()->width(), rootObject()->height()); const auto location = m_latteView->containment()->location(); const auto scrGeometry = m_latteView->screenGeometry(); const auto availGeometry = m_availableScreenGeometry; int clearThickness = m_latteView->editThickness(); QPoint position{0, 0}; int xPos{0}; int yPos{0}; switch (m_latteView->formFactor()) { case Plasma::Types::Horizontal: { if (m_inAdvancedMode) { if (qApp->isLeftToRight()) { xPos = availGeometry.x() + availGeometry.width() - size.width(); } else { xPos = availGeometry.x(); } } else { xPos = scrGeometry.center().x() - size.width() / 2; } if (location == Plasma::Types::TopEdge) { yPos = scrGeometry.y() + clearThickness; } else if (location == Plasma::Types::BottomEdge) { yPos = scrGeometry.y() + scrGeometry.height() - clearThickness - size.height(); } } break; case Plasma::Types::Vertical: { if (location == Plasma::Types::LeftEdge) { xPos = scrGeometry.x() + clearThickness; yPos = availGeometry.y() + (availGeometry.height() - size.height())/2; } else if (location == Plasma::Types::RightEdge) { xPos = scrGeometry.x() + scrGeometry.width() - clearThickness - size.width(); yPos = availGeometry.y() + (availGeometry.height() - size.height())/2; } } break; default: qWarning() << "no sync geometry, wrong formFactor"; break; } position = {xPos, yPos}; updateEnabledBorders(); auto geometry = QRect(position.x(), position.y(), size.width(), size.height()); if (m_geometryWhenVisible == geometry) { return; } m_geometryWhenVisible = geometry; setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } setMaximumSize(size); setMinimumSize(size); resize(size); updateViewMask(); emit m_latteView->configWindowGeometryChanged(); } void PrimaryConfigView::updateViewMask() { bool environmentState = (KWindowSystem::isPlatformX11() && KWindowSystem::compositingActive()); if (!environmentState) { return; } int x, y, thickness, length; QRegion area; thickness = m_latteView->effects()->editShadow(); if (m_latteView->formFactor() == Plasma::Types::Vertical) { length = m_geometryWhenVisible.height(); } else { length = m_geometryWhenVisible.width(); } if (m_latteView->formFactor() == Plasma::Types::Horizontal) { x = m_geometryWhenVisible.x() - m_latteView->x(); } else { y = m_geometryWhenVisible.y() - m_latteView->y(); } if (m_latteView->location() == Plasma::Types::BottomEdge) { y = m_latteView->height() - m_latteView->editThickness() - m_latteView->effects()->editShadow(); } else if (m_latteView->location() == Plasma::Types::TopEdge) { y = m_latteView->editThickness(); } else if (m_latteView->location() == Plasma::Types::LeftEdge) { x = m_latteView->editThickness(); } else if (m_latteView->location() == Plasma::Types::RightEdge) { x = m_latteView->width() - m_latteView->editThickness() - m_latteView->effects()->editShadow(); } if (m_latteView->formFactor() == Plasma::Types::Horizontal) { area = QRect(x, y, length, thickness); } else { area = QRect(x, y, thickness, length); } m_latteView->effects()->setSubtractedMaskRegion(validTitle(), area); } void PrimaryConfigView::syncSlideEffect() { if (!m_latteView || !m_latteView->containment()) { return; } auto slideLocation = WindowSystem::AbstractWindowInterface::Slide::None; switch (m_latteView->containment()->location()) { case Plasma::Types::TopEdge: slideLocation = WindowSystem::AbstractWindowInterface::Slide::Top; break; case Plasma::Types::RightEdge: slideLocation = WindowSystem::AbstractWindowInterface::Slide::Right; break; case Plasma::Types::BottomEdge: slideLocation = WindowSystem::AbstractWindowInterface::Slide::Bottom; break; case Plasma::Types::LeftEdge: slideLocation = WindowSystem::AbstractWindowInterface::Slide::Left; break; default: qDebug() << staticMetaObject.className() << "wrong location"; break; } m_corona->wm()->slideWindow(*this, slideLocation); } void PrimaryConfigView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); if (!m_latteView) { return; } setFlags(wFlags()); m_corona->wm()->setViewExtraFlags(this, false, Latte::Types::NormalWindow); syncGeometry(); syncSlideEffect(); if (m_latteView && m_latteView->containment()) { m_latteView->containment()->setUserConfiguring(true); } m_screenSyncTimer.start(); QTimer::singleShot(400, this, &PrimaryConfigView::syncGeometry); updateShowInlineProperties(); updateViewMask(); emit showSignal(); if (m_latteView && m_latteView->layout()) { m_latteView->layout()->setLastConfigViewFor(m_latteView); } if (m_shellSurface) { //! readd shadows after hiding because the window shadows are not shown again after first showing m_corona->dialogShadows()->addWindow(this, m_enabledBorders); } } void PrimaryConfigView::hideEvent(QHideEvent *ev) { if (!m_latteView) { deleteLater(); return; } if (m_latteView->containment()) { m_latteView->containment()->setUserConfiguring(false); } m_latteView->effects()->removeSubtractedMaskRegion(validTitle()); const auto mode = m_latteView->visibility()->mode(); if ((mode == Types::AlwaysVisible || mode == Types::WindowsGoBelow) && !(m_originalMode == Types::AlwaysVisible || m_originalMode == Types::WindowsGoBelow)) { //! mode changed to AlwaysVisible OR WindowsGoBelow FROM Dodge mode if (m_originalByPassWM) { //! if original by pass is active m_latteView->layout()->recreateView(m_latteView->containment()); } } else if (m_latteView->byPassWM() != m_originalByPassWM) { m_latteView->layout()->recreateView(m_latteView->containment()); } if (m_latteView->hiddenConfigurationWindowsAreDeleted()) { deleteLater(); } else { setVisible(false); } } void PrimaryConfigView::focusOutEvent(QFocusEvent *ev) { Q_UNUSED(ev); const auto *focusWindow = qGuiApp->focusWindow(); if (!m_latteView || (focusWindow && (focusWindow->flags().testFlag(Qt::Popup) || focusWindow->flags().testFlag(Qt::ToolTip))) || m_latteView->alternativesIsShown()) { return; } if (!m_blockFocusLost && !m_latteView->containsMouse() && (!m_secConfigView || (m_secConfigView && !m_secConfigView->isActive()))) { hideConfigWindow(); } } void PrimaryConfigView::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) { // already setup return; } if (m_corona) { using namespace KWayland::Client; PlasmaShell *interface = m_corona->waylandCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland primary settings surface was created..."; m_shellSurface = interface->createSurface(s, this); m_corona->wm()->setViewExtraFlags(m_shellSurface, false); syncGeometry(); } } bool PrimaryConfigView::event(QEvent *e) { if (e->type() == QEvent::PlatformSurface) { if (auto pe = dynamic_cast(e)) { switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: if (m_shellSurface) { break; } setupWaylandIntegration(); break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; qDebug() << "WAYLAND config window surface was deleted..."; } break; } } } return PlasmaQuick::ConfigView::event(e); } void PrimaryConfigView::immutabilityChanged(Plasma::Types::ImmutabilityType type) { if (type != Plasma::Types::Mutable && isVisible()) hideConfigWindow(); } bool PrimaryConfigView::sticker() const { return m_blockFocusLost; } void PrimaryConfigView::setSticker(bool blockFocusLost) { if (m_blockFocusLost == blockFocusLost) return; m_blockFocusLost = blockFocusLost; } bool PrimaryConfigView::showInlineProperties() const { return m_showInlineProperties; } void PrimaryConfigView::setShowInlineProperties(bool show) { if (m_showInlineProperties == show) { return; } m_showInlineProperties = show; emit showInlinePropertiesChanged(); } void PrimaryConfigView::updateShowInlineProperties() { if (!m_latteView) { return; } bool showSecWindow{false}; bool advancedApprovedSecWindow{false}; if (m_inAdvancedMode && m_latteView->formFactor() != Plasma::Types::Vertical) { showSecWindow = true; advancedApprovedSecWindow = true; } //! consider screen geometry for showing or not the secondary window if (showSecWindow && !geometryWhenVisible().isNull()) { if (m_secConfigView && m_secConfigView->geometryWhenVisible().intersects(geometryWhenVisible())) { showSecWindow = false; } else if (advancedApprovedSecWindow) { showSecWindow = true; } } if (showSecWindow) { showSecondaryWindow(); // QTimer::singleShot(150, m_secConfigView, SLOT(show())); setShowInlineProperties(false); } else { hideSecondaryWindow(); setShowInlineProperties(true); } // qDebug() << " showSecWindow:" << showSecWindow << " _ " << " inline:"<< !showSecWindow; } void PrimaryConfigView::updateWaylandId() { Latte::WindowSystem::WindowId newId = m_corona->wm()->winIdFor("latte-dock", validTitle()); if (m_waylandWindowId != newId) { if (!m_waylandWindowId.isNull()) { m_corona->wm()->unregisterIgnoredWindow(m_waylandWindowId); } m_waylandWindowId = newId; m_corona->wm()->registerIgnoredWindow(m_waylandWindowId); } } bool PrimaryConfigView::inAdvancedMode() const { return m_inAdvancedMode; } void PrimaryConfigView::setInAdvancedMode(bool advanced) { if (m_inAdvancedMode == advanced) { return; } m_inAdvancedMode = advanced; emit inAdvancedModeChanged(); } void PrimaryConfigView::hideConfigWindow() { if (m_shellSurface) { //!NOTE: Avoid crash in wayland environment with qt5.9 close(); } else { hide(); } hideSecondaryWindow(); } void PrimaryConfigView::updateLaunchersForGroup(int groupInt) { Types::LaunchersGroup group = (Types::LaunchersGroup)groupInt; //! when the layout/global launchers list is empty then the current dock launchers are used for them //! as a start point if (m_corona && m_latteView->layout()) { if ((group == Types::LayoutLaunchers && m_latteView->layout()->launchers().isEmpty()) || (group == Types::GlobalLaunchers && m_corona->universalSettings()->launchers().isEmpty())) { Plasma::Containment *c = m_latteView->containment(); const auto &applets = c->applets(); for (auto *applet : applets) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("getLauncherList()"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); QVariant launchers; if (method.invoke(item, Q_RETURN_ARG(QVariant, launchers))) { if (group == Types::LayoutLaunchers) { m_latteView->layout()->setLaunchers(launchers.toStringList()); } else if (group == Types::GlobalLaunchers) { m_corona->universalSettings()->setLaunchers(launchers.toStringList()); } } } } } } } } } - } //!BEGIN borders Plasma::FrameSvg::EnabledBorders PrimaryConfigView::enabledBorders() const { return m_enabledBorders; } void PrimaryConfigView::updateEnabledBorders() { if (!this->screen()) { return; } Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; switch (m_latteView->location()) { case Plasma::Types::TopEdge: borders &= m_inReverse ? ~Plasma::FrameSvg::BottomBorder : ~Plasma::FrameSvg::TopBorder; break; case Plasma::Types::LeftEdge: borders &= ~Plasma::FrameSvg::LeftBorder; break; case Plasma::Types::RightEdge: borders &= ~Plasma::FrameSvg::RightBorder; break; case Plasma::Types::BottomEdge: borders &= m_inReverse ? ~Plasma::FrameSvg::TopBorder : ~Plasma::FrameSvg::BottomBorder; break; default: break; } if (m_enabledBorders != borders) { m_enabledBorders = borders; m_corona->dialogShadows()->addWindow(this, m_enabledBorders); emit enabledBordersChanged(); } } //!END borders void PrimaryConfigView::updateEffects() { //! Don't apply any effect before the wayland surface is created under wayland //! https://bugs.kde.org/show_bug.cgi?id=392890 if (KWindowSystem::isPlatformWayland() && !m_shellSurface) { return; } if (!m_background) { m_background = new Plasma::FrameSvg(this); } if (m_background->imagePath() != "dialogs/background") { m_background->setImagePath(QStringLiteral("dialogs/background")); } m_background->setEnabledBorders(m_enabledBorders); m_background->resizeFrame(size()); QRegion mask = m_background->mask(); QRegion fixedMask = mask.isNull() ? QRegion(QRect(0,0,width(),height())) : mask; if (!fixedMask.isEmpty()) { setMask(fixedMask); } else { setMask(QRegion()); } if (KWindowSystem::compositingActive()) { KWindowEffects::enableBlurBehind(winId(), true, fixedMask); } else { KWindowEffects::enableBlurBehind(winId(), false); } } //!BEGIN configuration void PrimaryConfigView::loadConfig() { if (!m_latteView || !m_latteView->containment()) { return; } auto config = m_latteView->containment()->config(); int complexity = config.readEntry("settingsComplexity", 0); setInAdvancedMode(complexity>0); } void PrimaryConfigView::saveConfig() { if (!m_latteView || !m_latteView->containment()) { return; } auto config = m_latteView->containment()->config(); int complexity = m_inAdvancedMode ? 1 : 0; config.writeEntry("settingsComplexity", complexity); } //!END configuration } } diff --git a/plasmoid/package/contents/ui/main.qml b/plasmoid/package/contents/ui/main.qml index 2e90032b..999e384d 100644 --- a/plasmoid/package/contents/ui/main.qml +++ b/plasmoid/package/contents/ui/main.qml @@ -1,2030 +1,2031 @@ /* * 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.8 import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.plasmoid 2.0 import org.kde.taskmanager 0.1 as TaskManager import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet import org.kde.activities 0.1 as Activities import org.kde.latte.core 0.2 as LatteCore import org.kde.latte.components 1.0 as LatteComponents import org.kde.latte.abilities.applets 0.1 as AppletAbility import org.kde.latte.private.tasks 0.1 as LatteTasks import "abilities" as Ability import "previews" as Previews import "task" as Task import "taskslayout" as TasksLayout import "../code/tools.js" as TaskTools import "../code/activitiesTools.js" as ActivitiesTools import "../code/ColorizerTools.js" as ColorizerTools Item { id:root Layout.fillWidth: scrollingEnabled && !root.vertical Layout.fillHeight: scrollingEnabled && root.vertical Layout.minimumWidth: -1 Layout.minimumHeight: -1 Layout.preferredWidth: tasksWidth Layout.preferredHeight: tasksHeight Layout.maximumWidth: -1 Layout.maximumHeight: -1 LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft && !root.vertical LayoutMirroring.childrenInherit: true property bool plasma515: latteView ? latteView.plasma515 : LatteCore.Environment.plasmaDesktopVersion >= LatteCore.Environment.makeVersion(5,15,0) property bool plasma518: latteView ? latteView.plasma518 : LatteCore.Environment.plasmaDesktopVersion >= LatteCore.Environment.makeVersion(5,18,0) property bool editMode: latteView ? latteView.editMode : plasmoid.userConfiguring property bool inConfigureAppletsMode: latteView ? latteView.inConfigureAppletsMode : true property bool disableRestoreZoom: false //blocks restore animation in rightClick property bool disableAllWindowsFunctionality: plasmoid.configuration.hideAllTasks property bool dropNewLauncher: false property bool inActivityChange: false property bool inDraggingPhase: false property bool initializationStep: false //true property bool isHovered: false property bool showBarLine: plasmoid.configuration.showBarLine property bool useThemePanel: plasmoid.configuration.useThemePanel property bool taskInAnimation: noTasksInAnimation > 0 ? true : false property bool transparentPanel: plasmoid.configuration.transparentPanel property bool vertical: plasmoid.formFactor === PlasmaCore.Types.Vertical ? true : false property int clearWidth property int clearHeight property int newDroppedPosition: -1 property int noInitCreatedBuffers: 0 property int noTasksInAnimation: 0 property int themePanelSize: plasmoid.configuration.panelSize property int location : { if (plasmoid.location === PlasmaCore.Types.LeftEdge || plasmoid.location === PlasmaCore.Types.RightEdge || plasmoid.location === PlasmaCore.Types.TopEdge) { return plasmoid.location; } return PlasmaCore.Types.BottomEdge; } property int tasksStarting: 0 ///Don't use Math.floor it adds one pixel in animations and creates glitches property int widthMargins: root.vertical ? metrics.totals.thicknessEdges : metrics.totals.lengthEdges property int heightMargins: !root.vertical ? metrics.totals.thicknessEdges : metrics.totals.lengthEdges property int internalWidthMargins: root.vertical ? metrics.totals.thicknessEdges : metrics.totals.lengthPaddings property int internalHeightMargins: !root.vertical ? metrics.totals.thicknessEdges : metrics.totals.lengthPaddings property real textColorBrightness: ColorizerTools.colorBrightness(themeTextColor) property color minimizedDotColor: { if (latteView) { return latteView.minimizedDotColor; } return textColorBrightness > 127.5 ? Qt.darker(themeTextColor, 1.7) : Qt.lighter(themeBackgroundColor, 7) } property color themeTextColor: theme.textColor property color themeBackgroundColor: theme.backgroundColor property color lightTextColor: textColorBrightness > 127.5 ? themeTextColor : themeBackgroundColor //a small badgers record (id,value) //in order to track badgers when there are changes //in launcher reference from libtaskmanager property variant badgers:[] property variant launchersOnActivities: [] //global plasmoid reference to the context menu property QtObject contextMenu: null property QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml"); property Item dragSource: null property Item tasksExtendedManager: _tasksExtendedManager readonly property alias animations: _animations readonly property alias indexer: _indexer readonly property alias launchers: _launchers readonly property alias metrics: _metrics readonly property alias parabolic: _parabolic readonly property alias shortcuts: _shortcuts readonly property alias containsDrag: mouseHandler.containsDrag readonly property bool dragAreaEnabled: latteView ? (root.dragSource !== null || latteView.dragInfo.isSeparator || latteView.dragInfo.isTask || !latteView.dragInfo.isPlasmoid) : true //! it is used to play the animation correct when the user removes a launcher property string launcherForRemoval: "" //BEGIN Latte Dock properties property bool badges3DStyle: latteView ? latteView.badges3DStyle : true property bool dockIsShownCompletely: latteView ? latteView.dockIsShownCompletely : true property bool enableShadows: latteView ? latteView.enableShadows > 0 : plasmoid.configuration.showShadows property bool forceHidePanel: false property bool disableLeftSpacer: false property bool disableRightSpacer: false property bool dockIsHidden: latteView ? latteView.dockIsHidden : false property bool groupTasksByDefault: plasmoid.configuration.groupTasksByDefault property bool highlightWindows: hoverAction === LatteTasks.Types.HighlightWindows || hoverAction === LatteTasks.Types.PreviewAndHighlightWindows property bool parabolicEffectEnabled: latteView ? latteView.parabolicEffectEnabled : parabolic.factor.zoom && !root.editMode property bool scrollingEnabled: plasmoid.configuration.scrollTasksEnabled property bool autoScrollTasksEnabled: scrollingEnabled && plasmoid.configuration.autoScrollTasksEnabled property bool manualScrollTasksEnabled: scrollingEnabled && manualScrollTasksType !== LatteTasks.Types.ManualScrollDisabled property int manualScrollTasksType: plasmoid.configuration.manualScrollTasksType property bool showInfoBadge: plasmoid.configuration.showInfoBadge property bool showProgressBadge: plasmoid.configuration.showInfoBadge property bool showAudioBadge: plasmoid.configuration.showAudioBadge property bool infoBadgeProminentColorEnabled: plasmoid.configuration.infoBadgeProminentColorEnabled property bool audioBadgeActionsEnabled: plasmoid.configuration.audioBadgeActionsEnabled property bool showOnlyCurrentScreen: plasmoid.configuration.showOnlyCurrentScreen property bool showOnlyCurrentDesktop: plasmoid.configuration.showOnlyCurrentDesktop property bool showOnlyCurrentActivity: plasmoid.configuration.showOnlyCurrentActivity property bool showPreviews: hoverAction === LatteTasks.Types.PreviewWindows || hoverAction === LatteTasks.Types.PreviewAndHighlightWindows property bool showWindowActions: plasmoid.configuration.showWindowActions && !disableAllWindowsFunctionality - property bool showWindowsOnlyFromLaunchers: plasmoid.configuration.showWindowsOnlyFromLaunchers || disableAllWindowsFunctionality + property bool showWindowsOnlyFromLaunchers: plasmoid.configuration.showWindowsOnlyFromLaunchers && !disableAllWindowsFunctionality property bool titleTooltips: latteView ? latteView.titleTooltips : false property alias windowPreviewIsShown: windowsPreviewDlg.visible property int launchersGroup: plasmoid.configuration.launchersGroup property int leftClickAction: plasmoid.configuration.leftClickAction property int middleClickAction: plasmoid.configuration.middleClickAction property int hoverAction: plasmoid.configuration.hoverAction property int modifier: plasmoid.configuration.modifier property int modifierClickAction: plasmoid.configuration.modifierClickAction property int modifierClick: plasmoid.configuration.modifierClick property int modifierQt:{ if (modifier === LatteTasks.Types.Shift) return Qt.ShiftModifier; else if (modifier === LatteTasks.Types.Ctrl) return Qt.ControlModifier; else if (modifier === LatteTasks.Types.Alt) return Qt.AltModifier; else if (modifier === LatteTasks.Types.Meta) return Qt.MetaModifier; else return -1; } property int taskScrollAction: plasmoid.configuration.taskScrollAction onTaskScrollActionChanged: { if (taskScrollAction > LatteTasks.Types.ScrollToggleMinimized) { //! migrating scroll action to LatteTasks.Types.ScrollAction plasmoid.configuration.taskScrollAction = plasmoid.configuration.taskScrollAction-LatteTasks.Types.ScrollToggleMinimized; } } //! Real properties are need in order for parabolic effect to be 1px precise perfect. //! This way moving from Tasks to Applets and vice versa is pretty stable when hovering with parabolic effect. property real tasksHeight: mouseHandler.height property real tasksWidth: mouseHandler.width //updated from Binding property int alignment readonly property real currentPanelOpacity: latteView ? latteView.currentPanelTransparency / 100 : 1 property int appShadowSize: latteView ? latteView.appShadowSize : Math.ceil(0.12*metrics.iconSize) property string appShadowColor: latteView ? latteView.appShadowColor : "#ff080808" property string appShadowColorSolid: latteView ? latteView.appShadowColorSolid : "#ff080808" property alias tasksCount: tasksModel.count readonly property rect screenGeometry: latteView ? latteView.screenGeometry : plasmoid.screenGeometry readonly property bool viewLayoutIsCurrent: latteView && viewLayout && latteView.layoutsManager && viewLayout.name === latteView.layoutsManager.currentLayoutName readonly property string viewLayoutName: viewLayout ? viewLayout.name : "" readonly property QtObject viewLayout : latteView && latteView.viewLayout ? latteView.viewLayout : null property Item latteView: null readonly property Item indicators: latteView ? latteView.indicatorsManager : indicatorsStandaloneLoader.item //END Latte Dock Panel properties //BEGIN Latte Dock Communicator property QtObject latteBridge: null readonly property bool enforceLattePalette: latteBridge && latteBridge.applyPalette && latteBridge.palette readonly property bool latteInEditMode: latteBridge && latteBridge.inEditMode //END Latte Dock Communicator Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground signal draggingFinished(); signal hiddenTasksUpdated(); signal launchersUpdatedFor(string launcher); signal presentWindows(variant winIds); signal requestLayout; signal signalPreviewsShown(); //signal signalDraggingState(bool value); signal showPreviewForTasks(QtObject group); //trigger updating scaling of neighbour delegates of zoomed delegate signal updateScale(int delegateIndex, real newScale, real step) signal mimicEnterForParabolic(); signal publishTasksGeometries(); signal windowsHovered(variant winIds, bool hovered) //onAnimationsChanged: console.log(animations); /* Rectangle{ anchors.fill: parent border.width: 1 border.color: "red" color: "white" } */ onLatteViewChanged: { if (latteView) { plasmoid.action("configure").visible = false; plasmoid.configuration.isInLatteDock = true; if (root.launchersGroup === LatteCore.Types.LayoutLaunchers || root.launchersGroup === LatteCore.Types.GlobalLaunchers) { tasksModel.updateLaunchersList(); } } else { plasmoid.configuration.isInLatteDock = false; } } onLaunchersGroupChanged:{ if(latteView) { tasksModel.updateLaunchersList(); } } Connections { target: plasmoid onLocationChanged: { iconGeometryTimer.start(); } } Connections { target: plasmoid.configuration // onLaunchersChanged: tasksModel.launcherList = plasmoid.configuration.launchers onGroupingAppIdBlacklistChanged: tasksModel.groupingAppIdBlacklist = plasmoid.configuration.groupingAppIdBlacklist; onGroupingLauncherUrlBlacklistChanged: tasksModel.groupingLauncherUrlBlacklist = plasmoid.configuration.groupingLauncherUrlBlacklist; } Connections{ target: latteView onDockIsHiddenChanged:{ if (latteView.dockIsHidden) { windowsPreviewDlg.hide("3.3"); } } } Connections{ target: latteView && latteView.layoutsManager ? latteView.layoutsManager : null onCurrentLayoutNameChanged: root.publishTasksGeometries(); } Binding { target: plasmoid property: "status" value: (tasksModel.anyTaskDemandsAttentionInValidTime || root.dragSource ? PlasmaCore.Types.NeedsAttentionStatus : PlasmaCore.Types.PassiveStatus); } ///// PlasmaCore.ColorScope{ id: colorScopePalette } //! Item { id: graphicsSystem readonly property bool isAccelerated: (GraphicsInfo.api !== GraphicsInfo.Software) && (GraphicsInfo.api !== GraphicsInfo.Unknown) } Loader { id: indicatorsStandaloneLoader active: !latteView && !plasmoid.configuration.isInLatteDock source: "indicators/Manager.qml" } Binding { target: root property: "alignment" value: { if (latteView) { if (latteView.panelAlignment === -1) { return; } if (latteView.panelAlignment === LatteCore.Types.Justify) { if (latteView.latteAppletPos>=0 && latteView.latteAppletPos<100) { return plasmoid.formFactor === PlasmaCore.Types.Horizontal ? LatteCore.Types.Left : LatteCore.Types.Top; } else if (latteView.latteAppletPos>=100 && latteView.latteAppletPos<200) { return LatteCore.Types.Center; } else if (latteView.latteAppletPos>=200) { return plasmoid.formFactor === PlasmaCore.Types.Horizontal ? LatteCore.Types.Right : LatteCore.Types.Bottom; } return LatteCore.Types.Center; } return latteView.panelAlignment; } return LatteCore.Types.Center; } } ///// function launchersDropped(urls){ mouseHandler.urlsDropped(urls); } ///UPDATE function launcherExists(url) { return (ActivitiesTools.getIndex(url, tasksModel.launcherList)>=0); } function taskExists(url) { var tasks = icList.contentItem.children; for(var i=0; i -1) { launch.push(explicitLauncher); } } } return launch; } function currentListViewLauncherList() { var launch = []; var tasks = icList.contentItem.children; for(var i=0; i