make bg clock draggable

This commit is contained in:
end-4
2025-11-05 00:07:38 +01:00
parent 67695c8edb
commit 0e7422c335
23 changed files with 521 additions and 353 deletions
@@ -4,6 +4,7 @@ import qs
import qs.services import qs.services
import qs.modules.common import qs.modules.common
import qs.modules.common.widgets import qs.modules.common.widgets
import qs.modules.common.widgets.widgetCanvas
import qs.modules.common.functions as CF import qs.modules.common.functions as CF
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
@@ -13,18 +14,11 @@ import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.modules.background.cookieClock import qs.modules.background.widgets
import qs.modules.background.widgets.clock
Variants { Variants {
id: root id: root
readonly property bool fixedClockPosition: Config.options.background.clock.fixedPosition
readonly property real fixedClockX: Config.options.background.clock.x
readonly property real fixedClockY: Config.options.background.clock.y
readonly property real clockSizePadding: 20
readonly property real screenSizePadding: 50
readonly property string clockStyle: Config.options.background.clock.style
readonly property bool showCookieQuote: Config.options.background.showQuote && Config.options.background.quote !== "" && !GlobalStates.screenLocked && Config.options.background.clock.style === "cookie"
readonly property real clockParallaxFactor: Config.options.background.parallax.clockFactor // 0 = full parallax, 1 = no parallax
model: Quickshell.screens model: Quickshell.screens
PanelWindow { PanelWindow {
@@ -46,9 +40,9 @@ Variants {
property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov") property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov")
property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath
property bool wallpaperSafetyTriggered: { property bool wallpaperSafetyTriggered: {
const enabled = Config.options.workSafety.enable.wallpaper const enabled = Config.options.workSafety.enable.wallpaper;
const sensitiveWallpaper = (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.workSafety.triggerCondition.fileKeywords)) const sensitiveWallpaper = (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.workSafety.triggerCondition.fileKeywords));
const sensitiveNetwork = (CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) const sensitiveNetwork = (CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords));
return enabled && sensitiveWallpaper && sensitiveNetwork; return enabled && sensitiveWallpaper && sensitiveNetwork;
} }
property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height) property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height)
@@ -59,18 +53,6 @@ Variants {
property real movableXSpace: ((wallpaperWidth / wallpaperToScreenRatio * effectiveWallpaperScale) - screen.width) / 2 property real movableXSpace: ((wallpaperWidth / wallpaperToScreenRatio * effectiveWallpaperScale) - screen.width) / 2
property real movableYSpace: ((wallpaperHeight / wallpaperToScreenRatio * effectiveWallpaperScale) - screen.height) / 2 property real movableYSpace: ((wallpaperHeight / wallpaperToScreenRatio * effectiveWallpaperScale) - screen.height) / 2
readonly property bool verticalParallax: (Config.options.background.parallax.autoVertical && wallpaperHeight > wallpaperWidth) || Config.options.background.parallax.vertical readonly property bool verticalParallax: (Config.options.background.parallax.autoVertical && wallpaperHeight > wallpaperWidth) || Config.options.background.parallax.vertical
// Position
property real clockX: (modelData.width / 2)
property real clockY: (modelData.height / 2)
property var textHorizontalAlignment: {
if ((Config.options.lock.centerClock && GlobalStates.screenLocked) || wallpaperSafetyTriggered)
return Text.AlignHCenter;
if (clockX < screen.width / 3)
return Text.AlignLeft;
if (clockX > screen.width * 2 / 3)
return Text.AlignRight;
return Text.AlignHCenter;
}
// Colors // Colors
property bool shouldBlur: (GlobalStates.screenLocked && Config.options.lock.blur.enable) property bool shouldBlur: (GlobalStates.screenLocked && Config.options.lock.blur.enable)
property color dominantColor: Appearance.colors.colPrimary // Default, to be changed property color dominantColor: Appearance.colors.colPrimary // Default, to be changed
@@ -97,8 +79,9 @@ Variants {
right: true right: true
} }
color: { color: {
if (!bgRoot.wallpaperSafetyTriggered || bgRoot.wallpaperIsVideo) return "transparent"; if (!bgRoot.wallpaperSafetyTriggered || bgRoot.wallpaperIsVideo)
return CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75) return "transparent";
return CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75);
} }
Behavior on color { Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
@@ -134,53 +117,15 @@ Variants {
// Oversized = can be zoomed for parallax, yay // Oversized = can be zoomed for parallax, yay
bgRoot.effectiveWallpaperScale = Math.min(bgRoot.preferredWallpaperScale, width / screenWidth, height / screenHeight); bgRoot.effectiveWallpaperScale = Math.min(bgRoot.preferredWallpaperScale, width / screenWidth, height / screenHeight);
} }
bgRoot.updateClockPosition();
} }
} }
} }
// Clock positioning
function updateClockPosition() {
// Somehow all this manual setting is needed to make the proc correctly use the new values
leastBusyRegionProc.path = bgRoot.wallpaperPath;
leastBusyRegionProc.contentWidth = clockLoader.implicitWidth + root.clockSizePadding * 2;
leastBusyRegionProc.contentHeight = clockLoader.implicitHeight + root.clockSizePadding * 2;
leastBusyRegionProc.horizontalPadding = bgRoot.movableXSpace + root.screenSizePadding * 2;
leastBusyRegionProc.verticalPadding = bgRoot.movableYSpace + root.screenSizePadding * 2;
leastBusyRegionProc.running = false;
leastBusyRegionProc.running = true;
}
Process {
id: leastBusyRegionProc
property string path: bgRoot.wallpaperPath
property int contentWidth: 300
property int contentHeight: 300
property int horizontalPadding: bgRoot.movableXSpace
property int verticalPadding: bgRoot.movableYSpace
command: [Quickshell.shellPath("scripts/images/least-busy-region-venv.sh"), "--screen-width", Math.round(bgRoot.screen.width / bgRoot.effectiveWallpaperScale), "--screen-height", Math.round(bgRoot.screen.height / bgRoot.effectiveWallpaperScale), "--width", contentWidth, "--height", contentHeight, "--horizontal-padding", horizontalPadding, "--vertical-padding", verticalPadding, path
// "--visual-output",
,]
stdout: StdioCollector {
id: leastBusyRegionOutputCollector
onStreamFinished: {
const output = leastBusyRegionOutputCollector.text;
// console.log("[Background] Least busy region output:", output)
if (output.length === 0)
return;
const parsedContent = JSON.parse(output);
bgRoot.clockX = parsedContent.center_x * bgRoot.effectiveWallpaperScale;
bgRoot.clockY = parsedContent.center_y * bgRoot.effectiveWallpaperScale;
bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary;
}
}
}
// Wallpaper
Item { Item {
anchors.fill: parent anchors.fill: parent
clip: true clip: true
// Wallpaper
StyledImage { StyledImage {
id: wallpaper id: wallpaper
visible: opacity > 0 && !blurLoader.active visible: opacity > 0 && !blurLoader.active
@@ -261,25 +206,23 @@ Variants {
} }
} }
// The clock WidgetCanvas {
Loader { id: widgetCanvas
id: clockLoader
scale: Config.options.background.clock.scale
active: Config.options.background.clock.show
anchors { anchors {
left: wallpaper.left left: wallpaper.left
right: wallpaper.right
top: wallpaper.top top: wallpaper.top
horizontalCenter: undefined bottom: wallpaper.bottom
verticalCenter: undefined readonly property real parallaxFactor: Config.options.background.parallax.widgetsFactor
leftMargin: { leftMargin: {
const clockXOnWallpaper = bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) const xOnWallpaper = bgRoot.movableXSpace;
const extraMove = (wallpaper.effectiveValueX * 2 * bgRoot.movableXSpace) * (root.clockParallaxFactor - 1); const extraMove = (wallpaper.effectiveValueX * 2 * bgRoot.movableXSpace) * (parallaxFactor - 1);
return clockXOnWallpaper - extraMove; return xOnWallpaper - extraMove;
} }
topMargin: { topMargin: {
const clockYOnWallpaper = bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) const yOnWallpaper = bgRoot.movableYSpace;
const extraMove = (wallpaper.effectiveValueY * 2 * bgRoot.movableYSpace) * (root.clockParallaxFactor - 1); const extraMove = (wallpaper.effectiveValueY * 2 * bgRoot.movableYSpace) * (parallaxFactor - 1);
return clockYOnWallpaper - extraMove; return yOnWallpaper - extraMove;
} }
Behavior on leftMargin { Behavior on leftMargin {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this) animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -288,193 +231,54 @@ Variants {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this) animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
} }
} }
width: wallpaper.width
height: wallpaper.height
states: State { states: State {
name: "centered" name: "centered"
when: (GlobalStates.screenLocked && Config.options.lock.centerClock) || bgRoot.wallpaperSafetyTriggered when: GlobalStates.screenLocked || bgRoot.wallpaperSafetyTriggered
PropertyChanges {
target: widgetCanvas
width: parent.width
height: parent.height
}
AnchorChanges { AnchorChanges {
target: clockLoader target: widgetCanvas
anchors { anchors {
left: undefined left: undefined
right: undefined right: undefined
top: undefined top: undefined
verticalCenter: parent.verticalCenter bottom: undefined
horizontalCenter: parent.horizontalCenter // horizontalCenter: parent.horizontalCenter
// verticalCenter: parent.verticalCenter
} }
} }
} }
transitions: Transition { transitions: Transition {
PropertyAnimation {
properties: "width,height"
duration: Appearance.animation.elementMove.duration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
}
AnchorAnimation { AnchorAnimation {
duration: Appearance.animation.elementMove.duration duration: Appearance.animation.elementMove.duration
easing.type: Appearance.animation.elementMove.type easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
} }
} }
sourceComponent: Column {
Loader {
id: digitalClockLoader
visible: root.clockStyle === "digital"
active: visible
sourceComponent: ColumnLayout {
id: clockColumn
spacing: 6
ClockText { FadeLoader {
font.pixelSize: 90 shown: Config.options.background.widgets.clock.enable
text: DateTime.time sourceComponent: ClockWidget {
} screenWidth: bgRoot.screen.width
ClockText { screenHeight: bgRoot.screen.height
Layout.topMargin: -5 scaledScreenWidth: bgRoot.screen.width / bgRoot.effectiveWallpaperScale
text: DateTime.date scaledScreenHeight: bgRoot.screen.height / bgRoot.effectiveWallpaperScale
} wallpaperScale: bgRoot.effectiveWallpaperScale
StyledText { wallpaperSafetyTriggered: bgRoot.wallpaperSafetyTriggered
// Somehow gets fucked up if made a ClockText???
visible: Config.options.background.showQuote && Config.options.background.quote.length > 0
Layout.fillWidth: true
horizontalAlignment: bgRoot.textHorizontalAlignment
font {
pixelSize: Appearance.font.pixelSize.normal
weight: 350
}
color: bgRoot.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
text: Config.options.background.quote
}
}
}
Loader {
id: cookieClockLoader
visible: root.clockStyle === "cookie"
active: visible
sourceComponent: CookieClock {}
}
Loader {
id: cookieQuoteLoader
visible: root.showCookieQuote
active: visible
sourceComponent: CookieQuote {}
anchors.horizontalCenter: cookieClockLoader.horizontalCenter
}
}
Item {
anchors {
top: clockLoader.bottom
topMargin: 8
horizontalCenter: (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") ? clockLoader.horizontalCenter : undefined
left: (bgRoot.textHorizontalAlignment === Text.AlignLeft) ? clockLoader.left : undefined
right: (bgRoot.textHorizontalAlignment === Text.AlignRight) ? clockLoader.right : undefined
leftMargin: -26
rightMargin: -26
}
implicitWidth: statusTextBg.implicitWidth
implicitHeight: statusTextBg.implicitHeight
StyledRectangularShadow {
target: statusTextBg
visible: statusTextBg.visible && root.clockStyle === "cookie"
opacity: statusTextBg.opacity
}
Rectangle {
id: statusTextBg
anchors.centerIn: parent
clip: true
opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0
visible: opacity > 0
implicitHeight: statusTextRow.implicitHeight + 5 * 2
implicitWidth: statusTextRow.implicitWidth + 5 * 2
radius: Appearance.rounding.small
color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, root.clockStyle === "cookie" ? 0 : 1)
Behavior on implicitWidth {
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
}
Behavior on implicitHeight {
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
}
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
RowLayout {
id: statusTextRow
anchors.centerIn: parent
spacing: 14
Item {
Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft
implicitWidth: 1
}
ClockStatusText {
id: safetyStatusText
shown: bgRoot.wallpaperSafetyTriggered
statusIcon: "hide_image"
statusText: Translation.tr("Wallpaper safety enforced")
}
ClockStatusText {
id: lockStatusText
shown: GlobalStates.screenLocked && Config.options.lock.showLockedText
statusIcon: "lock"
statusText: Translation.tr("Locked")
}
Item {
Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight
implicitWidth: 1
}
}
} }
} }
} }
} }
} }
// ComponentsCookieClock {}
component ClockText: StyledText {
Layout.fillWidth: true
horizontalAlignment: bgRoot.textHorizontalAlignment
font {
family: Appearance.font.family.expressive
pixelSize: 20
weight: Font.DemiBold
}
color: bgRoot.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
animateChange: Config.options.background.clock.digital.animateChange
}
component ClockStatusText: Row {
id: statusTextRow
property alias statusIcon: statusIconWidget.text
property alias statusText: statusTextWidget.text
property bool shown: true
property color textColor: root.clockStyle === "cookie" ? Appearance.colors.colOnSecondaryContainer : bgRoot.colText
opacity: shown ? 1 : 0
visible: opacity > 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
spacing: 4
MaterialSymbol {
id: statusIconWidget
anchors.verticalCenter: statusTextRow.verticalCenter
iconSize: Appearance.font.pixelSize.huge
color: statusTextRow.textColor
style: Text.Raised
styleColor: Appearance.colors.colShadow
}
ClockText {
id: statusTextWidget
color: statusTextRow.textColor
anchors.verticalCenter: statusTextRow.verticalCenter
font {
pixelSize: Appearance.font.pixelSize.large
weight: Font.Normal
}
style: Text.Raised
styleColor: Appearance.colors.colShadow
}
}
} }
@@ -0,0 +1,97 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets.widgetCanvas
AbstractWidget {
id: root
required property string configEntryName
required property int screenWidth
required property int screenHeight
required property int scaledScreenWidth
required property int scaledScreenHeight
required property real wallpaperScale
property bool visibleWhenLocked: false
property var configEntry: Config.options.background.widgets[configEntryName]
property string placementStrategy: configEntry.placementStrategy
property real targetX: Math.max(0, Math.min(configEntry.x, scaledScreenWidth - width))
property real targetY : Math.max(0, Math.min(configEntry.y, scaledScreenHeight - height))
x: targetX
y: targetY
visible: opacity > 0
opacity: (GlobalStates.screenLocked && !visibleWhenLocked) ? 0 : 1
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
draggable: placementStrategy === "free"
onReleased: {
root.targetX = root.x;
root.targetY = root.y;
configEntry.x = root.targetX;
configEntry.y = root.targetY ;
}
property bool needsColText: false
property color dominantColor: Appearance.colors.colPrimary
property bool dominantColorIsDark: dominantColor.hslLightness < 0.5
property color colText: {
const onNormalBackground = (GlobalStates.screenLocked && Config.options.lock.blur.enable)
const adaptiveColor = ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12))
return onNormalBackground ? Appearance.colors.colOnLayer0 : adaptiveColor;
}
property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov")
property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath
onWallpaperPathChanged: refreshPlacementIfNeeded()
onPlacementStrategyChanged: refreshPlacementIfNeeded()
Connections {
target: Config
function onReadyChanged() { refreshPlacementIfNeeded() }
}
function refreshPlacementIfNeeded() {
if (!Config.ready || (root.placementStrategy === "free" && root.needsColText)) return;
leastBusyRegionProc.wallpaperPath = root.wallpaperPath;
leastBusyRegionProc.running = false;
leastBusyRegionProc.running = true;
}
Process {
id: leastBusyRegionProc
property string wallpaperPath: root.wallpaperPath
// TODO: make these less arbitrary
property int contentWidth: 300
property int contentHeight: 300
property int horizontalPadding: 200
property int verticalPadding: 200
command: [Quickshell.shellPath("scripts/images/least-busy-region-venv.sh") // Comments to force the formatter to break lines
, "--screen-width", Math.round(root.scaledScreenWidth) //
, "--screen-height", Math.round(root.scaledScreenHeight) //
, "--width", contentWidth //
, "--height", contentHeight //
, "--horizontal-padding", horizontalPadding //
, "--vertical-padding", verticalPadding //
, wallpaperPath //
, ...(root.placementStrategy === "mostBusy" ? ["--busiest"] : [])
// "--visual-output",
]
stdout: StdioCollector {
id: leastBusyRegionOutputCollector
onStreamFinished: {
const output = leastBusyRegionOutputCollector.text;
// console.log("[Background] Least busy region output:", output)
if (output.length === 0) return;
const parsedContent = JSON.parse(output);
root.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary;
if (root.placementStrategy === "free") return;
root.targetX = parsedContent.center_x * root.wallpaperScale - root.width / 2;
root.targetY = parsedContent.center_y * root.wallpaperScale - root.height / 2;
}
}
}
}
@@ -0,0 +1,195 @@
import QtQuick
import QtQuick.Layouts
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import qs.modules.common.widgets.widgetCanvas
import qs.modules.background.widgets
AbstractBackgroundWidget {
id: root
configEntryName: "clock"
implicitHeight: contentColumn.implicitHeight
implicitWidth: contentColumn.implicitWidth
property string clockStyle: Config.options.background.widgets.clock.style
property bool forceCenter: (GlobalStates.screenLocked && Config.options.lock.centerClock)
property bool wallpaperSafetyTriggered: false
needsColText: clockStyle === "digital"
x: forceCenter ? ((root.screenWidth - root.width) / 2) : targetX
y: forceCenter ? ((root.screenHeight - root.height) / 2) : targetY
visibleWhenLocked: true
property var textHorizontalAlignment: {
if (root.forceCenter)
return Text.AlignHCenter;
if (root.x < root.scaledScreenWidth / 3)
return Text.AlignLeft;
if (root.x > root.scaledScreenWidth * 2 / 3)
return Text.AlignRight;
return Text.AlignHCenter;
}
Column {
id: contentColumn
anchors.centerIn: parent
spacing: 10
FadeLoader {
id: cookieClockLoader
anchors.horizontalCenter: parent.horizontalCenter
shown: root.clockStyle === "cookie"
sourceComponent: Column {
CookieClock {
anchors.horizontalCenter: parent.horizontalCenter
}
FadeLoader {
anchors.horizontalCenter: parent.horizontalCenter
shown: Config.options.background.showQuote && Config.options.background.quote !== ""
sourceComponent: CookieQuote {}
}
}
}
FadeLoader {
id: digitalClockLoader
anchors.horizontalCenter: parent.horizontalCenter
shown: root.clockStyle === "digital"
sourceComponent: ColumnLayout {
id: clockColumn
spacing: 6
ClockText {
font.pixelSize: 90
text: DateTime.time
}
ClockText {
Layout.topMargin: -5
text: DateTime.date
}
StyledText {
// Somehow gets fucked up if made a ClockText???
visible: Config.options.background.showQuote && Config.options.background.quote.length > 0
Layout.fillWidth: true
horizontalAlignment: root.textHorizontalAlignment
font {
pixelSize: Appearance.font.pixelSize.normal
weight: 350
}
color: root.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
text: Config.options.background.quote
}
}
}
Item {
id: statusText
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: statusTextBg.implicitHeight
implicitWidth: statusTextBg.implicitWidth
StyledRectangularShadow {
target: statusTextBg
visible: statusTextBg.visible && root.clockStyle === "cookie"
opacity: statusTextBg.opacity
}
Rectangle {
id: statusTextBg
anchors.centerIn: parent
clip: true
opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0
visible: opacity > 0
implicitHeight: statusTextRow.implicitHeight + 5 * 2
implicitWidth: statusTextRow.implicitWidth + 5 * 2
radius: Appearance.rounding.small
color: ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, root.clockStyle === "cookie" ? 0 : 1)
Behavior on implicitWidth {
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
}
Behavior on implicitHeight {
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
}
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
RowLayout {
id: statusTextRow
anchors.centerIn: parent
spacing: 14
Item {
Layout.fillWidth: root.textHorizontalAlignment !== Text.AlignLeft
implicitWidth: 1
}
ClockStatusText {
id: safetyStatusText
shown: root.wallpaperSafetyTriggered
statusIcon: "hide_image"
statusText: Translation.tr("Wallpaper safety enforced")
}
ClockStatusText {
id: lockStatusText
shown: GlobalStates.screenLocked && Config.options.lock.showLockedText
statusIcon: "lock"
statusText: Translation.tr("Locked")
}
Item {
Layout.fillWidth: root.textHorizontalAlignment !== Text.AlignRight
implicitWidth: 1
}
}
}
}
}
component ClockText: StyledText {
Layout.fillWidth: true
horizontalAlignment: root.textHorizontalAlignment
font {
family: Appearance.font.family.expressive
pixelSize: 20
weight: Font.DemiBold
}
color: root.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
animateChange: Config.options.background.widgets.clock.digital.animateChange
}
component ClockStatusText: Row {
id: statusTextRow
property alias statusIcon: statusIconWidget.text
property alias statusText: statusTextWidget.text
property bool shown: true
property color textColor: root.clockStyle === "cookie" ? Appearance.colors.colOnSecondaryContainer : root.colText
opacity: shown ? 1 : 0
visible: opacity > 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
spacing: 4
MaterialSymbol {
id: statusIconWidget
anchors.verticalCenter: statusTextRow.verticalCenter
iconSize: Appearance.font.pixelSize.huge
color: statusTextRow.textColor
style: Text.Raised
styleColor: Appearance.colors.colShadow
}
ClockText {
id: statusTextWidget
color: statusTextRow.textColor
anchors.verticalCenter: statusTextRow.verticalCenter
font {
pixelSize: Appearance.font.pixelSize.large
weight: Font.Normal
}
style: Text.Raised
styleColor: Appearance.colors.colShadow
}
}
}
@@ -9,13 +9,13 @@ import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Quickshell.Io import Quickshell.Io
import qs.modules.background.cookieClock.dateIndicator import qs.modules.background.widgets.clock.dateIndicator
import qs.modules.background.cookieClock.minuteMarks import qs.modules.background.widgets.clock.minuteMarks
Item { Item {
id: root id: root
readonly property string clockStyle: Config.options.background.clock.style readonly property string clockStyle: Config.options.background.widgets.clock.style
property real implicitSize: 230 property real implicitSize: 230
@@ -36,16 +36,16 @@ Item {
implicitHeight: implicitSize implicitHeight: implicitSize
function applyStyle(sides, dialStyle, hourHandStyle, minuteHandStyle, secondHandStyle, dateStyle) { function applyStyle(sides, dialStyle, hourHandStyle, minuteHandStyle, secondHandStyle, dateStyle) {
Config.options.background.clock.cookie.sides = sides Config.options.background.widgets.clock.cookie.sides = sides
Config.options.background.clock.cookie.dialNumberStyle = dialStyle Config.options.background.widgets.clock.cookie.dialNumberStyle = dialStyle
Config.options.background.clock.cookie.hourHandStyle = hourHandStyle Config.options.background.widgets.clock.cookie.hourHandStyle = hourHandStyle
Config.options.background.clock.cookie.minuteHandStyle = minuteHandStyle Config.options.background.widgets.clock.cookie.minuteHandStyle = minuteHandStyle
Config.options.background.clock.cookie.secondHandStyle = secondHandStyle Config.options.background.widgets.clock.cookie.secondHandStyle = secondHandStyle
Config.options.background.clock.cookie.dateStyle = dateStyle Config.options.background.widgets.clock.cookie.dateStyle = dateStyle
} }
function setClockPreset(category) { function setClockPreset(category) {
if (!Config.options.background.clock.cookie.aiStyling) return; if (!Config.options.background.widgets.clock.cookie.aiStyling) return;
if (category === "") return; if (category === "") return;
print("[Cookie clock] Setting clock preset for category: " + category) print("[Cookie clock] Setting clock preset for category: " + category)
// "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space" // "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space"
@@ -83,7 +83,7 @@ Item {
} }
} }
property bool useSineCookie: Config.options.background.clock.cookie.useSineCookie property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie
DropShadow { DropShadow {
source: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader source: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
anchors.fill: source anchors.fill: source
@@ -93,7 +93,7 @@ Item {
transparentBorder: true transparentBorder: true
RotationAnimation on rotation { RotationAnimation on rotation {
running: Config.options.background.clock.cookie.constantlyRotate running: Config.options.background.widgets.clock.cookie.constantlyRotate
duration: 30000 duration: 30000
easing.type: Easing.Linear easing.type: Easing.Linear
loops: Animation.Infinite loops: Animation.Infinite
@@ -108,7 +108,7 @@ Item {
active: useSineCookie active: useSineCookie
sourceComponent: SineCookie { sourceComponent: SineCookie {
implicitSize: root.implicitSize implicitSize: root.implicitSize
sides: Config.options.background.clock.cookie.sides sides: Config.options.background.widgets.clock.cookie.sides
color: root.colBackground color: root.colBackground
} }
} }
@@ -119,7 +119,7 @@ Item {
active: !useSineCookie active: !useSineCookie
sourceComponent: MaterialCookie { sourceComponent: MaterialCookie {
implicitSize: root.implicitSize implicitSize: root.implicitSize
sides: Config.options.background.clock.cookie.sides sides: Config.options.background.widgets.clock.cookie.sides
color: root.colBackground color: root.colBackground
} }
} }
@@ -134,7 +134,7 @@ Item {
FadeLoader { FadeLoader {
id: hourMarksLoader id: hourMarksLoader
anchors.centerIn: parent anchors.centerIn: parent
shown: Config.options.background.clock.cookie.hourMarks shown: Config.options.background.widgets.clock.cookie.hourMarks
sourceComponent: HourMarks { sourceComponent: HourMarks {
implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity) implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity)
color: root.colOnBackground color: root.colOnBackground
@@ -146,7 +146,7 @@ Item {
FadeLoader { FadeLoader {
id: timeColumnLoader id: timeColumnLoader
anchors.centerIn: parent anchors.centerIn: parent
shown: Config.options.background.clock.cookie.timeIndicators shown: Config.options.background.widgets.clock.cookie.timeIndicators
scale: 1.4 - 0.4 * timeColumnLoader.shown scale: 1.4 - 0.4 * timeColumnLoader.shown
Behavior on scale { Behavior on scale {
animation: Appearance.animation.elementResize.numberAnimation.createObject(this) animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
@@ -161,11 +161,11 @@ Item {
FadeLoader { FadeLoader {
anchors.fill: parent anchors.fill: parent
z: 1 z: 1
shown: Config.options.background.clock.cookie.minuteHandStyle !== "hide" shown: Config.options.background.widgets.clock.cookie.minuteHandStyle !== "hide"
sourceComponent: MinuteHand { sourceComponent: MinuteHand {
anchors.fill: parent anchors.fill: parent
clockMinute: root.clockMinute clockMinute: root.clockMinute
style: Config.options.background.clock.cookie.minuteHandStyle style: Config.options.background.widgets.clock.cookie.minuteHandStyle
color: root.colMinuteHand color: root.colMinuteHand
} }
} }
@@ -174,11 +174,11 @@ Item {
FadeLoader { FadeLoader {
anchors.fill: parent anchors.fill: parent
z: item?.style === "hollow" ? 0 : 2 z: item?.style === "hollow" ? 0 : 2
shown: Config.options.background.clock.cookie.hourHandStyle !== "hide" shown: Config.options.background.widgets.clock.cookie.hourHandStyle !== "hide"
sourceComponent: HourHand { sourceComponent: HourHand {
clockHour: root.clockHour clockHour: root.clockHour
clockMinute: root.clockMinute clockMinute: root.clockMinute
style: Config.options.background.clock.cookie.hourHandStyle style: Config.options.background.widgets.clock.cookie.hourHandStyle
color: root.colHourHand color: root.colHourHand
} }
} }
@@ -186,13 +186,13 @@ Item {
// Second hand // Second hand
FadeLoader { FadeLoader {
id: secondHandLoader id: secondHandLoader
z: (Config.options.background.clock.cookie.secondHandStyle === "line") ? 2 : 3 z: (Config.options.background.widgets.clock.cookie.secondHandStyle === "line") ? 2 : 3
shown: Config.options.time.secondPrecision && Config.options.background.clock.cookie.secondHandStyle !== "hide" shown: Config.options.time.secondPrecision && Config.options.background.widgets.clock.cookie.secondHandStyle !== "hide"
anchors.fill: parent anchors.fill: parent
sourceComponent: SecondHand { sourceComponent: SecondHand {
id: secondHand id: secondHand
clockSecond: root.clockSecond clockSecond: root.clockSecond
style: Config.options.background.clock.cookie.secondHandStyle style: Config.options.background.widgets.clock.cookie.secondHandStyle
color: root.colSecondHand color: root.colSecondHand
} }
} }
@@ -201,9 +201,9 @@ Item {
FadeLoader { FadeLoader {
z: 4 z: 4
anchors.centerIn: parent anchors.centerIn: parent
shown: Config.options.background.clock.cookie.minuteHandStyle !== "bold" shown: Config.options.background.widgets.clock.cookie.minuteHandStyle !== "bold"
sourceComponent: Rectangle { sourceComponent: Rectangle {
color: Config.options.background.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand color: Config.options.background.widgets.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand
implicitWidth: 6 implicitWidth: 6
implicitHeight: implicitWidth implicitHeight: implicitWidth
radius: width / 2 radius: width / 2
@@ -213,11 +213,11 @@ Item {
// Date // Date
FadeLoader { FadeLoader {
anchors.fill: parent anchors.fill: parent
shown: Config.options.background.clock.cookie.dateStyle !== "hide" shown: Config.options.background.widgets.clock.cookie.dateStyle !== "hide"
sourceComponent: DateIndicator { sourceComponent: DateIndicator {
color: root.colBackgroundInfo color: root.colBackgroundInfo
style: Config.options.background.clock.cookie.dateStyle style: Config.options.background.widgets.clock.cookie.dateStyle
} }
} }
} }
@@ -18,7 +18,7 @@ Item {
rotation: (360 / 60 * clockSecond) + 90 rotation: (360 / 60 * clockSecond) + 90
Behavior on rotation { Behavior on rotation {
enabled: Config.options.background.clock.cookie.constantlyRotate // Animating every second is expensive... enabled: Config.options.background.widgets.clock.cookie.constantlyRotate // Animating every second is expensive...
animation: RotationAnimation { animation: RotationAnimation {
direction: RotationAnimation.Clockwise direction: RotationAnimation.Clockwise
duration: 1000 // 1 second duration: 1000 // 1 second
@@ -8,10 +8,10 @@ import QtQuick
Column { Column {
id: root id: root
property list<string> clockNumbers: DateTime.time.split(/[: ]/) property list<string> clockNumbers: DateTime.time.split(/[: ]/)
property bool isEnabled: Config.options.background.clock.cookie.timeIndicators property bool isEnabled: Config.options.background.widgets.clock.cookie.timeIndicators
property color color: Appearance.colors.colOnSecondaryContainer property color color: Appearance.colors.colOnSecondaryContainer
property bool hourMarksEnabled: Config.options.background.clock.cookie.hourMarks property bool hourMarksEnabled: Config.options.background.widgets.clock.cookie.hourMarks
spacing: -16 spacing: -16
Repeater { Repeater {
@@ -14,7 +14,7 @@ Item {
// Rotating date // Rotating date
FadeLoader { FadeLoader {
anchors.fill: parent anchors.fill: parent
shown: Config.options.background.clock.cookie.dateStyle === "border" shown: Config.options.background.widgets.clock.cookie.dateStyle === "border"
sourceComponent: RotatingDate { sourceComponent: RotatingDate {
color: root.color color: root.color
} }
@@ -6,7 +6,7 @@ import QtQuick
Rectangle { Rectangle {
id: rect id: rect
readonly property string dialStyle: Config.options.background.clock.cookie.dialNumberStyle readonly property string dialStyle: Config.options.background.widgets.clock.cookie.dialNumberStyle
StyledText { StyledText {
anchors.centerIn: parent anchors.centerIn: parent
@@ -8,14 +8,14 @@ import QtQuick
Item { Item {
id: root id: root
property string style: Config.options.background.clock.cookie.dateStyle property string style: Config.options.background.widgets.clock.cookie.dateStyle
property color color: Appearance.colors.colOnSecondaryContainer property color color: Appearance.colors.colOnSecondaryContainer
property real angleStep: 12 * Math.PI / 180 property real angleStep: 12 * Math.PI / 180
property string dateText: Qt.locale().toString(DateTime.clock.date, "ddd dd") property string dateText: Qt.locale().toString(DateTime.clock.date, "ddd dd")
readonly property int clockSecond: DateTime.clock.seconds readonly property int clockSecond: DateTime.clock.seconds
readonly property string dialStyle: Config.options.background.clock.cookie.dialNumberStyle readonly property string dialStyle: Config.options.background.widgets.clock.cookie.dialNumberStyle
readonly property bool timeIndicators: Config.options.background.clock.cookie.timeIndicators readonly property bool timeIndicators: Config.options.background.widgets.clock.cookie.timeIndicators
property real radius: style === "border" ? 90 : 0 property real radius: style === "border" ? 90 : 0
Behavior on radius { Behavior on radius {
@@ -8,8 +8,8 @@ Item {
id: root id: root
property color color: Appearance.colors.colOnSecondaryContainer property color color: Appearance.colors.colOnSecondaryContainer
property string style: Config.options.background.clock.cookie.dialNumberStyle // "dots", "numbers", "full", "hide" property string style: Config.options.background.widgets.clock.cookie.dialNumberStyle // "dots", "numbers", "full", "hide"
property string dateStyle : Config.options.background.clock.cookie.dateStyle property string dateStyle : Config.options.background.widgets.clock.cookie.dateStyle
// 12 Dots // 12 Dots
FadeLoader { FadeLoader {
@@ -145,31 +145,33 @@ Singleton {
} }
property JsonObject background: JsonObject { property JsonObject background: JsonObject {
property JsonObject clock: JsonObject { property JsonObject widgets: JsonObject {
property bool fixedPosition: false property JsonObject clock: JsonObject {
property real x: -500 property bool enable: true
property real y: -500 property string placementStrategy: "leastBusy" // "free", "leastBusy", "mostBusy"
property bool show: true property real x: 100
property string style: "cookie" // Options: "cookie", "digital" property real y: 100
property real scale: 1 property string style: "cookie" // Options: "cookie", "digital"
property JsonObject cookie: JsonObject { property real scale: 1
property bool aiStyling: false property JsonObject cookie: JsonObject {
property int sides: 14 property bool aiStyling: false
property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none" property int sides: 14
property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide" property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none"
property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide" property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide"
property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide" property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide"
property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide" property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide"
property bool timeIndicators: true property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide"
property bool hourMarks: false property bool timeIndicators: true
property bool dateInClock: true property bool hourMarks: false
property bool constantlyRotate: false property bool dateInClock: true
property bool useSineCookie: false property bool constantlyRotate: false
} property bool useSineCookie: false
property JsonObject digital: JsonObject { }
property bool animateChange: true property JsonObject digital: JsonObject {
} property bool animateChange: true
}
}
} }
property string wallpaperPath: "" property string wallpaperPath: ""
property string thumbnailPath: "" property string thumbnailPath: ""
@@ -182,7 +184,7 @@ Singleton {
property bool enableWorkspace: true property bool enableWorkspace: true
property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size
property bool enableSidebar: true property bool enableSidebar: true
property real clockFactor: 1.2 property real widgetsFactor: 1.2
} }
} }
@@ -318,7 +320,7 @@ Singleton {
property bool useHyprlock: false property bool useHyprlock: false
property bool launchOnStartup: false property bool launchOnStartup: false
property JsonObject blur: JsonObject { property JsonObject blur: JsonObject {
property bool enable: false property bool enable: true
property real radius: 100 property real radius: 100
property real extraZoom: 1.1 property real extraZoom: 1.1
} }
@@ -0,0 +1,26 @@
import QtQuick
import Quickshell
import qs.modules.common
/*
* Widget to be placed on a WidgetCanvas
*/
MouseArea {
id: root
property bool draggable: true
drag.target: draggable ? root : undefined
cursorShape: drag.active ? Qt.ClosedHandCursor : draggable ? Qt.OpenHandCursor : Qt.ArrowCursor
function center() {
root.x = (root.parent.width - root.width) / 2
root.y = (root.parent.height - root.height) / 2
}
Behavior on x {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
}
Behavior on y {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
}
}
@@ -0,0 +1,7 @@
import QtQuick
Item {
id: root
// uh this is stupid turns out we don't need anything here
}
@@ -14,9 +14,9 @@ ContentPage {
ConfigSwitch { ConfigSwitch {
buttonIcon: "nest_clock_farsight_analog" buttonIcon: "nest_clock_farsight_analog"
text: Translation.tr("Show clock") text: Translation.tr("Show clock")
checked: Config.options.background.clock.show checked: Config.options.background.widgets.clock.enable
onCheckedChanged: { onCheckedChanged: {
Config.options.background.clock.show = checked; Config.options.background.widgets.clock.enable = checked;
} }
} }
@@ -24,21 +24,48 @@ ContentPage {
ConfigSpinBox { ConfigSpinBox {
icon: "loupe" icon: "loupe"
text: Translation.tr("Scale (%)") text: Translation.tr("Scale (%)")
value: Config.options.background.clock.scale * 100 value: Config.options.background.widgets.clock.scale * 100
from: 1 from: 1
to: 200 to: 200
stepSize: 2 stepSize: 2
onValueChanged: { onValueChanged: {
Config.options.background.clock.scale = value / 100; Config.options.background.widgets.clock.scale = value / 100;
}
}
ContentSubsection {
title: Translation.tr("Clock placement strategy")
ConfigSelectionArray {
currentValue: Config.options.background.widgets.clock.placementStrategy
onSelected: newValue => {
Config.options.background.widgets.clock.placementStrategy = newValue;
}
options: [
{
displayName: Translation.tr("Draggable"),
icon: "drag_pan",
value: "free"
},
{
displayName: Translation.tr("Least busy"),
icon: "arrows_output",
value: "leastBusy"
},
{
displayName: Translation.tr("Most busy"),
icon: "arrows_input",
value: "mostBusy"
},
]
} }
} }
ContentSubsection { ContentSubsection {
title: Translation.tr("Clock style") title: Translation.tr("Clock style")
ConfigSelectionArray { ConfigSelectionArray {
currentValue: Config.options.background.clock.style currentValue: Config.options.background.widgets.clock.style
onSelected: newValue => { onSelected: newValue => {
Config.options.background.clock.style = newValue; Config.options.background.widgets.clock.style = newValue;
} }
options: [ options: [
{ {
@@ -56,29 +83,29 @@ ContentPage {
} }
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "digital" visible: Config.options.background.widgets.clock.style === "digital"
title: Translation.tr("Digital clock settings") title: Translation.tr("Digital clock settings")
ConfigSwitch { ConfigSwitch {
buttonIcon: "animation" buttonIcon: "animation"
text: Translation.tr("Animate time change") text: Translation.tr("Animate time change")
checked: Config.options.background.clock.digital.animateChange checked: Config.options.background.widgets.clock.digital.animateChange
onCheckedChanged: { onCheckedChanged: {
Config.options.background.clock.digital.animateChange = checked; Config.options.background.widgets.clock.digital.animateChange = checked;
} }
} }
} }
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "cookie" visible: Config.options.background.widgets.clock.style === "cookie"
title: Translation.tr("Cookie clock settings") title: Translation.tr("Cookie clock settings")
ConfigSwitch { ConfigSwitch {
buttonIcon: "wand_stars" buttonIcon: "wand_stars"
text: Translation.tr("Auto styling with Gemini") text: Translation.tr("Auto styling with Gemini")
checked: Config.options.background.clock.cookie.aiStyling checked: Config.options.background.widgets.clock.cookie.aiStyling
onCheckedChanged: { onCheckedChanged: {
Config.options.background.clock.cookie.aiStyling = checked; Config.options.background.widgets.clock.cookie.aiStyling = checked;
} }
StyledToolTip { StyledToolTip {
text: Translation.tr("Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.") text: Translation.tr("Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.")
@@ -88,9 +115,9 @@ ContentPage {
ConfigSwitch { ConfigSwitch {
buttonIcon: "airwave" buttonIcon: "airwave"
text: Translation.tr("Use old sine wave cookie implementation") text: Translation.tr("Use old sine wave cookie implementation")
checked: Config.options.background.clock.cookie.useSineCookie checked: Config.options.background.widgets.clock.cookie.useSineCookie
onCheckedChanged: { onCheckedChanged: {
Config.options.background.clock.cookie.useSineCookie = checked; Config.options.background.widgets.clock.cookie.useSineCookie = checked;
} }
StyledToolTip { StyledToolTip {
text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing" text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing"
@@ -100,21 +127,21 @@ ContentPage {
ConfigSpinBox { ConfigSpinBox {
icon: "add_triangle" icon: "add_triangle"
text: Translation.tr("Sides") text: Translation.tr("Sides")
value: Config.options.background.clock.cookie.sides value: Config.options.background.widgets.clock.cookie.sides
from: 0 from: 0
to: 40 to: 40
stepSize: 1 stepSize: 1
onValueChanged: { onValueChanged: {
Config.options.background.clock.cookie.sides = value; Config.options.background.widgets.clock.cookie.sides = value;
} }
} }
ConfigSwitch { ConfigSwitch {
buttonIcon: "autoplay" buttonIcon: "autoplay"
text: Translation.tr("Constantly rotate") text: Translation.tr("Constantly rotate")
checked: Config.options.background.clock.cookie.constantlyRotate checked: Config.options.background.widgets.clock.cookie.constantlyRotate
onCheckedChanged: { onCheckedChanged: {
Config.options.background.clock.cookie.constantlyRotate = checked; Config.options.background.widgets.clock.cookie.constantlyRotate = checked;
} }
StyledToolTip { StyledToolTip {
text: "Makes the clock always rotate. This is extremely expensive\n(expect 50% usage on Intel UHD Graphics) and thus impractical." text: "Makes the clock always rotate. This is extremely expensive\n(expect 50% usage on Intel UHD Graphics) and thus impractical."
@@ -124,15 +151,15 @@ ContentPage {
ConfigRow { ConfigRow {
ConfigSwitch { ConfigSwitch {
enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle === "dots" || Config.options.background.clock.cookie.dialNumberStyle === "full" enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full"
buttonIcon: "brightness_7" buttonIcon: "brightness_7"
text: Translation.tr("Hour marks") text: Translation.tr("Hour marks")
checked: Config.options.background.clock.cookie.hourMarks checked: Config.options.background.widgets.clock.cookie.hourMarks
onEnabledChanged: { onEnabledChanged: {
checked = Config.options.background.clock.cookie.hourMarks; checked = Config.options.background.widgets.clock.cookie.hourMarks;
} }
onCheckedChanged: { onCheckedChanged: {
Config.options.background.clock.cookie.hourMarks = checked; Config.options.background.widgets.clock.cookie.hourMarks = checked;
} }
StyledToolTip { StyledToolTip {
text: "Can only be turned on using the 'Dots' or 'Full' dial style for aesthetic reasons" text: "Can only be turned on using the 'Dots' or 'Full' dial style for aesthetic reasons"
@@ -140,15 +167,15 @@ ContentPage {
} }
ConfigSwitch { ConfigSwitch {
enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle !== "numbers" enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers"
buttonIcon: "timer_10" buttonIcon: "timer_10"
text: Translation.tr("Digits in the middle") text: Translation.tr("Digits in the middle")
checked: Config.options.background.clock.cookie.timeIndicators checked: Config.options.background.widgets.clock.cookie.timeIndicators
onEnabledChanged: { onEnabledChanged: {
checked = Config.options.background.clock.cookie.timeIndicators; checked = Config.options.background.widgets.clock.cookie.timeIndicators;
} }
onCheckedChanged: { onCheckedChanged: {
Config.options.background.clock.cookie.timeIndicators = checked; Config.options.background.widgets.clock.cookie.timeIndicators = checked;
} }
StyledToolTip { StyledToolTip {
text: "Can't be turned on when using 'Numbers' dial style for aesthetic reasons" text: "Can't be turned on when using 'Numbers' dial style for aesthetic reasons"
@@ -158,17 +185,17 @@ ContentPage {
} }
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "cookie" visible: Config.options.background.widgets.clock.style === "cookie"
title: Translation.tr("Dial style") title: Translation.tr("Dial style")
ConfigSelectionArray { ConfigSelectionArray {
currentValue: Config.options.background.clock.cookie.dialNumberStyle currentValue: Config.options.background.widgets.clock.cookie.dialNumberStyle
onSelected: newValue => { onSelected: newValue => {
Config.options.background.clock.cookie.dialNumberStyle = newValue; Config.options.background.widgets.clock.cookie.dialNumberStyle = newValue;
if (newValue !== "dots" && newValue !== "full") { if (newValue !== "dots" && newValue !== "full") {
Config.options.background.clock.cookie.hourMarks = false; Config.options.background.widgets.clock.cookie.hourMarks = false;
} }
if (newValue === "numbers") { if (newValue === "numbers") {
Config.options.background.clock.cookie.timeIndicators = false; Config.options.background.widgets.clock.cookie.timeIndicators = false;
} }
} }
options: [ options: [
@@ -197,12 +224,12 @@ ContentPage {
} }
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "cookie" visible: Config.options.background.widgets.clock.style === "cookie"
title: Translation.tr("Hour hand") title: Translation.tr("Hour hand")
ConfigSelectionArray { ConfigSelectionArray {
currentValue: Config.options.background.clock.cookie.hourHandStyle currentValue: Config.options.background.widgets.clock.cookie.hourHandStyle
onSelected: newValue => { onSelected: newValue => {
Config.options.background.clock.cookie.hourHandStyle = newValue; Config.options.background.widgets.clock.cookie.hourHandStyle = newValue;
} }
options: [ options: [
{ {
@@ -230,13 +257,13 @@ ContentPage {
} }
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "cookie" visible: Config.options.background.widgets.clock.style === "cookie"
title: Translation.tr("Minute hand") title: Translation.tr("Minute hand")
ConfigSelectionArray { ConfigSelectionArray {
currentValue: Config.options.background.clock.cookie.minuteHandStyle currentValue: Config.options.background.widgets.clock.cookie.minuteHandStyle
onSelected: newValue => { onSelected: newValue => {
Config.options.background.clock.cookie.minuteHandStyle = newValue; Config.options.background.widgets.clock.cookie.minuteHandStyle = newValue;
} }
options: [ options: [
{ {
@@ -269,13 +296,13 @@ ContentPage {
} }
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "cookie" visible: Config.options.background.widgets.clock.style === "cookie"
title: Translation.tr("Second hand") title: Translation.tr("Second hand")
ConfigSelectionArray { ConfigSelectionArray {
currentValue: Config.options.background.clock.cookie.secondHandStyle currentValue: Config.options.background.widgets.clock.cookie.secondHandStyle
onSelected: newValue => { onSelected: newValue => {
Config.options.background.clock.cookie.secondHandStyle = newValue; Config.options.background.widgets.clock.cookie.secondHandStyle = newValue;
} }
options: [ options: [
{ {
@@ -303,13 +330,13 @@ ContentPage {
} }
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "cookie" visible: Config.options.background.widgets.clock.style === "cookie"
title: Translation.tr("Date style") title: Translation.tr("Date style")
ConfigSelectionArray { ConfigSelectionArray {
currentValue: Config.options.background.clock.cookie.dateStyle currentValue: Config.options.background.widgets.clock.cookie.dateStyle
onSelected: newValue => { onSelected: newValue => {
Config.options.background.clock.cookie.dateStyle = newValue; Config.options.background.widgets.clock.cookie.dateStyle = newValue;
} }
options: [ options: [
{ {
@@ -18,7 +18,7 @@ def center_crop(img, target_w, target_h):
y2 = y1 + target_h y2 = y1 + target_h
return img[y1:y2, x1:x2] return img[y1:y2, x1:x2]
def find_least_busy_region(image_path, region_width=300, region_height=200, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", horizontal_padding=50, vertical_padding=50): def find_least_busy_region(image_path, region_width=300, region_height=200, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", horizontal_padding=50, vertical_padding=50, busiest=False):
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if img is None: if img is None:
raise FileNotFoundError(f"Image not found: {image_path}") raise FileNotFoundError(f"Image not found: {image_path}")
@@ -77,7 +77,9 @@ def find_least_busy_region(image_path, region_width=300, region_height=200, scre
total += ii[y1-1, x1-1] total += ii[y1-1, x1-1]
return total return total
min_var = None min_var = None
max_var = None
min_coords = (horizontal_padding, vertical_padding) min_coords = (horizontal_padding, vertical_padding)
max_coords = (horizontal_padding, vertical_padding)
area = region_width * region_height area = region_width * region_height
x_start = horizontal_padding x_start = horizontal_padding
y_start = vertical_padding y_start = vertical_padding
@@ -100,7 +102,13 @@ def find_least_busy_region(image_path, region_width=300, region_height=200, scre
if (min_var is None) or (var < min_var): if (min_var is None) or (var < min_var):
min_var = var min_var = var
min_coords = (x, y) min_coords = (x, y)
return min_coords, min_var if (max_var is None) or (var > max_var):
max_var = var
max_coords = (x, y)
if busiest:
return max_coords, max_var
else:
return min_coords, min_var
def find_largest_region(image_path, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", threshold=100.0, aspect_ratio=1.0, horizontal_padding=50, vertical_padding=50): def find_largest_region(image_path, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", threshold=100.0, aspect_ratio=1.0, horizontal_padding=50, vertical_padding=50):
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
@@ -313,6 +321,7 @@ def main():
parser.add_argument("--aspect-ratio", type=float, default=1.78, help="Aspect ratio (width/height) for largest region mode") parser.add_argument("--aspect-ratio", type=float, default=1.78, help="Aspect ratio (width/height) for largest region mode")
parser.add_argument("--horizontal-padding", "-hp", type=int, default=50, help="Minimum horizontal distance from region to image edge") parser.add_argument("--horizontal-padding", "-hp", type=int, default=50, help="Minimum horizontal distance from region to image edge")
parser.add_argument("--vertical-padding", "-vp", type=int, default=50, help="Minimum vertical distance from region to image edge") parser.add_argument("--vertical-padding", "-vp", type=int, default=50, help="Minimum vertical distance from region to image edge")
parser.add_argument("--busiest", action="store_true", help="Find the busiest region instead of the least busy")
args = parser.parse_args() args = parser.parse_args()
if args.largest_region: if args.largest_region:
@@ -363,7 +372,8 @@ def main():
stride=args.stride, stride=args.stride,
screen_mode=args.screen_mode, screen_mode=args.screen_mode,
horizontal_padding=args.horizontal_padding, horizontal_padding=args.horizontal_padding,
vertical_padding=args.vertical_padding vertical_padding=args.vertical_padding,
busiest=args.busiest
) )
if args.visual_output: if args.visual_output:
draw_region(args.image_path, coords, region_width=args.width, region_height=args.height, screen_width=args.screen_width, screen_height=args.screen_height, screen_mode=args.screen_mode) draw_region(args.image_path, coords, region_width=args.width, region_height=args.height, screen_width=args.screen_width, screen_height=args.screen_height, screen_mode=args.screen_mode)