Kde/patches: remove patches that are in plasma 6.5
This commit is contained in:
parent
2c49a7318b
commit
e15a9c0c01
31 changed files with 0 additions and 7021 deletions
|
|
@ -101,7 +101,6 @@
|
||||||
name = "patched-nixpkgs-unstable";
|
name = "patched-nixpkgs-unstable";
|
||||||
src = nixpkgs-unstable-raw;
|
src = nixpkgs-unstable-raw;
|
||||||
patches = [
|
patches = [
|
||||||
./nixpkgs-patches/plasma-workspace-patch-trimming.patch
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
nixpkgs-patched = nixpkgs-raw.legacyPackages.x86_64-linux.applyPatches {
|
nixpkgs-patched = nixpkgs-raw.legacyPackages.x86_64-linux.applyPatches {
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
diff --git a/pkgs/kde/plasma/plasma-workspace/dependency-paths.patch b/pkgs/kde/plasma/plasma-workspace/dependency-paths.patch
|
|
||||||
index b4d46cd869bb..ca5bfd2c4889 100644
|
|
||||||
--- a/pkgs/kde/plasma/plasma-workspace/dependency-paths.patch
|
|
||||||
+++ b/pkgs/kde/plasma/plasma-workspace/dependency-paths.patch
|
|
||||||
@@ -1,13 +1,14 @@
|
|
||||||
diff --git a/applets/devicenotifier/plugin/deviceerrormonitor_p.cpp b/applets/devicenotifier/plugin/deviceerrormonitor_p.cpp
|
|
||||||
-index ba214a555d..421d940738 100644
|
|
||||||
+index 183f207946..0f64b068ab 100644
|
|
||||||
--- a/applets/devicenotifier/plugin/deviceerrormonitor_p.cpp
|
|
||||||
+++ b/applets/devicenotifier/plugin/deviceerrormonitor_p.cpp
|
|
||||||
-@@ -155,7 +155,7 @@ void DeviceErrorMonitor::queryBlockingApps(const QString &devicePath)
|
|
||||||
+@@ -168,7 +168,8 @@ void DeviceErrorMonitor::queryBlockingApps(const QString &devicePath)
|
|
||||||
Q_EMIT blockingAppsReady(blockApps);
|
|
||||||
p->deleteLater();
|
|
||||||
});
|
|
||||||
- p->start(QStringLiteral("lsof"), {QStringLiteral("-t"), devicePath});
|
|
||||||
+ p->start(QStringLiteral("@lsof@"), {QStringLiteral("-t"), devicePath});
|
|
||||||
++ // @dbusSend@
|
|
||||||
// p.start(QStringLiteral("fuser"), {QStringLiteral("-m"), devicePath});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ index e4d1ad4311..d45bdfad98 100644
|
|
||||||
|
|
||||||
void CFcQuery::procExited()
|
|
||||||
diff --git a/kcms/krdb/krdb.cpp b/kcms/krdb/krdb.cpp
|
|
||||||
-index f3c9956921..09c818739d 100644
|
|
||||||
+index 53f77d0a18..680e81b6e4 100644
|
|
||||||
--- a/kcms/krdb/krdb.cpp
|
|
||||||
+++ b/kcms/krdb/krdb.cpp
|
|
||||||
@@ -425,7 +425,7 @@ void runRdb(unsigned int flags)
|
|
||||||
@@ -107,7 +108,7 @@ index 7218628ce9..9126475ea4 100644
|
|
||||||
+ExecStart=@qdbus@ org.kde.kcminit /kcminit org.kde.KCMInit.runPhase1
|
|
||||||
Slice=session.slice
|
|
||||||
diff --git a/startkde/startplasma.cpp b/startkde/startplasma.cpp
|
|
||||||
-index 4d31c6f408..17418b1ff7 100644
|
|
||||||
+index 02c3f260fb..795244064d 100644
|
|
||||||
--- a/startkde/startplasma.cpp
|
|
||||||
+++ b/startkde/startplasma.cpp
|
|
||||||
@@ -57,7 +57,7 @@ void sigtermHandler(int signalNumber)
|
|
||||||
@@ -119,7 +120,7 @@ index 4d31c6f408..17418b1ff7 100644
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList allServices(const QLatin1String &prefix)
|
|
||||||
-@@ -512,7 +512,7 @@ QProcess *setupKSplash()
|
|
||||||
+@@ -508,7 +508,7 @@ QProcess *setupKSplash()
|
|
||||||
if (ksplashCfg.readEntry("Engine", QStringLiteral("KSplashQML")) == QLatin1String("KSplashQML")) {
|
|
||||||
p = new QProcess;
|
|
||||||
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
|
||||||
@@ -128,14 +129,4 @@ index 4d31c6f408..17418b1ff7 100644
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
-diff --git a/startkde/systemd/plasma-ksplash-ready.service.in b/startkde/systemd/plasma-ksplash-ready.service.in
|
|
||||||
-index 1e903130a9..0861c3d136 100644
|
|
||||||
---- a/startkde/systemd/plasma-ksplash-ready.service.in
|
|
||||||
-+++ b/startkde/systemd/plasma-ksplash-ready.service.in
|
|
||||||
-@@ -6,5 +6,5 @@ PartOf=graphical-session.target
|
|
||||||
-
|
|
||||||
- [Service]
|
|
||||||
- Type=oneshot
|
|
||||||
--ExecStart=dbus-send --session --reply-timeout=1 --type=method_call --dest=org.kde.KSplash /KSplash org.kde.KSplash.setStage string:ready
|
|
||||||
-+ExecStart=@dbusSend@ --session --reply-timeout=1 --type=method_call --dest=org.kde.KSplash /KSplash org.kde.KSplash.setStage string:ready
|
|
||||||
- Slice=session.slice
|
|
||||||
+
|
|
||||||
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
Plasma 6.5.0:
|
|
||||||
Pr 218 https://invent.kde.org/plasma/bluedevil/-/merge_requests/218
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
From ddbf7452c6f2fe8849de723175710dbf39242422 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Fri, 13 Jun 2025 11:55:47 -0600
|
|
||||||
Subject: [PATCH] applet: use standard section headers
|
|
||||||
|
|
||||||
We have them; might as well use them. This lets us get rid of a bunch of
|
|
||||||
complex and fragile code that's responsible for the existing separator.
|
|
||||||
---
|
|
||||||
.../contents/ui/FullRepresentation.qml | 27 +++----------------
|
|
||||||
1 file changed, 3 insertions(+), 24 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/applet/package/contents/ui/FullRepresentation.qml b/src/applet/package/contents/ui/FullRepresentation.qml
|
|
||||||
index df989267d..27295de21 100644
|
|
||||||
--- a/src/applet/package/contents/ui/FullRepresentation.qml
|
|
||||||
+++ b/src/applet/package/contents/ui/FullRepresentation.qml
|
|
||||||
@@ -111,31 +111,10 @@ PlasmaExtras.Representation {
|
|
||||||
bottomMargin: Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
section.property: "Section"
|
|
||||||
- // We want to hide the section delegate for the "Connected"
|
|
||||||
- // group because it's unnecessary; all we want to do here is
|
|
||||||
- // separate the connected devices from the available ones
|
|
||||||
- section.delegate: Loader {
|
|
||||||
+ section.delegate: PlasmaExtras.ListSectionHeader {
|
|
||||||
required property string section
|
|
||||||
-
|
|
||||||
- active: section !== "Connected" && BluezQt.Manager.connectedDevices.length > 0
|
|
||||||
-
|
|
||||||
- width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
|
|
||||||
- // Need to manually set the height or else the loader takes up
|
|
||||||
- // space after the first time it unloads a previously-loaded item
|
|
||||||
- height: active ? Kirigami.Units.gridUnit : 0
|
|
||||||
-
|
|
||||||
- // give us 2 frames to try and figure out a layout, this reduces jumpyness quite a bit but doesn't
|
|
||||||
- // entirely eliminate it https://bugs.kde.org/show_bug.cgi?id=438610
|
|
||||||
- Behavior on height { PropertyAnimation { duration: 32 } }
|
|
||||||
-
|
|
||||||
- sourceComponent: Item {
|
|
||||||
- KSvg.SvgItem {
|
|
||||||
- width: parent.width - Kirigami.Units.gridUnit * 2
|
|
||||||
- anchors.centerIn: parent
|
|
||||||
- imagePath: "widgets/line"
|
|
||||||
- elementId: "horizontal-line"
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
+ width: listView.width - listView.leftMargin - listView.rightMargin
|
|
||||||
+ text: section
|
|
||||||
}
|
|
||||||
highlight: PlasmaExtras.Highlight {}
|
|
||||||
highlightMoveDuration: Kirigami.Units.shortDuration
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
Plasma 6.5.0:
|
|
||||||
Pr 545 https://invent.kde.org/plasma/breeze/-/merge_requests/545
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
From 38567c2f2d2d0f8524e42224dff53a929cbf086a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kai Uwe Broulik <kde@privat.broulik.de>
|
|
||||||
Date: Sat, 24 May 2025 09:33:44 +0200
|
|
||||||
Subject: [PATCH 1/3] kstyle/animations: Use QObject as base type rather than
|
|
||||||
QWidget
|
|
||||||
|
|
||||||
This prepares for the animation engine being used with Qt Quick Controls.
|
|
||||||
|
|
||||||
In most cases the type is cast to the relevant widget type for checking
|
|
||||||
anyway, so might as well use the cast object further down but not require
|
|
||||||
a QWidget at the beginning.
|
|
||||||
---
|
|
||||||
kstyle/animations/breezeanimations.cpp | 48 +++++++++++++-------------
|
|
||||||
kstyle/animations/breezeanimations.h | 4 +--
|
|
||||||
2 files changed, 26 insertions(+), 26 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/kstyle/animations/breezeanimations.cpp b/kstyle/animations/breezeanimations.cpp
|
|
||||||
index 12e5107c5..ceab53802 100644
|
|
||||||
--- a/kstyle/animations/breezeanimations.cpp
|
|
||||||
+++ b/kstyle/animations/breezeanimations.cpp
|
|
||||||
@@ -82,7 +82,7 @@ void Animations::setupEngines()
|
|
||||||
}
|
|
||||||
|
|
||||||
//____________________________________________________________
|
|
||||||
-void Animations::registerWidget(QWidget *widget) const
|
|
||||||
+void Animations::registerWidget(QObject *widget) const
|
|
||||||
{
|
|
||||||
if (!widget) {
|
|
||||||
return;
|
|
||||||
@@ -101,37 +101,37 @@ void Animations::registerWidget(QWidget *widget) const
|
|
||||||
// for optimization, one should put with most used widgets here first
|
|
||||||
|
|
||||||
// buttons
|
|
||||||
- if (qobject_cast<QToolButton *>(widget)) {
|
|
||||||
- _toolButtonEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
- _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
+ if (auto toolButton = qobject_cast<QToolButton *>(widget)) {
|
|
||||||
+ _toolButtonEngine->registerWidget(toolButton, AnimationHover | AnimationFocus);
|
|
||||||
+ _widgetStateEngine->registerWidget(toolButton, AnimationHover | AnimationFocus);
|
|
||||||
|
|
||||||
} else if (qobject_cast<QCheckBox *>(widget) || qobject_cast<QRadioButton *>(widget)) {
|
|
||||||
_widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus | AnimationPressed);
|
|
||||||
|
|
||||||
- } else if (qobject_cast<QAbstractButton *>(widget)) {
|
|
||||||
+ } else if (auto button = qobject_cast<QAbstractButton *>(widget)) {
|
|
||||||
// register to toolbox engine if needed
|
|
||||||
- if (qobject_cast<QToolBox *>(widget->parent())) {
|
|
||||||
- _toolBoxEngine->registerWidget(widget);
|
|
||||||
+ if (auto toolBox = qobject_cast<QToolBox *>(widget->parent())) {
|
|
||||||
+ _toolBoxEngine->registerWidget(toolBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
- _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
+ _widgetStateEngine->registerWidget(button, AnimationHover | AnimationFocus);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// groupboxes
|
|
||||||
else if (QGroupBox *groupBox = qobject_cast<QGroupBox *>(widget)) {
|
|
||||||
if (groupBox->isCheckable()) {
|
|
||||||
- _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
+ _widgetStateEngine->registerWidget(groupBox, AnimationHover | AnimationFocus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sliders
|
|
||||||
- else if (qobject_cast<QScrollBar *>(widget)) {
|
|
||||||
- _scrollBarEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
- } else if (qobject_cast<QSlider *>(widget)) {
|
|
||||||
- _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
- } else if (qobject_cast<QDial *>(widget)) {
|
|
||||||
- _dialEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
+ else if (auto scrollBar = qobject_cast<QScrollBar *>(widget)) {
|
|
||||||
+ _scrollBarEngine->registerWidget(scrollBar, AnimationHover | AnimationFocus);
|
|
||||||
+ } else if (auto slider = qobject_cast<QSlider *>(widget)) {
|
|
||||||
+ _widgetStateEngine->registerWidget(slider, AnimationHover | AnimationFocus);
|
|
||||||
+ } else if (auto dial = qobject_cast<QDial *>(widget)) {
|
|
||||||
+ _dialEngine->registerWidget(dial, AnimationHover | AnimationFocus);
|
|
||||||
}
|
|
||||||
|
|
||||||
// progress bar
|
|
||||||
@@ -162,24 +162,24 @@ void Animations::registerWidget(QWidget *widget) const
|
|
||||||
|
|
||||||
// header views
|
|
||||||
// need to come before abstract item view, otherwise is skipped
|
|
||||||
- else if (qobject_cast<QHeaderView *>(widget)) {
|
|
||||||
- _headerViewEngine->registerWidget(widget);
|
|
||||||
+ else if (auto headerView = qobject_cast<QHeaderView *>(widget)) {
|
|
||||||
+ _headerViewEngine->registerWidget(headerView);
|
|
||||||
}
|
|
||||||
|
|
||||||
// lists
|
|
||||||
- else if (qobject_cast<QAbstractItemView *>(widget)) {
|
|
||||||
- _inputWidgetEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
+ else if (auto itemView = qobject_cast<QAbstractItemView *>(widget)) {
|
|
||||||
+ _inputWidgetEngine->registerWidget(itemView, AnimationHover | AnimationFocus);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tabbar
|
|
||||||
- else if (qobject_cast<QTabBar *>(widget)) {
|
|
||||||
- _tabBarEngine->registerWidget(widget);
|
|
||||||
+ else if (auto tabBar = qobject_cast<QTabBar *>(widget)) {
|
|
||||||
+ _tabBarEngine->registerWidget(tabBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
// scrollarea
|
|
||||||
else if (QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(widget)) {
|
|
||||||
- if (scrollArea->frameShadow() == QFrame::Sunken && (widget->focusPolicy() & Qt::StrongFocus)) {
|
|
||||||
- _inputWidgetEngine->registerWidget(widget, AnimationHover | AnimationFocus);
|
|
||||||
+ if (scrollArea->frameShadow() == QFrame::Sunken && (scrollArea->focusPolicy() & Qt::StrongFocus)) {
|
|
||||||
+ _inputWidgetEngine->registerWidget(scrollArea, AnimationHover | AnimationFocus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ void Animations::registerWidget(QWidget *widget) const
|
|
||||||
}
|
|
||||||
|
|
||||||
//____________________________________________________________
|
|
||||||
-void Animations::unregisterWidget(QWidget *widget) const
|
|
||||||
+void Animations::unregisterWidget(QObject *widget) const
|
|
||||||
{
|
|
||||||
if (!widget) {
|
|
||||||
return;
|
|
||||||
diff --git a/kstyle/animations/breezeanimations.h b/kstyle/animations/breezeanimations.h
|
|
||||||
index 6551900a5..3215e8fb9 100644
|
|
||||||
--- a/kstyle/animations/breezeanimations.h
|
|
||||||
+++ b/kstyle/animations/breezeanimations.h
|
|
||||||
@@ -31,10 +31,10 @@ public:
|
|
||||||
explicit Animations();
|
|
||||||
|
|
||||||
//* register animations corresponding to given widget, depending on its type.
|
|
||||||
- void registerWidget(QWidget *widget) const;
|
|
||||||
+ void registerWidget(QObject *widget) const;
|
|
||||||
|
|
||||||
/** unregister all animations associated to a widget */
|
|
||||||
- void unregisterWidget(QWidget *widget) const;
|
|
||||||
+ void unregisterWidget(QObject *widget) const;
|
|
||||||
|
|
||||||
//* enability engine
|
|
||||||
[[nodiscard]] WidgetStateEngine &widgetEnabilityEngine() const
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From 75201d1e63d82f7282c8bedcc7cc3d2417b1e123 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kai Uwe Broulik <kde@privat.broulik.de>
|
|
||||||
Date: Sat, 24 May 2025 09:40:01 +0200
|
|
||||||
Subject: [PATCH 2/3] kstyle: Use styleObject for widgetStateEngine in
|
|
||||||
CheckBox/RadioButton
|
|
||||||
|
|
||||||
Prepares it for doing the animation also with Qt Quick Controls
|
|
||||||
without an actual QWidget.
|
|
||||||
---
|
|
||||||
kstyle/breezestyle.cpp | 44 ++++++++++++++++++++++++++++--------------
|
|
||||||
1 file changed, 29 insertions(+), 15 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/kstyle/breezestyle.cpp b/kstyle/breezestyle.cpp
|
|
||||||
index 09cee6003..19149c382 100644
|
|
||||||
--- a/kstyle/breezestyle.cpp
|
|
||||||
+++ b/kstyle/breezestyle.cpp
|
|
||||||
@@ -4738,6 +4738,8 @@ bool Style::drawPanelItemViewItemPrimitive(const QStyleOption *option, QPainter
|
|
||||||
//___________________________________________________________________________________
|
|
||||||
bool Style::drawIndicatorCheckBoxPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const
|
|
||||||
{
|
|
||||||
+ const QObject *styleObject = widget ? widget : option->styleObject;
|
|
||||||
+
|
|
||||||
// copy rect and palette
|
|
||||||
const auto &rect(option->rect);
|
|
||||||
const auto &palette(option->palette);
|
|
||||||
@@ -4757,15 +4759,15 @@ bool Style::drawIndicatorCheckBoxPrimitive(const QStyleOption *option, QPainter
|
|
||||||
}
|
|
||||||
|
|
||||||
// animation state
|
|
||||||
- _animations->widgetStateEngine().updateState(widget, AnimationHover, mouseOver);
|
|
||||||
- _animations->widgetStateEngine().updateState(widget, AnimationPressed, checkBoxState != CheckOff);
|
|
||||||
+ _animations->widgetStateEngine().updateState(styleObject, AnimationHover, mouseOver);
|
|
||||||
+ _animations->widgetStateEngine().updateState(styleObject, AnimationPressed, checkBoxState != CheckOff);
|
|
||||||
auto target = checkBoxState;
|
|
||||||
- if (_animations->widgetStateEngine().isAnimated(widget, AnimationPressed)) {
|
|
||||||
+ if (_animations->widgetStateEngine().isAnimated(styleObject, AnimationPressed)) {
|
|
||||||
checkBoxState = CheckAnimated;
|
|
||||||
}
|
|
||||||
- const qreal animation(_animations->widgetStateEngine().opacity(widget, AnimationPressed));
|
|
||||||
+ const qreal animation(_animations->widgetStateEngine().opacity(styleObject, AnimationPressed));
|
|
||||||
|
|
||||||
- const qreal opacity(_animations->widgetStateEngine().opacity(widget, AnimationHover));
|
|
||||||
+ const qreal opacity(_animations->widgetStateEngine().opacity(styleObject, AnimationHover));
|
|
||||||
|
|
||||||
// render
|
|
||||||
_helper->renderCheckBoxBackground(painter, rect, palette, checkBoxState, hasHighlightNeutral(widget, option, mouseOver), sunken, animation);
|
|
||||||
@@ -4777,6 +4779,8 @@ bool Style::drawIndicatorCheckBoxPrimitive(const QStyleOption *option, QPainter
|
|
||||||
//___________________________________________________________________________________
|
|
||||||
bool Style::drawIndicatorRadioButtonPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const
|
|
||||||
{
|
|
||||||
+ const QObject *styleObject = widget ? widget : option->styleObject;
|
|
||||||
+
|
|
||||||
// copy rect and palette
|
|
||||||
const auto &rect(option->rect);
|
|
||||||
const auto &palette(option->palette);
|
|
||||||
@@ -4791,19 +4795,27 @@ bool Style::drawIndicatorRadioButtonPrimitive(const QStyleOption *option, QPaint
|
|
||||||
RadioButtonState radioButtonState((state & State_On) ? RadioOn : RadioOff);
|
|
||||||
|
|
||||||
// animation state
|
|
||||||
- _animations->widgetStateEngine().updateState(widget, AnimationHover, mouseOver);
|
|
||||||
- _animations->widgetStateEngine().updateState(widget, AnimationPressed, radioButtonState != RadioOff);
|
|
||||||
- if (_animations->widgetStateEngine().isAnimated(widget, AnimationPressed)) {
|
|
||||||
+ _animations->widgetStateEngine().updateState(styleObject, AnimationHover, mouseOver);
|
|
||||||
+ _animations->widgetStateEngine().updateState(styleObject, AnimationPressed, radioButtonState != RadioOff);
|
|
||||||
+ if (_animations->widgetStateEngine().isAnimated(styleObject, AnimationPressed)) {
|
|
||||||
radioButtonState = RadioAnimated;
|
|
||||||
}
|
|
||||||
- const qreal animation(_animations->widgetStateEngine().opacity(widget, AnimationPressed));
|
|
||||||
+ const qreal animation(_animations->widgetStateEngine().opacity(styleObject, AnimationPressed));
|
|
||||||
|
|
||||||
// colors
|
|
||||||
- const qreal opacity(_animations->widgetStateEngine().opacity(widget, AnimationHover));
|
|
||||||
+ const qreal opacity(_animations->widgetStateEngine().opacity(styleObject, AnimationHover));
|
|
||||||
|
|
||||||
// render
|
|
||||||
- _helper->renderRadioButtonBackground(painter, rect, palette, radioButtonState, hasHighlightNeutral(widget, option, mouseOver), sunken, animation);
|
|
||||||
- _helper->renderRadioButton(painter, rect, palette, mouseOver, radioButtonState, hasHighlightNeutral(widget, option, mouseOver), sunken, animation, opacity);
|
|
||||||
+ _helper->renderRadioButtonBackground(painter, rect, palette, radioButtonState, hasHighlightNeutral(styleObject, option, mouseOver), sunken, animation);
|
|
||||||
+ _helper->renderRadioButton(painter,
|
|
||||||
+ rect,
|
|
||||||
+ palette,
|
|
||||||
+ mouseOver,
|
|
||||||
+ radioButtonState,
|
|
||||||
+ hasHighlightNeutral(styleObject, option, mouseOver),
|
|
||||||
+ sunken,
|
|
||||||
+ animation,
|
|
||||||
+ opacity);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -5450,6 +5462,8 @@ bool Style::drawCheckBoxLabelControl(const QStyleOption *option, QPainter *paint
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ const QObject *styleObject = widget ? widget : option->styleObject;
|
|
||||||
+
|
|
||||||
// copy palette and rect
|
|
||||||
const auto &palette(option->palette);
|
|
||||||
const auto &rect(option->rect);
|
|
||||||
@@ -5501,10 +5515,10 @@ bool Style::drawCheckBoxLabelControl(const QStyleOption *option, QPainter *paint
|
|
||||||
const bool hasFocus(enabled && (state & State_HasFocus));
|
|
||||||
|
|
||||||
// update animation state
|
|
||||||
- _animations->widgetStateEngine().updateState(widget, AnimationFocus, hasFocus);
|
|
||||||
+ _animations->widgetStateEngine().updateState(styleObject, AnimationFocus, hasFocus);
|
|
||||||
|
|
||||||
- const bool isFocusAnimated(_animations->widgetStateEngine().isAnimated(widget, AnimationFocus));
|
|
||||||
- const qreal opacity(_animations->widgetStateEngine().opacity(widget, AnimationFocus));
|
|
||||||
+ const bool isFocusAnimated(_animations->widgetStateEngine().isAnimated(styleObject, AnimationFocus));
|
|
||||||
+ const qreal opacity(_animations->widgetStateEngine().opacity(styleObject, AnimationFocus));
|
|
||||||
// focus color
|
|
||||||
QColor focusColor;
|
|
||||||
if (isFocusAnimated) {
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From a572df8f099969919544dbc2478de6aa2082a705 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kai Uwe Broulik <kde@privat.broulik.de>
|
|
||||||
Date: Sat, 24 May 2025 09:54:44 +0200
|
|
||||||
Subject: [PATCH 3/3] kstyle: Register Qt Quick Controls with animations
|
|
||||||
|
|
||||||
Since it's all software-rendered and indirect, only the
|
|
||||||
CheckBox and RadioButton controls are actually wired up
|
|
||||||
since the rest of the animations is quite subtle.
|
|
||||||
---
|
|
||||||
kstyle/animations/breezeanimations.cpp | 9 +++++++++
|
|
||||||
kstyle/breezestyle.cpp | 4 ++++
|
|
||||||
2 files changed, 13 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/kstyle/animations/breezeanimations.cpp b/kstyle/animations/breezeanimations.cpp
|
|
||||||
index ceab53802..5285f5daf 100644
|
|
||||||
--- a/kstyle/animations/breezeanimations.cpp
|
|
||||||
+++ b/kstyle/animations/breezeanimations.cpp
|
|
||||||
@@ -100,6 +100,15 @@ void Animations::registerWidget(QObject *widget) const
|
|
||||||
// install animation timers
|
|
||||||
// for optimization, one should put with most used widgets here first
|
|
||||||
|
|
||||||
+ // KQuickStyleItem.
|
|
||||||
+ const QString elementType = widget->property("elementType").toString();
|
|
||||||
+ if (!elementType.isEmpty()) {
|
|
||||||
+ if (elementType == QLatin1String("checkbox") || elementType == QLatin1String("radiobutton")) {
|
|
||||||
+ _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus | AnimationPressed);
|
|
||||||
+ }
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
// buttons
|
|
||||||
if (auto toolButton = qobject_cast<QToolButton *>(widget)) {
|
|
||||||
_toolButtonEngine->registerWidget(toolButton, AnimationHover | AnimationFocus);
|
|
||||||
diff --git a/kstyle/breezestyle.cpp b/kstyle/breezestyle.cpp
|
|
||||||
index 19149c382..e549803f1 100644
|
|
||||||
--- a/kstyle/breezestyle.cpp
|
|
||||||
+++ b/kstyle/breezestyle.cpp
|
|
||||||
@@ -4739,6 +4739,7 @@ bool Style::drawPanelItemViewItemPrimitive(const QStyleOption *option, QPainter
|
|
||||||
bool Style::drawIndicatorCheckBoxPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const
|
|
||||||
{
|
|
||||||
const QObject *styleObject = widget ? widget : option->styleObject;
|
|
||||||
+ isQtQuickControl(option, widget); // registers it with widgetStateEngine.
|
|
||||||
|
|
||||||
// copy rect and palette
|
|
||||||
const auto &rect(option->rect);
|
|
||||||
@@ -4780,6 +4781,7 @@ bool Style::drawIndicatorCheckBoxPrimitive(const QStyleOption *option, QPainter
|
|
||||||
bool Style::drawIndicatorRadioButtonPrimitive(const QStyleOption *option, QPainter *painter, const QWidget *widget) const
|
|
||||||
{
|
|
||||||
const QObject *styleObject = widget ? widget : option->styleObject;
|
|
||||||
+ isQtQuickControl(option, widget); // registers it with widgetStateEngine.
|
|
||||||
|
|
||||||
// copy rect and palette
|
|
||||||
const auto &rect(option->rect);
|
|
||||||
@@ -5463,6 +5465,7 @@ bool Style::drawCheckBoxLabelControl(const QStyleOption *option, QPainter *paint
|
|
||||||
}
|
|
||||||
|
|
||||||
const QObject *styleObject = widget ? widget : option->styleObject;
|
|
||||||
+ isQtQuickControl(option, widget); // registers it with widgetStateEngine.
|
|
||||||
|
|
||||||
// copy palette and rect
|
|
||||||
const auto &palette(option->palette);
|
|
||||||
@@ -8460,6 +8463,7 @@ bool Style::isQtQuickControl(const QStyleOption *option, const QWidget *widget)
|
|
||||||
if (!widget && option) {
|
|
||||||
if (const auto item = qobject_cast<QQuickItem *>(option->styleObject)) {
|
|
||||||
_windowManager->registerQuickItem(item);
|
|
||||||
+ _animations->registerWidget(item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
Plasma 6.5.0:
|
|
||||||
Pr 3612 https://invent.kde.org/plasma/kwin/-/merge_requests/3612
|
|
||||||
Pr 7823 https://invent.kde.org/plasma/kwin/-/merge_requests/7823
|
|
||||||
Pr 8005 https://invent.kde.org/plasma/kwin/-/merge_requests/8005
|
|
||||||
Depends on Pr 7927 https://invent.kde.org/plasma/kwin/-/merge_requests/7927
|
|
||||||
Pr 8155 https://invent.kde.org/plasma/kwin/-/merge_requests/8155
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,26 +0,0 @@
|
||||||
From a1868942fbc2ad5de4c053dd17335dff34d81779 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
Date: Wed, 25 Jun 2025 14:10:18 +0300
|
|
||||||
Subject: [PATCH] plugins/login: Reduce animation duration
|
|
||||||
|
|
||||||
This makes the startup feel a bit faster.
|
|
||||||
---
|
|
||||||
src/plugins/login/package/contents/code/main.js | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/src/plugins/login/package/contents/code/main.js b/src/plugins/login/package/contents/code/main.js
|
|
||||||
index 7f849a6e2e6..e3ad0b76c07 100644
|
|
||||||
--- a/src/plugins/login/package/contents/code/main.js
|
|
||||||
+++ b/src/plugins/login/package/contents/code/main.js
|
|
||||||
@@ -11,7 +11,7 @@
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var loginEffect = {
|
|
||||||
- duration: animationTime(1000),
|
|
||||||
+ duration: animationTime(500),
|
|
||||||
isFadeToBlack: false,
|
|
||||||
loadConfig: function () {
|
|
||||||
loginEffect.isFadeToBlack = effect.readConfig("FadeToBlack", false);
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,669 +0,0 @@
|
||||||
From 9f6c92806490d662117575a766f9fcb01e253344 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
||||||
Date: Tue, 22 Jul 2025 23:24:51 +0200
|
|
||||||
Subject: [PATCH 1/4] xdgactivation: move the activation token to workspace
|
|
||||||
|
|
||||||
---
|
|
||||||
src/activation.cpp | 14 ++++++++++++
|
|
||||||
src/workspace.h | 6 +++++
|
|
||||||
src/xdgactivationv1.cpp | 50 +++++++++--------------------------------
|
|
||||||
src/xdgactivationv1.h | 14 ++----------
|
|
||||||
4 files changed, 32 insertions(+), 52 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/activation.cpp b/src/activation.cpp
|
|
||||||
index 60b921247ce..9e743b8b098 100644
|
|
||||||
--- a/src/activation.cpp
|
|
||||||
+++ b/src/activation.cpp
|
|
||||||
@@ -630,4 +630,18 @@ void Workspace::windowAttentionChanged(Window *window, bool set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+void Workspace::setActivationToken(const QString &token, uint32_t serial)
|
|
||||||
+{
|
|
||||||
+ m_activationToken = token;
|
|
||||||
+ m_activationTokenSerial = serial;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+bool Workspace::mayActivate(const QString &token) const
|
|
||||||
+{
|
|
||||||
+ if (!m_activeWindow) {
|
|
||||||
+ return true;
|
|
||||||
+ }
|
|
||||||
+ return !m_activationToken.isEmpty() && token == m_activationToken && m_activeWindow->lastUsageSerial() <= m_activationTokenSerial;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
} // namespace
|
|
||||||
diff --git a/src/workspace.h b/src/workspace.h
|
|
||||||
index 2082bbe148d..03c54e06750 100644
|
|
||||||
--- a/src/workspace.h
|
|
||||||
+++ b/src/workspace.h
|
|
||||||
@@ -436,6 +436,9 @@ public:
|
|
||||||
OutputConfigurationError applyOutputConfiguration(OutputConfiguration &config, const std::optional<QList<Output *>> &outputOrder = std::nullopt);
|
|
||||||
void updateXwaylandScale();
|
|
||||||
|
|
||||||
+ void setActivationToken(const QString &token, uint32_t serial);
|
|
||||||
+ bool mayActivate(const QString &token) const;
|
|
||||||
+
|
|
||||||
public Q_SLOTS:
|
|
||||||
void performWindowOperation(KWin::Window *window, Options::WindowOperation op);
|
|
||||||
// Keybindings
|
|
||||||
@@ -729,6 +732,9 @@ private:
|
|
||||||
std::unique_ptr<DpmsInputEventFilter> m_dpmsFilter;
|
|
||||||
KConfigWatcher::Ptr m_kdeglobalsWatcher;
|
|
||||||
|
|
||||||
+ QString m_activationToken;
|
|
||||||
+ uint32_t m_activationTokenSerial = 0;
|
|
||||||
+
|
|
||||||
private:
|
|
||||||
friend bool performTransiencyCheck();
|
|
||||||
friend Workspace *workspace();
|
|
||||||
diff --git a/src/xdgactivationv1.cpp b/src/xdgactivationv1.cpp
|
|
||||||
index 39dade95332..a6be350399a 100644
|
|
||||||
--- a/src/xdgactivationv1.cpp
|
|
||||||
+++ b/src/xdgactivationv1.cpp
|
|
||||||
@@ -33,22 +33,6 @@ static bool isPrivilegedInWindowManagement(const ClientConnection *client)
|
|
||||||
XdgActivationV1Integration::XdgActivationV1Integration(XdgActivationV1Interface *activation, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
- Workspace *ws = Workspace::self();
|
|
||||||
- connect(ws, &Workspace::windowActivated, this, [this](Window *window) {
|
|
||||||
- if (!m_currentActivationToken || !window || window->property("token").toString() == m_currentActivationToken->token) {
|
|
||||||
- return;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- // We check that it's not the app that we are trying to activate
|
|
||||||
- if (window->desktopFileName() != m_currentActivationToken->applicationId) {
|
|
||||||
- // But also that the new one has been requested after the token was requested
|
|
||||||
- if (window->lastUsageSerial() < m_currentActivationToken->serial) {
|
|
||||||
- return;
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- clear();
|
|
||||||
- });
|
|
||||||
activation->setActivationTokenCreator([this](ClientConnection *client, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId) -> QString {
|
|
||||||
Workspace *ws = Workspace::self();
|
|
||||||
Q_ASSERT(client); // Should always be available as it's coming straight from the wayland implementation
|
|
||||||
@@ -69,9 +53,6 @@ QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInter
|
|
||||||
static int i = 0;
|
|
||||||
const auto newToken = QStringLiteral("kwin-%1").arg(++i);
|
|
||||||
|
|
||||||
- if (m_currentActivationToken) {
|
|
||||||
- clear();
|
|
||||||
- }
|
|
||||||
bool showNotify = false;
|
|
||||||
QIcon icon = QIcon::fromTheme(QStringLiteral("system-run"));
|
|
||||||
if (const QString desktopFilePath = Window::findDesktopFile(appId); !desktopFilePath.isEmpty()) {
|
|
||||||
@@ -83,13 +64,13 @@ QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInter
|
|
||||||
}
|
|
||||||
icon = QIcon::fromTheme(df.readIcon(), icon);
|
|
||||||
}
|
|
||||||
- std::unique_ptr<PlasmaWindowActivationInterface> activation;
|
|
||||||
if (showNotify) {
|
|
||||||
- activation = waylandServer()->plasmaActivationFeedback()->createActivation(appId);
|
|
||||||
+ m_lastToken = newToken;
|
|
||||||
+ m_activation = waylandServer()->plasmaActivationFeedback()->createActivation(appId);
|
|
||||||
}
|
|
||||||
- m_currentActivationToken = std::make_unique<ActivationToken>(ActivationToken{newToken, isPrivileged, surface, serial, seat, appId, showNotify, std::move(activation)});
|
|
||||||
+ workspace()->setActivationToken(newToken, serial);
|
|
||||||
if (showNotify) {
|
|
||||||
- Q_EMIT effects->startupAdded(m_currentActivationToken->token, icon);
|
|
||||||
+ Q_EMIT effects->startupAdded(newToken, icon);
|
|
||||||
}
|
|
||||||
return newToken;
|
|
||||||
}
|
|
||||||
@@ -103,31 +84,20 @@ void XdgActivationV1Integration::activateSurface(SurfaceInterface *surface, cons
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (!m_currentActivationToken || m_currentActivationToken->token != token) {
|
|
||||||
- qCDebug(KWIN_CORE) << "Refusing to activate " << window << " (provided token: " << token << ", current token:" << (m_currentActivationToken ? m_currentActivationToken->token : QStringLiteral("null")) << ")";
|
|
||||||
+ if (!ws->mayActivate(token)) {
|
|
||||||
window->demandAttention();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
-
|
|
||||||
- auto ownerWindow = waylandServer()->findWindow(m_currentActivationToken->surface);
|
|
||||||
- qCDebug(KWIN_CORE) << "activating" << window << surface << "on behalf of" << m_currentActivationToken->surface << "into" << ownerWindow;
|
|
||||||
- if (!ws->activeWindow() || ws->activeWindow() == ownerWindow || ws->activeWindow()->lastUsageSerial() < m_currentActivationToken->serial || m_currentActivationToken->isPrivileged) {
|
|
||||||
- ws->activateWindow(window);
|
|
||||||
- } else {
|
|
||||||
- qCWarning(KWIN_CORE) << "Activation requested while owner isn't active" << (ownerWindow ? ownerWindow->desktopFileName() : "null")
|
|
||||||
- << m_currentActivationToken->applicationId;
|
|
||||||
- window->demandAttention();
|
|
||||||
- clear();
|
|
||||||
- }
|
|
||||||
+ ws->activateWindow(window);
|
|
||||||
+ clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XdgActivationV1Integration::clear()
|
|
||||||
{
|
|
||||||
- Q_ASSERT(m_currentActivationToken);
|
|
||||||
- if (m_currentActivationToken->showNotify) {
|
|
||||||
- Q_EMIT effects->startupRemoved(m_currentActivationToken->token);
|
|
||||||
+ if (m_activation) {
|
|
||||||
+ Q_EMIT effects->startupRemoved(m_lastToken);
|
|
||||||
+ m_activation.reset();
|
|
||||||
}
|
|
||||||
- m_currentActivationToken.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
diff --git a/src/xdgactivationv1.h b/src/xdgactivationv1.h
|
|
||||||
index 98835def8aa..77d21856095 100644
|
|
||||||
--- a/src/xdgactivationv1.h
|
|
||||||
+++ b/src/xdgactivationv1.h
|
|
||||||
@@ -38,18 +38,8 @@ private:
|
|
||||||
QString requestToken(bool isPrivileged, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
- struct ActivationToken
|
|
||||||
- {
|
|
||||||
- QString token;
|
|
||||||
- bool isPrivileged;
|
|
||||||
- QPointer<const SurfaceInterface> surface;
|
|
||||||
- uint serial;
|
|
||||||
- SeatInterface *seat;
|
|
||||||
- QString applicationId;
|
|
||||||
- bool showNotify;
|
|
||||||
- std::unique_ptr<PlasmaWindowActivationInterface> activation;
|
|
||||||
- };
|
|
||||||
- std::unique_ptr<ActivationToken> m_currentActivationToken;
|
|
||||||
+ QString m_lastToken;
|
|
||||||
+ std::unique_ptr<PlasmaWindowActivationInterface> m_activation;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From 6c673a479412902a14c06046199f976e2192dc65 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
||||||
Date: Tue, 22 Jul 2025 23:43:14 +0200
|
|
||||||
Subject: [PATCH 2/4] xdgactivation: also allow using activation tokens before
|
|
||||||
the window is mapped
|
|
||||||
|
|
||||||
We just store the token in the window, and then Workspace uses it to decide whether
|
|
||||||
or not to activate the window when it's actually shown.
|
|
||||||
---
|
|
||||||
src/window.cpp | 10 ++++++++++
|
|
||||||
src/window.h | 5 +++++
|
|
||||||
src/workspace.cpp | 4 ++--
|
|
||||||
src/xdgactivationv1.cpp | 6 +++++-
|
|
||||||
4 files changed, 22 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/window.cpp b/src/window.cpp
|
|
||||||
index 4a97a54aa74..879147ec673 100644
|
|
||||||
--- a/src/window.cpp
|
|
||||||
+++ b/src/window.cpp
|
|
||||||
@@ -4675,6 +4675,16 @@ void Window::setDescription(const QString &description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+void Window::setActivationToken(const QString &token)
|
|
||||||
+{
|
|
||||||
+ m_activationToken = token;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+QString Window::activationToken() const
|
|
||||||
+{
|
|
||||||
+ return m_activationToken;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
} // namespace KWin
|
|
||||||
|
|
||||||
#include "moc_window.cpp"
|
|
||||||
diff --git a/src/window.h b/src/window.h
|
|
||||||
index 1eb371018ca..91addb46819 100644
|
|
||||||
--- a/src/window.h
|
|
||||||
+++ b/src/window.h
|
|
||||||
@@ -1357,6 +1357,9 @@ public:
|
|
||||||
QString tag() const;
|
|
||||||
QString description() const;
|
|
||||||
|
|
||||||
+ void setActivationToken(const QString &token);
|
|
||||||
+ QString activationToken() const;
|
|
||||||
+
|
|
||||||
public Q_SLOTS:
|
|
||||||
virtual void closeWindow() = 0;
|
|
||||||
|
|
||||||
@@ -1880,6 +1883,8 @@ protected:
|
|
||||||
|
|
||||||
QString m_tag;
|
|
||||||
QString m_description;
|
|
||||||
+
|
|
||||||
+ QString m_activationToken;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline QRectF Window::bufferGeometry() const
|
|
||||||
diff --git a/src/workspace.cpp b/src/workspace.cpp
|
|
||||||
index 3a5cb12677b..866abb8080d 100644
|
|
||||||
--- a/src/workspace.cpp
|
|
||||||
+++ b/src/workspace.cpp
|
|
||||||
@@ -781,8 +781,8 @@ void Workspace::addWaylandWindow(Window *window)
|
|
||||||
rearrange();
|
|
||||||
}
|
|
||||||
if (window->wantsInput() && !window->isMinimized()) {
|
|
||||||
- // Never activate a window on its own in "Extreme" mode.
|
|
||||||
- if (options->focusStealingPreventionLevel() < 4) {
|
|
||||||
+ // In "Extreme" mode, require an activation token to activate new windows
|
|
||||||
+ if (options->focusStealingPreventionLevel() < 4 || (!m_activationToken.isEmpty() && window->activationToken() == m_activationToken)) {
|
|
||||||
if (!window->isDesktop()
|
|
||||||
// If there's no active window, make this desktop the active one.
|
|
||||||
|| (activeWindow() == nullptr && should_get_focus.count() == 0)) {
|
|
||||||
diff --git a/src/xdgactivationv1.cpp b/src/xdgactivationv1.cpp
|
|
||||||
index a6be350399a..11c95c84b32 100644
|
|
||||||
--- a/src/xdgactivationv1.cpp
|
|
||||||
+++ b/src/xdgactivationv1.cpp
|
|
||||||
@@ -88,7 +88,11 @@ void XdgActivationV1Integration::activateSurface(SurfaceInterface *surface, cons
|
|
||||||
window->demandAttention();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
- ws->activateWindow(window);
|
|
||||||
+ if (window->readyForPainting()) {
|
|
||||||
+ ws->activateWindow(window);
|
|
||||||
+ } else {
|
|
||||||
+ window->setActivationToken(token);
|
|
||||||
+ }
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From 2436625a66257f586a0934c3d678c910bbdb3705 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
||||||
Date: Wed, 23 Jul 2025 13:18:39 +0200
|
|
||||||
Subject: [PATCH 3/4] xdgactivation: move the "not granted" token to
|
|
||||||
requestToken
|
|
||||||
|
|
||||||
Having some but not all checks in that method is a bit confusing
|
|
||||||
---
|
|
||||||
src/xdgactivationv1.cpp | 14 ++++++--------
|
|
||||||
1 file changed, 6 insertions(+), 8 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/xdgactivationv1.cpp b/src/xdgactivationv1.cpp
|
|
||||||
index 11c95c84b32..e818466f8ad 100644
|
|
||||||
--- a/src/xdgactivationv1.cpp
|
|
||||||
+++ b/src/xdgactivationv1.cpp
|
|
||||||
@@ -34,15 +34,8 @@ XdgActivationV1Integration::XdgActivationV1Integration(XdgActivationV1Interface
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
activation->setActivationTokenCreator([this](ClientConnection *client, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId) -> QString {
|
|
||||||
- Workspace *ws = Workspace::self();
|
|
||||||
Q_ASSERT(client); // Should always be available as it's coming straight from the wayland implementation
|
|
||||||
- const bool isPrivileged = isPrivilegedInWindowManagement(client);
|
|
||||||
- if (!isPrivileged && ws->activeWindow() && ws->activeWindow()->surface() != surface) {
|
|
||||||
- qCDebug(KWIN_CORE) << "Cannot grant a token to" << client;
|
|
||||||
- return QStringLiteral("not-granted-666");
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- return requestToken(isPrivileged, surface, serial, seat, appId);
|
|
||||||
+ return requestToken(isPrivilegedInWindowManagement(client), surface, serial, seat, appId);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(activation, &XdgActivationV1Interface::activateRequested, this, &XdgActivationV1Integration::activateSurface);
|
|
||||||
@@ -50,6 +43,11 @@ XdgActivationV1Integration::XdgActivationV1Integration(XdgActivationV1Interface
|
|
||||||
|
|
||||||
QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId)
|
|
||||||
{
|
|
||||||
+ auto window = waylandServer()->findWindow(surface);
|
|
||||||
+ if (!isPrivileged && workspace()->activeWindow() && workspace()->activeWindow()->surface() != surface) {
|
|
||||||
+ qCWarning(KWIN_CORE) << "Cannot grant a token to" << window;
|
|
||||||
+ return QStringLiteral("not-granted-666");
|
|
||||||
+ }
|
|
||||||
static int i = 0;
|
|
||||||
const auto newToken = QStringLiteral("kwin-%1").arg(++i);
|
|
||||||
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From 8508b8060813c07fe035801381bad1f9a375acf0 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
||||||
Date: Thu, 24 Jul 2025 20:43:46 +0200
|
|
||||||
Subject: [PATCH 4/4] autotests/integration: add a test case for xdg activation
|
|
||||||
|
|
||||||
---
|
|
||||||
autotests/integration/CMakeLists.txt | 3 +-
|
|
||||||
autotests/integration/activation_test.cpp | 126 +++++++++++++++++++++-
|
|
||||||
autotests/integration/kwin_wayland_test.h | 31 ++++++
|
|
||||||
autotests/integration/test_helpers.cpp | 50 +++++++++
|
|
||||||
4 files changed, 208 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt
|
|
||||||
index 13b0cde04f5..408af6ac8b8 100644
|
|
||||||
--- a/autotests/integration/CMakeLists.txt
|
|
||||||
+++ b/autotests/integration/CMakeLists.txt
|
|
||||||
@@ -16,13 +16,14 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
|
|
||||||
FILES
|
|
||||||
${CMAKE_SOURCE_DIR}/src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
|
|
||||||
${WaylandProtocols_DATADIR}/stable/presentation-time/presentation-time.xml
|
|
||||||
+ ${WaylandProtocols_DATADIR}/stable/tablet/tablet-v2.xml
|
|
||||||
${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
|
|
||||||
${WaylandProtocols_DATADIR}/staging/color-management/color-management-v1.xml
|
|
||||||
- ${WaylandProtocols_DATADIR}/stable/tablet/tablet-v2.xml
|
|
||||||
${WaylandProtocols_DATADIR}/staging/cursor-shape/cursor-shape-v1.xml
|
|
||||||
${WaylandProtocols_DATADIR}/staging/fifo/fifo-v1.xml
|
|
||||||
${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
|
|
||||||
${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml
|
|
||||||
+ ${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
|
|
||||||
${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
|
|
||||||
${WaylandProtocols_DATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
|
|
||||||
${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v3.xml
|
|
||||||
diff --git a/autotests/integration/activation_test.cpp b/autotests/integration/activation_test.cpp
|
|
||||||
index 75c9e7e8c7b..30f671d6fe2 100644
|
|
||||||
--- a/autotests/integration/activation_test.cpp
|
|
||||||
+++ b/autotests/integration/activation_test.cpp
|
|
||||||
@@ -15,6 +15,7 @@
|
|
||||||
#include "workspace.h"
|
|
||||||
#include "x11window.h"
|
|
||||||
|
|
||||||
+#include <KWayland/Client/seat.h>
|
|
||||||
#include <KWayland/Client/surface.h>
|
|
||||||
#include <netwm.h>
|
|
||||||
#include <xcb/xcb_icccm.h>
|
|
||||||
@@ -40,6 +41,7 @@ private Q_SLOTS:
|
|
||||||
void testSwitchToWindowMaximized();
|
|
||||||
void testSwitchToWindowFullScreen();
|
|
||||||
void testActiveFullscreen();
|
|
||||||
+ void testXdgActivation();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void stackScreensHorizontally();
|
|
||||||
@@ -64,7 +66,7 @@ void ActivationTest::initTestCase()
|
|
||||||
|
|
||||||
void ActivationTest::init()
|
|
||||||
{
|
|
||||||
- QVERIFY(Test::setupWaylandConnection());
|
|
||||||
+ QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgActivation | Test::AdditionalWaylandInterface::Seat));
|
|
||||||
|
|
||||||
workspace()->setActiveOutput(QPoint(640, 512));
|
|
||||||
input()->pointer()->warp(QPoint(640, 512));
|
|
||||||
@@ -592,6 +594,128 @@ void ActivationTest::testActiveFullscreen()
|
|
||||||
QCOMPARE(workspace()->activeWindow(), waylandWindow);
|
|
||||||
QCOMPARE(x11Window->layer(), Layer::NormalLayer);
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+void ActivationTest::testXdgActivation()
|
|
||||||
+{
|
|
||||||
+ Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
|
|
||||||
+
|
|
||||||
+ uint32_t time = 0;
|
|
||||||
+
|
|
||||||
+ std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
|
||||||
+ std::vector<std::unique_ptr<Test::XdgToplevel>> shellSurfaces;
|
|
||||||
+ std::vector<Window *> windows;
|
|
||||||
+ const auto setupWindows = [&]() {
|
|
||||||
+ windows.clear();
|
|
||||||
+ shellSurfaces.clear();
|
|
||||||
+ surfaces.clear();
|
|
||||||
+
|
|
||||||
+ // re-create the same setup every time for reduced confusion
|
|
||||||
+ for (int i = 0; i < 3; i++) {
|
|
||||||
+ surfaces.push_back(Test::createSurface());
|
|
||||||
+ shellSurfaces.push_back(Test::createXdgToplevelSurface(surfaces.back().get()));
|
|
||||||
+ windows.push_back(Test::renderAndWaitForShown(surfaces.back().get(), QSize(100, 50), Qt::blue));
|
|
||||||
+ windows.back()->move(QPoint(150 * i, 0));
|
|
||||||
+
|
|
||||||
+ Test::pointerMotion(windows.back()->frameGeometry().center(), time++);
|
|
||||||
+ Test::pointerButtonPressed(1, time++);
|
|
||||||
+ Test::pointerButtonReleased(1, time++);
|
|
||||||
+ }
|
|
||||||
+ };
|
|
||||||
+ setupWindows();
|
|
||||||
+
|
|
||||||
+ QSignalSpy activationSpy(workspace(), &Workspace::windowActivated);
|
|
||||||
+
|
|
||||||
+ // activating a window without a valid token should fail
|
|
||||||
+ Test::xdgActivation()->activate(QString(), *surfaces[1]);
|
|
||||||
+ QVERIFY(!activationSpy.wait(10));
|
|
||||||
+
|
|
||||||
+ // activating it without a surface should fail as well, even if a serial is present
|
|
||||||
+ auto token = Test::xdgActivation()->createToken();
|
|
||||||
+ token->set_serial(windows.back()->lastUsageSerial(), *Test::waylandSeat());
|
|
||||||
+ Test::xdgActivation()->activate(token->commitAndWait(), *surfaces[1]);
|
|
||||||
+ QVERIFY(!activationSpy.wait(10));
|
|
||||||
+
|
|
||||||
+ // adding the surface should make it work
|
|
||||||
+ token = Test::xdgActivation()->createToken();
|
|
||||||
+ token->set_surface(*surfaces.back());
|
|
||||||
+ token->set_serial(windows.back()->lastUsageSerial(), *Test::waylandSeat());
|
|
||||||
+ Test::xdgActivation()->activate(token->commitAndWait(), *surfaces[1]);
|
|
||||||
+ QVERIFY(activationSpy.wait(10));
|
|
||||||
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
||||||
+
|
|
||||||
+ // activation should still work if the window is closed after creating the token
|
|
||||||
+ setupWindows();
|
|
||||||
+ token = Test::xdgActivation()->createToken();
|
|
||||||
+ token->set_surface(*surfaces[2]);
|
|
||||||
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
||||||
+ QString result = token->commitAndWait();
|
|
||||||
+
|
|
||||||
+ surfaces[2]->attachBuffer((wl_buffer *)nullptr);
|
|
||||||
+ surfaces[2]->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
||||||
+ QVERIFY(activationSpy.wait(10));
|
|
||||||
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
||||||
+
|
|
||||||
+ Test::xdgActivation()->activate(result, *surfaces[0]);
|
|
||||||
+ QVERIFY(activationSpy.wait(10));
|
|
||||||
+ QCOMPARE(workspace()->activeWindow(), windows[0]);
|
|
||||||
+
|
|
||||||
+ // ...unless the user interacted with another window in between
|
|
||||||
+ setupWindows();
|
|
||||||
+ token = Test::xdgActivation()->createToken();
|
|
||||||
+ token->set_surface(*surfaces[2]);
|
|
||||||
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
||||||
+ result = token->commitAndWait();
|
|
||||||
+
|
|
||||||
+ surfaces[2]->attachBuffer((wl_buffer *)nullptr);
|
|
||||||
+ surfaces[2]->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
||||||
+ QVERIFY(activationSpy.wait(10));
|
|
||||||
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
||||||
+
|
|
||||||
+ Test::pointerMotion(windows[1]->frameGeometry().center(), time++);
|
|
||||||
+ Test::pointerButtonPressed(1, time++);
|
|
||||||
+ Test::pointerButtonReleased(1, time++);
|
|
||||||
+
|
|
||||||
+ Test::xdgActivation()->activate(result, *surfaces[0]);
|
|
||||||
+ QVERIFY(!activationSpy.wait(10));
|
|
||||||
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
||||||
+
|
|
||||||
+ // ensure that windows are only activated on show with a valid activation token
|
|
||||||
+ options->setFocusStealingPreventionLevel(4);
|
|
||||||
+
|
|
||||||
+ // creating a new window and immediately activating it should work
|
|
||||||
+ setupWindows();
|
|
||||||
+ token = Test::xdgActivation()->createToken();
|
|
||||||
+ token->set_surface(*surfaces[2]);
|
|
||||||
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
||||||
+ result = token->commitAndWait();
|
|
||||||
+ surfaces.push_back(Test::createSurface());
|
|
||||||
+ shellSurfaces.push_back(Test::createXdgToplevelSurface(surfaces.back().get(), [&](Test::XdgToplevel *toplevel) {
|
|
||||||
+ Test::xdgActivation()->activate(result, *surfaces.back());
|
|
||||||
+ }));
|
|
||||||
+ windows.push_back(Test::renderAndWaitForShown(surfaces.back().get(), QSize(100, 50), Qt::blue));
|
|
||||||
+ QCOMPARE(workspace()->activeWindow(), windows.back());
|
|
||||||
+ windows.back()->move(QPoint(150 * 3, 0));
|
|
||||||
+
|
|
||||||
+ // activation should fail if the user clicks on another window in between
|
|
||||||
+ // creating the activation token and using it
|
|
||||||
+ setupWindows();
|
|
||||||
+ token = Test::xdgActivation()->createToken();
|
|
||||||
+ token->set_surface(*surfaces[2]);
|
|
||||||
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
||||||
+ result = token->commitAndWait();
|
|
||||||
+
|
|
||||||
+ Test::pointerMotion(windows[1]->frameGeometry().center(), time++);
|
|
||||||
+ Test::pointerButtonPressed(1, time++);
|
|
||||||
+ Test::pointerButtonReleased(1, time++);
|
|
||||||
+
|
|
||||||
+ surfaces.push_back(Test::createSurface());
|
|
||||||
+ shellSurfaces.push_back(Test::createXdgToplevelSurface(surfaces.back().get(), [&](Test::XdgToplevel *toplevel) {
|
|
||||||
+ Test::xdgActivation()->activate(result, *surfaces.back());
|
|
||||||
+ }));
|
|
||||||
+ windows.push_back(Test::renderAndWaitForShown(surfaces.back().get(), QSize(100, 50), Qt::blue));
|
|
||||||
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
||||||
+ windows.back()->move(QPoint(150 * 3, 0));
|
|
||||||
+}
|
|
||||||
}
|
|
||||||
|
|
||||||
WAYLANDTEST_MAIN(KWin::ActivationTest)
|
|
||||||
diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h
|
|
||||||
index b0f63dadbc5..a6616189617 100644
|
|
||||||
--- a/autotests/integration/kwin_wayland_test.h
|
|
||||||
+++ b/autotests/integration/kwin_wayland_test.h
|
|
||||||
@@ -34,6 +34,7 @@
|
|
||||||
#include "qwayland-security-context-v1.h"
|
|
||||||
#include "qwayland-text-input-unstable-v3.h"
|
|
||||||
#include "qwayland-wlr-layer-shell-unstable-v1.h"
|
|
||||||
+#include "qwayland-xdg-activation-v1.h"
|
|
||||||
#include "qwayland-xdg-decoration-unstable-v1.h"
|
|
||||||
#include "qwayland-xdg-dialog-v1.h"
|
|
||||||
#include "qwayland-xdg-shell.h"
|
|
||||||
@@ -611,6 +612,7 @@ enum class AdditionalWaylandInterface {
|
|
||||||
ColorManagement = 1 << 22,
|
|
||||||
FifoV1 = 1 << 23,
|
|
||||||
PresentationTime = 1 << 24,
|
|
||||||
+ XdgActivation = 1 << 25,
|
|
||||||
};
|
|
||||||
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
|
|
||||||
|
|
||||||
@@ -717,6 +719,33 @@ private:
|
|
||||||
void wp_presentation_feedback_discarded() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
+class XdgActivationToken : public QObject, public QtWayland::xdg_activation_token_v1
|
|
||||||
+{
|
|
||||||
+ Q_OBJECT
|
|
||||||
+public:
|
|
||||||
+ explicit XdgActivationToken(::xdg_activation_token_v1 *object);
|
|
||||||
+ ~XdgActivationToken() override;
|
|
||||||
+
|
|
||||||
+ QString commitAndWait();
|
|
||||||
+
|
|
||||||
+Q_SIGNALS:
|
|
||||||
+ void tokenReceived();
|
|
||||||
+
|
|
||||||
+private:
|
|
||||||
+ void xdg_activation_token_v1_done(const QString &token) override;
|
|
||||||
+
|
|
||||||
+ QString m_token;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+class XdgActivation : public QtWayland::xdg_activation_v1
|
|
||||||
+{
|
|
||||||
+public:
|
|
||||||
+ explicit XdgActivation(::wl_registry *registry, uint32_t id, int version);
|
|
||||||
+ ~XdgActivation() override;
|
|
||||||
+
|
|
||||||
+ std::unique_ptr<XdgActivationToken> createToken();
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
struct Connection
|
|
||||||
{
|
|
||||||
static std::unique_ptr<Connection> setup(AdditionalWaylandInterfaces interfaces = AdditionalWaylandInterfaces());
|
|
||||||
@@ -757,6 +786,7 @@ struct Connection
|
|
||||||
std::unique_ptr<ColorManagerV1> colorManager;
|
|
||||||
std::unique_ptr<FifoManagerV1> fifoManager;
|
|
||||||
std::unique_ptr<PresentationTime> presentationTime;
|
|
||||||
+ std::unique_ptr<XdgActivation> xdgActivation;
|
|
||||||
};
|
|
||||||
|
|
||||||
void keyboardKeyPressed(quint32 key, quint32 time);
|
|
||||||
@@ -821,6 +851,7 @@ SecurityContextManagerV1 *waylandSecurityContextManagerV1();
|
|
||||||
ColorManagerV1 *colorManager();
|
|
||||||
FifoManagerV1 *fifoManager();
|
|
||||||
PresentationTime *presentationTime();
|
|
||||||
+XdgActivation *xdgActivation();
|
|
||||||
|
|
||||||
bool waitForWaylandSurface(Window *window);
|
|
||||||
|
|
||||||
diff --git a/autotests/integration/test_helpers.cpp b/autotests/integration/test_helpers.cpp
|
|
||||||
index 22380c947d3..e524f10826d 100644
|
|
||||||
--- a/autotests/integration/test_helpers.cpp
|
|
||||||
+++ b/autotests/integration/test_helpers.cpp
|
|
||||||
@@ -535,6 +535,11 @@ std::unique_ptr<Connection> Connection::setup(AdditionalWaylandInterfaces flags)
|
|
||||||
c->presentationTime = std::make_unique<PresentationTime>(*c->registry, name, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+ if (flags & AdditionalWaylandInterface::XdgActivation) {
|
|
||||||
+ if (interface == xdg_activation_v1_interface.name) {
|
|
||||||
+ c->xdgActivation = std::make_unique<XdgActivation>(*c->registry, name, version);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
});
|
|
||||||
|
|
||||||
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
|
|
||||||
@@ -665,6 +670,7 @@ Connection::~Connection()
|
|
||||||
colorManager.reset();
|
|
||||||
fifoManager.reset();
|
|
||||||
presentationTime.reset();
|
|
||||||
+ xdgActivation.reset();
|
|
||||||
|
|
||||||
delete queue; // Must be destroyed last
|
|
||||||
queue = nullptr;
|
|
||||||
@@ -796,6 +802,11 @@ PresentationTime *presentationTime()
|
|
||||||
return s_waylandConnection->presentationTime.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
+XdgActivation *xdgActivation()
|
|
||||||
+{
|
|
||||||
+ return s_waylandConnection->xdgActivation.get();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
bool waitForWaylandSurface(Window *window)
|
|
||||||
{
|
|
||||||
if (window->surface()) {
|
|
||||||
@@ -1817,6 +1828,45 @@ void WpPresentationFeedback::wp_presentation_feedback_discarded()
|
|
||||||
Q_EMIT discarded();
|
|
||||||
}
|
|
||||||
|
|
||||||
+XdgActivationToken::XdgActivationToken(::xdg_activation_token_v1 *object)
|
|
||||||
+ : QtWayland::xdg_activation_token_v1(object)
|
|
||||||
+{
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+XdgActivationToken::~XdgActivationToken()
|
|
||||||
+{
|
|
||||||
+ destroy();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+QString XdgActivationToken::commitAndWait()
|
|
||||||
+{
|
|
||||||
+ QSignalSpy received(this, &XdgActivationToken::tokenReceived);
|
|
||||||
+ commit();
|
|
||||||
+ received.wait();
|
|
||||||
+ return m_token;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void XdgActivationToken::xdg_activation_token_v1_done(const QString &token)
|
|
||||||
+{
|
|
||||||
+ m_token = token;
|
|
||||||
+ Q_EMIT tokenReceived();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+XdgActivation::XdgActivation(::wl_registry *registry, uint32_t id, int version)
|
|
||||||
+ : QtWayland::xdg_activation_v1(registry, id, version)
|
|
||||||
+{
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+XdgActivation::~XdgActivation()
|
|
||||||
+{
|
|
||||||
+ destroy();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+std::unique_ptr<XdgActivationToken> XdgActivation::createToken()
|
|
||||||
+{
|
|
||||||
+ return std::make_unique<XdgActivationToken>(get_activation_token());
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
void keyboardKeyPressed(quint32 key, quint32 time)
|
|
||||||
{
|
|
||||||
auto virtualKeyboard = static_cast<WaylandTestApplication *>(kwinApp())->virtualKeyboard();
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
From dc692e89f101a47b9049b1f6ae4cc3cebef46edb Mon Sep 17 00:00:00 2001
|
|
||||||
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
||||||
Date: Tue, 12 Aug 2025 15:59:16 +0200
|
|
||||||
Subject: [PATCH] xdgactivation: clear activation feedback if no token is
|
|
||||||
provided too
|
|
||||||
|
|
||||||
If the window is activated, the user expectation is that feedback stops. The underlying
|
|
||||||
reason for why it's activated doesn't matter.
|
|
||||||
---
|
|
||||||
src/xdgactivationv1.cpp | 12 ++++++++++--
|
|
||||||
src/xdgactivationv1.h | 3 ++-
|
|
||||||
2 files changed, 12 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/xdgactivationv1.cpp b/src/xdgactivationv1.cpp
|
|
||||||
index 360ca9b8743..567b792025f 100644
|
|
||||||
--- a/src/xdgactivationv1.cpp
|
|
||||||
+++ b/src/xdgactivationv1.cpp
|
|
||||||
@@ -33,6 +33,13 @@ static bool isPrivilegedInWindowManagement(const ClientConnection *client)
|
|
||||||
XdgActivationV1Integration::XdgActivationV1Integration(XdgActivationV1Interface *activation, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
+ connect(Workspace::self(), &Workspace::windowActivated, this, [this](Window *window) {
|
|
||||||
+ if (!m_activation || !window || m_lastTokenAppId != window->desktopFileName()) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ clearFeedback();
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
activation->setActivationTokenCreator([this](ClientConnection *client, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId) -> QString {
|
|
||||||
Q_ASSERT(client); // Should always be available as it's coming straight from the wayland implementation
|
|
||||||
return requestToken(isPrivilegedInWindowManagement(client), surface, serial, seat, appId);
|
|
||||||
@@ -64,6 +71,7 @@ QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInter
|
|
||||||
}
|
|
||||||
if (showNotify) {
|
|
||||||
m_lastToken = newToken;
|
|
||||||
+ m_lastTokenAppId = appId;
|
|
||||||
m_activation = waylandServer()->plasmaActivationFeedback()->createActivation(appId);
|
|
||||||
}
|
|
||||||
if (isPrivileged && workspace()->activeWindow()) {
|
|
||||||
@@ -95,10 +103,10 @@ void XdgActivationV1Integration::activateSurface(SurfaceInterface *surface, cons
|
|
||||||
} else {
|
|
||||||
window->setActivationToken(token);
|
|
||||||
}
|
|
||||||
- clear();
|
|
||||||
+ clearFeedback();
|
|
||||||
}
|
|
||||||
|
|
||||||
-void XdgActivationV1Integration::clear()
|
|
||||||
+void XdgActivationV1Integration::clearFeedback()
|
|
||||||
{
|
|
||||||
if (m_activation) {
|
|
||||||
Q_EMIT effects->startupRemoved(m_lastToken);
|
|
||||||
diff --git a/src/xdgactivationv1.h b/src/xdgactivationv1.h
|
|
||||||
index 77d21856095..ad007c088b6 100644
|
|
||||||
--- a/src/xdgactivationv1.h
|
|
||||||
+++ b/src/xdgactivationv1.h
|
|
||||||
@@ -36,9 +36,10 @@ public:
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString requestToken(bool isPrivileged, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId);
|
|
||||||
- void clear();
|
|
||||||
+ void clearFeedback();
|
|
||||||
|
|
||||||
QString m_lastToken;
|
|
||||||
+ QString m_lastTokenAppId;
|
|
||||||
std::unique_ptr<PlasmaWindowActivationInterface> m_activation;
|
|
||||||
};
|
|
||||||
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
From bab7c4a4c5e2fb7be83ba94fdd0da7fd196654fa Mon Sep 17 00:00:00 2001
|
|
||||||
From: David Redondo <kde@david-redondo.de>
|
|
||||||
Date: Thu, 25 Sep 2025 12:00:51 +0200
|
|
||||||
Subject: [PATCH] Make sure XdgToplevelWindow always has an icon
|
|
||||||
|
|
||||||
If the client never called set_app_id and did not set a custom
|
|
||||||
icon the window would end up without an icon at all. This ensures
|
|
||||||
that all windows always have at least the default icon.
|
|
||||||
---
|
|
||||||
autotests/integration/xdgshellwindow_test.cpp | 13 +++++++++++--
|
|
||||||
src/xdgshellwindow.cpp | 1 +
|
|
||||||
2 files changed, 12 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/autotests/integration/xdgshellwindow_test.cpp b/autotests/integration/xdgshellwindow_test.cpp
|
|
||||||
index 0903043808d..a3658ee62e7 100644
|
|
||||||
--- a/autotests/integration/xdgshellwindow_test.cpp
|
|
||||||
+++ b/autotests/integration/xdgshellwindow_test.cpp
|
|
||||||
@@ -683,16 +683,25 @@ void TestXdgShellWindow::testDesktopFileName()
|
|
||||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
|
||||||
// only xdg-shell as ShellSurface misses the setter
|
|
||||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
|
||||||
- shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
|
|
||||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
|
||||||
QVERIFY(window);
|
|
||||||
+
|
|
||||||
+ // A client that never call set_app_id still gets the default icon
|
|
||||||
+ QCOMPARE(window->desktopFileName(), QString());
|
|
||||||
+ QVERIFY(window->resourceClass().startsWith("testXdgShellWindow"));
|
|
||||||
+ QVERIFY(window->resourceName().startsWith("testXdgShellWindow"));
|
|
||||||
+ QCOMPARE(window->icon().name(), QStringLiteral("wayland"));
|
|
||||||
+
|
|
||||||
+ QSignalSpy desktopFileNameChangedSpy(window, &Window::desktopFileNameChanged);
|
|
||||||
+
|
|
||||||
+ shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
|
|
||||||
+ QVERIFY(desktopFileNameChangedSpy.wait());
|
|
||||||
QCOMPARE(window->desktopFileName(), QStringLiteral("org.kde.foo"));
|
|
||||||
QCOMPARE(window->resourceClass(), QStringLiteral("org.kde.foo"));
|
|
||||||
QVERIFY(window->resourceName().startsWith("testXdgShellWindow"));
|
|
||||||
// the desktop file does not exist, so icon should be generic Wayland
|
|
||||||
QCOMPARE(window->icon().name(), QStringLiteral("wayland"));
|
|
||||||
|
|
||||||
- QSignalSpy desktopFileNameChangedSpy(window, &Window::desktopFileNameChanged);
|
|
||||||
QSignalSpy iconChangedSpy(window, &Window::iconChanged);
|
|
||||||
shellSurface->set_app_id(QStringLiteral("org.kde.bar"));
|
|
||||||
QVERIFY(desktopFileNameChangedSpy.wait());
|
|
||||||
diff --git a/src/xdgshellwindow.cpp b/src/xdgshellwindow.cpp
|
|
||||||
index f585d3aef95..092c284b8c2 100644
|
|
||||||
--- a/src/xdgshellwindow.cpp
|
|
||||||
+++ b/src/xdgshellwindow.cpp
|
|
||||||
@@ -1426,6 +1426,7 @@ void XdgToplevelWindow::initialize()
|
|
||||||
scheduleConfigure();
|
|
||||||
updateColorScheme();
|
|
||||||
updateCapabilities();
|
|
||||||
+ updateIcon();
|
|
||||||
setupWindowManagementInterface();
|
|
||||||
|
|
||||||
m_isInitialized = true;
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
Plasma 6.5.0:
|
|
||||||
|
|
||||||
Pr 439 https://invent.kde.org/plasma/plasma-nm/-/merge_requests/439
|
|
||||||
Pr 442 https://invent.kde.org/plasma/plasma-nm/-/merge_requests/442
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
From ac32824009397188131aab4fcc3394fcc4551c5c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Tue, 10 Jun 2025 15:43:13 -0600
|
|
||||||
Subject: [PATCH 1/2] applet: handle some more states
|
|
||||||
|
|
||||||
1. Looking for Wi-Fi networks but haven't found any yet
|
|
||||||
2. NetworkManager isn't running
|
|
||||||
|
|
||||||
FEATURE: 485982
|
|
||||||
BUG: 462454
|
|
||||||
FIXED-IN: 6.5.0
|
|
||||||
---
|
|
||||||
applet/contents/ui/ConnectionListPage.qml | 15 ++++++++++++---
|
|
||||||
applet/contents/ui/Toolbar.qml | 3 ++-
|
|
||||||
applet/metadata.json | 1 -
|
|
||||||
libs/networkstatus.h | 3 +--
|
|
||||||
4 files changed, 15 insertions(+), 7 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/applet/contents/ui/ConnectionListPage.qml b/applet/contents/ui/ConnectionListPage.qml
|
|
||||||
index b9a949d87..056f52de3 100644
|
|
||||||
--- a/applet/contents/ui/ConnectionListPage.qml
|
|
||||||
+++ b/applet/contents/ui/ConnectionListPage.qml
|
|
||||||
@@ -101,9 +101,12 @@ ColumnLayout {
|
|
||||||
if (toolbar.displayplaneModeMessage) {
|
|
||||||
return "network-flightmode-on"
|
|
||||||
}
|
|
||||||
- if (toolbar.displayWifiMessage) {
|
|
||||||
+ if (toolbar.displayWifiOffMessage) {
|
|
||||||
return "network-wireless-off"
|
|
||||||
}
|
|
||||||
+ if (toolbar.displayWifiConnectingMessage) {
|
|
||||||
+ return "view-refresh-symbolic"
|
|
||||||
+ }
|
|
||||||
if (toolbar.displayWwanMessage) {
|
|
||||||
return "network-mobile-off"
|
|
||||||
}
|
|
||||||
@@ -113,19 +116,25 @@ ColumnLayout {
|
|
||||||
if (toolbar.displayplaneModeMessage) {
|
|
||||||
return i18n("Airplane mode is enabled")
|
|
||||||
}
|
|
||||||
- if (toolbar.displayWifiMessage) {
|
|
||||||
+ if (toolbar.displayWifiOffMessage) {
|
|
||||||
if (toolbar.displayWwanMessage) {
|
|
||||||
return i18n("Wireless and mobile networks are deactivated")
|
|
||||||
}
|
|
||||||
return i18n("Wireless is deactivated")
|
|
||||||
}
|
|
||||||
+ if (toolbar.displayWifiConnectingMessage) {
|
|
||||||
+ return i18n("Looking for wireless networks")
|
|
||||||
+ }
|
|
||||||
if (toolbar.displayWwanMessage) {
|
|
||||||
return i18n("Mobile network is deactivated")
|
|
||||||
}
|
|
||||||
if (toolbar.searchTextField.text.length > 0) {
|
|
||||||
return i18n("No matches")
|
|
||||||
}
|
|
||||||
- return i18n("No available connections")
|
|
||||||
+ if (connectionListPage.nmStatus.connectivity === NMQt.NetworkManager.Full) {
|
|
||||||
+ return i18n("No available connections")
|
|
||||||
+ }
|
|
||||||
+ return nmStatus.checkUnknownReason()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diff --git a/applet/contents/ui/Toolbar.qml b/applet/contents/ui/Toolbar.qml
|
|
||||||
index 87b3f1654..0d382109b 100644
|
|
||||||
--- a/applet/contents/ui/Toolbar.qml
|
|
||||||
+++ b/applet/contents/ui/Toolbar.qml
|
|
||||||
@@ -16,7 +16,8 @@ import org.kde.kcmutils as KCMUtils
|
|
||||||
RowLayout {
|
|
||||||
id: toolbar
|
|
||||||
|
|
||||||
- readonly property var displayWifiMessage: !wifiSwitchButton.checked && wifiSwitchButton.visible
|
|
||||||
+ readonly property var displayWifiOffMessage: !wifiSwitchButton.checked && wifiSwitchButton.visible
|
|
||||||
+ readonly property var displayWifiConnectingMessage: wifiSwitchButton.checked && wifiSwitchButton.visible
|
|
||||||
readonly property var displayWwanMessage: !wwanSwitchButton.checked && wwanSwitchButton.visible
|
|
||||||
readonly property var displayplaneModeMessage: planeModeSwitchButton.checked && planeModeSwitchButton.visible
|
|
||||||
|
|
||||||
diff --git a/applet/metadata.json b/applet/metadata.json
|
|
||||||
index 0ca36a2b7..2f12891b5 100644
|
|
||||||
--- a/applet/metadata.json
|
|
||||||
+++ b/applet/metadata.json
|
|
||||||
@@ -210,6 +210,5 @@
|
|
||||||
"X-KDE-Keywords[zh_CN]": "network,internet,ethernet,wireless,wifi,wlan,vpn,wangluo,hulianwang,yitaiwang,juyuwang,wuxianwangluo,xunizhuanyongwangluo,网络,互联网,以太网,局域网,无线网络,虚拟专用网络",
|
|
||||||
"X-KDE-Keywords[zh_TW]": "網路,網絡,網際網路,乙太網路,以太網路,無線",
|
|
||||||
"X-Plasma-API-Minimum-Version": "6.0",
|
|
||||||
- "X-Plasma-DBusActivationService": "org.freedesktop.NetworkManager",
|
|
||||||
"X-Plasma-NotificationAreaCategory": "Hardware"
|
|
||||||
}
|
|
||||||
diff --git a/libs/networkstatus.h b/libs/networkstatus.h
|
|
||||||
index aa88bf7c5..a885f04dd 100644
|
|
||||||
--- a/libs/networkstatus.h
|
|
||||||
+++ b/libs/networkstatus.h
|
|
||||||
@@ -59,6 +59,7 @@ public:
|
|
||||||
QString activeConnections() const;
|
|
||||||
QString networkStatus() const;
|
|
||||||
NetworkManager::Connectivity connectivity() const;
|
|
||||||
+ Q_INVOKABLE QString checkUnknownReason() const;
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void activeConnectionsChanged();
|
|
||||||
@@ -75,8 +76,6 @@ private:
|
|
||||||
QString m_activeConnections;
|
|
||||||
QString m_networkStatus;
|
|
||||||
NetworkManager::Connectivity m_connectivity = NetworkManager::UnknownConnectivity;
|
|
||||||
-
|
|
||||||
- QString checkUnknownReason() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PLAMA_NM_NETWORK_STATUS_H
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From a45534cd9c19f267075fe4261087045f1f3a9318 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Wed, 11 Jun 2025 10:14:40 -0600
|
|
||||||
Subject: [PATCH 2/2] Rephrase "NetworkManager not running" message to be more
|
|
||||||
user-friendly
|
|
||||||
|
|
||||||
---
|
|
||||||
libs/networkstatus.cpp | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/libs/networkstatus.cpp b/libs/networkstatus.cpp
|
|
||||||
index c89ca9fca..b778eedda 100644
|
|
||||||
--- a/libs/networkstatus.cpp
|
|
||||||
+++ b/libs/networkstatus.cpp
|
|
||||||
@@ -253,7 +253,7 @@ QString NetworkStatus::checkUnknownReason() const
|
|
||||||
{
|
|
||||||
// Check if NetworkManager is running.
|
|
||||||
if (!QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral(NM_DBUS_INTERFACE))) {
|
|
||||||
- return i18n("NetworkManager not running");
|
|
||||||
+ return i18nc("@info:status", "NetworkManager service is not running");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for compatible NetworkManager version.
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
From 63a9d544dc4f5160f28e5f3203a2c0a5e639e5ac Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Fri, 13 Jun 2025 12:24:22 -0600
|
|
||||||
Subject: [PATCH] applet: use standard section headers
|
|
||||||
|
|
||||||
We have them, so we might as well use them. This lets us get rid of a
|
|
||||||
bunch of complex and fragile code that's responsible for the existing
|
|
||||||
separator.
|
|
||||||
---
|
|
||||||
applet/contents/ui/ConnectionItem.qml | 14 -----
|
|
||||||
applet/contents/ui/ConnectionListPage.qml | 10 ++--
|
|
||||||
applet/contents/ui/ListItem.qml | 64 -----------------------
|
|
||||||
libs/models/networkmodelitem.cpp | 8 ++-
|
|
||||||
4 files changed, 12 insertions(+), 84 deletions(-)
|
|
||||||
delete mode 100644 applet/contents/ui/ListItem.qml
|
|
||||||
|
|
||||||
diff --git a/applet/contents/ui/ConnectionItem.qml b/applet/contents/ui/ConnectionItem.qml
|
|
||||||
index ed154e633..5bb5c530c 100644
|
|
||||||
--- a/applet/contents/ui/ConnectionItem.qml
|
|
||||||
+++ b/applet/contents/ui/ConnectionItem.qml
|
|
||||||
@@ -321,20 +321,6 @@ PlasmaExtras.ExpandableListItem {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- onDeactivatedChanged: {
|
|
||||||
- /* Separator is part of section, which is visible only when available connections exist. Need to determine
|
|
||||||
- if there is a connection in use, to show Separator. Otherwise need to hide it from the top of the list.
|
|
||||||
- Connections in use are always on top, only need to check the first one. */
|
|
||||||
- if (appletProxyModel.data(appletProxyModel.index(0, 0), PlasmaNM.NetworkModel.SectionRole) !== "Available connections") {
|
|
||||||
- if (connectionView.showSeparator != true) {
|
|
||||||
- connectionView.showSeparator = true
|
|
||||||
- }
|
|
||||||
- return
|
|
||||||
- }
|
|
||||||
- connectionView.showSeparator = false
|
|
||||||
- return
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
onItemCollapsed: {
|
|
||||||
connectionItem.customExpandedViewContent = detailsComponent;
|
|
||||||
setDelayModelUpdates(false);
|
|
||||||
diff --git a/applet/contents/ui/ConnectionListPage.qml b/applet/contents/ui/ConnectionListPage.qml
|
|
||||||
index b62da252c..d8ebd2c54 100644
|
|
||||||
--- a/applet/contents/ui/ConnectionListPage.qml
|
|
||||||
+++ b/applet/contents/ui/ConnectionListPage.qml
|
|
||||||
@@ -8,7 +8,7 @@ import QtQuick 2.15
|
|
||||||
import QtQuick.Layouts 1.2
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
|
||||||
import org.kde.kirigami 2.20 as Kirigami
|
|
||||||
-import org.kde.plasma.extras 2.0 as PlasmaExtras
|
|
||||||
+import org.kde.plasma.extras as PlasmaExtras
|
|
||||||
import org.kde.plasma.networkmanagement as PlasmaNM
|
|
||||||
import org.kde.networkmanager as NMQt
|
|
||||||
|
|
||||||
@@ -55,7 +55,6 @@ ColumnLayout {
|
|
||||||
id: connectionView
|
|
||||||
|
|
||||||
property int currentVisibleButtonIndex: -1
|
|
||||||
- property bool showSeparator: false
|
|
||||||
|
|
||||||
Keys.onDownPressed: event => {
|
|
||||||
connectionView.incrementCurrentIndex();
|
|
||||||
@@ -80,9 +79,10 @@ ColumnLayout {
|
|
||||||
model: appletProxyModel
|
|
||||||
currentIndex: -1
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
- section.property: showSeparator ? "Section" : ""
|
|
||||||
- section.delegate: ListItem {
|
|
||||||
- separator: true
|
|
||||||
+ section.property: "Section"
|
|
||||||
+ section.delegate: PlasmaExtras.ListSectionHeader {
|
|
||||||
+ width: connectionView.width - connectionView.leftMargin - connectionView.rightMargin
|
|
||||||
+ text: section
|
|
||||||
}
|
|
||||||
highlight: PlasmaExtras.Highlight { }
|
|
||||||
highlightMoveDuration: Kirigami.Units.shortDuration
|
|
||||||
diff --git a/applet/contents/ui/ListItem.qml b/applet/contents/ui/ListItem.qml
|
|
||||||
deleted file mode 100644
|
|
||||||
index bfe1390d1..000000000
|
|
||||||
--- a/applet/contents/ui/ListItem.qml
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,64 +0,0 @@
|
|
||||||
-/*
|
|
||||||
- SPDX-FileCopyrightText: 2010 Marco Martin <notmart@gmail.com>
|
|
||||||
- SPDX-FileCopyrightText: 2016 Jan Grulich <jgrulich@redhat.com>
|
|
||||||
- SPDX-FileCopyrightText: 2020 George Vogiatzis <gvgeo@protonmail.com>
|
|
||||||
-
|
|
||||||
- SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
-*/
|
|
||||||
-
|
|
||||||
-import QtQuick 2.1
|
|
||||||
-import org.kde.kirigami 2.20 as Kirigami
|
|
||||||
-import org.kde.ksvg 1.0 as KSvg
|
|
||||||
-
|
|
||||||
-/**
|
|
||||||
- * Ignores the theme's listItem margins, and uses custom highlight(pressed) area.
|
|
||||||
- * Could break some themes but the majority look fine.
|
|
||||||
- * Also includes a separator to be used in sections.
|
|
||||||
- */
|
|
||||||
-MouseArea {
|
|
||||||
- id: listItem
|
|
||||||
-
|
|
||||||
- property bool checked: false
|
|
||||||
- property bool separator: false
|
|
||||||
- property rect highlightRect: Qt.rect(0, 0, width, height)
|
|
||||||
-
|
|
||||||
- width: parent.width
|
|
||||||
-
|
|
||||||
- // Sections have spacing above but not below. Will use 2 of them below.
|
|
||||||
- height: separator ? separatorLine.height + Kirigami.Units.smallSpacing * 3 : parent.height
|
|
||||||
- hoverEnabled: true
|
|
||||||
-
|
|
||||||
- KSvg.SvgItem {
|
|
||||||
- id: separatorLine
|
|
||||||
- anchors {
|
|
||||||
- horizontalCenter: parent.horizontalCenter
|
|
||||||
- top: parent.top
|
|
||||||
- topMargin: Kirigami.Units.smallSpacing
|
|
||||||
- }
|
|
||||||
- imagePath: "widgets/line"
|
|
||||||
- elementId: "horizontal-line"
|
|
||||||
- width: parent.width - Kirigami.Units.gridUnit * 2
|
|
||||||
- visible: listItem.separator
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- KSvg.FrameSvgItem {
|
|
||||||
- id: background
|
|
||||||
- imagePath: "widgets/listitem"
|
|
||||||
- prefix: "normal"
|
|
||||||
- anchors.fill: parent
|
|
||||||
- visible: listItem.separator ? false : true
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- KSvg.FrameSvgItem {
|
|
||||||
- id: pressed
|
|
||||||
- imagePath: "widgets/listitem"
|
|
||||||
- prefix: "pressed"
|
|
||||||
- opacity: listItem.checked ? 1 : 0
|
|
||||||
- Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } }
|
|
||||||
-
|
|
||||||
- x: listItem.highlightRect.x
|
|
||||||
- y: listItem.highlightRect.y
|
|
||||||
- height: listItem.highlightRect.height
|
|
||||||
- width: listItem.highlightRect.width
|
|
||||||
- }
|
|
||||||
-}
|
|
||||||
diff --git a/libs/models/networkmodelitem.cpp b/libs/models/networkmodelitem.cpp
|
|
||||||
index b92ec0df2..4d7cbd77d 100644
|
|
||||||
--- a/libs/models/networkmodelitem.cpp
|
|
||||||
+++ b/libs/models/networkmodelitem.cpp
|
|
||||||
@@ -344,7 +344,13 @@ QString NetworkModelItem::originalName() const
|
|
||||||
QString NetworkModelItem::sectionType() const
|
|
||||||
{
|
|
||||||
if (m_connectionState == NetworkManager::ActiveConnection::Deactivated) {
|
|
||||||
- return QStringLiteral("Available connections");
|
|
||||||
+ return i18nc("@title:column header for list of available network connections", "Available");
|
|
||||||
+ // clang-format off
|
|
||||||
+ } else if (m_connectionState == NetworkManager::ActiveConnection::Activating
|
|
||||||
+ || m_connectionState == NetworkManager::ActiveConnection::Activated
|
|
||||||
+ || m_connectionState == NetworkManager::ActiveConnection::Deactivating) {
|
|
||||||
+ // clang-format on
|
|
||||||
+ return i18nc("@title:column header for list of connected network connections", "Connected");
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,248 +0,0 @@
|
||||||
From 8202ba92b610c691b8bc6bab8ad5a1c3b9ac73da Mon Sep 17 00:00:00 2001
|
|
||||||
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
Date: Wed, 25 Jun 2025 17:29:19 +0300
|
|
||||||
Subject: [PATCH] startkde: Drop ready stage
|
|
||||||
|
|
||||||
In order to hide the splash screen, it is sufficient just to see the
|
|
||||||
wallpaper. If more desktop environment components are loaded soon
|
|
||||||
afterwards, it is okay.
|
|
||||||
|
|
||||||
With systemd boot, the way the ready stage is integrated is also kind
|
|
||||||
of a hack.
|
|
||||||
---
|
|
||||||
appiumtests/CMakeLists.txt | 1 -
|
|
||||||
appiumtests/ksplash/CMakeLists.txt | 14 ----
|
|
||||||
appiumtests/ksplash/ksplashtest.py | 79 -------------------
|
|
||||||
ksplash/ksplashqml/splashapp.cpp | 5 +-
|
|
||||||
startkde/plasma-session/startup.cpp | 11 ---
|
|
||||||
startkde/plasma-session/startup.h | 1 -
|
|
||||||
startkde/systemd/CMakeLists.txt | 3 -
|
|
||||||
.../systemd/plasma-ksplash-ready.service.in | 10 ---
|
|
||||||
startkde/systemd/plasma-workspace.target | 1 -
|
|
||||||
9 files changed, 2 insertions(+), 123 deletions(-)
|
|
||||||
delete mode 100644 appiumtests/ksplash/CMakeLists.txt
|
|
||||||
delete mode 100755 appiumtests/ksplash/ksplashtest.py
|
|
||||||
delete mode 100644 startkde/systemd/plasma-ksplash-ready.service.in
|
|
||||||
|
|
||||||
diff --git a/appiumtests/CMakeLists.txt b/appiumtests/CMakeLists.txt
|
|
||||||
index 68d0b6895ba..22234aeb031 100644
|
|
||||||
--- a/appiumtests/CMakeLists.txt
|
|
||||||
+++ b/appiumtests/CMakeLists.txt
|
|
||||||
@@ -20,5 +20,4 @@ add_subdirectory(applets)
|
|
||||||
add_subdirectory(components_tests)
|
|
||||||
add_subdirectory(kcms)
|
|
||||||
add_subdirectory(krunner)
|
|
||||||
-add_subdirectory(ksplash)
|
|
||||||
add_subdirectory(wallpapers)
|
|
||||||
diff --git a/appiumtests/ksplash/CMakeLists.txt b/appiumtests/ksplash/CMakeLists.txt
|
|
||||||
deleted file mode 100644
|
|
||||||
index 3bea5174f5a..00000000000
|
|
||||||
--- a/appiumtests/ksplash/CMakeLists.txt
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,14 +0,0 @@
|
|
||||||
-# SPDX-FileCopyrightText: 2024 Fushan Wen <qydwhotmail@gmail.com>
|
|
||||||
-# SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
-
|
|
||||||
-add_test(
|
|
||||||
- NAME ksplashtest_wayland
|
|
||||||
- COMMAND sh -c "mkdir -p /tmp/appium/ksplashtest_wayland;dbus-launch selenium-webdriver-at-spi-run ${CMAKE_CURRENT_SOURCE_DIR}/ksplashtest.py --failfast"
|
|
||||||
-)
|
|
||||||
-set_tests_properties(ksplashtest_wayland PROPERTIES TIMEOUT 60 ENVIRONMENT "XDG_RUNTIME_DIR=/tmp/appium/ksplashtest_wayland;FLASK_PORT=5701")
|
|
||||||
-
|
|
||||||
-add_test(
|
|
||||||
- NAME ksplashtest_x11
|
|
||||||
- COMMAND sh -c "mkdir -p /tmp/appium/ksplashtest_x11;dbus-launch selenium-webdriver-at-spi-run ${CMAKE_CURRENT_SOURCE_DIR}/ksplashtest.py --failfast"
|
|
||||||
-)
|
|
||||||
-set_tests_properties(ksplashtest_x11 PROPERTIES TIMEOUT 60 ENVIRONMENT "XDG_RUNTIME_DIR=/tmp/appium/ksplashtest_x11;FLASK_PORT=5702;TEST_WITH_KWIN_WAYLAND=0")
|
|
||||||
diff --git a/appiumtests/ksplash/ksplashtest.py b/appiumtests/ksplash/ksplashtest.py
|
|
||||||
deleted file mode 100755
|
|
||||||
index b7ca43c0519..00000000000
|
|
||||||
--- a/appiumtests/ksplash/ksplashtest.py
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,79 +0,0 @@
|
|
||||||
-#!/usr/bin/env python3
|
|
||||||
-
|
|
||||||
-# SPDX-FileCopyrightText: 2024 Fushan Wen <qydwhotmail@gmail.com>
|
|
||||||
-# SPDX-License-Identifier: MIT
|
|
||||||
-
|
|
||||||
-# pylint: disable=too-many-arguments
|
|
||||||
-
|
|
||||||
-import os
|
|
||||||
-import subprocess
|
|
||||||
-import sys
|
|
||||||
-import time
|
|
||||||
-import unittest
|
|
||||||
-
|
|
||||||
-from appium import webdriver
|
|
||||||
-from appium.options.common.base import AppiumOptions
|
|
||||||
-from appium.webdriver.common.appiumby import AppiumBy
|
|
||||||
-from gi.repository import Gio, GLib
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-class KSplashTest(unittest.TestCase):
|
|
||||||
-
|
|
||||||
- driver: webdriver.Remote
|
|
||||||
-
|
|
||||||
- @classmethod
|
|
||||||
- def setUpClass(cls) -> None:
|
|
||||||
- options = AppiumOptions()
|
|
||||||
- options.set_capability("app", "ksplashqml --window")
|
|
||||||
- options.set_capability("environ", {
|
|
||||||
- "LC_ALL": "en_US.UTF-8",
|
|
||||||
- "QT_FATAL_WARNINGS": "1",
|
|
||||||
- "QT_LOGGING_RULES": "qt.accessibility.atspi.warning=false;kf.plasma.core.warning=false;kf.windowsystem.warning=false;kf.kirigami.platform.warning=false;org.kde.plasma.ksplashqml.debug=true",
|
|
||||||
- })
|
|
||||||
- options.set_capability("timeouts", {'implicit': 10000})
|
|
||||||
- cls.driver = webdriver.Remote(command_executor=f'http://127.0.0.1:{os.getenv("FLASK_PORT", "4723")}', options=options)
|
|
||||||
-
|
|
||||||
- def tearDown(self) -> None:
|
|
||||||
- """
|
|
||||||
- Take screenshot when the current test fails
|
|
||||||
- """
|
|
||||||
- if not self._outcome.result.wasSuccessful():
|
|
||||||
- self.driver.get_screenshot_as_file(f"failed_test_shot_ksplash_#{self.id()}.png")
|
|
||||||
-
|
|
||||||
- def test_1_bug494840_setStage(self) -> None:
|
|
||||||
- """
|
|
||||||
- Checks if the setStage method is ever called after starting plasma-ksplash-ready.service.
|
|
||||||
- """
|
|
||||||
- if os.getenv("TEST_WITH_KWIN_WAYLAND", "1") == "0":
|
|
||||||
- stages = ("wm", "kcminit", "ksmserver", "startPlasma", "desktop")
|
|
||||||
- else:
|
|
||||||
- stages = ("kcminit", "ksmserver", "startPlasma", "desktop")
|
|
||||||
-
|
|
||||||
- session_bus = Gio.bus_get_sync(Gio.BusType.SESSION)
|
|
||||||
- for stage in stages:
|
|
||||||
- message: Gio.DBusMessage = Gio.DBusMessage.new_method_call("org.kde.KSplash", "/KSplash", "org.kde.KSplash", "setStage")
|
|
||||||
- message.set_body(GLib.Variant("(s)", [stage]))
|
|
||||||
- session_bus.send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE, 3000)
|
|
||||||
-
|
|
||||||
- self.driver.find_element(AppiumBy.NAME, "Plasma made by KDE")
|
|
||||||
-
|
|
||||||
- with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir, "startkde", "systemd", "plasma-ksplash-ready.service.in"), encoding="utf-8") as handler:
|
|
||||||
- for line in handler:
|
|
||||||
- if line.startswith("ExecStart="):
|
|
||||||
- command = line.removeprefix("ExecStart=").strip().split(" ")
|
|
||||||
- subprocess.check_call(command, stdout=sys.stderr, stderr=sys.stderr)
|
|
||||||
- break
|
|
||||||
-
|
|
||||||
- success = False
|
|
||||||
- for _ in range(10):
|
|
||||||
- try:
|
|
||||||
- subprocess.check_call(["pidof", "ksplashqml"])
|
|
||||||
- except subprocess.CalledProcessError:
|
|
||||||
- success = True
|
|
||||||
- break
|
|
||||||
- time.sleep(1)
|
|
||||||
- self.assertTrue(success)
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-if __name__ == '__main__':
|
|
||||||
- unittest.main()
|
|
||||||
diff --git a/ksplash/ksplashqml/splashapp.cpp b/ksplash/ksplashqml/splashapp.cpp
|
|
||||||
index b60a58724be..2262503b1c3 100644
|
|
||||||
--- a/ksplash/ksplashqml/splashapp.cpp
|
|
||||||
+++ b/ksplash/ksplashqml/splashapp.cpp
|
|
||||||
@@ -26,13 +26,12 @@
|
|
||||||
#define TEST_STEP_INTERVAL 2000
|
|
||||||
|
|
||||||
/**
|
|
||||||
- * There are 7 stages in ksplash
|
|
||||||
+ * There are 6 stages in ksplash
|
|
||||||
* - initial (from this class)
|
|
||||||
* - startPlasma (from startplasma)
|
|
||||||
* - kcminit
|
|
||||||
* - ksmserver
|
|
||||||
* - wm (for X11 from KWin, for Wayland from this class)
|
|
||||||
- * - ready (from plasma-session startup)
|
|
||||||
* - desktop (from shellcorona)
|
|
||||||
*/
|
|
||||||
|
|
||||||
@@ -114,7 +113,7 @@ void SplashApp::setStage(const QString &stage)
|
|
||||||
void SplashApp::setStage(int stage)
|
|
||||||
{
|
|
||||||
m_stage = stage;
|
|
||||||
- if (m_stage == 7) {
|
|
||||||
+ if (m_stage == 6) {
|
|
||||||
QGuiApplication::exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
for (SplashWindow *w : std::as_const(m_windows)) {
|
|
||||||
diff --git a/startkde/plasma-session/startup.cpp b/startkde/plasma-session/startup.cpp
|
|
||||||
index a731c7b2791..0567e00881f 100644
|
|
||||||
--- a/startkde/plasma-session/startup.cpp
|
|
||||||
+++ b/startkde/plasma-session/startup.cpp
|
|
||||||
@@ -206,20 +206,9 @@ Startup::Startup(QObject *parent)
|
|
||||||
// app will be closed when all KJobs finish thanks to the QEventLoopLocker in each KJob
|
|
||||||
}
|
|
||||||
|
|
||||||
-void Startup::upAndRunning(const QString &msg)
|
|
||||||
-{
|
|
||||||
- QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"),
|
|
||||||
- QStringLiteral("/KSplash"),
|
|
||||||
- QStringLiteral("org.kde.KSplash"),
|
|
||||||
- QStringLiteral("setStage"));
|
|
||||||
- ksplashProgressMessage.setArguments(QList<QVariant>() << msg);
|
|
||||||
- QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage);
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
void Startup::finishStartup()
|
|
||||||
{
|
|
||||||
qCDebug(PLASMA_SESSION) << "Finished";
|
|
||||||
- upAndRunning(QStringLiteral("ready"));
|
|
||||||
|
|
||||||
playStartupSound();
|
|
||||||
new SessionTrack(m_processes);
|
|
||||||
diff --git a/startkde/plasma-session/startup.h b/startkde/plasma-session/startup.h
|
|
||||||
index 6ef4fee9bdd..876a1439fce 100644
|
|
||||||
--- a/startkde/plasma-session/startup.h
|
|
||||||
+++ b/startkde/plasma-session/startup.h
|
|
||||||
@@ -20,7 +20,6 @@ class Startup : public QObject
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Startup(QObject *parent);
|
|
||||||
- void upAndRunning(const QString &msg);
|
|
||||||
void finishStartup();
|
|
||||||
|
|
||||||
static Startup *self()
|
|
||||||
diff --git a/startkde/systemd/CMakeLists.txt b/startkde/systemd/CMakeLists.txt
|
|
||||||
index 2f5d30e8456..c3455ebae81 100644
|
|
||||||
--- a/startkde/systemd/CMakeLists.txt
|
|
||||||
+++ b/startkde/systemd/CMakeLists.txt
|
|
||||||
@@ -1,6 +1,3 @@
|
|
||||||
-ecm_install_configured_files(INPUT plasma-ksplash-ready.service.in @ONLY
|
|
||||||
- DESTINATION ${KDE_INSTALL_SYSTEMDUSERUNITDIR})
|
|
||||||
-
|
|
||||||
install(FILES plasma-core.target DESTINATION ${KDE_INSTALL_SYSTEMDUSERUNITDIR})
|
|
||||||
install(FILES plasma-workspace.target DESTINATION ${KDE_INSTALL_SYSTEMDUSERUNITDIR})
|
|
||||||
install(FILES plasma-workspace-wayland.target DESTINATION ${KDE_INSTALL_SYSTEMDUSERUNITDIR})
|
|
||||||
diff --git a/startkde/systemd/plasma-ksplash-ready.service.in b/startkde/systemd/plasma-ksplash-ready.service.in
|
|
||||||
deleted file mode 100644
|
|
||||||
index 1e903130a96..00000000000
|
|
||||||
--- a/startkde/systemd/plasma-ksplash-ready.service.in
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,10 +0,0 @@
|
|
||||||
-[Unit]
|
|
||||||
-Description=KSplash "ready" Stage
|
|
||||||
-Wants=plasma-core.target
|
|
||||||
-After=plasma-core.target
|
|
||||||
-PartOf=graphical-session.target
|
|
||||||
-
|
|
||||||
-[Service]
|
|
||||||
-Type=oneshot
|
|
||||||
-ExecStart=dbus-send --session --reply-timeout=1 --type=method_call --dest=org.kde.KSplash /KSplash org.kde.KSplash.setStage string:ready
|
|
||||||
-Slice=session.slice
|
|
||||||
diff --git a/startkde/systemd/plasma-workspace.target b/startkde/systemd/plasma-workspace.target
|
|
||||||
index a9113f49112..4cc8d9330c3 100644
|
|
||||||
--- a/startkde/systemd/plasma-workspace.target
|
|
||||||
+++ b/startkde/systemd/plasma-workspace.target
|
|
||||||
@@ -6,7 +6,6 @@ Wants=plasma-restoresession.service
|
|
||||||
Wants=plasma-xembedsniproxy.service
|
|
||||||
Wants=plasma-gmenudbusmenuproxy.service
|
|
||||||
Wants=plasma-powerdevil.service
|
|
||||||
-Wants=plasma-ksplash-ready.service
|
|
||||||
Wants=plasma-polkit-agent.service
|
|
||||||
Wants=kde-baloo.service
|
|
||||||
Wants=plasma-foreground-booster.service
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,21 +1,3 @@
|
||||||
Plasma 6.5.0:
|
|
||||||
|
|
||||||
Pr 5589 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5589
|
|
||||||
Pr 5626 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5626
|
|
||||||
Pr 5627 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5627
|
|
||||||
Commit 8202ba92 https://invent.kde.org/plasma/plasma-workspace/-/commit/8202ba92b610c691b8bc6bab8ad5a1c3b9ac73da
|
|
||||||
Part of https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5628, the other part got cherry picked on 6.4.2
|
|
||||||
Pr 5657 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5657
|
|
||||||
Pr 5734 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5734
|
|
||||||
Depends on Pr 5609 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5609
|
|
||||||
Pr 5746 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5746
|
|
||||||
Pr 5782 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5782
|
|
||||||
Pr 5678 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5678
|
|
||||||
Depends on Pr 5673 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5673
|
|
||||||
Pr 5788 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5788
|
|
||||||
Allows compiling on gcc 14 after applying pr 5678
|
|
||||||
|
|
||||||
Plasma 6.6.0:
|
Plasma 6.6.0:
|
||||||
|
|
||||||
Pr 5818 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5818
|
|
||||||
Pr 5816 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5816
|
Pr 5816 https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/5816
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
From 2278398309c68ce401a8e35b193fca3782391a4a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Thu, 12 Jun 2025 09:15:15 -0600
|
|
||||||
Subject: [PATCH] applets/devicenotifier: use standard section header
|
|
||||||
|
|
||||||
No need for a custom header here when we have a standard one.
|
|
||||||
|
|
||||||
CCBUG: 442724
|
|
||||||
---
|
|
||||||
.../package/contents/ui/FullRepresentation.qml | 10 ++--------
|
|
||||||
1 file changed, 2 insertions(+), 8 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/applets/devicenotifier/package/contents/ui/FullRepresentation.qml b/applets/devicenotifier/package/contents/ui/FullRepresentation.qml
|
|
||||||
index 967223e0a1a..a0c28df45fe 100644
|
|
||||||
--- a/applets/devicenotifier/package/contents/ui/FullRepresentation.qml
|
|
||||||
+++ b/applets/devicenotifier/package/contents/ui/FullRepresentation.qml
|
|
||||||
@@ -132,15 +132,9 @@ PlasmaExtras.Representation {
|
|
||||||
|
|
||||||
section {
|
|
||||||
property: "deviceType"
|
|
||||||
- delegate: Item {
|
|
||||||
- height: Math.floor(childrenRect.height)
|
|
||||||
+ delegate: PlasmaExtras.ListSectionHeader {
|
|
||||||
width: notifierDialog.width - (scrollView.PlasmaComponents3.ScrollBar.vertical.visible ? Kirigami.Units.largeSpacing * 2 : 0)
|
|
||||||
- Kirigami.Heading {
|
|
||||||
- level: 3
|
|
||||||
- opacity: 0.6
|
|
||||||
- text: section
|
|
||||||
- textFormat: Text.PlainText
|
|
||||||
- }
|
|
||||||
+ text: section
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,456 +0,0 @@
|
||||||
From 439b251bcb3ea24f52c052e7244fb7ced04503aa Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bohdan Onofriichuk <bogdan.onofriuchuk@gmail.com>
|
|
||||||
Date: Thu, 19 Jun 2025 14:51:30 +0000
|
|
||||||
Subject: [PATCH] applets/devicenotifier: port to plasma_add_applet
|
|
||||||
|
|
||||||
---
|
|
||||||
applets/devicenotifier/CMakeLists.txt | 58 ++++++++++++++++---
|
|
||||||
.../{plugin => }/actioninterface.cpp | 0
|
|
||||||
.../{plugin => }/actioninterface.h | 0
|
|
||||||
.../{plugin => }/actions/defaultaction.cpp | 0
|
|
||||||
.../{plugin => }/actions/defaultaction.h | 0
|
|
||||||
.../{plugin => }/actions/mountaction.cpp | 0
|
|
||||||
.../{plugin => }/actions/mountaction.h | 0
|
|
||||||
.../actions/mountandopenaction.cpp | 0
|
|
||||||
.../{plugin => }/actions/mountandopenaction.h | 0
|
|
||||||
.../actions/openwithfilemanageraction.cpp | 0
|
|
||||||
.../actions/openwithfilemanageraction.h | 0
|
|
||||||
.../{plugin => }/actions/unmountaction.cpp | 0
|
|
||||||
.../{plugin => }/actions/unmountaction.h | 0
|
|
||||||
.../{plugin => }/actionscontrol.cpp | 0
|
|
||||||
.../{plugin => }/actionscontrol.h | 0
|
|
||||||
.../{plugin => }/devicecontrol.cpp | 0
|
|
||||||
.../{plugin => }/devicecontrol.h | 0
|
|
||||||
.../{plugin => }/deviceerrormonitor_p.cpp | 0
|
|
||||||
.../{plugin => }/deviceerrormonitor_p.h | 0
|
|
||||||
.../{plugin => }/devicefiltercontrol.cpp | 0
|
|
||||||
.../{plugin => }/devicefiltercontrol.h | 0
|
|
||||||
.../{plugin => }/devicenotifications.notifyrc | 0
|
|
||||||
.../{plugin => }/deviceserviceaction.cpp | 0
|
|
||||||
.../{plugin => }/deviceserviceaction.h | 0
|
|
||||||
.../{plugin => }/devicestatemonitor_p.cpp | 0
|
|
||||||
.../{plugin => }/devicestatemonitor_p.h | 0
|
|
||||||
.../{package/contents/config => }/main.xml | 0
|
|
||||||
.../{package => }/metadata.json | 1 -
|
|
||||||
applets/devicenotifier/plugin/CMakeLists.txt | 51 ----------------
|
|
||||||
.../{plugin => }/predicatesmonitor_p.cpp | 0
|
|
||||||
.../{plugin => }/predicatesmonitor_p.h | 0
|
|
||||||
.../contents/ui => qml}/DeviceItem.qml | 22 ++++---
|
|
||||||
.../ui => qml}/FullRepresentation.qml | 0
|
|
||||||
.../{package/contents/ui => qml}/main.qml | 9 ++-
|
|
||||||
.../{plugin => }/spacemonitor_p.cpp | 0
|
|
||||||
.../{plugin => }/spacemonitor_p.h | 0
|
|
||||||
36 files changed, 65 insertions(+), 76 deletions(-)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actioninterface.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actioninterface.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/defaultaction.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/defaultaction.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/mountaction.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/mountaction.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/mountandopenaction.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/mountandopenaction.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/openwithfilemanageraction.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/openwithfilemanageraction.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/unmountaction.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actions/unmountaction.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actionscontrol.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/actionscontrol.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/devicecontrol.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/devicecontrol.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/deviceerrormonitor_p.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/deviceerrormonitor_p.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/devicefiltercontrol.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/devicefiltercontrol.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/devicenotifications.notifyrc (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/deviceserviceaction.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/deviceserviceaction.h (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/devicestatemonitor_p.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/devicestatemonitor_p.h (100%)
|
|
||||||
rename applets/devicenotifier/{package/contents/config => }/main.xml (100%)
|
|
||||||
rename applets/devicenotifier/{package => }/metadata.json (99%)
|
|
||||||
delete mode 100644 applets/devicenotifier/plugin/CMakeLists.txt
|
|
||||||
rename applets/devicenotifier/{plugin => }/predicatesmonitor_p.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/predicatesmonitor_p.h (100%)
|
|
||||||
rename applets/devicenotifier/{package/contents/ui => qml}/DeviceItem.qml (81%)
|
|
||||||
rename applets/devicenotifier/{package/contents/ui => qml}/FullRepresentation.qml (100%)
|
|
||||||
rename applets/devicenotifier/{package/contents/ui => qml}/main.qml (96%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/spacemonitor_p.cpp (100%)
|
|
||||||
rename applets/devicenotifier/{plugin => }/spacemonitor_p.h (100%)
|
|
||||||
|
|
||||||
diff --git a/applets/devicenotifier/CMakeLists.txt b/applets/devicenotifier/CMakeLists.txt
|
|
||||||
index bde4a38dd30..f336db13a69 100644
|
|
||||||
--- a/applets/devicenotifier/CMakeLists.txt
|
|
||||||
+++ b/applets/devicenotifier/CMakeLists.txt
|
|
||||||
@@ -1,11 +1,55 @@
|
|
||||||
-add_subdirectory(plugin)
|
|
||||||
+# SPDX-FileCopyrightText: 2024 Fushan Wen <qydwhotmail@gmail.com>
|
|
||||||
+# SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
-ecm_qt_install_logging_categories(
|
|
||||||
- EXPORT APPLETS::DEVICENOTIFIER
|
|
||||||
- FILE applets/devicenotifier.categories
|
|
||||||
- DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
|
||||||
+add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.plasma.devicenotifier\")
|
|
||||||
+
|
|
||||||
+plasma_add_applet(org.kde.plasma.devicenotifier
|
|
||||||
+ QML_SOURCES
|
|
||||||
+ qml/DeviceItem.qml
|
|
||||||
+ qml/FullRepresentation.qml
|
|
||||||
+ qml/main.qml
|
|
||||||
+ CPP_SOURCES
|
|
||||||
+ actionscontrol.cpp
|
|
||||||
+ devicecontrol.cpp
|
|
||||||
+ spacemonitor_p.cpp
|
|
||||||
+ devicestatemonitor_p.cpp
|
|
||||||
+ deviceserviceaction.cpp
|
|
||||||
+ predicatesmonitor_p.cpp
|
|
||||||
+ deviceerrormonitor_p.cpp
|
|
||||||
+ actioninterface.cpp
|
|
||||||
+ devicefiltercontrol.cpp
|
|
||||||
+ actions/defaultaction.cpp
|
|
||||||
+ actions/mountandopenaction.cpp
|
|
||||||
+ actions/mountaction.cpp
|
|
||||||
+ actions/unmountaction.cpp
|
|
||||||
+ actions/openwithfilemanageraction.cpp
|
|
||||||
+ RESOURCES
|
|
||||||
+ main.xml
|
|
||||||
+ GENERATE_APPLET_CLASS
|
|
||||||
+)
|
|
||||||
+
|
|
||||||
+target_link_libraries(org.kde.plasma.devicenotifier
|
|
||||||
+ PRIVATE
|
|
||||||
+ Qt::Qml
|
|
||||||
+ Plasma::Plasma
|
|
||||||
+ KF6::Solid
|
|
||||||
+ KF6::I18n
|
|
||||||
+ KF6::CoreAddons
|
|
||||||
+ KF6::Service
|
|
||||||
+ KF6::KIOCore
|
|
||||||
+ KF6::KIOGui # KIO::CommandLauncherJob
|
|
||||||
+ KF6::JobWidgets # KNotificationJobUiDelegate
|
|
||||||
+ KSysGuard::ProcessCore
|
|
||||||
+ KF6::Notifications
|
|
||||||
)
|
|
||||||
|
|
||||||
-plasma_install_package(package org.kde.plasma.devicenotifier)
|
|
||||||
+ecm_qt_declare_logging_category(org.kde.plasma.devicenotifier
|
|
||||||
+ HEADER "devicenotifier_debug.h"
|
|
||||||
+ IDENTIFIER "APPLETS::DEVICENOTIFIER"
|
|
||||||
+ CATEGORY_NAME org.kde.applets.devicenotifier
|
|
||||||
+ DEFAULT_SEVERITY Warning
|
|
||||||
+ DESCRIPTION "Device Notifier applet" EXPORT "APPLETS::DEVICENOTIFIER"
|
|
||||||
+)
|
|
||||||
|
|
||||||
-install(FILES openWithFileManager.desktop DESTINATION ${KDE_INSTALL_DATADIR}/solid/actions )
|
|
||||||
+install(FILES devicenotifications.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
|
||||||
+install(FILES openWithFileManager.desktop DESTINATION ${KDE_INSTALL_DATADIR}/solid/actions)
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actioninterface.cpp b/applets/devicenotifier/actioninterface.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actioninterface.cpp
|
|
||||||
rename to applets/devicenotifier/actioninterface.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actioninterface.h b/applets/devicenotifier/actioninterface.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actioninterface.h
|
|
||||||
rename to applets/devicenotifier/actioninterface.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/defaultaction.cpp b/applets/devicenotifier/actions/defaultaction.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/defaultaction.cpp
|
|
||||||
rename to applets/devicenotifier/actions/defaultaction.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/defaultaction.h b/applets/devicenotifier/actions/defaultaction.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/defaultaction.h
|
|
||||||
rename to applets/devicenotifier/actions/defaultaction.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/mountaction.cpp b/applets/devicenotifier/actions/mountaction.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/mountaction.cpp
|
|
||||||
rename to applets/devicenotifier/actions/mountaction.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/mountaction.h b/applets/devicenotifier/actions/mountaction.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/mountaction.h
|
|
||||||
rename to applets/devicenotifier/actions/mountaction.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/mountandopenaction.cpp b/applets/devicenotifier/actions/mountandopenaction.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/mountandopenaction.cpp
|
|
||||||
rename to applets/devicenotifier/actions/mountandopenaction.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/mountandopenaction.h b/applets/devicenotifier/actions/mountandopenaction.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/mountandopenaction.h
|
|
||||||
rename to applets/devicenotifier/actions/mountandopenaction.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/openwithfilemanageraction.cpp b/applets/devicenotifier/actions/openwithfilemanageraction.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/openwithfilemanageraction.cpp
|
|
||||||
rename to applets/devicenotifier/actions/openwithfilemanageraction.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/openwithfilemanageraction.h b/applets/devicenotifier/actions/openwithfilemanageraction.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/openwithfilemanageraction.h
|
|
||||||
rename to applets/devicenotifier/actions/openwithfilemanageraction.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/unmountaction.cpp b/applets/devicenotifier/actions/unmountaction.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/unmountaction.cpp
|
|
||||||
rename to applets/devicenotifier/actions/unmountaction.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actions/unmountaction.h b/applets/devicenotifier/actions/unmountaction.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actions/unmountaction.h
|
|
||||||
rename to applets/devicenotifier/actions/unmountaction.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actionscontrol.cpp b/applets/devicenotifier/actionscontrol.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actionscontrol.cpp
|
|
||||||
rename to applets/devicenotifier/actionscontrol.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/actionscontrol.h b/applets/devicenotifier/actionscontrol.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/actionscontrol.h
|
|
||||||
rename to applets/devicenotifier/actionscontrol.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/devicecontrol.cpp b/applets/devicenotifier/devicecontrol.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/devicecontrol.cpp
|
|
||||||
rename to applets/devicenotifier/devicecontrol.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/devicecontrol.h b/applets/devicenotifier/devicecontrol.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/devicecontrol.h
|
|
||||||
rename to applets/devicenotifier/devicecontrol.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/deviceerrormonitor_p.cpp b/applets/devicenotifier/deviceerrormonitor_p.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/deviceerrormonitor_p.cpp
|
|
||||||
rename to applets/devicenotifier/deviceerrormonitor_p.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/deviceerrormonitor_p.h b/applets/devicenotifier/deviceerrormonitor_p.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/deviceerrormonitor_p.h
|
|
||||||
rename to applets/devicenotifier/deviceerrormonitor_p.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/devicefiltercontrol.cpp b/applets/devicenotifier/devicefiltercontrol.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/devicefiltercontrol.cpp
|
|
||||||
rename to applets/devicenotifier/devicefiltercontrol.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/devicefiltercontrol.h b/applets/devicenotifier/devicefiltercontrol.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/devicefiltercontrol.h
|
|
||||||
rename to applets/devicenotifier/devicefiltercontrol.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/devicenotifications.notifyrc b/applets/devicenotifier/devicenotifications.notifyrc
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/devicenotifications.notifyrc
|
|
||||||
rename to applets/devicenotifier/devicenotifications.notifyrc
|
|
||||||
diff --git a/applets/devicenotifier/plugin/deviceserviceaction.cpp b/applets/devicenotifier/deviceserviceaction.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/deviceserviceaction.cpp
|
|
||||||
rename to applets/devicenotifier/deviceserviceaction.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/deviceserviceaction.h b/applets/devicenotifier/deviceserviceaction.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/deviceserviceaction.h
|
|
||||||
rename to applets/devicenotifier/deviceserviceaction.h
|
|
||||||
diff --git a/applets/devicenotifier/plugin/devicestatemonitor_p.cpp b/applets/devicenotifier/devicestatemonitor_p.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/devicestatemonitor_p.cpp
|
|
||||||
rename to applets/devicenotifier/devicestatemonitor_p.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/devicestatemonitor_p.h b/applets/devicenotifier/devicestatemonitor_p.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/devicestatemonitor_p.h
|
|
||||||
rename to applets/devicenotifier/devicestatemonitor_p.h
|
|
||||||
diff --git a/applets/devicenotifier/package/contents/config/main.xml b/applets/devicenotifier/main.xml
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/package/contents/config/main.xml
|
|
||||||
rename to applets/devicenotifier/main.xml
|
|
||||||
diff --git a/applets/devicenotifier/package/metadata.json b/applets/devicenotifier/metadata.json
|
|
||||||
similarity index 99%
|
|
||||||
rename from applets/devicenotifier/package/metadata.json
|
|
||||||
rename to applets/devicenotifier/metadata.json
|
|
||||||
index 0a330dfc189..77d7feac1cb 100644
|
|
||||||
--- a/applets/devicenotifier/package/metadata.json
|
|
||||||
+++ b/applets/devicenotifier/metadata.json
|
|
||||||
@@ -115,7 +115,6 @@
|
|
||||||
"desktop"
|
|
||||||
],
|
|
||||||
"Icon": "device-notifier",
|
|
||||||
- "Id": "org.kde.plasma.devicenotifier",
|
|
||||||
"License": "GPL-2.0+",
|
|
||||||
"Name": "Disks & Devices",
|
|
||||||
"Name[ar]": "الأجهزة والأقراص",
|
|
||||||
diff --git a/applets/devicenotifier/plugin/CMakeLists.txt b/applets/devicenotifier/plugin/CMakeLists.txt
|
|
||||||
deleted file mode 100644
|
|
||||||
index 34a3456d690..00000000000
|
|
||||||
--- a/applets/devicenotifier/plugin/CMakeLists.txt
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,51 +0,0 @@
|
|
||||||
-# SPDX-FileCopyrightText: 2024 Fushan Wen <qydwhotmail@gmail.com>
|
|
||||||
-# SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
-
|
|
||||||
-add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.plasma.devicenotifier\")
|
|
||||||
-
|
|
||||||
-ecm_add_qml_module(devicenotifierplugin URI org.kde.plasma.private.devicenotifier GENERATE_PLUGIN_SOURCE)
|
|
||||||
-
|
|
||||||
-target_sources(devicenotifierplugin
|
|
||||||
- PRIVATE
|
|
||||||
- actionscontrol.cpp actionscontrol.h
|
|
||||||
- devicecontrol.cpp devicecontrol.h
|
|
||||||
- spacemonitor_p.cpp spacemonitor_p.h
|
|
||||||
- devicestatemonitor_p.cpp devicestatemonitor_p.h
|
|
||||||
- deviceserviceaction.cpp deviceserviceaction.h
|
|
||||||
- predicatesmonitor_p.cpp predicatesmonitor_p.h
|
|
||||||
- deviceerrormonitor_p.cpp deviceerrormonitor_p.h
|
|
||||||
- actioninterface.cpp actioninterface.h
|
|
||||||
- devicefiltercontrol.cpp devicefiltercontrol.h
|
|
||||||
- actions/defaultaction.cpp actions/defaultaction.h
|
|
||||||
- actions/mountandopenaction.cpp actions/mountandopenaction.h
|
|
||||||
- actions/mountaction.cpp actions/mountaction.h
|
|
||||||
- actions/unmountaction.cpp actions/unmountaction.h
|
|
||||||
- actions/openwithfilemanageraction.cpp actions/openwithfilemanageraction.h
|
|
||||||
-)
|
|
||||||
-
|
|
||||||
-target_link_libraries(devicenotifierplugin
|
|
||||||
- PRIVATE
|
|
||||||
- Qt::Qml
|
|
||||||
- Plasma::Plasma
|
|
||||||
- KF6::Solid
|
|
||||||
- KF6::I18n
|
|
||||||
- KF6::CoreAddons
|
|
||||||
- KF6::Service
|
|
||||||
- KF6::KIOCore
|
|
||||||
- KF6::KIOGui # KIO::CommandLauncherJob
|
|
||||||
- KF6::JobWidgets # KNotificationJobUiDelegate
|
|
||||||
- KSysGuard::ProcessCore
|
|
||||||
- KF6::Notifications
|
|
||||||
-)
|
|
||||||
-
|
|
||||||
-ecm_qt_declare_logging_category(devicenotifierplugin
|
|
||||||
- HEADER "devicenotifier_debug.h"
|
|
||||||
- IDENTIFIER "APPLETS::DEVICENOTIFIER"
|
|
||||||
- CATEGORY_NAME org.kde.applets.devicenotifier
|
|
||||||
- DEFAULT_SEVERITY Warning
|
|
||||||
- DESCRIPTION "Device Notifier applet" EXPORT "APPLETS::DEVICENOTIFIER"
|
|
||||||
-)
|
|
||||||
-
|
|
||||||
-ecm_finalize_qml_module(devicenotifierplugin)
|
|
||||||
-
|
|
||||||
-install(FILES devicenotifications.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
|
||||||
diff --git a/applets/devicenotifier/plugin/predicatesmonitor_p.cpp b/applets/devicenotifier/predicatesmonitor_p.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/predicatesmonitor_p.cpp
|
|
||||||
rename to applets/devicenotifier/predicatesmonitor_p.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/predicatesmonitor_p.h b/applets/devicenotifier/predicatesmonitor_p.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/predicatesmonitor_p.h
|
|
||||||
rename to applets/devicenotifier/predicatesmonitor_p.h
|
|
||||||
diff --git a/applets/devicenotifier/package/contents/ui/DeviceItem.qml b/applets/devicenotifier/qml/DeviceItem.qml
|
|
||||||
similarity index 81%
|
|
||||||
rename from applets/devicenotifier/package/contents/ui/DeviceItem.qml
|
|
||||||
rename to applets/devicenotifier/qml/DeviceItem.qml
|
|
||||||
index c19c9535b04..861996af45c 100644
|
|
||||||
--- a/applets/devicenotifier/package/contents/ui/DeviceItem.qml
|
|
||||||
+++ b/applets/devicenotifier/qml/DeviceItem.qml
|
|
||||||
@@ -19,8 +19,6 @@ import org.kde.kirigami as Kirigami
|
|
||||||
|
|
||||||
import org.kde.kquickcontrolsaddons
|
|
||||||
|
|
||||||
-import org.kde.plasma.private.devicenotifier as DN
|
|
||||||
-
|
|
||||||
PlasmaExtras.ExpandableListItem {
|
|
||||||
id: deviceItem
|
|
||||||
|
|
||||||
@@ -41,18 +39,18 @@ PlasmaExtras.ExpandableListItem {
|
|
||||||
|
|
||||||
property bool hasMessage: deviceItem.deviceErrorMessage !== ""
|
|
||||||
|
|
||||||
- property bool isFree: deviceItem.deviceOperationResult !== DN.DevicesStateMonitor.Working && deviceItem.deviceOperationResult !== DN.DevicesStateMonitor.Checking && deviceItem.deviceOperationResult !== DN.DevicesStateMonitor.Repairing && deviceItem.deviceOperationResult !== DN.DevicesStateMonitor.NotPresent && !(deviceItem.deviceMounted === false && deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Successful)
|
|
||||||
+ property bool isFree: deviceItem.deviceOperationResult !== DevicesStateMonitor.Working && deviceItem.deviceOperationResult !== DevicesStateMonitor.Checking && deviceItem.deviceOperationResult !== DevicesStateMonitor.Repairing && deviceItem.deviceOperationResult !== DevicesStateMonitor.NotPresent && !(deviceItem.deviceMounted === false && deviceItem.deviceOperationResult === DevicesStateMonitor.Successful)
|
|
||||||
|
|
||||||
onDeviceOperationResultChanged: {
|
|
||||||
if (!popupIconTimer.running) {
|
|
||||||
- if (deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Working) {
|
|
||||||
+ if (deviceItem.deviceOperationResult === DevicesStateMonitor.Working) {
|
|
||||||
if(deviceMounted){
|
|
||||||
unmountTimer.restart();
|
|
||||||
}
|
|
||||||
- } else if (deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Successful) {
|
|
||||||
+ } else if (deviceItem.deviceOperationResult === DevicesStateMonitor.Successful) {
|
|
||||||
devicenotifier.popupIcon = "dialog-ok"
|
|
||||||
popupIconTimer.restart()
|
|
||||||
- } else if (deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Unsuccessful) {
|
|
||||||
+ } else if (deviceItem.deviceOperationResult === DevicesStateMonitor.Unsuccessful) {
|
|
||||||
devicenotifier.popupIcon = "dialog-error"
|
|
||||||
popupIconTimer.restart()
|
|
||||||
}
|
|
||||||
@@ -80,7 +78,7 @@ PlasmaExtras.ExpandableListItem {
|
|
||||||
} else {
|
|
||||||
return "emblem-error"
|
|
||||||
}
|
|
||||||
- } else if (deviceItem.deviceOperationResult !== DN.DevicesStateMonitor.Working && deviceItem.deviceEmblems[0]) {
|
|
||||||
+ } else if (deviceItem.deviceOperationResult !== DevicesStateMonitor.Working && deviceItem.deviceEmblems[0]) {
|
|
||||||
return deviceItem.deviceEmblems[0]
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
@@ -93,16 +91,16 @@ PlasmaExtras.ExpandableListItem {
|
|
||||||
if (deviceItem.hasMessage) {
|
|
||||||
return deviceItem.deviceErrorMessage
|
|
||||||
}
|
|
||||||
- if (deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Checking) {
|
|
||||||
+ if (deviceItem.deviceOperationResult === DevicesStateMonitor.Checking) {
|
|
||||||
return i18nc("Accessing is a less technical word for Mounting; translation should be short and mean \'Currently mounting this device\'", "Checking…")
|
|
||||||
- } else if (deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Repairing) {
|
|
||||||
+ } else if (deviceItem.deviceOperationResult === DevicesStateMonitor.Repairing) {
|
|
||||||
return i18nc("Accessing is a less technical word for Mounting; translation should be short and mean \'Currently mounting this device\'", "Repairing…")
|
|
||||||
- } else if (deviceItem.deviceOperationResult !== DN.DevicesStateMonitor.Working) {
|
|
||||||
+ } else if (deviceItem.deviceOperationResult !== DevicesStateMonitor.Working) {
|
|
||||||
if (deviceItem.deviceFreeSpace > 0 && deviceItem.deviceSize > 0) {
|
|
||||||
return i18nc("@info:status Free disk space", "%1 free of %2", deviceItem.deviceFreeSpaceText, deviceItem.deviceSizeText)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
- } else if (!deviceItem.deviceMounted && deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Working) {
|
|
||||||
+ } else if (!deviceItem.deviceMounted && deviceItem.deviceOperationResult === DevicesStateMonitor.Working) {
|
|
||||||
return i18nc("Accessing is a less technical word for Mounting; translation should be short and mean \'Currently mounting this device\'", "Accessing…")
|
|
||||||
} else if (unmountTimer.running) {
|
|
||||||
// Unmounting; shown if unmount takes less than 1 second
|
|
||||||
@@ -139,7 +137,7 @@ PlasmaExtras.ExpandableListItem {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- isBusy: deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Working || deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Checking || deviceItem.deviceOperationResult === DN.DevicesStateMonitor.Repairing
|
|
||||||
+ isBusy: deviceItem.deviceOperationResult === DevicesStateMonitor.Working || deviceItem.deviceOperationResult === DevicesStateMonitor.Checking || deviceItem.deviceOperationResult === DevicesStateMonitor.Repairing
|
|
||||||
|
|
||||||
customExpandedViewContent: deviceActions !== undefined && deviceActions.rowCount() !== 0 && isFree ? actionComponent : null
|
|
||||||
|
|
||||||
diff --git a/applets/devicenotifier/package/contents/ui/FullRepresentation.qml b/applets/devicenotifier/qml/FullRepresentation.qml
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/package/contents/ui/FullRepresentation.qml
|
|
||||||
rename to applets/devicenotifier/qml/FullRepresentation.qml
|
|
||||||
diff --git a/applets/devicenotifier/package/contents/ui/main.qml b/applets/devicenotifier/qml/main.qml
|
|
||||||
similarity index 96%
|
|
||||||
rename from applets/devicenotifier/package/contents/ui/main.qml
|
|
||||||
rename to applets/devicenotifier/qml/main.qml
|
|
||||||
index 4061480becc..7fcd76a6d16 100644
|
|
||||||
--- a/applets/devicenotifier/package/contents/ui/main.qml
|
|
||||||
+++ b/applets/devicenotifier/qml/main.qml
|
|
||||||
@@ -15,21 +15,20 @@ import org.kde.kirigami as Kirigami
|
|
||||||
|
|
||||||
import org.kde.kcmutils // For KCMLauncher
|
|
||||||
import org.kde.config // KAuthorized
|
|
||||||
-import org.kde.plasma.private.devicenotifier as DN
|
|
||||||
|
|
||||||
PlasmoidItem {
|
|
||||||
id: devicenotifier
|
|
||||||
|
|
||||||
- DN.DeviceFilterControl {
|
|
||||||
+ DeviceFilterControl {
|
|
||||||
id: filterModel
|
|
||||||
|
|
||||||
filterType: {
|
|
||||||
if (Plasmoid.configuration.allDevices) {
|
|
||||||
- return DN.DeviceFilterControl.All
|
|
||||||
+ return DeviceFilterControl.All
|
|
||||||
} else if (Plasmoid.configuration.removableDevices) {
|
|
||||||
- return DN.DeviceFilterControl.Removable
|
|
||||||
+ return DeviceFilterControl.Removable
|
|
||||||
} else {
|
|
||||||
- return DN.DeviceFilterControl.Unremovable
|
|
||||||
+ return DeviceFilterControl.Unremovable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diff --git a/applets/devicenotifier/plugin/spacemonitor_p.cpp b/applets/devicenotifier/spacemonitor_p.cpp
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/spacemonitor_p.cpp
|
|
||||||
rename to applets/devicenotifier/spacemonitor_p.cpp
|
|
||||||
diff --git a/applets/devicenotifier/plugin/spacemonitor_p.h b/applets/devicenotifier/spacemonitor_p.h
|
|
||||||
similarity index 100%
|
|
||||||
rename from applets/devicenotifier/plugin/spacemonitor_p.h
|
|
||||||
rename to applets/devicenotifier/spacemonitor_p.h
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
From 9fabf42c39a25308739dd3483881cc889243bf58 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kristen McWilliam <kristen@kde.org>
|
|
||||||
Date: Tue, 24 Jun 2025 17:44:33 -0400
|
|
||||||
Subject: [PATCH] applets/notifications: Add actions to missed notifications
|
|
||||||
notification
|
|
||||||
|
|
||||||
When a notification is displayed informing the user that notifications were missed while in Do Not
|
|
||||||
Disturb, now clicking the button or the notification itself will open the notifications applet to
|
|
||||||
show the missed notifications.
|
|
||||||
|
|
||||||
BUG: 502423
|
|
||||||
---
|
|
||||||
.../package/contents/ui/main.qml | 10 ++++++
|
|
||||||
libnotificationmanager/notifications.cpp | 34 ++++++++++++++-----
|
|
||||||
libnotificationmanager/notifications.h | 7 ++++
|
|
||||||
3 files changed, 42 insertions(+), 9 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/applets/notifications/package/contents/ui/main.qml b/applets/notifications/package/contents/ui/main.qml
|
|
||||||
index c96afa0558d..ab66a52f45e 100644
|
|
||||||
--- a/applets/notifications/package/contents/ui/main.qml
|
|
||||||
+++ b/applets/notifications/package/contents/ui/main.qml
|
|
||||||
@@ -268,4 +268,14 @@ PlasmoidItem {
|
|
||||||
Component.onDestruction: {
|
|
||||||
Globals.forget(root);
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ Connections {
|
|
||||||
+ target: Globals.popupNotificationsModel
|
|
||||||
+
|
|
||||||
+ // The user requested to show the notifications popup, probably by
|
|
||||||
+ // clicking the "Missed Notifications in Do Not Disturb" notification.
|
|
||||||
+ function onShowNotificationsRequested(): void {
|
|
||||||
+ root.expanded = true;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
diff --git a/libnotificationmanager/notifications.cpp b/libnotificationmanager/notifications.cpp
|
|
||||||
index 2cba1360b10..7b5cd9d68c4 100644
|
|
||||||
--- a/libnotificationmanager/notifications.cpp
|
|
||||||
+++ b/libnotificationmanager/notifications.cpp
|
|
||||||
@@ -903,15 +903,31 @@ void Notifications::showInhibitionSummary(Urgency urgency, const QStringList &bl
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
- KNotification::event(u"inhibitionSummary"_s,
|
|
||||||
- i18ncp("@title", "Unread Notification", "Unread Notifications", inhibited),
|
|
||||||
- i18ncp("@info",
|
|
||||||
- "%1 notification was received while Do Not Disturb was active.",
|
|
||||||
- "%1 notifications were received while Do Not Disturb was active.",
|
|
||||||
- inhibited),
|
|
||||||
- u"preferences-desktop-notification-bell"_s,
|
|
||||||
- KNotification::CloseOnTimeout,
|
|
||||||
- u"libnotificationmanager"_s);
|
|
||||||
+ KNotification *notification = new KNotification(u"inhibitionSummary"_s);
|
|
||||||
+ notification->setTitle(i18ncp("@title", "Unread Notification", "Unread Notifications", inhibited));
|
|
||||||
+ notification->setText(i18ncp("@info",
|
|
||||||
+ "%1 notification was received while Do Not Disturb was active.",
|
|
||||||
+ "%1 notifications were received while Do Not Disturb was active.",
|
|
||||||
+ inhibited));
|
|
||||||
+ notification->setIconName(u"preferences-desktop-notification-bell"_s);
|
|
||||||
+ notification->setFlags(KNotification::CloseOnTimeout);
|
|
||||||
+ notification->setComponentName(u"libnotificationmanager"_s);
|
|
||||||
+
|
|
||||||
+ const QString showNotificationsText = i18nc( //
|
|
||||||
+ "@action:button Show the notifications popup; translate this in as short a form as possible",
|
|
||||||
+ "Show Notifications");
|
|
||||||
+
|
|
||||||
+ const KNotificationAction *defaultShowNotificationsAction = notification->addDefaultAction(showNotificationsText);
|
|
||||||
+ connect(defaultShowNotificationsAction, &KNotificationAction::activated, this, [this]() {
|
|
||||||
+ Q_EMIT showNotificationsRequested();
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ const KNotificationAction *showNotificationsAction = notification->addAction(showNotificationsText);
|
|
||||||
+ connect(showNotificationsAction, &KNotificationAction::activated, this, [this]() {
|
|
||||||
+ Q_EMIT showNotificationsRequested();
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ notification->sendEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant Notifications::data(const QModelIndex &index, int role) const
|
|
||||||
diff --git a/libnotificationmanager/notifications.h b/libnotificationmanager/notifications.h
|
|
||||||
index e927c472c8f..8544b6271ae 100644
|
|
||||||
--- a/libnotificationmanager/notifications.h
|
|
||||||
+++ b/libnotificationmanager/notifications.h
|
|
||||||
@@ -597,6 +597,13 @@ Q_SIGNALS:
|
|
||||||
void jobsPercentageChanged();
|
|
||||||
void windowChanged(QWindow *window);
|
|
||||||
|
|
||||||
+ /**
|
|
||||||
+ * Emitted when the user has requested to show the notifications popup.
|
|
||||||
+ *
|
|
||||||
+ * This is typically connected to a button in the "Missed Notifications in Do Not Disturb" notification.
|
|
||||||
+ */
|
|
||||||
+ void showNotificationsRequested();
|
|
||||||
+
|
|
||||||
protected:
|
|
||||||
void classBegin() override;
|
|
||||||
void componentComplete() override;
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
From 1641ea3897d565d672e29a7524ce4171ddbcfd32 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
Date: Wed, 25 Jun 2025 14:00:05 +0300
|
|
||||||
Subject: [PATCH 1/2] shell: Set desktop ksplash stage when all desktop views
|
|
||||||
are ready
|
|
||||||
|
|
||||||
If there are two outputs and the wallpaper for the first one is ready
|
|
||||||
but for the second one is not, the ksplash will be notified about the
|
|
||||||
desktop stage.
|
|
||||||
|
|
||||||
This changes fixes checkAllDesktopsUiReady() so it sets the desktop
|
|
||||||
stage only if all desktop views are ready.
|
|
||||||
---
|
|
||||||
shell/shellcorona.cpp | 28 +++++++++++++++-------------
|
|
||||||
shell/shellcorona.h | 2 +-
|
|
||||||
2 files changed, 16 insertions(+), 14 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/shell/shellcorona.cpp b/shell/shellcorona.cpp
|
|
||||||
index 37d93d05353..65c8ccc272e 100644
|
|
||||||
--- a/shell/shellcorona.cpp
|
|
||||||
+++ b/shell/shellcorona.cpp
|
|
||||||
@@ -1338,6 +1338,8 @@ void ShellCorona::removeDesktop(DesktopView *desktopView)
|
|
||||||
|
|
||||||
desktopView->destroy();
|
|
||||||
desktopView->containment()->reactToScreenChange();
|
|
||||||
+
|
|
||||||
+ checkAllDesktopsUiReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelView *ShellCorona::panelView(Plasma::Containment *containment) const
|
|
||||||
@@ -1529,22 +1531,22 @@ void ShellCorona::addOutput(QScreen *screen)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
-void ShellCorona::checkAllDesktopsUiReady(bool ready)
|
|
||||||
+void ShellCorona::checkAllDesktopsUiReady()
|
|
||||||
{
|
|
||||||
- if (!ready)
|
|
||||||
+ const bool ready = std::ranges::all_of(std::as_const(m_desktopViewForScreen), [](const DesktopView *view) {
|
|
||||||
+ return view->containment()->isUiReady();
|
|
||||||
+ });
|
|
||||||
+ if (!ready) {
|
|
||||||
return;
|
|
||||||
- for (auto v : std::as_const(m_desktopViewForScreen)) {
|
|
||||||
- if (!v->containment()->isUiReady())
|
|
||||||
- return;
|
|
||||||
-
|
|
||||||
- qCDebug(PLASMASHELL) << "Plasma Shell startup completed";
|
|
||||||
- QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"),
|
|
||||||
- QStringLiteral("/KSplash"),
|
|
||||||
- QStringLiteral("org.kde.KSplash"),
|
|
||||||
- QStringLiteral("setStage"));
|
|
||||||
- ksplashProgressMessage.setArguments(QList<QVariant>() << QStringLiteral("desktop"));
|
|
||||||
- QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage);
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ qCDebug(PLASMASHELL) << "Plasma Shell startup completed";
|
|
||||||
+ QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"),
|
|
||||||
+ QStringLiteral("/KSplash"),
|
|
||||||
+ QStringLiteral("org.kde.KSplash"),
|
|
||||||
+ QStringLiteral("setStage"));
|
|
||||||
+ ksplashProgressMessage.setArguments(QList<QVariant>() << QStringLiteral("desktop"));
|
|
||||||
+ QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
Plasma::Containment *ShellCorona::createContainmentForActivity(const QString &activity, int screenNum)
|
|
||||||
diff --git a/shell/shellcorona.h b/shell/shellcorona.h
|
|
||||||
index 0f544ca266f..7b6c6a559a1 100644
|
|
||||||
--- a/shell/shellcorona.h
|
|
||||||
+++ b/shell/shellcorona.h
|
|
||||||
@@ -275,7 +275,7 @@ private:
|
|
||||||
DesktopView *desktopForScreen(QScreen *screen) const;
|
|
||||||
void setupWaylandIntegration();
|
|
||||||
void executeSetupPlasmoidScript(Plasma::Containment *containment, Plasma::Applet *applet);
|
|
||||||
- void checkAllDesktopsUiReady(bool ready);
|
|
||||||
+ void checkAllDesktopsUiReady();
|
|
||||||
void activateLauncherMenu(const QString &screenName);
|
|
||||||
void handleColorRequestedFromDBus(const QDBusMessage &msg);
|
|
||||||
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From 00bd19ecaccbb10d5cfcc6006b06a9714c615741 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
Date: Wed, 25 Jun 2025 14:03:28 +0300
|
|
||||||
Subject: [PATCH 2/2] shell: Create panel views after desktop views are ready
|
|
||||||
|
|
||||||
With the current code, the splash screen will be hidden as soon as the
|
|
||||||
wallpapers are loaded.
|
|
||||||
|
|
||||||
However, the splash screnn is actually notified about the desktop stage
|
|
||||||
about 1-1.5 second later after the wallpaper plugin resets the loading
|
|
||||||
property.
|
|
||||||
|
|
||||||
The reason for that is that the panel is loaded between
|
|
||||||
`wallpaper.loading = false` in the wallpaper and ShellCorona::checkAllDesktopsUiReady().
|
|
||||||
|
|
||||||
This change re-arranges the startup sequence so the panels are loaded
|
|
||||||
after the desktop views become ready. It reduces plasma startup time a bit.
|
|
||||||
|
|
||||||
In long term, we should look for making panel loading as async as
|
|
||||||
possible so the main thread doesn't get blocked for too long.
|
|
||||||
---
|
|
||||||
shell/shellcorona.cpp | 15 +++++----------
|
|
||||||
1 file changed, 5 insertions(+), 10 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/shell/shellcorona.cpp b/shell/shellcorona.cpp
|
|
||||||
index 65c8ccc272e..6a59683dba7 100644
|
|
||||||
--- a/shell/shellcorona.cpp
|
|
||||||
+++ b/shell/shellcorona.cpp
|
|
||||||
@@ -868,10 +868,6 @@ void ShellCorona::load()
|
|
||||||
connect(m_screenPool, &ScreenPool::screenOrderChanged, this, &ShellCorona::handleScreenOrderChanged, Qt::UniqueConnection);
|
|
||||||
connect(m_screenPool, &ScreenPool::screenRemoved, this, &ShellCorona::handleScreenRemoved, Qt::UniqueConnection);
|
|
||||||
|
|
||||||
- if (!m_waitingPanels.isEmpty()) {
|
|
||||||
- m_waitingPanelsTimer.start();
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
if (config()->isImmutable() || !KAuthorized::authorize(QStringLiteral("plasma/plasmashell/unlockedDesktop"))) {
|
|
||||||
setImmutability(Plasma::Types::SystemImmutable);
|
|
||||||
} else {
|
|
||||||
@@ -1517,11 +1513,6 @@ void ShellCorona::addOutput(QScreen *screen)
|
|
||||||
// in the list. We still don't want to have an invisible view added.
|
|
||||||
containment->reactToScreenChange();
|
|
||||||
|
|
||||||
- // were there any panels for this screen before it popped up?
|
|
||||||
- if (!m_waitingPanels.isEmpty()) {
|
|
||||||
- m_waitingPanelsTimer.start();
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
if (!m_screenReorderInProgress) {
|
|
||||||
Q_EMIT availableScreenRectChanged(m_screenPool->idForScreen(screen));
|
|
||||||
}
|
|
||||||
@@ -1547,6 +1538,10 @@ void ShellCorona::checkAllDesktopsUiReady()
|
|
||||||
QStringLiteral("setStage"));
|
|
||||||
ksplashProgressMessage.setArguments(QList<QVariant>() << QStringLiteral("desktop"));
|
|
||||||
QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage);
|
|
||||||
+
|
|
||||||
+ if (!m_waitingPanels.isEmpty()) {
|
|
||||||
+ m_waitingPanelsTimer.start();
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
Plasma::Containment *ShellCorona::createContainmentForActivity(const QString &activity, int screenNum)
|
|
||||||
@@ -1604,7 +1599,7 @@ void ShellCorona::createWaitingPanels()
|
|
||||||
|
|
||||||
QScreen *screen = m_screenPool->screenForId(requestedScreen);
|
|
||||||
DesktopView *desktopView = desktopForScreen(screen);
|
|
||||||
- if (!screen || !desktopView) {
|
|
||||||
+ if (!screen || !desktopView || !desktopView->containment()->isUiReady()) {
|
|
||||||
stillWaitingPanels << cont;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
From aa1e466e5f7684a8b624c34d466dda9d10a331d2 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Sun, 6 Jul 2025 23:20:47 -0400
|
|
||||||
Subject: [PATCH 1/2] Improve UX of USB plug/unplug notifications when popup is
|
|
||||||
shown
|
|
||||||
|
|
||||||
1. When plugged or unplugged, revoke the opposite notification if it's
|
|
||||||
visible.
|
|
||||||
2. Set the urgency to low so it won't clutter up the notification
|
|
||||||
history.
|
|
||||||
---
|
|
||||||
devicenotifications/devicenotifications.cpp | 55 ++++++++++++++++-----
|
|
||||||
devicenotifications/devicenotifications.h | 5 ++
|
|
||||||
2 files changed, 48 insertions(+), 12 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/devicenotifications/devicenotifications.cpp b/devicenotifications/devicenotifications.cpp
|
|
||||||
index cc462f58f5..f326691c11 100644
|
|
||||||
--- a/devicenotifications/devicenotifications.cpp
|
|
||||||
+++ b/devicenotifications/devicenotifications.cpp
|
|
||||||
@@ -15,6 +15,7 @@
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
+#include <knotification.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
K_PLUGIN_CLASS_WITH_JSON(KdedDeviceNotifications, "devicenotifications.json")
|
|
||||||
@@ -375,13 +376,28 @@ void KdedDeviceNotifications::onDeviceAdded(const UdevDevice &device)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
- const QString text = !displayName.isEmpty() ? i18n("%1 has been plugged in.", displayName.toHtmlEscaped()) : i18n("A USB device has been plugged in.");
|
|
||||||
+ // If the user unplugged something and then immediately plugged it in again,
|
|
||||||
+ // there's no need to keep the unplug notification around.
|
|
||||||
+ if (m_usbDeviceRemovedNotification) {
|
|
||||||
+ m_usbDeviceRemovedNotification->close();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Only show one of these at a time. We already suppressed creating a bunch
|
|
||||||
+ // in quick succession for the dock/hub use case, so any that are created
|
|
||||||
+ // over that time limit anyway are not necessary to stack up.
|
|
||||||
+ if (m_usbDeviceAddedNotification) {
|
|
||||||
+ m_usbDeviceAddedNotification->close();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ const QString text = !displayName.isEmpty() ? i18n("%1 has been connected.", displayName.toHtmlEscaped()) : i18n("A USB device has been connected.");
|
|
||||||
+
|
|
||||||
+ m_usbDeviceAddedNotification = new KNotification(QStringLiteral("deviceAdded"));
|
|
||||||
+ m_usbDeviceAddedNotification->setFlags(KNotification::DefaultEvent);
|
|
||||||
+ m_usbDeviceAddedNotification->setIconName(QStringLiteral("drive-removable-media-usb"));
|
|
||||||
+ m_usbDeviceAddedNotification->setTitle(i18nc("@title:notifications", "USB Device Detected"));
|
|
||||||
+ m_usbDeviceAddedNotification->setText(text);
|
|
||||||
+ m_usbDeviceAddedNotification->sendEvent();
|
|
||||||
|
|
||||||
- KNotification::event(QStringLiteral("deviceAdded"),
|
|
||||||
- i18nc("@title:notifications", "USB Device Detected"),
|
|
||||||
- text,
|
|
||||||
- QStringLiteral("drive-removable-media-usb"),
|
|
||||||
- KNotification::DefaultEvent);
|
|
||||||
m_deviceAddedTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -401,13 +417,28 @@ void KdedDeviceNotifications::onDeviceRemoved(const UdevDevice &device)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
- const QString text = !displayName.isEmpty() ? i18n("%1 has been unplugged.", displayName.toHtmlEscaped()) : i18n("A USB device has been unplugged.");
|
|
||||||
+ // If the user plugged something in and then immediately unplugged it again,
|
|
||||||
+ // there's no need to keep the plug notification around.
|
|
||||||
+ if (m_usbDeviceAddedNotification) {
|
|
||||||
+ m_usbDeviceAddedNotification->close();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Only show one of these at a time. We already suppressed removing a bunch
|
|
||||||
+ // in quick succession for the dock/hub use case, so any that are removed
|
|
||||||
+ // over that time limit anyway are not necessary to stack up.
|
|
||||||
+ if (m_usbDeviceRemovedNotification) {
|
|
||||||
+ m_usbDeviceRemovedNotification->close();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ const QString text = !displayName.isEmpty() ? i18n("%1 has been disconnected.", displayName.toHtmlEscaped()) : i18n("A USB device has been disconnected.");
|
|
||||||
+
|
|
||||||
+ m_usbDeviceRemovedNotification = new KNotification(QStringLiteral("deviceRemoved"));
|
|
||||||
+ m_usbDeviceRemovedNotification->setFlags(KNotification::DefaultEvent);
|
|
||||||
+ m_usbDeviceRemovedNotification->setIconName(QStringLiteral("drive-removable-media-usb"));
|
|
||||||
+ m_usbDeviceRemovedNotification->setTitle(i18nc("@title:notifications", "USB Device Went Away"));
|
|
||||||
+ m_usbDeviceRemovedNotification->setText(text);
|
|
||||||
+ m_usbDeviceRemovedNotification->sendEvent();
|
|
||||||
|
|
||||||
- KNotification::event(QStringLiteral("deviceRemoved"),
|
|
||||||
- i18nc("@title:notifications", "USB Device Removed"),
|
|
||||||
- text,
|
|
||||||
- QStringLiteral("drive-removable-media-usb"),
|
|
||||||
- KNotification::DefaultEvent);
|
|
||||||
m_deviceRemovedTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
diff --git a/devicenotifications/devicenotifications.h b/devicenotifications/devicenotifications.h
|
|
||||||
index 11334008b0..ab7e6b3ff9 100644
|
|
||||||
--- a/devicenotifications/devicenotifications.h
|
|
||||||
+++ b/devicenotifications/devicenotifications.h
|
|
||||||
@@ -8,11 +8,13 @@
|
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QList>
|
|
||||||
+#include <QPointer>
|
|
||||||
#include <QSocketNotifier>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include <KDEDModule>
|
|
||||||
+#include <KNotification>
|
|
||||||
|
|
||||||
#include <libudev.h>
|
|
||||||
|
|
||||||
@@ -98,4 +100,7 @@ private:
|
|
||||||
|
|
||||||
QTimer m_deviceAddedTimer;
|
|
||||||
QTimer m_deviceRemovedTimer;
|
|
||||||
+
|
|
||||||
+ QPointer<KNotification> m_usbDeviceAddedNotification;
|
|
||||||
+ QPointer<KNotification> m_usbDeviceRemovedNotification;
|
|
||||||
};
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
||||||
From 05f72383fd0b29105f3b5494759500d26b38ffc2 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Fri, 11 Jul 2025 11:25:23 -0600
|
|
||||||
Subject: [PATCH 2/2] Delete closed notifications too
|
|
||||||
|
|
||||||
Closing is async; make sure we actually delete them when we want them
|
|
||||||
gone.
|
|
||||||
---
|
|
||||||
devicenotifications/devicenotifications.cpp | 4 ++++
|
|
||||||
1 file changed, 4 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/devicenotifications/devicenotifications.cpp b/devicenotifications/devicenotifications.cpp
|
|
||||||
index f326691c11..987d65d805 100644
|
|
||||||
--- a/devicenotifications/devicenotifications.cpp
|
|
||||||
+++ b/devicenotifications/devicenotifications.cpp
|
|
||||||
@@ -380,6 +380,7 @@ void KdedDeviceNotifications::onDeviceAdded(const UdevDevice &device)
|
|
||||||
// there's no need to keep the unplug notification around.
|
|
||||||
if (m_usbDeviceRemovedNotification) {
|
|
||||||
m_usbDeviceRemovedNotification->close();
|
|
||||||
+ m_usbDeviceRemovedNotification = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only show one of these at a time. We already suppressed creating a bunch
|
|
||||||
@@ -387,6 +388,7 @@ void KdedDeviceNotifications::onDeviceAdded(const UdevDevice &device)
|
|
||||||
// over that time limit anyway are not necessary to stack up.
|
|
||||||
if (m_usbDeviceAddedNotification) {
|
|
||||||
m_usbDeviceAddedNotification->close();
|
|
||||||
+ m_usbDeviceAddedNotification = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString text = !displayName.isEmpty() ? i18n("%1 has been connected.", displayName.toHtmlEscaped()) : i18n("A USB device has been connected.");
|
|
||||||
@@ -421,6 +423,7 @@ void KdedDeviceNotifications::onDeviceRemoved(const UdevDevice &device)
|
|
||||||
// there's no need to keep the plug notification around.
|
|
||||||
if (m_usbDeviceAddedNotification) {
|
|
||||||
m_usbDeviceAddedNotification->close();
|
|
||||||
+ m_usbDeviceAddedNotification = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only show one of these at a time. We already suppressed removing a bunch
|
|
||||||
@@ -428,6 +431,7 @@ void KdedDeviceNotifications::onDeviceRemoved(const UdevDevice &device)
|
|
||||||
// over that time limit anyway are not necessary to stack up.
|
|
||||||
if (m_usbDeviceRemovedNotification) {
|
|
||||||
m_usbDeviceRemovedNotification->close();
|
|
||||||
+ m_usbDeviceRemovedNotification = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString text = !displayName.isEmpty() ? i18n("%1 has been disconnected.", displayName.toHtmlEscaped()) : i18n("A USB device has been disconnected.");
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
@ -1,392 +0,0 @@
|
||||||
From 97c77a8e3259d77cb615dadd1c92185545513ebb Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Sun, 13 Jul 2025 16:06:08 +0200
|
|
||||||
Subject: [PATCH 1/7] servicerunner: en_US spelling please
|
|
||||||
|
|
||||||
---
|
|
||||||
runners/services/servicerunner.cpp | 14 +++++++-------
|
|
||||||
runners/services/servicerunner.h | 4 ++--
|
|
||||||
2 files changed, 9 insertions(+), 9 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index 454cf4e99f..357558a77d 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -296,7 +296,7 @@ private:
|
|
||||||
relevance += .09;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (const auto foundIt = m_runner->m_favourites.constFind(service->desktopEntryName()); foundIt != m_runner->m_favourites.cend()) {
|
|
||||||
+ if (const auto foundIt = m_runner->m_favorites.constFind(service->desktopEntryName()); foundIt != m_runner->m_favorites.cend()) {
|
|
||||||
if (foundIt->isGlobal || foundIt->linkedActivities.contains(m_currentActivity)) {
|
|
||||||
qCDebug(RUNNER_SERVICES) << "entry is a favorite" << id << match.subtext() << relevance;
|
|
||||||
relevance *= 1.25; // Give favorites a relative boost,
|
|
||||||
@@ -423,7 +423,7 @@ ServiceRunner::ServiceRunner(QObject *parent, const KPluginMetaData &metaData)
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(&m_kactivitiesWatcher, &ResultWatcher::resultUnlinked, [this](QString resource) {
|
|
||||||
- m_favourites.remove(resource.remove(".desktop"_L1));
|
|
||||||
+ m_favorites.remove(resource.remove(".desktop"_L1));
|
|
||||||
// In case it was only unlinked from one activity
|
|
||||||
processActivitiesResults(ResultSet(m_kactivitiesQuery | Terms::Url::contains(resource)));
|
|
||||||
});
|
|
||||||
@@ -466,11 +466,11 @@ void ServiceRunner::processActivitiesResults(const ResultSet &results)
|
|
||||||
const static QLatin1String applicationScheme("applications");
|
|
||||||
for (const ResultSet::Result &result : results) {
|
|
||||||
if (result.url().scheme() == applicationScheme) {
|
|
||||||
- m_favourites.insert(result.url().path().remove(QLatin1String(".desktop")),
|
|
||||||
- ActivityFavourite{
|
|
||||||
- result.linkedActivities(),
|
|
||||||
- result.linkedActivities().contains(globalActivity),
|
|
||||||
- });
|
|
||||||
+ m_favorites.insert(result.url().path().remove(QLatin1String(".desktop")),
|
|
||||||
+ ActivityFavorite{
|
|
||||||
+ result.linkedActivities(),
|
|
||||||
+ result.linkedActivities().contains(globalActivity),
|
|
||||||
+ });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diff --git a/runners/services/servicerunner.h b/runners/services/servicerunner.h
|
|
||||||
index e0507ea459..571d22d90c 100644
|
|
||||||
--- a/runners/services/servicerunner.h
|
|
||||||
+++ b/runners/services/servicerunner.h
|
|
||||||
@@ -33,11 +33,11 @@ public:
|
|
||||||
void run(const KRunner::RunnerContext &context, const KRunner::QueryMatch &match) override;
|
|
||||||
void init() override;
|
|
||||||
|
|
||||||
- struct ActivityFavourite {
|
|
||||||
+ struct ActivityFavorite {
|
|
||||||
QStringList linkedActivities;
|
|
||||||
bool isGlobal;
|
|
||||||
};
|
|
||||||
- QMap<QString, ActivityFavourite> m_favourites;
|
|
||||||
+ QMap<QString, ActivityFavorite> m_favorites;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setupMatch(const KService::Ptr &service, KRunner::QueryMatch &action);
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
||||||
From 537d0cf67d600cb40636f9aaef7db6957f002eb2 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Sun, 13 Jul 2025 16:06:50 +0200
|
|
||||||
Subject: [PATCH 2/7] servicerunner: use designated initializers
|
|
||||||
|
|
||||||
makes code easier to read
|
|
||||||
---
|
|
||||||
runners/services/servicerunner.cpp | 4 ++--
|
|
||||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index 357558a77d..2cade45b26 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -468,8 +468,8 @@ void ServiceRunner::processActivitiesResults(const ResultSet &results)
|
|
||||||
if (result.url().scheme() == applicationScheme) {
|
|
||||||
m_favorites.insert(result.url().path().remove(QLatin1String(".desktop")),
|
|
||||||
ActivityFavorite{
|
|
||||||
- result.linkedActivities(),
|
|
||||||
- result.linkedActivities().contains(globalActivity),
|
|
||||||
+ .linkedActivities = result.linkedActivities(),
|
|
||||||
+ .isGlobal = result.linkedActivities().contains(globalActivity),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
||||||
From 2c5eb156410c022a50a5b6e08a6abc454dd49b83 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Sun, 13 Jul 2025 16:09:37 +0200
|
|
||||||
Subject: [PATCH 3/7] servicerunner: use ranges algorithms
|
|
||||||
|
|
||||||
makes for nicer to read code
|
|
||||||
---
|
|
||||||
runners/services/servicerunner.cpp | 10 +++++-----
|
|
||||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index 2cade45b26..87b38a2da3 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -46,15 +46,15 @@ int weightedLength(const QString &query)
|
|
||||||
|
|
||||||
inline bool contains(const QString &result, const QList<QStringView> &queryList)
|
|
||||||
{
|
|
||||||
- return std::all_of(queryList.cbegin(), queryList.cend(), [&result](QStringView query) {
|
|
||||||
+ return std::ranges::all_of(queryList, [&result](QStringView query) {
|
|
||||||
return result.contains(query, Qt::CaseInsensitive);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool contains(const QStringList &results, const QList<QStringView> &queryList)
|
|
||||||
{
|
|
||||||
- return std::all_of(queryList.cbegin(), queryList.cend(), [&results](QStringView query) {
|
|
||||||
- return std::any_of(results.cbegin(), results.cend(), [&query](QStringView result) {
|
|
||||||
+ return std::ranges::all_of(queryList, [&results](QStringView query) {
|
|
||||||
+ return std::ranges::any_of(results, [&query](QStringView result) {
|
|
||||||
return result.contains(query, Qt::CaseInsensitive);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -327,7 +327,7 @@ private:
|
|
||||||
setupMatch(service, match);
|
|
||||||
|
|
||||||
qreal relevance = 0.4;
|
|
||||||
- if (std::any_of(categories.begin(), categories.end(), [this](const QString &category) {
|
|
||||||
+ if (std::ranges::any_of(categories, [this](const QString &category) {
|
|
||||||
return category.compare(query, Qt::CaseInsensitive) == 0;
|
|
||||||
})) {
|
|
||||||
relevance = 0.6;
|
|
||||||
@@ -499,7 +499,7 @@ void ServiceRunner::run(const KRunner::RunnerContext & /*context*/, const KRunne
|
|
||||||
job = new KIO::ApplicationLauncherJob(service);
|
|
||||||
} else {
|
|
||||||
const auto actions = service->actions();
|
|
||||||
- auto it = std::find_if(actions.begin(), actions.end(), [&actionName](const KServiceAction &action) {
|
|
||||||
+ auto it = std::ranges::find_if(actions, [&actionName](const KServiceAction &action) {
|
|
||||||
return action.name() == actionName;
|
|
||||||
});
|
|
||||||
Q_ASSERT(it != actions.end());
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
||||||
From 0599abb0af9d1da43d8067dd59b8afad7c7be9c6 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Sun, 13 Jul 2025 16:11:05 +0200
|
|
||||||
Subject: [PATCH 4/7] servicerunner: put helper functions into anon namespace
|
|
||||||
|
|
||||||
they are translation unit local after all
|
|
||||||
---
|
|
||||||
runners/services/servicerunner.cpp | 4 ++++
|
|
||||||
1 file changed, 4 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index 87b38a2da3..baef7ae50f 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -38,6 +38,8 @@
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
|
||||||
+namespace
|
|
||||||
+{
|
|
||||||
|
|
||||||
int weightedLength(const QString &query)
|
|
||||||
{
|
|
||||||
@@ -60,6 +62,8 @@ inline bool contains(const QStringList &results, const QList<QStringView> &query
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
+} // namespace
|
|
||||||
+
|
|
||||||
/**
|
|
||||||
* @brief Finds all KServices for a given runner query
|
|
||||||
*/
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
||||||
From 2f81c3ab0520729ed4f97d666b5c74258eed149b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Sun, 13 Jul 2025 16:12:37 +0200
|
|
||||||
Subject: [PATCH 5/7] servicerunner: typos--
|
|
||||||
|
|
||||||
---
|
|
||||||
runners/services/autotests/servicerunnertest.cpp | 4 ++--
|
|
||||||
runners/services/servicerunner.cpp | 10 +++++-----
|
|
||||||
runners/services/servicerunner.h | 2 +-
|
|
||||||
3 files changed, 8 insertions(+), 8 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/runners/services/autotests/servicerunnertest.cpp b/runners/services/autotests/servicerunnertest.cpp
|
|
||||||
index ecb8a4816c..fcfd3275ac 100644
|
|
||||||
--- a/runners/services/autotests/servicerunnertest.cpp
|
|
||||||
+++ b/runners/services/autotests/servicerunnertest.cpp
|
|
||||||
@@ -27,7 +27,7 @@ private Q_SLOTS:
|
|
||||||
void initTestCase();
|
|
||||||
void cleanupTestCase();
|
|
||||||
|
|
||||||
- void testExcutableExactMatch();
|
|
||||||
+ void testExecutableExactMatch();
|
|
||||||
void testKonsoleVsYakuakeComment();
|
|
||||||
void testSystemSettings();
|
|
||||||
void testSystemSettings2();
|
|
||||||
@@ -76,7 +76,7 @@ void ServiceRunnerTest::cleanupTestCase()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
-void ServiceRunnerTest::testExcutableExactMatch()
|
|
||||||
+void ServiceRunnerTest::testExecutableExactMatch()
|
|
||||||
{
|
|
||||||
const auto matches = launchQuery(QStringLiteral("Virtual Machine Manager ServiceRunnerTest")); // virt-manager.desktop
|
|
||||||
QVERIFY(std::any_of(matches.cbegin(), matches.cend(), [](const KRunner::QueryMatch &match) {
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index baef7ae50f..ced1b526ce 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -125,7 +125,7 @@ private:
|
|
||||||
GenericName,
|
|
||||||
Comment,
|
|
||||||
};
|
|
||||||
- qreal increaseMatchRelavance(const QString &serviceProperty, const QList<QStringView> &strList, Category category)
|
|
||||||
+ qreal increaseMatchRelevance(const QString &serviceProperty, const QList<QStringView> &strList, Category category)
|
|
||||||
{
|
|
||||||
// Increment the relevance based on all the words (other than the first) of the query list
|
|
||||||
qreal relevanceIncrement = 0;
|
|
||||||
@@ -273,20 +273,20 @@ private:
|
|
||||||
categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Highest;
|
|
||||||
} else if (const int idx = name.indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
relevance = 0.8;
|
|
||||||
- relevance += increaseMatchRelavance(name, queryList, Category::Name);
|
|
||||||
+ relevance += increaseMatchRelevance(name, queryList, Category::Name);
|
|
||||||
if (idx == 0) {
|
|
||||||
relevance += 0.1;
|
|
||||||
categoryRelevance = KRunner::QueryMatch::CategoryRelevance::High;
|
|
||||||
}
|
|
||||||
} else if (const int idx = service->genericName().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
relevance = 0.65;
|
|
||||||
- relevance += increaseMatchRelavance(service->genericName(), queryList, Category::GenericName);
|
|
||||||
+ relevance += increaseMatchRelevance(service->genericName(), queryList, Category::GenericName);
|
|
||||||
if (idx == 0) {
|
|
||||||
relevance += 0.05;
|
|
||||||
}
|
|
||||||
} else if (const int idx = service->comment().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
relevance = 0.5;
|
|
||||||
- relevance += increaseMatchRelavance(service->comment(), queryList, Category::Comment);
|
|
||||||
+ relevance += increaseMatchRelevance(service->comment(), queryList, Category::Comment);
|
|
||||||
if (idx == 0) {
|
|
||||||
relevance += 0.05;
|
|
||||||
}
|
|
||||||
@@ -481,7 +481,7 @@ void ServiceRunner::processActivitiesResults(const ResultSet &results)
|
|
||||||
|
|
||||||
void ServiceRunner::match(KRunner::RunnerContext &context)
|
|
||||||
{
|
|
||||||
- ServiceFinder finder(this, m_services, m_activitiesConsuer.currentActivity());
|
|
||||||
+ ServiceFinder finder(this, m_services, m_activitiesConsumer.currentActivity());
|
|
||||||
finder.match(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.h b/runners/services/servicerunner.h
|
|
||||||
index 571d22d90c..96a110789b 100644
|
|
||||||
--- a/runners/services/servicerunner.h
|
|
||||||
+++ b/runners/services/servicerunner.h
|
|
||||||
@@ -46,7 +46,7 @@ private:
|
|
||||||
void processActivitiesResults(const ResultSet &results);
|
|
||||||
const Query m_kactivitiesQuery;
|
|
||||||
const ResultWatcher m_kactivitiesWatcher;
|
|
||||||
- const KActivities::Consumer m_activitiesConsuer;
|
|
||||||
+ const KActivities::Consumer m_activitiesConsumer;
|
|
||||||
QList<KService::Ptr> m_services;
|
|
||||||
bool m_matching = false;
|
|
||||||
};
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
||||||
From d25a269e9dbf6209ae51f94c298cb1ef640b045c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Sun, 13 Jul 2025 16:14:53 +0200
|
|
||||||
Subject: [PATCH 6/7] servicerunner: don't narrow qsizetype to int
|
|
||||||
|
|
||||||
use auto instead since we don't actually care about their size anyway
|
|
||||||
since we only perform trivial >=0 checks
|
|
||||||
---
|
|
||||||
runners/services/servicerunner.cpp | 10 +++++-----
|
|
||||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index ced1b526ce..551717947f 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -206,7 +206,7 @@ private:
|
|
||||||
static const auto specialArgs = {QStringLiteral("-qwindowtitle"), QStringLiteral("-qwindowicon"), QStringLiteral("--started-from-file")};
|
|
||||||
|
|
||||||
for (const auto &specialArg : specialArgs) {
|
|
||||||
- int index = resultingArgs.indexOf(specialArg);
|
|
||||||
+ auto index = resultingArgs.indexOf(specialArg);
|
|
||||||
if (index > -1) {
|
|
||||||
if (resultingArgs.count() > index) {
|
|
||||||
resultingArgs.removeAt(index);
|
|
||||||
@@ -271,20 +271,20 @@ private:
|
|
||||||
} else if (name.compare(query, Qt::CaseInsensitive) == 0) {
|
|
||||||
relevance = 1;
|
|
||||||
categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Highest;
|
|
||||||
- } else if (const int idx = name.indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
+ } else if (const auto idx = name.indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
relevance = 0.8;
|
|
||||||
relevance += increaseMatchRelevance(name, queryList, Category::Name);
|
|
||||||
if (idx == 0) {
|
|
||||||
relevance += 0.1;
|
|
||||||
categoryRelevance = KRunner::QueryMatch::CategoryRelevance::High;
|
|
||||||
}
|
|
||||||
- } else if (const int idx = service->genericName().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
+ } else if (const auto idx = service->genericName().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
relevance = 0.65;
|
|
||||||
relevance += increaseMatchRelevance(service->genericName(), queryList, Category::GenericName);
|
|
||||||
if (idx == 0) {
|
|
||||||
relevance += 0.05;
|
|
||||||
}
|
|
||||||
- } else if (const int idx = service->comment().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
+ } else if (const auto idx = service->comment().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
relevance = 0.5;
|
|
||||||
relevance += increaseMatchRelevance(service->comment(), queryList, Category::Comment);
|
|
||||||
if (idx == 0) {
|
|
||||||
@@ -364,7 +364,7 @@ private:
|
|
||||||
}
|
|
||||||
seen(action);
|
|
||||||
|
|
||||||
- const int matchIndex = action.text().indexOf(query, 0, Qt::CaseInsensitive);
|
|
||||||
+ const auto matchIndex = action.text().indexOf(query, 0, Qt::CaseInsensitive);
|
|
||||||
if (matchIndex < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
||||||
From 1a14af41b78a192d10fb5dcef93bba430872eab4 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Sun, 13 Jul 2025 16:15:55 +0200
|
|
||||||
Subject: [PATCH 7/7] servicerunner: remove inline noise
|
|
||||||
|
|
||||||
functions defined inside a definition are always inline
|
|
||||||
---
|
|
||||||
runners/services/servicerunner.cpp | 8 ++++----
|
|
||||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index 551717947f..eb9f02e74b 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -92,22 +92,22 @@ public:
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
- inline void seen(const KService::Ptr &service)
|
|
||||||
+ void seen(const KService::Ptr &service)
|
|
||||||
{
|
|
||||||
m_seen.insert(service->exec());
|
|
||||||
}
|
|
||||||
|
|
||||||
- inline void seen(const KServiceAction &action)
|
|
||||||
+ void seen(const KServiceAction &action)
|
|
||||||
{
|
|
||||||
m_seen.insert(action.exec());
|
|
||||||
}
|
|
||||||
|
|
||||||
- inline bool hasSeen(const KService::Ptr &service)
|
|
||||||
+ bool hasSeen(const KService::Ptr &service)
|
|
||||||
{
|
|
||||||
return m_seen.contains(service->exec());
|
|
||||||
}
|
|
||||||
|
|
||||||
- inline bool hasSeen(const KServiceAction &action)
|
|
||||||
+ bool hasSeen(const KServiceAction &action)
|
|
||||||
{
|
|
||||||
return m_seen.contains(action.exec());
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
@ -1,913 +0,0 @@
|
||||||
From 312c215e717654e55fa48ec968f412201d2a5544 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Harald Sitter <sitter@kde.org>
|
|
||||||
Date: Mon, 14 Jul 2025 17:28:14 +0200
|
|
||||||
Subject: [PATCH] servicerunner: fuzzy match
|
|
||||||
|
|
||||||
use a bitap implementation instead of doing awkward contains dances.
|
|
||||||
this should lead to somewhat more reliable results, which are now more
|
|
||||||
comprehensively asserted in the unit test
|
|
||||||
|
|
||||||
at the heart of this is a new fuzzyScore function that assigns a score
|
|
||||||
to a service vis a vis a query. this score is adjusted depending on
|
|
||||||
which field it is regarding (name > genericname > keywords).
|
|
||||||
this should hopefully ensure that a match against name outweighs most
|
|
||||||
other matches. all scores are eventually assembled into a final score
|
|
||||||
that gets used as match relevance
|
|
||||||
---
|
|
||||||
runners/services/autotests/CMakeLists.txt | 3 +
|
|
||||||
runners/services/autotests/bitaptest.cpp | 70 +++++
|
|
||||||
.../autotests/fixtures/audacity.desktop | 2 +-
|
|
||||||
.../fixtures/org.kde.discover.desktop | 17 ++
|
|
||||||
.../autotests/fixtures/org.kde.kpat.desktop | 2 +-
|
|
||||||
.../services/autotests/servicerunnertest.cpp | 94 ++++--
|
|
||||||
runners/services/bitap.h | 178 +++++++++++
|
|
||||||
runners/services/levenshtein.h | 58 ++++
|
|
||||||
runners/services/servicerunner.cpp | 286 +++++++++++-------
|
|
||||||
9 files changed, 576 insertions(+), 134 deletions(-)
|
|
||||||
create mode 100644 runners/services/autotests/bitaptest.cpp
|
|
||||||
create mode 100755 runners/services/autotests/fixtures/org.kde.discover.desktop
|
|
||||||
create mode 100644 runners/services/bitap.h
|
|
||||||
create mode 100644 runners/services/levenshtein.h
|
|
||||||
|
|
||||||
diff --git a/runners/services/autotests/CMakeLists.txt b/runners/services/autotests/CMakeLists.txt
|
|
||||||
index 04849a2928..ff7ec66634 100644
|
|
||||||
--- a/runners/services/autotests/CMakeLists.txt
|
|
||||||
+++ b/runners/services/autotests/CMakeLists.txt
|
|
||||||
@@ -6,3 +6,6 @@ remove_definitions(-DQT_NO_CAST_FROM_ASCII)
|
|
||||||
ecm_add_test(servicerunnertest.cpp TEST_NAME servicerunnertest
|
|
||||||
LINK_LIBRARIES Qt::Test KF6::Service KF6::Runner)
|
|
||||||
krunner_configure_test(servicerunnertest krunner_services)
|
|
||||||
+
|
|
||||||
+ecm_add_test(bitaptest.cpp TEST_NAME bitaptest
|
|
||||||
+ LINK_LIBRARIES Qt::Test)
|
|
||||||
diff --git a/runners/services/autotests/bitaptest.cpp b/runners/services/autotests/bitaptest.cpp
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000000..1a1cb856ec
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/runners/services/autotests/bitaptest.cpp
|
|
||||||
@@ -0,0 +1,70 @@
|
|
||||||
+// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
+// SPDX-FileCopyrightText: 2025 Harald Sitter <sitter@kde.org>
|
|
||||||
+
|
|
||||||
+#include <QDebug>
|
|
||||||
+#include <QDir>
|
|
||||||
+#include <QFile>
|
|
||||||
+#include <QObject>
|
|
||||||
+#include <QStandardPaths>
|
|
||||||
+#include <QTest>
|
|
||||||
+#include <QThread>
|
|
||||||
+
|
|
||||||
+#include "../bitap.h"
|
|
||||||
+
|
|
||||||
+class BitapTest : public QObject
|
|
||||||
+{
|
|
||||||
+ Q_OBJECT
|
|
||||||
+private Q_SLOTS:
|
|
||||||
+ void initTestCase()
|
|
||||||
+ {
|
|
||||||
+ }
|
|
||||||
+ void cleanupTestCase()
|
|
||||||
+ {
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ void testBitap()
|
|
||||||
+ {
|
|
||||||
+ using namespace Bitap;
|
|
||||||
+ // The macro has trouble with designated initializers, so we wrap them in ().
|
|
||||||
+ QCOMPARE(bitap(u"hello world", u"hello", 1), (Match{.end = 4, .distance = 0}));
|
|
||||||
+ QCOMPARE(bitap(u"wireshark", u"di", 1), (Match{.end = 1, .distance = 1}));
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"disk", 1), (Match{.end = 2, .distance = 1}));
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"disc", 1), (Match{.end = 3, .distance = 0}));
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"scov", 1), (Match{.end = 5, .distance = 0}));
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"diki", 1), std::nullopt);
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"obo", 1), std::nullopt);
|
|
||||||
+ // With a hamming distance of 1 this may match because it is a single transposition.
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"dicsover", 1), (Match{.end = 7, .distance = 1}));
|
|
||||||
+ // … but with three characters out of place things should not match.
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"dicosver", 1), std::nullopt);
|
|
||||||
+ // pattern too long
|
|
||||||
+ QCOMPARE(bitap(u"discover", u" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", 1), std::nullopt);
|
|
||||||
+ // This is not a transposition as per Damerau–Levenshtein distance because the characters are not adjacent.
|
|
||||||
+ QCOMPARE(bitap(u"steam", u"skeap", 1), std::nullopt);
|
|
||||||
+ // Deletion required
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"discover", 1), (Match{.end = 7, .distance = 0}));
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"discovery", 1), (Match{.end = 7, .distance = 1}));
|
|
||||||
+ // Insertion required
|
|
||||||
+ QCOMPARE(bitap(u"discover", u"dicover", 1), (Match{.end = 7, .distance = 1}));
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ void testScore()
|
|
||||||
+ {
|
|
||||||
+ using namespace Bitap;
|
|
||||||
+ // aperfectten has 10 big beautiful indexes. The maximum end is therefore 10.
|
|
||||||
+ QCOMPARE(score(u"aperfectten", Match{.end = 10, .distance = 0}, 1), 1.0);
|
|
||||||
+ QCOMPARE(score(u"aperfectten", Match{.end = 4, .distance = 0}, 1), 0.4);
|
|
||||||
+ QCOMPARE(score(u"aperfectten", Match{.end = 4, .distance = 1}, 1), 0.35);
|
|
||||||
+ QCOMPARE(score(u"aperfectten", Match{.end = 0, .distance = 0}, 0), 0);
|
|
||||||
+ QCOMPARE(score(u"aperfectten", Match{.end = 0, .distance = 0}, 1), 0);
|
|
||||||
+ QCOMPARE(score(u"aperfectten", Match{.end = 1, .distance = 1}, 1), 0.05);
|
|
||||||
+
|
|
||||||
+ QCOMPARE(score(u"abc", Match{.end = 2, .distance = 1}, 1), 0.95);
|
|
||||||
+ // Ask for distance 0 but it has a distance so this is a super bad match.
|
|
||||||
+ QCOMPARE(score(u"abc", Match{.end = 2, .distance = 1}, 0), 0);
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+QTEST_MAIN(BitapTest)
|
|
||||||
+
|
|
||||||
+#include "bitaptest.moc"
|
|
||||||
diff --git a/runners/services/autotests/fixtures/audacity.desktop b/runners/services/autotests/fixtures/audacity.desktop
|
|
||||||
index 7613d9f32f..05e1b9d929 100644
|
|
||||||
--- a/runners/services/autotests/fixtures/audacity.desktop
|
|
||||||
+++ b/runners/services/autotests/fixtures/audacity.desktop
|
|
||||||
@@ -1,5 +1,5 @@
|
|
||||||
[Desktop Entry]
|
|
||||||
-Name=Audacity
|
|
||||||
+Name=Audacity ServiceRunnerTest
|
|
||||||
GenericName=Sound Editor
|
|
||||||
Comment=Record and edit audio files
|
|
||||||
Keywords=audio;sound;alsa;jack;editor;
|
|
||||||
diff --git a/runners/services/autotests/fixtures/org.kde.discover.desktop b/runners/services/autotests/fixtures/org.kde.discover.desktop
|
|
||||||
new file mode 100755
|
|
||||||
index 0000000000..978b2b4152
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/runners/services/autotests/fixtures/org.kde.discover.desktop
|
|
||||||
@@ -0,0 +1,17 @@
|
|
||||||
+# SPDX-FileCopyrightText: None
|
|
||||||
+# SPDX-License-Identifier: CC0-1.0
|
|
||||||
+[Desktop Entry]
|
|
||||||
+Name=Discover ServiceRunnerTest
|
|
||||||
+Comment=Install and remove apps and add-ons
|
|
||||||
+MimeType=application/vnd.flatpak;application/vnd.flatpak.repo;application/vnd.flatpak.ref;
|
|
||||||
+Exec=plasma-discover %F
|
|
||||||
+Icon=plasmadiscover
|
|
||||||
+Type=Application
|
|
||||||
+X-DocPath=plasma-discover/index.html
|
|
||||||
+InitialPreference=5
|
|
||||||
+NoDisplay=false
|
|
||||||
+Actions=Updates;
|
|
||||||
+SingleMainWindow=true
|
|
||||||
+GenericName=Software Center
|
|
||||||
+Categories=Qt;KDE;System;
|
|
||||||
+Keywords=program;software;store;repository;package;add;install;uninstall;remove;update;apps;applications;games;flatpak;snap;addons;add-ons;firmware;
|
|
||||||
diff --git a/runners/services/autotests/fixtures/org.kde.kpat.desktop b/runners/services/autotests/fixtures/org.kde.kpat.desktop
|
|
||||||
index 71d7fd2a89..3a91d89afe 100644
|
|
||||||
--- a/runners/services/autotests/fixtures/org.kde.kpat.desktop
|
|
||||||
+++ b/runners/services/autotests/fixtures/org.kde.kpat.desktop
|
|
||||||
@@ -1,7 +1,7 @@
|
|
||||||
# SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
|
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
|
||||||
[Desktop Entry]
|
|
||||||
-Name=KPatience
|
|
||||||
+Name=KPatience ServiceRunnerTest
|
|
||||||
Exec=true -qwindowtitle %c %u
|
|
||||||
Type=Application
|
|
||||||
Icon=kpat
|
|
||||||
diff --git a/runners/services/autotests/servicerunnertest.cpp b/runners/services/autotests/servicerunnertest.cpp
|
|
||||||
index fcfd3275ac..b911667a3b 100644
|
|
||||||
--- a/runners/services/autotests/servicerunnertest.cpp
|
|
||||||
+++ b/runners/services/autotests/servicerunnertest.cpp
|
|
||||||
@@ -36,6 +36,10 @@ private Q_SLOTS:
|
|
||||||
void testINotifyUsage();
|
|
||||||
void testSpecialArgs();
|
|
||||||
void testEnv();
|
|
||||||
+ void testDisassociation();
|
|
||||||
+ void testMultipleKeywords();
|
|
||||||
+ void testMultipleNameWords();
|
|
||||||
+ void testDiscover();
|
|
||||||
};
|
|
||||||
|
|
||||||
void ServiceRunnerTest::initTestCase()
|
|
||||||
@@ -86,8 +90,8 @@ void ServiceRunnerTest::testExecutableExactMatch()
|
|
||||||
|
|
||||||
void ServiceRunnerTest::testKonsoleVsYakuakeComment()
|
|
||||||
{
|
|
||||||
- // Yakuake has konsole mentioned in comment, should be rated lower.
|
|
||||||
- const auto matches = launchQuery(QStringLiteral("kons"));
|
|
||||||
+ // Yakuake has konsole mentioned in comment, should not be listed (if it was it should be lower)
|
|
||||||
+ auto matches = launchQueryAndSort(QStringLiteral("kons"));
|
|
||||||
|
|
||||||
bool konsoleFound = false;
|
|
||||||
bool yakuakeFound = false;
|
|
||||||
@@ -97,17 +101,10 @@ void ServiceRunnerTest::testKonsoleVsYakuakeComment()
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (match.text() == QLatin1String("Konsole ServiceRunnerTest")) {
|
|
||||||
- QCOMPARE(match.relevance(), 0.99);
|
|
||||||
- konsoleFound = true;
|
|
||||||
- } else if (match.text() == QLatin1String("Yakuake ServiceRunnerTest")) {
|
|
||||||
- // Rates lower because it doesn't have it in the name.
|
|
||||||
- QCOMPARE(match.relevance(), 0.59);
|
|
||||||
- yakuakeFound = true;
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
- QVERIFY(konsoleFound);
|
|
||||||
- QVERIFY(yakuakeFound);
|
|
||||||
+ QCOMPARE(texts,
|
|
||||||
+ QStringList({
|
|
||||||
+ u"Konsole ServiceRunnerTest"_s,
|
|
||||||
+ }));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServiceRunnerTest::testSystemSettings()
|
|
||||||
@@ -150,8 +147,9 @@ void ServiceRunnerTest::testSystemSettings2()
|
|
||||||
foreignSystemSettingsFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- QVERIFY(systemSettingsFound);
|
|
||||||
- QVERIFY(!foreignSystemSettingsFound);
|
|
||||||
+
|
|
||||||
+ // The matched texts will contain much more because of the generic search term. Make sure our settings win.
|
|
||||||
+ QCOMPARE(texts.at(0), u"System Settings ServiceRunnerTest"_s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServiceRunnerTest::testCategories()
|
|
||||||
@@ -172,10 +170,6 @@ void ServiceRunnerTest::testCategories()
|
|
||||||
QVERIFY(std::none_of(matches.cbegin(), matches.cend(), [](const KRunner::QueryMatch &match) {
|
|
||||||
return match.text() == QLatin1String("Konsole ServiceRunnerTest");
|
|
||||||
}));
|
|
||||||
-
|
|
||||||
- // Query too short to match any category
|
|
||||||
- matches = launchQuery(QStringLiteral("Dumm"));
|
|
||||||
- QVERIFY(matches.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServiceRunnerTest::testJumpListActions()
|
|
||||||
@@ -234,6 +228,68 @@ void ServiceRunnerTest::testEnv()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
+void ServiceRunnerTest::testDisassociation()
|
|
||||||
+{
|
|
||||||
+ // This test makes sure that we do not associate a service with a query that is not relevant.
|
|
||||||
+ auto matches = launchQueryAndSort(u"new laptop com"_s); // particularly notorious because it has two three letter words; 'com' is an incomplete word
|
|
||||||
+
|
|
||||||
+ QStringList texts;
|
|
||||||
+ for (const auto &match : matches) {
|
|
||||||
+ texts.push_back(match.text());
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ QCOMPARE(texts, QStringList());
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void ServiceRunnerTest::testMultipleKeywords()
|
|
||||||
+{
|
|
||||||
+ auto matches = launchQueryAndSort(u"text editor programming"_s);
|
|
||||||
+
|
|
||||||
+ QStringList texts;
|
|
||||||
+ for (const auto &match : matches) {
|
|
||||||
+ texts.push_back(match.text());
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ QCOMPARE(texts,
|
|
||||||
+ QStringList({
|
|
||||||
+ u"Kate ServiceRunnerTest"_s,
|
|
||||||
+ }));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void ServiceRunnerTest::testMultipleNameWords()
|
|
||||||
+{
|
|
||||||
+ auto matches = launchQueryAndSort(u"system settings"_s);
|
|
||||||
+
|
|
||||||
+ QStringList texts;
|
|
||||||
+ for (const auto &match : matches) {
|
|
||||||
+ if (!match.text().contains("ServiceRunnerTest"_L1)) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ texts.push_back(match.text());
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ QCOMPARE(texts,
|
|
||||||
+ QStringList({
|
|
||||||
+ u"System Settings ServiceRunnerTest"_s,
|
|
||||||
+ }));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void ServiceRunnerTest::testDiscover()
|
|
||||||
+{
|
|
||||||
+ auto matches = launchQueryAndSort(u"disco"_s);
|
|
||||||
+
|
|
||||||
+ QStringList texts;
|
|
||||||
+ for (const auto &match : matches) {
|
|
||||||
+ texts.push_back(match.text());
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ qDebug() << texts;
|
|
||||||
+ QCOMPARE(texts,
|
|
||||||
+ QStringList({
|
|
||||||
+ u"Discover ServiceRunnerTest"_s,
|
|
||||||
+ }));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
QTEST_MAIN(ServiceRunnerTest)
|
|
||||||
|
|
||||||
#include "servicerunnertest.moc"
|
|
||||||
diff --git a/runners/services/bitap.h b/runners/services/bitap.h
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000000..a6aedb7eaf
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/runners/services/bitap.h
|
|
||||||
@@ -0,0 +1,178 @@
|
|
||||||
+// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
+// SPDX-FileCopyrightText: 2025 Harald Sitter <sitter@kde.org>
|
|
||||||
+
|
|
||||||
+#pragma once
|
|
||||||
+
|
|
||||||
+#include <bitset>
|
|
||||||
+#include <optional>
|
|
||||||
+
|
|
||||||
+#include <QDebug>
|
|
||||||
+#include <QLoggingCategory>
|
|
||||||
+#include <QString>
|
|
||||||
+
|
|
||||||
+namespace Bitap
|
|
||||||
+{
|
|
||||||
+
|
|
||||||
+Q_DECLARE_LOGGING_CATEGORY(BITAP)
|
|
||||||
+Q_LOGGING_CATEGORY(BITAP, "org.kde.plasma.runner.services.bitap", QtWarningMsg)
|
|
||||||
+
|
|
||||||
+struct Match {
|
|
||||||
+ qsizetype end;
|
|
||||||
+ qsizetype distance;
|
|
||||||
+
|
|
||||||
+ bool operator==(const Match &other) const = default;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+inline QDebug operator<<(QDebug dbg, const Bitap::Match &match)
|
|
||||||
+{
|
|
||||||
+ dbg.nospace() << "Bitap::Match(" << match.end << ", " << match.distance << ")";
|
|
||||||
+ return dbg;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+// Bitap is a bit of a complicated algorithm thanks to bitwise operations. I've opted to replace them with bitsets for readability.
|
|
||||||
+// It creates a patternMask based on all characters in the pattern. Basically each character gets assigned a representative bit.
|
|
||||||
+// e.g. in the pattern 'abc' the character 'a' would be 110, 'b' 101, 'c' 011.
|
|
||||||
+// This is a bit expensive up front but allows it to carry out everything else using bitwise operations.
|
|
||||||
+// For each match we set a matching bit in the bits vector.
|
|
||||||
+// Matching happens within a hamming distance, meaning up to `hammingDistance` characters can be out of place.
|
|
||||||
+inline std::optional<Match> bitap(const QStringView &name, const QStringView &pattern, int hammingDistance)
|
|
||||||
+{
|
|
||||||
+ qCDebug(BITAP) << "Bitap called with name:" << name << "and pattern:" << pattern << "with hamming distance:" << hammingDistance;
|
|
||||||
+ const auto patternEndIndex = pattern.size() - 1;
|
|
||||||
+ if (name == pattern) {
|
|
||||||
+ return Match{.end = patternEndIndex, .distance = 0}; // Perfect match
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (pattern.isEmpty() || name.isEmpty()) {
|
|
||||||
+ return std::nullopt;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Being a bitset we could have any number of bits, but practically we probably don't need more than 64, most bitaps I've seen even use 32.
|
|
||||||
+ constexpr auto maxMaskBits = 64;
|
|
||||||
+ using Mask = std::bitset<maxMaskBits>;
|
|
||||||
+ using PatternMask = std::array<Mask, std::numeric_limits<char16_t>::max()>;
|
|
||||||
+
|
|
||||||
+ // The way bitap works is that each bit of the Mask represents a character position. Because of this we cannot match
|
|
||||||
+ // more characters than we have bits for.
|
|
||||||
+ // -1 because one bit is used for the result (I think)
|
|
||||||
+ if (pattern.size() >= qsizetype(Mask().size()) - 1) {
|
|
||||||
+ qCWarning(BITAP) << "Pattern is too long for bitap algorithm, max length is" << Mask().size() - 1;
|
|
||||||
+ return std::nullopt;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ const PatternMask patternMask = [&pattern, &name] {
|
|
||||||
+ PatternMask patternMask;
|
|
||||||
+ // The following is an optimized version of patternMask.fill(Mask().set()); to set all **necessary** bits to 1.
|
|
||||||
+ for (const auto &qchar : pattern) {
|
|
||||||
+ patternMask.at(qchar.unicode()).set();
|
|
||||||
+ }
|
|
||||||
+ for (const auto &qchar : name) {
|
|
||||||
+ patternMask.at(qchar.unicode()).set();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for (int i = 0; i < pattern.size(); ++i) {
|
|
||||||
+ const auto char_ = pattern.at(i).unicode();
|
|
||||||
+ patternMask.at(char_).reset(i); // unset the relevant index bits
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (BITAP().isDebugEnabled()) {
|
|
||||||
+ for (const auto &i : pattern) {
|
|
||||||
+ const auto char_ = i.unicode();
|
|
||||||
+ qCDebug(BITAP) << "Pattern mask for" << char_ << "is" << patternMask.at(char_).to_string();
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return patternMask;
|
|
||||||
+ }();
|
|
||||||
+
|
|
||||||
+ Match match{
|
|
||||||
+ .end = -1, // -1 means no match found for convenience
|
|
||||||
+ .distance = name.size(),
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ std::vector<Mask> bits((hammingDistance + 1), Mask().set().reset(0));
|
|
||||||
+ std::vector<Mask> transpositions(bits.cbegin(), bits.cend());
|
|
||||||
+ for (int i = 0; i < name.size(); ++i) {
|
|
||||||
+ const auto &char_ = name.at(i);
|
|
||||||
+ auto previousBit = bits[0];
|
|
||||||
+ const auto mask = patternMask.at(char_.unicode());
|
|
||||||
+ bits[0] |= mask;
|
|
||||||
+ bits[0] <<= 1;
|
|
||||||
+
|
|
||||||
+ for (int j = 1; j <= hammingDistance; ++j) {
|
|
||||||
+ auto bit = bits[j];
|
|
||||||
+ auto current = (bit | mask) << 1;
|
|
||||||
+ // https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
|
|
||||||
+ auto substitute = previousBit << 1;
|
|
||||||
+ auto delete_ = bits[j - 1] << 1;
|
|
||||||
+ auto insert = previousBit;
|
|
||||||
+ auto transpose = (transpositions[j - 1] | (mask << 1)) << 1;
|
|
||||||
+ bits[j] = current & substitute & transpose & delete_ & insert;
|
|
||||||
+ transpositions[j - 1] = (previousBit << 1) | mask;
|
|
||||||
+ previousBit = bit;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (BITAP().isDebugEnabled()) {
|
|
||||||
+ qCDebug(BITAP) << "After processing character" << char_ << "at index" << i;
|
|
||||||
+ for (const auto &bit : bits) {
|
|
||||||
+ qCDebug(BITAP) << "bit" << bit.to_string();
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for (int k = 0; k <= hammingDistance; ++k) {
|
|
||||||
+ // If the bit at the end of the mask is 0, it means we have a match.
|
|
||||||
+ if (0 == (bits[k] & Mask().set(pattern.size()))) {
|
|
||||||
+ if (k < match.distance && match.end < i) {
|
|
||||||
+ qCDebug(BITAP) << "Match found at index" << i << "with hamming distance" << k << "better than previous match with distance"
|
|
||||||
+ << match.distance << "at index" << match.end;
|
|
||||||
+ match = {
|
|
||||||
+ .end = i,
|
|
||||||
+ .distance = k,
|
|
||||||
+ };
|
|
||||||
+ }
|
|
||||||
+ // We do not return early because we want to find the best match, not just any.
|
|
||||||
+ // e.g. with a maximum distance of 1 `disc` could match `disc` either at index two with distance one, or at index three with distance zero.
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Because we use a complete Damerau–Levenshtein distance the return value is a bit complicated. The trick is that the distance incurs a negative penalty
|
|
||||||
+ // in relation to the max distance. While an end that is closer to the real end is generally favorably. Combining the two into a single value
|
|
||||||
+ // would complicate the meaning of the return value to mean "approximate end with random penalty". This is garbage to reason about so instead we return
|
|
||||||
+ // both values and then assign them meaning in the score function.
|
|
||||||
+ if (match.end != -1) {
|
|
||||||
+ return match;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ qCDebug(BITAP) << "No match found for pattern" << pattern << "in name" << name;
|
|
||||||
+ return std::nullopt;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+inline qreal score(const QStringView &name, const auto &match, auto hammingDistance)
|
|
||||||
+{
|
|
||||||
+ // Normalize the score to a value between 0.0 and 1.0
|
|
||||||
+ // No distance means the score is directly correlated to the end index. The more characters matched the higher the score.
|
|
||||||
+ // Any distance will lower the score by a sub 0.1 margin.
|
|
||||||
+
|
|
||||||
+ if (name.size() == 0) {
|
|
||||||
+ return 0.0; // No name, no score.
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ const auto maxEnd = name.size() - 1;
|
|
||||||
+ const auto penalty = [&] {
|
|
||||||
+ if (hammingDistance <= 0) {
|
|
||||||
+ return 1.0; // No penalty for no distance
|
|
||||||
+ }
|
|
||||||
+ constexpr auto tenth = 10.0;
|
|
||||||
+ constexpr auto half = 2.0;
|
|
||||||
+ return qreal(match.distance) / qreal(hammingDistance) / tenth / half;
|
|
||||||
+ }();
|
|
||||||
+ auto score = qreal(match.end) / qreal(maxEnd);
|
|
||||||
+ // Prevent underflows when the penalty is larger than the score.
|
|
||||||
+ score = std::max(0.0, score - penalty);
|
|
||||||
+
|
|
||||||
+ Q_ASSERT(score >= 0.0 && score <= 1.0);
|
|
||||||
+ return score;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+} // namespace Bitap
|
|
||||||
diff --git a/runners/services/levenshtein.h b/runners/services/levenshtein.h
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000000..0efb960be3
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/runners/services/levenshtein.h
|
|
||||||
@@ -0,0 +1,58 @@
|
|
||||||
+// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
+// SPDX-FileCopyrightText: 2025 Harald Sitter <sitter@kde.org>
|
|
||||||
+
|
|
||||||
+#pragma
|
|
||||||
+
|
|
||||||
+#include <QLoggingCategory>
|
|
||||||
+#include <QString>
|
|
||||||
+
|
|
||||||
+namespace Levenshtein
|
|
||||||
+{
|
|
||||||
+
|
|
||||||
+inline int distance(const QStringView &name, const QStringView &query)
|
|
||||||
+{
|
|
||||||
+ if (name == query) {
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ std::vector<int> distance0(query.size() + 1, 0);
|
|
||||||
+ std::vector<int> distance1(query.size() + 1, 0);
|
|
||||||
+
|
|
||||||
+ for (int i = 0; i <= query.size(); ++i) {
|
|
||||||
+ distance0[i] = i;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for (int i = 0; i < name.size(); ++i) {
|
|
||||||
+ distance1[0] = i + 1;
|
|
||||||
+ for (int j = 0; j < query.size(); ++j) {
|
|
||||||
+ const auto deletionCost = distance0[j + 1] + 1;
|
|
||||||
+ const auto insertionCost = distance1[j] + 1;
|
|
||||||
+ const auto substitutionCost = [&] {
|
|
||||||
+ if (name[i] == query[j]) {
|
|
||||||
+ return distance0[j];
|
|
||||||
+ }
|
|
||||||
+ return distance0[j] + 1;
|
|
||||||
+ }();
|
|
||||||
+ distance1[j + 1] = std::min({deletionCost, insertionCost, substitutionCost});
|
|
||||||
+ }
|
|
||||||
+ std::swap(distance0, distance1);
|
|
||||||
+ }
|
|
||||||
+ return distance0[query.size()];
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+inline qreal score(const QStringView &name, int distance)
|
|
||||||
+{
|
|
||||||
+ // Normalize the distance to a value between 0.0 and 1.0
|
|
||||||
+ // The maximum distance is the length of the pattern.
|
|
||||||
+ // If the distance is 0, it means a perfect match, so we return 1.0.
|
|
||||||
+ // If the distance is equal to the length of the pattern, we return 0.0.
|
|
||||||
+ if (distance == 0) {
|
|
||||||
+ return 1.0;
|
|
||||||
+ }
|
|
||||||
+ if (distance >= name.size()) {
|
|
||||||
+ return 0.0;
|
|
||||||
+ }
|
|
||||||
+ return 1.0 - (qreal(distance) / qreal(name.size()));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+} // namespace Levenshtein
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index eb9f02e74b..3d5de8feb2 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -1,7 +1,7 @@
|
|
||||||
/*
|
|
||||||
SPDX-FileCopyrightText: 2006 Aaron Seigo <aseigo@kde.org>
|
|
||||||
SPDX-FileCopyrightText: 2014 Vishesh Handa <vhanda@kde.org>
|
|
||||||
- SPDX-FileCopyrightText: 2016-2020 Harald Sitter <sitter@kde.org>
|
|
||||||
+ SPDX-FileCopyrightText: 2016-2025 Harald Sitter <sitter@kde.org>
|
|
||||||
SPDX-FileCopyrightText: 2022-2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.0-only
|
|
||||||
@@ -21,6 +21,7 @@
|
|
||||||
#include <QUrlQuery>
|
|
||||||
|
|
||||||
#include <KApplicationTrader>
|
|
||||||
+#include <KFuzzyMatcher>
|
|
||||||
#include <KLocalizedString>
|
|
||||||
#include <KNotificationJobUiDelegate>
|
|
||||||
#include <KServiceAction>
|
|
||||||
@@ -35,22 +36,130 @@
|
|
||||||
#include <KIO/ApplicationLauncherJob>
|
|
||||||
#include <KIO/DesktopExecParser>
|
|
||||||
|
|
||||||
+#include "bitap.h"
|
|
||||||
#include "debug.h"
|
|
||||||
+#include "levenshtein.h"
|
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
-int weightedLength(const QString &query)
|
|
||||||
+struct Score {
|
|
||||||
+ qreal value = 0.0; // The final score, it is the sum of all scores.
|
|
||||||
+ KRunner::QueryMatch::CategoryRelevance categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Lowest; // The category relevance of the match.
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ScoreCard {
|
|
||||||
+ Bitap::Match bitap;
|
|
||||||
+ qreal bitapScore;
|
|
||||||
+ int levenshtein;
|
|
||||||
+ qreal levenshteinScore;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+QDebug operator<<(QDebug dbg, const ScoreCard &card)
|
|
||||||
{
|
|
||||||
- return KStringHandler::logicalLength(query);
|
|
||||||
+ dbg.nospace() << "Scorecard(" << "bitap: " << card.bitap << ", bitapScore: " << card.bitapScore << ", levenshtein: " << card.levenshtein
|
|
||||||
+ << ", levenshteinScore: " << card.levenshteinScore << ")";
|
|
||||||
+ return dbg;
|
|
||||||
}
|
|
||||||
|
|
||||||
-inline bool contains(const QString &result, const QList<QStringView> &queryList)
|
|
||||||
+using ScoreCards = std::vector<ScoreCard>;
|
|
||||||
+
|
|
||||||
+struct WeightedScoreCard {
|
|
||||||
+ ScoreCards cards;
|
|
||||||
+ qreal weight;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+QDebug operator<<(QDebug dbg, const WeightedScoreCard &card)
|
|
||||||
{
|
|
||||||
- return std::ranges::all_of(queryList, [&result](QStringView query) {
|
|
||||||
- return result.contains(query, Qt::CaseInsensitive);
|
|
||||||
- });
|
|
||||||
+
|
|
||||||
+ dbg.nospace() << "WeightedCard[";
|
|
||||||
+ for (const auto &scoreCard : card.cards) {
|
|
||||||
+ dbg.nospace() << scoreCard;
|
|
||||||
+ if (&scoreCard != &card.cards.back()) {
|
|
||||||
+ dbg.nospace() << ", ";
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ dbg.nospace() << "]";
|
|
||||||
+ return dbg;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+auto makeScores(const auto ¬NormalizedString, const auto &queryList) {
|
|
||||||
+ if (notNormalizedString.isEmpty()) {
|
|
||||||
+ return ScoreCards{}; // No string, no score.
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ const auto string = notNormalizedString.toLower();
|
|
||||||
+
|
|
||||||
+ ScoreCards cards;
|
|
||||||
+ for (const auto &queryItem : queryList) {
|
|
||||||
+ constexpr auto maxDistance = 1;
|
|
||||||
+ const auto bitap = Bitap::bitap(string, queryItem, maxDistance);
|
|
||||||
+ if (!bitap) {
|
|
||||||
+ // One of the query items didn't match. This means the entire query is not a match
|
|
||||||
+ return ScoreCards{};
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ const auto bitapScore = Bitap::score(string, bitap.value(), maxDistance);
|
|
||||||
+
|
|
||||||
+ // Mind that we give different levels of bonus. This is important to imply ordering within competing matches of the same "type".
|
|
||||||
+ // If we perfectly match that gives a bonus for not requiring any changes.
|
|
||||||
+ const auto noSubstitionBonus = Bitap::score(string, bitap.value(), 0) == 1.0 ? 4.0 : 1.0;
|
|
||||||
+ // If we match the entire length of the string that gets a bonus (disregarding distance, that was considered above).
|
|
||||||
+ const auto completeMatchBonus = bitap->end >= (queryItem.size() - 1) ? 3.0 : 1.0;
|
|
||||||
+ // If the string starts with the query item that gets a bonus.
|
|
||||||
+ const auto startsWithBonus = (string.startsWith(queryItem, Qt::CaseInsensitive)) ? 2.0 : 1.0;
|
|
||||||
+
|
|
||||||
+ // Also consider the distance between the input and the query item.
|
|
||||||
+ // If one is "yolotrollingservice" and the other is "yolo" then we must consider them worse matches than say "yolotroll".
|
|
||||||
+ const auto levenshtein = Levenshtein::distance(string, queryItem);
|
|
||||||
+
|
|
||||||
+ cards.emplace_back(ScoreCard{
|
|
||||||
+ .bitap = *bitap,
|
|
||||||
+ .bitapScore = bitapScore + completeMatchBonus + noSubstitionBonus + startsWithBonus,
|
|
||||||
+ .levenshtein = levenshtein,
|
|
||||||
+ .levenshteinScore = Levenshtein::score(string, levenshtein),
|
|
||||||
+ });
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return cards;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+auto makeScoreFromList(const auto &queryList, const QStringList &strings) {
|
|
||||||
+ // This turns the loop inside out. For every query item we must find a match in our keywords or we discard
|
|
||||||
+ ScoreCards cards;
|
|
||||||
+ // e.g. text,editor,programming
|
|
||||||
+ for (const auto &queryItem : queryList) {
|
|
||||||
+ // e.g. text;txt;editor;programming;programmer;development;developer;code;
|
|
||||||
+ auto found = false;
|
|
||||||
+ ScoreCards queryCards;
|
|
||||||
+ for (const auto &string : strings) {
|
|
||||||
+ auto stringCards = makeScores(string, QList{queryItem});
|
|
||||||
+ if (stringCards.empty()) {
|
|
||||||
+ continue; // The combination didn't match.
|
|
||||||
+ }
|
|
||||||
+ for (auto &scoreCard : stringCards) {
|
|
||||||
+ if (scoreCard.levenshteinScore < 0.8) {
|
|
||||||
+ continue; // Not a good match, skip it. We are very strict with keywords
|
|
||||||
+ }
|
|
||||||
+ found = true;
|
|
||||||
+ queryCards.append_range(stringCards);
|
|
||||||
+ }
|
|
||||||
+ // We do not break because other string might also match, improving the score.
|
|
||||||
+ }
|
|
||||||
+ if (!found) {
|
|
||||||
+ // No item in strings matched the query item. This means the entire query is not a match.
|
|
||||||
+ return ScoreCards{};
|
|
||||||
+ }
|
|
||||||
+ cards.append_range(queryCards);
|
|
||||||
+ }
|
|
||||||
+ return cards;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+int weightedLength(const QString &query)
|
|
||||||
+{
|
|
||||||
+ return KStringHandler::logicalLength(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool contains(const QStringList &results, const QList<QStringView> &queryList)
|
|
||||||
@@ -79,7 +188,7 @@ public:
|
|
||||||
|
|
||||||
void match(KRunner::RunnerContext &context)
|
|
||||||
{
|
|
||||||
- query = context.query();
|
|
||||||
+ query = context.query().toLower();
|
|
||||||
// Splitting the query term to match using subsequences
|
|
||||||
queryList = QStringView(query).split(QLatin1Char(' '));
|
|
||||||
weightedTermLength = weightedLength(query);
|
|
||||||
@@ -120,36 +229,6 @@ private:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
- enum class Category {
|
|
||||||
- Name,
|
|
||||||
- GenericName,
|
|
||||||
- Comment,
|
|
||||||
- };
|
|
||||||
- qreal increaseMatchRelevance(const QString &serviceProperty, const QList<QStringView> &strList, Category category)
|
|
||||||
- {
|
|
||||||
- // Increment the relevance based on all the words (other than the first) of the query list
|
|
||||||
- qreal relevanceIncrement = 0;
|
|
||||||
-
|
|
||||||
- for (int i = 1; i < strList.size(); ++i) {
|
|
||||||
- const auto &str = strList.at(i);
|
|
||||||
- if (category == Category::Name) {
|
|
||||||
- if (serviceProperty.contains(str, Qt::CaseInsensitive)) {
|
|
||||||
- relevanceIncrement += 0.01;
|
|
||||||
- }
|
|
||||||
- } else if (category == Category::GenericName) {
|
|
||||||
- if (serviceProperty.contains(str, Qt::CaseInsensitive)) {
|
|
||||||
- relevanceIncrement += 0.01;
|
|
||||||
- }
|
|
||||||
- } else if (category == Category::Comment) {
|
|
||||||
- if (serviceProperty.contains(str, Qt::CaseInsensitive)) {
|
|
||||||
- relevanceIncrement += 0.01;
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- return relevanceIncrement;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
void setupMatch(const KService::Ptr &service, KRunner::QueryMatch &match)
|
|
||||||
{
|
|
||||||
const QString name = service->name();
|
|
||||||
@@ -219,96 +298,77 @@ private:
|
|
||||||
return resultingArgs.join(QLatin1Char(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
- void matchNameKeywordAndGenericName()
|
|
||||||
+ [[nodiscard]] std::optional<Score> fuzzyScore(KService::Ptr service)
|
|
||||||
{
|
|
||||||
- const auto nameKeywordAndGenericNameFilter = [this](const KService::Ptr &service) {
|
|
||||||
- // Name
|
|
||||||
- if (contains(service->name(), queryList)) {
|
|
||||||
- return true;
|
|
||||||
- }
|
|
||||||
- // If the term length is < 3, no real point searching the untranslated Name, Keywords and GenericName
|
|
||||||
- if (weightedTermLength < 3) {
|
|
||||||
- return false;
|
|
||||||
- }
|
|
||||||
- if (contains(service->untranslatedName(), queryList)) {
|
|
||||||
- return true;
|
|
||||||
- }
|
|
||||||
+ if (queryList.isEmpty()) {
|
|
||||||
+ return std::nullopt; // No query, no score.
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ const auto name = service->name();
|
|
||||||
+ if (name.compare(query, Qt::CaseInsensitive) == 0) {
|
|
||||||
+ // Absolute match. Can't get any better than this.
|
|
||||||
+ return Score{.value = std::numeric_limits<decltype(Score::value)>::max(), .categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Highest};
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- // Keywords
|
|
||||||
- if (contains(service->keywords(), queryList)) {
|
|
||||||
- return true;
|
|
||||||
+ std::array<WeightedScoreCard, 4> weightedCards = {
|
|
||||||
+ WeightedScoreCard{.cards = makeScores(name, queryList), .weight = 1.0},
|
|
||||||
+ WeightedScoreCard{.cards = makeScores(service->untranslatedName(), queryList), .weight = 0.8},
|
|
||||||
+ WeightedScoreCard{.cards = makeScores(service->genericName(), queryList), .weight = 0.6},
|
|
||||||
+ WeightedScoreCard{.cards = makeScoreFromList(queryList, service->keywords()), .weight = 0.1},
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ if (RUNNER_SERVICES().isDebugEnabled()) {
|
|
||||||
+ qCDebug(RUNNER_SERVICES) << "+++++++ Weighted Cards for" << name;
|
|
||||||
+ for (const auto &weightedCard : weightedCards) {
|
|
||||||
+ qCDebug(RUNNER_SERVICES) << weightedCard;
|
|
||||||
}
|
|
||||||
- // GenericName
|
|
||||||
- if (contains(service->genericName(), queryList) || contains(service->untranslatedGenericName(), queryList)) {
|
|
||||||
- return true;
|
|
||||||
+ qCDebug(RUNNER_SERVICES) << "-------";
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ int scores = 1; // starts at 1 to avoid division by zero
|
|
||||||
+ qreal finalScore = 0.0;
|
|
||||||
+ for (const auto &weightedCard : weightedCards) {
|
|
||||||
+ if (weightedCard.cards.empty()) {
|
|
||||||
+ continue; // No scores, no match.
|
|
||||||
}
|
|
||||||
- // Comment
|
|
||||||
- if (contains(service->comment(), queryList)) {
|
|
||||||
- return true;
|
|
||||||
+
|
|
||||||
+ qreal weightedScore = 0.0;
|
|
||||||
+ for (const auto &scoreCard : weightedCard.cards) {
|
|
||||||
+ weightedScore += (scoreCard.bitapScore + scoreCard.levenshteinScore) * weightedCard.weight;
|
|
||||||
+ scores++;
|
|
||||||
}
|
|
||||||
|
|
||||||
- return false;
|
|
||||||
- };
|
|
||||||
+ finalScore += weightedScore;
|
|
||||||
+ }
|
|
||||||
+ finalScore = finalScore / scores; // Average the score for this card
|
|
||||||
|
|
||||||
- for (const KService::Ptr &service : m_services) {
|
|
||||||
- if (!nameKeywordAndGenericNameFilter(service) || disqualify(service)) {
|
|
||||||
- continue;
|
|
||||||
- }
|
|
||||||
+ qCDebug(RUNNER_SERVICES) << "Final score for" << name << "is" << finalScore;
|
|
||||||
+ if (finalScore > 0.0) {
|
|
||||||
+ return Score{.value = finalScore, .categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Moderate};
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- const QString id = service->storageId();
|
|
||||||
- const QString name = service->name();
|
|
||||||
+ return std::nullopt;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- KRunner::QueryMatch::CategoryRelevance categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Moderate;
|
|
||||||
- qreal relevance(0.6);
|
|
||||||
+ void matchNameKeywordAndGenericName()
|
|
||||||
+ {
|
|
||||||
+ static auto isTest = QStandardPaths::isTestModeEnabled();
|
|
||||||
|
|
||||||
- // If the term was < 3 chars and NOT at the beginning of the App's name, then chances are the user doesn't want that app
|
|
||||||
- if (weightedTermLength < 3) {
|
|
||||||
- if (name.startsWith(query, Qt::CaseInsensitive)) {
|
|
||||||
- relevance = 0.9;
|
|
||||||
- } else {
|
|
||||||
- continue;
|
|
||||||
- }
|
|
||||||
- } else if (name.compare(query, Qt::CaseInsensitive) == 0) {
|
|
||||||
- relevance = 1;
|
|
||||||
- categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Highest;
|
|
||||||
- } else if (const auto idx = name.indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
- relevance = 0.8;
|
|
||||||
- relevance += increaseMatchRelevance(name, queryList, Category::Name);
|
|
||||||
- if (idx == 0) {
|
|
||||||
- relevance += 0.1;
|
|
||||||
- categoryRelevance = KRunner::QueryMatch::CategoryRelevance::High;
|
|
||||||
- }
|
|
||||||
- } else if (const auto idx = service->genericName().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
- relevance = 0.65;
|
|
||||||
- relevance += increaseMatchRelevance(service->genericName(), queryList, Category::GenericName);
|
|
||||||
- if (idx == 0) {
|
|
||||||
- relevance += 0.05;
|
|
||||||
- }
|
|
||||||
- } else if (const auto idx = service->comment().indexOf(queryList[0], 0, Qt::CaseInsensitive); idx != -1) {
|
|
||||||
- relevance = 0.5;
|
|
||||||
- relevance += increaseMatchRelevance(service->comment(), queryList, Category::Comment);
|
|
||||||
- if (idx == 0) {
|
|
||||||
- relevance += 0.05;
|
|
||||||
- }
|
|
||||||
+ for (const KService::Ptr &service : m_services) {
|
|
||||||
+ if (isTest && !service->name().contains("ServiceRunnerTest"_L1)) {
|
|
||||||
+ continue; // Skip services that are not part of the test.
|
|
||||||
}
|
|
||||||
|
|
||||||
KRunner::QueryMatch match(m_runner);
|
|
||||||
- match.setCategoryRelevance(categoryRelevance);
|
|
||||||
- setupMatch(service, match);
|
|
||||||
- if (service->categories().contains(QLatin1String("KDE"))) {
|
|
||||||
- qCDebug(RUNNER_SERVICES) << "found a kde thing" << id << match.subtext() << relevance;
|
|
||||||
- relevance += .09;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- if (const auto foundIt = m_runner->m_favorites.constFind(service->desktopEntryName()); foundIt != m_runner->m_favorites.cend()) {
|
|
||||||
- if (foundIt->isGlobal || foundIt->linkedActivities.contains(m_currentActivity)) {
|
|
||||||
- qCDebug(RUNNER_SERVICES) << "entry is a favorite" << id << match.subtext() << relevance;
|
|
||||||
- relevance *= 1.25; // Give favorites a relative boost,
|
|
||||||
- }
|
|
||||||
+ auto score = fuzzyScore(service);
|
|
||||||
+ if (!score || disqualify(service)) {
|
|
||||||
+ continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
- qCDebug(RUNNER_SERVICES) << name << "is this relevant:" << relevance;
|
|
||||||
- match.setRelevance(relevance);
|
|
||||||
+ setupMatch(service, match);
|
|
||||||
+ match.setCategoryRelevance(score->categoryRelevance);
|
|
||||||
+ match.setRelevance(score->value);
|
|
||||||
+ qCDebug(RUNNER_SERVICES) << match.text() << "is this relevant:" << match.relevance() << "category relevance" << match.categoryRelevance();
|
|
||||||
|
|
||||||
matches << match;
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.51.0
|
|
||||||
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
From 0168ee68b484995ed9398d31004dd80678ac7e37 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kai Uwe Broulik <kde@privat.broulik.de>
|
|
||||||
Date: Tue, 5 Aug 2025 12:57:00 +0200
|
|
||||||
Subject: [PATCH] Close USB device added notification when devicenotifier pops
|
|
||||||
up
|
|
||||||
|
|
||||||
The device notification is supposed to be super quick feedback that
|
|
||||||
"something" got detected. Once it has been identified as storage
|
|
||||||
device (disks spun up and what not), devicenotifier will show up
|
|
||||||
and then you have two popups.
|
|
||||||
---
|
|
||||||
applets/devicenotifier/CMakeLists.txt | 1 +
|
|
||||||
applets/devicenotifier/devicefiltercontrol.cpp | 14 ++++++++++++++
|
|
||||||
applets/devicenotifier/devicefiltercontrol.h | 1 +
|
|
||||||
applets/devicenotifier/qml/main.qml | 1 +
|
|
||||||
devicenotifications/devicenotifications.cpp | 8 ++++++++
|
|
||||||
devicenotifications/devicenotifications.h | 3 +++
|
|
||||||
6 files changed, 28 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/applets/devicenotifier/CMakeLists.txt b/applets/devicenotifier/CMakeLists.txt
|
|
||||||
index d87964dc46d..fa415837e68 100644
|
|
||||||
--- a/applets/devicenotifier/CMakeLists.txt
|
|
||||||
+++ b/applets/devicenotifier/CMakeLists.txt
|
|
||||||
@@ -31,6 +31,7 @@ plasma_add_applet(org.kde.plasma.devicenotifier
|
|
||||||
|
|
||||||
target_link_libraries(org.kde.plasma.devicenotifier
|
|
||||||
PRIVATE
|
|
||||||
+ Qt::DBus
|
|
||||||
Qt::Qml
|
|
||||||
Plasma::Plasma
|
|
||||||
KF6::Solid
|
|
||||||
diff --git a/applets/devicenotifier/devicefiltercontrol.cpp b/applets/devicenotifier/devicefiltercontrol.cpp
|
|
||||||
index dfdb51a4304..f585eb2f063 100644
|
|
||||||
--- a/applets/devicenotifier/devicefiltercontrol.cpp
|
|
||||||
+++ b/applets/devicenotifier/devicefiltercontrol.cpp
|
|
||||||
@@ -11,9 +11,14 @@
|
|
||||||
#include "devicecontrol.h"
|
|
||||||
#include "devicestatemonitor_p.h"
|
|
||||||
|
|
||||||
+#include <QDBusConnection>
|
|
||||||
+#include <QDBusMessage>
|
|
||||||
+
|
|
||||||
#include <Solid/Device>
|
|
||||||
#include <Solid/OpticalDrive>
|
|
||||||
|
|
||||||
+using namespace Qt::Literals::StringLiterals;
|
|
||||||
+
|
|
||||||
DeviceFilterControl::DeviceFilterControl(QObject *parent)
|
|
||||||
: QSortFilterProxyModel(parent)
|
|
||||||
, m_filterType(Removable)
|
|
||||||
@@ -54,6 +59,15 @@ void DeviceFilterControl::unmountAllRemovables()
|
|
||||||
qCDebug(APPLETS::DEVICENOTIFIER) << "Device Filter Control: unmount all removables function finished";
|
|
||||||
}
|
|
||||||
|
|
||||||
+void DeviceFilterControl::dismissUsbDeviceAddedNotification()
|
|
||||||
+{
|
|
||||||
+ QDBusMessage msg = QDBusMessage::createMethodCall(u"org.kde.kded6"_s,
|
|
||||||
+ u"/modules/devicenotifications"_s,
|
|
||||||
+ u"org.kde.plasma.devicenotifications"_s,
|
|
||||||
+ u"dismissUsbDeviceAdded"_s);
|
|
||||||
+ QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
QBindable<QString> DeviceFilterControl::bindableLastUdi()
|
|
||||||
{
|
|
||||||
return &m_lastUdi;
|
|
||||||
diff --git a/applets/devicenotifier/devicefiltercontrol.h b/applets/devicenotifier/devicefiltercontrol.h
|
|
||||||
index e4c0a321657..fa6266fb197 100644
|
|
||||||
--- a/applets/devicenotifier/devicefiltercontrol.h
|
|
||||||
+++ b/applets/devicenotifier/devicefiltercontrol.h
|
|
||||||
@@ -41,6 +41,7 @@ public:
|
|
||||||
Q_ENUM(DevicesType)
|
|
||||||
|
|
||||||
Q_INVOKABLE void unmountAllRemovables();
|
|
||||||
+ Q_INVOKABLE void dismissUsbDeviceAddedNotification();
|
|
||||||
|
|
||||||
explicit DeviceFilterControl(QObject *parent = nullptr);
|
|
||||||
~DeviceFilterControl() override;
|
|
||||||
diff --git a/applets/devicenotifier/qml/main.qml b/applets/devicenotifier/qml/main.qml
|
|
||||||
index 7fcd76a6d16..c7fe6e6197d 100644
|
|
||||||
--- a/applets/devicenotifier/qml/main.qml
|
|
||||||
+++ b/applets/devicenotifier/qml/main.qml
|
|
||||||
@@ -35,6 +35,7 @@ PlasmoidItem {
|
|
||||||
onLastUdiChanged: {
|
|
||||||
if (lastDeviceAdded) {
|
|
||||||
if (Plasmoid.configuration.popupOnNewDevice) {
|
|
||||||
+ filterModel.dismissUsbDeviceAddedNotification();
|
|
||||||
devicenotifier.expanded = true;
|
|
||||||
fullRepresentationItem.spontaneousOpen = true;
|
|
||||||
}
|
|
||||||
diff --git a/devicenotifications/devicenotifications.cpp b/devicenotifications/devicenotifications.cpp
|
|
||||||
index 71ae0ff340e..196e28ca948 100644
|
|
||||||
--- a/devicenotifications/devicenotifications.cpp
|
|
||||||
+++ b/devicenotifications/devicenotifications.cpp
|
|
||||||
@@ -323,6 +323,14 @@ void KdedDeviceNotifications::setupWaylandOutputListener()
|
|
||||||
wl_callback_add_listener(syncCallback, &syncCallbackListener, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
+void KdedDeviceNotifications::dismissUsbDeviceAdded()
|
|
||||||
+{
|
|
||||||
+ if (m_usbDeviceAddedNotification) {
|
|
||||||
+ m_usbDeviceAddedNotification->close();
|
|
||||||
+ m_usbDeviceAddedNotification = nullptr;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
void KdedDeviceNotifications::notifyOutputAdded()
|
|
||||||
{
|
|
||||||
if (m_deviceAddedTimer.isActive()) {
|
|
||||||
diff --git a/devicenotifications/devicenotifications.h b/devicenotifications/devicenotifications.h
|
|
||||||
index ab7e6b3ff9b..75005193287 100644
|
|
||||||
--- a/devicenotifications/devicenotifications.h
|
|
||||||
+++ b/devicenotifications/devicenotifications.h
|
|
||||||
@@ -77,6 +77,7 @@ private:
|
|
||||||
class KdedDeviceNotifications : public KDEDModule
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
+ Q_CLASSINFO("D-Bus Interface", "org.kde.plasma.devicenotifications")
|
|
||||||
|
|
||||||
public:
|
|
||||||
KdedDeviceNotifications(QObject *parent, const QVariantList &args);
|
|
||||||
@@ -84,6 +85,8 @@ public:
|
|
||||||
|
|
||||||
void setupWaylandOutputListener();
|
|
||||||
|
|
||||||
+ Q_SCRIPTABLE void dismissUsbDeviceAdded();
|
|
||||||
+
|
|
||||||
private:
|
|
||||||
void notifyOutputAdded();
|
|
||||||
void notifyOutputRemoved();
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
From f6ec2847358178a5b6ff0497e52d1e2be43d2a48 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?UTF-8?q?Luan=20Vitor=20Simi=C3=A3o=20oliveira?=
|
|
||||||
<luanv.oliveira@outlook.com>
|
|
||||||
Date: Fri, 8 Aug 2025 14:41:49 -0300
|
|
||||||
Subject: [PATCH] kcms/style: add special case for Adwaita gtk theme
|
|
||||||
|
|
||||||
Allows the user to set the GTK theme to the default "Adwaita"
|
|
||||||
needs to be special cased because the theme is implemented in code.
|
|
||||||
---
|
|
||||||
kcms/style/gtkthemesmodel.cpp | 7 ++++++-
|
|
||||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/kcms/style/gtkthemesmodel.cpp b/kcms/style/gtkthemesmodel.cpp
|
|
||||||
index 002e87dbd0d..6dcdf4f1a1d 100644
|
|
||||||
--- a/kcms/style/gtkthemesmodel.cpp
|
|
||||||
+++ b/kcms/style/gtkthemesmodel.cpp
|
|
||||||
@@ -35,7 +35,12 @@ void GtkThemesModel::load()
|
|
||||||
if (possibleThemeDirectory.dirName() == u"Breeze-Dark") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
-
|
|
||||||
+ if (possibleThemeDirectory.dirName() == u"Default") {
|
|
||||||
+ // Adwaita is a special case, since it is implemented inside GTK itself
|
|
||||||
+ // also setting gtk-theme-name to "Default" breaks dark theme
|
|
||||||
+ gtk3ThemesNames.insert(QStringLiteral("Adwaita"), possibleThemeDirectory.path());
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
gtk3ThemesNames.insert(possibleThemeDirectory.dirName(), possibleThemeDirectory.path());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
From 4ab3894d75e1f9c6c7738a893a9b707ff0575953 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Nate Graham <nate@kde.org>
|
|
||||||
Date: Thu, 21 Aug 2025 19:37:33 -0600
|
|
||||||
Subject: [PATCH] notifications: make "you missed some notifications"
|
|
||||||
notification transient
|
|
||||||
|
|
||||||
Its purpose is to direct you to the notifications history. If you're
|
|
||||||
seeing it *in* the notification history, its purpose has been bypassed
|
|
||||||
because you're already where it wanted to take you.
|
|
||||||
|
|
||||||
Don't show it in the notification history.
|
|
||||||
---
|
|
||||||
libnotificationmanager/notifications.cpp | 3 +++
|
|
||||||
1 file changed, 3 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/libnotificationmanager/notifications.cpp b/libnotificationmanager/notifications.cpp
|
|
||||||
index f68c342e7e9..128665f4de9 100644
|
|
||||||
--- a/libnotificationmanager/notifications.cpp
|
|
||||||
+++ b/libnotificationmanager/notifications.cpp
|
|
||||||
@@ -917,6 +917,9 @@ void Notifications::showInhibitionSummary(Urgency urgency, const QStringList &bl
|
|
||||||
notification->setIconName(u"preferences-desktop-notification-bell"_s);
|
|
||||||
notification->setFlags(KNotification::CloseOnTimeout);
|
|
||||||
notification->setComponentName(u"libnotificationmanager"_s);
|
|
||||||
+ // Don't put it in the history because this doesn't make sense; if you're seeing it
|
|
||||||
+ // in the history, you're seeing the notifications it was telling you about!
|
|
||||||
+ notification->setHint(u"transient"_s, true);
|
|
||||||
|
|
||||||
const QString showNotificationsText = i18nc("@action:button Show the notifications popup", "Show Notifications");
|
|
||||||
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
From f0d2dd20803f2eee364d26656715b89e7c74366c Mon Sep 17 00:00:00 2001
|
|
||||||
From: David Redondo <kde@david-redondo.de>
|
|
||||||
Date: Wed, 27 Aug 2025 09:40:43 +0200
|
|
||||||
Subject: [PATCH] servicerunner: use vector::insert on compilers that don't
|
|
||||||
support append_range yet
|
|
||||||
|
|
||||||
g++ only gained support for it with g++ 15 which was released this month.
|
|
||||||
---
|
|
||||||
runners/services/servicerunner.cpp | 8 ++++++++
|
|
||||||
1 file changed, 8 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/runners/services/servicerunner.cpp b/runners/services/servicerunner.cpp
|
|
||||||
index 2ccc9a0af37..9e9d4e70a72 100644
|
|
||||||
--- a/runners/services/servicerunner.cpp
|
|
||||||
+++ b/runners/services/servicerunner.cpp
|
|
||||||
@@ -142,7 +142,11 @@ auto makeScoreFromList(const auto &queryList, const QStringList &strings) {
|
|
||||||
continue; // Not a good match, skip it. We are very strict with keywords
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
+#ifdef __cpp_lib_containers_ranges
|
|
||||||
queryCards.append_range(stringCards);
|
|
||||||
+#else
|
|
||||||
+ queryCards.insert(queryCards.end(), stringCards.cbegin(), stringCards.cend());
|
|
||||||
+#endif
|
|
||||||
}
|
|
||||||
// We do not break because other string might also match, improving the score.
|
|
||||||
}
|
|
||||||
@@ -150,7 +154,11 @@ auto makeScoreFromList(const auto &queryList, const QStringList &strings) {
|
|
||||||
// No item in strings matched the query item. This means the entire query is not a match.
|
|
||||||
return ScoreCards{};
|
|
||||||
}
|
|
||||||
+#ifdef __cpp_lib_containers_ranges
|
|
||||||
cards.append_range(queryCards);
|
|
||||||
+#else
|
|
||||||
+ cards.insert(cards.end(), queryCards.cbegin(), queryCards.cend());
|
|
||||||
+#endif
|
|
||||||
}
|
|
||||||
return cards;
|
|
||||||
};
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,349 +0,0 @@
|
||||||
From 77b3ec8cf4fb9414167bb15bcc3c69b1be6c2dbf Mon Sep 17 00:00:00 2001
|
|
||||||
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
Date: Tue, 16 Sep 2025 15:52:29 +0300
|
|
||||||
Subject: [PATCH 1/2] shell: Pass an xdg activation token from DesktopView to
|
|
||||||
KRunner
|
|
||||||
|
|
||||||
This makes sure that krunner gets focused after starting to type while
|
|
||||||
a desktop view is focused when using medium or high focus stealing
|
|
||||||
prevention level.
|
|
||||||
---
|
|
||||||
krunner/dbus/org.kde.krunner.App.xml | 4 ++
|
|
||||||
krunner/view.cpp | 14 +++++++
|
|
||||||
krunner/view.h | 1 +
|
|
||||||
shell/desktopview.cpp | 61 ++++++++++++++++++++++++++--
|
|
||||||
shell/desktopview.h | 2 +
|
|
||||||
5 files changed, 79 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/krunner/dbus/org.kde.krunner.App.xml b/krunner/dbus/org.kde.krunner.App.xml
|
|
||||||
index 6553e9a1b56..e6926b1b63a 100644
|
|
||||||
--- a/krunner/dbus/org.kde.krunner.App.xml
|
|
||||||
+++ b/krunner/dbus/org.kde.krunner.App.xml
|
|
||||||
@@ -17,5 +17,9 @@
|
|
||||||
<arg name="runnerName" type="s" direction="in"/>
|
|
||||||
<arg name="term" type="s" direction="in"/>
|
|
||||||
</method>
|
|
||||||
+ <method name="queryWithActivationToken">
|
|
||||||
+ <arg name="term" type="s" direction="in"/>
|
|
||||||
+ <arg name="activationToken" type="s" direction="in"/>
|
|
||||||
+ </method>
|
|
||||||
</interface>
|
|
||||||
</node>
|
|
||||||
diff --git a/krunner/view.cpp b/krunner/view.cpp
|
|
||||||
index afefe9ab486..bb7edd5d654 100644
|
|
||||||
--- a/krunner/view.cpp
|
|
||||||
+++ b/krunner/view.cpp
|
|
||||||
@@ -263,6 +263,20 @@ void View::querySingleRunner(const QString &runnerName, const QString &term)
|
|
||||||
m_engine->rootObject()->setProperty("query", term);
|
|
||||||
}
|
|
||||||
|
|
||||||
+void View::queryWithActivationToken(const QString &term, const QString &activationToken)
|
|
||||||
+{
|
|
||||||
+ qputenv("XDG_ACTIVATION_TOKEN", activationToken.toUtf8());
|
|
||||||
+
|
|
||||||
+ if (!isVisible()) {
|
|
||||||
+ display();
|
|
||||||
+ } else {
|
|
||||||
+ requestActivate();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ m_engine->rootObject()->setProperty("singleRunner", QString());
|
|
||||||
+ m_engine->rootObject()->setProperty("query", term);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
bool View::pinned() const
|
|
||||||
{
|
|
||||||
return m_pinned;
|
|
||||||
diff --git a/krunner/view.h b/krunner/view.h
|
|
||||||
index 979c6d3b483..af1d477d6f3 100644
|
|
||||||
--- a/krunner/view.h
|
|
||||||
+++ b/krunner/view.h
|
|
||||||
@@ -93,6 +93,7 @@ public Q_SLOTS:
|
|
||||||
void displayWithClipboardContents();
|
|
||||||
void query(const QString &term);
|
|
||||||
void querySingleRunner(const QString &runnerName, const QString &term);
|
|
||||||
+ void queryWithActivationToken(const QString &term, const QString &activationToken);
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void loadConfig();
|
|
||||||
diff --git a/shell/desktopview.cpp b/shell/desktopview.cpp
|
|
||||||
index 36e23149272..744176e6d3e 100644
|
|
||||||
--- a/shell/desktopview.cpp
|
|
||||||
+++ b/shell/desktopview.cpp
|
|
||||||
@@ -23,6 +23,7 @@
|
|
||||||
|
|
||||||
#include <KAuthorized>
|
|
||||||
#include <KStartupInfo>
|
|
||||||
+#include <KWaylandExtras>
|
|
||||||
#include <KX11Extras>
|
|
||||||
#include <klocalizedstring.h>
|
|
||||||
#include <kwindowsystem.h>
|
|
||||||
@@ -384,11 +385,54 @@ bool DesktopView::event(QEvent *e)
|
|
||||||
{
|
|
||||||
if (e->type() == QEvent::FocusOut) {
|
|
||||||
m_krunnerText.clear();
|
|
||||||
+
|
|
||||||
+ if (!m_krunnerFuture.isCanceled()) {
|
|
||||||
+ m_krunnerFuture.cancel();
|
|
||||||
+ m_krunnerFuture = {};
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
return PlasmaQuick::ContainmentView::event(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
+class ActivationTokenRequest : public QObject
|
|
||||||
+{
|
|
||||||
+ Q_OBJECT
|
|
||||||
+
|
|
||||||
+public:
|
|
||||||
+ explicit ActivationTokenRequest(QWindow *window)
|
|
||||||
+ : m_serial(KWaylandExtras::lastInputSerial(window))
|
|
||||||
+ {
|
|
||||||
+ m_promise.start();
|
|
||||||
+
|
|
||||||
+ connect(KWaylandExtras::self(), &KWaylandExtras::xdgActivationTokenArrived, this, [this](int serial, const QString &token) {
|
|
||||||
+ if (m_serial == serial) {
|
|
||||||
+ if (!m_promise.isCanceled()) {
|
|
||||||
+ m_promise.addResult(token);
|
|
||||||
+ }
|
|
||||||
+ m_promise.finish();
|
|
||||||
+ delete this;
|
|
||||||
+ }
|
|
||||||
+ });
|
|
||||||
+ KWaylandExtras::requestXdgActivationToken(window, m_serial, QString());
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ QFuture<QString> future() const
|
|
||||||
+ {
|
|
||||||
+ return m_promise.future();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+private:
|
|
||||||
+ QPromise<QString> m_promise;
|
|
||||||
+ int m_serial;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static QFuture<QString> fetchActivationToken(QWindow *window)
|
|
||||||
+{
|
|
||||||
+ auto request = new ActivationTokenRequest(window);
|
|
||||||
+ return request->future();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
bool DesktopView::handleKRunnerTextInput(QKeyEvent *e)
|
|
||||||
{
|
|
||||||
// allow only Shift and GroupSwitch modifiers
|
|
||||||
@@ -408,12 +452,22 @@ bool DesktopView::handleKRunnerTextInput(QKeyEvent *e)
|
|
||||||
krunnerTextChanged = true;
|
|
||||||
}
|
|
||||||
if (krunnerTextChanged) {
|
|
||||||
- const QString interface(QStringLiteral("org.kde.krunner"));
|
|
||||||
if (!KAuthorized::authorize(QStringLiteral("run_command"))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
- org::kde::krunner::App krunner(interface, QStringLiteral("/App"), QDBusConnection::sessionBus());
|
|
||||||
- krunner.query(m_krunnerText);
|
|
||||||
+ if (KWindowSystem::isPlatformWayland()) {
|
|
||||||
+ if (!m_krunnerFuture.isCanceled()) {
|
|
||||||
+ m_krunnerFuture.cancel();
|
|
||||||
+ }
|
|
||||||
+ m_krunnerFuture = fetchActivationToken(this);
|
|
||||||
+ m_krunnerFuture.then(this, [this](const QString &token) {
|
|
||||||
+ org::kde::krunner::App krunner(QStringLiteral("org.kde.krunner"), QStringLiteral("/App"), QDBusConnection::sessionBus());
|
|
||||||
+ krunner.queryWithActivationToken(m_krunnerText, token);
|
|
||||||
+ });
|
|
||||||
+ } else {
|
|
||||||
+ org::kde::krunner::App krunner(QStringLiteral("org.kde.krunner"), QStringLiteral("/App"), QDBusConnection::sessionBus());
|
|
||||||
+ krunner.query(m_krunnerText);
|
|
||||||
+ }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
@@ -596,4 +650,5 @@ void DesktopView::setAccentColorFromWallpaper(const QColor &accentColor)
|
|
||||||
QDBusConnection::sessionBus().send(applyAccentColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
+#include "desktopview.moc"
|
|
||||||
#include "moc_desktopview.cpp"
|
|
||||||
diff --git a/shell/desktopview.h b/shell/desktopview.h
|
|
||||||
index c2b642d9a15..9e152611a31 100644
|
|
||||||
--- a/shell/desktopview.h
|
|
||||||
+++ b/shell/desktopview.h
|
|
||||||
@@ -10,6 +10,7 @@
|
|
||||||
|
|
||||||
#include <PlasmaQuick/ConfigView>
|
|
||||||
#include <PlasmaQuick/ContainmentView>
|
|
||||||
+#include <QFuture>
|
|
||||||
#include <QPointer>
|
|
||||||
|
|
||||||
#include <KConfigWatcher>
|
|
||||||
@@ -127,6 +128,7 @@ private:
|
|
||||||
QPointer<QScreen> m_screenToFollow;
|
|
||||||
LayerShellQt::Window *m_layerWindow = nullptr;
|
|
||||||
QString m_krunnerText;
|
|
||||||
+ QFuture<QString> m_krunnerFuture;
|
|
||||||
|
|
||||||
// KRunner config
|
|
||||||
KConfigWatcher::Ptr m_configWatcher;
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From b3d97405cb5b0ee9859576309bd826dea6b74274 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
||||||
Date: Wed, 17 Sep 2025 09:59:07 +0300
|
|
||||||
Subject: [PATCH 2/2] shell: Use org.freedesktop.Application.ActivateAction to
|
|
||||||
pass xdg activation token to krunner
|
|
||||||
|
|
||||||
---
|
|
||||||
krunner/dbus/org.kde.krunner.App.xml | 4 ----
|
|
||||||
krunner/main.cpp | 4 +++-
|
|
||||||
krunner/view.cpp | 14 ------------
|
|
||||||
krunner/view.h | 1 -
|
|
||||||
shell/CMakeLists.txt | 4 ----
|
|
||||||
shell/desktopview.cpp | 32 +++++++++++++++++++++++-----
|
|
||||||
6 files changed, 30 insertions(+), 29 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/krunner/dbus/org.kde.krunner.App.xml b/krunner/dbus/org.kde.krunner.App.xml
|
|
||||||
index e6926b1b63a..6553e9a1b56 100644
|
|
||||||
--- a/krunner/dbus/org.kde.krunner.App.xml
|
|
||||||
+++ b/krunner/dbus/org.kde.krunner.App.xml
|
|
||||||
@@ -17,9 +17,5 @@
|
|
||||||
<arg name="runnerName" type="s" direction="in"/>
|
|
||||||
<arg name="term" type="s" direction="in"/>
|
|
||||||
</method>
|
|
||||||
- <method name="queryWithActivationToken">
|
|
||||||
- <arg name="term" type="s" direction="in"/>
|
|
||||||
- <arg name="activationToken" type="s" direction="in"/>
|
|
||||||
- </method>
|
|
||||||
</interface>
|
|
||||||
</node>
|
|
||||||
diff --git a/krunner/main.cpp b/krunner/main.cpp
|
|
||||||
index 514c34b032f..02174bb2cda 100644
|
|
||||||
--- a/krunner/main.cpp
|
|
||||||
+++ b/krunner/main.cpp
|
|
||||||
@@ -136,9 +136,11 @@ int main(int argc, char **argv)
|
|
||||||
parser.parse(arguments);
|
|
||||||
updateVisibility();
|
|
||||||
});
|
|
||||||
- QObject::connect(&service, &KDBusService::activateActionRequested, &view, [&view](const QString &action) {
|
|
||||||
+ QObject::connect(&service, &KDBusService::activateActionRequested, &view, [&view](const QString &action, const QVariant ¶meter) {
|
|
||||||
if (action == QLatin1String("RunClipboard")) {
|
|
||||||
view.displayWithClipboardContents();
|
|
||||||
+ } else if (action == QLatin1String("Query")) {
|
|
||||||
+ view.query(parameter.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
diff --git a/krunner/view.cpp b/krunner/view.cpp
|
|
||||||
index bb7edd5d654..afefe9ab486 100644
|
|
||||||
--- a/krunner/view.cpp
|
|
||||||
+++ b/krunner/view.cpp
|
|
||||||
@@ -263,20 +263,6 @@ void View::querySingleRunner(const QString &runnerName, const QString &term)
|
|
||||||
m_engine->rootObject()->setProperty("query", term);
|
|
||||||
}
|
|
||||||
|
|
||||||
-void View::queryWithActivationToken(const QString &term, const QString &activationToken)
|
|
||||||
-{
|
|
||||||
- qputenv("XDG_ACTIVATION_TOKEN", activationToken.toUtf8());
|
|
||||||
-
|
|
||||||
- if (!isVisible()) {
|
|
||||||
- display();
|
|
||||||
- } else {
|
|
||||||
- requestActivate();
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- m_engine->rootObject()->setProperty("singleRunner", QString());
|
|
||||||
- m_engine->rootObject()->setProperty("query", term);
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
bool View::pinned() const
|
|
||||||
{
|
|
||||||
return m_pinned;
|
|
||||||
diff --git a/krunner/view.h b/krunner/view.h
|
|
||||||
index af1d477d6f3..979c6d3b483 100644
|
|
||||||
--- a/krunner/view.h
|
|
||||||
+++ b/krunner/view.h
|
|
||||||
@@ -93,7 +93,6 @@ public Q_SLOTS:
|
|
||||||
void displayWithClipboardContents();
|
|
||||||
void query(const QString &term);
|
|
||||||
void querySingleRunner(const QString &runnerName, const QString &term);
|
|
||||||
- void queryWithActivationToken(const QString &term, const QString &activationToken);
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void loadConfig();
|
|
||||||
diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt
|
|
||||||
index 96b91888f1b..814413ac827 100644
|
|
||||||
--- a/shell/CMakeLists.txt
|
|
||||||
+++ b/shell/CMakeLists.txt
|
|
||||||
@@ -92,10 +92,6 @@ qt6_generate_wayland_protocol_client_sources(plasmashell
|
|
||||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/plasma-shell.xml
|
|
||||||
)
|
|
||||||
|
|
||||||
-set(krunner_xml ${plasma-workspace_SOURCE_DIR}/krunner/dbus/org.kde.krunner.App.xml)
|
|
||||||
-qt_add_dbus_interface(plasma_shell_SRCS ${krunner_xml} krunner_interface)
|
|
||||||
-
|
|
||||||
-
|
|
||||||
target_sources(plasmashell PRIVATE ${plasma_shell_SRCS})
|
|
||||||
|
|
||||||
target_link_libraries(plasmashell PRIVATE
|
|
||||||
diff --git a/shell/desktopview.cpp b/shell/desktopview.cpp
|
|
||||||
index 744176e6d3e..9c3baa84b91 100644
|
|
||||||
--- a/shell/desktopview.cpp
|
|
||||||
+++ b/shell/desktopview.cpp
|
|
||||||
@@ -6,12 +6,12 @@
|
|
||||||
|
|
||||||
#include "desktopview.h"
|
|
||||||
#include "containmentconfigview.h"
|
|
||||||
-#include "krunner_interface.h"
|
|
||||||
#include "screenpool.h"
|
|
||||||
#include "shellcorona.h"
|
|
||||||
|
|
||||||
#include <QDBusConnection>
|
|
||||||
#include <QDBusMessage>
|
|
||||||
+#include <QDBusPendingCall>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <QQmlContext>
|
|
||||||
#include <QQmlEngine>
|
|
||||||
@@ -461,12 +461,34 @@ bool DesktopView::handleKRunnerTextInput(QKeyEvent *e)
|
|
||||||
}
|
|
||||||
m_krunnerFuture = fetchActivationToken(this);
|
|
||||||
m_krunnerFuture.then(this, [this](const QString &token) {
|
|
||||||
- org::kde::krunner::App krunner(QStringLiteral("org.kde.krunner"), QStringLiteral("/App"), QDBusConnection::sessionBus());
|
|
||||||
- krunner.queryWithActivationToken(m_krunnerText, token);
|
|
||||||
+ auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.krunner"),
|
|
||||||
+ QStringLiteral("/org/kde/krunner"),
|
|
||||||
+ QStringLiteral("org.freedesktop.Application"),
|
|
||||||
+ QStringLiteral("ActivateAction"));
|
|
||||||
+ message.setArguments({
|
|
||||||
+ QStringLiteral("Query"),
|
|
||||||
+ QVariantList{
|
|
||||||
+ m_krunnerText,
|
|
||||||
+ },
|
|
||||||
+ QVariantMap{
|
|
||||||
+ {QStringLiteral("activation-token"), token},
|
|
||||||
+ },
|
|
||||||
+ });
|
|
||||||
+ QDBusConnection::sessionBus().asyncCall(message);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
- org::kde::krunner::App krunner(QStringLiteral("org.kde.krunner"), QStringLiteral("/App"), QDBusConnection::sessionBus());
|
|
||||||
- krunner.query(m_krunnerText);
|
|
||||||
+ auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.krunner"),
|
|
||||||
+ QStringLiteral("/org/kde/krunner"),
|
|
||||||
+ QStringLiteral("org.freedesktop.Application"),
|
|
||||||
+ QStringLiteral("ActivateAction"));
|
|
||||||
+ message.setArguments({
|
|
||||||
+ QStringLiteral("Query"),
|
|
||||||
+ QVariantList{
|
|
||||||
+ m_krunnerText,
|
|
||||||
+ },
|
|
||||||
+ QVariantMap{},
|
|
||||||
+ });
|
|
||||||
+ QDBusConnection::sessionBus().asyncCall(message);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
Plasma 6.5.0:
|
|
||||||
|
|
||||||
Pr 460 https://invent.kde.org/plasma/spectacle/-/merge_requests/460
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
From 97f209559c00acc1ea6d0736bb318ac0254a3e37 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Noah Davis <noahadvs@gmail.com>
|
|
||||||
Date: Wed, 4 Jun 2025 16:06:06 -0400
|
|
||||||
Subject: [PATCH 1/2] Say which shortcut can stop recording in recording
|
|
||||||
notification
|
|
||||||
|
|
||||||
It will say Meta+R by default, but will show the shortcut used to initiate the recording instead if Meta+R isn't assigned to region recording.
|
|
||||||
|
|
||||||
BUG: 505081
|
|
||||||
---
|
|
||||||
src/Platforms/VideoPlatform.cpp | 16 +++++++++++++++
|
|
||||||
src/Platforms/VideoPlatform.h | 4 ++++
|
|
||||||
src/Platforms/VideoPlatformWayland.cpp | 6 ++++--
|
|
||||||
src/SpectacleCore.cpp | 28 +++++++++++++++++++++++++-
|
|
||||||
4 files changed, 51 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/Platforms/VideoPlatform.cpp b/src/Platforms/VideoPlatform.cpp
|
|
||||||
index 592539048..333692326 100644
|
|
||||||
--- a/src/Platforms/VideoPlatform.cpp
|
|
||||||
+++ b/src/Platforms/VideoPlatform.cpp
|
|
||||||
@@ -69,6 +69,11 @@ VideoPlatform::RecordingState VideoPlatform::recordingState() const
|
|
||||||
return m_recordingState;
|
|
||||||
}
|
|
||||||
|
|
||||||
+VideoPlatform::RecordingMode VideoPlatform::recordingMode() const
|
|
||||||
+{
|
|
||||||
+ return m_recordingMode;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
void VideoPlatform::setRecordingState(RecordingState state)
|
|
||||||
{
|
|
||||||
if (state == m_recordingState) {
|
|
||||||
@@ -91,9 +96,20 @@ void VideoPlatform::setRecordingState(RecordingState state)
|
|
||||||
m_elapsedTimer.invalidate();
|
|
||||||
m_basicTimer.stop();
|
|
||||||
}
|
|
||||||
+ if (state != RecordingState::Recording) {
|
|
||||||
+ setRecordingMode(NoRecordingModes);
|
|
||||||
+ }
|
|
||||||
m_recordingState = state;
|
|
||||||
Q_EMIT recordingStateChanged(state);
|
|
||||||
Q_EMIT recordedTimeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
+void VideoPlatform::setRecordingMode(RecordingMode mode)
|
|
||||||
+{
|
|
||||||
+ if (m_recordingMode == mode) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ m_recordingMode = mode;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
#include "moc_VideoPlatform.cpp"
|
|
||||||
diff --git a/src/Platforms/VideoPlatform.h b/src/Platforms/VideoPlatform.h
|
|
||||||
index 4f5fba720..eea81e32a 100644
|
|
||||||
--- a/src/Platforms/VideoPlatform.h
|
|
||||||
+++ b/src/Platforms/VideoPlatform.h
|
|
||||||
@@ -129,8 +129,11 @@ public:
|
|
||||||
|
|
||||||
RecordingState recordingState() const;
|
|
||||||
|
|
||||||
+ RecordingMode recordingMode() const;
|
|
||||||
+
|
|
||||||
protected:
|
|
||||||
void setRecordingState(RecordingState state);
|
|
||||||
+ void setRecordingMode(RecordingMode mode);
|
|
||||||
void timerEvent(QTimerEvent *event) override;
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
@@ -157,6 +160,7 @@ private:
|
|
||||||
QBasicTimer m_basicTimer;
|
|
||||||
qint64 m_recordedTime = 0;
|
|
||||||
RecordingState m_recordingState = RecordingState::NotRecording;
|
|
||||||
+ RecordingMode m_recordingMode = RecordingMode::NoRecordingModes;
|
|
||||||
};
|
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(VideoPlatform::RecordingModes)
|
|
||||||
diff --git a/src/Platforms/VideoPlatformWayland.cpp b/src/Platforms/VideoPlatformWayland.cpp
|
|
||||||
index 648f8adbf..984ede0eb 100644
|
|
||||||
--- a/src/Platforms/VideoPlatformWayland.cpp
|
|
||||||
+++ b/src/Platforms/VideoPlatformWayland.cpp
|
|
||||||
@@ -245,11 +245,12 @@ void VideoPlatformWayland::startRecording(const QUrl &fileUrl, RecordingMode rec
|
|
||||||
m_recorder->setNodeId(0);
|
|
||||||
|
|
||||||
Q_ASSERT(stream);
|
|
||||||
- connect(stream, &ScreencastingStream::created, this, [this, stream] {
|
|
||||||
+ connect(stream, &ScreencastingStream::created, this, [this, stream, recordingMode] {
|
|
||||||
m_recorder->setNodeId(stream->nodeId());
|
|
||||||
if (!m_recorder->output().isEmpty()) {
|
|
||||||
m_recorder->start();
|
|
||||||
}
|
|
||||||
+ setRecordingMode(recordingMode);
|
|
||||||
setRecordingState(VideoPlatform::RecordingState::Recording);
|
|
||||||
});
|
|
||||||
connect(stream, &ScreencastingStream::failed, this, [this](const QString &error) {
|
|
||||||
@@ -302,7 +303,7 @@ void VideoPlatformWayland::startRecording(const QUrl &fileUrl, RecordingMode rec
|
|
||||||
m_recorder->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
- connect(m_recorder.get(), &PipeWireRecord::stateChanged, this, [this] {
|
|
||||||
+ connect(m_recorder.get(), &PipeWireRecord::stateChanged, this, [this, recordingMode] {
|
|
||||||
if (m_recorder->state() == PipeWireRecord::Idle) {
|
|
||||||
m_memoryTimer.stop();
|
|
||||||
if (recordingState() != RecordingState::NotRecording && recordingState() != RecordingState::Finished) {
|
|
||||||
@@ -311,6 +312,7 @@ void VideoPlatformWayland::startRecording(const QUrl &fileUrl, RecordingMode rec
|
|
||||||
}
|
|
||||||
} else if (m_recorder->state() == PipeWireRecord::Recording) {
|
|
||||||
m_memoryTimer.start(5000, Qt::CoarseTimer, this);
|
|
||||||
+ setRecordingMode(recordingMode);
|
|
||||||
setRecordingState(VideoPlatform::RecordingState::Recording);
|
|
||||||
} else if (m_recorder->state() == PipeWireRecord::Rendering) {
|
|
||||||
m_memoryTimer.stop();
|
|
||||||
diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp
|
|
||||||
index 8033751c9..aa2e8c6e1 100644
|
|
||||||
--- a/src/SpectacleCore.cpp
|
|
||||||
+++ b/src/SpectacleCore.cpp
|
|
||||||
@@ -281,7 +281,33 @@ SpectacleCore::SpectacleCore(QObject *parent)
|
|
||||||
SpectacleCore::instance()->finishRecording();
|
|
||||||
});
|
|
||||||
const auto messageTitle = i18nc("recording notification title", "Spectacle is Recording");
|
|
||||||
- const auto messageBody = i18nc("recording notification message", "Click the system tray icon to finish recording");
|
|
||||||
+ auto getSimpleDefaultShortcut = [] {
|
|
||||||
+ const auto shortcuts = KGlobalAccel::self()->shortcut(ShortcutActions::self()->recordRegionAction());
|
|
||||||
+ if (shortcuts.contains(QKeySequence{Qt::META | Qt::Key_R})) {
|
|
||||||
+ return QKeySequence{Qt::META | Qt::Key_R};
|
|
||||||
+ }
|
|
||||||
+ return QKeySequence{};
|
|
||||||
+ };
|
|
||||||
+ auto getShortcut = [](const auto &list) {
|
|
||||||
+ auto it = std::find_if(list.cbegin(), list.cend(), [](const QKeySequence &shortcut) {
|
|
||||||
+ return !shortcut.isEmpty();
|
|
||||||
+ });
|
|
||||||
+ return it != list.cend() ? *it : QKeySequence{};
|
|
||||||
+ };
|
|
||||||
+ QKeySequence stopShortcut = getSimpleDefaultShortcut();
|
|
||||||
+ if (stopShortcut.isEmpty()) {
|
|
||||||
+ auto mode = m_videoPlatform->recordingMode();
|
|
||||||
+ if (mode == VideoPlatform::Screen) {
|
|
||||||
+ stopShortcut = getShortcut(KGlobalAccel::self()->shortcut(ShortcutActions::self()->recordScreenAction()));
|
|
||||||
+ } else if (mode == VideoPlatform::Window) {
|
|
||||||
+ stopShortcut = getShortcut(KGlobalAccel::self()->shortcut(ShortcutActions::self()->recordWindowAction()));
|
|
||||||
+ } else if (mode == VideoPlatform::Region) {
|
|
||||||
+ stopShortcut = getShortcut(KGlobalAccel::self()->shortcut(ShortcutActions::self()->recordRegionAction()));
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ const auto messageBody = stopShortcut.isEmpty()
|
|
||||||
+ ? i18nc("recording notification message without shortcut", "To finish the recording, click the pulsing red System Tray icon.")
|
|
||||||
+ : xi18nc("recording notification message with shortcut", "To finish the recording, click the pulsing red System Tray icon or press <shortcut>%1</shortcut>.", stopShortcut.toString(QKeySequence::NativeText));
|
|
||||||
auto notification = new KNotification(u"notification"_s, KNotification::CloseOnTimeout | KNotification::DefaultEvent, this);
|
|
||||||
notification->setTitle(messageTitle);
|
|
||||||
notification->setText(messageBody);
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
|
|
||||||
From 7fd2fe158408b6285f9855f7fcf34e77d4ecb764 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Noah Davis <noahadvs@gmail.com>
|
|
||||||
Date: Thu, 12 Jun 2025 16:12:48 -0400
|
|
||||||
Subject: [PATCH 2/2] Rename shortcuts to show that they can start or stop
|
|
||||||
recording
|
|
||||||
|
|
||||||
BUG: 505081
|
|
||||||
---
|
|
||||||
desktop/org.kde.spectacle.desktop.cmake | 6 +++---
|
|
||||||
src/ShortcutActions.cpp | 6 +++---
|
|
||||||
2 files changed, 6 insertions(+), 6 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/desktop/org.kde.spectacle.desktop.cmake b/desktop/org.kde.spectacle.desktop.cmake
|
|
||||||
index 4e36b18e6..800fcb62b 100644
|
|
||||||
--- a/desktop/org.kde.spectacle.desktop.cmake
|
|
||||||
+++ b/desktop/org.kde.spectacle.desktop.cmake
|
|
||||||
@@ -474,7 +474,7 @@ Exec=${KDE_INSTALL_FULL_BINDIR}/spectacle -u
|
|
||||||
X-KDE-Shortcuts=Meta+Ctrl+Print
|
|
||||||
|
|
||||||
[Desktop Action RecordRegion]
|
|
||||||
-Name=Record Rectangular Region
|
|
||||||
+Name=Start/Stop Region Recording
|
|
||||||
Name[ar]=يسجل منطقة مستطيلة
|
|
||||||
Name[az]=Düzbucaqlı sahəni yazmaq
|
|
||||||
Name[bg]=Заснемане на правоъгълен регион
|
|
||||||
@@ -522,7 +522,7 @@ Exec=${KDE_INSTALL_FULL_BINDIR}/spectacle -R region
|
|
||||||
X-KDE-Shortcuts=Meta+Shift+R,Meta+R
|
|
||||||
|
|
||||||
[Desktop Action RecordScreen]
|
|
||||||
-Name=Record Screen
|
|
||||||
+Name=Start/Stop Screen Recording
|
|
||||||
Name[ar]=سجّل الشاشة
|
|
||||||
Name[az]=Ekranı yazmaq
|
|
||||||
Name[bg]=Запис на екрана
|
|
||||||
@@ -570,7 +570,7 @@ Exec=${KDE_INSTALL_FULL_BINDIR}/spectacle -R screen
|
|
||||||
X-KDE-Shortcuts=Meta+Alt+R
|
|
||||||
|
|
||||||
[Desktop Action RecordWindow]
|
|
||||||
-Name=Record Window
|
|
||||||
+Name=Start/Stop Window Recording
|
|
||||||
Name[ar]=سجل النافذة
|
|
||||||
Name[az]=Pəncərəni yazmaq
|
|
||||||
Name[bg]=Запис на прозорец
|
|
||||||
diff --git a/src/ShortcutActions.cpp b/src/ShortcutActions.cpp
|
|
||||||
index ee73bcfe5..a1464d06d 100644
|
|
||||||
--- a/src/ShortcutActions.cpp
|
|
||||||
+++ b/src/ShortcutActions.cpp
|
|
||||||
@@ -73,19 +73,19 @@ ShortcutActions::ShortcutActions()
|
|
||||||
mActions.addAction(action->objectName(), action);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
- QAction *action = new QAction(i18nc("@action global shortcut", "Record Screen"), &mActions);
|
|
||||||
+ QAction *action = new QAction(i18nc("@action global shortcut", "Start/Stop Screen Recording"), &mActions);
|
|
||||||
action->setObjectName(u"RecordScreen"_s);
|
|
||||||
action->setProperty("isConfigurationAction", true);
|
|
||||||
mActions.addAction(action->objectName(), action);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
- QAction *action = new QAction(i18nc("@action global shortcut", "Record Window"), &mActions);
|
|
||||||
+ QAction *action = new QAction(i18nc("@action global shortcut", "Start/Stop Window Recording"), &mActions);
|
|
||||||
action->setObjectName(u"RecordWindow"_s);
|
|
||||||
action->setProperty("isConfigurationAction", true);
|
|
||||||
mActions.addAction(action->objectName(), action);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
- QAction *action = new QAction(i18nc("@action global shortcut", "Record Rectangular Region"), &mActions);
|
|
||||||
+ QAction *action = new QAction(i18nc("@action global shortcut", "Start/Stop Region Recording"), &mActions);
|
|
||||||
action->setObjectName(u"RecordRegion"_s);
|
|
||||||
action->setProperty("isConfigurationAction", true);
|
|
||||||
mActions.addAction(action->objectName(), action);
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue