diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 7cdb4b2b..6f5e036e 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,86 +1,86 @@ set(lattedock-app_SRCS ../liblattedock/dock.cpp alternativeshelper.cpp commontools.cpp dockcorona.cpp globalshortcuts.cpp importer.cpp infoview.cpp launcherssignals.cpp layout.cpp layoutmanager.cpp plasmathemeextended.cpp schemecolors.cpp screenpool.cpp - dock/contextmenu.cpp - dock/dockconfigview.cpp - dock/docksecconfigview.cpp - dock/dockview.cpp - dock/effects.cpp - dock/panelshadows.cpp - dock/positioner.cpp - dock/screenedgeghostwindow.cpp - dock/visibilitymanager.cpp packageplugins/shell/dockpackage.cpp settings/settingsdialog.cpp settings/sortedactivitiesmodel.cpp settings/universalsettings.cpp settings/delegates/activitycmbboxdelegate.cpp settings/delegates/checkboxdelegate.cpp settings/delegates/colorcmbboxdelegate.cpp settings/delegates/colorcmbboxitemdelegate.cpp settings/delegates/layoutnamedelegate.cpp + view/contextmenu.cpp + view/dockconfigview.cpp + view/docksecconfigview.cpp + view/effects.cpp + view/panelshadows.cpp + view/positioner.cpp + view/screenedgeghostwindow.cpp + view/view.cpp + view/visibilitymanager.cpp wm/abstractwindowinterface.cpp wm/waylandinterface.cpp wm/windowinfowrap.cpp wm/xwindowinterface.cpp main.cpp ) set(latte_dbusXML dbus/org.kde.LatteDock.xml) qt5_add_dbus_adaptor(lattedock-app_SRCS ${latte_dbusXML} dockcorona.h Latte::DockCorona lattedockadaptor) ki18n_wrap_ui(lattedock-app_SRCS settings/settingsdialog.ui) add_executable(latte-dock ${lattedock-app_SRCS}) include(FakeTarget.cmake) target_link_libraries(latte-dock Qt5::DBus Qt5::Quick Qt5::Qml KF5::I18n KF5::DBusAddons KF5::Declarative KF5::CoreAddons KF5::GlobalAccel KF5::Archive KF5::Crash KF5::XmlGui KF5::Plasma KF5::PlasmaQuick KF5::Activities KF5::Notifications KF5::NewStuff KF5::QuickAddons KF5::WaylandClient ) if(HAVE_X11) target_link_libraries(latte-dock Qt5::X11Extras KF5::WindowSystem ${X11_LIBRARIES} ${XCB_LIBRARIES} ) endif() configure_file(org.kde.latte-dock.desktop.cmake org.kde.latte-dock.desktop) configure_file(org.kde.latte-dock.appdata.xml.cmake org.kde.latte-dock.appdata.xml) install(TARGETS latte-dock ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.latte-dock.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.latte-dock.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES dbus/org.kde.LatteDock.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) install(FILES lattedock.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) install(FILES latte-layouts.knsrc DESTINATION ${CONFIG_INSTALL_DIR}) diff --git a/app/dockcorona.cpp b/app/dockcorona.cpp index 5c219fba..0de78234 100644 --- a/app/dockcorona.cpp +++ b/app/dockcorona.cpp @@ -1,956 +1,956 @@ /* * 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 "dockcorona.h" // local #include "alternativeshelper.h" #include "globalshortcuts.h" #include "importer.h" #include "lattedockadaptor.h" #include "launcherssignals.h" #include "layoutmanager.h" #include "plasmathemeextended.h" #include "screenpool.h" -#include "dock/dockview.h" #include "packageplugins/shell/dockpackage.h" #include "settings/universalsettings.h" +#include "view/view.h" #include "wm/abstractwindowinterface.h" #include "wm/waylandinterface.h" #include "wm/xwindowinterface.h" // Qt #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 { DockCorona::DockCorona(bool defaultLayoutOnStartup, QString layoutNameOnStartUp, int userSetMemoryUsage, QObject *parent) : Plasma::Corona(parent), m_defaultLayoutOnStartup(defaultLayoutOnStartup), m_userSetMemoryUsage(userSetMemoryUsage), m_layoutNameOnStartUp(layoutNameOnStartUp), m_activityConsumer(new KActivities::Consumer(this)), m_screenPool(new ScreenPool(KSharedConfig::openConfig(), this)), m_globalShortcuts(new GlobalShortcuts(this)), m_universalSettings(new UniversalSettings(KSharedConfig::openConfig(), this)), m_themeExtended(new PlasmaThemeExtended(KSharedConfig::openConfig(), this)), m_layoutManager(new LayoutManager(this)) { //! create the window manager if (KWindowSystem::isPlatformWayland()) { m_wm = new WaylandInterface(this); } else { m_wm = new XWindowInterface(this); } setupWaylandIntegration(); KPackage::Package package(new DockPackage(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_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running)) { load(); } connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); m_docksScreenSyncTimer.setSingleShot(true); m_docksScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); connect(&m_docksScreenSyncTimer, &QTimer::timeout, this, &DockCorona::syncDockViewsToScreens); connect(m_universalSettings, &UniversalSettings::screenTrackerIntervalChanged, this, [this]() { m_docksScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); }); //! Dbus adaptor initialization new LatteDockAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Latte"), this); } DockCorona::~DockCorona() { //! BEGIN: Give the time to slide-out docks when closing m_layoutManager->hideAllDocks(); //! 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 docks when closing m_docksScreenSyncTimer.stop(); if (m_layoutManager->memoryUsage() == Dock::SingleLayout) { cleanConfig(); } qDebug() << "Latte Corona - unload: containments ..."; m_layoutManager->unload(); m_wm->deleteLater(); m_globalShortcuts->deleteLater(); m_layoutManager->deleteLater(); m_screenPool->deleteLater(); m_universalSettings->deleteLater(); m_themeExtended->deleteLater(); disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); delete m_activityConsumer; qDebug() << "Latte Corona - deleted..."; } void DockCorona::load() { if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) { disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &DockCorona::load); m_layoutManager->load(); m_activitiesStarting = false; connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &DockCorona::primaryOutputChanged, Qt::UniqueConnection); connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged, this, &DockCorona::screenCountChanged); connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &DockCorona::screenCountChanged); QString assignedLayout = m_layoutManager->shouldSwitchToLayout(m_activityConsumer->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_layoutManager->layoutExists(loadLayoutName)) { loadLayoutName = m_layoutManager->defaultLayoutName(); m_layoutManager->importDefaultLayout(false); } } else if (m_defaultLayoutOnStartup) { loadLayoutName = m_layoutManager->importer()->uniqueLayoutName(m_layoutManager->defaultLayoutName()); m_layoutManager->importDefaultLayout(true); } else { loadLayoutName = m_layoutNameOnStartUp; } if (m_userSetMemoryUsage != -1 && !KWindowSystem::isPlatformWayland()) { Dock::LayoutsMemoryUsage usage = static_cast(m_userSetMemoryUsage); m_universalSettings->setLayoutsMemoryUsage(usage); } if (KWindowSystem::isPlatformWayland()) { m_universalSettings->setLayoutsMemoryUsage(Dock::SingleLayout); } m_layoutManager->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, &DockCorona::addOutput, Qt::UniqueConnection); } } void DockCorona::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 DockCorona::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_waylandDockCorona = 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); WaylandInterface *wI = qobject_cast(m_wm); if (wI) { wI->initWindowManagement(pwm); } }); registry->setup(); connection->roundtrip(); } KWayland::Client::PlasmaShell *DockCorona::waylandDockCoronaInterface() const { return m_waylandDockCorona; } void DockCorona::cleanConfig() { auto containmentsEntries = config()->group("Containments"); bool changed = false; foreach (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"); foreach (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 DockCorona::containmentExists(uint id) const { foreach (auto containment, containments()) { if (id == containment->id()) { return true; } } return false; } bool DockCorona::appletExists(uint containmentId, uint appletId) const { Plasma::Containment *containment = nullptr; foreach (auto cont, containments()) { if (containmentId == cont->id()) { containment = cont; break; } } if (!containment) { return false; } foreach (auto applet, containment->applets()) { if (applet->id() == appletId) { return true; } } return false; } KActivities::Consumer *DockCorona::activitiesConsumer() const { return m_activityConsumer; } ScreenPool *DockCorona::screenPool() const { return m_screenPool; } UniversalSettings *DockCorona::universalSettings() const { return m_universalSettings; } LayoutManager *DockCorona::layoutManager() const { return m_layoutManager; } AbstractWindowInterface *DockCorona::wm() const { return m_wm; } PlasmaThemeExtended *DockCorona::themeExtended() const { return m_themeExtended; } int DockCorona::numScreens() const { return qGuiApp->screens().count(); } QRect DockCorona::screenGeometry(int id) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->knownIds().contains(id)) screenName = m_screenPool->connector(id); foreach (auto scr, screens) { if (scr->name() == screenName) { screen = scr; break; } } return screen->geometry(); } QRegion DockCorona::availableScreenRegion(int id) const { return availableScreenRegionWithCriteria(id); } QRegion DockCorona::availableScreenRegionWithCriteria(int id, QString forLayout) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->knownIds().contains(id)) screenName = m_screenPool->connector(id); foreach (auto scr, screens) { if (scr->name() == screenName) { screen = scr; break; } } if (!screen) return QRegion(); - QHash *views; + QHash *views; if (forLayout.isEmpty()) { views = m_layoutManager->currentDockViews(); } else { views = m_layoutManager->layoutDockViews(forLayout); } QRegion available(screen->geometry()); if (views) { for (const auto *view : *views) { if (view && view->containment() && view->screen() == screen && view->visibility() && (view->visibility()->mode() != Latte::Dock::AutoHide)) { int realThickness = view->normalThickness() - view->effects()->innerShadow(); // 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()) { available -= view->geometry(); } else { QRect realGeometry; int realWidth = view->maxLength() * view->width(); switch (view->alignment()) { case Latte::Dock::Left: realGeometry = QRect(view->x(), view->y(), realWidth, realThickness); break; case Latte::Dock::Center: case Latte::Dock::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), view->y(), realWidth, realThickness); break; case Latte::Dock::Right: realGeometry = QRect(view->geometry().right() - realWidth + 1, view->y(), realWidth, realThickness); break; } available -= realGeometry; } break; case Plasma::Types::BottomEdge: if (view->behaveAsPlasmaPanel()) { available -= view->geometry(); } else { QRect realGeometry; int realWidth = view->maxLength() * view->width(); int realY = view->geometry().bottom() - realThickness + 1; switch (view->alignment()) { case Latte::Dock::Left: realGeometry = QRect(view->x(), realY, realWidth, realThickness); break; case Latte::Dock::Center: case Latte::Dock::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), realY, realWidth, realThickness); break; case Latte::Dock::Right: realGeometry = QRect(view->geometry().right() - realWidth + 1, realY, realWidth, realThickness); break; } available -= realGeometry; } 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 DockCorona::availableScreenRect(int id) const { return availableScreenRectWithCriteria(id); } QRect DockCorona::availableScreenRectWithCriteria(int id, QList modes, QList edges) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; if (m_screenPool->knownIds().contains(id)) { QString scrName = m_screenPool->connector(id); foreach (auto scr, screens) { if (scr->name() == scrName) { screen = scr; break; } } } if (!screen) return {}; bool allModes = modes.isEmpty(); bool allEdges = edges.isEmpty(); auto available = screen->geometry(); - QHash *views = m_layoutManager->currentDockViews(); + QHash *views = m_layoutManager->currentDockViews(); if (views) { for (const auto *view : *views) { if (view && view->containment() && view->screen() == screen && ((allEdges || edges.contains(view->location())) && (allModes || (view->visibility() && modes.contains(view->visibility()->mode()))))) { auto dockRect = view->absGeometry(); // 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: available.setTop(dockRect.bottom() + 1); break; case Plasma::Types::BottomEdge: available.setBottom(dockRect.top() - 1); break; case Plasma::Types::LeftEdge: available.setLeft(dockRect.right() + 1); break; case Plasma::Types::RightEdge: available.setRight(dockRect.left() - 1); break; default: //! bypass clang warnings break; } } } } return available; } void DockCorona::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())); } void DockCorona::primaryOutputChanged() { m_docksScreenSyncTimer.start(); } void DockCorona::screenRemoved(QScreen *screen) { Q_ASSERT(screen); } void DockCorona::screenCountChanged() { m_docksScreenSyncTimer.start(); } //! the central functions that updates loading/unloading dockviews //! concerning screen changed (for multi-screen setups mainly) void DockCorona::syncDockViewsToScreens() { m_layoutManager->syncDockViewsToScreens(); } int DockCorona::primaryScreenId() const { return m_screenPool->id(qGuiApp->primaryScreen()->name()); } void DockCorona::closeApplication() { //! this code must be called asynchronously because it is called //! also from qml (Settings window). QTimer::singleShot(5, [this]() { m_layoutManager->hideLatteSettingsDialog(); m_layoutManager->hideAllDocks(); }); //! give the time for the docks to hide themselves QTimer::singleShot(500, [this]() { qGuiApp->quit(); }); } void DockCorona::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, true); aboutDialog->show(); } int DockCorona::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 dockView 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; } } - QHash *views = m_layoutManager->currentDockViews(); + QHash *views = m_layoutManager->currentDockViews(); //if the panel views already exist, base upon them - DockView *view = views ? views->value(containment) : nullptr; + Latte::View *view = views ? views->value(containment) : nullptr; 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_activityConsumer->currentActivity() || containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment)) { return containment->lastScreen(); } } return -1; } void DockCorona::showAlternativesForApplet(Plasma::Applet *applet) { const QString alternativesQML = kPackage().filePath("appletalternativesui"); if (alternativesQML.isEmpty()) { return; } - QHash *views = m_layoutManager->currentDockViews(); + QHash *views = m_layoutManager->currentDockViews(); - DockView *dockView = (*views)[applet->containment()]; + Latte::View *dockView = (*views)[applet->containment()]; KDeclarative::QmlObject *qmlObj{nullptr}; if (dockView) { dockView->setAlternativesIsShown(true); qmlObj = new KDeclarative::QmlObject(dockView); } else { qmlObj = new KDeclarative::QmlObject(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, [dockView]() { dockView->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::QmlObject *obj = it.next(); if (obj == qmlObj) { it.remove(); obj->deleteLater(); } } }); } void DockCorona::alternativesVisibilityChanged(bool visible) { if (visible) { return; } QObject *root = sender(); QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObject *obj = it.next(); if (obj->rootObject() == root) { it.remove(); obj->deleteLater(); } } } void DockCorona::loadDefaultLayout() { qDebug() << "loading default layout"; //! Settting 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 *currentLayout = m_layoutManager->activeLayout(m_layoutManager->currentLayoutName()); if (currentLayout) { edges = currentLayout->freeEdges(defaultContainment->screen()); } if ((edges.count() > 0)) { defaultContainment->setLocation(edges.at(0)); } else { defaultContainment->setLocation(Plasma::Types::BottomEdge); } if (m_layoutManager->memoryUsage() == Dock::MultipleLayouts) { config.writeEntry("layoutId", m_layoutManager->currentLayoutName()); } defaultContainment->updateConstraints(Plasma::Types::StartupCompletedConstraint); defaultContainment->save(config); requestConfigSync(); defaultContainment->flushPendingConstraintsEvents(); emit containmentAdded(defaultContainment); emit containmentCreated(defaultContainment); //m_layoutManager->addDock(defaultContainment); defaultContainment->createApplet(QStringLiteral("org.kde.latte.plasmoid")); defaultContainment->createApplet(QStringLiteral("org.kde.plasma.analogclock")); } QStringList DockCorona::containmentsIds() { QStringList ids; foreach (auto containment, containments()) { ids << QString::number(containment->id()); } return ids; } QStringList DockCorona::appletsIds() { QStringList ids; foreach (auto containment, containments()) { auto applets = containment->config().group("Applets"); ids << applets.groupList(); } return ids; } //! Activate launcher menu through dbus interface void DockCorona::activateLauncherMenu() { m_globalShortcuts->activateLauncherMenu(); } void DockCorona::windowColorScheme(QString windowIdAndScheme) { int firstSlash = windowIdAndScheme.indexOf("-"); QString windowIdStr = windowIdAndScheme.mid(0, firstSlash); QString schemeStr = windowIdAndScheme.mid(firstSlash + 1); m_wm->setColorSchemeForWindow(windowIdStr, schemeStr); } //! update badge for specific dock item void DockCorona::updateDockItemBadge(QString identifier, QString value) { m_globalShortcuts->updateDockItemBadge(identifier, value); } void DockCorona::switchToLayout(QString layout) { m_layoutManager->switchToLayout(layout); } void DockCorona::showSettingsWindow(int page) { Dock::LatteConfigPage p = Dock::LayoutPage; if (page >= Dock::LayoutPage && page <= Dock::PreferencesPage) { p = static_cast(page); } m_layoutManager->showLatteSettingsDialog(p); } QStringList DockCorona::contextMenuData() { QStringList data; data << QString::number((int)m_layoutManager->memoryUsage()); data << m_layoutManager->currentLayoutName(); foreach (auto layoutName, m_layoutManager->menuLayouts()) { if (m_layoutManager->activeLayout(layoutName)) { data << QString("1," + layoutName); } else { data << QString("0," + layoutName); } } return data; } inline void DockCorona::qmlRegisterTypes() const { qmlRegisterType(); } } diff --git a/app/globalshortcuts.cpp b/app/globalshortcuts.cpp index c47382a1..ef592cf0 100644 --- a/app/globalshortcuts.cpp +++ b/app/globalshortcuts.cpp @@ -1,921 +1,921 @@ /* * 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 "globalshortcuts.h" // local #include "dockcorona.h" #include "layoutmanager.h" -#include "dock/dockview.h" #include "settings/universalsettings.h" +#include "view/view.h" // C++ #include // Qt #include #include #include #include #include // KDE #include #include #include #include // Plasma #include #include // X11 #include #include #include //this code is used by activityswitcher in plasma in order to check if the //user has release all the modifier keys from the globalshortcut namespace { bool isPlatformX11() { static const bool isX11 = QX11Info::isPlatformX11(); return isX11; } // Taken from kwin/tabbox/tabbox.cpp Display *x11_display() { static Display *s_display = nullptr; if (!s_display) { s_display = QX11Info::display(); } return s_display; } bool x11_areKeySymXsDepressed(bool bAll, const uint keySyms[], int nKeySyms) { char keymap[32]; XQueryKeymap(x11_display(), keymap); for (int iKeySym = 0; iKeySym < nKeySyms; iKeySym++) { uint keySymX = keySyms[ iKeySym ]; uchar keyCodeX = XKeysymToKeycode(x11_display(), keySymX); int i = keyCodeX / 8; char mask = 1 << (keyCodeX - (i * 8)); // Abort if bad index value, if (i < 0 || i >= 32) return false; // If ALL keys passed need to be depressed, if (bAll) { if ((keymap[i] & mask) == 0) return false; } else { // If we are looking for ANY key press, and this key is depressed, if (keymap[i] & mask) return true; } } // If we were looking for ANY key press, then none was found, return false, // If we were looking for ALL key presses, then all were found, return true. return bAll; } bool x11_areModKeysDepressed(const QKeySequence &seq) { uint rgKeySyms[10]; int nKeySyms = 0; if (seq.isEmpty()) { return false; } int mod = seq[seq.count() - 1] & Qt::KeyboardModifierMask; if (mod & Qt::SHIFT) { rgKeySyms[nKeySyms++] = XK_Shift_L; rgKeySyms[nKeySyms++] = XK_Shift_R; } if (mod & Qt::CTRL) { rgKeySyms[nKeySyms++] = XK_Control_L; rgKeySyms[nKeySyms++] = XK_Control_R; } if (mod & Qt::ALT) { rgKeySyms[nKeySyms++] = XK_Alt_L; rgKeySyms[nKeySyms++] = XK_Alt_R; } if (mod & Qt::META) { // It would take some code to determine whether the Win key // is associated with Super or Meta, so check for both. // See bug #140023 for details. rgKeySyms[nKeySyms++] = XK_Super_L; rgKeySyms[nKeySyms++] = XK_Super_R; rgKeySyms[nKeySyms++] = XK_Meta_L; rgKeySyms[nKeySyms++] = XK_Meta_R; } return x11_areKeySymXsDepressed(false, rgKeySyms, nKeySyms); } } namespace Latte { const int APPLETEXECUTIONDELAY = 400; GlobalShortcuts::GlobalShortcuts(QObject *parent) : QObject(parent) { m_corona = qobject_cast(parent); if (m_corona) { init(); } m_hideDocksTimer.setSingleShot(true); if (isPlatformX11()) { //in X11 the timer is a poller that checks to see if the modifier keys //from user global shortcut have been released m_hideDocksTimer.setInterval(300); } else { //on wayland in acting just as simple timer that hides the dock afterwards m_hideDocksTimer.setInterval(2500); } connect(&m_hideDocksTimer, &QTimer::timeout, this, &GlobalShortcuts::hideDocksTimerSlot); } GlobalShortcuts::~GlobalShortcuts() { } void GlobalShortcuts::init() { KActionCollection *generalActions = new KActionCollection(m_corona); //show-hide the main dock in the primary screen QAction *showAction = generalActions->addAction(QStringLiteral("show latte dock")); showAction->setText(i18n("Show Dock")); showAction->setShortcut(QKeySequence(Qt::META + '`')); KGlobalAccel::setGlobalShortcut(showAction, QKeySequence(Qt::META + '`')); connect(showAction, &QAction::triggered, this, [this]() { showDocks(); }); //show-cycle between Latte settings windows QAction *settingsAction = generalActions->addAction(QStringLiteral("show dock settings")); settingsAction->setText(i18n("Show Dock Settings")); KGlobalAccel::setGlobalShortcut(settingsAction, QKeySequence(Qt::META + Qt::Key_A)); connect(settingsAction, &QAction::triggered, this, [this] { showSettings(); }); //show the layouts editor QAction *layoutsAction = generalActions->addAction(QStringLiteral("show layout settings")); layoutsAction->setText(i18n("Show Layout Settings")); layoutsAction->setShortcut(QKeySequence(Qt::META + Qt::Key_W)); KGlobalAccel::setGlobalShortcut(layoutsAction, QKeySequence(Qt::META + Qt::Key_W)); connect(layoutsAction, &QAction::triggered, this, [this]() { m_corona->layoutManager()->showLatteSettingsDialog(Dock::LayoutPage); }); //show the latter universal settings QAction *universalSettingsAction = generalActions->addAction(QStringLiteral("show latte universal settings")); universalSettingsAction->setText(i18n("Show Latte Settings")); universalSettingsAction->setShortcut(QKeySequence(Qt::META + Qt::Key_E)); KGlobalAccel::setGlobalShortcut(universalSettingsAction, QKeySequence(Qt::META + Qt::Key_E)); connect(universalSettingsAction, &QAction::triggered, this, [this]() { m_corona->layoutManager()->showLatteSettingsDialog(Dock::PreferencesPage); }); KActionCollection *taskbarActions = new KActionCollection(m_corona); //activate actions [1-9] for (int i = 1; i < 10; ++i) { const int entryNumber = i; const Qt::Key key = static_cast(Qt::Key_0 + i); QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(entryNumber))); action->setText(i18n("Activate Entry %1", entryNumber)); action->setShortcut(QKeySequence(Qt::META + key)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + key)); connect(action, &QAction::triggered, this, [this, i] { // qDebug() << "meta action..."; activateEntry(i, static_cast(Qt::META)); }); } //! Array that is used to register correctly actions for task index>=10 and <19 std::array keysAboveTen{ Qt::Key_0, Qt::Key_Z, Qt::Key_X, Qt::Key_C, Qt::Key_V, Qt::Key_B, Qt::Key_N, Qt::Key_M, Qt::Key_Comma, Qt::Key_Period }; //activate actions [10-19] for (int i = 10; i < 20; ++i) { QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(i))); action->setText(i18n("Activate Entry %1", i)); action->setShortcut(QKeySequence(Qt::META + keysAboveTen[i - 10])); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + keysAboveTen[i - 10])); connect(action, &QAction::triggered, this, [this, i] { activateEntry(i, static_cast(Qt::META)); }); } //new instance actions [1-9] for (int i = 1; i < 10; ++i) { const int entryNumber = i; const Qt::Key key = static_cast(Qt::Key_0 + i); QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(entryNumber))); action->setText(i18n("New Instance for Entry %1", entryNumber)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + key)); connect(action, &QAction::triggered, this, [this, i] { // qDebug() << "meta + ctrl + action..."; activateEntry(i, static_cast(Qt::CTRL)); }); } //new instance actions [10-19] for (int i = 10; i < 20; ++i) { QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(i))); action->setText(i18n("New Instance for Entry %1", i)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + keysAboveTen[i - 10])); connect(action, &QAction::triggered, this, [this, i] { activateEntry(i, static_cast(Qt::CTRL)); }); } m_singleMetaAction = new QAction(this); m_singleMetaAction->setShortcut(QKeySequence(Qt::META)); } //! Activate launcher menu through dbus interface void GlobalShortcuts::activateLauncherMenu() { - QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); + QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); foreach (auto view, sortedViews) { const auto applets = view->containment()->applets(); for (auto applet : applets) { const auto provides = applet->kPackage().metadata().value(QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) { if (view->visibility()->isHidden()) { m_lastInvokedAction = m_singleMetaAction; m_hideDocks.clear(); m_hideDocks.append(view); view->visibility()->setBlockHiding(true); m_hideDocksTimer.start(); //! delay the execution in order to show first the dock QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view, applet]() { view->toggleAppletExpanded(applet->id()); }); } else { view->toggleAppletExpanded(applet->id()); } return; } } } } bool GlobalShortcuts::activatePlasmaTaskManagerEntryAtContainment(const Plasma::Containment *c, int index, Qt::Key modifier) { const auto &applets = c->applets(); for (auto *applet : applets) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value()) { const auto &childItems = appletInterface->childItems(); if (childItems.isEmpty()) { continue; } KPluginMetaData meta = applet->kPackage().metadata(); for (QQuickItem *item : childItems) { if (auto *metaObject = item->metaObject()) { // 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 = modifier == static_cast(Qt::META) ? metaObject->indexOfMethod("activateTaskAtIndex(QVariant)") : metaObject->indexOfMethod("newInstanceForTaskAtIndex(QVariant)"); int methodIndex2 = metaObject->indexOfMethod("setShowTasksNumbers(QVariant)"); if (methodIndex == -1 || (methodIndex2 == -1 && meta.pluginId() == "org.kde.latte.plasmoid")) { continue; } int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex)); } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, index - 1))) { if (methodIndex2 != -1) { m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true)); } return true; } } } } } } return false; } -bool GlobalShortcuts::activateLatteEntryAtContainment(const DockView *view, int index, Qt::Key modifier) +bool GlobalShortcuts::activateLatteEntryAtContainment(const Latte::View *view, int index, Qt::Key modifier) { if (QQuickItem *containmentInterface = view->containment()->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); 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 = modifier == static_cast(Qt::META) ? metaObject->indexOfMethod("activateEntryAtIndex(QVariant)") : metaObject->indexOfMethod("newInstanceForEntryAtIndex(QVariant)"); int methodIndex2 = metaObject->indexOfMethod("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1 || (methodIndex2 == -1)) { continue; } int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? applicationLauncherId(view->containment()) : -1; int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex2)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex2)); } QMetaMethod method = metaObject->method(methodIndex); if (view->visibility()->isHidden()) { //! delay the execution in order to show first the dock if (m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { QTimer::singleShot(APPLETEXECUTIONDELAY, [this, item, method, index]() { method.invoke(item, Q_ARG(QVariant, index)); }); } return true; } else { if (method.invoke(item, Q_ARG(QVariant, index))) { m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher)); return true; } } } } } return false; } //! Activate task manager entry void GlobalShortcuts::activateEntry(int index, Qt::Key modifier) { m_lastInvokedAction = dynamic_cast(sender()); - QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); + QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); foreach (auto view, sortedViews) { if ((!view->latteTasksPresent() && view->tasksPresent() && activatePlasmaTaskManagerEntryAtContainment(view->containment(), index, modifier)) || (activateLatteEntryAtContainment(view, index, modifier))) { if (!m_hideDocks.contains(view)) { m_hideDocks.append(view); } view->visibility()->setBlockHiding(true); m_hideDocksTimer.start(); return; } } } //! update badge for specific dock item void GlobalShortcuts::updateDockItemBadge(QString identifier, QString value) { //qDebug() << "DBUS CALL ::: " << identifier << " - " << value; auto updateBadgeForTaskInContainment = [this](const Plasma::Containment * c, QString identifier, QString value) { 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("updateBadge(QVariant,QVariant)"); if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, identifier), Q_ARG(QVariant, value))) { return true; } } } } } } return false; }; - QHash *views = m_corona->layoutManager()->currentDockViews(); + QHash *views = m_corona->layoutManager()->currentDockViews(); // update badges in all Latte Tasks plasmoids for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { updateBadgeForTaskInContainment(it.key(), identifier, value); } } -bool GlobalShortcuts::isCapableToShowAppletsNumbers(DockView *view) +bool GlobalShortcuts::isCapableToShowAppletsNumbers(Latte::View *view) { if (!view->latteTasksPresent() && view->tasksPresent()) { return false; } const Plasma::Containment *c = view->containment(); if (QQuickItem *containmentInterface = c->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); 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("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } return true; } } } return false; } int GlobalShortcuts::applicationLauncherId(const Plasma::Containment *c) { const auto applets = c->applets(); for (auto applet : applets) { const auto provides = applet->kPackage().metadata().value(QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) { return applet->id(); } } return -1; } void GlobalShortcuts::showDocks() { m_lastInvokedAction = dynamic_cast(sender()); auto invokeShowNumbers = [this](const Plasma::Containment * c) { if (QQuickItem *containmentInterface = c->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); 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("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? applicationLauncherId(c) : -1; int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex)); } if (m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, true), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { return true; } } } } return false; }; auto invokeShowOnlyMeta = [this](const Plasma::Containment * c) { if (QQuickItem *containmentInterface = c->property("_plasma_graphicObject").value()) { const auto &childItems = containmentInterface->childItems(); 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("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? applicationLauncherId(c) : -1; int showMethodIndex = -1; if (!m_calledItems.contains(item)) { m_calledItems.append(item); m_methodsShowNumbers.append(metaObject->method(methodIndex)); showMethodIndex = m_methodsShowNumbers.count() - 1; } else { showMethodIndex = m_methodsShowNumbers.indexOf(metaObject->method(methodIndex)); } if (m_methodsShowNumbers[showMethodIndex].invoke(item, Q_ARG(QVariant, false), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { return true; } } } } return false; }; - QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); + QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); - DockView *viewWithTasks{nullptr}; - DockView *viewWithMeta{nullptr}; + Latte::View *viewWithTasks{nullptr}; + Latte::View *viewWithMeta{nullptr}; foreach (auto view, sortedViews) { if (!viewWithTasks && isCapableToShowAppletsNumbers(view)) { viewWithTasks = view; } if (!viewWithMeta && m_corona->universalSettings()->metaForwardedToLatte() && applicationLauncherId(view->containment()) > -1) { viewWithMeta = view; } } bool dockFound{false}; if (!m_hideDocksTimer.isActive()) { m_hideDocks.clear(); } if (viewWithTasks || viewWithMeta) { m_calledItems.clear(); m_methodsShowNumbers.clear(); } if (viewWithTasks && invokeShowNumbers(viewWithTasks->containment())) { dockFound = true; if (!m_hideDocksTimer.isActive()) { m_hideDocks.append(viewWithTasks); viewWithTasks->visibility()->setBlockHiding(true); } } if (viewWithMeta && viewWithMeta != viewWithTasks && invokeShowOnlyMeta(viewWithMeta->containment())) { dockFound = true; if (!m_hideDocksTimer.isActive()) { m_hideDocks.append(viewWithMeta); viewWithMeta->visibility()->setBlockHiding(true); } } if (dockFound) { if (!m_hideDocksTimer.isActive()) { m_hideDocksTimer.start(); } else { m_hideDocksTimer.stop(); hideDocksTimerSlot(); } } } bool GlobalShortcuts::docksToHideAreValid() { foreach (auto view, m_hideDocks) { if (!m_corona->layoutManager()->dockViewExists(view)) { return false; } } return true; } -bool GlobalShortcuts::dockAtLowerScreenPriority(DockView *test, DockView *base) +bool GlobalShortcuts::dockAtLowerScreenPriority(Latte::View *test, Latte::View *base) { if (!base || ! test) { return true; } if (base->screen() == test->screen()) { return false; } else if (base->screen() != qGuiApp->primaryScreen() && test->screen() == qGuiApp->primaryScreen()) { return false; } else if (base->screen() == qGuiApp->primaryScreen() && test->screen() != qGuiApp->primaryScreen()) { return true; } else { int basePriority = -1; int testPriority = -1; for (int i = 0; i < qGuiApp->screens().count(); ++i) { if (base->screen() == qGuiApp->screens()[i]) { basePriority = i; } if (test->screen() == qGuiApp->screens()[i]) { testPriority = i; } } if (testPriority <= basePriority) { return true; } else { return false; } } qDebug() << "dockAtLowerScreenPriority : shouldn't had reached here..."; return false; } -bool GlobalShortcuts::dockAtLowerEdgePriority(DockView *test, DockView *base) +bool GlobalShortcuts::dockAtLowerEdgePriority(Latte::View *test, Latte::View *base) { if (!base || ! test) { return true; } QList edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge, Plasma::Types::LeftEdge, Plasma::Types::BottomEdge}; int testPriority = -1; int basePriority = -1; for (int i = 0; i < edges.count(); ++i) { if (edges[i] == base->location()) { basePriority = i; } if (edges[i] == test->location()) { testPriority = i; } } if (testPriority < basePriority) return true; else return false; } -QList GlobalShortcuts::sortedViewsList(QHash *views) +QList GlobalShortcuts::sortedViewsList(QHash *views) { - QList docks; + QList docks; //QHash *views = m_corona->layoutManager()->currentDockViews(); //! create a docks list to sorted out for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { docks.append(it.value()); } qDebug() << " -------- "; for (int i = 0; i < docks.count(); ++i) { qDebug() << i << ". " << docks[i]->screen()->name() << " - " << docks[i]->location(); } //! sort the docks based on screens and edges priorities //! docks on primary screen have higher priority and //! for docks in the same screen the priority goes to //! Bottom,Left,Top,Right for (int i = 0; i < docks.size(); ++i) { for (int j = 0; j < docks.size() - i - 1; ++j) { if (dockAtLowerScreenPriority(docks[j], docks[j + 1]) || (docks[j]->screen() == docks[j + 1]->screen() && dockAtLowerEdgePriority(docks[j], docks[j + 1]))) { - DockView *temp = docks[j + 1]; + Latte::View *temp = docks[j + 1]; docks[j + 1] = docks[j]; docks[j] = temp; } } } - DockView *highestPriorityView{nullptr}; + Latte::View *highestPriorityView{nullptr}; for (int i = 0; i < docks.size(); ++i) { if (docks[i]->isPreferredForShortcuts()) { highestPriorityView = docks[i]; docks.removeAt(i); break; } } if (highestPriorityView) { docks.prepend(highestPriorityView); } qDebug() << " -------- sorted -----"; for (int i = 0; i < docks.count(); ++i) { qDebug() << i << ". " << docks[i]->isPreferredForShortcuts() << " - " << docks[i]->screen()->name() << " - " << docks[i]->location(); } return docks; } void GlobalShortcuts::showSettings() { - QList docks = sortedViewsList(m_corona->layoutManager()->currentDockViews()); + QList docks = sortedViewsList(m_corona->layoutManager()->currentDockViews()); //! find which is the next dock to show its settings if (docks.count() > 0) { int openSettings = -1; //! check if there is a dock with opened settings window for (int i = 0; i < docks.size(); ++i) { if (docks[i]->settingsWindowIsShown()) { openSettings = i; break; } } if (openSettings >= 0 && docks.count() > 1) { openSettings = openSettings + 1; if (openSettings >= docks.size()) { openSettings = 0; } docks[openSettings]->showSettingsWindow(); } else { docks[0]->showSettingsWindow(); } } } void GlobalShortcuts::hideDocksTimerSlot() { if (!m_lastInvokedAction || m_hideDocks.count() == 0) { return; } // qDebug() << "MEMORY ::: " << m_hideDocks.count() << " _ " << m_calledItems.count() << " _ " << m_methodsShowNumbers.count(); if (isPlatformX11()) { if (!x11_areModKeysDepressed(m_lastInvokedAction->shortcut())) { m_lastInvokedAction = Q_NULLPTR; if (docksToHideAreValid()) { foreach (auto dockView, m_hideDocks) { dockView->visibility()->setBlockHiding(false); } if (m_calledItems.count() > 0) { for (int i = 0; i < m_calledItems.count(); ++i) { m_methodsShowNumbers[i].invoke(m_calledItems[i], Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, -1)); } } } m_hideDocks.clear(); m_calledItems.clear(); m_methodsShowNumbers.clear(); return; } else { m_hideDocksTimer.start(); } } else { // TODO: This is needs to be fixed in wayland m_lastInvokedAction = Q_NULLPTR; if (docksToHideAreValid()) { foreach (auto dockView, m_hideDocks) { dockView->visibility()->setBlockHiding(false); } if (m_calledItems.count() > 0) { for (int i = 0; i < m_calledItems.count(); ++i) { m_methodsShowNumbers[i].invoke(m_calledItems[i], Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, -1)); } } } m_hideDocks.clear(); m_calledItems.clear(); m_methodsShowNumbers.clear(); } } } #include "moc_globalshortcuts.cpp" diff --git a/app/globalshortcuts.h b/app/globalshortcuts.h index 439daf64..525d6a5b 100644 --- a/app/globalshortcuts.h +++ b/app/globalshortcuts.h @@ -1,89 +1,89 @@ /* * 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 GLOBALSHORTCUTS_H #define GLOBALSHORTCUTS_H // local #include "../liblattedock/dock.h" // Qt #include #include #include namespace Plasma { class Containment; } namespace Latte { class DockCorona; -class DockView; +class View; } namespace Latte { class GlobalShortcuts : public QObject { Q_OBJECT public: GlobalShortcuts(QObject *parent = nullptr); ~GlobalShortcuts() override; void activateLauncherMenu(); void updateDockItemBadge(QString identifier, QString value); private slots: void hideDocksTimerSlot(); private: void init(); void activateEntry(int index, Qt::Key modifier); void showDocks(); void showSettings(); - bool activateLatteEntryAtContainment(const DockView *view, int index, Qt::Key modifier); + bool activateLatteEntryAtContainment(const Latte::View *view, int index, Qt::Key modifier); bool activatePlasmaTaskManagerEntryAtContainment(const Plasma::Containment *c, int index, Qt::Key modifier); - bool dockAtLowerEdgePriority(DockView *test, DockView *base); - bool dockAtLowerScreenPriority(DockView *test, DockView *base); + bool dockAtLowerEdgePriority(Latte::View *test, Latte::View *base); + bool dockAtLowerScreenPriority(Latte::View *test, Latte::View *base); bool docksToHideAreValid(); - bool isCapableToShowAppletsNumbers(DockView *view); + bool isCapableToShowAppletsNumbers(Latte::View *view); int applicationLauncherId(const Plasma::Containment *c); - QList sortedViewsList(QHash *views); + QList sortedViewsList(QHash *views); QAction *m_lastInvokedAction; //!it is used when the dock is hidden in order to delay the app launcher showing QAction *m_singleMetaAction; QTimer m_hideDocksTimer; - QList m_hideDocks; + QList m_hideDocks; QList m_calledItems; QList m_methodsShowNumbers; DockCorona *m_corona{nullptr}; }; } #endif // GLOBALSHORTCUTS_H diff --git a/app/infoview.cpp b/app/infoview.cpp index e6ab59e8..7542fc4d 100644 --- a/app/infoview.cpp +++ b/app/infoview.cpp @@ -1,201 +1,201 @@ /* * 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 "infoview.h" // local -#include "dock/panelshadows_p.h" #include "wm/abstractwindowinterface.h" +#include "view/panelshadows_p.h" // Qt #include #include #include #include // KDE #include #include #include #include #include // Plasma #include namespace Latte { InfoView::InfoView(DockCorona *corona, QString message, QScreen *screen, QWindow *parent) : QQuickView(parent), m_corona(corona), m_message(message), m_screen(screen) { setupWaylandIntegration(); setResizeMode(QQuickView::SizeViewToRootObject); setColor(QColor(Qt::transparent)); setDefaultAlphaBuffer(true); setIcon(qGuiApp->windowIcon()); setScreen(screen); setFlags(wFlags()); init(); } InfoView::~InfoView() { PanelShadows::self()->removeWindow(this); qDebug() << "InfoView deleting ..."; if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; } } void InfoView::init() { rootContext()->setContextProperty(QStringLiteral("infoWindow"), this); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); kdeclarative.setupBindings(); auto source = QUrl::fromLocalFile(m_corona->kPackage().filePath("infoviewui")); setSource(source); rootObject()->setProperty("message", m_message); syncGeometry(); } Plasma::FrameSvg::EnabledBorders InfoView::enabledBorders() const { return m_borders; } inline Qt::WindowFlags InfoView::wFlags() const { return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; } void InfoView::syncGeometry() { const QSize size(rootObject()->width(), rootObject()->height()); const auto sGeometry = screen()->geometry(); setMaximumSize(size); setMinimumSize(size); resize(size); QPoint position{sGeometry.center().x() - size.width() / 2, sGeometry.center().y() - size.height() / 2 }; setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } } void InfoView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); m_corona->wm()->setDockExtraFlags(*this); setFlags(wFlags()); m_corona->wm()->enableBlurBehind(*this); syncGeometry(); QTimer::singleShot(400, this, &InfoView::syncGeometry); PanelShadows::self()->addWindow(this); PanelShadows::self()->setEnabledBorders(this, m_borders); } void InfoView::setupWaylandIntegration() { if (m_shellSurface) { // already setup return; } if (m_corona) { using namespace KWayland::Client; PlasmaShell *interface = m_corona->waylandDockCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland dock window surface was created..."; m_shellSurface = interface->createSurface(s, this); } } bool InfoView::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; } PanelShadows::self()->removeWindow(this); break; } } } return QQuickWindow::event(e); } void InfoView::setOnActivities(QStringList activities) { KWindowSystem::setOnActivities(winId(), activities); } } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/layout.cpp b/app/layout.cpp index bbc831b0..194d97dd 100644 --- a/app/layout.cpp +++ b/app/layout.cpp @@ -1,1884 +1,1884 @@ /* * Copyright 2017 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 "layout.h" // local #include "dockcorona.h" #include "importer.h" #include "layoutmanager.h" #include "screenpool.h" -#include "dock/dockview.h" -#include "dock/positioner.h" +#include "view/positioner.h" +#include "view/view.h" #include "settings/universalsettings.h" // Qt #include #include #include // KDE #include #include // Plasma #include #include #include namespace Latte { const QString Layout::MultipleLayoutsName = ".multiple-layouts_hidden"; Layout::Layout(QObject *parent, QString layoutFile, QString assignedName) : QObject(parent) { qDebug() << "Layout file to create object: " << layoutFile << " with name: " << assignedName; if (QFile(layoutFile).exists()) { if (assignedName.isEmpty()) { assignedName = layoutName(layoutFile); } //!this order is important because setFile initializes also the m_layoutGroup setFile(layoutFile); setName(assignedName); loadConfig(); init(); } } Layout::~Layout() { if (!m_layoutFile.isEmpty()) { m_layoutGroup.sync(); } } void Layout::syncToLayoutFile(bool removeLayoutId) { if (!m_corona || !isWritable()) { return; } KSharedConfigPtr filePtr = KSharedConfig::openConfig(m_layoutFile); KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments"); oldContainments.deleteGroup(); oldContainments.sync(); qDebug() << " LAYOUT :: " << m_layoutName << " is syncing its original file."; foreach (auto containment, m_containments) { if (removeLayoutId) { containment->config().writeEntry("layoutId", ""); } KConfigGroup newGroup = oldContainments.group(QString::number(containment->id())); containment->config().copyTo(&newGroup); if (!removeLayoutId) { newGroup.writeEntry("layoutId", ""); newGroup.sync(); } } oldContainments.sync(); } void Layout::unloadContainments() { if (!m_corona) { return; } //!disconnect signals in order to avoid crashes when the layout is unloading disconnect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); disconnect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); qDebug() << "Layout - " + name() + " unload: containments ... size ::: " << m_containments.size() << " ,dockViews in memory ::: " << m_dockViews.size() << " ,hidden dockViews in memory ::: " << m_waitingDockViews.size(); foreach (auto view, m_dockViews) { view->disconnectSensitiveSignals(); } foreach (auto view, m_waitingDockViews) { view->disconnectSensitiveSignals(); } m_unloadedContainmentsIds.clear(); QList systrays; //!identify systrays and unload them first foreach (auto containment, m_containments) { if (Plasma::Applet *parentApplet = qobject_cast(containment->parent())) { systrays.append(containment); } } while (!systrays.isEmpty()) { Plasma::Containment *systray = systrays.at(0); m_unloadedContainmentsIds << QString::number(systray->id()); systrays.removeFirst(); m_containments.removeAll(systray); delete systray; } while (!m_containments.isEmpty()) { Plasma::Containment *containment = m_containments.at(0); m_unloadedContainmentsIds << QString::number(containment->id()); m_containments.removeFirst(); delete containment; } } void Layout::unloadDockViews() { if (!m_corona) { return; } qDebug() << "Layout - " + name() + " unload: dockViews ... size: " << m_dockViews.size(); qDeleteAll(m_dockViews); qDeleteAll(m_waitingDockViews); m_dockViews.clear(); m_waitingDockViews.clear(); } void Layout::init() { connect(this, &Layout::activitiesChanged, this, &Layout::saveConfig); connect(this, &Layout::backgroundChanged, this, &Layout::saveConfig); connect(this, &Layout::versionChanged, this, &Layout::saveConfig); connect(this, &Layout::colorChanged, this, &Layout::textColorChanged); connect(this, &Layout::disableBordersForMaximizedWindowsChanged, this, &Layout::saveConfig); connect(this, &Layout::showInMenuChanged, this, &Layout::saveConfig); connect(this, &Layout::textColorChanged, this, &Layout::saveConfig); connect(this, &Layout::launchersChanged, this, &Layout::saveConfig); connect(this, &Layout::lastUsedActivityChanged, this, &Layout::saveConfig); } void Layout::initToCorona(DockCorona *corona) { m_corona = corona; foreach (auto containment, m_corona->containments()) { if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { addContainment(containment); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { addContainment(containment); } } } qDebug() << "Layout ::::: " << name() << " added contaiments ::: " << m_containments.size(); connect(m_corona->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else { kwin_setDisabledMaximizedBorders(false); } }); if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout && m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders() && m_corona->layoutManager()->currentLayoutName() == name()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } }); } if (m_layoutName != MultipleLayoutsName) { updateLastUsedActivity(); } connect(m_corona, &Plasma::Corona::containmentAdded, this, &Layout::addContainment); connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &Layout::updateLastUsedActivity); //!connect signals after adding the containment connect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); connect(this, &Layout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); emit viewsCountChanged(); } int Layout::version() const { return m_version; } void Layout::setVersion(int ver) { if (m_version == ver) { return; } m_version = ver; emit versionChanged(); } bool Layout::blockAutomaticDockViewCreation() const { return m_blockAutomaticDockViewCreation; } void Layout::setBlockAutomaticDockViewCreation(bool block) { if (m_blockAutomaticDockViewCreation == block) { return; } m_blockAutomaticDockViewCreation = block; } bool Layout::disableBordersForMaximizedWindows() const { return m_disableBordersForMaximizedWindows; } void Layout::setDisableBordersForMaximizedWindows(bool disable) { if (m_disableBordersForMaximizedWindows == disable) { return; } m_disableBordersForMaximizedWindows = disable; kwin_setDisabledMaximizedBorders(disable); emit disableBordersForMaximizedWindowsChanged(); } bool Layout::kwin_disabledMaximizedBorders() const { //! Indentify Plasma Desktop version QProcess process; process.start("kreadconfig5 --file kwinrc --group Windows --key BorderlessMaximizedWindows"); process.waitForFinished(); QString output(process.readAllStandardOutput()); output = output.remove("\n"); return (output == "true"); } void Layout::kwin_setDisabledMaximizedBorders(bool disable) { if (kwin_disabledMaximizedBorders() == disable) { return; } QString disableText = disable ? "true" : "false"; QProcess process; QString commandStr = "kwriteconfig5 --file kwinrc --group Windows --key BorderlessMaximizedWindows --type bool " + disableText; process.start(commandStr); process.waitForFinished(); QDBusInterface iface("org.kde.KWin", "/KWin", "", QDBusConnection::sessionBus()); if (iface.isValid()) { iface.call("reconfigure"); } } bool Layout::showInMenu() const { return m_showInMenu; } void Layout::setShowInMenu(bool show) { if (m_showInMenu == show) { return; } m_showInMenu = show; emit showInMenuChanged(); } bool Layout::isWritable() const { QFileInfo layoutFileInfo(m_layoutFile); if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) { return false; } else { return true; } } void Layout::lock() { QFileInfo layoutFileInfo(m_layoutFile); if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) { QFile(m_layoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } void Layout::unlock() { QFileInfo layoutFileInfo(m_layoutFile); if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) { QFile(m_layoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } QString Layout::background() const { return m_background; } void Layout::setBackground(QString path) { if (path == m_background) { return; } if (!path.isEmpty() && !QFileInfo(path).exists()) { return; } m_background = path; //! initialize the text color also if (path.isEmpty()) { setTextColor(QString()); } emit backgroundChanged(); } QString Layout::name() const { return m_layoutName; } void Layout::setName(QString name) { if (m_layoutName == name) { return; } qDebug() << "Layout name:" << name; m_layoutName = name; emit nameChanged(); } void Layout::renameLayout(QString newName) { if (m_layoutFile != Importer::layoutFilePath(newName)) { setFile(Importer::layoutFilePath(newName)); } if (m_layoutName != newName) { setName(newName); } //! thus this is a linked file if (m_corona) { foreach (auto containment, m_containments) { containment->config().writeEntry("layoutId", m_layoutName); } } } QString Layout::color() const { return m_color; } void Layout::setColor(QString color) { if (m_color == color) { return; } m_color = color; emit colorChanged(); } QString Layout::textColor() const { //! the user is in default layout theme if (m_background.isEmpty()) { if (m_color == "blue") { return "#D7E3FF"; } else if (m_color == "brown") { return "#F1DECB"; } else if (m_color == "darkgrey") { return "#ECECEC"; } else if (m_color == "gold") { return "#7C3636"; } else if (m_color == "green") { return "#4D7549"; } else if (m_color == "lightskyblue") { return "#0C2A43"; } else if (m_color == "orange") { return "#6F3902"; } else if (m_color == "pink") { return "#743C46"; } else if (m_color == "purple") { return "#ECD9FF"; } else if (m_color == "red") { return "#F3E4E4"; } else if (m_color == "wheat") { return "#6A4E25"; } else { return "#FCFCFC"; } } return "#" + m_textColor; } void Layout::setTextColor(QString color) { //! remove # if someone is trying to set it this way if (color.startsWith("#")) { color.remove(0, 1); } if (m_textColor == color) { return; } m_textColor = color; emit textColorChanged(); } QString Layout::file() const { return m_layoutFile; } void Layout::setFile(QString file) { if (m_layoutFile == file) { return; } qDebug() << "Layout file:" << file; m_layoutFile = file; KSharedConfigPtr filePtr = KSharedConfig::openConfig(m_layoutFile); m_layoutGroup = KConfigGroup(filePtr, "LayoutSettings"); emit fileChanged(); } QStringList Layout::launchers() const { return m_launchers; } void Layout::setLaunchers(QStringList launcherList) { if (m_launchers == launcherList) return; m_launchers = launcherList; emit launchersChanged(); } QStringList Layout::activities() const { return m_activities; } void Layout::setActivities(QStringList activities) { if (m_activities == activities) { return; } m_activities = activities; emit activitiesChanged(); } QStringList Layout::unloadedContainmentsIds() { return m_unloadedContainmentsIds; } bool Layout::isActiveLayout() const { if (!m_corona) { return false; } Layout *activeLayout = m_corona->layoutManager()->activeLayout(m_layoutName); if (activeLayout) { return true; } else { return false; } } bool Layout::isOriginalLayout() const { return m_layoutName != MultipleLayoutsName; } bool Layout::layoutIsBroken() const { if (m_layoutFile.isEmpty() || !QFile(m_layoutFile).exists()) { return false; } QStringList ids; QStringList conts; QStringList applets; KSharedConfigPtr lFile = KSharedConfig::openConfig(m_layoutFile); if (!m_corona) { KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments"); ids << containmentsEntries.groupList(); conts << ids; foreach (auto cId, containmentsEntries.groupList()) { auto appletsEntries = containmentsEntries.group(cId).group("Applets"); ids << appletsEntries.groupList(); applets << appletsEntries.groupList(); } } else { foreach (auto containment, m_containments) { ids << QString::number(containment->id()); conts << QString::number(containment->id()); foreach (auto applet, containment->applets()) { ids << QString::number(applet->id()); applets << QString::number(applet->id()); } } } QSet idsSet = QSet::fromList(ids); /* a different way to count duplicates QMap countOfStrings; for (int i = 0; i < ids.count(); i++) { countOfStrings[ids[i]]++; }*/ if (idsSet.count() != ids.count()) { qDebug() << " ---- ERROR - BROKEN LAYOUT :: " << m_layoutName << " ----"; if (!m_corona) { qDebug() << " --- file : " << m_layoutFile; } else { if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { qDebug() << " --- in multiple layouts hidden file : " << Importer::layoutFilePath(Layout::MultipleLayoutsName); } else { qDebug() << " --- in layout file : " << m_layoutFile; } } qDebug() << "Contaiments :: " << conts; qDebug() << "Applets :: " << applets; foreach (QString c, conts) { if (applets.contains(c)) { qDebug() << "Error: Same applet and containment id found ::: " << c; } } for (int i = 0; i < ids.count(); ++i) { for (int j = i + 1; j < ids.count(); ++j) { if (ids[i] == ids[j]) { qDebug() << "Error: Applets with same id ::: " << ids[i]; } } } qDebug() << " -- - -- - -- - -- - - -- - - - - -- - - - - "; if (!m_corona) { KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments"); foreach (auto cId, containmentsEntries.groupList()) { auto appletsEntries = containmentsEntries.group(cId).group("Applets"); qDebug() << " CONTAINMENT : " << cId << " APPLETS : " << appletsEntries.groupList(); } } else { foreach (auto containment, m_containments) { QStringList appletsIds; foreach (auto applet, containment->applets()) { appletsIds << QString::number(applet->id()); } qDebug() << " CONTAINMENT : " << containment->id() << " APPLETS : " << appletsIds.join(","); } } return true; } return false; } QString Layout::layoutName(const QString &fileName) { int lastSlash = fileName.lastIndexOf("/"); QString tempLayoutFile = fileName; QString layoutName = tempLayoutFile.remove(0, lastSlash + 1); int ext = layoutName.lastIndexOf(".layout.latte"); layoutName = layoutName.remove(ext, 13); return layoutName; } void Layout::loadConfig() { m_version = m_layoutGroup.readEntry("version", 2); m_color = m_layoutGroup.readEntry("color", QString("blue")); m_disableBordersForMaximizedWindows = m_layoutGroup.readEntry("disableBordersForMaximizedWindows", false); m_showInMenu = m_layoutGroup.readEntry("showInMenu", false); m_textColor = m_layoutGroup.readEntry("textColor", QString("fcfcfc")); m_activities = m_layoutGroup.readEntry("activities", QStringList()); m_launchers = m_layoutGroup.readEntry("launchers", QStringList()); m_lastUsedActivity = m_layoutGroup.readEntry("lastUsedActivity", QString()); QString back = m_layoutGroup.readEntry("background", ""); if (!back.isEmpty()) { if (QFileInfo(back).exists()) { m_background = back; } else { m_layoutGroup.writeEntry("background", QString()); } } emit activitiesChanged(); } void Layout::saveConfig() { qDebug() << "layout is saving... for layout:" << m_layoutName; m_layoutGroup.writeEntry("version", m_version); m_layoutGroup.writeEntry("showInMenu", m_showInMenu); m_layoutGroup.writeEntry("color", m_color); m_layoutGroup.writeEntry("disableBordersForMaximizedWindows", m_disableBordersForMaximizedWindows); m_layoutGroup.writeEntry("launchers", m_launchers); m_layoutGroup.writeEntry("background", m_background); m_layoutGroup.writeEntry("activities", m_activities); m_layoutGroup.writeEntry("lastUsedActivity", m_lastUsedActivity); m_layoutGroup.writeEntry("textColor", m_textColor); m_layoutGroup.sync(); } //! Containments Actions void Layout::addContainment(Plasma::Containment *containment) { if (!containment || m_containments.contains(containment)) { return; } bool containmentInLayout{false}; if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { m_containments.append(containment); containmentInLayout = true; } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { m_containments.append(containment); containmentInLayout = true; } } if (containmentInLayout) { if (!blockAutomaticDockViewCreation()) { addDock(containment); } else { qDebug() << "delaying DockView creation for containment :: " << containment->id(); } connect(containment, &QObject::destroyed, this, &Layout::containmentDestroyed); } } -QHash *Layout::dockViews() +QHash *Layout::dockViews() { return &m_dockViews; } QList *Layout::containments() { return &m_containments; } const QStringList Layout::appliedActivities() { if (!m_corona) { return {}; } if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { return {"0"}; } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { if (m_activities.isEmpty()) { return m_corona->layoutManager()->orphanedActivities(); } else { return m_activities; } } else { return {"0"}; } } QString Layout::lastUsedActivity() { return m_lastUsedActivity; } void Layout::clearLastUsedActivity() { m_lastUsedActivity = ""; emit lastUsedActivityChanged(); } void Layout::updateLastUsedActivity() { if (!m_corona) { return; } if (!m_lastUsedActivity.isEmpty() && !m_corona->layoutManager()->activities().contains(m_lastUsedActivity)) { clearLastUsedActivity(); } QString currentId = m_corona->activitiesConsumer()->currentActivity(); QStringList appliedActivitiesIds = appliedActivities(); if (m_lastUsedActivity != currentId && (appliedActivitiesIds.contains(currentId) || m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout)) { m_lastUsedActivity = currentId; emit lastUsedActivityChanged(); } } void Layout::destroyedChanged(bool destroyed) { if (!m_corona) { return; } qDebug() << "dock containment destroyed changed!!!!"; Plasma::Containment *sender = qobject_cast(QObject::sender()); if (!sender) { return; } if (destroyed) { m_waitingDockViews[sender] = m_dockViews.take(static_cast(sender)); } else { m_dockViews[sender] = m_waitingDockViews.take(static_cast(sender)); } emit viewsCountChanged(); } void Layout::containmentDestroyed(QObject *cont) { if (!m_corona) { return; } Plasma::Containment *containment = static_cast(cont); if (containment) { int containmentIndex = m_containments.indexOf(containment); if (containmentIndex >= 0) { m_containments.removeAt(containmentIndex); } qDebug() << "Layout " << name() << " :: containment destroyed!!!!"; auto view = m_dockViews.take(containment); if (!view) { view = m_waitingDockViews.take(containment); } if (view) { view->disconnectSensitiveSignals(); view->deleteLater(); emit viewsCountChanged(); emit viewColorizerChanged(); } } } void Layout::addDock(Plasma::Containment *containment, bool forceOnPrimary, int expDockScreen) { qDebug() << "Layout :::: " << m_layoutName << " ::: addDock was called... m_containments :: " << m_containments.size(); if (!containment || !m_corona || !containment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } qDebug() << "step 1..."; if (!isLatteContainment(containment)) return; qDebug() << "step 2..."; for (auto *dock : m_dockViews) { if (dock->containment() == containment) return; } qDebug() << "step 3..."; QScreen *nextScreen{qGuiApp->primaryScreen()}; bool onPrimary = containment->config().readEntry("onPrimary", true); int id = containment->screen(); if (id == -1 && expDockScreen == -1) { id = containment->lastScreen(); } if (expDockScreen > -1) { id = expDockScreen; } qDebug() << "add dock - containment id: " << containment->id() << " ,screen : " << id << " - " << m_corona->screenPool()->connector(id) << " ,onprimary:" << onPrimary << " - " << qGuiApp->primaryScreen()->name() << " ,forceOnPrimary:" << forceOnPrimary; if (id >= 0 && !onPrimary && !forceOnPrimary) { QString connector = m_corona->screenPool()->connector(id); qDebug() << "add dock - connector : " << connector; bool found{false}; foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == connector) { found = true; nextScreen = scr; break; } } if (!found) { qDebug() << "reject : adding explicit dock, screen not available ! : " << connector; return; } //! explicit dock can not be added at explicit screen when that screen is the same with //! primary screen and that edge is already occupied by a primary dock if (nextScreen == qGuiApp->primaryScreen() && primaryDockOccupyEdge(containment->location())) { qDebug() << "reject : adding explicit dock, primary dock occupies edge at screen ! : " << connector; return; } } if (id >= 0 && onPrimary) { QString connector = m_corona->screenPool()->connector(id); qDebug() << "add dock - connector : " << connector; foreach (auto view, m_dockViews) { auto testContainment = view->containment(); int testScreenId = testContainment->screen(); if (testScreenId == -1) { testScreenId = testContainment->lastScreen(); } bool testOnPrimary = testContainment->config().readEntry("onPrimary", true); Plasma::Types::Location testLocation = static_cast((int)testContainment->config().readEntry("location", (int)Plasma::Types::BottomEdge)); if (!testOnPrimary && m_corona->screenPool()->primaryScreenId() == testScreenId && testLocation == containment->location()) { qDebug() << "Rejected explicit dockView and removing it in order add an onPrimary with higher priority at screen: " << connector; auto viewToDelete = m_dockViews.take(testContainment); viewToDelete->disconnectSensitiveSignals(); viewToDelete->deleteLater(); } } } /* old behavior to not add primary docks on explicit one else if (onPrimary) { if (explicitDockOccupyEdge(m_corona->screenPool()->primaryScreenId(), containment->location())) { qDebug() << "CORONA ::: adding dock rejected, the edge is occupied by explicit dock ! : " << containment->location(); //we must check that an onPrimary dock should never catch up the same edge on //the same screen with an explicit dock return; } }*/ qDebug() << "Adding dock for container..."; qDebug() << "onPrimary: " << onPrimary << "screen!!! :" << nextScreen->name(); //! it is used to set the correct flag during the creation //! of the window... This of course is also used during //! recreations of the window between different visibility modes auto mode = static_cast(containment->config().readEntry("visibility", static_cast(Dock::DodgeActive))); bool dockWin{true}; if (mode == Dock::AlwaysVisible || mode == Dock::WindowsGoBelow) { dockWin = true; } else { dockWin = containment->config().readEntry("dockWindowBehavior", true); } - auto dockView = new DockView(m_corona, nextScreen, dockWin); + auto dockView = new Latte::View(m_corona, nextScreen, dockWin); dockView->init(); dockView->setContainment(containment); dockView->setManagedLayout(this); //! force this special dock case to become primary //! even though it isnt if (forceOnPrimary) { qDebug() << "Enforcing onPrimary:true as requested for DockView..."; dockView->setOnPrimary(true); } // connect(containment, &QObject::destroyed, this, &Layout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &Layout::destroyedChanged); connect(containment, &Plasma::Applet::locationChanged, m_corona, &DockCorona::dockLocationChanged); connect(containment, &Plasma::Containment::appletAlternativesRequested , m_corona, &DockCorona::showAlternativesForApplet, Qt::QueuedConnection); if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { connect(containment, &Plasma::Containment::appletCreated, this, &Layout::appletCreated); } connect(dockView->effects(), &ViewPart::Effects::colorizerEnabledChanged, this, &Layout::viewColorizerChanged); //! Qt 5.9 creates a crash for this in wayland, that is why the check is used //! but on the other hand we need this for copy to work correctly and show //! the copied dock under X11 //if (!KWindowSystem::isPlatformWayland()) { dockView->show(); //} m_dockViews[containment] = dockView; emit viewColorizerChanged(); emit viewsCountChanged(); } void Layout::addNewDock() { if (!m_corona) { return; } m_corona->loadDefaultLayout(); } void Layout::copyDock(Plasma::Containment *containment) { if (!containment || !m_corona) return; qDebug() << "copying containment layout"; //! Settting mutable for create a containment m_corona->setImmutability(Plasma::Types::Mutable); QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; //! WE NEED A WAY TO COPY A CONTAINMENT!!!! QFile copyFile(temp1File); if (copyFile.exists()) copyFile.remove(); KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1File); KConfigGroup copied_conts = KConfigGroup(newFile, "Containments"); KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id())); KConfigGroup copied_systray; // toCopyContainmentIds << QString::number(containment->id()); // toCopyAppletIds << containment->config().group("Applets").groupList(); containment->config().copyTo(&copied_c1); //!investigate if there is a systray in the containment to copy also int systrayId = -1; QString systrayAppletId; auto applets = containment->config().group("Applets"); foreach (auto applet, applets.groupList()) { KConfigGroup appletSettings = applets.group(applet).group("Configuration"); int tSysId = appletSettings.readEntry("SystrayContainmentId", -1); if (tSysId != -1) { systrayId = tSysId; systrayAppletId = applet; qDebug() << "systray was found in the containment... ::: " << tSysId; break; } } if (systrayId != -1) { Plasma::Containment *systray{nullptr}; foreach (auto containment, m_corona->containments()) { if (containment->id() == systrayId) { systray = containment; break; } } if (systray) { copied_systray = KConfigGroup(&copied_conts, QString::number(systray->id())); // toCopyContainmentIds << QString::number(systray->id()); // toCopyAppletIds << systray->config().group("Applets").groupList(); systray->config().copyTo(&copied_systray); } } //! end of systray specific code //! update ids to unique ones QString temp2File = newUniqueIdsLayoutFromFile(temp1File); //! Don't create DockView when the containment is created because we must update //! its screen settings first setBlockAutomaticDockViewCreation(true); //! Finally import the configuration QList importedDocks = importLayoutFile(temp2File); Plasma::Containment *newContainment{nullptr}; if (importedDocks.size() == 1) { newContainment = importedDocks[0]; } if (!newContainment || !newContainment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } auto config = newContainment->config(); //in multi-screen environment the copied dock is moved to alternative screens first const auto screens = qGuiApp->screens(); auto dock = m_dockViews[containment]; bool setOnExplicitScreen = false; int dockScrId = -1; int copyScrId = -1; if (dock) { dockScrId = dock->positioner()->currentScreenId(); qDebug() << "COPY DOCK SCREEN ::: " << dockScrId; if (dockScrId != -1 && screens.count() > 1) { foreach (auto scr, screens) { copyScrId = m_corona->screenPool()->id(scr->name()); //the screen must exist and not be the same with the original dock if (copyScrId > -1 && copyScrId != dockScrId) { QList fEdges = freeEdges(copyScrId); if (fEdges.contains((Plasma::Types::Location)containment->location())) { ///set this containment to an explicit screen config.writeEntry("onPrimary", false); config.writeEntry("lastScreen", copyScrId); newContainment->setLocation(containment->location()); qDebug() << "COPY DOCK SCREEN NEW SCREEN ::: " << copyScrId; setOnExplicitScreen = true; break; } } } } } if (!setOnExplicitScreen) { QList edges = freeEdges(newContainment->screen()); if (edges.count() > 0) { newContainment->setLocation(edges.at(0)); } else { newContainment->setLocation(Plasma::Types::BottomEdge); } config.writeEntry("onPrimary", false); config.writeEntry("lastScreen", dockScrId); } newContainment->config().sync(); if (setOnExplicitScreen && copyScrId > -1) { qDebug() << "Copy Dock in explicit screen ::: " << copyScrId; addDock(newContainment, false, copyScrId); newContainment->reactToScreenChange(); } else { qDebug() << "Copy Dock in current screen..."; addDock(newContainment, false, dockScrId); } setBlockAutomaticDockViewCreation(false); } void Layout::appletCreated(Plasma::Applet *applet) { //! In Multiple Layout the orphaned systrays must be assigned to layouts //! when the user adds them KConfigGroup appletSettings = applet->containment()->config().group("Applets").group(QString::number(applet->id())).group("Configuration"); int systrayId = appletSettings.readEntry("SystrayContainmentId", -1); if (systrayId != -1) { uint sId = (uint)systrayId; foreach (auto containment, m_corona->containments()) { if (containment->id() == sId) { containment->config().writeEntry("layoutId", m_layoutName); } addContainment(containment); } } } void Layout::importToCorona() { if (!m_corona) { return; } //! Settting mutable for create a containment m_corona->setImmutability(Plasma::Types::Mutable); QString temp1FilePath = QDir::homePath() + "/.config/lattedock.copy1.bak"; //! we need to copy first the layout file because the kde cache //! may not have yet been updated (KSharedConfigPtr) //! this way we make sure at the latest changes stored in the layout file //! will be also available when changing to Myltiple Layouts QString tempLayoutFilePath = QDir::homePath() + "/.config/lattedock.layout.bak"; //! WE NEED A WAY TO COPY A CONTAINMENT!!!! QFile tempLayoutFile(tempLayoutFilePath); QFile copyFile(temp1FilePath); QFile layoutOriginalFile(m_layoutFile); if (tempLayoutFile.exists()) { tempLayoutFile.remove(); } if (copyFile.exists()) copyFile.remove(); layoutOriginalFile.copy(tempLayoutFilePath); KSharedConfigPtr filePtr = KSharedConfig::openConfig(tempLayoutFilePath); KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1FilePath); KConfigGroup copyGroup = KConfigGroup(newFile, "Containments"); KConfigGroup current_containments = KConfigGroup(filePtr, "Containments"); current_containments.copyTo(©Group); copyGroup.sync(); //! update ids to unique ones QString temp2File = newUniqueIdsLayoutFromFile(temp1FilePath); //! Finally import the configuration importLayoutFile(temp2File); } QString Layout::availableId(QStringList all, QStringList assigned, int base) { bool found = false; int i = base; while (!found && i < 32000) { QString iStr = QString::number(i); if (!all.contains(iStr) && !assigned.contains(iStr)) { return iStr; } i++; } return QString(""); } QString Layout::newUniqueIdsLayoutFromFile(QString file) { if (!m_corona) { return QString(); } QString tempFile = QDir::homePath() + "/.config/lattedock.copy2.bak"; QFile copyFile(tempFile); if (copyFile.exists()) copyFile.remove(); //! BEGIN updating the ids in the temp file QStringList allIds; allIds << m_corona->containmentsIds(); allIds << m_corona->appletsIds(); QStringList toInvestigateContainmentIds; QStringList toInvestigateAppletIds; QStringList toInvestigateSystrayContIds; //! first is the systray containment id QHash systrayParentContainmentIds; QHash systrayAppletIds; //qDebug() << "Ids:" << allIds; //qDebug() << "to copy containments: " << toCopyContainmentIds; //qDebug() << "to copy applets: " << toCopyAppletIds; QStringList assignedIds; QHash assigned; KSharedConfigPtr filePtr = KSharedConfig::openConfig(file); KConfigGroup investigate_conts = KConfigGroup(filePtr, "Containments"); //KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id())); //! Record the containment and applet ids foreach (auto cId, investigate_conts.groupList()) { toInvestigateContainmentIds << cId; auto appletsEntries = investigate_conts.group(cId).group("Applets"); toInvestigateAppletIds << appletsEntries.groupList(); //! investigate for systrays foreach (auto appletId, appletsEntries.groupList()) { KConfigGroup appletSettings = appletsEntries.group(appletId).group("Configuration"); int tSysId = appletSettings.readEntry("SystrayContainmentId", -1); //! It is a systray !!! if (tSysId != -1) { QString tSysIdStr = QString::number(tSysId); toInvestigateSystrayContIds << tSysIdStr; systrayParentContainmentIds[tSysIdStr] = cId; systrayAppletIds[tSysIdStr] = appletId; qDebug() << "systray was found in the containment..."; } } } //! Reassign containment and applet ids to unique ones foreach (auto contId, toInvestigateContainmentIds) { QString newId = availableId(allIds, assignedIds, 12); assignedIds << newId; assigned[contId] = newId; } foreach (auto appId, toInvestigateAppletIds) { QString newId = availableId(allIds, assignedIds, 40); assignedIds << newId; assigned[appId] = newId; } qDebug() << "ALL CORONA IDS ::: " << allIds; qDebug() << "FULL ASSIGNMENTS ::: " << assigned; foreach (auto cId, toInvestigateContainmentIds) { QString value = assigned[cId]; if (assigned.contains(value)) { QString value2 = assigned[value]; if (cId != assigned[cId] && !value2.isEmpty() && cId == value2) { qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << cId << " .. fixed .."; assigned[cId] = cId; assigned[value] = value; } } } foreach (auto aId, toInvestigateAppletIds) { QString value = assigned[aId]; if (assigned.contains(value)) { QString value2 = assigned[value]; if (aId != assigned[aId] && !value2.isEmpty() && aId == value2) { qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << aId << " .. fixed .."; assigned[aId] = aId; assigned[value] = value; } } } qDebug() << "FIXED FULL ASSIGNMENTS ::: " << assigned; //! update applet ids in their contaiment order and in MultipleLayouts update also the layoutId foreach (auto cId, investigate_conts.groupList()) { //! Update (appletOrder) and (lockedZoomApplets) for (int i = 1; i <= 2; ++i) { QString settingStr = (i == 1) ? "appletOrder" : "lockedZoomApplets"; QString order1 = investigate_conts.group(cId).group("General").readEntry(settingStr, QString()); if (!order1.isEmpty()) { QStringList order1Ids = order1.split(";"); QStringList fixedOrder1Ids; for (int i = 0; i < order1Ids.count(); ++i) { fixedOrder1Ids.append(assigned[order1Ids[i]]); } QString fixedOrder1 = fixedOrder1Ids.join(";"); investigate_conts.group(cId).group("General").writeEntry(settingStr, fixedOrder1); } } if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { investigate_conts.group(cId).writeEntry("layoutId", m_layoutName); } } //! must update also the systray id in its applet foreach (auto systrayId, toInvestigateSystrayContIds) { KConfigGroup systrayParentContainment = investigate_conts.group(systrayParentContainmentIds[systrayId]); systrayParentContainment.group("Applets").group(systrayAppletIds[systrayId]).group("Configuration").writeEntry("SystrayContainmentId", assigned[systrayId]); systrayParentContainment.sync(); } investigate_conts.sync(); //! Copy To Temp 2 File And Update Correctly The Ids KSharedConfigPtr file2Ptr = KSharedConfig::openConfig(tempFile); KConfigGroup fixedNewContainmets = KConfigGroup(file2Ptr, "Containments"); foreach (auto contId, investigate_conts.groupList()) { QString pluginId = investigate_conts.group(contId).readEntry("plugin", ""); if (pluginId != "org.kde.desktopcontainment") { //!don't add ghost containments KConfigGroup newContainmentGroup = fixedNewContainmets.group(assigned[contId]); investigate_conts.group(contId).copyTo(&newContainmentGroup); newContainmentGroup.group("Applets").deleteGroup(); foreach (auto appId, investigate_conts.group(contId).group("Applets").groupList()) { KConfigGroup appletGroup = investigate_conts.group(contId).group("Applets").group(appId); KConfigGroup newAppletGroup = fixedNewContainmets.group(assigned[contId]).group("Applets").group(assigned[appId]); appletGroup.copyTo(&newAppletGroup); } } } fixedNewContainmets.sync(); return tempFile; } QList Layout::importLayoutFile(QString file) { KSharedConfigPtr filePtr = KSharedConfig::openConfig(file); auto newContainments = m_corona->importLayout(KConfigGroup(filePtr, "")); ///Find latte and systray containments qDebug() << " imported containments ::: " << newContainments.length(); QList importedDocks; //QList systrays; foreach (auto containment, newContainments) { if (isLatteContainment(containment)) { qDebug() << "new latte containment id: " << containment->id(); importedDocks << containment; } } ///after systrays were found we must update in latte the relevant ids /*if (!systrays.isEmpty()) { foreach (auto systray, systrays) { qDebug() << "systray found with id : " << systray->id(); Plasma::Applet *parentApplet = qobject_cast(systray->parent()); if (parentApplet) { KConfigGroup appletSettings = parentApplet->config().group("Configuration"); if (appletSettings.hasKey("SystrayContainmentId")) { qDebug() << "!!! updating systray id to : " << systray->id(); appletSettings.writeEntry("SystrayContainmentId", systray->id()); } } } }*/ return importedDocks; } void Layout::recreateDock(Plasma::Containment *containment) { if (!m_corona) { return; } //! give the time to config window to close itself first and then recreate the dock //! step:1 remove the dockview QTimer::singleShot(350, [this, containment]() { auto view = m_dockViews.take(containment); if (view) { qDebug() << "recreate - step 1: removing dock for containment:" << containment->id(); //! step:2 add the new dockview connect(view, &QObject::destroyed, this, [this, containment]() { QTimer::singleShot(250, this, [this, containment]() { if (!m_dockViews.contains(containment)) { qDebug() << "recreate - step 2: adding dock for containment:" << containment->id(); addDock(containment); } }); }); view->deleteLater(); } }); } //! the central functions that updates loading/unloading dockviews //! concerning screen changed (for multi-screen setups mainly) void Layout::syncDockViewsToScreens() { if (!m_corona) { return; } qDebug() << "start of, syncDockViewsToScreens ...."; qDebug() << "LAYOUT ::: " << name(); qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size(); QHash> futureDocksLocations; QList futureShownViews; QString prmScreenName = qGuiApp->primaryScreen()->name(); //! first step: primary docks must be placed in primary screen free edges foreach (auto containment, m_containments) { if (isLatteContainment(containment)) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location location = static_cast((int)containment->config().readEntry("location", (int)Plasma::Types::BottomEdge)); if (onPrimary && !futureDocksLocations[prmScreenName].contains(location)) { futureDocksLocations[prmScreenName].append(location); futureShownViews.append(containment->id()); } } } //! second step: explicit docks must be placed in their screens if the screen edge is free foreach (auto containment, m_containments) { if (isLatteContainment(containment)) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location location = static_cast((int)containment->config().readEntry("location", (int)Plasma::Types::BottomEdge)); if (!onPrimary) { QString expScreenName = m_corona->screenPool()->connector(screenId); if (m_corona->screenPool()->screenExists(screenId) && !futureDocksLocations[expScreenName].contains(location)) { futureDocksLocations[expScreenName].append(location); futureShownViews.append(containment->id()); } } } } qDebug() << "PRIMARY SCREEN :: " << prmScreenName; qDebug() << "DOCKVIEWS MUST BE PRESENT AT :: " << futureDocksLocations; qDebug() << "FUTURESHOWNVIEWS MUST BE :: " << futureShownViews; //! add views foreach (auto containment, m_containments) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } if (!dockViewExists(containment) && futureShownViews.contains(containment->id())) { qDebug() << "syncDockViewsToScreens: view must be added... for containment:" << containment->id() << " at screen:" << m_corona->screenPool()->connector(screenId); addDock(containment); } } //! remove views foreach (auto view, m_dockViews) { if (view->containment() && !futureShownViews.contains(view->containment()->id())) { qDebug() << "syncDockViewsToScreens: view must be deleted... for containment:" << view->containment()->id() << " at screen:" << view->positioner()->currentScreenName(); auto viewToDelete = m_dockViews.take(view->containment()); viewToDelete->disconnectSensitiveSignals(); viewToDelete->deleteLater(); } } //! reconsider views foreach (auto view, m_dockViews) { if (view->containment() && futureShownViews.contains(view->containment()->id())) { //! if the dock will not be deleted its a very good point to reconsider //! if the screen in which is running is the correct one view->reconsiderScreen(); } } qDebug() << "end of, syncDockViewsToScreens ...."; } -void Layout::assignToLayout(DockView *dockView, QList containments) +void Layout::assignToLayout(Latte::View *dockView, QList containments) { if (!m_corona) { return; } if (dockView) { m_dockViews[dockView->containment()] = dockView; m_containments << containments; foreach (auto containment, containments) { containment->config().writeEntry("layoutId", name()); connect(containment, &QObject::destroyed, this, &Layout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &Layout::destroyedChanged); connect(containment, &Plasma::Containment::appletCreated, this, &Layout::appletCreated); } dockView->setManagedLayout(this); emit viewsCountChanged(); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { syncToLayoutFile(false); } } -QList Layout::unassignFromLayout(DockView *dockView) +QList Layout::unassignFromLayout(Latte::View *dockView) { QList containments; if (!m_corona) { return containments; } containments << dockView->containment(); foreach (auto containment, m_containments) { Plasma::Applet *parentApplet = qobject_cast(containment->parent()); //! add systrays from that dockView if (parentApplet && parentApplet->containment() && parentApplet->containment() == dockView->containment()) { containments << containment; disconnect(containment, &QObject::destroyed, this, &Layout::containmentDestroyed); disconnect(containment, &Plasma::Applet::destroyedChanged, this, &Layout::destroyedChanged); disconnect(containment, &Plasma::Containment::appletCreated, this, &Layout::appletCreated); } } foreach (auto containment, containments) { m_containments.removeAll(containment); } if (containments.size() > 0) { m_dockViews.remove(dockView->containment()); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { syncToLayoutFile(false); } return containments; } bool Layout::dockViewExists(Plasma::Containment *containment) { if (!m_corona) { return false; } return m_dockViews.keys().contains(containment); } -QList Layout::availableEdgesForView(QScreen *scr, DockView *forView) const +QList Layout::availableEdgesForView(QScreen *scr, Latte::View *forView) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } foreach (auto view, m_dockViews) { //! make sure that availabe edges takes into account only views that should be excluded, //! this is why the forView should not be excluded if (view && view != forView && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } QList Layout::qmlFreeEdges(int screen) const { if (!m_corona) { const QList emptyEdges; return emptyEdges; } const auto edges = freeEdges(screen); QList edgesInt; foreach (Plasma::Types::Location edge, edges) { edgesInt.append(static_cast(edge)); } return edgesInt; } QList Layout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } foreach (auto view, m_dockViews) { if (view && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } QList Layout::freeEdges(int screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } QScreen *scr = m_corona->screenPool()->screenForId(screen); foreach (auto view, m_dockViews) { if (view && scr && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } bool Layout::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const { if (!m_corona) { return false; } foreach (auto containment, m_containments) { if (isLatteContainment(containment)) { bool onPrimary = containment->config().readEntry("onPrimary", true); int id = containment->lastScreen(); Plasma::Types::Location contLocation = containment->location(); if (!onPrimary && id == screen && contLocation == location) { return true; } } } return false; } bool Layout::primaryDockOccupyEdge(Plasma::Types::Location location) const { if (!m_corona) { return false; } foreach (auto containment, m_containments) { if (isLatteContainment(containment)) { bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location contLocation = containment->location(); if (onPrimary && contLocation == location) { return true; } } } return false; } bool Layout::isLatteContainment(Plasma::Containment *containment) const { if (!containment) { return false; } if (containment->pluginMetaData().pluginId() == "org.kde.latte.containment") { return true; } return false; } int Layout::viewsWithTasks() const { if (!m_corona) { return 0; } int result = 0; foreach (auto view, m_dockViews) { if (view->tasksPresent()) { result++; } } return result; } int Layout::viewsCount(int screen) const { if (!m_corona) { return 0; } QScreen *scr = m_corona->screenPool()->screenForId(screen); int docks{0}; foreach (auto view, m_dockViews) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++docks; } } return docks; } int Layout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } int docks{0}; foreach (auto view, m_dockViews) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++docks; } } return docks; } int Layout::viewsCount() const { if (!m_corona) { return 0; } int docks{0}; foreach (auto view, m_dockViews) { if (view && view->containment() && !view->containment()->destroyed()) { ++docks; } } return docks; } } diff --git a/app/layout.h b/app/layout.h index 0f8650b5..1afa47ac 100644 --- a/app/layout.h +++ b/app/layout.h @@ -1,256 +1,256 @@ /* * Copyright 2017 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 LAYOUT_H #define LAYOUT_H // Qt #include #include // KDE #include #include // Plasma #include namespace Plasma { class Applet; class Containment; class Types; } namespace Latte { class DockCorona; -class DockView; +class View; } namespace Latte { //! This class is responsible to hold the settings for a specific layout. //! It also updates always the relevant layout configuration concerning //! its general settings (no the containments) class Layout : public QObject { Q_OBJECT Q_PROPERTY(bool showInMenu READ showInMenu WRITE setShowInMenu NOTIFY showInMenuChanged) Q_PROPERTY(int viewsCount READ viewsCount NOTIFY viewsCountChanged) Q_PROPERTY(QString background READ background NOTIFY backgroundChanged) Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QString lastUsedActivity READ lastUsedActivity NOTIFY lastUsedActivityChanged) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString textColor READ textColor NOTIFY textColorChanged) Q_PROPERTY(QStringList launchers READ launchers WRITE setLaunchers NOTIFY launchersChanged) Q_PROPERTY(QStringList activities READ activities WRITE setActivities NOTIFY activitiesChanged) public: Layout(QObject *parent, QString layoutFile, QString layoutName = QString()); ~Layout() override; static const QString MultipleLayoutsName; void initToCorona(DockCorona *corona); void syncToLayoutFile(bool removeLayoutId = false); void unloadContainments(); void unloadDockViews(); bool disableBordersForMaximizedWindows() const; void setDisableBordersForMaximizedWindows(bool disable); bool showInMenu() const; void setShowInMenu(bool show); bool layoutIsBroken() const; //!this layout is loaded and running bool isActiveLayout() const; //!it is original layout compared to pseudo-layouts that are combinations of multiple-original layouts bool isOriginalLayout() const; bool isWritable() const; bool dockViewExists(Plasma::Containment *containment); int version() const; void setVersion(int ver); QString background() const; void setBackground(QString path); QString color() const; void setColor(QString color); QString lastUsedActivity(); void clearLastUsedActivity(); //!e.g. when we export a layout QString name() const; QString file() const; QString textColor() const; void setTextColor(QString color); QStringList activities() const; void setActivities(QStringList activities); QStringList launchers() const; void setLaunchers(QStringList launcherList); static QString layoutName(const QString &fileName); void renameLayout(QString newName); QStringList unloadedContainmentsIds(); //! this function needs the layout to have first set the corona through initToCorona() function void addDock(Plasma::Containment *containment, bool forceOnPrimary = false, int expDockScreen = -1); void copyDock(Plasma::Containment *containment); void recreateDock(Plasma::Containment *containment); void syncDockViewsToScreens(); void importToCorona(); const QStringList appliedActivities(); QList *containments(); - QHash *dockViews(); + QHash *dockViews(); - //! Bind this dockView and its relevant containments(including systrays) - //! to this layout. It is used for moving a dockView from layout to layout) - void assignToLayout(DockView *dockView, QList containments); + //! Bind this latteView and its relevant containments(including systrays) + //! to this layout. It is used for moving a Latte::View from layout to layout) + void assignToLayout(Latte::View *dockView, QList containments); - //! Unassign that dockView from this layout (this is used for moving a dockView + //! Unassign that latteView from this layout (this is used for moving a latteView //! from layout to layout) and returns all the containments relevant to - //! that dockView - QList unassignFromLayout(DockView *dockView); + //! that latteView + QList unassignFromLayout(Latte::View *dockView); //! Available edges for specific view in that screen - QList availableEdgesForView(QScreen *scr, DockView *forView) const; + QList availableEdgesForView(QScreen *scr, Latte::View *forView) const; //! All free edges in that screen QList freeEdges(QScreen *scr) const; QList freeEdges(int screen) const; //! make it only read-only void lock(); //! make it writable which it should be the default void unlock(); int viewsCount(int screen) const; int viewsCount(QScreen *screen) const; int viewsCount() const; public slots: Q_INVOKABLE int viewsWithTasks() const; //change to types Q_INVOKABLE QList qmlFreeEdges(int screen) const; Q_INVOKABLE void addNewDock(); signals: void activitiesChanged(); void backgroundChanged(); void colorChanged(); void disableBordersForMaximizedWindowsChanged(); void fileChanged(); void lastUsedActivityChanged(); void launchersChanged(); void nameChanged(); void versionChanged(); void showInMenuChanged(); void textColorChanged(); void viewColorizerChanged(); void viewsCountChanged(); - //! used from DockView(s) in order to exist only one each time that has the highest priority + //! used from LatteView(s) in order to exist only one each time that has the highest priority //! to use the global shortcuts activations - void preferredViewForShortcutsChanged(DockView *view); + void preferredViewForShortcutsChanged(Latte::View *view); private slots: void loadConfig(); void saveConfig(); void addContainment(Plasma::Containment *containment); void appletCreated(Plasma::Applet *applet); void destroyedChanged(bool destroyed); void containmentDestroyed(QObject *cont); void updateLastUsedActivity(); private: void importLocalLayout(QString file); void init(); void setName(QString name); void setFile(QString file); - //! It can be used in order for DockViews to not be created automatically when + //! It can be used in order for LatteViews to not be created automatically when //! their corresponding containments are created e.g. copyDock functionality bool blockAutomaticDockViewCreation() const; void setBlockAutomaticDockViewCreation(bool block); bool explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const; bool primaryDockOccupyEdge(Plasma::Types::Location location) const; //! Check if a containment is a latte dock/panel bool isLatteContainment(Plasma::Containment *containment) const; bool kwin_disabledMaximizedBorders() const; void kwin_setDisabledMaximizedBorders(bool disable); QString availableId(QStringList all, QStringList assigned, int base); //! provides a new file path based the provided file. The new file //! has updated ids for containments and applets based on the corona //! loaded ones QString newUniqueIdsLayoutFromFile(QString file); //! imports a layout file and returns the containments for the docks QList importLayoutFile(QString file); private: bool m_blockAutomaticDockViewCreation{false}; bool m_disableBordersForMaximizedWindows{false}; bool m_showInMenu{false}; //if version doesn't exist it is and old layout file int m_version{2}; QString m_background; QString m_color; QString m_lastUsedActivity; //the last used activity for this layout QString m_layoutFile; QString m_layoutName; QString m_textColor; QStringList m_activities; QStringList m_launchers; QStringList m_unloadedContainmentsIds; DockCorona *m_corona{nullptr}; KConfigGroup m_layoutGroup; QList m_containments; - QHash m_dockViews; - QHash m_waitingDockViews; + QHash m_dockViews; + QHash m_waitingDockViews; }; } #endif // LAYOUT_H diff --git a/app/layoutmanager.cpp b/app/layoutmanager.cpp index 77411e6d..173f7d11 100644 --- a/app/layoutmanager.cpp +++ b/app/layoutmanager.cpp @@ -1,1216 +1,1216 @@ /* * Copyright 2017 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 "layoutmanager.h" // local #include "importer.h" #include "infoview.h" #include "launcherssignals.h" #include "layout.h" #include "screenpool.h" -#include "dock/dockview.h" #include "settings/settingsdialog.h" #include "settings/universalsettings.h" +#include "view/view.h" // Qt #include #include #include #include #include // KDE #include #include #include #include namespace Latte { const int MultipleLayoutsPresetId = 10; LayoutManager::LayoutManager(QObject *parent) : QObject(parent), m_importer(new Importer(this)), m_launchersSignals(new LaunchersSignals(this)), m_activitiesController(new KActivities::Controller(this)) { m_corona = qobject_cast(parent); if (m_corona) { connect(m_corona->universalSettings(), &UniversalSettings::currentLayoutNameChanged, this, &LayoutManager::currentLayoutNameChanged); connect(m_corona->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &LayoutManager::showInfoWindowChanged); m_dynamicSwitchTimer.setSingleShot(true); showInfoWindowChanged(); connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &LayoutManager::confirmDynamicSwitch); } } LayoutManager::~LayoutManager() { m_importer->deleteLater(); m_launchersSignals->deleteLater(); while (!m_activeLayouts.isEmpty()) { Layout *layout = m_activeLayouts.at(0); m_activeLayouts.removeFirst(); layout->unloadContainments(); layout->unloadDockViews(); layout->deleteLater(); } m_activitiesController->deleteLater(); } void LayoutManager::load() { int configVer = m_corona->universalSettings()->version(); qDebug() << "Universal Settings version : " << configVer; if (configVer < 2 && QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { qDebug() << "Latte must update its configuration..."; m_importer->updateOldConfiguration(); importPresets(false); } else if (!QFile(QDir::homePath() + "/.config/lattedockrc").exists()) { //startup create what is necessary.... QDir layoutDir(QDir::homePath() + "/.config/latte"); if (!layoutDir.exists()) { QDir(QDir::homePath() + "/.config").mkdir("latte"); } newLayout(i18n("My Layout")); importPresets(false); m_corona->universalSettings()->setCurrentLayoutName(i18n("My Layout")); m_corona->universalSettings()->setVersion(2); } //! Check if the multiple-layouts hidden file is present, add it if it isnt if (!QFile(QDir::homePath() + "/.config/latte/" + Layout::MultipleLayoutsName + ".layout.latte").exists()) { importPreset(MultipleLayoutsPresetId, false); } qDebug() << "Latte is loading its layouts..."; connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &LayoutManager::currentActivityChanged); connect(m_corona->m_activityConsumer, &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (memoryUsage() == Dock::MultipleLayouts) { syncMultipleLayoutsToActivities(); } }); loadLayouts(); } void LayoutManager::unload() { //! Unload all Layouts foreach (auto layout, m_activeLayouts) { if (memoryUsage() == Dock::MultipleLayouts && layout->isOriginalLayout()) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadDockViews(); if (memoryUsage() == Dock::MultipleLayouts && layout->isOriginalLayout()) { clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); } layout->deleteLater(); } //! Cleanup pseudo-layout from Containments if (memoryUsage() == Dock::MultipleLayouts) { // auto containmentsEntries = m_corona->config()->group("Containments"); // containmentsEntries.deleteGroup(); // containmentsEntries.sync(); } //! Remove no-needed temp files QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; QString temp2File = QDir::homePath() + "/.config/lattedock.copy2.bak"; QFile file1(temp1File); QFile file2(temp2File); if (file1.exists()) file1.remove(); if (file2.exists()) file2.remove(); } DockCorona *LayoutManager::corona() { return m_corona; } Importer *LayoutManager::importer() { return m_importer; } LaunchersSignals *LayoutManager::launchersSignals() { return m_launchersSignals; } QString LayoutManager::currentLayoutName() const { if (memoryUsage() == Dock::SingleLayout) { return m_corona->universalSettings()->currentLayoutName(); } else if (memoryUsage() == Dock::MultipleLayouts) { return m_currentLayoutNameInMultiEnvironment; } return QString(); } QString LayoutManager::defaultLayoutName() const { QByteArray presetNameOrig = QString("preset" + QString::number(1)).toUtf8(); QString presetPath = m_corona->kPackage().filePath(presetNameOrig); QString presetName = Layout::layoutName(presetPath); QByteArray presetNameChars = presetName.toUtf8(); presetName = i18n(presetNameChars); return presetName; } bool LayoutManager::hasColorizer() const { foreach (auto layout, m_activeLayouts) { for (const auto *view : *layout->dockViews()) { if (view->effects()->colorizerEnabled()) { return true; } } } return false; } bool LayoutManager::layoutExists(QString layoutName) const { return m_layouts.contains(layoutName); } QStringList LayoutManager::layouts() const { return m_layouts; } QStringList LayoutManager::menuLayouts() const { QStringList fixedMenuLayouts = m_menuLayouts; //! in case the current layout isnt checked to be shown in the menus //! we must add it on top if (!fixedMenuLayouts.contains(currentLayoutName()) && memoryUsage() == Dock::SingleLayout) { fixedMenuLayouts.prepend(currentLayoutName()); } else if (memoryUsage() == Dock::MultipleLayouts) { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout() && !fixedMenuLayouts.contains(layout->name())) { fixedMenuLayouts.prepend(layout->name()); } } } return fixedMenuLayouts; } void LayoutManager::setMenuLayouts(QStringList layouts) { if (m_menuLayouts == layouts) { return; } m_menuLayouts = layouts; emit menuLayoutsChanged(); } QStringList LayoutManager::activities() { return m_corona->m_activityConsumer->activities(); } QStringList LayoutManager::runningActivities() { return m_corona->m_activityConsumer->runningActivities(); } QStringList LayoutManager::orphanedActivities() { QStringList orphans; foreach (auto activity, activities()) { if (m_assignedLayouts[activity].isEmpty()) { orphans.append(activity); } } return orphans; } QStringList LayoutManager::presetsPaths() const { return m_presetsPaths; } QString LayoutManager::layoutPath(QString layoutName) { QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"; if (!QFile(path).exists()) { path = ""; } return path; } Dock::LayoutsMemoryUsage LayoutManager::memoryUsage() const { return m_corona->universalSettings()->layoutsMemoryUsage(); } int LayoutManager::layoutsMemoryUsage() { return (int)m_corona->universalSettings()->layoutsMemoryUsage(); } void LayoutManager::setMemoryUsage(Dock::LayoutsMemoryUsage memoryUsage) { m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage); } void LayoutManager::addDock(Plasma::Containment *containment, bool forceLoading, int expDockScreen) { if (memoryUsage() == Dock::SingleLayout) { m_activeLayouts.at(0)->addDock(containment, forceLoading, expDockScreen); } else if (memoryUsage() == Dock::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty()) { auto layout = activeLayout(layoutId); if (layout) { layout->addDock(containment, forceLoading, expDockScreen); } } } } -bool LayoutManager::dockViewExists(DockView *view) const +bool LayoutManager::dockViewExists(Latte::View *view) const { foreach (auto layout, m_activeLayouts) { for (auto it = layout->dockViews()->constBegin(), end = layout->dockViews()->constEnd(); it != end; ++it) { if (it.value() == view) { return true; } } } return false; } -QHash *LayoutManager::currentDockViews() const +QHash *LayoutManager::currentDockViews() const { if (memoryUsage() == Dock::SingleLayout) { return m_activeLayouts.at(0)->dockViews(); } else { foreach (auto layout, m_activeLayouts) { if (layout->activities().contains(m_corona->m_activityConsumer->currentActivity())) { return layout->dockViews(); } } foreach (auto layout, m_activeLayouts) { if ((layout->name() != Layout::MultipleLayoutsName) && (layout->activities().isEmpty())) { return layout->dockViews(); } } } return nullptr; } -QHash *LayoutManager::layoutDockViews(const QString &layoutName) const +QHash *LayoutManager::layoutDockViews(const QString &layoutName) const { Layout *layout = activeLayout(layoutName); if (layout) { return layout->dockViews(); } return nullptr; } QStringList LayoutManager::activeLayoutsNames() { QStringList names; if (memoryUsage() == Dock::SingleLayout) { names << currentLayoutName(); } else { for (int i = 0; i < m_activeLayouts.size(); ++i) { Layout *layout = m_activeLayouts.at(i); if (layout->isOriginalLayout()) { names << layout->name(); } } } return names; } Layout *LayoutManager::activeLayout(QString id) const { for (int i = 0; i < m_activeLayouts.size(); ++i) { Layout *layout = m_activeLayouts.at(i); if (layout->name() == id) { return layout; } } return nullptr; } int LayoutManager::activeLayoutPos(QString id) const { for (int i = 0; i < m_activeLayouts.size(); ++i) { Layout *layout = m_activeLayouts.at(i); if (layout->name() == id) { return i; } } return -1; } void LayoutManager::updateCurrentLayoutNameInMultiEnvironment() { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout() && layout->activities().contains(m_corona->activitiesConsumer()->currentActivity())) { m_currentLayoutNameInMultiEnvironment = layout->name(); emit currentLayoutNameChanged(); return; } } foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout() && layout->activities().isEmpty()) { m_currentLayoutNameInMultiEnvironment = layout->name(); emit currentLayoutNameChanged(); return; } } } void LayoutManager::currentActivityChanged(const QString &id) { if (memoryUsage() == Dock::SingleLayout) { qDebug() << "activity changed :: " << id; m_shouldSwitchToLayout = shouldSwitchToLayout(id); m_dynamicSwitchTimer.start(); } else if (memoryUsage() == Dock::MultipleLayouts) { updateCurrentLayoutNameInMultiEnvironment(); } } void LayoutManager::showInfoWindowChanged() { if (m_corona->universalSettings()->showInfoWindow()) { m_dynamicSwitchTimer.setInterval(1800); } else { m_dynamicSwitchTimer.setInterval(2300); } } QString LayoutManager::shouldSwitchToLayout(QString activityId) { if (m_assignedLayouts.contains(activityId) && m_assignedLayouts[activityId] != currentLayoutName()) { return m_assignedLayouts[activityId]; } else if (!m_assignedLayouts.contains(activityId) && !m_corona->universalSettings()->lastNonAssignedLayoutName().isEmpty() && m_corona->universalSettings()->lastNonAssignedLayoutName() != currentLayoutName()) { return m_corona->universalSettings()->lastNonAssignedLayoutName(); } return QString(); } void LayoutManager::confirmDynamicSwitch() { QString tempShouldSwitch = shouldSwitchToLayout(m_corona->m_activityConsumer->currentActivity()); if (tempShouldSwitch.isEmpty()) { return; } if (m_shouldSwitchToLayout == tempShouldSwitch && m_shouldSwitchToLayout != currentLayoutName()) { qDebug() << "dynamic switch to layout :: " << m_shouldSwitchToLayout; emit currentLayoutIsSwitching(currentLayoutName()); if (m_corona->universalSettings()->showInfoWindow()) { showInfoWindow(i18n("Switching to layout %0 ...").arg(m_shouldSwitchToLayout), 4000); } QTimer::singleShot(500, [this, tempShouldSwitch]() { switchToLayout(tempShouldSwitch); }); } else { m_shouldSwitchToLayout = tempShouldSwitch; m_dynamicSwitchTimer.start(); } } void LayoutManager::loadLayouts() { m_layouts.clear(); m_menuLayouts.clear(); m_presetsPaths.clear(); m_assignedLayouts.clear(); QDir layoutDir(QDir::homePath() + "/.config/latte"); QStringList filter; filter.append(QString("*.layout.latte")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); foreach (auto layout, files) { Layout layoutSets(this, layoutDir.absolutePath() + "/" + layout); QStringList validActivityIds = validActivities(layoutSets.activities()); layoutSets.setActivities(validActivityIds); foreach (auto activity, validActivityIds) { m_assignedLayouts[activity] = layoutSets.name(); } m_layouts.append(layoutSets.name()); if (layoutSets.showInMenu()) { m_menuLayouts.append(layoutSets.name()); } } m_presetsPaths.append(m_corona->kPackage().filePath("preset1")); m_presetsPaths.append(m_corona->kPackage().filePath("preset2")); m_presetsPaths.append(m_corona->kPackage().filePath("preset3")); m_presetsPaths.append(m_corona->kPackage().filePath("preset4")); emit layoutsChanged(); emit menuLayoutsChanged(); } void LayoutManager::loadLayoutOnStartup(QString layoutName) { // if (memoryUsage() == Dock::MultipleLayouts) { QStringList layouts = m_importer->checkRepairMultipleLayoutsLinkedFile(); //! Latte didn't close correctly, maybe a crash if (layouts.size() > 0) { QMessageBox *msg = new QMessageBox(); msg->setAttribute(Qt::WA_DeleteOnClose); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Multiple Layouts Warning")); msg->setText(i18n("Latte did not close properly in the previous session. The following layout(s) [%0] were updated for consistency!!!").arg(layouts.join(","))); msg->setStandardButtons(QMessageBox::Ok); msg->open(); } //} switchToLayout(layoutName); } void LayoutManager::loadLatteLayout(QString layoutPath) { qDebug() << " -------------------------------------------------------------------- "; qDebug() << " -------------------------------------------------------------------- "; if (m_corona->containments().size() > 0) { qDebug() << "LOAD LATTE LAYOUT ::: There are still containments present !!!! :: " << m_corona->containments().size(); } if (!layoutPath.isEmpty() && m_corona->containments().size() == 0) { cleanupOnStartup(layoutPath); qDebug() << "LOADING CORONA LAYOUT:" << layoutPath; m_corona->loadLayout(layoutPath); //! ~~~ ADDING DOCKVIEWS AND ENFORCE LOADING IF TASKS ARENT PRESENT BASED ON SCREENS ~~~ !// //! this is used to record the first dock having tasks in it. It is used //! to specify which dock will be loaded on startup if a case that no "dock //! with tasks" will be loaded otherwise. Currently the older one dock wins /*int firstContainmentWithTasks = -1; //! this is used to check if a dock with tasks in it will be loaded on startup bool tasksWillBeLoaded = heuresticForLoadingDockWithTasks(&firstContainmentWithTasks); qDebug() << "TASKS WILL BE PRESENT AFTER LOADING ::: " << tasksWillBeLoaded; foreach (auto containment, m_corona->containments()) { //! forceDockLoading is used when a latte configuration based on the //! current running screens does not provide a dock containing tasks. //! in such case the lowest latte containment containing tasks is loaded //! and it forcefully becomes primary dock if (!tasksWillBeLoaded && firstContainmentWithTasks == containment->id()) { tasksWillBeLoaded = true; //this protects by loading more than one dock at startup addDock(containment, true); } else { addDock(containment); } }*/ } } void LayoutManager::cleanupOnStartup(QString path) { KSharedConfigPtr filePtr = KSharedConfig::openConfig(path); KConfigGroup actionGroups = KConfigGroup(filePtr, "ActionPlugins"); QStringList deprecatedActionGroup; foreach (auto actId, actionGroups.groupList()) { QString pluginId = actionGroups.group(actId).readEntry("RightButton;NoModifier", ""); if (pluginId == "org.kde.contextmenu") { deprecatedActionGroup << actId; } } foreach (auto pId, deprecatedActionGroup) { qDebug() << "!!!!!!!!!!!!!!!! !!!!!!!!!!!! !!!!!!! REMOVING :::: " << pId; actionGroups.group(pId).deleteGroup(); } KConfigGroup containmentGroups = KConfigGroup(filePtr, "Containments"); QStringList removeContaimentsList; foreach (auto cId, containmentGroups.groupList()) { QString pluginId = containmentGroups.group(cId).readEntry("plugin", ""); if (pluginId == "org.kde.desktopcontainment") { //!must remove ghost containments first removeContaimentsList << cId; } } foreach (auto cId, removeContaimentsList) { containmentGroups.group(cId).deleteGroup(); } actionGroups.sync(); containmentGroups.sync(); } void LayoutManager::showAboutDialog() { m_corona->aboutApplication(); } void LayoutManager::importLatteLayout(QString layoutPath) { //! This might not be needed as it is Layout responsibility } void LayoutManager::hideAllDocks() { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout()) { emit currentLayoutIsSwitching(layout->name()); } } } void LayoutManager::addLayout(Layout *layout) { if (!m_activeLayouts.contains(layout)) { m_activeLayouts.append(layout); layout->initToCorona(m_corona); connect(layout, &Layout::viewColorizerChanged, this, &LayoutManager::viewColorizerChanged); } } bool LayoutManager::switchToLayout(QString layoutName, int previousMemoryUsage) { if (m_activeLayouts.size() > 0 && currentLayoutName() == layoutName && previousMemoryUsage == -1) { return false; } //! First Check If that Layout is already present if (memoryUsage() == Dock::MultipleLayouts && previousMemoryUsage == -1) { Layout *layout = activeLayout(layoutName); if (layout) { QStringList appliedActivities = layout->appliedActivities(); QString nextActivity = !layout->lastUsedActivity().isEmpty() ? layout->lastUsedActivity() : appliedActivities[0]; //! it means we are at a foreign activity if (!appliedActivities.contains(m_corona->activitiesConsumer()->currentActivity())) { m_activitiesController->setCurrentActivity(nextActivity); return true; } } } //! When going from memory usage to different memory usage we first //! send the layouts that will be changed. This signal creates the //! nice animation that hides these docks/panels if (previousMemoryUsage != -1) { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout()) { emit currentLayoutIsSwitching(layout->name()); } } } QString lPath = layoutPath(layoutName); if (lPath.isEmpty() && layoutName == i18n("Alternative")) { lPath = newLayout(i18n("Alternative"), i18n("Default")); } if (!lPath.isEmpty()) { if (memoryUsage() == Dock::SingleLayout) { emit currentLayoutIsSwitching(currentLayoutName()); } else if (memoryUsage() == Dock::MultipleLayouts && layoutName != Layout::MultipleLayoutsName) { Layout toLayout(this, lPath); QStringList toActivities = toLayout.activities(); Layout *activeForOrphans{nullptr}; foreach (auto fromLayout, m_activeLayouts) { if (fromLayout->isOriginalLayout() && fromLayout->activities().isEmpty()) { activeForOrphans = fromLayout; break; } } if (toActivities.isEmpty() && activeForOrphans && (toLayout.name() != activeForOrphans->name())) { emit currentLayoutIsSwitching(activeForOrphans->name()); } } //! this code must be called asynchronously because it is called //! also from qml (Tasks plasmoid). This change fixes a very important //! crash when switching sessions through the Tasks plasmoid Context menu //! Latte was unstable and was crashing very often during changing //! sessions. QTimer::singleShot(350, [this, layoutName, lPath, previousMemoryUsage]() { qDebug() << layoutName << " - " << lPath; QString fixedLPath = lPath; QString fixedLayoutName = layoutName; bool initializingMultipleLayouts{false}; if (memoryUsage() == Dock::MultipleLayouts && !activeLayout(Layout::MultipleLayoutsName)) { initializingMultipleLayouts = true; } if (memoryUsage() == Dock::SingleLayout || initializingMultipleLayouts || previousMemoryUsage == Dock::MultipleLayouts) { while (!m_activeLayouts.isEmpty()) { Layout *layout = m_activeLayouts.at(0); m_activeLayouts.removeFirst(); if (layout->isOriginalLayout() && previousMemoryUsage == Dock::MultipleLayouts) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadDockViews(); if (layout->isOriginalLayout() && previousMemoryUsage == Dock::MultipleLayouts) { clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds(), true); } delete layout; } if (initializingMultipleLayouts) { fixedLayoutName = QString(Layout::MultipleLayoutsName); fixedLPath = layoutPath(fixedLayoutName); } Layout *newLayout = new Layout(this, fixedLPath, fixedLayoutName); addLayout(newLayout); loadLatteLayout(fixedLPath); emit activeLayoutsChanged(); } if (memoryUsage() == Dock::MultipleLayouts) { if (!initializingMultipleLayouts && !activeLayout(layoutName)) { //! When we are in Multiple Layouts Environment and the user activates //! a Layout that is assigned to specific activities but this //! layout isnt loaded (this means neither of its activities are running) //! is such case we just activate these Activities Layout layout(this, Importer::layoutFilePath(layoutName)); int i = 0; bool lastUsedActivityFound{false}; QString lastUsedActivity = layout.lastUsedActivity(); bool orphanedLayout = !layoutIsAssigned(layoutName); QStringList assignedActivities = orphanedLayout ? orphanedActivities() : layout.activities(); if (!orphanedLayout) { foreach (auto assignedActivity, assignedActivities) { //! Starting the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(i * 1000, [this, assignedActivity, lastUsedActivity]() { m_activitiesController->startActivity(assignedActivity); if (lastUsedActivity == assignedActivity) { m_activitiesController->setCurrentActivity(lastUsedActivity); } }); if (lastUsedActivity == assignedActivity) { lastUsedActivityFound = true; } i = i + 1; } } else { //! orphaned layout foreach (auto assignedActivity, assignedActivities) { if (lastUsedActivity == assignedActivity) { lastUsedActivityFound = true; } } if ((!lastUsedActivityFound && assignedActivities.count() == 0) || !assignedActivities.contains(m_corona->m_activityConsumer->currentActivity())) { //! Starting the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(1000, [this, lastUsedActivity, lastUsedActivityFound]() { m_activitiesController->startActivity(lastUsedActivity); m_activitiesController->setCurrentActivity(lastUsedActivity); }); } } if (orphanedLayout) { syncMultipleLayoutsToActivities(layoutName); } else if (!orphanedLayout && !lastUsedActivityFound) { m_activitiesController->setCurrentActivity(layout.activities()[0]); } } else { syncMultipleLayoutsToActivities(layoutName); } } m_corona->universalSettings()->setCurrentLayoutName(layoutName); if (!layoutIsAssigned(layoutName)) { m_corona->universalSettings()->setLastNonAssignedLayoutName(layoutName); } }); } else { qDebug() << "Layout : " << layoutName << " was not found..."; } return true; } void LayoutManager::syncMultipleLayoutsToActivities(QString layoutForOrphans) { qDebug() << " ---- --------- ------ syncMultipleLayoutsToActivities ------- "; qDebug() << " ---- --------- ------ ------------------------------- ------- "; QStringList layoutsToUnload; QStringList layoutsToLoad; layoutsToLoad << Layout::MultipleLayoutsName; bool allRunningActivitiesWillBeReserved{true}; if (layoutForOrphans.isEmpty() || m_assignedLayouts.values().contains(layoutForOrphans)) { layoutForOrphans = m_corona->universalSettings()->lastNonAssignedLayoutName(); } foreach (auto activity, runningActivities()) { if (!m_assignedLayouts[activity].isEmpty()) { if (!layoutsToLoad.contains(m_assignedLayouts[activity])) { layoutsToLoad.append(m_assignedLayouts[activity]); } } else { allRunningActivitiesWillBeReserved = false; } } foreach (auto layout, m_activeLayouts) { QString tempLayoutName; if (!layoutsToLoad.contains(layout->name()) && layout->name() != layoutForOrphans) { tempLayoutName = layout->name(); } else if (layout->activities().isEmpty() && allRunningActivitiesWillBeReserved) { //! in such case the layout for the orphaned must be unloaded tempLayoutName = layout->name(); } if (!tempLayoutName.isEmpty() && !layoutsToUnload.contains(tempLayoutName)) { layoutsToUnload << tempLayoutName; } } //! Unload no needed Layouts foreach (auto layoutName, layoutsToUnload) { if (layoutName != Layout::MultipleLayoutsName) { Layout *layout = activeLayout(layoutName); int posLayout = activeLayoutPos(layoutName); if (posLayout >= 0) { qDebug() << "REMOVING LAYOUT ::::: " << layoutName; m_activeLayouts.removeAt(posLayout); if (layout->isOriginalLayout()) { layout->syncToLayoutFile(true); } layout->unloadContainments(); layout->unloadDockViews(); clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); delete layout; } } } //! Add Layout for orphan activities if (!allRunningActivitiesWillBeReserved) { if (!activeLayout(layoutForOrphans)) { Layout *newLayout = new Layout(this, layoutPath(layoutForOrphans), layoutForOrphans); if (newLayout) { qDebug() << "ACTIVATING ORPHANED LAYOUT ::::: " << layoutForOrphans; addLayout(newLayout); newLayout->importToCorona(); } } } //! Add needed Layouts based on Activities foreach (auto layoutName, layoutsToLoad) { if (!activeLayout(layoutName)) { Layout *newLayout = new Layout(this, QString(layoutPath(layoutName)), layoutName); if (newLayout) { qDebug() << "ACTIVATING LAYOUT ::::: " << layoutName; addLayout(newLayout); newLayout->importToCorona(); if (newLayout->isOriginalLayout() && m_corona->universalSettings()->showInfoWindow()) { showInfoWindow(i18n("Activating layout: %0 ...").arg(newLayout->name()), 5000, newLayout->appliedActivities()); } } } } updateCurrentLayoutNameInMultiEnvironment(); emit activeLayoutsChanged(); } void LayoutManager::pauseLayout(QString layoutName) { if (memoryUsage() == Dock::MultipleLayouts) { Layout *layout = activeLayout(layoutName); if (layout && !layout->activities().isEmpty()) { int i = 0; foreach (auto activityId, layout->activities()) { //! Stopping the activities must be done asynchronous because otherwise //! the activity manager cant close multiple activities QTimer::singleShot(i * 1000, [this, activityId]() { m_activitiesController->stopActivity(activityId); }); i = i + 1; } } } } void LayoutManager::syncActiveLayoutsToOriginalFiles() { if (memoryUsage() == Dock::MultipleLayouts) { foreach (auto layout, m_activeLayouts) { if (layout->isOriginalLayout()) { layout->syncToLayoutFile(); } } } } void LayoutManager::clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks) { if (!m_corona || (memoryUsage() == Dock::SingleLayout && !bypassChecks)) { return; } auto containments = m_corona->config()->group("Containments"); foreach (auto conId, containmentsIds) { qDebug() << "unloads ::: " << conId; KConfigGroup containment = containments.group(conId); containment.deleteGroup(); } containments.sync(); } void LayoutManager::syncDockViewsToScreens() { foreach (auto layout, m_activeLayouts) { layout->syncDockViewsToScreens(); } } QString LayoutManager::newLayout(QString layoutName, QString preset) { QDir layoutDir(QDir::homePath() + "/.config/latte"); QStringList filter; filter.append(QString(layoutName + "*.layout.latte")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); //! if the newLayout already exists provide a newName that doesn't if (files.count() >= 1) { int newCounter = files.count() + 1; layoutName = layoutName + "-" + QString::number(newCounter); } QString newLayoutPath = layoutDir.absolutePath() + "/" + layoutName + ".layout.latte"; qDebug() << "adding layout : " << layoutName << " based on preset:" << preset; if (preset == i18n("Default") && !QFile(newLayoutPath).exists()) { qDebug() << "adding layout : succeed"; QFile(m_corona->kPackage().filePath("preset1")).copy(newLayoutPath); } return newLayoutPath; } //! This function figures in the beginning if a dock with tasks //! in it will be loaded taking into account also the screens are present. bool LayoutManager::heuresticForLoadingDockWithTasks(int *firstContainmentWithTasks) { foreach (auto containment, m_corona->containments()) { QString plugin = containment->pluginMetaData().pluginId(); if (plugin == "org.kde.latte.containment") { bool onPrimary = containment->config().readEntry("onPrimary", true); int lastScreen = containment->lastScreen(); qDebug() << "containment values: " << onPrimary << " - " << lastScreen; bool containsTasks = false; foreach (auto applet, containment->applets()) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { containsTasks = true; break; } } if (containsTasks) { *firstContainmentWithTasks = containment->id(); if (onPrimary) { return true; } else { if (lastScreen >= 0) { QString connector = m_corona->screenPool()->connector(lastScreen); foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == connector) { return true; break; } } } } } } } return false; } void LayoutManager::importDefaultLayout(bool newInstanceIfPresent) { importPreset(1, newInstanceIfPresent); if (newInstanceIfPresent) { loadLayouts(); } } void LayoutManager::importPresets(bool includeDefault) { int start = 1; if (!includeDefault) { start = 2; } for (int i = start; i <= 4; ++i) { importPreset(i, false); } } void LayoutManager::importPreset(int presetNo, bool newInstanceIfPresent) { QByteArray presetNameOrig = QString("preset" + QString::number(presetNo)).toUtf8(); QString presetPath = m_corona->kPackage().filePath(presetNameOrig); QString presetName = Layout::layoutName(presetPath); QByteArray presetNameChars = presetName.toUtf8(); presetName = i18n(presetNameChars); //! hide the multiple layouts layout file from user presetName = (presetNo == MultipleLayoutsPresetId) ? "." + presetName : presetName; QString newLayoutFile = ""; if (newInstanceIfPresent) { newLayoutFile = QDir::homePath() + "/.config/latte/" + m_importer->uniqueLayoutName(presetName) + ".layout.latte"; } else { newLayoutFile = QDir::homePath() + "/.config/latte/" + presetName + ".layout.latte"; } if (!QFile(newLayoutFile).exists()) { QFile(presetPath).copy(newLayoutFile); QFileInfo newFileInfo(newLayoutFile); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(newLayoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } } } QStringList LayoutManager::validActivities(QStringList currentList) { QStringList validIds; foreach (auto activity, currentList) { if (activities().contains(activity)) { validIds.append(activity); } } return validIds; } bool LayoutManager::layoutIsAssigned(QString layoutName) { QHashIterator i(m_assignedLayouts); while (i.hasNext()) { i.next(); if (i.value() == layoutName) { return true; } } return false; } void LayoutManager::showLatteSettingsDialog(int page) { if (!m_latteSettingsDialog) { m_latteSettingsDialog = new SettingsDialog(nullptr, m_corona); } m_latteSettingsDialog->show(); if (m_latteSettingsDialog->isMinimized()) { m_latteSettingsDialog->showNormal(); } Dock::LatteConfigPage configPage = static_cast(page); m_latteSettingsDialog->setCurrentPage(configPage); m_latteSettingsDialog->activateWindow(); } void LayoutManager::hideLatteSettingsDialog() { if (m_latteSettingsDialog) { m_latteSettingsDialog->deleteLater(); m_latteSettingsDialog = nullptr; } } void LayoutManager::showInfoWindow(QString info, int duration, QStringList activities) { foreach (auto screen, qGuiApp->screens()) { InfoView *infoView = new InfoView(m_corona, info, screen); infoView->show(); infoView->setOnActivities(activities); QTimer::singleShot(duration, [this, infoView]() { infoView->deleteLater(); }); } } //! it is used just in order to provide translations for the presets void LayoutManager::ghostForTranslatedPresets() { QString preset1 = i18n("Default"); QString preset2 = i18n("Plasma"); QString preset3 = i18n("Unity"); QString preset4 = i18n("Extended"); } } diff --git a/app/layoutmanager.h b/app/layoutmanager.h index e94aa132..2ea4739f 100644 --- a/app/layoutmanager.h +++ b/app/layoutmanager.h @@ -1,202 +1,202 @@ /* * Copyright 2017 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 LAYOUTMANAGER_H #define LAYOUTMANAGER_H // local #include "launcherssignals.h" #include "settings/settingsdialog.h" // Qt #include #include #include // KDE #include namespace Plasma { class Containment; class Types; } namespace KActivities { class Controller; } namespace Latte { class DockCorona; -class DockView; class Importer; class Layout; class LaunchersSignals; +class View; } namespace Latte { //! This class is responsible to manipulate all layouts. //! add,remove,rename, update configurations etc. class LayoutManager : public QObject { Q_OBJECT Q_PROPERTY(QString currentLayoutName READ currentLayoutName NOTIFY currentLayoutNameChanged) Q_PROPERTY(QStringList layouts READ layouts NOTIFY layoutsChanged) Q_PROPERTY(QStringList menuLayouts READ menuLayouts NOTIFY menuLayoutsChanged) Q_PROPERTY(LaunchersSignals *launchersSignals READ launchersSignals NOTIFY launchersSignalsChanged) public: LayoutManager(QObject *parent = nullptr); ~LayoutManager() override; DockCorona *corona(); Importer *importer(); void load(); void loadLayoutOnStartup(QString layoutName); void unload(); void addDock(Plasma::Containment *containment, bool forceLoading = false, int expDockScreen = -1); void hideAllDocks(); void pauseLayout(QString layoutName); void syncDockViewsToScreens(); void syncActiveLayoutsToOriginalFiles(); - bool dockViewExists(DockView *view) const; + bool dockViewExists(Latte::View *view) const; bool hasColorizer() const; bool layoutExists(QString layoutName) const; QString shouldSwitchToLayout(QString activityId); QString currentLayoutName() const; QString defaultLayoutName() const; QStringList layouts() const; QStringList menuLayouts() const; QStringList presetsPaths() const; Dock::LayoutsMemoryUsage memoryUsage() const; void setMemoryUsage(Dock::LayoutsMemoryUsage memoryUsage); - QHash *currentDockViews() const; - QHash *layoutDockViews(const QString &layoutName) const; + QHash *currentDockViews() const; + QHash *layoutDockViews(const QString &layoutName) const; //! returns an active layout with that #id (name), it returns null if such //! layout cant be found Layout *activeLayout(QString id) const; int activeLayoutPos(QString id) const; LaunchersSignals *launchersSignals(); QStringList activities(); QStringList runningActivities(); QStringList orphanedActivities(); //! These are activities that havent been assigned to specific layout void importDefaultLayout(bool newInstanceIfPresent = false); void importPresets(bool includeDefault = false); public slots: void showAboutDialog(); void hideLatteSettingsDialog(); Q_INVOKABLE void showLatteSettingsDialog(int page = Latte::Dock::LayoutPage); //! switch to specified layout, default previousMemoryUsage means that it didn't change Q_INVOKABLE bool switchToLayout(QString layoutName, int previousMemoryUsage = -1); Q_INVOKABLE int layoutsMemoryUsage(); //! creates a new layout with layoutName based on the preset Q_INVOKABLE QString newLayout(QString layoutName, QString preset = i18n("Default")); Q_INVOKABLE QStringList activeLayoutsNames(); signals: void activeLayoutsChanged(); void currentLayoutChanged(); void currentLayoutNameChanged(); void launchersSignalsChanged(); void layoutsChanged(); void menuLayoutsChanged(); void viewColorizerChanged(); void currentLayoutIsSwitching(QString layoutName); private slots: void currentActivityChanged(const QString &id); void showInfoWindowChanged(); void syncMultipleLayoutsToActivities(QString layoutForOrphans = QString()); private: void addLayout(Layout *layout); void cleanupOnStartup(QString path); //!remove deprecated or oldstyle config options void clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks = false); void confirmDynamicSwitch(); //! it is used just in order to provide translations for the presets void ghostForTranslatedPresets(); //! This function figures in the beginning if a dock with tasks //! in it will be loaded taking into account also the screens are present. //! returns true if it will be loaded, false otherwise //! firstContainmentWithTasks = the first containment containing a taskmanager plasmoid bool heuresticForLoadingDockWithTasks(int *firstContainmentWithTasks); void importLatteLayout(QString layoutPath); void importPreset(int presetNo, bool newInstanceIfPresent = false); void loadLatteLayout(QString layoutPath); void loadLayouts(); void setMenuLayouts(QStringList layouts); void showInfoWindow(QString info, int duration, QStringList activities = {"0"}); void updateCurrentLayoutNameInMultiEnvironment(); bool layoutIsAssigned(QString layoutName); QString layoutPath(QString layoutName); QStringList validActivities(QStringList currentList); private: QString m_currentLayoutNameInMultiEnvironment; QString m_shouldSwitchToLayout; QStringList m_layouts; QStringList m_menuLayouts; QStringList m_presetsPaths; QHash m_assignedLayouts; QTimer m_dynamicSwitchTimer; QPointer m_latteSettingsDialog; DockCorona *m_corona{nullptr}; Importer *m_importer{nullptr}; LaunchersSignals *m_launchersSignals{nullptr}; QList m_activeLayouts; KActivities::Controller *m_activitiesController; friend class SettingsDialog; }; } #endif // LAYOUTMANAGER_H diff --git a/app/plasmathemeextended.cpp b/app/plasmathemeextended.cpp index 841bd41f..2ef36c2b 100644 --- a/app/plasmathemeextended.cpp +++ b/app/plasmathemeextended.cpp @@ -1,351 +1,351 @@ /* * 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 "plasmathemeextended.h" // local #include "commontools.h" #include "dockcorona.h" #include "schemecolors.h" -#include "dock/panelshadows_p.h" +#include "view/panelshadows_p.h" // Qt #include #include // KDE #include #include #include #define REVERSEDCOLORSCHEME "reversed.colors" namespace Latte { PlasmaThemeExtended::PlasmaThemeExtended(KSharedConfig::Ptr config, QObject *parent) : QObject(parent), m_themeGroup(KConfigGroup(config, QStringLiteral("PlasmaThemeExtended"))) { m_corona = qobject_cast(parent); loadConfig(); connect(&m_theme, &Plasma::Theme::themeChanged, this, &PlasmaThemeExtended::hasShadowChanged); connect(&m_theme, &Plasma::Theme::themeChanged, this, &PlasmaThemeExtended::load); connect(&m_theme, &Plasma::Theme::themeChanged, this, &PlasmaThemeExtended::themeChanged); } void PlasmaThemeExtended::load() { loadThemePaths(); loadRoundness(); } PlasmaThemeExtended::~PlasmaThemeExtended() { saveConfig(); m_normalScheme->deleteLater(); m_reversedScheme->deleteLater(); } bool PlasmaThemeExtended::hasShadow() const { return PanelShadows::self()->enabled(); } int PlasmaThemeExtended::bottomEdgeRoundness() const { return (themeHasExtendedInfo() ? m_bottomEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::leftEdgeRoundness() const { return (themeHasExtendedInfo() ? m_leftEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::topEdgeRoundness() const { return (themeHasExtendedInfo() ? m_topEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::rightEdgeRoundness() const { return (themeHasExtendedInfo() ? m_rightEdgeRoundness : userThemeRoundness()); } int PlasmaThemeExtended::userThemeRoundness() const { return m_userRoundness; } void PlasmaThemeExtended::setUserThemeRoundness(int roundness) { if (m_userRoundness == roundness) { return; } m_userRoundness = roundness; if (!themeHasExtendedInfo()) { emit roundnessChanged(); } saveConfig(); } bool PlasmaThemeExtended::themeHasExtendedInfo() const { return m_themeHasExtendedInfo; } SchemeColors *PlasmaThemeExtended::lightTheme() const { return m_isLightTheme ? m_normalScheme : m_reversedScheme; } SchemeColors *PlasmaThemeExtended::darkTheme() const { return !m_isLightTheme ? m_normalScheme : m_reversedScheme; } void PlasmaThemeExtended::setNormalSchemeFile(const QString &file) { if (m_normalSchemePath == file) { return; } m_normalSchemePath = file; if (m_normalScheme) { disconnect(m_normalScheme, &SchemeColors::colorsChanged, this, &PlasmaThemeExtended::loadThemeLightness); m_normalScheme->deleteLater(); } m_normalScheme = new SchemeColors(this, m_normalSchemePath, true); connect(m_normalScheme, &SchemeColors::colorsChanged, this, &PlasmaThemeExtended::loadThemeLightness); qDebug() << "plasma theme normal colors ::: " << m_normalSchemePath; updateReversedScheme(); loadThemeLightness(); emit themeChanged(); } void PlasmaThemeExtended::updateReversedScheme() { QString reversedFilePath = m_extendedThemeDir.path() + "/" + REVERSEDCOLORSCHEME; QFile(m_normalSchemePath).copy(reversedFilePath); m_reversedSchemePath = reversedFilePath; updateReversedSchemeValues(); if (m_reversedScheme) { m_reversedScheme->deleteLater(); } m_reversedScheme = new SchemeColors(this, m_reversedSchemePath, true); qDebug() << "plasma theme reversed colors ::: " << m_reversedSchemePath; } void PlasmaThemeExtended::updateReversedSchemeValues() { //! reverse values based on original scheme KSharedConfigPtr normalPtr = KSharedConfig::openConfig(m_normalSchemePath); KSharedConfigPtr reversedPtr = KSharedConfig::openConfig(m_reversedSchemePath); if (normalPtr && reversedPtr) { foreach (auto groupName, reversedPtr->groupList()) { if (groupName != "Colors:Button") { KConfigGroup reversedGroup(reversedPtr, groupName); if (reversedGroup.keyList().contains("BackgroundNormal") && reversedGroup.keyList().contains("ForegroundNormal")) { //! reverse usual text/background values KConfigGroup normalGroup(normalPtr, groupName); reversedGroup.writeEntry("BackgroundNormal", normalGroup.readEntry("ForegroundNormal", QColor())); reversedGroup.writeEntry("ForegroundNormal", normalGroup.readEntry("BackgroundNormal", QColor())); reversedGroup.sync(); } } } //! update WM group KConfigGroup reversedGroup(reversedPtr, "WM"); if (reversedGroup.keyList().contains("activeBackground") && reversedGroup.keyList().contains("activeForeground") && reversedGroup.keyList().contains("inactiveBackground") && reversedGroup.keyList().contains("inactiveForeground")) { //! reverse usual wm titlebar values KConfigGroup normalGroup(normalPtr, "WM"); reversedGroup.writeEntry("activeBackground", normalGroup.readEntry("activeForeground", QColor())); reversedGroup.writeEntry("activeForeground", normalGroup.readEntry("activeBackground", QColor())); reversedGroup.writeEntry("inactiveBackground", normalGroup.readEntry("inactiveForeground", QColor())); reversedGroup.writeEntry("inactiveForeground", normalGroup.readEntry("inactiveBackground", QColor())); reversedGroup.sync(); } if (reversedGroup.keyList().contains("activeBlend") && reversedGroup.keyList().contains("inactiveBlend")) { KConfigGroup normalGroup(normalPtr, "WM"); reversedGroup.writeEntry("activeBlend", normalGroup.readEntry("inactiveBlend", QColor())); reversedGroup.writeEntry("inactiveBlend", normalGroup.readEntry("activeBlend", QColor())); reversedGroup.sync(); } //! update scheme name QString normalSchemeName = SchemeColors::schemeName(m_normalSchemePath); KConfigGroup generalGroup(reversedPtr, "General"); generalGroup.writeEntry("Name", normalSchemeName + "_reversed"); generalGroup.sync(); } } void PlasmaThemeExtended::loadRoundness() { if (!m_corona) { return; } QString extendedInfoFilePath = m_corona->kPackage().filePath("themesExtendedInfo"); KSharedConfigPtr extInfoPtr = KSharedConfig::openConfig(extendedInfoFilePath); KConfigGroup roundGroup(extInfoPtr, "Roundness"); m_themeHasExtendedInfo = false; foreach (auto key, roundGroup.keyList()) { if (m_theme.themeName().toUpper().startsWith(key.toUpper())) { QStringList rs = roundGroup.readEntry(key, QStringList()); qDebug() << "roundness ::: " << rs; if (rs.size() > 0) { m_themeHasExtendedInfo = true; if (rs.size() <= 3) { //assign same roundness for all edges m_bottomEdgeRoundness = rs[0].toInt(); m_leftEdgeRoundness = m_bottomEdgeRoundness; m_topEdgeRoundness = m_bottomEdgeRoundness; m_rightEdgeRoundness = m_bottomEdgeRoundness; } else if (rs.size() >= 4) { m_bottomEdgeRoundness = rs[0].toInt(); m_leftEdgeRoundness = rs[1].toInt(); m_topEdgeRoundness = rs[2].toInt(); m_rightEdgeRoundness = rs[3].toInt(); } } break; } } emit roundnessChanged(); } void PlasmaThemeExtended::loadThemePaths() { m_themePath = ""; QString localD = QDir::homePath() + "/.local/share/plasma/desktoptheme/" + m_theme.themeName(); QString globalD = "/usr/share/plasma/desktoptheme/" + m_theme.themeName(); if (QDir(localD).exists()) { m_themePath = localD; } else if (QDir(globalD).exists()) { m_themePath = globalD; } qDebug() << "current plasma theme ::: " << m_theme.themeName(); qDebug() << "theme path ::: " << m_themePath; //! clear kde connections for (auto &c : m_kdeConnections) { disconnect(c); } //! assign color schemes QString themeColorScheme = m_themePath + "/colors"; if (QFileInfo(themeColorScheme).exists()) { setNormalSchemeFile(themeColorScheme); } else { //! when plasma theme uses the kde colors //! we track when kde color scheme is changing QString kdeSettingsFile = QDir::homePath() + "/.config/kdeglobals"; KDirWatch::self()->addFile(kdeSettingsFile); m_kdeConnections[0] = connect(KDirWatch::self(), &KDirWatch::dirty, this, [ &, kdeSettingsFile](const QString & path) { if (path == kdeSettingsFile) { this->setNormalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } }); m_kdeConnections[1] = connect(KDirWatch::self(), &KDirWatch::created, this, [ &, kdeSettingsFile](const QString & path) { if (path == kdeSettingsFile) { this->setNormalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } }); setNormalSchemeFile(SchemeColors::possibleSchemeFile("kdeglobals")); } } void PlasmaThemeExtended::loadThemeLightness() { float textColorLum = Latte::colorLumina(m_normalScheme->textColor()); float backColorLum = Latte::colorLumina(m_normalScheme->backgroundColor()); if (backColorLum > textColorLum) { m_isLightTheme = true; } else { m_isLightTheme = false; } if (m_isLightTheme) { qDebug() << "Plasma theme is light..."; } else { qDebug() << "Plasma theme is dark..."; } } void PlasmaThemeExtended::loadConfig() { m_userRoundness = m_themeGroup.readEntry("userSetPlasmaThemeRoundness", 0); } void PlasmaThemeExtended::saveConfig() { m_themeGroup.writeEntry("userSetPlasmaThemeRoundness", m_userRoundness); m_themeGroup.sync(); } } diff --git a/app/dock/contextmenu.cpp b/app/view/contextmenu.cpp similarity index 99% rename from app/dock/contextmenu.cpp rename to app/view/contextmenu.cpp index 4837ae9f..657fc737 100644 --- a/app/dock/contextmenu.cpp +++ b/app/view/contextmenu.cpp @@ -1,472 +1,472 @@ /* * 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 "contextmenu.h" // local -#include "dockview.h" +#include "view.h" #include "visibilitymanager.h" #include "../dockcorona.h" #include "../layoutmanager.h" // Qt #include #include // KDE #include #include #include // Plasma #include #include #include #include #include namespace Latte { namespace ViewPart { -ContextMenu::ContextMenu(Latte::DockView *view) : +ContextMenu::ContextMenu(Latte::View *view) : QObject(view), m_dockView(view) { } ContextMenu::~ContextMenu() { } QMenu *ContextMenu::menu() { return m_contextMenu; } void ContextMenu::menuAboutToHide() { if (!m_dockView) { return; } m_contextMenu = 0; if (!m_dockView->containment()->isUserConfiguring()) { m_dockView->visibility()->setBlockHiding(false); } emit menuChanged(); } bool ContextMenu::mousePressEvent(QMouseEvent *event) { //qDebug() << "Step -1 ..."; if (!event || !m_dockView->containment()) { return false; } //qDebug() << "Step 0..."; //even if the menu is executed synchronously, other events may be processed //by the qml incubator when plasma is loading, so we need to guard there if (m_contextMenu) { //qDebug() << "Step 0.5 ..."; m_contextMenu->close(); m_contextMenu = 0; emit menuChanged(); // PlasmaQuick::ContainmentView::mousePressEvent(event); return false; } //qDebug() << "1 ..."; QString trigger = Plasma::ContainmentActions::eventToString(event); if (trigger == "RightButton;NoModifier") { Plasma::ContainmentActions *plugin = m_dockView->containment()->containmentActions().value(trigger); if (!plugin || plugin->contextualActions().isEmpty()) { event->setAccepted(false); return false; } //qDebug() << "2 ..."; //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data /*if (plugin->contextualActions().length() == 1) { QAction *action = plugin->contextualActions().at(0); action->setData(event->pos()); action->trigger(); event->accept(); return; }*/ //FIXME: very inefficient appletAt() implementation Plasma::Applet *applet = 0; bool inSystray = false; //! initialize the appletContainsMethod on the first right click if (!m_appletContainsMethod.isValid()) { updateAppletContainsMethod(); } foreach (Plasma::Applet *appletTemp, m_dockView->containment()->applets()) { PlasmaQuick::AppletQuickItem *ai = appletTemp->property("_plasma_graphicObject").value(); bool appletContainsMouse = false; if (m_appletContainsMethod.isValid()) { QVariant retVal; m_appletContainsMethod.invoke(m_appletContainsMethodItem, Qt::DirectConnection, Q_RETURN_ARG(QVariant, retVal) , Q_ARG(QVariant, appletTemp->id()), Q_ARG(QVariant, event->pos())); appletContainsMouse = retVal.toBool(); } else { appletContainsMouse = ai->contains(ai->mapFromItem(m_dockView->contentItem(), event->pos())); } if (ai && ai->isVisible() && appletContainsMouse) { applet = ai->applet(); KPluginMetaData meta = applet->kPackage().metadata(); //Try to find applets inside a systray if (meta.pluginId() == "org.kde.plasma.systemtray" || meta.pluginId() == "org.nomad.systemtray") { auto systrayId = applet->config().readEntry("SystrayContainmentId"); applet = 0; inSystray = true; Plasma::Containment *cont = containmentById(systrayId.toInt()); if (cont) { foreach (Plasma::Applet *appletCont, cont->applets()) { PlasmaQuick::AppletQuickItem *ai2 = appletCont->property("_plasma_graphicObject").value(); if (ai2 && ai2->isVisible() && ai2->contains(ai2->mapFromItem(m_dockView->contentItem(), event->pos()))) { applet = ai2->applet(); break; } } } break; } else { ai = 0; } } } if (!applet && !inSystray) { applet = m_dockView->containment(); } //qDebug() << "3 ..."; if (applet) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); //qDebug() << "3.5 ..."; if (!provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { //qDebug() << "4..."; QMenu *desktopMenu = new QMenu; //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute desktopMenu->setAttribute(Qt::WA_TranslucentBackground); //end workaround if (desktopMenu->winId()) { desktopMenu->windowHandle()->setTransientParent(m_dockView); } desktopMenu->setAttribute(Qt::WA_DeleteOnClose); m_contextMenu = desktopMenu; //! deprecated old code that can be removed if the following plasma approach doesn't //! create any issues with context menu creation in Latte /*if (m_dockView->mouseGrabberItem()) { //workaround, this fixes for me most of the right click menu behavior m_dockView->mouseGrabberItem()->ungrabMouse(); return; }*/ //!plasma official code //this is a workaround where Qt will fail to realise a mouse has been released // this happens if a window which does not accept focus spawns a new window that takes focus and X grab // whilst the mouse is depressed // https://bugreports.qt.io/browse/QTBUG-59044 // this causes the next click to go missing //by releasing manually we avoid that situation auto ungrabMouseHack = [this]() { if (m_dockView->mouseGrabberItem()) { m_dockView->mouseGrabberItem()->ungrabMouse(); } }; //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() if (QVersionNumber::fromString(qVersion()) > QVersionNumber(5, 8, 0)) { QTimer::singleShot(0, this, ungrabMouseHack); } else { ungrabMouseHack(); } //end workaround //!end of plasma official code(workaround) //qDebug() << "5 ..."; if (applet && applet != m_dockView->containment()) { //qDebug() << "5.3 ..."; emit applet->contextualActionsAboutToShow(); addAppletActions(desktopMenu, applet, event); } else { //qDebug() << "5.6 ..."; emit m_dockView->containment()->contextualActionsAboutToShow(); addContainmentActions(desktopMenu, event); } //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute desktopMenu->setAttribute(Qt::WA_TranslucentBackground); //end workaround QPoint pos = event->globalPos(); if (applet) { //qDebug() << "6 ..."; desktopMenu->adjustSize(); if (m_dockView->screen()) { const QRect scr = m_dockView->screen()->geometry(); int smallStep = 3; int x = event->globalPos().x() + smallStep; int y = event->globalPos().y() + smallStep; //qDebug()<globalPos().x() > scr.center().x()) { x = event->globalPos().x() - desktopMenu->width() - smallStep; } if (event->globalPos().y() > scr.center().y()) { y = event->globalPos().y() - desktopMenu->height() - smallStep; } pos = QPoint(x, y); } } //qDebug() << "7..."; if (desktopMenu->isEmpty()) { //qDebug() << "7.5 ..."; delete desktopMenu; event->accept(); return false; } connect(desktopMenu, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); m_dockView->visibility()->setBlockHiding(true); desktopMenu->popup(pos); event->setAccepted(true); emit menuChanged(); return false; } //qDebug() << "8 ..."; } //qDebug() << "9 ..."; } //qDebug() << "10 ..."; emit menuChanged(); return true; // PlasmaQuick::ContainmentView::mousePressEvent(event); } //! update the appletContainsPos method from Panel view void ContextMenu::updateAppletContainsMethod() { for (QQuickItem *item : m_dockView->contentItem()->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("appletContainsPos(QVariant,QVariant)"); if (methodIndex == -1) { continue; } m_appletContainsMethod = metaObject->method(methodIndex); m_appletContainsMethodItem = item; } } } void ContextMenu::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) { if (!m_dockView->containment()) { return; } foreach (QAction *action, applet->contextualActions()) { if (action) { desktopMenu->addAction(action); } } if (!applet->failedToLaunch()) { QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { desktopMenu->addAction(runAssociatedApplication); } QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); if (configureApplet && configureApplet->isEnabled()) { desktopMenu->addAction(configureApplet); } QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); if (appletAlternatives && appletAlternatives->isEnabled() && m_dockView->containment()->isUserConfiguring()) { desktopMenu->addAction(appletAlternatives); } } QAction *containmentAction = desktopMenu->menuAction(); containmentAction->setText(i18nc("%1 is the name of the containment", "%1 Options", m_dockView->containment()->title())); addContainmentActions(containmentAction->menu(), event); if (!containmentAction->menu()->isEmpty()) { int enabled = 0; //count number of real actions QListIterator actionsIt(containmentAction->menu()->actions()); while (enabled < 3 && actionsIt.hasNext()) { QAction *action = actionsIt.next(); if (action->isVisible() && !action->isSeparator()) { ++enabled; } } desktopMenu->addSeparator(); if (enabled) { //if there is only one, don't create a submenu // if (enabled < 2) { foreach (QAction *action, containmentAction->menu()->actions()) { if (action->isVisible()) { desktopMenu->addAction(action); } } // } else { // desktopMenu->addMenu(containmentMenu); // } } } if (m_dockView->containment()->immutability() == Plasma::Types::Mutable && (m_dockView->containment()->containmentType() != Plasma::Types::PanelContainment || m_dockView->containment()->isUserConfiguring())) { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); //qDebug() << "checking for removal" << closeApplet; if (closeApplet) { if (!desktopMenu->isEmpty()) { desktopMenu->addSeparator(); } //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); desktopMenu->addAction(closeApplet); } } } void ContextMenu::addContainmentActions(QMenu *desktopMenu, QEvent *event) { if (!m_dockView->containment()) { return; } if (m_dockView->containment()->corona()->immutability() != Plasma::Types::Mutable && !KAuthorized::authorizeAction(QStringLiteral("plasma/containment_actions"))) { //qDebug() << "immutability"; return; } //this is what ContainmentPrivate::prepareContainmentActions was const QString trigger = Plasma::ContainmentActions::eventToString(event); //"RightButton;NoModifier" Plasma::ContainmentActions *plugin = m_dockView->containment()->containmentActions().value(trigger); if (!plugin) { return; } if (plugin->containment() != m_dockView->containment()) { plugin->setContainment(m_dockView->containment()); // now configure it KConfigGroup cfg(m_dockView->containment()->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(m_dockView->containment()->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } QList actions = plugin->contextualActions(); foreach (auto act, actions) { if (act->menu()) { //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute act->menu()->setAttribute(Qt::WA_TranslucentBackground); //end workaround if (act->menu()->winId()) { act->menu()->windowHandle()->setTransientParent(m_dockView); } } } desktopMenu->addActions(actions); return; } Plasma::Containment *ContextMenu::containmentById(uint id) { foreach (auto containment, m_dockView->corona()->containments()) { if (id == containment->id()) { return containment; } } return 0; } } } diff --git a/app/dock/contextmenu.h b/app/view/contextmenu.h similarity index 93% rename from app/dock/contextmenu.h rename to app/view/contextmenu.h index 8b0e45b0..7c5903ae 100644 --- a/app/dock/contextmenu.h +++ b/app/view/contextmenu.h @@ -1,81 +1,81 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONTEXTMENU_H #define CONTEXTMENU_H // Qt #include #include #include #include #include #include namespace Plasma { class Applet; class Containment; } namespace Latte { -class DockView; +class View; } namespace Latte { namespace ViewPart { class ContextMenu : public QObject { Q_OBJECT public: - ContextMenu(Latte::DockView *view); + ContextMenu(Latte::View *view); ~ContextMenu() override; QMenu *menu(); bool mousePressEvent(QMouseEvent *event); signals: void menuChanged(); private slots: void menuAboutToHide(); private: void addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event); void addContainmentActions(QMenu *desktopMenu, QEvent *event); void updateAppletContainsMethod(); Plasma::Containment *containmentById(uint id); private: QMenu *m_contextMenu{nullptr}; QMetaMethod m_appletContainsMethod; QQuickItem *m_appletContainsMethodItem{nullptr}; - Latte::DockView *m_dockView; + Latte::View *m_dockView; - friend class Latte::DockView; + friend class Latte::View; }; } } #endif // DOCKMENUMANAGER_H diff --git a/app/dock/dockconfigview.cpp b/app/view/dockconfigview.cpp similarity index 98% rename from app/dock/dockconfigview.cpp rename to app/view/dockconfigview.cpp index 25ffba41..8f3aa7a2 100644 --- a/app/dock/dockconfigview.cpp +++ b/app/view/dockconfigview.cpp @@ -1,585 +1,585 @@ /* * 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 "dockconfigview.h" // local -#include "dockview.h" +#include "view.h" #include "panelshadows_p.h" #include "../dockcorona.h" #include "../layoutmanager.h" #include "../settings/universalsettings.h" #include "../wm/abstractwindowinterface.h" // Qt #include #include #include #include #include // KDE #include #include #include #include #include // Plasma #include namespace Latte { -DockConfigView::DockConfigView(Plasma::Containment *containment, DockView *dockView, QWindow *parent) +DockConfigView::DockConfigView(Plasma::Containment *containment, Latte::View *view, QWindow *parent) : PlasmaQuick::ConfigView(containment, parent), - m_dockView(dockView) + m_dockView(view) { m_corona = qobject_cast(m_dockView->containment()->corona()); setupWaylandIntegration(); setScreen(m_dockView->screen()); if (containment) { setIcon(qGuiApp->windowIcon()); } m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(100); connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { setScreen(m_dockView->screen()); setFlags(wFlags()); syncGeometry(); syncSlideEffect(); }); - connections << connect(dockView->visibility(), &VisibilityManager::modeChanged, this, &DockConfigView::syncGeometry); + connections << connect(m_dockView->visibility(), &VisibilityManager::modeChanged, this, &DockConfigView::syncGeometry); connections << connect(containment, &Plasma::Containment::immutabilityChanged, this, &DockConfigView::immutabilityChanged); m_thicknessSyncTimer.setSingleShot(true); m_thicknessSyncTimer.setInterval(200); connections << connect(&m_thicknessSyncTimer, &QTimer::timeout, this, [this]() { syncGeometry(); }); - connections << connect(dockView, &DockView::normalThicknessChanged, [&]() { + connections << connect(m_dockView, &Latte::View::normalThicknessChanged, [&]() { m_thicknessSyncTimer.start(); }); if (m_corona) { connections << connect(m_corona, SIGNAL(raiseDocksTemporaryChanged()), this, SIGNAL(raiseDocksTemporaryChanged())); } } DockConfigView::~DockConfigView() { qDebug() << "DockConfigView deleting ..."; deleteSecondaryWindow(); foreach (auto var, connections) { QObject::disconnect(var); } if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; } } void DockConfigView::init() { qDebug() << "dock config view : initialization started..."; setDefaultAlphaBuffer(true); setColor(Qt::transparent); PanelShadows::self()->addWindow(this); rootContext()->setContextProperty(QStringLiteral("dock"), m_dockView); rootContext()->setContextProperty(QStringLiteral("dockConfig"), this); if (m_corona) { rootContext()->setContextProperty(QStringLiteral("universalSettings"), m_corona->universalSettings()); rootContext()->setContextProperty(QStringLiteral("layoutManager"), m_corona->layoutManager()); } KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); kdeclarative.setupBindings(); QByteArray tempFilePath = "lattedockconfigurationui"; updateEnabledBorders(); auto source = QUrl::fromLocalFile(m_dockView->containment()->corona()->kPackage().filePath(tempFilePath)); setSource(source); syncGeometry(); syncSlideEffect(); qDebug() << "dock config view : initialization ended..."; } inline Qt::WindowFlags DockConfigView::wFlags() const { return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; } QWindow *DockConfigView::secondaryWindow() { return m_secConfigView; } void DockConfigView::setAdvanced(bool advanced) { if (m_advanced == advanced) { return; } m_advanced = advanced; if (m_advanced) { createSecondaryWindow(); } else { deleteSecondaryWindow(); } } void DockConfigView::createSecondaryWindow() { //! do not proceed when secondary window is already created //! or when main dock settings window has not updated yet //! its geometry if (m_secConfigView || geometryWhenVisible().isNull()) { return; } QRect geometry = m_dockView->screenGeometry(); m_secConfigView = new DockSecConfigView(m_dockView, this); m_secConfigView->init(); if (m_secConfigView->geometryWhenVisible().intersects(geometryWhenVisible())) { setShowInlineProperties(true); m_secConfigView->hideConfigWindow(); } else { if (!KWindowSystem::isPlatformWayland()) { QTimer::singleShot(150, m_secConfigView, SLOT(show())); } else { QTimer::singleShot(150, [this]() { m_secConfigView->setVisible(true); }); } setShowInlineProperties(false); } } void DockConfigView::deleteSecondaryWindow() { if (m_secConfigView) { m_secConfigView->deleteLater(); } } QRect DockConfigView::geometryWhenVisible() const { return m_geometryWhenVisible; } void DockConfigView::syncGeometry() { if (!m_dockView->managedLayout() || !m_dockView->containment() || !rootObject()) return; const QSize size(rootObject()->width(), rootObject()->height()); setMaximumSize(size); setMinimumSize(size); resize(size); const auto location = m_dockView->containment()->location(); const auto sGeometry = m_dockView->screenGeometry(); int clearThickness = m_dockView->normalThickness() + m_dockView->fontPixelSize(); QPoint position{0, 0}; switch (m_dockView->containment()->formFactor()) { case Plasma::Types::Horizontal: { if (location == Plasma::Types::TopEdge) { position = {sGeometry.center().x() - size.width() / 2 , sGeometry.y() + clearThickness }; } else if (location == Plasma::Types::BottomEdge) { position = {sGeometry.center().x() - size.width() / 2 , sGeometry.y() + sGeometry.height() - clearThickness - size.height() }; } } break; case Plasma::Types::Vertical: { if (location == Plasma::Types::LeftEdge) { position = {sGeometry.x() + clearThickness , sGeometry.center().y() - size.height() / 2 }; } else if (location == Plasma::Types::RightEdge) { position = {sGeometry.x() + sGeometry.width() - clearThickness - size.width() , sGeometry.center().y() - size.height() / 2 }; } } break; default: qWarning() << "no sync geometry, wrong formFactor"; break; } updateEnabledBorders(); m_geometryWhenVisible = QRect(position.x(), position.y(), size.width(), size.height()); setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } if (m_advanced) { //! consider even the secondary window can be create createSecondaryWindow(); } } void DockConfigView::syncSlideEffect() { if (!m_dockView->containment()) return; auto slideLocation = WindowSystem::Slide::None; switch (m_dockView->containment()->location()) { case Plasma::Types::TopEdge: slideLocation = WindowSystem::Slide::Top; break; case Plasma::Types::RightEdge: slideLocation = WindowSystem::Slide::Right; break; case Plasma::Types::BottomEdge: slideLocation = WindowSystem::Slide::Bottom; break; case Plasma::Types::LeftEdge: slideLocation = WindowSystem::Slide::Left; break; default: qDebug() << staticMetaObject.className() << "wrong location"; break; } m_corona->wm()->slideWindow(*this, slideLocation); } void DockConfigView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); m_corona->wm()->setDockExtraFlags(*this); setFlags(wFlags()); m_corona->wm()->enableBlurBehind(*this); syncGeometry(); syncSlideEffect(); if (m_dockView && m_dockView->containment()) m_dockView->containment()->setUserConfiguring(true); m_screenSyncTimer.start(); QTimer::singleShot(400, this, &DockConfigView::syncGeometry); emit showSignal(); } void DockConfigView::hideEvent(QHideEvent *ev) { if (!m_dockView) { QQuickWindow::hideEvent(ev); return; } if (m_dockView->containment()) m_dockView->containment()->setUserConfiguring(false); QQuickWindow::hideEvent(ev); const auto mode = m_dockView->visibility()->mode(); const auto previousDockWinBehavior = (m_dockView->flags() & Qt::BypassWindowManagerHint) ? false : true; if (mode == Dock::AlwaysVisible || mode == Dock::WindowsGoBelow) { if (!previousDockWinBehavior) { m_dockView->managedLayout()->recreateDock(m_dockView->containment()); } } else if (m_dockView->dockWinBehavior() != previousDockWinBehavior) { m_dockView->managedLayout()->recreateDock(m_dockView->containment()); } deleteLater(); } void DockConfigView::focusOutEvent(QFocusEvent *ev) { Q_UNUSED(ev); const auto *focusWindow = qGuiApp->focusWindow(); if (focusWindow && (focusWindow->flags().testFlag(Qt::Popup) || focusWindow->flags().testFlag(Qt::ToolTip))) return; if (!m_blockFocusLost && (!m_secConfigView || (m_secConfigView && !m_secConfigView->isActive()))) { hideConfigWindow(); } } void DockConfigView::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_dockView || !m_dockView->containment()) { // already setup return; } if (m_corona) { using namespace KWayland::Client; PlasmaShell *interface = m_corona->waylandDockCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland dock window surface was created..."; m_shellSurface = interface->createSurface(s, this); m_shellSurface->setSkipTaskbar(true); syncGeometry(); } } bool DockConfigView::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..."; PanelShadows::self()->removeWindow(this); } break; } } } return PlasmaQuick::ConfigView::event(e); } void DockConfigView::immutabilityChanged(Plasma::Types::ImmutabilityType type) { if (type != Plasma::Types::Mutable && isVisible()) hideConfigWindow(); } bool DockConfigView::sticker() const { return m_blockFocusLost; } void DockConfigView::setSticker(bool blockFocusLost) { if (m_blockFocusLost == blockFocusLost) return; m_blockFocusLost = blockFocusLost; } bool DockConfigView::showInlineProperties() const { return m_showInlineProperties; } void DockConfigView::setShowInlineProperties(bool show) { if (m_showInlineProperties == show) { return; } m_showInlineProperties = show; emit showInlinePropertiesChanged(); } void DockConfigView::addPanelSpacer() { if (m_dockView && m_dockView->containment()) { m_dockView->containment()->createApplet(QStringLiteral("org.kde.latte.spacer")); } } void DockConfigView::hideConfigWindow() { if (m_shellSurface) { //!NOTE: Avoid crash in wayland environment with qt5.9 close(); } else { hide(); } } void DockConfigView::updateLaunchersForGroup(int groupInt) { Dock::LaunchersGroup group = (Dock::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_dockView->managedLayout()) { if ((group == Dock::LayoutLaunchers && m_dockView->managedLayout()->launchers().isEmpty()) || (group == Dock::GlobalLaunchers && m_corona->universalSettings()->launchers().isEmpty())) { Plasma::Containment *c = m_dockView->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 == Dock::LayoutLaunchers) { m_dockView->managedLayout()->setLaunchers(launchers.toStringList()); } else if (group == Dock::GlobalLaunchers) { m_corona->universalSettings()->setLaunchers(launchers.toStringList()); } } } } } } } } } } //!BEGIN borders Plasma::FrameSvg::EnabledBorders DockConfigView::enabledBorders() const { return m_enabledBorders; } void DockConfigView::updateEnabledBorders() { if (!this->screen()) { return; } Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; switch (m_dockView->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; PanelShadows::self()->addWindow(this, m_enabledBorders); emit enabledBordersChanged(); } } //!END borders } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/dock/dockconfigview.h b/app/view/dockconfigview.h similarity index 95% rename from app/dock/dockconfigview.h rename to app/view/dockconfigview.h index 90085853..0a9b2b71 100644 --- a/app/dock/dockconfigview.h +++ b/app/view/dockconfigview.h @@ -1,143 +1,145 @@ /* * 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 DOCKCONFIGVIEW_H #define DOCKCONFIGVIEW_H // local #include "docksecconfigview.h" #include "../plasmaquick/configview.h" #include "../../liblattedock/dock.h" //Qt #include #include #include #include // Plasma #include #include namespace Plasma { class Applet; class Containment; class FrameSvg; class Types; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { - class DockCorona; -class DockView; +class View; +} + +namespace Latte { class DockConfigView : public PlasmaQuick::ConfigView { Q_OBJECT //! used when the secondary config window can not be shown Q_PROPERTY(bool showInlineProperties READ showInlineProperties WRITE setShowInlineProperties NOTIFY showInlinePropertiesChanged) Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged) public: enum ConfigViewType { PrimaryConfig = 0, SecondaryConfig }; - DockConfigView(Plasma::Containment *containment, DockView *dockView, QWindow *parent = nullptr); + DockConfigView(Plasma::Containment *containment, Latte::View *view, QWindow *parent = nullptr); ~DockConfigView() override; void init() override; Qt::WindowFlags wFlags() const; bool showInlineProperties() const; void setShowInlineProperties(bool show); bool sticker() const; QRect geometryWhenVisible() const; Plasma::FrameSvg::EnabledBorders enabledBorders() const; QWindow *secondaryWindow(); public slots: Q_INVOKABLE void addPanelSpacer(); Q_INVOKABLE void hideConfigWindow(); Q_INVOKABLE void setSticker(bool blockFocusLost); Q_INVOKABLE void syncGeometry(); Q_INVOKABLE void updateLaunchersForGroup(int groupInt); Q_INVOKABLE void setAdvanced(bool advanced); signals: void enabledBordersChanged(); void raiseDocksTemporaryChanged(); void showInlinePropertiesChanged(); void showSignal(); protected: void showEvent(QShowEvent *ev) override; void hideEvent(QHideEvent *ev) override; void focusOutEvent(QFocusEvent *ev) override; bool event(QEvent *e) override; void syncSlideEffect(); private slots: void immutabilityChanged(Plasma::Types::ImmutabilityType type); void updateEnabledBorders(); void createSecondaryWindow(); void deleteSecondaryWindow(); private: void setupWaylandIntegration(); bool m_advanced{false}; bool m_blockFocusLost{false}; bool m_blockFocusLostOnStartup{true}; bool m_inReverse{false}; //! it is used by the borders bool m_showInlineProperties{false}; QRect m_geometryWhenVisible; - QPointer m_dockView; + QPointer m_dockView; QPointer m_secConfigView; QTimer m_screenSyncTimer; QTimer m_thicknessSyncTimer; QList connections; Plasma::FrameSvg::EnabledBorders m_enabledBorders{Plasma::FrameSvg::AllBorders}; DockCorona *m_corona{nullptr}; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif //DOCKCONFIGVIEW_H // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/dock/docksecconfigview.cpp b/app/view/docksecconfigview.cpp similarity index 96% rename from app/dock/docksecconfigview.cpp rename to app/view/docksecconfigview.cpp index 57a367e3..9f99606b 100644 --- a/app/dock/docksecconfigview.cpp +++ b/app/view/docksecconfigview.cpp @@ -1,389 +1,389 @@ /* * 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 "docksecconfigview.h" // local #include "dockconfigview.h" -#include "dockview.h" #include "panelshadows_p.h" +#include "view.h" #include "../dockcorona.h" #include "../wm/abstractwindowinterface.h" // Qt #include #include #include #include #include // KDE #include #include #include #include #include // Plasma #include namespace Latte { -DockSecConfigView::DockSecConfigView(DockView *dockView, QWindow *parent) +DockSecConfigView::DockSecConfigView(Latte::View *view, QWindow *parent) : QQuickView(nullptr), m_parent(parent), - m_dockView(dockView) + m_dockView(view) { m_corona = qobject_cast(m_dockView->containment()->corona()); setupWaylandIntegration(); setResizeMode(QQuickView::SizeViewToRootObject); setScreen(m_dockView->screen()); - if (dockView && dockView->containment()) { + if (m_dockView && m_dockView->containment()) { setIcon(qGuiApp->windowIcon()); } m_screenSyncTimer.setSingleShot(true); m_screenSyncTimer.setInterval(100); connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { setScreen(m_dockView->screen()); setFlags(wFlags()); syncGeometry(); syncSlideEffect(); }); - connections << connect(dockView->visibility(), &VisibilityManager::modeChanged, this, &DockSecConfigView::syncGeometry); + connections << connect(m_dockView->visibility(), &VisibilityManager::modeChanged, this, &DockSecConfigView::syncGeometry); m_thicknessSyncTimer.setSingleShot(true); m_thicknessSyncTimer.setInterval(200); connections << connect(&m_thicknessSyncTimer, &QTimer::timeout, this, [this]() { syncGeometry(); }); - connections << connect(dockView, &DockView::normalThicknessChanged, [&]() { + connections << connect(m_dockView, &Latte::View::normalThicknessChanged, [&]() { m_thicknessSyncTimer.start(); }); } DockSecConfigView::~DockSecConfigView() { qDebug() << "SecDockConfigView deleting ..."; foreach (auto var, connections) { QObject::disconnect(var); } if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; } } void DockSecConfigView::init() { qDebug() << "dock secondary config view : initialization started..."; setDefaultAlphaBuffer(true); setColor(Qt::transparent); PanelShadows::self()->addWindow(this); rootContext()->setContextProperty(QStringLiteral("dock"), m_dockView); rootContext()->setContextProperty(QStringLiteral("dockConfig"), this); rootContext()->setContextProperty(QStringLiteral("plasmoid"), m_dockView->containment()->property("_plasma_graphicObject").value()); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); kdeclarative.setupBindings(); QByteArray tempFilePath = "lattedocksecondaryconfigurationui"; updateEnabledBorders(); auto source = QUrl::fromLocalFile(m_dockView->containment()->corona()->kPackage().filePath(tempFilePath)); setSource(source); syncGeometry(); syncSlideEffect(); m_parent->requestActivate(); qDebug() << "dock secondary config view : initialization ended..."; } inline Qt::WindowFlags DockSecConfigView::wFlags() const { return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; } QRect DockSecConfigView::geometryWhenVisible() const { return m_geometryWhenVisible; } void DockSecConfigView::syncGeometry() { if (!m_dockView->managedLayout() || !m_dockView->containment() || !rootObject()) return; const QSize size(rootObject()->width(), rootObject()->height()); setMaximumSize(size); setMinimumSize(size); resize(size); const auto location = m_dockView->containment()->location(); const auto sGeometry = m_dockView->screenGeometry(); int clearThickness = m_dockView->normalThickness() + m_dockView->fontPixelSize(); int secondaryConfigSpacing = 2 * m_dockView->fontPixelSize(); QPoint position{0, 0}; switch (m_dockView->containment()->formFactor()) { case Plasma::Types::Horizontal: { if (location == Plasma::Types::TopEdge) { int yPos = m_dockView->y() + clearThickness; position = {m_dockView->x() + secondaryConfigSpacing, yPos}; } else if (location == Plasma::Types::BottomEdge) { int yPos; yPos = sGeometry.y() + sGeometry.height() - clearThickness - size.height(); position = {m_dockView->x() + m_dockView->width() - secondaryConfigSpacing - size.width(), yPos}; } } break; case Plasma::Types::Vertical: { if (location == Plasma::Types::LeftEdge) { position = {sGeometry.x() + clearThickness , m_dockView->y() + secondaryConfigSpacing }; } else if (location == Plasma::Types::RightEdge) { position = {sGeometry.x() + sGeometry.width() - clearThickness - size.width() , m_dockView->y() + secondaryConfigSpacing }; } } break; default: qWarning() << "no sync geometry, wrong formFactor"; break; } updateEnabledBorders(); m_geometryWhenVisible = QRect(position.x(), position.y(), size.width(), size.height()); setPosition(position); if (m_shellSurface) { m_shellSurface->setPosition(position); } } void DockSecConfigView::syncSlideEffect() { if (!m_dockView->containment()) return; auto slideLocation = WindowSystem::Slide::None; switch (m_dockView->containment()->location()) { case Plasma::Types::TopEdge: slideLocation = WindowSystem::Slide::Top; break; case Plasma::Types::RightEdge: slideLocation = WindowSystem::Slide::Right; break; case Plasma::Types::BottomEdge: slideLocation = WindowSystem::Slide::Bottom; break; case Plasma::Types::LeftEdge: slideLocation = WindowSystem::Slide::Left; break; default: qDebug() << staticMetaObject.className() << "wrong location"; break; } m_corona->wm()->slideWindow(*this, slideLocation); } void DockSecConfigView::showEvent(QShowEvent *ev) { QQuickWindow::showEvent(ev); m_corona->wm()->setDockExtraFlags(*this); setFlags(wFlags()); m_corona->wm()->enableBlurBehind(*this); syncGeometry(); syncSlideEffect(); m_screenSyncTimer.start(); QTimer::singleShot(400, this, &DockSecConfigView::syncGeometry); emit showSignal(); } void DockSecConfigView::focusOutEvent(QFocusEvent *ev) { Q_UNUSED(ev); const auto *focusWindow = qGuiApp->focusWindow(); if (focusWindow && (focusWindow->flags().testFlag(Qt::Popup) || focusWindow->flags().testFlag(Qt::ToolTip))) return; const auto parent = qobject_cast(m_parent); if (parent && !parent->sticker() && !parent->isActive()) { parent->hideConfigWindow(); } } void DockSecConfigView::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_dockView || !m_dockView->containment()) { // already setup return; } if (m_corona) { using namespace KWayland::Client; PlasmaShell *interface = m_corona->waylandDockCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland dock window surface was created..."; m_shellSurface = interface->createSurface(s, this); m_shellSurface->setSkipTaskbar(true); syncGeometry(); } } bool DockSecConfigView::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 secondary config window surface was deleted..."; PanelShadows::self()->removeWindow(this); } break; } } } return QQuickView::event(e); } void DockSecConfigView::hideConfigWindow() { if (m_shellSurface) { //!NOTE: Avoid crash in wayland environment with qt5.9 close(); } else { hide(); } } //!BEGIN borders Plasma::FrameSvg::EnabledBorders DockSecConfigView::enabledBorders() const { return m_enabledBorders; } void DockSecConfigView::updateEnabledBorders() { if (!this->screen()) { return; } Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; switch (m_dockView->location()) { case Plasma::Types::TopEdge: borders &= ~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 &= ~Plasma::FrameSvg::BottomBorder; break; default: break; } if (m_enabledBorders != borders) { m_enabledBorders = borders; PanelShadows::self()->addWindow(this, m_enabledBorders); emit enabledBordersChanged(); } } //!END borders } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/dock/docksecconfigview.h b/app/view/docksecconfigview.h similarity index 95% rename from app/dock/docksecconfigview.h rename to app/view/docksecconfigview.h index 5969f64e..cff0617e 100644 --- a/app/dock/docksecconfigview.h +++ b/app/view/docksecconfigview.h @@ -1,108 +1,110 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DOCKSECCONFIGVIEW_H #define DOCKSECCONFIGVIEW_H // local #include "../../liblattedock/dock.h" //Qt #include #include #include #include // Plasma #include #include namespace Plasma { class Applet; class Containment; class FrameSvg; class Types; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { - class DockCorona; -class DockView; +class View; +} + +namespace Latte { class DockSecConfigView : public QQuickView { Q_OBJECT Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged) public: - DockSecConfigView(DockView *dockView, QWindow *parent); + DockSecConfigView(Latte::View *view, QWindow *parent); ~DockSecConfigView() override; void init(); Qt::WindowFlags wFlags() const; QRect geometryWhenVisible() const; Plasma::FrameSvg::EnabledBorders enabledBorders() const; public slots: Q_INVOKABLE void hideConfigWindow(); Q_INVOKABLE void syncGeometry(); signals: void enabledBordersChanged(); void showSignal(); protected: void showEvent(QShowEvent *ev) override; void focusOutEvent(QFocusEvent *ev) override; bool event(QEvent *e) override; void syncSlideEffect(); private slots: void updateEnabledBorders(); private: void setupWaylandIntegration(); QRect m_geometryWhenVisible; - QPointer m_dockView; + QPointer m_dockView; QPointer m_parent; QTimer m_screenSyncTimer; QTimer m_thicknessSyncTimer; QList connections; Plasma::FrameSvg::EnabledBorders m_enabledBorders{Plasma::FrameSvg::AllBorders}; DockCorona *m_corona{nullptr}; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif //DOCKSECCONFIGVIEW_H // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/app/dock/effects.cpp b/app/view/effects.cpp similarity index 96% rename from app/dock/effects.cpp rename to app/view/effects.cpp index da6c6b14..07c09b32 100644 --- a/app/dock/effects.cpp +++ b/app/view/effects.cpp @@ -1,400 +1,400 @@ /* * 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 "effects.h" // local -#include "dockview.h" #include "panelshadows_p.h" +#include "view.h" #include "../../liblattedock/dock.h" // Qt #include // KDE #include #include namespace Latte { namespace ViewPart { -Effects::Effects(Latte::DockView *parent) +Effects::Effects(Latte::View *parent) : QObject(parent), m_view(parent) { init(); } Effects::~Effects() { } void Effects::init() { connect(this, &Effects::backgroundOpacityChanged, this, &Effects::updateEffects); connect(this, &Effects::drawEffectsChanged, this, &Effects::updateEffects); connect(this, &Effects::rectChanged, this, &Effects::updateEffects); connect(this, &Effects::drawShadowsChanged, this, [&]() { if (m_view->behaveAsPlasmaPanel()) { updateEnabledBorders(); } }); - connect(m_view, &Latte::DockView::alignmentChanged, this, &Effects::updateEnabledBorders); - connect(m_view, &Latte::DockView::behaveAsPlasmaPanelChanged, this, &Effects::updateEffects); - connect(m_view, &Latte::DockView::behaveAsPlasmaPanelChanged, this, &Effects::updateShadows); + connect(m_view, &Latte::View::alignmentChanged, this, &Effects::updateEnabledBorders); + connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, &Effects::updateEffects); + connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, &Effects::updateShadows); connect(this, SIGNAL(innerShadowChanged()), m_view->corona(), SIGNAL(availableScreenRectChanged())); } bool Effects::animationsBlocked() const { return m_animationsBlocked; } void Effects::setAnimationsBlocked(bool blocked) { if (m_animationsBlocked == blocked) { return; } m_animationsBlocked = blocked; emit animationsBlockedChanged(); } bool Effects::colorizerEnabled() const { return m_colorizerEnabled; } void Effects::setColorizerEnabled(bool enabled) { if (m_colorizerEnabled == enabled) { return; } m_colorizerEnabled = enabled; emit colorizerEnabledChanged(); } bool Effects::drawShadows() const { return m_drawShadows; } void Effects::setDrawShadows(bool draw) { if (m_drawShadows == draw) { return; } m_drawShadows = draw; if (m_view->behaveAsPlasmaPanel() && m_drawShadows) { PanelShadows::self()->addWindow(m_view, m_enabledBorders); } else { PanelShadows::self()->removeWindow(m_view); } emit drawShadowsChanged(); } bool Effects::drawEffects() const { return m_drawEffects; } void Effects::setDrawEffects(bool draw) { if (m_drawEffects == draw) { return; } m_drawEffects = draw; emit drawEffectsChanged(); } bool Effects::forceDrawCenteredBorders() const { return m_forceDrawCenteredBorders; } void Effects::setForceDrawCenteredBorders(bool draw) { if (m_forceDrawCenteredBorders == draw) { return; } m_forceDrawCenteredBorders = draw; } int Effects::backgroundOpacity() const { return m_backgroundOpacity; } void Effects::setBackgroundOpacity(int opacity) { if (m_backgroundOpacity == opacity) { return; } m_backgroundOpacity = opacity; emit backgroundOpacityChanged(); } int Effects::innerShadow() const { return m_innerShadow; } void Effects::setInnerShadow(int shadow) { if (m_innerShadow == shadow) return; m_innerShadow = shadow; emit innerShadowChanged(); } QRect Effects::rect() const { return m_rect; } void Effects::setRect(QRect area) { QRect inWindowRect = area.intersected(QRect(0, 0, m_view->width(), m_view->height())); if (m_rect == inWindowRect) { return; } m_rect = inWindowRect; emit rectChanged(); } QRect Effects::mask() const { return m_mask; } void Effects::setMask(QRect area) { if (m_mask == area) return; m_mask = area; if (KWindowSystem::compositingActive()) { if (m_view->behaveAsPlasmaPanel()) { m_view->setMask(QRect()); } else { m_view->setMask(m_mask); } } else { //! this is used when compositing is disabled and provides //! the correct way for the mask to be painted in order for //! rounded corners to be shown correctly //! the enabledBorders check was added because there was cases //! that the mask region wasnt calculated correctly after location changes if (!m_background || m_background->enabledBorders() != m_enabledBorders) { m_background = new Plasma::FrameSvg(this); } if (m_background->imagePath() != "opaque/dialogs/background") { m_background->setImagePath(QStringLiteral("opaque/dialogs/background")); } m_background->setEnabledBorders(m_enabledBorders); m_background->resizeFrame(area.size()); QRegion fixedMask = m_background->mask(); fixedMask.translate(m_mask.x(), m_mask.y()); //! fix for KF5.32 that return empty QRegion's for the mask if (fixedMask.isEmpty()) { fixedMask = QRegion(m_mask); } m_view->setMask(fixedMask); } // qDebug() << "dock mask set:" << m_mask; emit maskChanged(); } void Effects::clearShadows() { PanelShadows::self()->removeWindow(m_view); } void Effects::updateShadows() { if (m_view->behaveAsPlasmaPanel() && drawShadows()) { PanelShadows::self()->addWindow(m_view, enabledBorders()); } else { PanelShadows::self()->removeWindow(m_view); } } void Effects::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_view->surface()) { return; } if (!m_view->behaveAsPlasmaPanel()) { if (m_drawEffects && !m_rect.isNull() && !m_rect.isEmpty()) { //! this is used when compositing is disabled and provides //! the correct way for the mask to be painted in order for //! rounded corners to be shown correctly if (!m_background) { m_background = new Plasma::FrameSvg(this); } if (m_background->imagePath() != "widgets/panel-background") { m_background->setImagePath(QStringLiteral("widgets/panel-background")); } m_background->setEnabledBorders(m_enabledBorders); m_background->resizeFrame(m_rect.size()); QRegion fixedMask = m_background->mask(); fixedMask.translate(m_rect.x(), m_rect.y()); //! fix1, for KF5.32 that return empty QRegion's for the mask if (fixedMask.isEmpty()) { fixedMask = QRegion(m_rect); } KWindowEffects::enableBlurBehind(m_view->winId(), true, fixedMask); bool drawBackgroundEffect = m_theme.backgroundContrastEnabled() && (m_backgroundOpacity == 100); //based on Breeze Dark theme behavior the enableBackgroundContrast even though it does accept //a QRegion it uses only the first rect. The bug was that for Breeze Dark there was a line //at the dock bottom that was distinguishing it from other themes KWindowEffects::enableBackgroundContrast(m_view->winId(), drawBackgroundEffect, m_theme.backgroundContrast(), m_theme.backgroundIntensity(), m_theme.backgroundSaturation(), fixedMask.boundingRect()); } else { KWindowEffects::enableBlurBehind(m_view->winId(), false); KWindowEffects::enableBackgroundContrast(m_view->winId(), false); } } else if (m_view->behaveAsPlasmaPanel() && m_drawEffects) { KWindowEffects::enableBlurBehind(m_view->winId(), true); bool drawBackgroundEffect = m_theme.backgroundContrastEnabled() && (m_backgroundOpacity == 100); KWindowEffects::enableBackgroundContrast(m_view->winId(), drawBackgroundEffect, m_theme.backgroundContrast(), m_theme.backgroundIntensity(), m_theme.backgroundSaturation()); } else { KWindowEffects::enableBlurBehind(m_view->winId(), false); KWindowEffects::enableBackgroundContrast(m_view->winId(), false); } } //!BEGIN draw panel shadows outside the dock window Plasma::FrameSvg::EnabledBorders Effects::enabledBorders() const { return m_enabledBorders; } void Effects::updateEnabledBorders() { if (!m_view->screen()) { return; } Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; switch (m_view->location()) { case Plasma::Types::TopEdge: borders &= ~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 &= ~Plasma::FrameSvg::BottomBorder; break; default: break; } if ((m_view->location() == Plasma::Types::LeftEdge || m_view->location() == Plasma::Types::RightEdge)) { if (m_view->maxLength() == 1 && m_view->alignment() == Latte::Dock::Justify && !m_forceDrawCenteredBorders) { borders &= ~Plasma::FrameSvg::TopBorder; borders &= ~Plasma::FrameSvg::BottomBorder; } if (m_view->alignment() == Latte::Dock::Top && !m_forceDrawCenteredBorders && m_view->offset() == 0) { borders &= ~Plasma::FrameSvg::TopBorder; } if (m_view->alignment() == Latte::Dock::Bottom && !m_forceDrawCenteredBorders && m_view->offset() == 0) { borders &= ~Plasma::FrameSvg::BottomBorder; } } if (m_view->location() == Plasma::Types::TopEdge || m_view->location() == Plasma::Types::BottomEdge) { if (m_view->maxLength() == 1 && m_view->alignment() == Latte::Dock::Justify) { borders &= ~Plasma::FrameSvg::LeftBorder; borders &= ~Plasma::FrameSvg::RightBorder; } if (m_view->alignment() == Latte::Dock::Left && m_view->offset() == 0) { borders &= ~Plasma::FrameSvg::LeftBorder; } if (m_view->alignment() == Latte::Dock::Right && m_view->offset() == 0) { borders &= ~Plasma::FrameSvg::RightBorder; } } if (m_enabledBorders != borders) { m_enabledBorders = borders; emit enabledBordersChanged(); } if (!m_view->behaveAsPlasmaPanel() || !m_drawShadows) { PanelShadows::self()->removeWindow(m_view); } else { PanelShadows::self()->setEnabledBorders(m_view, borders); } } //!END draw panel shadows outside the dock window } } diff --git a/app/dock/effects.h b/app/view/effects.h similarity index 97% rename from app/dock/effects.h rename to app/view/effects.h index c1efd513..cc658237 100644 --- a/app/dock/effects.h +++ b/app/view/effects.h @@ -1,135 +1,135 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EFFECTS_H #define EFFECTS_H // Qt #include #include #include // Plasma #include #include namespace Latte { -class DockView; +class View; } namespace Latte { namespace ViewPart { class Effects: public QObject { Q_OBJECT Q_PROPERTY(bool animationsBlocked READ animationsBlocked NOTIFY animationsBlockedChanged) Q_PROPERTY(bool colorizerEnabled READ colorizerEnabled WRITE setColorizerEnabled NOTIFY colorizerEnabledChanged) Q_PROPERTY(bool drawShadows READ drawShadows WRITE setDrawShadows NOTIFY drawShadowsChanged) Q_PROPERTY(bool drawEffects READ drawEffects WRITE setDrawEffects NOTIFY drawEffectsChanged) //! thickness shadow size when is drawn inside the window from qml Q_PROPERTY(int backgroundOpacity READ backgroundOpacity WRITE setBackgroundOpacity NOTIFY backgroundOpacityChanged) Q_PROPERTY(int innerShadow READ innerShadow WRITE setInnerShadow NOTIFY innerShadowChanged) Q_PROPERTY(QRect mask READ mask WRITE setMask NOTIFY maskChanged) Q_PROPERTY(QRect rect READ rect WRITE setRect NOTIFY rectChanged) Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders NOTIFY enabledBordersChanged) public: - Effects(DockView *parent); + Effects(Latte::View *parent); virtual ~Effects(); bool animationsBlocked() const; void setAnimationsBlocked(bool blocked); bool colorizerEnabled() const; void setColorizerEnabled(bool enabled); bool drawShadows() const; void setDrawShadows(bool draw); bool drawEffects() const; void setDrawEffects(bool draw); bool forceDrawCenteredBorders() const; void setForceDrawCenteredBorders(bool draw); int backgroundOpacity() const; void setBackgroundOpacity(int opacity); int innerShadow() const; void setInnerShadow(int shadow); QRect mask() const; void setMask(QRect area); QRect rect() const; void setRect(QRect area); Plasma::FrameSvg::EnabledBorders enabledBorders() const; public slots: void clearShadows(); void updateShadows(); void updateEffects(); void updateEnabledBorders(); signals: void animationsBlockedChanged(); void backgroundOpacityChanged(); void colorizerEnabledChanged(); void drawShadowsChanged(); void drawEffectsChanged(); void enabledBordersChanged(); void maskChanged(); void innerShadowChanged(); void rectChanged(); private slots: void init(); private: bool m_animationsBlocked{false}; bool m_colorizerEnabled{false}; bool m_drawShadows{true}; bool m_drawEffects{false}; bool m_forceDrawCenteredBorders{false}; int m_backgroundOpacity{100}; int m_innerShadow{0}; QRect m_rect; QRect m_mask; - QPointer m_view; + QPointer m_view; Plasma::Theme m_theme; //only for the mask on disabled compositing, not to actually paint Plasma::FrameSvg *m_background{nullptr}; //only for the mask, not to actually paint Plasma::FrameSvg::EnabledBorders m_enabledBorders{Plasma::FrameSvg::AllBorders}; }; } } #endif diff --git a/app/dock/panelshadows.cpp b/app/view/panelshadows.cpp similarity index 100% rename from app/dock/panelshadows.cpp rename to app/view/panelshadows.cpp diff --git a/app/dock/panelshadows_p.h b/app/view/panelshadows_p.h similarity index 100% rename from app/dock/panelshadows_p.h rename to app/view/panelshadows_p.h diff --git a/app/dock/positioner.cpp b/app/view/positioner.cpp similarity index 96% rename from app/dock/positioner.cpp rename to app/view/positioner.cpp index 0279085c..0c281781 100644 --- a/app/dock/positioner.cpp +++ b/app/view/positioner.cpp @@ -1,664 +1,664 @@ /* * 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 "dockview.h" #include "effects.h" +#include "view.h" #include "../dockcorona.h" #include "../screenpool.h" #include "../settings/universalsettings.h" #include "../../liblattedock/dock.h" // Qt #include // KDE #include #include #include namespace Latte { namespace ViewPart { -Positioner::Positioner(Latte::DockView *parent) +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); auto *dockCorona = qobject_cast(m_view->corona()); if (dockCorona) { m_screenSyncTimer.setInterval(qMax(dockCorona->universalSettings()->screenTrackerInterval() - 500, 1000)); connect(dockCorona->universalSettings(), &UniversalSettings::screenTrackerIntervalChanged, this, [this, dockCorona]() { m_screenSyncTimer.setInterval(qMax(dockCorona->universalSettings()->screenTrackerInterval() - 500, 1000)); }); connect(dockCorona, &DockCorona::dockLocationChanged, 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_screenSyncTimer.stop(); m_validateGeometryTimer.stop(); } void Positioner::init() { //! connections connect(this, &Positioner::screenGeometryChanged, this, &Positioner::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::DockView::absGeometryChanged, this, &Positioner::syncGeometry); - connect(m_view, &Latte::DockView::behaveAsPlasmaPanelChanged, this, &Positioner::syncGeometry); - connect(m_view, &Latte::DockView::maxThicknessChanged, this, &Positioner::syncGeometry); - connect(m_view, &Latte::DockView::maxLengthChanged, this, &Positioner::syncGeometry); - connect(m_view, &Latte::DockView::offsetChanged, this, &Positioner::syncGeometry); + connect(m_view, &Latte::View::absGeometryChanged, this, &Positioner::syncGeometry); + 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::DockView::locationChanged, this, [&]() { + connect(m_view, &Latte::View::locationChanged, this, [&]() { updateFormFactor(); syncGeometry(); }); - connect(m_view, &Latte::DockView::normalThicknessChanged, this, [&]() { + connect(m_view, &Latte::View::normalThicknessChanged, this, [&]() { if (m_view->behaveAsPlasmaPanel()) { 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); initSignalingForLocationChangeSliding(); } int Positioner::currentScreenId() const { auto *dockCorona = qobject_cast(m_view->corona()); if (dockCorona) { return dockCorona->screenPool()->id(m_screenToFollowId); } return -1; } QString Positioner::currentScreenName() const { return m_screenToFollowId; } bool Positioner::setCurrentScreen(const QString id) { QScreen *nextScreen{qGuiApp->primaryScreen()}; if (id != "primary") { foreach (auto scr, qGuiApp->screens()) { if (scr && scr->name() == id) { nextScreen = scr; break; } } } if (m_screenToFollow == nextScreen) { return true; } if (nextScreen) { if (m_view->managedLayout()) { auto freeEdges = m_view->managedLayout()->freeEdges(nextScreen); if (!freeEdges.contains(m_view->location())) { return false; } else { 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); if (m_view->containment()) { m_view->containment()->reactToScreenChange(); } connect(scr, &QScreen::geometryChanged, this, &Positioner::screenGeometryChanged); syncGeometry(); m_view->updateAbsDockGeometry(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 "; foreach (auto scr, qGuiApp->screens()) { qDebug() << " D, found screen: " << scr->name(); } bool screenExists{false}; //!check if the associated screen is running foreach (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())) { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; edges = m_view->managedLayout() ? m_view->managedLayout()->availableEdgesForView(qGuiApp->primaryScreen(), m_view) : edges; //change to primary screen only if the specific edge is free qDebug() << "updating the primary screen for dock..."; qDebug() << "available primary screen edges:" << edges; qDebug() << "dock location:" << m_view->location(); if (edges.contains(m_view->location())) { //! 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 foreach (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::Dock::AlwaysVisible) { m_view->updateAbsDockGeometry(true); } } void Positioner::syncGeometry() { if (!(m_view->screen() && m_view->containment()) || m_inDelete) { return; } bool found{false}; qDebug() << "syncGeometry() 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->managedLayout() ? m_view->managedLayout()->name() : QString(); auto dockCorona = qobject_cast(m_view->corona()); int fixedScreen = m_view->onPrimary() ? dockCorona->screenPool()->primaryScreenId() : m_view->containment()->screen(); freeRegion = dockCorona->availableScreenRegionWithCriteria(fixedScreen, layoutName); 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 (int i = 0; i < availableRegion.rectCount(); ++i) { QRect rect = availableRegion.rects().at(i); //! 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)(rect.width() * rect.height()) / 2500; if (tempArea > area) { availableScreenRect = rect; area = tempArea; } } if (availableRegion.rectCount() > 1 && m_view->behaveAsPlasmaPanel()) { m_view->effects()->setForceDrawCenteredBorders(true); } else { m_view->effects()->setForceDrawCenteredBorders(false); } } else { m_view->effects()->setForceDrawCenteredBorders(false); } m_view->effects()->updateEnabledBorders(); resizeWindow(availableScreenRect); updatePosition(availableScreenRect); qDebug() << "syncGeometry() calculations for screen: " << m_view->screen()->name() << " _ " << m_view->screen()->geometry(); } qDebug() << "syncGeometry() ended..."; // qDebug() << "dock geometry:" << qRectToStr(geometry()); } void Positioner::validateDockGeometry() { if (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 = 0; int maxHeight = m_view->maxLength() * 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(); switch (m_view->alignment()) { case Latte::Dock::Top: yPos = m_view->screen()->geometry().y(); break; case Latte::Dock::Center: case Latte::Dock::Justify: yPos = qMax(m_view->screen()->geometry().center().y() - maxHeight / 2, m_view->screen()->geometry().y()); break; case Latte::Dock::Bottom: yPos = m_view->screen()->geometry().bottom() - maxHeight + 1; break; } maxGeometry.setRect(xPos, yPos, maxWidth, maxHeight); break; case Plasma::Types::RightEdge: xPos = m_view->screen()->geometry().right() - maxWidth + 1; switch (m_view->alignment()) { case Latte::Dock::Top: yPos = m_view->screen()->geometry().y(); break; case Latte::Dock::Center: case Latte::Dock::Justify: yPos = qMax(m_view->screen()->geometry().center().y() - maxHeight / 2, m_view->screen()->geometry().y()); break; case Latte::Dock::Bottom: yPos = m_view->screen()->geometry().bottom() - maxHeight + 1; break; } 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 if (m_view->location() == Plasma::Types::TopEdge) { KWindowSystem::setState(m_view->winId(), NET::KeepAbove); } else { KWindowSystem::clearState(m_view->winId(), NET::KeepAbove); } return maxGeometry; } void Positioner::updatePosition(QRect availableScreenRect) { QRect screenGeometry{availableScreenRect}; QPoint position; position = {0, 0}; const auto length = [&](int length) -> int { float offs = static_cast(m_view->offset()); return static_cast(length * ((1 - m_view->maxLength()) / 2) + length * (offs / 100)); }; int cleanThickness = m_view->normalThickness() - m_view->effects()->innerShadow(); switch (m_view->location()) { case Plasma::Types::TopEdge: if (m_view->behaveAsPlasmaPanel()) { position = {screenGeometry.x() + length(screenGeometry.width()), screenGeometry.y()}; } else { position = {screenGeometry.x(), screenGeometry.y()}; } break; case Plasma::Types::BottomEdge: if (m_view->behaveAsPlasmaPanel()) { position = {screenGeometry.x() + length(screenGeometry.width()), screenGeometry.y() + screenGeometry.height() - cleanThickness }; } else { position = {screenGeometry.x(), screenGeometry.y() + screenGeometry.height() - m_view->height()}; } break; case Plasma::Types::RightEdge: if (m_view->behaveAsPlasmaPanel() && !m_view->mask().isNull()) { position = {availableScreenRect.right() - cleanThickness + 1, availableScreenRect.y() + length(availableScreenRect.height()) }; } else { position = {availableScreenRect.right() - m_view->width() + 1, availableScreenRect.y()}; } break; case Plasma::Types::LeftEdge: if (m_view->behaveAsPlasmaPanel() && !m_view->mask().isNull()) { position = {availableScreenRect.x(), availableScreenRect.y() + length(availableScreenRect.height())}; } else { position = {availableScreenRect.x(), availableScreenRect.y()}; } break; default: qWarning() << "wrong location, couldn't update the panel position" << m_view->location(); } m_validGeometry.setTopLeft(position); m_view->setPosition(position); if (m_view->surface()) { m_view->surface()->setPosition(position); } } 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 && m_view->corona()) { emit m_view->corona()->availableScreenRectChanged(); } } 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, &DockView::locationChanged, this, [&]() { + 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(); if (m_view->managedLayout()) { m_view->managedLayout()->syncDockViewsToScreens(); } }); } }); //! 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(); if (m_view->managedLayout()) { m_view->managedLayout()->syncDockViewsToScreens(); } }); } }); //! signals to handle the sliding-in/out during moving to another layout connect(this, &Positioner::hideDockDuringMovingToLayoutStarted, this, &Positioner::onHideWindowsForSlidingOut); - connect(m_view, &DockView::managedLayoutChanged, this, [&]() { + connect(m_view, &View::managedLayoutChanged, this, [&]() { if (!m_moveToLayout.isEmpty() && m_view->managedLayout()) { m_moveToLayout = ""; QTimer::singleShot(100, [this]() { m_view->effects()->setAnimationsBlocked(false); emit showDockAfterMovingToLayoutFinished(); m_view->showSettingsWindow(); }); } }); //! ---- 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::inLocationChangeAnimation() { return ((m_goToLocation != Plasma::Types::Floating) || (m_moveToLayout != "") || m_goToScreen); } 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/dock/positioner.h b/app/view/positioner.h similarity index 97% rename from app/dock/positioner.h rename to app/view/positioner.h index 19eb2cb8..cec02482 100644 --- a/app/dock/positioner.h +++ b/app/view/positioner.h @@ -1,125 +1,125 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef POSITIONER_H #define POSITIONER_H // Qt #include #include #include #include // Plasma #include namespace Plasma { class Types; } namespace Latte { -class DockView; +class View; } namespace Latte { namespace ViewPart { class Positioner: public QObject { Q_OBJECT Q_PROPERTY(int currentScreenId READ currentScreenId NOTIFY currentScreenChanged) Q_PROPERTY(QString currentScreenName READ currentScreenName NOTIFY currentScreenChanged) public: - Positioner(DockView *parent); + Positioner(Latte::View *parent); virtual ~Positioner(); int currentScreenId() const; QString currentScreenName() const; bool inLocationChangeAnimation(); void setScreenToFollow(QScreen *scr, bool updateScreenId = true); void reconsiderScreen(); public slots: Q_INVOKABLE void hideDockDuringLocationChange(int goToLocation); Q_INVOKABLE void hideDockDuringMovingToLayout(QString layoutName); Q_INVOKABLE bool setCurrentScreen(const QString id); void syncGeometry(); signals: void currentScreenChanged(); void screenGeometryChanged(); //! these two signals are used from config ui and containment ui //! in order to orchestrate an animated hiding/showing of dock //! during changing location void hideDockDuringLocationChangeStarted(); void hideDockDuringLocationChangeFinished(); void hideDockDuringScreenChangeStarted(); void hideDockDuringScreenChangeFinished(); void hideDockDuringMovingToLayoutStarted(); void hideDockDuringMovingToLayoutFinished(); void onHideWindowsForSlidingOut(); void showDockAfterLocationChangeFinished(); void showDockAfterScreenChangeFinished(); void showDockAfterMovingToLayoutFinished(); private slots: void screenChanged(QScreen *screen); void validateDockGeometry(); private: void init(); void initSignalingForLocationChangeSliding(); void resizeWindow(QRect availableScreenRect = QRect()); void updateFormFactor(); void updatePosition(QRect availableScreenRect = QRect()); QRect maximumNormalGeometry(); private: bool m_inDelete{false}; //! it is used in order to enforce X11 to never miss window geometry QRect m_validGeometry; - QPointer m_view; + QPointer m_view; QString m_screenToFollowId; QPointer m_screenToFollow; QTimer m_screenSyncTimer; QTimer m_validateGeometryTimer; //!used at sliding out/in animation QString m_moveToLayout; Plasma::Types::Location m_goToLocation{Plasma::Types::Floating}; QScreen *m_goToScreen{nullptr}; }; } } #endif diff --git a/app/dock/screenedgeghostwindow.cpp b/app/view/screenedgeghostwindow.cpp similarity index 95% rename from app/dock/screenedgeghostwindow.cpp rename to app/view/screenedgeghostwindow.cpp index 96f8d99a..9bd0e736 100644 --- a/app/dock/screenedgeghostwindow.cpp +++ b/app/view/screenedgeghostwindow.cpp @@ -1,241 +1,241 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "screenedgeghostwindow.h" // local -#include "dockview.h" +#include "view.h" #include "../dockcorona.h" // Qt #include #include #include #include // KDE #include #include #include // X11 #include namespace Latte { -ScreenEdgeGhostWindow::ScreenEdgeGhostWindow(DockView *view) : +ScreenEdgeGhostWindow::ScreenEdgeGhostWindow(Latte::View *view) : m_dockView(view) { setColor(QColor(Qt::transparent)); setDefaultAlphaBuffer(true); setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); m_fixGeometryTimer.setSingleShot(true); m_fixGeometryTimer.setInterval(500); connect(&m_fixGeometryTimer, &QTimer::timeout, this, &ScreenEdgeGhostWindow::fixGeometry); connect(this, &QQuickView::xChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::yChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::widthChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); connect(this, &QQuickView::heightChanged, this, &ScreenEdgeGhostWindow::startGeometryTimer); - connect(m_dockView, &DockView::absGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); - connect(m_dockView, &DockView::screenGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); - connect(m_dockView, &DockView::locationChanged, this, &ScreenEdgeGhostWindow::updateGeometry); + connect(m_dockView, &Latte::View::absGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); + connect(m_dockView, &Latte::View::screenGeometryChanged, this, &ScreenEdgeGhostWindow::updateGeometry); + connect(m_dockView, &Latte::View::locationChanged, this, &ScreenEdgeGhostWindow::updateGeometry); connect(m_dockView, &QQuickView::screenChanged, this, [this]() { setScreen(m_dockView->screen()); updateGeometry(); }); if (!KWindowSystem::isPlatformWayland()) { connect(this, &QWindow::visibleChanged, this, [&]() { //! IMPORTANT!!! ::: This fixes a bug when closing an Activity all docks from all Activities are //! disappearing! With this they reappear!!! if (m_dockView && m_dockView->managedLayout()) { if (!isVisible()) { QTimer::singleShot(100, [this]() { if (!m_inDelete && m_dockView && m_dockView->managedLayout() && !isVisible()) { setVisible(true); } }); QTimer::singleShot(1500, [this]() { if (!m_inDelete && m_dockView && m_dockView->managedLayout() && !isVisible()) { setVisible(true); } }); } else { //! For some reason when the window is hidden in the edge under X11 afterwards //! is losing its window flags if (!m_inDelete) { KWindowSystem::setType(winId(), NET::Dock); KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); KWindowSystem::setOnAllDesktops(winId(), true); } } } }); } setupWaylandIntegration(); setScreen(m_dockView->screen()); setVisible(true); updateGeometry(); hideWithMask(); } ScreenEdgeGhostWindow::~ScreenEdgeGhostWindow() { m_inDelete = true; m_dockView = nullptr; if (m_shellSurface) { delete m_shellSurface; } } int ScreenEdgeGhostWindow::location() { return (int)m_dockView->location(); } -DockView *ScreenEdgeGhostWindow::parentDock() +Latte::View *ScreenEdgeGhostWindow::parentDock() { return m_dockView; } KWayland::Client::PlasmaShellSurface *ScreenEdgeGhostWindow::surface() { return m_shellSurface; } void ScreenEdgeGhostWindow::updateGeometry() { QRect newGeometry; int thickness{1}; if (m_dockView->location() == Plasma::Types::BottomEdge) { newGeometry.setX(m_dockView->absGeometry().left()); newGeometry.setY(m_dockView->screenGeometry().bottom() - thickness); } else if (m_dockView->location() == Plasma::Types::TopEdge) { newGeometry.setX(m_dockView->absGeometry().left()); newGeometry.setY(m_dockView->screenGeometry().top()); } else if (m_dockView->location() == Plasma::Types::LeftEdge) { newGeometry.setX(m_dockView->screenGeometry().left()); newGeometry.setY(m_dockView->absGeometry().top()); } else if (m_dockView->location() == Plasma::Types::RightEdge) { newGeometry.setX(m_dockView->screenGeometry().right() - thickness); newGeometry.setY(m_dockView->absGeometry().top()); } if (m_dockView->formFactor() == Plasma::Types::Horizontal) { newGeometry.setWidth(qMin(m_dockView->absGeometry().width(), m_dockView->screenGeometry().width() - 1)); newGeometry.setHeight(thickness + 1); } else { newGeometry.setWidth(thickness + 1); newGeometry.setHeight(qMin(m_dockView->absGeometry().height(), m_dockView->screenGeometry().height() - 1)); } m_calculatedGeometry = newGeometry; fixGeometry(); } void ScreenEdgeGhostWindow::fixGeometry() { if (!m_calculatedGeometry.isEmpty() && (m_calculatedGeometry.x() != x() || m_calculatedGeometry.y() != y() || m_calculatedGeometry.width() != width() || m_calculatedGeometry.height() != height())) { setMinimumSize(m_calculatedGeometry.size()); setMaximumSize(m_calculatedGeometry.size()); resize(m_calculatedGeometry.size()); setPosition(m_calculatedGeometry.x(), m_calculatedGeometry.y()); if (m_shellSurface) { m_shellSurface->setPosition(m_calculatedGeometry.topLeft()); } } } void ScreenEdgeGhostWindow::startGeometryTimer() { m_fixGeometryTimer.start(); } void ScreenEdgeGhostWindow::setupWaylandIntegration() { if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_dockView || !m_dockView->containment()) { // already setup return; } if (DockCorona *c = qobject_cast(m_dockView->containment()->corona())) { using namespace KWayland::Client; PlasmaShell *interface = c->waylandDockCoronaInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(this); if (!s) { return; } qDebug() << "wayland screen edge ghost window surface was created..."; m_shellSurface = interface->createSurface(s, this); m_shellSurface->setSkipTaskbar(true); m_shellSurface->setPanelTakesFocus(false); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); } } bool ScreenEdgeGhostWindow::event(QEvent *e) { if (e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) { emit containsMouseChanged(true); } else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave) { emit containsMouseChanged(false); } return QQuickView::event(e); } void ScreenEdgeGhostWindow::hideWithMask() { QRect maskGeometry{0, 0, 1, 1}; setMask(maskGeometry); } void ScreenEdgeGhostWindow::showWithMask() { setMask(QRect()); } } diff --git a/app/dock/screenedgeghostwindow.h b/app/view/screenedgeghostwindow.h similarity index 95% rename from app/dock/screenedgeghostwindow.h rename to app/view/screenedgeghostwindow.h index cda841c1..aa8ad3ff 100644 --- a/app/dock/screenedgeghostwindow.h +++ b/app/view/screenedgeghostwindow.h @@ -1,101 +1,101 @@ /* * Copyright 2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SCREENEDGEGHOSTWINDOW_H #define SCREENEDGEGHOSTWINDOW_H // Qt #include #include #include namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { -class DockView; +class View; } namespace Latte { //! What is the importance of this class? //! //! Plasma is activating the screen edges for the main panel window //! unfortunately this isnt possible for the Latte case. //! When a window is hidden at an edge it becomes NOT visible //! unfortunately that means that all the animations are //! stopped (Qt behaviour) and that creates confusion to the user after the window //! reappears because various animations are played (adding-removing tasks/launchers) //! that arent relevant any more. //! //! In order to workaround the above behaviour Latte is using a //! fake window to communicate with KWin and the MAIN dock window //! continues to use only mask technique to hide //! //! KDE BUGS: https://bugs.kde.org/show_bug.cgi?id=382219 //! https://bugs.kde.org/show_bug.cgi?id=392464 class ScreenEdgeGhostWindow : public QQuickView { Q_OBJECT public: - ScreenEdgeGhostWindow(DockView *view); + ScreenEdgeGhostWindow(Latte::View *view); ~ScreenEdgeGhostWindow() override; int location(); void hideWithMask(); void showWithMask(); - DockView *parentDock(); + Latte::View *parentDock(); KWayland::Client::PlasmaShellSurface *surface(); signals: void containsMouseChanged(bool contains); protected: bool event(QEvent *ev) override; private slots: void startGeometryTimer(); void updateGeometry(); void fixGeometry(); private: void setupWaylandIntegration(); private: bool m_inDelete{false}; QRect m_calculatedGeometry; QTimer m_fixGeometryTimer; - DockView *m_dockView{nullptr}; + Latte::View *m_dockView{nullptr}; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif diff --git a/app/dock/dockview.cpp b/app/view/view.cpp similarity index 85% rename from app/dock/dockview.cpp rename to app/view/view.cpp index fc6b46f2..551e3bee 100644 --- a/app/dock/dockview.cpp +++ b/app/view/view.cpp @@ -1,1071 +1,1071 @@ /* * 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 "dockview.h" +#include "view.h" // local #include "contextmenu.h" #include "dockconfigview.h" #include "effects.h" #include "positioner.h" #include "visibilitymanager.h" #include "../dockcorona.h" #include "../layout.h" #include "../layoutmanager.h" #include "../screenpool.h" #include "../plasmathemeextended.h" #include "../settings/universalsettings.h" #include "../../liblattedock/extras.h" // Qt #include #include #include #include #include #include // KDe #include #include #include #include #include // Plasma #include #include #include namespace Latte { //! both alwaysVisible and dockWinBehavior are passed through corona because //! during the dock window creation containment hasn't been set, but these variables //! are needed in order for window flags to be set correctly -DockView::DockView(Plasma::Corona *corona, QScreen *targetScreen, bool dockWindowBehavior) +View::View(Plasma::Corona *corona, QScreen *targetScreen, bool dockWindowBehavior) : PlasmaQuick::ContainmentView(corona), m_contextMenu(new ViewPart::ContextMenu(this)), m_effects(new ViewPart::Effects(this)), m_positioner(new ViewPart::Positioner(this)) //needs to be created after Effects becuase it catches some of its signals { setTitle(corona->kPackage().metadata().name()); setIcon(qGuiApp->windowIcon()); setResizeMode(QuickViewSharedEngine::SizeRootObjectToView); setColor(QColor(Qt::transparent)); setClearBeforeRendering(true); const auto flags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus; if (dockWindowBehavior) { setFlags(flags); } else { setFlags(flags | Qt::BypassWindowManagerHint); } KWindowSystem::setOnAllDesktops(winId(), true); if (targetScreen) m_positioner->setScreenToFollow(targetScreen); else m_positioner->setScreenToFollow(qGuiApp->primaryScreen()); - connect(this, &DockView::containmentChanged + connect(this, &View::containmentChanged , this, [ &, dockWindowBehavior]() { qDebug() << "dock view c++ containment changed 1..."; if (!this->containment()) return; qDebug() << "dock view c++ containment changed 2..."; //! First load default values from file restoreConfig(); //! Afterwards override that values in case during creation something different is needed setDockWinBehavior(dockWindowBehavior); //! Check the screen assigned to this dock reconsiderScreen(); if (!m_visibility) { m_visibility = new VisibilityManager(this); connect(m_visibility, &VisibilityManager::isHiddenChanged, this, [&]() { if (m_visibility->isHidden()) { deactivateApplets(); } }); } connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus))); }, Qt::DirectConnection); auto *dockCorona = qobject_cast(this->corona()); if (dockCorona) { - connect(dockCorona, &DockCorona::dockLocationChanged, this, &DockView::dockLocationChanged); + connect(dockCorona, &DockCorona::dockLocationChanged, this, &View::dockLocationChanged); } } -DockView::~DockView() +View::~View() { m_inDelete = true; - disconnect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &DockView::availableScreenRectChanged); + disconnect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &View::availableScreenRectChanged); disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus))); qDebug() << "dock view deleting..."; rootContext()->setContextProperty(QStringLiteral("dock"), nullptr); rootContext()->setContextProperty(QStringLiteral("themeExtended"), nullptr); rootContext()->setContextProperty(QStringLiteral("universalSettings"), nullptr); rootContext()->setContextProperty(QStringLiteral("layoutManager"), nullptr); //! this disconnect does not free up connections correctly when - //! dockView is deleted. A crash for this example is the following: + //! 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) { m_configView->setVisible(false);//hide(); } if (m_contextMenu) { delete m_contextMenu; } //needs to be deleted before Effects becuase it catches some of its signals if (m_positioner) { delete m_positioner; } if (m_effects) { delete m_effects; } if (m_visibility) delete m_visibility; } -void DockView::init() +void View::init() { - connect(this, &QQuickWindow::xChanged, this, &DockView::xChanged); - connect(this, &QQuickWindow::xChanged, this, &DockView::updateAbsDockGeometry); - connect(this, &QQuickWindow::yChanged, this, &DockView::yChanged); - connect(this, &QQuickWindow::yChanged, this, &DockView::updateAbsDockGeometry); - connect(this, &QQuickWindow::widthChanged, this, &DockView::widthChanged); - connect(this, &QQuickWindow::widthChanged, this, &DockView::updateAbsDockGeometry); - connect(this, &QQuickWindow::heightChanged, this, &DockView::heightChanged); - connect(this, &QQuickWindow::heightChanged, this, &DockView::updateAbsDockGeometry); + connect(this, &QQuickWindow::xChanged, this, &View::xChanged); + connect(this, &QQuickWindow::xChanged, this, &View::updateAbsDockGeometry); + connect(this, &QQuickWindow::yChanged, this, &View::yChanged); + connect(this, &QQuickWindow::yChanged, this, &View::updateAbsDockGeometry); + connect(this, &QQuickWindow::widthChanged, this, &View::widthChanged); + connect(this, &QQuickWindow::widthChanged, this, &View::updateAbsDockGeometry); + connect(this, &QQuickWindow::heightChanged, this, &View::heightChanged); + connect(this, &QQuickWindow::heightChanged, this, &View::updateAbsDockGeometry); - connect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &DockView::availableScreenRectChanged); + connect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &View::availableScreenRectChanged); - connect(this, &DockView::dockWinBehaviorChanged, this, &DockView::saveConfig); - connect(this, &DockView::onPrimaryChanged, this, &DockView::saveConfig); - connect(this, &DockView::isPreferredForShortcutsChanged, this, &DockView::saveConfig); + connect(this, &View::dockWinBehaviorChanged, this, &View::saveConfig); + connect(this, &View::onPrimaryChanged, this, &View::saveConfig); + connect(this, &View::isPreferredForShortcutsChanged, this, &View::saveConfig); connect(this, SIGNAL(normalThicknessChanged()), corona(), SIGNAL(availableScreenRectChanged())); - connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &DockView::hideWindowsForSlidingOut); - connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &DockView::screenGeometryChanged); - connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &DockView::contextMenuIsShownChanged); + connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &View::hideWindowsForSlidingOut); + connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &View::screenGeometryChanged); + connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged); ///!!!!! rootContext()->setContextProperty(QStringLiteral("dock"), this); auto *dockCorona = qobject_cast(this->corona()); if (dockCorona) { rootContext()->setContextProperty(QStringLiteral("universalSettings"), dockCorona->universalSettings()); rootContext()->setContextProperty(QStringLiteral("layoutManager"), dockCorona->layoutManager()); rootContext()->setContextProperty(QStringLiteral("themeExtended"), dockCorona->themeExtended()); } setSource(corona()->kPackage().filePath("lattedockui")); // setVisible(true); m_positioner->syncGeometry(); if (!KWindowSystem::isPlatformWayland()) { setVisible(true); } qDebug() << "SOURCE:" << source(); } -bool DockView::inDelete() const +bool View::inDelete() const { return m_inDelete; } -void DockView::disconnectSensitiveSignals() +void View::disconnectSensitiveSignals() { - disconnect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &DockView::availableScreenRectChanged); + disconnect(corona(), &Plasma::Corona::availableScreenRectChanged, this, &View::availableScreenRectChanged); setManagedLayout(nullptr); if (visibility()) { visibility()->setEnabledDynamicBackground(false); } } -void DockView::availableScreenRectChanged() +void View::availableScreenRectChanged() { if (m_inDelete) return; if (formFactor() == Plasma::Types::Vertical) { m_positioner->syncGeometry(); } } -void DockView::setupWaylandIntegration() +void View::setupWaylandIntegration() { if (m_shellSurface) return; if (DockCorona *c = qobject_cast(corona())) { using namespace KWayland::Client; PlasmaShell *interface {c->waylandDockCoronaInterface()}; if (!interface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; m_shellSurface = interface->createSurface(s, this); qDebug() << "WAYLAND dock window surface was created..."; m_shellSurface->setSkipTaskbar(true); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::WindowsGoBelow); } } -KWayland::Client::PlasmaShellSurface *DockView::surface() +KWayland::Client::PlasmaShellSurface *View::surface() { return m_shellSurface; } //! the main function which decides if this dock is at the //! correct screen -void DockView::reconsiderScreen() +void View::reconsiderScreen() { m_positioner->reconsiderScreen(); } -void DockView::copyDock() +void View::copyDock() { m_managedLayout->copyDock(containment()); } -void DockView::removeDock() +void View::removeDock() { if (m_managedLayout && m_managedLayout->viewsCount() > 1) { QAction *removeAct = this->containment()->actions()->action(QStringLiteral("remove")); if (removeAct) { removeAct->trigger(); } } } -bool DockView::settingsWindowIsShown() +bool View::settingsWindowIsShown() { auto configView = qobject_cast(m_configView); return (configView != nullptr); } -void DockView::showSettingsWindow() +void View::showSettingsWindow() { showConfigurationInterface(containment()); applyActivitiesToWindows(); } -void DockView::showConfigurationInterface(Plasma::Applet *applet) +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->setVisible(false); //m_configView->hide(); } else { m_configView->setVisible(true); //m_configView->show(); } return; } else if (m_configView) { if (m_configView->applet() == applet) { m_configView->setVisible(true); //m_configView->show(); m_configView->requestActivate(); return; } else { m_configView->setVisible(false); //m_configView->hide(); m_configView->deleteLater(); } } bool delayConfigView = false; if (c && containment() && c->isContainment() && c->id() == this->containment()->id()) { m_configView = new DockConfigView(c, this); delayConfigView = true; } else { m_configView = new PlasmaQuick::ConfigView(applet); } m_configView.data()->init(); if (!delayConfigView) { m_configView->setVisible(true); //m_configView.data()->show(); } else { //add a timer for showing the configuration window the first time it is //created in order to give the containmnent's layouts the time to //calculate the window's height if (!KWindowSystem::isPlatformWayland()) { QTimer::singleShot(150, m_configView, SLOT(show())); } else { QTimer::singleShot(150, [this]() { m_configView->setVisible(true); }); } } } -QRect DockView::localGeometry() const +QRect View::localGeometry() const { return m_localGeometry; } -void DockView::setLocalGeometry(const QRect &geometry) +void View::setLocalGeometry(const QRect &geometry) { if (m_localGeometry == geometry) { return; } m_localGeometry = geometry; emit localGeometryChanged(); updateAbsDockGeometry(); } -void DockView::updateAbsDockGeometry(bool bypassChecks) +void View::updateAbsDockGeometry(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 rigth 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 {x() + m_localGeometry.x(), y() + m_localGeometry.y() , m_localGeometry.width(), m_localGeometry.height()}; if (m_absGeometry == absGeometry && !bypassChecks) return; m_absGeometry = absGeometry; emit absGeometryChanged(m_absGeometry); //! this is needed in order to update correctly the screenGeometries if (visibility() && corona() && visibility()->mode() == Dock::AlwaysVisible) { emit corona()->availableScreenRectChanged(); emit corona()->availableScreenRegionChanged(); } } -void DockView::statusChanged(Plasma::Types::ItemStatus status) +void View::statusChanged(Plasma::Types::ItemStatus status) { if (containment()) { if (containment()->status() >= Plasma::Types::NeedsAttentionStatus && containment()->status() != Plasma::Types::HiddenStatus) { setBlockHiding(true); } else { setBlockHiding(false); } } } -bool DockView::alternativesIsShown() const +bool View::alternativesIsShown() const { return m_alternativesIsShown; } -void DockView::setAlternativesIsShown(bool show) +void View::setAlternativesIsShown(bool show) { if (m_alternativesIsShown == show) { return; } m_alternativesIsShown = show; setBlockHiding(show); emit alternativesIsShownChanged(); } -bool DockView::contextMenuIsShown() const +bool View::contextMenuIsShown() const { if (!m_contextMenu) { return false; } return m_contextMenu->menu(); } -int DockView::currentThickness() const +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 DockView::normalThickness() const +int View::normalThickness() const { return m_normalThickness; } -void DockView::setNormalThickness(int thickness) +void View::setNormalThickness(int thickness) { if (m_normalThickness == thickness) { return; } m_normalThickness = thickness; emit normalThicknessChanged(); } -bool DockView::dockWinBehavior() const +bool View::dockWinBehavior() const { return m_dockWinBehavior; } -void DockView::setDockWinBehavior(bool dock) +void View::setDockWinBehavior(bool dock) { if (m_dockWinBehavior == dock) { return; } m_dockWinBehavior = dock; emit dockWinBehaviorChanged(); } -bool DockView::behaveAsPlasmaPanel() const +bool View::behaveAsPlasmaPanel() const { return m_behaveAsPlasmaPanel; } -void DockView::setBehaveAsPlasmaPanel(bool behavior) +void View::setBehaveAsPlasmaPanel(bool behavior) { if (m_behaveAsPlasmaPanel == behavior) { return; } m_behaveAsPlasmaPanel = behavior; emit behaveAsPlasmaPanelChanged(); } -bool DockView::inEditMode() const +bool View::inEditMode() const { return m_inEditMode; } -void DockView::setInEditMode(bool edit) +void View::setInEditMode(bool edit) { if (m_inEditMode == edit) { return; } m_inEditMode = edit; emit inEditModeChanged(); } -bool DockView::isPreferredForShortcuts() const +bool View::isPreferredForShortcuts() const { return m_isPreferredForShortcuts; } -void DockView::setIsPreferredForShortcuts(bool preferred) +void View::setIsPreferredForShortcuts(bool preferred) { if (m_isPreferredForShortcuts == preferred) { return; } m_isPreferredForShortcuts = preferred; emit isPreferredForShortcutsChanged(); if (m_isPreferredForShortcuts && m_managedLayout) { emit m_managedLayout->preferredViewForShortcutsChanged(this); } } -void DockView::preferredViewForShortcutsChangedSlot(DockView *view) +void View::preferredViewForShortcutsChangedSlot(Latte::View *view) { if (view != this) { setIsPreferredForShortcuts(false); } } -bool DockView::onPrimary() const +bool View::onPrimary() const { return m_onPrimary; } -void DockView::setOnPrimary(bool flag) +void View::setOnPrimary(bool flag) { if (m_onPrimary == flag) { return; } m_onPrimary = flag; emit onPrimaryChanged(); } -float DockView::maxLength() const +float View::maxLength() const { return m_maxLength; } -void DockView::setMaxLength(float length) +void View::setMaxLength(float length) { if (m_maxLength == length) { return; } m_maxLength = length; emit maxLengthChanged(); } -int DockView::maxThickness() const +int View::maxThickness() const { return m_maxThickness; } -void DockView::setMaxThickness(int thickness) +void View::setMaxThickness(int thickness) { if (m_maxThickness == thickness) return; m_maxThickness = thickness; emit maxThicknessChanged(); } -int DockView::alignment() const +int View::alignment() const { return m_alignment; } -void DockView::setAlignment(int alignment) +void View::setAlignment(int alignment) { Dock::Alignment align = static_cast(alignment); if (m_alignment == alignment) { return; } m_alignment = align; emit alignmentChanged(); } -QRect DockView::absGeometry() const +QRect View::absGeometry() const { return m_absGeometry; } -QRect DockView::screenGeometry() const +QRect View::screenGeometry() const { if (this->screen()) { QRect geom = this->screen()->geometry(); return geom; } return QRect(); } -int DockView::offset() const +int View::offset() const { return m_offset; } -void DockView::setOffset(int offset) +void View::setOffset(int offset) { if (m_offset == offset) { return; } m_offset = offset; emit offsetChanged(); } -int DockView::fontPixelSize() const +int View::fontPixelSize() const { return m_fontPixelSize; } -void DockView::setFontPixelSize(int size) +void View::setFontPixelSize(int size) { if (m_fontPixelSize == size) { return; } m_fontPixelSize = size; emit fontPixelSizeChanged(); } -void DockView::applyActivitiesToWindows() +void View::applyActivitiesToWindows() { if (m_visibility) { QStringList activities = m_managedLayout->appliedActivities(); m_visibility->setWindowOnActivities(*this, activities); if (m_configView) { m_visibility->setWindowOnActivities(*m_configView, activities); auto configView = qobject_cast(m_configView); if (configView && configView->secondaryWindow()) { m_visibility->setWindowOnActivities(*configView->secondaryWindow(), activities); } } if (m_visibility->supportsKWinEdges()) { m_visibility->applyActivitiesToHiddenWindows(activities); } } } -Layout *DockView::managedLayout() const +Layout *View::managedLayout() const { return m_managedLayout; } -void DockView::setManagedLayout(Layout *layout) +void View::setManagedLayout(Layout *layout) { if (m_managedLayout == layout) { return; } // clear mode for (auto &c : connectionsManagedLayout) { disconnect(c); } m_managedLayout = layout; if (m_managedLayout) { //! Sometimes the activity isnt completely ready, by adding a delay //! we try to catch up QTimer::singleShot(100, [this]() { if (m_managedLayout && m_visibility) { qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_managedLayout->name() << " - activities: " << m_managedLayout->appliedActivities(); applyActivitiesToWindows(); emit activitiesChanged(); } }); - connectionsManagedLayout[0] = connect(m_managedLayout, &Layout::preferredViewForShortcutsChanged, this, &DockView::preferredViewForShortcutsChangedSlot); + connectionsManagedLayout[0] = connect(m_managedLayout, &Layout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot); } DockCorona *dockCorona = qobject_cast(this->corona()); if (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { connectionsManagedLayout[1] = connect(dockCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (m_managedLayout && m_visibility) { qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_managedLayout->name() << " - activities: " << m_managedLayout->appliedActivities(); applyActivitiesToWindows(); emit activitiesChanged(); } }); connectionsManagedLayout[2] = connect(m_managedLayout, &Layout::activitiesChanged, this, [&]() { if (m_managedLayout) { applyActivitiesToWindows(); emit activitiesChanged(); } }); connectionsManagedLayout[3] = connect(dockCorona->layoutManager(), &LayoutManager::layoutsChanged, this, [&]() { if (m_managedLayout) { applyActivitiesToWindows(); emit activitiesChanged(); } }); //!IMPORTANT!!! ::: This fixes a bug when closing an Activity all docks from all Activities are //! disappearing! With this they reappear!!! connectionsManagedLayout[4] = connect(this, &QWindow::visibleChanged, this, [&]() { if (!isVisible() && m_managedLayout) { QTimer::singleShot(100, [this]() { if (m_managedLayout && containment() && !containment()->destroyed()) { setVisible(true); applyActivitiesToWindows(); emit activitiesChanged(); } }); QTimer::singleShot(1500, [this]() { if (m_managedLayout && containment() && !containment()->destroyed()) { setVisible(true); applyActivitiesToWindows(); emit activitiesChanged(); } }); } }); } emit managedLayoutChanged(); } -void DockView::moveToLayout(QString layoutName) +void View::moveToLayout(QString layoutName) { if (!m_managedLayout) { return; } QList containments = m_managedLayout->unassignFromLayout(this); DockCorona *dockCorona = qobject_cast(this->corona()); if (dockCorona && containments.size() > 0) { Layout *newLayout = dockCorona->layoutManager()->activeLayout(layoutName); if (newLayout) { newLayout->assignToLayout(this, containments); } } } -void DockView::setBlockHiding(bool block) +void View::setBlockHiding(bool block) { if (!block) { auto *configView = qobject_cast(m_configView); if (m_alternativesIsShown || (configView && configView->sticker() && configView->isVisible())) { return; } if (m_visibility) { m_visibility->setBlockHiding(false); } } else { if (m_visibility) { m_visibility->setBlockHiding(true); } } } -void DockView::hideWindowsForSlidingOut() +void View::hideWindowsForSlidingOut() { setBlockHiding(false); if (m_configView) { auto configDialog = qobject_cast(m_configView); if (configDialog) { configDialog->hideConfigWindow(); } } } //! remove latte tasks plasmoid -void DockView::removeTasksPlasmoid() +void View::removeTasksPlasmoid() { if (!tasksPresent() || !containment()) { return; } foreach (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 DockView::tasksPresent() +bool View::tasksPresent() { if (!this->containment()) { return false; } foreach (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 tasks plasmoid exist in the dock -bool DockView::latteTasksPresent() +bool View::latteTasksPresent() { if (!this->containment()) { return false; } foreach (Plasma::Applet *applet, this->containment()->applets()) { KPluginMetaData metadata = applet->pluginMetaData(); if (metadata.pluginId() == "org.kde.latte.plasmoid") { return true; } } return false; } //!check if the plasmoid with _name_ exists in the midedata -bool DockView::mimeContainsPlasmoid(QMimeData *mimeData, QString name) +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); foreach (const QString &appletName, appletNames) { if (appletName == name) return true; } } return false; } -ViewPart::Effects *DockView::effects() const +ViewPart::Effects *View::effects() const { return m_effects; } -ViewPart::Positioner *DockView::positioner() const +ViewPart::Positioner *View::positioner() const { return m_positioner; } -VisibilityManager *DockView::visibility() const +VisibilityManager *View::visibility() const { return m_visibility; } -bool DockView::event(QEvent *e) +bool View::event(QEvent *e) { if (!m_inDelete) { emit eventTriggered(e); switch (e->type()) { case QEvent::Leave: engine()->trimComponentCache(); 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; default: break; } } return ContainmentView::event(e);; } -void DockView::deactivateApplets() +void View::deactivateApplets() { if (!containment()) { return; } foreach (auto applet, containment()->applets()) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { ai->setExpanded(false); } } } -void DockView::toggleAppletExpanded(const int id) +void View::toggleAppletExpanded(const int id) { if (!containment()) { return; } foreach (auto applet, containment()->applets()) { if (applet->id() == id) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { if (!ai->isActivationTogglesExpanded()) { ai->setActivationTogglesExpanded(true); } emit applet->activated(); } } } } -QVariantList DockView::containmentActions() +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); } foreach (QAction *ac, plugin->contextualActions()) { actions << QVariant::fromValue(ac); } return actions; } -void DockView::disableGrabItemBehavior() +void View::disableGrabItemBehavior() { setMouseGrabEnabled(false); } -void DockView::restoreGrabItemBehavior() +void View::restoreGrabItemBehavior() { setMouseGrabEnabled(true); if (mouseGrabberItem()) { mouseGrabberItem()->ungrabMouse(); } } //!BEGIN overriding context menus behavior -void DockView::mousePressEvent(QMouseEvent *event) +void View::mousePressEvent(QMouseEvent *event) { bool result = m_contextMenu->mousePressEvent(event); emit contextMenuIsShownChanged(); if (result) { PlasmaQuick::ContainmentView::mousePressEvent(event); } } //!END overriding context menus behavior //!BEGIN configuration functions -void DockView::saveConfig() +void View::saveConfig() { if (!this->containment()) return; auto config = this->containment()->config(); config.writeEntry("onPrimary", onPrimary()); config.writeEntry("dockWindowBehavior", dockWinBehavior()); config.writeEntry("isPreferredForShortcuts", isPreferredForShortcuts()); config.sync(); } -void DockView::restoreConfig() +void View::restoreConfig() { if (!this->containment()) return; auto config = this->containment()->config(); m_onPrimary = config.readEntry("onPrimary", true); m_dockWinBehavior = config.readEntry("dockWindowBehavior", true); 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 dockWinBehaviorChanged(); } //!END configuration functions } //!END namespace diff --git a/app/dock/dockview.h b/app/view/view.h similarity index 96% rename from app/dock/dockview.h rename to app/view/view.h index 5eba09ad..a3d86ed4 100644 --- a/app/dock/dockview.h +++ b/app/view/view.h @@ -1,286 +1,286 @@ /* * 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 DOCKVIEW_H -#define DOCKVIEW_H +#ifndef VIEW_H +#define VIEW_H // local #include "dockconfigview.h" #include "effects.h" #include "positioner.h" #include "visibilitymanager.h" #include "../layout.h" #include "../plasmaquick/containmentview.h" #include "../plasmaquick/configview.h" #include "../../liblattedock/dock.h" // C++ #include // Qt #include #include #include #include #include #include namespace Plasma { class Types; class Corona; class Containment; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { class Layout; namespace ViewPart { class ContextMenu; } } namespace Latte { -class DockView : public PlasmaQuick::ContainmentView +class View : public PlasmaQuick::ContainmentView { Q_OBJECT Q_PROPERTY(bool alternativesIsShown READ alternativesIsShown NOTIFY alternativesIsShownChanged) Q_PROPERTY(bool behaveAsPlasmaPanel READ behaveAsPlasmaPanel WRITE setBehaveAsPlasmaPanel NOTIFY behaveAsPlasmaPanelChanged) Q_PROPERTY(bool contextMenuIsShown READ contextMenuIsShown NOTIFY contextMenuIsShownChanged) Q_PROPERTY(bool dockWinBehavior READ dockWinBehavior WRITE setDockWinBehavior NOTIFY dockWinBehaviorChanged) //! 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 onPrimary READ onPrimary WRITE setOnPrimary NOTIFY onPrimaryChanged) 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 maxThickness READ maxThickness WRITE setMaxThickness NOTIFY maxThicknessChanged) Q_PROPERTY(int normalThickness READ normalThickness WRITE setNormalThickness NOTIFY normalThicknessChanged) Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged) Q_PROPERTY(float maxLength READ maxLength WRITE setMaxLength NOTIFY maxLengthChanged) Q_PROPERTY(Latte::ViewPart::Effects *effects READ effects NOTIFY effectsChanged) Q_PROPERTY(Layout *managedLayout READ managedLayout WRITE setManagedLayout NOTIFY managedLayoutChanged) Q_PROPERTY(Latte::ViewPart::Positioner *positioner READ positioner NOTIFY positionerChanged) Q_PROPERTY(VisibilityManager *visibility READ visibility NOTIFY visibilityChanged) Q_PROPERTY(QRect absoluteGeometry READ absGeometry NOTIFY absGeometryChanged) Q_PROPERTY(QRect localGeometry READ localGeometry WRITE setLocalGeometry NOTIFY localGeometryChanged) Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) public: - DockView(Plasma::Corona *corona, QScreen *targetScreen = nullptr, bool dockWindowBehavior = false); - virtual ~DockView(); + View(Plasma::Corona *corona, QScreen *targetScreen = nullptr, bool dockWindowBehavior = false); + virtual ~View(); void init(); bool alternativesIsShown() const; void setAlternativesIsShown(bool show); bool inDelete() const; bool onPrimary() const; void setOnPrimary(bool flag); int currentThickness() const; bool behaveAsPlasmaPanel() const; void setBehaveAsPlasmaPanel(bool behavior); bool contextMenuIsShown() const; bool dockWinBehavior() const; void setDockWinBehavior(bool dock); bool inEditMode() const; void setInEditMode(bool edit); bool isPreferredForShortcuts() const; void setIsPreferredForShortcuts(bool preferred); float maxLength() const; void setMaxLength(float length); int fontPixelSize() const; void setFontPixelSize(int size); int maxThickness() const; void setMaxThickness(int thickness); int normalThickness() const; void setNormalThickness(int thickness); int offset() const; void setOffset(int offset); int alignment() const; void setAlignment(int alignment); QRect absGeometry() const; QRect screenGeometry() const; QRect localGeometry() const; void setLocalGeometry(const QRect &geometry); bool settingsWindowIsShown(); void showSettingsWindow(); ViewPart::Effects *effects() const; ViewPart::Positioner *positioner() const; VisibilityManager *visibility() const; Layout *managedLayout() const; void setManagedLayout(Layout *layout); KWayland::Client::PlasmaShellSurface *surface(); 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 removeDock(); Q_INVOKABLE void copyDock(); Q_INVOKABLE QVariantList containmentActions(); Q_INVOKABLE void deactivateApplets(); Q_INVOKABLE void moveToLayout(QString layoutName); Q_INVOKABLE void removeTasksPlasmoid(); Q_INVOKABLE void setBlockHiding(bool block); Q_INVOKABLE void toggleAppletExpanded(const int id); Q_INVOKABLE bool mimeContainsPlasmoid(QMimeData *mimeData, QString name); Q_INVOKABLE bool tasksPresent(); Q_INVOKABLE bool latteTasksPresent(); void updateAbsDockGeometry(bool bypassChecks = false); Q_INVOKABLE void disableGrabItemBehavior(); Q_INVOKABLE void restoreGrabItemBehavior(); protected slots: void showConfigurationInterface(Plasma::Applet *applet) override; protected: bool event(QEvent *ev) override; void mousePressEvent(QMouseEvent *event) override; signals: void addInternalViewSplitter(); void removeInternalViewSplitter(); void eventTriggered(QEvent *ev); void activitiesChanged(); void alternativesIsShownChanged(); void alignmentChanged(); void behaveAsPlasmaPanelChanged(); void contextMenuIsShownChanged(); void dockLocationChanged(); void dockWinBehaviorChanged(); void effectsChanged(); void fontPixelSizeChanged(); void widthChanged(); void heightChanged(); void inEditModeChanged(); void isPreferredForShortcutsChanged(); void localGeometryChanged(); void managedLayoutChanged(); void maxLengthChanged(); void maxThicknessChanged(); void normalThicknessChanged(); void offsetChanged(); void onPrimaryChanged(); void visibilityChanged(); void positionerChanged(); void screenGeometryChanged(); void xChanged(); void yChanged(); void absGeometryChanged(const QRect &geometry); private slots: void availableScreenRectChanged(); void hideWindowsForSlidingOut(); - void preferredViewForShortcutsChangedSlot(DockView *view); + void preferredViewForShortcutsChangedSlot(Latte::View *view); void statusChanged(Plasma::Types::ItemStatus); void restoreConfig(); void saveConfig(); private: void applyActivitiesToWindows(); void initSignalingForLocationChangeSliding(); void setupWaylandIntegration(); void updateAppletContainsMethod(); private: Plasma::Containment *containmentById(uint id); bool m_alternativesIsShown{false}; bool m_behaveAsPlasmaPanel{false}; bool m_dockWinBehavior{true}; bool m_inDelete{false}; bool m_inEditMode{false}; bool m_isPreferredForShortcuts{false}; bool m_onPrimary{true}; int m_fontPixelSize{ -1}; int m_maxThickness{24}; int m_normalThickness{24}; int m_offset{0}; float m_maxLength{1}; Dock::Alignment m_alignment{Dock::Center}; QRect m_localGeometry; QRect m_absGeometry; Layout *m_managedLayout{nullptr}; QPointer m_configView; QPointer m_contextMenu; QPointer m_effects; QPointer m_positioner; QPointer m_visibility; //! Connections to release and bound for the managed layout std::array connectionsManagedLayout; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif diff --git a/app/dock/visibilitymanager.cpp b/app/view/visibilitymanager.cpp similarity index 98% rename from app/dock/visibilitymanager.cpp rename to app/view/visibilitymanager.cpp index a0deaed8..ebfd7bd3 100644 --- a/app/dock/visibilitymanager.cpp +++ b/app/view/visibilitymanager.cpp @@ -1,1254 +1,1254 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "visibilitymanager.h" #include "visibilitymanager_p.h" // local -#include "dockview.h" #include "positioner.h" #include "screenedgeghostwindow.h" +#include "view.h" #include "../dockcorona.h" #include "../layoutmanager.h" #include "../screenpool.h" #include "../wm/windowinfowrap.h" #include "../../liblattedock/extras.h" // Qt #include // KDE #include #include namespace Latte { //! BEGIN: VisiblityManagerPrivate implementation VisibilityManagerPrivate::VisibilityManagerPrivate(PlasmaQuick::ContainmentView *view, VisibilityManager *q) : QObject(nullptr), q(q), view(view) { - dockView = qobject_cast(view); + dockView = qobject_cast(view); dockCorona = qobject_cast(view->corona()); wm = dockCorona->wm(); if (dockView) { - connect(dockView, &DockView::eventTriggered, this, &VisibilityManagerPrivate::viewEventManager); - connect(dockView, &DockView::absGeometryChanged, this, &VisibilityManagerPrivate::setDockGeometry); + connect(dockView, &Latte::View::eventTriggered, this, &VisibilityManagerPrivate::viewEventManager); + connect(dockView, &Latte::View::absGeometryChanged, this, &VisibilityManagerPrivate::setDockGeometry); } timerStartUp.setInterval(5000); timerStartUp.setSingleShot(true); timerCheckWindows.setInterval(350); timerCheckWindows.setSingleShot(true); timerShow.setSingleShot(true); timerHide.setSingleShot(true); connect(&timerCheckWindows, &QTimer::timeout, this, &VisibilityManagerPrivate::checkAllWindows); connect(&timerShow, &QTimer::timeout, this, [this, q]() { if (isHidden) { // qDebug() << "must be shown"; emit this->q->mustBeShown(VisibilityManager::QPrivateSignal{}); } }); connect(&timerHide, &QTimer::timeout, this, [this]() { if (!blockHiding && !isHidden && !dragEnter) { // qDebug() << "must be hide"; emit this->q->mustBeHide(VisibilityManager::QPrivateSignal{}); } }); wm->setDockExtraFlags(*view); wm->addDock(view->winId()); restoreConfig(); } VisibilityManagerPrivate::~VisibilityManagerPrivate() { qDebug() << "VisibilityManagerPrivate deleting..."; wm->removeDockStruts(*view); wm->removeDock(view->winId()); if (edgeGhostWindow) { edgeGhostWindow->deleteLater(); } } inline void VisibilityManagerPrivate::setMode(Dock::Visibility mode) { if (this->mode == mode) return; Q_ASSERT_X(mode != Dock::None, q->staticMetaObject.className(), "set visibility to Dock::None"); // clear mode for (auto &c : connections) { disconnect(c); } if (mode != Dock::DodgeAllWindows && !enabledDynamicBackgroundFlag) { windows.clear(); } if (this->mode == Dock::AlwaysVisible) { wm->removeDockStruts(*view); } else { connections[3] = connect(wm, &WindowSystem::currentDesktopChanged , this, [&] { if (raiseOnDesktopChange) raiseDockTemporarily(); }); connections[4] = connect(wm, &WindowSystem::currentActivityChanged , this, [&]() { if (raiseOnActivityChange) raiseDockTemporarily(); else updateHiddenState(); }); } timerShow.stop(); timerHide.stop(); timerCheckWindows.stop(); this->mode = mode; switch (this->mode) { case Dock::AlwaysVisible: { //set wayland visibility mode if (dockView->surface()) { dockView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsGoBelow); } if (view->containment() && !dockView->inEditMode() && view->screen()) { updateStrutsBasedOnLayoutsAndActivities(); } connections[0] = connect(view->containment(), &Plasma::Containment::locationChanged , this, [&]() { if (dockView->inEditMode()) wm->removeDockStruts(*view); }); - connections[1] = connect(dockView, &DockView::inEditModeChanged + connections[1] = connect(dockView, &Latte::View::inEditModeChanged , this, [&]() { if (!dockView->inEditMode() && !dockView->positioner()->inLocationChangeAnimation() && view->screen()) wm->setDockStruts(*view, dockGeometry, view->containment()->location()); }); if (dockCorona && dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { connections[2] = connect(dockCorona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() { updateStrutsBasedOnLayoutsAndActivities(); }); - connections[3] = connect(dockView, &DockView::activitiesChanged, this, [&]() { + connections[3] = connect(dockView, &Latte::View::activitiesChanged, this, [&]() { updateStrutsBasedOnLayoutsAndActivities(); }); } raiseDock(true); } break; case Dock::AutoHide: { //set wayland visibility mode if (dockView->surface()) { dockView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } raiseDock(containsMouse); } break; case Dock::DodgeActive: { //set wayland visibility mode if (dockView->surface()) { dockView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } connections[0] = connect(wm, &WindowSystem::activeWindowChanged , this, &VisibilityManagerPrivate::dodgeActive); connections[1] = connect(wm, &WindowSystem::windowChanged , this, &VisibilityManagerPrivate::dodgeActive); dodgeActive(wm->activeWindow()); } break; case Dock::DodgeMaximized: { //set wayland visibility mode if (dockView->surface()) { dockView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } connections[0] = connect(wm, &WindowSystem::activeWindowChanged , this, &VisibilityManagerPrivate::dodgeMaximized); connections[1] = connect(wm, &WindowSystem::windowChanged , this, &VisibilityManagerPrivate::dodgeMaximized); dodgeMaximized(wm->activeWindow()); } break; case Dock::DodgeAllWindows: { //set wayland visibility mode if (dockView->surface()) { dockView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); } for (const auto &wid : wm->windows()) { windows.insert(wid, wm->requestInfo(wid)); } connections[0] = connect(wm, &WindowSystem::windowChanged , this, &VisibilityManagerPrivate::dodgeWindows); connections[1] = connect(wm, &WindowSystem::windowRemoved , this, [&](WindowId wid) { windows.remove(wid); timerCheckWindows.start(); }); connections[2] = connect(wm, &WindowSystem::windowAdded , this, [&](WindowId wid) { windows.insert(wid, wm->requestInfo(wid)); timerCheckWindows.start(); }); timerCheckWindows.start(); } break; case Dock::WindowsGoBelow: //set wayland visibility mode if (dockView->surface()) { dockView->surface()->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsGoBelow); } break; default: break; } view->containment()->config().writeEntry("visibility", static_cast(mode)); updateKWinEdgesSupport(); emit q->modeChanged(); } void VisibilityManagerPrivate::updateStrutsBasedOnLayoutsAndActivities() { bool multipleLayoutsAndCurrent = (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && dockView->managedLayout() && !dockView->positioner()->inLocationChangeAnimation() && dockView->managedLayout()->name() == dockCorona->layoutManager()->currentLayoutName()); if (dockCorona->layoutManager()->memoryUsage() == Dock::SingleLayout || multipleLayoutsAndCurrent) { wm->setDockStruts(*view, dockGeometry, view->location()); } else { wm->removeDockStruts(*view); } } void VisibilityManagerPrivate::setRaiseOnDesktop(bool enable) { if (enable == raiseOnDesktopChange) return; raiseOnDesktopChange = enable; emit q->raiseOnDesktopChanged(); } void VisibilityManagerPrivate::setRaiseOnActivity(bool enable) { if (enable == raiseOnActivityChange) return; raiseOnActivityChange = enable; emit q->raiseOnActivityChanged(); } inline void VisibilityManagerPrivate::setIsHidden(bool isHidden) { if (this->isHidden == isHidden) return; if (blockHiding && isHidden) { qWarning() << "isHidden property is blocked, ignoring update"; return; } this->isHidden = isHidden; if (q->supportsKWinEdges()) { bool inCurrentLayout = (dockCorona->layoutManager()->memoryUsage() == Dock::SingleLayout || (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && dockView->managedLayout() && !dockView->positioner()->inLocationChangeAnimation() && dockView->managedLayout()->name() == dockCorona->layoutManager()->currentLayoutName())); if (inCurrentLayout) { wm->setEdgeStateFor(edgeGhostWindow, isHidden); } else { wm->setEdgeStateFor(edgeGhostWindow, false); } } emit q->isHiddenChanged(); } void VisibilityManagerPrivate::setBlockHiding(bool blockHiding) { if (this->blockHiding == blockHiding) return; this->blockHiding = blockHiding; // qDebug() << "blockHiding:" << blockHiding; if (this->blockHiding) { timerHide.stop(); if (isHidden) { emit q->mustBeShown(VisibilityManager::QPrivateSignal{}); } } else { updateHiddenState(); } emit q->blockHidingChanged(); } inline void VisibilityManagerPrivate::setTimerShow(int msec) { timerShow.setInterval(msec); emit q->timerShowChanged(); } inline void VisibilityManagerPrivate::setTimerHide(int msec) { timerHide.setInterval(msec); emit q->timerHideChanged(); } inline void VisibilityManagerPrivate::raiseDock(bool raise) { if (blockHiding) return; if (raise) { timerHide.stop(); if (!timerShow.isActive()) { timerShow.start(); } } else if (!dragEnter) { timerShow.stop(); if (hideNow) { hideNow = false; emit q->mustBeHide(VisibilityManager::QPrivateSignal{}); } else if (!timerHide.isActive()) { timerHide.start(); } } } void VisibilityManagerPrivate::raiseDockTemporarily() { if (raiseTemporarily) return; raiseTemporarily = true; timerHide.stop(); timerShow.stop(); if (isHidden) emit q->mustBeShown(VisibilityManager::QPrivateSignal{}); QTimer::singleShot(qBound(1800, 2 * timerHide.interval(), 3000), this, [&]() { raiseTemporarily = false; hideNow = true; updateHiddenState(); }); } void VisibilityManagerPrivate::updateHiddenState() { if (dragEnter) return; switch (mode) { case Dock::AutoHide: raiseDock(containsMouse); break; case Dock::DodgeActive: dodgeActive(wm->activeWindow()); break; case Dock::DodgeMaximized: dodgeMaximized(wm->activeWindow()); break; case Dock::DodgeAllWindows: dodgeWindows(wm->activeWindow()); break; default: break; } } inline void VisibilityManagerPrivate::setDockGeometry(const QRect &geometry) { if (!view->containment()) return; this->dockGeometry = geometry; if (mode == Dock::AlwaysVisible && !dockView->inEditMode() && view->screen()) { updateStrutsBasedOnLayoutsAndActivities(); } } void VisibilityManagerPrivate::setWindowOnActivities(QWindow &window, const QStringList &activities) { wm->setWindowOnActivities(window, activities); } void VisibilityManagerPrivate::applyActivitiesToHiddenWindows(const QStringList &activities) { if (edgeGhostWindow) { wm->setWindowOnActivities(*edgeGhostWindow, activities); } } void VisibilityManagerPrivate::dodgeActive(WindowId wid) { if (raiseTemporarily) return; //!don't send false raiseDock signal when containing mouse if (containsMouse) { raiseDock(true); return; } auto winfo = wm->requestInfo(wid); if (!winfo.isValid() || !winfo.isActive()) { winfo = wm->requestInfo(wm->activeWindow()); if (!winfo.isValid()) { //! very rare case that window manager doesnt have any active window at all raiseDock(true); return; } } //! don't send false raiseDock signal when containing mouse, // Johan comment //! I dont know why that wasnt winfo.wid() //active window, but just wid//the window that made the call if (wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { bool overlaps{intersects(winfo)}; raiseDock(!overlaps); } } void VisibilityManagerPrivate::dodgeMaximized(WindowId wid) { if (raiseTemporarily) return; //!don't send false raiseDock signal when containing mouse if (containsMouse) { raiseDock(true); return; } auto winfo = wm->requestInfo(wid); if (!winfo.isValid() || !winfo.isActive()) { winfo = wm->requestInfo(wm->activeWindow()); if (!winfo.isValid()) { //! very rare case that window manager doesnt have any active window at all raiseDock(true); return; } } auto intersectsMaxVert = [&]() noexcept -> bool { return ((winfo.isMaxVert() || (view->screen() && view->screen()->availableSize().height() <= winfo.geometry().height())) && intersects(winfo)); }; auto intersectsMaxHoriz = [&]() noexcept -> bool { return ((winfo.isMaxHoriz() || (view->screen() && view->screen()->availableSize().width() <= winfo.geometry().width())) && intersects(winfo)); }; //! don't send false raiseDock signal when containing mouse, // Johan comment //! I dont know why that wasnt winfo.wid() //active window, but just wid//the window that made the call if (wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { bool overlapsMaximized{view->formFactor() == Plasma::Types::Vertical ? intersectsMaxHoriz() : intersectsMaxVert()}; raiseDock(!overlapsMaximized); } } void VisibilityManagerPrivate::dodgeWindows(WindowId wid) { if (raiseTemporarily) return; if (windows.find(wid) == std::end(windows)) return; //!don't send false raiseDock signal when containing mouse if (containsMouse) { raiseDock(true); return; } windows[wid] = wm->requestInfo(wid); auto &winfo = windows[wid]; if (!winfo.isValid() || !wm->isOnCurrentDesktop(wid) || !wm->isOnCurrentActivity(wid)) return; if (intersects(winfo)) raiseDock(false); else timerCheckWindows.start(); } void VisibilityManagerPrivate::checkAllWindows() { if (raiseTemporarily) return; bool raise{true}; bool existsFaultyWindow{false}; for (const auto &winfo : windows) { // if (winfo.geometry() == QRect(0, 0, 0, 0)) { existsFaultyWindow = true; } if (!winfo.isValid() || !wm->isOnCurrentDesktop(winfo.wid()) || !wm->isOnCurrentActivity(winfo.wid())) continue; if (winfo.isFullscreen()) { raise = false; break; } else if (intersects(winfo)) { raise = false; break; } } cleanupFaultyWindows(); raiseDock(raise); } inline bool VisibilityManagerPrivate::intersects(const WindowInfoWrap &winfo) { return (!winfo.isMinimized() && winfo.geometry().intersects(dockGeometry) && !winfo.isShaded()); } inline void VisibilityManagerPrivate::saveConfig() { if (!view->containment()) return; auto config = view->containment()->config(); config.writeEntry("enableKWinEdges", enableKWinEdgesFromUser); config.writeEntry("timerShow", timerShow.interval()); config.writeEntry("timerHide", timerHide.interval()); config.writeEntry("raiseOnDesktopChange", raiseOnDesktopChange); config.writeEntry("raiseOnActivityChange", raiseOnActivityChange); view->containment()->configNeedsSaving(); } inline void VisibilityManagerPrivate::restoreConfig() { if (!view->containment()) return; auto config = view->containment()->config(); timerShow.setInterval(config.readEntry("timerShow", 0)); timerHide.setInterval(config.readEntry("timerHide", 700)); emit q->timerShowChanged(); emit q->timerHideChanged(); enableKWinEdgesFromUser = config.readEntry("enableKWinEdges", true); emit q->enableKWinEdgesChanged(); setRaiseOnDesktop(config.readEntry("raiseOnDesktopChange", false)); setRaiseOnActivity(config.readEntry("raiseOnActivityChange", false)); auto mode = [&]() { return static_cast(view->containment()->config() .readEntry("visibility", static_cast(Dock::DodgeActive))); }; if (mode() == Dock::AlwaysVisible) { setMode(Dock::AlwaysVisible); } else { connect(&timerStartUp, &QTimer::timeout, this, [ &, mode]() { setMode(mode()); }); connect(view->containment(), &Plasma::Containment::userConfiguringChanged , this, [&](bool configuring) { if (configuring && timerStartUp.isActive()) timerStartUp.start(100); }); timerStartUp.start(); } connect(view->containment(), &Plasma::Containment::userConfiguringChanged , this, [&](bool configuring) { if (!configuring) saveConfig(); }); } void VisibilityManagerPrivate::setContainsMouse(bool contains) { if (containsMouse == contains) { return; } containsMouse = contains; emit q->containsMouseChanged(); if (contains && mode != Dock::AlwaysVisible) { raiseDock(true); } } void VisibilityManagerPrivate::viewEventManager(QEvent *ev) { switch (ev->type()) { case QEvent::Enter: setContainsMouse(true); break; case QEvent::Leave: setContainsMouse(false); updateHiddenState(); break; case QEvent::DragEnter: dragEnter = true; if (isHidden) emit q->mustBeShown(VisibilityManager::QPrivateSignal{}); break; case QEvent::DragLeave: case QEvent::Drop: dragEnter = false; updateHiddenState(); break; case QEvent::Show: wm->setDockExtraFlags(*view); break; default: break; } } void VisibilityManagerPrivate::cleanupFaultyWindows() { foreach (auto key, windows.keys()) { auto winfo = windows[key]; //! garbage windows removing if (winfo.geometry() == QRect(0, 0, 0, 0)) { //qDebug() << "Faulty Geometry ::: " << winfo.wid(); windows.remove(key); } } } //! Dynamic Background functions void VisibilityManagerPrivate::setEnabledDynamicBackground(bool active) { if (enabledDynamicBackgroundFlag == active) { return; } enabledDynamicBackgroundFlag = active; if (active) { if (mode != Dock::DodgeAllWindows) { for (const auto &wid : wm->windows()) { windows.insert(wid, wm->requestInfo(wid)); } } connectionsDynBackground[0] = connect(view->corona(), &Plasma::Corona::availableScreenRectChanged, this, &VisibilityManagerPrivate::updateAvailableScreenGeometry); connectionsDynBackground[1] = connect(wm, &WindowSystem::windowChanged, this, [&](WindowId wid) { windows[wid] = wm->requestInfo(wid); updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[2] = connect(wm, &WindowSystem::windowRemoved, this, [&](WindowId wid) { windows.remove(wid); }); connectionsDynBackground[3] = connect(wm, &WindowSystem::windowAdded, this, [&](WindowId wid) { windows.insert(wid, wm->requestInfo(wid)); updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[4] = connect(wm, &WindowSystem::activeWindowChanged, this, [&](WindowId wid) { if (windows.contains(lastActiveWindowWid)) { windows[lastActiveWindowWid] = wm->requestInfo(lastActiveWindowWid); } windows[wid] = wm->requestInfo(wid); lastActiveWindowWid = wid; updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[5] = connect(wm, &WindowSystem::currentDesktopChanged, this, [&] { updateDynamicBackgroundWindowFlags(); }); connectionsDynBackground[6] = connect(wm, &WindowSystem::currentActivityChanged, this, [&] { updateDynamicBackgroundWindowFlags(); }); updateAvailableScreenGeometry(); updateDynamicBackgroundWindowFlags(); } else { // clear mode for (auto &c : connectionsDynBackground) { disconnect(c); } if (mode != Dock::DodgeAllWindows) { windows.clear(); } // ATTENTION: this was creating a crash under wayland environment through the blur effect // setExistsWindowMaximized(false); // setExistsWindowSnapped(false); } emit q->enabledDynamicBackgroundChanged(); } void VisibilityManagerPrivate::setExistsWindowMaximized(bool windowMaximized) { if (windowIsMaximizedFlag == windowMaximized) { return; } windowIsMaximizedFlag = windowMaximized; emit q->existsWindowMaximizedChanged(); } void VisibilityManagerPrivate::setExistsWindowSnapped(bool windowSnapped) { if (windowIsSnappedFlag == windowSnapped) { return; } windowIsSnappedFlag = windowSnapped; emit q->existsWindowSnappedChanged(); } void VisibilityManagerPrivate::setTouchingWindowScheme(SchemeColors *scheme) { if (touchingScheme == scheme) { return; } touchingScheme = scheme; emit q->touchingWindowSchemeChanged(); } void VisibilityManagerPrivate::updateAvailableScreenGeometry() { if (!view || !view->containment()) { return; } int currentScrId = dockView->positioner()->currentScreenId(); QRect tempAvailableScreenGeometry = dockCorona->availableScreenRectWithCriteria(currentScrId, {Dock::AlwaysVisible}, {}); if (tempAvailableScreenGeometry != availableScreenGeometry) { availableScreenGeometry = tempAvailableScreenGeometry; snappedWindowsGeometries.clear(); //! for top dock the snapped geometries would be int halfWidth1 = std::floor(availableScreenGeometry.width() / 2); int halfWidth2 = availableScreenGeometry.width() - halfWidth1; int halfHeight1 = std::floor((availableScreenGeometry.height()) / 2); int halfHeight2 = availableScreenGeometry.height() - halfHeight1; int x1 = availableScreenGeometry.x(); int x2 = availableScreenGeometry.x() + halfWidth1; int y1 = availableScreenGeometry.y(); int y2 = availableScreenGeometry.y() + halfHeight1; QRect snap1; QRect snap2; QRect snap3; QRect snap4; if (view->formFactor() == Plasma::Types::Horizontal) { if (view->location() == Plasma::Types::TopEdge) { snap1 = QRect(x1, y1, halfWidth1, halfHeight1); snap3 = QRect(x2, y1, halfWidth2, halfHeight1); } else if ((view->location() == Plasma::Types::BottomEdge)) { snap1 = QRect(x1, y2, halfWidth1, halfHeight2); snap3 = QRect(x2, y2, halfWidth2, halfHeight2); } snap2 = QRect(x1, y1, halfWidth1, availableScreenGeometry.height()); snap4 = QRect(x2, y1, halfWidth2, availableScreenGeometry.height()); } else if (view->formFactor() == Plasma::Types::Vertical) { QRect snap5; if (view->location() == Plasma::Types::LeftEdge) { snap1 = QRect(x1, y1, halfWidth1, halfHeight1); snap3 = QRect(x1, y2, halfWidth1, halfHeight2); snap5 = QRect(x1, y1, halfWidth1, availableScreenGeometry.height()); } else if ((view->location() == Plasma::Types::RightEdge)) { snap1 = QRect(x2, y1, halfWidth2, halfHeight1); snap3 = QRect(x2, y2, halfWidth2, halfHeight2); snap5 = QRect(x2, y1, halfWidth2, availableScreenGeometry.height()); } snap2 = QRect(x1, y1, availableScreenGeometry.width(), halfHeight1); snap4 = QRect(x1, y2, availableScreenGeometry.width(), halfHeight2); snappedWindowsGeometries.append(snap5); } snappedWindowsGeometries.append(snap1); snappedWindowsGeometries.append(snap2); snappedWindowsGeometries.append(snap3); snappedWindowsGeometries.append(snap4); updateDynamicBackgroundWindowFlags(); } } bool VisibilityManagerPrivate::isMaximizedInCurrentScreen(const WindowInfoWrap &winfo) { //! updated implementation to identify the screen that the maximized window is present //! in order to avoid: https://bugs.kde.org/show_bug.cgi?id=397700 if (winfo.isValid() && !winfo.isMinimized() && wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { if (winfo.isMaximized() && availableScreenGeometry.contains(winfo.geometry().center())) { return true; } } return false; } bool VisibilityManagerPrivate::isTouchingPanelEdge(const WindowInfoWrap &winfo) { if (winfo.isValid() && !winfo.isMinimized() && wm->isOnCurrentDesktop(winfo.wid()) && wm->isOnCurrentActivity(winfo.wid())) { bool touchingPanelEdge{false}; QRect screenGeometry = dockView->screenGeometry(); bool inCurrentScreen{screenGeometry.contains(winfo.geometry().topLeft()) || screenGeometry.contains(winfo.geometry().bottomRight())}; if (inCurrentScreen) { if (view->location() == Plasma::Types::TopEdge) { touchingPanelEdge = (winfo.geometry().y() == availableScreenGeometry.y()); } else if (view->location() == Plasma::Types::BottomEdge) { touchingPanelEdge = (winfo.geometry().bottom() == availableScreenGeometry.bottom()); } else if (view->location() == Plasma::Types::LeftEdge) { touchingPanelEdge = (winfo.geometry().x() == availableScreenGeometry.x()); } else if (view->location() == Plasma::Types::RightEdge) { touchingPanelEdge = (winfo.geometry().right() == availableScreenGeometry.right()); } } return touchingPanelEdge; } return false; } void VisibilityManagerPrivate::updateDynamicBackgroundWindowFlags() { bool foundSnap{false}; bool foundMaximized{false}; //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0), //! maybe a garbage collector here is a good idea!!! bool existsFaultyWindow{false}; WindowId maxWinId; WindowId snapWinId; for (const auto &winfo : windows) { if (isMaximizedInCurrentScreen(winfo)) { foundMaximized = true; maxWinId = winfo.wid(); } if (winfo.isActive() && isTouchingPanelEdge(winfo)) { foundSnap = true; snapWinId = winfo.wid(); } if (!existsFaultyWindow && winfo.geometry() == QRect(0, 0, 0, 0)) { existsFaultyWindow = true; } //qDebug() << "window geometry ::: " << winfo.geometry(); } //! active windows that are touching the panel edge should have a higher priority //! this is why are identified first if (!foundSnap) { for (const auto &winfo : windows) { if ((winfo.isKeepAbove() && isTouchingPanelEdge(winfo)) || (!winfo.isActive() && snappedWindowsGeometries.contains(winfo.geometry()))) { foundSnap = true; snapWinId = winfo.wid(); break; } } } if (existsFaultyWindow) { cleanupFaultyWindows(); } /*if (!foundMaximized && !foundSnap) { qDebug() << "SCREEN GEOMETRY : " << availableScreenGeometry; qDebug() << "SNAPS ::: " << snappedWindowsGeometries; } qDebug() << " FOUND ::: " << foundMaximized << foundSnap;*/ setExistsWindowMaximized(foundMaximized); setExistsWindowSnapped(foundSnap); //! update color scheme for touching window if (foundSnap) { //! first the snap one because that would mean it is active setTouchingWindowScheme(wm->schemeForWindow(snapWinId)); } else if (foundMaximized) { setTouchingWindowScheme(wm->schemeForWindow(maxWinId)); } else { setTouchingWindowScheme(nullptr); } } //! KWin Edges Support functions void VisibilityManagerPrivate::setEnableKWinEdges(bool enable) { if (enableKWinEdgesFromUser == enable) { return; } enableKWinEdgesFromUser = enable; emit q->enableKWinEdgesChanged(); updateKWinEdgesSupport(); } void VisibilityManagerPrivate::updateKWinEdgesSupport() { if (mode == Dock::AutoHide || mode == Dock::DodgeActive || mode == Dock::DodgeAllWindows || mode == Dock::DodgeMaximized) { if (enableKWinEdgesFromUser) { createEdgeGhostWindow(); } else if (!enableKWinEdgesFromUser) { deleteEdgeGhostWindow(); } } else if (mode == Dock::AlwaysVisible || mode == Dock::WindowsGoBelow) { deleteEdgeGhostWindow(); } } void VisibilityManagerPrivate::createEdgeGhostWindow() { if (!edgeGhostWindow) { edgeGhostWindow = new ScreenEdgeGhostWindow(dockView); wm->setDockExtraFlags(*edgeGhostWindow); connect(edgeGhostWindow, &ScreenEdgeGhostWindow::containsMouseChanged, this, [ = ](bool contains) { if (contains) { emit this->q->mustBeShown(VisibilityManager::QPrivateSignal{}); } }); connectionsKWinEdges[0] = connect(wm, &WindowSystem::currentActivityChanged, this, [&]() { bool inCurrentLayout = (dockCorona->layoutManager()->memoryUsage() == Dock::SingleLayout || (dockCorona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && dockView->managedLayout() && !dockView->positioner()->inLocationChangeAnimation() && dockView->managedLayout()->name() == dockCorona->layoutManager()->currentLayoutName())); if (edgeGhostWindow) { if (inCurrentLayout) { wm->setEdgeStateFor(edgeGhostWindow, isHidden); } else { wm->setEdgeStateFor(edgeGhostWindow, false); } } }); emit q->supportsKWinEdgesChanged(); } } void VisibilityManagerPrivate::deleteEdgeGhostWindow() { if (edgeGhostWindow) { edgeGhostWindow->deleteLater(); edgeGhostWindow = nullptr; for (auto &c : connectionsKWinEdges) { disconnect(c); } emit q->supportsKWinEdgesChanged(); } } //! Window Functions void VisibilityManagerPrivate::requestToggleMaximizeForActiveWindow() { WindowInfoWrap actInfo = wm->requestInfoActive(); //active window can be toggled only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && dockView->screenGeometry().contains(actInfo.geometry().center())) { wm->requestToggleMaximized(actInfo.wid()); } } void VisibilityManagerPrivate::requestMoveActiveWindow(int localX, int localY) { WindowInfoWrap actInfo = wm->requestInfoActive(); //active window can be dragged only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && dockView->screenGeometry().contains(actInfo.geometry().center())) { QPoint globalPoint{dockView->x() + localX, dockView->y() + localY}; wm->requestMoveWindow(actInfo.wid(), globalPoint); } } bool VisibilityManagerPrivate::activeWindowCanBeDragged() { WindowInfoWrap actInfo = wm->requestInfoActive(); //active window can be dragged only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && dockView->screenGeometry().contains(actInfo.geometry().center())) { return wm->windowCanBeDragged(actInfo.wid()); } return false; } //! END: VisibilityManagerPrivate implementation //! BEGIN: VisibilityManager implementation VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view) : d(new VisibilityManagerPrivate(view, this)) { - DockView *dockView = qobject_cast(view); + Latte::View *dockView = qobject_cast(view); if (dockView) { connect(this, &VisibilityManager::modeChanged, dockView->corona(), &Plasma::Corona::availableScreenRectChanged); } } VisibilityManager::~VisibilityManager() { qDebug() << "VisibilityManager deleting..."; delete d; } Dock::Visibility VisibilityManager::mode() const { return d->mode; } void VisibilityManager::setMode(Dock::Visibility mode) { d->setMode(mode); } void VisibilityManager::setWindowOnActivities(QWindow &window, const QStringList &activities) { d->setWindowOnActivities(window, activities); } void VisibilityManager::applyActivitiesToHiddenWindows(const QStringList &activities) { d->applyActivitiesToHiddenWindows(activities); } bool VisibilityManager::raiseOnDesktop() const { return d->raiseOnDesktopChange; } void VisibilityManager::setRaiseOnDesktop(bool enable) { d->setRaiseOnDesktop(enable); } bool VisibilityManager::raiseOnActivity() const { return d->raiseOnActivityChange; } void VisibilityManager::setRaiseOnActivity(bool enable) { d->setRaiseOnActivity(enable); } bool VisibilityManager::isHidden() const { return d->isHidden; } void VisibilityManager::setIsHidden(bool isHidden) { d->setIsHidden(isHidden); } bool VisibilityManager::blockHiding() const { return d->blockHiding; } void VisibilityManager::setBlockHiding(bool blockHiding) { d->setBlockHiding(blockHiding); } bool VisibilityManager::containsMouse() const { return d->containsMouse; } int VisibilityManager::timerShow() const { return d->timerShow.interval(); } void VisibilityManager::setTimerShow(int msec) { d->setTimerShow(msec); } int VisibilityManager::timerHide() const { return d->timerHide.interval(); } void VisibilityManager::setTimerHide(int msec) { d->setTimerHide(msec); } //! Dynamic Background functions bool VisibilityManager::enabledDynamicBackground() const { return d->enabledDynamicBackgroundFlag; } void VisibilityManager::setEnabledDynamicBackground(bool active) { d->setEnabledDynamicBackground(active); } bool VisibilityManager::existsWindowMaximized() const { return d->windowIsMaximizedFlag; } bool VisibilityManager::existsWindowSnapped() const { return d->windowIsSnappedFlag; } SchemeColors *VisibilityManager::touchingWindowScheme() const { return d->touchingScheme; } //! KWin Edges Support functions bool VisibilityManager::enableKWinEdges() const { return d->enableKWinEdgesFromUser; } void VisibilityManager::setEnableKWinEdges(bool enable) { d->setEnableKWinEdges(enable); } bool VisibilityManager::supportsKWinEdges() const { return (d->edgeGhostWindow != nullptr); } //! Window Functions void VisibilityManager::requestToggleMaximizeForActiveWindow() { d->requestToggleMaximizeForActiveWindow(); } void VisibilityManager::requestMoveActiveWindow(int localX, int localY) { d->requestMoveActiveWindow(localX, localY); } bool VisibilityManager::activeWindowCanBeDragged() { return d->activeWindowCanBeDragged(); } //! END: VisibilityManager implementation } diff --git a/app/dock/visibilitymanager.h b/app/view/visibilitymanager.h similarity index 100% rename from app/dock/visibilitymanager.h rename to app/view/visibilitymanager.h diff --git a/app/dock/visibilitymanager_p.h b/app/view/visibilitymanager_p.h similarity index 98% rename from app/dock/visibilitymanager_p.h rename to app/view/visibilitymanager_p.h index 0cb0bb9c..44ba6cdb 100644 --- a/app/dock/visibilitymanager_p.h +++ b/app/view/visibilitymanager_p.h @@ -1,142 +1,142 @@ #ifndef VISIBILITYMANAGERPRIVATE_H #define VISIBILITYMANAGERPRIVATE_H // local #include "../schemecolors.h" #include "../wm/abstractwindowinterface.h" #include "../wm/windowinfowrap.h" #include "../../liblattedock/dock.h" // C++ #include #include // Qt #include #include #include #include #include // Plasma #include namespace Latte { class DockCorona; -class DockView; +class View; class VisibilityManager; class ScreenEdgeGhostWindow; /*! * \brief The Latte::VisibilityManagerPrivate is a class d-pointer */ class VisibilityManagerPrivate : public QObject { Q_GADGET public: VisibilityManagerPrivate(PlasmaQuick::ContainmentView *view, VisibilityManager *q); ~VisibilityManagerPrivate(); void setMode(Dock::Visibility mode); void setRaiseOnDesktop(bool enable); void setRaiseOnActivity(bool enable); void setContainsMouse(bool contains); void setIsHidden(bool isHidden); void setBlockHiding(bool blockHiding); void setTimerShow(int msec); void setTimerHide(int msec); void raiseDock(bool raise); void raiseDockTemporarily(); void updateHiddenState(); //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0), //! this is a garbage collector to collect such windows in order to not break the windows array validity. void cleanupFaultyWindows(); //! Dynamic Background Feature void setEnabledDynamicBackground(bool active); void setExistsWindowMaximized(bool windowMaximized); void setExistsWindowSnapped(bool windowSnapped); void setTouchingWindowScheme(SchemeColors *scheme); void updateAvailableScreenGeometry(); void updateDynamicBackgroundWindowFlags(); //! KWin Edges Support functions void createEdgeGhostWindow(); void deleteEdgeGhostWindow(); void setEnableKWinEdges(bool enable); void updateKWinEdgesSupport(); void setDockGeometry(const QRect &rect); void setWindowOnActivities(QWindow &window, const QStringList &activities); void applyActivitiesToHiddenWindows(const QStringList &activities); void windowAdded(WindowId id); void dodgeActive(WindowId id); void dodgeMaximized(WindowId id); void dodgeWindows(WindowId id); void checkAllWindows(); bool intersects(const WindowInfoWrap &winfo); bool isMaximizedInCurrentScreen(const WindowInfoWrap &winfo); bool isTouchingPanelEdge(const WindowInfoWrap &winfo); void updateStrutsBasedOnLayoutsAndActivities(); void requestToggleMaximizeForActiveWindow(); void requestMoveActiveWindow(int localX, int localY); bool activeWindowCanBeDragged(); void saveConfig(); void restoreConfig(); void viewEventManager(QEvent *ev); VisibilityManager *q; PlasmaQuick::ContainmentView *view; AbstractWindowInterface *wm; Dock::Visibility mode{Dock::None}; std::array connections; QMap windows; QTimer timerShow; QTimer timerHide; QTimer timerCheckWindows; QTimer timerStartUp; QRect dockGeometry; bool isHidden{false}; bool dragEnter{false}; bool blockHiding{false}; bool containsMouse{false}; bool raiseTemporarily{false}; bool raiseOnDesktopChange{false}; bool raiseOnActivityChange{false}; bool hideNow{false}; //! Dynamic Background flags and needed information bool enabledDynamicBackgroundFlag{false}; bool windowIsSnappedFlag{false}; bool windowIsMaximizedFlag{false}; QRect availableScreenGeometry; QList snappedWindowsGeometries; std::array connectionsDynBackground; WindowId lastActiveWindowWid; SchemeColors *touchingScheme{nullptr}; //! KWin Edges bool enableKWinEdgesFromUser{true}; std::array connectionsKWinEdges; ScreenEdgeGhostWindow *edgeGhostWindow{nullptr}; DockCorona *dockCorona{nullptr}; - DockView *dockView{nullptr}; + Latte::View *dockView{nullptr}; }; } #endif // VISIBILITYMANAGERPRIVATE_H diff --git a/app/wm/waylandinterface.cpp b/app/wm/waylandinterface.cpp index e8f7d7a5..562951de 100644 --- a/app/wm/waylandinterface.cpp +++ b/app/wm/waylandinterface.cpp @@ -1,440 +1,440 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "waylandinterface.h" // local #include "dockcorona.h" -#include "dock/dockview.h" -#include "dock/screenedgeghostwindow.h" +#include "view/screenedgeghostwindow.h" +#include "view/view.h" #include "../liblattedock/extras.h" // Qt #include #include #include #include #include #include // KDE #include #include #include // X11 #include using namespace KWayland::Client; namespace Latte { class Private::GhostWindow : public QRasterWindow { Q_OBJECT public: GhostWindow(WaylandInterface *waylandInterface) : m_waylandInterface(waylandInterface) { setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); setupWaylandIntegration(); show(); } ~GhostWindow() { delete m_shellSurface; } void setGeometry(const QRect &rect) { QWindow::setGeometry(rect); setMaximumSize(rect.size()); m_shellSurface->setPosition(rect.topLeft()); } void setupWaylandIntegration() { using namespace KWayland::Client; if (m_shellSurface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; m_shellSurface = m_waylandInterface->waylandDockCoronaInterface()->createSurface(s, this); qDebug() << "wayland ghost window surface was created..."; m_shellSurface->setSkipTaskbar(true); m_shellSurface->setPanelTakesFocus(false); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); } KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; WaylandInterface *m_waylandInterface{nullptr}; }; WaylandInterface::WaylandInterface(QObject *parent) : AbstractWindowInterface(parent) { m_corona = qobject_cast(parent); m_activities = new KActivities::Consumer(this); connect(m_activities.data(), &KActivities::Consumer::currentActivityChanged , this, &WaylandInterface::currentActivityChanged); } WaylandInterface::~WaylandInterface() { } void WaylandInterface::init() { } void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement) { m_windowManagement = windowManagement; connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, &WaylandInterface::windowCreatedProxy); connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, this, [&]() noexcept { auto w = m_windowManagement->activeWindow(); emit activeWindowChanged(w ? w->internalId() : 0); }, Qt::QueuedConnection); } KWayland::Client::PlasmaShell *WaylandInterface::waylandDockCoronaInterface() const { return m_corona->waylandDockCoronaInterface(); } void WaylandInterface::setDockExtraFlags(QWindow &view) { Q_UNUSED(view) } void WaylandInterface::setDockStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location) { if (!m_ghostWindows.contains(view.winId())) m_ghostWindows[view.winId()] = new Private::GhostWindow(this); auto w = m_ghostWindows[view.winId()]; switch (location) { case Plasma::Types::TopEdge: case Plasma::Types::BottomEdge: w->setGeometry({rect.x() + rect.width() / 2, rect.y(), 1, rect.height()}); break; case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: w->setGeometry({rect.x(), rect.y() + rect.height() / 2, rect.width(), 1}); break; default: break; } } void WaylandInterface::setWindowOnActivities(QWindow &window, const QStringList &activities) { //! needs to updated to wayland case // KWindowSystem::setOnActivities(view.winId(), activities); } void WaylandInterface::removeDockStruts(QWindow &view) const { delete m_ghostWindows.take(view.winId()); } WindowId WaylandInterface::activeWindow() const { if (!m_windowManagement) { return 0; } auto wid = m_windowManagement->activeWindow(); return wid ? wid->internalId() : 0; } const std::list &WaylandInterface::windows() const { return m_windows; } void WaylandInterface::setKeepAbove(const QDialog &dialog, bool above) const { if (above) { KWindowSystem::setState(dialog.winId(), NET::KeepAbove); } else { KWindowSystem::clearState(dialog.winId(), NET::KeepAbove); } } void WaylandInterface::skipTaskBar(const QDialog &dialog) const { KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); } void WaylandInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) const { auto slideLocation = KWindowEffects::NoEdge; switch (location) { case Slide::Top: slideLocation = KWindowEffects::TopEdge; break; case Slide::Bottom: slideLocation = KWindowEffects::BottomEdge; break; case Slide::Left: slideLocation = KWindowEffects::LeftEdge; break; case Slide::Right: slideLocation = KWindowEffects::RightEdge; break; default: break; } KWindowEffects::slideWindow(view.winId(), slideLocation, -1); } void WaylandInterface::enableBlurBehind(QWindow &view) const { KWindowEffects::enableBlurBehind(view.winId()); } void WaylandInterface::setEdgeStateFor(QWindow *view, bool active) const { ScreenEdgeGhostWindow *window = qobject_cast(view); if (!window) { return; } if (window->parentDock()->surface() && window->parentDock()->visibility() && (window->parentDock()->visibility()->mode() == Dock::DodgeActive || window->parentDock()->visibility()->mode() == Dock::DodgeMaximized || window->parentDock()->visibility()->mode() == Dock::DodgeAllWindows || window->parentDock()->visibility()->mode() == Dock::AutoHide)) { if (active) { window->showWithMask(); window->surface()->requestHideAutoHidingPanel(); } else { window->hideWithMask(); window->surface()->requestShowAutoHidingPanel(); } } } WindowInfoWrap WaylandInterface::requestInfoActive() const { if (!m_windowManagement) { return {}; } auto w = m_windowManagement->activeWindow(); if (!w) return {}; WindowInfoWrap winfoWrap; winfoWrap.setIsValid(true); winfoWrap.setWid(w->internalId()); winfoWrap.setIsActive(w->isActive()); winfoWrap.setIsMinimized(w->isMinimized()); winfoWrap.setIsMaxVert(w->isMaximized()); winfoWrap.setIsMaxHoriz(w->isMaximized()); winfoWrap.setIsFullscreen(w->isFullscreen()); winfoWrap.setIsShaded(w->isShaded()); winfoWrap.setGeometry(w->geometry()); winfoWrap.setIsKeepAbove(w->isKeepAbove()); winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); return winfoWrap; } bool WaylandInterface::isOnCurrentDesktop(WindowId wid) const { if (!m_windowManagement) { return false; } auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); //qDebug() << "desktop:" << (it != m_windowManagement->windows().constEnd() ? (*it)->virtualDesktop() : -1) << KWindowSystem::currentDesktop(); //return true; return it != m_windowManagement->windows().constEnd() && ((*it)->virtualDesktop() == KWindowSystem::currentDesktop() || (*it)->isOnAllDesktops()); } bool WaylandInterface::isOnCurrentActivity(WindowId wid) const { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); //TODO: Not yet implemented return it != m_windowManagement->windows().constEnd() && true; } WindowInfoWrap WaylandInterface::requestInfo(WindowId wid) const { WindowInfoWrap winfoWrap; auto w = windowFor(wid); if (w) { if (isValidWindow(w)) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setIsActive(w->isActive()); winfoWrap.setIsMinimized(w->isMinimized()); winfoWrap.setIsMaxVert(w->isMaximized()); winfoWrap.setIsMaxHoriz(w->isMaximized()); winfoWrap.setIsFullscreen(w->isFullscreen()); winfoWrap.setIsShaded(w->isShaded()); winfoWrap.setGeometry(w->geometry()); winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); } else if (w->appId() == QLatin1String("org.kde.plasmashell")) { winfoWrap.setIsValid(true); winfoWrap.setIsPlasmaDesktop(true); winfoWrap.setWid(wid); } } else { return {}; } return winfoWrap; } KWayland::Client::PlasmaWindow *WaylandInterface::windowFor(WindowId wid) const { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); if (it == m_windowManagement->windows().constEnd()) { return nullptr; } return *it; } bool WaylandInterface::windowCanBeDragged(WindowId wid) const { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && !winfo.isPlasmaDesktop() && !winfo.hasSkipTaskbar()); } void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from) const { if (windowCanBeDragged(wid)) { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestMove(); } } } void WaylandInterface::requestToggleMaximized(WindowId wid) const { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestToggleMaximized(); } } inline bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w) const { //! because wayland does not have any way yet to identify the window type //! a trick is to just consider windows as valid when they can be shown in the //! taskbar. Of course that creates issues with plasma native dialogs //! e.g. widgets explorer, Activities etc. that are not used to hide //! the dodge docks/panels appropriately return w->isValid() && !w->skipTaskbar(); } void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w) { if (!isValidWindow(w)) return; if (!mapper) mapper = new QSignalMapper(this); mapper->setMapping(w, w); connect(w, &PlasmaWindow::unmapped, this, [ &, win = w]() noexcept { mapper->removeMappings(win); m_windows.remove(win->internalId()); emit windowRemoved(win->internalId()); }); connect(w, SIGNAL(activeChanged()), mapper, SLOT(map())); connect(w, SIGNAL(fullscreenChanged()), mapper, SLOT(map())); connect(w, SIGNAL(geometryChanged()), mapper, SLOT(map())); connect(w, SIGNAL(maximizedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(minimizedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(shadedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(skipTaskbarChanged()), mapper, SLOT(map())); connect(w, SIGNAL(onAllDesktopsChanged()), mapper, SLOT(map())); connect(w, SIGNAL(virtualDesktopChanged()), mapper, SLOT(map())); connect(mapper, static_cast(&QSignalMapper::mapped) , this, [&](QObject * w) noexcept { //qDebug() << "window changed:" << qobject_cast(w)->appId(); emit windowChanged(qobject_cast(w)->internalId()); }); m_windows.push_back(w->internalId()); emit windowAdded(w->internalId()); } } #include "waylandinterface.moc" diff --git a/app/wm/xwindowinterface.cpp b/app/wm/xwindowinterface.cpp index 8620d220..748b5841 100644 --- a/app/wm/xwindowinterface.cpp +++ b/app/wm/xwindowinterface.cpp @@ -1,441 +1,441 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "xwindowinterface.h" // local -#include "dock/dockview.h" -#include "dock/screenedgeghostwindow.h" +#include "view/screenedgeghostwindow.h" +#include "view/view.h" #include "../liblattedock/extras.h" // Qt #include #include #include // KDE #include #include // X11 #include #include namespace Latte { XWindowInterface::XWindowInterface(QObject *parent) : AbstractWindowInterface(parent) { m_activities = new KActivities::Consumer(this); connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged , this, &AbstractWindowInterface::activeWindowChanged); connect(KWindowSystem::self() , static_cast (&KWindowSystem::windowChanged) , this, &XWindowInterface::windowChangedProxy); auto addWindow = [&](WindowId wid) { if (std::find(m_windows.cbegin(), m_windows.cend(), wid) == m_windows.cend()) { if (isValidWindow(KWindowInfo(wid.value(), NET::WMWindowType))) { m_windows.push_back(wid); emit windowAdded(wid); } } }; connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, addWindow); connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, [this](WindowId wid) noexcept { if (std::find(m_windows.cbegin(), m_windows.cend(), wid) != m_windows.end()) { m_windows.remove(wid); emit windowRemoved(wid); } }); connect(KWindowSystem::self(), &KWindowSystem::currentDesktopChanged , this, &XWindowInterface::currentDesktopChanged); connect(m_activities.data(), &KActivities::Consumer::currentActivityChanged , this, &XWindowInterface::currentActivityChanged); // fill windows list foreach (const auto &wid, KWindowSystem::self()->windows()) { addWindow(wid); } } XWindowInterface::~XWindowInterface() { } void XWindowInterface::setDockExtraFlags(QWindow &view) { NETWinInfo winfo(QX11Info::connection() , static_cast(view.winId()) , static_cast(view.winId()) , 0, 0); winfo.setAllowedActions(NET::ActionChangeDesktop); KWindowSystem::setType(view.winId(), NET::Dock); KWindowSystem::setState(view.winId(), NET::SkipTaskbar | NET::SkipPager); KWindowSystem::setOnAllDesktops(view.winId(), true); } void XWindowInterface::setDockStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) { NETExtendedStrut strut; const auto screen = view.screen(); const QRect currentScreen {screen->geometry()}; const QRect wholeScreen {{0, 0}, screen->virtualSize()}; switch (location) { case Plasma::Types::TopEdge: { const int topOffset {screen->geometry().top()}; strut.top_width = rect.height() + topOffset; strut.top_start = rect.x(); strut.top_end = rect.x() + rect.width() - 1; break; } case Plasma::Types::BottomEdge: { const int bottomOffset {wholeScreen.bottom() - currentScreen.bottom()}; strut.bottom_width = rect.height() + bottomOffset; strut.bottom_start = rect.x(); strut.bottom_end = rect.x() + rect.width() - 1; break; } case Plasma::Types::LeftEdge: { const int leftOffset = {screen->geometry().left()}; strut.left_width = rect.width() + leftOffset; strut.left_start = rect.y(); strut.left_end = rect.y() + rect.height() - 1; break; } case Plasma::Types::RightEdge: { const int rightOffset = {wholeScreen.right() - currentScreen.right()}; strut.right_width = rect.width() + rightOffset; strut.right_start = rect.y(); strut.right_end = rect.y() + rect.height() - 1; break; } default: qWarning() << "wrong location:" << qEnumToStr(location); return; } KWindowSystem::setExtendedStrut(view.winId(), strut.left_width, strut.left_start, strut.left_end, strut.right_width, strut.right_start, strut.right_end, strut.top_width, strut.top_start, strut.top_end, strut.bottom_width, strut.bottom_start, strut.bottom_end ); } void XWindowInterface::setWindowOnActivities(QWindow &window, const QStringList &activities) { KWindowSystem::setOnActivities(window.winId(), activities); } void XWindowInterface::removeDockStruts(QWindow &view) const { KWindowSystem::setStrut(view.winId(), 0, 0, 0, 0); } WindowId XWindowInterface::activeWindow() const { return KWindowSystem::self()->activeWindow(); } const std::list &XWindowInterface::windows() const { return m_windows; } void XWindowInterface::setKeepAbove(const QDialog &dialog, bool above) const { if (above) { KWindowSystem::setState(dialog.winId(), NET::KeepAbove); } else { KWindowSystem::clearState(dialog.winId(), NET::KeepAbove); } } void XWindowInterface::skipTaskBar(const QDialog &dialog) const { KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); } void XWindowInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) const { auto slideLocation = KWindowEffects::NoEdge; switch (location) { case Slide::Top: slideLocation = KWindowEffects::TopEdge; break; case Slide::Bottom: slideLocation = KWindowEffects::BottomEdge; break; case Slide::Left: slideLocation = KWindowEffects::LeftEdge; break; case Slide::Right: slideLocation = KWindowEffects::RightEdge; break; default: break; } KWindowEffects::slideWindow(view.winId(), slideLocation, -1); } void XWindowInterface::enableBlurBehind(QWindow &view) const { KWindowEffects::enableBlurBehind(view.winId()); } void XWindowInterface::setEdgeStateFor(QWindow *view, bool active) const { ScreenEdgeGhostWindow *window = qobject_cast(view); if (!window) { return; } xcb_connection_t *c = QX11Info::connection(); const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"); xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData()); QScopedPointer atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); if (!atom) { return; } if (!active) { xcb_delete_property(c, window->winId(), atom->atom); window->hideWithMask(); return; } window->showWithMask(); uint32_t value = 0; switch (window->location()) { case Plasma::Types::TopEdge: value = 0; break; case Plasma::Types::RightEdge: value = 1; break; case Plasma::Types::BottomEdge: value = 2; break; case Plasma::Types::LeftEdge: value = 3; break; case Plasma::Types::Floating: default: value = 4; break; } int hideType = 0; value |= hideType << 8; xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, 1, &value); } WindowInfoWrap XWindowInterface::requestInfoActive() const { return requestInfo(KWindowSystem::activeWindow()); } bool XWindowInterface::isOnCurrentDesktop(WindowId wid) const { KWindowInfo winfo(wid.value(), NET::WMDesktop); return winfo.valid() && winfo.isOnCurrentDesktop(); } bool XWindowInterface::isOnCurrentActivity(WindowId wid) const { KWindowInfo winfo(wid.value(), 0, NET::WM2Activities); return winfo.valid() && (winfo.activities().contains(m_activities->currentActivity()) || winfo.activities().empty()); } WindowInfoWrap XWindowInterface::requestInfo(WindowId wid) const { const KWindowInfo winfo{wid.value(), NET::WMFrameExtents | NET::WMWindowType | NET::WMGeometry | NET::WMState}; WindowInfoWrap winfoWrap; if (isValidWindow(winfo)) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setIsActive(KWindowSystem::activeWindow() == wid.value()); winfoWrap.setIsMinimized(winfo.hasState(NET::Hidden)); winfoWrap.setIsMaxVert(winfo.hasState(NET::MaxVert)); winfoWrap.setIsMaxHoriz(winfo.hasState(NET::MaxHoriz)); winfoWrap.setIsFullscreen(winfo.hasState(NET::FullScreen)); winfoWrap.setIsShaded(winfo.hasState(NET::Shaded)); winfoWrap.setGeometry(winfo.frameGeometry()); winfoWrap.setIsKeepAbove(winfo.hasState(NET::KeepAbove)); winfoWrap.setHasSkipTaskbar(winfo.hasState(NET::SkipTaskbar)); } else if (m_desktopId == wid) { winfoWrap.setIsValid(true); winfoWrap.setIsPlasmaDesktop(true); winfoWrap.setWid(wid); winfoWrap.setHasSkipTaskbar(true); } return winfoWrap; } bool XWindowInterface::windowCanBeDragged(WindowId wid) const { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && !winfo.isPlasmaDesktop() && !winfo.hasSkipTaskbar()); } void XWindowInterface::requestMoveWindow(WindowId wid, QPoint from) const { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || wInfo.isPlasmaDesktop()) { return; } int borderX{wInfo.geometry().width() > 120 ? 60 : 10}; int borderY{10}; //! find min/max values for x,y based on active window geometry int minX = wInfo.geometry().x() + borderX; int maxX = wInfo.geometry().x() + wInfo.geometry().width() - borderX; int minY = wInfo.geometry().y() + borderY; int maxY = wInfo.geometry().y() + wInfo.geometry().height() - borderY; //! set the point from which this window will be moved, //! make sure that it is in window boundaries int validX = qBound(minX, from.x(), maxX); int validY = qBound(minY, from.y(), maxY); NETRootInfo ri(QX11Info::connection(), NET::WMMoveResize); ri.moveResizeRequest(wInfo.wid().toUInt(), validX, validY, NET::Move); } void XWindowInterface::requestToggleMaximized(WindowId wid) const { WindowInfoWrap wInfo = requestInfo(wid); bool restore = wInfo.isMaxHoriz() && wInfo.isMaxVert(); NETWinInfo ni(QX11Info::connection(), wid.toInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); if (restore) { ni.setState(NET::States(), NET::Max); } else { ni.setState(NET::Max, NET::Max); } } bool XWindowInterface::isValidWindow(const KWindowInfo &winfo) const { constexpr auto types = NET::DockMask | NET::MenuMask | NET::SplashMask | NET::NormalMask; auto winType = winfo.windowType(types); if (winType == -1) { // Trying to get more types for verify if the window have any other type winType = winfo.windowType(~types & NET::AllTypesMask); if (winType == -1) { qWarning() << KWindowInfo(winfo.win(), 0, NET::WM2WindowClass).windowClassName() << "doesn't have any WindowType, assuming as NET::Normal"; return true; } } return !((winType & NET::Menu) || (winType & NET::Dock) || (winType & NET::Splash)); } void XWindowInterface::windowChangedProxy(WId wid, NET::Properties prop1, NET::Properties2 prop2) { //! if the dock changed is ignored if (std::find(m_docks.cbegin(), m_docks.cend(), wid) != m_docks.cend()) return; const auto winType = KWindowInfo(wid, NET::WMWindowType).windowType(NET::DesktopMask); //! update desktop id if (winType != -1 && (winType & NET::Desktop)) { m_desktopId = wid; emit windowChanged(wid); return; } //! accept only NET::Properties events, //! ignore when the user presses a key, or a window is sending X events etc. //! without needing to (e.g. Firefox, https://bugzilla.mozilla.org/show_bug.cgi?id=1389953) //! NET::WM2UserTime, NET::WM2IconPixmap etc.... if (prop1 == 0) { return; } //! accepty only the following NET:Properties changed signals //! NET::WMState, NET::WMGeometry, NET::ActiveWindow if (!((prop1 & NET::WMState) || (prop1 & NET::WMGeometry) || (prop1 & NET::ActiveWindow))) { return; } //! when only WMState changed we can whitelist the acceptable states if ((prop1 & NET::WMState) && !(prop1 & NET::WMGeometry) && !(prop1 & NET::ActiveWindow)) { KWindowInfo info(wid, NET::WMState); if (info.valid()) { if (!info.hasState(NET::Sticky) && !info.hasState(NET::Shaded) && !info.hasState(NET::FullScreen) && !info.hasState(NET::Hidden)) { return; } } else { return; } } emit windowChanged(wid); } }