mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03:09 +00:00
components/mobileshell: Add mechanism to keep track of and stop launched apps
This commit is contained in:
parent
c2b9a06e61
commit
7385ca9dca
9 changed files with 138 additions and 17 deletions
|
|
@ -22,10 +22,6 @@ MouseArea { // use mousearea to ensure clicks don't go behind
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
property alias backgroundColor: background.color
|
property alias backgroundColor: background.color
|
||||||
Kirigami.ImageColors {
|
|
||||||
id: colorGenerator
|
|
||||||
source: icon.source
|
|
||||||
}
|
|
||||||
|
|
||||||
function open(splashIcon, title, x, y, sourceIconSize, color) {
|
function open(splashIcon, title, x, y, sourceIconSize, color) {
|
||||||
iconParent.scale = sourceIconSize/iconParent.width;
|
iconParent.scale = sourceIconSize/iconParent.width;
|
||||||
|
|
@ -84,6 +80,11 @@ MouseArea { // use mousearea to ensure clicks don't go behind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Kirigami.ImageColors {
|
||||||
|
id: colorGenerator
|
||||||
|
source: icon.source
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: backgroundParent
|
id: backgroundParent
|
||||||
width: root.width
|
width: root.width
|
||||||
|
|
|
||||||
|
|
@ -243,5 +243,12 @@ Item {
|
||||||
id: startupFeedback
|
id: startupFeedback
|
||||||
z: 999999
|
z: 999999
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// if the startup feedback closes, clear the shell's stored launching app
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (!visible) {
|
||||||
|
MobileShell.ShellUtil.clearLaunchingApp();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ PlasmaComponents.Label {
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onTapped: {
|
onTapped: {
|
||||||
MobileShell.ShellUtil.launchApp("org.kde.kclock");
|
MobileShell.ShellUtil.launchApp("org.kde.kclock.desktop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import QtQuick.Controls 2.15 as Controls
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtGraphicalEffects 1.15
|
import QtGraphicalEffects 1.15
|
||||||
|
|
||||||
import org.kde.plasma.plasmoid 2.0
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "shellutil.h"
|
#include "shellutil.h"
|
||||||
|
#include "windowutil.h"
|
||||||
|
|
||||||
#include <KConfigGroup>
|
#include <KConfigGroup>
|
||||||
#include <KFileUtils>
|
#include <KFileUtils>
|
||||||
#include <KIO/ApplicationLauncherJob>
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <KNotification>
|
#include <KNotification>
|
||||||
|
#include <KNotificationJobUiDelegate>
|
||||||
|
|
||||||
#include <QDBusPendingReply>
|
#include <QDBusPendingReply>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
@ -24,6 +25,7 @@
|
||||||
|
|
||||||
ShellUtil::ShellUtil(QObject *parent)
|
ShellUtil::ShellUtil(QObject *parent)
|
||||||
: QObject{parent}
|
: QObject{parent}
|
||||||
|
, m_launchingApp{nullptr}
|
||||||
{
|
{
|
||||||
m_localeConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig);
|
m_localeConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig);
|
||||||
m_localeConfigWatcher = KConfigWatcher::create(m_localeConfig);
|
m_localeConfigWatcher = KConfigWatcher::create(m_localeConfig);
|
||||||
|
|
@ -77,13 +79,54 @@ bool ShellUtil::isSystem24HourFormat()
|
||||||
return timeFormat == QStringLiteral(FORMAT24H);
|
return timeFormat == QStringLiteral(FORMAT24H);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShellUtil::launchApp(const QString &app)
|
void ShellUtil::launchApp(const QString &storageId)
|
||||||
{
|
{
|
||||||
const KService::Ptr appService = KService::serviceByDesktopName(app);
|
// try to activate a running window first
|
||||||
if (!appService) {
|
auto windows = WindowUtil::instance()->windowsFromStorageId(storageId);
|
||||||
qWarning() << "Could not find" << app;
|
|
||||||
|
if (!windows.empty()) {
|
||||||
|
windows[0]->requestActivate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto job = new KIO::ApplicationLauncherJob(appService, this);
|
|
||||||
job->start();
|
// now try launching the window
|
||||||
|
KService::Ptr service = KService::serviceByStorageId(storageId);
|
||||||
|
if (!service) {
|
||||||
|
qWarning() << "Could not find" << storageId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto job = new KIO::ApplicationLauncherJob(service, this);
|
||||||
|
job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
|
||||||
|
job->start();
|
||||||
|
|
||||||
|
setLaunchingApp(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShellUtil::isLaunchingApp()
|
||||||
|
{
|
||||||
|
return m_launchingApp != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::setLaunchingApp(KIO::ApplicationLauncherJob *launcherJob)
|
||||||
|
{
|
||||||
|
m_launchingApp = launcherJob;
|
||||||
|
connect(launcherJob, &KIO::ApplicationLauncherJob::result, this, [this](auto *job) {
|
||||||
|
m_launchingAppPids = m_launchingApp->pids();
|
||||||
|
});
|
||||||
|
Q_EMIT isLaunchingAppChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::cancelLaunchingApp()
|
||||||
|
{
|
||||||
|
for (auto pid : m_launchingAppPids) {
|
||||||
|
QProcess::execute("kill", {QString::number(pid)});
|
||||||
|
}
|
||||||
|
clearLaunchingApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::clearLaunchingApp()
|
||||||
|
{
|
||||||
|
m_launchingApp = nullptr;
|
||||||
|
Q_EMIT isLaunchingAppChanged();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
|
|
||||||
#include <KConfigWatcher>
|
#include <KConfigWatcher>
|
||||||
|
#include <KIO/ApplicationLauncherJob>
|
||||||
#include <KSharedConfig>
|
#include <KSharedConfig>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -22,7 +23,7 @@ class ShellUtil : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool isSystem24HourFormat READ isSystem24HourFormat NOTIFY isSystem24HourFormatChanged)
|
Q_PROPERTY(bool isSystem24HourFormat READ isSystem24HourFormat NOTIFY isSystem24HourFormatChanged)
|
||||||
Q_PROPERTY(bool launchingApp READ isLaunchingApp NOTIFY isLaunchingAppChanged)
|
Q_PROPERTY(bool isLaunchingApp READ isLaunchingApp NOTIFY isLaunchingAppChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ShellUtil(QObject *parent = nullptr);
|
ShellUtil(QObject *parent = nullptr);
|
||||||
|
|
@ -52,21 +53,44 @@ public:
|
||||||
Q_INVOKABLE void executeCommand(const QString &command);
|
Q_INVOKABLE void executeCommand(const QString &command);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch an application by name.
|
* Launch an application by name. Sets the internal "launched app" state.
|
||||||
*
|
*
|
||||||
* @param app The name of the application to launch.
|
* @param storageId The storage id of the application to launch.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void launchApp(const QString &app);
|
Q_INVOKABLE void launchApp(const QString &storageId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the system is using 24 hour format.
|
* Whether the system is using 24 hour format.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool isSystem24HourFormat();
|
Q_INVOKABLE bool isSystem24HourFormat();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an application is being launched.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool isLaunchingApp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels an application launch by running `kill pid` for every associated pid of the launching app.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void cancelLaunchingApp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the currently stored launching app.
|
||||||
|
*
|
||||||
|
* This should be called if the application window finally shows.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void clearLaunchingApp();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void isSystem24HourFormatChanged();
|
void isSystem24HourFormatChanged();
|
||||||
|
void isLaunchingAppChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setLaunchingApp(KIO::ApplicationLauncherJob *launcherJob);
|
||||||
|
|
||||||
KConfigWatcher::Ptr m_localeConfigWatcher;
|
KConfigWatcher::Ptr m_localeConfigWatcher;
|
||||||
KSharedConfig::Ptr m_localeConfig;
|
KSharedConfig::Ptr m_localeConfig;
|
||||||
|
|
||||||
|
KIO::ApplicationLauncherJob *m_launchingApp;
|
||||||
|
QVector<qint64> m_launchingAppPids;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ void WindowUtil::initWayland()
|
||||||
connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, [this](KWayland::Client::PlasmaWindow *window) {
|
connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, [this](KWayland::Client::PlasmaWindow *window) {
|
||||||
Q_EMIT windowCreated(window);
|
Q_EMIT windowCreated(window);
|
||||||
});
|
});
|
||||||
|
connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, &WindowUtil::windowCreatedSlot);
|
||||||
|
|
||||||
connect(m_windowManagement, &PlasmaWindowManagement::showingDesktopChanged, this, &WindowUtil::updateShowingDesktop);
|
connect(m_windowManagement, &PlasmaWindowManagement::showingDesktopChanged, this, &WindowUtil::updateShowingDesktop);
|
||||||
connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, m_activeWindowTimer, qOverload<>(&QTimer::start));
|
connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, m_activeWindowTimer, qOverload<>(&QTimer::start));
|
||||||
|
|
@ -222,3 +223,34 @@ void WindowUtil::forgetActiveWindow()
|
||||||
m_activeWindow.clear();
|
m_activeWindow.clear();
|
||||||
Q_EMIT hasCloseableActiveWindowChanged();
|
Q_EMIT hasCloseableActiveWindowChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<KWayland::Client::PlasmaWindow *> WindowUtil::windowsFromStorageId(const QString &storageId) const
|
||||||
|
{
|
||||||
|
if (!m_windows.contains(storageId)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return m_windows[storageId];
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowUtil::windowCreatedSlot(KWayland::Client::PlasmaWindow *window)
|
||||||
|
{
|
||||||
|
QString storageId = window->appId() + QStringLiteral(".desktop");
|
||||||
|
|
||||||
|
// ignore empty windows
|
||||||
|
if (storageId == ".desktop" || storageId == "org.kde.plasmashell.desktop") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_windows.contains(storageId)) {
|
||||||
|
m_windows[storageId] = {};
|
||||||
|
}
|
||||||
|
m_windows[storageId].push_back(window);
|
||||||
|
|
||||||
|
// listen for window close
|
||||||
|
connect(window, &KWayland::Client::PlasmaWindow::unmapped, this, [this, storageId]() {
|
||||||
|
m_windows.remove(storageId);
|
||||||
|
Q_EMIT windowChanged(storageId);
|
||||||
|
});
|
||||||
|
|
||||||
|
Q_EMIT windowChanged(storageId);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,11 @@ public:
|
||||||
*/
|
*/
|
||||||
bool hasCloseableActiveWindow() const;
|
bool hasCloseableActiveWindow() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of windows associated to a storage id.
|
||||||
|
*/
|
||||||
|
QList<KWayland::Client::PlasmaWindow *> windowsFromStorageId(const QString &storageId) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the current active window.
|
* Close the current active window.
|
||||||
*/
|
*/
|
||||||
|
|
@ -99,11 +104,13 @@ Q_SIGNALS:
|
||||||
void hasCloseableActiveWindowChanged();
|
void hasCloseableActiveWindowChanged();
|
||||||
void activeWindowChanged();
|
void activeWindowChanged();
|
||||||
void activeWindowIsShellChanged();
|
void activeWindowIsShellChanged();
|
||||||
|
void windowChanged(QString storageId); // emitted on window open or close
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void updateActiveWindowIsShell();
|
void updateActiveWindowIsShell();
|
||||||
void forgetActiveWindow();
|
void forgetActiveWindow();
|
||||||
void updateShowingDesktop(bool showing);
|
void updateShowingDesktop(bool showing);
|
||||||
|
void windowCreatedSlot(KWayland::Client::PlasmaWindow *window);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initWayland();
|
void initWayland();
|
||||||
|
|
@ -117,4 +124,6 @@ private:
|
||||||
bool m_allWindowsMinimized = true;
|
bool m_allWindowsMinimized = true;
|
||||||
bool m_allWindowsMinimizedExcludingShell = true;
|
bool m_allWindowsMinimizedExcludingShell = true;
|
||||||
bool m_activeWindowIsShell = false;
|
bool m_activeWindowIsShell = false;
|
||||||
|
|
||||||
|
QHash<QString, QList<KWayland::Client::PlasmaWindow *>> m_windows; // <storageId, window>
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ MobileShell.NavigationPanel {
|
||||||
rightAction: MobileShell.NavigationPanelAction {
|
rightAction: MobileShell.NavigationPanelAction {
|
||||||
id: closeAppAction
|
id: closeAppAction
|
||||||
|
|
||||||
enabled: Keyboards.KWinVirtualKeyboard.visible || root.taskSwitcher.visible || MobileShell.WindowUtil.hasCloseableActiveWindow
|
enabled: Keyboards.KWinVirtualKeyboard.visible || root.taskSwitcher.visible || MobileShell.WindowUtil.hasCloseableActiveWindow || MobileShell.ShellUtil.isLaunchingApp
|
||||||
iconSource: Keyboards.KWinVirtualKeyboard.visible ? "go-down-symbolic" : "mobile-close-app"
|
iconSource: Keyboards.KWinVirtualKeyboard.visible ? "go-down-symbolic" : "mobile-close-app"
|
||||||
// mobile-close-app (from plasma-frameworks) seems to have less margins than icons from breeze-icons
|
// mobile-close-app (from plasma-frameworks) seems to have less margins than icons from breeze-icons
|
||||||
iconSizeFactor: Keyboards.KWinVirtualKeyboard.visible ? 1 : 0.75
|
iconSizeFactor: Keyboards.KWinVirtualKeyboard.visible ? 1 : 0.75
|
||||||
|
|
@ -101,6 +101,12 @@ MobileShell.NavigationPanel {
|
||||||
if (root.taskSwitcher.tasksModel.activeTask !== 0) {
|
if (root.taskSwitcher.tasksModel.activeTask !== 0) {
|
||||||
root.taskSwitcher.tasksModel.requestClose(root.taskSwitcher.tasksModel.activeTask);
|
root.taskSwitcher.tasksModel.requestClose(root.taskSwitcher.tasksModel.activeTask);
|
||||||
}
|
}
|
||||||
|
MobileShell.HomeScreenControls.closeAppLaunchAnimation();
|
||||||
|
} else if (MobileShell.ShellUtil.isLaunchingApp) {
|
||||||
|
|
||||||
|
// cancel the launching of the app
|
||||||
|
MobileShell.HomeScreenControls.closeAppLaunchAnimation();
|
||||||
|
MobileShell.ShellUtil.cancelLaunchingApp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue