diff --git a/quickshell/Common/KeybindActions.js b/quickshell/Common/KeybindActions.js index 9f570a853..101486ca1 100644 --- a/quickshell/Common/KeybindActions.js +++ b/quickshell/Common/KeybindActions.js @@ -18,6 +18,7 @@ const DMS_ACTIONS = [ { id: "spawn dms ipc call island open controlcenter", label: "DankIsland: Open Control Center" }, { id: "spawn dms ipc call island open wallpaper", label: "DankIsland: Open Wallpapers" }, { id: "spawn dms ipc call island open weather", label: "DankIsland: Open Weather" }, + { id: "spawn dms ipc call island open notificationcenter", label: "DankIsland: Open Notification Center" }, { id: "spawn dms ipc call island cycle", label: "DankIsland: Cycle Activity" }, { id: "spawn dms ipc call island close", label: "DankIsland: Close" }, { id: "spawn dms ipc call defaultApp browser", label: "Default Web Browser: Open" }, diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index f4fa5aff6..df7d784ea 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -520,6 +520,7 @@ var SPEC = { notificationRules: { def: [] }, notificationDndAllowCritical: { def: true }, notificationFocusedMonitor: { def: false }, + dankIslandBarId: { def: "", onChange: "updateBarConfigs" }, dankIslandFloating: { def: false }, dankIslandUseOverlayLayer: { def: false }, diff --git a/quickshell/DMSShellIPC.qml b/quickshell/DMSShellIPC.qml index 0ef88b336..1f63d0d9b 100644 --- a/quickshell/DMSShellIPC.qml +++ b/quickshell/DMSShellIPC.qml @@ -217,7 +217,7 @@ Item { IpcHandler { function open(): string { - if (PopoutService.routeControlCenterToIsland(null, "", false)) + if (PopoutService.routeToIsland("controlcenter", null, false, "")) return "CONTROL_CENTER_OPEN_SUCCESS"; const bar = root.getPreferredBar("controlCenterButtonRef"); if (bar) { @@ -228,7 +228,7 @@ Item { } function hide(): string { - if (PopoutService.dankIslandRouter?.closeControlCenter?.() === true) + if (PopoutService.closeIslandActivity("controlcenter")) return "CONTROL_CENTER_HIDE_SUCCESS"; if (root.controlCenterLoader.item && root.controlCenterLoader.item.shouldBeVisible) { root.controlCenterLoader.item.close(); @@ -242,7 +242,7 @@ Item { root.controlCenterLoader.item.close(); return "CONTROL_CENTER_TOGGLE_SUCCESS"; } - if (PopoutService.routeControlCenterToIsland(null, "", true)) + if (PopoutService.routeToIsland("controlcenter", null, true, "")) return "CONTROL_CENTER_TOGGLE_SUCCESS"; const bar = root.getPreferredBar("controlCenterButtonRef"); @@ -313,13 +313,13 @@ Item { function _openDash(tab, position) { const tabId = _resolveTabId(tab); if (!position) { - if (tabId === "overview" && PopoutService.routeOverviewToIsland(null, false)) + if (tabId === "overview" && PopoutService.routeToIsland("home", null, false)) return true; - if (tabId === "media" && PopoutService.routeMediaToIsland(null, false)) + if (tabId === "media" && PopoutService.routeToIsland("media", null, false)) return true; - if (tabId === "wallpaper" && PopoutService.routeWallpaperToIsland(null, false)) + if (tabId === "wallpaper" && PopoutService.routeToIsland("wallpaper", null, false)) return true; - if (tabId === "weather" && PopoutService.routeWeatherToIsland(null, false)) + if (tabId === "weather" && PopoutService.routeToIsland("weather", null, false)) return true; } @@ -348,13 +348,13 @@ Item { const tabId = _resolveTabId(tab); if (!position) { - if (tabId === "overview" && PopoutService.routeOverviewToIsland(null, true)) + if (tabId === "overview" && PopoutService.routeToIsland("home", null, true)) return true; - if (tabId === "media" && PopoutService.routeMediaToIsland(null, true)) + if (tabId === "media" && PopoutService.routeToIsland("media", null, true)) return true; - if (tabId === "wallpaper" && PopoutService.routeWallpaperToIsland(null, true)) + if (tabId === "wallpaper" && PopoutService.routeToIsland("wallpaper", null, true)) return true; - if (tabId === "weather" && PopoutService.routeWeatherToIsland(null, true)) + if (tabId === "weather" && PopoutService.routeToIsland("weather", null, true)) return true; } @@ -377,7 +377,7 @@ Item { } function close(): string { - if (PopoutService.closeIslandHome() || PopoutService.closeIslandMedia() || PopoutService.closeIslandWallpaper() || PopoutService.closeIslandWeather()) + if (PopoutService.closeIslandActivity("home") || PopoutService.closeIslandActivity("media") || PopoutService.closeIslandActivity("wallpaper") || PopoutService.closeIslandActivity("weather")) return "DASH_CLOSE_SUCCESS"; if (root.dankDashPopoutLoader.item) { root.dankDashPopoutLoader.item.dashVisible = false; @@ -785,7 +785,7 @@ Item { // ! TODO - remove for v1.6 IpcHandler { function wallpaper(): string { - if (PopoutService.routeWallpaperToIsland(null, true)) + if (PopoutService.routeToIsland("wallpaper", null, true)) return "WARN; deprecated, use dms ipc call dash toggle wallpaper instead"; const bar = root.getPreferredBar("clockButtonRef") || root.getPreferredBar(); if (bar) { diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalIsland.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalIsland.qml index da2e5b236..f2b184bf6 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalIsland.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalIsland.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import QtQuick import qs.Services @@ -98,10 +100,12 @@ Item { return; if (spotlightOpen) { _islandWasOpen = true; - } else if (_islandWasOpen) { - _islandWasOpen = false; - dialogClosed(); + return; } + if (!_islandWasOpen) + return; + _islandWasOpen = false; + dialogClosed(); } DankLauncherV2ModalSpotlight { diff --git a/quickshell/Modals/NotificationModal.qml b/quickshell/Modals/NotificationModal.qml index c8a939a6b..5526f99c3 100644 --- a/quickshell/Modals/NotificationModal.qml +++ b/quickshell/Modals/NotificationModal.qml @@ -123,21 +123,21 @@ DankModal { IpcHandler { // DankIsland owns this trigger whenever it is the sole bar for the screen. function open(): string { - if (PopoutService.routeNotificationCenterToIsland(null, false)) + if (PopoutService.routeToIsland("notificationcenter", null, false)) return "NOTIFICATION_ISLAND_OPEN_SUCCESS"; notificationModal.show(); return "NOTIFICATION_MODAL_OPEN_SUCCESS"; } function close(): string { - if (PopoutService.closeIslandNotificationCenter()) + if (PopoutService.closeIslandActivity("notificationcenter")) return "NOTIFICATION_ISLAND_CLOSE_SUCCESS"; notificationModal.hide(); return "NOTIFICATION_MODAL_CLOSE_SUCCESS"; } function toggle(): string { - if (PopoutService.routeNotificationCenterToIsland(null, true)) + if (PopoutService.routeToIsland("notificationcenter", null, true)) return "NOTIFICATION_ISLAND_TOGGLE_SUCCESS"; notificationModal.toggle(); return "NOTIFICATION_MODAL_TOGGLE_SUCCESS"; diff --git a/quickshell/Modules/ControlCenter/ControlCenterContent.qml b/quickshell/Modules/ControlCenter/ControlCenterContent.qml index a352f0bf5..81b7d2570 100644 --- a/quickshell/Modules/ControlCenter/ControlCenterContent.qml +++ b/quickshell/Modules/ControlCenter/ControlCenterContent.qml @@ -7,7 +7,6 @@ import qs.Modules.ControlCenter.Models import qs.Modules.ControlCenter.Details import qs.Widgets -// Host-agnostic Control Center body; popout and island both host this. Rectangle { id: root diff --git a/quickshell/Modules/DankDash/MediaPlayerIslandChrome.qml b/quickshell/Modules/DankDash/MediaPlayerIslandChrome.qml index b792ef8aa..103b8796d 100644 --- a/quickshell/Modules/DankDash/MediaPlayerIslandChrome.qml +++ b/quickshell/Modules/DankDash/MediaPlayerIslandChrome.qml @@ -4,7 +4,6 @@ import QtQuick import Quickshell.Services.Mpris import Quickshell.Widgets import qs.Common -import qs.Modules.DankIsland.Activities import qs.Services import qs.Widgets diff --git a/quickshell/Modules/DankIsland/Activities/BatteryPill.qml b/quickshell/Modules/DankIsland/Activities/BatteryPill.qml index aee680add..915116fb3 100644 --- a/quickshell/Modules/DankIsland/Activities/BatteryPill.qml +++ b/quickshell/Modules/DankIsland/Activities/BatteryPill.qml @@ -50,7 +50,6 @@ Item { id: numberGlyph anchors.verticalCenter: parent.verticalCenter - // Digits have no descenders; sink by half the descent to optically center. anchors.verticalCenterOffset: Math.round(digitMetrics.descent / 2) text: root.numberText color: glyphs.glyphColor @@ -72,7 +71,11 @@ Item { width: root.bodyWidth height: root.thickness radius: Math.round(Math.min(width, height) * 0.34) - color: root.outlined ? (root.hovered ? Theme.withAlpha(Theme.surfaceVariant, 0.45) : "transparent") : (root.hovered ? Theme.surfaceVariant : Theme.withAlpha(Theme.surfaceVariant, 0.9)) + color: { + if (root.outlined) + return root.hovered ? Theme.withAlpha(Theme.surfaceVariant, 0.45) : "transparent"; + return root.hovered ? Theme.surfaceVariant : Theme.withAlpha(Theme.surfaceVariant, 0.9); + } border.width: root.outlined ? 1.2 : 0 border.color: root.fillColor diff --git a/quickshell/Modules/DankIsland/Activities/ControlCenterExpanded.qml b/quickshell/Modules/DankIsland/Activities/ControlCenterExpanded.qml index 1ac10512e..bce5bc7bf 100644 --- a/quickshell/Modules/DankIsland/Activities/ControlCenterExpanded.qml +++ b/quickshell/Modules/DankIsland/Activities/ControlCenterExpanded.qml @@ -5,7 +5,6 @@ import qs.Common import qs.Modules.ControlCenter import qs.Services -// Shared Control Center body hosted in the island silhouette. FocusScope { id: root @@ -17,7 +16,6 @@ FocusScope { property real alignedY: 0 property real alignedWidth: 0 property real alignedHeight: 0 - // Report content height once per turn; the controller clamps it before retargeting the spring. property real bottomInset: Theme.spacingM property bool _heightReportPending: false @@ -46,7 +44,7 @@ FocusScope { root._heightReportPending = true; Qt.callLater(() => { root._heightReportPending = false; - root.islandController.setControlCenterContentHeight(content.targetImplicitHeight + Theme.spacingXS + root.bottomInset); + root.islandController.setDestinationContentHeight("controlcenter", content.targetImplicitHeight + Theme.spacingXS + root.bottomInset); }); } @@ -59,7 +57,6 @@ FocusScope { property var expandedWidgetData: null readonly property bool shouldBeVisible: root.islandController.activeActivity === "controlcenter" && root.islandController.expanded - // The island has no outside chrome to click, so the header row closes it. readonly property bool headerTogglesClose: true readonly property bool powerMenuOpen: root.powerMenuModalLoader?.item?.shouldBeVisible ?? false readonly property var screen: root.effectiveScreen @@ -111,7 +108,6 @@ FocusScope { root.releaseScanState(); } - // Hot-unplug skips the visibility path; release scans here too. Component.onDestruction: { if (hostContract.shouldBeVisible) root.releaseScanState(); @@ -156,8 +152,9 @@ FocusScope { Connections { target: root.islandController - function onControlCenterSessionSerialChanged() { - Qt.callLater(root.beginSession); + function onSessionStarted(activityId) { + if (activityId === "controlcenter") + Qt.callLater(root.beginSession); } function onExpandedChanged() { @@ -171,5 +168,5 @@ FocusScope { } } - Component.onCompleted: root.islandController.markControlCenterVisualsReady() + Component.onCompleted: root.islandController.markVisualsReady("controlcenter") } diff --git a/quickshell/Modules/DankIsland/Activities/HomeCompact.qml b/quickshell/Modules/DankIsland/Activities/HomeCompact.qml index e97c3b211..ae7a79b05 100644 --- a/quickshell/Modules/DankIsland/Activities/HomeCompact.qml +++ b/quickshell/Modules/DankIsland/Activities/HomeCompact.qml @@ -139,7 +139,7 @@ Item { id: weatherRow anchors.centerIn: parent - spacing: 2 + spacing: Theme.spacingXXS visible: slot.isWeather DankIcon { @@ -213,11 +213,10 @@ Item { anchors.verticalCenter: parent.verticalCenter spacing: root.tight ? Theme.spacingXS : Theme.spacingS - Text { + StyledText { anchors.verticalCenter: parent.verticalCenter text: root.timeText color: Theme.surfaceText - font.family: Theme.fontFamily font.pixelSize: root.tight ? Theme.fontSizeSmall : Theme.fontSizeMedium font.weight: Font.DemiBold } @@ -230,13 +229,12 @@ Item { color: Theme.primary } - Text { + StyledText { id: dateLabel anchors.verticalCenter: parent.verticalCenter text: root.dateText color: Theme.surfaceTextMedium - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: Font.Medium } diff --git a/quickshell/Modules/DankIsland/Activities/IslandSlotHoverArea.qml b/quickshell/Modules/DankIsland/Activities/IslandSlotHoverArea.qml index 68b16b5b5..057917da0 100644 --- a/quickshell/Modules/DankIsland/Activities/IslandSlotHoverArea.qml +++ b/quickshell/Modules/DankIsland/Activities/IslandSlotHoverArea.qml @@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound import QtQuick -// Compact-face slot click target; hover here suppresses island hover-expand. MouseArea { id: root diff --git a/quickshell/Modules/DankIsland/Activities/LauncherExpanded.qml b/quickshell/Modules/DankIsland/Activities/LauncherExpanded.qml index fcd250d5f..45d874f4d 100644 --- a/quickshell/Modules/DankIsland/Activities/LauncherExpanded.qml +++ b/quickshell/Modules/DankIsland/Activities/LauncherExpanded.qml @@ -86,11 +86,12 @@ FocusScope { Connections { target: root.islandController - function onLauncherSessionSerialChanged() { - Qt.callLater(root.initializeSession); + function onSessionStarted(activityId) { + if (activityId === "launcher") + Qt.callLater(root.initializeSession); } } - Component.onCompleted: root.islandController.markLauncherVisualsReady() + Component.onCompleted: root.islandController.markVisualsReady("launcher") Component.onDestruction: root.islandController.launcherInputFocused = false } diff --git a/quickshell/Modules/DankIsland/Activities/MediaCava.qml b/quickshell/Modules/DankIsland/Activities/MediaCava.qml index 61dc86101..7191afb33 100644 --- a/quickshell/Modules/DankIsland/Activities/MediaCava.qml +++ b/quickshell/Modules/DankIsland/Activities/MediaCava.qml @@ -43,28 +43,20 @@ Item { bandsB = nextB; } - onLiveChanged: { - if (live) - updateBands(); - else + function syncBands() { + if (!live) { applyIdleBands(); - } - - onAvailableChanged: { - if (!available) return; - if (live) - updateBands(); - else - applyIdleBands(); + } + updateBands(); } - Component.onCompleted: { - if (root.live) - updateBands(); - else - applyIdleBands(); + onLiveChanged: syncBands() + onAvailableChanged: { + if (available) + syncBands(); } + Component.onCompleted: syncBands() Loader { active: root.live && root.available diff --git a/quickshell/Modules/DankIsland/Activities/MediaCompact.qml b/quickshell/Modules/DankIsland/Activities/MediaCompact.qml index f7d8dbc2a..229ae7739 100644 --- a/quickshell/Modules/DankIsland/Activities/MediaCompact.qml +++ b/quickshell/Modules/DankIsland/Activities/MediaCompact.qml @@ -4,6 +4,7 @@ import QtQuick import Quickshell import qs.Common import qs.Services +import qs.Widgets Item { id: root @@ -35,8 +36,6 @@ Item { TextMetrics { id: titleMetrics - - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: Font.DemiBold text: root.mediaModel.title @@ -44,8 +43,6 @@ Item { TextMetrics { id: artistMetrics - - font.family: Theme.fontFamily font.pixelSize: Math.max(9, Theme.fontSizeSmall - 2) text: root.dense ? "" : root.mediaModel.artist } @@ -104,7 +101,7 @@ Item { height: titleText.implicitHeight clip: true - Text { + StyledText { id: titleText readonly property bool onScreen: Window.window?.visible ?? false @@ -153,7 +150,6 @@ Item { x: needsScrolling ? -scrollOffset : 0 text: root.mediaModel.title color: Theme.surfaceText - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: Font.DemiBold wrapMode: Text.NoWrap @@ -186,13 +182,12 @@ Item { } } - Text { + StyledText { x: root.textGutter width: parent.width - root.textGutter * 2 visible: !root.dense text: root.mediaModel.artist color: Theme.surfaceTextSecondary - font.family: Theme.fontFamily font.pixelSize: Math.max(9, Theme.fontSizeSmall - 2) elide: Text.ElideRight } @@ -218,13 +213,12 @@ Item { radius: height / 2 color: clockArea.containsMouse ? Theme.surfaceTextHover : "transparent" - Text { + StyledText { id: clockText anchors.centerIn: parent text: root.timeText color: Theme.surfaceText - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: Font.DemiBold } diff --git a/quickshell/Modules/DankIsland/Activities/NotificationCenterExpanded.qml b/quickshell/Modules/DankIsland/Activities/NotificationCenterExpanded.qml index 71c9c546a..693ae82ee 100644 --- a/quickshell/Modules/DankIsland/Activities/NotificationCenterExpanded.qml +++ b/quickshell/Modules/DankIsland/Activities/NotificationCenterExpanded.qml @@ -5,7 +5,6 @@ import qs.Common import qs.Modules.Notifications.Center import qs.Services -// Shared notification center body hosted in the island silhouette. FocusScope { id: root @@ -28,7 +27,6 @@ FocusScope { if (!hostContract.shouldBeVisible) return; root._sessionOpen = true; - // Suppresses transient island cards for as long as the list owns them. NotificationService.onOverlayOpen(); keyboardController.reset(); keyboardController.listView = content.notificationList; @@ -56,14 +54,14 @@ FocusScope { Qt.callLater(() => { root._heightReportPending = false; content.notificationList.syncSessionContentHeight(); - root.islandController.setNotificationCenterContentHeight(content.targetImplicitHeight + root.bottomInset); + root.islandController.setDestinationContentHeight("notificationcenter", content.targetImplicitHeight + root.bottomInset); }); } function prepareVisuals() { content.notificationList.syncSessionContentHeight(); - root.islandController.setNotificationCenterContentHeight(content.targetImplicitHeight + root.bottomInset); - root.islandController.markNotificationCenterVisualsReady(); + root.islandController.setDestinationContentHeight("notificationcenter", content.targetImplicitHeight + root.bottomInset); + root.islandController.markVisualsReady("notificationcenter"); } QtObject { @@ -116,8 +114,9 @@ FocusScope { Connections { target: root.islandController - function onNotificationCenterSessionSerialChanged() { - Qt.callLater(root.beginSession); + function onSessionStarted(activityId) { + if (activityId === "notificationcenter") + Qt.callLater(root.beginSession); } function onExpandedChanged() { diff --git a/quickshell/Modules/DankIsland/Activities/NotificationCompact.qml b/quickshell/Modules/DankIsland/Activities/NotificationCompact.qml index 4ac3c0b2a..f4c792301 100644 --- a/quickshell/Modules/DankIsland/Activities/NotificationCompact.qml +++ b/quickshell/Modules/DankIsland/Activities/NotificationCompact.qml @@ -43,23 +43,20 @@ Item { width: parent.width - root.iconSize - parent.spacing - (root.notificationModel.critical ? 3 + parent.spacing : 0) spacing: 1 - // A dense pill keeps one line, so the app name folds into the summary. - Text { + StyledText { width: parent.width visible: !root.dense text: root.notificationModel.appName + (root.notificationModel.timeText ? " · " + root.notificationModel.timeText : "") color: Theme.surfaceTextSecondary - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: Font.Medium elide: Text.ElideRight } - Text { + StyledText { width: parent.width text: root.dense && root.notificationModel.appName ? root.notificationModel.appName + " · " + root.notificationModel.summary : root.notificationModel.summary color: Theme.surfaceText - font.family: Theme.fontFamily font.pixelSize: root.dense ? Theme.fontSizeSmall : Theme.fontSizeMedium font.weight: Font.DemiBold elide: Text.ElideRight diff --git a/quickshell/Modules/DankIsland/Activities/NotificationExpanded.qml b/quickshell/Modules/DankIsland/Activities/NotificationExpanded.qml index ee058c093..572ebef1b 100644 --- a/quickshell/Modules/DankIsland/Activities/NotificationExpanded.qml +++ b/quickshell/Modules/DankIsland/Activities/NotificationExpanded.qml @@ -39,52 +39,34 @@ Item { Column { anchors.verticalCenter: parent.verticalCenter width: parent.width - 46 - closeButton.width - parent.spacing * 2 - spacing: 2 + spacing: Theme.spacingXXS - Text { + StyledText { width: parent.width text: root.notificationModel.appName color: Theme.surfaceText - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeMedium font.weight: Font.DemiBold elide: Text.ElideRight } - Text { + StyledText { width: parent.width text: root.notificationModel.timeText color: Theme.surfaceTextSecondary - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall elide: Text.ElideRight } } - Rectangle { + DankActionButton { id: closeButton anchors.verticalCenter: parent.verticalCenter - width: 38 - height: 38 - radius: 19 - color: closeArea.containsMouse ? Theme.surfaceTextHover : "transparent" - - DankIcon { - anchors.centerIn: parent - name: "close" - size: 21 - color: Theme.surfaceText - } - - MouseArea { - id: closeArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: root.notificationModel.dismiss() - } + iconName: "close" + iconSize: 21 + buttonSize: 38 + onClicked: root.notificationModel.dismiss() } } @@ -99,11 +81,10 @@ Item { } spacing: Theme.spacingS - Text { + StyledText { width: parent.width text: root.notificationModel.summary color: Theme.surfaceText - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeLarge font.weight: Font.DemiBold wrapMode: Text.WrapAtWordBoundaryOrAnywhere @@ -111,11 +92,10 @@ Item { elide: Text.ElideRight } - Text { + StyledText { width: parent.width text: root.notificationModel.body color: Theme.surfaceTextMedium - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall wrapMode: Text.WrapAtWordBoundaryOrAnywhere maximumLineCount: 3 @@ -134,61 +114,29 @@ Item { height: 34 spacing: Theme.spacingS - Rectangle { - width: dismissLabel.implicitWidth + Theme.spacingL * 2 + DankButton { height: parent.height radius: height / 2 - color: dismissArea.containsMouse ? Theme.surfaceTextHover : "transparent" + text: I18n.tr("Dismiss") + backgroundColor: "transparent" + textColor: Theme.surfaceText + buttonHeight: parent.height + horizontalPadding: Theme.spacingL border.width: 1 border.color: Theme.withAlpha(Theme.outline, 0.5) - - Text { - id: dismissLabel - - anchors.centerIn: parent - text: "Dismiss" - color: Theme.surfaceText - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall - font.weight: Font.Medium - } - - MouseArea { - id: dismissArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: root.notificationModel.dismiss() - } + onClicked: root.notificationModel.dismiss() } - Rectangle { + DankButton { visible: root.notificationModel.hasAction - width: actionLabel.implicitWidth + Theme.spacingL * 2 height: parent.height radius: height / 2 - color: actionArea.containsMouse ? Theme.primaryContainer : Theme.primary - - Text { - id: actionLabel - - anchors.centerIn: parent - text: root.notificationModel.actionLabel - color: actionArea.containsMouse ? Theme.primary : Theme.onPrimary - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall - font.weight: Font.DemiBold - } - - MouseArea { - id: actionArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: root.notificationModel.activate() - } + text: root.notificationModel.actionLabel + backgroundColor: Theme.primary + textColor: Theme.onPrimary + buttonHeight: parent.height + horizontalPadding: Theme.spacingL + onClicked: root.notificationModel.activate() } } } diff --git a/quickshell/Modules/DankIsland/Activities/SystemLevelExpanded.qml b/quickshell/Modules/DankIsland/Activities/SystemLevelExpanded.qml index 0ab1b6405..80ab67359 100644 --- a/quickshell/Modules/DankIsland/Activities/SystemLevelExpanded.qml +++ b/quickshell/Modules/DankIsland/Activities/SystemLevelExpanded.qml @@ -12,7 +12,7 @@ Item { Column { anchors { fill: parent - margins: 24 + margins: Theme.spacingXL } spacing: Theme.spacingL @@ -44,85 +44,50 @@ Item { Column { anchors.verticalCenter: parent.verticalCenter width: parent.width - 48 - valueText.width - parent.spacing * 2 - spacing: 2 + spacing: Theme.spacingXXS - Text { + StyledText { text: root.systemModel.title color: Theme.surfaceText - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeLarge font.weight: Font.DemiBold } - Text { - text: root.systemModel.volumeActivity ? "Click the icon to mute" : "Display level" + StyledText { + text: root.systemModel.volumeActivity ? I18n.tr("Click the icon to mute") : I18n.tr("Display level") color: Theme.surfaceTextSecondary - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } } - Text { + StyledText { id: valueText anchors.verticalCenter: parent.verticalCenter text: root.systemModel.displayValue color: Theme.primary - font.family: Theme.fontFamily font.pixelSize: 28 font.weight: Font.DemiBold } } - Item { - id: slider + DankSlider { + id: levelSlider width: parent.width height: 40 + minimum: root.systemModel.minimum + maximum: Math.max(root.systemModel.minimum + 1, Math.round(root.systemModel.maximum)) + enabled: root.systemModel.available + showValue: false + unit: root.systemModel.unit + thumbOutlineColor: Theme.surfaceContainerHigh + valueOverride: Math.round(root.systemModel.value) + onSliderValueChanged: newValue => root.systemModel.setRatio(newValue / maximum) - Rectangle { - id: track - - anchors.verticalCenter: parent.verticalCenter - width: parent.width - height: 12 - radius: 6 - color: Theme.withAlpha(Theme.surfaceText, 0.14) - - Rectangle { - width: parent.width * root.systemModel.ratio - height: parent.height - radius: parent.radius - color: Theme.primary - } - - Rectangle { - x: Math.max(0, Math.min(parent.width - width, parent.width * root.systemModel.ratio - width / 2)) - anchors.verticalCenter: parent.verticalCenter - width: 22 - height: 22 - radius: 11 - color: Theme.primary - border.width: 3 - border.color: Theme.surfaceContainerHigh - } - } - - MouseArea { - anchors.fill: parent - enabled: root.systemModel.available - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - - function updateValue(mouse) { - root.systemModel.setRatio(mouse.x / width); - } - - onPressed: mouse => updateValue(mouse) - onPositionChanged: mouse => { - if (pressed) - updateValue(mouse); - } + Binding on value { + value: Math.round(root.systemModel.value) + when: !levelSlider.isDragging } } } diff --git a/quickshell/Modules/DankIsland/Activities/WallpaperExpanded.qml b/quickshell/Modules/DankIsland/Activities/WallpaperExpanded.qml index 1a01f627e..0edff88f8 100644 --- a/quickshell/Modules/DankIsland/Activities/WallpaperExpanded.qml +++ b/quickshell/Modules/DankIsland/Activities/WallpaperExpanded.qml @@ -81,18 +81,19 @@ FocusScope { Connections { target: root.islandController - function onWallpaperSessionSerialChanged() { - Qt.callLater(root.beginSession); + function onSessionStarted(activityId) { + if (activityId === "wallpaper") + Qt.callLater(root.beginSession); } } Component.onCompleted: { root.contentStaged = true; - root.islandController.markWallpaperVisualsReady(); + root.islandController.markVisualsReady("wallpaper"); } Component.onDestruction: { root.islandController.keyboardYielded = false; - root.islandController.wallpaperVisualsReady = false; + root.islandController.setVisualsReady("wallpaper", false); } } diff --git a/quickshell/Modules/DankIsland/Activities/WeatherExpanded.qml b/quickshell/Modules/DankIsland/Activities/WeatherExpanded.qml index 4daabe84e..42f059833 100644 --- a/quickshell/Modules/DankIsland/Activities/WeatherExpanded.qml +++ b/quickshell/Modules/DankIsland/Activities/WeatherExpanded.qml @@ -60,10 +60,10 @@ FocusScope { Component.onCompleted: { root.contentStaged = true; - root.islandController.markWeatherVisualsReady(); + root.islandController.markVisualsReady("weather"); } Component.onDestruction: { - root.islandController.weatherVisualsReady = false; + root.islandController.setVisualsReady("weather", false); } } diff --git a/quickshell/Modules/DankIsland/DankIsland.qml b/quickshell/Modules/DankIsland/DankIsland.qml index 0de791607..9c7099147 100644 --- a/quickshell/Modules/DankIsland/DankIsland.qml +++ b/quickshell/Modules/DankIsland/DankIsland.qml @@ -16,79 +16,95 @@ Item { readonly property bool launcherOpen: root.activityOpen("launcher") readonly property bool controlCenterOpen: root.activityOpen("controlcenter") - readonly property bool wallpaperOpen: root.activityOpen("wallpaper") - readonly property bool weatherOpen: root.activityOpen("weather") - readonly property bool mediaOpen: root.activityOpen("media") - readonly property bool homeOpen: root.activityOpen("home") - readonly property bool notificationCenterOpen: root.activityOpen("notificationcenter") - function activityOpen(activityId) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.islandController?.activeActivity === activityId && hosts[i].islandController.expanded) - return true; - } - return false; + function hosts() { + return islandVariants.instances || []; } function hostWithActivity(activityId) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.islandController?.activeActivity === activityId && hosts[i].islandController.expanded) - return hosts[i]; + for (const host of hosts()) { + if (host?.islandController?.activeActivity === activityId && host.islandController.expanded) + return host; } return null; } + function activityOpen(activityId) { + return root.hostWithActivity(activityId) !== null; + } + + function hostForExactScreen(screen) { + if (!screen) + return null; + for (const host of hosts()) { + if (host?.screen === screen || host?.screen?.name === screen.name) + return host; + } + return null; + } + + function hostForScreenName(screenName) { + for (const host of hosts()) { + if (host?.screen?.name === screenName) + return host; + } + return null; + } + + function focusedHost() { + const focusedName = CompositorService.getFocusedScreen()?.name ?? ""; + return focusedName ? root.hostForScreenName(focusedName) : null; + } + + function focusedIslandScreen() { + return root.focusedHost()?.screen ?? null; + } + function hasHostForScreen(screen) { if (!screen) - return (islandVariants.instances || []).length > 0; - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen === screen || hosts[i]?.screen?.name === screen.name) - return true; - } - return false; + return hosts().length > 0; + return root.hostForExactScreen(screen) !== null; + } + + function hostForScreenOrFocused(screen) { + return root.hostForExactScreen(screen) ?? root.focusedHost(); } function hostForScreen(screenName) { const requestedName = (screenName || "").trim(); - const hosts = islandVariants.instances || []; - // An explicitly named screen with no island is an error, not a redirect to another display. - if (requestedName) { - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen?.name === requestedName) - return hosts[i]; - } - return null; - } - const focusedName = CompositorService.getFocusedScreen()?.name ?? ""; - if (focusedName) { - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen?.name === focusedName) - return hosts[i]; - } - } - return hosts.length > 0 ? hosts[0] : null; + if (requestedName) + return root.hostForScreenName(requestedName); + return root.focusedHost() ?? (hosts()[0] ?? null); } function activityName(activity) { const requested = (activity || "home").trim().toLowerCase(); - if (requested === "media" || requested === "launcher" || requested === "controlcenter" || requested === "wallpaper" || requested === "weather" || requested === "notificationcenter") + switch (requested) { + case "media": + case "launcher": + case "controlcenter": + case "wallpaper": + case "weather": + case "notificationcenter": return requested; - if (requested === "control-center" || requested === "cc") + case "control-center": + case "cc": return "controlcenter"; - if (requested === "notifications" || requested === "notification-center" || requested === "notification" || requested === "nc") + case "notifications": + case "notification-center": + case "notification": + case "nc": return "notificationcenter"; + } return "home"; } - function openActivityOn(host, activityId) { + function openActivityOn(host, activityId, section) { switch (activityId) { case "launcher": return host.islandController.requestLauncher("", "", false); case "controlcenter": - return host.islandController.requestControlCenter("", false); + return host.islandController.requestControlCenter(section || "", false); case "wallpaper": return host.islandController.requestWallpaper(false); case "weather": @@ -99,24 +115,26 @@ Item { return host.islandController.requestActivity(activityId, true, true); } - function focusedHost() { - const focusedName = CompositorService.getFocusedScreen()?.name ?? ""; - if (!focusedName) - return null; - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen?.name === focusedName) - return hosts[i]; + function openActivity(activityId, screen, section): bool { + const host = root.hostForScreenOrFocused(screen); + return host ? root.openActivityOn(host, activityId, section) === true : false; + } + + function toggleActivity(activityId, screen, section): bool { + const openHost = root.hostWithActivity(activityId); + if (openHost) { + openHost.islandController.requestCollapse(); + return true; } - return null; + return root.openActivity(activityId, screen, section); } - function focusedIslandScreen() { - return root.focusedHost()?.screen ?? null; - } - - function activeLauncherHost() { - return root.hostWithActivity("launcher"); + function closeActivity(activityId): bool { + const host = root.hostWithActivity(activityId); + if (!host) + return false; + host.islandController.requestCollapse(); + return true; } function openLauncher(query, mode): bool { @@ -125,219 +143,86 @@ Item { } function toggleLauncher(query, mode): bool { - const openHost = root.activeLauncherHost(); - if (openHost) { - openHost.islandController.requestCollapse(); + if (root.closeActivity("launcher")) return true; - } - const host = root.focusedHost(); - return host ? host.islandController.requestLauncher(query || "", mode || "", false) : false; + return root.openLauncher(query, mode); } function closeLauncher(): bool { - const host = root.activeLauncherHost(); + return root.closeActivity("launcher"); + } + + function ipcOpen(activity, screen) { + const host = root.hostForScreen(screen); if (!host) - return false; - host.islandController.requestCollapse(); - return true; + return "DANK_ISLAND_UNAVAILABLE"; + const requested = root.activityName(activity); + if (!root.openActivityOn(host, requested, "")) + return `DANK_ISLAND_ACTIVITY_UNAVAILABLE: ${requested}`; + return `DANK_ISLAND_OPEN: ${requested}\t${host.screen?.name ?? ""}`; } - function controlCenterHostForScreen(screen) { - if (screen) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen === screen || hosts[i]?.screen?.name === screen.name) - return hosts[i]; - } - } - return root.focusedHost(); - } - - function openControlCenter(section, screen): bool { - const host = root.controlCenterHostForScreen(screen); - return host ? host.islandController.requestControlCenter(section || "", false) : false; - } - - function toggleControlCenter(section, screen): bool { - const openHost = root.hostWithActivity("controlcenter"); - if (openHost) { - openHost.islandController.requestCollapse(); - return true; - } - return root.openControlCenter(section, screen); - } - - function closeControlCenter(): bool { - const host = root.hostWithActivity("controlcenter"); + function ipcToggle(activity, screen) { + const host = root.hostForScreen(screen); if (!host) - return false; - host.islandController.requestCollapse(); - return true; - } - - function wallpaperHostForScreen(screen) { - if (screen) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen === screen || hosts[i]?.screen?.name === screen.name) - return hosts[i]; - } + return "DANK_ISLAND_UNAVAILABLE"; + if (host.islandController.expanded && !host.islandController.notificationActive) { + host.islandController.requestCollapse(); + return `DANK_ISLAND_CLOSED: ${host.screen?.name ?? ""}`; } - return root.focusedHost(); + return ipcOpen(activity, screen); } - function openWallpaper(screen): bool { - const host = root.wallpaperHostForScreen(screen); - return host ? host.islandController.requestWallpaper(false) : false; - } - - function toggleWallpaper(screen): bool { - const openHost = root.hostWithActivity("wallpaper"); - if (openHost) { - openHost.islandController.requestCollapse(); - return true; - } - return root.openWallpaper(screen); - } - - function closeWallpaper(): bool { - const host = root.hostWithActivity("wallpaper"); + function ipcShow(activity, screen) { + const host = root.hostForScreen(screen); if (!host) - return false; - host.islandController.requestCollapse(); - return true; + return "DANK_ISLAND_UNAVAILABLE"; + const requested = root.activityName(activity); + if (!host.islandController.requestActivity(requested, false, false)) + return `DANK_ISLAND_ACTIVITY_UNAVAILABLE: ${requested}`; + return `DANK_ISLAND_SHOW: ${requested}\t${host.screen?.name ?? ""}`; } - function weatherHostForScreen(screen) { - if (screen) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen === screen || hosts[i]?.screen?.name === screen.name) - return hosts[i]; - } - } - return root.focusedHost(); - } - - function openWeather(screen): bool { - const host = root.weatherHostForScreen(screen); - return host ? host.islandController.requestWeather(false) : false; - } - - function toggleWeather(screen): bool { - const openHost = root.hostWithActivity("weather"); - if (openHost) { - openHost.islandController.requestCollapse(); - return true; - } - return root.openWeather(screen); - } - - function closeWeather(): bool { - const host = root.hostWithActivity("weather"); + function ipcClose(screen) { + const host = root.hostForScreen(screen); if (!host) - return false; + return "DANK_ISLAND_UNAVAILABLE"; host.islandController.requestCollapse(); - return true; + return `DANK_ISLAND_CLOSED: ${host.screen?.name ?? ""}`; } - function notificationHostForScreen(screen) { - if (screen) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen === screen || hosts[i]?.screen?.name === screen.name) - return hosts[i]; - } - } - return root.focusedHost(); - } - - function openNotifications(screen): bool { - const host = root.notificationHostForScreen(screen); - return host ? host.islandController.requestNotificationCenter(false) : false; - } - - function toggleNotifications(screen): bool { - const openHost = root.hostWithActivity("notificationcenter"); - if (openHost) { - openHost.islandController.requestCollapse(); - return true; - } - return root.openNotifications(screen); - } - - function closeNotifications(): bool { - const host = root.hostWithActivity("notificationcenter"); + function ipcCycle(screen) { + const host = root.hostForScreen(screen); if (!host) - return false; - host.islandController.requestCollapse(); - return true; + return "DANK_ISLAND_UNAVAILABLE"; + host.islandController.cycleActivity(1, host.islandController.expanded); + return `DANK_ISLAND_ACTIVITY: ${host.islandController.activeActivity}\t${host.screen?.name ?? ""}`; } - function mediaHostForScreen(screen) { - if (screen) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen === screen || hosts[i]?.screen?.name === screen.name) - return hosts[i]; - } - } - return root.focusedHost(); - } - - function openMedia(screen): bool { - const host = root.mediaHostForScreen(screen); - return host ? host.islandController.requestActivity("media", true, true) : false; - } - - function toggleMedia(screen): bool { - const openHost = root.hostWithActivity("media"); - if (openHost) { - openHost.islandController.requestCollapse(); - return true; - } - return root.openMedia(screen); - } - - function closeMedia(): bool { - const host = root.hostWithActivity("media"); + function ipcStatus(screen) { + const host = root.hostForScreen(screen); if (!host) - return false; - host.islandController.requestCollapse(); - return true; - } - - function homeHostForScreen(screen) { - if (screen) { - const hosts = islandVariants.instances || []; - for (let i = 0; i < hosts.length; i++) { - if (hosts[i]?.screen === screen || hosts[i]?.screen?.name === screen.name) - return hosts[i]; - } - } - return root.focusedHost(); - } - - function openHome(screen): bool { - const host = root.homeHostForScreen(screen); - return host ? host.islandController.requestActivity("home", true, true) : false; - } - - function toggleHome(screen): bool { - const openHost = root.hostWithActivity("home"); - if (openHost) { - openHost.islandController.requestCollapse(); - return true; - } - return root.openHome(screen); - } - - function closeHome(): bool { - const host = root.hostWithActivity("home"); - if (!host) - return false; - host.islandController.requestCollapse(); - return true; + return JSON.stringify({ + "available": false, + "enabled": true, + "launcherAvailable": false + }); + return JSON.stringify({ + "available": true, + "enabled": true, + "screen": host.screen?.name ?? "", + "activity": host.islandController.activeActivity, + "expanded": host.islandController.expanded, + "mediaAvailable": host.islandController.mediaAvailable, + "launcherAvailable": true, + "controlCenterAvailable": true, + "wallpaperAvailable": true, + "weatherAvailable": true, + "notificationCenterAvailable": true, + "launcherInputFocused": host.islandController.launcherInputFocused, + "launcherResultCount": host.launcherResultCount, + "compactHeight": host.islandController.compactHeight + }); } Component.onCompleted: PopoutService.dankIslandRouter = root @@ -364,134 +249,60 @@ Item { IpcHandler { target: "island" - function _runOpen(activity, screen) { - const host = root.hostForScreen(screen); - if (!host) - return "DANK_ISLAND_UNAVAILABLE"; - const requested = root.activityName(activity); - const accepted = root.openActivityOn(host, requested); - if (!accepted) - return `DANK_ISLAND_ACTIVITY_UNAVAILABLE: ${requested}`; - return `DANK_ISLAND_OPEN: ${requested}\t${host.screen?.name ?? ""}`; - } - - function _runToggle(activity, screen) { - const host = root.hostForScreen(screen); - if (!host) - return "DANK_ISLAND_UNAVAILABLE"; - if (host.islandController.expanded && !host.islandController.notificationActive) { - host.islandController.requestCollapse(); - return `DANK_ISLAND_CLOSED: ${host.screen?.name ?? ""}`; - } - return _runOpen(activity, screen); - } - - function _runShow(activity, screen) { - const host = root.hostForScreen(screen); - if (!host) - return "DANK_ISLAND_UNAVAILABLE"; - const requested = root.activityName(activity); - if (!host.islandController.requestActivity(requested, false, false)) - return `DANK_ISLAND_ACTIVITY_UNAVAILABLE: ${requested}`; - return `DANK_ISLAND_SHOW: ${requested}\t${host.screen?.name ?? ""}`; - } - - function _runClose(screen) { - const host = root.hostForScreen(screen); - if (!host) - return "DANK_ISLAND_UNAVAILABLE"; - host.islandController.requestCollapse(); - return `DANK_ISLAND_CLOSED: ${host.screen?.name ?? ""}`; - } - - function _runCycle(screen) { - const host = root.hostForScreen(screen); - if (!host) - return "DANK_ISLAND_UNAVAILABLE"; - host.islandController.cycleActivity(1, host.islandController.expanded); - return `DANK_ISLAND_ACTIVITY: ${host.islandController.activeActivity}\t${host.screen?.name ?? ""}`; - } - - function _runStatus(screen) { - const host = root.hostForScreen(screen); - if (!host) - return JSON.stringify({ - "available": false, - "enabled": true, - "launcherAvailable": false - }); - return JSON.stringify({ - "available": true, - "enabled": true, - "screen": host.screen?.name ?? "", - "activity": host.islandController.activeActivity, - "expanded": host.islandController.expanded, - "mediaAvailable": host.islandController.mediaAvailable, - "launcherAvailable": true, - "controlCenterAvailable": true, - "wallpaperAvailable": true, - "weatherAvailable": true, - "notificationCenterAvailable": true, - "launcherInputFocused": host.islandController.launcherInputFocused, - "launcherResultCount": host.launcherResultCount, - "compactHeight": host.islandController.compactHeight - }); - } - function open(activity: string): string { - return _runOpen(activity, ""); + return root.ipcOpen(activity, ""); } function toggle(activity: string): string { - return _runToggle(activity, ""); + return root.ipcToggle(activity, ""); } function show(activity: string): string { - return _runShow(activity, ""); + return root.ipcShow(activity, ""); } function close(): string { - return _runClose(""); + return root.ipcClose(""); } function cycle(): string { - return _runCycle(""); + return root.ipcCycle(""); } function status(): string { - return _runStatus(""); + return root.ipcStatus(""); } function openOn(activity: string, screen: string): string { - return _runOpen(activity, screen); + return root.ipcOpen(activity, screen); } function toggleOn(activity: string, screen: string): string { - return _runToggle(activity, screen); + return root.ipcToggle(activity, screen); } function showOn(activity: string, screen: string): string { - return _runShow(activity, screen); + return root.ipcShow(activity, screen); } function closeOn(screen: string): string { - return _runClose(screen); + return root.ipcClose(screen); } function cycleOn(screen: string): string { - return _runCycle(screen); + return root.ipcCycle(screen); } function statusOn(screen: string): string { - return _runStatus(screen); + return root.ipcStatus(screen); } function notifications(): string { - return _runToggle("notificationcenter", ""); + return root.ipcToggle("notificationcenter", ""); } function notificationsOn(screen: string): string { - return _runToggle("notificationcenter", screen); + return root.ipcToggle("notificationcenter", screen); } } } diff --git a/quickshell/Modules/DankIsland/DankIslandHostWindow.qml b/quickshell/Modules/DankIsland/DankIslandHostWindow.qml index a2184c935..85f458c39 100644 --- a/quickshell/Modules/DankIsland/DankIslandHostWindow.qml +++ b/quickshell/Modules/DankIsland/DankIslandHostWindow.qml @@ -48,14 +48,13 @@ PanelWindow { readonly property var islandLayer: LayerShell.fromEnv("DMS_DANKISLAND_LAYER", usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top) readonly property bool bottomEdge: SettingsData.dankIslandEdge === "bottom" readonly property int reservedStripHeight: Math.max(reserveHeight, outerGap + compactHeight) - // Input bands hug the anchored edge, which is the far side of the host when anchored bottom. readonly property int stripY: root.bottomEdge ? root.height - root.reservedStripHeight : 0 - // Screen-space Y of this strip's local origin. Zero on top, where the two spaces coincide. readonly property int hostOriginY: root.bottomEdge ? Math.max(0, (root.screen?.height ?? 0) - root.height) : 0 readonly property int outerGap: Math.max(0, Math.min(48, SettingsData.dankIslandOuterGap)) - // The host is larger than the visible island to cover every clamped activity target. readonly property int maxActivityHeight: Math.max(560, Math.min(680, (screen?.height ?? 1080) - 200)) readonly property int hostHeight: outerGap + maxActivityHeight + 8 + readonly property int collapsedHostHeight: Math.max(reservedStripHeight, outerGap + Math.ceil(Math.max(controller.compactFaceHeight, satelliteHost.barThickness)) + 4) + readonly property bool hostTall: controller.expanded || surface.targetEdgeExtent > collapsedHostHeight || (surface.motionRunning && surface.motionStartEdgeExtent > collapsedHostHeight) readonly property int maxActivityWidth: 736 readonly property real maximumHorizontalOffset: Math.max(0, (width - maxActivityWidth) / 2 - 8) property var colorPickerModal: null @@ -65,7 +64,7 @@ PanelWindow { signal lockRequested color: "transparent" - implicitHeight: hostHeight + implicitHeight: hostTall ? hostHeight : collapsedHostHeight exclusiveZone: floating ? 0 : reservedStripHeight readonly property alias islandController: controller readonly property int launcherResultCount: launcherController.flatModel?.length ?? 0 @@ -156,14 +155,8 @@ PanelWindow { IslandController { id: controller - readonly property string interactionOverride: Quickshell.env("DMS_DANKISLAND_INTERACTION") ?? "" - - interactionMode: { - const raw = interactionOverride || SettingsData.dankIslandInteractionMode; - return raw === "click" ? "click" : "hybrid"; - } + interactionMode: SettingsData.dankIslandInteractionMode === "click" ? "click" : "hybrid" inputSuspended: PopoutManager.screenshotActive - autoDemo: Quickshell.env("DMS_DANKISLAND_DEMO") === "1" horizontalOffset: Math.max(-root.maximumHorizontalOffset, Math.min(root.maximumHorizontalOffset, SettingsData.dankIslandHorizontalOffset)) outerGap: root.outerGap compactHeight: root.compactHeight @@ -201,26 +194,25 @@ PanelWindow { } } - DmsMediaActivitySource { + IslandMediaSource { id: mediaSource controller: controller } - DmsSystemActivitySource { + IslandSystemSource { id: systemSource controller: controller } - DmsNotificationActivitySource { + IslandNotificationSource { id: notificationSource controller: controller targetScreen: root.screen } - // Empty strip dismisses satellite popouts while something is open; click-through otherwise. // PopoutManager mutates the map in place, so this tracks popoutChanged instead. property int popoutRevision: 0 readonly property bool satelliteSurfacesOpen: { diff --git a/quickshell/Modules/DankIsland/DankIslandLab.qml b/quickshell/Modules/DankIsland/DankIslandLab.qml deleted file mode 100644 index 05c5033fe..000000000 --- a/quickshell/Modules/DankIsland/DankIslandLab.qml +++ /dev/null @@ -1,14 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell - -Variants { - model: Quickshell.screens - - delegate: DankIslandLabWindow { - required property var modelData - - screen: modelData - } -} diff --git a/quickshell/Modules/DankIsland/DankIslandLabWindow.qml b/quickshell/Modules/DankIsland/DankIslandLabWindow.qml deleted file mode 100644 index cfae04e3d..000000000 --- a/quickshell/Modules/DankIsland/DankIslandLabWindow.qml +++ /dev/null @@ -1,37 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Wayland - -PanelWindow { - id: root - - property int reserveHeight: 58 - property int hostHeight: 300 - - color: "transparent" - implicitHeight: hostHeight - exclusiveZone: reserveHeight - - anchors { - top: true - left: true - right: true - } - - WlrLayershell.namespace: "dms:dankisland-lab" - WlrLayershell.layer: WlrLayer.Overlay - WlrLayershell.keyboardFocus: WlrKeyboardFocus.None - - mask: Region { - item: lab.inputMaskItem - } - - DankIslandMotionLab { - id: lab - - anchors.fill: parent - reserveHeight: root.reserveHeight - } -} diff --git a/quickshell/Modules/DankIsland/DankIslandMotionLab.qml b/quickshell/Modules/DankIsland/DankIslandMotionLab.qml deleted file mode 100644 index 11fb3f9d9..000000000 --- a/quickshell/Modules/DankIsland/DankIslandMotionLab.qml +++ /dev/null @@ -1,299 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import qs.Common -import qs.Services - -Item { - id: root - - required property int reserveHeight - - readonly property alias inputMaskItem: inputEnvelope - readonly property var activeMotion: engineMode === "native" ? nativeMotion : vectorMotion - readonly property list targetStates: ["home", "compact", "expanded", "offset-test"] - readonly property var log: Log.scoped("DankIslandMotionLab") - readonly property bool hudEnabled: Quickshell.env("DMS_DANKISLAND_LAB_HUD") !== "0" - readonly property bool metricsLogEnabled: Quickshell.env("DMS_DANKISLAND_LAB_LOG") === "1" - - property string engineMode: Quickshell.env("DMS_DANKISLAND_LAB_ENGINE") === "native" ? "native" : "vector" - property string targetState: "home" - property bool stressRunning: Quickshell.env("DMS_DANKISLAND_LAB_STRESS") === "1" - property real lastFrameMs: 0 - property real smoothFrameMs: 0 - property real peakFrameMs: 0 - property real currentPeakFrameMs: 0 - property int metricFrameCount: 0 - property string statusText: "" - - function targetForState(state) { - switch (state) { - case "compact": - return { - "width": 252, - "height": 48, - "offsetX": 0, - "offsetY": 8, - "topLeftRadius": 24, - "topRightRadius": 24, - "bottomLeftRadius": 24, - "bottomRightRadius": 24 - }; - case "expanded": - return { - "width": 448, - "height": 176, - "offsetX": 0, - "offsetY": 8, - "topLeftRadius": 26, - "topRightRadius": 26, - "bottomLeftRadius": 32, - "bottomRightRadius": 32 - }; - case "offset-test": - return { - "width": 520, - "height": 132, - "offsetX": -72, - "offsetY": 14, - "topLeftRadius": 22, - "topRightRadius": 30, - "bottomLeftRadius": 44, - "bottomRightRadius": 18 - }; - default: - return { - "width": 176, - "height": 42, - "offsetX": 0, - "offsetY": 8, - "topLeftRadius": 21, - "topRightRadius": 21, - "bottomLeftRadius": 21, - "bottomRightRadius": 21 - }; - } - } - - function motionSnapshot(motion) { - return { - "width": motion.currentWidth, - "height": motion.currentHeight, - "offsetX": motion.currentOffsetX, - "offsetY": motion.currentOffsetY, - "topLeftRadius": motion.currentTopLeftRadius, - "topRightRadius": motion.currentTopRightRadius, - "bottomLeftRadius": motion.currentBottomLeftRadius, - "bottomRightRadius": motion.currentBottomRightRadius - }; - } - - function applyState(state) { - targetState = state; - activeMotion.setTarget(targetForState(state)); - } - - function cycleState(direction) { - const currentIndex = targetStates.indexOf(targetState); - const nextIndex = (currentIndex + direction + targetStates.length) % targetStates.length; - applyState(targetStates[nextIndex]); - } - - function toggleEngine() { - const snapshot = motionSnapshot(activeMotion); - const target = targetForState(targetState); - if (engineMode === "vector") { - nativeMotion.snapTo(snapshot); - engineMode = "native"; - nativeMotion.setTarget(target); - } else { - vectorMotion.snapTo(snapshot); - engineMode = "vector"; - vectorMotion.setTarget(target); - } - peakFrameMs = 0; - currentPeakFrameMs = 0; - metricFrameCount = 0; - } - - function updateStatusText() { - if (!hudEnabled) - return; - - const engineLabel = engineMode === "vector" ? "VECTOR" : "NATIVE"; - const motionLabel = activeMotion.running ? "MOVING" : "SETTLED"; - const stressLabel = stressRunning ? " · STRESS" : ""; - statusText = engineLabel + " · " + targetState.toUpperCase() + " · " + motionLabel + stressLabel + "\n" + Math.round(activeMotion.currentWidth) + "×" - + Math.round(activeMotion.currentHeight) + " · " + smoothFrameMs.toFixed(2) + " ms smooth · " + peakFrameMs.toFixed(2) + " ms peak"; - } - - Component.onCompleted: { - const home = targetForState("home"); - nativeMotion.snapTo(home); - vectorMotion.snapTo(home); - updateStatusText(); - } - - NativeSpringMotion { - id: nativeMotion - enabled: root.engineMode === "native" - } - - VectorSpringMotion { - id: vectorMotion - enabled: root.engineMode === "vector" - reducedMotion: SettingsData.dankIslandReducedMotion || SettingsData.animationSpeed === SettingsData.AnimationSpeed.None - stiffness: Math.max(100, Math.min(1200, SettingsData.dankIslandSpringStiffness)) - damping: Math.max(10, Math.min(100, SettingsData.dankIslandSpringDamping)) - mass: Math.max(0.25, Math.min(3, SettingsData.dankIslandSpringMass)) - } - - Timer { - interval: 110 - repeat: true - running: root.stressRunning - onTriggered: root.cycleState(1) - } - - Timer { - interval: 200 - repeat: true - running: root.hudEnabled - triggeredOnStart: true - onTriggered: root.updateStatusText() - } - - FrameAnimation { - running: root.activeMotion.running || root.stressRunning - - onRunningChanged: { - if (running) { - root.peakFrameMs = 0; - root.currentPeakFrameMs = 0; - root.metricFrameCount = 0; - } - } - - onTriggered: { - root.lastFrameMs = frameTime * 1000; - root.smoothFrameMs = smoothFrameTime * 1000; - root.metricFrameCount++; - if (root.metricFrameCount > 8) - root.currentPeakFrameMs = Math.max(root.currentPeakFrameMs, root.lastFrameMs); - } - } - - Timer { - interval: 1000 - repeat: true - running: root.hudEnabled || root.metricsLogEnabled - onTriggered: { - root.peakFrameMs = root.currentPeakFrameMs; - root.currentPeakFrameMs = 0; - if (root.metricsLogEnabled) - root.log.info("DankIslandLabMetrics engine=" + root.engineMode + " state=" + root.targetState + " moving=" + root.activeMotion.running + " frameMs=" + root.lastFrameMs.toFixed(3) + " smoothMs=" + root.smoothFrameMs.toFixed(3) + " peakMs=" + root.peakFrameMs.toFixed(3)); - } - } - - readonly property real currentVisualX: (width - activeMotion.currentWidth) / 2 + activeMotion.currentOffsetX - readonly property real currentVisualY: activeMotion.currentOffsetY - readonly property real targetVisualX: (width - activeMotion.targetWidth) / 2 + activeMotion.targetOffsetX - readonly property real targetVisualY: activeMotion.targetOffsetY - - Item { - id: inputEnvelope - - x: Math.min(root.currentVisualX, root.targetVisualX) - y: Math.min(root.currentVisualY, root.targetVisualY) - width: Math.max(root.currentVisualX + root.activeMotion.currentWidth, root.targetVisualX + root.activeMotion.targetWidth) - x - height: Math.max(root.currentVisualY + root.activeMotion.currentHeight, root.targetVisualY + root.activeMotion.targetHeight) - y - } - - Rectangle { - x: 0 - y: root.reserveHeight - 1 - width: root.width - height: 1 - color: Theme.outline - opacity: 0.22 - visible: root.hudEnabled - } - - Rectangle { - id: island - - x: root.currentVisualX - y: root.currentVisualY - width: root.activeMotion.currentWidth - height: root.activeMotion.currentHeight - topLeftRadius: Math.max(0, root.activeMotion.currentTopLeftRadius) - topRightRadius: Math.max(0, root.activeMotion.currentTopRightRadius) - bottomLeftRadius: Math.max(0, root.activeMotion.currentBottomLeftRadius) - bottomRightRadius: Math.max(0, root.activeMotion.currentBottomRightRadius) - color: Theme.surfaceContainerHigh - border.width: 1 - border.color: Theme.withAlpha(Theme.outline, 0.28) - - Rectangle { - anchors { - left: parent.left - right: parent.right - top: parent.top - } - height: 2 - color: root.engineMode === "vector" ? Theme.primary : Theme.tertiary - opacity: 0.85 - } - - Column { - anchors.centerIn: parent - spacing: 2 - visible: root.hudEnabled - - Text { - anchors.horizontalCenter: parent.horizontalCenter - text: root.statusText.split("\n")[0] ?? "" - color: Theme.surfaceText - font.pixelSize: Theme.fontSizeSmall - font.weight: Font.DemiBold - } - - Text { - anchors.horizontalCenter: parent.horizontalCenter - text: root.statusText.split("\n")[1] ?? "" - color: Theme.withAlpha(Theme.surfaceText, 0.72) - font.pixelSize: Math.max(9, Theme.fontSizeSmall - 2) - visible: island.width >= 230 - } - - Text { - anchors.horizontalCenter: parent.horizontalCenter - text: "left: next · right: engine · middle: stress · wheel: retarget" - color: Theme.withAlpha(Theme.surfaceText, 0.62) - font.pixelSize: Math.max(9, Theme.fontSizeSmall - 2) - visible: island.width >= 400 && island.height >= 100 - } - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - - onClicked: mouse => { - if (mouse.button === Qt.RightButton) { - root.toggleEngine(); - } else if (mouse.button === Qt.MiddleButton) { - root.stressRunning = !root.stressRunning; - } else { - root.cycleState(1); - } - } - - onWheel: wheel => { - root.cycleState(wheel.angleDelta.y >= 0 ? 1 : -1); - wheel.accepted = true; - } - } - } -} diff --git a/quickshell/Modules/DankIsland/DankIslandSurface.qml b/quickshell/Modules/DankIsland/DankIslandSurface.qml index def575fb9..a1b298cff 100644 --- a/quickshell/Modules/DankIsland/DankIslandSurface.qml +++ b/quickshell/Modules/DankIsland/DankIslandSurface.qml @@ -23,7 +23,6 @@ Item { property real springStiffness: 560 property real springDamping: 37 property real springMass: 1 - // Screen-space Y of the host strip's local origin; non-zero only on the bottom edge. property real hostOriginY: 0 property string palette: "default" property bool highContrast: false @@ -43,6 +42,10 @@ Item { readonly property alias inputMaskItem: inputEnvelope readonly property bool motionRunning: motion.running + readonly property real targetEdgeExtent: motion.targetOffsetY + motion.targetHeight + readonly property real currentEdgeExtent: motion.currentOffsetY + motion.currentHeight + property real motionStartEdgeExtent: 0 + property real trackedHeight: 0 readonly property Item mediaDropdownMaskItem: mediaDropdowns.__activePanel property real fadeCompactHeight: 48 property real fadeExpandedHeight: 352 @@ -56,7 +59,6 @@ Item { readonly property real currentVisualHeight: motion.currentHeight readonly property real targetVisualX: (width - motion.targetWidth) / 2 + motion.targetOffsetX readonly property real targetVisualY: bottomEdge ? height - motion.targetOffsetY - motion.targetHeight : motion.targetOffsetY - // Activity hosts hand alignedY to full-screen windows, which need screen space, not surface space. readonly property real targetScreenY: targetVisualY + root.hostOriginY readonly property real targetVisualWidth: motion.targetWidth readonly property real targetVisualHeight: motion.targetHeight @@ -72,15 +74,15 @@ Item { fadeExpandedHeight = controller.expandedTarget.height; else fadeCompactHeight = controller.compactTarget.height; - // A retarget leaves the frozen start stale, so widen it to cover where the island is now. if (motion.running) root.unionMotionStartBounds(); motion.setTarget(controller.targetDescriptor); if (!motion.running) - root.releaseIdleVisuals(); + root.controller.releaseIdleVisuals(); } function unionMotionStartBounds() { + root.motionStartEdgeExtent = Math.max(root.motionStartEdgeExtent, root.currentEdgeExtent); const b = root.motionStartBounds; const left = Math.min(b.x, root.currentVisualX); const top = Math.min(b.y, root.currentVisualY); @@ -89,14 +91,6 @@ Item { root.motionStartBounds = Qt.rect(left, top, right - left, bottom - top); } - function releaseIdleVisuals() { - root.controller.releaseWallpaperVisuals(); - root.controller.releaseWeatherVisuals(); - root.controller.releaseNotificationCenterVisuals(); - root.controller.releaseLauncherVisuals(); - root.controller.releaseControlCenterVisuals(); - } - function openMediaDropdown(type, pos) { stopMediaDropdownCloseTimer(); mediaDropdownAnchor = pos; @@ -123,11 +117,21 @@ Item { } Component.onCompleted: { + trackedHeight = height; fadeCompactHeight = controller.compactTarget.height; fadeExpandedHeight = controller.expandedTarget.height; motion.snapTo(controller.targetDescriptor); } + onHeightChanged: { + const delta = height - trackedHeight; + trackedHeight = height; + if (!bottomEdge || !motion.running || delta === 0) + return; + const b = motionStartBounds; + motionStartBounds = Qt.rect(b.x, b.y + delta, b.width, b.height); + } + Connections { target: root.controller @@ -152,11 +156,12 @@ Item { function onRunningChanged() { if (motion.running) { root.motionStartBounds = Qt.rect(root.currentVisualX, root.currentVisualY, motion.currentWidth, motion.currentHeight); + root.motionStartEdgeExtent = root.currentEdgeExtent; if (root.controller.activeActivity === "media") root.hideMediaDropdowns(); return; } - root.releaseIdleVisuals(); + root.controller.releaseIdleVisuals(); } } @@ -180,7 +185,6 @@ Item { Item { id: inputEnvelope - // Cover underdamped overshoot for the frozen flight, scaled to travel. readonly property real overshootBudget: { if (!motion.running) return 0; @@ -216,7 +220,6 @@ Item { border.width: root.highContrast ? 2 : 0 border.color: root.highContrast ? Theme.outlineStrong : "transparent" - // Full destinations keep blank clicks for their own content instead of collapsing. MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton @@ -236,6 +239,7 @@ Item { id: contentHost anchors.fill: parent + controller: root.controller morphProgress: root.morphProgress expanded: root.controller.expanded pointerInside: root.controller.pointerInside @@ -246,32 +250,22 @@ Item { mediaExpandedComponent: expandedMediaComponent launcherCompactComponent: compactLauncherComponent launcherExpandedComponent: expandedLauncherComponent - launcherVisualsRequested: root.controller.launcherVisualsRequested controlCenterCompactComponent: compactControlCenterComponent controlCenterExpandedComponent: expandedControlCenterComponent - controlCenterVisualsRequested: root.controller.controlCenterVisualsRequested wallpaperCompactComponent: compactWallpaperComponent wallpaperExpandedComponent: expandedWallpaperComponent - wallpaperVisualsRequested: root.controller.wallpaperVisualsRequested weatherCompactComponent: compactWeatherComponent weatherExpandedComponent: expandedWeatherComponent - weatherVisualsRequested: root.controller.weatherVisualsRequested systemCompactComponent: compactSystemComponent systemExpandedComponent: expandedSystemComponent notificationCompactComponent: compactNotificationComponent notificationExpandedComponent: expandedNotificationComponent notificationCenterCompactComponent: compactNotificationCenterComponent notificationCenterExpandedComponent: expandedNotificationCenterComponent - notificationCenterVisualsRequested: root.controller.notificationCenterVisualsRequested } HoverHandler { - onHoveredChanged: { - if (hovered) - root.controller.updatePointerInside(true); - else - root.controller.updatePointerInside(false); - } + onHoveredChanged: root.controller.updatePointerInside(hovered) } } @@ -335,7 +329,6 @@ Item { geometrySettled: !motion.running effectiveScreen: root.effectiveScreen alignedX: root.targetVisualX - // Surface-local on purpose: the media dropdowns it anchors live inside this surface. alignedY: root.targetVisualY alignedWidth: root.targetVisualWidth alignedHeight: root.targetVisualHeight @@ -363,7 +356,6 @@ Item { launcherController: root.launcherController transientSurfaceTracker: root.launcherTransientSurfaceTracker effectiveScreen: root.effectiveScreen - // Settled bounds; animated values rebind the tree every frame. alignedX: root.targetVisualX alignedY: root.targetScreenY } @@ -385,7 +377,6 @@ Item { effectiveScreen: root.effectiveScreen colorPickerModal: root.colorPickerModal powerMenuModalLoader: root.powerMenuModalLoader - // Settled bounds; animated values rebind the tree every frame. alignedX: root.targetVisualX alignedY: root.targetScreenY alignedWidth: root.targetVisualWidth diff --git a/quickshell/Modules/DankIsland/DmsMediaActivitySource.qml b/quickshell/Modules/DankIsland/DmsMediaActivitySource.qml deleted file mode 100644 index 8fcd7ed94..000000000 --- a/quickshell/Modules/DankIsland/DmsMediaActivitySource.qml +++ /dev/null @@ -1,115 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import Quickshell -import Quickshell.Services.Mpris -import qs.Services - -QtObject { - id: root - - required property IslandController controller - - readonly property bool demoEnabled: Quickshell.env("DMS_DANKISLAND_MEDIA_DEMO") === "1" - readonly property var demoTracks: [ - { - "title": "DankIsland Motion Study", - "artist": "DankMaterialShell", - "album": "Physical Springs", - "icon": "applications-multimedia" - }, - { - "title": "Event Horizon", - "artist": "Quickshell Ensemble", - "album": "Signal Driven", - "icon": "audio-x-generic" - }, - { - "title": "Warm Surface", - "artist": "Scene Graph", - "album": "Sixty Frames", - "icon": "multimedia-player" - } - ] - readonly property var demoTrack: demoTracks[demoTrackIndex] - readonly property var player: MprisController.activePlayer - readonly property bool realMediaAvailable: !!player && player.playbackState !== MprisPlaybackState.Stopped - readonly property bool available: demoEnabled || realMediaAvailable - readonly property string title: demoEnabled ? demoTrack.title : (MprisController.stableTitle || player?.trackTitle || "Unknown track") - readonly property string artist: demoEnabled ? demoTrack.artist : (MprisController.stableArtist || player?.trackArtist || player?.identity || "Unknown artist") - readonly property string album: demoEnabled ? demoTrack.album : (MprisController.stableAlbum || player?.trackAlbum || "") - readonly property string artUrl: demoEnabled ? Quickshell.iconPath(demoTrack.icon, true) : TrackArtService.resolvedArtUrl - readonly property bool playing: demoEnabled ? demoPlaying : !!player?.isPlaying - readonly property bool canToggle: demoEnabled || !!player?.canTogglePlaying - readonly property bool canGoPrevious: demoEnabled || !!player?.canGoPrevious - readonly property bool canGoNext: demoEnabled || !!player?.canGoNext - readonly property bool canSeek: demoEnabled || (!!player?.canSeek && length > 0) - readonly property real position: demoEnabled ? demoPosition : Math.max(0, player?.position || 0) - readonly property real length: demoEnabled ? 243 : Math.max(0, MprisController.activePlayerStableLength) - readonly property real progress: length > 0 ? Math.max(0, Math.min(1, position / length)) : 0 - - property int demoTrackIndex: 0 - property bool demoPlaying: true - property real demoPosition: 74 - - function advanceDemoTrack(delta) { - demoTrackIndex = (demoTrackIndex + delta + demoTracks.length) % demoTracks.length; - demoPosition = 0; - } - - function syncAvailability() { - controller.updateMediaAvailability(available); - } - - function togglePlaying() { - if (demoEnabled) { - demoPlaying = !demoPlaying; - return; - } - if (player?.canTogglePlaying) - player.togglePlaying(); - } - - function previous() { - if (demoEnabled) { - advanceDemoTrack(-1); - return; - } - MprisController.previousOrRewind(); - } - - function next() { - if (demoEnabled) { - advanceDemoTrack(1); - return; - } - MprisController.next(); - } - - function seekRatio(ratio) { - if (!canSeek || length <= 0) - return; - const nextPosition = Math.max(0.1, Math.min(length - 0.1, length * Math.max(0, Math.min(1, ratio)))); - if (demoEnabled) - demoPosition = nextPosition; - else - player.position = nextPosition; - } - - Component.onCompleted: syncAvailability() - onAvailableChanged: syncAvailability() - - property Timer demoTimer: Timer { - interval: 1000 - repeat: true - running: root.demoEnabled && root.demoPlaying - onTriggered: root.demoPosition = (root.demoPosition + 1) % root.length - } - - property Timer demoTrackTimer: Timer { - interval: 4200 - repeat: true - running: root.demoEnabled - onTriggered: root.advanceDemoTrack(1) - } -} diff --git a/quickshell/Modules/DankIsland/IslandContentHost.qml b/quickshell/Modules/DankIsland/IslandContentHost.qml index 80c04c331..e8e881ad4 100644 --- a/quickshell/Modules/DankIsland/IslandContentHost.qml +++ b/quickshell/Modules/DankIsland/IslandContentHost.qml @@ -1,10 +1,12 @@ pragma ComponentBehavior: Bound import QtQuick +import qs.Common Item { id: root + required property var controller required property real morphProgress required property bool expanded required property bool pointerInside @@ -15,27 +17,21 @@ Item { required property Component mediaExpandedComponent required property Component launcherCompactComponent required property Component launcherExpandedComponent - required property bool launcherVisualsRequested required property Component controlCenterCompactComponent required property Component controlCenterExpandedComponent - required property bool controlCenterVisualsRequested required property Component wallpaperCompactComponent required property Component wallpaperExpandedComponent - required property bool wallpaperVisualsRequested required property Component weatherCompactComponent required property Component weatherExpandedComponent - required property bool weatherVisualsRequested required property Component systemCompactComponent required property Component systemExpandedComponent required property Component notificationCompactComponent required property Component notificationExpandedComponent required property Component notificationCenterCompactComponent required property Component notificationCenterExpandedComponent - required property bool notificationCenterVisualsRequested readonly property real compactFade: root.fadeCompact(root.morphProgress) readonly property real expandedFade: root.fadeExpanded(root.morphProgress) - // Outgoing faces freeze at the morph they were drawn at so they do not flash expanded. readonly property real outgoingCompactFade: root.fadeCompact(root.outgoingMorph) readonly property real outgoingExpandedFade: root.fadeExpanded(root.outgoingMorph) readonly property bool mediaSurfaceActive: root.activityId === "media" || root.outgoingActivity === "media" @@ -103,7 +99,6 @@ Item { homeExpandedTouched = true; } - // Overview is latched on first pointer intent, not at startup. onPointerInsideChanged: { if (root.pointerInside) root.homeExpandedTouched = true; @@ -126,14 +121,14 @@ Item { id: activityTransition PauseAnimation { - duration: 45 + duration: Theme.shorterDuration } NumberAnimation { target: root property: "activityFade" to: 1 - duration: 180 + duration: Theme.mediumDuration easing.type: Easing.OutCubic } @@ -156,12 +151,11 @@ Item { id: homeExpandedLoader anchors.fill: parent - // Latched: built on first expansion, then kept so later morphs never hitch. active: root.homeExpandedTouched asynchronous: true sourceComponent: root.homeExpandedComponent opacity: root.expandedOpacity("home") - visible: active || opacity > 0.001 + visible: opacity > 0.001 enabled: opacity >= 0.5 } @@ -201,7 +195,7 @@ Item { id: launcherExpandedLoader anchors.fill: parent - active: root.launcherVisualsRequested + active: root.controller.visualsRequested("launcher") asynchronous: true sourceComponent: root.launcherExpandedComponent opacity: root.expandedOpacity("launcher") @@ -223,7 +217,7 @@ Item { id: controlCenterExpandedLoader anchors.fill: parent - active: root.controlCenterVisualsRequested + active: root.controller.visualsRequested("controlcenter") asynchronous: false sourceComponent: root.controlCenterExpandedComponent opacity: root.expandedOpacity("controlcenter") @@ -245,11 +239,11 @@ Item { id: wallpaperExpandedLoader anchors.fill: parent - active: root.wallpaperVisualsRequested + active: root.controller.visualsRequested("wallpaper") asynchronous: true sourceComponent: root.wallpaperExpandedComponent opacity: root.expandedOpacity("wallpaper") - visible: active || opacity > 0.001 + visible: opacity > 0.001 enabled: opacity >= 0.5 } @@ -267,11 +261,11 @@ Item { id: weatherExpandedLoader anchors.fill: parent - active: root.weatherVisualsRequested + active: root.controller.visualsRequested("weather") asynchronous: true sourceComponent: root.weatherExpandedComponent opacity: root.expandedOpacity("weather") - visible: active || opacity > 0.001 + visible: opacity > 0.001 enabled: opacity >= 0.5 } @@ -289,7 +283,7 @@ Item { id: notificationCenterExpandedLoader anchors.fill: parent - active: root.notificationCenterVisualsRequested + active: root.controller.visualsRequested("notificationcenter") asynchronous: true sourceComponent: root.notificationCenterExpandedComponent opacity: root.expandedOpacity("notificationcenter") diff --git a/quickshell/Modules/DankIsland/IslandController.qml b/quickshell/Modules/DankIsland/IslandController.qml index 7fef4acca..eea925a90 100644 --- a/quickshell/Modules/DankIsland/IslandController.qml +++ b/quickshell/Modules/DankIsland/IslandController.qml @@ -6,25 +6,19 @@ import qs.Common QtObject { id: root - // Hybrid peeks the current compact face on hover. Click pins destinations. property string interactionMode: "click" property bool hoverExpanded: false property bool inputSuspended: false property bool expanded: false property bool pointerInside: false - // Slots with their own click target suppress hover expansion so the click wins. property int slotHoverCount: 0 readonly property bool slotHovered: slotHoverCount > 0 property bool mediaDropdownOpen: false - property bool autoDemo: false property bool keyboardDismissRequested: false property string activeActivity: "home" property bool mediaAvailable: false property bool mediaPreferred: false property bool launcherCycleEnabled: false - property bool launcherVisualsRequested: false - property bool launcherVisualsReady: false - property bool launcherRequestPending: false property bool launcherSessionActive: false property bool launcherInputFocused: false onLauncherInputFocusedChanged: { @@ -33,37 +27,18 @@ QtObject { } property string launcherPendingQuery: "" property string launcherPendingMode: "" - property int launcherSessionSerial: 0 property bool _launcherPendingExpand: true property bool _launcherPendingKeyboardFocus: true - property bool controlCenterVisualsRequested: false - property bool controlCenterVisualsReady: false - property bool controlCenterRequestPending: false property string controlCenterPendingSection: "" - property int controlCenterSessionSerial: 0 - property bool wallpaperVisualsRequested: false - property bool wallpaperVisualsReady: false - property bool wallpaperRequestPending: false - property int wallpaperSessionSerial: 0 - property bool weatherVisualsRequested: false - property bool weatherVisualsReady: false - property bool weatherRequestPending: false - property bool notificationCenterVisualsRequested: false - property bool notificationCenterVisualsReady: false - property bool notificationCenterRequestPending: false - property int notificationCenterSessionSerial: 0 - // Arriving notifications stay pill-sized unless the user opts into full expansion. property bool notificationExpandAllowed: false property bool keyboardYielded: false property real horizontalOffset: 0 property real outerGap: 8 - // Expanded silhouette signature: tight corners on the screen edge, open corners facing the desktop. readonly property bool bottomEdge: SettingsData.dankIslandEdge === "bottom" readonly property real anchoredRadius: bottomEdge ? 34 : 26 readonly property real freeRadius: bottomEdge ? 26 : 34 property real compactHeight: 38 property int transientTimeout: 2200 - // Adopted popup timeout; 0 is sticky until dismiss. property int notificationTimeout: 5000 property string transientReturnActivity: "" property string suspendedActivity: "" @@ -72,14 +47,11 @@ QtObject { property int hoverOpenDelay: 150 property int hoverCloseDelay: 150 property int mediaReturnDelay: 1800 - property real controlCenterContentHeight: 420 property real controlCenterMaxHeight: 640 - readonly property real controlCenterHeight: Math.max(320, Math.min(controlCenterMaxHeight, controlCenterContentHeight)) - property real notificationCenterContentHeight: 320 property real notificationCenterMaxHeight: 640 - readonly property real notificationCenterHeight: Math.max(320, Math.min(notificationCenterMaxHeight, notificationCenterContentHeight)) + readonly property real controlCenterHeight: Math.max(320, Math.min(controlCenterMaxHeight, destinationContentHeight("controlcenter"))) + readonly property real notificationCenterHeight: Math.max(320, Math.min(notificationCenterMaxHeight, destinationContentHeight("notificationcenter"))) - // Below the pill threshold every compact face drops to a single text line. readonly property bool compactDense: compactHeight < 40 readonly property real compactFaceHeight: compactHeight + (compactDense ? 2 : 4) readonly property real compactIconSize: Math.max(14, Math.min(32, compactHeight - 8)) @@ -88,12 +60,15 @@ QtObject { property int unreadNotificationCount: 0 readonly property bool homeNotificationBadge: unreadNotificationCount > 0 - readonly property real homeSlotMargin: homeCompactTight ? 4 : 8 - readonly property real homeSlotSpacing: homeCompactTight ? 2 : 4 + readonly property real homeSlotMargin: homeCompactTight ? Theme.spacingXS : Theme.spacingS + readonly property real homeSlotSpacing: homeCompactTight ? Theme.spacingXXS : Theme.spacingXS readonly property real homeClusterGap: homeCompactTight ? 6 : 10 property real homeContentWidth: 200 + property real mediaContentWidth: 360 + readonly property real mediaCompactMaxWidth: 360 + + signal sessionStarted(string activityId) - // Ignore sub-2px clock jitter so the spring does not restart every tick. function setHomeContentWidth(width) { const next = Math.ceil(width); if (!isFinite(next) || next <= 0 || Math.abs(next - homeContentWidth) < 2) @@ -101,9 +76,6 @@ QtObject { homeContentWidth = next; } - property real mediaContentWidth: 360 - readonly property real mediaCompactMaxWidth: 360 - function setMediaContentWidth(width) { const next = Math.ceil(Math.min(root.mediaCompactMaxWidth, width)); if (!isFinite(next) || next <= 0 || Math.abs(next - mediaContentWidth) < 2) @@ -111,52 +83,177 @@ QtObject { mediaContentWidth = next; } - property real launcherContentWidth: 160 - property real controlCenterContentWidth: 180 - property real wallpaperContentWidth: 150 - property real weatherContentWidth: 130 - property real notificationCenterContentWidth: 170 - readonly property real destinationCompactEndPad: Math.max(root.compactFaceHeight / 2, Theme.spacingM) + Theme.spacingS + readonly property var destinations: ["launcher", "controlcenter", "wallpaper", "weather", "notificationcenter"] + readonly property var destinationDefaults: ({ + "launcher": { + "contentWidth": 160, + "contentHeight": 0 + }, + "controlcenter": { + "contentWidth": 180, + "contentHeight": 420 + }, + "wallpaper": { + "contentWidth": 150, + "contentHeight": 0 + }, + "weather": { + "contentWidth": 130, + "contentHeight": 0 + }, + "notificationcenter": { + "contentWidth": 170, + "contentHeight": 320 + } + }) + property var destinationState: root.freshDestinationState() + property int destinationRevision: 0 - function destinationCompactWidthFromContent(contentWidth) { - return Math.ceil(Math.max(root.compactFaceHeight, contentWidth + root.destinationCompactEndPad * 2)); + function freshDestinationState() { + const state = {}; + for (const id of destinations) { + state[id] = { + "requested": false, + "ready": false, + "pending": false, + "contentWidth": destinationDefaults[id].contentWidth, + "contentHeight": destinationDefaults[id].contentHeight + }; + } + return state; + } + + function destinationEntry(activityId) { + destinationRevision; + return destinationState[activityId] ?? null; + } + + function isDestination(activityId) { + return destinations.indexOf(activityId) !== -1; + } + + function visualsRequested(activityId) { + return destinationEntry(activityId)?.requested ?? false; + } + + function visualsReady(activityId) { + return destinationEntry(activityId)?.ready ?? false; + } + + function destinationContentWidth(activityId) { + return destinationEntry(activityId)?.contentWidth ?? 0; + } + + function destinationContentHeight(activityId) { + return destinationEntry(activityId)?.contentHeight ?? 0; } function setDestinationContentWidth(activityId, width) { + const entry = destinationState[activityId]; const next = Math.ceil(width); - if (!isFinite(next) || next <= 0) + if (!entry || !isFinite(next) || next <= 0 || Math.abs(next - entry.contentWidth) < 2) return; + entry.contentWidth = next; + destinationRevision++; + } + + function setDestinationContentHeight(activityId, height) { + const entry = destinationState[activityId]; + const next = Math.round(height); + if (!entry || !isFinite(next) || Math.abs(next - entry.contentHeight) < 1) + return; + entry.contentHeight = next; + destinationRevision++; + } + + function setVisualsReady(activityId, ready) { + const entry = destinationState[activityId]; + if (!entry || entry.ready === ready) + return; + entry.ready = ready; + destinationRevision++; + } + + function markVisualsReady(activityId) { + const entry = destinationState[activityId]; + if (!entry) + return; + const pending = entry.pending; + entry.ready = true; + entry.pending = false; + destinationRevision++; + if (!pending) + return; + if (activityId === "launcher") { + requestActivity("launcher", _launcherPendingExpand, _launcherPendingKeyboardFocus); + return; + } + requestActivity(activityId, true, true); + } + + function clearPendingRequests(exceptId) { + let changed = false; + for (const id of destinations) { + const entry = destinationState[id]; + if (id === exceptId || !entry.pending) + continue; + entry.pending = false; + changed = true; + } + if (changed) + destinationRevision++; + } + + function releaseIdleVisuals() { + for (const id of destinations) { + if (expanded && activeActivity === id) + continue; + const entry = destinationState[id]; + if (!entry.requested && !entry.ready) + continue; + visualsReleaseTimer.restart(); + return; + } + visualsReleaseTimer.stop(); + } + + function dropIdleVisuals() { + for (const id of destinations) { + if (expanded && activeActivity === id) + continue; + if (destinationState[id].pending) + continue; + dropVisuals(id); + } + } + + function dropVisuals(activityId) { + const entry = destinationState[activityId]; + entry.requested = false; + entry.ready = false; + entry.pending = false; + entry.contentHeight = destinationDefaults[activityId].contentHeight; + destinationRevision++; switch (activityId) { case "launcher": - if (Math.abs(next - launcherContentWidth) < 2) - return; - launcherContentWidth = next; + launcherSessionActive = false; + launcherInputFocused = false; + launcherPendingQuery = ""; + launcherPendingMode = ""; return; case "controlcenter": - if (Math.abs(next - controlCenterContentWidth) < 2) - return; - controlCenterContentWidth = next; + controlCenterPendingSection = ""; return; case "wallpaper": - if (Math.abs(next - wallpaperContentWidth) < 2) - return; - wallpaperContentWidth = next; - return; - case "weather": - if (Math.abs(next - weatherContentWidth) < 2) - return; - weatherContentWidth = next; - return; - case "notificationcenter": - if (Math.abs(next - notificationCenterContentWidth) < 2) - return; - notificationCenterContentWidth = next; + keyboardYielded = false; return; } } - function setLauncherContentWidth(width) { - root.setDestinationContentWidth("launcher", width); + readonly property real destinationCompactEndPad: Math.max(root.compactFaceHeight / 2, Theme.spacingM) + Theme.spacingS + + function destinationCompactWidth(activityId) { + return Math.ceil(Math.max(root.compactFaceHeight, destinationContentWidth(activityId) + root.destinationCompactEndPad * 2)); } function resolvedHomeSlot(value, fallback) { @@ -170,245 +267,89 @@ QtObject { readonly property string homeWeatherSlot: SettingsData.weatherEnabled ? resolvedHomeSlot(SettingsData.dankIslandHomeWeatherSlot, "hidden") : "hidden" readonly property real homeCompactWidth: Math.ceil(homeSlotMargin * 2 + Math.max(homeContentWidth, 1)) - - readonly property var homeCompactTarget: ({ - "width": root.homeCompactWidth, - "height": root.homeCompactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.homeCompactFaceHeight / 2, - "topRightRadius": root.homeCompactFaceHeight / 2, - "bottomLeftRadius": root.homeCompactFaceHeight / 2, - "bottomRightRadius": root.homeCompactFaceHeight / 2 - }) - readonly property var homeExpandedTarget: ({ - "width": SettingsData.showWeekNumber ? 736 : 700, - "height": 452, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) readonly property real mediaCompactWidth: Math.ceil(Math.max(1, mediaContentWidth)) - readonly property var mediaCompactTarget: ({ - "width": root.mediaCompactWidth, - "height": root.compactFaceHeight, + function pillTarget(width, height) { + const radius = height / 2; + return { + "width": width, + "height": height, "offsetX": root.horizontalOffset, "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var mediaExpandedTarget: ({ - "width": 600, - "height": 352, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) - readonly property real launcherCompactWidth: root.destinationCompactWidthFromContent(launcherContentWidth) - readonly property real controlCenterCompactWidth: root.destinationCompactWidthFromContent(controlCenterContentWidth) - readonly property real wallpaperCompactWidth: root.destinationCompactWidthFromContent(wallpaperContentWidth) - readonly property real weatherCompactWidth: root.destinationCompactWidthFromContent(weatherContentWidth) - readonly property real notificationCenterCompactWidth: root.destinationCompactWidthFromContent(notificationCenterContentWidth) + "topLeftRadius": radius, + "topRightRadius": radius, + "bottomLeftRadius": radius, + "bottomRightRadius": radius + }; + } - readonly property var launcherCompactTarget: ({ - "width": root.launcherCompactWidth, - "height": root.compactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var launcherExpandedTarget: ({ - "width": 680, - "height": 560, + function sheetTarget(width, height) { + return { + "width": width, + "height": height, "offsetX": root.horizontalOffset, "offsetY": root.outerGap, "topLeftRadius": root.anchoredRadius, "topRightRadius": root.anchoredRadius, "bottomLeftRadius": root.freeRadius, "bottomRightRadius": root.freeRadius - }) - readonly property var controlCenterCompactTarget: ({ - "width": root.controlCenterCompactWidth, - "height": root.compactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var controlCenterExpandedTarget: ({ - "width": 580, - "height": root.controlCenterHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) - readonly property var wallpaperCompactTarget: ({ - "width": root.wallpaperCompactWidth, - "height": root.compactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var wallpaperExpandedTarget: ({ - "width": 700, - "height": 452, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) - readonly property var weatherCompactTarget: ({ - "width": root.weatherCompactWidth, - "height": root.compactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var weatherExpandedTarget: ({ - "width": SettingsData.showWeekNumber ? 736 : 700, - "height": 452, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) - readonly property var systemCompactTarget: ({ - "width": SettingsData.osdAlwaysShowValue ? 330 : 282, - "height": root.compactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var systemExpandedTarget: ({ - "width": 460, - "height": 176, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) - readonly property var notificationCompactTarget: ({ - "width": root.compactDense ? 320 : 360, - "height": root.compactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var notificationExpandedTarget: ({ - "width": 520, - "height": 220, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) - readonly property var notificationCenterCompactTarget: ({ - "width": root.notificationCenterCompactWidth, - "height": root.compactFaceHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.compactFaceHeight / 2, - "topRightRadius": root.compactFaceHeight / 2, - "bottomLeftRadius": root.compactFaceHeight / 2, - "bottomRightRadius": root.compactFaceHeight / 2 - }) - readonly property var notificationCenterExpandedTarget: ({ - "width": 480, - "height": root.notificationCenterHeight, - "offsetX": root.horizontalOffset, - "offsetY": root.outerGap, - "topLeftRadius": root.anchoredRadius, - "topRightRadius": root.anchoredRadius, - "bottomLeftRadius": root.freeRadius, - "bottomRightRadius": root.freeRadius - }) + }; + } + + readonly property var homeCompactTarget: pillTarget(homeCompactWidth, homeCompactFaceHeight) + readonly property var homeExpandedTarget: sheetTarget(SettingsData.showWeekNumber ? 736 : 700, 452) + readonly property var mediaCompactTarget: pillTarget(mediaCompactWidth, compactFaceHeight) + readonly property var mediaExpandedTarget: sheetTarget(600, 352) + readonly property var launcherExpandedTarget: sheetTarget(680, 560) + readonly property var controlCenterExpandedTarget: sheetTarget(580, controlCenterHeight) + readonly property var wallpaperExpandedTarget: sheetTarget(700, 452) + readonly property var weatherExpandedTarget: sheetTarget(SettingsData.showWeekNumber ? 736 : 700, 452) + readonly property var systemCompactTarget: pillTarget(SettingsData.osdAlwaysShowValue ? 330 : 282, compactFaceHeight) + readonly property var systemExpandedTarget: sheetTarget(460, 176) + readonly property var notificationCompactTarget: pillTarget(compactDense ? 320 : 360, compactFaceHeight) + readonly property var notificationExpandedTarget: sheetTarget(520, 220) + readonly property var notificationCenterExpandedTarget: sheetTarget(480, notificationCenterHeight) + readonly property bool systemActivityActive: activeActivity === "volume" || activeActivity === "brightness" readonly property bool notificationActive: activeActivity === "notification" readonly property bool notificationHeldForSystem: root.systemActivityActive && root.transientReturnActivity === "notification" readonly property bool transientActive: systemActivityActive || notificationActive readonly property bool timeoutSuspended: pointerInside || (expanded && !notificationActive) - readonly property var fullDestinations: ["launcher", "controlcenter", "wallpaper", "weather", "notificationcenter"] - readonly property bool activityOwnsBlankClicks: root.fullDestinations.indexOf(root.activeActivity) !== -1 + readonly property bool activityOwnsBlankClicks: isDestination(activeActivity) readonly property bool hoverExpandEnabled: root.interactionMode === "hybrid" && !root.systemActivityActive readonly property var compactTarget: { - if (root.notificationActive) - return root.notificationCompactTarget; - switch (root.activeActivity) { - case "launcher": - return root.launcherCompactTarget; - case "controlcenter": - return root.controlCenterCompactTarget; - case "wallpaper": - return root.wallpaperCompactTarget; - case "weather": - return root.weatherCompactTarget; - case "notificationcenter": - return root.notificationCenterCompactTarget; - case "media": - return root.mediaCompactTarget; - } - return root.systemActivityActive ? root.systemCompactTarget : root.homeCompactTarget; + if (notificationActive) + return notificationCompactTarget; + if (systemActivityActive) + return systemCompactTarget; + if (isDestination(activeActivity)) + return pillTarget(destinationCompactWidth(activeActivity), compactFaceHeight); + return activeActivity === "media" ? mediaCompactTarget : homeCompactTarget; } readonly property var expandedTarget: { - if (root.notificationActive) - return root.notificationExpandedTarget; - switch (root.activeActivity) { + if (notificationActive) + return notificationExpandedTarget; + if (systemActivityActive) + return systemExpandedTarget; + switch (activeActivity) { case "launcher": - return root.launcherExpandedTarget; + return launcherExpandedTarget; case "controlcenter": - return root.controlCenterExpandedTarget; + return controlCenterExpandedTarget; case "wallpaper": - return root.wallpaperExpandedTarget; + return wallpaperExpandedTarget; case "weather": - return root.weatherExpandedTarget; + return weatherExpandedTarget; case "notificationcenter": - return root.notificationCenterExpandedTarget; + return notificationCenterExpandedTarget; case "media": - return root.mediaExpandedTarget; + return mediaExpandedTarget; } - return root.systemActivityActive ? root.systemExpandedTarget : root.homeExpandedTarget; + return homeExpandedTarget; } readonly property var targetDescriptor: expanded ? expandedTarget : compactTarget function syncNotificationTimeout(restart) { - // Hover pauses arrival expiry; opt-in expansion remains a timed glance. if (!notificationActive || timeoutSuspended || notificationTimeout <= 0) { notificationTimer.stop(); return; @@ -443,7 +384,6 @@ QtObject { } onInputSuspendedChanged: { - // Screenshot selection owns input while the island remains mapped for the session. if (!inputSuspended) return; hoverExpanded = false; @@ -452,13 +392,9 @@ QtObject { keyboardDismissRequested = false; expanded = false; keyboardYielded = false; - launcherRequestPending = false; launcherSessionActive = false; - controlCenterRequestPending = false; - wallpaperRequestPending = false; - weatherRequestPending = false; - notificationCenterRequestPending = false; - if (fullDestinations.indexOf(activeActivity) !== -1) + clearPendingRequests(""); + if (activityOwnsBlankClicks) activeActivity = mediaAvailable && mediaPreferred ? "media" : "home"; } @@ -504,11 +440,7 @@ QtObject { if (notificationActive && !notificationExpandAllowed) return requestNotificationCenter(false); hoverExpanded = false; - launcherRequestPending = false; - controlCenterRequestPending = false; - wallpaperRequestPending = false; - weatherRequestPending = false; - notificationCenterRequestPending = false; + clearPendingRequests(""); hoverCloseTimer.stop(); transientTimer.stop(); notificationTimer.stop(); @@ -525,12 +457,8 @@ QtObject { keyboardDismissRequested = false; keyboardYielded = false; expanded = false; - launcherRequestPending = false; launcherSessionActive = false; - controlCenterRequestPending = false; - wallpaperRequestPending = false; - weatherRequestPending = false; - notificationCenterRequestPending = false; + clearPendingRequests(""); if (notificationActive) { resumeAfterNotification(false); return true; @@ -539,12 +467,8 @@ QtObject { finishTransient(); return true; } - - if (activityOwnsBlankClicks) { + if (activityOwnsBlankClicks) activeActivity = mediaAvailable && mediaPreferred ? "media" : "home"; - return true; - } - return true; } @@ -553,13 +477,14 @@ QtObject { return "notification"; if (activityId === "media" && !mediaAvailable) return "home"; - if (activityId === "home" || activityId === "media" || fullDestinations.indexOf(activityId) !== -1) + if (activityId === "home" || activityId === "media" || isDestination(activityId)) return activityId; return mediaAvailable && mediaPreferred ? "media" : "home"; } function requestActivity(activityId, shouldExpand, requestKeyboardFocus) { - if (activityId !== "home" && activityId !== "media" && fullDestinations.indexOf(activityId) === -1) + const destination = isDestination(activityId); + if (activityId !== "home" && activityId !== "media" && !destination) return false; if (inputSuspended && shouldExpand === true) return false; @@ -568,24 +493,9 @@ QtObject { if (activityId === "weather" && !SettingsData.weatherEnabled) return false; - if (activityId !== "launcher") { - launcherRequestPending = false; + if (activityId !== "launcher") launcherSessionActive = false; - } - if (activityId !== "controlcenter") - controlCenterRequestPending = false; - if (activityId === "wallpaper") - wallpaperReleaseTimer.stop(); - else - wallpaperRequestPending = false; - if (activityId === "weather") - weatherReleaseTimer.stop(); - else - weatherRequestPending = false; - if (activityId === "notificationcenter") - notificationCenterReleaseTimer.stop(); - else - notificationCenterRequestPending = false; + clearPendingRequests(activityId); if (activeActivity === activityId && expanded && shouldExpand === true) { if (requestKeyboardFocus === true) { @@ -595,35 +505,15 @@ QtObject { return true; } - if (activityId === "launcher" && shouldExpand === true && !launcherVisualsReady) { - launcherRequestPending = true; - _launcherPendingExpand = true; - _launcherPendingKeyboardFocus = requestKeyboardFocus === true; - launcherVisualsRequested = true; - return true; - } - - if (activityId === "controlcenter" && shouldExpand === true && !controlCenterVisualsReady) { - controlCenterRequestPending = true; - controlCenterVisualsRequested = true; - return true; - } - - if (activityId === "wallpaper" && shouldExpand === true && !wallpaperVisualsReady) { - wallpaperRequestPending = true; - wallpaperVisualsRequested = true; - return true; - } - - if (activityId === "weather" && shouldExpand === true && !weatherVisualsReady) { - weatherRequestPending = true; - weatherVisualsRequested = true; - return true; - } - - if (activityId === "notificationcenter" && shouldExpand === true && !notificationCenterVisualsReady) { - notificationCenterRequestPending = true; - notificationCenterVisualsRequested = true; + if (destination && shouldExpand === true && !visualsReady(activityId)) { + const entry = destinationState[activityId]; + entry.pending = true; + entry.requested = true; + destinationRevision++; + if (activityId === "launcher") { + _launcherPendingExpand = true; + _launcherPendingKeyboardFocus = requestKeyboardFocus === true; + } return true; } @@ -632,24 +522,15 @@ QtObject { hoverOpenTimer.stop(); hoverCloseTimer.stop(); transientTimer.stop(); - // IPC and other destinations consume the card so its timeout cannot snap back. - root.consumeTransientNotification(); + consumeTransientNotification(); keyboardDismissRequested = shouldExpand === true && requestKeyboardFocus === true; expanded = shouldExpand === true; activeActivity = activityId; - if (activityId === "media") - mediaPreferred = true; - else if (activityId === "home") - mediaPreferred = false; + if (activityId === "media" || activityId === "home") + mediaPreferred = activityId === "media"; launcherSessionActive = activityId === "launcher" && expanded; - if (launcherSessionActive) - launcherSessionSerial++; - if (activityId === "controlcenter" && expanded) - controlCenterSessionSerial++; - if (activityId === "wallpaper" && expanded) - wallpaperSessionSerial++; - if (activityId === "notificationcenter" && expanded) - notificationCenterSessionSerial++; + if (destination && expanded) + sessionStarted(activityId); return true; } @@ -680,66 +561,6 @@ QtObject { return requestActivity("weather", true, true); } - function markLauncherVisualsReady() { - launcherVisualsReady = true; - if (!launcherRequestPending) - return; - launcherRequestPending = false; - requestActivity("launcher", _launcherPendingExpand, _launcherPendingKeyboardFocus); - } - - function releaseLauncherVisuals() { - if (expanded && activeActivity === "launcher") { - launcherReleaseTimer.stop(); - return; - } - if (!launcherVisualsRequested && !launcherVisualsReady) - return; - launcherReleaseTimer.restart(); - } - - function _dropLauncherVisuals() { - launcherReleaseTimer.stop(); - if (expanded && activeActivity === "launcher") - return; - launcherRequestPending = false; - launcherVisualsRequested = false; - launcherVisualsReady = false; - launcherSessionActive = false; - launcherInputFocused = false; - launcherPendingQuery = ""; - launcherPendingMode = ""; - } - - function markControlCenterVisualsReady() { - controlCenterVisualsReady = true; - if (!controlCenterRequestPending) - return; - controlCenterRequestPending = false; - requestActivity("controlcenter", true, true); - } - - function releaseControlCenterVisuals() { - if (expanded && activeActivity === "controlcenter") { - controlCenterReleaseTimer.stop(); - return; - } - if (!controlCenterVisualsRequested && !controlCenterVisualsReady) - return; - controlCenterReleaseTimer.restart(); - } - - function _dropControlCenterVisuals() { - controlCenterReleaseTimer.stop(); - if (expanded && activeActivity === "controlcenter") - return; - controlCenterRequestPending = false; - controlCenterVisualsRequested = false; - controlCenterVisualsReady = false; - controlCenterPendingSection = ""; - controlCenterContentHeight = 420; - } - function requestNotificationCenter(shouldToggle) { if (shouldToggle === true && activeActivity === "notificationcenter" && expanded) return requestCollapse(); @@ -747,97 +568,6 @@ QtObject { return requestActivity("notificationcenter", true, true); } - function markNotificationCenterVisualsReady() { - notificationCenterVisualsReady = true; - if (!notificationCenterRequestPending) - return; - notificationCenterRequestPending = false; - requestActivity("notificationcenter", true, true); - } - - function setNotificationCenterContentHeight(height) { - const next = Math.round(height); - if (!isFinite(next) || Math.abs(next - notificationCenterContentHeight) < 1) - return; - notificationCenterContentHeight = next; - } - - function releaseNotificationCenterVisuals() { - if (expanded && activeActivity === "notificationcenter") { - notificationCenterReleaseTimer.stop(); - return; - } - if (!notificationCenterVisualsRequested && !notificationCenterVisualsReady) - return; - notificationCenterReleaseTimer.restart(); - } - - function _dropNotificationCenterVisuals() { - notificationCenterReleaseTimer.stop(); - notificationCenterRequestPending = false; - notificationCenterVisualsRequested = false; - notificationCenterVisualsReady = false; - notificationCenterContentHeight = 320; - } - - function markWallpaperVisualsReady() { - wallpaperVisualsReady = true; - if (!wallpaperRequestPending) - return; - wallpaperRequestPending = false; - requestActivity("wallpaper", true, true); - } - - function releaseWallpaperVisuals() { - if (expanded && activeActivity === "wallpaper") { - wallpaperReleaseTimer.stop(); - return; - } - if (!wallpaperVisualsRequested && !wallpaperVisualsReady) - return; - wallpaperReleaseTimer.restart(); - } - - function _dropWallpaperVisuals() { - wallpaperReleaseTimer.stop(); - wallpaperRequestPending = false; - wallpaperVisualsRequested = false; - wallpaperVisualsReady = false; - keyboardYielded = false; - } - - function markWeatherVisualsReady() { - weatherVisualsReady = true; - if (!weatherRequestPending) - return; - weatherRequestPending = false; - requestActivity("weather", true, true); - } - - function releaseWeatherVisuals() { - if (expanded && activeActivity === "weather") { - weatherReleaseTimer.stop(); - return; - } - if (!weatherVisualsRequested && !weatherVisualsReady) - return; - weatherReleaseTimer.restart(); - } - - function _dropWeatherVisuals() { - weatherReleaseTimer.stop(); - weatherRequestPending = false; - weatherVisualsRequested = false; - weatherVisualsReady = false; - } - - function setControlCenterContentHeight(height) { - const next = Math.round(height); - if (!isFinite(next) || Math.abs(next - controlCenterContentHeight) < 1) - return; - controlCenterContentHeight = next; - } - function cycleActivity(direction, shouldExpand) { const activities = ["home"]; if (mediaAvailable) @@ -874,12 +604,8 @@ QtObject { if (activityId !== "volume" && activityId !== "brightness") return false; - launcherRequestPending = false; launcherSessionActive = false; - controlCenterRequestPending = false; - wallpaperRequestPending = false; - weatherRequestPending = false; - notificationCenterRequestPending = false; + clearPendingRequests(""); if (notificationActive) { notificationTimer.stop(); @@ -927,16 +653,10 @@ QtObject { if (!canRequestNotification(critical)) return false; - if (notificationActive) { + if (notificationActive) return refreshNotification(); - } - - launcherRequestPending = false; - controlCenterRequestPending = false; - wallpaperRequestPending = false; - weatherRequestPending = false; - notificationCenterRequestPending = false; + clearPendingRequests(""); transientTimer.stop(); suspendedActivity = activeActivity; suspendedExpanded = expanded; @@ -971,14 +691,8 @@ QtObject { keyboardDismissRequested = nextKeyboardDismissRequested; activeActivity = nextActivity; launcherSessionActive = nextActivity === "launcher" && nextExpanded; - if (launcherSessionActive) - launcherSessionSerial++; - if (nextActivity === "controlcenter" && nextExpanded) - controlCenterSessionSerial++; - if (nextActivity === "wallpaper" && nextExpanded) - wallpaperSessionSerial++; - if (nextActivity === "notificationcenter" && nextExpanded) - notificationCenterSessionSerial++; + if (nextExpanded && isDestination(nextActivity)) + sessionStarted(nextActivity); if (systemActivityActive && !expanded) transientTimer.restart(); } @@ -1029,17 +743,17 @@ QtObject { function updatePointerInside(inside) { pointerInside = inside === true; - if (pointerInside) { - hoverCloseTimer.stop(); - if (hoverExpandEnabled && !expanded && !slotHovered) - hoverOpenTimer.restart(); - } else { + syncNotificationTimeout(!pointerInside); + syncTransientTimeout(!pointerInside); + if (!pointerInside) { hoverOpenTimer.stop(); if (hoverExpanded && expanded && !mediaDropdownOpen) hoverCloseTimer.restart(); + return true; } - syncNotificationTimeout(!pointerInside); - syncTransientTimeout(!pointerInside); + hoverCloseTimer.stop(); + if (hoverExpandEnabled && !expanded && !slotHovered) + hoverOpenTimer.restart(); return true; } @@ -1060,13 +774,6 @@ QtObject { } } - property Timer autoDemoTimer: Timer { - interval: 1600 - repeat: true - running: root.autoDemo - onTriggered: root.requestToggle(false) - } - property Timer mediaReturnTimer: Timer { interval: root.mediaReturnDelay onTriggered: { @@ -1092,28 +799,8 @@ QtObject { onTriggered: root.completeNotification() } - property Timer launcherReleaseTimer: Timer { + property Timer visualsReleaseTimer: Timer { interval: 260 - onTriggered: root._dropLauncherVisuals() - } - - property Timer controlCenterReleaseTimer: Timer { - interval: 260 - onTriggered: root._dropControlCenterVisuals() - } - - property Timer wallpaperReleaseTimer: Timer { - interval: 260 - onTriggered: root._dropWallpaperVisuals() - } - - property Timer weatherReleaseTimer: Timer { - interval: 260 - onTriggered: root._dropWeatherVisuals() - } - - property Timer notificationCenterReleaseTimer: Timer { - interval: 260 - onTriggered: root._dropNotificationCenterVisuals() + onTriggered: root.dropIdleVisuals() } } diff --git a/quickshell/Modules/DankIsland/IslandMediaSource.qml b/quickshell/Modules/DankIsland/IslandMediaSource.qml new file mode 100644 index 000000000..d47a07a1b --- /dev/null +++ b/quickshell/Modules/DankIsland/IslandMediaSource.qml @@ -0,0 +1,49 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell.Services.Mpris +import qs.Common +import qs.Services + +QtObject { + id: root + + required property IslandController controller + + readonly property var player: MprisController.activePlayer + readonly property bool available: !!player && player.playbackState !== MprisPlaybackState.Stopped + readonly property string title: MprisController.stableTitle || player?.trackTitle || I18n.tr("Unknown Track") + readonly property string artist: MprisController.stableArtist || player?.trackArtist || player?.identity || I18n.tr("Unknown Artist") + readonly property string album: MprisController.stableAlbum || player?.trackAlbum || "" + readonly property string artUrl: TrackArtService.resolvedArtUrl + readonly property bool playing: !!player?.isPlaying + readonly property bool canToggle: !!player?.canTogglePlaying + readonly property bool canGoPrevious: !!player?.canGoPrevious + readonly property bool canGoNext: !!player?.canGoNext + readonly property bool canSeek: !!player?.canSeek && length > 0 + readonly property real position: Math.max(0, player?.position || 0) + readonly property real length: Math.max(0, MprisController.activePlayerStableLength) + readonly property real progress: length > 0 ? Math.max(0, Math.min(1, position / length)) : 0 + + function togglePlaying() { + if (player?.canTogglePlaying) + player.togglePlaying(); + } + + function previous() { + MprisController.previousOrRewind(); + } + + function next() { + MprisController.next(); + } + + function seekRatio(ratio) { + if (!canSeek || length <= 0) + return; + player.position = Math.max(0.1, Math.min(length - 0.1, length * Math.max(0, Math.min(1, ratio)))); + } + + Component.onCompleted: controller.updateMediaAvailability(available) + onAvailableChanged: controller.updateMediaAvailability(available) +} diff --git a/quickshell/Modules/DankIsland/DmsNotificationActivitySource.qml b/quickshell/Modules/DankIsland/IslandNotificationSource.qml similarity index 68% rename from quickshell/Modules/DankIsland/DmsNotificationActivitySource.qml rename to quickshell/Modules/DankIsland/IslandNotificationSource.qml index ab514ad09..2cba076f7 100644 --- a/quickshell/Modules/DankIsland/DmsNotificationActivitySource.qml +++ b/quickshell/Modules/DankIsland/IslandNotificationSource.qml @@ -1,7 +1,6 @@ pragma ComponentBehavior: Bound import QtQuick -import Quickshell import Quickshell.Services.Notifications import qs.Common import qs.Services @@ -22,11 +21,8 @@ QtObject { property string fallbackText: "" property string actionLabel: "" property bool critical: false - property int demoIndex: 0 - readonly property bool demoEnabled: Quickshell.env("DMS_DANKISLAND_NOTIFICATION_DEMO") === "1" readonly property bool hasAction: actionLabel.length > 0 - // Keep the wrapper alive while the island is hovered or expanded, matching NotificationPopup. readonly property bool wrapperTimeoutHeld: (controller.notificationActive && controller.timeoutSuspended) || controller.notificationHeldForSystem function isFocusedScreen() { @@ -36,7 +32,6 @@ QtObject { return !!focused && !!root.targetScreen && focused.name === root.targetScreen.name; } - // Hold display fields through the outgoing crossfade. function scheduleDisplayFieldClear() { displayFieldClearTimer.restart(); } @@ -111,16 +106,16 @@ QtObject { releaseWrapperTimeoutHold(); displayFieldClearTimer.stop(); currentWrapper = wrapper; - appName = wrapper.appName || "Notification"; + appName = wrapper.appName || I18n.tr("Notification"); summary = plainText(wrapper.summary) || appName; body = plainText(wrapper.body); - timeText = wrapper.timeStr || "Now"; + timeText = wrapper.timeStr || ""; imageSource = resolveImage(wrapper); fallbackIcon = resolveFallbackIcon(wrapper); fallbackText = appName.charAt(0).toUpperCase(); critical = isCritical; const actions = wrapper.actions || []; - actionLabel = actions.length > 0 ? (actions[0].text || "Open") : ""; + actionLabel = actions.length > 0 ? (actions[0].text || I18n.tr("Open")) : ""; controller.notificationTimeout = wrapperTimeout(wrapper); const accepted = controller.requestNotification(isCritical); applyWrapperTimeoutHold(); @@ -137,9 +132,6 @@ QtObject { onWrapperTimeoutHeldChanged: applyWrapperTimeoutHold() function syncVisibleNotifications() { - if (demoEnabled) - return; - const visible = NotificationService.visibleNotifications || []; if (visible.length > 0) { const latest = visible[visible.length - 1]; @@ -147,18 +139,14 @@ QtObject { return; } - if (currentWrapper && visible.indexOf(currentWrapper) === -1) { - currentWrapper = null; - controller.completeNotification(); - scheduleDisplayFieldClear(); - } + if (!currentWrapper || visible.indexOf(currentWrapper) !== -1) + return; + currentWrapper = null; + controller.completeNotification(); + scheduleDisplayFieldClear(); } function activate() { - if (demoEnabled) { - controller.completeNotification(); - return; - } const actions = currentWrapper?.actions || []; if (actions.length > 0) actions[0].invoke(); @@ -170,47 +158,10 @@ QtObject { currentWrapper = null; controller.completeNotification(); scheduleDisplayFieldClear(); - if (!demoEnabled && wrapper) + if (wrapper) NotificationService.dismissNotification(wrapper); } - function showDemo() { - const variants = [ - { - "appName": "Messages", - "summary": "New message from Alex", - "body": "The new activity transition looks great. Are we still on for tomorrow?", - "icon": "material:mark_chat_unread", - "action": "Reply" - }, - { - "appName": "Calendar", - "summary": "Design review in 10 minutes", - "body": "DankIsland activity protocol · Studio call", - "icon": "material:event_upcoming", - "action": "Open" - } - ]; - const demo = variants[demoIndex % variants.length]; - demoIndex += 1; - if (!controller.canRequestNotification(false)) - return; - releaseWrapperTimeoutHold(); - displayFieldClearTimer.stop(); - currentWrapper = null; - appName = demo.appName; - summary = demo.summary; - body = demo.body; - timeText = "Now"; - imageSource = ""; - fallbackIcon = demo.icon; - fallbackText = demo.appName.charAt(0); - actionLabel = demo.action; - critical = false; - controller.notificationTimeout = 5000; - controller.requestNotification(false); - } - property Connections notificationConnections: Connections { target: NotificationService @@ -228,17 +179,5 @@ QtObject { onTriggered: root.clearDisplayFields() } - property Timer demoTimer: Timer { - interval: 7000 - repeat: true - running: root.demoEnabled - onTriggered: root.showDemo() - } - - Component.onCompleted: { - if (demoEnabled) - Qt.callLater(showDemo); - else - syncVisibleNotifications(); - } + Component.onCompleted: syncVisibleNotifications() } diff --git a/quickshell/Modules/DankIsland/DmsSystemActivitySource.qml b/quickshell/Modules/DankIsland/IslandSystemSource.qml similarity index 56% rename from quickshell/Modules/DankIsland/DmsSystemActivitySource.qml rename to quickshell/Modules/DankIsland/IslandSystemSource.qml index 1eab5b2cb..cc1a38bce 100644 --- a/quickshell/Modules/DankIsland/DmsSystemActivitySource.qml +++ b/quickshell/Modules/DankIsland/IslandSystemSource.qml @@ -1,7 +1,6 @@ pragma ComponentBehavior: Bound import QtQuick -import Quickshell import qs.Common import qs.Services @@ -11,17 +10,12 @@ QtObject { required property IslandController controller property string kind: "volume" - property real demoRatio: 0.68 - - readonly property bool demoEnabled: Quickshell.env("DMS_DANKISLAND_SYSTEM_DEMO") === "1" readonly property bool volumeActivity: kind === "volume" - readonly property bool available: demoEnabled || (volumeActivity ? !!AudioService.sink?.audio : DisplayService.brightnessAvailable) - readonly property bool muted: !demoEnabled && volumeActivity && (AudioService.sink?.audio?.muted ?? false) - readonly property real value: demoEnabled ? demoRatio * 100 : volumeActivity ? Math.min(AudioService.sinkMaxVolume, Math.round((AudioService.sink?.audio?.volume ?? 0) * 100)) : DisplayService.brightnessLevel + readonly property bool available: volumeActivity ? !!AudioService.sink?.audio : DisplayService.brightnessAvailable + readonly property bool muted: volumeActivity && (AudioService.sink?.audio?.muted ?? false) + readonly property real value: volumeActivity ? Math.min(AudioService.sinkMaxVolume, Math.round((AudioService.sink?.audio?.volume ?? 0) * 100)) : DisplayService.brightnessLevel readonly property real maximum: { - if (demoEnabled) - return 100; if (volumeActivity) return AudioService.sinkMaxVolume; const deviceInfo = DisplayService.getCurrentDeviceInfo(); @@ -32,7 +26,7 @@ QtObject { return deviceInfo.displayMax || 100; } readonly property int minimum: { - if (volumeActivity || demoEnabled) + if (volumeActivity) return 0; const deviceInfo = DisplayService.getCurrentDeviceInfo(); if (!deviceInfo) @@ -42,11 +36,11 @@ QtObject { return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0; } readonly property real ratio: maximum > 0 ? Math.max(0, Math.min(1, value / maximum)) : 0 - readonly property string title: volumeActivity ? "Volume" : "Brightness" + readonly property string title: volumeActivity ? I18n.tr("Volume") : I18n.tr("Brightness") readonly property string unit: { if (volumeActivity) return "%"; - const deviceInfo = demoEnabled ? null : DisplayService.getCurrentDeviceInfo(); + const deviceInfo = DisplayService.getCurrentDeviceInfo(); if (!deviceInfo) return "%"; if (SessionData.getBrightnessExponential(deviceInfo.id)) @@ -54,28 +48,31 @@ QtObject { return deviceInfo.class === "ddc" ? "" : "%"; } readonly property string displayValue: muted ? I18n.tr("Muted") : Math.round(value) + unit - readonly property string iconName: { - if (!volumeActivity) { - const deviceInfo = demoEnabled ? null : DisplayService.getCurrentDeviceInfo(); - if (!demoEnabled && (!available || !deviceInfo)) - return "brightness_low"; - if (demoEnabled || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") { - if (ratio <= 0.33) - return "brightness_low"; - return ratio <= 0.66 ? "brightness_medium" : "brightness_high"; - } - if (String(deviceInfo.name ?? "").includes("kbd")) - return "keyboard"; - return "lightbulb"; - } + readonly property string iconName: volumeActivity ? volumeIconName() : brightnessIconName() + + function volumeIconName() { if (!available || muted) return "volume_off"; - const volume = demoEnabled ? ratio : (AudioService.sink?.audio?.volume ?? 0); + const volume = AudioService.sink?.audio?.volume ?? 0; if (volume === 0) return "volume_mute"; return volume <= 0.33 ? "volume_down" : "volume_up"; } + function brightnessIconName() { + const deviceInfo = DisplayService.getCurrentDeviceInfo(); + if (!available || !deviceInfo) + return "brightness_low"; + if (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") { + if (ratio <= 0.33) + return "brightness_low"; + return ratio <= 0.66 ? "brightness_medium" : "brightness_high"; + } + if (String(deviceInfo.name ?? "").includes("kbd")) + return "keyboard"; + return "lightbulb"; + } + function show(activityKind) { if (controller.requestSystemActivity(activityKind)) kind = activityKind; @@ -83,22 +80,17 @@ QtObject { function setRatio(nextRatio) { const clampedRatio = Math.max(0, Math.min(1, nextRatio)); - if (demoEnabled) { - demoRatio = clampedRatio; + SessionData.suppressOSDTemporarily(); + if (volumeActivity) { + AudioService.setVolume(Math.round(clampedRatio * maximum)); return; } - SessionData.suppressOSDTemporarily(); - if (volumeActivity) - AudioService.setVolume(Math.round(clampedRatio * maximum)); - else - DisplayService.setBrightness(Math.round(clampedRatio * maximum), DisplayService.lastIpcDevice, true); + DisplayService.setBrightness(Math.round(clampedRatio * maximum), DisplayService.lastIpcDevice, true); } function toggleMute() { if (!volumeActivity || !available) return; - if (demoEnabled) - return; SessionData.suppressOSDTemporarily(); AudioService.toggleMute(); } @@ -125,21 +117,4 @@ QtObject { root.show("brightness"); } } - - property Timer demoTimer: Timer { - interval: 3200 - repeat: true - running: root.demoEnabled - - onTriggered: { - const nextKind = root.kind === "volume" ? "brightness" : "volume"; - root.demoRatio = nextKind === "volume" ? 0.68 : 0.82; - root.show(nextKind); - } - } - - Component.onCompleted: { - if (demoEnabled) - show("volume"); - } } diff --git a/quickshell/Modules/DankIsland/NativeSpringMotion.qml b/quickshell/Modules/DankIsland/NativeSpringMotion.qml deleted file mode 100644 index cabba82c3..000000000 --- a/quickshell/Modules/DankIsland/NativeSpringMotion.qml +++ /dev/null @@ -1,176 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick - -QtObject { - id: root - - property bool enabled: true - property bool animationsEnabled: true - - property real spring: 5 - property real damping: 0.32 - property real mass: 1 - property real epsilon: 0.04 - - property real currentWidth: 176 - property real currentHeight: 42 - property real currentOffsetX: 0 - property real currentOffsetY: 8 - property real currentTopLeftRadius: 21 - property real currentTopRightRadius: 21 - property real currentBottomLeftRadius: 21 - property real currentBottomRightRadius: 21 - - property real targetWidth: currentWidth - property real targetHeight: currentHeight - property real targetOffsetX: currentOffsetX - property real targetOffsetY: currentOffsetY - property real targetTopLeftRadius: currentTopLeftRadius - property real targetTopRightRadius: currentTopRightRadius - property real targetBottomLeftRadius: currentBottomLeftRadius - property real targetBottomRightRadius: currentBottomRightRadius - - readonly property bool running: widthSpring.running || heightSpring.running || offsetXSpring.running || offsetYSpring.running || topLeftRadiusSpring.running || topRightRadiusSpring.running - || bottomLeftRadiusSpring.running || bottomRightRadiusSpring.running - - function setTarget(target) { - targetWidth = target.width; - targetHeight = target.height; - targetOffsetX = target.offsetX; - targetOffsetY = target.offsetY; - targetTopLeftRadius = target.topLeftRadius; - targetTopRightRadius = target.topRightRadius; - targetBottomLeftRadius = target.bottomLeftRadius; - targetBottomRightRadius = target.bottomRightRadius; - - currentWidth = targetWidth; - currentHeight = targetHeight; - currentOffsetX = targetOffsetX; - currentOffsetY = targetOffsetY; - currentTopLeftRadius = targetTopLeftRadius; - currentTopRightRadius = targetTopRightRadius; - currentBottomLeftRadius = targetBottomLeftRadius; - currentBottomRightRadius = targetBottomRightRadius; - } - - function snapTo(target) { - animationsEnabled = false; - - targetWidth = target.width; - targetHeight = target.height; - targetOffsetX = target.offsetX; - targetOffsetY = target.offsetY; - targetTopLeftRadius = target.topLeftRadius; - targetTopRightRadius = target.topRightRadius; - targetBottomLeftRadius = target.bottomLeftRadius; - targetBottomRightRadius = target.bottomRightRadius; - - currentWidth = target.width; - currentHeight = target.height; - currentOffsetX = target.offsetX; - currentOffsetY = target.offsetY; - currentTopLeftRadius = target.topLeftRadius; - currentTopRightRadius = target.topRightRadius; - currentBottomLeftRadius = target.bottomLeftRadius; - currentBottomRightRadius = target.bottomRightRadius; - - animationsEnabled = true; - } - - Behavior on currentWidth { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: widthSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } - - Behavior on currentHeight { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: heightSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } - - Behavior on currentOffsetX { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: offsetXSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } - - Behavior on currentOffsetY { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: offsetYSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } - - Behavior on currentTopLeftRadius { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: topLeftRadiusSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } - - Behavior on currentTopRightRadius { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: topRightRadiusSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } - - Behavior on currentBottomLeftRadius { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: bottomLeftRadiusSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } - - Behavior on currentBottomRightRadius { - enabled: root.enabled && root.animationsEnabled - - SpringAnimation { - id: bottomRightRadiusSpring - spring: root.spring - damping: root.damping - mass: root.mass - epsilon: root.epsilon - } - } -} diff --git a/quickshell/Modules/DankIsland/VectorSpringMotion.qml b/quickshell/Modules/DankIsland/VectorSpringMotion.qml index 86b85dade..651cd12be 100644 --- a/quickshell/Modules/DankIsland/VectorSpringMotion.qml +++ b/quickshell/Modules/DankIsland/VectorSpringMotion.qml @@ -44,7 +44,13 @@ QtObject { property real velocityBottomLeftRadius: 0 property real velocityBottomRightRadius: 0 + function matchesTarget(target) { + return targetWidth === target.width && targetHeight === target.height && targetOffsetX === target.offsetX && targetOffsetY === target.offsetY && targetTopLeftRadius === target.topLeftRadius && targetTopRightRadius === target.topRightRadius && targetBottomLeftRadius === target.bottomLeftRadius && targetBottomRightRadius === target.bottomRightRadius; + } + function setTarget(target) { + if (matchesTarget(target)) + return; targetWidth = target.width; targetHeight = target.height; targetOffsetX = target.offsetX; @@ -94,13 +100,8 @@ QtObject { } function isSettled() { - const positionSettled = Math.abs(targetWidth - currentWidth) <= positionEpsilon && Math.abs(targetHeight - currentHeight) <= positionEpsilon - && Math.abs(targetOffsetX - currentOffsetX) <= positionEpsilon && Math.abs(targetOffsetY - currentOffsetY) <= positionEpsilon - && Math.abs(targetTopLeftRadius - currentTopLeftRadius) <= positionEpsilon && Math.abs(targetTopRightRadius - currentTopRightRadius) <= positionEpsilon - && Math.abs(targetBottomLeftRadius - currentBottomLeftRadius) <= positionEpsilon && Math.abs(targetBottomRightRadius - currentBottomRightRadius) <= positionEpsilon; - const velocitySettled = Math.abs(velocityWidth) <= velocityEpsilon && Math.abs(velocityHeight) <= velocityEpsilon && Math.abs(velocityOffsetX) <= velocityEpsilon - && Math.abs(velocityOffsetY) <= velocityEpsilon && Math.abs(velocityTopLeftRadius) <= velocityEpsilon && Math.abs(velocityTopRightRadius) <= velocityEpsilon - && Math.abs(velocityBottomLeftRadius) <= velocityEpsilon && Math.abs(velocityBottomRightRadius) <= velocityEpsilon; + const positionSettled = Math.abs(targetWidth - currentWidth) <= positionEpsilon && Math.abs(targetHeight - currentHeight) <= positionEpsilon && Math.abs(targetOffsetX - currentOffsetX) <= positionEpsilon && Math.abs(targetOffsetY - currentOffsetY) <= positionEpsilon && Math.abs(targetTopLeftRadius - currentTopLeftRadius) <= positionEpsilon && Math.abs(targetTopRightRadius - currentTopRightRadius) <= positionEpsilon && Math.abs(targetBottomLeftRadius - currentBottomLeftRadius) <= positionEpsilon && Math.abs(targetBottomRightRadius - currentBottomRightRadius) <= positionEpsilon; + const velocitySettled = Math.abs(velocityWidth) <= velocityEpsilon && Math.abs(velocityHeight) <= velocityEpsilon && Math.abs(velocityOffsetX) <= velocityEpsilon && Math.abs(velocityOffsetY) <= velocityEpsilon && Math.abs(velocityTopLeftRadius) <= velocityEpsilon && Math.abs(velocityTopRightRadius) <= velocityEpsilon && Math.abs(velocityBottomLeftRadius) <= velocityEpsilon && Math.abs(velocityBottomRightRadius) <= velocityEpsilon; return positionSettled && velocitySettled; } diff --git a/quickshell/Modules/Notifications/Center/NotificationCenterContent.qml b/quickshell/Modules/Notifications/Center/NotificationCenterContent.qml index db7476865..498e58a82 100644 --- a/quickshell/Modules/Notifications/Center/NotificationCenterContent.qml +++ b/quickshell/Modules/Notifications/Center/NotificationCenterContent.qml @@ -4,7 +4,6 @@ import QtQuick import qs.Common import qs.Services -// Host-agnostic notification center body; popout and island both host this. Item { id: root diff --git a/quickshell/Modules/Settings/DankIslandTab.qml b/quickshell/Modules/Settings/DankIslandTab.qml index 6e37bfee2..63a091deb 100644 --- a/quickshell/Modules/Settings/DankIslandTab.qml +++ b/quickshell/Modules/Settings/DankIslandTab.qml @@ -48,7 +48,7 @@ Item { width: parent.width iconName: "view_in_ar" title: I18n.tr("Dank Island - Beta") - settingKey: "dankIslandBarId" + settingKey: "dankIslandInstance" SettingsButtonGroupRow { settingKey: "dankIslandBarId" @@ -260,11 +260,13 @@ Item { tags: ["island", "appearance", "palette", "surface", "bright", "dim"] text: I18n.tr("Palette") model: [I18n.tr("Default"), I18n.tr("Bright"), I18n.tr("Dim")] - currentIndex: SettingsData.dankIslandPalette === "bright" ? 1 : SettingsData.dankIslandPalette === "dim" ? 2 : 0 + readonly property var _paletteValues: ["default", "bright", "dim"] + + currentIndex: Math.max(0, _paletteValues.indexOf(SettingsData.dankIslandPalette)) onSelectionChanged: (index, selected) => { if (!selected) return; - SettingsData.set("dankIslandPalette", index === 1 ? "bright" : index === 2 ? "dim" : "default"); + SettingsData.set("dankIslandPalette", _paletteValues[index] ?? "default"); } } @@ -369,7 +371,7 @@ Item { SettingsCard { width: parent.width iconName: "touch_app" - title: I18n.tr("Interaction") + title: I18n.tr("Behavior") settingKey: "dankIslandInteraction" enabled: SettingsData.dankIslandEnabled @@ -399,7 +401,7 @@ Item { SettingsSliderRow { settingKey: "dankIslandHoverOpenDelay" tags: ["island", "interaction", "hover", "open", "delay"] - text: I18n.tr("Hover Open Delay") + text: I18n.tr("Open Delay") unit: "ms" minimum: 0 maximum: 1000 @@ -413,7 +415,7 @@ Item { SettingsSliderRow { settingKey: "dankIslandHoverCloseDelay" tags: ["island", "interaction", "hover", "close", "delay"] - text: I18n.tr("Hover Close Delay") + text: I18n.tr("Hide Delay") unit: "ms" minimum: 0 maximum: 1000 @@ -435,6 +437,7 @@ Item { SettingsToggleRow { settingKey: "dankIslandReducedMotion" + tags: ["island", "motion", "animation", "reduce", "accessibility", "spring"] text: I18n.tr("Reduce Motion") description: I18n.tr("Apply island geometry changes immediately without spring overshoot") checked: SettingsData.dankIslandReducedMotion @@ -443,6 +446,7 @@ Item { SettingsSliderRow { settingKey: "dankIslandSpringStiffness" + tags: ["island", "motion", "spring", "stiffness", "animation"] text: I18n.tr("Spring Stiffness") description: I18n.tr("Higher values pull island toward its target more strongly") minimum: 100 @@ -456,6 +460,7 @@ Item { SettingsSliderRow { settingKey: "dankIslandSpringDamping" + tags: ["island", "motion", "spring", "damping", "bounce", "animation"] text: I18n.tr("Spring Damping") description: I18n.tr("Higher values settle island with less bounce") minimum: 10 @@ -469,6 +474,7 @@ Item { SettingsSliderRow { settingKey: "dankIslandSpringMass" + tags: ["island", "motion", "spring", "mass", "inertia", "animation"] text: I18n.tr("Spring Mass") description: I18n.tr("Higher percentages give island more inertia") minimum: 25 diff --git a/quickshell/Services/PopoutService.qml b/quickshell/Services/PopoutService.qml index eada1ec7d..387b60e95 100644 --- a/quickshell/Services/PopoutService.qml +++ b/quickshell/Services/PopoutService.qml @@ -141,84 +141,21 @@ Singleton { } readonly property bool islandControlCenterOpen: dankIslandRouter?.controlCenterOpen ?? false - readonly property bool islandMediaOpen: dankIslandRouter?.mediaOpen ?? false - readonly property bool islandWallpaperOpen: dankIslandRouter?.wallpaperOpen ?? false - readonly property bool islandWeatherOpen: dankIslandRouter?.weatherOpen ?? false - readonly property bool islandHomeOpen: dankIslandRouter?.homeOpen ?? false - readonly property bool islandNotificationCenterOpen: dankIslandRouter?.notificationCenterOpen ?? false - // Shared triggers use the island only when it owns that screen; false lets callers keep the popout path. - function routeControlCenterToIsland(screen, section, shouldToggle) { + function routeToIsland(activityId, screen, shouldToggle, section) { if (!_islandOwnsSharedTrigger(screen)) return false; if (shouldToggle === true) - return dankIslandRouter.toggleControlCenter(section || "", screen ?? null); - return dankIslandRouter.openControlCenter(section || "", screen ?? null); + return dankIslandRouter.toggleActivity(activityId, screen ?? null, section || "") === true; + return dankIslandRouter.openActivity(activityId, screen ?? null, section || "") === true; } - // A missing media session is a deliberate fallback signal, not a handled request. - function routeMediaToIsland(screen, shouldToggle) { - if (!_islandOwnsSharedTrigger(screen)) - return false; - if (shouldToggle === true) - return dankIslandRouter.toggleMedia(screen ?? null) === true; - return dankIslandRouter.openMedia(screen ?? null) === true; - } - - function routeWallpaperToIsland(screen, shouldToggle) { - if (!_islandOwnsSharedTrigger(screen)) - return false; - if (shouldToggle === true) - return dankIslandRouter.toggleWallpaper(screen ?? null) === true; - return dankIslandRouter.openWallpaper(screen ?? null) === true; - } - - function routeWeatherToIsland(screen, shouldToggle) { - if (!_islandOwnsSharedTrigger(screen)) - return false; - if (shouldToggle === true) - return dankIslandRouter.toggleWeather(screen ?? null) === true; - return dankIslandRouter.openWeather(screen ?? null) === true; - } - - function routeOverviewToIsland(screen, shouldToggle) { - if (!_islandOwnsSharedTrigger(screen)) - return false; - if (shouldToggle === true) - return dankIslandRouter.toggleHome(screen ?? null) === true; - return dankIslandRouter.openHome(screen ?? null) === true; - } - - function routeNotificationCenterToIsland(screen, shouldToggle) { - if (!_islandOwnsSharedTrigger(screen)) - return false; - if (shouldToggle === true) - return dankIslandRouter.toggleNotifications(screen ?? null) === true; - return dankIslandRouter.openNotifications(screen ?? null) === true; - } - - function closeIslandNotificationCenter() { - return dankIslandRouter?.closeNotifications?.() === true; - } - - function closeIslandMedia() { - return dankIslandRouter?.closeMedia?.() === true; - } - - function closeIslandWallpaper() { - return dankIslandRouter?.closeWallpaper?.() === true; - } - - function closeIslandWeather() { - return dankIslandRouter?.closeWeather?.() === true; - } - - function closeIslandHome() { - return dankIslandRouter?.closeHome?.() === true; + function closeIslandActivity(activityId) { + return dankIslandRouter?.closeActivity?.(activityId) === true; } function openControlCenter(x, y, width, section, screen) { - if (_islandOwnsSharedTrigger(screen) && dankIslandRouter.openControlCenter(section || "", screen ?? null)) + if (routeToIsland("controlcenter", screen, false, section)) return; if (controlCenterPopout) { setPosition(controlCenterPopout, x, y, width, section, screen); @@ -227,7 +164,7 @@ Singleton { } function closeControlCenter() { - if (dankIslandRouter?.closeControlCenter?.() === true) + if (closeIslandActivity("controlcenter")) return; controlCenterPopout?.close(); } @@ -237,7 +174,7 @@ Singleton { } function toggleControlCenter(x, y, width, section, screen) { - if (_islandOwnsSharedTrigger(screen) && dankIslandRouter.toggleControlCenter(section || "", screen ?? null)) + if (routeToIsland("controlcenter", screen, true, section)) return; if (controlCenterPopout) { setPosition(controlCenterPopout, x, y, width, section, screen); @@ -246,7 +183,7 @@ Singleton { } function openNotificationCenter(x, y, width, section, screen) { - if (routeNotificationCenterToIsland(screen, false)) + if (routeToIsland("notificationcenter", screen, false)) return; if (notificationCenterPopout) { setPosition(notificationCenterPopout, x, y, width, section, screen); @@ -255,7 +192,7 @@ Singleton { } function closeNotificationCenter() { - if (closeIslandNotificationCenter()) + if (closeIslandActivity("notificationcenter")) return; notificationCenterPopout?.close(); } @@ -265,7 +202,7 @@ Singleton { } function toggleNotificationCenter(x, y, width, section, screen) { - if (routeNotificationCenterToIsland(screen, true)) + if (routeToIsland("notificationcenter", screen, true)) return; if (notificationCenterPopout) { setPosition(notificationCenterPopout, x, y, width, section, screen); diff --git a/quickshell/Modules/DankIsland/Activities/MediaBlobHalo.qml b/quickshell/Widgets/MediaBlobHalo.qml similarity index 98% rename from quickshell/Modules/DankIsland/Activities/MediaBlobHalo.qml rename to quickshell/Widgets/MediaBlobHalo.qml index 2bddfebfc..c5bd10c93 100644 --- a/quickshell/Modules/DankIsland/Activities/MediaBlobHalo.qml +++ b/quickshell/Widgets/MediaBlobHalo.qml @@ -110,7 +110,7 @@ Item { Connections { target: CavaService - enabled: blobEffect.visible && root.available + enabled: root.blobActive function onValuesChanged() { root.updateBands(); @@ -147,7 +147,7 @@ Item { Behavior on activation { NumberAnimation { - duration: 550 + duration: Theme.extraLongDuration easing.type: Easing.InOutQuad } } diff --git a/quickshell/translations/settings_search_index.json b/quickshell/translations/settings_search_index.json index 3a51a07c6..3611a8285 100644 --- a/quickshell/translations/settings_search_index.json +++ b/quickshell/translations/settings_search_index.json @@ -11987,6 +11987,33 @@ ], "description": "Solid material type or Outline" }, + { + "section": "dankIslandInteraction", + "label": "Behavior", + "tabIndex": 46, + "category": "Dank Island", + "keywords": [ + "activities", + "behavior", + "click", + "compact", + "current", + "dank", + "dynamic island", + "expand", + "expands", + "face", + "hover", + "hybrid", + "intentional", + "interaction", + "island", + "peeks", + "press" + ], + "icon": "touch_app", + "description": "Click expands only on an intentional press. Hybrid peeks the current compact face on hover" + }, { "section": "dankIslandHomeCompactTight", "label": "Compact Clock Pill", @@ -12036,7 +12063,7 @@ "description": "Resize the resting island to align with nearby widgets" }, { - "section": "dankIslandBarId", + "section": "dankIslandInstance", "label": "Dank Island - Beta", "tabIndex": 46, "category": "Dank Island", @@ -12126,6 +12153,23 @@ ], "description": "Floats above windows and other content" }, + { + "section": "dankIslandHoverCloseDelay", + "label": "Hide Delay", + "tabIndex": 46, + "category": "Dank Island", + "keywords": [ + "activities", + "close", + "dank", + "delay", + "dynamic island", + "hide", + "hover", + "interaction", + "island" + ] + }, { "section": "dankIslandHighContrast", "label": "High Contrast", @@ -12200,64 +12244,6 @@ ], "description": "Move island and satellites left or right from display center" }, - { - "section": "dankIslandHoverCloseDelay", - "label": "Hover Close Delay", - "tabIndex": 46, - "category": "Dank Island", - "keywords": [ - "activities", - "close", - "dank", - "delay", - "dynamic island", - "hover", - "interaction", - "island" - ] - }, - { - "section": "dankIslandHoverOpenDelay", - "label": "Hover Open Delay", - "tabIndex": 46, - "category": "Dank Island", - "keywords": [ - "activities", - "dank", - "delay", - "dynamic island", - "hover", - "interaction", - "island", - "open" - ] - }, - { - "section": "dankIslandInteraction", - "label": "Interaction", - "tabIndex": 46, - "category": "Dank Island", - "keywords": [ - "activities", - "click", - "compact", - "current", - "dank", - "dynamic island", - "expand", - "expands", - "face", - "hover", - "hybrid", - "intentional", - "interaction", - "island", - "peeks", - "press" - ], - "icon": "touch_app", - "description": "Click expands only on an intentional press. Hybrid peeks the current compact face on hover" - }, { "section": "_tab_46", "label": "Island", @@ -12287,6 +12273,23 @@ "widgets" ] }, + { + "section": "dankIslandBarId", + "label": "Island Instance", + "tabIndex": 46, + "category": "Dank Island", + "keywords": [ + "activities", + "bar", + "dank", + "dynamic island", + "instance", + "island", + "media", + "notifications", + "osd" + ] + }, { "section": "dankIslandMediaClockVisible", "label": "Keep Clock with Media", @@ -12348,6 +12351,7 @@ "keywords": [ "accessibility", "activities", + "animation", "apply", "changes", "dank", @@ -12357,12 +12361,29 @@ "island", "motion", "overshoot", + "reduce", "spring", "without" ], "icon": "animation", "description": "Apply island geometry changes immediately without spring overshoot" }, + { + "section": "dankIslandHoverOpenDelay", + "label": "Open Delay", + "tabIndex": 46, + "category": "Dank Island", + "keywords": [ + "activities", + "dank", + "delay", + "dynamic island", + "hover", + "interaction", + "island", + "open" + ] + }, { "section": "dankIslandOuterGap", "label": "Outer Gap", @@ -12463,7 +12484,9 @@ "tabIndex": 46, "category": "Dank Island", "keywords": [ + "accessibility", "activities", + "animation", "apply", "changes", "dank", @@ -12553,6 +12576,7 @@ "category": "Dank Island", "keywords": [ "activities", + "animation", "bounce", "damping", "dank", @@ -12560,6 +12584,7 @@ "higher", "island", "less", + "motion", "settle", "spring", "values" @@ -12573,6 +12598,7 @@ "category": "Dank Island", "keywords": [ "activities", + "animation", "dank", "dynamic island", "give", @@ -12581,6 +12607,7 @@ "island", "mass", "more", + "motion", "percentages", "spring" ], @@ -12593,11 +12620,13 @@ "category": "Dank Island", "keywords": [ "activities", + "animation", "dank", "dynamic island", "higher", "island", "more", + "motion", "pull", "spring", "stiffness",