diff --git a/app/layout/abstractlayout.cpp b/app/layout/abstractlayout.cpp index c08b7b70..070bce54 100644 --- a/app/layout/abstractlayout.cpp +++ b/app/layout/abstractlayout.cpp @@ -1,307 +1,320 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "abstractlayout.h" // Qt #include #include #include // KDE #include namespace Latte { namespace Layout { const QString AbstractLayout::MultipleLayoutsName = ".multiple-layouts_hidden"; AbstractLayout::AbstractLayout(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(); m_loadedCorrectly = true; } } AbstractLayout::~AbstractLayout() { } void AbstractLayout::init() { connect(this, &AbstractLayout::backgroundChanged, this, &AbstractLayout::saveConfig); connect(this, &AbstractLayout::colorChanged, this, &AbstractLayout::textColorChanged); connect(this, &AbstractLayout::lastUsedActivityChanged, this, &AbstractLayout::saveConfig); connect(this, &AbstractLayout::launchersChanged, this, &AbstractLayout::saveConfig); connect(this, &AbstractLayout::preferredForShortcutsTouchedChanged, this, &AbstractLayout::saveConfig); connect(this, &AbstractLayout::textColorChanged, this, &AbstractLayout::saveConfig); connect(this, &AbstractLayout::versionChanged, this, &AbstractLayout::saveConfig); } int AbstractLayout::version() const { return m_version; } void AbstractLayout::setVersion(int ver) { if (m_version == ver) { return; } m_version = ver; emit versionChanged(); } bool AbstractLayout::preferredForShortcutsTouched() const { return m_preferredForShortcutsTouched; } void AbstractLayout::setPreferredForShortcutsTouched(bool touched) { if (m_preferredForShortcutsTouched == touched) { return; } m_preferredForShortcutsTouched = touched; emit preferredForShortcutsTouchedChanged(); } QString AbstractLayout::background() const { return m_background; } void AbstractLayout::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 AbstractLayout::file() const { return m_layoutFile; } void AbstractLayout::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(); } QString AbstractLayout::name() const { return m_layoutName; } void AbstractLayout::setName(QString name) { if (m_layoutName == name) { return; } qDebug() << "Layout name:" << name; m_layoutName = name; emit nameChanged(); } QString AbstractLayout::color() const { return m_color; } void AbstractLayout::setColor(QString color) { if (m_color == color) { return; } m_color = color; emit colorChanged(); } QString AbstractLayout::lastUsedActivity() { return m_lastUsedActivity; } void AbstractLayout::clearLastUsedActivity() { m_lastUsedActivity = ""; emit lastUsedActivityChanged(); } QString AbstractLayout::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 AbstractLayout::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(); } QStringList AbstractLayout::launchers() const { return m_launchers; } void AbstractLayout::setLaunchers(QStringList launcherList) { if (m_launchers == launcherList) return; m_launchers = launcherList; emit launchersChanged(); } +QList combinedFreeEdges(const QList &edges1, const QList &edges2) +{ + QList validFreeEdges; + + for (int i=0; i * * 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 ABSTRACTLAYOUT_H #define ABSTRACTLAYOUT_H // Qt #include // KDE #include +// Plasma +#include + +namespace Plasma { +class Types; +} + namespace Latte { namespace Layout { class AbstractLayout : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(bool preferredForShortcutsTouched READ preferredForShortcutsTouched WRITE setPreferredForShortcutsTouched NOTIFY preferredForShortcutsTouchedChanged) 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 textColor READ textColor NOTIFY textColorChanged) Q_PROPERTY(QStringList launchers READ launchers WRITE setLaunchers NOTIFY launchersChanged) public: AbstractLayout(QObject *parent, QString layoutFile, QString assignedName = QString()); ~AbstractLayout() override; static const QString MultipleLayoutsName; int version() const; void setVersion(int ver); bool preferredForShortcutsTouched() const; void setPreferredForShortcutsTouched(bool touched); QString lastUsedActivity(); void clearLastUsedActivity(); //!e.g. when we export a layout QString name() const; QString file() const; QString background() const; void setBackground(QString path); QString color() const; void setColor(QString color); QString textColor() const; void setTextColor(QString color); QStringList launchers() const; void setLaunchers(QStringList launcherList); // STATIC static QString layoutName(const QString &fileName); + static QList combinedFreeEdges(const QList &edges1, + const QList &edges2); signals: void backgroundChanged(); void colorChanged(); void fileChanged(); void lastUsedActivityChanged(); void launchersChanged(); void nameChanged(); void preferredForShortcutsTouchedChanged(); void textColorChanged(); void versionChanged(); protected slots: void loadConfig(); void saveConfig(); protected: void init(); void setName(QString name); void setFile(QString file); protected: bool m_loadedCorrectly{false}; bool m_preferredForShortcutsTouched{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_textColor; QString m_layoutFile; QString m_layoutName; QStringList m_launchers; KConfigGroup m_layoutGroup; }; } } #endif diff --git a/app/layout/activelayout.cpp b/app/layout/activelayout.cpp index 371f769a..1b3d0b11 100644 --- a/app/layout/activelayout.cpp +++ b/app/layout/activelayout.cpp @@ -1,291 +1,444 @@ /* * 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 "activelayout.h" // local #include "toplayout.h" #include "../lattecorona.h" #include "../layoutmanager.h" +#include "../screenpool.h" #include "../settings/universalsettings.h" +#include "../view/view.h" #include "../../liblatte2/types.h" // Qt #include // KDE #include #include namespace Latte { ActiveLayout::ActiveLayout(QObject *parent, QString layoutFile, QString assignedName) : Layout::GenericLayout(parent, layoutFile, assignedName) { if (m_loadedCorrectly) { loadConfig(); init(); } } ActiveLayout::~ActiveLayout() { if (!m_layoutFile.isEmpty()) { m_layoutGroup.sync(); } } void ActiveLayout::unloadContainments() { Layout::GenericLayout::unloadContainments(); if (m_topLayout) { disconnect(m_topLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); m_topLayout->removeActiveLayout(this); } } void ActiveLayout::init() { connect(this, &ActiveLayout::activitiesChanged, this, &ActiveLayout::saveConfig); connect(this, &ActiveLayout::disableBordersForMaximizedWindowsChanged, this, &ActiveLayout::saveConfig); connect(this, &ActiveLayout::showInMenuChanged, this, &ActiveLayout::saveConfig); connect(this, &ActiveLayout::topLayoutNameChanged, this, &ActiveLayout::saveConfig); } void ActiveLayout::initToCorona(Latte::Corona *corona) { if (GenericLayout::initToCorona(corona)) { connect(m_corona->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else { kwin_setDisabledMaximizedBorders(false); } }); if (m_corona->layoutManager()->memoryUsage() == Types::SingleLayout && m_corona->universalSettings()->canDisableBorders()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } else if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, [&]() { if (m_corona->universalSettings()->canDisableBorders() && m_corona->layoutManager()->currentLayoutName() == name()) { kwin_setDisabledMaximizedBorders(disableBordersForMaximizedWindows()); } }); } //! Request the TopLayout in case there is one and Latte is functioning in MultipleLayouts mode if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts && !m_topLayoutName.isEmpty()) { if (m_corona->layoutManager()->assignActiveToTopLayout(this, m_topLayoutName)) { setTopLayout(m_corona->layoutManager()->topLayout(m_topLayoutName)); } } } } bool ActiveLayout::disableBordersForMaximizedWindows() const { return m_disableBordersForMaximizedWindows; } void ActiveLayout::setDisableBordersForMaximizedWindows(bool disable) { if (m_disableBordersForMaximizedWindows == disable) { return; } m_disableBordersForMaximizedWindows = disable; kwin_setDisabledMaximizedBorders(disable); emit disableBordersForMaximizedWindowsChanged(); } bool ActiveLayout::kwin_disabledMaximizedBorders() const { //! Identify 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 ActiveLayout::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 ActiveLayout::showInMenu() const { return m_showInMenu; } void ActiveLayout::setShowInMenu(bool show) { if (m_showInMenu == show) { return; } m_showInMenu = show; emit showInMenuChanged(); } QStringList ActiveLayout::activities() const { return m_activities; } void ActiveLayout::setActivities(QStringList activities) { if (m_activities == activities) { return; } m_activities = activities; emit activitiesChanged(); } QString ActiveLayout::topLayoutName() const { return m_topLayoutName; } void ActiveLayout::setTopLayoutName(QString name) { if (m_topLayoutName == name) { return; } m_topLayoutName = name; emit topLayoutNameChanged(); } void ActiveLayout::setTopLayout(TopLayout *layout) { if (m_topLayout == layout) { return; } disconnect(m_topLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); m_topLayout = layout; connect(m_topLayout, &Layout::GenericLayout::viewsCountChanged, this, &Layout::GenericLayout::viewsCountChanged); emit viewsCountChanged(); } bool ActiveLayout::isActiveLayout() const { if (!m_corona) { return false; } ActiveLayout *activeLayout = m_corona->layoutManager()->activeLayout(m_layoutName); if (activeLayout) { return true; } else { return false; } } bool ActiveLayout::isOriginalLayout() const { return m_layoutName != MultipleLayoutsName; } void ActiveLayout::loadConfig() { m_disableBordersForMaximizedWindows = m_layoutGroup.readEntry("disableBordersForMaximizedWindows", false); m_showInMenu = m_layoutGroup.readEntry("showInMenu", false); m_topLayoutName = m_layoutGroup.readEntry("topLayoutName", QString()); m_activities = m_layoutGroup.readEntry("activities", QStringList()); emit activitiesChanged(); } //! OVERRIDES void ActiveLayout::saveConfig() { qDebug() << "active layout is saving... for layout:" << m_layoutName; m_layoutGroup.writeEntry("showInMenu", m_showInMenu); m_layoutGroup.writeEntry("disableBordersForMaximizedWindows", m_disableBordersForMaximizedWindows); m_layoutGroup.writeEntry("topLayoutName", m_topLayoutName); m_layoutGroup.writeEntry("activities", m_activities); m_layoutGroup.sync(); } const QStringList ActiveLayout::appliedActivities() { if (!m_corona) { return {}; } if (m_corona->layoutManager()->memoryUsage() == Types::SingleLayout) { return {"0"}; } else if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { if (m_activities.isEmpty()) { return m_corona->layoutManager()->orphanedActivities(); } else { return m_activities; } } else { return {"0"}; } } QList ActiveLayout::latteViews() { if (m_topLayout) { QList views = Layout::GenericLayout::latteViews(); views << m_topLayout->latteViews(); return views; } return Layout::GenericLayout::latteViews(); } +int ActiveLayout::viewsCount(int screen) const +{ + if (!m_corona) { + return 0; + } + + int views = Layout::GenericLayout::viewsCount(screen); + + if (m_topLayout) { + QScreen *scr = m_corona->screenPool()->screenForId(screen); + + for (const auto view : m_topLayout->latteViews()) { + if (view && view->screen() == scr && !view->containment()->destroyed()) { + ++views; + } + } + } + + return views; +} + +int ActiveLayout::viewsCount(QScreen *screen) const +{ + if (!m_corona) { + return 0; + } + + int views = Layout::GenericLayout::viewsCount(screen); + + if (m_topLayout) { + for (const auto view : m_topLayout->latteViews()) { + if (view && view->screen() == screen && !view->containment()->destroyed()) { + ++views; + } + } + } + + return views; +} + +int ActiveLayout::viewsCount() const +{ + if (!m_corona) { + return 0; + } + + int views = Layout::GenericLayout::viewsCount(); + + if (m_topLayout) { + for (const auto view : m_topLayout->latteViews()) { + if (view && view->containment() && !view->containment()->destroyed()) { + ++views; + } + } + } + + return views; +} + +QList ActiveLayout::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; + } + + edges = Layout::GenericLayout::availableEdgesForView(scr, forView); + + if (m_topLayout) { + for (const auto view : m_topLayout->latteViews()) { + //! 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 ActiveLayout::freeEdges(QScreen *scr) const +{ + using Plasma::Types; + QList edges{Types::BottomEdge, Types::LeftEdge, + Types::TopEdge, Types::RightEdge}; + + if (!m_corona) { + return edges; + } + + edges = Layout::GenericLayout::freeEdges(scr); + + if (m_topLayout) { + for (const auto view : m_topLayout->latteViews()) { + if (view && view->positioner()->currentScreenName() == scr->name()) { + edges.removeOne(view->location()); + } + } + } + + return edges; +} + +QList ActiveLayout::freeEdges(int screen) const +{ + using Plasma::Types; + QList edges{Types::BottomEdge, Types::LeftEdge, + Types::TopEdge, Types::RightEdge}; + + if (!m_corona) { + return edges; + } + + edges = Layout::GenericLayout::freeEdges(screen); + QScreen *scr = m_corona->screenPool()->screenForId(screen); + + if (m_topLayout) { + for (const auto view : m_topLayout->latteViews()) { + if (view && scr && view->positioner()->currentScreenName() == scr->name()) { + edges.removeOne(view->location()); + } + } + } + + return edges; +} + +QList ActiveLayout::sortedLatteViews(QList views) +{ + QList combined = latteViews(); + if (m_topLayout) { + combined << m_topLayout->latteViews(); + } + + return Layout::GenericLayout::sortedLatteViews(combined); +} + +QList ActiveLayout::viewsWithPlasmaShortcuts() +{ + QList combined = Layout::GenericLayout::viewsWithPlasmaShortcuts(); + + if (m_topLayout) { + combined << m_topLayout->viewsWithPlasmaShortcuts(); + } + + return combined; +} } diff --git a/app/layout/activelayout.h b/app/layout/activelayout.h index e2fca758..3609add1 100644 --- a/app/layout/activelayout.h +++ b/app/layout/activelayout.h @@ -1,102 +1,115 @@ /* * 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 ACTIVELAYOUT_H #define ACTIVELAYOUT_H // local #include "genericlayout.h" // Qt #include namespace Latte { class Corona; class TopLayout; } 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 ActiveLayout : public Layout::GenericLayout { Q_OBJECT public: ActiveLayout(QObject *parent, QString layoutFile, QString layoutName = QString()); ~ActiveLayout() override; void initToCorona(Latte::Corona *corona); bool disableBordersForMaximizedWindows() const; void setDisableBordersForMaximizedWindows(bool disable); bool showInMenu() const; void setShowInMenu(bool show); //!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; QString topLayoutName() const; void setTopLayoutName(QString name); QStringList activities() const; void setActivities(QStringList activities); //! OVERRIDE GeneralLayout implementations void unloadContainments() override; const QStringList appliedActivities() override; QList latteViews() override; + int viewsCount(int screen) const override; + int viewsCount(QScreen *screen) const override; + int viewsCount() const override; + + //! Available edges for specific view in that screen + QList availableEdgesForView(QScreen *scr, Latte::View *forView) const override; + //! All free edges in that screen + QList freeEdges(QScreen *scr) const override; + QList freeEdges(int screen) const override; + + QList sortedLatteViews(QList views = QList()) override; + QList viewsWithPlasmaShortcuts() override; + signals: void activitiesChanged(); void disableBordersForMaximizedWindowsChanged(); void showInMenuChanged(); void topLayoutNameChanged(); private slots: void loadConfig(); void saveConfig(); void setTopLayout(TopLayout *layout); private: void init(); void importLocalLayout(QString file); bool kwin_disabledMaximizedBorders() const; void kwin_setDisabledMaximizedBorders(bool disable); private: bool m_disableBordersForMaximizedWindows{false}; bool m_showInMenu{false}; QString m_topLayoutName; QStringList m_activities; QPointer m_topLayout; }; } #endif //ACTIVELAYOUT_H diff --git a/app/layout/genericlayout.cpp b/app/layout/genericlayout.cpp index 6b711152..54879c48 100644 --- a/app/layout/genericlayout.cpp +++ b/app/layout/genericlayout.cpp @@ -1,1182 +1,1177 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "genericlayout.h" // local #include "abstractlayout.h" #include "storage.h" #include "../importer.h" #include "../lattecorona.h" #include "../layoutmanager.h" #include "../screenpool.h" #include "../shortcuts/shortcutstracker.h" #include "../view/view.h" #include "../view/positioner.h" // Qt #include #include // Plasma #include #include #include // KDE #include namespace Latte { namespace Layout { GenericLayout::GenericLayout(QObject *parent, QString layoutFile, QString assignedName) : AbstractLayout (parent, layoutFile, assignedName), m_storage(new Storage(this)) { } GenericLayout::~GenericLayout() { } void GenericLayout::unloadContainments() { if (!m_corona) { return; } //!disconnect signals in order to avoid crashes when the layout is unloading disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); disconnect(m_corona->activityConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity); qDebug() << "Layout - " + name() + " unload: containments ... size ::: " << m_containments.size() << " ,latteViews in memory ::: " << m_latteViews.size() << " ,hidden latteViews in memory ::: " << m_waitingLatteViews.size(); for (const auto view : m_latteViews) { view->disconnectSensitiveSignals(); } for (const auto view : m_waitingLatteViews) { view->disconnectSensitiveSignals(); } m_unloadedContainmentsIds.clear(); QList systrays; //!identify systrays and unload them first for (const 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 GenericLayout::unloadLatteViews() { if (!m_corona) { return; } qDebug() << "Layout - " + name() + " unload: latteViews ... size: " << m_latteViews.size(); qDeleteAll(m_latteViews); qDeleteAll(m_waitingLatteViews); m_latteViews.clear(); m_waitingLatteViews.clear(); } bool GenericLayout::blockAutomaticLatteViewCreation() const { return m_blockAutomaticLatteViewCreation; } void GenericLayout::setBlockAutomaticLatteViewCreation(bool block) { if (m_blockAutomaticLatteViewCreation == block) { return; } m_blockAutomaticLatteViewCreation = block; } bool GenericLayout::isCurrent() const { return name() == m_corona->layoutManager()->currentLayoutName(); } int GenericLayout::viewsCount(int screen) const { if (!m_corona) { return 0; } QScreen *scr = m_corona->screenPool()->screenForId(screen); int views{0}; for (const auto view : m_latteViews) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++views; } } return views; } int GenericLayout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } int views{0}; for (const auto view : m_latteViews) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++views; } } return views; } int GenericLayout::viewsCount() const { if (!m_corona) { return 0; } int views{0}; for (const auto view : m_latteViews) { if (view && view->containment() && !view->containment()->destroyed()) { ++views; } } return views; } QList GenericLayout::qmlFreeEdges(int screen) const { if (!m_corona) { const QList emptyEdges; return emptyEdges; } const auto edges = freeEdges(screen); QList edgesInt; for (const Plasma::Types::Location &edge : edges) { edgesInt.append(static_cast(edge)); } return edgesInt; } QList GenericLayout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } for (const auto view : m_latteViews) { if (view && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } QList GenericLayout::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); for (const auto view : m_latteViews) { if (view && scr && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } int GenericLayout::viewsWithTasks() const { if (!m_corona) { return 0; } int result = 0; for (const auto view : m_latteViews) { if (view->tasksPresent()) { result++; } } return result; } QStringList GenericLayout::unloadedContainmentsIds() { return m_unloadedContainmentsIds; } Latte::Corona *GenericLayout::corona() { return m_corona; } Types::ViewType GenericLayout::latteViewType(int containmentId) const { for (const auto view : m_latteViews) { if (view->containment() && view->containment()->id() == containmentId) { return view->type(); } } return Types::DockView; } Latte::View *GenericLayout::highestPriorityView() { QList views = sortedLatteViews(); return (views.count() > 0 ? views[0] : nullptr); } Latte::View *GenericLayout::viewForContainment(const Plasma::Containment *containment) { if (m_latteViews.contains(containment)) { return m_latteViews[containment]; } return nullptr; } QList GenericLayout::latteViews() { return m_latteViews.values(); } -QList GenericLayout::sortedLatteViews() +QList GenericLayout::sortedLatteViews(QList views) { - QList sortedViews; - - //! create views list to be sorted out - for (const auto view : m_latteViews) { - sortedViews.append(view); - } + QList sortedViews = views.isEmpty() ? latteViews() : views; qDebug() << " -------- "; for (int i = 0; i < sortedViews.count(); ++i) { qDebug() << i << ". " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location(); } //! sort the views based on screens and edges priorities //! views on primary screen have higher priority and //! for views in the same screen the priority goes to //! Bottom,Left,Top,Right for (int i = 0; i < sortedViews.size(); ++i) { for (int j = 0; j < sortedViews.size() - i - 1; ++j) { if (viewAtLowerScreenPriority(sortedViews[j], sortedViews[j + 1]) || (sortedViews[j]->screen() == sortedViews[j + 1]->screen() && viewAtLowerEdgePriority(sortedViews[j], sortedViews[j + 1]))) { Latte::View *temp = sortedViews[j + 1]; sortedViews[j + 1] = sortedViews[j]; sortedViews[j] = temp; } } } Latte::View *highestPriorityView{nullptr}; for (int i = 0; i < sortedViews.size(); ++i) { if (sortedViews[i]->isPreferredForShortcuts()) { highestPriorityView = sortedViews[i]; sortedViews.removeAt(i); break; } } if (highestPriorityView) { sortedViews.prepend(highestPriorityView); } qDebug() << " -------- sorted -----"; for (int i = 0; i < sortedViews.count(); ++i) { qDebug() << i << ". " << sortedViews[i]->isPreferredForShortcuts() << " - " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location(); } return sortedViews; } bool GenericLayout::viewAtLowerScreenPriority(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() << "viewAtLowerScreenPriority : shouldn't had reached here..."; return false; } bool GenericLayout::viewAtLowerEdgePriority(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; } const QList *GenericLayout::containments() { return &m_containments; } QList GenericLayout::viewsWithPlasmaShortcuts() { QList views; if (!m_corona) { return views; } QList appletsWithShortcuts = m_corona->globalShortcuts()->shortcutsTracker()->appletsWithPlasmaShortcuts(); for (const auto &appletId : appletsWithShortcuts) { for (const auto view : m_latteViews) { bool found{false}; for (const auto applet : view->containment()->applets()) { if (appletId == applet->id()) { if (!views.contains(view)) { views.append(view); found = true; break; } } } if (found) { break; } } } return views; } //! Containments Actions void GenericLayout::addContainment(Plasma::Containment *containment) { if (!containment || m_containments.contains(containment)) { return; } bool containmentInLayout{false}; if (m_corona->layoutManager()->memoryUsage() == Types::SingleLayout) { m_containments.append(containment); containmentInLayout = true; } else if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { m_containments.append(containment); containmentInLayout = true; } } if (containmentInLayout) { if (!blockAutomaticLatteViewCreation()) { addView(containment); } else { qDebug() << "delaying LatteView creation for containment :: " << containment->id(); } connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); } } void GenericLayout::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; for (const auto containment : m_corona->containments()) { if (containment->id() == sId) { containment->config().writeEntry("layoutId", m_layoutName); } addContainment(containment); } } } void GenericLayout::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_latteViews.take(containment); if (!view) { view = m_waitingLatteViews.take(containment); } if (view) { view->disconnectSensitiveSignals(); view->deleteLater(); emit viewsCountChanged(); } } } void GenericLayout::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_waitingLatteViews[sender] = m_latteViews.take(static_cast(sender)); } else { m_latteViews[sender] = m_waitingLatteViews.take(static_cast(sender)); } emit viewsCountChanged(); } void GenericLayout::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) { for (const auto containment : m_containments) { containment->config().writeEntry("layoutId", m_layoutName); } } } void GenericLayout::addNewView() { if (!m_corona) { return; } m_corona->loadDefaultLayout(); } void GenericLayout::addView(Plasma::Containment *containment, bool forceOnPrimary, int explicitScreen) { qDebug() << "Layout :::: " << m_layoutName << " ::: addView 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 (!m_storage->isLatteContainment(containment)) return; qDebug() << "step 2..."; for (auto *dock : m_latteViews) { 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 && explicitScreen == -1) { id = containment->lastScreen(); } if (explicitScreen > -1) { id = explicitScreen; } 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}; for (const 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; for (const auto view : m_latteViews) { 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 latteView and removing it in order add an onPrimary with higher priority at screen: " << connector; auto viewToDelete = m_latteViews.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(Types::DodgeActive))); bool byPassWM{false}; if (mode == Types::AlwaysVisible || mode == Types::WindowsGoBelow) { byPassWM = false; } else { byPassWM = containment->config().readEntry("byPassWM", false); } auto latteView = new Latte::View(m_corona, nextScreen, byPassWM); latteView->init(); latteView->setContainment(containment); latteView->setManagedLayout(this); //! force this special dock case to become primary //! even though it isnt if (forceOnPrimary) { qDebug() << "Enforcing onPrimary:true as requested for LatteView..."; latteView->setOnPrimary(true); } // connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); connect(containment, &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::viewLocationChanged); connect(containment, &Plasma::Containment::appletAlternativesRequested , m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection); if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { connect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } //! 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()) { latteView->show(); //} m_latteViews[containment] = latteView; emit viewsCountChanged(); } bool GenericLayout::initToCorona(Latte::Corona *corona) { if (m_corona) { return false; } m_corona = corona; for (const auto containment : m_corona->containments()) { if (m_corona->layoutManager()->memoryUsage() == Types::SingleLayout) { addContainment(containment); } else if (m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { addContainment(containment); } } } qDebug() << "Layout ::::: " << name() << " added containments ::: " << m_containments.size(); //! last used activity if (m_layoutName != MultipleLayoutsName) { updateLastUsedActivity(); } //! signals connect(m_corona->activityConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity); connect(m_corona, &Plasma::Corona::containmentAdded, this, &GenericLayout::addContainment); //!connect signals after adding the containment connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); emit viewsCountChanged(); return true; } void GenericLayout::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() == Types::SingleLayout)) { m_lastUsedActivity = currentId; emit lastUsedActivityChanged(); } } void GenericLayout::assignToLayout(Latte::View *latteView, QList containments) { if (!m_corona) { return; } if (latteView) { m_latteViews[latteView->containment()] = latteView; m_containments << containments; for (const auto containment : containments) { containment->config().writeEntry("layoutId", name()); connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); connect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } latteView->setManagedLayout(this); emit viewsCountChanged(); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { m_storage->syncToLayoutFile(false); } } QList GenericLayout::unassignFromLayout(Latte::View *latteView) { QList containments; if (!m_corona) { return containments; } containments << latteView->containment(); for (const auto containment : m_containments) { Plasma::Applet *parentApplet = qobject_cast(containment->parent()); //! add systrays from that latteView if (parentApplet && parentApplet->containment() && parentApplet->containment() == latteView->containment()) { containments << containment; disconnect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); disconnect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); disconnect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } } for (const auto containment : containments) { m_containments.removeAll(containment); } if (containments.size() > 0) { m_latteViews.remove(latteView->containment()); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutManager()->memoryUsage() == Types::MultipleLayouts) { m_storage->syncToLayoutFile(false); } return containments; } void GenericLayout::recreateView(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 latteview QTimer::singleShot(350, [this, containment]() { auto view = m_latteViews.take(containment); if (view) { qDebug() << "recreate - step 1: removing dock for containment:" << containment->id(); //! step:2 add the new latteview connect(view, &QObject::destroyed, this, [this, containment]() { QTimer::singleShot(250, this, [this, containment]() { if (!m_latteViews.contains(containment)) { qDebug() << "recreate - step 2: adding dock for containment:" << containment->id(); addView(containment); } }); }); view->deleteLater(); } }); } bool GenericLayout::latteViewExists(Plasma::Containment *containment) { if (!m_corona) { return false; } return m_latteViews.keys().contains(containment); } QList GenericLayout::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; } for (const auto view : m_latteViews) { //! 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; } bool GenericLayout::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const { if (!m_corona) { return false; } for (const auto containment : m_containments) { if (m_storage->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 GenericLayout::primaryDockOccupyEdge(Plasma::Types::Location location) const { if (!m_corona) { return false; } for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { bool onPrimary = containment->config().readEntry("onPrimary", true); Plasma::Types::Location contLocation = containment->location(); if (onPrimary && contLocation == location) { return true; } } } return false; } bool GenericLayout::mapContainsId(const ViewsMap *map, uint viewId) const { for(const auto &scr : map->keys()) { for(const auto &edge : (*map)[scr].keys()) { if ((*map)[scr][edge] == viewId) { return true; } } } return false; } //! screen name, location, containmentId ViewsMap GenericLayout::validViewsMap() { ViewsMap map; if (!m_corona) { return map; } QString prmScreenName = qGuiApp->primaryScreen()->name(); //! first step: primary docks must be placed in primary screen free edges for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { int screenId = 0; //! valid screen id if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); } else { screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } } bool onPrimary{true}; //! valid onPrimary flag if (latteViewExists(containment)) { onPrimary = m_latteViews[containment]->onPrimary(); } else { onPrimary = containment->config().readEntry("onPrimary", true); } //! valid location Plasma::Types::Location location = containment->location(); if (onPrimary && !map[prmScreenName].contains(location)) { map[prmScreenName][location] = containment->id(); } } } //! second step: explicit docks must be placed in their screens if the screen edge is free for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { int screenId = 0; //! valid screen id if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); } else { screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } } bool onPrimary{true}; //! valid onPrimary flag if (latteViewExists(containment)) { onPrimary = m_latteViews[containment]->onPrimary(); } else { onPrimary = containment->config().readEntry("onPrimary", true); } //! valid location Plasma::Types::Location location = containment->location(); if (!onPrimary) { QString expScreenName = m_corona->screenPool()->connector(screenId); if (m_corona->screenPool()->screenExists(screenId) && !map[expScreenName].contains(location)) { map[expScreenName][location] = containment->id(); } } } } return map; } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) void GenericLayout::syncLatteViewsToScreens() { if (!m_corona) { return; } qDebug() << "START of SyncLatteViewsToScreens ...."; qDebug() << "LAYOUT ::: " << name(); qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size(); //QHash> futureDocksLocations; //QList futureShownViews; QHash> viewsMap = validViewsMap(); QString prmScreenName = qGuiApp->primaryScreen()->name(); qDebug() << "PRIMARY SCREEN :: " << prmScreenName; qDebug() << "LATTEVIEWS MAP :: " << viewsMap; //! add views for (const auto containment : m_containments) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } if (!latteViewExists(containment) && mapContainsId(&viewsMap, containment->id())) { qDebug() << "syncLatteViewsToScreens: view must be added... for containment:" << containment->id() << " at screen:" << m_corona->screenPool()->connector(screenId); addView(containment); } } //! remove views for (const auto view : m_latteViews) { if (view->containment() && !mapContainsId(&viewsMap, view->containment()->id())) { qDebug() << "syncLatteViewsToScreens: view must be deleted... for containment:" << view->containment()->id() << " at screen:" << view->positioner()->currentScreenName(); auto viewToDelete = m_latteViews.take(view->containment()); viewToDelete->disconnectSensitiveSignals(); viewToDelete->deleteLater(); } } //! reconsider views for (const auto view : m_latteViews) { if (view->containment() && !mapContainsId(&viewsMap, 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, syncLatteViewsToScreens ...."; } //! STORAGE bool GenericLayout::isWritable() const { return m_storage->isWritable(); } void GenericLayout::lock() { m_storage->lock(); } void GenericLayout::unlock() { m_storage->unlock(); } void GenericLayout::syncToLayoutFile(bool removeLayoutId) { m_storage->syncToLayoutFile(removeLayoutId); } void GenericLayout::copyView(Plasma::Containment *containment) { m_storage->copyView(containment); } void GenericLayout::importToCorona() { m_storage->importToCorona(); } bool GenericLayout::layoutIsBroken() const { return m_storage->layoutIsBroken(); } } } diff --git a/app/layout/genericlayout.h b/app/layout/genericlayout.h index cbcd0817..038c00dd 100644 --- a/app/layout/genericlayout.h +++ b/app/layout/genericlayout.h @@ -1,186 +1,186 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GENERICLAYOUT_H #define GENERICLAYOUT_H // local #include "abstractlayout.h" #include "../../liblatte2/types.h" // Qt #include #include #include #include // Plasma #include namespace Plasma { class Applet; class Containment; class Types; } namespace Latte { class Corona; class View; } namespace Latte { namespace Layout { class Storage; } } namespace Latte { namespace Layout { //! This is views map in the following structure: //! SCREEN_NAME -> EDGE -> VIEWID typedef QHash> ViewsMap; class GenericLayout : public AbstractLayout { Q_OBJECT Q_PROPERTY(int viewsCount READ viewsCount NOTIFY viewsCountChanged) public: GenericLayout(QObject *parent, QString layoutFile, QString assignedName = QString()); ~GenericLayout() override; virtual const QStringList appliedActivities() = 0; // to move at an interface void importToCorona(); bool initToCorona(Latte::Corona *corona); virtual bool isCurrent() const; bool isWritable() const; bool layoutIsBroken() const; - int viewsCount(int screen) const; - int viewsCount(QScreen *screen) const; - int viewsCount() const; + virtual int viewsCount(int screen) const; + virtual int viewsCount(QScreen *screen) const; + virtual int viewsCount() const; Latte::Corona *corona(); QStringList unloadedContainmentsIds(); Types::ViewType latteViewType(int containmentId) const; const QList *containments(); Latte::View *highestPriorityView(); Latte::View *viewForContainment(const Plasma::Containment *containment); - QList sortedLatteViews(); - QList viewsWithPlasmaShortcuts(); + virtual QList sortedLatteViews(QList views = QList()); + virtual QList viewsWithPlasmaShortcuts(); virtual QList latteViews(); - ViewsMap validViewsMap(); + virtual ViewsMap validViewsMap(); void syncToLayoutFile(bool removeLayoutId = false); void lock(); //! make it only read-only void renameLayout(QString newName); void syncLatteViewsToScreens(); virtual void unloadContainments(); void unloadLatteViews(); void unlock(); //! make it writable which it should be the default //! this function needs the layout to have first set the corona through initToCorona() function void addView(Plasma::Containment *containment, bool forceOnPrimary = false, int explicitScreen = -1); void copyView(Plasma::Containment *containment); void recreateView(Plasma::Containment *containment); bool latteViewExists(Plasma::Containment *containment); //! Available edges for specific view in that screen - QList availableEdgesForView(QScreen *scr, Latte::View *forView) const; + virtual QList availableEdgesForView(QScreen *scr, Latte::View *forView) const; //! All free edges in that screen - QList freeEdges(QScreen *scr) const; - QList freeEdges(int screen) const; + virtual QList freeEdges(QScreen *scr) const; + virtual QList freeEdges(int screen) const; //! 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 *latteView, QList containments); //! 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 latteView QList unassignFromLayout(Latte::View *latteView); public slots: Q_INVOKABLE void addNewView(); Q_INVOKABLE int viewsWithTasks() const; - Q_INVOKABLE QList qmlFreeEdges(int screen) const; //change to types + virtual Q_INVOKABLE QList qmlFreeEdges(int screen) const; //change to types signals: void activitiesChanged(); // to move at an interface void viewsCountChanged(); //! used from ConfigView(s) in order to be informed which is one should be shown void configViewCreated(QQuickView *configView); //! 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(Latte::View *view); protected: void updateLastUsedActivity(); protected: Latte::Corona *m_corona{nullptr}; QList m_containments; QHash m_latteViews; QHash m_waitingLatteViews; private slots: void addContainment(Plasma::Containment *containment); void appletCreated(Plasma::Applet *applet); void destroyedChanged(bool destroyed); void containmentDestroyed(QObject *cont); private: //! It can be used in order for LatteViews to not be created automatically when //! their corresponding containments are created e.g. copyView functionality bool blockAutomaticLatteViewCreation() const; void setBlockAutomaticLatteViewCreation(bool block); bool explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const; bool primaryDockOccupyEdge(Plasma::Types::Location location) const; bool viewAtLowerScreenPriority(Latte::View *test, Latte::View *base); bool viewAtLowerEdgePriority(Latte::View *test, Latte::View *base); bool mapContainsId(const ViewsMap *map, uint viewId) const; private: bool m_blockAutomaticLatteViewCreation{false}; QStringList m_unloadedContainmentsIds; QPointer m_storage; friend class Storage; }; } } #endif diff --git a/app/layout/toplayout.cpp b/app/layout/toplayout.cpp index 46fbd3d1..116f5790 100644 --- a/app/layout/toplayout.cpp +++ b/app/layout/toplayout.cpp @@ -1,95 +1,246 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "toplayout.h" // local #include "activelayout.h" +#include "../lattecorona.h" +#include "../screenpool.h" +#include "../view/view.h" namespace Latte { TopLayout::TopLayout(ActiveLayout *assigned, QObject *parent, QString layoutFile, QString layoutName) : Layout::GenericLayout (parent, layoutFile, layoutName) { initToCorona(assigned->corona()); addActiveLayout(assigned); } TopLayout::~TopLayout() { } bool TopLayout::isCurrent() const { for (const auto &layout : m_activeLayouts) { if (layout->isCurrent()) { return true; } } return false; } const QStringList TopLayout::appliedActivities() { if (!m_corona) { return {}; } QStringList activities; for (const auto &layout : m_activeLayouts) { activities << layout->appliedActivities(); } return activities; } +ActiveLayout *TopLayout::currentActiveLayout() const +{ + for (const auto &layout : m_activeLayouts) { + if (layout->isCurrent()) { + return layout; + } + } + + return nullptr; +} + void TopLayout::addActiveLayout(ActiveLayout *layout) { if (layout != nullptr && !m_activeLayouts.contains(layout)) { m_activeLayouts.append(layout); connect(layout, &GenericLayout::activitiesChanged, this, &GenericLayout::activitiesChanged); emit activitiesChanged(); emit viewsCountChanged(); updateLastUsedActivity(); } } void TopLayout::removeActiveLayout(ActiveLayout *layout) { if (m_activeLayouts.contains(layout)) { qDebug() << "TOPLAYOUT <" << name() << "> : Removing active layout, " << layout->name(); m_activeLayouts.removeAll(layout); disconnect(layout, &GenericLayout::activitiesChanged, this, &GenericLayout::activitiesChanged); emit activitiesChanged(); //! viewsCount signal is not needed to be trigerred here because //! in such case the views number has not been changed for the rest //! active layouts } } +//! OVERRIDE +int TopLayout::viewsCount(int screen) const +{ + if (!m_corona) { + return 0; + } + + int views = Layout::GenericLayout::viewsCount(screen); + ActiveLayout *current = currentActiveLayout(); + + if (current) { + views += current->viewsCount(screen); + } + + return views; +} + +int TopLayout::viewsCount(QScreen *screen) const +{ + if (!m_corona) { + return 0; + } + + int views = Layout::GenericLayout::viewsCount(screen); + ActiveLayout *current = currentActiveLayout(); + + if (current) { + views += current->viewsCount(screen); + } + + return views; +} + +int TopLayout::viewsCount() const +{ + if (!m_corona) { + return 0; + } + + int views = Layout::GenericLayout::viewsCount(); + ActiveLayout *current = currentActiveLayout(); + + if (current) { + views += current->viewsCount(); + } + + return views; +} + +QList TopLayout::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; + } + + edges = Layout::GenericLayout::availableEdgesForView(scr, forView); + + ActiveLayout *current = currentActiveLayout(); + if (current) { + for (const auto view : current->latteViews()) { + //! 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 TopLayout::freeEdges(QScreen *scr) const +{ + using Plasma::Types; + QList edges{Types::BottomEdge, Types::LeftEdge, + Types::TopEdge, Types::RightEdge}; + + if (!m_corona) { + return edges; + } + + edges = Layout::GenericLayout::freeEdges(scr); + + ActiveLayout *current = currentActiveLayout(); + + if (current) { + for (const auto view : current->latteViews()) { + if (view && view->positioner()->currentScreenName() == scr->name()) { + edges.removeOne(view->location()); + } + } + } + + return edges; +} + +QList TopLayout::freeEdges(int screen) const +{ + using Plasma::Types; + QList edges{Types::BottomEdge, Types::LeftEdge, + Types::TopEdge, Types::RightEdge}; + + if (!m_corona) { + return edges; + } + + edges = Layout::GenericLayout::freeEdges(screen); + QScreen *scr = m_corona->screenPool()->screenForId(screen); + ActiveLayout *current = currentActiveLayout(); + + if (current) { + for (const auto view : current->latteViews()) { + if (view && scr && view->positioner()->currentScreenName() == scr->name()) { + edges.removeOne(view->location()); + } + } + } + + return edges; +} + +QList TopLayout::sortedLatteViews(QList views) +{ + QList combined = latteViews(); + + ActiveLayout *current = currentActiveLayout(); + + if (current) { + combined << current->latteViews(); + } + + return Layout::GenericLayout::sortedLatteViews(combined); +} } diff --git a/app/layout/toplayout.h b/app/layout/toplayout.h index 0f69fc1b..da505dcf 100644 --- a/app/layout/toplayout.h +++ b/app/layout/toplayout.h @@ -1,65 +1,80 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TOPLAYOUT_H #define TOPLAYOUT_H // local #include "genericlayout.h" // Qt #include namespace Latte { class ActiveLayout; } namespace Latte { //! TopLayout is a layout that exists only as long as it belongs to one or //! more ActiveLayout(s). It is a layer above an active or more layouts and can //! be used from ActiveLayouts to share Latte:View(s) . Much of its functionality //! is provided by the ActiveLayouts it belongs to. For example the activities //! that its views should be shown is identified only from the active layouts //! it belongs to class TopLayout : public Layout::GenericLayout { public: TopLayout(ActiveLayout *assigned, QObject *parent, QString layoutFile, QString layoutName = QString()); ~TopLayout() override; const QStringList appliedActivities(); //! OVERRIDE GeneralLayout implementations bool isCurrent() const override; + int viewsCount(int screen) const override; + int viewsCount(QScreen *screen) const override; + int viewsCount() const override; + + //! Available edges for specific view in that screen + QList availableEdgesForView(QScreen *scr, Latte::View *forView) const override; + //! All free edges in that screen + QList freeEdges(QScreen *scr) const override; + QList freeEdges(int screen) const override; + + QList sortedLatteViews(QList views = QList()) override; + public slots: void addActiveLayout(ActiveLayout *layout); void removeActiveLayout(ActiveLayout *layout); +private: + ActiveLayout *currentActiveLayout() const; + private: QList m_activeLayouts; }; } -#endif +#endif //TOPLAYOUT_H