diff --git a/app/shortcuts/globalshortcuts.cpp b/app/shortcuts/globalshortcuts.cpp index ee4ae3b2..2bda9e2b 100644 --- a/app/shortcuts/globalshortcuts.cpp +++ b/app/shortcuts/globalshortcuts.cpp @@ -1,538 +1,561 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "globalshortcuts.h" // local #include "modifiertracker.h" #include "shortcutstracker.h" #include "../lattecorona.h" #include "../layout/centrallayout.h" #include "../layouts/manager.h" #include "../layouts/synchronizer.h" #include "../settings/universalsettings.h" #include "../view/containmentinterface.h" #include "../view/view.h" // C++ #include // Qt #include #include #include #include #include // KDE #include #include #include #include // Plasma #include #include namespace Latte { const int APPLETEXECUTIONDELAY = 400; GlobalShortcuts::GlobalShortcuts(QObject *parent) : QObject(parent) { m_corona = qobject_cast(parent); m_modifierTracker = new ShortcutsPart::ModifierTracker(this); m_shortcutsTracker = new ShortcutsPart::ShortcutsTracker(this); if (m_corona) { init(); } m_hideViewsTimer.setSingleShot(true); if (QX11Info::isPlatformX11()) { //in X11 the timer is a poller that checks to see if the modifier keys //from user global shortcut have been released m_hideViewsTimer.setInterval(300); } else { //on wayland in acting just as simple timer that hides the view afterwards m_hideViewsTimer.setInterval(2500); } connect(&m_hideViewsTimer, &QTimer::timeout, this, &GlobalShortcuts::hideViewsTimerSlot); } GlobalShortcuts::~GlobalShortcuts() { if (m_modifierTracker) { m_modifierTracker->deleteLater(); } if (m_shortcutsTracker) { m_shortcutsTracker->deleteLater(); } } void GlobalShortcuts::init() { KActionCollection *generalActions = new KActionCollection(m_corona); //show-hide the main view in the primary screen QAction *showAction = generalActions->addAction(QStringLiteral("show latte view")); showAction->setText(i18n("Show Latte Dock/Panel")); showAction->setShortcut(QKeySequence(Qt::META + '`')); KGlobalAccel::setGlobalShortcut(showAction, QKeySequence(Qt::META + '`')); connect(showAction, &QAction::triggered, this, [this]() { showViews(); }); //show-cycle between Latte settings windows QAction *settingsAction = generalActions->addAction(QStringLiteral("show view settings")); settingsAction->setText(i18n("Cycle Through Dock/Panel Settings Windows")); KGlobalAccel::setGlobalShortcut(settingsAction, QKeySequence(Qt::META + Qt::Key_A)); connect(settingsAction, &QAction::triggered, this, [this] { m_modifierTracker->cancelMetaPressed(); showSettings(); }); //show the layouts editor QAction *layoutsAction = generalActions->addAction(QStringLiteral("show latte global settings")); layoutsAction->setText(i18n("Show Latte Global 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_modifierTracker->cancelMetaPressed(); m_corona->layoutsManager()->showLatteSettingsDialog(Types::LayoutPage); }); 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..."; m_modifierTracker->cancelMetaPressed(); 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] { m_modifierTracker->cancelMetaPressed(); 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..."; m_modifierTracker->cancelMetaPressed(); 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] { m_modifierTracker->cancelMetaPressed(); activateEntry(i, static_cast(Qt::CTRL)); }); } m_singleMetaAction = new QAction(this); m_singleMetaAction->setShortcut(QKeySequence(Qt::META)); connect(m_corona->universalSettings(), &UniversalSettings::metaPressAndHoldEnabledChanged , this, [&]() { if (!m_corona->universalSettings()->metaPressAndHoldEnabled()) { m_modifierTracker->blockModifierTracking(Qt::Key_Super_L); m_modifierTracker->blockModifierTracking(Qt::Key_Super_R); } else { m_modifierTracker->unblockModifierTracking(Qt::Key_Super_L); m_modifierTracker->unblockModifierTracking(Qt::Key_Super_R); } }); //display shortcut badges while holding Meta connect(m_modifierTracker, &ShortcutsPart::ModifierTracker::metaModifierPressed, this, [&]() { m_metaShowedViews = true; showViews(); }); } ShortcutsPart::ShortcutsTracker *GlobalShortcuts::shortcutsTracker() const { return m_shortcutsTracker; } +Latte::View *GlobalShortcuts::highestApplicationLauncherView(const QList &views) const +{ + if (views.isEmpty()) { + return nullptr; + } + + Latte::View *highestPriorityView{nullptr}; + + for (const auto view : views) { + if (view->interface()->applicationLauncherHasGlobalShortcut()) { + highestPriorityView = view; + break; + } + } + + if (!highestPriorityView) { + for (const auto view : views) { + if (view->interface()->containsApplicationLauncher()) { + highestPriorityView = view; + break; + } + } + } + + return highestPriorityView; +} + //! Activate launcher menu through dbus interface void GlobalShortcuts::activateLauncherMenu() { if (m_metaShowedViews) { return; } QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } - for (const auto view : sortedViews) { - if (view->interface()->containsApplicationLauncher()) { - if (view->visibility()->isHidden() && view->interface()->applicationLauncherInPopup()) { - if (!m_hideViews.contains(view)) { - m_hideViews.append(view); - } + Latte::View *highestPriorityView = highestApplicationLauncherView(sortedViews); - m_lastInvokedAction = m_singleMetaAction; + if (highestPriorityView) { + if (highestPriorityView->visibility()->isHidden() && highestPriorityView->interface()->applicationLauncherInPopup()) { + if (!m_hideViews.contains(highestPriorityView)) { + m_hideViews.append(highestPriorityView); + } - view->visibility()->setBlockHiding(true); + m_lastInvokedAction = m_singleMetaAction; - //! delay the execution in order to show first the view - QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view]() { - view->toggleAppletExpanded(view->interface()->applicationLauncherId()); - }); + highestPriorityView->visibility()->setBlockHiding(true); - m_hideViewsTimer.start(); - } else { - view->toggleAppletExpanded(view->interface()->applicationLauncherId()); - } + //! delay the execution in order to show first the view + QTimer::singleShot(APPLETEXECUTIONDELAY, [this, highestPriorityView]() { + highestPriorityView->toggleAppletExpanded(highestPriorityView->interface()->applicationLauncherId()); + }); - return; + m_hideViewsTimer.start(); + } else { + highestPriorityView->toggleAppletExpanded(highestPriorityView->interface()->applicationLauncherId()); } } } bool GlobalShortcuts::activatePlasmaTaskManager(const Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution) { bool activation{modifier == static_cast(Qt::META)}; bool newInstance{!activation}; if (view->visibility()->isHidden()) { //! delay the execution in order to show first the view QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view, index, activation]() { if (activation) { view->interface()->activatePlasmaTask(index); } else { view->interface()->newInstanceForPlasmaTask(index); } }); *delayedExecution = true; return true; } else { *delayedExecution = false; return (activation ? view->interface()->activatePlasmaTask(index) : view->interface()->newInstanceForPlasmaTask(index)); } } bool GlobalShortcuts::activateLatteEntry(Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution) { bool activation{modifier == static_cast(Qt::META)}; bool newInstance{!activation}; int appletId = view->interface()->appletIdForIndex(index); bool hasPopUp {(appletId>-1 && view->appletIsExpandable(appletId))}; if (view->visibility()->isHidden() && hasPopUp) { //! delay the execution in order to show first the view QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view, index, activation]() { if (activation) { view->interface()->activateEntry(index); } else { view->interface()->newInstanceForEntry(index); } }); *delayedExecution = true; return true; } else { *delayedExecution = false; return (activation ? view->interface()->activateEntry(index) : view->interface()->newInstanceForEntry(index)); } } //! Activate task manager entry void GlobalShortcuts::activateEntry(int index, Qt::Key modifier) { m_lastInvokedAction = dynamic_cast(sender()); QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } for (const auto view : sortedViews) { if (view->layout()->preferredForShortcutsTouched() && !view->isPreferredForShortcuts()) { continue; } bool delayed{false}; if ((!view->latteTasksArePresent() && view->tasksPresent() && activatePlasmaTaskManager(view, index, modifier, &delayed)) || activateLatteEntry(view, index, modifier, &delayed)) { if (!m_hideViews.contains(view)) { m_hideViews.append(view); } if (delayed) { view->visibility()->setBlockHiding(true); m_hideViewsTimer.start(); } return; } } } //! update badge for specific view item void GlobalShortcuts::updateViewItemBadge(QString identifier, QString value) { CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); QList views; if (currentLayout) { views = currentLayout->latteViews(); } // update badges in all Latte Tasks plasmoids for (const auto &view : views) { view->interface()->updateBadgeForLatteTask(identifier, value); } } void GlobalShortcuts::showViews() { m_lastInvokedAction = dynamic_cast(sender()); if (!m_lastInvokedAction) { // when holding Meta m_lastInvokedAction = m_singleMetaAction; } QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } Latte::View *viewWithTasks{nullptr}; Latte::View *viewWithMeta{nullptr}; for(const auto view : sortedViews) { if (!viewWithTasks && view->interface()->isCapableToShowShortcutBadges() && (!view->layout()->preferredForShortcutsTouched() || view->isPreferredForShortcuts())) { viewWithTasks = view; break; } } - //! show Meta if it is not already shown for Tasks Latte View - if (!viewWithTasks || !viewWithTasks->interface()->containsApplicationLauncher()) { - for(const auto view : sortedViews) { - if (!viewWithMeta && m_corona->universalSettings()->kwin_metaForwardedToLatte() && view->interface()->containsApplicationLauncher()) { - viewWithMeta = view; - break; - } - } + if (m_corona->universalSettings()->kwin_metaForwardedToLatte()) { + viewWithMeta = highestApplicationLauncherView(sortedViews); } bool viewFound{false}; if (!m_hideViewsTimer.isActive()) { m_hideViews.clear(); } //! show view that contains tasks plasmoid - if (viewWithTasks && viewWithTasks->interface()->showShortcutBadges(true, true)) { + if (viewWithTasks) { viewFound = true; + bool showMeta = (viewWithMeta && (viewWithMeta == viewWithTasks)); + + viewWithTasks->interface()->showShortcutBadges(true, showMeta); + if (!m_hideViewsTimer.isActive()) { m_hideViews.append(viewWithTasks); viewWithTasks->visibility()->setBlockHiding(true); } } //! show view that contains only meta if (viewWithMeta && viewWithMeta != viewWithTasks && viewWithMeta->interface()->showOnlyMeta()) { viewFound = true; if (!m_hideViewsTimer.isActive()) { m_hideViews.append(viewWithMeta); viewWithMeta->visibility()->setBlockHiding(true); } } //! show all the rest views that contain plasma shortcuts QList viewsWithShortcuts; if (currentLayout) { viewsWithShortcuts = currentLayout->viewsWithPlasmaShortcuts(); } if (viewsWithShortcuts.count() > 0) { viewFound = true; if (!m_hideViewsTimer.isActive()) { for(const auto view : viewsWithShortcuts) { if (view != viewWithTasks && view != viewWithMeta) { if (view->interface()->showShortcutBadges(false, false)) { m_hideViews.append(view); view->visibility()->setBlockHiding(true); } } } } } if (viewFound) { if (!m_hideViewsTimer.isActive()) { m_hideViewsTimer.start(); } else { m_hideViewsTimer.stop(); hideViewsTimerSlot(); } } } bool GlobalShortcuts::viewsToHideAreValid() { for(const auto view : m_hideViews) { if (!m_corona->layoutsManager()->synchronizer()->latteViewExists(view)) { return false; } } return true; } void GlobalShortcuts::showSettings() { QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } //! find which is the next view to show its settings if (sortedViews.count() > 0) { int openSettings = -1; //! check if there is a view with opened settings window for (int i = 0; i < sortedViews.size(); ++i) { if (sortedViews[i] == currentLayout->lastConfigViewFor()) { openSettings = i; break; } } if (openSettings >= 0 && sortedViews.count() > 1) { openSettings = currentLayout->configViewIsShown() ? openSettings + 1 : openSettings; if (openSettings >= sortedViews.size()) { openSettings = 0; } sortedViews[openSettings]->showSettingsWindow(); } else { sortedViews[0]->showSettingsWindow(); } } } void GlobalShortcuts::hideViewsTimerSlot() { if (!m_lastInvokedAction || m_hideViews.count() == 0) { return; } auto initParameters = [this]() { m_lastInvokedAction = Q_NULLPTR; if (viewsToHideAreValid()) { for(const auto latteView : m_hideViews) { latteView->visibility()->setBlockHiding(false); latteView->interface()->hideShortcutBadges(); - } + } } m_hideViews.clear(); m_metaShowedViews = false; }; // qDebug() << "MEMORY ::: " << m_hideViews.count() << " _ " << m_viewItemsCalled.count() << " _ " << m_showShortcutBadgesMethods.count(); if (QX11Info::isPlatformX11()) { if (!m_modifierTracker->sequenceModifierPressed(m_lastInvokedAction->shortcut())) { initParameters(); return; } else { m_hideViewsTimer.start(); } } else { // TODO: This is needs to be fixed in wayland initParameters(); } } } #include "moc_globalshortcuts.cpp" diff --git a/app/shortcuts/globalshortcuts.h b/app/shortcuts/globalshortcuts.h index a9c7abab..8376799b 100644 --- a/app/shortcuts/globalshortcuts.h +++ b/app/shortcuts/globalshortcuts.h @@ -1,103 +1,106 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GLOBALSHORTCUTS_H #define GLOBALSHORTCUTS_H // local #include "../liblatte2/types.h" // Qt #include #include #include // KDE #include namespace Plasma { class Containment; } namespace Latte { class Corona; class View; namespace ShortcutsPart{ class ModifierTracker; class ShortcutsTracker; } } namespace Latte { class GlobalShortcuts : public QObject { Q_OBJECT public: GlobalShortcuts(QObject *parent = nullptr); ~GlobalShortcuts() override; void activateLauncherMenu(); void updateViewItemBadge(QString identifier, QString value); ShortcutsPart::ShortcutsTracker *shortcutsTracker() const; signals: void modifiersChanged(); private slots: void hideViewsTimerSlot(); private: void init(); void initModifiers(); void activateEntry(int index, Qt::Key modifier); void showViews(); void showSettings(); bool activateLatteEntry(Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution); bool activatePlasmaTaskManager(const Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution); bool viewAtLowerEdgePriority(Latte::View *test, Latte::View *base); bool viewAtLowerScreenPriority(Latte::View *test, Latte::View *base); bool viewsToHideAreValid(); + //! highest priority application launcher view + Latte::View *highestApplicationLauncherView(const QList &views) const; + QList sortedViewsList(QHash *views); private: bool m_metaShowedViews{false}; //! last action that was trigerred from the user QAction *m_lastInvokedAction; //! it is used for code compatibility reasons in order to replicate a single Meta action QAction *m_singleMetaAction; //! delayer for hiding the shown latte views QTimer m_hideViewsTimer; QList m_hideViews; QPointer m_modifierTracker; QPointer m_shortcutsTracker; QPointer m_corona; }; } #endif // GLOBALSHORTCUTS_H diff --git a/app/view/containmentinterface.cpp b/app/view/containmentinterface.cpp index 6af5626d..8480387e 100644 --- a/app/view/containmentinterface.cpp +++ b/app/view/containmentinterface.cpp @@ -1,346 +1,373 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "containmentinterface.h" // local #include "view.h" #include "../lattecorona.h" #include "../settings/universalsettings.h" // Qt #include // Plasma #include #include // KDE #include #include namespace Latte { namespace ViewPart { ContainmentInterface::ContainmentInterface(Latte::View *parent) : QObject(parent), m_view(parent) { m_corona = qobject_cast(m_view->corona()); } ContainmentInterface::~ContainmentInterface() { } void ContainmentInterface::identifyMainItem() { if (m_mainItem) { return; } if (QQuickItem *graphicItem = m_view->containment()->property("_plasma_graphicObject").value()) { const auto &childItems = graphicItem->childItems(); for (QQuickItem *item : childItems) { if (item->objectName() == "containmentViewLayout" ) { m_mainItem = item; identifyMethods(); return; } } } } void ContainmentInterface::identifyMethods() { int aeIndex = m_mainItem->metaObject()->indexOfMethod("activateEntryAtIndex(QVariant)"); int niIndex = m_mainItem->metaObject()->indexOfMethod("newInstanceForEntryAtIndex(QVariant)"); int sbIndex = m_mainItem->metaObject()->indexOfMethod("setShowAppletShortcutBadges(QVariant,QVariant,QVariant,QVariant)"); int afiIndex = m_mainItem->metaObject()->indexOfMethod("appletIdForIndex(QVariant)"); m_activateEntryMethod = m_mainItem->metaObject()->method(aeIndex); m_appletIdForIndexMethod = m_mainItem->metaObject()->method(afiIndex); m_newInstanceMethod = m_mainItem->metaObject()->method(niIndex); m_showShortcutsMethod = m_mainItem->metaObject()->method(sbIndex); } +bool ContainmentInterface::applicationLauncherHasGlobalShortcut() const +{ + if (!containsApplicationLauncher()) { + return false; + } + + int launcherAppletId = applicationLauncherId(); + + const auto applets = m_view->containment()->applets(); + + for (auto applet : applets) { + if (applet->id() == launcherAppletId) { + return !applet->globalShortcut().isEmpty(); + } + } + + return false; +} + bool ContainmentInterface::applicationLauncherInPopup() const { if (!containsApplicationLauncher()) { return false; } int launcherAppletId = applicationLauncherId(); QString launcherPluginId; const auto applets = m_view->containment()->applets(); for (auto applet : applets) { if (applet->id() == launcherAppletId) { launcherPluginId = applet->kPackage().metadata().pluginId(); } } return launcherPluginId != "org.kde.plasma.kickerdash"; } bool ContainmentInterface::containsApplicationLauncher() const { - return applicationLauncherId() == -1 ? false : true; + return (applicationLauncherId() >= 0); } -bool ContainmentInterface::isCapableToShowShortcutBadges() const +bool ContainmentInterface::isCapableToShowShortcutBadges() { + identifyMainItem(); + if (!m_view->latteTasksArePresent() && m_view->tasksPresent()) { return false; } return m_showShortcutsMethod.isValid(); } int ContainmentInterface::applicationLauncherId() const { const auto applets = m_view->containment()->applets(); + auto launcherId{-1}; + 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(); + if (!applet->globalShortcut().isEmpty()) { + return applet->id(); + } else if (launcherId == -1) { + launcherId = applet->id(); + } } } - return -1; + return launcherId; } bool ContainmentInterface::updateBadgeForLatteTask(const QString identifier, const QString value) { if (!m_view->latteTasksArePresent()) { return false; } const auto &applets = m_view->containment()->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; } bool ContainmentInterface::activatePlasmaTask(const int index) { bool containsPlasmaTaskManager{m_view->tasksPresent() && !m_view->latteTasksArePresent()}; if (!containsPlasmaTaskManager) { return false; } const auto &applets = m_view->containment()->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()) { int methodIndex{metaObject->indexOfMethod("activateTaskAtIndex(QVariant)")}; if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, index - 1))) { showShortcutBadges(false, true); return true; } } } } } } return false; } bool ContainmentInterface::newInstanceForPlasmaTask(const int index) { bool containsPlasmaTaskManager{m_view->tasksPresent() && !m_view->latteTasksArePresent()}; if (!containsPlasmaTaskManager) { return false; } const auto &applets = m_view->containment()->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()) { int methodIndex{metaObject->indexOfMethod("ewInstanceForTaskAtIndex(QVariant)")}; if (methodIndex == -1) { continue; } QMetaMethod method = metaObject->method(methodIndex); if (method.invoke(item, Q_ARG(QVariant, index - 1))) { showShortcutBadges(false, true); return true; } } } } } } return false; } bool ContainmentInterface::activateEntry(const int index) { identifyMainItem(); if (!m_activateEntryMethod.isValid()) { return false; } return m_activateEntryMethod.invoke(m_mainItem, Q_ARG(QVariant, index)); } bool ContainmentInterface::newInstanceForEntry(const int index) { identifyMainItem(); if (!m_newInstanceMethod.isValid()) { return false; } return m_newInstanceMethod.invoke(m_mainItem, Q_ARG(QVariant, index)); } bool ContainmentInterface::hideShortcutBadges() { identifyMainItem(); if (!m_showShortcutsMethod.isValid()) { return false; } return m_showShortcutsMethod.invoke(m_mainItem, Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, -1)); } bool ContainmentInterface::showOnlyMeta() { if (!m_corona->universalSettings()->kwin_metaForwardedToLatte()) { return false; } return showShortcutBadges(false, true); } bool ContainmentInterface::showShortcutBadges(const bool showLatteShortcuts, const bool showMeta) { identifyMainItem(); if (!m_showShortcutsMethod.isValid()) { return false; } int appLauncherId = m_corona->universalSettings()->kwin_metaForwardedToLatte() && showMeta ? applicationLauncherId() : -1; return m_showShortcutsMethod.invoke(m_mainItem, Q_ARG(QVariant, showLatteShortcuts), Q_ARG(QVariant, true), Q_ARG(QVariant, showMeta), Q_ARG(QVariant, appLauncherId)); } int ContainmentInterface::appletIdForIndex(const int index) { identifyMainItem(); if (!m_appletIdForIndexMethod.isValid()) { return false; } QVariant appletId{-1}; m_appletIdForIndexMethod.invoke(m_mainItem, Q_RETURN_ARG(QVariant, appletId), Q_ARG(QVariant, index)); return appletId.toInt(); } } } diff --git a/app/view/containmentinterface.h b/app/view/containmentinterface.h index 06039c52..f53625e2 100644 --- a/app/view/containmentinterface.h +++ b/app/view/containmentinterface.h @@ -1,83 +1,84 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VIEWCONTAINMENTINTERFACE_H #define VIEWCONTAINMENTINTERFACE_H // Qt #include #include #include #include namespace Latte { class Corona; class View; } namespace Latte { namespace ViewPart { class ContainmentInterface: public QObject { Q_OBJECT public: ContainmentInterface(Latte::View *parent); virtual ~ContainmentInterface(); bool applicationLauncherInPopup() const; + bool applicationLauncherHasGlobalShortcut() const; bool containsApplicationLauncher() const; - bool isCapableToShowShortcutBadges() const; + bool isCapableToShowShortcutBadges(); bool activateEntry(const int index); bool newInstanceForEntry(const int index); bool activatePlasmaTask(const int index); bool newInstanceForPlasmaTask(const int index); bool hideShortcutBadges(); bool showOnlyMeta(); bool showShortcutBadges(const bool showLatteShortcuts, const bool showMeta); //! this is updated from external apps e.g. a thunderbird plugin bool updateBadgeForLatteTask(const QString identifier, const QString value); int applicationLauncherId() const; int appletIdForIndex(const int index); private slots: void identifyMainItem(); void identifyMethods(); private: QMetaMethod m_activateEntryMethod; QMetaMethod m_appletIdForIndexMethod; QMetaMethod m_newInstanceMethod; QMetaMethod m_showShortcutsMethod; QPointer m_corona; QPointer m_view; QPointer m_mainItem; }; } } #endif diff --git a/containment/package/contents/ui/applet/ShortcutBadge.qml b/containment/package/contents/ui/applet/ShortcutBadge.qml index 236ae919..7d969f03 100644 --- a/containment/package/contents/ui/applet/ShortcutBadge.qml +++ b/containment/package/contents/ui/applet/ShortcutBadge.qml @@ -1,121 +1,123 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.1 import QtGraphicalEffects 1.0 import org.kde.latte 0.2 as Latte import org.kde.latte.components 1.0 as LatteComponents Loader{ id: appletNumberLoader active: appletItem.canShowAppletNumberBadge && (root.showLatteShortcutBadges || root.showAppletShortcutBadges || root.showMetaBadge && applet.id===applicationLauncherId) asynchronous: true visible: badgeString!=="" property int fixedIndex: -1 property string badgeString: "" onActiveChanged: { if (active && root.showLatteShortcutBadges && root.unifiedGlobalShortcuts) { fixedIndex = parabolicManager.pseudoAppletIndex(index); } else { fixedIndex = -1; } } Component.onCompleted: { if (active && root.showLatteShortcutBadges && root.unifiedGlobalShortcuts) { fixedIndex = parabolicManager.pseudoAppletIndex(index); } else { fixedIndex = -1; } } Binding{ target: appletNumberLoader property:"badgeString" value: { //! don't change value on hiding/releasing if (!root.showMetaBadge && !root.showAppletShortcutBadges) { return; } + if (root.showMetaBadge && applet && applet.id === applicationLauncherId) { + return '\u2318'; + } + if (root.showAppletShortcutBadges) { var plasmaShortcut = applet ? shortcutsEngine.appletShortcutBadge(applet.id) : ""; if (plasmaShortcut !== "") { return plasmaShortcut; } } - if (root.showMetaBadge && applet && applet.id === applicationLauncherId) { - return '\u2318'; - } else if (appletNumberLoader.fixedIndex>=1 && appletNumberLoader.fixedIndex<20) { + if (appletNumberLoader.fixedIndex>=1 && appletNumberLoader.fixedIndex<20) { return root.badgesForActivate[appletNumberLoader.fixedIndex-1]; } else { return ""; } } } sourceComponent: Item{ Loader{ anchors.fill: appletNumber active: root.enableShadows && graphicsSystem.isAccelerated sourceComponent: DropShadow{ color: root.appShadowColor fast: true samples: 2 * radius source: appletNumber radius: root.appShadowSize/2 verticalOffset: 2 } } LatteComponents.BadgeText { id: appletNumber // when iconSize < 48, height is always = 24, height / iconSize > 50% // we prefer center aligned badges to top-left aligned ones property bool centerInParent: root.iconSize < 48 anchors.left: centerInParent? undefined : parent.left anchors.top: centerInParent? undefined : parent.top anchors.centerIn: centerInParent? parent : undefined minimumWidth: 0.4 * (wrapper.zoomScale * root.iconSize) height: Math.max(24, 0.4 * (wrapper.zoomScale * root.iconSize)) borderColor: colorizerManager.originalLightTextColor proportion: 0 radiusPerCentage: 100 showNumber: false showText: true textValue: appletNumberLoader.badgeString style3d: root.badges3DStyle } } }