diff --git a/Assets/settings-default.json b/Assets/settings-default.json index a4f469f505..76dc7c164f 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -65,7 +65,38 @@ } ] }, - "screenOverrides": [] + "screenOverrides": [], + "sidePanels": { + "leftEnabled": true, + "rightEnabled": true, + "widthMode": "auto", + "width": 320, + "minWidth": 220, + "maxWidth": 520, + "triggerSize": 8, + "hideDelay": 250, + "padding": 12, + "spacing": 8, + "layoutMode": "list", + "gridColumns": 2, + "itemStyle": "filled", + "leftPanels": [ + { + "id": "controlCenterPanel" + }, + { + "id": "notificationHistoryPanel" + } + ], + "rightPanels": [ + { + "id": "networkPanel" + }, + { + "id": "sessionMenuPanel" + } + ] + } }, "general": { "avatarImage": "", diff --git a/Assets/settings-search-index.json b/Assets/settings-search-index.json index 5eef66912d..d1482820e4 100644 --- a/Assets/settings-search-index.json +++ b/Assets/settings-search-index.json @@ -381,7 +381,7 @@ "labelKey": "common.position", "descriptionKey": "panels.control-center.position-description", "widget": "NComboBox", - "tab": 7, + "tab": 8, "tabLabel": "panels.control-center.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -390,7 +390,7 @@ "labelKey": "panels.control-center.system-monitor-disk-path-label", "descriptionKey": "panels.control-center.system-monitor-disk-path-description", "widget": "NComboBox", - "tab": 7, + "tab": 8, "tabLabel": "panels.control-center.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -399,7 +399,7 @@ "labelKey": "panels.desktop-widgets.enabled-label", "descriptionKey": "panels.desktop-widgets.enabled-description", "widget": "NToggle", - "tab": 6, + "tab": 7, "tabLabel": "panels.desktop-widgets.title", "subTab": null }, @@ -479,7 +479,7 @@ "labelKey": "panels.dock.enabled-label", "descriptionKey": "panels.dock.enabled-description", "widget": "NToggle", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -488,7 +488,7 @@ "labelKey": "panels.dock.appearance-position-label", "descriptionKey": "panels.dock.appearance-position-description", "widget": "NComboBox", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -497,7 +497,7 @@ "labelKey": "panels.display.title", "descriptionKey": "panels.dock.appearance-display-description", "widget": "NComboBox", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -506,7 +506,7 @@ "labelKey": "panels.osd.background-opacity-label", "descriptionKey": "panels.dock.appearance-background-opacity-description", "widget": "NValueSlider", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -515,7 +515,7 @@ "labelKey": "panels.dock.appearance-dead-opacity-label", "descriptionKey": "panels.dock.appearance-dead-opacity-description", "widget": "NValueSlider", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -524,7 +524,7 @@ "labelKey": "panels.dock.appearance-floating-distance-label", "descriptionKey": "panels.dock.appearance-floating-distance-description", "widget": "NValueSlider", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -533,7 +533,7 @@ "labelKey": "panels.dock.appearance-icon-size-label", "descriptionKey": "panels.dock.appearance-icon-size-description", "widget": "NValueSlider", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -542,7 +542,7 @@ "labelKey": "panels.dock.appearance-hide-show-speed-label", "descriptionKey": "panels.dock.appearance-hide-show-speed-description", "widget": "NValueSlider", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -551,7 +551,7 @@ "labelKey": "panels.dock.appearance-inactive-indicators-label", "descriptionKey": "panels.dock.appearance-inactive-indicators-description", "widget": "NToggle", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -560,7 +560,7 @@ "labelKey": "panels.dock.appearance-pinned-static-label", "descriptionKey": "panels.dock.appearance-pinned-static-description", "widget": "NToggle", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -569,7 +569,7 @@ "labelKey": "panels.dock.monitors-only-same-monitor-label", "descriptionKey": "panels.dock.monitors-only-same-monitor-description", "widget": "NToggle", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -578,7 +578,7 @@ "labelKey": "panels.dock.appearance-colorize-icons-label", "descriptionKey": "panels.dock.appearance-colorize-icons-description", "widget": "NToggle", - "tab": 5, + "tab": 6, "tabLabel": "panels.dock.title", "subTab": 0, "subTabLabel": "common.appearance" @@ -725,7 +725,7 @@ "labelKey": "panels.launcher.settings-clipboard-history-label", "descriptionKey": "panels.launcher.settings-clipboard-history-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 1, "subTabLabel": "common.clipboard" @@ -734,7 +734,7 @@ "labelKey": "panels.launcher.settings-clip-preview-label", "descriptionKey": "panels.launcher.settings-clip-preview-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 1, "subTabLabel": "common.clipboard" @@ -743,7 +743,7 @@ "labelKey": "panels.launcher.settings-clip-wrap-text-label", "descriptionKey": "panels.launcher.settings-clip-wrap-text-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 1, "subTabLabel": "common.clipboard" @@ -752,7 +752,7 @@ "labelKey": "panels.launcher.settings-auto-paste-label", "descriptionKey": "panels.launcher.settings-auto-paste-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 1, "subTabLabel": "common.clipboard" @@ -761,7 +761,7 @@ "labelKey": "panels.launcher.settings-clipboard-watch-text-label", "descriptionKey": "panels.launcher.settings-clipboard-watch-text-description", "widget": "NTextInput", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 1, "subTabLabel": "common.clipboard" @@ -770,7 +770,7 @@ "labelKey": "panels.launcher.settings-clipboard-watch-image-label", "descriptionKey": "panels.launcher.settings-clipboard-watch-image-description", "widget": "NTextInput", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 1, "subTabLabel": "common.clipboard" @@ -779,7 +779,7 @@ "labelKey": "panels.launcher.settings-use-app2unit-label", "descriptionKey": "panels.launcher.settings-use-app2unit-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 2, "subTabLabel": "common.execute" @@ -788,7 +788,7 @@ "labelKey": "panels.launcher.settings-terminal-command-label", "descriptionKey": "panels.launcher.settings-terminal-command-description", "widget": "NTextInput", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 2, "subTabLabel": "common.execute" @@ -797,7 +797,7 @@ "labelKey": "panels.launcher.settings-custom-launch-prefix-enabled-label", "descriptionKey": "panels.launcher.settings-custom-launch-prefix-enabled-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 2, "subTabLabel": "common.execute" @@ -806,7 +806,7 @@ "labelKey": "panels.launcher.settings-custom-launch-prefix-label", "descriptionKey": "panels.launcher.settings-custom-launch-prefix-description", "widget": "NTextInput", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 2, "subTabLabel": "common.execute" @@ -815,7 +815,7 @@ "labelKey": "panels.launcher.settings-annotation-tool-label", "descriptionKey": "panels.launcher.settings-annotation-tool-description", "widget": "NTextInput", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 2, "subTabLabel": "common.execute" @@ -824,7 +824,7 @@ "labelKey": "panels.launcher.settings-overlay-layer-label", "descriptionKey": "panels.launcher.settings-overlay-layer-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -833,7 +833,7 @@ "labelKey": "panels.launcher.settings-view-mode-label", "descriptionKey": "panels.launcher.settings-view-mode-description", "widget": "NComboBox", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -842,7 +842,7 @@ "labelKey": "panels.launcher.settings-density-label", "descriptionKey": "panels.launcher.settings-density-description", "widget": "NComboBox", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -851,7 +851,7 @@ "labelKey": "panels.launcher.settings-show-categories-label", "descriptionKey": "panels.launcher.settings-show-categories-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -860,7 +860,7 @@ "labelKey": "panels.launcher.settings-icon-mode-label", "descriptionKey": "panels.launcher.settings-icon-mode-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -869,7 +869,7 @@ "labelKey": "panels.launcher.settings-show-icon-background-label", "descriptionKey": "panels.launcher.settings-show-icon-background-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -878,7 +878,7 @@ "labelKey": "panels.launcher.settings-sort-by-usage-label", "descriptionKey": "panels.launcher.settings-sort-by-usage-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -887,7 +887,7 @@ "labelKey": "panels.launcher.settings-enable-settings-search-label", "descriptionKey": "panels.launcher.settings-enable-settings-search-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -896,7 +896,7 @@ "labelKey": "panels.launcher.settings-enable-windows-search-label", "descriptionKey": "panels.launcher.settings-enable-windows-search-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -905,7 +905,7 @@ "labelKey": "panels.launcher.settings-enable-session-search-label", "descriptionKey": "panels.launcher.settings-enable-session-search-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" @@ -914,128 +914,11 @@ "labelKey": "panels.launcher.settings-ignore-mouse-input-label", "descriptionKey": "panels.launcher.settings-ignore-mouse-input-description", "widget": "NToggle", - "tab": 8, + "tab": 9, "tabLabel": "panels.launcher.title", "subTab": 0, "subTabLabel": "common.general" }, - { - "labelKey": "panels.lock-screen.clock-style-label", - "descriptionKey": "panels.lock-screen.clock-style-description", - "widget": "NComboBox", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.clock-format-label", - "descriptionKey": "panels.lock-screen.clock-format-description", - "widget": "NTextInput", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.lock-on-suspend-label", - "descriptionKey": "panels.lock-screen.lock-on-suspend-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.compact-lockscreen-label", - "descriptionKey": "panels.lock-screen.compact-lockscreen-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.lock-screen-animations-label", - "descriptionKey": "panels.lock-screen.lock-screen-animations-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.auto-start-auth-label", - "descriptionKey": "panels.lock-screen.auto-start-auth-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.allow-password-with-fprintd-label", - "descriptionKey": "panels.lock-screen.allow-password-with-fprintd-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.show-session-buttons-label", - "descriptionKey": "panels.lock-screen.show-session-buttons-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.show-hibernate-label", - "descriptionKey": "panels.lock-screen.show-hibernate-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.session-menu.enable-countdown-label", - "descriptionKey": "panels.session-menu.enable-countdown-description", - "widget": "NToggle", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.session-menu.countdown-duration-label", - "descriptionKey": "panels.session-menu.countdown-duration-description", - "widget": "NValueSlider", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.lock-screen-blur-strength-label", - "descriptionKey": "panels.lock-screen.lock-screen-blur-strength-description", - "widget": "NValueSlider", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, - { - "labelKey": "panels.lock-screen.lock-screen-tint-strength-label", - "descriptionKey": "panels.lock-screen.lock-screen-tint-strength-description", - "widget": "NValueSlider", - "tab": 11, - "tabLabel": "panels.lock-screen.title", - "subTab": 0, - "subTabLabel": "common.appearance" - }, { "labelKey": "actions.enable-wifi", "descriptionKey": "panels.network.wifi-description", @@ -1064,7 +947,7 @@ "labelKey": "panels.notifications.duration-respect-expire-label", "descriptionKey": "panels.notifications.duration-respect-expire-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 1, "subTabLabel": "common.duration" @@ -1073,7 +956,7 @@ "labelKey": "panels.notifications.duration-low-urgency-label", "descriptionKey": "panels.notifications.duration-low-urgency-description", "widget": "NValueSlider", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 1, "subTabLabel": "common.duration" @@ -1082,7 +965,7 @@ "labelKey": "panels.notifications.duration-normal-urgency-label", "descriptionKey": "panels.notifications.duration-normal-urgency-description", "widget": "NValueSlider", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 1, "subTabLabel": "common.duration" @@ -1091,7 +974,7 @@ "labelKey": "panels.notifications.duration-critical-urgency-label", "descriptionKey": "panels.notifications.duration-critical-urgency-description", "widget": "NValueSlider", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 1, "subTabLabel": "common.duration" @@ -1100,7 +983,7 @@ "labelKey": "panels.notifications.settings-enabled-label", "descriptionKey": "panels.notifications.settings-enabled-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 0, "subTabLabel": "common.appearance" @@ -1109,7 +992,7 @@ "labelKey": "panels.notifications.settings-density-label", "descriptionKey": "panels.notifications.settings-density-description", "widget": "NComboBox", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 0, "subTabLabel": "common.appearance" @@ -1118,7 +1001,7 @@ "labelKey": "tooltips.do-not-disturb-enabled", "descriptionKey": "panels.notifications.settings-do-not-disturb-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 0, "subTabLabel": "common.appearance" @@ -1127,7 +1010,7 @@ "labelKey": "panels.osd.always-on-top-label", "descriptionKey": "panels.notifications.settings-always-on-top-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 0, "subTabLabel": "common.appearance" @@ -1136,7 +1019,7 @@ "labelKey": "panels.notifications.history-low-urgency-label", "descriptionKey": "panels.notifications.history-low-urgency-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 2, "subTabLabel": "common.history" @@ -1145,7 +1028,7 @@ "labelKey": "panels.notifications.history-normal-urgency-label", "descriptionKey": "panels.notifications.history-normal-urgency-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 2, "subTabLabel": "common.history" @@ -1154,7 +1037,7 @@ "labelKey": "panels.notifications.history-critical-urgency-label", "descriptionKey": "panels.notifications.history-critical-urgency-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 2, "subTabLabel": "common.history" @@ -1163,7 +1046,7 @@ "labelKey": "panels.notifications.sounds-unavailable-label", "descriptionKey": "panels.notifications.sounds-unavailable-description", "widget": "NLabel", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1172,7 +1055,7 @@ "labelKey": "panels.notifications.sounds-enabled-label", "descriptionKey": "panels.notifications.sounds-enabled-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1181,7 +1064,7 @@ "labelKey": "panels.notifications.sounds-volume-label", "descriptionKey": "panels.notifications.sounds-volume-description", "widget": "NValueSlider", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1190,7 +1073,7 @@ "labelKey": "panels.notifications.sounds-separate-label", "descriptionKey": "panels.notifications.sounds-separate-description", "widget": "NToggle", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1199,7 +1082,7 @@ "labelKey": "panels.notifications.sounds-files-unified-label", "descriptionKey": "panels.notifications.sounds-files-unified-description", "widget": "NLabel", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1208,7 +1091,7 @@ "labelKey": "panels.notifications.sounds-files-low-label", "descriptionKey": "panels.notifications.sounds-files-low-description", "widget": "NLabel", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1217,7 +1100,7 @@ "labelKey": "panels.notifications.sounds-files-normal-label", "descriptionKey": "panels.notifications.sounds-files-normal-description", "widget": "NLabel", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1226,7 +1109,7 @@ "labelKey": "panels.notifications.sounds-files-critical-label", "descriptionKey": "panels.notifications.sounds-files-critical-description", "widget": "NLabel", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1235,7 +1118,7 @@ "labelKey": "panels.notifications.sounds-excluded-apps-label", "descriptionKey": "panels.notifications.sounds-excluded-apps-description", "widget": "NLabel", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 3, "subTabLabel": "common.sound" @@ -1244,7 +1127,7 @@ "labelKey": "panels.notifications.toast-media-label", "descriptionKey": "panels.notifications.toast-media-description", "widget": "NCheckbox", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 4, "subTabLabel": "common.toast" @@ -1253,7 +1136,7 @@ "labelKey": "panels.notifications.toast-keyboard-label", "descriptionKey": "panels.notifications.toast-keyboard-description", "widget": "NCheckbox", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 4, "subTabLabel": "common.toast" @@ -1262,7 +1145,7 @@ "labelKey": "panels.notifications.toast-battery-label", "descriptionKey": "panels.notifications.toast-battery-description", "widget": "NCheckbox", - "tab": 9, + "tab": 10, "tabLabel": "common.notifications", "subTab": 4, "subTabLabel": "common.toast" @@ -1271,7 +1154,7 @@ "labelKey": "panels.osd.enabled-label", "descriptionKey": "panels.osd.enabled-description", "widget": "NToggle", - "tab": 10, + "tab": 11, "tabLabel": "panels.osd.title", "subTab": 0, "subTabLabel": "common.general" @@ -1280,7 +1163,7 @@ "labelKey": "panels.osd.duration-auto-hide-label", "descriptionKey": "panels.osd.duration-auto-hide-description", "widget": "NValueSlider", - "tab": 10, + "tab": 11, "tabLabel": "panels.osd.title", "subTab": 0, "subTabLabel": "common.general" @@ -1465,6 +1348,24 @@ "subTab": 0, "subTabLabel": "common.general" }, + { + "labelKey": "panels.session-menu.enable-countdown-label", + "descriptionKey": "panels.session-menu.enable-countdown-description", + "widget": "NToggle", + "tab": 12, + "tabLabel": "session-menu.title", + "subTab": 0, + "subTabLabel": "common.general" + }, + { + "labelKey": "panels.session-menu.countdown-duration-label", + "descriptionKey": "panels.session-menu.countdown-duration-description", + "widget": "NValueSlider", + "tab": 12, + "tabLabel": "session-menu.title", + "subTab": 0, + "subTabLabel": "common.general" + }, { "labelKey": "panels.system-monitor.enable-dgpu-monitoring-label", "descriptionKey": "panels.system-monitor.enable-dgpu-monitoring-description", diff --git a/Commons/Settings.qml b/Commons/Settings.qml index a2991fb334..2ee2edab55 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -253,6 +253,40 @@ Singleton { // Per-screen overrides for position and widgets // Format: [{ "name": "HDMI-1", "position": "left" }, { "name": "DP-1", "position": "bottom", "widgets": {...} }] property list screenOverrides: [] + + // Side widget panels (left and/or right) + property JsonObject sidePanels + sidePanels: JsonObject { + property bool leftEnabled: true + property bool rightEnabled: true + property string widthMode: "auto" // "auto" or "fixed" + property int width: 320 + property int minWidth: 220 + property int maxWidth: 520 + property int triggerSize: 8 + property int hideDelay: 250 + property int padding: 12 + property int spacing: 8 + property string layoutMode: "list" + property int gridColumns: 2 + property string itemStyle: "filled" + property list leftPanels: [ + { + "id": "controlCenterPanel" + }, + { + "id": "notificationHistoryPanel" + } + ] + property list rightPanels: [ + { + "id": "networkPanel" + }, + { + "id": "sessionMenuPanel" + } + ] + } } // general diff --git a/Modules/MainScreen/MainScreen.qml b/Modules/MainScreen/MainScreen.qml index c66f8996bf..749e217db7 100644 --- a/Modules/MainScreen/MainScreen.qml +++ b/Modules/MainScreen/MainScreen.qml @@ -128,7 +128,7 @@ PanelWindow { // Only include regions that are actually needed // panelRegions is handled by PanelService, bar is local to this screen - regions: [barMaskRegion, backgroundMaskRegion] + regions: [barMaskRegion, backgroundMaskRegion, leftSidePanelMaskRegion, rightSidePanelMaskRegion, leftSideTriggerMaskRegion, rightSideTriggerMaskRegion] // Bar region - subtract bar area from mask (only if bar should be shown on this screen) Region { @@ -198,6 +198,42 @@ PanelWindow { height: root.isAnyPanelOpen ? root.height : 0 intersection: Intersection.Subtract } + + Region { + id: leftSidePanelMaskRegion + x: leftSidePanel.visiblePanel ? leftSidePanel.panelBody.x : 0 + y: leftSidePanel.visiblePanel ? leftSidePanel.panelBody.y : 0 + width: leftSidePanel.visiblePanel ? leftSidePanel.panelBody.width : 0 + height: leftSidePanel.visiblePanel ? leftSidePanel.panelBody.height : 0 + intersection: Intersection.Subtract + } + + Region { + id: rightSidePanelMaskRegion + x: rightSidePanel.visiblePanel ? rightSidePanel.panelBody.x : 0 + y: rightSidePanel.visiblePanel ? rightSidePanel.panelBody.y : 0 + width: rightSidePanel.visiblePanel ? rightSidePanel.panelBody.width : 0 + height: rightSidePanel.visiblePanel ? rightSidePanel.panelBody.height : 0 + intersection: Intersection.Subtract + } + + Region { + id: leftSideTriggerMaskRegion + x: leftSidePanel.visiblePanel ? leftSidePanel.triggerZone.x : 0 + y: leftSidePanel.visiblePanel ? leftSidePanel.triggerZone.y : 0 + width: leftSidePanel.visiblePanel ? leftSidePanel.triggerZone.width : 0 + height: leftSidePanel.visiblePanel ? leftSidePanel.triggerZone.height : 0 + intersection: Intersection.Subtract + } + + Region { + id: rightSideTriggerMaskRegion + x: rightSidePanel.visiblePanel ? rightSidePanel.triggerZone.x : 0 + y: rightSidePanel.visiblePanel ? rightSidePanel.triggerZone.y : 0 + width: rightSidePanel.visiblePanel ? rightSidePanel.triggerZone.width : 0 + height: rightSidePanel.visiblePanel ? rightSidePanel.triggerZone.height : 0 + intersection: Intersection.Subtract + } } // -------------------------------------- @@ -232,6 +268,22 @@ PanelWindow { z: 0 // Behind panels and bar } + SideWidgetPanel { + id: leftSidePanel + screen: root.screen + side: "left" + anchors.fill: parent + z: 20 + } + + SideWidgetPanel { + id: rightSidePanel + screen: root.screen + side: "right" + anchors.fill: parent + z: 20 + } + // --------------------------------------- // All panels always exist // --------------------------------------- diff --git a/Modules/MainScreen/SideWidgetPanel.qml b/Modules/MainScreen/SideWidgetPanel.qml new file mode 100644 index 0000000000..67174b3df0 --- /dev/null +++ b/Modules/MainScreen/SideWidgetPanel.qml @@ -0,0 +1,384 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.Commons +import qs.Services.UI +import qs.Widgets + +Item { + id: root + + required property ShellScreen screen + required property string side // "left" or "right" + + readonly property bool isLeft: side === "left" + readonly property bool isRight: side === "right" + + readonly property var settings: Settings.data.bar.sidePanels + readonly property bool panelEnabled: { + if (!settings) + return true; + return isLeft ? (settings?.leftEnabled ?? true) : (settings?.rightEnabled ?? true); + } + + readonly property string layoutMode: settings?.layoutMode ?? "list" // "list" | "grid" + readonly property int gridColumns: Math.max(1, Math.min(4, settings?.gridColumns ?? 2)) + readonly property string itemStyle: settings?.itemStyle ?? "filled" // "filled" | "outline" | "minimal" + + function normalizePanelId(panelId: string): string { + switch (panelId) { + case "Audio": + return "audioPanel"; + case "Battery": + return "batteryPanel"; + case "Bluetooth": + return "bluetoothPanel"; + case "Brightness": + return "brightnessPanel"; + case "Clock": + return "clockPanel"; + case "ControlCenter": + return "controlCenterPanel"; + case "Launcher": + return "launcherPanel"; + case "MediaMini": + return "mediaPlayerPanel"; + case "Network": + return "networkPanel"; + case "NotificationHistory": + return "notificationHistoryPanel"; + case "SessionMenu": + return "sessionMenuPanel"; + case "Settings": + return "settingsPanel"; + case "WallpaperSelector": + return "wallpaperPanel"; + default: + return panelId; + } + } + + readonly property var panelEntries: { + var _rev = BarService.widgetsRevision; + + var fromSidePanels = isLeft ? settings?.leftPanels : settings?.rightPanels; + var sourceEntries = []; + + if (fromSidePanels && fromSidePanels.length !== undefined) { + sourceEntries = fromSidePanels; + } else { + // Backward compatibility with older settings keys + var fromLegacy = isLeft ? settings?.leftWidgets : settings?.rightWidgets; + if (fromLegacy && fromLegacy.length !== undefined) + sourceEntries = fromLegacy; + } + + var normalized = []; + for (var i = 0; i < sourceEntries.length; i++) { + var entry = sourceEntries[i] || {}; + var normalizedId = normalizePanelId(entry.id || ""); + if (normalizedId === "") + continue; + normalized.push({ + "id": normalizedId + }); + } + return normalized; + } + + readonly property real panelPadding: settings?.padding ?? Style.marginM + readonly property real panelSpacing: settings?.spacing ?? Style.marginS + readonly property int triggerSize: Math.max(1, settings?.triggerSize ?? 8) + readonly property int hideDelay: Math.max(0, settings?.hideDelay ?? 250) + readonly property int panelMinWidth: Math.max(120, settings?.minWidth ?? 220) + readonly property int panelMaxWidth: Math.max(panelMinWidth, settings?.maxWidth ?? 520) + readonly property bool useAutoWidth: (settings?.widthMode || "auto") === "auto" + readonly property int configuredWidth: Math.max(panelMinWidth, settings?.width ?? 320) + + readonly property real computedPanelWidth: { + var width = useAutoWidth ? (contentLayout.implicitWidth + panelPadding * 2) : configuredWidth; + return Math.min(panelMaxWidth, Math.max(panelMinWidth, width)); + } + + readonly property bool visiblePanel: panelEnabled && panelEntries.length > 0 + + property bool revealed: false + property int panelLookupRevision: 0 + + property alias panelBody: panelBody + property alias triggerZone: triggerZone + + readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name) + readonly property bool barShouldShow: { + if (!BarService.effectivelyVisible) + return false; + var monitors = Settings.data.bar.monitors || []; + var screenName = screen?.name || ""; + return monitors.length === 0 || monitors.includes(screenName); + } + readonly property bool barFloating: Settings.data.bar.floating || false + readonly property real barHeight: Style.getBarHeightForScreen(screen?.name) + readonly property real barMarginH: barFloating ? Math.floor(Settings.data.bar.marginHorizontal || 0) : 0 + readonly property real barMarginV: barFloating ? Math.floor(Settings.data.bar.marginVertical || 0) : 0 + + readonly property real insetTop: (barShouldShow && barPosition === "top") ? (barHeight + barMarginV) : 0 + readonly property real insetBottom: (barShouldShow && barPosition === "bottom") ? (barHeight + barMarginV) : 0 + readonly property real insetLeft: (barShouldShow && barPosition === "left") ? (barHeight + barMarginH) : 0 + readonly property real insetRight: (barShouldShow && barPosition === "right") ? (barHeight + barMarginH) : 0 + + readonly property bool attachedToTopBar: insetTop > 0 + readonly property bool attachedToBottomBar: insetBottom > 0 + + readonly property real shownX: isLeft ? insetLeft : (parent ? parent.width - insetRight - panelBody.width : 0) + readonly property real hiddenX: isLeft ? (insetLeft - panelBody.width) : (parent ? parent.width - insetRight : 0) + + function panelName(panelId: string): string { + switch (panelId) { + case "audioPanel": + return I18n.tr("panels.audio.title"); + case "batteryPanel": + return I18n.tr("battery.battery"); + case "bluetoothPanel": + return I18n.tr("common.bluetooth"); + case "brightnessPanel": + return I18n.tr("panels.osd.types-brightness-label"); + case "changelogPanel": + return I18n.tr("panels.changelog.title"); + case "clockPanel": + return I18n.tr("common.calendar"); + case "controlCenterPanel": + return I18n.tr("panels.control-center.title"); + case "launcherPanel": + return I18n.tr("panels.launcher.title"); + case "mediaPlayerPanel": + return I18n.tr("common.media"); + case "networkPanel": + return I18n.tr("common.network"); + case "notificationHistoryPanel": + return I18n.tr("panels.notifications.history-title"); + case "sessionMenuPanel": + return I18n.tr("session-menu.title"); + case "settingsPanel": + return I18n.tr("panels.general.title"); + case "setupWizardPanel": + return I18n.tr("setup-wizard.title"); + case "systemStatsPanel": + return I18n.tr("panels.system-monitor.title"); + case "trayDrawerPanel": + return I18n.tr("common.tray"); + case "wallpaperPanel": + return I18n.tr("common.wallpaper"); + default: + return panelId; + } + } + + + function openPanel(panelId: string) { + var panel = PanelService.getPanel(panelId, screen, true); + if (panel && panel.toggle) { + panel.toggle(); + } + } + + function reveal() { + if (!visiblePanel) + return; + hideTimer.stop(); + revealed = true; + } + + function conceal() { + if (!visiblePanel) + return; + hideTimer.restart(); + } + + function concealNow() { + hideTimer.stop(); + revealed = false; + } + + onVisiblePanelChanged: { + if (!visiblePanel) + concealNow(); + } + + + Timer { + id: panelResolveTimer + interval: 250 + repeat: true + running: root.visiblePanel + + onTriggered: { + root.panelLookupRevision++; + + var unresolved = 0; + for (var i = 0; i < root.panelEntries.length; i++) { + var entry = root.panelEntries[i] || {}; + var panelId = entry.id || ""; + if (!panelId) + continue; + if (!PanelService.getPanel(panelId, root.screen, true)) + unresolved++; + } + + if (unresolved === 0) + stop(); + } + } + + Timer { + id: hideTimer + interval: hideDelay + repeat: false + onTriggered: { + if (!triggerZone.containsMouse && !panelMouseArea.containsMouse) + root.revealed = false; + } + } + + MouseArea { + id: triggerZone + visible: root.visiblePanel + enabled: root.visiblePanel + width: root.triggerSize + anchors { + top: parent.top + bottom: parent.bottom + left: root.isLeft ? parent.left : undefined + right: root.isRight ? parent.right : undefined + } + hoverEnabled: true + acceptedButtons: Qt.NoButton + onEntered: root.reveal() + onExited: root.conceal() + z: 60 + } + + Rectangle { + id: panelBody + visible: root.visiblePanel + width: Math.round(root.computedPanelWidth) + height: Math.max(0, (parent ? parent.height : 0) - root.insetTop - root.insetBottom) + x: root.revealed ? root.shownX : root.hiddenX + y: root.insetTop + + color: Qt.alpha(Color.mSurface, 0.96) + border.color: Color.mOutline + border.width: 1 + radius: Style.radiusL + + topLeftRadius: root.isLeft || root.attachedToTopBar ? 0 : radius + bottomLeftRadius: root.isLeft || root.attachedToBottomBar ? 0 : radius + topRightRadius: root.isRight || root.attachedToTopBar ? 0 : radius + bottomRightRadius: root.isRight || root.attachedToBottomBar ? 0 : radius + + Behavior on x { + NumberAnimation { + duration: Style.animationFast + easing.type: Easing.OutCubic + } + } + + MouseArea { + id: panelMouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton + onEntered: root.reveal() + onExited: root.conceal() + z: 1 + } + + Flickable { + id: flick + anchors.fill: parent + anchors.margins: root.panelPadding + contentWidth: width + contentHeight: contentLayout.implicitHeight + clip: true + interactive: contentHeight > height + + GridLayout { + id: contentLayout + width: flick.width + columns: root.layoutMode === "grid" ? root.gridColumns : 1 + columnSpacing: root.panelSpacing + rowSpacing: root.panelSpacing + + Repeater { + model: root.panelEntries + + delegate: Item { + required property var modelData + readonly property var entry: modelData || {} + readonly property string panelId: entry.id || "" + readonly property var panelObject: { + var _rev = root.panelLookupRevision; + return PanelService.getPanel(panelId, root.screen, true); + } + + Layout.fillWidth: true + Layout.columnSpan: root.layoutMode === "list" ? contentLayout.columns : 1 + implicitHeight: embeddedLoader.active && embeddedLoader.item ? ((embeddedLoader.item.contentPreferredHeight !== undefined && embeddedLoader.item.contentPreferredHeight > 0) ? embeddedLoader.item.contentPreferredHeight : Math.max(embeddedLoader.item.implicitHeight || 0, 120 * Style.uiScaleRatio)) : fallbackButton.implicitHeight + + Loader { + id: embeddedLoader + anchors.left: parent.left + anchors.right: parent.right + active: panelId !== "" + sourceComponent: panelObject && panelObject.panelContent ? panelObject.panelContent : null + + onLoaded: { + if (!item) + return; + + if (item.anchors && item.anchors.fill !== undefined) + item.anchors.fill = embeddedLoader; + + if (item.hasOwnProperty("width")) { + item.width = Qt.binding(function () { + return embeddedLoader.width; + }); + } + + if (item.hasOwnProperty("height")) { + item.height = Qt.binding(function () { + if (item.contentPreferredHeight !== undefined && item.contentPreferredHeight > 0) + return item.contentPreferredHeight; + if (item.implicitHeight !== undefined && item.implicitHeight > 0) + return item.implicitHeight; + return 120 * Style.uiScaleRatio; + }); + } + + if (item.hasOwnProperty("screen")) + item.screen = root.screen; + if (item.hasOwnProperty("panelID") && panelObject && panelObject.panelID !== undefined) + item.panelID = panelObject.panelID; + } + } + + NButton { + id: fallbackButton + visible: !embeddedLoader.item + enabled: panelId !== "" + anchors.left: parent.left + anchors.right: parent.right + text: root.panelName(panelId) + icon: "chevron-right" + fontSize: Style.fontSizeM + backgroundColor: Qt.alpha(Color.mSurfaceContainerHighest, 0.7) + hoverColor: Qt.alpha(Color.mSurfaceContainerHighest, 0.95) + textColor: Color.mOnSurface + textHoverColor: Color.mOnSurface + onClicked: root.openPanel(panelId) + } + } + } + } + } + } +} diff --git a/Modules/Panels/Settings/SettingsContent.qml b/Modules/Panels/Settings/SettingsContent.qml index b6d9f8a4bd..ee80c40338 100644 --- a/Modules/Panels/Settings/SettingsContent.qml +++ b/Modules/Panels/Settings/SettingsContent.qml @@ -14,12 +14,12 @@ import qs.Modules.Panels.Settings.Tabs.Display import qs.Modules.Panels.Settings.Tabs.Dock import qs.Modules.Panels.Settings.Tabs.Hooks import qs.Modules.Panels.Settings.Tabs.Launcher -import qs.Modules.Panels.Settings.Tabs.LockScreen import qs.Modules.Panels.Settings.Tabs.Notifications import qs.Modules.Panels.Settings.Tabs.Osd import qs.Modules.Panels.Settings.Tabs.Plugins import qs.Modules.Panels.Settings.Tabs.Region import qs.Modules.Panels.Settings.Tabs.SessionMenu +import qs.Modules.Panels.Settings.Tabs.SidePanels import qs.Modules.Panels.Settings.Tabs.SystemMonitor import qs.Modules.Panels.Settings.Tabs.UserInterface import qs.Modules.Panels.Settings.Tabs.Wallpaper @@ -61,6 +61,8 @@ Item { readonly property bool panelVeryTransparent: Settings.data.ui.panelBackgroundOpacity <= 0.75 + readonly property url lockScreenTabUrl: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Tabs/LockScreen/LockScreenTab.qml") + onSearchResultsChanged: { searchSelectedIndex = 0; ignoreMouseHover = true; @@ -446,6 +448,10 @@ Item { id: dockTab DockTab {} } + Component { + id: sidePanelsTab + SidePanelsTab {} + } Component { id: notificationsTab NotificationsTab {} @@ -458,10 +464,6 @@ Item { id: userInterfaceTab UserInterfaceTab {} } - Component { - id: lockScreenTab - LockScreenTab {} - } Component { id: sessionMenuTab SessionMenuTab {} @@ -511,6 +513,12 @@ Item { "icon": "settings-bar", "source": barTab }, + { + "id": SettingsPanel.Tab.SidePanels, + "label": "Side Panels", + "icon": "settings-bar", + "source": sidePanelsTab + }, { "id": SettingsPanel.Tab.Dock, "label": "panels.dock.title", @@ -551,7 +559,7 @@ Item { "id": SettingsPanel.Tab.LockScreen, "label": "panels.lock-screen.title", "icon": "settings-lock-screen", - "source": lockScreenTab + "sourceUrl": lockScreenTabUrl }, { "id": SettingsPanel.Tab.SessionMenu, @@ -1230,7 +1238,8 @@ Item { Loader { active: true - sourceComponent: root.tabsModel[index]?.source + source: root.tabsModel[index]?.sourceUrl || "" + sourceComponent: root.tabsModel[index]?.sourceUrl ? undefined : root.tabsModel[index]?.source width: scrollView.availableWidth onLoaded: { if (item && item.hasOwnProperty("screen")) { diff --git a/Modules/Panels/Settings/SettingsPanel.qml b/Modules/Panels/Settings/SettingsPanel.qml index 331d6eaa9e..92e6843eeb 100644 --- a/Modules/Panels/Settings/SettingsPanel.qml +++ b/Modules/Panels/Settings/SettingsPanel.qml @@ -73,6 +73,7 @@ SmartPanel { About, Audio, Bar, + SidePanels, ColorScheme, LockScreen, ControlCenter, diff --git a/Modules/Panels/Settings/Tabs/Bar/BarTab.qml b/Modules/Panels/Settings/Tabs/Bar/BarTab.qml index e3df17db91..a6327b1398 100644 --- a/Modules/Panels/Settings/Tabs/Bar/BarTab.qml +++ b/Modules/Panels/Settings/Tabs/Bar/BarTab.qml @@ -88,6 +88,98 @@ ColumnLayout { } } + + function _ensureSidePanelWidgets() { + if (!Settings.data.bar.sidePanels) { + return; + } + if (!Settings.data.bar.sidePanels.leftWidgets) { + Settings.data.bar.sidePanels.leftWidgets = []; + } + if (!Settings.data.bar.sidePanels.rightWidgets) { + Settings.data.bar.sidePanels.rightWidgets = []; + } + } + + function _addSidePanelWidgetToSection(widgetId, section) { + _ensureSidePanelWidgets(); + + var newWidget = { + "id": widgetId + }; + if (BarWidgetRegistry.widgetHasUserSettings(widgetId)) { + var metadata = BarWidgetRegistry.widgetMetadata[widgetId]; + if (metadata) { + Object.keys(metadata).forEach(function (key) { + newWidget[key] = metadata[key]; + }); + } + } + + var key = section + "Widgets"; + Settings.data.bar.sidePanels[key].push(newWidget); + BarService.widgetsRevision++; + } + + function _removeSidePanelWidgetFromSection(section, index) { + _ensureSidePanelWidgets(); + + var key = section + "Widgets"; + var widgets = Settings.data.bar.sidePanels[key] || []; + if (index >= 0 && index < widgets.length) { + var newArray = widgets.slice(); + newArray.splice(index, 1); + Settings.data.bar.sidePanels[key] = newArray; + BarService.widgetsRevision++; + } + } + + function _reorderSidePanelWidgetInSection(section, fromIndex, toIndex) { + _ensureSidePanelWidgets(); + + var key = section + "Widgets"; + var widgets = Settings.data.bar.sidePanels[key] || []; + if (fromIndex >= 0 && fromIndex < widgets.length && toIndex >= 0 && toIndex < widgets.length) { + var newArray = widgets.slice(); + var item = newArray[fromIndex]; + newArray.splice(fromIndex, 1); + newArray.splice(toIndex, 0, item); + Settings.data.bar.sidePanels[key] = newArray; + BarService.widgetsRevision++; + } + } + + function _updateSidePanelWidgetSettingsInSection(section, index, settings) { + _ensureSidePanelWidgets(); + + var key = section + "Widgets"; + var widgets = Settings.data.bar.sidePanels[key] || []; + if (index >= 0 && index < widgets.length) { + widgets[index] = settings; + Settings.data.bar.sidePanels[key] = widgets.slice(); + } + } + + function _moveSidePanelWidgetBetweenSections(fromSection, index, toSection) { + _ensureSidePanelWidgets(); + + var fromKey = fromSection + "Widgets"; + var toKey = toSection + "Widgets"; + var fromWidgets = Settings.data.bar.sidePanels[fromKey] || []; + var toWidgets = Settings.data.bar.sidePanels[toKey] || []; + + if (index >= 0 && index < fromWidgets.length) { + var widget = fromWidgets[index]; + var sourceArray = fromWidgets.slice(); + sourceArray.splice(index, 1); + var targetArray = toWidgets.slice(); + targetArray.push(widget); + Settings.data.bar.sidePanels[fromKey] = sourceArray; + Settings.data.bar.sidePanels[toKey] = targetArray; + BarService.widgetsRevision++; + } + } + function getWidgetLocations(widgetId) { if (!BarService) return []; diff --git a/Modules/Panels/Settings/Tabs/Bar/SidePanelsSubTab.qml b/Modules/Panels/Settings/Tabs/Bar/SidePanelsSubTab.qml new file mode 100644 index 0000000000..c05399ba00 --- /dev/null +++ b/Modules/Panels/Settings/Tabs/Bar/SidePanelsSubTab.qml @@ -0,0 +1,230 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import QtQuick.Layouts +import qs.Commons +import qs.Services.UI +import qs.Widgets + +ColumnLayout { + id: root + spacing: Style.marginL + Layout.fillWidth: true + + property var availableWidgets + property var addPanelToSection + property var removePanelFromSection + property var reorderPanelInSection + property var updatePanelInSection + property var movePanelBetweenSections + + signal openPluginSettings(var manifest) + + readonly property var sidePanels: Settings.data.bar.sidePanels + + function getSectionIcons() { + return { + "left": "arrow-bar-to-left", + "right": "arrow-bar-to-right" + }; + } + + NText { + text: "Configure side panels and choose which panels are opened from each side." + wrapMode: Text.WordWrap + Layout.fillWidth: true + color: Color.mOnSurfaceVariant + } + + NToggle { + label: "Enable left side panel" + description: "Show a hover-revealed side panel on the left screen edge." + checked: sidePanels.leftEnabled + onToggled: checked => sidePanels.leftEnabled = checked + } + + NToggle { + label: "Enable right side panel" + description: "Show a hover-revealed side panel on the right screen edge." + checked: sidePanels.rightEnabled + onToggled: checked => sidePanels.rightEnabled = checked + } + + NComboBox { + label: "Panel width mode" + description: "Auto fits to panel content or use a fixed width." + model: [{"key": "auto", "name": "Auto"}, {"key": "fixed", "name": "Fixed"}] + currentKey: sidePanels.widthMode + onSelected: key => sidePanels.widthMode = key + } + + NLabel { + label: "Fixed width" + description: "Used when width mode is fixed." + visible: sidePanels.widthMode === "fixed" + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + visible: sidePanels.widthMode === "fixed" + + NSlider { + id: fixedWidthSlider + Layout.fillWidth: true + from: 180 + to: 700 + stepSize: 1 + value: sidePanels.width + onPressedChanged: { + if (!pressed) + sidePanels.width = Math.round(value); + } + } + + NText { + text: Math.round(fixedWidthSlider.value) + "px" + pointSize: Style.fontSizeM + color: Color.mOnSurfaceVariant + } + } + + NComboBox { + label: "Panel items layout" + description: "Choose how panel launchers are arranged inside each side panel." + model: [{"key": "list", "name": "List"}, {"key": "grid", "name": "Grid"}] + currentKey: sidePanels.layoutMode ?? "list" + onSelected: key => sidePanels.layoutMode = key + } + + NLabel { + label: "Grid columns" + description: "Used when layout is set to grid." + visible: (sidePanels.layoutMode ?? "list") === "grid" + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + visible: (sidePanels.layoutMode ?? "list") === "grid" + + NSlider { + id: gridColumnsSlider + Layout.fillWidth: true + from: 1 + to: 4 + stepSize: 1 + value: sidePanels.gridColumns ?? 2 + onPressedChanged: { + if (!pressed) + sidePanels.gridColumns = Math.round(value); + } + } + + NText { + text: Math.round(gridColumnsSlider.value) + " cols" + pointSize: Style.fontSizeM + color: Color.mOnSurfaceVariant + } + } + + NComboBox { + label: "Panel item style" + description: "Visual style for each panel launcher item." + model: [{"key": "filled", "name": "Filled"}, {"key": "outline", "name": "Outline"}, {"key": "minimal", "name": "Minimal"}] + currentKey: sidePanels.itemStyle ?? "filled" + onSelected: key => sidePanels.itemStyle = key + } + + NLabel { + label: "Edge trigger size" + description: "How wide the hover activation strip is on each side." + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + + NSlider { + id: triggerSlider + Layout.fillWidth: true + from: 1 + to: 32 + stepSize: 1 + value: sidePanels.triggerSize + onPressedChanged: { + if (!pressed) + sidePanels.triggerSize = Math.round(value); + } + } + + NText { + text: Math.round(triggerSlider.value) + "px" + pointSize: Style.fontSizeM + color: Color.mOnSurfaceVariant + } + } + + NLabel { + label: "Auto-hide delay" + description: "Delay before panel hides after leaving its area." + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + + NSlider { + id: hideDelaySlider + Layout.fillWidth: true + from: 0 + to: 2000 + stepSize: 10 + value: sidePanels.hideDelay + onPressedChanged: { + if (!pressed) + sidePanels.hideDelay = Math.round(value); + } + } + + NText { + text: Math.round(hideDelaySlider.value) + "ms" + pointSize: Style.fontSizeM + color: Color.mOnSurfaceVariant + } + } + + NSectionEditor { + sectionName: I18n.tr("positions.left") + sectionId: "left" + settingsDialogComponent: "" + widgetRegistry: null + widgetModel: sidePanels.leftPanels + availableSections: ["left", "right"] + sectionIcons: root.getSectionIcons() + availableWidgets: root.availableWidgets + onAddWidget: (panelId, section) => root.addPanelToSection(panelId, section) + onRemoveWidget: (section, index) => root.removePanelFromSection(section, index) + onReorderWidget: (section, fromIndex, toIndex) => root.reorderPanelInSection(section, fromIndex, toIndex) + onUpdateWidgetSettings: (section, index, settings) => root.updatePanelInSection(section, index, settings) + onMoveWidget: (fromSection, index, toSection) => root.movePanelBetweenSections(fromSection, index, toSection) + onOpenPluginSettingsRequested: manifest => root.openPluginSettings(manifest) + } + + NSectionEditor { + sectionName: I18n.tr("positions.right") + sectionId: "right" + settingsDialogComponent: "" + widgetRegistry: null + widgetModel: sidePanels.rightPanels + availableSections: ["left", "right"] + sectionIcons: root.getSectionIcons() + availableWidgets: root.availableWidgets + onAddWidget: (panelId, section) => root.addPanelToSection(panelId, section) + onRemoveWidget: (section, index) => root.removePanelFromSection(section, index) + onReorderWidget: (section, fromIndex, toIndex) => root.reorderPanelInSection(section, fromIndex, toIndex) + onUpdateWidgetSettings: (section, index, settings) => root.updatePanelInSection(section, index, settings) + onMoveWidget: (fromSection, index, toSection) => root.movePanelBetweenSections(fromSection, index, toSection) + onOpenPluginSettingsRequested: manifest => root.openPluginSettings(manifest) + } +} diff --git a/Modules/Panels/Settings/Tabs/SidePanels/SidePanelsTab.qml b/Modules/Panels/Settings/Tabs/SidePanels/SidePanelsTab.qml new file mode 100644 index 0000000000..04e2e02968 --- /dev/null +++ b/Modules/Panels/Settings/Tabs/SidePanels/SidePanelsTab.qml @@ -0,0 +1,190 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Commons +import qs.Modules.Panels.Settings.Tabs.Bar +import qs.Services.UI +import qs.Widgets + +ColumnLayout { + id: root + spacing: 0 + + readonly property var legacyWidgetToPanelMap: ({ + "Audio": "audioPanel", + "Battery": "batteryPanel", + "Bluetooth": "bluetoothPanel", + "Brightness": "brightnessPanel", + "Clock": "clockPanel", + "ControlCenter": "controlCenterPanel", + "Launcher": "launcherPanel", + "MediaMini": "mediaPlayerPanel", + "Network": "networkPanel", + "NotificationHistory": "notificationHistoryPanel", + "SessionMenu": "sessionMenuPanel", + "Settings": "settingsPanel", + "WallpaperSelector": "wallpaperPanel" + }) + + function normalizePanelId(id) { + return legacyWidgetToPanelMap[id] || id; + } + + function normalizePanelEntries(list) { + var source = list || []; + var normalized = []; + for (var i = 0; i < source.length; i++) { + var entry = source[i] || {}; + var id = normalizePanelId(entry.id || ""); + if (id === "") + continue; + normalized.push({ + "id": id + }); + } + return normalized; + } + + function _ensureSidePanelLists() { + if (!Settings.data.bar.sidePanels) + return; + + if (!Settings.data.bar.sidePanels.leftPanels) { + Settings.data.bar.sidePanels.leftPanels = normalizePanelEntries(Settings.data.bar.sidePanels.leftWidgets || []); + } else { + Settings.data.bar.sidePanels.leftPanels = normalizePanelEntries(Settings.data.bar.sidePanels.leftPanels); + } + + if (!Settings.data.bar.sidePanels.rightPanels) { + Settings.data.bar.sidePanels.rightPanels = normalizePanelEntries(Settings.data.bar.sidePanels.rightWidgets || []); + } else { + Settings.data.bar.sidePanels.rightPanels = normalizePanelEntries(Settings.data.bar.sidePanels.rightPanels); + } + } + + function _addSidePanelToSection(panelId, section) { + _ensureSidePanelLists(); + + var panelEntry = { + "id": panelId + }; + + var key = section + "Panels"; + Settings.data.bar.sidePanels[key].push(panelEntry); + BarService.widgetsRevision++; + } + + function _removeSidePanelFromSection(section, index) { + _ensureSidePanelLists(); + + var key = section + "Panels"; + var panels = Settings.data.bar.sidePanels[key] || []; + if (index >= 0 && index < panels.length) { + var newArray = panels.slice(); + newArray.splice(index, 1); + Settings.data.bar.sidePanels[key] = newArray; + BarService.widgetsRevision++; + } + } + + function _reorderSidePanelInSection(section, fromIndex, toIndex) { + _ensureSidePanelLists(); + + var key = section + "Panels"; + var panels = Settings.data.bar.sidePanels[key] || []; + if (fromIndex >= 0 && fromIndex < panels.length && toIndex >= 0 && toIndex < panels.length) { + var newArray = panels.slice(); + var item = newArray[fromIndex]; + newArray.splice(fromIndex, 1); + newArray.splice(toIndex, 0, item); + Settings.data.bar.sidePanels[key] = newArray; + BarService.widgetsRevision++; + } + } + + function _updateSidePanelInSection(section, index, panelEntry) { + _ensureSidePanelLists(); + + var key = section + "Panels"; + var panels = Settings.data.bar.sidePanels[key] || []; + if (index >= 0 && index < panels.length) { + panels[index] = { + "id": normalizePanelId((panelEntry || {}).id || "") + }; + Settings.data.bar.sidePanels[key] = panels.slice(); + } + } + + function _moveSidePanelBetweenSections(fromSection, index, toSection) { + _ensureSidePanelLists(); + + var fromKey = fromSection + "Panels"; + var toKey = toSection + "Panels"; + var fromPanels = Settings.data.bar.sidePanels[fromKey] || []; + var toPanels = Settings.data.bar.sidePanels[toKey] || []; + + if (index >= 0 && index < fromPanels.length) { + var panelEntry = fromPanels[index]; + var sourceArray = fromPanels.slice(); + sourceArray.splice(index, 1); + var targetArray = toPanels.slice(); + targetArray.push(panelEntry); + Settings.data.bar.sidePanels[fromKey] = sourceArray; + Settings.data.bar.sidePanels[toKey] = targetArray; + BarService.widgetsRevision++; + } + } + + function updateAvailablePanelsModel() { + availablePanels.clear(); + + var panels = [ + {"key": "audioPanel", "name": I18n.tr("panels.audio.title")}, + {"key": "batteryPanel", "name": I18n.tr("battery.battery")}, + {"key": "bluetoothPanel", "name": I18n.tr("common.bluetooth")}, + {"key": "brightnessPanel", "name": I18n.tr("panels.osd.types-brightness-label")}, + {"key": "changelogPanel", "name": I18n.tr("panels.changelog.title")}, + {"key": "clockPanel", "name": I18n.tr("common.calendar")}, + {"key": "controlCenterPanel", "name": I18n.tr("panels.control-center.title")}, + {"key": "launcherPanel", "name": I18n.tr("panels.launcher.title")}, + {"key": "mediaPlayerPanel", "name": I18n.tr("common.media")}, + {"key": "networkPanel", "name": I18n.tr("common.network")}, + {"key": "notificationHistoryPanel", "name": I18n.tr("panels.notifications.history-title")}, + {"key": "sessionMenuPanel", "name": I18n.tr("session-menu.title")}, + {"key": "settingsPanel", "name": I18n.tr("panels.general.title")}, + {"key": "setupWizardPanel", "name": I18n.tr("setup-wizard.title")}, + {"key": "systemStatsPanel", "name": I18n.tr("panels.system-monitor.title")}, + {"key": "trayDrawerPanel", "name": I18n.tr("common.tray")}, + {"key": "wallpaperPanel", "name": I18n.tr("common.wallpaper")} + ]; + + for (var i = 0; i < panels.length; i++) { + availablePanels.append(panels[i]); + } + } + + ListModel { + id: availablePanels + } + + Component.onCompleted: { + _ensureSidePanelLists(); + updateAvailablePanelsModel(); + } + + SidePanelsSubTab { + availableWidgets: availablePanels + addPanelToSection: root._addSidePanelToSection + removePanelFromSection: root._removeSidePanelFromSection + reorderPanelInSection: root._reorderSidePanelInSection + updatePanelInSection: root._updateSidePanelInSection + movePanelBetweenSections: root._moveSidePanelBetweenSections + onOpenPluginSettings: manifest => pluginSettingsDialog.openPluginSettings(manifest) + } + + NPluginSettingsPopup { + id: pluginSettingsDialog + parent: Overlay.overlay + showToastOnSave: false + } +} diff --git a/Scripts/dev/deploy-sidepanels.sh b/Scripts/dev/deploy-sidepanels.sh new file mode 100755 index 0000000000..a8f0080250 --- /dev/null +++ b/Scripts/dev/deploy-sidepanels.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Deploy side-panels related files from a downloaded Noctalia zip. +# Usage: +# ./Scripts/dev/deploy-sidepanels.sh /path/to/noctalia.zip [/etc/xdg/quickshell/noctalia-shell] + +ZIP_PATH="${1:-}" +TARGET_ROOT="${2:-/etc/xdg/quickshell/noctalia-shell}" + +if [[ -z "$ZIP_PATH" ]]; then + echo "Usage: $0 /path/to/noctalia.zip [target_root]" + exit 1 +fi + +if [[ ! -f "$ZIP_PATH" ]]; then + echo "ERROR: Zip file not found: $ZIP_PATH" + exit 1 +fi + +if [[ ! -d "$TARGET_ROOT" ]]; then + echo "ERROR: Target root not found: $TARGET_ROOT" + exit 1 +fi + +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT + +UNZIP_DIR="$TMP_DIR/unzipped" +mkdir -p "$UNZIP_DIR" + +echo "==> Unzipping: $ZIP_PATH" +unzip -q "$ZIP_PATH" -d "$UNZIP_DIR" + +# Detect repo root by locating shell.qml (safe with spaces) +SOURCE_SHELL_QML="$(find "$UNZIP_DIR" -type f -name shell.qml -print -quit || true)" +if [[ -z "$SOURCE_SHELL_QML" ]]; then + echo "ERROR: Could not find shell.qml inside zip (cannot detect source root)." + exit 1 +fi +SOURCE_ROOT="$(dirname "$SOURCE_SHELL_QML")" + +echo "==> Source root: $SOURCE_ROOT" +echo "==> Target root: $TARGET_ROOT" + +STAMP="$(date +%Y%m%d-%H%M%S)" +BACKUP_ROOT="$TARGET_ROOT/.backup-sidepanels-$STAMP" +mkdir -p "$BACKUP_ROOT" + +# Side-panel feature files (new + changed) +FILES=( + "Assets/settings-default.json" + "Assets/settings-search-index.json" + "Commons/Settings.qml" + "Modules/MainScreen/MainScreen.qml" + "Modules/MainScreen/SideWidgetPanel.qml" + "Modules/Panels/Settings/SettingsContent.qml" + "Modules/Panels/Settings/SettingsPanel.qml" + "Modules/Panels/Settings/Tabs/Bar/BarTab.qml" + "Modules/Panels/Settings/Tabs/Bar/SidePanelsSubTab.qml" + "Modules/Panels/Settings/Tabs/SidePanels/SidePanelsTab.qml" +) + +copied=0 +missing=0 + +for rel in "${FILES[@]}"; do + src="$SOURCE_ROOT/$rel" + dst="$TARGET_ROOT/$rel" + + if [[ ! -f "$src" ]]; then + echo "WARN: Missing in zip, skipped: $rel" + ((missing+=1)) + continue + fi + + mkdir -p "$(dirname "$dst")" + mkdir -p "$(dirname "$BACKUP_ROOT/$rel")" + + if [[ -f "$dst" ]]; then + cp -a "$dst" "$BACKUP_ROOT/$rel" + fi + + cp -a "$src" "$dst" + echo "OK: $rel" + ((copied+=1)) +done + +echo +echo "==> Done" +echo "Copied: $copied" +echo "Missing: $missing" +echo "Backup: $BACKUP_ROOT" + +echo +echo "Now restart quickshell/noctalia (example):" +echo " pkill -f quickshell || true" +echo " qs -c noctalia-shell &"