From 23e8bb5935f29c99d95ea2bdc64b79641ccc9ae0 Mon Sep 17 00:00:00 2001 From: bbedward Date: Sun, 4 Oct 2026 17:49:25 -0400 Subject: [PATCH] dash: rebuild the calendar event editor on settings rows notifications: paint the popup and toast surface once --- dank-qml-common | 2 +- quickshell/Modules/DankDash/DashMetrics.qml | 1 - .../DankDash/Overview/CalendarEventEditor.qml | 266 +++++++----------- .../Overview/CalendarOverviewCard.qml | 16 +- quickshell/Modules/DankDash/OverviewTab.qml | 4 + quickshell/Modules/Lock/LockWidgetItem.qml | 11 +- quickshell/Modules/Lock/LockWidgetLayer.qml | 23 +- .../Modules/Lock/Widgets/LockAuthWidget.qml | 2 + .../Lock/Widgets/LockNotificationsWidget.qml | 256 ++++++++++------- .../Notifications/Popup/NotificationPopup.qml | 4 +- quickshell/Modules/Toast.qml | 7 +- 11 files changed, 310 insertions(+), 282 deletions(-) diff --git a/dank-qml-common b/dank-qml-common index 07d634a3f..f5522736e 160000 --- a/dank-qml-common +++ b/dank-qml-common @@ -1 +1 @@ -Subproject commit 07d634a3fc731afdcd309d385f3134ba8c22b37c +Subproject commit f5522736ed2691484b5aa455bb1447c2b42415f4 diff --git a/quickshell/Modules/DankDash/DashMetrics.qml b/quickshell/Modules/DankDash/DashMetrics.qml index b25b2dafb..db8c85550 100644 --- a/quickshell/Modules/DankDash/DashMetrics.qml +++ b/quickshell/Modules/DankDash/DashMetrics.qml @@ -190,7 +190,6 @@ Singleton { readonly property real eventActionIconSize: Theme.iconSizeSmall readonly property real taskInputHeight: Theme.buttonHeightS readonly property real sheetWidth: 400 - readonly property real sheetFormHeight: 300 readonly property real mediaCardMargin: Theme.spacingL readonly property real mediaArtSize: 150 diff --git a/quickshell/Modules/DankDash/Overview/CalendarEventEditor.qml b/quickshell/Modules/DankDash/Overview/CalendarEventEditor.qml index 97cd5ba7b..98f164400 100644 --- a/quickshell/Modules/DankDash/Overview/CalendarEventEditor.qml +++ b/quickshell/Modules/DankDash/Overview/CalendarEventEditor.qml @@ -2,7 +2,7 @@ import QtQuick import qs.Common import qs.Services import qs.Widgets -import qs.Modules.DankDash +import qs.Modules.Settings.Widgets Column { id: root @@ -20,8 +20,10 @@ Column { property string fTitle: "" property bool fAllDay: false property date fDate: initialDate - property string fStart: "10:00" - property string fEnd: "11:00" + property int fStartHour: 10 + property int fStartMinute: 0 + property int fEndHour: 11 + property int fEndMinute: 0 property string fLocation: "" property string fDescription: "" property string fCalendarId: "" @@ -35,20 +37,6 @@ Column { spacing: Theme.spacingM - function _parseTime(value) { - const m = value.trim().match(/^(\d{1,2}):(\d{2})$/); - if (!m) - return null; - const h = parseInt(m[1]); - const min = parseInt(m[2]); - if (h > 23 || min > 59) - return null; - return { - "h": h, - "m": min - }; - } - function _isoFromDateTime(dateObj, h, m) { const d = new Date(dateObj); d.setHours(h, m, 0, 0); @@ -93,14 +81,8 @@ Column { startIso = _allDayIso(fDate, 0); endIso = _allDayIso(fDate, 1); } else { - const s = _parseTime(fStart); - const e = _parseTime(fEnd); - if (!s || !e) { - errorText = I18n.tr("Use HH:MM time format"); - return; - } - startIso = _isoFromDateTime(fDate, s.h, s.m); - endIso = _isoFromDateTime(fDate, e.h, e.m); + startIso = _isoFromDateTime(fDate, fStartHour, fStartMinute); + endIso = _isoFromDateTime(fDate, fEndHour, fEndMinute); if (new Date(endIso).getTime() <= new Date(startIso).getTime()) { errorText = I18n.tr("End must be after start"); return; @@ -145,8 +127,10 @@ Column { fTitle = eventData.title || ""; fAllDay = !!eventData.allDay; fDate = eventData.start; - fStart = Qt.formatTime(eventData.start, "HH:mm"); - fEnd = Qt.formatTime(eventData.end, "HH:mm"); + fStartHour = eventData.start.getHours(); + fStartMinute = eventData.start.getMinutes(); + fEndHour = eventData.end.getHours(); + fEndMinute = eventData.end.getMinutes(); fLocation = eventData.location || ""; fDescription = eventData.description || ""; fCalendarId = eventData.calendarId || ""; @@ -154,149 +138,101 @@ Column { fReminder = eventData.reminders[0].minutes; } - DankFlickable { + SettingsGroup { width: parent.width - height: Math.min(DashMetrics.sheetFormHeight, form.implicitHeight) - showScrollBar: false - contentWidth: width - contentHeight: form.implicitHeight - clip: true + slotColor: Theme.chipSurface - Column { - id: form - width: parent.width - spacing: Theme.spacingS + SettingsTextFieldRow { + text: I18n.tr("Title") + leftIconName: "title" + placeholderText: I18n.tr("Event title") + value: root.fTitle + onValueEdited: value => root.fTitle = value + onAccepted: root.save() + } - DankTextField { - width: parent.width - labelText: I18n.tr("Title") - leftIconName: "title" - leftIconSize: Theme.iconSizeSmall - placeholderText: I18n.tr("Event title") - text: root.fTitle - onTextChanged: root.fTitle = text + SettingsToggleRow { + text: I18n.tr("All day") + checked: root.fAllDay + onToggled: checked => root.fAllDay = checked + } + + SettingsRow { + title: I18n.tr("Date") + subtitle: Qt.formatDate(root.fDate, "ddd, MMM d yyyy") + subtitleColor: Theme.surfaceText + + DankActionButton { + iconName: I18n.isRtl ? "chevron_right" : "chevron_left" + Accessible.name: I18n.tr("Previous") + onClicked: root.shiftDate(-1) } - DankToggle { - width: parent.width - text: I18n.tr("All day") - checked: root.fAllDay - onToggled: checked => root.fAllDay = checked - } - - Row { - width: parent.width - spacing: Theme.spacingXS - - DankActionButton { - id: prevDay - iconName: I18n.isRtl ? "chevron_right" : "chevron_left" - Accessible.name: I18n.tr("Previous") - iconSize: Theme.iconSizeSmall - onClicked: root.shiftDate(-1) - } - - StyledText { - width: parent.width - prevDay.width - nextDay.width - parent.spacing * 2 - text: Qt.formatDate(root.fDate, "ddd, MMM d yyyy") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - height: Theme.buttonHeightXS - } - - DankActionButton { - id: nextDay - iconName: I18n.isRtl ? "chevron_left" : "chevron_right" - Accessible.name: I18n.tr("Next") - iconSize: Theme.iconSizeSmall - onClicked: root.shiftDate(1) - } - } - - Row { - width: parent.width - spacing: Theme.spacingS - visible: !root.fAllDay - - DankTextField { - width: (parent.width - Theme.spacingS) / 2 - labelText: I18n.tr("Start") - leftIconName: "schedule" - leftIconSize: Theme.iconSizeSmall - placeholderText: "HH:MM" - text: root.fStart - onTextChanged: root.fStart = text - } - - DankTextField { - width: (parent.width - Theme.spacingS) / 2 - labelText: I18n.tr("End") - placeholderText: "HH:MM" - text: root.fEnd - onTextChanged: root.fEnd = text - } - } - - DankDropdown { - width: parent.width - text: I18n.tr("Calendar") - transientSurfaceTracker: root.transientSurfaceTracker - options: root._cals.map(c => c.name) - currentValue: root._calendarName(root.fCalendarId) - onValueChanged: value => { - for (let i = 0; i < root._cals.length; i++) { - if (root._cals[i].name === value) { - root.fCalendarId = root._cals[i].id; - return; - } - } - } - } - - DankDropdown { - width: parent.width - text: I18n.tr("Reminder", "noun, calendar event reminder time dropdown label") - transientSurfaceTracker: root.transientSurfaceTracker - options: root._remLabels - currentValue: root._remLabels[Math.max(0, root._remMins.indexOf(root.fReminder))] - onValueChanged: value => { - const idx = root._remLabels.indexOf(value); - if (idx >= 0) - root.fReminder = root._remMins[idx]; - } - } - - DankTextField { - width: parent.width - labelText: I18n.tr("Location", "calendar event venue field label", true) - leftIconName: "place" - leftIconSize: Theme.iconSizeSmall - placeholderText: I18n.tr("Add location") - text: root.fLocation - onTextChanged: root.fLocation = text - } - - DankTextField { - width: parent.width - labelText: I18n.tr("Notes", "noun, calendar event notes field label") - leftIconName: "notes" - leftIconSize: Theme.iconSizeSmall - placeholderText: I18n.tr("Add notes") - text: root.fDescription - onTextChanged: root.fDescription = text + DankActionButton { + iconName: I18n.isRtl ? "chevron_left" : "chevron_right" + Accessible.name: I18n.tr("Next") + onClicked: root.shiftDate(1) } } - } - StyledText { - width: parent.width - text: root.errorText - visible: root.errorText !== "" - font.pixelSize: Theme.fontSizeSmall - color: Theme.error - wrapMode: Text.WordWrap + SettingsTimeRow { + visible: !root.fAllDay + is24Hour: SettingsData.use24HourClock + startTitle: I18n.tr("Start") + startHour: root.fStartHour + startMinute: root.fStartMinute + endTitle: I18n.tr("End") + endHour: root.fEndHour + endMinute: root.fEndMinute + onStartChanged: (hour, minute) => { + root.fStartHour = hour; + root.fStartMinute = minute; + } + onEndChanged: (hour, minute) => { + root.fEndHour = hour; + root.fEndMinute = minute; + } + } + + SettingsDropdownRow { + text: I18n.tr("Calendar") + transientSurfaceTracker: root.transientSurfaceTracker + options: root._cals.map(c => c.name) + currentValue: root._calendarName(root.fCalendarId) + onValueChanged: value => { + const cal = root._cals.find(c => c.name === value); + if (cal) + root.fCalendarId = cal.id; + } + } + + SettingsDropdownRow { + text: I18n.tr("Reminder", "noun, calendar event reminder time dropdown label") + transientSurfaceTracker: root.transientSurfaceTracker + options: root._remLabels + currentValue: root._remLabels[Math.max(0, root._remMins.indexOf(root.fReminder))] + onValueChanged: value => { + const idx = root._remLabels.indexOf(value); + if (idx >= 0) + root.fReminder = root._remMins[idx]; + } + } + + SettingsTextFieldRow { + text: I18n.tr("Location", "calendar event venue field label", true) + leftIconName: "place" + placeholderText: I18n.tr("Add location") + value: root.fLocation + onValueEdited: value => root.fLocation = value + } + + SettingsTextFieldRow { + text: I18n.tr("Notes", "noun, calendar event notes field label") + leftIconName: "notes" + placeholderText: I18n.tr("Add notes") + value: root.fDescription + onValueEdited: value => root.fDescription = value + } } Row { @@ -307,16 +243,14 @@ Column { DankButton { text: root.saving ? I18n.tr("Saving...") : I18n.tr("Save") iconName: "check" - buttonHeight: Theme.buttonHeightS - backgroundColor: Theme.primary - textColor: Theme.onPrimary enabled: !root.saving onClicked: root.save() } DankButton { text: I18n.tr("Cancel") - buttonHeight: Theme.buttonHeightS + backgroundColor: "transparent" + textColor: Theme.primary onClicked: root.closeRequested() } } diff --git a/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml b/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml index dcce558a7..8394cadbd 100644 --- a/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml +++ b/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml @@ -596,6 +596,13 @@ Card { } } + StateLayer { + cornerRadius: taskItem.radius + stateColor: taskItem.accentColor + disabled: !taskItem.modelData || taskItem.isEditing || !root.interactive + onClicked: taskItem.activate() + } + FocusRing {} onIndexChanged: visualIndex = index @@ -777,15 +784,6 @@ Card { } } - StateLayer { - anchors.leftMargin: taskItem.leadingWidth - anchors.rightMargin: taskItem.trailingWidth - cornerRadius: taskItem.radius - stateColor: taskItem.accentColor - disabled: !taskItem.modelData || taskItem.isEditing || !root.interactive - onClicked: taskItem.activate() - } - DankActionButton { anchors.right: parent.right anchors.rightMargin: Theme.spacingS diff --git a/quickshell/Modules/DankDash/OverviewTab.qml b/quickshell/Modules/DankDash/OverviewTab.qml index 7c9d8ceff..169bd7ab7 100644 --- a/quickshell/Modules/DankDash/OverviewTab.qml +++ b/quickshell/Modules/DankDash/OverviewTab.qml @@ -181,6 +181,8 @@ FocusScope { panelWidth: DashMetrics.sheetWidth iconName: "edit_calendar" title: eventData ? I18n.tr("Edit event") : I18n.tr("New event") + statusText: editorLoader.item?.errorText ?? "" + statusColor: Theme.error onDismissed: { eventData = null; @@ -188,6 +190,8 @@ FocusScope { } Loader { + id: editorLoader + width: parent.width active: editorSheet.visible diff --git a/quickshell/Modules/Lock/LockWidgetItem.qml b/quickshell/Modules/Lock/LockWidgetItem.qml index ccdc3f5d3..256dfb73e 100644 --- a/quickshell/Modules/Lock/LockWidgetItem.qml +++ b/quickshell/Modules/Lock/LockWidgetItem.qml @@ -87,6 +87,13 @@ Item { return Qt.point(guideX ?? snapped.x, guideY ?? snapped.y); } + function pinPosition() { + geometry.savePosition(geometry.widgetX, geometry.widgetY); + SettingsData.updateDesktopWidgetInstanceConfig(instanceId, { + autoPosition: false + }); + } + function commitPosition(finalX, finalY) { if (stock && finalX === stock.x && finalY === stock.y) { SessionData.resetDesktopWidgetInstanceGeometry(instanceId, ["x", "y"]); @@ -223,8 +230,10 @@ Item { geometry.dragOverrideH = next.height; } onResizeEnded: { - resizing = false; geometry.saveSize(geometry.dragOverrideW, geometry.dragOverrideH); + if (root.automaticPlacement) + root.pinPosition(); + resizing = false; geometry.clearDragOverrides(); } onResizeCanceled: { diff --git a/quickshell/Modules/Lock/LockWidgetLayer.qml b/quickshell/Modules/Lock/LockWidgetLayer.qml index f66a78c6c..c9da0c770 100644 --- a/quickshell/Modules/Lock/LockWidgetLayer.qml +++ b/quickshell/Modules/Lock/LockWidgetLayer.qml @@ -95,8 +95,20 @@ FocusScope { readonly property real edgeInset: Theme.spacingXL * 2 property bool completed: false property var lockedPositions: ({}) + // Snapshot taken when a drag or resize starts so the auto-placed clock cannot jump under the pointer. + property var heldPlacement: ({}) readonly property string backgroundKey: LockPlacementService.backgroundKey(screenName, width, height) - readonly property var autoPositions: editMode && completed ? currentPlacement() : lockedPositions + readonly property var autoPositions: { + if (!editMode || !completed) + return lockedPositions; + if (interactingItem) + return heldPlacement; + return currentPlacement(); + } + onInteractingItemChanged: { + if (interactingItem) + heldPlacement = currentPlacement(); + } function currentPlacement() { return LockPlacementService.layoutFor(screenName, LockPlacementService.sampleFor(screenName, backgroundKey), placementKey, placeClocks); @@ -218,6 +230,15 @@ FocusScope { return null; } + readonly property var interactingItem: { + for (let i = 0; i < repeater.count; i++) { + const item = repeater.itemAt(i); + if (item && item.interacting) + return item; + } + return null; + } + Rectangle { x: (parent.width - width) / 2 width: Theme.dividerWidth diff --git a/quickshell/Modules/Lock/Widgets/LockAuthWidget.qml b/quickshell/Modules/Lock/Widgets/LockAuthWidget.qml index d57a4c9d4..c4bc6e1cc 100644 --- a/quickshell/Modules/Lock/Widgets/LockAuthWidget.qml +++ b/quickshell/Modules/Lock/Widgets/LockAuthWidget.qml @@ -842,6 +842,8 @@ Item { } if (root.pam.passwd.active) return I18n.tr("Authenticating...", "lock screen status text while the password is checked"); + if (root.passwordVisibility !== "always") + return ""; return I18n.tr("Password", "lock screen password field placeholder") + "…"; } color: { diff --git a/quickshell/Modules/Lock/Widgets/LockNotificationsWidget.qml b/quickshell/Modules/Lock/Widgets/LockNotificationsWidget.qml index 6586254f5..2db5c01b0 100644 --- a/quickshell/Modules/Lock/Widgets/LockNotificationsWidget.qml +++ b/quickshell/Modules/Lock/Widgets/LockNotificationsWidget.qml @@ -16,7 +16,9 @@ Item { readonly property int notificationMode: cfg.mode ?? 1 readonly property bool resizable: true readonly property real minWidth: Theme.listItemTwoLineHeight * 2 - readonly property real minHeight: Theme.listItemHeight + readonly property real minHeight: Theme.buttonHeightXS + readonly property real defaultWidth: LockMetrics.notificationCardWidth + readonly property real defaultHeight: fullContent ? LockMetrics.notificationMaxHeight : Theme.buttonHeightS readonly property var notifications: NotificationService.groupedNotifications readonly property int totalCount: { @@ -46,53 +48,98 @@ Item { readonly property bool editPlaceholder: !hasNotifications && (lockHost?.demoMode ?? false) readonly property bool chips: notificationMode === 2 && !editPlaceholder + readonly property bool fullContent: notificationMode === 3 && !editPlaceholder readonly property var visibleGroups: appNameGroups.slice(0, LockMetrics.notificationLimit) - readonly property int hiddenGroups: Math.max(0, appNameGroups.length - LockMetrics.notificationLimit) - implicitWidth: chips ? contentLoader.implicitWidth : LockMetrics.notificationCardWidth + implicitWidth: contentLoader.implicitWidth implicitHeight: hasNotifications || editPlaceholder ? contentLoader.implicitHeight : 0 clip: true Loader { id: contentLoader - anchors.left: parent.left - anchors.right: root.chips ? undefined : parent.right - anchors.top: parent.top + anchors.fill: parent active: root.hasNotifications || root.editPlaceholder sourceComponent: { - if (root.notificationMode === 1 || root.editPlaceholder) - return countOnlyComponent; - return root.chips ? chipRowComponent : notificationListComponent; + if (root.chips) + return chipRowComponent; + if (root.fullContent) + return notificationListComponent; + return countOnlyComponent; + } + } + + component Chip: Rectangle { + default property alias content: chipContent.data + readonly property real contentWidth: chipContent.implicitWidth + height: Theme.buttonHeightXS + width: contentWidth + Theme.spacingM * 2 + radius: Theme.cornerRadiusM + color: Theme.notificationFloatingSurface + border.width: Theme.layerOutlineWidth + border.color: Theme.outlineVariant + + Row { + id: chipContent + anchors.centerIn: parent + spacing: Theme.spacingXS } } Component { id: chipRowComponent - Row { - spacing: Theme.spacingS + Item { + id: chipHost - Repeater { - model: root.visibleGroups + property int fitCount: root.visibleGroups.length + readonly property int hiddenCount: root.appNameGroups.length - fitCount + implicitWidth: chipRow.implicitWidth + implicitHeight: chipRow.implicitHeight - Rectangle { - id: chip + function measure() { + const widths = []; + for (let i = 0; i < chipRepeater.count; i++) + widths.push(chipRepeater.itemAt(i)?.width ?? 0); + const total = count => widths.slice(0, count).reduce((sum, w) => sum + w, 0) + Math.max(0, count - 1) * chipRow.spacing; + const overflow = count => root.appNameGroups.length > count ? moreMetrics.width + Theme.spacingM * 2 + chipRow.spacing : 0; + let count = widths.length; + while (count > 1 && total(count) + overflow(count) > width) + count--; + fitCount = count; + } - required property var modelData - readonly property string appIcon: NotificationService.notificationAppIcon(modelData.latestNotification?.appIcon || "", modelData.latestNotification?.desktopEntry || "") + onWidthChanged: Qt.callLater(measure) + Component.onCompleted: measure() - height: Theme.buttonHeightXS - width: chipRow.implicitWidth + Theme.spacingM * 2 - radius: Theme.cornerRadiusM - color: Theme.notificationFloatingSurface - border.width: Theme.layerOutlineWidth - border.color: Theme.outlineVariant - Accessible.name: modelData.appName + TextMetrics { + id: moreMetrics + font.pixelSize: Theme.fontSizeSmall + font.weight: Theme.fontWeightMedium + text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(root.appNameGroups.length) + onWidthChanged: Qt.callLater(chipHost.measure) + } - Row { - id: chipRow - anchors.centerIn: parent - spacing: Theme.spacingXS + Row { + id: chipRow + anchors.centerIn: parent + spacing: Theme.spacingS + + Repeater { + id: chipRepeater + model: root.visibleGroups + onItemAdded: Qt.callLater(chipHost.measure) + onItemRemoved: Qt.callLater(chipHost.measure) + + Chip { + id: chip + + required property var modelData + required property int index + readonly property string appIcon: NotificationService.notificationAppIcon(modelData.latestNotification?.appIcon || "", modelData.latestNotification?.desktopEntry || "") + + visible: index < chipHost.fitCount + Accessible.name: modelData.appName + onWidthChanged: Qt.callLater(chipHost.measure) Item { width: Theme.iconSizeSmall @@ -151,24 +198,17 @@ Item { } } } - } - Rectangle { - visible: root.hiddenGroups > 0 - height: Theme.buttonHeightXS - width: moreText.implicitWidth + Theme.spacingM * 2 - radius: Theme.cornerRadiusM - color: Theme.notificationFloatingSurface - border.width: Theme.layerOutlineWidth - border.color: Theme.outlineVariant + Chip { + visible: chipHost.hiddenCount > 0 - StyledText { - id: moreText - anchors.centerIn: parent - text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(root.hiddenGroups) - font.pixelSize: Theme.fontSizeSmall - font.weight: Theme.fontWeightMedium - color: Theme.onSurfaceVariant + StyledText { + anchors.verticalCenter: parent.verticalCenter + text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(chipHost.hiddenCount) + font.pixelSize: Theme.fontSizeSmall + font.weight: Theme.fontWeightMedium + color: Theme.onSurfaceVariant + } } } } @@ -177,28 +217,42 @@ Item { Component { id: countOnlyComponent - LockNotificationCard { - implicitHeight: Theme.listItemHeight - color: Theme.notificationFloatingSurface - Accessible.name: countLabel.text + Item { + implicitWidth: countPill.width + implicitHeight: countPill.height - Row { + Rectangle { + id: countPill anchors.centerIn: parent - spacing: Theme.spacingS + width: countRow.implicitWidth + Theme.spacingL * 2 + height: Theme.buttonHeightS + radius: Theme.fullRadius(width, height) + color: Theme.notificationFloatingSurface + border.width: Theme.layerOutlineWidth + border.color: Theme.outlineVariant + scale: Math.min(1, parent.width / width, parent.height / height) + Accessible.name: countLabel.text - DankIcon { - name: "notifications" - size: Theme.iconSize - color: Theme.onSurfaceVariant - anchors.verticalCenter: parent.verticalCenter - } + Row { + id: countRow + anchors.centerIn: parent + spacing: Theme.spacingS - StyledText { - id: countLabel - text: root.editPlaceholder ? I18n.tr("Notifications") : root.totalCount === 1 ? I18n.tr("1 notification") : I18n.tr("%1 notifications").arg(root.totalCount) - font.pixelSize: Theme.fontSizeMedium - color: Theme.onSurface - anchors.verticalCenter: parent.verticalCenter + DankIcon { + name: "notifications" + size: Theme.iconSizeSmall + Theme.spacingXXS + color: Theme.onSurfaceVariant + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + id: countLabel + text: root.editPlaceholder ? I18n.tr("Notifications") : root.totalCount === 1 ? I18n.tr("1 notification") : I18n.tr("%1 notifications").arg(root.totalCount) + font.pixelSize: Theme.fontSizeMedium + font.weight: Theme.fontWeightMedium + color: Theme.onSurface + anchors.verticalCenter: parent.verticalCenter + } } } } @@ -207,46 +261,52 @@ Item { Component { id: notificationListComponent - DankFlickable { - implicitHeight: Math.min(notificationColumn.implicitHeight, LockMetrics.notificationMaxHeight, Math.max(0, (root.lockHost?.height ?? 0) - (root.parent?.y ?? 0) - Theme.spacingXL)) - height: Math.min(notificationColumn.implicitHeight, root.height) - contentHeight: notificationColumn.implicitHeight - clip: true + Item { + implicitWidth: LockMetrics.notificationCardWidth + implicitHeight: Math.min(notificationColumn.implicitHeight, LockMetrics.notificationMaxHeight) - Column { - id: notificationColumn + DankFlickable { + anchors.centerIn: parent width: parent.width - spacing: Theme.groupedListGap + height: Math.min(notificationColumn.implicitHeight, parent.height) + contentHeight: notificationColumn.implicitHeight + clip: true - Repeater { - id: notificationRepeater - model: root.appNameGroups.slice(0, LockMetrics.notificationLimit) - - Notifications.NotificationCard { - required property var modelData - required property int index - width: notificationColumn.width - notificationData: modelData.latestNotification - groupCount: modelData.count - interactive: false - headerOnly: false - showActions: false - showDismiss: false - showClose: false - showTime: !headerOnly - animateHeight: false - firstInGroup: index === 0 - lastInGroup: index === notificationRepeater.count - 1 - } - } - - StyledText { + Column { + id: notificationColumn width: parent.width - visible: root.appNameGroups.length > LockMetrics.notificationLimit - text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(root.appNameGroups.length - LockMetrics.notificationLimit) - font.pixelSize: Theme.fontSizeSmall - color: Theme.lockScreenContentColor - horizontalAlignment: Text.AlignHCenter + spacing: Theme.groupedListGap + + Repeater { + id: notificationRepeater + model: root.visibleGroups + + Notifications.NotificationCard { + required property var modelData + required property int index + width: notificationColumn.width + notificationData: modelData.latestNotification + groupCount: modelData.count + interactive: false + headerOnly: false + showActions: false + showDismiss: false + showClose: false + showTime: !headerOnly + animateHeight: false + firstInGroup: index === 0 + lastInGroup: index === notificationRepeater.count - 1 + } + } + + StyledText { + width: parent.width + visible: root.appNameGroups.length > LockMetrics.notificationLimit + text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(root.appNameGroups.length - LockMetrics.notificationLimit) + font.pixelSize: Theme.fontSizeSmall + color: Theme.lockScreenContentColor + horizontalAlignment: Text.AlignHCenter + } } } } diff --git a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml index 01af8910c..cfe7c5e82 100644 --- a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml +++ b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml @@ -667,7 +667,7 @@ PanelWindow { anchors.fill: parent anchors.margins: content.cardInset radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : NotificationMetrics.popupRadius - color: Theme.notificationFloatingSurface + color: "transparent" HoverHandler { id: cardHoverHandler @@ -711,7 +711,7 @@ PanelWindow { dismissText: I18n.tr("Clear") animateHeight: false outerRadius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : NotificationMetrics.popupRadius - color: Theme.notificationFloatingSurface + color: "transparent" onExpandRequested: win.descriptionExpanded = !win.descriptionExpanded onCloseRequested: win.dismissPopupReliably() onDismissRequested: { diff --git a/quickshell/Modules/Toast.qml b/quickshell/Modules/Toast.qml index 1aa0895c6..bfd06690a 100644 --- a/quickshell/Modules/Toast.qml +++ b/quickshell/Modules/Toast.qml @@ -107,7 +107,7 @@ PanelWindow { y: shadowBuffer width: root.toastWidth height: root.toastHeight - color: { + readonly property color surfaceColor: { switch (ToastService.currentLevel) { case ToastService.levelError: return Theme.error; @@ -119,6 +119,7 @@ PanelWindow { return Theme.readableSurface; } } + color: "transparent" radius: Theme.windowRadius opacity: presented ? 1 : 0 @@ -452,7 +453,7 @@ PanelWindow { level: Theme.elevationLevel3 fallbackOffset: 6 targetRadius: toast.radius - targetColor: toast.color + targetColor: toast.surfaceColor shadowOpacity: Theme.elevationLevel3 && Theme.elevationLevel3.alpha !== undefined ? Theme.elevationLevel3.alpha : 0.3 shadowEnabled: Theme.elevationEnabled } @@ -480,7 +481,7 @@ PanelWindow { WindowBlur { targetWindow: root blurEnabled: root.presented - surfaceColor: toast.color + surfaceColor: toast.surfaceColor blurX: toast.x blurY: toast.y blurWidth: root.presented ? toast.width : 0