mirror of
https://github.com/belsabbagh/dotfiles.git
synced 2026-04-11 09:36:46 +00:00
quickshell and hyprland additions
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs.config
|
||||
import qs.modules.functions
|
||||
import qs.modules.components
|
||||
import qs.services
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: backgroundContainer
|
||||
|
||||
required property var modelData
|
||||
property string displayName: modelData.name
|
||||
|
||||
property url wallpaperPath: {
|
||||
const displays = Config.runtime.monitors
|
||||
const fallback = Config.runtime.appearance.background.defaultPath
|
||||
|
||||
if (!displays)
|
||||
return fallback
|
||||
|
||||
const monitor = displays?.[displayName]
|
||||
return monitor?.wallpaper ?? fallback
|
||||
}
|
||||
|
||||
// parallax config
|
||||
property bool parallaxEnabled: Config.runtime.appearance.background.parallax.enabled
|
||||
property real parallaxZoom: Config.runtime.appearance.background.parallax.zoom
|
||||
property int workspaceRange: Config.runtime.bar.modules.workspaces.workspaceIndicators
|
||||
|
||||
// hyprland
|
||||
property int activeWorkspaceId: Hyprland.focusedWorkspace?.id ?? 1
|
||||
|
||||
// wallpaper geometry
|
||||
property real wallpaperWidth: bgImg.implicitWidth
|
||||
property real wallpaperHeight: bgImg.implicitHeight
|
||||
|
||||
property real wallpaperToScreenRatio: {
|
||||
if (wallpaperWidth <= 0 || wallpaperHeight <= 0)
|
||||
return 1
|
||||
return Math.min(
|
||||
wallpaperWidth / width,
|
||||
wallpaperHeight / height
|
||||
)
|
||||
}
|
||||
|
||||
property real effectiveScale: parallaxEnabled ? parallaxZoom : 1
|
||||
|
||||
property real movableXSpace: Math.max(
|
||||
0,
|
||||
((wallpaperWidth / wallpaperToScreenRatio * effectiveScale) - width) / 2
|
||||
)
|
||||
|
||||
// workspace mapping
|
||||
property int lowerWorkspace: Math.floor((activeWorkspaceId - 1) / workspaceRange) * workspaceRange + 1
|
||||
property int upperWorkspace: lowerWorkspace + workspaceRange
|
||||
property int workspaceSpan: Math.max(1, upperWorkspace - lowerWorkspace)
|
||||
|
||||
property real valueX: {
|
||||
if (!parallaxEnabled)
|
||||
return 0.5
|
||||
return (activeWorkspaceId - lowerWorkspace) / workspaceSpan
|
||||
}
|
||||
|
||||
// sidebar globals
|
||||
property bool sidebarLeftOpen: Globals.visiblility.sidebarLeft
|
||||
&& Config.runtime.appearance.background.parallax.enableSidebarLeft
|
||||
|
||||
property bool sidebarRightOpen: Globals.visiblility.sidebarRight
|
||||
&& Config.runtime.appearance.background.parallax.enableSidebarRight
|
||||
|
||||
property real sidebarOffset: {
|
||||
if (sidebarLeftOpen && !sidebarRightOpen)
|
||||
if (Config.runtime.bar.position === "right")
|
||||
return 0.15
|
||||
else return -0.15
|
||||
if (sidebarRightOpen && !sidebarLeftOpen)
|
||||
if (Config.runtime.bar.position === "left")
|
||||
return -0.15
|
||||
else return 0.15
|
||||
return 0
|
||||
}
|
||||
|
||||
property real effectiveValueX: Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
1,
|
||||
valueX + sidebarOffset
|
||||
)
|
||||
)
|
||||
|
||||
// window
|
||||
color: (bgImg.status === Image.Error) ? Appearance.colors.colLayer2 : "transparent"
|
||||
WlrLayershell.namespace: "nucleus:background"
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: WlrLayer.Background
|
||||
screen: modelData
|
||||
visible: Config.initialized && Config.runtime.appearance.background.enabled
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
// wallpaper picker
|
||||
Process {
|
||||
id: wallpaperProc
|
||||
|
||||
command: ["bash", "-c", Directories.scriptsPath + "/interface/changebg.sh"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const out = text.trim()
|
||||
|
||||
if (out !== "null" && out.length > 0) {
|
||||
const parts = out.split("|")
|
||||
|
||||
if (parts.length === 2) {
|
||||
const monitor = parts[0]
|
||||
const wallpaper = parts[1]
|
||||
|
||||
Config.updateKey(
|
||||
"monitors." + monitor + ".wallpaper",
|
||||
wallpaper
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Quickshell.execDetached([
|
||||
"nucleus", "ipc", "call", "clock", "changePosition"
|
||||
])
|
||||
if (Config.runtime.appearance.colors.autogenerated) {
|
||||
Quickshell.execDetached([
|
||||
"nucleus", "ipc", "call", "global", "regenColors"
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wallpaper
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
StyledImage {
|
||||
id: bgImg
|
||||
|
||||
visible: status === Image.Ready
|
||||
smooth: false
|
||||
cache: false
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: wallpaperPath + "?t=" + Date.now()
|
||||
|
||||
width: wallpaperWidth / wallpaperToScreenRatio * effectiveScale
|
||||
height: wallpaperHeight / wallpaperToScreenRatio * effectiveScale
|
||||
|
||||
x: -movableXSpace - (effectiveValueX - 0.5) * 2 * movableXSpace
|
||||
y: 0
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Metrics.chronoDuration(600)
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === Image.Ready) {
|
||||
backgroundContainer.wallpaperWidth = implicitWidth
|
||||
backgroundContainer.wallpaperHeight = implicitHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: widgetCanvas
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// error ui
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
visible: bgImg.status === Image.Error
|
||||
|
||||
Rectangle {
|
||||
width: 550
|
||||
height: 400
|
||||
radius: Appearance.rounding.windowRounding
|
||||
color: "transparent"
|
||||
anchors.centerIn: parent
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
anchors.margins: Metrics.margin("normal")
|
||||
spacing: Metrics.margin("small")
|
||||
|
||||
MaterialSymbol {
|
||||
text: "wallpaper"
|
||||
font.pixelSize: Metrics.fontSize("wildass")
|
||||
color: Appearance.colors.colOnLayer2
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Wallpaper Missing"
|
||||
font.pixelSize: Metrics.fontSize("hugeass")
|
||||
font.bold: true
|
||||
color: Appearance.colors.colOnLayer2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Seems like you haven't set a wallpaper yet."
|
||||
font.pixelSize: Metrics.fontSize("small")
|
||||
color: Appearance.colors.colSubtext
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
StyledButton {
|
||||
text: "Set wallpaper"
|
||||
icon: "wallpaper"
|
||||
secondary: true
|
||||
radius: Metrics.radius("large")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: wallpaperProc.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "background"
|
||||
|
||||
function change() {
|
||||
wallpaperProc.running = true
|
||||
}
|
||||
|
||||
function next() {
|
||||
WallpaperSlideshow.nextWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Clock {
|
||||
id: clock
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
import "../../components/morphedPolygons/geometry/offset.js" as Offset
|
||||
import "../../components/morphedPolygons/material-shapes.js" as MaterialShapes // For polygons
|
||||
import "../../components/morphedPolygons/shapes/corner-rounding.js" as CornerRounding
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import qs.config
|
||||
import qs.modules.components
|
||||
import qs.modules.components.morphedPolygons
|
||||
import qs.services
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
property bool imageFailed: false
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: clock
|
||||
|
||||
required property var modelData
|
||||
property int padding: Config.runtime.appearance.background.clock.edgeSpacing
|
||||
property int clockHeight: Config.runtime.appearance.background.clock.isAnalog ? 250 : 160
|
||||
property int clockWidth: Config.runtime.appearance.background.clock.isAnalog ? 250 : 360
|
||||
|
||||
function setRandomPosition() {
|
||||
const x = Math.floor(Math.random() * (width - clockWidth));
|
||||
const y = Math.floor(Math.random() * (height - clockHeight));
|
||||
animX.to = x;
|
||||
animY.to = y;
|
||||
moveAnim.start();
|
||||
Config.updateKey("appearance.background.clock.xPos", x);
|
||||
Config.updateKey("appearance.background.clock.yPos", y);
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
visible: (Config.runtime.appearance.background.clock.enabled && Config.initialized && !imageFailed)
|
||||
exclusiveZone: 0
|
||||
WlrLayershell.layer: WlrLayer.Bottom
|
||||
screen: modelData
|
||||
|
||||
ParallelAnimation {
|
||||
id: moveAnim
|
||||
|
||||
NumberAnimation {
|
||||
id: animX
|
||||
|
||||
target: rootContentContainer
|
||||
property: "x"
|
||||
duration: Metrics.chronoDuration(400)
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: animY
|
||||
|
||||
target: rootContentContainer
|
||||
property: "y"
|
||||
duration: Metrics.chronoDuration(400)
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
margins {
|
||||
top: padding
|
||||
bottom: padding
|
||||
left: padding
|
||||
right: padding
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rootContentContainer
|
||||
|
||||
property real releasedX: 0
|
||||
property real releasedY: 0
|
||||
|
||||
height: clockHeight
|
||||
width: clockWidth
|
||||
Component.onCompleted: {
|
||||
Qt.callLater(() => {
|
||||
x = Config.runtime.appearance.background.clock.xPos;
|
||||
y = Config.runtime.appearance.background.clock.yPos;
|
||||
});
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: ma
|
||||
|
||||
anchors.fill: parent
|
||||
drag.target: rootContentContainer
|
||||
drag.axis: Drag.XAndYAxis
|
||||
acceptedButtons: Qt.RightButton
|
||||
onReleased: {
|
||||
if (ma.button === Qt.RightButton)
|
||||
return
|
||||
Config.updateKey("appearance.background.clock.xPos", rootContentContainer.x);
|
||||
Config.updateKey("appearance.background.clock.yPos", rootContentContainer.y);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: digitalClockContainer
|
||||
|
||||
visible: !Config.runtime.appearance.background.clock.isAnalog
|
||||
|
||||
Column {
|
||||
spacing: Metrics.spacing(-40)
|
||||
|
||||
StyledText {
|
||||
animate: false
|
||||
text: Time.format("hh:mm")
|
||||
font.pixelSize: Metrics.fontSize(Appearance.font.size.wildass * 3)
|
||||
font.family: Metrics.fontFamily("main")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Metrics.margin(8)
|
||||
animate: false
|
||||
text: Time.format("dddd, dd/MM")
|
||||
font.pixelSize: Metrics.fontSize(32)
|
||||
font.family: Metrics.fontFamily("main")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
id: analogClockContainer
|
||||
|
||||
property int hours: parseInt(Time.format("hh"))
|
||||
property int minutes: parseInt(Time.format("mm"))
|
||||
property int seconds: parseInt(Time.format("ss"))
|
||||
readonly property real cx: width / 2
|
||||
readonly property real cy: height / 2
|
||||
property var shapes: [MaterialShapes.getCookie7Sided, MaterialShapes.getCookie9Sided, MaterialShapes.getCookie12Sided, MaterialShapes.getPixelCircle, MaterialShapes.getCircle, MaterialShapes.getGhostish]
|
||||
|
||||
anchors.fill: parent
|
||||
visible: Config.runtime.appearance.background.clock.isAnalog
|
||||
width: clock.width / 1.1
|
||||
height: clock.height / 1.1
|
||||
|
||||
// Polygon
|
||||
MorphedPolygon {
|
||||
id: shapeCanvas
|
||||
|
||||
anchors.fill: parent
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
roundedPolygon: analogClockContainer.shapes[Config.runtime.appearance.background.clock.shape]()
|
||||
|
||||
transform: Rotation {
|
||||
origin.x: shapeCanvas.width / 2
|
||||
origin.y: shapeCanvas.height / 2
|
||||
angle: shapeCanvas.rotation
|
||||
}
|
||||
|
||||
NumberAnimation on rotation {
|
||||
from: 0
|
||||
to: 360
|
||||
running: Config.runtime.appearance.animations.enabled && Config.runtime.appearance.background.clock.rotatePolygonBg
|
||||
duration: Config.runtime.appearance.background.clock.rotationDuration * 1000
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
}
|
||||
|
||||
ClockDial {
|
||||
id: dial
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.width * 0.12
|
||||
color: Appearance.colors.colOnSecondaryContainer
|
||||
z: 0
|
||||
}
|
||||
|
||||
// Hour hand
|
||||
StyledRect {
|
||||
z: 2
|
||||
width: 10
|
||||
height: parent.height * 0.3
|
||||
radius: Metrics.radius("full")
|
||||
color: Qt.darker(Appearance.m3colors.m3secondary, 0.8)
|
||||
x: analogClockContainer.cx - width / 2
|
||||
y: analogClockContainer.cy - height
|
||||
transformOrigin: Item.Bottom
|
||||
rotation: (analogClockContainer.hours % 12 + analogClockContainer.minutes / 60) * 30
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
anchors.centerIn: parent
|
||||
width: 16
|
||||
height: 16
|
||||
radius: width / 2
|
||||
color: Appearance.m3colors.m3secondary
|
||||
z: 99 // Ensures its on top of everthing
|
||||
|
||||
// Inner dot
|
||||
StyledRect {
|
||||
width: parent.width / 2
|
||||
height: parent.height / 2
|
||||
radius: width / 2
|
||||
anchors.centerIn: parent
|
||||
z: 100
|
||||
color: Appearance.m3colors.m3primaryContainer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Minute hand
|
||||
StyledRect {
|
||||
width: 18
|
||||
height: parent.height * 0.35
|
||||
radius: Metrics.radius("full")
|
||||
color: Appearance.m3colors.m3secondary
|
||||
x: analogClockContainer.cx - width / 2
|
||||
y: analogClockContainer.cy - height
|
||||
transformOrigin: Item.Bottom
|
||||
rotation: analogClockContainer.minutes * 6
|
||||
z: 10 // On top of all hands
|
||||
}
|
||||
|
||||
// Second hand
|
||||
StyledRect {
|
||||
visible: true
|
||||
width: 4
|
||||
height: parent.height * 0.28
|
||||
radius: Metrics.radius("full")
|
||||
color: Appearance.m3colors.m3error
|
||||
x: analogClockContainer.cx - width / 2
|
||||
y: analogClockContainer.cy - height
|
||||
transformOrigin: Item.Bottom
|
||||
rotation: analogClockContainer.seconds * 6
|
||||
z: 2
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: Time.format("hh")
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: Metrics.margin(30)
|
||||
font.pixelSize: Metrics.fontSize(80)
|
||||
font.bold: true
|
||||
opacity: 0.3
|
||||
animate: false
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: Time.format("mm")
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: Metrics.margin(110)
|
||||
font.pixelSize: Metrics.fontSize(80)
|
||||
font.bold: true
|
||||
opacity: 0.3
|
||||
animate: false
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function changePosition() {
|
||||
clock.setRandomPosition();
|
||||
}
|
||||
|
||||
target: "clock"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import QtQuick
|
||||
import qs.modules.components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property color color: "white"
|
||||
readonly property real cx: width / 2
|
||||
readonly property real cy: height / 2
|
||||
readonly property real radius: Math.min(width, height) / 2
|
||||
opacity: 0.4
|
||||
|
||||
// Hour marks (12 ticks)
|
||||
Repeater {
|
||||
model: 12
|
||||
|
||||
Item {
|
||||
width: root.width
|
||||
height: root.height
|
||||
anchors.centerIn: parent
|
||||
rotation: index * 30
|
||||
transformOrigin: Item.Center
|
||||
|
||||
Rectangle {
|
||||
width: 3 // thickness of tick
|
||||
height: 15 // length of tick
|
||||
color: root.color
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: -root.radius * 0.15 / 2
|
||||
radius: width / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Minute marks (60 ticks)
|
||||
Repeater {
|
||||
model: 60
|
||||
|
||||
Item {
|
||||
width: root.width
|
||||
height: root.height
|
||||
anchors.centerIn: parent
|
||||
rotation: index * 6
|
||||
transformOrigin: Item.Center
|
||||
|
||||
Rectangle {
|
||||
width: index % 5 === 0 ? 3 : 2 // thicker for 5-minute marks
|
||||
height: index % 5 === 0 ? 15 : 8 // longer for 5-minute marks
|
||||
color: root.color
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: -root.radius * 0.15 / 2
|
||||
radius: width / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user