refactor(island): updated hover/click targets - fitt's law

- New option to dismiss the notfication badge
- Updated dynamic notification widths
This commit is contained in:
purian23
2026-08-23 23:51:52 -04:00
parent 4cf375f2fd
commit d951dbddbd
10 changed files with 132 additions and 39 deletions
+1
View File
@@ -995,6 +995,7 @@ Singleton {
property bool dankIslandHighContrast: false
property bool dankIslandMediaClockVisible: true
property bool dankIslandHomeNotificationBadge: true
property bool dankIslandNotificationBadgeClearOnOpen: false
property bool dankIslandNotificationExpand: false
property string dankIslandHomeMediaSlot: "left"
property string dankIslandHomeStatusSlot: "hidden"
@@ -535,6 +535,7 @@ var SPEC = {
dankIslandHighContrast: { def: false },
dankIslandMediaClockVisible: { def: true },
dankIslandHomeNotificationBadge: { def: true },
dankIslandNotificationBadgeClearOnOpen: { def: false },
dankIslandNotificationExpand: { def: false },
dankIslandHomeMediaSlot: { def: "left" },
dankIslandHomeStatusSlot: { def: "hidden" },
@@ -20,6 +20,7 @@ Item {
readonly property real iconSize: root.textSize + Theme.spacingXS
readonly property real statusIconSize: root.textSize + Theme.spacingXXS
readonly property real groupSpacing: root.controller.homeSlotMargin
readonly property real edgePad: Math.max(root.groupSpacing, (root.width - compactRow.width) / 2)
readonly property bool weatherSlotEnabled: root.controller.homeWeatherSlot !== "hidden"
readonly property var groupIds: root.groupsForSide("left").concat(["clock"]).concat(root.groupsForSide("right"))
property bool weatherRefHeld: false
@@ -86,7 +87,10 @@ Item {
id: item
required property string groupId
required property int slotIndex
readonly property real leadPad: item.slotIndex === 0 ? root.edgePad : root.groupSpacing / 2
readonly property real trailPad: item.slotIndex === root.groupIds.length - 1 ? root.edgePad : root.groupSpacing / 2
readonly property bool isClock: item.groupId === "clock"
readonly property bool isMedia: item.groupId === "media"
readonly property bool isWeather: item.groupId === "weather"
@@ -228,7 +232,10 @@ Item {
IslandSlotHoverArea {
id: groupArea
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
x: -item.leadPad
width: parent.width + item.leadPad + item.trailPad
height: root.height
enabled: !item.isClock
controller: root.controller
onClicked: {
@@ -265,7 +272,9 @@ Item {
GroupItem {
required property var modelData
required property int index
groupId: String(modelData)
slotIndex: index
}
}
}
@@ -18,6 +18,8 @@ Item {
readonly property real textGutter: Theme.spacingXS
readonly property real cavaWidth: 20
readonly property real minTextWidth: 48
readonly property real clockLeadPad: Theme.spacingXS / 2
readonly property real clockTrailPad: Theme.spacingS
readonly property real clockPillHeight: root.dense ? Math.max(22, root.artworkSize - Theme.spacingXS) : 32
readonly property string timeText: systemClock.date.toLocaleTimeString(I18n.locale(), SettingsData.getEffectiveTimeFormat())
readonly property bool clockVisible: SettingsData.dankIslandMediaClockVisible
@@ -155,7 +157,10 @@ Item {
IslandSlotHoverArea {
id: clockArea
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
x: -root.clockLeadPad
width: parent.width + root.clockLeadPad + root.clockTrailPad
height: root.height
controller: root.controller
onClicked: root.controller.requestActivity("home", false, false)
}
@@ -15,7 +15,7 @@ Item {
readonly property string headerText: root.notificationModel.appName + (root.notificationModel.timeText ? " · " + root.notificationModel.timeText : "")
readonly property string summaryText: root.dense && root.notificationModel.appName ? root.notificationModel.appName + " · " + root.notificationModel.summary : root.notificationModel.summary
readonly property real criticalWidth: root.notificationModel.critical ? 3 + Theme.spacingS : 0
readonly property real measuredWidth: Theme.spacingS * 3 + root.criticalWidth + root.iconSize + Math.max(root.dense ? 0 : headerMetrics.width, summaryMetrics.width)
readonly property real measuredWidth: Theme.spacingS * 3 + root.criticalWidth + root.iconSize + Theme.spacingXS + Math.max(root.dense ? 0 : headerLabel.implicitWidth, summaryLabel.implicitWidth)
function pushMeasuredWidth() {
root.controller.setNotificationContentWidth(root.measuredWidth);
@@ -24,22 +24,6 @@ Item {
onMeasuredWidthChanged: root.pushMeasuredWidth()
Component.onCompleted: root.pushMeasuredWidth()
StyledTextMetrics {
id: headerMetrics
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
text: root.headerText
}
StyledTextMetrics {
id: summaryMetrics
font.pixelSize: root.dense ? Theme.fontSizeSmall : Theme.fontSizeMedium
font.weight: Font.DemiBold
text: root.summaryText
}
Row {
anchors {
fill: parent
@@ -73,6 +57,8 @@ Item {
spacing: 1
StyledText {
id: headerLabel
width: parent.width
visible: !root.dense
text: root.headerText
@@ -84,6 +70,8 @@ Item {
}
StyledText {
id: summaryLabel
width: parent.width
text: root.summaryText
color: Theme.surfaceText
@@ -159,7 +159,7 @@ PanelWindow {
controlCenterMaxHeight: root.maxActivityHeight
notificationCenterMaxHeight: root.maxActivityHeight
notificationExpandAllowed: SettingsData.dankIslandNotificationExpand
unreadNotificationCount: NotificationService.notifications.length
unreadNotificationCount: SettingsData.dankIslandNotificationBadgeClearOnOpen ? NotificationService.unreadCount : NotificationService.notifications.length
hoverOpenDelay: Math.max(0, Math.min(1000, SettingsData.dankIslandHoverOpenDelay))
hoverCloseDelay: Math.max(0, Math.min(1000, SettingsData.dankIslandHoverCloseDelay))
}
@@ -66,7 +66,7 @@ QtObject {
readonly property real mediaCompactMaxWidth: 360
property real notificationContentWidth: 0
readonly property real notificationCompactMinWidth: compactDense ? 200 : 240
readonly property real notificationCompactMaxWidth: compactDense ? 320 : 360
readonly property real notificationCompactMaxWidth: mediaCompactMaxWidth
signal sessionStarted(string activityId)
+36 -18
View File
@@ -275,15 +275,6 @@ Item {
onToggled: checked => SettingsData.set("dankIslandHighContrast", checked)
}
SettingsToggleRow {
settingKey: "dankIslandNotificationExpand"
tags: ["island", "notifications", "expand", "arrival", "size"]
text: I18n.tr("Expand Notifications", "island settings: expanded notification toggle")
description: I18n.tr("Expand notifications by default instead of click or hover", "island settings: expanded notification description")
checked: SettingsData.dankIslandNotificationExpand
onToggled: checked => SettingsData.set("dankIslandNotificationExpand", checked)
}
SettingsToggleRow {
settingKey: "dankIslandMediaClockVisible"
tags: ["island", "media", "clock", "compact", "time"]
@@ -293,15 +284,6 @@ Item {
onToggled: checked => SettingsData.set("dankIslandMediaClockVisible", checked)
}
SettingsToggleRow {
settingKey: "dankIslandHomeNotificationBadge"
tags: ["island", "home", "notifications", "badge", "unread", "count"]
text: I18n.tr("Show Badge", "island settings: unread notification count on the home face")
description: I18n.tr("Unread notification count beside the clock", "island settings: notification badge description")
checked: SettingsData.dankIslandHomeNotificationBadge
onToggled: checked => SettingsData.set("dankIslandHomeNotificationBadge", checked)
}
SettingsButtonGroupRow {
settingKey: "dankIslandBatteryStyle"
tags: ["island", "battery", "gauge", "solid", "outline", "appearance"]
@@ -317,6 +299,42 @@ Item {
}
}
SettingsCard {
width: parent.width
iconName: "notifications"
title: I18n.tr("Notifications", "island settings: notifications card title")
settingKey: "dankIslandNotifications"
enabled: SettingsData.dankIslandEnabled
SettingsToggleRow {
settingKey: "dankIslandNotificationExpand"
tags: ["island", "notifications", "expand", "arrival", "size"]
text: I18n.tr("Expand Notifications", "island settings: expanded notification toggle")
description: I18n.tr("Expand notifications by default instead of click or hover", "island settings: expanded notification description")
checked: SettingsData.dankIslandNotificationExpand
onToggled: checked => SettingsData.set("dankIslandNotificationExpand", checked)
}
SettingsToggleRow {
settingKey: "dankIslandHomeNotificationBadge"
tags: ["island", "home", "notifications", "badge", "unread", "count"]
text: I18n.tr("Show Badge", "island settings: unread notification count on the home face")
description: I18n.tr("Unread notification count beside the clock", "island settings: notification badge description")
checked: SettingsData.dankIslandHomeNotificationBadge
onToggled: checked => SettingsData.set("dankIslandHomeNotificationBadge", checked)
}
SettingsToggleRow {
settingKey: "dankIslandNotificationBadgeClearOnOpen"
tags: ["island", "home", "notifications", "badge", "unread", "clear", "dismiss", "open"]
text: I18n.tr("Clear Badge on Open", "island settings: clear the notification badge when the center opens")
description: I18n.tr("Clears the badge on open but keeps notifications active", "island settings: clear badge on open description")
checked: SettingsData.dankIslandNotificationBadgeClearOnOpen
enabled: SettingsData.dankIslandHomeNotificationBadge
onToggled: checked => SettingsData.set("dankIslandNotificationBadgeClearOnOpen", checked)
}
}
SettingsCard {
width: parent.width
iconName: "widgets"
@@ -17,6 +17,23 @@ Singleton {
readonly property list<NotifWrapper> allWrappers: []
readonly property list<NotifWrapper> popups: allWrappers.filter(n => n && n.popup)
property var seenNotifications: []
readonly property int unreadCount: {
const seen = root.seenNotifications;
if (seen.length === 0)
return root.notifications.length;
let count = 0;
for (const wrapper of root.notifications) {
if (seen.indexOf(wrapper) === -1)
count++;
}
return count;
}
function markNotificationsSeen() {
root.seenNotifications = root.notifications.slice();
}
property var historyList: []
readonly property string historyFile: Paths.strip(Paths.cache) + "/notification_history.json"
readonly property string imageCacheDir: Paths.strip(Paths.cache) + "/notification_images"
@@ -548,6 +565,7 @@ Singleton {
function onOverlayOpen() {
popupsDisabled = true;
markNotificationsSeen();
addGate.stop();
addGateBusy = false;
@@ -564,6 +582,7 @@ Singleton {
function onOverlayClose() {
popupsDisabled = false;
markNotificationsSeen();
processQueue();
}
@@ -11991,6 +11991,33 @@
"icon": "touch_app",
"description": "Click expands only on an intentional press. Hybrid peeks the current compact face on hover"
},
{
"section": "dankIslandNotificationBadgeClearOnOpen",
"label": "Clear Badge on Open",
"tabIndex": 46,
"category": "Dank Island",
"keywords": [
"active",
"activities",
"alert",
"alerts",
"badge",
"clear",
"clears",
"dank",
"dismiss",
"dynamic island",
"home",
"island",
"keeps",
"notif",
"notifications",
"notifs",
"open",
"unread"
],
"description": "Clears the badge on open but keeps notifications active"
},
{
"section": "dankIslandHomeCompactTight",
"label": "Compact Clock Pill",
@@ -12369,6 +12396,31 @@
"icon": "animation",
"description": "Apply island geometry changes immediately without spring overshoot"
},
{
"section": "dankIslandNotifications",
"label": "Notifications",
"tabIndex": 46,
"category": "Dank Island",
"keywords": [
"activities",
"alert",
"alerts",
"arrival",
"click",
"dank",
"default",
"dynamic island",
"expand",
"hover",
"island",
"notif",
"notifications",
"notifs",
"size"
],
"icon": "notifications",
"description": "Expand notifications by default instead of click or hover"
},
{
"section": "dankIslandHoverOpenDelay",
"label": "Open Delay",