mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-17 16:54:01 -04:00
island: general optimizations and cleanups
This commit is contained in:
@@ -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" },
|
||||
|
||||
@@ -520,6 +520,7 @@ var SPEC = {
|
||||
notificationRules: { def: [] },
|
||||
notificationDndAllowCritical: { def: true },
|
||||
notificationFocusedMonitor: { def: false },
|
||||
|
||||
dankIslandBarId: { def: "", onChange: "updateBarConfigs" },
|
||||
dankIslandFloating: { def: false },
|
||||
dankIslandUseOverlayLayer: { def: false },
|
||||
|
||||
+13
-13
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
|
||||
// Compact-face slot click target; hover here suppresses island hover-expand.
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
delegate: DankIslandLabWindow {
|
||||
required property var modelData
|
||||
|
||||
screen: modelData
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<string> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
+10
-71
@@ -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()
|
||||
}
|
||||
+28
-53
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+2
-2
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user