diff --git a/app/globalshortcuts.cpp b/app/globalshortcuts.cpp index ad08df4b..c4a56639 100644 --- a/app/globalshortcuts.cpp +++ b/app/globalshortcuts.cpp @@ -1,749 +1,872 @@ /* * 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" #include "globalshortcuts.h" #include "layoutmanager.h" +#include "universalsettings.h" #include "dock/dockview.h" #include #include #include #include #include #include #include #include #include #include #include // X11 #include #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_hideDockTimer.setSingleShot(true); + 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_hideDockTimer.setInterval(300); + m_hideDocksTimer.setInterval(300); } else { //on wayland in acting just as simple timer that hides the dock afterwards - m_hideDockTimer.setInterval(2500); + m_hideDocksTimer.setInterval(2500); } - connect(&m_hideDockTimer, &QTimer::timeout, this, &GlobalShortcuts::hideDockTimerSlot); + 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]() { - showDock(); + 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() { - QHash *views = m_corona->layoutManager()->currentDockViews(); + QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); - for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { - const auto applets = it.key()->applets(); + 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 (it.value()->visibility()->isHidden()) { + if (view->visibility()->isHidden()) { m_lastInvokedAction = m_singleMetaAction; - m_hideDock = it.value(); - m_hideDock->visibility()->setBlockHiding(true); - m_hideDockTimer.start(); + 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, it, applet]() { - it.value()->toggleAppletExpanded(applet->id()); + QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view, applet]() { + view->toggleAppletExpanded(applet->id()); }); } else { - it.value()->toggleAppletExpanded(applet->id()); + 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; } - m_calledItem = item; - m_numbersMethodIndex = methodIndex; - m_methodShowNumbers = metaObject->method(m_numbersMethodIndex); + m_calledItems.append(item); + m_methodsShowNumbers.append(metaObject->method(methodIndex)); QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, index - 1))) { if (methodIndex2 != -1) { - m_methodShowNumbers.invoke(item, Q_ARG(QVariant, true)); + m_methodsShowNumbers[m_methodsShowNumbers.count() - 1].invoke(item, Q_ARG(QVariant, true)); } return true; } } } } } } return false; } bool GlobalShortcuts::activateLatteEntryAtContainment(const DockView *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)"); + int methodIndex2 = metaObject->indexOfMethod("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1 || (methodIndex2 == -1)) { continue; } - m_calledItem = item; - m_numbersMethodIndex = methodIndex2; - m_methodShowNumbers = metaObject->method(m_numbersMethodIndex); + int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? + applicationLauncherId(view->containment()) : -1; + + m_calledItems.append(item); + m_methodsShowNumbers.append(metaObject->method(methodIndex2)); + int lastMethod = m_methodsShowNumbers.count() - 1; QMetaMethod method = metaObject->method(methodIndex); if (view->visibility()->isHidden()) { //! delay the execution in order to show first the dock - if (m_methodShowNumbers.invoke(item, Q_ARG(QVariant, true))) { + if (m_methodsShowNumbers[lastMethod].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_methodShowNumbers.invoke(item, Q_ARG(QVariant, true)); + m_methodsShowNumbers[lastMethod].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()); - QHash *views = m_corona->layoutManager()->currentDockViews(); + QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); - // To avoid overly complex configuration, we'll try to get the 90% usecase to work - // which is activating a task on the task manager on a panel on the primary screen. - for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { - if (it.value()->screen() != qGuiApp->primaryScreen()) { - continue; - } + m_calledItems.clear(); + m_methodsShowNumbers.clear(); - if ((it.value()->latteTasksPresent() && activateLatteEntryAtContainment(it.value(), index, modifier)) - || (!it.value()->latteTasksPresent() && it.value()->tasksPresent() && - activatePlasmaTaskManagerEntryAtContainment(it.key(), index, modifier))) { - m_hideDock = it.value(); - m_hideDock->visibility()->setBlockHiding(true); - m_hideDockTimer.start(); - return; - } - } - - // we didn't find anything on primary, try all the panels - for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { - if ((it.value()->latteTasksPresent() && activateLatteEntryAtContainment(it.value(), index, modifier)) - || (!it.value()->latteTasksPresent() && it.value()->tasksPresent() && - activatePlasmaTaskManagerEntryAtContainment(it.key(), index, modifier))) { - m_hideDock = it.value(); - m_hideDock->visibility()->setBlockHiding(true); - m_hideDockTimer.start(); + foreach (auto view, sortedViews) { + if ((view->latteTasksPresent() && activateLatteEntryAtContainment(view, index, modifier)) + || (!view->latteTasksPresent() && view->tasksPresent() && + activatePlasmaTaskManagerEntryAtContainment(view->containment(), index, modifier))) { + m_hideDocks.clear(); + 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(); // update badges in all Latte Tasks plasmoids for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { updateBadgeForTaskInContainment(it.key(), identifier, value); } } -void GlobalShortcuts::showDock() +bool GlobalShortcuts::isCapableToShowAppletsNumbers(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; + } + + 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)"); + int methodIndex = metaObject->indexOfMethod("setShowAppletsNumbers(QVariant,QVariant,QVariant)"); if (methodIndex == -1) { continue; } - m_calledItem = item; - m_numbersMethodIndex = methodIndex; - m_methodShowNumbers = metaObject->method(m_numbersMethodIndex); + int appLauncher = m_corona->universalSettings()->metaForwardedToLatte() ? + applicationLauncherId(c) : -1; - if (m_methodShowNumbers.invoke(item, Q_ARG(QVariant, true))) { + m_calledItems.append(item); + m_methodsShowNumbers.append(metaObject->method(methodIndex)); + int lastMethod = m_methodsShowNumbers.count() - 1; + + if (m_methodsShowNumbers[lastMethod].invoke(item, Q_ARG(QVariant, true), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { return true; } } } } return false; }; - QHash *views = m_corona->layoutManager()->currentDockViews(); + auto invokeShowOnlyMeta = [this](const Plasma::Containment * c) { + if (QQuickItem *containmentInterface = c->property("_plasma_graphicObject").value()) { + const auto &childItems = containmentInterface->childItems(); - // To avoid overly complex configuration, we'll try to get the 90% usecase to work - // which is activating a task on the task manager on a panel on the primary screen. - for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { - if (it.value()->screen() != qGuiApp->primaryScreen()) { - 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. - if (it.value()->latteTasksPresent() && invokeShowNumbers(it.key())) { - if (!m_hideDockTimer.isActive()) { - m_hideDock = it.value(); - m_hideDock->visibility()->setBlockHiding(true); - m_hideDockTimer.start(); - } else { - m_hideDockTimer.stop(); - hideDockTimerSlot(); + // 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; + + m_calledItems.append(item); + m_methodsShowNumbers.append(metaObject->method(methodIndex)); + int lastMethod = m_methodsShowNumbers.count() - 1; + + if (m_methodsShowNumbers[lastMethod].invoke(item, Q_ARG(QVariant, false), Q_ARG(QVariant, true), Q_ARG(QVariant, appLauncher))) { + return true; + } + } } + } - return; + return false; + }; + + QList sortedViews = sortedViewsList(m_corona->layoutManager()->currentDockViews()); + + DockView *viewWithTasks{nullptr}; + DockView *viewWithMeta{nullptr}; + + foreach (auto view, sortedViews) { + if (!viewWithTasks && isCapableToShowAppletsNumbers(view->containment())) { + viewWithTasks = view; + } + + if (!viewWithMeta && m_corona->universalSettings()->metaForwardedToLatte() && applicationLauncherId(view->containment()) > -1) { + viewWithMeta = view; } } - // we didn't find anything on primary, try all the panels - for (auto it = views->constBegin(), end = views->constEnd(); it != end; ++it) { - if (it.value()->latteTasksPresent() && invokeShowNumbers(it.key())) { - if (!m_hideDockTimer.isActive()) { - m_hideDock = it.value(); - m_hideDock->visibility()->setBlockHiding(true); - m_hideDockTimer.start(); - } else { - m_hideDockTimer.stop(); - hideDockTimerSlot(); - } + bool dockFound{false}; - return; + 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) { 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 : shouldnt had reached here..."; return false; } bool GlobalShortcuts::dockAtLowerEdgePriority(DockView *test, DockView *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; } -void GlobalShortcuts::showSettings() +QList GlobalShortcuts::sortedViewsList(QHash *views) { QList docks; - QHash *views = m_corona->layoutManager()->currentDockViews(); + //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]; docks[j + 1] = docks[j]; docks[j] = temp; } } } qDebug() << " -------- sorted -----"; for (int i = 0; i < docks.count(); ++i) { qDebug() << i << ". " << docks[i]->screen()->name() << " - " << docks[i]->location(); } + return docks; +} + +void GlobalShortcuts::showSettings() +{ + 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::hideDockTimerSlot() +void GlobalShortcuts::hideDocksTimerSlot() { - if (!m_lastInvokedAction || !m_hideDock) { + if (!m_lastInvokedAction || m_hideDocks.count() == 0) { return; } if (isPlatformX11()) { if (!x11_areModKeysDepressed(m_lastInvokedAction->shortcut())) { m_lastInvokedAction = Q_NULLPTR; - m_hideDock->visibility()->setBlockHiding(false); - m_hideDock = Q_NULLPTR; - if (m_calledItem) { - m_methodShowNumbers.invoke(m_calledItem, Q_ARG(QVariant, false)); - m_calledItem = Q_NULLPTR; - m_numbersMethodIndex = -1; + if (docksToHideAreValid()) { + foreach (auto dockView, m_hideDocks) { + dockView->visibility()->setBlockHiding(false); + } + + m_hideDocks.clear(); + + 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_calledItems.clear(); + } + } else { + m_hideDocks.clear(); + m_calledItems.clear(); } return; } - m_hideDockTimer.start(); + m_hideDocksTimer.start(); } else { // TODO: This is needs to be fixed in wayland m_lastInvokedAction = Q_NULLPTR; - m_hideDock->visibility()->setBlockHiding(false); - m_hideDock = Q_NULLPTR; - if (m_calledItem) { - m_methodShowNumbers.invoke(m_calledItem, Q_ARG(QVariant, false)); - m_calledItem = Q_NULLPTR; - m_numbersMethodIndex = -1; + if (docksToHideAreValid()) { + foreach (auto dockView, m_hideDocks) { + dockView->visibility()->setBlockHiding(false); + } + + m_hideDocks.clear(); + + 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_calledItems.clear(); + } + } else { + m_hideDocks.clear(); + m_calledItems.clear(); } } } } #include "moc_globalshortcuts.cpp" diff --git a/app/globalshortcuts.h b/app/globalshortcuts.h index f8ca8b91..7b1fb545 100644 --- a/app/globalshortcuts.h +++ b/app/globalshortcuts.h @@ -1,79 +1,87 @@ /* * 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 #include "../liblattedock/dock.h" #include #include #include +namespace Plasma { +class Containment; +} + namespace Latte { class DockCorona; class DockView; } 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 hideDockTimerSlot(); + void hideDocksTimerSlot(); private: void init(); void activateEntry(int index, Qt::Key modifier); - void showDock(); - void hideDock(); + void showDocks(); void showSettings(); bool activateLatteEntryAtContainment(const DockView *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 docksToHideAreValid(); + bool isCapableToShowAppletsNumbers(const Plasma::Containment *c); + + int applicationLauncherId(const Plasma::Containment *c); + + 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_hideDockTimer; - DockView *m_hideDock; + QTimer m_hideDocksTimer; + QList m_hideDocks; - int m_numbersMethodIndex{ -1}; - QQuickItem *m_calledItem{nullptr}; - QMetaMethod m_methodShowNumbers; + QList m_calledItems; + QList m_methodsShowNumbers; DockCorona *m_corona{nullptr}; }; } #endif // GLOBALSHORTCUTS_H diff --git a/app/layoutmanager.cpp b/app/layoutmanager.cpp index 3c305184..adf8789f 100644 --- a/app/layoutmanager.cpp +++ b/app/layoutmanager.cpp @@ -1,1206 +1,1219 @@ /* * 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" #include "importer.h" #include "infoview.h" #include "settingsdialog.h" #include "launcherssignals.h" #include "layout.h" #include "screenpool.h" #include "universalsettings.h" #include "dock/dockview.h" #include #include #include #include #include #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::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 +{ + 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 { 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 { 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 didnt 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()); } } } 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); m_activeLayouts.append(newLayout); newLayout->initToCorona(m_corona); 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; m_activeLayouts.append(newLayout); newLayout->initToCorona(m_corona); 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; m_activeLayouts.append(newLayout); newLayout->initToCorona(m_corona); 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 doesnt 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(); }); } } void LayoutManager::updateColorizerSupport() { bool enable{false}; foreach (auto layout, m_activeLayouts) { for (const auto *view : *layout->dockViews()) { if (view->colorizerSupport()) { enable = true; break; } } if (enable) { break; } } if (enable) { m_corona->universalSettings()->enableActivitiesModel(); } else { m_corona->universalSettings()->disableActivitiesModel(); } } //! 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 2e5ce042..5d134d39 100644 --- a/app/layoutmanager.h +++ b/app/layoutmanager.h @@ -1,196 +1,197 @@ /* * 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 #include "launcherssignals.h" #include "settingsdialog.h" #include #include #include #include namespace Plasma { class Containment; class Types; } namespace KActivities { class Controller; } namespace Latte { class DockCorona; class DockView; class Importer; class Layout; class LaunchersSignals; } 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 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; //! 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 updateColorizerSupport(); void hideLatteSettingsDialog(); Q_INVOKABLE void showLatteSettingsDialog(int page = Latte::Dock::LayoutPage); //! switch to specified layout, default previousMemoryUsage means that it didnt 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 currentLayoutIsSwitching(QString layoutName); private slots: void currentActivityChanged(const QString &id); void showInfoWindowChanged(); void syncMultipleLayoutsToActivities(QString layoutForOrphans = QString()); private: 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/universalsettings.cpp b/app/universalsettings.cpp index 398abec1..b0c6c9e4 100644 --- a/app/universalsettings.cpp +++ b/app/universalsettings.cpp @@ -1,418 +1,441 @@ /* * 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 "universalsettings.h" #include "dockcorona.h" #include "sortedactivitiesmodel.h" #include +#include #include namespace Latte { +const QString UniversalSettings::KWinMetaForwardToLatteString = "org.kde.lattedock,/Latte,org.kde.LatteDock,activateLauncherMenu"; + UniversalSettings::UniversalSettings(KSharedConfig::Ptr config, QObject *parent) : QObject(parent), m_config(config), m_universalGroup(KConfigGroup(config, QStringLiteral("UniversalSettings"))) { connect(this, &UniversalSettings::canDisableBordersChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::currentLayoutNameChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::downloadWindowSizeChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::lastNonAssignedLayoutNameChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::launchersChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::layoutsColumnWidthsChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::layoutsMemoryUsageChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::layoutsWindowSizeChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::mouseSensitivityChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::screenTrackerIntervalChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::showInfoWindowChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::unifiedGlobalShortcutsChanged, this, &UniversalSettings::saveConfig); connect(this, &UniversalSettings::versionChanged, this, &UniversalSettings::saveConfig); + + m_metaForwardedToLatte = kwin_metaForwardedToLatte(); } UniversalSettings::~UniversalSettings() { saveConfig(); cleanupSettings(); if (m_runningActivitiesModel) { m_runningActivitiesModel->deleteLater(); } } void UniversalSettings::load() { //! check if user has set the autostart option bool autostartUserSet = m_universalGroup.readEntry("userConfiguredAutostart", false); if (!autostartUserSet && !autostart()) { setAutostart(true); } //! load configuration loadConfig(); } bool UniversalSettings::showInfoWindow() const { return m_showInfoWindow; } void UniversalSettings::setShowInfoWindow(bool show) { if (m_showInfoWindow == show) { return; } m_showInfoWindow = show; emit showInfoWindowChanged(); } bool UniversalSettings::unifiedGlobalShortcuts() const { return m_unifiedGlobalShortcuts; } void UniversalSettings::setUnifiedGlobalShortcuts(bool unified) { if (m_unifiedGlobalShortcuts == unified) { return; } m_unifiedGlobalShortcuts = unified; emit unifiedGlobalShortcutsChanged(); } int UniversalSettings::version() const { return m_version; } void UniversalSettings::setVersion(int ver) { if (m_version == ver) { return; } m_version = ver; emit versionChanged(); } int UniversalSettings::screenTrackerInterval() const { return m_screenTrackerInterval; } void UniversalSettings::setScreenTrackerInterval(int duration) { if (m_screenTrackerInterval == duration) { return; } m_screenTrackerInterval = duration; emit screenTrackerIntervalChanged(); } QString UniversalSettings::currentLayoutName() const { return m_currentLayoutName; } void UniversalSettings::setCurrentLayoutName(QString layoutName) { if (m_currentLayoutName == layoutName) { return; } m_currentLayoutName = layoutName; emit currentLayoutNameChanged(); } QString UniversalSettings::lastNonAssignedLayoutName() const { return m_lastNonAssignedLayoutName; } void UniversalSettings::setLastNonAssignedLayoutName(QString layoutName) { if (m_lastNonAssignedLayoutName == layoutName) { return; } m_lastNonAssignedLayoutName = layoutName; emit lastNonAssignedLayoutNameChanged(); } QSize UniversalSettings::downloadWindowSize() const { return m_downloadWindowSize; } void UniversalSettings::setDownloadWindowSize(QSize size) { if (m_downloadWindowSize == size) { return; } m_downloadWindowSize = size; emit downloadWindowSizeChanged(); } QSize UniversalSettings::layoutsWindowSize() const { return m_layoutsWindowSize; } void UniversalSettings::setLayoutsWindowSize(QSize size) { if (m_layoutsWindowSize == size) { return; } m_layoutsWindowSize = size; emit layoutsWindowSizeChanged(); } QStringList UniversalSettings::layoutsColumnWidths() const { return m_layoutsColumnWidths; } void UniversalSettings::setLayoutsColumnWidths(QStringList widths) { if (m_layoutsColumnWidths == widths) { return; } m_layoutsColumnWidths = widths; emit layoutsColumnWidthsChanged(); } QStringList UniversalSettings::launchers() const { return m_launchers; } void UniversalSettings::setLaunchers(QStringList launcherList) { if (m_launchers == launcherList) { return; } m_launchers = launcherList; emit launchersChanged(); } bool UniversalSettings::autostart() const { QFile autostartFile(QDir::homePath() + "/.config/autostart/org.kde.latte-dock.desktop"); return autostartFile.exists(); } void UniversalSettings::setAutostart(bool state) { //! remove old autostart file QFile oldAutostartFile(QDir::homePath() + "/.config/autostart/latte-dock.desktop"); if (oldAutostartFile.exists()) { oldAutostartFile.remove(); } //! end of removal of old autostart file QFile autostartFile(QDir::homePath() + "/.config/autostart/org.kde.latte-dock.desktop"); QFile metaFile("/usr/share/applications/org.kde.latte-dock.desktop"); if (!state && autostartFile.exists()) { //! the first time that the user disables the autostart, this is recorded //! and from now own it will not be recreated it in the beginning if (!m_universalGroup.readEntry("userConfiguredAutostart", false)) { m_universalGroup.writeEntry("userConfiguredAutostart", true); } autostartFile.remove(); emit autostartChanged(); } else if (state && metaFile.exists()) { metaFile.copy(autostartFile.fileName()); //! I havent added the flag "OnlyShowIn=KDE;" into the autostart file //! because I fall onto a Plasma 5.8 case that this flag //! didnt let the plasma desktop to start emit autostartChanged(); } } bool UniversalSettings::canDisableBorders() const { return m_canDisableBorders; } void UniversalSettings::setCanDisableBorders(bool enable) { if (m_canDisableBorders == enable) { return; } m_canDisableBorders = enable; emit canDisableBordersChanged(); } +bool UniversalSettings::metaForwardedToLatte() const +{ + return m_metaForwardedToLatte; +} + +bool UniversalSettings::kwin_metaForwardedToLatte() const +{ + //! Indentify Plasma Desktop version + QProcess process; + process.start("kreadconfig5 --file kwinrc --group ModifierOnlyShortcuts --key Meta"); + process.waitForFinished(); + QString output(process.readAllStandardOutput()); + + output = output.remove("\n"); + + return (output == UniversalSettings::KWinMetaForwardToLatteString); +} + Dock::LayoutsMemoryUsage UniversalSettings::layoutsMemoryUsage() const { return m_memoryUsage; } void UniversalSettings::setLayoutsMemoryUsage(Dock::LayoutsMemoryUsage layoutsMemoryUsage) { if (m_memoryUsage == layoutsMemoryUsage) { return; } m_memoryUsage = layoutsMemoryUsage; emit layoutsMemoryUsageChanged(); } Dock::MouseSensitivity UniversalSettings::mouseSensitivity() const { return m_mouseSensitivity; } void UniversalSettings::setMouseSensitivity(Dock::MouseSensitivity sensitivity) { if (m_mouseSensitivity == sensitivity) { return; } m_mouseSensitivity = sensitivity; emit mouseSensitivityChanged(); } void UniversalSettings::loadConfig() { m_version = m_universalGroup.readEntry("version", 1); m_canDisableBorders = m_universalGroup.readEntry("canDisableBorders", false); m_currentLayoutName = m_universalGroup.readEntry("currentLayout", QString()); m_downloadWindowSize = m_universalGroup.readEntry("downloadWindowSize", QSize(800, 550)); m_lastNonAssignedLayoutName = m_universalGroup.readEntry("lastNonAssignedLayout", QString()); m_layoutsWindowSize = m_universalGroup.readEntry("layoutsWindowSize", QSize(700, 450)); m_layoutsColumnWidths = m_universalGroup.readEntry("layoutsColumnWidths", QStringList()); m_launchers = m_universalGroup.readEntry("launchers", QStringList()); m_screenTrackerInterval = m_universalGroup.readEntry("screenTrackerInterval", 2500); m_showInfoWindow = m_universalGroup.readEntry("showInfoWindow", true); m_unifiedGlobalShortcuts = m_universalGroup.readEntry("unifiedGlobalShortcuts", true); m_memoryUsage = static_cast(m_universalGroup.readEntry("memoryUsage", (int)Dock::SingleLayout)); m_mouseSensitivity = static_cast(m_universalGroup.readEntry("mouseSensitivity", (int)Dock::HighSensitivity)); } void UniversalSettings::saveConfig() { m_universalGroup.writeEntry("version", m_version); m_universalGroup.writeEntry("canDisableBorders", m_canDisableBorders); m_universalGroup.writeEntry("currentLayout", m_currentLayoutName); m_universalGroup.writeEntry("downloadWindowSize", m_downloadWindowSize); m_universalGroup.writeEntry("lastNonAssignedLayout", m_lastNonAssignedLayoutName); m_universalGroup.writeEntry("layoutsWindowSize", m_layoutsWindowSize); m_universalGroup.writeEntry("layoutsColumnWidths", m_layoutsColumnWidths); m_universalGroup.writeEntry("launchers", m_launchers); m_universalGroup.writeEntry("screenTrackerInterval", m_screenTrackerInterval); m_universalGroup.writeEntry("showInfoWindow", m_showInfoWindow); m_universalGroup.writeEntry("unifiedGlobalShortcuts", m_unifiedGlobalShortcuts); m_universalGroup.writeEntry("memoryUsage", (int)m_memoryUsage); m_universalGroup.writeEntry("mouseSensitivity", (int)m_mouseSensitivity); m_universalGroup.sync(); } void UniversalSettings::cleanupSettings() { KConfigGroup containments = KConfigGroup(m_config, QStringLiteral("Containments")); containments.deleteGroup(); containments.sync(); } QString UniversalSettings::splitterIconPath() { auto *dockCorona = qobject_cast(parent()); if (dockCorona) { return dockCorona->kPackage().filePath("splitter"); } return ""; } QString UniversalSettings::trademarkIconPath() { auto *dockCorona = qobject_cast(parent()); if (dockCorona) { return dockCorona->kPackage().filePath("trademark"); } return ""; } QAbstractItemModel *UniversalSettings::runningActivitiesModel() const { return m_runningActivitiesModel; } void UniversalSettings::setRunningActivitiesModel(SortedActivitiesModel *model) { if (m_runningActivitiesModel == model) { return; } if (m_runningActivitiesModel) { m_runningActivitiesModel->deleteLater(); } m_runningActivitiesModel = model; emit runningActivitiesModelChanged(); } void UniversalSettings::enableActivitiesModel() { if (!m_runningActivitiesModel) { setRunningActivitiesModel(new SortedActivitiesModel({KActivities::Info::Running, KActivities::Info::Stopping}, this)); } } void UniversalSettings::disableActivitiesModel() { if (m_runningActivitiesModel) { setRunningActivitiesModel(nullptr); } } float UniversalSettings::luminasFromFile(QString imageFile, int edge) { enableActivitiesModel(); return m_runningActivitiesModel->luminasFromFile(imageFile, edge); } } diff --git a/app/universalsettings.h b/app/universalsettings.h index 5110eb3b..ef62f3e3 100644 --- a/app/universalsettings.h +++ b/app/universalsettings.h @@ -1,173 +1,180 @@ /* * 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 UNIVERSALSETTINGS_H #define UNIVERSALSETTINGS_H #include #include #include #include #include "../liblattedock/dock.h" class SortedActivitiesModel; namespace Latte { class LayoutManager; //! This class holds all the settings that are universally available //! independent of layouts class UniversalSettings : public QObject { Q_OBJECT Q_PROPERTY(bool autostart READ autostart WRITE setAutostart NOTIFY autostartChanged) Q_PROPERTY(bool showInfoWindow READ showInfoWindow WRITE setShowInfoWindow NOTIFY showInfoWindowChanged) //! some v0.7 users wanted the previous global shortcuts behavior that applets werent taken //! into account. By default unification will take place but the user will be able to change //! this from latte global settings Q_PROPERTY(bool unifiedGlobalShortcuts READ unifiedGlobalShortcuts NOTIFY unifiedGlobalShortcutsChanged) Q_PROPERTY(QString currentLayoutName READ currentLayoutName WRITE setCurrentLayoutName NOTIFY currentLayoutNameChanged) Q_PROPERTY(QStringList launchers READ launchers WRITE setLaunchers NOTIFY launchersChanged) Q_PROPERTY(Latte::Dock::MouseSensitivity mouseSensitivity READ mouseSensitivity WRITE setMouseSensitivity NOTIFY mouseSensitivityChanged) Q_PROPERTY(QAbstractItemModel *runningActivitiesModel READ runningActivitiesModel NOTIFY runningActivitiesModelChanged) public: UniversalSettings(KSharedConfig::Ptr config, QObject *parent = nullptr); ~UniversalSettings() override; void load(); bool autostart() const; void setAutostart(bool state); bool canDisableBorders() const; void setCanDisableBorders(bool enable); + bool metaForwardedToLatte() const; + bool showInfoWindow() const; void setShowInfoWindow(bool show); bool unifiedGlobalShortcuts() const; int version() const; void setVersion(int ver); int screenTrackerInterval() const; void setScreenTrackerInterval(int duration); QString currentLayoutName() const; void setCurrentLayoutName(QString layoutName); QString lastNonAssignedLayoutName() const; void setLastNonAssignedLayoutName(QString layoutName); QSize downloadWindowSize() const; void setDownloadWindowSize(QSize size); QSize layoutsWindowSize() const; void setLayoutsWindowSize(QSize size); QStringList layoutsColumnWidths() const; void setLayoutsColumnWidths(QStringList widths); QStringList launchers() const; void setLaunchers(QStringList launcherList); Dock::MouseSensitivity mouseSensitivity() const; void setMouseSensitivity(Dock::MouseSensitivity sensitivity); QAbstractItemModel *runningActivitiesModel() const; void setRunningActivitiesModel(SortedActivitiesModel *model); void enableActivitiesModel(); void disableActivitiesModel(); public slots: Q_INVOKABLE QString splitterIconPath(); Q_INVOKABLE QString trademarkIconPath(); Q_INVOKABLE float luminasFromFile(QString imageFile, int edge); signals: void autostartChanged(); void canDisableBordersChanged(); void currentLayoutNameChanged(); void downloadWindowSizeChanged(); void lastNonAssignedLayoutNameChanged(); void layoutsColumnWidthsChanged(); void layoutsWindowSizeChanged(); void launchersChanged(); void layoutsMemoryUsageChanged(); void mouseSensitivityChanged(); void runningActivitiesModelChanged(); void screenTrackerIntervalChanged(); void showInfoWindowChanged(); void unifiedGlobalShortcutsChanged(); void versionChanged(); private slots: void loadConfig(); void saveConfig(); private: + static const QString KWinMetaForwardToLatteString; + void cleanupSettings(); void setUnifiedGlobalShortcuts(bool unified); + bool kwin_metaForwardedToLatte() const; + Dock::LayoutsMemoryUsage layoutsMemoryUsage() const; void setLayoutsMemoryUsage(Dock::LayoutsMemoryUsage layoutsMemoryUsage); private: bool m_canDisableBorders{false}; + bool m_metaForwardedToLatte{false}; bool m_showInfoWindow{true}; bool m_unifiedGlobalShortcuts{true}; //when there isnt a version it is an old universal file int m_version{1}; int m_screenTrackerInterval{2500}; QString m_currentLayoutName; QString m_lastNonAssignedLayoutName; QSize m_downloadWindowSize{800, 550}; QSize m_layoutsWindowSize{700, 450}; QStringList m_layoutsColumnWidths; QStringList m_launchers; Dock::LayoutsMemoryUsage m_memoryUsage; Dock::MouseSensitivity m_mouseSensitivity{Dock::HighSensitivity}; KConfigGroup m_universalGroup; KSharedConfig::Ptr m_config; SortedActivitiesModel *m_runningActivitiesModel{nullptr}; friend class LayoutManager; friend class DockCorona; }; } #endif //UNIVERSALSETTINGS_H diff --git a/containment/package/contents/ui/applet/AppletItemWrapper.qml b/containment/package/contents/ui/applet/AppletItemWrapper.qml index 285c4a90..fcafb8c2 100644 --- a/containment/package/contents/ui/applet/AppletItemWrapper.qml +++ b/containment/package/contents/ui/applet/AppletItemWrapper.qml @@ -1,775 +1,785 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.latte 0.1 as Latte Item{ id: wrapper width: { if (container.isInternalViewSplitter && !root.editMode) return 0; if (container.isSeparator && !root.editMode) { if (!root.isVertical) return -1; else return root.iconSize; } if (container.needsFillSpace && (container.sizeForFill>-1) && root.isHorizontal){ return container.sizeForFill; } if (container.latteApplet) { //! commented because it was breaking the context menu available area, I dont remember where //! we needed this... // if (container.showZoomed && root.isVertical) // return root.statesLineSize + root.thickMargin + root.iconSize + 1; //else return latteApplet.tasksWidth; } else { return scaledWidth; } } height: { if (container.isInternalViewSplitter && !root.editMode) return 0; if (container.isSeparator && !root.editMode) { if (root.isVertical) return -1; else return root.iconSize; } if (container.needsFillSpace && (container.sizeForFill>-1) && root.isVertical){ return container.sizeForFill; } if (container.latteApplet) { //! commented because it was breaking the context menu available area, I dont remember where //! we needed this... //if (container.showZoomed && root.isHorizontal) // return root.statesLineSize + root.thickMargin + root.iconSize + 1; // else return latteApplet.tasksHeight; } else { return scaledHeight; } } //width: container.isInternalViewSplitter && !root.editMode ? 0 : Math.round( latteApplet ? ((container.showZoomed && root.isVertical) ? // scaledWidth : latteApplet.tasksWidth) : scaledWidth ) //height: container.isInternalViewSplitter&& !root.editMode ? 0 : Math.round( latteApplet ? ((container.showZoomed && root.isHorizontal) ? // scaledHeight : latteApplet.tasksHeight ): scaledHeight ) property bool disableScaleWidth: false property bool disableScaleHeight: false property bool editMode: root.editMode property int appletMinimumWidth: applet && applet.Layout ? applet.Layout.minimumWidth : -1 property int appletMinimumHeight: applet && applet.Layout ? applet.Layout.minimumHeight : -1 property int appletPreferredWidth: applet && applet.Layout ? applet.Layout.preferredWidth : -1 property int appletPreferredHeight: applet && applet.Layout ? applet.Layout.preferredHeight : -1 property int appletMaximumWidth: applet && applet.Layout ? applet.Layout.maximumWidth : -1 property int appletMaximumHeight: applet && applet.Layout ? applet.Layout.maximumHeight : -1 property int iconSize: root.iconSize property int marginWidth: root.isVertical ? (container.isSystray ? root.thickMarginBase : root.thickMargin ) : (root.inFullJustify && (container.firstChildOfStartLayout || container.lastChildOfEndLayout ) ? 0 : root.iconMargin) //Fitt's Law property int marginHeight: root.isHorizontal ? (container.isSystray ? root.thickMarginBase : root.thickMargin ) : (root.inFullJustify && (container.firstChildOfStartLayout || container.lastChildOfEndLayout ) ? 0 : root.iconMargin) //Fitt's Law property real scaledWidth: zoomScaleWidth * (layoutWidth + marginWidth) property real scaledHeight: zoomScaleHeight * (layoutHeight + marginHeight) property real zoomScaleWidth: disableScaleWidth ? 1 : zoomScale property real zoomScaleHeight: disableScaleHeight ? 1 : zoomScale property int layoutWidthResult: 0 property int layoutWidth property int layoutHeight // property int localMoreSpace: root.reverseLinesPosition ? root.statesLineSize + 2 : appletMargin property int localMoreSpace: appletMargin property int moreHeight: (container.isSystray || root.reverseLinesPosition) && root.isHorizontal ? localMoreSpace : 0 property int moreWidth: (container.isSystray || root.reverseLinesPosition) && root.isVertical ? localMoreSpace : 0 property real center:(width + hiddenSpacerLeft.separatorSpace + hiddenSpacerRight.separatorSpace) / 2 property real zoomScale: 1 property int index: container.index property Item wrapperContainer: _wrapperContainer property Item clickedEffect: _clickedEffect property Item fakeIconItemContainer: _fakeIconItemContainer // property int pHeight: applet ? applet.Layout.preferredHeight : -10 /*function debugLayouts(){ if(applet){ console.log("---------- "+ applet.pluginName +" ----------"); console.log("MinW "+applet.Layout.minimumWidth); console.log("PW "+applet.Layout.preferredWidth); console.log("MaxW "+applet.Layout.maximumWidth); console.log("FillW "+applet.Layout.fillWidth); console.log("-----"); console.log("MinH "+applet.Layout.minimumHeight); console.log("PH "+applet.Layout.preferredHeight); console.log("MaxH "+applet.Layout.maximumHeight); console.log("FillH "+applet.Layout.fillHeight); console.log("-----"); console.log("LayoutW: " + layoutWidth); console.log("LayoutH: " + layoutHeight); } } onLayoutWidthChanged: { debugLayouts(); } onLayoutHeightChanged: { debugLayouts(); }*/ onAppletMinimumWidthChanged: { if(zoomScale == 1) checkCanBeHovered(); updateLayoutWidth(); } onAppletMinimumHeightChanged: { if(zoomScale == 1) checkCanBeHovered(); updateLayoutHeight(); } onAppletPreferredWidthChanged: updateLayoutWidth(); onAppletPreferredHeightChanged: updateLayoutHeight(); onAppletMaximumWidthChanged: updateLayoutWidth(); onAppletMaximumHeightChanged: updateLayoutHeight(); onIconSizeChanged: { updateLayoutWidth(); updateLayoutHeight(); } onEditModeChanged: { updateLayoutWidth(); updateLayoutHeight(); } onZoomScaleChanged: { if ((zoomScale === root.zoomFactor) && !root.globalDirectRender) { root.setGlobalDirectRender(true); } if ((zoomScale > 1) && !container.isZoomed) { container.isZoomed = true; if (!root.editMode && !animationWasSent) { root.slotAnimationsNeedBothAxis(1); animationWasSent = true; } } else if ((zoomScale == 1) && container.isZoomed) { container.isZoomed = false; if (animationWasSent) { root.slotAnimationsNeedBothAxis(-1); animationWasSent = false; } } } Connections { target: root onIsVerticalChanged: { if (container.latteApplet) { return; } wrapper.disableScaleWidth = false; wrapper.disableScaleHeight = false; if (root.isVertical) { wrapper.updateLayoutHeight(); wrapper.updateLayoutWidth(); } else { wrapper.updateLayoutWidth(); wrapper.updateLayoutHeight(); } } } function updateLayoutHeight(){ if(container.isInternalViewSplitter){ if(!root.editMode) layoutHeight = 0; else layoutHeight = root.iconSize + moreHeight + root.statesLineSize; } else if(applet && applet.pluginName === "org.kde.plasma.panelspacer"){ layoutHeight = root.iconSize + moreHeight; } else if(container.isSystray && root.isHorizontal){ layoutHeight = root.statesLineSize + root.iconSize; } else{ if(applet && (applet.Layout.minimumHeight > root.iconSize) && root.isVertical && !canBeHovered && !container.fakeIconItem){ // return applet.Layout.minimumHeight; layoutHeight = applet.Layout.minimumHeight; } //it is used for plasmoids that need to scale only one axis... e.g. the Weather Plasmoid else if(applet && ( applet.Layout.maximumHeight < root.iconSize || applet.Layout.preferredHeight > root.iconSize || container.lockZoom) && root.isVertical && !disableScaleWidth && !container.fakeIconItem) { //&& !root.editMode ){ if (!container.isSpacer) { disableScaleHeight = true; } //this way improves performance, probably because during animation the preferred sizes update a lot if((applet.Layout.maximumHeight < root.iconSize)){ layoutHeight = applet.Layout.maximumHeight; } else if (applet.Layout.minimumHeight > root.iconSize){ layoutHeight = applet.Layout.minimumHeight; } else if ((applet.Layout.preferredHeight > root.iconSize) || (container.lockZoom && applet.Layout.preferredHeight !== 0 )){ layoutHeight = applet.Layout.preferredHeight; } else{ layoutHeight = root.iconSize + moreHeight; } } else { layoutHeight = root.iconSize + moreHeight; } } //return root.iconSize + moreHeight; } function updateLayoutWidth(){ if(container.isInternalViewSplitter){ if(!root.editMode) layoutWidth = 0; else layoutWidth = root.iconSize + moreWidth + root.statesLineSize; } else if(applet && applet.pluginName === "org.kde.plasma.panelspacer"){ layoutWidth = root.iconSize + moreWidth; } else if(container.isSystray && root.isVertical){ layoutWidth = root.statesLineSize + root.iconSize; } else{ if(applet && (applet.Layout.minimumWidth > root.iconSize) && root.isHorizontal && !canBeHovered && !container.fakeIconItem){ layoutWidth = applet.Layout.minimumWidth; } //it is used for plasmoids that need to scale only one axis... e.g. the Weather Plasmoid else if(applet && ( (applet.Layout.maximumWidth < root.iconSize) || (applet.Layout.preferredWidth > root.iconSize)) && root.isHorizontal && !disableScaleHeight && !container.fakeIconItem){ // && !root.editMode){ if (!container.isSpacer) { disableScaleWidth = true; } //this way improves performance, probably because during animation the preferred sizes update a lot if((applet.Layout.maximumWidth < root.iconSize)){ // return applet.Layout.maximumWidth; layoutWidth = applet.Layout.maximumWidth; } else if (applet.Layout.minimumWidth > root.iconSize){ layoutWidth = applet.Layout.minimumWidth; } else if ((applet.Layout.preferredWidth > root.iconSize) || (container.lockZoom && applet.Layout.preferredWidth !== 0 )){ layoutWidth = applet.Layout.preferredWidth; } else{ layoutWidth = root.iconSize + moreWidth; } } else{ layoutWidth = root.iconSize + moreWidth; } } } Loader{ anchors.fill: parent active: root.activeIndicator === Latte.Dock.AllIndicator || (root.activeIndicator === Latte.Dock.InternalsIndicator && fakeIconItem) sourceComponent: Item{ anchors.fill: parent ActiveIndicator{} } } Item{ id:_wrapperContainer width:{ if (container.needsFillSpace && (container.sizeForFill>-1) && root.isHorizontal){ return wrapper.width; } if (container.isInternalViewSplitter) return wrapper.layoutWidth; else return parent.zoomScaleWidth * wrapper.layoutWidth; } height:{ if (container.needsFillSpace && (container.sizeForFill>-1) && root.isVertical){ return wrapper.height; } if (container.isInternalViewSplitter) return wrapper.layoutHeight; else return parent.zoomScaleHeight * wrapper.layoutHeight; } //width: Math.round( container.isInternalViewSplitter ? wrapper.layoutWidth : parent.zoomScaleWidth * wrapper.layoutWidth ) //height: Math.round( container.isInternalViewSplitter ? wrapper.layoutHeight : parent.zoomScaleHeight * wrapper.layoutHeight ) anchors.rightMargin: plasmoid.location === PlasmaCore.Types.RightEdge ? lowThickUsed : 0 anchors.leftMargin: plasmoid.location === PlasmaCore.Types.LeftEdge ? lowThickUsed : 0 anchors.topMargin: plasmoid.location === PlasmaCore.Types.TopEdge ? lowThickUsed : 0 anchors.bottomMargin: plasmoid.location === PlasmaCore.Types.BottomEdge ? lowThickUsed : 0 opacity: appletShadow.active ? 0 : 1 property int lowThickUsed: root.thickMarginBase //BEGIN states states: [ State { name: "left" when: (plasmoid.location === PlasmaCore.Types.LeftEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:wrapper.verticalCenter; horizontalCenter:undefined; top:undefined; bottom:undefined; left:parent.left; right:undefined;} } }, State { name: "right" when: (plasmoid.location === PlasmaCore.Types.RightEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:wrapper.verticalCenter; horizontalCenter:undefined; top:undefined; bottom:undefined; left:undefined; right:parent.right;} } }, State { name: "bottom" when: (plasmoid.location === PlasmaCore.Types.BottomEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:undefined; horizontalCenter:wrapper.horizontalCenter; top:undefined; bottom:parent.bottom; left:undefined; right:undefined;} } }, State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:undefined; horizontalCenter:wrapper.horizontalCenter; top:parent.top; bottom:undefined; left:undefined; right:undefined;} } } ] //END states ///Secret MouseArea to be used by the folder widget Loader{ anchors.fill: parent active: container.fakeIconItem && applet.pluginName === "org.kde.plasma.folder" && !container.disableLatteParabolicIconHeuristics sourceComponent: MouseArea{ onClicked: dock.toggleAppletExpanded(applet.id); } } Item{ id: _fakeIconItemContainer anchors.centerIn: parent //we setup as maximum for hidden container of some applets that break //the Latte experience the size:96 or units.iconSizeHints.panel. //This is why after that size the folder widget changes //to fullRepresentation instead of compact one width: Math.min(maxSize, parent.width) height: width property int maxSize:{ if (Latte.WindowSystem.plasmaDesktopVersion === 0 || (Latte.WindowSystem.plasmaDesktopVersion < Latte.WindowSystem.makeVersion(5,12,80)) ) { return 96; } else { return units.iconSizeHints.panel; } } } Loader{ anchors.fill: parent active: container.fakeIconItem && !container.disableLatteParabolicIconHeuristics sourceComponent: Latte.IconItem{ id: fakeAppletIconItem anchors.fill: parent source: { if (appletIconItem && appletIconItem.visible) return appletIconItem.source; else if (appletImageItem && appletImageItem.visible) return appletImageItem.source; } usesPlasmaTheme: appletIconItem && appletIconItem.visible ? appletIconItem.usesPlasmaTheme : false Loader{ anchors.centerIn: parent active: root.debugModeOverloadedIcons sourceComponent: Rectangle{ width: 30 height: 30 color: "green" opacity: 0.65 } } //ActiveIndicator{} } } } //spacer background Loader{ anchors.fill: _wrapperContainer active: applet && (applet.pluginName === "org.kde.plasma.panelspacer") && root.editMode sourceComponent: Rectangle{ anchors.fill: parent border.width: 1 border.color: theme.textColor color: "transparent" opacity: 0.7 radius: root.iconMargin Rectangle{ anchors.centerIn: parent color: parent.border.color width: parent.width - 1 height: parent.height - 1 opacity: 0.2 } } } Loader{ anchors.fill: _wrapperContainer active: container.isInternalViewSplitter && root.editMode rotation: root.isVertical ? 90 : 0 sourceComponent: PlasmaCore.SvgItem{ id:splitterImage anchors.fill: parent svg: PlasmaCore.Svg{ imagePath: root.universalSettings.splitterIconPath() } layer.enabled: true layer.effect: DropShadow { radius: root.appShadowSize fast: true samples: 2 * radius color: root.appShadowColor verticalOffset: 2 } Component.onCompleted: { if (root.isVertical) { wrapper.updateLayoutHeight(); wrapper.updateLayoutWidth(); } else { wrapper.updateLayoutWidth(); wrapper.updateLayoutHeight(); } } } } ///Shadow in applets Loader{ id: appletShadow anchors.fill: container.appletWrapper active: container.applet && !colorizerLoader.isShown && (((plasmoid.configuration.shadows === 1 /*Locked Applets*/ && (!container.canBeHovered || (container.lockZoom && (applet.pluginName !== root.plasmoidName))) ) || (plasmoid.configuration.shadows === 2 /*All Applets*/ && (applet.pluginName !== root.plasmoidName))) || (root.forceTransparentPanel && plasmoid.configuration.shadows>0 && applet.pluginName !== root.plasmoidName)) /*on forced transparent state*/ onActiveChanged: { if (active) { wrapperContainer.opacity = 0; } else { wrapperContainer.opacity = 1; } } sourceComponent: DropShadow{ anchors.fill: parent color: root.appShadowColor //"#ff080808" fast: true samples: 2 * radius source: container.fakeIconItem ? _wrapperContainer : container.applet radius: shadowSize verticalOffset: forcedShadow ? 0 : 2 property int shadowSize : root.appShadowSize //Math.ceil(root.iconSize / 12) property bool forcedShadow: root.forceTransparentPanel && plasmoid.configuration.shadows>0 && applet.pluginName !== root.plasmoidName ? true : false } } /// START Applets Number Loader{ id: appletNumberLoader anchors.fill: container.appletWrapper active: opacityN>0 asynchronous: true property int fixedIndex:-1 onActiveChanged: { if (active) { fixedIndex = parabolicManager.pseudoAppletIndex(index); } } Component.onCompleted: fixedIndex = parabolicManager.pseudoAppletIndex(index); - property real opacityN: universalSettings && universalSettings.unifiedGlobalShortcuts && root.showAppletsNumbers - && container.canShowAppletNumberBadge && fixedIndex<20 ? 1 : 0 + property real opacityN: universalSettings && container.canShowAppletNumberBadge && + ((universalSettings.unifiedGlobalShortcuts && root.showAppletsNumbers && fixedIndex<20) + || (root.showMetaBadge && applet.id===applicationLauncherId)) ? 1 : 0 Behavior on opacityN { NumberAnimation { duration: root.durationTime*2*units.longDuration } } sourceComponent: Item{ Loader{ anchors.fill: appletNumber active: root.enableShadows sourceComponent: DropShadow{ color: root.appShadowColor fast: true samples: 2 * radius source: appletNumber radius: root.appShadowSize/2 verticalOffset: 2 } } Latte.BadgeText { id: appletNumber anchors.centerIn: parent minimumWidth: 0.4 * root.iconSize height: width numberValue: appletNumberLoader.fixedIndex < 10 ? appletNumberLoader.fixedIndex : 0 - textValue: (keysArrayIndex>=0 && keysArrayIndex<10) ? keysAboveTen[keysArrayIndex] : '' + textValue: { + if (root.showMetaBadge && applet.id === applicationLauncherId) { + return '\u2318'; + } else if (keysArrayIndex>=0 && keysArrayIndex<10) { + return keysAboveTen[keysArrayIndex]; + } else { + return ''; + } + } - showNumber: appletNumberLoader.fixedIndex < 10 - showText: appletNumberLoader.fixedIndex>=10 && appletNumberLoader.fixedIndex<20 + showNumber: appletNumberLoader.fixedIndex < 10 && !(root.showMetaBadge && applet.id === applicationLauncherId) + showText: (appletNumberLoader.fixedIndex>=10 && appletNumberLoader.fixedIndex<20) || + (root.showMetaBadge && applet.id === applicationLauncherId) proportion: 0 radiusPerCentage: 50 property int keysArrayIndex: appletNumberLoader.fixedIndex-10; property var keysAboveTen: ['0', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.'] } } } //END of Applets number BrightnessContrast{ id:hoveredImage anchors.fill: _wrapperContainer source: _wrapperContainer enabled: opacity != 0 ? true : false opacity: appletMouseArea.containsMouse ? 1 : 0 brightness: 0.25 contrast: 0.15 Behavior on opacity { NumberAnimation { duration: root.durationTime*units.longDuration } } } BrightnessContrast { id: _clickedEffect anchors.fill: _wrapperContainer source: _wrapperContainer visible: clickedAnimation.running } /* onHeightChanged: { if ((index == 1)|| (index==3)){ console.log("H: "+index+" ("+zoomScale+"). "+currentLayout.children[1].height+" - "+currentLayout.children[3].height+" - "+(currentLayout.children[1].height+currentLayout.children[3].height)); } } onZoomScaleChanged:{ if ((index == 1)|| (index==3)){ console.log(index+" ("+zoomScale+"). "+currentLayout.children[1].height+" - "+currentLayout.children[3].height+" - "+(currentLayout.children[1].height+currentLayout.children[3].height)); } }*/ Loader{ anchors.fill: parent active: root.debugMode sourceComponent: Rectangle{ anchors.fill: parent color: "transparent" //! red visualizer, in debug mode for the applets that use fillWidth or fillHeight //! green, for the rest border.color: (container.needsFillSpace && (container.sizeForFill>-1) && root.isHorizontal) ? "red" : "green" border.width: 1 } } Behavior on zoomScale { enabled: !root.globalDirectRender NumberAnimation { duration: 3 * container.animationTime easing.type: Easing.OutCubic } } Behavior on zoomScale { enabled: root.globalDirectRender && !restoreAnimation.running NumberAnimation { duration: root.directRenderAnimationTime } } function calculateScales( currentMousePosition ){ if (root.editMode || root.zoomFactor===1 || root.durationTime===0) { return; } var distanceFromHovered = Math.abs(index - layoutsContainer.hoveredIndex); // A new algorithm tryig to make the zoom calculation only once // and at the same time fixing glitches if ((distanceFromHovered == 0)&& (currentMousePosition > 0) ){ //use the new parabolicManager in order to handle all parabolic effect messages var scales = parabolicManager.applyParabolicEffect(index, currentMousePosition, center); /*if (root.latteApplet && Math.abs(index - root.latteAppletPos) > 2){ root.latteApplet.clearZoom(); }*/ //Left hiddenSpacer if(container.startEdge){ hiddenSpacerLeft.nScale = scales.leftScale - 1; } //Right hiddenSpacer ///there is one more item in the currentLayout ???? if(container.endEdge){ hiddenSpacerRight.nScale = scales.rightScale - 1; } zoomScale = root.zoomFactor; } } //scale function signalUpdateScale(nIndex, nScale, step){ if(container && !container.containsMouse && (container.index === nIndex)){ if ( ((canBeHovered && !lockZoom ) || container.latteApplet) && (applet && applet.status !== PlasmaCore.Types.HiddenStatus) //&& (index != currentLayout.hoveredIndex) ){ if(!container.latteApplet){ if(nScale >= 0) zoomScale = nScale + step; else zoomScale = zoomScale + step; } } } } Component.onCompleted: { root.updateScale.connect(signalUpdateScale); } Component.onDestruction: { root.updateScale.disconnect(signalUpdateScale); } }// Main task area // id:wrapper diff --git a/containment/package/contents/ui/main.qml b/containment/package/contents/ui/main.qml index 0f79f47e..ed4183f4 100644 --- a/containment/package/contents/ui/main.qml +++ b/containment/package/contents/ui/main.qml @@ -1,1879 +1,1885 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 import org.kde.draganddrop 2.0 as DragDrop import org.kde.plasma.plasmoid 2.0 import org.kde.latte 0.1 as Latte import "applet" as Applet import "../code/LayoutManager.js" as LayoutManager DragDrop.DropArea { id: root objectName: "dockLayoutView" LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft && !root.isVertical LayoutMirroring.childrenInherit: true //// BEGIN SIGNALS signal clearZoomSignal(); signal separatorsUpdated(); signal signalActivateEntryAtIndex(int entryIndex); signal signalNewInstanceForEntryAtIndex(int entryIndex); signal updateEffectsArea(); signal updateIndexes(); signal updateScale(int delegateIndex, real newScale, real step); //// END SIGNALS ////BEGIN properties property bool debugMode: Qt.application.arguments.indexOf("--graphics")>=0 property bool debugModeSpacers: Qt.application.arguments.indexOf("--spacers")>=0 property bool debugModeTimers: Qt.application.arguments.indexOf("--timers")>=0 property bool debugModeWindow: Qt.application.arguments.indexOf("--with-window")>=0 property bool debugModeOverloadedIcons: Qt.application.arguments.indexOf("--overloaded-icons")>=0 property bool globalDirectRender: false //it is used as a globalDirectRender for all elements in the dock property int directRenderAnimationTime: 0 property bool addLaunchersMessage: false property bool addLaunchersInTaskManager: plasmoid.configuration.addLaunchersInTaskManager // when there are only plasma style task managers the automatic icon size algorithm should better be disabled property bool autoDecreaseIconSize: plasmoid.configuration.autoDecreaseIconSize && !containsOnlyPlasmaTasks && layoutsContainer.fillApplets<=0 property bool backgroundOnlyOnMaximized: plasmoid.configuration.backgroundOnlyOnMaximized property bool behaveAsPlasmaPanel: { if (!dock || !dock.visibility) return false; return (visibilityManager.panelIsBiggerFromIconSize && (zoomFactor === 1.0) && (dock.visibility.mode === Latte.Dock.AlwaysVisible || dock.visibility.mode === Latte.Dock.WindowsGoBelow) && (plasmoid.configuration.panelPosition === Latte.Dock.Justify) && !(root.solidPanel && panelShadowsActive)); } property bool blurEnabled: plasmoid.configuration.blurEnabled && !root.forceTransparentPanel || (hasExpandedApplet && zoomFactor===1 && plasmoid.configuration.panelSize===100) property bool confirmedDragEntered: false property bool containsOnlyPlasmaTasks: false //this is flag to indicate when from tasks only a plasma based one is found property bool dockContainsMouse: dock && dock.visibility ? dock.visibility.containsMouse : false property bool disablePanelShadowMaximized: plasmoid.configuration.disablePanelShadowForMaximized && Latte.WindowSystem.compositingActive property bool drawShadowsExternal: panelShadowsActive && behaveAsPlasmaPanel && !visibilityManager.inTempHiding property bool editMode: editModeVisual.inEditMode property bool windowIsTouching: dock && dock.visibility && (dock.visibility.existsWindowMaximized || dock.visibility.existsWindowSnapped || hasExpandedApplet) property bool forceSemiTransparentPanel: ((!plasmoid.configuration.solidBackgroundForMaximized && plasmoid.configuration.backgroundOnlyOnMaximized && windowIsTouching) || (plasmoid.configuration.solidBackgroundForMaximized && !plasmoid.configuration.backgroundOnlyOnMaximized && !windowIsTouching)) && Latte.WindowSystem.compositingActive property bool forceSolidPanel: plasmoid.configuration.solidBackgroundForMaximized && dock && dock.visibility && Latte.WindowSystem.compositingActive - &&(dock.visibility.existsWindowMaximized || dock.visibility.existsWindowSnapped || hasExpandedApplet) + &&(dock.visibility.existsWindowMaximized || dock.visibility.existsWindowSnapped || hasExpandedApplet + || showAppletsNumbers || showMetaBadge) + property bool forceTransparentPanel: root.backgroundOnlyOnMaximized && !(dock.visibility.existsWindowMaximized || dock.visibility.existsWindowSnapped) && Latte.WindowSystem.compositingActive && !(hasExpandedApplet && zoomFactor===1 && plasmoid.configuration.panelSize===100) property bool forceColorizer: Latte.WindowSystem.compositingActive && plasmoid.configuration.colorizeTransparentPanels property bool maximizedWindowTitleBarBehavesAsPanelBackground: dock && dock.visibility && (!plasmoid.configuration.solidBackgroundForMaximized && plasmoid.configuration.backgroundOnlyOnMaximized) && (dock.visibility.mode === Latte.Dock.WindowsGoBelow) && (plasmoid.location === PlasmaCore.Types.TopEdge) && (!useThemePanel || panelTransparency<40) && dock.visibility.existsWindowMaximized readonly property bool hasExpandedApplet: plasmoid.applets.some(function (item) { return (item.status >= PlasmaCore.Types.NeedsAttentionStatus && item.pluginName !== root.plasmoidName && item.status !== PlasmaCore.Types.HiddenStatus); }) readonly property bool hasUserSpecifiedBackground: (dock && dock.managedLayout && dock.managedLayout.background.startsWith("/")) ? true : false property bool dockIsShownCompletely: !(dockIsHidden || inSlidingIn || inSlidingOut) && !root.editMode property bool immutable: plasmoid.immutable property bool indicateAudioStreams: plasmoid.configuration.indicateAudioStreams property bool inFullJustify: (plasmoid.configuration.panelPosition === Latte.Dock.Justify) && (plasmoid.configuration.maxLength===100) property bool inSlidingIn: visibilityManager ? visibilityManager.inSlidingIn : false property bool inSlidingOut: visibilityManager ? visibilityManager.inSlidingOut : false property bool inStartup: true property bool isHalfShown: false //is used to disable the zoom hovering effect at sliding in-out the dock property bool isHorizontal: plasmoid.formFactor === PlasmaCore.Types.Horizontal property bool isReady: !(dockIsHidden || inSlidingIn || inSlidingOut) property bool isVertical: !isHorizontal property bool isHovered: latteApplet ? ((latteAppletHoveredIndex !== -1) && (layoutsContainer.hoveredIndex !== -1)) //|| wholeArea.containsMouse : (layoutsContainer.hoveredIndex !== -1) //|| wholeArea.containsMouse property bool mouseWheelActions: plasmoid.configuration.mouseWheelActions property bool normalState : false property bool onlyAddingStarup: true //is used for the initialization phase in startup where there arent removals, this variable provides a way to grow icon size property bool shrinkThickMargins: plasmoid.configuration.shrinkThickMargins property bool showAppletsNumbers: false + property bool showMetaBadge: false + property int applicationLauncherId: -1 property bool solidPanel: Latte.WindowSystem.compositingActive ? plasmoid.configuration.solidPanel : true //FIXME: possibly this is going to be the default behavior, this user choice //has been dropped from the Dock Configuration Window //property bool smallAutomaticIconJumps: plasmoid.configuration.smallAutomaticIconJumps property bool smallAutomaticIconJumps: true property bool useThemePanel: noApplets === 0 || !Latte.WindowSystem.compositingActive ? true : (plasmoid.configuration.useThemePanel || plasmoid.configuration.solidBackgroundForMaximized) property alias hoveredIndex: layoutsContainer.hoveredIndex property alias directRenderDelayerIsRunning: directRenderDelayerForEnteringTimer.running property int activeIndicator: plasmoid.configuration.activeIndicator property int actionsBlockHiding: 0 //actions that block hiding property int animationsNeedBothAxis:0 //animations need space in both axes, e.g zooming a task property int animationsNeedLength: 0 // animations need length, e.g. adding a task property int animationsNeedThickness: 0 // animations need thickness, e.g. bouncing animation property int animationTime: durationTime*2.8*units.longDuration property int automaticIconSizeBasedSize: -1 //it is not set, this is the defautl //what is the highest icon size based on what icon size is used, screen calculated or user specified property int maxIconSize: proportionIconSize!==-1 ? proportionIconSize : plasmoid.configuration.iconSize property int iconSize: automaticIconSizeBasedSize > 0 && autoDecreaseIconSize ? Math.min(automaticIconSizeBasedSize, root.maxIconSize) : root.maxIconSize property int proportionIconSize: { //icon size based on screen height if ((plasmoid.configuration.proportionIconSize===-1) || !dock) return -1; return Math.max(16,Math.round(dock.screenGeometry.height * plasmoid.configuration.proportionIconSize/100/8)*8); } property int iconStep: 8 property int latteAppletPos: -1 property int maxLength: root.isHorizontal ? width * (plasmoid.configuration.maxLength/100) : height * (plasmoid.configuration.maxLength/100) property int middleClickAction: plasmoid.configuration.middleClickAction property int modifier: plasmoid.configuration.modifier property int modifierClickAction: plasmoid.configuration.modifierClickAction property int modifierClick: plasmoid.configuration.modifierClick property int panelEdgeSpacing: Math.max(panelBoxBackground.lengthMargins, 1.5*appShadowSize) property int panelTransparency: plasmoid.configuration.panelTransparency property bool panelShadowsActive: (( (plasmoid.configuration.panelShadows && !root.backgroundOnlyOnMaximized) || (plasmoid.configuration.panelShadows && root.backgroundOnlyOnMaximized && !root.forceTransparentPanel)) && !(disablePanelShadowMaximized && dock.visibility.existsWindowMaximized)) || (hasExpandedApplet && zoomFactor===1 && plasmoid.configuration.panelSize===100 && !(root.solidPanel && !plasmoid.configuration.panelShadows) ) property int appShadowOpacity: (plasmoid.configuration.shadowOpacity/100) * 255 property int appShadowSize: enableShadows ? (0.4*root.iconSize) * (plasmoid.configuration.shadowSize/100) : 0 property int appShadowSizeOriginal: enableShadows ? (0.4*maxIconSize) * (plasmoid.configuration.shadowSize/100) : 0 property string appShadowColor: "#" + decimalToHex(appShadowOpacity) + plasmoid.configuration.shadowColor property string appShadowColorSolid: "#" + plasmoid.configuration.shadowColor property int totalPanelEdgeSpacing: 0 //this is set by PanelBox //FIXME: this is not needed any more probably property int previousAllTasks: -1 //is used to forbit updateAutomaticIconSize when hovering property int offset: { /*if (behaveAsPlasmaPanel) { return 0; }*/ if (root.isHorizontal) { return width * (plasmoid.configuration.offset/100); } else { height * (plasmoid.configuration.offset/100) } } //center the layout correctly when the user uses an offset property int offsetFixed: (offset===0 || panelAlignment === Latte.Dock.Center || plasmoid.configuration.panelPosition === Latte.Dock.Justify)? offset : offset+panelMarginLength/2+totalPanelEdgeSpacing/2 property int realSize: iconSize + iconMargin property int realPanelSize: 0 property int realPanelLength: 0 property int realPanelThickness: 0 //this is set by the PanelBox property int panelMargin: 0 property int panelMarginLength: 0 property int panelShadow: 0 //shadowsSize property int editShadow: { if (!Latte.WindowSystem.compositingActive) { return 0; } else if (dock && dock.screenGeometry) { return (dock.screenGeometry.height/90); } else { return 7; } } property int themePanelSize: { //root.statesLineSize + root.iconSize + root.iconMargin + 1 var panelBase = root.statesLineSize + root.panelMargin; var margin = latteApplet ? thickMargin : 0; var maxPanelSize = (root.statesLineSize + iconSize + margin + 1) - panelBase; var percentage = Latte.WindowSystem.compositingActive ? plasmoid.configuration.panelSize/100 : 1; return Math.max(panelBase, panelBase + percentage*maxPanelSize); } //decouple iconMargin which now is used only for length calculations with thickMargins //which are used for thickness calculations property int thickMarginBase: { if (shrinkThickMargins) { if (behaveAsPlasmaPanel){ return 0; } else { return 1; } } else { return Math.ceil(0.06 * iconSize); } } property int thickMarginHigh: { if (shrinkThickMargins) { if (behaveAsPlasmaPanel){ return (reverseLinesPosition ? Math.max(root.statesLineSize/2, 1) : 1); } else { return Math.max(1, 0.5 * appShadowSize); } } else { if (behaveAsPlasmaPanel) { return (reverseLinesPosition ? Math.max(root.statesLineSize, 4) : 4); } else { return Math.max( Math.ceil(0.06 * iconSize), 0.5 * appShadowSize); } } } property int thickMargin: thickMarginBase + thickMarginHigh //it is used in order to not break the calculations for the thickness placement //especially in automatic icon sizes calculations property int thickMarginOriginal: Math.ceil(0.06 * maxIconSize + Math.max( Math.ceil(0.06 * maxIconSize), 0.5 * appShadowSizeOriginal)) //! iconMargin from configuration is a percentage. The calculation provides a length //! for that value between 0.04 - 0.5 of iconSize, this way 100% iconMargin means //! equal to the iconSize property int iconMargin: Math.ceil( ((0.5 * (plasmoid.configuration.iconMargin))/100) * iconSize) property int statesLineSize: (latteApplet && !(root.showWindowsOnlyFromLaunchers && root.activeIndicator === Latte.Dock.NoneIndicator)) || (activeIndicator !== Latte.Dock.NoneIndicator) ? Math.ceil( root.iconSize/13 ) : 0 ///FIXME: I can't remember why this is needed, maybe for the anchorings!!! In order for the Double Layout to not mess the anchorings... //property int layoutsContainer.mainLayoutPosition: !plasmoid.immutable ? Latte.Dock.Center : (root.isVertical ? Latte.Dock.Top : Latte.Dock.Left) //property int panelAlignment: plasmoid.configuration.panelPosition !== Latte.Dock.Justify ? plasmoid.configuration.panelPosition : layoutsContainer.mainLayoutPosition property int panelAlignment: !root.editMode ? plasmoid.configuration.panelPosition : ( plasmoid.configuration.panelPosition === Latte.Dock.Justify ? Latte.Dock.Center : plasmoid.configuration.panelPosition ) property real zoomFactor: (Latte.WindowSystem.compositingActive && durationTime>0) ? ( 1 + (plasmoid.configuration.zoomLevel / 20) ) : 1 readonly property string plasmoidName: "org.kde.latte.plasmoid" property var iconsArray: [16, 22, 32, 48, 64, 96, 128, 256] property var layoutManager: LayoutManager property Item dragOverlay property Item toolBox property Item latteAppletContainer property Item latteApplet property Item parabolicManager: _parabolicManager property QtObject dock property QtObject universalSettings property QtObject universalLayoutManager property QtObject dockManagedLayout: dock && dock.managedLayout ? dock.managedLayout : null // TO BE DELETED, if not needed: property int counter:0; ///BEGIN properties provided to Latte Plasmoid //shadows for applets, it should be removed as the appleitems dont need it any more property bool enableShadows: plasmoid.configuration.shadows || root.forceTransparentPanel property bool dockIsHidden: dock ? dock.visibility.isHidden : true property bool groupTasksByDefault: plasmoid.configuration.groupTasksByDefault property bool dotsOnActive: plasmoid.configuration.dotsOnActive property bool highlightWindows: plasmoid.configuration.highlightWindows property bool reverseLinesPosition: !latteApplet && plasmoid.configuration.panelSize===100 ? !plasmoid.configuration.reverseLinesPosition : plasmoid.configuration.reverseLinesPosition property bool showGlow: plasmoid.configuration.showGlow property bool glow3D: plasmoid.configuration.glow3D property bool showToolTips: plasmoid.configuration.showToolTips property bool showWindowActions: plasmoid.configuration.showWindowActions property bool showWindowsOnlyFromLaunchers: plasmoid.configuration.showWindowsOnlyFromLaunchers property bool showOnlyCurrentScreen: plasmoid.configuration.showOnlyCurrentScreen property bool showOnlyCurrentDesktop: plasmoid.configuration.showOnlyCurrentDesktop property bool showOnlyCurrentActivity: plasmoid.configuration.showOnlyCurrentActivity property bool smartLaunchersEnabled: plasmoid.configuration.smartLaunchersEnabled property bool threeColorsWindows: plasmoid.configuration.threeColorsWindows property bool titleTooltips: plasmoid.configuration.titleTooltips readonly property bool hasInternalSeparator: latteApplet ? latteApplet.hasInternalSeparator : false property int activeIndicatorType: plasmoid.configuration.activeIndicatorType property int animationStep: { if (!universalSettings || universalSettings.mouseSensitivity === Latte.Dock.HighSensitivity) { return 1; } else if (universalSettings.mouseSensitivity === Latte.Dock.MediumSensitivity) { return Math.max(3, root.iconSize / 18); } else if (universalSettings.mouseSensitivity === Latte.Dock.LowSensitivity) { return Math.max(5, root.iconSize / 10); } } property int glowOption: plasmoid.configuration.glowOption property real glowOpacity: plasmoid.configuration.glowOpacity/100 property int latteAppletHoveredIndex: latteApplet ? latteApplet.hoveredIndex : -1 property int launchersGroup: plasmoid.configuration.launchersGroup property int tasksCount: latteApplet ? latteApplet.tasksCount : 0 property real durationTime: { if ((dock && dock.blockAnimations) || !Latte.WindowSystem.compositingActive) { return 0; } if (plasmoid.configuration.durationTime === 0 || plasmoid.configuration.durationTime === 2 ) return plasmoid.configuration.durationTime; if (plasmoid.configuration.durationTime === 1) return 1.65; else if (plasmoid.configuration.durationTime === 3) return 2.35; return 2; } property rect screenGeometry: dock ? dock.screenGeometry : plasmoid.screenGeometry ///END properties from latteApplet /* Layout.preferredWidth: plasmoid.immutable ? (plasmoid.configuration.panelPosition === Latte.Dock.Justify ? layoutsContainer.width + 0.5*iconMargin : layoutsContainer.mainLayout.width + iconMargin) : Screen.width //on unlocked state use the maximum Layout.preferredHeight: plasmoid.immutable ? (plasmoid.configuration.panelPosition === Latte.Dock.Justify ? layoutsContainer.height + 0.5*iconMargin : layoutsContainer.mainLayout.height + iconMargin) : Screen.height //on unlocked state use the maximum*/ Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground //// BEGIN properties in functions property int noApplets: { var count1 = 0; var count2 = 0; count1 = layoutsContainer.mainLayout.children.length; var tempLength = layoutsContainer.mainLayout.children.length; for (var i=tempLength-1; i>=0; --i) { var applet = layoutsContainer.mainLayout.children[i]; if (applet && (applet === dndSpacer || applet === lastSpacer || applet.isInternalViewSplitter)) count1--; } count2 = layoutsContainer.endLayout.children.length; tempLength = layoutsContainer.endLayout.children.length; for (var i=tempLength-1; i>=0; --i) { var applet = layoutsContainer.endLayout.children[i]; if (applet && (applet === dndSpacer || applet === lastSpacer || applet.isInternalViewSplitter)) count2--; } return (count1 + count2); } ///The index of user's current icon size property int currentIconIndex:{ for(var i=iconsArray.length-1; i>=0; --i){ if(iconsArray[i] === iconSize){ return i; } } return 3; } //// END properties in functions ////////////////END properties //// BEGIN OF Behaviors Behavior on iconMargin { NumberAnimation { duration: 0.8 * root.animationTime easing.type: Easing.OutCubic } } Behavior on iconSize { enabled: !(root.editMode && root.behaveAsPlasmaPanel) NumberAnimation { duration: 0.8 * root.animationTime onRunningChanged: { if (!running) { delayUpdateMaskArea.start(); } } } } Behavior on offset { enabled: editModeVisual.plasmaEditMode NumberAnimation { id: offsetAnimation duration: 0.8 * root.animationTime easing.type: Easing.OutCubic } } //// END OF Behaviors //////////////START OF CONNECTIONS onContainsOnlyPlasmaTasksChanged: updateAutomaticIconSize(); onEditModeChanged: { if (editMode) { visibilityManager.updateMaskArea(); updateAutomaticIconSize(); clearZoom(); } else { updateAutomaticIconSize(); layoutsContainer.updateSizeForAppletsInFill(); } updateLayouts(); //! This is used in case the dndspacer has been left behind //! e.g. the user drops a folder and a context menu is appearing //! but the user decides to not make a choice for the applet type if (dndSpacer.parent !== root) { dndSpacer.parent = root; } } onDockChanged: { if (dock) { dock.onAddInternalViewSplitter.connect(addInternalViewSplitters); dock.onRemoveInternalViewSplitter.connect(removeInternalViewSplitters); dock.onXChanged.connect(visibilityManager.updateMaskArea); dock.onYChanged.connect(visibilityManager.updateMaskArea); dock.onWidthChanged.connect(visibilityManager.updateMaskArea); dock.onHeightChanged.connect(visibilityManager.updateMaskArea); dock.hideDockDuringLocationChangeStarted.connect(visibilityManager.slotHideDockDuringLocationChange); dock.showDockAfterLocationChangeFinished.connect(visibilityManager.slotShowDockAfterLocationChange); dock.hideDockDuringScreenChangeStarted.connect(visibilityManager.slotHideDockDuringLocationChange); dock.showDockAfterScreenChangeFinished.connect(visibilityManager.slotShowDockAfterLocationChange); dock.hideDockDuringMovingToLayoutStarted.connect(visibilityManager.slotHideDockDuringLocationChange); dock.showDockAfterMovingToLayoutFinished.connect(visibilityManager.slotShowDockAfterLocationChange); dock.visibility.onContainsMouseChanged.connect(visibilityManager.slotContainsMouseChanged); dock.visibility.onMustBeHide.connect(visibilityManager.slotMustBeHide); dock.visibility.onMustBeShown.connect(visibilityManager.slotMustBeShown); updateContainsOnlyPlasmaTasks(); } } onDockContainsMouseChanged: { if (!dockContainsMouse) { initializeHoveredIndexes(); } } onDragEnter: { if (plasmoid.immutable || dockIsHidden || visibilityManager.inSlidingIn || visibilityManager.inSlidingOut) { event.ignore(); return; } if (event.mimeData.formats.indexOf("application/x-orgkdeplasmataskmanager_taskbuttonitem") >= 0) { return; } if (latteApplet) { if (latteApplet.launchersDrop(event)) { root.addLaunchersMessage = true; if (root.addLaunchersInTaskManager) { return; } } else if (dock.mimeContainsPlasmoid(event.mimeData, "audoban.applet.separator") && root.latteAppletContainer.containsPos(event)) { confirmedDragEntered = true dndSpacer.opacity = 0; dndSpacer.parent = root; return; } } if (!confirmedDragEntered) { confirmedDragEntered = true; slotAnimationsNeedLength(1); } if (!latteApplet || (latteApplet && !dock.mimeContainsPlasmoid(event.mimeData, "org.kde.latte.plasmoid"))) { LayoutManager.insertAtCoordinates2(dndSpacer, event.x, event.y) dndSpacer.opacity = 1; } } onDragMove: { if (event.mimeData.formats.indexOf("application/x-orgkdeplasmataskmanager_taskbuttonitem") >= 0 || dockIsHidden || visibilityManager.inSlidingIn || visibilityManager.inSlidingOut) { return; } if (latteApplet) { if (latteApplet.launchersDrop(event)) { root.addLaunchersMessage = true; if (root.addLaunchersInTaskManager) { return; } } else if (dock.mimeContainsPlasmoid(event.mimeData, "audoban.applet.separator") && root.latteAppletContainer.containsPos(event)) { confirmedDragEntered = true dndSpacer.opacity = 0; dndSpacer.parent = root; return; } } if (!latteApplet || (latteApplet && !dock.mimeContainsPlasmoid(event.mimeData, "org.kde.latte.plasmoid"))) { LayoutManager.insertAtCoordinates2(dndSpacer, event.x, event.y) dndSpacer.opacity = 1; } } onDragLeave: { if (confirmedDragEntered) { slotAnimationsNeedLength(-1); confirmedDragEntered = false; } root.addLaunchersMessage = false; dndSpacer.opacity = 0; dndSpacer.parent = root; } onDrop: { if (dockIsHidden || visibilityManager.inSlidingIn || visibilityManager.inSlidingOut) { return; } if (event.mimeData.formats.indexOf("application/x-orgkdeplasmataskmanager_taskbuttonitem") < 0) { if (latteApplet && latteApplet.launchersDrop(event) && root.addLaunchersInTaskManager) { latteApplet.launchersDropped(event.mimeData.urls); } else if (!latteApplet || (latteApplet && !dock.mimeContainsPlasmoid(event.mimeData, "org.kde.latte.plasmoid"))) { plasmoid.processMimeData(event.mimeData, event.x, event.y); event.accept(event.proposedAction); } } if (confirmedDragEntered) { slotAnimationsNeedLength(-1); confirmedDragEntered = false; } root.addLaunchersMessage = false; dndSpacer.opacity = 0; //! this line is very important because it positions correctly the new applets //dndSpacer.parent = root; } onMaxLengthChanged: { layoutsContainer.updateSizeForAppletsInFill(); if (root.editMode) { updateAutomaticIconSize(); } } onToolBoxChanged: { if (toolBox) { toolBox.visible = false; } } property bool automaticSizeAnimation: false; onAutomaticIconSizeBasedSizeChanged: { if (!automaticSizeAnimation) { automaticSizeAnimation = true; slotAnimationsNeedBothAxis(1); } } onIconSizeChanged: { if (((iconSize === automaticIconSizeBasedSize) || (iconSize === root.maxIconSize)) && automaticSizeAnimation){ slotAnimationsNeedBothAxis(-1); automaticSizeAnimation=false; } } onIsReadyChanged: { if (isReady && !titleTooltipDialog.visible && titleTooltipDialog.activeItemHovered){ titleTooltipDialog.show(titleTooltipDialog.activeItem, titleTooltipDialog.activeItemText); } } onIsVerticalChanged: { if (isVertical) { if (plasmoid.configuration.panelPosition === Latte.Dock.Left) plasmoid.configuration.panelPosition = Latte.Dock.Top; else if (plasmoid.configuration.panelPosition === Latte.Dock.Right) plasmoid.configuration.panelPosition = Latte.Dock.Bottom; } else { if (plasmoid.configuration.panelPosition === Latte.Dock.Top) plasmoid.configuration.panelPosition = Latte.Dock.Left; else if (plasmoid.configuration.panelPosition === Latte.Dock.Bottom) plasmoid.configuration.panelPosition = Latte.Dock.Right; } } onProportionIconSizeChanged: { if (proportionIconSize!==-1) updateAutomaticIconSize(); } // onIconSizeChanged: console.log("Icon Size Changed:"+iconSize); Component.onCompleted: { // currentLayout.isLayoutHorizontal = isHorizontal LayoutManager.plasmoid = plasmoid; LayoutManager.root = root; LayoutManager.layout = layoutsContainer.mainLayout; LayoutManager.layoutS = layoutsContainer.startLayout; layoutManager.layoutE = layoutsContainer.endLayout; LayoutManager.lastSpacer = lastSpacer; LayoutManager.restore(); plasmoid.action("configure").visible = !plasmoid.immutable; plasmoid.action("configure").enabled = !plasmoid.immutable; inStartupTimer.start(); } Component.onDestruction: { console.debug("Destroying Latte Dock Containment ui..."); if (dock) { dock.onAddInternalViewSplitter.disconnect(addInternalViewSplitters); dock.onRemoveInternalViewSplitter.disconnect(removeInternalViewSplitters); dock.onXChanged.disconnect(visibilityManager.updateMaskArea); dock.onYChanged.disconnect(visibilityManager.updateMaskArea); dock.onWidthChanged.disconnect(visibilityManager.updateMaskArea); dock.onHeightChanged.disconnect(visibilityManager.updateMaskArea); dock.hideDockDuringLocationChangeStarted.disconnect(visibilityManager.slotHideDockDuringLocationChange); dock.showDockAfterLocationChangeFinished.disconnect(visibilityManager.slotShowDockAfterLocationChange); dock.hideDockDuringScreenChangeStarted.disconnect(visibilityManager.slotHideDockDuringLocationChange); dock.showDockAfterScreenChangeFinished.disconnect(visibilityManager.slotShowDockAfterLocationChange); dock.hideDockDuringMovingToLayoutStarted.disconnect(visibilityManager.slotHideDockDuringLocationChange); dock.showDockAfterMovingToLayoutFinished.disconnect(visibilityManager.slotShowDockAfterLocationChange); if (dock.visibility) { dock.visibility.onContainsMouseChanged.disconnect(visibilityManager.slotContainsMouseChanged); dock.visibility.onMustBeHide.disconnect(visibilityManager.slotMustBeHide); dock.visibility.onMustBeShown.disconnect(visibilityManager.slotMustBeShown); } } } Containment.onAppletAdded: { addApplet(applet, x, y); LayoutManager.save(); updateIndexes(); } Containment.onAppletRemoved: { LayoutManager.removeApplet(applet); var flexibleFound = false; for (var i = 0; i < layoutsContainer.mainLayout.children.length; ++i) { var applet = layoutsContainer.mainLayout.children[i].applet; if (applet && ((root.isHorizontal && applet.Layout.fillWidth) || (!root.isHorizontal && applet.Layout.fillHeight)) && applet.visible) { flexibleFound = true; break } } if (!flexibleFound) { lastSpacer.parent = layoutsContainer.mainLayout; } LayoutManager.save(); updateIndexes(); updateContainsOnlyPlasmaTasks(); } Plasmoid.onUserConfiguringChanged: { if (plasmoid.immutable) { if (dragOverlay) { dragOverlay.destroy(); } return; } // console.debug("user configuring", plasmoid.userConfiguring) if (plasmoid.userConfiguring) { dock.setBlockHiding(true); // console.log("applets------"); for (var i = 0; i < plasmoid.applets.length; ++i) { // console.log("applet:"+i); plasmoid.applets[i].expanded = false; } if (!dragOverlay) { var component = Qt.createComponent("ConfigOverlay.qml"); if (component.status == Component.Ready) { dragOverlay = component.createObject(root); } else { console.log("Could not create ConfigOverlay"); console.log(component.errorString()); } component.destroy(); } else { dragOverlay.visible = true; } } else { dock.setBlockHiding(false); if (dock.visibility.isHidden) { dock.visibility.mustBeShown(); } if (dragOverlay) { dragOverlay.visible = false; dragOverlay.destroy(); } } } Plasmoid.onImmutableChanged: { plasmoid.action("configure").visible = !plasmoid.immutable; plasmoid.action("configure").enabled = !plasmoid.immutable; ///Set Preferred Sizes/// ///Notice: they are set here because if they are set with a binding ///they break the !immutable experience, the dock becomes too small ///to add applets if (plasmoid.immutable) { if(root.isHorizontal) { root.Layout.preferredWidth = (plasmoid.configuration.panelPosition === Latte.Dock.Justify ? layoutsContainer.width + 0.5*iconMargin : layoutsContainer.mainLayout.width + iconMargin); } else { root.Layout.preferredHeight = (plasmoid.configuration.panelPosition === Latte.Dock.Justify ? layoutsContainer.height + 0.5*iconMargin : layoutsContainer.mainLayout.height + iconMargin); } } else { if (root.isHorizontal) { root.Layout.preferredWidth = Screen.width; } else { root.Layout.preferredHeight = Screen.height; } } visibilityManager.updateMaskArea(); } //////////////END OF CONNECTIONS //////////////START OF FUNCTIONS function addApplet(applet, x, y) { var container = appletContainerComponent.createObject(dndSpacer.parent) container.applet = applet; applet.parent = container.appletWrapper; applet.anchors.fill = container.appletWrapper; applet.visible = true; // don't show applet if it choses to be hidden but still make it // accessible in the panelcontroller container.visible = Qt.binding(function() { return applet.status !== PlasmaCore.Types.HiddenStatus || (!plasmoid.immutable && plasmoid.userConfiguring) }) addContainerInLayout(container, applet, x, y); updateContainsOnlyPlasmaTasks(); } function addContainerInLayout(container, applet, x, y){ // Is there a DND placeholder? Replace it! if ( (dndSpacer.parent === layoutsContainer.mainLayout) || (dndSpacer.parent === layoutsContainer.startLayout) || (dndSpacer.parent===layoutsContainer.endLayout)) { LayoutManager.insertBeforeForLayout(dndSpacer.parent, dndSpacer, container); dndSpacer.parent = root; return; // If the provided position is valid, use it. } else if (x >= 0 && y >= 0) { var index = LayoutManager.insertAtCoordinates2(container, x , y); // Fall through to determining an appropriate insert position. } else { var before = null; container.animationsEnabled = false; if (lastSpacer.parent === layoutsContainer.mainLayout) { before = lastSpacer; } // Insert icons to the left of whatever is at the center (usually a Task Manager), // if it exists. // FIXME TODO: This is a real-world fix to produce a sensible initial position for // launcher icons added by launcher menu applets. The basic approach has been used // since Plasma 1. However, "add launcher to X" is a generic-enough concept and // frequent-enough occurence that we'd like to abstract it further in the future // and get rid of the uglyness of parties external to the containment adding applets // of a specific type, and the containment caring about the applet type. In a better // system the containment would be informed of requested launchers, and determine by // itself what it wants to do with that information. if (applet.pluginName == "org.kde.plasma.icon") { var middle = layoutsContainer.mainLayout.childAt(root.width / 2, root.height / 2); if (middle) { before = middle; } // Otherwise if lastSpacer is here, enqueue before it. } if (before) { LayoutManager.insertBefore(before, container); // Fall through to adding at the end. } else { container.parent = layoutsContainer.mainLayout; } } //Important, removes the first children of the layoutsContainer.mainLayout after the first //applet has been added lastSpacer.parent = root; updateIndexes(); } function addInternalViewSplitters(){ addInternalViewSplitter(-1); addInternalViewSplitter(-1); } function addInternalViewSplitter(pos){ var splittersCount = internalViewSplittersCount(); if(splittersCount<2){ var container = appletContainerComponent.createObject(root); container.internalSplitterId = splittersCount+1; container.visible = true; if(pos>=0 ){ layoutManager.insertAtIndex(container, pos); } else { layoutManager.insertAtIndex(container, Math.floor(layoutsContainer.mainLayout.count / 2)); } layoutManager.save(); } } //! it is used in order to check the right click position //! the only way to take into account the visual appearance //! of the applet (including its spacers) function appletContainsPos(appletId, pos){ for (var i = 0; i < layoutsContainer.startLayout.children.length; ++i) { var child = layoutsContainer.startLayout.children[i]; if (child && child.applet && child.applet.id === appletId && child.containsPos(pos)) return true; } for (var i = 0; i < layoutsContainer.mainLayout.children.length; ++i) { var child = layoutsContainer.mainLayout.children[i]; if (child && child.applet && child.applet.id === appletId && child.containsPos(pos)) return true; } for (var i = 0; i < layoutsContainer.endLayout.children.length; ++i) { var child = layoutsContainer.endLayout.children[i]; if (child && child.applet && child.applet.id === appletId && child.containsPos(pos)) return true; } return false; } function checkLastSpacer() { lastSpacer.parent = root var expands = false; if (isHorizontal) { for (var container in layoutsContainer.mainLayout.children) { var item = layoutsContainer.mainLayout.children[container]; if (item.Layout && item.Layout.fillWidth) { expands = true; } } } else { for (var container in layoutsContainer.mainLayout.children) { var item = layoutsContainer.mainLayout.children[container]; if (item.Layout && item.Layout.fillHeight) { expands = true; } } } if (!expands) { lastSpacer.parent = layoutsContainer.mainLayout } } function clearZoom(){ if (latteApplet){ latteApplet.clearZoom(); } root.clearZoomSignal(); } function containmentActions(){ return dock.containmentActions(); } function decimalToHex(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; while (hex.length < padding) { hex = "0" + hex; } return hex; } function disableDirectRender(){ // root.globalDirectRender = false; } function internalViewSplittersCount(){ var splitters = 0; for (var container in layoutsContainer.startLayout.children) { var item = layoutsContainer.startLayout.children[container]; if(item && item.isInternalViewSplitter) { splitters++; } } for (var container in layoutsContainer.mainLayout.children) { var item = layoutsContainer.mainLayout.children[container]; if(item && item.isInternalViewSplitter) { splitters++; } } for (var container in layoutsContainer.endLayout.children) { var item = layoutsContainer.endLayout.children[container]; if(item && item.isInternalViewSplitter) { splitters++; } } return splitters; } function initializeHoveredIndexes() { layoutsContainer.hoveredIndex = -1; layoutsContainer.currentSpot = -1000; if (latteApplet) { latteApplet.initializeHoveredIndex(); } } function mouseInCanBeHoveredApplet(){ if (latteApplet && latteApplet.containsMouse()) return true; var applets = layoutsContainer.startLayout.children; for(var i=0; i=0; --i){ if(iconsArray[i] === size){ return true; } } return false; } function slotAnimationsNeedBothAxis(step) { if (step === 0) { return; } animationsNeedBothAxis = Math.max(animationsNeedBothAxis + step, 0); visibilityManager.updateMaskArea(); } function slotAnimationsNeedLength(step) { if (step === 0) { return; } animationsNeedLength = Math.max(animationsNeedLength + step, 0); //when need length animations are ended it would be a good idea //to update the tasks geometries in the plasmoid if(animationsNeedLength === 0 && latteApplet) { latteApplet.publishTasksGeometries(); } visibilityManager.updateMaskArea(); } function slotAnimationsNeedThickness(step) { if (step === 0) { return; } animationsNeedThickness = Math.max(animationsNeedThickness + step, 0); visibilityManager.updateMaskArea(); } //this is used when dragging a task in order to not hide the dock //and also by the menu appearing from tasks for the same reason function slotActionsBlockHiding(step) { //if (root.editMode) { // return; // } if ((step === 0) || (!dock)) { return; } actionsBlockHiding = Math.max(actionsBlockHiding + step, 0); if (actionsBlockHiding > 0){ dock.setBlockHiding(true); } else { if (!root.editMode) dock.setBlockHiding(false); } } function slotPreviewsShown(){ if (dock) { dock.deactivateApplets(); } } function startCheckRestoreZoomTimer(){ checkRestoreZoom.start(); } function stopCheckRestoreZoomTimer(){ checkRestoreZoom.stop(); } function startDirectRenderDelayerDuringEntering(){ directRenderDelayerForEnteringTimer.start(); } function setGlobalDirectRender(value) { if (latteApplet && latteApplet.waitingLaunchers.length > 0) return; if (value === true) { if (mouseInCanBeHoveredApplet()) { root.globalDirectRender = true; } else { // console.log("direct render true ignored..."); } } else { root.globalDirectRender = false; } } function updateAutomaticIconSize() { if ( !blockAutomaticUpdateIconSize.running && !visibilityManager.inTempHiding && ((visibilityManager.normalState || root.editMode) && (root.autoDecreaseIconSize || (!root.autoDecreaseIconSize && root.iconSize!=root.maxIconSize))) && (iconSize===root.maxIconSize || iconSize === automaticIconSizeBasedSize) ) { blockAutomaticUpdateIconSize.start(); var layoutLength; var maxLength = root.maxLength; //console.log("------Entered check-----"); //console.log("max length: "+ maxLength); if (root.isVertical) { layoutLength = (plasmoid.configuration.panelPosition === Latte.Dock.Justify) ? layoutsContainer.startLayout.height+layoutsContainer.mainLayout.height+layoutsContainer.endLayout.height : layoutsContainer.mainLayout.height } else { layoutLength = (plasmoid.configuration.panelPosition === Latte.Dock.Justify) ? layoutsContainer.startLayout.width+layoutsContainer.mainLayout.width+layoutsContainer.endLayout.width : layoutsContainer.mainLayout.width } var toShrinkLimit = maxLength-((root.zoomFactor-1)*(iconSize+2*iconMargin)); var toGrowLimit = maxLength-1.5*((root.zoomFactor-1)*(iconSize+2*iconMargin)); var newIconSizeFound = false; if (layoutLength > toShrinkLimit) { //must shrink // console.log("step3"); var nextIconSize = root.maxIconSize; do { nextIconSize = nextIconSize - iconStep; var factor = nextIconSize / iconSize; var nextLength = factor * layoutLength; } while ( (nextLength>toShrinkLimit) && (nextIconSize !== 16)); automaticIconSizeBasedSize = nextIconSize; newIconSizeFound = true; console.log("Step 3 - found:"+automaticIconSizeBasedSize); } else if ((layoutLength 0) { if (foundGoodSize === root.maxIconSize) { automaticIconSizeBasedSize = -1; } else { automaticIconSizeBasedSize = foundGoodSize; } newIconSizeFound = true // console.log("Step 4 - found:"+automaticIconSizeBasedSize); } else { // console.log("Step 4 - did not found..."); } } } } function updateContainsOnlyPlasmaTasks() { if (dock) { root.containsOnlyPlasmaTasks = (dock.tasksPresent() && !dock.latteTasksPresent()); } else { root.containsOnlyPlasmaTasks = false; } } function updateSizeForAppletsInFill() { layoutsContainer.updateSizeForAppletsInFill(); } function updateLayouts(){ if(!root.editMode){ // console.log("update layout - internal view splitters count:"+internalViewSplittersCount()); if (internalViewSplittersCount() === 2) { var splitter = -1; var splitter2 = -1; var totalChildren = layoutsContainer.mainLayout.children.length; for (var i=0; i=0 && splitter2 === -1) { splitter2 = i; } } // console.log("update layouts 1:"+splitter + " - "+splitter2); for (var i=0; i<=splitter; ++i){ var item = layoutsContainer.mainLayout.children[0]; item.parent = layoutsContainer.startLayout; } splitter2 = splitter2 - splitter - 1; // console.log("update layouts 2:"+splitter + " - "+splitter2); totalChildren = layoutsContainer.mainLayout.children.length; for (var i=splitter2+1; i=0; --i) { var item1 = layoutsContainer.mainLayout.children[0]; item1.parent = layoutsContainer.startLayout; } var totalChildren2 = layoutsContainer.endLayout.children.length; for (var i=totalChildren2-1; i>=0; --i) { var item2 = layoutsContainer.endLayout.children[0]; item2.parent = layoutsContainer.startLayout; } var totalChildrenL = layoutsContainer.startLayout.children.length; for (var i=totalChildrenL-1; i>=0; --i) { var itemL = layoutsContainer.startLayout.children[0]; itemL.parent = layoutsContainer.mainLayout; } } } updateIndexes(); } //END functions ////BEGIN interfaces Connections { target: Latte.WindowSystem onCompositingActiveChanged: { visibilityManager.updateMaskArea(); } } Connections { target: dock onWidthChanged:{ if (root.isHorizontal && proportionIconSize!==-1) updateAutomaticIconSize(); } onHeightChanged:{ if (root.isVertical && proportionIconSize!==-1) updateAutomaticIconSize(); } onContextMenuIsShownChanged: { if (!dock.contextMenuIsShown) { checkRestoreZoom.start(); } else { root.setGlobalDirectRender(false); } } } Connections{ target: dock && dock.visibility ? dock.visibility : root ignoreUnknownSignals : true onContainsMouseChanged: { if (mouseInHoverableArea()) { stopCheckRestoreZoomTimer(); } else { startCheckRestoreZoomTimer(); } } } Connections{ target: layoutsContainer onHoveredIndexChanged: { if (latteApplet && layoutsContainer.hoveredIndex>-1){ latteApplet.setHoveredIndex(-1); } if (latteApplet && latteApplet.windowPreviewIsShown && layoutsContainer.hoveredIndex>-1) { latteApplet.hidePreview(); } } } ////END interfaces /////BEGIN: Title Tooltip/////////// PlasmaCore.Dialog{ id: titleTooltipDialog type: PlasmaCore.Dialog.Tooltip flags: Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.ToolTip location: plasmoid.location mainItem: RowLayout{ Layout.fillWidth: true Layout.fillHeight: true PlasmaComponents.Label{ id:titleLbl Layout.leftMargin: 4 Layout.rightMargin: 4 Layout.topMargin: 2 Layout.bottomMargin: 2 text: titleTooltipDialog.title } } visible: false property string title: "" property bool activeItemHovered: false property Item activeItem: null property Item activeItemTooltipParent: null property string activeItemText: "" Component.onCompleted: { root.clearZoomSignal.connect(titleTooltipDialog.hide); } Component.onDestruction: { root.clearZoomSignal.disconnect(titleTooltipDialog.hide); } function hide(debug){ if (!root.titleTooltips) return; activeItemHovered = false; hideTitleTooltipTimer.start(); } function show(taskItem, text){ if (!root.titleTooltips || (latteApplet && latteApplet.contextMenu)){ return; } activeItemHovered = true; if (activeItem !== taskItem) { activeItem = taskItem; activeItemTooltipParent = taskItem.tooltipVisualParent; activeItemText = text; } if (isReady) { showTitleTooltipTimer.start(); } } function update() { activeItemHovered = true title = activeItemText; visualParent = activeItemTooltipParent; if (latteApplet && latteApplet.windowPreviewIsShown) { latteApplet.hidePreview(); } visible = true; } } Timer { id: showTitleTooltipTimer interval: 100 onTriggered: { if (dock && dock.visibility && dock.visibility.containsMouse) { titleTooltipDialog.update(); } if (titleTooltipDialog.visible) { titleTooltipCheckerToNotShowTimer.start(); } if (root.debugModeTimers) { console.log("containment timer: showTitleTooltipTimer called..."); } } } Timer { id: hideTitleTooltipTimer interval: 200 onTriggered: { if (!titleTooltipDialog.activeItemHovered) { titleTooltipDialog.visible = false; } if (root.debugModeTimers) { console.log("containment timer: hideTitleTooltipTimer called..."); } } } //! Timer to fix #811, rare cases that both a window preview and context menu are //! shown Timer { id: titleTooltipCheckerToNotShowTimer interval: 250 onTriggered: { if (titleTooltipDialog.visible && latteApplet && (latteApplet.contextMenu || latteApplet.windowPreviewIsShown)) { titleTooltipDialog.visible = false; } } } /////END: Title Tooltip/////////// ///////////////BEGIN components Component { id: appletContainerComponent Applet.AppletItem{} } ParabolicManager{ id: _parabolicManager } ///////////////END components PlasmaCore.ColorScope{ id: colorScopePalette } ///////////////BEGIN UI elements //it is used to check if the mouse is outside the layoutsContainer borders, //so in that case the onLeave event behavior should be trigerred MouseArea{ id: rootMouseArea anchors.fill: parent hoverEnabled: true onContainsMouseChanged: { if (mouseInHoverableArea()) { stopCheckRestoreZoomTimer(); } else { initializeHoveredIndexes(); startCheckRestoreZoomTimer() } } } Loader{ active: root.debugModeWindow sourceComponent: DebugWindow{} } //! Load a sepia background in order to avoid black background Loader{ anchors.fill: parent active: !Latte.WindowSystem.compositingActive sourceComponent: Image{ anchors.fill: parent fillMode: Image.Tile source: root.hasUserSpecifiedBackground ? dock.managedLayout.background : "../icons/wheatprint.jpg" } } EditModeVisual{ id:editModeVisual // z: root.behaveAsPlasmaPanel ? 1 : 0 } Ruler{id: ruler} RulerMouseArea{ id: rulerMouseArea anchors.fill: ruler z:1100 } Item{ id: panelBox anchors.fill:layoutsContainer // z: root.behaveAsPlasmaPanel ? 0 : 1 PanelBox{ id: panelBoxBackground } } Item { id: lastSpacer parent: layoutsContainer.mainLayout Layout.fillWidth: true Layout.fillHeight: true z:10 Rectangle{ anchors.fill: parent color: "transparent" border.color: "yellow" border.width: 1 } } Item { id: dndSpacer property int normalSize: root.statesLineSize + root.iconSize + root.thickMargin - 1 //visibilityManager.statesLineSizeOriginal + root.maxIconSize + visibilityManager.iconMarginOriginal - 1 width: normalSize height: normalSize Layout.preferredWidth: width Layout.preferredHeight: height opacity: 0 z:10 AddWidgetVisual{} } Loader{ anchors.fill: parent active: root.debugMode z:10 sourceComponent: Item{ Rectangle{ anchors.fill: parent color: "yellow" opacity: 0.30 } } } VisibilityManager{ id: visibilityManager } LayoutsContainer { id: layoutsContainer opacity: colorizerLoader.isShown ? 0 : 1 Behavior on opacity { NumberAnimation { duration: 0.8 * root.animationTime easing.type: Easing.OutCubic } } } Loader{ id: colorizerLoader active: forceColorizer anchors.fill: layoutsContainer z: layoutsContainer.z + 1 // formula for luminance according to: // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef property real textColorRs: { var color = theme.textColor.r; if (color <= 0.03928) { return color / 12.92; } else { return Math.pow( ((color + 0.055) / 1.055), 2.4 ); } } property real textColorGs: { var color = theme.textColor.g; if (color <= 0.03928) { return color / 12.92; } else { return Math.pow( ((color + 0.055) / 1.055), 2.4 ); } } property real textColorBs: { var color = theme.textColor.b; if (color <= 0.03928) { return color / 12.92; } else { return Math.pow( ((color + 0.055) / 1.055), 2.4 ); } } //! ------- property real backColorRs: { var color = theme.backgroundColor.r; if (color <= 0.03928) { return color / 12.92; } else { return Math.pow( ((color + 0.055) / 1.055), 2.4 ); } } property real backColorGs: { var color = theme.backgroundColor.g; if (color <= 0.03928) { return color / 12.92; } else { return Math.pow( ((color + 0.055) / 1.055), 2.4 ); } } property real backColorBs: { var color = theme.backgroundColor.b; if (color <= 0.03928) { return color / 12.92; } else { return Math.pow( ((color + 0.055) / 1.055), 2.4 ); } } readonly property real themeBackgroundColorLuma: 0.2126*backColorRs + 0.7152*backColorGs + 0.0722*backColorBs readonly property real themeTextColorLuma: 0.2126*textColorRs + 0.7152*textColorGs + 0.0722*textColorBs property bool isShown: active && !forceSolidPanel //! when forceSemiTransparentPanel is enabled because of snapped or maximized etc. windows //! then the colorizer could be enabled for low panel transparency levels (<40%) && (!forceSemiTransparentPanel || (forceSemiTransparentPanel && root.panelTransparency<40)) && !maximizedWindowTitleBarBehavesAsPanelBackground && (plasmoid.configuration.solidBackgroundForMaximized || plasmoid.configuration.backgroundOnlyOnMaximized) && !root.editMode && Latte.WindowSystem.compositingActive property real currentBackgroundLuminas: -1000 property color themeBrightColor: themeBackgroundColorLuma > themeTextColorLuma ? theme.backgroundColor : theme.textColor property color themeDarkColor: themeBackgroundColorLuma > themeTextColorLuma ? theme.textColor : theme.backgroundColor property color applyColor: { if (currentBackgroundLuminas>=0) { var textAbs = Math.abs(themeTextColorLuma - currentBackgroundLuminas); var backAbs = Math.abs(themeBackgroundColorLuma - currentBackgroundLuminas); if (textAbs > backAbs) { return Qt.rgba(theme.textColor.r, theme.textColor.g, theme.textColor.b , 1); //remove any transparency } } return Qt.rgba(theme.backgroundColor.r, theme.backgroundColor.g, theme.backgroundColor.b, 1); //remove any transparency } sourceComponent: ColorizerManager{} } ///////////////END UI elements ///////////////BEGIN TIMER elements //Timer to check if the mouse is still outside the dock in order to restore zooms to 1.0 Timer{ id:checkRestoreZoom interval: 90 onTriggered: { if (latteApplet && (latteApplet.previewContainsMouse() || latteApplet.contextMenu)) return; if (dock.contextMenuIsShown) return; if (!mouseInHoverableArea()) { setGlobalDirectRender(false); root.initializeHoveredIndexes(); root.clearZoom(); } if (root.debugModeTimers) { console.log("containment timer: checkRestoreZoom called..."); } } } //! Delayer in order to not activate directRendering when the mouse //! enters until the timer has ended. This way we make sure that the //! zoom-in animations will have ended. Timer{ id:directRenderDelayerForEnteringTimer interval: 3.2 * root.durationTime * units.shortDuration } //this is a delayer to update mask area, it is used in cases //that animations can not catch up with animations signals //e.g. the automaicIconSize case Timer{ id:delayUpdateMaskArea repeat:false; interval:300; onTriggered: { if (layoutsContainer.animationSent) { root.slotAnimationsNeedLength(-1); layoutsContainer.animationSent = false; } visibilityManager.updateMaskArea(); if (root.debugModeTimers) { console.log("containment timer: delayUpdateMaskArea called..."); } } } // This function is very costly! This timer makes sure that it can be called // only once every 1sec. Timer{ id:blockAutomaticUpdateIconSize interval: 1000 repeat: false onTriggered: root.updateAutomaticIconSize(); } //! It is used in order to slide-in the dock on startup Timer{ id: inStartupTimer interval: 1500 repeat: false onTriggered: { if (inStartup) { visibilityManager.slotMustBeShown(); } } } ///////////////END TIMER elements }