From bb08c61b7628e079edf1803fb438802ee2f8b7a3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:16:58 +0200 Subject: [PATCH] sidebar: night light dialog --- .../modules/common/widgets/ConfigSwitch.qml | 2 + .../common/widgets/WindowDialogSlider.qml | 43 ++++++ .../sidebarRight/SidebarRightContent.qml | 60 +++++---- .../nightLight/NightLightDialog.qml | 125 ++++++++++++++++++ .../quickToggles/AbstractQuickPanel.qml | 5 +- .../quickToggles/AndroidQuickPanel.qml | 29 ++-- .../androidStyle/AndroidNightLightToggle.qml | 4 +- .../AndroidToggleDelegateChooser.qml | 8 +- .../quickshell/ii/services/Hyprsunset.qml | 15 ++- 9 files changed, 244 insertions(+), 47 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/WindowDialogSlider.qml create mode 100644 dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml index 13c43c552..02e0eabad 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml @@ -7,6 +7,7 @@ import QtQuick.Controls RippleButton { id: root property string buttonIcon + property alias iconSize: iconWidget.iconSize Layout.fillWidth: true implicitHeight: contentItem.implicitHeight + 8 * 2 @@ -17,6 +18,7 @@ RippleButton { contentItem: RowLayout { spacing: 10 OptionalMaterialSymbol { + id: iconWidget icon: root.buttonIcon opacity: root.enabled ? 1 : 0.4 iconSize: Appearance.font.pixelSize.larger diff --git a/dots/.config/quickshell/ii/modules/common/widgets/WindowDialogSlider.qml b/dots/.config/quickshell/ii/modules/common/widgets/WindowDialogSlider.qml new file mode 100644 index 000000000..5c6db1443 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/WindowDialogSlider.qml @@ -0,0 +1,43 @@ +pragma ComponentBehavior: Bound +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell.Widgets + +Column { + id: root + + property alias text: sliderName.text + property alias from: sliderWidget.from + property alias to: sliderWidget.to + property alias value: sliderWidget.value + property alias tooltipContent: sliderWidget.tooltipContent + property alias stopIndicatorValues: sliderWidget.stopIndicatorValues + + signal moved() + + spacing: -2 + ContentSubsectionLabel { + id: sliderName + visible: text?.length > 0 + text: "" + anchors { + left: parent.left + right: parent.right + } + } + StyledSlider { + id: sliderWidget + anchors { + left: parent.left + right: parent.right + leftMargin: 4 + rightMargin: leftMargin + } + configuration: StyledSlider.Configuration.S + onMoved: root.moved() + } +} diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml b/dots/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml index cf228d365..f2aee10fe 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml @@ -11,19 +11,22 @@ import Quickshell.Hyprland import qs.modules.sidebarRight.quickToggles import qs.modules.sidebarRight.quickToggles.classicStyle -import qs.modules.sidebarRight.wifiNetworks + import qs.modules.sidebarRight.bluetoothDevices +import qs.modules.sidebarRight.nightLight import qs.modules.sidebarRight.volumeMixer +import qs.modules.sidebarRight.wifiNetworks Item { id: root property int sidebarWidth: Appearance.sizes.sidebarWidth property int sidebarPadding: 10 property string settingsQmlPath: Quickshell.shellPath("settings.qml") - property bool showWifiDialog: false - property bool showBluetoothDialog: false property bool showAudioOutputDialog: false property bool showAudioInputDialog: false + property bool showBluetoothDialog: false + property bool showNightLightDialog: false + property bool showWifiDialog: false property bool editMode: false Connections { @@ -109,18 +112,20 @@ Item { } ToggleDialog { - id: wifiDialogLoader - shownPropertyString: "showWifiDialog" - dialog: WifiDialog {} - onShownChanged: { - if (!shown) return; - Network.enableWifi(); - Network.rescanWifi(); + shownPropertyString: "showAudioOutputDialog" + dialog: VolumeDialog { + isSink: true + } + } + + ToggleDialog { + shownPropertyString: "showAudioInputDialog" + dialog: VolumeDialog { + isSink: false } } ToggleDialog { - id: bluetoothDialogLoader shownPropertyString: "showBluetoothDialog" dialog: BluetoothDialog {} onShownChanged: { @@ -130,23 +135,21 @@ Item { Bluetooth.defaultAdapter.enabled = true; Bluetooth.defaultAdapter.discovering = true; } - } } ToggleDialog { - id: audioOutputDialogLoader - shownPropertyString: "showAudioOutputDialog" - dialog: VolumeDialog { - isSink: true - } + shownPropertyString: "showNightLightDialog" + dialog: NightLightDialog {} } ToggleDialog { - id: audioInputDialogLoader - shownPropertyString: "showAudioInputDialog" - dialog: VolumeDialog { - isSink: false + shownPropertyString: "showWifiDialog" + dialog: WifiDialog {} + onShownChanged: { + if (!shown) return; + Network.enableWifi(); + Network.rescanWifi(); } } @@ -186,18 +189,21 @@ Item { active: Config.options.sidebar.quickToggles.style === styleName Connections { target: quickPanelImplLoader.item - function onOpenWifiDialog() { - root.showWifiDialog = true; - } - function onOpenBluetoothDialog() { - root.showBluetoothDialog = true; - } function onOpenAudioOutputDialog() { root.showAudioOutputDialog = true; } function onOpenAudioInputDialog() { root.showAudioInputDialog = true; } + function onOpenBluetoothDialog() { + root.showBluetoothDialog = true; + } + function onOpenNightLightDialog() { + root.showNightLightDialog = true; + } + function onOpenWifiDialog() { + root.showWifiDialog = true; + } } } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml b/dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml new file mode 100644 index 000000000..25fb120f0 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml @@ -0,0 +1,125 @@ +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell.Io +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland + +WindowDialog { + id: root + property var screen: root.QsWindow.window?.screen + property var brightnessMonitor: Brightness.getMonitorForScreen(screen) + + WindowDialogTitle { + text: Translation.tr("Eye protection") + } + + WindowDialogSectionHeader { + text: Translation.tr("Night Light") + } + + WindowDialogSeparator { + Layout.topMargin: -22 + Layout.leftMargin: 0 + Layout.rightMargin: 0 + } + + Column { + id: nightLightColumn + Layout.topMargin: -16 + Layout.fillWidth: true + Layout.fillHeight: true + + ConfigSwitch { + anchors { + left: parent.left + right: parent.right + } + iconSize: Appearance.font.pixelSize.larger + buttonIcon: "lightbulb" + text: Translation.tr("Enable now") + checked: Hyprsunset.active + onCheckedChanged: { + Hyprsunset.toggle(checked) + } + } + + ConfigSwitch { + anchors { + left: parent.left + right: parent.right + } + iconSize: Appearance.font.pixelSize.larger + buttonIcon: "night_sight_auto" + text: Translation.tr("Automatic") + checked: Config.options.light.night.automatic + onCheckedChanged: { + Config.options.light.night.automatic = checked; + } + } + + WindowDialogSlider { + anchors { + left: parent.left + right: parent.right + leftMargin: 4 + rightMargin: 4 + } + text: Translation.tr("Color temperature") + from: 1000 + to: 20000 + stopIndicatorValues: [6000, to] + value: Config.options.light.night.colorTemperature + onMoved: Config.options.light.night.colorTemperature = value + tooltipContent: `${Math.round(value)}K` + } + } + + WindowDialogSectionHeader { + text: Translation.tr("Brightness") + } + + WindowDialogSeparator { + Layout.topMargin: -22 + Layout.leftMargin: 0 + Layout.rightMargin: 0 + } + + Column { + id: brightnessColumn + Layout.topMargin: -16 + Layout.fillWidth: true + // Layout.fillHeight: true + + WindowDialogSlider { + anchors { + left: parent.left + right: parent.right + leftMargin: 4 + rightMargin: 4 + } + // text: Translation.tr("Brightness") + value: root.brightnessMonitor.brightness + onMoved: root.brightnessMonitor.setBrightness(value) + } + } + + WindowDialogButtonRow { + Layout.fillWidth: true + + Item { + Layout.fillWidth: true + } + + DialogButton { + buttonText: Translation.tr("Done") + onClicked: root.dismiss() + } + } +} diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AbstractQuickPanel.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AbstractQuickPanel.qml index 6f2861409..c6ef4b9c8 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AbstractQuickPanel.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AbstractQuickPanel.qml @@ -7,8 +7,9 @@ Rectangle { radius: Appearance.rounding.normal color: Appearance.colors.colLayer1 - signal openWifiDialog() - signal openBluetoothDialog() signal openAudioOutputDialog() signal openAudioInputDialog() + signal openBluetoothDialog() + signal openNightLightDialog() + signal openWifiDialog() } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml index a3c94d7b7..353107822 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml @@ -12,15 +12,23 @@ AbstractQuickPanel { id: root property bool editMode: false Layout.fillWidth: true - implicitHeight: (editMode ? contentItem.implicitHeight : usedRows.implicitHeight) + root.padding * 2 + // Sizes + implicitHeight: (editMode ? contentItem.implicitHeight : usedRows.implicitHeight) + root.padding * 2 Behavior on implicitHeight { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } - property real spacing: 6 property real padding: 6 + readonly property real baseCellWidth: { + // This is the wrong calculation, but it looks correct in reality??? + // (theoretically spacing should be multiplied by 1 column less) + const availableWidth = root.width - (root.padding * 2) - (root.spacing * (root.columns)) + return availableWidth / root.columns + } + readonly property real baseCellHeight: 56 + // Toggles readonly property list availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile"] readonly property int columns: Config.options.sidebar.quickToggles.android.columns readonly property list toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : [] @@ -30,13 +38,6 @@ AbstractQuickPanel { return types.map(type => { return { type: type, size: 1 } }) } readonly property list unusedToggleRows: toggleRowsForList(unusedToggles) - readonly property real baseCellWidth: { - // This is the wrong calculation, but it looks correct in reality??? - // (theoretically spacing should be multiplied by 1 column less) - const availableWidth = root.width - (root.padding * 2) - (root.spacing * (root.columns)) - return availableWidth / root.columns - } - readonly property real baseCellHeight: 56 function toggleRowsForList(togglesList) { var rows = []; @@ -78,7 +79,10 @@ AbstractQuickPanel { delegate: ButtonGroup { id: toggleRow required property int index - property var modelData: root.toggleRows[index] + property var modelData: { + print(JSON.stringify(root.toggleRows[index])) + return root.toggleRows[index] + } property int startingIndex: { const rows = root.toggleRows; let sum = 0; @@ -100,10 +104,11 @@ AbstractQuickPanel { baseCellWidth: root.baseCellWidth baseCellHeight: root.baseCellHeight spacing: root.spacing - onOpenWifiDialog: root.openWifiDialog() - onOpenBluetoothDialog: root.openBluetoothDialog() onOpenAudioOutputDialog: root.openAudioOutputDialog() onOpenAudioInputDialog: root.openAudioInputDialog() + onOpenBluetoothDialog: root.openBluetoothDialog() + onOpenNightLightDialog: root.openNightLightDialog() + onOpenWifiDialog: root.openWifiDialog() } } } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml index 378dd0284..699d93dc3 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml @@ -19,7 +19,7 @@ AndroidQuickToggleButton { } altAction: () => { - Config.options.light.night.automatic = !Config.options.light.night.automatic + root.openMenu() } Component.onCompleted: { @@ -27,7 +27,7 @@ AndroidQuickToggleButton { } StyledToolTip { - text: Translation.tr("Night Light | Right-click to toggle Auto mode") + text: Translation.tr("Night Light | Right-click to configure") } } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml index 2cfaa9585..a8f79c843 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml @@ -14,10 +14,11 @@ DelegateChooser { required property real baseCellHeight required property real spacing required property int startingIndex - signal openWifiDialog() - signal openBluetoothDialog() signal openAudioOutputDialog() signal openAudioInputDialog() + signal openBluetoothDialog() + signal openNightLightDialog() + signal openWifiDialog() role: "type" @@ -90,6 +91,9 @@ DelegateChooser { baseCellHeight: root.baseCellHeight cellSpacing: root.spacing cellSize: modelData.size + onOpenMenu: { + root.openNightLightDialog() + } } } DelegateChoice { roleValue: "darkMode"; AndroidDarkModeToggle { diff --git a/dots/.config/quickshell/ii/services/Hyprsunset.qml b/dots/.config/quickshell/ii/services/Hyprsunset.qml index 2f7b3569f..2642025ad 100644 --- a/dots/.config/quickshell/ii/services/Hyprsunset.qml +++ b/dots/.config/quickshell/ii/services/Hyprsunset.qml @@ -4,6 +4,7 @@ import QtQuick import qs.modules.common import Quickshell import Quickshell.Io +import Quickshell.Hyprland /** * Simple hyprsunset service with automatic mode. @@ -111,18 +112,28 @@ Singleton { } } - function toggle() { + function toggle(active = undefined) { if (root.manualActive === undefined) { root.manualActive = root.active; root.manualActiveHour = root.clockHour; root.manualActiveMinute = root.clockMinute; } - root.manualActive = !root.manualActive; + root.manualActive = active !== undefined ? active : !root.manualActive; if (root.manualActive) { root.enable(); } else { root.disable(); } } + + // Change temp + Connections { + target: Config.options.light.night + onColorTemperatureChanged: { + if (!root.active) return; + Hyprland.dispatch(`hyprctl hyprsunset temperature ${Config.options.light.night.colorTemperature}`); + Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", `${Config.options.light.night.colorTemperature}`]); + } + } }