forked from Shinonome/dots-hyprland
Merge branch 'end-4:main' into main
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
<details>
|
<details>
|
||||||
<summary>Installation (illogical-impulse Quickshell)</summary>
|
<summary>Installation (illogical-impulse Quickshell)</summary>
|
||||||
|
|
||||||
|
- _If you're new to Linux and decide to use Hyprland, you're in for a tough ride._
|
||||||
- Just run `bash <(curl -s https://ii.clsty.link/get)`
|
- Just run `bash <(curl -s https://ii.clsty.link/get)`
|
||||||
- Or, clone this repo and run `./setup install`
|
- Or, clone this repo and run `./setup install`
|
||||||
- See [document](https://ii.clsty.link/en/ii-qs/01setup/) for details.
|
- See [document](https://ii.clsty.link/en/ii-qs/01setup/) for details.
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ name: Comment on Discussion When sdata/dist-arch/ Changes
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
paths:
|
paths:
|
||||||
- 'sdata/dist-arch/**'
|
- "sdata/dist-arch/**"
|
||||||
|
- "!sdata/dist-arch/README.md"
|
||||||
# workflow_dispatch:
|
# workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
$lock_cmd = swaylock
|
$lock_cmd = swaylock -c 000000
|
||||||
# $lock_cmd = pidof hyprlock || hyprlock
|
# $lock_cmd = pidof hyprlock || hyprlock
|
||||||
$suspend_cmd = systemctl suspend || loginctl suspend
|
$suspend_cmd = systemctl suspend || loginctl suspend
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle #
|
|||||||
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
|
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
|
||||||
bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
||||||
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
||||||
bind = Super, G, global, quickshell:crosshairToggle # Toggle crosshair
|
bind = Super, G, global, quickshell:overlayToggle # Toggle overlay
|
||||||
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
||||||
bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
||||||
bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
||||||
|
|||||||
@@ -134,11 +134,11 @@ layerrule = blur, quickshell:.*
|
|||||||
layerrule = ignorealpha 0.79, quickshell:.*
|
layerrule = ignorealpha 0.79, quickshell:.*
|
||||||
layerrule = animation slide, quickshell:bar
|
layerrule = animation slide, quickshell:bar
|
||||||
layerrule = animation slide bottom, quickshell:cheatsheet
|
layerrule = animation slide bottom, quickshell:cheatsheet
|
||||||
layerrule = noanim, quickshell:crosshair
|
|
||||||
layerrule = animation slide bottom, quickshell:dock
|
layerrule = animation slide bottom, quickshell:dock
|
||||||
layerrule = animation popin 120%, quickshell:screenCorners
|
layerrule = animation popin 120%, quickshell:screenCorners
|
||||||
layerrule = noanim, quickshell:lockWindowPusher
|
layerrule = noanim, quickshell:lockWindowPusher
|
||||||
layerrule = animation fade, quickshell:notificationPopup
|
layerrule = animation fade, quickshell:notificationPopup
|
||||||
|
layerrule = noanim, quickshell:overlay
|
||||||
layerrule = noanim, quickshell:overview
|
layerrule = noanim, quickshell:overview
|
||||||
layerrule = animation slide bottom, quickshell:osk
|
layerrule = animation slide bottom, quickshell:osk
|
||||||
layerrule = noanim, quickshell:polkit
|
layerrule = noanim, quickshell:polkit
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Singleton {
|
|||||||
property bool osdBrightnessOpen: false
|
property bool osdBrightnessOpen: false
|
||||||
property bool osdVolumeOpen: false
|
property bool osdVolumeOpen: false
|
||||||
property bool oskOpen: false
|
property bool oskOpen: false
|
||||||
|
property bool overlayOpen: false
|
||||||
property bool overviewOpen: false
|
property bool overviewOpen: false
|
||||||
property bool regionSelectorOpen: false
|
property bool regionSelectorOpen: false
|
||||||
property bool screenLocked: false
|
property bool screenLocked: false
|
||||||
|
|||||||
@@ -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,12 @@ 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
|
||||||
|
import qs.modules.background.widgets.weather
|
||||||
|
|
||||||
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 +41,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 +54,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 +80,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 +118,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 +207,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 +232,65 @@ 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.weather.enable
|
||||||
text: DateTime.time
|
sourceComponent: WeatherWidget {
|
||||||
}
|
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 {
|
|
||||||
// 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 {
|
FadeLoader {
|
||||||
anchors {
|
shown: Config.options.background.widgets.clock.enable
|
||||||
top: clockLoader.bottom
|
sourceComponent: ClockWidget {
|
||||||
topMargin: 8
|
screenWidth: bgRoot.screen.width
|
||||||
horizontalCenter: (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") ? clockLoader.horizontalCenter : undefined
|
screenHeight: bgRoot.screen.height
|
||||||
left: (bgRoot.textHorizontalAlignment === Text.AlignLeft) ? clockLoader.left : undefined
|
scaledScreenWidth: bgRoot.screen.width / bgRoot.effectiveWallpaperScale
|
||||||
right: (bgRoot.textHorizontalAlignment === Text.AlignRight) ? clockLoader.right : undefined
|
scaledScreenHeight: bgRoot.screen.height / bgRoot.effectiveWallpaperScale
|
||||||
leftMargin: -26
|
wallpaperScale: bgRoot.effectiveWallpaperScale
|
||||||
rightMargin: -26
|
wallpaperSafetyTriggered: bgRoot.wallpaperSafetyTriggered
|
||||||
}
|
|
||||||
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,101 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
scale: (draggable && containsPress) ? 1.05 : 1
|
||||||
|
Behavior on scale {
|
||||||
|
animation: Appearance.animation.elementResize.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: 6
|
||||||
|
|
||||||
|
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.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text !== ""
|
||||||
|
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.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.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.widgets.clock.quote.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
-34
@@ -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,17 +83,12 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool useSineCookie: Config.options.background.clock.cookie.useSineCookie
|
property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie
|
||||||
DropShadow {
|
StyledDropShadow {
|
||||||
source: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
|
target: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
|
||||||
anchors.fill: source
|
|
||||||
radius: 8
|
|
||||||
samples: radius * 2 + 1
|
|
||||||
color: root.colShadow
|
|
||||||
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 +103,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 +114,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 +129,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 +141,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 +156,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 +169,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 +181,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 +196,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 +208,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -7,7 +7,7 @@ import Qt5Compat.GraphicalEffects
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property string quoteText: Config.options.background.quote
|
readonly property string quoteText: Config.options.background.widgets.clock.quote.text
|
||||||
|
|
||||||
implicitWidth: quoteBox.implicitWidth
|
implicitWidth: quoteBox.implicitWidth
|
||||||
implicitHeight: quoteBox.implicitHeight
|
implicitHeight: quoteBox.implicitHeight
|
||||||
@@ -47,7 +47,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
id: quoteStyledText
|
id: quoteStyledText
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
text: Config.options.background.quote
|
text: Config.options.background.widgets.clock.quote.text
|
||||||
color: Appearance.colors.colOnSecondaryContainer
|
color: Appearance.colors.colOnSecondaryContainer
|
||||||
font {
|
font {
|
||||||
family: Appearance.font.family.reading
|
family: Appearance.font.family.reading
|
||||||
+1
-1
@@ -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
|
||||||
+2
-2
@@ -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 {
|
||||||
+1
-1
@@ -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
|
||||||
}
|
}
|
||||||
+1
-1
@@ -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
|
||||||
+3
-3
@@ -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 {
|
||||||
+2
-2
@@ -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 {
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import QtQuick
|
||||||
|
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: "weather"
|
||||||
|
|
||||||
|
implicitHeight: backgroundShape.implicitHeight
|
||||||
|
implicitWidth: backgroundShape.implicitWidth
|
||||||
|
|
||||||
|
StyledDropShadow {
|
||||||
|
target: backgroundShape
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialShape {
|
||||||
|
id: backgroundShape
|
||||||
|
anchors.fill: parent
|
||||||
|
shape: MaterialShape.Shape.Pill
|
||||||
|
color: Appearance.colors.colPrimaryContainer
|
||||||
|
implicitSize: 200
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
font {
|
||||||
|
pixelSize: 80
|
||||||
|
family: Appearance.font.family.expressive
|
||||||
|
weight: Font.Medium
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colPrimary
|
||||||
|
text: Weather.data?.temp.substring(0,Weather.data?.temp.length - 1) ?? "--°"
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
rightMargin: 16
|
||||||
|
topMargin: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
iconSize: 80
|
||||||
|
color: Appearance.colors.colOnPrimaryContainer
|
||||||
|
text: Icons.getWeatherIcon(Weather.data.wCode) ?? "cloud"
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
bottom: parent.bottom
|
||||||
|
|
||||||
|
leftMargin: 16
|
||||||
|
bottomMargin: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,121 +13,60 @@ StyledPopup {
|
|||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
Row {
|
StyledPopupHeaderRow {
|
||||||
id: header
|
icon: "battery_android_full"
|
||||||
spacing: 5
|
label: Translation.tr("Battery")
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
fill: 0
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "battery_android_full"
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: "Battery"
|
|
||||||
font {
|
|
||||||
weight: Font.Medium
|
|
||||||
pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
}
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This row is hidden when the battery is full.
|
StyledPopupValueRow {
|
||||||
RowLayout {
|
visible: {
|
||||||
spacing: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
property bool rowVisible: {
|
|
||||||
let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty;
|
let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty;
|
||||||
let power = Battery.energyRate;
|
let power = Battery.energyRate;
|
||||||
return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01);
|
return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01);
|
||||||
}
|
}
|
||||||
visible: rowVisible
|
icon: "schedule"
|
||||||
opacity: rowVisible ? 1 : 0
|
label: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:")
|
||||||
Behavior on opacity {
|
value: {
|
||||||
NumberAnimation {
|
function formatTime(seconds) {
|
||||||
duration: 500
|
var h = Math.floor(seconds / 3600);
|
||||||
|
var m = Math.floor((seconds % 3600) / 60);
|
||||||
|
if (h > 0)
|
||||||
|
return `${h}h, ${m}m`;
|
||||||
|
else
|
||||||
|
return `${m}m`;
|
||||||
|
}
|
||||||
|
if (Battery.isCharging)
|
||||||
|
return formatTime(Battery.timeToFull);
|
||||||
|
else
|
||||||
|
return formatTime(Battery.timeToEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledPopupValueRow {
|
||||||
|
visible: !(Battery.chargeState != 4 && Battery.energyRate == 0)
|
||||||
|
icon: "bolt"
|
||||||
|
label: {
|
||||||
|
if (Battery.chargeState == 4) {
|
||||||
|
return Translation.tr("Fully charged");
|
||||||
|
} else if (Battery.chargeState == 1) {
|
||||||
|
return Translation.tr("Charging:");
|
||||||
|
} else {
|
||||||
|
return Translation.tr("Discharging:");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
value: {
|
||||||
MaterialSymbol {
|
if (Battery.chargeState == 4) {
|
||||||
text: "schedule"
|
return "";
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
} else {
|
||||||
iconSize: Appearance.font.pixelSize.large
|
return `${Battery.energyRate.toFixed(2)}W`;
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
text: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:")
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
text: {
|
|
||||||
function formatTime(seconds) {
|
|
||||||
var h = Math.floor(seconds / 3600);
|
|
||||||
var m = Math.floor((seconds % 3600) / 60);
|
|
||||||
if (h > 0)
|
|
||||||
return `${h}h, ${m}m`;
|
|
||||||
else
|
|
||||||
return `${m}m`;
|
|
||||||
}
|
|
||||||
if (Battery.isCharging)
|
|
||||||
return formatTime(Battery.timeToFull);
|
|
||||||
else
|
|
||||||
return formatTime(Battery.timeToEmpty);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
StyledPopupValueRow {
|
||||||
spacing: 5
|
icon: "heart_check"
|
||||||
Layout.fillWidth: true
|
label: Translation.tr("Health:")
|
||||||
|
value: `${(Battery.health).toFixed(1)}%`
|
||||||
property bool rowVisible: !(Battery.chargeState != 4 && Battery.energyRate == 0)
|
|
||||||
visible: rowVisible
|
|
||||||
opacity: rowVisible ? 1 : 0
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 500
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
text: "bolt"
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (Battery.chargeState == 4) {
|
|
||||||
return Translation.tr("Fully charged");
|
|
||||||
} else if (Battery.chargeState == 1) {
|
|
||||||
return Translation.tr("Charging:");
|
|
||||||
} else {
|
|
||||||
return Translation.tr("Discharging:");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
text: {
|
|
||||||
if (Battery.chargeState == 4) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return `${Battery.energyRate.toFixed(2)}W`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Item {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
ClockWidgetTooltip {
|
ClockWidgetPopup {
|
||||||
hoverTarget: mouseArea
|
hoverTarget: mouseArea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.services
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
StyledPopup {
|
||||||
|
id: root
|
||||||
|
property string formattedDate: Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy")
|
||||||
|
property string formattedTime: DateTime.time
|
||||||
|
property string formattedUptime: DateTime.uptime
|
||||||
|
property string todosSection: getUpcomingTodos()
|
||||||
|
|
||||||
|
function getUpcomingTodos() {
|
||||||
|
const unfinishedTodos = Todo.list.filter(function (item) {
|
||||||
|
return !item.done;
|
||||||
|
});
|
||||||
|
if (unfinishedTodos.length === 0) {
|
||||||
|
return Translation.tr("No pending tasks");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to first 5 todos to keep popup manageable
|
||||||
|
const limitedTodos = unfinishedTodos.slice(0, 5);
|
||||||
|
let todoText = limitedTodos.map(function (item, index) {
|
||||||
|
return ` ${index + 1}. ${item.content}`;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
if (unfinishedTodos.length > 5) {
|
||||||
|
todoText += `\n ${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return todoText;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: columnLayout
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
StyledPopupHeaderRow {
|
||||||
|
icon: "calendar_month"
|
||||||
|
label: root.formattedDate
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledPopupValueRow {
|
||||||
|
icon: "timelapse"
|
||||||
|
label: Translation.tr("System uptime:")
|
||||||
|
value: root.formattedUptime
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tasks
|
||||||
|
Column {
|
||||||
|
spacing: 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
StyledPopupValueRow {
|
||||||
|
icon: "checklist"
|
||||||
|
label: Translation.tr("To Do:")
|
||||||
|
value: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
|
text: root.todosSection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
StyledPopup {
|
|
||||||
id: root
|
|
||||||
property string formattedDate: Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy")
|
|
||||||
property string formattedTime: DateTime.time
|
|
||||||
property string formattedUptime: DateTime.uptime
|
|
||||||
property string todosSection: getUpcomingTodos()
|
|
||||||
|
|
||||||
function getUpcomingTodos() {
|
|
||||||
const unfinishedTodos = Todo.list.filter(function (item) {
|
|
||||||
return !item.done;
|
|
||||||
});
|
|
||||||
if (unfinishedTodos.length === 0) {
|
|
||||||
return Translation.tr("No pending tasks");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit to first 5 todos to keep popup manageable
|
|
||||||
const limitedTodos = unfinishedTodos.slice(0, 5);
|
|
||||||
let todoText = limitedTodos.map(function (item, index) {
|
|
||||||
return `${index + 1}. ${item.content}`;
|
|
||||||
}).join('\n');
|
|
||||||
|
|
||||||
if (unfinishedTodos.length > 5) {
|
|
||||||
todoText += `\n${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return todoText;
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: columnLayout
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
// Date + Time row
|
|
||||||
Row {
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
fill: 0
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "calendar_month"
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
text: `${root.formattedDate}`
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uptime row
|
|
||||||
RowLayout {
|
|
||||||
spacing: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
MaterialSymbol {
|
|
||||||
text: "timelapse"
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.large
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
text: Translation.tr("System uptime:")
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
text: root.formattedUptime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tasks
|
|
||||||
Column {
|
|
||||||
spacing: 0
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 4
|
|
||||||
MaterialSymbol {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: "checklist"
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.large
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: Translation.tr("To Do:")
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
text: root.todosSection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,57 +12,6 @@ StyledPopup {
|
|||||||
return (kb / (1024 * 1024)).toFixed(1) + " GB";
|
return (kb / (1024 * 1024)).toFixed(1) + " GB";
|
||||||
}
|
}
|
||||||
|
|
||||||
component ResourceItem: RowLayout {
|
|
||||||
id: resourceItem
|
|
||||||
required property string icon
|
|
||||||
required property string label
|
|
||||||
required property string value
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
text: resourceItem.icon
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
text: resourceItem.label
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
visible: resourceItem.value !== ""
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
text: resourceItem.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component ResourceHeaderItem: Row {
|
|
||||||
id: headerItem
|
|
||||||
required property var icon
|
|
||||||
required property var label
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
fill: 0
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: headerItem.icon
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: headerItem.label
|
|
||||||
font {
|
|
||||||
weight: Font.Medium
|
|
||||||
pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
}
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 12
|
spacing: 12
|
||||||
@@ -71,23 +20,23 @@ StyledPopup {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ResourceHeaderItem {
|
StyledPopupHeaderRow {
|
||||||
icon: "memory"
|
icon: "memory"
|
||||||
label: "RAM"
|
label: "RAM"
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "clock_loader_60"
|
icon: "clock_loader_60"
|
||||||
label: Translation.tr("Used:")
|
label: Translation.tr("Used:")
|
||||||
value: root.formatKB(ResourceUsage.memoryUsed)
|
value: root.formatKB(ResourceUsage.memoryUsed)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "check_circle"
|
icon: "check_circle"
|
||||||
label: Translation.tr("Free:")
|
label: Translation.tr("Free:")
|
||||||
value: root.formatKB(ResourceUsage.memoryFree)
|
value: root.formatKB(ResourceUsage.memoryFree)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "empty_dashboard"
|
icon: "empty_dashboard"
|
||||||
label: Translation.tr("Total:")
|
label: Translation.tr("Total:")
|
||||||
value: root.formatKB(ResourceUsage.memoryTotal)
|
value: root.formatKB(ResourceUsage.memoryTotal)
|
||||||
@@ -100,23 +49,23 @@ StyledPopup {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ResourceHeaderItem {
|
StyledPopupHeaderRow {
|
||||||
icon: "swap_horiz"
|
icon: "swap_horiz"
|
||||||
label: "Swap"
|
label: "Swap"
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "clock_loader_60"
|
icon: "clock_loader_60"
|
||||||
label: Translation.tr("Used:")
|
label: Translation.tr("Used:")
|
||||||
value: root.formatKB(ResourceUsage.swapUsed)
|
value: root.formatKB(ResourceUsage.swapUsed)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "check_circle"
|
icon: "check_circle"
|
||||||
label: Translation.tr("Free:")
|
label: Translation.tr("Free:")
|
||||||
value: root.formatKB(ResourceUsage.swapFree)
|
value: root.formatKB(ResourceUsage.swapFree)
|
||||||
}
|
}
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "empty_dashboard"
|
icon: "empty_dashboard"
|
||||||
label: Translation.tr("Total:")
|
label: Translation.tr("Total:")
|
||||||
value: root.formatKB(ResourceUsage.swapTotal)
|
value: root.formatKB(ResourceUsage.swapTotal)
|
||||||
@@ -128,16 +77,16 @@ StyledPopup {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ResourceHeaderItem {
|
StyledPopupHeaderRow {
|
||||||
icon: "planner_review"
|
icon: "planner_review"
|
||||||
label: "CPU"
|
label: "CPU"
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
ResourceItem {
|
StyledPopupValueRow {
|
||||||
icon: "bolt"
|
icon: "bolt"
|
||||||
label: Translation.tr("Load:")
|
label: Translation.tr("Load:")
|
||||||
value: (ResourceUsage.cpuUsage > 0.8 ? Translation.tr("High") : ResourceUsage.cpuUsage > 0.4 ? Translation.tr("Medium") : Translation.tr("Low")) + ` (${Math.round(ResourceUsage.cpuUsage * 100)}%)`
|
value: `${Math.round(ResourceUsage.cpuUsage * 100)}%`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: root
|
||||||
|
required property var icon
|
||||||
|
required property var label
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
fill: 0
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
text: root.icon
|
||||||
|
iconSize: Appearance.font.pixelSize.large
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.label
|
||||||
|
font {
|
||||||
|
weight: Font.DemiBold
|
||||||
|
pixelSize: Appearance.font.pixelSize.normal
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
required property string icon
|
||||||
|
required property string label
|
||||||
|
required property string value
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
text: root.icon
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
|
iconSize: Appearance.font.pixelSize.large
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
text: root.label
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
visible: root.value !== ""
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
|
text: root.value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -104,7 +104,6 @@ Item {
|
|||||||
id: overflowPopup
|
id: overflowPopup
|
||||||
hoverTarget: trayOverflowButton
|
hoverTarget: trayOverflowButton
|
||||||
active: root.trayOverflowOpen && root.unpinnedItems.length > 0
|
active: root.trayOverflowOpen && root.unpinnedItems.length > 0
|
||||||
popupBackgroundMargin: 300 // This should be plenty... makes sure tooltips don't get cutoff (easily)
|
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
id: trayOverflowLayout
|
id: trayOverflowLayout
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Item {
|
|||||||
visible: Config.options.bar.utilButtons.showScreenSnip
|
visible: Config.options.bar.utilButtons.showScreenSnip
|
||||||
sourceComponent: CircleUtilButton {
|
sourceComponent: CircleUtilButton {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
onClicked: Hyprland.dispatch("global quickshell:regionScreenshot")
|
onClicked: Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
fill: 1
|
fill: 1
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ MouseArea {
|
|||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
fill: 0
|
fill: 0
|
||||||
text: WeatherIcons.codeToName[Weather.data.wCode] ?? "cloud"
|
text: Icons.getWeatherIcon(Weather.data.wCode) ?? "cloud"
|
||||||
iconSize: Appearance.font.pixelSize.large
|
iconSize: Appearance.font.pixelSize.large
|
||||||
color: Appearance.colors.colOnLayer1
|
color: Appearance.colors.colOnLayer1
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
// credits: calestia
|
|
||||||
// this snippet is taken from
|
|
||||||
// https://github.com/caelestia-dots/shell
|
|
||||||
readonly property var codeToName: ({
|
|
||||||
"113": "clear_day",
|
|
||||||
"116": "partly_cloudy_day",
|
|
||||||
"119": "cloud",
|
|
||||||
"122": "cloud",
|
|
||||||
"143": "foggy",
|
|
||||||
"176": "rainy",
|
|
||||||
"179": "rainy",
|
|
||||||
"182": "rainy",
|
|
||||||
"185": "rainy",
|
|
||||||
"200": "thunderstorm",
|
|
||||||
"227": "cloudy_snowing",
|
|
||||||
"230": "snowing_heavy",
|
|
||||||
"248": "foggy",
|
|
||||||
"260": "foggy",
|
|
||||||
"263": "rainy",
|
|
||||||
"266": "rainy",
|
|
||||||
"281": "rainy",
|
|
||||||
"284": "rainy",
|
|
||||||
"293": "rainy",
|
|
||||||
"296": "rainy",
|
|
||||||
"299": "rainy",
|
|
||||||
"302": "weather_hail",
|
|
||||||
"305": "rainy",
|
|
||||||
"308": "weather_hail",
|
|
||||||
"311": "rainy",
|
|
||||||
"314": "rainy",
|
|
||||||
"317": "rainy",
|
|
||||||
"320": "cloudy_snowing",
|
|
||||||
"323": "cloudy_snowing",
|
|
||||||
"326": "cloudy_snowing",
|
|
||||||
"329": "snowing_heavy",
|
|
||||||
"332": "snowing_heavy",
|
|
||||||
"335": "snowing",
|
|
||||||
"338": "snowing_heavy",
|
|
||||||
"350": "rainy",
|
|
||||||
"353": "rainy",
|
|
||||||
"356": "rainy",
|
|
||||||
"359": "weather_hail",
|
|
||||||
"362": "rainy",
|
|
||||||
"365": "rainy",
|
|
||||||
"368": "cloudy_snowing",
|
|
||||||
"371": "snowing",
|
|
||||||
"374": "rainy",
|
|
||||||
"377": "rainy",
|
|
||||||
"386": "thunderstorm",
|
|
||||||
"389": "thunderstorm",
|
|
||||||
"392": "thunderstorm",
|
|
||||||
"395": "snowing"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -145,36 +145,44 @@ 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 JsonObject cookie: JsonObject {
|
||||||
property bool aiStyling: false
|
property bool aiStyling: false
|
||||||
property int sides: 14
|
property int sides: 14
|
||||||
property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none"
|
property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none"
|
||||||
property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide"
|
property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide"
|
||||||
property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide"
|
property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide"
|
||||||
property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide"
|
property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide"
|
||||||
property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide"
|
property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide"
|
||||||
property bool timeIndicators: true
|
property bool timeIndicators: true
|
||||||
property bool hourMarks: false
|
property bool hourMarks: false
|
||||||
property bool dateInClock: true
|
property bool dateInClock: true
|
||||||
property bool constantlyRotate: false
|
property bool constantlyRotate: false
|
||||||
property bool useSineCookie: false
|
property bool useSineCookie: false
|
||||||
|
}
|
||||||
|
property JsonObject digital: JsonObject {
|
||||||
|
property bool animateChange: true
|
||||||
|
}
|
||||||
|
property JsonObject quote: JsonObject {
|
||||||
|
property bool enable: false
|
||||||
|
property string text: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
property JsonObject digital: JsonObject {
|
property JsonObject weather: JsonObject {
|
||||||
property bool animateChange: true
|
property bool enable: false
|
||||||
|
property string placementStrategy: "free" // "free", "leastBusy", "mostBusy"
|
||||||
|
property real x: 400
|
||||||
|
property real y: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
property string wallpaperPath: ""
|
property string wallpaperPath: ""
|
||||||
property string thumbnailPath: ""
|
property string thumbnailPath: ""
|
||||||
property string quote: ""
|
|
||||||
property bool showQuote: false
|
|
||||||
property bool hideWhenFullscreen: true
|
property bool hideWhenFullscreen: true
|
||||||
property JsonObject parallax: JsonObject {
|
property JsonObject parallax: JsonObject {
|
||||||
property bool vertical: false
|
property bool vertical: false
|
||||||
@@ -182,7 +190,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 +326,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
|
||||||
}
|
}
|
||||||
@@ -353,6 +361,11 @@ Singleton {
|
|||||||
property bool pinnedOnStartup: false
|
property bool pinnedOnStartup: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property JsonObject overlay: JsonObject {
|
||||||
|
property bool openingZoomAnimation: true
|
||||||
|
property bool darkenScreen: true
|
||||||
|
}
|
||||||
|
|
||||||
property JsonObject overview: JsonObject {
|
property JsonObject overview: JsonObject {
|
||||||
property bool enable: true
|
property bool enable: true
|
||||||
property real scale: 0.18 // Relative to screen size
|
property real scale: 0.18 // Relative to screen size
|
||||||
@@ -369,6 +382,7 @@ Singleton {
|
|||||||
property bool showLabel: false
|
property bool showLabel: false
|
||||||
property real opacity: 0.3
|
property real opacity: 0.3
|
||||||
property real contentRegionOpacity: 0.8
|
property real contentRegionOpacity: 0.8
|
||||||
|
property int selectionPadding: 5
|
||||||
}
|
}
|
||||||
property JsonObject rect: JsonObject {
|
property JsonObject rect: JsonObject {
|
||||||
property bool showAimLines: true
|
property bool showAimLines: true
|
||||||
@@ -461,6 +475,14 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property JsonObject screenRecord: JsonObject {
|
||||||
|
property string savePath: Directories.videos.replace("file://","") // strip "file://"
|
||||||
|
}
|
||||||
|
|
||||||
|
property JsonObject screenSnip: JsonObject {
|
||||||
|
property string savePath: "" // only copy to clipboard when empty
|
||||||
|
}
|
||||||
|
|
||||||
property JsonObject sounds: JsonObject {
|
property JsonObject sounds: JsonObject {
|
||||||
property bool battery: false
|
property bool battery: false
|
||||||
property bool pomodoro: false
|
property bool pomodoro: false
|
||||||
|
|||||||
@@ -20,4 +20,63 @@ Singleton {
|
|||||||
return "keyboard";
|
return "keyboard";
|
||||||
return "bluetooth";
|
return "bluetooth";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property var weatherIconMap: ({
|
||||||
|
"113": "clear_day",
|
||||||
|
"116": "partly_cloudy_day",
|
||||||
|
"119": "cloud",
|
||||||
|
"122": "cloud",
|
||||||
|
"143": "foggy",
|
||||||
|
"176": "rainy",
|
||||||
|
"179": "rainy",
|
||||||
|
"182": "rainy",
|
||||||
|
"185": "rainy",
|
||||||
|
"200": "thunderstorm",
|
||||||
|
"227": "cloudy_snowing",
|
||||||
|
"230": "snowing_heavy",
|
||||||
|
"248": "foggy",
|
||||||
|
"260": "foggy",
|
||||||
|
"263": "rainy",
|
||||||
|
"266": "rainy",
|
||||||
|
"281": "rainy",
|
||||||
|
"284": "rainy",
|
||||||
|
"293": "rainy",
|
||||||
|
"296": "rainy",
|
||||||
|
"299": "rainy",
|
||||||
|
"302": "weather_hail",
|
||||||
|
"305": "rainy",
|
||||||
|
"308": "weather_hail",
|
||||||
|
"311": "rainy",
|
||||||
|
"314": "rainy",
|
||||||
|
"317": "rainy",
|
||||||
|
"320": "cloudy_snowing",
|
||||||
|
"323": "cloudy_snowing",
|
||||||
|
"326": "cloudy_snowing",
|
||||||
|
"329": "snowing_heavy",
|
||||||
|
"332": "snowing_heavy",
|
||||||
|
"335": "snowing",
|
||||||
|
"338": "snowing_heavy",
|
||||||
|
"350": "rainy",
|
||||||
|
"353": "rainy",
|
||||||
|
"356": "rainy",
|
||||||
|
"359": "weather_hail",
|
||||||
|
"362": "rainy",
|
||||||
|
"365": "rainy",
|
||||||
|
"368": "cloudy_snowing",
|
||||||
|
"371": "snowing",
|
||||||
|
"374": "rainy",
|
||||||
|
"377": "rainy",
|
||||||
|
"386": "thunderstorm",
|
||||||
|
"389": "thunderstorm",
|
||||||
|
"392": "thunderstorm",
|
||||||
|
"395": "snowing"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function getWeatherIcon(code) {
|
||||||
|
const key = String(code)
|
||||||
|
if (weatherIconMap.hasOwnProperty(key)) {
|
||||||
|
return weatherIconMap[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,28 @@ Singleton {
|
|||||||
property bool inhibit: false
|
property bool inhibit: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property JsonObject overlay: JsonObject {
|
||||||
|
property list<string> open: ["crosshair"]
|
||||||
|
property JsonObject crosshair: JsonObject {
|
||||||
|
property bool pinned: false
|
||||||
|
property bool clickthrough: true
|
||||||
|
property real x: 835
|
||||||
|
property real y: 490
|
||||||
|
}
|
||||||
|
property JsonObject recorder: JsonObject {
|
||||||
|
property bool pinned: false
|
||||||
|
property bool clickthrough: false
|
||||||
|
property real x: 100
|
||||||
|
property real y: 130
|
||||||
|
}
|
||||||
|
property JsonObject volumeMixer: JsonObject {
|
||||||
|
property bool pinned: false
|
||||||
|
property bool clickthrough: false
|
||||||
|
property real x: 100
|
||||||
|
property real y: 320
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property JsonObject timer: JsonObject {
|
property JsonObject timer: JsonObject {
|
||||||
property JsonObject pomodoro: JsonObject {
|
property JsonObject pomodoro: JsonObject {
|
||||||
property bool running: false
|
property bool running: false
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
DropShadow {
|
||||||
|
required property var target
|
||||||
|
source: target
|
||||||
|
anchors.fill: source
|
||||||
|
radius: 8
|
||||||
|
samples: radius * 2 + 1
|
||||||
|
color: Appearance.colors.colShadow
|
||||||
|
transparentBorder: true
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abstract widgets for an overlay. Doesn't contain any visuals.
|
||||||
|
*/
|
||||||
|
AbstractWidget {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool pinned: false // Whether to stay visible when the overlay is dismissed
|
||||||
|
property bool clickthrough: true // When pinned, whether to allow clicks go through
|
||||||
|
}
|
||||||
@@ -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: (draggable && containsPress) ? 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
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// uh this is stupid turns out we don't need anything here
|
||||||
|
}
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: crosshairLoader
|
|
||||||
active: GlobalStates.crosshairOpen
|
|
||||||
sourceComponent: PanelWindow {
|
|
||||||
id: crosshairWindow
|
|
||||||
exclusionMode: ExclusionMode.Ignore
|
|
||||||
WlrLayershell.namespace: "quickshell:crosshair"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
visible: true
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
mask: Region { // Crosshair should not block mouse input
|
|
||||||
item: null
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: crosshairContent.implicitWidth
|
|
||||||
implicitHeight: crosshairContent.implicitHeight
|
|
||||||
|
|
||||||
CrosshairContent {
|
|
||||||
id: crosshairContent
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
target: "crosshair"
|
|
||||||
|
|
||||||
function toggle(): void {
|
|
||||||
GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "crosshairToggle"
|
|
||||||
description: "Toggles crosshair on press"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -128,11 +128,19 @@ Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lock() {
|
||||||
|
if (Config.options.lock.useHyprlock) {
|
||||||
|
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GlobalStates.screenLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "lock"
|
target: "lock"
|
||||||
|
|
||||||
function activate(): void {
|
function activate(): void {
|
||||||
GlobalStates.screenLocked = true;
|
root.lock();
|
||||||
}
|
}
|
||||||
function focus(): void {
|
function focus(): void {
|
||||||
lockContext.shouldReFocus();
|
lockContext.shouldReFocus();
|
||||||
@@ -144,11 +152,7 @@ Scope {
|
|||||||
description: "Locks the screen"
|
description: "Locks the screen"
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (Config.options.lock.useHyprlock) {
|
root.lock()
|
||||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GlobalStates.screenLocked = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +169,7 @@ Scope {
|
|||||||
function initIfReady() {
|
function initIfReady() {
|
||||||
if (!Config.ready || !Persistent.ready) return;
|
if (!Config.ready || !Persistent.ready) return;
|
||||||
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
|
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
|
||||||
Hyprland.dispatch("global quickshell:lock")
|
root.lock();
|
||||||
} else {
|
} else {
|
||||||
KeyringStorage.fetchKeyringData();
|
KeyringStorage.fetchKeyringData();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ Scope {
|
|||||||
}
|
}
|
||||||
onExited: (exitCode, exitStatus) => {
|
onExited: (exitCode, exitStatus) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("fprintd-list command exited with error:", exitCode, exitStatus);
|
// console.warn("[LockContext] fprintd-list command exited with error:", exitCode, exitStatus);
|
||||||
root.fingerprintsConfigured = false;
|
root.fingerprintsConfigured = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.services
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property Component regionComponent: Component {
|
||||||
|
Region {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: overlayLoader
|
||||||
|
active: GlobalStates.overlayOpen || OverlayContext.hasPinnedWidgets
|
||||||
|
sourceComponent: PanelWindow {
|
||||||
|
id: overlayWindow
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
WlrLayershell.namespace: "quickshell:overlay"
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
|
visible: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: GlobalStates.overlayOpen ? overlayContent : null
|
||||||
|
regions: OverlayContext.clickableWidgets.map((widget) => regionComponent.createObject(this, {
|
||||||
|
item: widget
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
OverlayContent {
|
||||||
|
id: overlayContent
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "overlay"
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
GlobalStates.overlayOpen = !GlobalStates.overlayOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "overlayToggle"
|
||||||
|
description: "Toggles overlay on press"
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
GlobalStates.overlayOpen = !GlobalStates.overlayOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.widgets.widgetCanvas
|
||||||
|
|
||||||
|
import qs.modules.overlay.crosshair
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true
|
||||||
|
|
||||||
|
Keys.onPressed: (event) => { // Esc to close
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
GlobalStates.overlayOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property real initScale: Config.options.overlay.openingZoomAnimation ? 1.08 : 1.000001
|
||||||
|
scale: initScale
|
||||||
|
Component.onCompleted: {
|
||||||
|
scale = 1
|
||||||
|
}
|
||||||
|
Behavior on scale {
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: bg
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Appearance.colors.colScrim
|
||||||
|
visible: Config.options.overlay.darkenScreen && opacity > 0
|
||||||
|
opacity: (GlobalStates.overlayOpen && root.scale !== initScale) ? 1 : 0
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetCanvas {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: GlobalStates.overlayOpen = false
|
||||||
|
|
||||||
|
OverlayTaskbar {
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: Persistent.states.overlay.open.map(identifier => {
|
||||||
|
return OverlayContext.availableWidgets.find(w => w.identifier === identifier);
|
||||||
|
})
|
||||||
|
objectProp: "identifier"
|
||||||
|
}
|
||||||
|
delegate: OverlayWidgetDelegateChooser {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property list<var> availableWidgets: [
|
||||||
|
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
||||||
|
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
|
||||||
|
{ identifier: "recorder", materialSymbol: "screen_record" },
|
||||||
|
]
|
||||||
|
|
||||||
|
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
|
||||||
|
|
||||||
|
property list<string> pinnedWidgetIdentifiers: []
|
||||||
|
property list<var> clickableWidgets: []
|
||||||
|
|
||||||
|
function pin(identifier: string, pin = true) {
|
||||||
|
if (pin) {
|
||||||
|
if (!root.pinnedWidgetIdentifiers.includes(identifier)) {
|
||||||
|
root.pinnedWidgetIdentifiers.push(identifier)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.pinnedWidgetIdentifiers = root.pinnedWidgetIdentifiers.filter(id => id !== identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerClickableWidget(widget: var, clickable = true) {
|
||||||
|
if (clickable) {
|
||||||
|
if (!root.clickableWidgets.includes(widget)) {
|
||||||
|
root.clickableWidgets.push(widget)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.clickableWidgets = root.clickableWidgets.filter(w => w !== widget)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
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
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real padding: 8
|
||||||
|
|
||||||
|
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||||
|
implicitWidth: contentRow.implicitWidth + (padding * 2)
|
||||||
|
implicitHeight: contentRow.implicitHeight + (padding * 2)
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
radius: Appearance.rounding.large
|
||||||
|
border.color: Appearance.colors.colOutlineVariant
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: contentRow
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: root.padding
|
||||||
|
}
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: 4
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: OverlayContext.availableWidgets
|
||||||
|
}
|
||||||
|
delegate: WidgetButton {
|
||||||
|
required property var modelData
|
||||||
|
identifier: modelData.identifier
|
||||||
|
materialSymbol: modelData.materialSymbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
TimeWidget {}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Separator: Rectangle {
|
||||||
|
implicitWidth: 1
|
||||||
|
color: Appearance.colors.colOutlineVariant
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: 10
|
||||||
|
Layout.bottomMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
component TimeWidget: StyledText {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: 8
|
||||||
|
Layout.rightMargin: 6
|
||||||
|
|
||||||
|
text: DateTime.time
|
||||||
|
font {
|
||||||
|
family: Appearance.font.family.numbers
|
||||||
|
variableAxes: Appearance.font.variableAxes.numbers
|
||||||
|
pixelSize: 22
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component WidgetButton: RippleButton {
|
||||||
|
id: widgetButton
|
||||||
|
required property string identifier
|
||||||
|
required property string materialSymbol
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
toggled: Persistent.states.overlay.open.includes(identifier)
|
||||||
|
onClicked: {
|
||||||
|
if (widgetButton.toggled) {
|
||||||
|
Persistent.states.overlay.open = Persistent.states.overlay.open.filter(type => type !== identifier);
|
||||||
|
} else {
|
||||||
|
Persistent.states.overlay.open.push(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
|
||||||
|
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||||
|
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||||
|
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||||
|
|
||||||
|
buttonRadius: root.radius - (root.height - height) / 2
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: 32
|
||||||
|
implicitHeight: 32
|
||||||
|
MaterialSymbol {
|
||||||
|
id: iconWidget
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 24
|
||||||
|
text: widgetButton.materialSymbol
|
||||||
|
color: widgetButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurfaceVariant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Bluetooth
|
||||||
|
import qs.modules.overlay.crosshair
|
||||||
|
import qs.modules.overlay.volumeMixer
|
||||||
|
import qs.modules.overlay.recorder
|
||||||
|
|
||||||
|
DelegateChooser {
|
||||||
|
id: root
|
||||||
|
role: "identifier"
|
||||||
|
|
||||||
|
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
||||||
|
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
||||||
|
DelegateChoice { roleValue: "recorder"; Recorder {} }
|
||||||
|
}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.widgets.widgetCanvas
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To make an overlay widget:
|
||||||
|
* 1. Create a modules/overlay/<yourWidget>/<YourWidget>.qml, using this as the base class and declare your widget content as contentItem
|
||||||
|
* 2. Add an entry to OverlayContext.availableWidgets with identifier=<yourWidgetIdentifier>
|
||||||
|
* 3. Add an entry in Persistent.states.overlay.<yourWidgetIdentifier> with x, y, pinned, clickthrough properties set to reasonable defaults
|
||||||
|
* 4. Add an entry in OverlayWidgetDelegateChooser with roleValue=<yourWidgetIdentifier> and Declare your widget in there
|
||||||
|
* Use existing entries as reference.
|
||||||
|
*/
|
||||||
|
AbstractOverlayWidget {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Item contentItem
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
readonly property string identifier: modelData.identifier
|
||||||
|
readonly property string materialSymbol: modelData.materialSymbol ?? "widgets"
|
||||||
|
property string title: identifier.replace(/([A-Z])/g, " $1").replace(/^./, function(str){ return str.toUpperCase(); })
|
||||||
|
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
||||||
|
property real radius: Appearance.rounding.windowRounding
|
||||||
|
property real minWidth: 250
|
||||||
|
|
||||||
|
draggable: GlobalStates.overlayOpen
|
||||||
|
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
||||||
|
y: Math.round(persistentStateEntry.y) // Round or it'll be blurry
|
||||||
|
pinned: persistentStateEntry.pinned
|
||||||
|
clickthrough: persistentStateEntry.clickthrough
|
||||||
|
drag {
|
||||||
|
minimumX: 0
|
||||||
|
minimumY: 0
|
||||||
|
maximumX: root.parent.width - root.width
|
||||||
|
maximumY: root.parent.height - root.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guarded states & registration funcs
|
||||||
|
property bool open: Persistent.states.overlay.open
|
||||||
|
property bool actuallyPinned: pinned && open
|
||||||
|
property bool actuallyClickable: !clickthrough && actuallyPinned && open
|
||||||
|
onActuallyPinnedChanged: reportPinnedState();
|
||||||
|
onActuallyClickableChanged: reportClickableState();
|
||||||
|
function reportPinnedState() {
|
||||||
|
OverlayContext.pin(identifier, actuallyPinned);
|
||||||
|
}
|
||||||
|
function reportClickableState() {
|
||||||
|
OverlayContext.registerClickableWidget(contentItem, actuallyClickable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self-registeration with OverlayContext
|
||||||
|
Component.onCompleted: {
|
||||||
|
reportPinnedState();
|
||||||
|
reportClickableState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
onReleased: savePosition();
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
Persistent.states.overlay.open = Persistent.states.overlay.open.filter(type => type !== root.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePinned() {
|
||||||
|
persistentStateEntry.pinned = !persistentStateEntry.pinned;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleClickthrough() {
|
||||||
|
persistentStateEntry.clickthrough = !persistentStateEntry.clickthrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
function savePosition(xPos = root.x, yPos = root.y) {
|
||||||
|
persistentStateEntry.x = xPos;
|
||||||
|
persistentStateEntry.y = yPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function center() {
|
||||||
|
const targetX = (root.parent.width - contentColumn.width) / 2
|
||||||
|
const targetY = (root.parent.height - contentItem.height) / 2 - titleBar.implicitHeight
|
||||||
|
root.x = targetX
|
||||||
|
root.y = targetY
|
||||||
|
root.savePosition(targetX, targetY)
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: GlobalStates.overlayOpen || actuallyPinned
|
||||||
|
implicitWidth: Math.max(contentColumn.implicitWidth, minWidth)
|
||||||
|
implicitHeight: contentColumn.implicitHeight
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: border
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
radius: root.radius
|
||||||
|
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
layer.enabled: GlobalStates.overlayOpen
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: border.width
|
||||||
|
height: border.height
|
||||||
|
radius: root.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: contentColumn
|
||||||
|
z: -1
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// Title bar
|
||||||
|
Rectangle {
|
||||||
|
id: titleBar
|
||||||
|
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
property real padding: 2
|
||||||
|
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
||||||
|
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
border.color: Appearance.colors.colOutlineVariant
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: titleBarRow
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: titleBar.padding
|
||||||
|
leftMargin: titleBar.padding + 8
|
||||||
|
}
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
text: root.materialSymbol
|
||||||
|
iconSize: 20
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.rightMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: root.title
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "recenter"
|
||||||
|
onClicked: root.center()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Center"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "mouse"
|
||||||
|
toggled: !root.clickthrough
|
||||||
|
onClicked: root.toggleClickthrough()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Clickable when pinned"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "keep"
|
||||||
|
toggled: root.pinned
|
||||||
|
onClicked: root.togglePinned()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Pin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "close"
|
||||||
|
onClicked: root.close()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Close"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content
|
||||||
|
Item {
|
||||||
|
id: contentContainer
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
implicitWidth: root.contentItem.implicitWidth
|
||||||
|
implicitHeight: root.contentItem.implicitHeight
|
||||||
|
children: [root.contentItem]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
component TitlebarButton: RippleButton {
|
||||||
|
id: titlebarButton
|
||||||
|
required property string materialSymbol
|
||||||
|
buttonRadius: height / 2
|
||||||
|
implicitHeight: contentItem.implicitHeight
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||||
|
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||||
|
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: 30
|
||||||
|
implicitHeight: 30
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
id: iconWidget
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 20
|
||||||
|
text: titlebarButton.materialSymbol
|
||||||
|
fill: titlebarButton.toggled
|
||||||
|
color: titlebarButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.overlay
|
||||||
|
|
||||||
|
StyledOverlayWidget {
|
||||||
|
id: root
|
||||||
|
contentItem: CrosshairContent {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.overlay
|
||||||
|
|
||||||
|
StyledOverlayWidget {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
contentItem: Rectangle {
|
||||||
|
id: contentItem
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
property real padding: 8
|
||||||
|
implicitHeight: contentColumn.implicitHeight + padding * 2
|
||||||
|
implicitWidth: 350
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: parent.padding
|
||||||
|
}
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Row {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
BigRecorderButton {
|
||||||
|
materialSymbol: "screenshot_region"
|
||||||
|
name: "Screenshot region"
|
||||||
|
onClicked: {
|
||||||
|
GlobalStates.overlayOpen = false;
|
||||||
|
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BigRecorderButton {
|
||||||
|
materialSymbol: "photo_camera"
|
||||||
|
name: "Screenshot"
|
||||||
|
onClicked: {
|
||||||
|
GlobalStates.overlayOpen = false;
|
||||||
|
Quickshell.execDetached(["bash", "-c", "grim - | wl-copy"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BigRecorderButton {
|
||||||
|
materialSymbol: "screen_record"
|
||||||
|
name: "Record region"
|
||||||
|
onClicked: {
|
||||||
|
GlobalStates.overlayOpen = false;
|
||||||
|
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "recordWithSound"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BigRecorderButton {
|
||||||
|
materialSymbol: "capture"
|
||||||
|
name: "Record screen"
|
||||||
|
onClicked: {
|
||||||
|
GlobalStates.overlayOpen = false;
|
||||||
|
Quickshell.execDetached([Directories.recordScriptPath, "--fullscreen", "--sound"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RippleButton {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: false
|
||||||
|
buttonRadius: height / 2
|
||||||
|
colBackground: Appearance.colors.colLayer3
|
||||||
|
colBackgroundHover: Appearance.colors.colLayer3Hover
|
||||||
|
colRipple: Appearance.colors.colLayer3Active
|
||||||
|
onClicked: {
|
||||||
|
GlobalStates.overlayOpen = false;
|
||||||
|
Qt.openUrlExternally(Directories.videos);
|
||||||
|
}
|
||||||
|
contentItem: Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 6
|
||||||
|
MaterialSymbol {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: "animated_images"
|
||||||
|
iconSize: 20
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: qsTr("Open recordings folder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component BigRecorderButton: RippleButton {
|
||||||
|
id: bigButton
|
||||||
|
required property string materialSymbol
|
||||||
|
required property string name
|
||||||
|
implicitHeight: 66
|
||||||
|
implicitWidth: 66
|
||||||
|
buttonRadius: height / 2
|
||||||
|
|
||||||
|
colBackground: Appearance.colors.colLayer3
|
||||||
|
colBackgroundHover: Appearance.colors.colLayer3Hover
|
||||||
|
colRipple: Appearance.colors.colLayer3Active
|
||||||
|
|
||||||
|
contentItem: MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: bigButton.materialSymbol
|
||||||
|
iconSize: 28
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledToolTip {
|
||||||
|
text: bigButton.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.overlay
|
||||||
|
import qs.modules.sidebarRight.volumeMixer
|
||||||
|
|
||||||
|
StyledOverlayWidget {
|
||||||
|
id: root
|
||||||
|
contentItem: Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
property real padding: 16
|
||||||
|
implicitHeight: 600
|
||||||
|
implicitWidth: 350
|
||||||
|
|
||||||
|
VolumeDialogContent {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: parent.padding
|
||||||
|
isSink: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@ RowLayout {
|
|||||||
Layout.bottomMargin: 4
|
Layout.bottomMargin: 4
|
||||||
onClicked: {
|
onClicked: {
|
||||||
GlobalStates.overviewOpen = false;
|
GlobalStates.overviewOpen = false;
|
||||||
Hyprland.dispatch("global quickshell:regionSearch")
|
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "search"]);
|
||||||
}
|
}
|
||||||
text: "image_search"
|
text: "image_search"
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ PanelWindow {
|
|||||||
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
||||||
signal dismiss()
|
signal dismiss()
|
||||||
|
|
||||||
|
property string saveScreenshotDir: Config.options.screenSnip.savePath !== ""
|
||||||
|
? Config.options.screenSnip.savePath
|
||||||
|
: ""
|
||||||
|
|
||||||
property string screenshotDir: Directories.screenshotTemp
|
property string screenshotDir: Directories.screenshotTemp
|
||||||
property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl
|
property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl
|
||||||
property string fileUploadApiEndpoint: "https://uguu.se/upload"
|
property string fileUploadApiEndpoint: "https://uguu.se/upload"
|
||||||
@@ -121,10 +125,11 @@ PanelWindow {
|
|||||||
return (root.targetedRegionX >= 0 && root.targetedRegionY >= 0)
|
return (root.targetedRegionX >= 0 && root.targetedRegionY >= 0)
|
||||||
}
|
}
|
||||||
function setRegionToTargeted() {
|
function setRegionToTargeted() {
|
||||||
root.regionX = root.targetedRegionX;
|
const padding = Config.options.regionSelector.targetRegions.selectionPadding; // Make borders not cut off n stuff
|
||||||
root.regionY = root.targetedRegionY;
|
root.regionX = root.targetedRegionX - padding;
|
||||||
root.regionWidth = root.targetedRegionWidth;
|
root.regionY = root.targetedRegionY - padding;
|
||||||
root.regionHeight = root.targetedRegionHeight;
|
root.regionWidth = root.targetedRegionWidth + padding * 2;
|
||||||
|
root.regionHeight = root.targetedRegionHeight + padding * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTargetedRegion(x, y) {
|
function updateTargetedRegion(x, y) {
|
||||||
@@ -258,7 +263,23 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
switch (root.action) {
|
switch (root.action) {
|
||||||
case RegionSelection.SnipAction.Copy:
|
case RegionSelection.SnipAction.Copy:
|
||||||
snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`]
|
if (saveScreenshotDir === "") {
|
||||||
|
// not saving the screenshot, just copy to clipboard
|
||||||
|
snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`]
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savePathBase = root.saveScreenshotDir
|
||||||
|
|
||||||
|
snipProc.command = [
|
||||||
|
"bash", "-c",
|
||||||
|
`mkdir -p '${StringUtils.shellSingleQuoteEscape(savePathBase)}' && \
|
||||||
|
saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \
|
||||||
|
savePath="${savePathBase}/$saveFileName" && \
|
||||||
|
${cropToStdout} | tee >(wl-copy) > "$savePath" && \
|
||||||
|
${cleanup}`
|
||||||
|
]
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case RegionSelection.SnipAction.Edit:
|
case RegionSelection.SnipAction.Edit:
|
||||||
snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`]
|
snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`]
|
||||||
|
|||||||
@@ -0,0 +1,471 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
|
ContentPage {
|
||||||
|
forceWidth: true
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "sync_alt"
|
||||||
|
title: Translation.tr("Parallax")
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "unfold_more_double"
|
||||||
|
text: Translation.tr("Vertical")
|
||||||
|
checked: Config.options.background.parallax.vertical
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.parallax.vertical = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigRow {
|
||||||
|
uniform: true
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "counter_1"
|
||||||
|
text: Translation.tr("Depends on workspace")
|
||||||
|
checked: Config.options.background.parallax.enableWorkspace
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.parallax.enableWorkspace = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "side_navigation"
|
||||||
|
text: Translation.tr("Depends on sidebars")
|
||||||
|
checked: Config.options.background.parallax.enableSidebar
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.parallax.enableSidebar = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigSpinBox {
|
||||||
|
icon: "loupe"
|
||||||
|
text: Translation.tr("Preferred wallpaper zoom (%)")
|
||||||
|
value: Config.options.background.parallax.workspaceZoom * 100
|
||||||
|
from: 100
|
||||||
|
to: 150
|
||||||
|
stepSize: 1
|
||||||
|
onValueChanged: {
|
||||||
|
Config.options.background.parallax.workspaceZoom = value / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "clock_loader_40"
|
||||||
|
title: Translation.tr("Widget: Clock")
|
||||||
|
|
||||||
|
ConfigRow {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
buttonIcon: "check"
|
||||||
|
text: Translation.tr("Enable")
|
||||||
|
checked: Config.options.background.widgets.clock.enable
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.enable = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
ConfigSelectionArray {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
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: "category",
|
||||||
|
value: "leastBusy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Most busy"),
|
||||||
|
icon: "shapes",
|
||||||
|
value: "mostBusy"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
title: Translation.tr("Clock style")
|
||||||
|
ConfigSelectionArray {
|
||||||
|
currentValue: Config.options.background.widgets.clock.style
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.background.widgets.clock.style = newValue;
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Digital"),
|
||||||
|
icon: "timer_10",
|
||||||
|
value: "digital"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Cookie"),
|
||||||
|
icon: "cookie",
|
||||||
|
value: "cookie"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.widgets.clock.style === "digital"
|
||||||
|
title: Translation.tr("Digital clock settings")
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "animation"
|
||||||
|
text: Translation.tr("Animate time change")
|
||||||
|
checked: Config.options.background.widgets.clock.digital.animateChange
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.digital.animateChange = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.widgets.clock.style === "cookie"
|
||||||
|
title: Translation.tr("Cookie clock settings")
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "wand_stars"
|
||||||
|
text: Translation.tr("Auto styling with Gemini")
|
||||||
|
checked: Config.options.background.widgets.clock.cookie.aiStyling
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.cookie.aiStyling = checked;
|
||||||
|
}
|
||||||
|
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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "airwave"
|
||||||
|
text: Translation.tr("Use old sine wave cookie implementation")
|
||||||
|
checked: Config.options.background.widgets.clock.cookie.useSineCookie
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.cookie.useSineCookie = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSpinBox {
|
||||||
|
icon: "add_triangle"
|
||||||
|
text: Translation.tr("Sides")
|
||||||
|
value: Config.options.background.widgets.clock.cookie.sides
|
||||||
|
from: 0
|
||||||
|
to: 40
|
||||||
|
stepSize: 1
|
||||||
|
onValueChanged: {
|
||||||
|
Config.options.background.widgets.clock.cookie.sides = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "autoplay"
|
||||||
|
text: Translation.tr("Constantly rotate")
|
||||||
|
checked: Config.options.background.widgets.clock.cookie.constantlyRotate
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.cookie.constantlyRotate = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Makes the clock always rotate. This is extremely expensive\n(expect 50% usage on Intel UHD Graphics) and thus impractical."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigRow {
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
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"
|
||||||
|
text: Translation.tr("Hour marks")
|
||||||
|
checked: Config.options.background.widgets.clock.cookie.hourMarks
|
||||||
|
onEnabledChanged: {
|
||||||
|
checked = Config.options.background.widgets.clock.cookie.hourMarks;
|
||||||
|
}
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.cookie.hourMarks = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Can only be turned on using the 'Dots' or 'Full' dial style for aesthetic reasons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers"
|
||||||
|
buttonIcon: "timer_10"
|
||||||
|
text: Translation.tr("Digits in the middle")
|
||||||
|
checked: Config.options.background.widgets.clock.cookie.timeIndicators
|
||||||
|
onEnabledChanged: {
|
||||||
|
checked = Config.options.background.widgets.clock.cookie.timeIndicators;
|
||||||
|
}
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.cookie.timeIndicators = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Can't be turned on when using 'Numbers' dial style for aesthetic reasons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.widgets.clock.style === "cookie"
|
||||||
|
title: Translation.tr("Dial style")
|
||||||
|
ConfigSelectionArray {
|
||||||
|
currentValue: Config.options.background.widgets.clock.cookie.dialNumberStyle
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.background.widgets.clock.cookie.dialNumberStyle = newValue;
|
||||||
|
if (newValue !== "dots" && newValue !== "full") {
|
||||||
|
Config.options.background.widgets.clock.cookie.hourMarks = false;
|
||||||
|
}
|
||||||
|
if (newValue === "numbers") {
|
||||||
|
Config.options.background.widgets.clock.cookie.timeIndicators = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: "",
|
||||||
|
icon: "block",
|
||||||
|
value: "none"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Dots"),
|
||||||
|
icon: "graph_6",
|
||||||
|
value: "dots"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Full"),
|
||||||
|
icon: "history_toggle_off",
|
||||||
|
value: "full"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Numbers"),
|
||||||
|
icon: "counter_1",
|
||||||
|
value: "numbers"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.widgets.clock.style === "cookie"
|
||||||
|
title: Translation.tr("Hour hand")
|
||||||
|
ConfigSelectionArray {
|
||||||
|
currentValue: Config.options.background.widgets.clock.cookie.hourHandStyle
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.background.widgets.clock.cookie.hourHandStyle = newValue;
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: "",
|
||||||
|
icon: "block",
|
||||||
|
value: "hide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Classic"),
|
||||||
|
icon: "radio",
|
||||||
|
value: "classic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Hollow"),
|
||||||
|
icon: "circle",
|
||||||
|
value: "hollow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Fill"),
|
||||||
|
icon: "eraser_size_5",
|
||||||
|
value: "fill"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.widgets.clock.style === "cookie"
|
||||||
|
title: Translation.tr("Minute hand")
|
||||||
|
|
||||||
|
ConfigSelectionArray {
|
||||||
|
currentValue: Config.options.background.widgets.clock.cookie.minuteHandStyle
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.background.widgets.clock.cookie.minuteHandStyle = newValue;
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: "",
|
||||||
|
icon: "block",
|
||||||
|
value: "hide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Classic"),
|
||||||
|
icon: "radio",
|
||||||
|
value: "classic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Thin"),
|
||||||
|
icon: "line_end",
|
||||||
|
value: "thin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Medium"),
|
||||||
|
icon: "eraser_size_2",
|
||||||
|
value: "medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Bold"),
|
||||||
|
icon: "eraser_size_4",
|
||||||
|
value: "bold"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.widgets.clock.style === "cookie"
|
||||||
|
title: Translation.tr("Second hand")
|
||||||
|
|
||||||
|
ConfigSelectionArray {
|
||||||
|
currentValue: Config.options.background.widgets.clock.cookie.secondHandStyle
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.background.widgets.clock.cookie.secondHandStyle = newValue;
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: "",
|
||||||
|
icon: "block",
|
||||||
|
value: "hide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Classic"),
|
||||||
|
icon: "radio",
|
||||||
|
value: "classic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Line"),
|
||||||
|
icon: "line_end",
|
||||||
|
value: "line"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Dot"),
|
||||||
|
icon: "adjust",
|
||||||
|
value: "dot"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.widgets.clock.style === "cookie"
|
||||||
|
title: Translation.tr("Date style")
|
||||||
|
|
||||||
|
ConfigSelectionArray {
|
||||||
|
currentValue: Config.options.background.widgets.clock.cookie.dateStyle
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.background.widgets.clock.cookie.dateStyle = newValue;
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: "",
|
||||||
|
icon: "block",
|
||||||
|
value: "hide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Bubble"),
|
||||||
|
icon: "bubble_chart",
|
||||||
|
value: "bubble"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Border"),
|
||||||
|
icon: "rotate_right",
|
||||||
|
value: "border"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Rect"),
|
||||||
|
icon: "rectangle",
|
||||||
|
value: "rect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
title: Translation.tr("Quote")
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "check"
|
||||||
|
text: Translation.tr("Enable")
|
||||||
|
checked: Config.options.background.widgets.clock.quote.enable
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.clock.quote.enable = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MaterialTextArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: Translation.tr("Quote")
|
||||||
|
text: Config.options.background.widgets.clock.quote.text
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
onTextChanged: {
|
||||||
|
Config.options.background.widgets.clock.quote.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "weather_mix"
|
||||||
|
title: Translation.tr("Widget: Weather")
|
||||||
|
|
||||||
|
ConfigRow {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
buttonIcon: "check"
|
||||||
|
text: Translation.tr("Enable")
|
||||||
|
checked: Config.options.background.widgets.weather.enable
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.widgets.weather.enable = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
ConfigSelectionArray {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
currentValue: Config.options.background.widgets.weather.placementStrategy
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.background.widgets.weather.placementStrategy = newValue;
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Draggable"),
|
||||||
|
icon: "drag_pan",
|
||||||
|
value: "free"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Least busy"),
|
||||||
|
icon: "category",
|
||||||
|
value: "leastBusy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: Translation.tr("Most busy"),
|
||||||
|
icon: "shapes",
|
||||||
|
value: "mostBusy"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -318,17 +318,17 @@ ContentPage {
|
|||||||
{
|
{
|
||||||
displayName: Translation.tr("Normal"),
|
displayName: Translation.tr("Normal"),
|
||||||
icon: "timer_10",
|
icon: "timer_10",
|
||||||
value: '["1","2","3","4","5","6","7","8","9","10"]'
|
value: '[]'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: Translation.tr("Japanese"),
|
displayName: Translation.tr("Han chars"),
|
||||||
icon: "square_dot",
|
icon: "square_dot",
|
||||||
value: '["一","二","三","四","五","六","七","八","九","十"]'
|
value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: Translation.tr("Roman"),
|
displayName: Translation.tr("Roman"),
|
||||||
icon: "account_balance",
|
icon: "account_balance",
|
||||||
value: '["I","II","III","IV","V","VI","VII","VIII","IX","X"]'
|
value: '["I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII","XIII","XIV","XV","XVI","XVII","XVIII","XIX","XX"]'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,440 +7,6 @@ import qs.modules.common.widgets
|
|||||||
ContentPage {
|
ContentPage {
|
||||||
forceWidth: true
|
forceWidth: true
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
icon: "wallpaper"
|
|
||||||
title: Translation.tr("Background")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "nest_clock_farsight_analog"
|
|
||||||
text: Translation.tr("Show clock")
|
|
||||||
checked: Config.options.background.clock.show
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.clock.show = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ConfigSpinBox {
|
|
||||||
icon: "loupe"
|
|
||||||
text: Translation.tr("Scale (%)")
|
|
||||||
value: Config.options.background.clock.scale * 100
|
|
||||||
from: 1
|
|
||||||
to: 200
|
|
||||||
stepSize: 2
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.background.clock.scale = value / 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Clock style")
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.background.clock.style
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.background.clock.style = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Simple digital"),
|
|
||||||
icon: "timer_10",
|
|
||||||
value: "digital"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Material cookie"),
|
|
||||||
icon: "cookie",
|
|
||||||
value: "cookie"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
visible: Config.options.background.clock.style === "digital"
|
|
||||||
title: Translation.tr("Digital clock settings")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "animation"
|
|
||||||
text: Translation.tr("Animate time change")
|
|
||||||
checked: Config.options.background.clock.digital.animateChange
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.clock.digital.animateChange = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
visible: Config.options.background.clock.style === "cookie"
|
|
||||||
title: Translation.tr("Cookie clock settings")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "wand_stars"
|
|
||||||
text: Translation.tr("Auto styling with Gemini")
|
|
||||||
checked: Config.options.background.clock.cookie.aiStyling
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.clock.cookie.aiStyling = checked;
|
|
||||||
}
|
|
||||||
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.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "airwave"
|
|
||||||
text: Translation.tr("Use old sine wave cookie implementation")
|
|
||||||
checked: Config.options.background.clock.cookie.useSineCookie
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.clock.cookie.useSineCookie = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigSpinBox {
|
|
||||||
icon: "add_triangle"
|
|
||||||
text: Translation.tr("Sides")
|
|
||||||
value: Config.options.background.clock.cookie.sides
|
|
||||||
from: 0
|
|
||||||
to: 40
|
|
||||||
stepSize: 1
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.background.clock.cookie.sides = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "autoplay"
|
|
||||||
text: Translation.tr("Constantly rotate")
|
|
||||||
checked: Config.options.background.clock.cookie.constantlyRotate
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.clock.cookie.constantlyRotate = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
text: "Makes the clock always rotate. This is extremely expensive\n(expect 50% usage on Intel UHD Graphics) and thus impractical."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle === "dots" || Config.options.background.clock.cookie.dialNumberStyle === "full"
|
|
||||||
buttonIcon: "brightness_7"
|
|
||||||
text: Translation.tr("Hour marks")
|
|
||||||
checked: Config.options.background.clock.cookie.hourMarks
|
|
||||||
onEnabledChanged: {
|
|
||||||
checked = Config.options.background.clock.cookie.hourMarks;
|
|
||||||
}
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.clock.cookie.hourMarks = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
text: "Can only be turned on using the 'Dots' or 'Full' dial style for aesthetic reasons"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle !== "numbers"
|
|
||||||
buttonIcon: "timer_10"
|
|
||||||
text: Translation.tr("Digits in the middle")
|
|
||||||
checked: Config.options.background.clock.cookie.timeIndicators
|
|
||||||
onEnabledChanged: {
|
|
||||||
checked = Config.options.background.clock.cookie.timeIndicators;
|
|
||||||
}
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.clock.cookie.timeIndicators = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
text: "Can't be turned on when using 'Numbers' dial style for aesthetic reasons"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
visible: Config.options.background.clock.style === "cookie"
|
|
||||||
title: Translation.tr("Dial style")
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.background.clock.cookie.dialNumberStyle
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.background.clock.cookie.dialNumberStyle = newValue;
|
|
||||||
if (newValue !== "dots" && newValue !== "full") {
|
|
||||||
Config.options.background.clock.cookie.hourMarks = false;
|
|
||||||
}
|
|
||||||
if (newValue === "numbers") {
|
|
||||||
Config.options.background.clock.cookie.timeIndicators = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: "",
|
|
||||||
icon: "block",
|
|
||||||
value: "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Dots"),
|
|
||||||
icon: "graph_6",
|
|
||||||
value: "dots"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Full"),
|
|
||||||
icon: "history_toggle_off",
|
|
||||||
value: "full"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Numbers"),
|
|
||||||
icon: "counter_1",
|
|
||||||
value: "numbers"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
visible: Config.options.background.clock.style === "cookie"
|
|
||||||
title: Translation.tr("Hour hand")
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.background.clock.cookie.hourHandStyle
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.background.clock.cookie.hourHandStyle = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: "",
|
|
||||||
icon: "block",
|
|
||||||
value: "hide"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Classic"),
|
|
||||||
icon: "radio",
|
|
||||||
value: "classic"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Hollow"),
|
|
||||||
icon: "circle",
|
|
||||||
value: "hollow"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Fill"),
|
|
||||||
icon: "eraser_size_5",
|
|
||||||
value: "fill"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
visible: Config.options.background.clock.style === "cookie"
|
|
||||||
title: Translation.tr("Minute hand")
|
|
||||||
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.background.clock.cookie.minuteHandStyle
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.background.clock.cookie.minuteHandStyle = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: "",
|
|
||||||
icon: "block",
|
|
||||||
value: "hide"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Classic"),
|
|
||||||
icon: "radio",
|
|
||||||
value: "classic"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Thin"),
|
|
||||||
icon: "line_end",
|
|
||||||
value: "thin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Medium"),
|
|
||||||
icon: "eraser_size_2",
|
|
||||||
value: "medium"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Bold"),
|
|
||||||
icon: "eraser_size_4",
|
|
||||||
value: "bold"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
visible: Config.options.background.clock.style === "cookie"
|
|
||||||
title: Translation.tr("Second hand")
|
|
||||||
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.background.clock.cookie.secondHandStyle
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.background.clock.cookie.secondHandStyle = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: "",
|
|
||||||
icon: "block",
|
|
||||||
value: "hide"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Classic"),
|
|
||||||
icon: "radio",
|
|
||||||
value: "classic"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Line"),
|
|
||||||
icon: "line_end",
|
|
||||||
value: "line"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Dot"),
|
|
||||||
icon: "adjust",
|
|
||||||
value: "dot"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
visible: Config.options.background.clock.style === "cookie"
|
|
||||||
title: Translation.tr("Date style")
|
|
||||||
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.background.clock.cookie.dateStyle
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.background.clock.cookie.dateStyle = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: "",
|
|
||||||
icon: "block",
|
|
||||||
value: "hide"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Bubble"),
|
|
||||||
icon: "bubble_chart",
|
|
||||||
value: "bubble"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Border"),
|
|
||||||
icon: "rotate_right",
|
|
||||||
value: "border"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Rect"),
|
|
||||||
icon: "rectangle",
|
|
||||||
value: "rect"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Quote settings")
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "format_quote"
|
|
||||||
text: Translation.tr("Show quote")
|
|
||||||
checked: Config.options.background.showQuote
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.showQuote = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MaterialTextArea {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("Quote")
|
|
||||||
text: Config.options.background.quote
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Config.options.background.quote = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Wallpaper parallax")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "unfold_more_double"
|
|
||||||
text: Translation.tr("Vertical")
|
|
||||||
checked: Config.options.background.parallax.vertical
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.parallax.vertical = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "counter_1"
|
|
||||||
text: Translation.tr("Depends on workspace")
|
|
||||||
checked: Config.options.background.parallax.enableWorkspace
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.parallax.enableWorkspace = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "side_navigation"
|
|
||||||
text: Translation.tr("Depends on sidebars")
|
|
||||||
checked: Config.options.background.parallax.enableSidebar
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.parallax.enableSidebar = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
icon: "loupe"
|
|
||||||
text: Translation.tr("Preferred wallpaper zoom (%)")
|
|
||||||
value: Config.options.background.parallax.workspaceZoom * 100
|
|
||||||
from: 100
|
|
||||||
to: 150
|
|
||||||
stepSize: 1
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.background.parallax.workspaceZoom = value / 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
icon: "point_scan"
|
|
||||||
title: Translation.tr("Crosshair overlay")
|
|
||||||
|
|
||||||
MaterialTextArea {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("Crosshair code (in Valorant's format)")
|
|
||||||
text: Config.options.crosshair.code
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Config.options.crosshair.code = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
StyledText {
|
|
||||||
Layout.leftMargin: 10
|
|
||||||
color: Appearance.colors.colSubtext
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.smallie
|
|
||||||
text: Translation.tr("Press Super+G to toggle appearance")
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
RippleButtonWithIcon {
|
|
||||||
id: editorButton
|
|
||||||
buttonRadius: Appearance.rounding.full
|
|
||||||
materialIcon: "open_in_new"
|
|
||||||
mainText: Translation.tr("Open editor")
|
|
||||||
onClicked: {
|
|
||||||
Qt.openUrlExternally(`https://www.vcrdb.net/builder?c=${Config.options.crosshair.code}`);
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
text: "www.vcrdb.net"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
ContentSection {
|
||||||
icon: "call_to_action"
|
icon: "call_to_action"
|
||||||
title: Translation.tr("Dock")
|
title: Translation.tr("Dock")
|
||||||
@@ -609,6 +175,67 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "select_window"
|
||||||
|
title: Translation.tr("Overlay: General")
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "high_density"
|
||||||
|
text: Translation.tr("Enable opening zoom animation")
|
||||||
|
checked: Config.options.overlay.openingZoomAnimation
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.overlay.openingZoomAnimation = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "texture"
|
||||||
|
text: Translation.tr("Darken screen")
|
||||||
|
checked: Config.options.overlay.darkenScreen
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.overlay.darkenScreen = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "point_scan"
|
||||||
|
title: Translation.tr("Overlay: Crosshair")
|
||||||
|
|
||||||
|
MaterialTextArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: Translation.tr("Crosshair code (in Valorant's format)")
|
||||||
|
text: Config.options.crosshair.code
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
onTextChanged: {
|
||||||
|
Config.options.crosshair.code = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
StyledText {
|
||||||
|
Layout.leftMargin: 10
|
||||||
|
color: Appearance.colors.colSubtext
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.smallie
|
||||||
|
text: Translation.tr("Press Super+G to open the overlay and pin the crosshair")
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
RippleButtonWithIcon {
|
||||||
|
id: editorButton
|
||||||
|
buttonRadius: Appearance.rounding.full
|
||||||
|
materialIcon: "open_in_new"
|
||||||
|
mainText: Translation.tr("Open editor")
|
||||||
|
onClicked: {
|
||||||
|
Qt.openUrlExternally(`https://www.vcrdb.net/builder?c=${Config.options.crosshair.code}`);
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: "www.vcrdb.net"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentSection {
|
ContentSection {
|
||||||
icon: "screenshot_frame_2"
|
icon: "screenshot_frame_2"
|
||||||
title: Translation.tr("Region selector (screen snipping/Google Lens)")
|
title: Translation.tr("Region selector (screen snipping/Google Lens)")
|
||||||
|
|||||||
@@ -85,6 +85,31 @@ ContentPage {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "file_open"
|
||||||
|
title: Translation.tr("Save paths")
|
||||||
|
|
||||||
|
MaterialTextArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: Translation.tr("Video Recording Path")
|
||||||
|
text: Config.options.screenRecord.savePath
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
onTextChanged: {
|
||||||
|
Config.options.screenRecord.savePath = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTextArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: Translation.tr("Screenshot Path (leave empty to just copy)")
|
||||||
|
text: Config.options.screenSnip.savePath
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
onTextChanged: {
|
||||||
|
Config.options.screenSnip.savePath = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentSection {
|
ContentSection {
|
||||||
icon: "search"
|
icon: "search"
|
||||||
title: Translation.tr("Search")
|
title: Translation.tr("Search")
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ Rectangle {
|
|||||||
segmentContent: thisBlock.content
|
segmentContent: thisBlock.content
|
||||||
messageData: root.messageData
|
messageData: root.messageData
|
||||||
done: root.messageData?.done ?? false
|
done: root.messageData?.done ?? false
|
||||||
forceDisableChunkSplitting: root.messageData.content.includes("```")
|
forceDisableChunkSplitting: root.messageData?.content.includes("```") ?? true
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ ColumnLayout {
|
|||||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||||
selectionColor: Appearance.colors.colSecondaryContainer
|
selectionColor: Appearance.colors.colSecondaryContainer
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
|
color: root.messageData?.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
|
||||||
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
|
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
|
||||||
text: modelData
|
text: modelData
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ AndroidQuickToggleButton {
|
|||||||
interval: 300
|
interval: 300
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
Hyprland.dispatch("global quickshell:regionScreenshot")
|
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,87 +11,14 @@ import Quickshell.Services.Pipewire
|
|||||||
WindowDialog {
|
WindowDialog {
|
||||||
id: root
|
id: root
|
||||||
property bool isSink: true
|
property bool isSink: true
|
||||||
function correctType(node) {
|
|
||||||
return (node.isSink === root.isSink) && node.audio
|
|
||||||
}
|
|
||||||
readonly property list<var> appPwNodes: Pipewire.nodes.values.filter((node) => { // Should be list<PwNode> but it breaks ScriptModel
|
|
||||||
return root.correctType(node) && node.isStream
|
|
||||||
})
|
|
||||||
readonly property bool hasApps: appPwNodes.length > 0
|
|
||||||
backgroundHeight: 700
|
backgroundHeight: 700
|
||||||
|
|
||||||
WindowDialogTitle {
|
WindowDialogTitle {
|
||||||
text: root.isSink ? Translation.tr("Audio output") : Translation.tr("Audio input")
|
text: root.isSink ? Translation.tr("Audio output") : Translation.tr("Audio input")
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowDialogSectionHeader {
|
VolumeDialogContent {
|
||||||
visible: root.hasApps
|
isSink: root.isSink
|
||||||
text: Translation.tr("Applications")
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowDialogSeparator {
|
|
||||||
visible: root.hasApps
|
|
||||||
Layout.topMargin: -22
|
|
||||||
Layout.leftMargin: 0
|
|
||||||
Layout.rightMargin: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogSectionListView {
|
|
||||||
visible: root.hasApps
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
model: ScriptModel {
|
|
||||||
values: root.appPwNodes
|
|
||||||
}
|
|
||||||
delegate: VolumeMixerEntry {
|
|
||||||
anchors {
|
|
||||||
left: parent?.left
|
|
||||||
right: parent?.right
|
|
||||||
}
|
|
||||||
required property var modelData
|
|
||||||
node: modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowDialogSectionHeader {
|
|
||||||
text: Translation.tr("Devices")
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowDialogSeparator {
|
|
||||||
Layout.topMargin: -22
|
|
||||||
Layout.leftMargin: 0
|
|
||||||
Layout.rightMargin: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogSectionListView {
|
|
||||||
Layout.fillHeight: !root.hasApps
|
|
||||||
Layout.preferredHeight: 180
|
|
||||||
|
|
||||||
model: ScriptModel {
|
|
||||||
values: Pipewire.nodes.values.filter(node => {
|
|
||||||
return root.correctType(node) && !node.isStream
|
|
||||||
})
|
|
||||||
}
|
|
||||||
delegate: StyledRadioButton {
|
|
||||||
id: radioButton
|
|
||||||
required property var modelData
|
|
||||||
anchors {
|
|
||||||
left: parent?.left
|
|
||||||
right: parent?.right
|
|
||||||
}
|
|
||||||
|
|
||||||
description: modelData.description
|
|
||||||
checked: modelData.id === (root.isSink ? Pipewire.preferredDefaultAudioSink?.id : Pipewire.preferredDefaultAudioSource?.id)
|
|
||||||
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (!checked) return;
|
|
||||||
if (root.isSink) {
|
|
||||||
Pipewire.preferredDefaultAudioSink = modelData
|
|
||||||
} else {
|
|
||||||
Pipewire.preferredDefaultAudioSource = modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowDialogSeparator {
|
WindowDialogSeparator {
|
||||||
@@ -117,20 +44,4 @@ WindowDialog {
|
|||||||
onClicked: root.dismiss()
|
onClicked: root.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component DialogSectionListView: StyledListView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: -22
|
|
||||||
Layout.bottomMargin: -16
|
|
||||||
Layout.leftMargin: -Appearance.rounding.large
|
|
||||||
Layout.rightMargin: -Appearance.rounding.large
|
|
||||||
topMargin: 12
|
|
||||||
bottomMargin: 12
|
|
||||||
leftMargin: 20
|
|
||||||
rightMargin: 20
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
spacing: 4
|
|
||||||
animateAppearance: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
required property bool isSink
|
||||||
|
function correctType(node) {
|
||||||
|
return (node.isSink === root.isSink) && node.audio
|
||||||
|
}
|
||||||
|
readonly property list<var> appPwNodes: Pipewire.nodes.values.filter((node) => { // Should be list<PwNode> but it breaks ScriptModel
|
||||||
|
return root.correctType(node) && node.isStream
|
||||||
|
})
|
||||||
|
readonly property bool hasApps: appPwNodes.length > 0
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
WindowDialogSectionHeader {
|
||||||
|
visible: root.hasApps
|
||||||
|
text: Translation.tr("Applications")
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSeparator {
|
||||||
|
visible: root.hasApps
|
||||||
|
Layout.topMargin: -22
|
||||||
|
Layout.leftMargin: 0
|
||||||
|
Layout.rightMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogSectionListView {
|
||||||
|
visible: root.hasApps
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: root.appPwNodes
|
||||||
|
}
|
||||||
|
delegate: VolumeMixerEntry {
|
||||||
|
anchors {
|
||||||
|
left: parent?.left
|
||||||
|
right: parent?.right
|
||||||
|
}
|
||||||
|
required property var modelData
|
||||||
|
node: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSectionHeader {
|
||||||
|
text: Translation.tr("Devices")
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSeparator {
|
||||||
|
Layout.topMargin: -22
|
||||||
|
Layout.leftMargin: 0
|
||||||
|
Layout.rightMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogSectionListView {
|
||||||
|
Layout.fillHeight: !root.hasApps
|
||||||
|
Layout.preferredHeight: 180
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: Pipewire.nodes.values.filter(node => {
|
||||||
|
return root.correctType(node) && !node.isStream
|
||||||
|
})
|
||||||
|
}
|
||||||
|
delegate: StyledRadioButton {
|
||||||
|
id: radioButton
|
||||||
|
required property var modelData
|
||||||
|
anchors {
|
||||||
|
left: parent?.left
|
||||||
|
right: parent?.right
|
||||||
|
}
|
||||||
|
|
||||||
|
description: modelData.description
|
||||||
|
checked: modelData.id === (root.isSink ? Pipewire.preferredDefaultAudioSink?.id : Pipewire.preferredDefaultAudioSource?.id)
|
||||||
|
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (!checked) return;
|
||||||
|
if (root.isSink) {
|
||||||
|
Pipewire.preferredDefaultAudioSink = modelData
|
||||||
|
} else {
|
||||||
|
Pipewire.preferredDefaultAudioSource = modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component DialogSectionListView: StyledListView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: -22
|
||||||
|
Layout.bottomMargin: -16
|
||||||
|
Layout.leftMargin: -Appearance.rounding.large
|
||||||
|
Layout.rightMargin: -Appearance.rounding.large
|
||||||
|
topMargin: 12
|
||||||
|
bottomMargin: 12
|
||||||
|
leftMargin: 20
|
||||||
|
rightMargin: 20
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
spacing: 4
|
||||||
|
animateAppearance: false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ Item {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
Bar.ClockWidgetTooltip {
|
Bar.ClockWidgetPopup {
|
||||||
hoverTarget: mouseArea
|
hoverTarget: mouseArea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,30 +74,13 @@ MouseArea {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
Row {
|
Bar.StyledPopupHeaderRow {
|
||||||
spacing: 4
|
icon: "music_note"
|
||||||
|
label: Translation.tr("Media")
|
||||||
MaterialSymbol {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
fill: 0
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "music_note"
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: "Media"
|
|
||||||
font {
|
|
||||||
weight: Font.Medium
|
|
||||||
pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
}
|
|
||||||
color: Appearance.colors.colOnSurfaceVariant
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
text: `${cleanedTitle}${activePlayer?.trackArtist ? '\n' + activePlayer.trackArtist : ''}`
|
text: `${cleanedTitle}${activePlayer?.trackArtist ? '\n' + activePlayer.trackArtist : ''}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CONFIG_FILE="$HOME/.config/illogical-impulse/config.json"
|
||||||
|
JSON_PATH=".screenRecord.savePath"
|
||||||
|
|
||||||
|
CUSTOM_PATH=$(jq -r "$JSON_PATH" "$CONFIG_FILE" 2>/dev/null)
|
||||||
|
|
||||||
|
RECORDING_DIR=""
|
||||||
|
|
||||||
|
if [[ -n "$CUSTOM_PATH" ]]; then
|
||||||
|
RECORDING_DIR="$CUSTOM_PATH"
|
||||||
|
else
|
||||||
|
RECORDING_DIR="$HOME/Videos" # Use default path
|
||||||
|
fi
|
||||||
|
|
||||||
getdate() {
|
getdate() {
|
||||||
date '+%Y-%m-%d_%H.%M.%S'
|
date '+%Y-%m-%d_%H.%M.%S'
|
||||||
}
|
}
|
||||||
@@ -10,12 +23,8 @@ getactivemonitor() {
|
|||||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
||||||
}
|
}
|
||||||
|
|
||||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
mkdir -p "$RECORDING_DIR"
|
||||||
if [[ $xdgvideo = "$HOME" ]]; then
|
cd "$RECORDING_DIR" || exit
|
||||||
unset xdgvideo
|
|
||||||
fi
|
|
||||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
|
||||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
|
||||||
|
|
||||||
# parse --region <value> without modifying $@ so other flags like --fullscreen still work
|
# parse --region <value> without modifying $@ so other flags like --fullscreen still work
|
||||||
ARGS=("$@")
|
ARGS=("$@")
|
||||||
@@ -66,4 +75,4 @@ else
|
|||||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -31,6 +31,25 @@ Singleton {
|
|||||||
property real timeToEmpty: UPower.displayDevice.timeToEmpty
|
property real timeToEmpty: UPower.displayDevice.timeToEmpty
|
||||||
property real timeToFull: UPower.displayDevice.timeToFull
|
property real timeToFull: UPower.displayDevice.timeToFull
|
||||||
|
|
||||||
|
property real health: (function() {
|
||||||
|
const devList = UPower.devices.values;
|
||||||
|
for (let i = 0; i < devList.length; ++i) {
|
||||||
|
const dev = devList[i];
|
||||||
|
if (dev.isLaptopBattery && dev.healthSupported) {
|
||||||
|
const health = dev.healthPercentage;
|
||||||
|
if (health === 0) {
|
||||||
|
return 0.01;
|
||||||
|
} else if (health < 1) {
|
||||||
|
return health * 100;
|
||||||
|
} else {
|
||||||
|
return health;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
})()
|
||||||
|
|
||||||
|
|
||||||
onIsLowAndNotChargingChanged: {
|
onIsLowAndNotChargingChanged: {
|
||||||
if (!root.available || !isLowAndNotCharging) return;
|
if (!root.available || !isLowAndNotCharging) return;
|
||||||
Quickshell.execDetached([
|
Quickshell.execDetached([
|
||||||
@@ -38,7 +57,8 @@ Singleton {
|
|||||||
Translation.tr("Low battery"),
|
Translation.tr("Low battery"),
|
||||||
Translation.tr("Consider plugging in your device"),
|
Translation.tr("Consider plugging in your device"),
|
||||||
"-u", "critical",
|
"-u", "critical",
|
||||||
"-a", "Shell"
|
"-a", "Shell",
|
||||||
|
"--hint=int:transient:1",
|
||||||
])
|
])
|
||||||
|
|
||||||
if (root.soundEnabled) Audio.playSystemSound("dialog-warning");
|
if (root.soundEnabled) Audio.playSystemSound("dialog-warning");
|
||||||
@@ -51,7 +71,8 @@ Singleton {
|
|||||||
Translation.tr("Critically low battery"),
|
Translation.tr("Critically low battery"),
|
||||||
Translation.tr("Please charge!\nAutomatic suspend triggers at %1%").arg(Config.options.battery.suspend),
|
Translation.tr("Please charge!\nAutomatic suspend triggers at %1%").arg(Config.options.battery.suspend),
|
||||||
"-u", "critical",
|
"-u", "critical",
|
||||||
"-a", "Shell"
|
"-a", "Shell",
|
||||||
|
"--hint=int:transient:1",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (root.soundEnabled) Audio.playSystemSound("suspend-error");
|
if (root.soundEnabled) Audio.playSystemSound("suspend-error");
|
||||||
@@ -69,7 +90,8 @@ Singleton {
|
|||||||
"notify-send",
|
"notify-send",
|
||||||
Translation.tr("Battery full"),
|
Translation.tr("Battery full"),
|
||||||
Translation.tr("Please unplug the charger"),
|
Translation.tr("Please unplug the charger"),
|
||||||
"-a", "Shell"
|
"-a", "Shell",
|
||||||
|
"--hint=int:transient:1",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (root.soundEnabled) Audio.playSystemSound("complete");
|
if (root.soundEnabled) Audio.playSystemSound("complete");
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onExited: (exitCode, exitStatus) => {
|
onExited: (exitCode, exitStatus) => {
|
||||||
console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode);
|
// console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode);
|
||||||
if (exitCode === 1) {
|
if (exitCode === 1) {
|
||||||
console.error("[KeyringStorage] Entry not found, initializing.");
|
console.error("[KeyringStorage] Entry not found, initializing.");
|
||||||
root.keyringData = {};
|
root.keyringData = {};
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Singleton {
|
|||||||
"text": action.text,
|
"text": action.text,
|
||||||
})) ?? []
|
})) ?? []
|
||||||
property bool popup: false
|
property bool popup: false
|
||||||
|
property bool isTransient: notification?.hints.transient ?? false
|
||||||
property string appIcon: notification?.appIcon ?? ""
|
property string appIcon: notification?.appIcon ?? ""
|
||||||
property string appName: notification?.appName ?? ""
|
property string appName: notification?.appName ?? ""
|
||||||
property string body: notification?.body ?? ""
|
property string body: notification?.body ?? ""
|
||||||
@@ -63,7 +64,11 @@ Singleton {
|
|||||||
interval: 7000
|
interval: 7000
|
||||||
running: true
|
running: true
|
||||||
onTriggered: () => {
|
onTriggered: () => {
|
||||||
root.timeoutNotification(notificationId);
|
const index = root.list.findIndex((notif) => notif.notificationId === notificationId);
|
||||||
|
const notifObject = root.list[index];
|
||||||
|
print("[Notifications] Notification timer triggered for ID: " + notificationId + ", transient: " + notifObject?.isTransient);
|
||||||
|
if (notifObject.isTransient) root.discardNotification(notificationId);
|
||||||
|
else root.timeoutNotification(notificationId);
|
||||||
destroy()
|
destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ ApplicationWindow {
|
|||||||
{
|
{
|
||||||
name: Translation.tr("General"),
|
name: Translation.tr("General"),
|
||||||
icon: "browse",
|
icon: "browse",
|
||||||
iconRotation: 180,
|
|
||||||
component: "modules/settings/GeneralConfig.qml"
|
component: "modules/settings/GeneralConfig.qml"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -40,6 +39,11 @@ ApplicationWindow {
|
|||||||
iconRotation: 180,
|
iconRotation: 180,
|
||||||
component: "modules/settings/BarConfig.qml"
|
component: "modules/settings/BarConfig.qml"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: Translation.tr("Background"),
|
||||||
|
icon: "texture",
|
||||||
|
component: "modules/settings/BackgroundConfig.qml"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: Translation.tr("Interface"),
|
name: Translation.tr("Interface"),
|
||||||
icon: "bottom_app_bar",
|
icon: "bottom_app_bar",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import qs.modules.common
|
|||||||
import qs.modules.background
|
import qs.modules.background
|
||||||
import qs.modules.bar
|
import qs.modules.bar
|
||||||
import qs.modules.cheatsheet
|
import qs.modules.cheatsheet
|
||||||
import qs.modules.crosshair
|
|
||||||
import qs.modules.dock
|
import qs.modules.dock
|
||||||
import qs.modules.lock
|
import qs.modules.lock
|
||||||
import qs.modules.mediaControls
|
import qs.modules.mediaControls
|
||||||
@@ -25,6 +24,7 @@ import qs.modules.screenCorners
|
|||||||
import qs.modules.sessionScreen
|
import qs.modules.sessionScreen
|
||||||
import qs.modules.sidebarLeft
|
import qs.modules.sidebarLeft
|
||||||
import qs.modules.sidebarRight
|
import qs.modules.sidebarRight
|
||||||
|
import qs.modules.overlay
|
||||||
import qs.modules.verticalBar
|
import qs.modules.verticalBar
|
||||||
import qs.modules.wallpaperSelector
|
import qs.modules.wallpaperSelector
|
||||||
|
|
||||||
@@ -39,7 +39,6 @@ ShellRoot {
|
|||||||
property bool enableBar: true
|
property bool enableBar: true
|
||||||
property bool enableBackground: true
|
property bool enableBackground: true
|
||||||
property bool enableCheatsheet: true
|
property bool enableCheatsheet: true
|
||||||
property bool enableCrosshair: true
|
|
||||||
property bool enableDock: true
|
property bool enableDock: true
|
||||||
property bool enableLock: true
|
property bool enableLock: true
|
||||||
property bool enableMediaControls: true
|
property bool enableMediaControls: true
|
||||||
@@ -47,6 +46,7 @@ ShellRoot {
|
|||||||
property bool enablePolkit: true
|
property bool enablePolkit: true
|
||||||
property bool enableOnScreenDisplay: true
|
property bool enableOnScreenDisplay: true
|
||||||
property bool enableOnScreenKeyboard: true
|
property bool enableOnScreenKeyboard: true
|
||||||
|
property bool enableOverlay: true
|
||||||
property bool enableOverview: true
|
property bool enableOverview: true
|
||||||
property bool enableRegionSelector: true
|
property bool enableRegionSelector: true
|
||||||
property bool enableReloadPopup: true
|
property bool enableReloadPopup: true
|
||||||
@@ -70,13 +70,13 @@ ShellRoot {
|
|||||||
LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} }
|
LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} }
|
||||||
LazyLoader { active: enableBackground; component: Background {} }
|
LazyLoader { active: enableBackground; component: Background {} }
|
||||||
LazyLoader { active: enableCheatsheet; component: Cheatsheet {} }
|
LazyLoader { active: enableCheatsheet; component: Cheatsheet {} }
|
||||||
LazyLoader { active: enableCrosshair; component: Crosshair {} }
|
|
||||||
LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} }
|
LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} }
|
||||||
LazyLoader { active: enableLock; component: Lock {} }
|
LazyLoader { active: enableLock; component: Lock {} }
|
||||||
LazyLoader { active: enableMediaControls; component: MediaControls {} }
|
LazyLoader { active: enableMediaControls; component: MediaControls {} }
|
||||||
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
|
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
|
||||||
LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} }
|
LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} }
|
||||||
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
|
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
|
||||||
|
LazyLoader { active: enableOverlay; component: Overlay {} }
|
||||||
LazyLoader { active: enableOverview; component: Overview {} }
|
LazyLoader { active: enableOverview; component: Overview {} }
|
||||||
LazyLoader { active: enablePolkit; component: Polkit {} }
|
LazyLoader { active: enablePolkit; component: Polkit {} }
|
||||||
LazyLoader { active: enableRegionSelector; component: RegionSelector {} }
|
LazyLoader { active: enableRegionSelector; component: RegionSelector {} }
|
||||||
|
|||||||
@@ -3,14 +3,23 @@
|
|||||||
- See also [Install scripts | illogical-impulse](https://ii.clsty.link/en/dev/inst-script/)
|
- See also [Install scripts | illogical-impulse](https://ii.clsty.link/en/dev/inst-script/)
|
||||||
- See also [#1061](https://github.com/end-4/dots-hyprland/issues/1061)
|
- See also [#1061](https://github.com/end-4/dots-hyprland/issues/1061)
|
||||||
|
|
||||||
**NOTE: The sdata/dist-nix is not for NixOS but every distro, using Nix and home-manager.**
|
**NOTE: The `dist-nix` is not for NixOS but every distro, using Nix and home-manager.**
|
||||||
|
|
||||||
|
As we all know Nix and Home-manager has two major functionalities:
|
||||||
|
- Handling dependencies (i.e. package installation)
|
||||||
|
- Handling dotfiles
|
||||||
|
|
||||||
|
They are discussed in following sections.
|
||||||
|
|
||||||
|
# Handling dependencies
|
||||||
|
## Status
|
||||||
|
Partially works. See [Discussion #2382](https://github.com/end-4/dots-hyprland/discussions/2382).
|
||||||
## plan
|
## plan
|
||||||
Note that this script must be idempotent.
|
Note that this script must be idempotent.
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
- [ ] Fix all TODOs inside `dist-nix`.
|
- [ ] Fix all TODOs inside `dist-nix`. ([search online](https://github.com/search?q=repo%3Aend-4%2Fdots-hyprland+path%3A%2F%5Esdata%5C%2Fdist-nix%5C%2F%2F+TODO&type=code))
|
||||||
- [ ] Warn user if inode-limited filesystem (typically ext4) is used.
|
- [ ] Since Nix uses a large number of inodes, need to warn user if inode-limited filesystem (typically ext4) is used.
|
||||||
- [ ] Deal with error when running `systemctl --user enable ydotool --now`:
|
- [ ] Deal with error when running `systemctl --user enable ydotool --now`:
|
||||||
```plain
|
```plain
|
||||||
Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user)
|
Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user)
|
||||||
@@ -23,9 +32,48 @@ On non-NixOS distros, programs using PAM (typically screen locker) will not work
|
|||||||
- One problem is that Debian(-based) distros use modified version of PAM which supports `@include` directive in `/etc/pam.d` config files but the PAM from Nix does not support it, see [this comment](https://github.com/NixOS/nixpkgs/issues/128523#issuecomment-1086106614).
|
- One problem is that Debian(-based) distros use modified version of PAM which supports `@include` directive in `/etc/pam.d` config files but the PAM from Nix does not support it, see [this comment](https://github.com/NixOS/nixpkgs/issues/128523#issuecomment-1086106614).
|
||||||
- Another problem is the location of a suid helper binary that is necessary, see [this comment](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3403195230).
|
- Another problem is the location of a suid helper binary that is necessary, see [this comment](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3403195230).
|
||||||
|
|
||||||
The problem could be solved by using the system-provided libpam instead.
|
As [commented](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3403195230) by @Cu3PO42 , both the problem could be solved by using the system-provided libpam instead.
|
||||||
|
|
||||||
See also https://github.com/caelestia-dots/shell/issues/668
|
See also [caelestia-dots/shell#668](https://github.com/caelestia-dots/shell/issues/668).
|
||||||
|
|
||||||
### NixGL
|
### NixGL
|
||||||
On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem.
|
On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem.
|
||||||
|
|
||||||
|
# Handling dot files
|
||||||
|
## Status
|
||||||
|
Paused, until some suitable method has been confirmed to meet the requirements below.
|
||||||
|
## Requirements
|
||||||
|
About handling the dotfiles, i.e. `dots/`, if we are doing this using Nix then the following requirements must be fulfilled.
|
||||||
|
|
||||||
|
**1. Allow modifications over the existing dotfiles.**
|
||||||
|
|
||||||
|
Current state of `./setup install`:
|
||||||
|
- After finishing running `./setup install`, users can modify any dotfiles in a traditional way, and if they run `./setup install` again to update then they need to skip the steps which overwrite the targets that they have modified and later sync the upgrade manually for such targets by themselves.
|
||||||
|
- For Hyprland, specially we have a `custom` folder along with `~/.config/hypr/hyprland.conf` which will only get overwritten the first time but not the later times running `./setup install`.
|
||||||
|
- This works but is not elegant. An experimental solution is using yaml config to store the selected behavior for each target, see [#2137](https://github.com/end-4/dots-hyprland/issues/2137).
|
||||||
|
|
||||||
|
If we use Nix to handle dotfiles, then it must be at least better than the current state described above, mainly in terms of convenience and automation.
|
||||||
|
|
||||||
|
**2. Allow choosing targets.**
|
||||||
|
|
||||||
|
This is similar to the above. For example user may want to use their own `~/.config/foot` instead of the files under `dots/.config/foot` entirely.
|
||||||
|
|
||||||
|
**3. Easy developing dotfiles or at least not worse than current state.**
|
||||||
|
|
||||||
|
About the current state:
|
||||||
|
- @clsty: "If I were the one who develops the dotfiles, I will make changes to the local Git repo `dots-hyprland` and rerun `./setup install-files -f` to apply the changes to observe the outcome."
|
||||||
|
- @end-4 (who develops the dots; see [comment](https://github.com/end-4/dots-hyprland/pull/2278#issuecomment-3454929577)): "I modify my local copy of stuff, copy the relevant parts over, optionally selectively pick changes then commit. It's.... the most obvious way but I guess not necessarily the cleanest"
|
||||||
|
|
||||||
|
If we use Nix to handle dotfiles, then it must be at least better than the current state described above, mainly in terms of convenience and automation.
|
||||||
|
|
||||||
|
**4. Others**
|
||||||
|
|
||||||
|
Find out a good method to avoid what @end-4 [mentioned](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-2954725029):
|
||||||
|
|
||||||
|
> About home-manager, from my limited understanding of and experience with it, any change to the config files require a rebuild right? If this is indeed the case, switching entirely to this is not okay. Having to wait 20 seconds for each change is absurd.
|
||||||
|
|
||||||
|
Some information may help, e.g. @darsh032 [commented](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3336839862):
|
||||||
|
|
||||||
|
> I mean thats not really needed you can use mkOutOfStoreSymlink or use hjem-impure to change the configs without rebuilding
|
||||||
|
|
||||||
|
And also the "hmrice" [mentioned](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3353345504) by @Markus328 , and the `flake.nix` (for quickshell only) [mentioned](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3354387126) by @darsh032 .
|
||||||
|
|||||||
@@ -18,8 +18,9 @@
|
|||||||
# "org.freedesktop.impl.portal.ScreenCast" = [ "gnome" ];
|
# "org.freedesktop.impl.portal.ScreenCast" = [ "gnome" ];
|
||||||
#};
|
#};
|
||||||
};
|
};
|
||||||
# The following seems to generate ~/.config/fontconfig conflicting with the one under dots/
|
# Note: The following generate files under ~/.config/fontconfig/conf.d/
|
||||||
#fonts.fontconfig.enable = true;
|
# fontconfig may rely on this to properly find fonts installed via Nix.
|
||||||
|
fonts.fontconfig.enable = true;
|
||||||
|
|
||||||
wayland.windowManager.hyprland = {
|
wayland.windowManager.hyprland = {
|
||||||
## Make sure home-manager not generate ~/.config/hypr/hyprland.conf
|
## Make sure home-manager not generate ~/.config/hypr/hyprland.conf
|
||||||
|
|||||||
Reference in New Issue
Block a user