diff --git a/app/layout/CMakeLists.txt b/app/layout/CMakeLists.txt index c84ce55ab..9efc40294 100644 --- a/app/layout/CMakeLists.txt +++ b/app/layout/CMakeLists.txt @@ -4,5 +4,6 @@ set(lattedock-app_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activelayout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/genericlayout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/storage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/toplayout.cpp PARENT_SCOPE ) diff --git a/app/layout/abstractlayout.h b/app/layout/abstractlayout.h index 685b0beaa..f1e830089 100644 --- a/app/layout/abstractlayout.h +++ b/app/layout/abstractlayout.h @@ -57,7 +57,6 @@ public: QString lastUsedActivity(); void clearLastUsedActivity(); //!e.g. when we export a layout - void updateLastUsedActivity(); QString name() const; QString file() const; diff --git a/app/layout/activelayout.cpp b/app/layout/activelayout.cpp index fc9cdd2a0..c667fa8c8 100644 --- a/app/layout/activelayout.cpp +++ b/app/layout/activelayout.cpp @@ -21,6 +21,7 @@ #include "activelayout.h" // local +#include "toplayout.h" #include "../lattecorona.h" #include "../layoutmanager.h" #include "../settings/universalsettings.h" @@ -56,6 +57,7 @@ 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) @@ -81,13 +83,12 @@ void ActiveLayout::initToCorona(Latte::Corona *corona) }); } - if (m_layoutName != MultipleLayoutsName) { - updateLastUsedActivity(); + //! 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)); + } } - - connect(m_corona->activityConsumer(), &KActivities::Consumer::currentActivityChanged, - this, &ActiveLayout::updateLastUsedActivity); - } } @@ -173,27 +174,32 @@ void ActiveLayout::setActivities(QStringList activities) emit activitiesChanged(); } -void ActiveLayout::updateLastUsedActivity() +QString ActiveLayout::topLayoutName() const { - if (!m_corona) { - return; - } + return m_topLayoutName; +} - if (!m_lastUsedActivity.isEmpty() && !m_corona->layoutManager()->activities().contains(m_lastUsedActivity)) { - clearLastUsedActivity(); +void ActiveLayout::setTopLayoutName(QString name) +{ + if (m_topLayoutName == name) { + return; } - QString currentId = m_corona->activitiesConsumer()->currentActivity(); + m_topLayoutName = name; + emit topLayoutNameChanged(); +} - QStringList appliedActivitiesIds = appliedActivities(); +void ActiveLayout::setTopLayout(TopLayout *layout) +{ + if (m_topLayout != layout) { + return; + } + disconnect(m_topLayout, &GenericLayout::viewsCountChanged, this, &GenericLayout::viewsCountChanged); - if (m_lastUsedActivity != currentId - && (appliedActivitiesIds.contains(currentId) - || m_corona->layoutManager()->memoryUsage() == Types::SingleLayout)) { - m_lastUsedActivity = currentId; + m_topLayout = layout; - emit lastUsedActivityChanged(); - } + connect(m_topLayout, &GenericLayout::viewsCountChanged, this, &GenericLayout::viewsCountChanged); + emit viewsCountChanged(); } bool ActiveLayout::isActiveLayout() const @@ -220,6 +226,7 @@ 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(); @@ -230,6 +237,7 @@ 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(); diff --git a/app/layout/activelayout.h b/app/layout/activelayout.h index cbe3b6fbd..4821e7d76 100644 --- a/app/layout/activelayout.h +++ b/app/layout/activelayout.h @@ -29,6 +29,7 @@ namespace Latte { class Corona; +class TopLayout; } namespace Latte { @@ -57,6 +58,9 @@ public: //!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); @@ -66,15 +70,17 @@ 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); - void updateLastUsedActivity(); bool kwin_disabledMaximizedBorders() const; void kwin_setDisabledMaximizedBorders(bool disable); @@ -82,7 +88,10 @@ private: private: bool m_disableBordersForMaximizedWindows{false}; bool m_showInMenu{false}; + QString m_topLayoutName; QStringList m_activities; + + QPointer m_topLayout; }; } diff --git a/app/layout/genericlayout.cpp b/app/layout/genericlayout.cpp index 2c3da1fcf..83cee01d0 100644 --- a/app/layout/genericlayout.cpp +++ b/app/layout/genericlayout.cpp @@ -757,6 +757,15 @@ bool GenericLayout::initToCorona(Latte::Corona *corona) 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 @@ -768,6 +777,29 @@ bool GenericLayout::initToCorona(Latte::Corona *corona) 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) { diff --git a/app/layout/genericlayout.h b/app/layout/genericlayout.h index 09aa2d011..57ab76a24 100644 --- a/app/layout/genericlayout.h +++ b/app/layout/genericlayout.h @@ -78,6 +78,8 @@ public: int viewsCount(QScreen *screen) const; int viewsCount() const; + Latte::Corona *corona(); + QStringList unloadedContainmentsIds(); Types::ViewType latteViewType(int containmentId) const; @@ -141,8 +143,6 @@ private slots: void destroyedChanged(bool destroyed); void containmentDestroyed(QObject *cont); - Latte::Corona *corona(); - protected: Latte::Corona *m_corona{nullptr}; @@ -152,6 +152,8 @@ protected: QHash m_waitingLatteViews; private: + void updateLastUsedActivity(); + //! 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; diff --git a/app/layout/toplayout.cpp b/app/layout/toplayout.cpp new file mode 100644 index 000000000..2377109df --- /dev/null +++ b/app/layout/toplayout.cpp @@ -0,0 +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 . +*/ + +#include "toplayout.h" + +// local +#include "activelayout.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() +{ +} + +const QStringList TopLayout::appliedActivities() +{ + if (!m_corona) { + return {}; + } + + QStringList activities; + + for (const auto &layout : m_activeLayouts) { + activities << layout->appliedActivities(); + } + + return activities; +} + +void TopLayout::addActiveLayout(ActiveLayout *layout) +{ + if (layout != nullptr && !m_activeLayouts.contains(layout)) { + m_activeLayouts.append(layout); + + connect(layout, &QObject::destroyed, this, [&]() { + disconnect(layout, &GenericLayout::activitiesChanged, this, &GenericLayout::activitiesChanged); + removeActiveLayout(layout); + }); + + connect(layout, &GenericLayout::activitiesChanged, this, &GenericLayout::activitiesChanged); + + emit viewsCountChanged(); + } +} + +void TopLayout::removeActiveLayout(ActiveLayout *layout) +{ + if (m_activeLayouts.contains(layout)) { + m_activeLayouts.removeAll(layout); + emit activitiesChanged(); + emit viewsCountChanged(); + } +} + + +} diff --git a/app/layout/toplayout.h b/app/layout/toplayout.h new file mode 100644 index 000000000..c9f8121e7 --- /dev/null +++ b/app/layout/toplayout.h @@ -0,0 +1,62 @@ +/* +* 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(); + +public slots: + void addActiveLayout(ActiveLayout *layout); + void removeActiveLayout(ActiveLayout *layout); + +private: + QList m_activeLayouts; + +}; + +} + +#endif diff --git a/app/layoutmanager.cpp b/app/layoutmanager.cpp index 4e4a36392..4a916508a 100644 --- a/app/layoutmanager.cpp +++ b/app/layoutmanager.cpp @@ -25,7 +25,9 @@ #include "infoview.h" #include "launcherssignals.h" #include "screenpool.h" +#include "layout/abstractlayout.h" #include "layout/activelayout.h" +#include "layout/toplayout.h" #include "settings/settingsdialog.h" #include "settings/universalsettings.h" #include "view/view.h" @@ -78,6 +80,14 @@ LayoutManager::~LayoutManager() layout->deleteLater(); } + while (!m_topLayouts.isEmpty()) { + TopLayout *layout = m_topLayouts.at(0); + m_topLayouts.removeFirst(); + layout->unloadContainments(); + layout->unloadLatteViews(); + layout->deleteLater(); + } + m_activitiesController->deleteLater(); } @@ -126,7 +136,7 @@ void LayoutManager::load() void LayoutManager::unload() { - //! Unload all Layouts + //! Unload all active Layouts for (const auto layout : m_activeLayouts) { if (memoryUsage() == Types::MultipleLayouts && layout->isOriginalLayout()) { layout->syncToLayoutFile(true); @@ -141,15 +151,20 @@ void LayoutManager::unload() layout->deleteLater(); } + m_activeLayouts.clear(); //! Cleanup pseudo-layout from Containments if (memoryUsage() == Types::MultipleLayouts) { - // auto containmentsEntries = m_corona->config()->group("Containments"); - // containmentsEntries.deleteGroup(); - // containmentsEntries.sync(); + for (const auto layout : m_topLayouts) { + layout->syncToLayoutFile(true); + layout->unloadContainments(); + layout->unloadLatteViews(); + clearUnloadedContainmentsFromLinkedFile(layout->unloadedContainmentsIds()); + layout->deleteLater(); + } + m_topLayouts.clear(); } - //! Remove no-needed temp files QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak"; QString temp2File = QDir::homePath() + "/.config/lattedock.copy2.bak"; @@ -356,6 +371,42 @@ int LayoutManager::activeLayoutPos(QString id) const return -1; } +TopLayout *LayoutManager::topLayout(QString id) const +{ + for (int i = 0; i < m_topLayouts.size(); ++i) { + TopLayout *layout = m_topLayouts.at(i); + + if (layout->name() == id) { + return layout; + } + } + + return nullptr; +} + +bool LayoutManager::assignActiveToTopLayout(ActiveLayout *active, QString id) +{ + if (memoryUsage() == Types::SingleLayout) { + return false; + } + + for (int i = 0; i < m_topLayouts.size(); ++i) { + TopLayout *layout = m_topLayouts.at(i); + + if (layout->name() == id) { + layout->addActiveLayout(active); + return true; + } + } + + //! If TopLayout was not found, we must create it + TopLayout *top = new TopLayout(active, this, Importer::layoutFilePath(id)); + m_topLayouts.append(top); + top->importToCorona(); + + return true; +} + ActiveLayout *LayoutManager::currentLayout() const { if (memoryUsage() == Types::SingleLayout) { diff --git a/app/layoutmanager.h b/app/layoutmanager.h index 8e9f00a71..81831dd52 100644 --- a/app/layoutmanager.h +++ b/app/layoutmanager.h @@ -47,6 +47,7 @@ class Corona; class Importer; class ActiveLayout; class LaunchersSignals; +class TopLayout; class View; } @@ -99,10 +100,10 @@ public: //! layout cant be found ActiveLayout *activeLayout(QString id) const; int activeLayoutPos(QString id) const; + TopLayout *topLayout(QString id) const; //! returns the current and active layout based on activities and user preferences ActiveLayout *currentLayout() const; - LaunchersSignals *launchersSignals(); QStringList activities(); @@ -112,6 +113,8 @@ public: void importDefaultLayout(bool newInstanceIfPresent = false); void importPresets(bool includeDefault = false); + bool assignActiveToTopLayout(ActiveLayout *active, QString id); + public slots: void showAboutDialog(); @@ -188,6 +191,7 @@ private: LaunchersSignals *m_launchersSignals{nullptr}; QList m_activeLayouts; + QList m_topLayouts; KActivities::Controller *m_activitiesController;