310 lines
10 KiB
QML
310 lines
10 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import "root:/Data" as Data
|
|
|
|
// Night light widget with pure Qt overlay (no external dependencies)
|
|
Rectangle {
|
|
id: root
|
|
property var shell: null
|
|
color: Qt.darker(Data.ThemeManager.bgColor, 1.15)
|
|
radius: 20
|
|
|
|
property bool containsMouse: nightLightMouseArea.containsMouse
|
|
property bool isActive: Data.Settings.nightLightEnabled
|
|
property real warmth: Data.Settings.nightLightWarmth // 0=no filter, 1=very warm (0-1 scale)
|
|
property real strength: isActive ? warmth : 0
|
|
property bool autoSchedulerActive: false // Flag to prevent manual override during auto changes
|
|
|
|
signal entered
|
|
signal exited
|
|
|
|
// Night light overlay window
|
|
property var overlayWindow: null
|
|
|
|
// Hover state management for parent components
|
|
onContainsMouseChanged: {
|
|
if (containsMouse) {
|
|
entered();
|
|
} else {
|
|
exited();
|
|
}
|
|
}
|
|
|
|
// Background with warm tint when active
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: parent.radius
|
|
color: isActive ? Qt.rgba(1.0, 0.6, 0.2, 0.15) : "transparent"
|
|
|
|
Behavior on color {
|
|
ColorAnimation {
|
|
duration: 300
|
|
}
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: nightLightMouseArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
|
|
// Right-click to cycle through warmth levels
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
onClicked: function (mouse) {
|
|
if (mouse.button === Qt.RightButton) {
|
|
cycleWarmth();
|
|
} else {
|
|
toggleNightLight();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Night light icon with dynamic color
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: isActive ? "light_mode" : "dark_mode"
|
|
font.pixelSize: 24
|
|
font.family: "Material Symbols Outlined"
|
|
color: isActive ? Qt.rgba(1.0, 0.8 - strength * 0.3, 0.4 - strength * 0.2, 1.0) : // Warm orange when active
|
|
(containsMouse ? Data.ThemeManager.accentColor : Data.ThemeManager.fgColor)
|
|
|
|
Behavior on color {
|
|
ColorAnimation {
|
|
duration: 200
|
|
}
|
|
}
|
|
}
|
|
|
|
// Warmth indicator dots
|
|
Row {
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 6
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: 3
|
|
visible: isActive && containsMouse
|
|
|
|
Repeater {
|
|
model: 3
|
|
delegate: Rectangle {
|
|
width: 4
|
|
height: 4
|
|
radius: 2
|
|
color: index < Math.ceil(warmth * 3) ? Qt.rgba(1.0, 0.7 - index * 0.2, 0.3, 0.8) : Qt.rgba(Data.ThemeManager.fgColor.r, Data.ThemeManager.fgColor.g, Data.ThemeManager.fgColor.b, 0.3)
|
|
|
|
Behavior on color {
|
|
ColorAnimation {
|
|
duration: 150
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Watch for settings changes
|
|
Connections {
|
|
target: Data.Settings
|
|
function onNightLightEnabledChanged() {
|
|
if (Data.Settings.nightLightEnabled) {
|
|
createOverlay();
|
|
} else {
|
|
removeOverlay();
|
|
}
|
|
|
|
// Set manual override flag if this wasn't an automatic change
|
|
if (!autoSchedulerActive) {
|
|
Data.Settings.nightLightManualOverride = true;
|
|
Data.Settings.nightLightManuallyEnabled = Data.Settings.nightLightEnabled;
|
|
console.log("Manual night light change detected - override enabled, manually set to:", Data.Settings.nightLightEnabled);
|
|
}
|
|
}
|
|
function onNightLightWarmthChanged() {
|
|
updateOverlay();
|
|
}
|
|
}
|
|
|
|
// Functions to control night light
|
|
function toggleNightLight() {
|
|
Data.Settings.nightLightEnabled = !Data.Settings.nightLightEnabled;
|
|
}
|
|
|
|
function cycleWarmth() {
|
|
// Cycle through warmth levels: 0.2 -> 0.4 -> 0.6 -> 1.0 -> 0.2
|
|
var newWarmth = warmth >= 1.0 ? 0.2 : (warmth >= 0.6 ? 1.0 : warmth + 0.2);
|
|
Data.Settings.nightLightWarmth = newWarmth;
|
|
}
|
|
|
|
function createOverlay() {
|
|
if (overlayWindow)
|
|
return;
|
|
|
|
var qmlString = `
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
|
|
PanelWindow {
|
|
id: nightLightOverlay
|
|
screen: Quickshell.primaryScreen || Quickshell.screens[0]
|
|
|
|
anchors.top: true
|
|
anchors.left: true
|
|
anchors.right: true
|
|
anchors.bottom: true
|
|
|
|
color: "transparent"
|
|
|
|
WlrLayershell.layer: WlrLayershell.Overlay
|
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
WlrLayershell.namespace: "quickshell-nightlight"
|
|
exclusiveZone: 0
|
|
|
|
// Click-through overlay
|
|
mask: Region {}
|
|
|
|
Rectangle {
|
|
id: overlayRect
|
|
anchors.fill: parent
|
|
color: "transparent" // Initial color, will be set by parent
|
|
|
|
// Smooth transitions when warmth changes
|
|
Behavior on color {
|
|
ColorAnimation { duration: 300 }
|
|
}
|
|
}
|
|
|
|
// Function to update overlay color
|
|
function updateColor(newWarmth) {
|
|
overlayRect.color = Qt.rgba(1.0, 0.8 - newWarmth * 0.4, 0.3 - newWarmth * 0.25, 0.1 + newWarmth * 0.2)
|
|
}
|
|
}
|
|
`
|
|
|
|
try {
|
|
overlayWindow = Qt.createQmlObject(qmlString, root);
|
|
// Set initial color
|
|
updateOverlay();
|
|
} catch (e) {
|
|
console.error("Failed to create night light overlay:", e);
|
|
}
|
|
}
|
|
|
|
function updateOverlay() {
|
|
if (overlayWindow && overlayWindow.updateColor) {
|
|
overlayWindow.updateColor(warmth);
|
|
}
|
|
}
|
|
|
|
function removeOverlay() {
|
|
if (overlayWindow) {
|
|
overlayWindow.destroy();
|
|
overlayWindow = null;
|
|
}
|
|
}
|
|
|
|
// Preset warmth levels for easy access
|
|
function setLow() {
|
|
Data.Settings.nightLightWarmth = 0.2;
|
|
} // Light warmth
|
|
function setMedium() {
|
|
Data.Settings.nightLightWarmth = 0.4;
|
|
} // Medium warmth
|
|
function setHigh() {
|
|
Data.Settings.nightLightWarmth = 0.6;
|
|
} // High warmth
|
|
function setMax() {
|
|
Data.Settings.nightLightWarmth = 1.0;
|
|
} // Maximum warmth
|
|
|
|
// Auto-enable based on time (basic sunset/sunrise simulation)
|
|
Timer {
|
|
interval: 60000 // Check every minute
|
|
running: true
|
|
repeat: true
|
|
onTriggered: checkAutoEnable()
|
|
}
|
|
|
|
function checkAutoEnable() {
|
|
if (!Data.Settings.nightLightAuto)
|
|
return;
|
|
var now = new Date();
|
|
var hour = now.getHours();
|
|
var minute = now.getMinutes();
|
|
var startHour = Data.Settings.nightLightStartHour || 20;
|
|
var endHour = Data.Settings.nightLightEndHour || 6;
|
|
|
|
// Handle overnight schedules (e.g., 20:00 to 6:00)
|
|
var shouldBeActive = false;
|
|
if (startHour > endHour) {
|
|
// Overnight: active from startHour onwards OR before endHour
|
|
shouldBeActive = (hour >= startHour || hour < endHour);
|
|
} else if (startHour < endHour) {
|
|
// Same day: active between startHour and endHour
|
|
shouldBeActive = (hour >= startHour && hour < endHour);
|
|
} else {
|
|
// startHour === endHour: never auto-enable
|
|
shouldBeActive = false;
|
|
}
|
|
|
|
// Debug logging
|
|
console.log(`Night Light Auto Check: ${hour}:${minute.toString().padStart(2, '0')} - Should be active: ${shouldBeActive}, Currently active: ${Data.Settings.nightLightEnabled}, Manual override: ${Data.Settings.nightLightManualOverride}`);
|
|
|
|
// Smart override logic - only block conflicting actions
|
|
if (Data.Settings.nightLightManualOverride) {
|
|
// If user manually enabled, allow auto-disable but block auto-enable
|
|
if (Data.Settings.nightLightManuallyEnabled && !shouldBeActive && Data.Settings.nightLightEnabled) {
|
|
console.log("Auto-disabling night light (respecting schedule after manual enable)");
|
|
autoSchedulerActive = true;
|
|
Data.Settings.nightLightEnabled = false;
|
|
Data.Settings.nightLightManualOverride = false; // Reset after respecting schedule
|
|
autoSchedulerActive = false;
|
|
return;
|
|
} else
|
|
// If user manually disabled, block auto-enable until next cycle
|
|
if (!Data.Settings.nightLightManuallyEnabled && shouldBeActive && !Data.Settings.nightLightEnabled) {
|
|
// Check if this is the start of a new schedule cycle
|
|
var isNewCycle = (hour === startHour && minute === 0);
|
|
if (isNewCycle) {
|
|
console.log("New schedule cycle starting - resetting manual override");
|
|
Data.Settings.nightLightManualOverride = false;
|
|
} else {
|
|
console.log("Manual disable override active - skipping auto-enable");
|
|
return;
|
|
}
|
|
} else
|
|
// Other cases - reset override and continue
|
|
{
|
|
Data.Settings.nightLightManualOverride = false;
|
|
}
|
|
}
|
|
|
|
// Auto-enable when schedule starts
|
|
if (shouldBeActive && !Data.Settings.nightLightEnabled) {
|
|
console.log("Auto-enabling night light");
|
|
autoSchedulerActive = true;
|
|
Data.Settings.nightLightEnabled = true;
|
|
autoSchedulerActive = false;
|
|
} else
|
|
// Auto-disable when schedule ends
|
|
if (!shouldBeActive && Data.Settings.nightLightEnabled) {
|
|
console.log("Auto-disabling night light");
|
|
autoSchedulerActive = true;
|
|
Data.Settings.nightLightEnabled = false;
|
|
autoSchedulerActive = false;
|
|
}
|
|
}
|
|
|
|
// Cleanup on destruction
|
|
Component.onDestruction: {
|
|
removeOverlay();
|
|
}
|
|
|
|
// Initialize overlay state based on settings
|
|
Component.onCompleted: {
|
|
if (Data.Settings.nightLightEnabled) {
|
|
createOverlay();
|
|
}
|
|
}
|
|
}
|