Merge branch 'main' of github.com:noctalia-dev/noctalia-shell
This commit is contained in:
commit
8148c0fa29
9 changed files with 467 additions and 12 deletions
85
Modules/Bar/Widgets/NightLight.qml
Normal file
85
Modules/Bar/Widgets/NightLight.qml
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Modules.SettingsPanel
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property ShellScreen screen
|
||||
property real scaling: ScalingService.scale(screen)
|
||||
|
||||
implicitWidth: pill.width
|
||||
implicitHeight: pill.height
|
||||
visible: true
|
||||
|
||||
function getIcon() {
|
||||
if (!NightLightService.enabled) {
|
||||
return "light_mode"
|
||||
}
|
||||
return NightLightService.isActive ? "dark_mode" : "light_mode"
|
||||
}
|
||||
|
||||
function getTooltipText() {
|
||||
if (!NightLightService.enabled) {
|
||||
return "Night Light: Disabled\nLeft click to open settings.\nRight click to enable."
|
||||
}
|
||||
|
||||
var status = NightLightService.isActive ? "Active" : "Inactive (outside schedule)"
|
||||
var warmth = Math.round(NightLightService.warmth * 10)
|
||||
var schedule = NightLightService.autoSchedule ? `Schedule: ${NightLightService.startTime} - ${NightLightService.stopTime}` : "Manual mode"
|
||||
|
||||
return `Night Light: ${status}\nWarmth: ${warmth}/10\n${schedule}\nLeft click to open settings.\nRight click to toggle.`
|
||||
}
|
||||
|
||||
NPill {
|
||||
id: pill
|
||||
icon: getIcon()
|
||||
iconCircleColor: NightLightService.isActive ? Color.mSecondary : Color.mOnSurfaceVariant
|
||||
collapsedIconColor: NightLightService.isActive ? Color.mOnSecondary : Color.mOnSurface
|
||||
autoHide: false
|
||||
text: NightLightService.enabled ? (NightLightService.isActive ? "ON" : "OFF") : "OFF"
|
||||
tooltipText: getTooltipText()
|
||||
|
||||
onClicked: {
|
||||
// Left click - open settings
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel")
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Display
|
||||
settingsPanel.open(screen)
|
||||
}
|
||||
|
||||
onRightClicked: {
|
||||
// Right click - toggle night light
|
||||
NightLightService.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
// Update when service state changes
|
||||
Connections {
|
||||
target: NightLightService
|
||||
function onEnabledChanged() {
|
||||
pill.icon = getIcon()
|
||||
pill.text = NightLightService.enabled ? (NightLightService.isActive ? "ON" : "OFF") : "OFF"
|
||||
pill.tooltipText = getTooltipText()
|
||||
}
|
||||
function onIsActiveChanged() {
|
||||
pill.icon = getIcon()
|
||||
pill.text = NightLightService.enabled ? (NightLightService.isActive ? "ON" : "OFF") : "OFF"
|
||||
pill.tooltipText = getTooltipText()
|
||||
}
|
||||
function onWarmthChanged() {
|
||||
pill.tooltipText = getTooltipText()
|
||||
}
|
||||
function onStartTimeChanged() {
|
||||
pill.tooltipText = getTooltipText()
|
||||
}
|
||||
function onStopTimeChanged() {
|
||||
pill.tooltipText = getTooltipText()
|
||||
}
|
||||
function onAutoScheduleChanged() {
|
||||
pill.tooltipText = getTooltipText()
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Modules/NightLight/NightLight.qml
Normal file
65
Modules/NightLight/NightLight.qml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
delegate: Loader {
|
||||
required property ShellScreen modelData
|
||||
readonly property real scaling: ScalingService.scale(modelData)
|
||||
|
||||
active: NightLightService.enabled
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
id: nightlightWindow
|
||||
|
||||
screen: modelData
|
||||
visible: NightLightService.isActive
|
||||
color: Color.transparent
|
||||
|
||||
mask: Region {}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
WlrLayershell.namespace: "noctalia-nightlight"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: NightLightService.overlayColor
|
||||
}
|
||||
|
||||
// Safe connection that checks if the window still exists
|
||||
Connections {
|
||||
target: NightLightService
|
||||
function onIsActiveChanged() {
|
||||
if (nightlightWindow && typeof nightlightWindow.visible !== 'undefined') {
|
||||
nightlightWindow.visible = NightLightService.isActive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup when component is being destroyed
|
||||
Component.onDestruction: {
|
||||
Logger.log("NightLight", "PanelWindow being destroyed")
|
||||
}
|
||||
}
|
||||
|
||||
// Safe state changes
|
||||
onActiveChanged: {
|
||||
if (!active) {
|
||||
Logger.log("NightLight", "Loader deactivating for screen:", modelData.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,24 @@ Item {
|
|||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
// Time dropdown options (00:00 .. 23:30)
|
||||
ListModel {
|
||||
id: timeOptions
|
||||
}
|
||||
Component.onCompleted: {
|
||||
for (var h = 0; h < 24; h++) {
|
||||
for (var m = 0; m < 60; m += 30) {
|
||||
var hh = ("0" + h).slice(-2)
|
||||
var mm = ("0" + m).slice(-2)
|
||||
var key = hh + ":" + mm
|
||||
timeOptions.append({
|
||||
"key": key,
|
||||
"name": key
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions to update arrays immutably
|
||||
function addMonitor(list, name) {
|
||||
const arr = (list || []).slice()
|
||||
|
|
@ -209,6 +227,154 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Night Light Section
|
||||
NText {
|
||||
text: "Night Light"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Reduce blue light emission to help you sleep better and reduce eye strain."
|
||||
font.pointSize: Style.fontSize * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: parent.width - (Style.marginL * 2 * scaling)
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: "Enable Night Light"
|
||||
description: "Apply a warm color filter to reduce blue light emission."
|
||||
checked: NightLightService.enabled
|
||||
onToggled: checked => {
|
||||
Settings.data.nightLight.enabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: "Auto Schedule"
|
||||
description: "Automatically enable night light based on time schedule."
|
||||
checked: NightLightService.autoSchedule
|
||||
enabled: NightLightService.enabled
|
||||
onToggled: checked => {
|
||||
NightLightService.setAutoSchedule(checked)
|
||||
}
|
||||
}
|
||||
|
||||
// Warmth settings
|
||||
NText {
|
||||
text: "Warmth"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
enabled: NightLightService.enabled
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Higher values create warmer (more orange) light, lower values create cooler (more blue) light."
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
enabled: NightLightService.enabled
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
enabled: NightLightService.enabled
|
||||
|
||||
NSlider {
|
||||
id: warmthSlider
|
||||
from: 0
|
||||
to: 10
|
||||
stepSize: 1
|
||||
value: Math.round(NightLightService.warmth * 10)
|
||||
onPressedChanged: {
|
||||
if (!pressed) {
|
||||
NightLightService.setWarmth(value / 10)
|
||||
}
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 150 * scaling
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: NightLightService
|
||||
function onWarmthChanged() {
|
||||
if (!warmthSlider.pressed) {
|
||||
warmthSlider.value = Math.round(NightLightService.warmth * 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: `${warmthSlider.value}`
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.minimumWidth: 60 * scaling
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule settings
|
||||
NText {
|
||||
text: "Schedule"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
enabled: NightLightService.enabled
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
Layout.fillWidth: true
|
||||
enabled: NightLightService.enabled
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXXS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "Start Time"
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
model: timeOptions
|
||||
currentKey: NightLightService.startTime
|
||||
placeholder: "Select time"
|
||||
onSelected: function (key) {
|
||||
NightLightService.setSchedule(key, NightLightService.stopTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXXS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "Stop Time"
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
model: timeOptions
|
||||
currentKey: NightLightService.stopTime
|
||||
placeholder: "Select time"
|
||||
onSelected: function (key) {
|
||||
NightLightService.setSchedule(NightLightService.startTime, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue