forked from Shinonome/dots-hyprland
Merge branch 'main' into keybinds-settings
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
<details>
|
||||
<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)`
|
||||
- Or, clone this repo and run `./setup install`
|
||||
- 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:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'sdata/dist-arch/**'
|
||||
- "sdata/dist-arch/**"
|
||||
- "!sdata/dist-arch/README.md"
|
||||
# workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$lock_cmd = swaylock
|
||||
$lock_cmd = swaylock -c 000000
|
||||
# $lock_cmd = pidof hyprlock || hyprlock
|
||||
$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, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
||||
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 = 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)
|
||||
|
||||
@@ -134,11 +134,11 @@ layerrule = blur, quickshell:.*
|
||||
layerrule = ignorealpha 0.79, quickshell:.*
|
||||
layerrule = animation slide, quickshell:bar
|
||||
layerrule = animation slide bottom, quickshell:cheatsheet
|
||||
layerrule = noanim, quickshell:crosshair
|
||||
layerrule = animation slide bottom, quickshell:dock
|
||||
layerrule = animation popin 120%, quickshell:screenCorners
|
||||
layerrule = noanim, quickshell:lockWindowPusher
|
||||
layerrule = animation fade, quickshell:notificationPopup
|
||||
layerrule = noanim, quickshell:overlay
|
||||
layerrule = noanim, quickshell:overview
|
||||
layerrule = animation slide bottom, quickshell:osk
|
||||
layerrule = noanim, quickshell:polkit
|
||||
|
||||
@@ -17,6 +17,7 @@ Singleton {
|
||||
property bool osdBrightnessOpen: false
|
||||
property bool osdVolumeOpen: false
|
||||
property bool oskOpen: false
|
||||
property bool overlayOpen: false
|
||||
property bool overviewOpen: false
|
||||
property bool regionSelectorOpen: false
|
||||
property bool screenLocked: false
|
||||
|
||||
@@ -4,6 +4,7 @@ import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.widgets.widgetCanvas
|
||||
import qs.modules.common.functions as CF
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
@@ -13,18 +14,12 @@ import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
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 {
|
||||
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
|
||||
|
||||
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 string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath
|
||||
property bool wallpaperSafetyTriggered: {
|
||||
const enabled = Config.options.workSafety.enable.wallpaper
|
||||
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 enabled = Config.options.workSafety.enable.wallpaper;
|
||||
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));
|
||||
return enabled && sensitiveWallpaper && sensitiveNetwork;
|
||||
}
|
||||
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 movableYSpace: ((wallpaperHeight / wallpaperToScreenRatio * effectiveWallpaperScale) - screen.height) / 2
|
||||
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
|
||||
property bool shouldBlur: (GlobalStates.screenLocked && Config.options.lock.blur.enable)
|
||||
property color dominantColor: Appearance.colors.colPrimary // Default, to be changed
|
||||
@@ -97,8 +80,9 @@ Variants {
|
||||
right: true
|
||||
}
|
||||
color: {
|
||||
if (!bgRoot.wallpaperSafetyTriggered || bgRoot.wallpaperIsVideo) return "transparent";
|
||||
return CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75)
|
||||
if (!bgRoot.wallpaperSafetyTriggered || bgRoot.wallpaperIsVideo)
|
||||
return "transparent";
|
||||
return CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75);
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
@@ -134,53 +118,15 @@ Variants {
|
||||
// Oversized = can be zoomed for parallax, yay
|
||||
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 {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
// Wallpaper
|
||||
StyledImage {
|
||||
id: wallpaper
|
||||
visible: opacity > 0 && !blurLoader.active
|
||||
@@ -261,25 +207,23 @@ Variants {
|
||||
}
|
||||
}
|
||||
|
||||
// The clock
|
||||
Loader {
|
||||
id: clockLoader
|
||||
scale: Config.options.background.clock.scale
|
||||
active: Config.options.background.clock.show
|
||||
WidgetCanvas {
|
||||
id: widgetCanvas
|
||||
anchors {
|
||||
left: wallpaper.left
|
||||
right: wallpaper.right
|
||||
top: wallpaper.top
|
||||
horizontalCenter: undefined
|
||||
verticalCenter: undefined
|
||||
bottom: wallpaper.bottom
|
||||
readonly property real parallaxFactor: Config.options.background.parallax.widgetsFactor
|
||||
leftMargin: {
|
||||
const clockXOnWallpaper = bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2)
|
||||
const extraMove = (wallpaper.effectiveValueX * 2 * bgRoot.movableXSpace) * (root.clockParallaxFactor - 1);
|
||||
return clockXOnWallpaper - extraMove;
|
||||
const xOnWallpaper = bgRoot.movableXSpace;
|
||||
const extraMove = (wallpaper.effectiveValueX * 2 * bgRoot.movableXSpace) * (parallaxFactor - 1);
|
||||
return xOnWallpaper - extraMove;
|
||||
}
|
||||
topMargin: {
|
||||
const clockYOnWallpaper = bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2)
|
||||
const extraMove = (wallpaper.effectiveValueY * 2 * bgRoot.movableYSpace) * (root.clockParallaxFactor - 1);
|
||||
return clockYOnWallpaper - extraMove;
|
||||
const yOnWallpaper = bgRoot.movableYSpace;
|
||||
const extraMove = (wallpaper.effectiveValueY * 2 * bgRoot.movableYSpace) * (parallaxFactor - 1);
|
||||
return yOnWallpaper - extraMove;
|
||||
}
|
||||
Behavior on leftMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
@@ -288,193 +232,65 @@ Variants {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
width: wallpaper.width
|
||||
height: wallpaper.height
|
||||
states: State {
|
||||
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 {
|
||||
target: clockLoader
|
||||
target: widgetCanvas
|
||||
anchors {
|
||||
left: undefined
|
||||
right: undefined
|
||||
top: undefined
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: undefined
|
||||
// horizontalCenter: parent.horizontalCenter
|
||||
// verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
PropertyAnimation {
|
||||
properties: "width,height"
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
AnchorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
sourceComponent: Column {
|
||||
Loader {
|
||||
id: digitalClockLoader
|
||||
visible: root.clockStyle === "digital"
|
||||
active: visible
|
||||
sourceComponent: ColumnLayout {
|
||||
id: clockColumn
|
||||
spacing: 6
|
||||
|
||||
ClockText {
|
||||
font.pixelSize: 90
|
||||
text: DateTime.time
|
||||
}
|
||||
ClockText {
|
||||
Layout.topMargin: -5
|
||||
text: DateTime.date
|
||||
}
|
||||
StyledText {
|
||||
// Somehow gets fucked up if made a ClockText???
|
||||
visible: Config.options.background.showQuote && Config.options.background.quote.length > 0
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: 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
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
shown: Config.options.background.widgets.weather.enable
|
||||
sourceComponent: WeatherWidget {
|
||||
screenWidth: bgRoot.screen.width
|
||||
screenHeight: bgRoot.screen.height
|
||||
scaledScreenWidth: bgRoot.screen.width / bgRoot.effectiveWallpaperScale
|
||||
scaledScreenHeight: bgRoot.screen.height / bgRoot.effectiveWallpaperScale
|
||||
wallpaperScale: bgRoot.effectiveWallpaperScale
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: cookieClockLoader
|
||||
visible: root.clockStyle === "cookie"
|
||||
active: visible
|
||||
sourceComponent: CookieClock {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: cookieQuoteLoader
|
||||
visible: root.showCookieQuote
|
||||
active: visible
|
||||
sourceComponent: CookieQuote {}
|
||||
anchors.horizontalCenter: cookieClockLoader.horizontalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors {
|
||||
top: clockLoader.bottom
|
||||
topMargin: 8
|
||||
horizontalCenter: (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") ? clockLoader.horizontalCenter : undefined
|
||||
left: (bgRoot.textHorizontalAlignment === Text.AlignLeft) ? clockLoader.left : undefined
|
||||
right: (bgRoot.textHorizontalAlignment === Text.AlignRight) ? clockLoader.right : undefined
|
||||
leftMargin: -26
|
||||
rightMargin: -26
|
||||
}
|
||||
implicitWidth: statusTextBg.implicitWidth
|
||||
implicitHeight: statusTextBg.implicitHeight
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: statusTextBg
|
||||
visible: statusTextBg.visible && root.clockStyle === "cookie"
|
||||
opacity: statusTextBg.opacity
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: statusTextBg
|
||||
anchors.centerIn: parent
|
||||
clip: true
|
||||
opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0
|
||||
visible: opacity > 0
|
||||
implicitHeight: statusTextRow.implicitHeight + 5 * 2
|
||||
implicitWidth: statusTextRow.implicitWidth + 5 * 2
|
||||
radius: Appearance.rounding.small
|
||||
color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, root.clockStyle === "cookie" ? 0 : 1)
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: statusTextRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 14
|
||||
Item {
|
||||
Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft
|
||||
implicitWidth: 1
|
||||
}
|
||||
ClockStatusText {
|
||||
id: safetyStatusText
|
||||
shown: bgRoot.wallpaperSafetyTriggered
|
||||
statusIcon: "hide_image"
|
||||
statusText: Translation.tr("Wallpaper safety enforced")
|
||||
}
|
||||
ClockStatusText {
|
||||
id: lockStatusText
|
||||
shown: GlobalStates.screenLocked && Config.options.lock.showLockedText
|
||||
statusIcon: "lock"
|
||||
statusText: Translation.tr("Locked")
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight
|
||||
implicitWidth: 1
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
shown: Config.options.background.widgets.clock.enable
|
||||
sourceComponent: ClockWidget {
|
||||
screenWidth: bgRoot.screen.width
|
||||
screenHeight: bgRoot.screen.height
|
||||
scaledScreenWidth: bgRoot.screen.width / bgRoot.effectiveWallpaperScale
|
||||
scaledScreenHeight: bgRoot.screen.height / bgRoot.effectiveWallpaperScale
|
||||
wallpaperScale: bgRoot.effectiveWallpaperScale
|
||||
wallpaperSafetyTriggered: bgRoot.wallpaperSafetyTriggered
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 Quickshell.Io
|
||||
|
||||
import qs.modules.background.cookieClock.dateIndicator
|
||||
import qs.modules.background.cookieClock.minuteMarks
|
||||
import qs.modules.background.widgets.clock.dateIndicator
|
||||
import qs.modules.background.widgets.clock.minuteMarks
|
||||
|
||||
Item {
|
||||
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
|
||||
|
||||
@@ -36,16 +36,16 @@ Item {
|
||||
implicitHeight: implicitSize
|
||||
|
||||
function applyStyle(sides, dialStyle, hourHandStyle, minuteHandStyle, secondHandStyle, dateStyle) {
|
||||
Config.options.background.clock.cookie.sides = sides
|
||||
Config.options.background.clock.cookie.dialNumberStyle = dialStyle
|
||||
Config.options.background.clock.cookie.hourHandStyle = hourHandStyle
|
||||
Config.options.background.clock.cookie.minuteHandStyle = minuteHandStyle
|
||||
Config.options.background.clock.cookie.secondHandStyle = secondHandStyle
|
||||
Config.options.background.clock.cookie.dateStyle = dateStyle
|
||||
Config.options.background.widgets.clock.cookie.sides = sides
|
||||
Config.options.background.widgets.clock.cookie.dialNumberStyle = dialStyle
|
||||
Config.options.background.widgets.clock.cookie.hourHandStyle = hourHandStyle
|
||||
Config.options.background.widgets.clock.cookie.minuteHandStyle = minuteHandStyle
|
||||
Config.options.background.widgets.clock.cookie.secondHandStyle = secondHandStyle
|
||||
Config.options.background.widgets.clock.cookie.dateStyle = dateStyle
|
||||
}
|
||||
|
||||
function setClockPreset(category) {
|
||||
if (!Config.options.background.clock.cookie.aiStyling) return;
|
||||
if (!Config.options.background.widgets.clock.cookie.aiStyling) return;
|
||||
if (category === "") return;
|
||||
print("[Cookie clock] Setting clock preset for category: " + category)
|
||||
// "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space"
|
||||
@@ -83,17 +83,12 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
property bool useSineCookie: Config.options.background.clock.cookie.useSineCookie
|
||||
DropShadow {
|
||||
source: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
|
||||
anchors.fill: source
|
||||
radius: 8
|
||||
samples: radius * 2 + 1
|
||||
color: root.colShadow
|
||||
transparentBorder: true
|
||||
property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie
|
||||
StyledDropShadow {
|
||||
target: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: Config.options.background.clock.cookie.constantlyRotate
|
||||
running: Config.options.background.widgets.clock.cookie.constantlyRotate
|
||||
duration: 30000
|
||||
easing.type: Easing.Linear
|
||||
loops: Animation.Infinite
|
||||
@@ -108,7 +103,7 @@ Item {
|
||||
active: useSineCookie
|
||||
sourceComponent: SineCookie {
|
||||
implicitSize: root.implicitSize
|
||||
sides: Config.options.background.clock.cookie.sides
|
||||
sides: Config.options.background.widgets.clock.cookie.sides
|
||||
color: root.colBackground
|
||||
}
|
||||
}
|
||||
@@ -119,7 +114,7 @@ Item {
|
||||
active: !useSineCookie
|
||||
sourceComponent: MaterialCookie {
|
||||
implicitSize: root.implicitSize
|
||||
sides: Config.options.background.clock.cookie.sides
|
||||
sides: Config.options.background.widgets.clock.cookie.sides
|
||||
color: root.colBackground
|
||||
}
|
||||
}
|
||||
@@ -134,7 +129,7 @@ Item {
|
||||
FadeLoader {
|
||||
id: hourMarksLoader
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.hourMarks
|
||||
shown: Config.options.background.widgets.clock.cookie.hourMarks
|
||||
sourceComponent: HourMarks {
|
||||
implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity)
|
||||
color: root.colOnBackground
|
||||
@@ -146,7 +141,7 @@ Item {
|
||||
FadeLoader {
|
||||
id: timeColumnLoader
|
||||
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
|
||||
Behavior on scale {
|
||||
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
|
||||
@@ -161,11 +156,11 @@ Item {
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
z: 1
|
||||
shown: Config.options.background.clock.cookie.minuteHandStyle !== "hide"
|
||||
shown: Config.options.background.widgets.clock.cookie.minuteHandStyle !== "hide"
|
||||
sourceComponent: MinuteHand {
|
||||
anchors.fill: parent
|
||||
clockMinute: root.clockMinute
|
||||
style: Config.options.background.clock.cookie.minuteHandStyle
|
||||
style: Config.options.background.widgets.clock.cookie.minuteHandStyle
|
||||
color: root.colMinuteHand
|
||||
}
|
||||
}
|
||||
@@ -174,11 +169,11 @@ Item {
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
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 {
|
||||
clockHour: root.clockHour
|
||||
clockMinute: root.clockMinute
|
||||
style: Config.options.background.clock.cookie.hourHandStyle
|
||||
style: Config.options.background.widgets.clock.cookie.hourHandStyle
|
||||
color: root.colHourHand
|
||||
}
|
||||
}
|
||||
@@ -186,13 +181,13 @@ Item {
|
||||
// Second hand
|
||||
FadeLoader {
|
||||
id: secondHandLoader
|
||||
z: (Config.options.background.clock.cookie.secondHandStyle === "line") ? 2 : 3
|
||||
shown: Config.options.time.secondPrecision && Config.options.background.clock.cookie.secondHandStyle !== "hide"
|
||||
z: (Config.options.background.widgets.clock.cookie.secondHandStyle === "line") ? 2 : 3
|
||||
shown: Config.options.time.secondPrecision && Config.options.background.widgets.clock.cookie.secondHandStyle !== "hide"
|
||||
anchors.fill: parent
|
||||
sourceComponent: SecondHand {
|
||||
id: secondHand
|
||||
clockSecond: root.clockSecond
|
||||
style: Config.options.background.clock.cookie.secondHandStyle
|
||||
style: Config.options.background.widgets.clock.cookie.secondHandStyle
|
||||
color: root.colSecondHand
|
||||
}
|
||||
}
|
||||
@@ -201,9 +196,9 @@ Item {
|
||||
FadeLoader {
|
||||
z: 4
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.minuteHandStyle !== "bold"
|
||||
shown: Config.options.background.widgets.clock.cookie.minuteHandStyle !== "bold"
|
||||
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
|
||||
implicitHeight: implicitWidth
|
||||
radius: width / 2
|
||||
@@ -213,11 +208,11 @@ Item {
|
||||
// Date
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
shown: Config.options.background.clock.cookie.dateStyle !== "hide"
|
||||
shown: Config.options.background.widgets.clock.cookie.dateStyle !== "hide"
|
||||
|
||||
sourceComponent: DateIndicator {
|
||||
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 {
|
||||
id: root
|
||||
|
||||
readonly property string quoteText: Config.options.background.quote
|
||||
readonly property string quoteText: Config.options.background.widgets.clock.quote.text
|
||||
|
||||
implicitWidth: quoteBox.implicitWidth
|
||||
implicitHeight: quoteBox.implicitHeight
|
||||
@@ -47,7 +47,7 @@ Item {
|
||||
StyledText {
|
||||
id: quoteStyledText
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: Config.options.background.quote
|
||||
text: Config.options.background.widgets.clock.quote.text
|
||||
color: Appearance.colors.colOnSecondaryContainer
|
||||
font {
|
||||
family: Appearance.font.family.reading
|
||||
+1
-1
@@ -18,7 +18,7 @@ Item {
|
||||
rotation: (360 / 60 * clockSecond) + 90
|
||||
|
||||
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 {
|
||||
direction: RotationAnimation.Clockwise
|
||||
duration: 1000 // 1 second
|
||||
+2
-2
@@ -8,10 +8,10 @@ import QtQuick
|
||||
Column {
|
||||
id: root
|
||||
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 bool hourMarksEnabled: Config.options.background.clock.cookie.hourMarks
|
||||
property bool hourMarksEnabled: Config.options.background.widgets.clock.cookie.hourMarks
|
||||
spacing: -16
|
||||
|
||||
Repeater {
|
||||
+1
-1
@@ -14,7 +14,7 @@ Item {
|
||||
// Rotating date
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
shown: Config.options.background.clock.cookie.dateStyle === "border"
|
||||
shown: Config.options.background.widgets.clock.cookie.dateStyle === "border"
|
||||
sourceComponent: RotatingDate {
|
||||
color: root.color
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@ import QtQuick
|
||||
|
||||
Rectangle {
|
||||
id: rect
|
||||
readonly property string dialStyle: Config.options.background.clock.cookie.dialNumberStyle
|
||||
readonly property string dialStyle: Config.options.background.widgets.clock.cookie.dialNumberStyle
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
+3
-3
@@ -8,14 +8,14 @@ import QtQuick
|
||||
Item {
|
||||
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 real angleStep: 12 * Math.PI / 180
|
||||
property string dateText: Qt.locale().toString(DateTime.clock.date, "ddd dd")
|
||||
|
||||
readonly property int clockSecond: DateTime.clock.seconds
|
||||
readonly property string dialStyle: Config.options.background.clock.cookie.dialNumberStyle
|
||||
readonly property bool timeIndicators: Config.options.background.clock.cookie.timeIndicators
|
||||
readonly property string dialStyle: Config.options.background.widgets.clock.cookie.dialNumberStyle
|
||||
readonly property bool timeIndicators: Config.options.background.widgets.clock.cookie.timeIndicators
|
||||
|
||||
property real radius: style === "border" ? 90 : 0
|
||||
Behavior on radius {
|
||||
+2
-2
@@ -8,8 +8,8 @@ Item {
|
||||
id: root
|
||||
|
||||
property color color: Appearance.colors.colOnSecondaryContainer
|
||||
property string style: Config.options.background.clock.cookie.dialNumberStyle // "dots", "numbers", "full", "hide"
|
||||
property string dateStyle : Config.options.background.clock.cookie.dateStyle
|
||||
property string style: Config.options.background.widgets.clock.cookie.dialNumberStyle // "dots", "numbers", "full", "hide"
|
||||
property string dateStyle : Config.options.background.widgets.clock.cookie.dateStyle
|
||||
|
||||
// 12 Dots
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ MouseArea {
|
||||
|
||||
MaterialSymbol {
|
||||
fill: 0
|
||||
text: WeatherIcons.codeToName[Weather.data.wCode] ?? "cloud"
|
||||
text: Icons.getWeatherIcon(Weather.data.wCode) ?? "cloud"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer1
|
||||
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 clock: JsonObject {
|
||||
property bool fixedPosition: false
|
||||
property real x: -500
|
||||
property real y: -500
|
||||
property bool show: true
|
||||
property string style: "cookie" // Options: "cookie", "digital"
|
||||
property real scale: 1
|
||||
property JsonObject cookie: JsonObject {
|
||||
property bool aiStyling: false
|
||||
property int sides: 14
|
||||
property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none"
|
||||
property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide"
|
||||
property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide"
|
||||
property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide"
|
||||
property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide"
|
||||
property bool timeIndicators: true
|
||||
property bool hourMarks: false
|
||||
property bool dateInClock: true
|
||||
property bool constantlyRotate: false
|
||||
property bool useSineCookie: false
|
||||
property JsonObject widgets: JsonObject {
|
||||
property JsonObject clock: JsonObject {
|
||||
property bool enable: true
|
||||
property string placementStrategy: "leastBusy" // "free", "leastBusy", "mostBusy"
|
||||
property real x: 100
|
||||
property real y: 100
|
||||
property string style: "cookie" // Options: "cookie", "digital"
|
||||
property JsonObject cookie: JsonObject {
|
||||
property bool aiStyling: false
|
||||
property int sides: 14
|
||||
property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none"
|
||||
property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide"
|
||||
property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide"
|
||||
property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide"
|
||||
property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide"
|
||||
property bool timeIndicators: true
|
||||
property bool hourMarks: false
|
||||
property bool dateInClock: true
|
||||
property bool constantlyRotate: 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 bool animateChange: true
|
||||
property JsonObject weather: JsonObject {
|
||||
property bool enable: false
|
||||
property string placementStrategy: "free" // "free", "leastBusy", "mostBusy"
|
||||
property real x: 400
|
||||
property real y: 100
|
||||
}
|
||||
|
||||
}
|
||||
property string wallpaperPath: ""
|
||||
property string thumbnailPath: ""
|
||||
property string quote: ""
|
||||
property bool showQuote: false
|
||||
property bool hideWhenFullscreen: true
|
||||
property JsonObject parallax: JsonObject {
|
||||
property bool vertical: false
|
||||
@@ -182,7 +190,7 @@ Singleton {
|
||||
property bool enableWorkspace: true
|
||||
property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size
|
||||
property bool enableSidebar: true
|
||||
property real clockFactor: 1.2
|
||||
property real widgetsFactor: 1.2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +342,7 @@ Singleton {
|
||||
property bool useHyprlock: false
|
||||
property bool launchOnStartup: false
|
||||
property JsonObject blur: JsonObject {
|
||||
property bool enable: false
|
||||
property bool enable: true
|
||||
property real radius: 100
|
||||
property real extraZoom: 1.1
|
||||
}
|
||||
@@ -369,6 +377,11 @@ Singleton {
|
||||
property bool pinnedOnStartup: false
|
||||
}
|
||||
|
||||
property JsonObject overlay: JsonObject {
|
||||
property bool openingZoomAnimation: true
|
||||
property bool darkenScreen: true
|
||||
}
|
||||
|
||||
property JsonObject overview: JsonObject {
|
||||
property bool enable: true
|
||||
property real scale: 0.18 // Relative to screen size
|
||||
@@ -385,6 +398,7 @@ Singleton {
|
||||
property bool showLabel: false
|
||||
property real opacity: 0.3
|
||||
property real contentRegionOpacity: 0.8
|
||||
property int selectionPadding: 5
|
||||
}
|
||||
property JsonObject rect: JsonObject {
|
||||
property bool showAimLines: true
|
||||
|
||||
@@ -20,4 +20,63 @@ Singleton {
|
||||
return "keyboard";
|
||||
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,22 @@ Singleton {
|
||||
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: 100
|
||||
property real y: 100
|
||||
}
|
||||
property JsonObject volumeMixer: JsonObject {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: false
|
||||
property real x: 55
|
||||
property real y: 188
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject timer: JsonObject {
|
||||
property JsonObject pomodoro: JsonObject {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ Scope {
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,37 @@
|
||||
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" }
|
||||
]
|
||||
|
||||
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,18 @@
|
||||
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
|
||||
|
||||
DelegateChooser {
|
||||
id: root
|
||||
role: "identifier"
|
||||
|
||||
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
||||
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
||||
}
|
||||
@@ -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,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: 700
|
||||
implicitWidth: 400
|
||||
|
||||
VolumeDialogContent {
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.padding
|
||||
isSink: true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -224,8 +224,8 @@ Item {
|
||||
}
|
||||
|
||||
z: Drag.active ? root.windowDraggingZ : (root.windowZ + windowData?.floating)
|
||||
Drag.hotSpot.x: targetWindowWidth / 2
|
||||
Drag.hotSpot.y: targetWindowHeight / 2
|
||||
Drag.hotSpot.x: width / 2
|
||||
Drag.hotSpot.y: height / 2
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -121,10 +121,11 @@ PanelWindow {
|
||||
return (root.targetedRegionX >= 0 && root.targetedRegionY >= 0)
|
||||
}
|
||||
function setRegionToTargeted() {
|
||||
root.regionX = root.targetedRegionX;
|
||||
root.regionY = root.targetedRegionY;
|
||||
root.regionWidth = root.targetedRegionWidth;
|
||||
root.regionHeight = root.targetedRegionHeight;
|
||||
const padding = Config.options.regionSelector.targetRegions.selectionPadding; // Make borders not cut off n stuff
|
||||
root.regionX = root.targetedRegionX - padding;
|
||||
root.regionY = root.targetedRegionY - padding;
|
||||
root.regionWidth = root.targetedRegionWidth + padding * 2;
|
||||
root.regionHeight = root.targetedRegionHeight + padding * 2;
|
||||
}
|
||||
|
||||
function updateTargetedRegion(x, y) {
|
||||
|
||||
@@ -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"),
|
||||
icon: "timer_10",
|
||||
value: '["1","2","3","4","5","6","7","8","9","10"]'
|
||||
value: '[]'
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Japanese"),
|
||||
icon: "square_dot",
|
||||
value: '["一","二","三","四","五","六","七","八","九","十"]'
|
||||
value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]'
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Roman"),
|
||||
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"]'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -701,6 +701,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 {
|
||||
icon: "screenshot_frame_2"
|
||||
title: Translation.tr("Region selector (screen snipping/Google Lens)")
|
||||
|
||||
@@ -329,7 +329,7 @@ Rectangle {
|
||||
segmentContent: thisBlock.content
|
||||
messageData: root.messageData
|
||||
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
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
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
|
||||
text: modelData
|
||||
|
||||
|
||||
@@ -11,87 +11,14 @@ import Quickshell.Services.Pipewire
|
||||
WindowDialog {
|
||||
id: root
|
||||
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
|
||||
|
||||
WindowDialogTitle {
|
||||
text: root.isSink ? Translation.tr("Audio output") : Translation.tr("Audio input")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
VolumeDialogContent {
|
||||
isSink: root.isSink
|
||||
}
|
||||
|
||||
WindowDialogSeparator {
|
||||
@@ -117,20 +44,4 @@ WindowDialog {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ def center_crop(img, target_w, target_h):
|
||||
y2 = y1 + target_h
|
||||
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)
|
||||
if img is None:
|
||||
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]
|
||||
return total
|
||||
min_var = None
|
||||
max_var = None
|
||||
min_coords = (horizontal_padding, vertical_padding)
|
||||
max_coords = (horizontal_padding, vertical_padding)
|
||||
area = region_width * region_height
|
||||
x_start = horizontal_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):
|
||||
min_var = var
|
||||
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):
|
||||
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("--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("--busiest", action="store_true", help="Find the busiest region instead of the least busy")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.largest_region:
|
||||
@@ -363,7 +372,8 @@ def main():
|
||||
stride=args.stride,
|
||||
screen_mode=args.screen_mode,
|
||||
horizontal_padding=args.horizontal_padding,
|
||||
vertical_padding=args.vertical_padding
|
||||
vertical_padding=args.vertical_padding,
|
||||
busiest=args.busiest
|
||||
)
|
||||
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)
|
||||
|
||||
@@ -38,7 +38,8 @@ Singleton {
|
||||
Translation.tr("Low battery"),
|
||||
Translation.tr("Consider plugging in your device"),
|
||||
"-u", "critical",
|
||||
"-a", "Shell"
|
||||
"-a", "Shell",
|
||||
"--hint=int:transient:1",
|
||||
])
|
||||
|
||||
if (root.soundEnabled) Audio.playSystemSound("dialog-warning");
|
||||
@@ -51,7 +52,8 @@ Singleton {
|
||||
Translation.tr("Critically low battery"),
|
||||
Translation.tr("Please charge!\nAutomatic suspend triggers at %1%").arg(Config.options.battery.suspend),
|
||||
"-u", "critical",
|
||||
"-a", "Shell"
|
||||
"-a", "Shell",
|
||||
"--hint=int:transient:1",
|
||||
]);
|
||||
|
||||
if (root.soundEnabled) Audio.playSystemSound("suspend-error");
|
||||
@@ -69,7 +71,8 @@ Singleton {
|
||||
"notify-send",
|
||||
Translation.tr("Battery full"),
|
||||
Translation.tr("Please unplug the charger"),
|
||||
"-a", "Shell"
|
||||
"-a", "Shell",
|
||||
"--hint=int:transient:1",
|
||||
]);
|
||||
|
||||
if (root.soundEnabled) Audio.playSystemSound("complete");
|
||||
|
||||
@@ -108,7 +108,7 @@ Singleton {
|
||||
}
|
||||
}
|
||||
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) {
|
||||
console.error("[KeyringStorage] Entry not found, initializing.");
|
||||
root.keyringData = {};
|
||||
|
||||
@@ -25,6 +25,7 @@ Singleton {
|
||||
"text": action.text,
|
||||
})) ?? []
|
||||
property bool popup: false
|
||||
property bool isTransient: notification?.hints.transient ?? false
|
||||
property string appIcon: notification?.appIcon ?? ""
|
||||
property string appName: notification?.appName ?? ""
|
||||
property string body: notification?.body ?? ""
|
||||
@@ -63,7 +64,11 @@ Singleton {
|
||||
interval: 7000
|
||||
running: true
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ ApplicationWindow {
|
||||
{
|
||||
name: Translation.tr("General"),
|
||||
icon: "browse",
|
||||
iconRotation: 180,
|
||||
component: "modules/settings/GeneralConfig.qml"
|
||||
},
|
||||
{
|
||||
@@ -40,6 +39,11 @@ ApplicationWindow {
|
||||
iconRotation: 180,
|
||||
component: "modules/settings/BarConfig.qml"
|
||||
},
|
||||
{
|
||||
name: Translation.tr("Background"),
|
||||
icon: "texture",
|
||||
component: "modules/settings/BackgroundConfig.qml"
|
||||
},
|
||||
{
|
||||
name: Translation.tr("Interface"),
|
||||
icon: "bottom_app_bar",
|
||||
|
||||
@@ -11,7 +11,6 @@ import qs.modules.common
|
||||
import qs.modules.background
|
||||
import qs.modules.bar
|
||||
import qs.modules.cheatsheet
|
||||
import qs.modules.crosshair
|
||||
import qs.modules.dock
|
||||
import qs.modules.lock
|
||||
import qs.modules.mediaControls
|
||||
@@ -25,6 +24,7 @@ import qs.modules.screenCorners
|
||||
import qs.modules.sessionScreen
|
||||
import qs.modules.sidebarLeft
|
||||
import qs.modules.sidebarRight
|
||||
import qs.modules.overlay
|
||||
import qs.modules.verticalBar
|
||||
import qs.modules.wallpaperSelector
|
||||
|
||||
@@ -39,7 +39,6 @@ ShellRoot {
|
||||
property bool enableBar: true
|
||||
property bool enableBackground: true
|
||||
property bool enableCheatsheet: true
|
||||
property bool enableCrosshair: true
|
||||
property bool enableDock: true
|
||||
property bool enableLock: true
|
||||
property bool enableMediaControls: true
|
||||
@@ -47,6 +46,7 @@ ShellRoot {
|
||||
property bool enablePolkit: true
|
||||
property bool enableOnScreenDisplay: true
|
||||
property bool enableOnScreenKeyboard: true
|
||||
property bool enableOverlay: true
|
||||
property bool enableOverview: true
|
||||
property bool enableRegionSelector: true
|
||||
property bool enableReloadPopup: true
|
||||
@@ -70,13 +70,13 @@ ShellRoot {
|
||||
LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} }
|
||||
LazyLoader { active: enableBackground; component: Background {} }
|
||||
LazyLoader { active: enableCheatsheet; component: Cheatsheet {} }
|
||||
LazyLoader { active: enableCrosshair; component: Crosshair {} }
|
||||
LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} }
|
||||
LazyLoader { active: enableLock; component: Lock {} }
|
||||
LazyLoader { active: enableMediaControls; component: MediaControls {} }
|
||||
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
|
||||
LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} }
|
||||
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
|
||||
LazyLoader { active: enableOverlay; component: Overlay {} }
|
||||
LazyLoader { active: enableOverview; component: Overview {} }
|
||||
LazyLoader { active: enablePolkit; component: Polkit {} }
|
||||
LazyLoader { active: enableRegionSelector; component: RegionSelector {} }
|
||||
|
||||
@@ -9,7 +9,7 @@ conflicts=("quickshell-git")
|
||||
_pkgname=quickshell
|
||||
pkgname="$_prefix-$_pkgname-git"
|
||||
pkgver=0.1.0.r1
|
||||
pkgrel=3
|
||||
pkgrel=4
|
||||
pkgdesc="$_pkgname-git which version pinned for $_prefix"
|
||||
arch=(x86_64 aarch64)
|
||||
url='https://git.outfoxxed.me/quickshell/quickshell'
|
||||
@@ -26,6 +26,21 @@ depends=(
|
||||
'libdrm'
|
||||
'mesa'
|
||||
'google-breakpad'
|
||||
# NOTE: Below are custom dependencies of illogical-impulse
|
||||
qt6-5compat
|
||||
qt6-avif-image-plugin
|
||||
qt6-imageformats
|
||||
qt6-multimedia
|
||||
qt6-positioning
|
||||
qt6-quicktimeline
|
||||
qt6-sensors
|
||||
qt6-svg
|
||||
qt6-tools
|
||||
qt6-translations
|
||||
qt6-virtualkeyboard
|
||||
qt6-wayland
|
||||
kdialog
|
||||
syntax-highlighting
|
||||
)
|
||||
makedepends=(
|
||||
'spirv-tools'
|
||||
|
||||
@@ -1,27 +1,11 @@
|
||||
pkgname=illogical-impulse-toolkit
|
||||
pkgver=1.0
|
||||
pkgrel=1
|
||||
pkgdesc='Illogical Impulse GTK/Qt Dependencies'
|
||||
pkgrel=2
|
||||
pkgdesc='Illogical Impulse Toolkit Dependencies'
|
||||
arch=(any)
|
||||
license=(None)
|
||||
depends=(
|
||||
kdialog
|
||||
syntax-highlighting
|
||||
upower
|
||||
wtype
|
||||
ydotool
|
||||
qt6-5compat
|
||||
qt6-avif-image-plugin
|
||||
qt6-base
|
||||
qt6-declarative
|
||||
qt6-imageformats
|
||||
qt6-multimedia
|
||||
qt6-positioning
|
||||
qt6-quicktimeline
|
||||
qt6-sensors
|
||||
qt6-svg
|
||||
qt6-tools
|
||||
qt6-translations
|
||||
qt6-virtualkeyboard
|
||||
qt6-wayland
|
||||
)
|
||||
|
||||
@@ -3,14 +3,23 @@
|
||||
- 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)
|
||||
|
||||
**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
|
||||
Note that this script must be idempotent.
|
||||
|
||||
TODO:
|
||||
- [ ] Fix all TODOs inside `dist-nix`.
|
||||
- [ ] Warn user if inode-limited filesystem (typically ext4) is used.
|
||||
- [ ] 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))
|
||||
- [ ] 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`:
|
||||
```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)
|
||||
@@ -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).
|
||||
- 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
|
||||
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 .
|
||||
|
||||
Generated
+11
-11
@@ -25,16 +25,16 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758463745,
|
||||
"narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=",
|
||||
"lastModified": 1762296971,
|
||||
"narHash": "sha256-Jyv3L5rrUYpecON+9zyFz2VqgTSTsIG35fXuCyuCQv0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3",
|
||||
"rev": "34fe48801d2a5301b814eaa1efb496499d06cebc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "release-25.05",
|
||||
"ref": "master",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -45,11 +45,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1752054764,
|
||||
"narHash": "sha256-Ob/HuUhANoDs+nvYqyTKrkcPXf4ZgXoqMTQoCK0RFgQ=",
|
||||
"lastModified": 1762090880,
|
||||
"narHash": "sha256-fbRQzIGPkjZa83MowjbD2ALaJf9y6KMDdJBQMKFeY/8=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixGL",
|
||||
"rev": "a8e1ce7d49a149ed70df676785b07f63288f53c5",
|
||||
"rev": "b6105297e6f0cd041670c3e8628394d4ee247ed5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -75,16 +75,16 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1761597516,
|
||||
"narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=",
|
||||
"lastModified": 1762111121,
|
||||
"narHash": "sha256-4vhDuZ7OZaZmKKrnDpxLZZpGIJvAeMtK6FKLJYUtAdw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "daf6dc47aa4b44791372d6139ab7b25269184d55",
|
||||
"rev": "b3d51a0365f6695e7dd5cdf3e180604530ed33b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-25.05",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
description = "illogical-impulse";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
# Qt 6.10 is not yet available from released version of nixpkgs.
|
||||
#nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-25.05";
|
||||
#url = "github:nix-community/home-manager/release-25.05";
|
||||
url = "github:nix-community/home-manager/master";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
#hyprland = {
|
||||
# url = "github:hyprwm/Hyprland";
|
||||
#};
|
||||
nixgl.url = "github:nix-community/nixGL";
|
||||
quickshell = {
|
||||
url = "github:quickshell-mirror/quickshell/db1777c20b936a86528c1095cbcb1ebd92801402";
|
||||
@@ -24,6 +24,7 @@
|
||||
home_attrs = rec {
|
||||
username = import ./username.nix;
|
||||
homeDirectory = "/home/${username}";
|
||||
# Do not edit stateVersion value, see https://github.com/nix-community/home-manager/issues/5794
|
||||
stateVersion = "25.05";
|
||||
};
|
||||
system = "x86_64-linux";
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
xdg-desktop-portal-wlr
|
||||
kdePackages.xdg-desktop-portal-kde
|
||||
];
|
||||
config.hyprland = {
|
||||
default = [ "hyprland" "gtk" ];
|
||||
"org.freedesktop.impl.portal.ScreenCast" = [
|
||||
"gnome"
|
||||
];
|
||||
};
|
||||
# The following seems to generate ~/.config/xdg-desktop-portal conflicting with the one under dots/
|
||||
#config.hyprland = {
|
||||
# default = [ "hyprland" "gtk" ];
|
||||
# "org.freedesktop.impl.portal.ScreenCast" = [ "gnome" ];
|
||||
#};
|
||||
};
|
||||
## Allow fontconfig to discover fonts in home.packages
|
||||
# Note: The following generate files under ~/.config/fontconfig/conf.d/
|
||||
# fontconfig may rely on this to properly find fonts installed via Nix.
|
||||
fonts.fontconfig.enable = true;
|
||||
|
||||
wayland.windowManager.hyprland = {
|
||||
@@ -30,8 +30,6 @@
|
||||
package = config.lib.nixGL.wrap pkgs.hyprland;
|
||||
};
|
||||
|
||||
|
||||
|
||||
home = {
|
||||
packages = with pkgs; [
|
||||
##### Sure #####
|
||||
@@ -90,7 +88,7 @@
|
||||
#breeze-plus (https://github.com/mjkim0727/breeze-plus) (TODO: Not available as nixpkg) (Used in kde-material-you-colors config)
|
||||
darkly darkly-qt5 #darkly-bin (darkly is supposed to be set as the theme for Qt apps, just have not figured out how to properly set it yet.)
|
||||
eza #eza (Used in Fish config: `alias ls 'eza --icons'`)
|
||||
#fish (Install via system PM instead)
|
||||
#fish (Install via system PM instead; TODO: should install via nix in future when authentication problem fixed)
|
||||
fontconfig #fontconfig (Basic thing)
|
||||
kitty #kitty (Used in fuzzel, Hyprland, kdeglobals and Quickshell config; kitty config is also included as dots)
|
||||
matugen #matugen-bin (Used in Quickshell)
|
||||
@@ -108,7 +106,7 @@
|
||||
### illogical-impulse-hyprland
|
||||
hypridle #hypridle (Used for loginctl to lock session)
|
||||
#hyprland (Need NixGL, included elsewhere)
|
||||
#hyprlock (Should not be installed via Nix)
|
||||
#hyprlock (Should not be installed via Nix; TODO: should install via nix in future when authentication problem fixed)
|
||||
hyprpicker #hyprpicker (Used in Hyprland and Quickshell config)
|
||||
hyprsunset #hyprsunset (Used in Quickshell config)
|
||||
#xdg-desktop-portal-hyprland (DUPLICATE)
|
||||
@@ -117,10 +115,10 @@
|
||||
|
||||
### illogical-impulse-kde
|
||||
kdePackages.bluedevil #bluedevil (Seems not being used anywhere, maybe a part of KDE settings panel)
|
||||
#gnome-keyring #gnome-keyring (TODO: Install via system PM instead) (Provide executable gnome-keyring-daemon, used in Hyprland and Quickshell config)
|
||||
#gnome-keyring #gnome-keyring (TODO: Install via system PM instead; should install via nix in future when authentication problem fixed) (Provide executable gnome-keyring-daemon, used in Hyprland and Quickshell config)
|
||||
networkmanager #networkmanager
|
||||
kdePackages.plasma-nm #plasma-nm (Seems not being used anywhere, maybe a part of KDE settings panel)
|
||||
#polkit-kde-agent (TODO: Install via system PM instead)
|
||||
#polkit-kde-agent (TODO: Install via system PM instead; should install via nix in future when authentication problem fixed)
|
||||
kdePackages.dolphin #dolphin (Used in Hyprland and Quickshell config)
|
||||
kdePackages.systemsettings #systemsettings (Used in Hyprland keybinds.conf)
|
||||
|
||||
@@ -153,10 +151,6 @@
|
||||
gobject-introspection #gobject-introspection (Not explicitly used)
|
||||
|
||||
|
||||
### illogical-impulse-quickshell-git
|
||||
#quickshell.packages.x86_64-linux.default (NixGL applicable, included elsewhere)
|
||||
|
||||
|
||||
### illogical-impulse-screencapture
|
||||
hyprshot #hyprshot (Used in Hyprland keybinds.conf as fallback)
|
||||
slurp #slurp (Used in Hyprland and Quickshell config)
|
||||
@@ -167,24 +161,6 @@
|
||||
|
||||
|
||||
### illogical-impulse-toolkit
|
||||
kdePackages.kdialog #kdialog (Used in Quickshell config)
|
||||
# https://nixos.wiki/wiki/Qt
|
||||
# https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/libraries/qt-6/srcs.nix
|
||||
qt6.qt5compat #qt6-5compat
|
||||
#qt6.qtimageformats (TODO: really?) #qt6-avif-image-plugin
|
||||
qt6.qtbase #qt6-base
|
||||
qt6.qtdeclarative #qt6-declarative
|
||||
qt6.qtimageformats #qt6-imageformats
|
||||
qt6.qtmultimedia #qt6-multimedia
|
||||
qt6.qtpositioning #qt6-positioning
|
||||
qt6.qtquicktimeline #qt6-quicktimeline
|
||||
qt6.qtsensors #qt6-sensors
|
||||
qt6.qtsvg #qt6-svg
|
||||
qt6.qttools #qt6-tools
|
||||
qt6.qttranslations #qt6-translations
|
||||
qt6.qtvirtualkeyboard #qt6-virtualkeyboard
|
||||
qt6.qtwayland #qt6-wayland
|
||||
kdePackages.syntax-highlighting #syntax-highlighting (Used in Quickshell config)
|
||||
upower #upower (Used in Quickshell config)
|
||||
wtype #wtype (Used in Hyprland scripts/fuzzel-emoji.sh)
|
||||
ydotool #ydotool (Used in Quickshell config)
|
||||
@@ -205,7 +181,10 @@
|
||||
]
|
||||
++ [
|
||||
#(config.lib.nixGL.wrap pkgs.hyprland)
|
||||
(config.lib.nixGL.wrap quickshell.packages.x86_64-linux.default)
|
||||
|
||||
### illogical-impulse-quickshell-git
|
||||
#(config.lib.nixGL.wrap quickshell.packages.x86_64-linux.default)
|
||||
(import ./quickshell.nix { inherit pkgs quickshell; nixGLWrap = config.lib.nixGL.wrap; })
|
||||
];
|
||||
}//home_attrs;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
{ pkgs, quickshell, nixGLWrap, ... }:
|
||||
let
|
||||
qs = nixGLWrap quickshell.packages.x86_64-linux.default;
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
name = "illogical-impulse-quickshell-wrapper";
|
||||
meta = with pkgs.lib; {
|
||||
description = "Quickshell wrapped with NixGL + bundled Qt deps for home-manager usage";
|
||||
license = licenses.gpl3Only;
|
||||
};
|
||||
|
||||
dontUnpack = true;
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.makeWrapper
|
||||
pkgs.qt6.wrapQtAppsHook
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
qs
|
||||
kdePackages.qtwayland
|
||||
kdePackages.qtpositioning
|
||||
kdePackages.qtlocation
|
||||
kdePackages.syntax-highlighting
|
||||
gsettings-desktop-schemas
|
||||
# https://nixos.wiki/wiki/Qt
|
||||
# https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/libraries/qt-6/srcs.nix
|
||||
qt6.qtbase #qt6-base
|
||||
qt6.qtdeclarative #qt6-declarative
|
||||
qt6.qt5compat #qt6-5compat
|
||||
#qt6-avif-image-plugin (TODO: seems not available as nixpkg)
|
||||
qt6.qtimageformats #qt6-imageformats
|
||||
qt6.qtmultimedia #qt6-multimedia
|
||||
qt6.qtpositioning #qt6-positioning
|
||||
qt6.qtquicktimeline #qt6-quicktimeline
|
||||
qt6.qtsensors #qt6-sensors
|
||||
qt6.qtsvg #qt6-svg
|
||||
qt6.qttools #qt6-tools
|
||||
qt6.qttranslations #qt6-translations
|
||||
qt6.qtvirtualkeyboard #qt6-virtualkeyboard
|
||||
qt6.qtwayland #qt6-wayland
|
||||
kdePackages.kdialog #kdialog (Used in Quickshell config)
|
||||
kdePackages.syntax-highlighting #syntax-highlighting (Used in Quickshell config)
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
ls -l ${qs}/bin || true
|
||||
makeWrapper ${qs}/bin/qs $out/bin/qs \
|
||||
--prefix XDG_DATA_DIRS : ${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}
|
||||
chmod +x $out/bin/qs
|
||||
'';
|
||||
}
|
||||
@@ -8,7 +8,54 @@ function vianix-warning(){
|
||||
printf "despite that this should be reversible.\n"
|
||||
pause
|
||||
}
|
||||
function install_cmds(){
|
||||
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||
local pkgs=()
|
||||
for cmd in "$@";do
|
||||
# For package name which is not cmd name, use "case" syntax to replace
|
||||
pkgs+=($cmd)
|
||||
done
|
||||
x sudo pacman -Syu
|
||||
x sudo pacman -S --noconfirm --needed "${pkgs[@]}"
|
||||
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||
local pkgs=()
|
||||
for cmd in "$@";do
|
||||
# For package name which is not cmd name, use "case" syntax to replace
|
||||
pkgs+=($cmd)
|
||||
done
|
||||
x sudo apt update -y
|
||||
x sudo apt install -y "${pkgs[@]}"
|
||||
elif [[ "$OS_DISTRO_ID" == "fedora" || "$OS_DISTRO_ID_LIKE" == "fedora" ]]; then
|
||||
local pkgs=()
|
||||
for cmd in "$@";do
|
||||
# For package name which is not cmd name, use "case" syntax to replace
|
||||
pkgs+=($cmd)
|
||||
done
|
||||
x sudo dnf install -y "${pkgs[@]}"
|
||||
elif [[ "$OS_DISTRO_ID" =~ ^(opensuse-leap|opensuse-tumbleweed)$ ]] || [[ "$OS_DISTRO_ID_LIKE" =~ ^(opensuse|suse)(\ (opensuse|suse))?$ ]]; then
|
||||
local pkgs=()
|
||||
for cmd in "$@";do
|
||||
# For package name which is not cmd name, use "case" syntax to replace
|
||||
pkgs+=($cmd)
|
||||
done
|
||||
x sudo zypper refresh
|
||||
x sudo zypper -n install "${pkgs[@]}"
|
||||
fi
|
||||
}
|
||||
function install_nix(){
|
||||
# https://github.com/NixOS/experimental-nix-installer
|
||||
local cmd=nix
|
||||
|
||||
x mkdir -p ${REPO_ROOT}/cache
|
||||
x curl -JLo ${REPO_ROOT}/cache/nix-installer https://artifacts.nixos.org/experimental-installer
|
||||
x sh ${REPO_ROOT}/cache/nix-installer install
|
||||
try source '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_home-manager(){
|
||||
# https://nix-community.github.io/home-manager/index.xhtml#sec-install-standalone
|
||||
local cmd=home-manager
|
||||
@@ -31,72 +78,6 @@ function install_home-manager(){
|
||||
echo "If this is the problem, use \"su - user\" instead."
|
||||
return 1
|
||||
}
|
||||
function install_nix(){
|
||||
# https://github.com/NixOS/experimental-nix-installer
|
||||
local cmd=nix
|
||||
|
||||
x mkdir -p ${REPO_ROOT}/cache
|
||||
x curl -JLo ${REPO_ROOT}/cache/nix-installer https://artifacts.nixos.org/experimental-installer
|
||||
x sh ${REPO_ROOT}/cache/nix-installer install
|
||||
try source '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_curl(){
|
||||
local cmd=curl
|
||||
|
||||
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||
x sudo pacman -Syu
|
||||
x sudo pacman -S --noconfirm $cmd
|
||||
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||
x sudo apt update
|
||||
x sudo apt install $cmd
|
||||
fi
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_fish(){
|
||||
local cmd=fish
|
||||
|
||||
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||
x sudo pacman -Syu
|
||||
x sudo pacman -S --noconfirm $cmd
|
||||
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||
x sudo apt update
|
||||
x sudo apt install $cmd
|
||||
fi
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_swaylock(){
|
||||
local cmd=swaylock
|
||||
echo "Detecting command \"$cmd\"..."
|
||||
command -v $cmd && return
|
||||
echo "Command \"$cmd\" not found, try to install..."
|
||||
|
||||
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||
x sudo pacman -Syu
|
||||
x sudo pacman -S --noconfirm $cmd
|
||||
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||
x sudo apt update
|
||||
x sudo apt install $cmd
|
||||
fi
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
|
||||
function hm_deps(){
|
||||
SETUP_HM_DIR="${REPO_ROOT}/sdata/dist-nix/home-manager"
|
||||
SETUP_USERNAME_NIXFILE="${SETUP_HM_DIR}/username.nix"
|
||||
@@ -115,21 +96,19 @@ function hm_deps(){
|
||||
|
||||
vianix-warning
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"curl\" not found.${STY_RST}"
|
||||
showfun install_curl
|
||||
v install_curl
|
||||
fi
|
||||
if ! command -v fish >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"fish\" not found.${STY_RST}"
|
||||
showfun install_fish
|
||||
v install_fish
|
||||
fi
|
||||
if ! command -v swaylock >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"swaylock\" not found.${STY_RST}"
|
||||
showfun install_swaylock
|
||||
v install_swaylock
|
||||
NOT_FOUND_CMDS=()
|
||||
TEST_CMDS=(curl fish swaylock gnome-keyring)
|
||||
for cmd in "${TEST_CMDS[@]}"; do
|
||||
if ! command -v $cmd >/dev/null 2>&1;then
|
||||
NOT_FOUND_CMDS+=($cmd)
|
||||
fi
|
||||
done
|
||||
if [[ ${#NOT_FOUND_CMDS[@]} -gt 0 ]]; then
|
||||
echo -e "${STY_YELLOW}[$0]: Not found: ${NOT_FOUND_CMDS[*]}.${STY_RST}"
|
||||
showfun install_cmds
|
||||
v install_cmds "${NOT_FOUND_CMDS[@]}"
|
||||
fi
|
||||
|
||||
if ! command -v nix >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"nix\" not found.${STY_RST}"
|
||||
showfun install_nix
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# This script is meant to be sourced.
|
||||
# It's not for directly running.
|
||||
|
||||
#####################################################################################
|
||||
# These python packages are installed using uv into the venv (virtual environment). Once the folder of the venv gets deleted, they are all gone cleanly. So it's considered as setups, not dependencies.
|
||||
showfun install-python-packages
|
||||
v install-python-packages
|
||||
|
||||
if [[ -z $(getent group i2c) ]]; then
|
||||
v sudo groupadd i2c
|
||||
fi
|
||||
|
||||
v sudo usermod -aG video,i2c,input "$(whoami)"
|
||||
|
||||
if [[ ! -z $(systemctl --version) ]]; then
|
||||
v bash -c "echo i2c-dev | sudo tee /etc/modules-load.d/i2c-dev.conf"
|
||||
# TODO: find a proper way for enable Nix installed ydotool
|
||||
if ! [[ "${INSTALL_VIA_NIX}" == true ]]; then
|
||||
v systemctl --user enable ydotool --now
|
||||
fi
|
||||
v sudo systemctl enable bluetooth --now
|
||||
elif [[ ! -z $(openrc --version) ]]; then
|
||||
v bash -c "echo 'modules=i2c-dev' | sudo tee -a /etc/conf.d/modules"
|
||||
v sudo rc-update add modules boot
|
||||
v sudo rc-update add ydotool default
|
||||
v sudo rc-update add bluetooth default
|
||||
|
||||
x sudo rc-service ydotool start
|
||||
x sudo rc-service bluetooth start
|
||||
else
|
||||
printf "${STY_RED}"
|
||||
printf "====================INIT SYSTEM NOT FOUND====================\n"
|
||||
printf "${STY_RST}"
|
||||
pause
|
||||
fi
|
||||
|
||||
v gsettings set org.gnome.desktop.interface font-name 'Rubik 11'
|
||||
v gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
|
||||
v kwriteconfig6 --file kdeglobals --group KDE --key widgetStyle Darkly
|
||||
@@ -19,7 +19,7 @@ export OS_DISTRO_ID_LIKE=$(awk -F'=' '/^ID_LIKE=/ { gsub("\"","",$2); print tolo
|
||||
|
||||
if [[ "$INSTALL_VIA_NIX" == "true" ]]; then
|
||||
|
||||
TARGET_ID=fallback
|
||||
TARGET_ID=nix
|
||||
printf "${STY_YELLOW}"
|
||||
printf "===WARNING===\n"
|
||||
printf "./sdata/dist-${TARGET_ID}/install-setups.sh will be used.\n"
|
||||
|
||||
Reference in New Issue
Block a user