Decrease gamma when attempting to decrease brightness below 0 (#3122)

This commit is contained in:
Minh
2026-03-24 22:57:53 +01:00
committed by GitHub
17 changed files with 264 additions and 103 deletions
@@ -12,11 +12,11 @@ QuickToggleModel {
name: Translation.tr("Night Light") name: Translation.tr("Night Light")
statusText: (auto ? Translation.tr("Auto, ") : "") + (toggled ? Translation.tr("Active") : Translation.tr("Inactive")) statusText: (auto ? Translation.tr("Auto, ") : "") + (toggled ? Translation.tr("Active") : Translation.tr("Inactive"))
toggled: Hyprsunset.active toggled: Hyprsunset.temperatureActive
icon: auto ? "night_sight_auto" : "bedtime" icon: auto ? "night_sight_auto" : "bedtime"
mainAction: () => { mainAction: () => {
Hyprsunset.toggle() Hyprsunset.toggleTemperature()
} }
hasMenu: true hasMenu: true
@@ -17,6 +17,7 @@ Slider {
id: root id: root
property list<real> stopIndicatorValues: [1] property list<real> stopIndicatorValues: [1]
property list<real> dividerValues: []
enum Configuration { enum Configuration {
Wavy = 4, Wavy = 4,
XS = 12, XS = 12,
@@ -45,6 +46,7 @@ Slider {
property real handleHeight: (configuration === StyledSlider.Configuration.Wavy) ? 24 : Math.max(33, trackWidth + 9) property real handleHeight: (configuration === StyledSlider.Configuration.Wavy) ? 24 : Math.max(33, trackWidth + 9)
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
property real handleMargins: 4 property real handleMargins: 4
property real dividerMargins: 2
property real trackDotSize: 3 property real trackDotSize: 3
property bool usePercentTooltip: true property bool usePercentTooltip: true
property string tooltipContent: usePercentTooltip ? `${Math.round(((value - from) / (to - from)) * 100)}%` : `${Math.round(value)}` property string tooltipContent: usePercentTooltip ? `${Math.round(((value - from) / (to - from)) * 100)}%` : `${Math.round(value)}`
@@ -94,71 +96,94 @@ Slider {
} }
background: Item { background: Item {
id: background
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: parent.width anchors.horizontalCenter: parent.horizontalCenter
width: root.width
implicitHeight: trackWidth implicitHeight: trackWidth
property var normalized: root.dividerValues.map(v => (v - root.from) / (root.to - root.from))
property var filtered: normalized.filter(v => Math.abs(v - root.visualPosition) * effectiveDraggingWidth > handleMargins + handleWidth / 2 - dividerMargins)
property var leftValues: [0, ...filtered.filter(v => v < root.visualPosition), root.visualPosition]
property var rightValues: [root.visualPosition, ...filtered.filter(v => v > root.visualPosition), 1]
property var leftWidths: leftValues.map((v, i, a) => a[i + 1] - v).slice(0, -1)
property var rightWidths: rightValues.map((v, i, a) => a[i + 1] - v).slice(0, -1)
// Fill left // Fill left
Loader { Repeater {
anchors { model: background.leftWidths.length
verticalCenter: parent.verticalCenter
left: parent.left Loader {
} required property real index
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins) anchors.verticalCenter: background.verticalCenter
height: root.trackWidth property real leftMargin: index > 0 ? root.dividerMargins : 0
active: !root.wavy property real rightMargin: index < background.leftWidths.length - 1 ? root.dividerMargins : root.handleMargins
sourceComponent: Rectangle { x: background.leftValues[index] * root.effectiveDraggingWidth + leftMargin + (index > 0 ? leftPadding : 0)
color: root.highlightColor width: background.leftWidths[index] * root.effectiveDraggingWidth - leftMargin - rightMargin - (index === background.leftWidths.length - 1 ? handleWidth / 2 : 0) + (index === 0 ? leftPadding : 0)
topLeftRadius: root.trackRadius height: root.trackWidth
bottomLeftRadius: root.trackRadius active: !root.wavy
topRightRadius: root.unsharpenRadius sourceComponent: Rectangle {
bottomRightRadius: root.unsharpenRadius color: root.highlightColor
topLeftRadius: index === 0 ? root.trackRadius : root.unsharpenRadius
bottomLeftRadius: index === 0 ? root.trackRadius : root.unsharpenRadius
topRightRadius: root.unsharpenRadius
bottomRightRadius: root.unsharpenRadius
}
} }
} }
Loader { Repeater {
anchors { model: background.leftWidths.length
verticalCenter: parent.verticalCenter
left: parent.left Loader {
} required property int index
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins) anchors.verticalCenter: background.verticalCenter
height: root.height property real leftMargin: index > 0 ? root.dividerMargins : 0
active: root.wavy property real rightMargin: index < background.leftWidths.length - 1 ? root.dividerMargins : root.handleMargins
sourceComponent: WavyLine { x: background.leftValues[index] * root.effectiveDraggingWidth + leftMargin + (index > 0 ? leftPadding : 0)
id: wavyFill width: background.leftWidths[index] * root.effectiveDraggingWidth - leftMargin - rightMargin - (index === background.leftWidths.length - 1 ? handleWidth / 2 : 0) + (index === 0 ? leftPadding : 0)
frequency: root.waveFrequency height: root.height
fullLength: root.width active: root.wavy
color: root.highlightColor sourceComponent: WavyLine {
amplitudeMultiplier: root.wavy ? 0.5 : 0 id: wavyFill
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins) frequency: root.waveFrequency
height: root.trackWidth fullLength: root.width
Connections { color: root.highlightColor
target: root amplitudeMultiplier: root.wavy ? 0.5 : 0
function onValueChanged() { wavyFill.requestPaint(); } width: parent.width
function onHighlightColorChanged() { wavyFill.requestPaint(); } height: root.trackWidth
} Connections {
FrameAnimation { target: root
running: root.animateWave function onValueChanged() { wavyFill.requestPaint(); }
onTriggered: { function onHighlightColorChanged() { wavyFill.requestPaint(); }
wavyFill.requestPaint() }
FrameAnimation {
running: root.animateWave
onTriggered: {
wavyFill.requestPaint()
}
} }
} }
} }
} }
// Fill right // Fill right
Rectangle { Repeater {
anchors { model: background.rightWidths.length
verticalCenter: parent.verticalCenter
right: parent.right Rectangle {
required property int index
anchors.verticalCenter: background.verticalCenter
property real leftMargin: index > 0 ? root.dividerMargins : root.handleMargins
property real rightMargin: index < background.rightWidths.length - 1 ? root.dividerMargins : 0
x: background.rightValues[index] * root.effectiveDraggingWidth + leftMargin + (index === 0 ? handleWidth / 2 : 0) + leftPadding
width: background.rightWidths[index] * root.effectiveDraggingWidth - leftMargin - rightMargin - (index === 0 ? handleWidth / 2 : 0) + (index === background.rightWidths.length - 1 ? rightPadding : 0)
height: trackWidth
color: root.trackColor
topRightRadius: index === background.rightWidths.length - 1 ? root.trackRadius : root.unsharpenRadius
bottomRightRadius: index === background.rightWidths.length - 1 ? root.trackRadius : root.unsharpenRadius
topLeftRadius: root.unsharpenRadius
bottomLeftRadius: root.unsharpenRadius
} }
width: root.handleMargins + ((1 - root.visualPosition) * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
height: trackWidth
color: root.trackColor
topRightRadius: root.trackRadius
bottomRightRadius: root.trackRadius
topLeftRadius: root.unsharpenRadius
bottomLeftRadius: root.unsharpenRadius
} }
// Stop indicators // Stop indicators
@@ -177,7 +202,7 @@ Slider {
implicitWidth: root.handleWidth implicitWidth: root.handleWidth
implicitHeight: root.handleHeight implicitHeight: root.handleHeight
x: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2) x: root.leftPadding + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: Appearance.rounding.full radius: Appearance.rounding.full
color: root.handleColor color: root.handleColor
@@ -59,8 +59,8 @@ Item { // Bar content region
implicitWidth: leftSectionRowLayout.implicitWidth implicitWidth: leftSectionRowLayout.implicitWidth
implicitHeight: Appearance.sizes.baseBarHeight implicitHeight: Appearance.sizes.baseBarHeight
onScrollDown: root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness - 0.05) onScrollDown: Brightness.decreaseBrightness()
onScrollUp: root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness + 0.05) onScrollUp: Brightness.increaseBrightness()
onMovedAway: GlobalStates.osdBrightnessOpen = false onMovedAway: GlobalStates.osdBrightnessOpen = false
onPressed: event => { onPressed: event => {
if (event.button === Qt.LeftButton) if (event.button === Qt.LeftButton)
@@ -70,7 +70,7 @@ Item { // Bar content region
// Visual content // Visual content
ScrollHint { ScrollHint {
reveal: barLeftSideMouseArea.hovered reveal: barLeftSideMouseArea.hovered
icon: "light_mode" icon: Hyprsunset.gamma === 100 ? "light_mode" : "wb_twilight"
tooltipText: Translation.tr("Scroll to change brightness") tooltipText: Translation.tr("Scroll to change brightness")
side: "left" side: "left"
anchors.left: parent.left anchors.left: parent.left
@@ -25,6 +25,10 @@ Scope {
id: "brightness", id: "brightness",
sourceUrl: "indicators/BrightnessIndicator.qml" sourceUrl: "indicators/BrightnessIndicator.qml"
}, },
{
id: "gamma",
sourceUrl: "indicators/GammaIndicator.qml"
},
] ]
function triggerOsd() { function triggerOsd() {
@@ -52,6 +56,15 @@ Scope {
} }
} }
Connections {
target: Hyprsunset
function onGammaChangeAttempt() {
root.protectionMessage = "";
root.currentIndicator = "gamma";
root.triggerOsd();
}
}
Connections { Connections {
// Listen to volume changes // Listen to volume changes
target: Audio.sink?.audio ?? null target: Audio.sink?.audio ?? null
@@ -11,6 +11,8 @@ Item {
required property string name required property string name
property bool rotateIcon: false property bool rotateIcon: false
property bool scaleIcon: false property bool scaleIcon: false
property alias from: valueProgressBar.from
property alias to: valueProgressBar.to
property real valueIndicatorVerticalPadding: 9 property real valueIndicatorVerticalPadding: 9
property real valueIndicatorLeftPadding: 10 property real valueIndicatorLeftPadding: 10
@@ -9,7 +9,7 @@ OsdValueIndicator {
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen) property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
icon: Hyprsunset.active ? "routine" : "light_mode" icon: Hyprsunset.temperatureActive ? "routine" : "light_mode"
rotateIcon: true rotateIcon: true
scaleIcon: true scaleIcon: true
name: Translation.tr("Brightness") name: Translation.tr("Brightness")
@@ -0,0 +1,14 @@
import qs.services
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.modules.ii.onScreenDisplay
OsdValueIndicator {
id: rotateIcon
icon: "wb_twilight"
name: Translation.tr("Gamma")
from: Hyprsunset.gammaLowerLimit / 100
value: Hyprsunset.gamma / 100 ?? 0.5
}
@@ -98,7 +98,7 @@ Scope {
if (!Config.options.sidebar.cornerOpen.valueScroll) if (!Config.options.sidebar.cornerOpen.valueScroll)
return; return;
if (cornerWidget.isLeft) if (cornerWidget.isLeft)
cornerPanelWindow.brightnessMonitor.setBrightness(cornerPanelWindow.brightnessMonitor.brightness - 0.05); Brightness.decreaseBrightness()
else { else {
const currentVolume = Audio.value; const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2; const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
@@ -109,7 +109,7 @@ Scope {
if (!Config.options.sidebar.cornerOpen.valueScroll) if (!Config.options.sidebar.cornerOpen.valueScroll)
return; return;
if (cornerWidget.isLeft) if (cornerWidget.isLeft)
cornerPanelWindow.brightnessMonitor.setBrightness(cornerPanelWindow.brightnessMonitor.brightness + 0.05); Brightness.increaseBrightness()
else { else {
const currentVolume = Audio.value; const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2; const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
@@ -40,10 +40,25 @@ Rectangle {
visible: active visible: active
active: Config.options.sidebar.quickSliders.showBrightness active: Config.options.sidebar.quickSliders.showBrightness
sourceComponent: QuickSlider { sourceComponent: QuickSlider {
materialSymbol: "brightness_6" materialSymbol: "light_mode"
value: root.brightnessMonitor.brightness secondaryMaterialSymbol: "wb_twilight"
stopIndicatorValues: Hyprsunset.gamma !== 100 && root.brightnessMonitor?.brightness !== 0 ? [0.3 + root.brightnessMonitor?.brightness * 0.7] : []
value: Hyprsunset.gamma === 100? 0.3 + root.brightnessMonitor?.brightness * 0.7 : Hyprsunset.gamma / 100 * 0.3
tooltipContent: Hyprsunset.gamma === 100 ? `${Math.round(root.brightnessMonitor?.brightness * 100)}%` : `${Translation.tr("Gamma")} ${Hyprsunset.gamma}%`
onMoved: { onMoved: {
root.brightnessMonitor.setBrightness(value) if (value >= 0.3) {
// 0.3 - 1.0 brightness
root.brightnessMonitor.setBrightness((value - 0.3) / 0.7);
if (Hyprsunset.gamma !== 100) {
Hyprsunset.setGamma(100);
}
} else {
// 0 - 0.3 gamma
if (root.brightnessMonitor.brightness !== 0) {
root.brightnessMonitor.setBrightness(0);
}
Hyprsunset.setGamma(value * 100 / 0.3);
}
} }
} }
} }
@@ -84,16 +99,18 @@ Rectangle {
component QuickSlider: StyledSlider { component QuickSlider: StyledSlider {
id: quickSlider id: quickSlider
required property string materialSymbol required property string materialSymbol
property string secondaryMaterialSymbol
configuration: StyledSlider.Configuration.M configuration: StyledSlider.Configuration.M
stopIndicatorValues: [] stopIndicatorValues: []
dividerValues: secondaryMaterialSymbol.length > 0 ? [secondaryIcon.iconLocation] : []
MaterialSymbol { MaterialSymbol {
id: icon id: icon
property bool nearFull: quickSlider.value >= 0.9 property bool nearFull: quickSlider.value >= 0.9
anchors { anchors {
verticalCenter: parent.verticalCenter verticalCenter: quickSlider.verticalCenter
right: nearFull ? quickSlider.handle.right : parent.right right: nearFull ? quickSlider.handle.right : quickSlider.right
rightMargin: quickSlider.nearFull ? 14 : 8 rightMargin: nearFull ? 14 : 8
} }
iconSize: 20 iconSize: 20
color: nearFull ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer color: nearFull ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
@@ -105,7 +122,25 @@ Rectangle {
Behavior on anchors.rightMargin { Behavior on anchors.rightMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
} }
}
MaterialSymbol {
id: secondaryIcon
visible: secondaryMaterialSymbol.length > 0
property real iconLocation: 0.3
property bool nearIcon: iconLocation - quickSlider.value <= 0.1 && iconLocation - quickSlider.value > (quickSlider.handleWidth + 8 - 14) / quickSlider.effectiveDraggingWidth
anchors {
verticalCenter: quickSlider.verticalCenter
right: nearIcon ? quickSlider.handle.right : quickSlider.right
rightMargin: nearIcon ? 14 : (1 - iconLocation) * quickSlider.effectiveDraggingWidth + quickSlider.rightPadding + 8
}
iconSize: 20
color: quickSlider.value >= iconLocation - 0.1 ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
text: secondaryMaterialSymbol
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
} }
} }
} }
@@ -15,7 +15,7 @@ WindowDialog {
id: root id: root
property var screen: root.QsWindow.window?.screen property var screen: root.QsWindow.window?.screen
property var brightnessMonitor: Brightness.getMonitorForScreen(screen) property var brightnessMonitor: Brightness.getMonitorForScreen(screen)
backgroundHeight: 600 backgroundHeight: 700
WindowDialogTitle { WindowDialogTitle {
text: Translation.tr("Eye protection") text: Translation.tr("Eye protection")
@@ -44,9 +44,9 @@ WindowDialog {
iconSize: Appearance.font.pixelSize.larger iconSize: Appearance.font.pixelSize.larger
buttonIcon: "check" buttonIcon: "check"
text: Translation.tr("Enable now") text: Translation.tr("Enable now")
checked: Hyprsunset.active checked: Hyprsunset.temperatureActive
onCheckedChanged: { onCheckedChanged: {
Hyprsunset.toggle(checked) Hyprsunset.toggleTemperature(checked)
} }
} }
@@ -146,6 +146,33 @@ WindowDialog {
id: brightnessColumn id: brightnessColumn
Layout.topMargin: -16 Layout.topMargin: -16
Layout.fillWidth: true Layout.fillWidth: true
WindowDialogSlider {
anchors {
left: parent.left
right: parent.right
leftMargin: 4
rightMargin: 4
}
value: root.brightnessMonitor.brightness
onMoved: root.brightnessMonitor.setBrightness(value)
}
}
WindowDialogSectionHeader {
text: Translation.tr("Gamma")
}
WindowDialogSeparator {
Layout.topMargin: -22
Layout.leftMargin: 0
Layout.rightMargin: 0
}
Column {
id: gammaColumn
Layout.topMargin: -16
Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
WindowDialogSlider { WindowDialogSlider {
@@ -155,9 +182,10 @@ WindowDialog {
leftMargin: 4 leftMargin: 4
rightMargin: 4 rightMargin: 4
} }
// text: Translation.tr("Brightness") from: Hyprsunset.gammaLowerLimit / 100
value: root.brightnessMonitor.brightness value: Hyprsunset.gamma / 100
onMoved: root.brightnessMonitor.setBrightness(value) onMoved: Hyprsunset.setGamma(value * 100)
tooltipContent: `${Math.round(value * 100)}%`
} }
} }
@@ -6,10 +6,10 @@ import Quickshell.Io
QuickToggleButton { QuickToggleButton {
id: nightLightButton id: nightLightButton
toggled: Hyprsunset.active toggled: Hyprsunset.temperatureActive
buttonIcon: Config.options.light.night.automatic ? "night_sight_auto" : "bedtime" buttonIcon: Config.options.light.night.automatic ? "night_sight_auto" : "bedtime"
onClicked: { onClicked: {
Hyprsunset.toggle() Hyprsunset.toggleTemperature()
} }
altAction: () => { altAction: () => {
@@ -54,8 +54,8 @@ Item { // Bar content region
height: (root.height - middleSection.height) / 2 height: (root.height - middleSection.height) / 2
width: Appearance.sizes.verticalBarWidth width: Appearance.sizes.verticalBarWidth
onScrollDown: root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness - 0.05) onScrollDown: Brightness.decreaseBrightness()
onScrollUp: root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness + 0.05) onScrollUp: Brightness.increaseBrightness()
onMovedAway: GlobalStates.osdBrightnessOpen = false onMovedAway: GlobalStates.osdBrightnessOpen = false
onPressed: event => { onPressed: event => {
if (event.button === Qt.LeftButton) if (event.button === Qt.LeftButton)
@@ -87,9 +87,9 @@ Item {
name: Translation.tr("Enable now") name: Translation.tr("Enable now")
description: Translation.tr("More comfortable viewing at night") description: Translation.tr("More comfortable viewing at night")
iconName: WIcons.nightLightIcon iconName: WIcons.nightLightIcon
checked: Hyprsunset.active checked: Hyprsunset.temperatureActive
onCheckedChanged: { onCheckedChanged: {
Hyprsunset.toggle(checked); Hyprsunset.toggleTemperature(checked);
} }
} }
@@ -71,7 +71,7 @@ Singleton {
property string bluetoothIcon: BluetoothStatus.connected ? "bluetooth-connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth-disabled" property string bluetoothIcon: BluetoothStatus.connected ? "bluetooth-connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth-disabled"
property string nightLightIcon: Hyprsunset.active ? "weather-moon" : "weather-moon-off" property string nightLightIcon: Hyprsunset.temperatureActive ? "weather-moon" : "weather-moon-off"
property string notificationsIcon: Notifications.silent ? "alert-snooze" : "alert" property string notificationsIcon: Notifications.silent ? "alert-snooze" : "alert"
@@ -28,6 +28,12 @@ Singleton {
} }
function increaseBrightness(): void { function increaseBrightness(): void {
// if gamma is not yet 100, first increase gamma
if (Hyprsunset.gamma !== 100) {
Hyprsunset.setGamma(Hyprsunset.gamma + 5);
return;
}
const focusedName = Hyprland.focusedMonitor.name; const focusedName = Hyprland.focusedMonitor.name;
const monitor = monitors.find(m => focusedName === m.screen.name); const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor) if (monitor)
@@ -37,8 +43,12 @@ Singleton {
function decreaseBrightness(): void { function decreaseBrightness(): void {
const focusedName = Hyprland.focusedMonitor.name; const focusedName = Hyprland.focusedMonitor.name;
const monitor = monitors.find(m => focusedName === m.screen.name); const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor) if (monitor && monitor.brightness > 0)
monitor.setBrightness(monitor.brightness - 0.05); monitor.setBrightness(monitor.brightness - 0.05);
// if brightness is 0, then decrease gamma
else {
Hyprsunset.setGamma(Hyprsunset.gamma - 5);
}
} }
reloadableId: "brightness" reloadableId: "brightness"
@@ -13,13 +13,18 @@ import Quickshell.Hyprland
*/ */
Singleton { Singleton {
id: root id: root
signal gammaChangeAttempt()
readonly property real gammaLowerLimit: 25
property string from: Config.options?.light?.night?.from ?? "19:00" property string from: Config.options?.light?.night?.from ?? "19:00"
property string to: Config.options?.light?.night?.to ?? "06:30" property string to: Config.options?.light?.night?.to ?? "06:30"
property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true) property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true)
property int colorTemperature: Config.options?.light?.night?.colorTemperature ?? 5000 property int colorTemperature: Config.options?.light?.night?.colorTemperature ?? 5000
property int gamma: 100
property bool shouldBeOn property bool shouldBeOn
property bool firstEvaluation: true property bool firstEvaluation: true
property bool active: false property bool temperatureActive: false
property int fromHour: Number(from.split(":")[0]) property int fromHour: Number(from.split(":")[0])
property int fromMinute: Number(from.split(":")[1]) property int fromMinute: Number(from.split(":")[1])
@@ -71,24 +76,52 @@ Singleton {
if (!root.automatic || root.manualActive !== undefined) if (!root.automatic || root.manualActive !== undefined)
return; return;
if (root.shouldBeOn) { if (root.shouldBeOn) {
root.enable(); root.enableTemperature();
} else { } else {
root.disable(); root.disableTemperature();
} }
} }
function load() { } // Dummy to force init function startHyprsunset() {
Quickshell.execDetached(["bash", "-c", `pidof hyprsunset || hyprsunset`]);
function enable() {
root.active = true;
// console.log("[Hyprsunset] Enabling");
Quickshell.execDetached(["bash", "-c", `pidof hyprsunset || hyprsunset --temperature ${root.colorTemperature}`]);
} }
function disable() { function load() {
root.active = false; root.startHyprsunset();
updateHyprsunset.restart();
}
Timer {
id: updateHyprsunset
interval: 100
repeat: false
onTriggered: {
root.ensureState();
root.setGamma(root.gamma);
}
}
function enableTemperature() {
root.temperatureActive = true;
// console.log("[Hyprsunset] Enabling");
root.startHyprsunset();
Quickshell.execDetached(["bash", "-c", `hyprctl hyprsunset temperature ${root.colorTemperature}`]);
}
function disableTemperature() {
root.temperatureActive = false;
// console.log("[Hyprsunset] Disabling"); // console.log("[Hyprsunset] Disabling");
Quickshell.execDetached(["bash", "-c", `pkill hyprsunset`]); Quickshell.execDetached(["hyprctl", "hyprsunset", "identity"]);
}
function setGamma(gamma) {
root.gamma = Math.max(root.gammaLowerLimit, Math.min(100, gamma));
root.gammaChangeAttempt();
root.startHyprsunset();
Quickshell.execDetached(["bash", "-c", `hyprctl hyprsunset gamma ${root.gamma}`]);
} }
function fetchState() { function fetchState() {
@@ -104,26 +137,26 @@ Singleton {
onStreamFinished: { onStreamFinished: {
const output = stateCollector.text.trim(); const output = stateCollector.text.trim();
if (output.length == 0 || output.startsWith("Couldn't")) if (output.length == 0 || output.startsWith("Couldn't"))
root.active = false; root.temperatureActive = false;
else else
root.active = (output != "6500"); // 6500 is the default when off root.temperatureActive = (output != "6500"); // 6500 is the default when off
// console.log("[Hyprsunset] Fetched state:", output, "->", root.active); // console.log("[Hyprsunset] Fetched state:", output, "->", root.temperatureActive);
} }
} }
} }
function toggle(active = undefined) { function toggleTemperature(active = undefined) {
if (root.manualActive === undefined) { if (root.manualActive === undefined) {
root.manualActive = root.active; root.manualActive = root.temperatureActive;
root.manualActiveHour = root.clockHour; root.manualActiveHour = root.clockHour;
root.manualActiveMinute = root.clockMinute; root.manualActiveMinute = root.clockMinute;
} }
root.manualActive = active !== undefined ? active : !root.manualActive; root.manualActive = active !== undefined ? active : !root.manualActive;
if (root.manualActive) { if (root.manualActive) {
root.enable(); root.enableTemperature();
} else { } else {
root.disable(); root.disableTemperature();
} }
} }
@@ -131,7 +164,7 @@ Singleton {
Connections { Connections {
target: Config.options.light.night target: Config.options.light.night
function onColorTemperatureChanged() { function onColorTemperatureChanged() {
if (!root.active) return; if (!root.temperatureActive) return;
Hyprland.dispatch(`hyprctl hyprsunset temperature ${Config.options.light.night.colorTemperature}`); Hyprland.dispatch(`hyprctl hyprsunset temperature ${Config.options.light.night.colorTemperature}`);
Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", `${Config.options.light.night.colorTemperature}`]); Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", `${Config.options.light.night.colorTemperature}`]);
} }
@@ -81,6 +81,7 @@
"Unknown function call: %1": "Unknown function call: %1", "Unknown function call: %1": "Unknown function call: %1",
"Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls": "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls", "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls": "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls",
"Volume": "Volume", "Volume": "Volume",
"Gamma": "Gamma",
"Medium": "Medium", "Medium": "Medium",
"Copy code": "Copy code", "Copy code": "Copy code",
"Exceeded max allowed": "Exceeded max allowed", "Exceeded max allowed": "Exceeded max allowed",