From 397a8c275d2dcf36f5eb2b73f15fca2e540934dc Mon Sep 17 00:00:00 2001 From: bbedward Date: Thu, 4 Dec 2025 14:31:20 -0500 Subject: [PATCH] settings: add IPCs to open specific settings tabs --- quickshell/DMSShellIPC.qml | 43 ++++++++++++ quickshell/Modals/Settings/SettingsModal.qml | 18 +++++ .../Modals/Settings/SettingsSidebar.qml | 50 +++++++++++++- .../BuiltinPlugins/CupsWidget.qml | 50 ++++++++------ .../ControlCenter/Details/NetworkDetail.qml | 20 +++++- quickshell/Services/PopoutService.qml | 68 ++++++++++++++++++- quickshell/Widgets/DankTooltipV2.qml | 12 +++- quickshell/Widgets/StateLayer.qml | 18 ++--- quickshell/Widgets/VpnDetailContent.qml | 12 ++++ 9 files changed, 256 insertions(+), 35 deletions(-) diff --git a/quickshell/DMSShellIPC.qml b/quickshell/DMSShellIPC.qml index 28307dd8..f0d00e27 100644 --- a/quickshell/DMSShellIPC.qml +++ b/quickshell/DMSShellIPC.qml @@ -648,6 +648,13 @@ Item { return "SETTINGS_OPEN_SUCCESS"; } + function openWith(tab: string): string { + if (!tab) + return "SETTINGS_OPEN_FAILED: No tab specified"; + PopoutService.openSettingsWithTab(tab); + return `SETTINGS_OPEN_SUCCESS: ${tab}`; + } + function close(): string { PopoutService.closeSettings(); return "SETTINGS_CLOSE_SUCCESS"; @@ -658,11 +665,47 @@ Item { return "SETTINGS_TOGGLE_SUCCESS"; } + function toggleWith(tab: string): string { + if (!tab) + return "SETTINGS_TOGGLE_FAILED: No tab specified"; + PopoutService.toggleSettingsWithTab(tab); + return `SETTINGS_TOGGLE_SUCCESS: ${tab}`; + } + function focusOrToggle(): string { PopoutService.focusOrToggleSettings(); return "SETTINGS_FOCUS_OR_TOGGLE_SUCCESS"; } + function focusOrToggleWith(tab: string): string { + if (!tab) + return "SETTINGS_FOCUS_OR_TOGGLE_FAILED: No tab specified"; + PopoutService.focusOrToggleSettingsWithTab(tab); + return `SETTINGS_FOCUS_OR_TOGGLE_SUCCESS: ${tab}`; + } + + function tabs(): string { + if (!PopoutService.settingsModal) + return "wallpaper\ntheme\ntypography\ntime_weather\nsounds\ndankbar\ndankbar_settings\ndankbar_widgets\nworkspaces\nmedia_player\nnotifications\nosd\nrunning_apps\nupdater\ndock\nlauncher\nkeybinds\ndisplays\nnetwork\nprinters\nlock_screen\npower_sleep\nplugins\nabout"; + var modal = PopoutService.settingsModal; + var ids = []; + var structure = modal.sidebar?.categoryStructure ?? []; + for (var i = 0; i < structure.length; i++) { + var cat = structure[i]; + if (cat.separator) + continue; + if (cat.id) + ids.push(cat.id); + if (cat.children) { + for (var j = 0; j < cat.children.length; j++) { + if (cat.children[j].id) + ids.push(cat.children[j].id); + } + } + } + return ids.join("\n"); + } + function get(key: string): string { return JSON.stringify(SettingsData?.[key]); } diff --git a/quickshell/Modals/Settings/SettingsModal.qml b/quickshell/Modals/Settings/SettingsModal.qml index aba45805..7333ef0f 100644 --- a/quickshell/Modals/Settings/SettingsModal.qml +++ b/quickshell/Modals/Settings/SettingsModal.qml @@ -10,6 +10,7 @@ FloatingWindow { property alias profileBrowser: profileBrowser property alias wallpaperBrowser: wallpaperBrowser + property alias sidebar: sidebar property int currentTabIndex: 0 property bool shouldHaveFocus: visible property bool allowFocusOverride: false @@ -32,6 +33,23 @@ FloatingWindow { visible = !visible; } + function showWithTab(tabIndex: int) { + if (tabIndex >= 0) + currentTabIndex = tabIndex; + visible = true; + } + + function showWithTabName(tabName: string) { + var idx = sidebar.resolveTabIndex(tabName); + if (idx >= 0) + currentTabIndex = idx; + visible = true; + } + + function resolveTabIndex(tabName: string): int { + return sidebar.resolveTabIndex(tabName); + } + function toggleMenu() { enableAnimations = true; menuVisible = !menuVisible; diff --git a/quickshell/Modals/Settings/SettingsSidebar.qml b/quickshell/Modals/Settings/SettingsSidebar.qml index 67349bde..bb2e96e2 100644 --- a/quickshell/Modals/Settings/SettingsSidebar.qml +++ b/quickshell/Modals/Settings/SettingsSidebar.qml @@ -21,26 +21,31 @@ Rectangle { "icon": "palette", "children": [ { + "id": "wallpaper", "text": I18n.tr("Wallpaper"), "icon": "wallpaper", "tabIndex": 0 }, { + "id": "theme", "text": I18n.tr("Theme & Colors"), "icon": "format_paint", "tabIndex": 10 }, { + "id": "typography", "text": I18n.tr("Typography & Motion"), "icon": "text_fields", "tabIndex": 14 }, { + "id": "time_weather", "text": I18n.tr("Time & Weather"), "icon": "schedule", "tabIndex": 1 }, { + "id": "sounds", "text": I18n.tr("Sounds"), "icon": "volume_up", "tabIndex": 15, @@ -54,11 +59,13 @@ Rectangle { "icon": "toolbar", "children": [ { + "id": "dankbar_settings", "text": I18n.tr("Settings"), "icon": "tune", "tabIndex": 3 }, { + "id": "dankbar_widgets", "text": I18n.tr("Widgets"), "icon": "widgets", "tabIndex": 22 @@ -72,32 +79,38 @@ Rectangle { "collapsedByDefault": true, "children": [ { + "id": "workspaces", "text": I18n.tr("Workspaces"), "icon": "view_module", "tabIndex": 4 }, { + "id": "media_player", "text": I18n.tr("Media Player"), "icon": "music_note", "tabIndex": 16 }, { + "id": "notifications", "text": I18n.tr("Notifications"), "icon": "notifications", "tabIndex": 17 }, { + "id": "osd", "text": I18n.tr("On-screen Displays"), "icon": "tune", "tabIndex": 18 }, { + "id": "running_apps", "text": I18n.tr("Running Apps"), "icon": "apps", "tabIndex": 19, "hyprlandNiriOnly": true }, { + "id": "updater", "text": I18n.tr("System Updater"), "icon": "refresh", "tabIndex": 20 @@ -111,11 +124,13 @@ Rectangle { "collapsedByDefault": true, "children": [ { + "id": "dock", "text": I18n.tr("Dock"), "icon": "dock_to_bottom", "tabIndex": 5 }, { + "id": "launcher", "text": I18n.tr("Launcher"), "icon": "grid_view", "tabIndex": 9 @@ -123,7 +138,7 @@ Rectangle { ] }, { - "id": "input", + "id": "keybinds", "text": I18n.tr("Keyboard Shortcuts"), "icon": "keyboard", "tabIndex": 2, @@ -156,11 +171,13 @@ Rectangle { "collapsedByDefault": true, "children": [ { + "id": "lock_screen", "text": I18n.tr("Lock Screen"), "icon": "lock", "tabIndex": 11 }, { + "id": "power_sleep", "text": I18n.tr("Power & Sleep"), "icon": "power_settings_new", "tabIndex": 21 @@ -338,6 +355,37 @@ Rectangle { return items; } + function resolveTabIndex(name: string): int { + if (!name) + return -1; + + var normalized = name.toLowerCase().replace(/[_\-\s]/g, ""); + + for (var i = 0; i < categoryStructure.length; i++) { + var cat = categoryStructure[i]; + if (cat.separator) + continue; + + var catId = (cat.id || "").toLowerCase().replace(/[_\-\s]/g, ""); + if (catId === normalized) { + if (cat.tabIndex !== undefined) + return cat.tabIndex; + if (cat.children && cat.children.length > 0) + return cat.children[0].tabIndex; + } + + if (cat.children) { + for (var j = 0; j < cat.children.length; j++) { + var child = cat.children[j]; + var childId = (child.id || "").toLowerCase().replace(/[_\-\s]/g, ""); + if (childId === normalized) + return child.tabIndex; + } + } + } + return -1; + } + width: 270 height: parent.height color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) diff --git a/quickshell/Modules/ControlCenter/BuiltinPlugins/CupsWidget.qml b/quickshell/Modules/ControlCenter/BuiltinPlugins/CupsWidget.qml index 9b862bca..7796dfc5 100644 --- a/quickshell/Modules/ControlCenter/BuiltinPlugins/CupsWidget.qml +++ b/quickshell/Modules/ControlCenter/BuiltinPlugins/CupsWidget.qml @@ -1,6 +1,5 @@ import QtQuick import QtQuick.Layouts -import Quickshell import qs.Common import qs.Services import qs.Widgets @@ -17,20 +16,18 @@ PluginComponent { ccWidgetPrimaryText: I18n.tr("Printers") ccWidgetSecondaryText: { if (CupsService.cupsAvailable && CupsService.getPrintersNum() > 0) { - return I18n.tr("Printers: ") + CupsService.getPrintersNum() + " - " + I18n.tr("Jobs: ") + CupsService.getTotalJobsNum() + return I18n.tr("Printers: ") + CupsService.getPrintersNum() + " - " + I18n.tr("Jobs: ") + CupsService.getTotalJobsNum(); } else { if (!CupsService.cupsAvailable) { - return I18n.tr("Print Server not available") + return I18n.tr("Print Server not available"); } else { - return I18n.tr("No printer found") + return I18n.tr("No printer found"); } } } ccWidgetIsActive: CupsService.cupsAvailable && CupsService.getTotalJobsNum() > 0 - onCcWidgetToggled: { - - } + onCcWidgetToggled: {} ccDetailContent: Component { Rectangle { @@ -39,6 +36,21 @@ PluginComponent { radius: Theme.cornerRadius color: Theme.surfaceContainerHigh + DankActionButton { + anchors.top: parent.top + anchors.right: parent.right + anchors.topMargin: Theme.spacingS + anchors.rightMargin: Theme.spacingS + iconName: "settings" + buttonSize: 24 + iconSize: 14 + iconColor: Theme.surfaceVariantText + onClicked: { + PopoutService.closeControlCenter(); + PopoutService.openSettingsWithTab("printers"); + } + } + Column { visible: !CupsService.cupsAvailable || CupsService.getPrintersNum() == 0 anchors.centerIn: parent @@ -58,7 +70,7 @@ PluginComponent { anchors.horizontalCenter: parent.horizontalCenter } } - + Column { id: detailColumn anchors.fill: parent @@ -78,12 +90,12 @@ PluginComponent { Layout.maximumWidth: parent.width - 180 description: "" currentValue: { - CupsService.getSelectedPrinter() + CupsService.getSelectedPrinter(); } options: CupsService.getPrintersNames() onValueChanged: value => { - CupsService.setSelectedPrinter(value) - } + CupsService.setSelectedPrinter(value); + } } Column { @@ -135,11 +147,11 @@ PluginComponent { cursorShape: Qt.PointingHandCursor enabled: true onClicked: { - const selected = CupsService.getSelectedPrinter() + const selected = CupsService.getSelectedPrinter(); if (CupsService.getCurrentPrinterState() === "stopped") { - CupsService.resumePrinter(selected) + CupsService.resumePrinter(selected); } else { - CupsService.pausePrinter(selected) + CupsService.pausePrinter(selected); } } } @@ -180,8 +192,8 @@ PluginComponent { cursorShape: Qt.PointingHandCursor enabled: true onClicked: { - const selected = CupsService.getSelectedPrinter() - CupsService.purgeJobs(selected) + const selected = CupsService.getSelectedPrinter(); + CupsService.purgeJobs(selected); } } } @@ -275,8 +287,8 @@ PluginComponent { StyledText { text: { - var date = new Date(modelData.timeCreated) - return date.toLocaleString(Qt.locale(), Locale.ShortFormat) + var date = new Date(modelData.timeCreated); + return date.toLocaleString(Qt.locale(), Locale.ShortFormat); } font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium @@ -296,7 +308,7 @@ PluginComponent { iconName: "delete" buttonSize: 36 onClicked: { - CupsService.cancelJob(CupsService.getSelectedPrinter(), modelData.id) + CupsService.cancelJob(CupsService.getSelectedPrinter(), modelData.id); } } } diff --git a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml index 3299a86b..8be386f3 100644 --- a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml @@ -69,8 +69,8 @@ Rectangle { height: 40 StyledText { - id: headerText - text: I18n.tr("Network Settings") + id: headerLeft + text: I18n.tr("Network") font.pixelSize: Theme.fontSizeLarge color: Theme.surfaceText font.weight: Font.Medium @@ -79,7 +79,7 @@ Rectangle { Item { height: 1 - width: parent.width - headerText.width - rightControls.width + width: parent.width - headerLeft.width - rightControls.width } Row { @@ -115,6 +115,8 @@ Rectangle { id: preferenceControls anchors.verticalCenter: parent.verticalCenter visible: NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10 + buttonHeight: 28 + textSize: Theme.fontSizeSmall model: ["Ethernet", "WiFi"] currentIndex: currentPreferenceIndex @@ -125,6 +127,18 @@ Rectangle { NetworkService.setNetworkPreference(index === 0 ? "ethernet" : "wifi"); } } + + DankActionButton { + anchors.verticalCenter: parent.verticalCenter + iconName: "settings" + buttonSize: 28 + iconSize: 16 + iconColor: Theme.surfaceVariantText + onClicked: { + PopoutService.closeControlCenter(); + PopoutService.openSettingsWithTab("network"); + } + } } } diff --git a/quickshell/Services/PopoutService.qml b/quickshell/Services/PopoutService.qml index c513855e..07fe233b 100644 --- a/quickshell/Services/PopoutService.qml +++ b/quickshell/Services/PopoutService.qml @@ -197,6 +197,8 @@ Singleton { property bool _settingsWantsOpen: false property bool _settingsWantsToggle: false + property string _settingsPendingTab: "" + function openSettings() { if (settingsModal) { settingsModal.show(); @@ -207,6 +209,19 @@ Singleton { } } + function openSettingsWithTab(tabName: string) { + if (settingsModal) { + settingsModal.showWithTabName(tabName); + return; + } + if (settingsModalLoader) { + _settingsPendingTab = tabName; + _settingsWantsOpen = true; + _settingsWantsToggle = false; + settingsModalLoader.activeAsync = true; + } + } + function closeSettings() { settingsModal?.close(); } @@ -221,6 +236,22 @@ Singleton { } } + function toggleSettingsWithTab(tabName: string) { + if (settingsModal) { + var idx = settingsModal.resolveTabIndex(tabName); + if (idx >= 0) + settingsModal.currentTabIndex = idx; + settingsModal.toggle(); + return; + } + if (settingsModalLoader) { + _settingsPendingTab = tabName; + _settingsWantsToggle = true; + _settingsWantsOpen = false; + settingsModalLoader.activeAsync = true; + } + } + function focusOrToggleSettings() { if (settingsModal?.visible) { const settingsTitle = I18n.tr("Settings", "settings window title"); @@ -238,6 +269,26 @@ Singleton { openSettings(); } + function focusOrToggleSettingsWithTab(tabName: string) { + if (settingsModal?.visible) { + const settingsTitle = I18n.tr("Settings", "settings window title"); + for (const toplevel of ToplevelManager.toplevels.values) { + if (toplevel.title !== "Settings" && toplevel.title !== settingsTitle) + continue; + if (toplevel.activated) { + settingsModal.hide(); + return; + } + var idx = settingsModal.resolveTabIndex(tabName); + if (idx >= 0) + settingsModal.currentTabIndex = idx; + toplevel.activate(); + return; + } + } + openSettingsWithTab(tabName); + } + function unloadSettings() { if (settingsModalLoader) { settingsModal = null; @@ -248,9 +299,22 @@ Singleton { function _onSettingsModalLoaded() { if (_settingsWantsOpen) { _settingsWantsOpen = false; - settingsModal?.show(); - } else if (_settingsWantsToggle) { + if (_settingsPendingTab) { + settingsModal?.showWithTabName(_settingsPendingTab); + _settingsPendingTab = ""; + } else { + settingsModal?.show(); + } + return; + } + if (_settingsWantsToggle) { _settingsWantsToggle = false; + if (_settingsPendingTab) { + var idx = settingsModal?.resolveTabIndex(_settingsPendingTab) ?? -1; + if (idx >= 0) + settingsModal.currentTabIndex = idx; + _settingsPendingTab = ""; + } settingsModal?.toggle(); } } diff --git a/quickshell/Widgets/DankTooltipV2.qml b/quickshell/Widgets/DankTooltipV2.qml index b3e87644..7076c996 100644 --- a/quickshell/Widgets/DankTooltipV2.qml +++ b/quickshell/Widgets/DankTooltipV2.qml @@ -11,7 +11,17 @@ Item { if (!item) return; - const windowContentItem = item.Window.window?.contentItem; + let windowContentItem = item.Window?.window?.contentItem; + if (!windowContentItem) { + let current = item; + while (current) { + if (current.Window?.window?.contentItem) { + windowContentItem = current.Window.window.contentItem; + break; + } + current = current.parent; + } + } if (!windowContentItem) return; diff --git a/quickshell/Widgets/StateLayer.qml b/quickshell/Widgets/StateLayer.qml index 19af569e..1d4e39f2 100644 --- a/quickshell/Widgets/StateLayer.qml +++ b/quickshell/Widgets/StateLayer.qml @@ -21,29 +21,29 @@ MouseArea { color: Qt.rgba(stateColor.r, stateColor.g, stateColor.b, stateOpacity) } - Timer { id: hoverDelay interval: 1000 repeat: false onTriggered: { - const p = root.mapToItem(null, parent.width / 2, parent.height + Theme.spacingXS) - tooltip.show(I18n.tr(""), p.x, p.y, null) + tooltip.show(root.tooltipText, root, 0, 0, "bottom"); } } onEntered: { - if (!tooltipText) { return } - hoverDelay.restart() + if (!tooltipText) + return; + hoverDelay.restart(); } onExited: { - if (!tooltipText) { return } - hoverDelay.stop() - tooltip.hide() + if (!tooltipText) + return; + hoverDelay.stop(); + tooltip.hide(); } - DankTooltip { + DankTooltipV2 { id: tooltip } } diff --git a/quickshell/Widgets/VpnDetailContent.qml b/quickshell/Widgets/VpnDetailContent.qml index 0b1025d7..b439bf1b 100644 --- a/quickshell/Widgets/VpnDetailContent.qml +++ b/quickshell/Widgets/VpnDetailContent.qml @@ -133,6 +133,18 @@ Rectangle { onClicked: DMSNetworkService.disconnectAllActive() } } + + DankActionButton { + Layout.alignment: Qt.AlignVCenter + iconName: "settings" + buttonSize: 28 + iconSize: 16 + iconColor: Theme.surfaceVariantText + onClicked: { + PopoutService.closeControlCenter(); + PopoutService.openSettingsWithTab("network"); + } + } } Rectangle {