From 0990b43a433b868271fa4ffc2f731d305deb084f Mon Sep 17 00:00:00 2001 From: Lichie <90825386+lichie567@users.noreply.github.com> Date: Wed, 20 May 2026 08:43:14 -0700 Subject: [PATCH] feat(FocusedWindow): Improve content width calculation and add size options (#2444) * use RowLayout in focusedapp widget for better width calculation * Add context menu with additional size options for focused app widget --- quickshell/Common/SettingsData.qml | 1 + quickshell/Common/settings/SettingsSpec.js | 1 + .../Modules/DankBar/Widgets/FocusedApp.qml | 37 +-- quickshell/Modules/Settings/WidgetsTab.qml | 39 +++- .../Modules/Settings/WidgetsTabSection.qml | 218 +++++++++++++++++- 5 files changed, 266 insertions(+), 30 deletions(-) diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 9cf57db0..64b2ab9b 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -394,6 +394,7 @@ Singleton { property string audioScrollMode: "volume" property int audioWheelScrollAmount: 5 property bool clockCompactMode: false + property int focusedWindowSize: 1 property bool focusedWindowCompactMode: false property bool runningAppsCompactMode: true property int barMaxVisibleApps: 0 diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index 9ff84901..51c7d67f 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -153,6 +153,7 @@ var SPEC = { audioWheelScrollAmount: { def: 5 }, clockCompactMode: { def: false }, focusedWindowCompactMode: { def: false }, + focusedWindowSize: { def: 1 }, runningAppsCompactMode: { def: true }, barMaxVisibleApps: { def: 0 }, barMaxVisibleRunningApps: { def: 0 }, diff --git a/quickshell/Modules/DankBar/Widgets/FocusedApp.qml b/quickshell/Modules/DankBar/Widgets/FocusedApp.qml index 1edef1ba..76680a92 100644 --- a/quickshell/Modules/DankBar/Widgets/FocusedApp.qml +++ b/quickshell/Modules/DankBar/Widgets/FocusedApp.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Effects +import QtQuick.Layouts import Quickshell import Quickshell.Wayland import Quickshell.Widgets @@ -14,9 +15,20 @@ BasePill { property var widgetData: null property bool compactMode: widgetData?.focusedWindowCompactMode !== undefined ? widgetData.focusedWindowCompactMode : SettingsData.focusedWindowCompactMode - property int availableWidth: 400 - readonly property int maxNormalWidth: 456 - readonly property int maxCompactWidth: 288 + readonly property int maxWidth: { + const size = widgetData?.focusedWindowSize !== undefined ? widgetData.focusedWindowSize : SettingsData.focusedWindowSize; + switch (size) { + case 0: + return 288; + case 2: + return 656; + case 3: + return 856; + default: + return 456; + } + } + property int availableWidth: maxWidth property Toplevel activeWindow: null property var activeDesktopEntry: null property bool isHovered: mouseArea.containsMouse @@ -171,8 +183,7 @@ BasePill { return 0; if (root.isVerticalOrientation) return root.widgetThickness - root.horizontalPadding * 2; - const baseWidth = contentRow.implicitWidth; - return compactMode ? Math.min(baseWidth, maxCompactWidth - root.horizontalPadding * 2) : Math.min(baseWidth, maxNormalWidth - root.horizontalPadding * 2); + return contentRow.implicitWidth; } implicitHeight: root.widgetThickness - root.horizontalPadding * 2 clip: false @@ -222,7 +233,7 @@ BasePill { color: Theme.widgetTextColor } - Row { + RowLayout { id: contentRow anchors.centerIn: parent spacing: Theme.spacingS @@ -231,24 +242,23 @@ BasePill { StyledText { id: appText text: { - if (!activeWindow || !activeWindow.appId) + if (compactMode || !activeWindow || !activeWindow.appId) return ""; return Paths.getAppName(activeWindow.appId, activeDesktopEntry); } font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText) color: Theme.widgetTextColor - anchors.verticalCenter: parent.verticalCenter elide: Text.ElideRight maximumLineCount: 1 - width: Math.min(implicitWidth, compactMode ? 80 : 180) - visible: !compactMode && text.length > 0 + Layout.maximumWidth: compactMode ? 80 : 180 + visible: text.length > 0 } StyledText { - text: "•" + id: appSeparator + text: compactMode ? "" : "•" font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText) color: Theme.outlineButton - anchors.verticalCenter: parent.verticalCenter visible: !compactMode && appText.text && titleText.text } @@ -276,10 +286,9 @@ BasePill { } font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText) color: Theme.widgetTextColor - anchors.verticalCenter: parent.verticalCenter elide: Text.ElideRight maximumLineCount: 1 - width: Math.min(implicitWidth, compactMode ? 280 : 250) + Layout.maximumWidth: maxWidth - appText.implicitWidth - appSeparator.implicitWidth visible: text.length > 0 } } diff --git a/quickshell/Modules/Settings/WidgetsTab.qml b/quickshell/Modules/Settings/WidgetsTab.qml index 46ac68c2..a489e39c 100644 --- a/quickshell/Modules/Settings/WidgetsTab.qml +++ b/quickshell/Modules/Settings/WidgetsTab.qml @@ -431,7 +431,7 @@ Item { "id": widget.id, "enabled": widget.enabled }; - var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion", "hideWhenIdle"]; + var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowSize", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion", "hideWhenIdle"]; for (var i = 0; i < keys.length; i++) { if (widget[keys[i]] !== undefined) result[keys[i]] = widget[keys[i]]; @@ -625,9 +625,6 @@ Item { var newWidget = cloneWidgetData(widget); switch (widgetId) { - case "music": - newWidget.mediaSize = value; - break; case "clock": newWidget.clockCompactMode = value; break; @@ -647,6 +644,29 @@ Item { setWidgetsForSection(sectionId, widgets); } + function handleWidgetSizeChanged(sectionId, widgetId, value) { + var widgets = getWidgetsForSection(sectionId).slice(); + for (var i = 0; i < widgets.length; i++) { + var widget = widgets[i]; + var currentId = typeof widget === "string" ? widget : widget.id; + if (currentId !== widgetId) + continue; + + var newWidget = cloneWidgetData(widget); + switch (widgetId) { + case "music": + newWidget.mediaSize = value; + break; + case "focusedWindow": + newWidget.focusedWindowSize = value; + break; + } + widgets[i] = newWidget; + break; + } + setWidgetsForSection(sectionId, widgets); + } + function getItemsForSection(sectionId) { var widgets = []; var widgetData = getWidgetsForSection(sectionId); @@ -708,6 +728,8 @@ Item { item.clockCompactMode = widget.clockCompactMode; if (widget.focusedWindowCompactMode !== undefined) item.focusedWindowCompactMode = widget.focusedWindowCompactMode; + if (widget.focusedWindowSize !== undefined) + item.focusedWindowSize = widget.focusedWindowSize; if (widget.runningAppsCompactMode !== undefined) item.runningAppsCompactMode = widget.runningAppsCompactMode; if (widget.runningAppsGroupByApp !== undefined) @@ -1014,6 +1036,9 @@ Item { onCompactModeChanged: (widgetId, value) => { widgetsTab.handleCompactModeChanged(sectionId, widgetId, value); } + onWidgetSizeChanged: (widgetId, value) => { + widgetsTab.handleWidgetSizeChanged(sectionId, widgetId, value); + } onOverflowSettingChanged: (sectionId, widgetIndex, settingName, value) => { widgetsTab.handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value); } @@ -1084,6 +1109,9 @@ Item { onCompactModeChanged: (widgetId, value) => { widgetsTab.handleCompactModeChanged(sectionId, widgetId, value); } + onWidgetSizeChanged: (widgetId, value) => { + widgetsTab.handleWidgetSizeChanged(sectionId, widgetId, value); + } onOverflowSettingChanged: (sectionId, widgetIndex, settingName, value) => { widgetsTab.handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value); } @@ -1154,6 +1182,9 @@ Item { onCompactModeChanged: (widgetId, value) => { widgetsTab.handleCompactModeChanged(sectionId, widgetId, value); } + onWidgetSizeChanged: (widgetId, value) => { + widgetsTab.handleWidgetSizeChanged(sectionId, widgetId, value); + } onOverflowSettingChanged: (sectionId, widgetIndex, settingName, value) => { widgetsTab.handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value); } diff --git a/quickshell/Modules/Settings/WidgetsTabSection.qml b/quickshell/Modules/Settings/WidgetsTabSection.qml index e02245e4..4576f3ff 100644 --- a/quickshell/Modules/Settings/WidgetsTabSection.qml +++ b/quickshell/Modules/Settings/WidgetsTabSection.qml @@ -24,6 +24,7 @@ Column { signal removeWidget(string sectionId, int widgetIndex) signal spacerSizeChanged(string sectionId, int widgetIndex, int newSize) signal compactModeChanged(string widgetId, var value) + signal widgetSizeChanged(string widgetId, var value) signal gpuSelectionChanged(string sectionId, int widgetIndex, int selectedIndex) signal diskMountSelectionChanged(string sectionId, int widgetIndex, string mountPath) signal controlCenterSettingChanged(string sectionId, int widgetIndex, string settingName, bool value) @@ -41,7 +42,7 @@ Column { "id": widget.id, "enabled": widget.enabled }; - var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion"]; + var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowSize", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion"]; for (var i = 0; i < keys.length; i++) { if (widget[keys[i]] !== undefined) result[keys[i]] = widget[keys[i]]; @@ -390,6 +391,39 @@ Column { } } + DankActionButton { + id: focusedWindowMenuButton + buttonSize: 32 + visible: modelData.id === "focusedWindow" + iconName: "more_vert" + iconSize: 18 + iconColor: Theme.outline + onClicked: { + focusedWindowContextMenu.widgetData = modelData; + focusedWindowContextMenu.sectionId = root.sectionId; + focusedWindowContextMenu.widgetIndex = index; + + var buttonPos = focusedWindowMenuButton.mapToItem(root, 0, 0); + var popupWidth = focusedWindowContextMenu.width; + var popupHeight = focusedWindowContextMenu.height; + + var xPos = buttonPos.x - popupWidth - Theme.spacingS; + if (xPos < 0) + xPos = buttonPos.x + focusedWindowMenuButton.width + Theme.spacingS; + + var yPos = buttonPos.y - popupHeight / 2 + focusedWindowMenuButton.height / 2; + if (yPos < 0) { + yPos = Theme.spacingS; + } else if (yPos + popupHeight > root.height) { + yPos = root.height - popupHeight - Theme.spacingS; + } + + focusedWindowContextMenu.x = xPos; + focusedWindowContextMenu.y = yPos; + focusedWindowContextMenu.open(); + } + } + DankActionButton { id: musicMenuButton visible: modelData.id === "music" @@ -458,19 +492,17 @@ Column { Row { spacing: Theme.spacingXS - visible: modelData.id === "clock" || modelData.id === "focusedWindow" || modelData.id === "keyboard_layout_name" || modelData.id === "appsDock" || modelData.id === "systemTray" + visible: modelData.id === "clock" || modelData.id === "keyboard_layout_name" || modelData.id === "appsDock" || modelData.id === "systemTray" DankActionButton { id: compactModeButton buttonSize: 28 - visible: modelData.id === "clock" || modelData.id === "focusedWindow" || modelData.id === "keyboard_layout_name" + visible: modelData.id === "clock" || modelData.id === "keyboard_layout_name" iconName: { const isCompact = (() => { switch (modelData.id) { case "clock": return modelData.clockCompactMode !== undefined ? modelData.clockCompactMode : SettingsData.clockCompactMode; - case "focusedWindow": - return modelData.focusedWindowCompactMode !== undefined ? modelData.focusedWindowCompactMode : SettingsData.focusedWindowCompactMode; case "keyboard_layout_name": return modelData.keyboardLayoutNameCompactMode !== undefined ? modelData.keyboardLayoutNameCompactMode : SettingsData.keyboardLayoutNameCompactMode; default: @@ -485,8 +517,6 @@ Column { switch (modelData.id) { case "clock": return modelData.clockCompactMode !== undefined ? modelData.clockCompactMode : SettingsData.clockCompactMode; - case "focusedWindow": - return modelData.focusedWindowCompactMode !== undefined ? modelData.focusedWindowCompactMode : SettingsData.focusedWindowCompactMode; case "keyboard_layout_name": return modelData.keyboardLayoutNameCompactMode !== undefined ? modelData.keyboardLayoutNameCompactMode : SettingsData.keyboardLayoutNameCompactMode; default: @@ -500,8 +530,6 @@ Column { switch (modelData.id) { case "clock": return modelData.clockCompactMode !== undefined ? modelData.clockCompactMode : SettingsData.clockCompactMode; - case "focusedWindow": - return modelData.focusedWindowCompactMode !== undefined ? modelData.focusedWindowCompactMode : SettingsData.focusedWindowCompactMode; case "keyboard_layout_name": return modelData.keyboardLayoutNameCompactMode !== undefined ? modelData.keyboardLayoutNameCompactMode : SettingsData.keyboardLayoutNameCompactMode; default: @@ -515,8 +543,6 @@ Column { switch (modelData.id) { case "clock": return modelData.clockCompactMode !== undefined ? modelData.clockCompactMode : SettingsData.clockCompactMode; - case "focusedWindow": - return modelData.focusedWindowCompactMode !== undefined ? modelData.focusedWindowCompactMode : SettingsData.focusedWindowCompactMode; case "keyboard_layout_name": return modelData.keyboardLayoutNameCompactMode !== undefined ? modelData.keyboardLayoutNameCompactMode : SettingsData.keyboardLayoutNameCompactMode; default: @@ -1067,6 +1093,174 @@ Column { } } + Popup { + id: focusedWindowContextMenu + + property var widgetData: null + property string sectionId: "" + property int widgetIndex: -1 + + width: 180 + height: focusedWindowMenuColumn.implicitHeight + Theme.spacingS * 2 + padding: 0 + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + background: Rectangle { + color: Theme.surfaceContainer + radius: Theme.cornerRadius + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) + border.width: 0 + } + + contentItem: Item { + Column { + id: focusedWindowMenuColumn + anchors.fill: parent + anchors.margins: Theme.spacingS + spacing: 2 + + Rectangle { + width: parent.width + height: 32 + radius: Theme.cornerRadius + color: fwCompactArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + + Row { + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "zoom_in" + size: 16 + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: I18n.tr("Compact") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + font.weight: Font.Normal + anchors.verticalCenter: parent.verticalCenter + } + } + + DankToggle { + id: fwCompactToggle + anchors.right: parent.right + anchors.rightMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + width: 40 + height: 20 + checked: focusedWindowContextMenu.currentWidgetData?.focusedWindowCompactMode ?? SettingsData.focusedWindowCompactMode + onToggled: { + root.overflowSettingChanged(focusedWindowContextMenu.sectionId, focusedWindowContextMenu.widgetIndex, "focuswedWindowCompactMode", toggled); + } + } + + MouseArea { + id: fwCompactArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onPressed: { + fwCompactToggle.checked = !fwCompactToggle.checked; + root.overflowSettingChanged(focusedWindowContextMenu.sectionId, focusedWindowContextMenu.widgetIndex, "focusedWindowCompactMode", fwCompactToggle.checked); + } + } + } + + Repeater { + model: [ + { + icon: "photo_size_select_small", + label: I18n.tr("Small"), + sizeValue: 0 + }, + { + icon: "photo_size_select_actual", + label: I18n.tr("Medium"), + sizeValue: 1 + }, + { + icon: "photo_size_select_large", + label: I18n.tr("Large"), + sizeValue: 2 + }, + { + icon: "fit_screen", + label: I18n.tr("Largest"), + sizeValue: 3 + } + ] + + delegate: Rectangle { + required property var modelData + required property int index + + function isSelected() { + var wd = focusedWindowContextMenu.widgetData; + var currentSize = wd?.focusedWindowSize ?? SettingsData.focusedWindowSize; + return currentSize === modelData.sizeValue; + } + + width: focusedWindowMenuColumn.width + height: Math.max(18, Theme.fontSizeSmall) + Theme.spacingM * 2 + radius: Theme.cornerRadius + color: focusedWindowOptionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + + Row { + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: modelData.icon + size: 18 + color: isSelected() ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: modelData.label + font.pixelSize: Theme.fontSizeSmall + font.weight: isSelected() ? Font.Medium : Font.Normal + color: isSelected() ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + } + + DankIcon { + anchors.right: parent.right + anchors.rightMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + name: "check" + size: 16 + color: Theme.primary + visible: isSelected() + } + + MouseArea { + id: focusedWindowOptionArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + root.widgetSizeChanged("focusedWindow", modelData.sizeValue); + focusedWindowContextMenu.close(); + } + } + } + } + } + } + } + Popup { id: diskUsageContextMenu @@ -2144,7 +2338,7 @@ Column { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.compactModeChanged("music", modelData.sizeValue); + root.widgetSizeChanged("music", modelData.sizeValue); musicContextMenu.close(); } }