quickshell: dock geometry/icon-centering, GlobalStates, ConflictKiller service
Dock + pill z-order and icon centering pass; GlobalStates.qml and a ConflictKiller service for the ii sidebar.
This commit is contained in:
parent
165d814492
commit
ec7c4e8e8c
9 changed files with 330 additions and 107 deletions
100
surfaces/quickshell/GlobalStates.qml
Normal file
100
surfaces/quickshell/GlobalStates.qml
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// Souveraine patch to ii's stock GlobalStates.qml.
|
||||
//
|
||||
// Adds dockRevealPulse: a self-clearing "show the dock for a few seconds"
|
||||
// trigger, driven by the gesture pill (pill/shell.qml) via the dock IPC
|
||||
// while the on-screen keyboard has suppressed the dock (see Dock.qml's
|
||||
// computeDockState). Everything else in this file is unchanged stock ii —
|
||||
// diff against upstream before re-applying this patch if ii updates.
|
||||
import qs.modules.common
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property bool barOpen: true
|
||||
property bool crosshairOpen: false
|
||||
property bool sidebarLeftOpen: false
|
||||
property bool sidebarRightOpen: false
|
||||
property bool mediaControlsOpen: false
|
||||
property bool osdBrightnessOpen: false
|
||||
property bool osdVolumeOpen: false
|
||||
property bool oskOpen: false
|
||||
property bool overlayOpen: false
|
||||
property bool overviewOpen: false
|
||||
property bool regionSelectorOpen: false
|
||||
property bool searchOpen: false
|
||||
property bool screenLocked: false
|
||||
property bool screenLockContainsCharacters: false
|
||||
property bool screenUnlockFailed: false
|
||||
property bool screenTranslatorOpen: false
|
||||
property bool sessionOpen: false
|
||||
property bool superDown: false
|
||||
property bool superReleaseMightTrigger: true
|
||||
property real superPressTime: 0
|
||||
property real superLastPressDuration: -1
|
||||
property real superLastReleaseTime: 0
|
||||
property bool wallpaperSelectorOpen: false
|
||||
property bool workspaceShowNumbers: false
|
||||
property bool dockRevealPulse: false
|
||||
|
||||
function pulseDockReveal() {
|
||||
root.dockRevealPulse = true;
|
||||
dockRevealPulseTimer.restart();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: dockRevealPulseTimer
|
||||
interval: 3000
|
||||
onTriggered: root.dockRevealPulse = false
|
||||
}
|
||||
|
||||
function superPressDuration() {
|
||||
const now = Date.now();
|
||||
if (root.superPressTime > 0)
|
||||
return now - root.superPressTime;
|
||||
if (root.superLastReleaseTime > 0 && now - root.superLastReleaseTime < 250)
|
||||
return root.superLastPressDuration;
|
||||
return -1;
|
||||
}
|
||||
|
||||
function shouldSuppressSuperReleaseSearch() {
|
||||
const autoHide = Config?.options?.bar?.autoHide;
|
||||
const showWhenPressingSuper = autoHide?.showWhenPressingSuper;
|
||||
if (!autoHide?.enable || !showWhenPressingSuper?.enable || !showWhenPressingSuper?.suppressSearchOnHold)
|
||||
return false;
|
||||
|
||||
const duration = root.superPressDuration();
|
||||
return duration >= (showWhenPressingSuper?.suppressSearchDelay ?? showWhenPressingSuper?.delay ?? 140);
|
||||
}
|
||||
|
||||
onSidebarRightOpenChanged: {
|
||||
if (GlobalStates.sidebarRightOpen) {
|
||||
Notifications.timeoutAll();
|
||||
Notifications.markAllRead();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "workspaceNumber"
|
||||
description: "Hold to show workspace numbers, release to show icons"
|
||||
|
||||
onPressed: {
|
||||
root.superDown = true
|
||||
root.superPressTime = Date.now()
|
||||
root.superLastPressDuration = -1
|
||||
}
|
||||
onReleased: {
|
||||
const now = Date.now()
|
||||
if (root.superPressTime > 0)
|
||||
root.superLastPressDuration = now - root.superPressTime
|
||||
root.superLastReleaseTime = now
|
||||
root.superPressTime = 0
|
||||
root.superDown = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,14 +24,26 @@ Edit here → `./deploy.sh --phone` → restart. Deployed via symlink; live edit
|
|||
4. empty desktop / no focused app → **Peek**
|
||||
5. else (normal app focused) → **Hidden**
|
||||
- **Reserves 32px pill strip** at the bottom so it never covers the pill.
|
||||
- Depends on **`GlobalStates.dockRevealPulse` + `pulseDockReveal()`** — MUST be in the surface tree + deploy manifest (currently drift-only on phone; see gap below).
|
||||
- **Bar height is content-driven**: the window sizes itself to the button row (64px buttons) + row margin + pill strip; `Config.options.dock.height` is only a floor. Don't tune the config height to "fix" icon clipping.
|
||||
- **App list width is capped** to the screen (`DockApps.maxWidth`); past that the icon row scrolls horizontally by touch (flick is only enabled when overflowing, so drag-to-combine keeps working when everything fits).
|
||||
- **DockStack renders with the same content block as DockAppButton** (icon + half-reserved dot strip, centered as one unit). Keep them structurally identical or they drift apart on the bar.
|
||||
- Depends on **`GlobalStates.dockRevealPulse` + `pulseDockReveal()`** — lives in the surface tree (`GlobalStates.qml`, stock ii + pulse patch) and the deploy manifest. If ii updates its GlobalStates, re-diff and re-apply the patch.
|
||||
|
||||
## souveraine-settings (planned, not built)
|
||||
|
||||
A future settings app for the phone shell. Planned sections:
|
||||
|
||||
- **Dock** — pinned apps, height floor, monochrome icons, ignored-app regexes (today: hand-edited in `~/.config/illogical-impulse/config.json`).
|
||||
- **Stacks** — create/rename/reorder app stacks and their members (today: `dock.stacks` strings in config.json, or drag-to-combine on the dock).
|
||||
|
||||
The dock already reserves its entry points: the long-press menu's "App settings…"
|
||||
and the `dockSettings` IPC target (`openApp(appId)` / `open()`) in `Dock.qml`.
|
||||
Both are STUBS — they log and pulse the dock, nothing opens. That's deliberate:
|
||||
the IPC name stays stable so the settings app can take it over without touching
|
||||
the dock.
|
||||
|
||||
## Fullscreen modes (Hyprland 0.55, lua dispatch)
|
||||
|
||||
- Dispatch: `hl.dsp.window.fullscreen({ window="address:0x…", mode=N })`. Classic `fullscreen 1` errors.
|
||||
- **mode 0** = app-mode: whole display, no border/gaps, bar+pill stay (Overlay outranks it). ← the pill uses this.
|
||||
- **mode 1** = maximize but keeps 20px gaps + 2px border.
|
||||
|
||||
## Known gap
|
||||
|
||||
- `GlobalStates.qml` (the pulse patch) is NOT in the surface tree or `deploy.sh` manifest — only hand-patched live on the phone. A clean deploy ships a dock whose swipe/pulse IPC no-ops. Adopt it into the tree + manifest.
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ II="${QS}/ii"
|
|||
# Files replacing an upstream ii file get a one-time .upstream backup;
|
||||
# files with no upstream counterpart (new components) are just linked.
|
||||
MANIFEST="
|
||||
GlobalStates.qml ii/GlobalStates.qml
|
||||
services/Souveraine.qml ii/services/Souveraine.qml
|
||||
services/Ai.qml ii/services/Ai.qml
|
||||
services/TaskbarApps.qml ii/services/TaskbarApps.qml
|
||||
services/GlobalFocusGrab.qml ii/services/GlobalFocusGrab.qml
|
||||
services/Idle.qml ii/services/Idle.qml
|
||||
services/ConflictKiller.qml ii/services/ConflictKiller.qml
|
||||
modules/ii/sidebarLeft/SidebarLeft.qml ii/modules/ii/sidebarLeft/SidebarLeft.qml
|
||||
modules/ii/sidebarLeft/AiChat.qml ii/modules/ii/sidebarLeft/AiChat.qml
|
||||
modules/ii/sidebarRight/SidebarRight.qml ii/modules/ii/sidebarRight/SidebarRight.qml
|
||||
|
|
|
|||
|
|
@ -314,6 +314,13 @@ Singleton {
|
|||
|
||||
property JsonObject conflictKiller: JsonObject {
|
||||
property bool autoKillNotificationDaemons: false
|
||||
// DO NOT auto-kill trays here. On this phone kded6 is not a
|
||||
// competing Plasma tray — it is the StatusNotifierWatcher that
|
||||
// ii's OWN tray (Quickshell.Services.SystemTray) registers as a
|
||||
// host with (IsStatusNotifierHostRegistered=true). Killing it
|
||||
// just makes D-Bus re-activate it on ii's next tray call — an
|
||||
// infinite loop. Our services/ConflictKiller.qml override drops
|
||||
// the kded6 check entirely. Leave this false.
|
||||
property bool autoKillTrays: false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -192,7 +192,18 @@ Scope { // Scope
|
|||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
color: "transparent"
|
||||
|
||||
implicitHeight: (Config.options?.dock.height ?? 70) + Appearance.sizes.elevationMargin + Appearance.sizes.hyprlandGapsOut
|
||||
// Content-driven: tall enough for one 64px dock button + the
|
||||
// row's 8px bottom margin + the 32px pill strip, with Config
|
||||
// dock.height as a floor. A fixed config height (72) left the
|
||||
// visible bar ~36px for 64px buttons — icons poked out the
|
||||
// bottom and count dots landed under the bar. Size off the
|
||||
// BUTTON's implicit height, NOT dockRow.implicitHeight: the
|
||||
// separator's Layout margins inflate the row's implicit and
|
||||
// made the bar overshoot (content then top-aligned with a dead
|
||||
// band underneath).
|
||||
implicitHeight: Math.max(Config.options?.dock.height ?? 70,
|
||||
overviewButton.implicitHeight + 8 + 32)
|
||||
+ Appearance.sizes.elevationMargin + Appearance.sizes.hyprlandGapsOut
|
||||
|
||||
mask: Region {
|
||||
item: dockMouseArea
|
||||
|
|
@ -257,16 +268,34 @@ Scope { // Scope
|
|||
anchors.fill: dockVisualBackground
|
||||
anchors.topMargin: 0
|
||||
anchors.bottomMargin: 8
|
||||
// The background is built 2*padding wider than the
|
||||
// row (dockBackground.implicitWidth); inset the row
|
||||
// so that width actually becomes side padding —
|
||||
// without it the first icon sat ON the rounded corner.
|
||||
anchors.leftMargin: padding
|
||||
anchors.rightMargin: padding
|
||||
spacing: 3
|
||||
property real padding: 5
|
||||
|
||||
DockApps {
|
||||
id: dockApps
|
||||
buttonPadding: dockRow.padding
|
||||
// Keep the whole dock on-screen: the app list
|
||||
// may take at most what's left after the fixed
|
||||
// separator + overview button + paddings. Past
|
||||
// that it scrolls horizontally.
|
||||
maxWidth: dockRoot.width
|
||||
- Appearance.sizes.elevationMargin * 2
|
||||
- dockSeparator.implicitWidth
|
||||
- overviewButton.implicitWidth
|
||||
- dockRow.spacing * 2 - 5 * 2
|
||||
onRequestDockShowChanged: root.previewShowing = requestDockShow
|
||||
}
|
||||
DockSeparator {}
|
||||
DockSeparator {
|
||||
id: dockSeparator
|
||||
}
|
||||
DockButton {
|
||||
id: overviewButton
|
||||
Layout.fillHeight: true
|
||||
onClicked: GlobalStates.overviewOpen = !GlobalStates.overviewOpen
|
||||
topInset: Appearance.sizes.hyprlandGapsOut + dockRow.padding
|
||||
|
|
|
|||
|
|
@ -290,66 +290,74 @@ DockButton {
|
|||
|
||||
contentItem: Loader {
|
||||
active: !isSeparator
|
||||
// The Loader stretches its loaded item to the Loader's own size, so
|
||||
// width/height/centerIn declared ON the loaded item are overridden —
|
||||
// that stretched the block to the content area and the top-anchored
|
||||
// icon rode the top of the bar. The stretched item is a passthrough;
|
||||
// the real fixed-size block centers INSIDE it (same structure as
|
||||
// DockStack — keep them identical).
|
||||
sourceComponent: Item {
|
||||
anchors.centerIn: parent
|
||||
// Center the icon+dots block. Reserve only HALF the dot strip
|
||||
// below the icon: reserving the full strip pushed the block's
|
||||
// center below the icon's center, so centerIn left extra gap
|
||||
// above the icon (icons read a few px high). Half-reserve splits
|
||||
// the difference so the glyph sits visually centered.
|
||||
width: root.iconSize
|
||||
height: root.iconSize + (root.countDotHeight + 2) / 2
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
// Center the icon+dots block. Reserve only HALF the dot strip
|
||||
// below the icon: reserving the full strip pushed the block's
|
||||
// center below the icon's center, so centerIn left extra gap
|
||||
// above the icon (icons read a few px high). Half-reserve splits
|
||||
// the difference so the glyph sits visually centered.
|
||||
width: root.iconSize
|
||||
height: root.iconSize + (root.countDotHeight + 2) / 2
|
||||
|
||||
Loader {
|
||||
id: iconImageLoader
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
height: root.iconSize
|
||||
active: !root.isSeparator
|
||||
sourceComponent: IconImage {
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing")
|
||||
implicitSize: root.iconSize
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.options.dock.monochromeIcons
|
||||
anchors.fill: iconImageLoader
|
||||
sourceComponent: Item {
|
||||
Desaturate {
|
||||
id: desaturatedIcon
|
||||
visible: false // There's already color overlay
|
||||
anchors.fill: parent
|
||||
source: iconImageLoader
|
||||
desaturation: 0.8
|
||||
Loader {
|
||||
id: iconImageLoader
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: desaturatedIcon
|
||||
source: desaturatedIcon
|
||||
color: ColorUtils.transparentize(Appearance.colors.colPrimary, 0.9)
|
||||
height: root.iconSize
|
||||
active: !root.isSeparator
|
||||
sourceComponent: IconImage {
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing")
|
||||
implicitSize: root.iconSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 3
|
||||
anchors {
|
||||
top: iconImageLoader.bottom
|
||||
topMargin: 2
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
Loader {
|
||||
active: Config.options.dock.monochromeIcons
|
||||
anchors.fill: iconImageLoader
|
||||
sourceComponent: Item {
|
||||
Desaturate {
|
||||
id: desaturatedIcon
|
||||
visible: false // There's already color overlay
|
||||
anchors.fill: parent
|
||||
source: iconImageLoader
|
||||
desaturation: 0.8
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: desaturatedIcon
|
||||
source: desaturatedIcon
|
||||
color: ColorUtils.transparentize(Appearance.colors.colPrimary, 0.9)
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: Math.min(appToplevel.toplevels.length, 3)
|
||||
delegate: Rectangle {
|
||||
required property int index
|
||||
radius: Appearance.rounding.full
|
||||
implicitWidth: (appToplevel.toplevels.length <= 3) ?
|
||||
root.countDotWidth : root.countDotHeight // Circles when too many
|
||||
implicitHeight: root.countDotHeight
|
||||
color: appIsActive ? Appearance.colors.colPrimary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4)
|
||||
|
||||
RowLayout {
|
||||
spacing: 3
|
||||
anchors {
|
||||
top: iconImageLoader.bottom
|
||||
topMargin: 2
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Repeater {
|
||||
model: Math.min(appToplevel.toplevels.length, 3)
|
||||
delegate: Rectangle {
|
||||
required property int index
|
||||
radius: Appearance.rounding.full
|
||||
implicitWidth: (appToplevel.toplevels.length <= 3) ?
|
||||
root.countDotWidth : root.countDotHeight // Circles when too many
|
||||
implicitHeight: root.countDotHeight
|
||||
color: appIsActive ? Appearance.colors.colPrimary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,11 +30,19 @@ Item {
|
|||
property string dragTargetAppId: ""
|
||||
property string dragTargetStackId: ""
|
||||
|
||||
// Cap from Dock.qml: the widest the app list may grow before the dock
|
||||
// would outgrow the screen. Past it the ListView scrolls horizontally.
|
||||
property real maxWidth: -1
|
||||
|
||||
Layout.fillHeight: true
|
||||
// No top-only margin: it shoved the whole icon list DOWN inside the bar
|
||||
// with nothing balancing it — the actual cause of icons drooping below
|
||||
// the dock. fillHeight + the dockRow centering handle vertical placement.
|
||||
implicitWidth: listView.implicitWidth
|
||||
implicitWidth: maxWidth >= 0 ? Math.min(listView.contentWidth, maxWidth) : listView.contentWidth
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
function popupCenterXForButton(button) {
|
||||
if (!button || !root.QsWindow)
|
||||
|
|
@ -46,15 +54,12 @@ Item {
|
|||
id: listView
|
||||
spacing: 2
|
||||
orientation: ListView.Horizontal
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
implicitWidth: contentWidth
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
// Only flickable when the list actually overflows, so touch drags on
|
||||
// the buttons (drag-to-combine) aren't stolen while everything fits.
|
||||
interactive: contentWidth > width
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
model: ScriptModel {
|
||||
objectProp: "appId"
|
||||
|
|
|
|||
|
|
@ -143,52 +143,65 @@ DockButton {
|
|||
// grid of the member icons (up to 4), so it reads as "a group of these
|
||||
// apps" at a glance instead of a mystery blob. Count dots underneath.
|
||||
contentItem: Item {
|
||||
// Same block pattern as DockAppButton: icon+dots block centered as a
|
||||
// unit, reserving half the dot strip, so the stack rides the bar
|
||||
// exactly like a plain app icon instead of being placed by its own
|
||||
// rules.
|
||||
Item {
|
||||
id: collapsedStack
|
||||
anchors.centerIn: parent
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
height: root.iconSize + (root.countDotHeight + 2) / 2
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.small
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainer, 0.15)
|
||||
border.width: 1
|
||||
border.color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.8)
|
||||
}
|
||||
Grid {
|
||||
anchors.centerIn: parent
|
||||
columns: 2
|
||||
spacing: 3
|
||||
Repeater {
|
||||
model: Math.min(root.members.length, 4)
|
||||
delegate: IconImage {
|
||||
required property int index
|
||||
implicitSize: (root.iconSize - 11) / 2
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(root.members[index] ?? ""), "image-missing")
|
||||
Item {
|
||||
id: collapsedStack
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
height: root.iconSize
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.small
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainer, 0.15)
|
||||
border.width: 1
|
||||
border.color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.8)
|
||||
}
|
||||
Grid {
|
||||
anchors.centerIn: parent
|
||||
columns: 2
|
||||
spacing: 3
|
||||
Repeater {
|
||||
model: Math.min(root.members.length, 4)
|
||||
delegate: IconImage {
|
||||
required property int index
|
||||
implicitSize: (root.iconSize - 11) / 2
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(root.members[index] ?? ""), "image-missing")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Count dot(s) under the icon — same pattern as DockAppButton.
|
||||
RowLayout {
|
||||
spacing: 3
|
||||
anchors {
|
||||
top: collapsedStack.bottom
|
||||
topMargin: 2
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Repeater {
|
||||
model: Math.min(root.members.length, 3)
|
||||
delegate: Rectangle {
|
||||
required property int index
|
||||
radius: Appearance.rounding.full
|
||||
implicitWidth: (root.members.length <= 3) ? root.countDotWidth : root.countDotHeight
|
||||
implicitHeight: root.countDotHeight
|
||||
color: (root.appToplevel?.toplevels?.length ?? 0) > 0
|
||||
? Appearance.colors.colPrimary
|
||||
: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4)
|
||||
// Count dot(s) under the icon — same pattern as DockAppButton.
|
||||
RowLayout {
|
||||
spacing: 3
|
||||
anchors {
|
||||
top: collapsedStack.bottom
|
||||
topMargin: 2
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Repeater {
|
||||
model: Math.min(root.members.length, 3)
|
||||
delegate: Rectangle {
|
||||
required property int index
|
||||
radius: Appearance.rounding.full
|
||||
implicitWidth: (root.members.length <= 3) ? root.countDotWidth : root.countDotHeight
|
||||
implicitHeight: root.countDotHeight
|
||||
color: (root.appToplevel?.toplevels?.length ?? 0) > 0
|
||||
? Appearance.colors.colPrimary
|
||||
: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
surfaces/quickshell/services/ConflictKiller.qml
Normal file
47
surfaces/quickshell/services/ConflictKiller.qml
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Souveraine patch to ii's stock ConflictKiller.qml.
|
||||
//
|
||||
// Stock ii flags kded6 as a "conflicting tray". On this phone kded6 is not
|
||||
// a competing Plasma tray — it is the StatusNotifierWatcher that ii's OWN
|
||||
// tray (Quickshell.Services.SystemTray) registers as a host with. Killing
|
||||
// it just makes D-Bus re-activate it on ii's next tray call, and leaving
|
||||
// autoKillTrays off pops the kill dialog on every shell start. So: drop
|
||||
// the kded6 check entirely; keep the notification-daemon check unchanged.
|
||||
pragma Singleton
|
||||
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property string killDialogQmlPath: FileUtils.trimFileProtocol(Quickshell.shellPath("killDialog.qml"))
|
||||
|
||||
function load() {
|
||||
// dummy to force init
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Config
|
||||
function onReadyChanged() {
|
||||
if (Config.ready) checkConflictsProc.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: checkConflictsProc
|
||||
command: ["bash", "-c", `pidof mako dunst`]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const conflictingNotifications = this.text.trim().length > 0;
|
||||
if (!conflictingNotifications) return;
|
||||
if (Config.options.conflictKiller.autoKillNotificationDaemons)
|
||||
Quickshell.execDetached(["killall", "mako", "dunst"])
|
||||
else
|
||||
Quickshell.execDetached(["qs", "-p", root.killDialogQmlPath])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue