changes to config plugin, reduce file amount + code, fix greeter

This commit is contained in:
2026-08-13 15:41:16 +02:00
parent 9d2f78eb5e
commit b2e092d0be
145 changed files with 2531 additions and 6949 deletions
+8 -9
View File
@@ -2,10 +2,9 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Paths
import ZShell.Config
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
ColumnLayout {
@@ -29,7 +28,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary
font.bold: true
font.family: Appearance.font.family.clock
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.hourStr
}
@@ -38,7 +37,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3primary
font.bold: true
font.family: Appearance.font.family.clock
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: ":"
}
@@ -47,7 +46,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary
font.bold: true
font.family: Appearance.font.family.clock
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.minuteStr
}
@@ -58,7 +57,7 @@ ColumnLayout {
Layout.topMargin: -Tokens.padding.large * 2
color: Colors.palette.m3tertiary
font.bold: true
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale)
text: Time.format("dddd, d MMMM yyyy")
}
@@ -89,7 +88,7 @@ ColumnLayout {
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
font.weight: 600
text: root.greeter.username
@@ -238,7 +237,7 @@ ColumnLayout {
anchors.right: parent.right
animateProp: "opacity"
color: Colors.palette.m3onSurfaceVariant
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
horizontalAlignment: Qt.AlignHCenter
lineHeight: 1.2
opacity: shouldBeVisible && !message.msg ? 1 : 0
@@ -293,7 +292,7 @@ ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
color: isError ? Colors.palette.m3error : Colors.palette.m3primary
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
horizontalAlignment: Qt.AlignHCenter
opacity: 0
@@ -1,24 +0,0 @@
import QtQuick
QtObject {
id: root
property real idx1: index
property int idx1Duration: 100
property real idx2: index
property int idx2Duration: 300
required property int index
Behavior on idx1 {
NumberAnimation {
duration: root.idx1Duration
easing.type: Easing.OutSine
}
}
Behavior on idx2 {
NumberAnimation {
duration: root.idx2Duration
easing.type: Easing.OutSine
}
}
}
-166
View File
@@ -1,166 +0,0 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
Slider {
id: root
property color color: Colors.palette.m3secondary
required property string icon
property bool initialized: false
readonly property bool isHorizontal: orientation === Qt.Horizontal
readonly property bool isVertical: orientation === Qt.Vertical
property real multiplier: 100
property real oldValue
// Wrapper components can inject their own track visuals here.
property Component trackContent
// Keep current behavior for existing usages.
orientation: Qt.Vertical
background: CustomRect {
id: groove
color: Colors.layer(Colors.palette.m3surfaceContainer, 2)
height: root.availableHeight
radius: Tokens.rounding.full
width: root.availableWidth
x: root.leftPadding
y: root.topPadding
Loader {
id: trackLoader
anchors.fill: parent
sourceComponent: root.trackContent
onLoaded: {
if (!item)
return;
item.rootSlider = root;
item.groove = groove;
item.handleItem = handle;
}
}
}
handle: Item {
id: handle
property alias moving: icon.moving
implicitHeight: Math.min(root.width, root.height)
implicitWidth: Math.min(root.width, root.height)
x: root.isHorizontal ? root.leftPadding + root.visualPosition * (root.availableWidth - width) : root.leftPadding + (root.availableWidth - width) / 2
y: root.isVertical ? root.topPadding + root.visualPosition * (root.availableHeight - height) : root.topPadding + (root.availableHeight - height) / 2
Elevation {
anchors.fill: parent
level: handleInteraction.containsMouse ? 2 : 1
radius: rect.radius
}
CustomRect {
id: rect
anchors.fill: parent
color: Colors.palette.m3inverseSurface
radius: Tokens.rounding.full
MouseArea {
id: handleInteraction
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
MaterialIcon {
id: icon
property bool moving
function update(): void {
animate = !moving;
binding.when = moving;
font.pointSize = moving ? Tokens.font.size.small : Tokens.font.size.larger;
font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material;
}
anchors.centerIn: parent
color: Colors.palette.m3inverseOnSurface
text: root.icon
onMovingChanged: anim.restart()
Binding {
id: binding
property: "text"
target: icon
value: Math.round(root.value * root.multiplier)
when: false
}
SequentialAnimation {
id: anim
Anim {
duration: Tokens.anim.durations.normal / 2
easing.bezierCurve: Tokens.anim.curves.standardAccel
property: "scale"
target: icon
to: 0
}
ScriptAction {
script: icon.update()
}
Anim {
duration: Tokens.anim.durations.normal / 2
easing.bezierCurve: Tokens.anim.curves.standardDecel
property: "scale"
target: icon
to: 1
}
}
}
}
}
Behavior on value {
Anim {
duration: Tokens.anim.durations.large
}
}
onPressedChanged: handle.moving = pressed
onValueChanged: {
if (!initialized) {
initialized = true;
oldValue = value;
return;
}
if (Math.abs(value - oldValue) < 0.01)
return;
oldValue = value;
handle.moving = true;
stateChangeDelay.restart();
}
Timer {
id: stateChangeDelay
interval: 500
onTriggered: {
if (!root.pressed)
handle.moving = false;
}
}
}
+4 -5
View File
@@ -18,8 +18,8 @@ BusyIndicator {
}
property int animState
property color bgColour: Colors.palette.m3secondaryContainer
property color fgColour: Colors.palette.m3primary
property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.size.normal * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
@@ -32,8 +32,8 @@ BusyIndicator {
contentItem: CircularProgress {
anchors.fill: parent
bgColour: root.bgColour
fgColour: root.fgColour
bgColor: root.bgColor
fgColor: root.fgColor
padding: root.padding
rotation: manager.rotation
startAngle: manager.startFraction * 360
@@ -74,7 +74,6 @@ BusyIndicator {
CircularIndicatorManager {
id: manager
}
NumberAnimation {
+89 -39
View File
@@ -1,67 +1,117 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Shapes
import ZShell.Components
import ZShell.Config
import qs.Services
Shape {
Item {
id: root
readonly property real arcRadius: (size - padding - strokeWidth) / 2
property color bgColour: Colors.palette.m3secondaryContainer
property color fgColour: Colors.palette.m3primary
readonly property real arcRadius: (size - padding - strokeWidth * (1 + waveAmplitude * 2)) / 2
property color bgColor: Colors.palette.m3secondaryContainer
property real clampedVal: Math.max(1 / 360, Math.min(1, isNaN(value) ? 0 : value))
readonly property real dotAngleRad: (startAngle + sweepAngle - gapAngle * (sweepAngle < 360 ? 0 : 1)) * Math.PI / 180
property color fgColor: Colors.palette.m3primary
readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI)
property alias hasEndIndicator: dot.active
property real implicitSize
property int padding: 0
readonly property real size: Math.min(width, height)
property int spacing: Tokens.spacing.small
property int startAngle: -90
property int strokeWidth: Tokens.padding.smaller
readonly property real vValue: value || 1 / 360
property int strokeWidth: Tokens.padding.small
property int sweepAngle: 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value
property alias waveAmplitude: wave.amplitudeMultiplier
property alias waveDuration: waveProgAnim.duration
property alias waveFrequency: wave.frequency
property bool wavePaused
property bool wavy: false
asynchronous: true
preferredRendererType: Shape.CurveRenderer
implicitHeight: implicitSize
implicitWidth: implicitSize
ShapePath {
capStyle: Tokens.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.bgColour
strokeWidth: root.strokeWidth
Shape {
asynchronous: true
opacity: Math.min(1, remainingArc.sweepAngle)
preferredRendererType: Shape.CurveRenderer
Behavior on strokeColor {
CAnim {
duration: Tokens.anim.durations.large
ShapePath {
capStyle: ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.bgColor
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
Behavior on strokeColor {
CAnim {
}
}
}
PathAngleArc {
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle + 360 * root.vValue + root.gapAngle
sweepAngle: Math.max(-root.gapAngle, 360 * (1 - root.vValue) - root.gapAngle * 2)
PathAngleArc {
id: remainingArc
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle + root.clampedVal * root.sweepAngle + root.gapAngle
sweepAngle: Math.max(1 / 360, root.sweepAngle * (1 - root.clampedVal) - root.gapAngle * (root.sweepAngle < 360 ? 1 : 2))
}
}
}
ShapePath {
capStyle: Tokens.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.fgColour
strokeWidth: root.strokeWidth
WavyLine {
id: wave
Behavior on strokeColor {
CAnim {
duration: Tokens.anim.durations.large
amplitudeMultiplier: root.wavy ? 0.5 : 0
anchors.fill: parent
anchors.margins: -lineWidth * amplitudeMultiplier
color: root.fgColor
frequency: 8
fullAngle: root.sweepAngle
lineWidth: root.strokeWidth
pathType: WavyLine.Arc
radius: root.arcRadius
startAngle: root.startAngle
value: root.clampedVal
Behavior on amplitudeMultiplier {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
id: waveProgAnim
PathAngleArc {
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle
sweepAngle: 360 * root.vValue
duration: 2000
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: root.wavePaused || wave.amplitudeMultiplier === 0
running: true
to: 1
}
}
Loader {
id: dot
x: root.size / 2 + root.arcRadius * Math.cos(root.dotAngleRad) - width / 2
y: root.size / 2 + root.arcRadius * Math.sin(root.dotAngleRad) - height / 2
sourceComponent: CustomRect {
color: root.fgColor
implicitHeight: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
implicitWidth: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
opacity: Math.min(1, remainingArc.sweepAngle)
radius: Tokens.rounding.full
}
}
}
-136
View File
@@ -1,136 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
ColumnLayout {
id: root
default property alias content: contentColumn.data
property string description: ""
property bool expanded: false
property bool nested: false
property bool showBackground: false
required property string title
signal toggleRequested
Layout.fillWidth: true
spacing: Tokens.spacing.small
Item {
id: sectionHeaderItem
Layout.fillWidth: true
Layout.preferredHeight: Math.max(titleRow.implicitHeight + Tokens.padding.normal * 2, 48)
RowLayout {
id: titleRow
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.normal
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.normal
anchors.verticalCenter: parent.verticalCenter
spacing: Tokens.spacing.normal
CustomText {
font.pointSize: Tokens.font.size.larger
font.weight: 500
text: root.title
}
Item {
Layout.fillWidth: true
}
MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.normal
rotation: root.expanded ? 180 : 0
text: "expand_more"
Behavior on rotation {
Anim {
duration: Tokens.anim.durations.small
easing.bezierCurve: Tokens.anim.curves.standard
}
}
}
}
StateLayer {
anchors.fill: parent
color: Colors.palette.m3onSurface
radius: Tokens.rounding.normal
showHoverBackground: false
onClicked: {
root.toggleRequested();
root.expanded = !root.expanded;
}
}
}
Item {
id: contentWrapper
Layout.fillWidth: true
Layout.preferredHeight: root.expanded ? (contentColumn.implicitHeight + Tokens.spacing.small * 2) : 0
clip: true
Behavior on Layout.preferredHeight {
Anim {
easing.bezierCurve: Tokens.anim.curves.standard
}
}
CustomRect {
id: backgroundRect
anchors.fill: parent
color: Colors.transparency.enabled ? Colors.layer(Colors.palette.m3surfaceContainer, root.nested ? 3 : 2) : (root.nested ? Colors.palette.m3surfaceContainerHigh : Colors.palette.m3surfaceContainer)
opacity: root.showBackground && root.expanded ? 1.0 : 0.0
radius: Tokens.rounding.normal
visible: root.showBackground
Behavior on opacity {
Anim {
easing.bezierCurve: Tokens.anim.curves.standard
}
}
}
ColumnLayout {
id: contentColumn
anchors.bottomMargin: Tokens.spacing.small
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.normal
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.normal
opacity: root.expanded ? 1.0 : 0.0
spacing: Tokens.spacing.small
y: Tokens.spacing.small
Behavior on opacity {
Anim {
easing.bezierCurve: Tokens.anim.curves.standard
}
}
CustomText {
id: descriptionText
Layout.bottomMargin: root.description !== "" ? Tokens.spacing.small : 0
Layout.fillWidth: true
Layout.topMargin: root.description !== "" ? Tokens.spacing.smaller : 0
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.small
text: root.description
visible: root.description !== ""
wrapMode: Text.Wrap
}
}
}
}
-209
View File
@@ -1,209 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import ZShell.Config
import qs.Services
Item {
id: root
readonly property real arcStartAngle: 0.75 * Math.PI
readonly property real arcSweep: 1.5 * Math.PI
property real currentHue: 0
property bool dragActive: false
required property var drawing
readonly property real handleAngle: hueToAngle(currentHue)
readonly property real handleCenterX: width / 2 + radius * Math.cos(handleAngle)
readonly property real handleCenterY: height / 2 + radius * Math.sin(handleAngle)
property real handleSize: 32
property real lastChromaticHue: 0
readonly property real radius: (Math.min(width, height) - handleSize) / 2
readonly property int segmentCount: 240
readonly property color thumbColor: Colors.palette.m3inverseSurface
readonly property color thumbContentColor: Colors.palette.m3inverseOnSurface
readonly property color trackColor: Colors.layer(Colors.palette.m3surfaceContainer, 2)
function hueToAngle(hue) {
return arcStartAngle + arcSweep * hue;
}
function normalizeAngle(angle) {
const tau = Math.PI * 2;
let a = angle % tau;
if (a < 0)
a += tau;
return a;
}
function pointIsOnTrack(x, y) {
const cx = width / 2;
const cy = height / 2;
const dx = x - cx;
const dy = y - cy;
const distance = Math.sqrt(dx * dx + dy * dy);
return distance >= radius - handleSize / 2 && distance <= radius + handleSize / 2;
}
function syncFromPenColor() {
if (!drawing)
return;
const c = drawing.penColor;
if (c.hsvSaturation > 0) {
currentHue = c.hsvHue;
lastChromaticHue = c.hsvHue;
} else {
currentHue = lastChromaticHue;
}
canvas.requestPaint();
}
function updateHueFromPoint(x, y, force = false) {
const cx = width / 2;
const cy = height / 2;
const dx = x - cx;
const dy = y - cy;
const distance = Math.sqrt(dx * dx + dy * dy);
if (!force && (distance < radius - handleSize / 2 || distance > radius + handleSize / 2))
return;
const angle = normalizeAngle(Math.atan2(dy, dx));
const start = normalizeAngle(arcStartAngle);
let relative = angle - start;
if (relative < 0)
relative += Math.PI * 2;
if (relative > arcSweep) {
const gap = Math.PI * 2 - arcSweep;
relative = relative < arcSweep + gap / 2 ? arcSweep : 0;
}
currentHue = relative / arcSweep;
lastChromaticHue = currentHue;
drawing.penColor = Qt.hsva(currentHue, drawing.penColor.hsvSaturation, drawing.penColor.hsvValue, drawing.penColor.a);
}
implicitHeight: 180
implicitWidth: 220
Component.onCompleted: syncFromPenColor()
onCurrentHueChanged: canvas.requestPaint()
onDrawingChanged: syncFromPenColor()
onHandleSizeChanged: canvas.requestPaint()
onHeightChanged: canvas.requestPaint()
onWidthChanged: canvas.requestPaint()
Connections {
function onPenColorChanged() {
root.syncFromPenColor();
}
target: root.drawing
}
Canvas {
id: canvas
anchors.fill: parent
renderStrategy: Canvas.Threaded
renderTarget: Canvas.Image
Component.onCompleted: requestPaint()
onPaint: {
const ctx = getContext("2d");
ctx.reset();
ctx.clearRect(0, 0, width, height);
const cx = width / 2;
const cy = height / 2;
const radius = root.radius;
const trackWidth = root.handleSize;
// Background track: always show the full hue spectrum
for (let i = 0; i < root.segmentCount; ++i) {
const t1 = i / root.segmentCount;
const t2 = (i + 1) / root.segmentCount;
const a1 = root.arcStartAngle + root.arcSweep * t1;
const a2 = root.arcStartAngle + root.arcSweep * t2;
ctx.beginPath();
ctx.arc(cx, cy, radius, a1, a2);
ctx.lineWidth = trackWidth;
ctx.lineCap = "round";
ctx.strokeStyle = Qt.hsla(t1, 1.0, 0.5, 1.0);
ctx.stroke();
}
}
}
Item {
id: handle
height: root.handleSize
width: root.handleSize
x: root.handleCenterX - width / 2
y: root.handleCenterY - height / 2
z: 1
Elevation {
anchors.fill: parent
level: handleHover.containsMouse ? 2 : 1
radius: rect.radius
}
Rectangle {
id: rect
anchors.fill: parent
color: root.thumbColor
radius: width / 2
MouseArea {
id: handleHover
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
Rectangle {
anchors.centerIn: parent
color: root.drawing ? root.drawing.penColor : Qt.hsla(root.currentHue, 1.0, 0.5, 1.0)
height: width
radius: width / 2
width: parent.width - 12
}
}
}
MouseArea {
id: dragArea
acceptedButtons: Qt.LeftButton
anchors.fill: parent
hoverEnabled: true
onCanceled: {
root.dragActive = false;
}
onPositionChanged: mouse => {
if ((mouse.buttons & Qt.LeftButton) && root.dragActive)
root.updateHueFromPoint(mouse.x, mouse.y, true);
}
onPressed: mouse => {
root.dragActive = root.pointIsOnTrack(mouse.x, mouse.y);
if (root.dragActive)
root.updateHueFromPoint(mouse.x, mouse.y);
}
onReleased: {
root.dragActive = false;
}
}
}
-34
View File
@@ -1,34 +0,0 @@
pragma ComponentBehavior: Bound
import ZShell
import Quickshell.Widgets
import QtQuick
IconImage {
id: root
required property color color
asynchronous: true
layer.enabled: true
layer.effect: Coloriser {
colorizationColor: root.color
sourceColor: analyser.dominantColour
}
layer.onEnabledChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
onStatusChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
ImageAnalyser {
id: analyser
sourceItem: root
}
}
-14
View File
@@ -1,14 +0,0 @@
import QtQuick
import QtQuick.Effects
MultiEffect {
property color sourceColor: "black"
brightness: 1 - sourceColor.hslLightness
colorization: 1
Behavior on colorizationColor {
CAnim {
}
}
}
-71
View File
@@ -1,71 +0,0 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
Slider {
id: root
property color nonPeakColor: Colors.tPalette.m3primary
required property real peak
property color peakColor: Colors.palette.m3primary
background: Item {
CustomRect {
anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 3
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 3
bottomRightRadius: root.implicitHeight / 15
color: root.nonPeakColor
implicitWidth: root.handle.x - root.implicitHeight
radius: Tokens.rounding.full
topRightRadius: root.implicitHeight / 15
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
bottomRightRadius: root.implicitHeight / 15
color: root.peakColor
implicitWidth: parent.width * root.peak
radius: Tokens.rounding.full
topRightRadius: root.implicitHeight / 15
Behavior on implicitWidth {
Anim {
duration: 50
}
}
}
}
CustomRect {
anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 3
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 3
bottomLeftRadius: root.implicitHeight / 15
color: Colors.tPalette.m3surfaceContainer
implicitWidth: root.implicitWidth - root.handle.x - root.handle.implicitWidth - root.implicitHeight
radius: Tokens.rounding.full
topLeftRadius: root.implicitHeight / 15
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3primary
implicitHeight: 15
implicitWidth: 5
radius: Tokens.rounding.full
x: root.visualPosition * root.availableWidth - implicitWidth / 2
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}
}
@@ -1,69 +0,0 @@
import QtQuick
import QtQuick.Controls.Basic
BusyIndicator {
id: control
property int busySize: 64
property color color: delegate.color
contentItem: Item {
implicitHeight: control.busySize
implicitWidth: control.busySize
Item {
id: item
height: control.busySize
opacity: control.running ? 1 : 0
width: control.busySize
x: parent.width / 2 - (control.busySize / 2)
y: parent.height / 2 - (control.busySize / 2)
Behavior on opacity {
OpacityAnimator {
duration: 250
}
}
RotationAnimator {
duration: 1250
from: 0
loops: Animation.Infinite
running: control.visible && control.running
target: item
to: 360
}
Repeater {
id: repeater
model: 6
CustomRect {
id: delegate
required property int index
color: control.color
implicitHeight: 10
implicitWidth: 10
radius: 5
x: item.width / 2 - width / 2
y: item.height / 2 - height / 2
transform: [
Translate {
y: -Math.min(item.width, item.height) * 0.5 + 5
},
Rotation {
angle: delegate.index / repeater.count * 360
origin.x: 5
origin.y: 5
}
]
}
}
}
}
}
-33
View File
@@ -1,33 +0,0 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
import qs.Services
Button {
id: control
property color bgColor: Colors.palette.m3primary
property int radius: Tokens.rounding.smallest / 2
property color textColor: Colors.palette.m3onPrimary
background: CustomRect {
color: control.bgColor
opacity: control.enabled ? 1.0 : 0.5
radius: control.radius
}
contentItem: CustomText {
color: control.textColor
horizontalAlignment: Text.AlignHCenter
opacity: control.enabled ? 1.0 : 0.5
text: control.text
verticalAlignment: Text.AlignVCenter
}
StateLayer {
radius: control.radius
onClicked: {
control.clicked();
}
}
}
-38
View File
@@ -1,38 +0,0 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
import qs.Services
CheckBox {
id: control
property int checkHeight: 20
property int checkWidth: 20
contentItem: CustomText {
anchors.left: parent.left
anchors.leftMargin: control.checkWidth + control.leftPadding + 8
anchors.verticalCenter: parent.verticalCenter
font.pointSize: control.font.pointSize
text: control.text
}
indicator: CustomRect {
// x: control.leftPadding
// y: parent.implicitHeight / 2 - implicitHeight / 2
border.color: control.checked ? Colors.palette.m3primary : "transparent"
color: Colors.palette.m3surfaceVariant
implicitHeight: control.checkHeight
implicitWidth: control.checkWidth
radius: Tokens.rounding.smallest / 2
CustomRect {
color: Colors.palette.m3primary
implicitHeight: control.checkHeight - (y * 2)
implicitWidth: control.checkWidth - (x * 2)
radius: 3
visible: control.checked
x: 4
y: 4
}
}
}
-170
View File
@@ -1,170 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import ZShell.Config
import qs.Services
ComboBox {
id: root
property int cornerRadius: Tokens.rounding.normal
property int fieldHeight: 42
property bool filled: true
property real focusRingOpacity: 0.70
property int hPadding: 16
property int menuCornerRadius: 16
property int menuRowHeight: 46
property int menuVisibleRows: 7
property bool preferPopupWindow: false
hoverEnabled: true
implicitHeight: fieldHeight
implicitWidth: 240
spacing: 8
// ---------- Field background (filled/outlined + state layers + focus ring) ----------
background: Item {
anchors.fill: parent
CustomRect {
id: container
anchors.fill: parent
color: Colors.palette.m3surfaceVariant
radius: root.cornerRadius
StateLayer {
}
}
}
// ---------- Content ----------
contentItem: RowLayout {
anchors.fill: parent
anchors.leftMargin: root.hPadding
anchors.rightMargin: root.hPadding
spacing: 12
// Display text
CustomText {
Layout.fillWidth: true
color: root.enabled ? Colors.palette.m3onSurface : Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pixelSize: 16
font.weight: Font.Medium
text: root.currentText
verticalAlignment: Text.AlignVCenter
}
// Indicator chevron (simple, replace with your icon system)
CustomText {
color: root.enabled ? Colors.palette.m3onSurfaceVariant : Colors.palette.m3onSurfaceVariant
rotation: root.popup.visible ? 180 : 0
text: "▾"
transformOrigin: Item.Center
verticalAlignment: Text.AlignVCenter
Behavior on rotation {
NumberAnimation {
duration: 140
easing.type: Easing.OutCubic
}
}
}
}
popup: Popup {
id: p
implicitHeight: list.contentItem.height + Tokens.padding.small * 2
implicitWidth: root.width
modal: true
popupType: root.preferPopupWindow ? Popup.Window : Popup.Item
y: -list.currentIndex * (root.menuRowHeight + Tokens.spacing.small) - Tokens.padding.small
background: CustomRect {
color: Colors.palette.m3surface
radius: root.menuCornerRadius
}
contentItem: ListView {
id: list
anchors.bottomMargin: Tokens.padding.small
anchors.fill: parent
anchors.topMargin: Tokens.padding.small
clip: true
currentIndex: root.currentIndex
model: root.delegateModel
spacing: Tokens.spacing.small
delegate: CustomRect {
required property int index
required property var modelData
anchors.horizontalCenter: parent.horizontalCenter
color: (index === root.currentIndex) ? Colors.palette.m3primary : "transparent"
implicitHeight: root.menuRowHeight
implicitWidth: p.implicitWidth - Tokens.padding.small * 2
radius: Tokens.rounding.normal - Tokens.padding.small
RowLayout {
anchors.fill: parent
spacing: 10
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3onSurface
elide: Text.ElideRight
font.pixelSize: 15
text: modelData
verticalAlignment: Text.AlignVCenter
}
CustomText {
color: Colors.palette.m3onSurfaceVariant
text: "✓"
verticalAlignment: Text.AlignVCenter
visible: index === root.currentIndex
}
}
StateLayer {
onClicked: {
root.currentIndex = index;
p.close();
}
}
}
}
// Expressive-ish open/close motion: subtle scale+fade (tune to taste). :contentReference[oaicite:5]{index=5}
enter: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
Anim {
from: 0.98
property: "scale"
to: 1.0
}
}
exit: Transition {
Anim {
from: 1
property: "opacity"
to: 0
}
}
Elevation {
anchors.fill: parent
level: 2
radius: root.menuCornerRadius
z: -1
}
}
}
-10
View File
@@ -1,10 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell.Widgets
import QtQuick
IconImage {
id: root
asynchronous: true
}
-54
View File
@@ -1,54 +0,0 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
RadioButton {
id: root
font.pointSize: Tokens.font.size.normal
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
contentItem: CustomText {
anchors.left: outerCircle.right
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
font.pointSize: root.font.pointSize
text: root.text
}
indicator: Rectangle {
id: outerCircle
anchors.verticalCenter: parent.verticalCenter
border.color: root.checked ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
border.width: 2
color: "transparent"
implicitHeight: 16
implicitWidth: 16
radius: Tokens.rounding.full
Behavior on border.color {
CAnim {
}
}
StateLayer {
anchors.margins: -7
color: root.checked ? Colors.palette.m3onSurface : Colors.palette.m3primary
z: -1
onClicked: {
root.click();
}
}
CustomRect {
anchors.centerIn: parent
color: Qt.alpha(Colors.palette.m3primary, root.checked ? 1 : 0)
implicitHeight: 8
implicitWidth: 8
radius: Tokens.rounding.full
}
}
}
-190
View File
@@ -1,190 +0,0 @@
import ZShell.Config
import QtQuick
import QtQuick.Templates
import qs.Services
ScrollBar {
id: root
property bool _updatingFromFlickable: false
property bool _updatingFromUser: false
property bool animating
required property Flickable flickable
property real nonAnimPosition
property bool shouldBeActive
implicitWidth: 8
contentItem: CustomRect {
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3secondary
opacity: {
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (mouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
Behavior on opacity {
Anim {
}
}
MouseArea {
id: mouse
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
Behavior on position {
enabled: !fullMouse.pressed
Anim {
}
}
Component.onCompleted: {
if (flickable) {
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
}
}
}
onHoveredChanged: {
if (hovered)
shouldBeActive = true;
else
shouldBeActive = flickable.moving;
}
// Sync nonAnimPosition with Qt's automatic position binding
onPositionChanged: {
if (_updatingFromUser) {
_updatingFromUser = false;
return;
}
if (position === nonAnimPosition) {
animating = false;
return;
}
if (!animating && !_updatingFromFlickable && !fullMouse.pressed) {
nonAnimPosition = position;
}
}
// Sync nonAnimPosition with flickable when not animating
Connections {
function onContentYChanged() {
if (!animating && !fullMouse.pressed) {
_updatingFromFlickable = true;
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
} else {
nonAnimPosition = 0;
}
_updatingFromFlickable = false;
}
}
target: flickable
}
Connections {
function onMovingChanged(): void {
if (root.flickable.moving)
root.shouldBeActive = true;
else
hideDelay.restart();
}
target: root.flickable
}
Timer {
id: hideDelay
interval: 600
onTriggered: root.shouldBeActive = root.flickable.moving || root.hovered
}
CustomMouseArea {
id: fullMouse
function onWheel(event: WheelEvent): void {
root.animating = true;
root._updatingFromUser = true;
let newPos = root.nonAnimPosition;
if (event.angleDelta.y > 0)
newPos = Math.max(0, root.nonAnimPosition - 0.1);
else if (event.angleDelta.y < 0)
newPos = Math.min(1 - root.size, root.nonAnimPosition + 0.1);
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
anchors.fill: parent
preventStealing: true
onPositionChanged: event => {
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
onPressed: event => {
root.animating = true;
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
}
}
-5
View File
@@ -1,5 +0,0 @@
import Quickshell.Hyprland
GlobalShortcut {
appid: "zshell"
}
-175
View File
@@ -1,175 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import ZShell.Config
import qs.Services
Slider {
id: root
property bool animateWave
property color bgColor: enabled ? Colors.palette.m3secondaryContainer : Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? Colors.palette.m3primary : Qt.alpha(Colors.palette.m3onSurface, 0.38)
property real filledWidth
property real pos: visualPosition
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
signal interaction(v: real)
implicitHeight: 12
implicitWidth: 200
contentItem: Item {
anchors.fill: parent
CustomRect {
id: remaining
anchors.left: handle.right
anchors.leftMargin: Tokens.spacing.extraSmall
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
bottomLeftRadius: Tokens.rounding.extraSmall / 2
color: root.bgColor
implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.extraSmall / 2
}
CustomRect {
anchors.right: parent.right
anchors.rightMargin: 4 * remaining.opacity
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Tokens.rounding.full
}
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Tokens.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
const mult = parent.height <= 12 ? 3 : 1.2;
const pressMult = parent.height <= 12 ? 4 : 1.5;
return parent.height * (mouse.pressed ? pressMult : mult);
}
implicitWidth: 4
radius: Tokens.rounding.full
Behavior on implicitHeight {
Anim {
type: Anim.FastSpatial
}
}
}
Loader {
id: filled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: root.wavy ? waveComp : lineComp
}
Component {
id: lineComp
CustomRect {
bottomRightRadius: Tokens.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: root.filledWidth
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.extraSmall / 2
}
}
Component {
id: waveComp
WavyLine {
color: root.fgColor
frequency: root.waveFrequency
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: !root.animateWave
running: true
to: 1
}
}
}
}
Behavior on filledWidth {
id: widthBehavior
Anim {
}
}
Component.onCompleted: filledWidth = Qt.binding(() => (width - handle.implicitWidth - handle.anchors.leftMargin) * pos)
Binding {
id: posBinding
property: "pos"
target: root
value: ZUtils.clamp(mouse.pressStartPos + mouse.dragMovement, 0, 1)
when: mouse.pressed
}
MouseArea {
id: mouse
property real dragMovement
property real pressStartPos
property real pressStartX
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: handle.implicitHeight
preventStealing: true
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
pressStartX = e.x;
pressStartPos = root.visualPosition;
}
onReleased: e => {
root.interaction(posBinding.value);
widthBehavior.enabled = true;
dragMovement = 0;
}
}
}
-169
View File
@@ -1,169 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
RowLayout {
id: root
property string displayText: root.value.toString()
property bool isEditing: false
property real max: Infinity
property real min: -Infinity
property alias repeatRate: timer.interval
property real step: 1
property real value
signal valueModified(value: real)
spacing: Tokens.spacing.small
onValueChanged: {
if (!root.isEditing) {
root.displayText = root.value.toString();
}
}
CustomTextField {
id: textField
color: root.enabled ? Colors.palette.m3onSurface : Qt.alpha(Colors.palette.m3onSurface, 0.5)
implicitHeight: upButton.implicitHeight
inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Tokens.padding.normal
padding: Tokens.padding.small
rightPadding: Tokens.padding.normal
text: root.isEditing ? text : root.displayText
background: CustomRect {
color: root.enabled ? Colors.tPalette.m3surfaceContainerHigh : Colors.tPalette.m3surfaceContainerLow
implicitWidth: 100
radius: Tokens.rounding.full
}
validator: DoubleValidator {
bottom: root.min
decimals: root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0
top: root.max
}
onAccepted: {
const numValue = parseFloat(text);
if (!isNaN(numValue)) {
const clampedValue = Math.max(root.min, Math.min(root.max, numValue));
root.value = clampedValue;
root.displayText = clampedValue.toString();
root.valueModified(clampedValue);
} else {
text = root.displayText;
}
root.isEditing = false;
}
onActiveFocusChanged: {
if (activeFocus) {
root.isEditing = true;
} else {
root.isEditing = false;
root.displayText = root.value.toString();
}
}
onEditingFinished: {
if (text !== root.displayText) {
const numValue = parseFloat(text);
if (!isNaN(numValue)) {
const clampedValue = Math.max(root.min, Math.min(root.max, numValue));
root.value = clampedValue;
root.displayText = clampedValue.toString();
root.valueModified(clampedValue);
} else {
text = root.displayText;
}
}
root.isEditing = false;
}
}
CustomRect {
id: upButton
color: root.enabled ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHighest, 1)
implicitHeight: upIcon.implicitHeight + Tokens.padding.small * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.full
StateLayer {
id: upState
color: Colors.palette.m3onPrimary
onClicked: {
let newValue = Math.min(root.max, root.value + root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
root.value = newValue;
root.displayText = newValue.toString();
root.valueModified(newValue);
}
onPressAndHold: timer.start()
onReleased: timer.stop()
}
MaterialIcon {
id: upIcon
anchors.centerIn: parent
color: root.enabled ? Colors.palette.m3onPrimary : Qt.alpha(Colors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_up"
}
}
CustomRect {
color: root.enabled ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHighest, 1)
implicitHeight: downIcon.implicitHeight + Tokens.padding.small * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.full
StateLayer {
id: downState
color: Colors.palette.m3onPrimary
onClicked: {
let newValue = Math.max(root.min, root.value - root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
root.value = newValue;
root.displayText = newValue.toString();
root.valueModified(newValue);
}
onPressAndHold: timer.start()
onReleased: timer.stop()
}
MaterialIcon {
id: downIcon
anchors.centerIn: parent
color: root.enabled ? Colors.palette.m3onPrimary : Qt.alpha(Colors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_down"
}
}
Timer {
id: timer
interval: 100
repeat: true
triggeredOnStart: true
onTriggered: {
if (upState.pressed)
upState.onClicked();
else if (downState.pressed)
downState.onClicked();
}
}
}
-149
View File
@@ -1,149 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
Row {
id: root
enum Type {
Filled,
Tonal
}
property alias active: menu.active
property color colour: type == CustomSplitButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondaryContainer
property bool disabled
property color disabledColour: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledTextColour: Qt.alpha(Colors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded
property string fallbackIcon
property string fallbackText
property real horizontalPadding: Tokens.padding.larger
readonly property alias iconLabel: iconLabel
readonly property alias label: label
readonly property alias menu: menu
property alias menuItems: menu.items
property bool menuOnTop
property real minLeftWidth
readonly property alias stateLayer: stateLayer
property color textColour: type == CustomSplitButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: CustomSplitButton.Filled
property real verticalPadding: Tokens.padding.small
spacing: Math.floor(Tokens.spacing.extraSmall)
CustomRect {
bottomRightRadius: Tokens.rounding.small / 2
color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandBtn.implicitHeight
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2)
radius: implicitHeight / 2 * Math.min(1, Tokens.rounding.scale)
topRightRadius: Tokens.rounding.small / 2
StateLayer {
id: stateLayer
bottomRightRadius: parent.bottomRightRadius
color: root.textColour
disabled: root.disabled
topRightRadius: parent.topRightRadius
onClicked: root.active?.clicked()
}
RowLayout {
id: textRow
anchors.centerIn: parent
anchors.horizontalCenterOffset: Math.floor(root.verticalPadding / 4)
spacing: Tokens.spacing.small
MaterialIcon {
id: iconLabel
Layout.alignment: Qt.AlignVCenter
animate: true
color: root.disabled ? root.disabledTextColour : root.textColour
fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon
}
CustomText {
id: label
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: implicitWidth
animate: true
clip: true
color: root.disabled ? root.disabledTextColour : root.textColour
text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth {
Anim {
type: Anim.Emphasized
}
}
}
}
}
CustomRect {
id: expandBtn
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) : Tokens.rounding.small / 2
bottomLeftRadius: rad
color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Tokens.rounding.scale)
topLeftRadius: rad
Behavior on rad {
Anim {
}
}
StateLayer {
id: expandStateLayer
color: root.textColour
disabled: root.disabled
rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius
onClicked: root.expanded = !root.expanded
}
MaterialIcon {
id: expandIcon
anchors.centerIn: parent
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
color: root.disabled ? root.disabledTextColour : root.textColour
rotation: root.expanded ? 180 : 0
text: "expand_more"
Behavior on anchors.horizontalCenterOffset {
Anim {
}
}
Behavior on rotation {
Anim {
}
}
}
}
Menu {
id: menu
attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom
attachTo: expandBtn
marginY: Tokens.spacing.small * (root.menuOnTop ? -1 : 1)
thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top
}
}
@@ -1,70 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
Item {
id: root
property alias active: splitButton.active
property alias buttonAlias: splitButton
property alias expanded: splitButton.expanded
property int expandedZ: 100
required property string label
property alias menuItems: splitButton.menuItems
property bool shouldBeActive: true
property alias type: splitButton.type
signal selected(item: MenuItem)
anchors.left: parent.left
anchors.right: parent.right
clip: false
implicitHeight: row.implicitHeight + Tokens.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Tokens.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Tokens.spacing.normal
CustomText {
Layout.fillWidth: true
color: root.enabled ? Colors.palette.m3onSurface : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.larger
text: root.label
}
CustomSplitButton {
id: splitButton
enabled: root.enabled
type: CustomSplitButton.Filled
z: 2
menu.onItemSelected: item => {
root.selected(item);
}
stateLayer.onClicked: {
splitButton.expanded = !splitButton.expanded;
}
}
}
}
-158
View File
@@ -1,158 +0,0 @@
import QtQuick
import QtQuick.Shapes
import QtQuick.Templates
import ZShell.Config
import qs.Services
Switch {
id: root
property int cLayer: 1
implicitHeight: implicitIndicatorHeight
implicitWidth: implicitIndicatorWidth
indicator: CustomRect {
color: root.checked && root.enabled ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Tokens.font.size.medium + Tokens.padding.normal * 2
implicitWidth: implicitHeight * 1.7
radius: Tokens.rounding.full
CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight
anchors.verticalCenter: parent.verticalCenter
color: root.checked && root.enabled ? Colors.palette.m3onPrimary : Colors.layer(Colors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Tokens.padding.extraSmall
implicitWidth: nonAnimWidth
radius: Tokens.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - Tokens.padding.extraSmall / 2 : Tokens.padding.extraSmall / 2
Behavior on implicitWidth {
Anim {
type: Anim.FastSpatial
}
}
Behavior on x {
Anim {
type: Anim.FastSpatial
}
}
CustomRect {
anchors.fill: parent
color: root.checked && root.enabled ? Colors.palette.m3primary : Colors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
Shape {
id: icon
property point end1: {
if (root.pressed) {
if (root.checked)
return Qt.point(width * 0.4, height / 2);
return Qt.point(width * 0.8, height / 2);
}
if (root.checked)
return Qt.point(width * 0.4, height * 0.7);
return Qt.point(width * 0.85, height * 0.85);
}
property point end2: {
if (root.pressed)
return Qt.point(width * 0.8, height / 2);
if (root.checked)
return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15);
}
property point start1: {
if (root.pressed)
return Qt.point(width * 0.2, height / 2);
if (root.checked)
return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15);
}
property point start2: {
if (root.pressed) {
if (root.checked)
return Qt.point(width * 0.4, height / 2);
return Qt.point(width * 0.2, height / 2);
}
if (root.checked)
return Qt.point(width * 0.4, height * 0.7);
return Qt.point(width * 0.15, height * 0.85);
}
anchors.centerIn: parent
asynchronous: true
height: parent.implicitHeight - Tokens.padding.larger
preferredRendererType: Shape.CurveRenderer
width: height
Behavior on end1 {
PropAnim {
}
}
Behavior on end2 {
PropAnim {
}
}
Behavior on start1 {
PropAnim {
}
}
Behavior on start2 {
PropAnim {
}
}
ShapePath {
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: icon.start1.x
startY: icon.start1.y
strokeColor: root.checked && root.enabled ? Colors.palette.m3primary : Colors.palette.m3surfaceContainerHighest
strokeWidth: Tokens.font.size.larger * 0.15
Behavior on strokeColor {
CAnim {
}
}
PathLine {
x: icon.end1.x
y: icon.end1.y
}
PathMove {
x: icon.start2.x
y: icon.start2.y
}
PathLine {
x: icon.end2.x
y: icon.end2.y
}
}
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
component PropAnim: PropertyAnimation {
duration: Tokens.anim.durations.expressiveFastSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ TextField {
background: null
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font.family: Appearance.font.family.sans
font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.smaller
placeholderTextColor: Colors.palette.m3outline
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
+3 -3
View File
@@ -6,11 +6,11 @@ import qs.Services
TextInput {
renderType: Text.NativeRendering
selectedTextColor: Colors.palette.m3onSecondaryContainer
selectionColor: Colors.tPalette.colSecondaryContainer
selectionColor: Colors.tPalette.m3onSecondaryContainer
font {
family: Appearance?.font.family.sans ?? "sans-serif"
family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting
pixelSize: Appearance?.font.size.normal ?? 15
pixelSize: Config.appearance?.font.size.normal ?? 15
}
}
-25
View File
@@ -1,25 +0,0 @@
import QtQuick
import QtQuick.Controls
import qs.Components
ToolTip {
id: root
property bool alternativeVisibleCondition: false
property bool extraVisibleCondition: true
readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
background: null
horizontalPadding: 10
verticalPadding: 5
visible: internalVisibleCondition
contentItem: CustomTooltipContent {
id: contentItem
horizontalPadding: root.horizontalPadding
shown: root.internalVisibleCondition
text: root.text
verticalPadding: root.verticalPadding
}
}
@@ -1,55 +0,0 @@
import QtQuick
import qs.Components
import ZShell.Config
import qs.Services
Item {
id: root
property real horizontalPadding: 10
property bool isVisible: backgroundRectangle.implicitHeight > 0
property bool shown: false
required property string text
property real verticalPadding: 5
implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding
implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding
Rectangle {
id: backgroundRectangle
clip: true
color: Colors.tPalette.m3inverseSurface ?? "#3C4043"
implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0
implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0
opacity: shown ? 1 : 0
radius: Tokens.rounding.smallest
Behavior on implicitHeight {
Anim {
}
}
Behavior on implicitWidth {
Anim {
}
}
Behavior on opacity {
Anim {
}
}
anchors {
bottom: root.bottom
horizontalCenter: root.horizontalCenter
}
CustomText {
id: tooltipTextObject
anchors.centerIn: parent
color: Colors.palette.m3inverseOnSurface ?? "#FFFFFF"
text: root.text
wrapMode: Text.Wrap
}
}
}
-44
View File
@@ -1,44 +0,0 @@
import ZShell.Config
import QtQuick
import qs.Services
CustomRect {
required property int extra
anchors.margins: 8
anchors.right: parent.right
color: Colors.palette.m3tertiary
implicitHeight: count.implicitHeight + 4 * 2
implicitWidth: count.implicitWidth + 8 * 2
opacity: extra > 0 ? 1 : 0
radius: Tokens.rounding.smallest
scale: extra > 0 ? 1 : 0.5
Behavior on opacity {
Anim {
type: Anim.FastEffects
}
}
Behavior on scale {
Anim {
type: Anim.FastEffects
}
}
Elevation {
anchors.fill: parent
level: 2
opacity: parent.opacity
radius: parent.radius
z: -1
}
CustomText {
id: count
anchors.centerIn: parent
animate: parent.opacity > 0
color: Colors.palette.m3onTertiary
text: qsTr("+%1").arg(parent.extra)
}
}
-29
View File
@@ -1,29 +0,0 @@
import QtQuick
import ZShell.Config
BaseStyledSlider {
id: root
trackContent: Component {
Item {
property var groove
readonly property real handleHeight: handleItem ? handleItem.height : 0
property var handleItem
readonly property real handleWidth: handleItem ? handleItem.width : 0
// Set by BaseStyledSlider's Loader
property var rootSlider
anchors.fill: parent
CustomRect {
color: rootSlider?.color
height: rootSlider?.isVertical ? handleHeight + (1 - rootSlider?.visualPosition) * (groove?.height - handleHeight) : groove?.height
radius: groove?.radius
width: rootSlider?.isHorizontal ? handleWidth + rootSlider?.visualPosition * (groove?.width - handleWidth) : groove?.width
x: rootSlider?.isHorizontal ? (rootSlider?.mirrored ? groove?.width - width : 0) : 0
y: rootSlider?.isVertical ? groove?.height - height : 0
}
}
}
}
-47
View File
@@ -1,47 +0,0 @@
import QtQuick
import ZShell.Config
BaseStyledSlider {
id: root
property real alpha: 1.0
property real brightness: 1.0
property string channel: "saturation"
readonly property color currentColor: Qt.hsva(hue, channel === "saturation" ? value : saturation, channel === "brightness" ? value : brightness, alpha)
property real hue: 0.0
property real saturation: 1.0
from: 0
to: 1
trackContent: Component {
Item {
property var groove
property var handleItem
property var rootSlider
anchors.fill: parent
Rectangle {
anchors.fill: parent
antialiasing: true
color: "transparent"
radius: groove?.radius ?? 0
gradient: Gradient {
orientation: rootSlider?.isHorizontal ? Gradient.Horizontal : Gradient.Vertical
GradientStop {
color: root.channel === "saturation" ? Qt.hsva(root.hue, 0.0, root.brightness, root.alpha) : Qt.hsva(root.hue, root.saturation, 0.0, root.alpha)
position: 0.0
}
GradientStop {
color: root.channel === "saturation" ? Qt.hsva(root.hue, 1.0, root.brightness, root.alpha) : Qt.hsva(root.hue, root.saturation, 1.0, root.alpha)
position: 1.0
}
}
}
}
}
}
-33
View File
@@ -1,33 +0,0 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
IconButton {
id: root
required property bool shouldBeVisible
opacity: 0
scale: 0
visible: root.scale > 0
Behavior on opacity {
Anim {
duration: Tokens.anim.durations.small
}
}
Behavior on scale {
Anim {
}
}
onShouldBeVisibleChanged: {
if (root.shouldBeVisible) {
root.opacity = 1;
root.scale = 1;
} else {
root.opacity = 0;
root.scale = 0;
}
}
}
-80
View File
@@ -1,80 +0,0 @@
import ZShell.Config
import QtQuick
import qs.Services
CustomRect {
id: root
enum Type {
Filled,
Tonal,
Text
}
property color activeColour: type === IconButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondary
property color activeOnColour: type === IconButton.Filled ? Colors.palette.m3onPrimary : type === IconButton.Tonal ? Colors.palette.m3onSecondary : Colors.palette.m3primary
property bool checked
property bool disabled
property color disabledColour: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledOnColour: Qt.alpha(Colors.palette.m3onSurface, 0.38)
property alias font: label.font
property alias icon: label.text
property color inactiveColour: {
if (!toggle && type === IconButton.Filled)
return Colors.palette.m3primary;
return type === IconButton.Filled ? Colors.tPalette.m3surfaceContainer : Colors.palette.m3secondaryContainer;
}
property color inactiveOnColour: {
if (!toggle && type === IconButton.Filled)
return Colors.palette.m3onPrimary;
return type === IconButton.Tonal ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onSurfaceVariant;
}
property bool internalChecked
property alias label: label
property real padding: type === IconButton.Text ? 10 / 2 : 7
property alias radiusAnim: radiusAnim
property alias stateLayer: stateLayer
property bool toggle
property int type: IconButton.Filled
signal clicked
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
implicitHeight: label.implicitHeight + padding * 2
implicitWidth: implicitHeight
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Tokens.rounding.scale
Behavior on radius {
Anim {
id: radiusAnim
}
}
onCheckedChanged: internalChecked = checked
StateLayer {
id: stateLayer
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
onClicked: {
if (root.toggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
}
MaterialIcon {
id: label
anchors.centerIn: parent
color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour
fill: !root.toggle || root.internalChecked ? 1 : 0
Behavior on fill {
Anim {
}
}
}
}
-224
View File
@@ -1,224 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
Item {
id: root
property alias anim: marqueeAnim
property bool animate: false
property color color: Colors.palette.m3onSurface
property int fadeStrengthAnimMs: 180
property real fadeStrengthIdle: 0.0
property real fadeStrengthMoving: 1.0
property alias font: elideText.font
property int gap: 40
property alias horizontalAlignment: elideText.horizontalAlignment
property bool leftFadeEnabled: false
property real leftFadeStrength: overflowing && leftFadeEnabled ? fadeStrengthMoving : fadeStrengthIdle
property int leftFadeWidth: 28
property bool marqueeEnabled: true
readonly property bool overflowing: metrics.width > root.width
property int pauseMs: 1200
property real pixelsPerSecond: 40
property real rightFadeStrength: overflowing ? fadeStrengthMoving : fadeStrengthIdle
property int rightFadeWidth: 28
property bool sliding: false
property alias text: elideText.text
function durationForDistance(px): int {
return Math.max(1, Math.round(Math.abs(px) / root.pixelsPerSecond * 1000));
}
function resetMarquee() {
marqueeAnim.stop();
strip.x = 0;
root.sliding = false;
root.leftFadeEnabled = false;
if (root.marqueeEnabled && root.overflowing && root.visible) {
marqueeAnim.restart();
}
}
clip: true
implicitHeight: elideText.implicitHeight
Behavior on leftFadeStrength {
Anim {
}
}
Behavior on rightFadeStrength {
Anim {
}
}
onTextChanged: resetMarquee()
onVisibleChanged: if (!visible)
resetMarquee()
onWidthChanged: resetMarquee()
TextMetrics {
id: metrics
font: elideText.font
text: elideText.text
}
CustomText {
id: elideText
anchors.verticalCenter: parent.verticalCenter
animate: root.animate
animateProp: "scale,opacity"
color: root.color
elide: Text.ElideNone
visible: !root.overflowing
width: root.width
}
Item {
id: marqueeViewport
anchors.fill: parent
clip: true
layer.enabled: true
visible: root.overflowing
layer.effect: OpacityMask {
maskSource: rightFadeMask
}
Item {
id: strip
anchors.verticalCenter: parent.verticalCenter
height: t1.implicitHeight
width: t1.width + root.gap + t2.width
x: 0
CustomText {
id: t1
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: elideText.text
}
CustomText {
id: t2
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: t1.text
x: t1.width + root.gap
}
}
SequentialAnimation {
id: marqueeAnim
running: false
onFinished: pauseTimer.restart()
ScriptAction {
script: {
root.sliding = true;
root.leftFadeEnabled = true;
}
}
Anim {
duration: root.durationForDistance(t1.width)
easing.bezierCurve: Easing.Linear
easing.type: Easing.Linear
from: 0
property: "x"
target: strip
to: -t1.width
}
ScriptAction {
script: {
root.leftFadeEnabled = false;
}
}
Anim {
duration: root.durationForDistance(root.gap)
easing.bezierCurve: Easing.Linear
easing.type: Easing.Linear
from: -t1.width
property: "x"
target: strip
to: -(t1.width + root.gap)
}
ScriptAction {
script: {
root.sliding = false;
strip.x = 0;
}
}
}
Timer {
id: pauseTimer
interval: root.pauseMs
repeat: false
running: true
onTriggered: {
if (root.marqueeEnabled)
marqueeAnim.start();
}
}
}
Rectangle {
id: rightFadeMask
readonly property real fadeStartPos: {
const w = Math.max(1, width);
return Math.max(0, Math.min(1, (w - root.rightFadeWidth) / w));
}
readonly property real leftFadeEndPos: {
const w = Math.max(1, width);
return Math.max(0, Math.min(1, root.leftFadeWidth / w));
}
anchors.fill: marqueeViewport
layer.enabled: true
visible: false
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0 - root.leftFadeStrength)
position: 0.0
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0)
position: rightFadeMask.leftFadeEndPos
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0)
position: rightFadeMask.fadeStartPos
}
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0 - root.rightFadeStrength)
position: 1.0
}
}
}
}
-176
View File
@@ -1,176 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Config
import qs.Drawers
import qs.Services
MouseArea {
id: root
enum Side {
Top,
Bottom,
Left,
Right
}
property MenuItem active: items[0] ?? null
property int attachSideX: Menu.Right
property int attachSideY: Menu.Bottom
required property Item attachTo
property bool expanded
property list<MenuItem> items
property real marginX
property real marginY
property int thisSideX: Menu.Right
property int thisSideY: Menu.Top
signal itemSelected(item: MenuItem)
anchors.fill: parent
enabled: expanded
layer.enabled: opacity < 1
opacity: expanded ? 1 : 0
parent: {
const win = QsWindow.window;
const contentWin = win as Windows;
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
onClicked: expanded = false
TransformWatcher {
id: watcher
a: root.parent
b: root.attachTo
}
Elevation {
id: menu
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Tokens.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideX === Menu.Left ? 0 : item.width;
if (root.thisSideX === Menu.Right)
off -= width;
return item.mapToItem(root.parent, off, 0).x + root.marginX;
}
y: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideY === Menu.Top ? 0 : item.height;
if (root.thisSideY === Menu.Bottom)
off -= height;
return item.mapToItem(root.parent, 0, off).y + root.marginY;
}
transform: Scale {
origin.y: root.thisSideY === Menu.Bottom ? menu.height : 0
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {
}
}
}
CustomRect {
anchors.fill: parent
color: Colors.palette.m3surfaceContainerLow
radius: parent.radius
ColumnLayout {
id: column
anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall
spacing: Tokens.spacing.extraSmall
Repeater {
id: repeater
model: root.items
CustomRect {
id: item
readonly property bool active: modelData === root.active
required property int index
required property MenuItem modelData
Layout.fillWidth: true
color: Qt.alpha(Colors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitHeight: menuOptionRow.implicitHeight + Tokens.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Tokens.padding.larger * 2
radius: Tokens.rounding.small
Behavior on radius {
Anim {
}
}
StateLayer {
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
disabled: !root.expanded
onClicked: {
root.itemSelected(item.modelData);
root.active = item.modelData;
item.modelData.clicked();
root.expanded = false;
}
}
RowLayout {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Tokens.padding.larger
spacing: Tokens.spacing.small
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: item.modelData.icon
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
text: item.modelData.text
}
Loader {
Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0
asynchronous: true
visible: active
sourceComponent: MaterialIcon {
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: item.modelData.trailingIcon
}
}
}
}
}
}
}
}
}
-12
View File
@@ -1,12 +0,0 @@
import QtQuick
QtObject {
property string activeIcon: icon
property string activeText: text
property string icon
required property string text
property string trailingIcon
property var value
signal clicked
}
-76
View File
@@ -1,76 +0,0 @@
import QtQuick
Path {
id: root
required property real viewHeight
required property real viewWidth
startX: root.viewWidth / 2
startY: 0
PathAttribute {
name: "itemOpacity"
value: 0.25
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (1 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.45
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (2 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.70
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (3 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 1.00
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (4 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.70
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight * (5 / 6)
}
PathAttribute {
name: "itemOpacity"
value: 0.45
}
PathLine {
x: root.viewWidth / 2
y: root.viewHeight
}
PathAttribute {
name: "itemOpacity"
value: 0.25
}
}
-181
View File
@@ -1,181 +0,0 @@
import QtQuick
import QtQuick.Effects
import ZShell.Config
import qs.Services
Elevation {
id: root
required property int currentIndex
property bool expanded
required property int from
property color insideTextColor: Colors.palette.m3onPrimary
property int itemHeight
property int listHeight: 200
property color outsideTextColor: Colors.palette.m3onSurfaceVariant
readonly property var spinnerModel: root.range(root.from, root.to)
required property int to
property Item triggerItem
signal itemSelected(item: int)
function range(first, last) {
let out = [];
for (let i = first; i <= last; ++i)
out.push(i);
return out;
}
implicitHeight: root.expanded ? view.implicitHeight : 0
level: root.expanded ? 2 : 0
radius: itemHeight / 2
visible: implicitHeight > 0
z: root.expanded ? 100 : 0
Behavior on implicitHeight {
Anim {
}
}
onExpandedChanged: {
if (!root.expanded)
root.itemSelected(view.currentIndex + 1);
}
Component {
id: spinnerDelegate
Item {
id: wrapper
readonly property color delegateTextColor: wrapper.PathView.view ? wrapper.PathView.view.delegateTextColor : "white"
required property var modelData
height: root.itemHeight
opacity: wrapper.PathView.itemOpacity
visible: wrapper.PathView.onPath
width: wrapper.PathView.view ? wrapper.PathView.view.width : 0
z: wrapper.PathView.isCurrentItem ? 100 : Math.round(wrapper.PathView.itemScale * 100)
CustomText {
anchors.centerIn: parent
color: wrapper.delegateTextColor
font.pointSize: Tokens.font.size.large
text: wrapper.modelData
}
}
}
CustomClippingRect {
anchors.fill: parent
color: Colors.palette.m3surfaceContainer
radius: parent.radius
z: root.z
// Main visible spinner: normal/outside text color
PathView {
id: view
property color delegateTextColor: root.outsideTextColor
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
currentIndex: root.currentIndex - 1
delegate: spinnerDelegate
dragMargin: width
highlightRangeMode: PathView.StrictlyEnforceRange
implicitHeight: root.listHeight
model: root.spinnerModel
pathItemCount: 7
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
snapMode: PathView.SnapToItem
path: PathMenu {
viewHeight: view.height
viewWidth: view.width
}
}
// The selection rectangle itself
CustomRect {
id: selectionRect
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3primary
height: root.itemHeight
radius: root.itemHeight / 2
width: parent.width
z: 2
}
// Hidden source: same PathView, but with the "inside selection" text color
Item {
id: selectedTextSource
anchors.fill: parent
layer.enabled: true
visible: false
PathView {
id: selectedTextView
property color delegateTextColor: root.insideTextColor
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
currentIndex: view.currentIndex
delegate: spinnerDelegate
dragMargin: view.dragMargin
highlightRangeMode: view.highlightRangeMode
implicitHeight: root.listHeight
interactive: false
model: view.model
// Keep this PathView visually locked to the real one
offset: view.offset
pathItemCount: view.pathItemCount
preferredHighlightBegin: view.preferredHighlightBegin
preferredHighlightEnd: view.preferredHighlightEnd
snapMode: view.snapMode
path: PathMenu {
viewHeight: selectedTextView.height
viewWidth: selectedTextView.width
}
}
}
// Mask matching the selection rectangle
Item {
id: selectionMask
anchors.fill: parent
layer.enabled: true
visible: false
CustomRect {
color: "white"
height: selectionRect.height
radius: selectionRect.radius
width: selectionRect.width
x: selectionRect.x
y: selectionRect.y
}
}
// Only show the "inside selection" text where the mask exists
MultiEffect {
anchors.fill: selectedTextSource
maskEnabled: true
maskInverted: false
maskSource: selectionMask
source: selectedTextSource
z: 3
}
}
}
-8
View File
@@ -1,8 +0,0 @@
import QtQuick
QtObject {
required property var service
Component.onCompleted: service.refCount++
Component.onDestruction: service.refCount--
}
-51
View File
@@ -1,51 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
CustomRect {
id: root
required property string label
required property real max
required property real min
property var onValueModified: function (value) {}
property real step: 1
required property real value
Layout.fillWidth: true
color: Colors.layer(Colors.palette.m3surfaceContainer, 2)
implicitHeight: row.implicitHeight + Tokens.padding.large * 2
radius: Tokens.rounding.normal
Behavior on implicitHeight {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Tokens.padding.large
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Tokens.spacing.normal
CustomText {
Layout.fillWidth: true
text: root.label
}
CustomSpinBox {
max: root.max
min: root.min
step: root.step
value: root.value
onValueModified: value => {
root.onValueModified(value);
}
}
}
}
-1
View File
@@ -66,7 +66,6 @@ MouseArea {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onClicked: event => !disabled && onClicked(event)
onManualPressOverrideChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
-132
View File
@@ -1,132 +0,0 @@
import ZShell
import QtQuick
import QtQuick.Layouts
import qs.Components
import ZShell.Config
import qs.Services
CustomRect {
id: root
required property Toast modelData
anchors.left: parent.left
anchors.right: parent.right
border.color: {
let colour = Colors.palette.m3outlineVariant;
if (root.modelData.type === Toast.Success)
colour = Colors.palette.m3success;
if (root.modelData.type === Toast.Warning)
colour = Colors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
colour = Colors.palette.m3error;
return Qt.alpha(colour, 0.3);
}
border.width: 1
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3successContainer;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3secondary;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3errorContainer;
return Colors.palette.m3surface;
}
implicitHeight: layout.implicitHeight + Tokens.padding.smaller * 2
radius: Tokens.rounding.normal
Behavior on border.color {
CAnim {
}
}
Elevation {
anchors.fill: parent
level: 3
opacity: parent.opacity
radius: parent.radius
z: -1
}
RowLayout {
id: layout
anchors.fill: parent
anchors.leftMargin: Tokens.padding.normal
anchors.margins: Tokens.padding.smaller
anchors.rightMargin: Tokens.padding.normal
spacing: Tokens.spacing.normal
CustomRect {
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3success;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3error;
return Colors.palette.m3surfaceContainerHigh;
}
implicitHeight: icon.implicitHeight + Tokens.padding.smaller * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.normal
MaterialIcon {
id: icon
anchors.centerIn: parent
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3onSuccess;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3onSecondaryContainer;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3onError;
return Colors.palette.m3onSurfaceVariant;
}
font.pointSize: Math.round(Tokens.font.size.large * 1.2)
text: root.modelData.icon
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
CustomText {
id: title
Layout.fillWidth: true
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3onErrorContainer;
return Colors.palette.m3onSurface;
}
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
text: root.modelData.title
}
CustomText {
Layout.fillWidth: true
color: {
if (root.modelData.type === Toast.Success)
return Colors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return Colors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return Colors.palette.m3onErrorContainer;
return Colors.palette.m3onSurface;
}
elide: Text.ElideRight
opacity: 0.8
text: root.modelData.message
textFormat: Text.StyledText
}
}
}
}
-142
View File
@@ -1,142 +0,0 @@
pragma ComponentBehavior: Bound
import ZShell
import Quickshell
import QtQuick
import qs.Components
import ZShell.Config
Item {
id: root
property bool flag
readonly property int spacing: Tokens.spacing.small
implicitHeight: {
let h = -spacing;
for (let i = 0; i < repeater.count; i++) {
const item = repeater.itemAt(i) as ToastWrapper;
if (!item.modelData.closed && !item.previewHidden)
h += item.implicitHeight + spacing;
}
return h;
}
implicitWidth: Config.utilities.sizes.toastWidth - Tokens.padding.normal * 2
Repeater {
id: repeater
model: ScriptModel {
values: {
const toasts = [];
let count = 0;
for (const toast of Toaster.toasts) {
toasts.push(toast);
if (!toast.closed) {
count++;
if (count > Config.utilities.maxToasts)
break;
}
}
return toasts;
}
onValuesChanged: root.flagChanged()
}
ToastWrapper {
}
}
component ToastWrapper: MouseArea {
id: toast
required property int index
required property Toast modelData
readonly property bool previewHidden: {
let extraHidden = 0;
for (let i = 0; i < index; i++)
if (Toaster.toasts[i].closed)
extraHidden++;
return index >= Config.utilities.maxToasts + extraHidden;
}
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
anchors.bottom: parent.bottom
anchors.bottomMargin: {
root.flag; // Force update
let y = 0;
for (let i = 0; i < index; i++) {
const item = repeater.itemAt(i) as ToastWrapper;
if (item && !item.modelData.closed && !item.previewHidden)
y += item.implicitHeight + root.spacing;
}
return y;
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: toastInner.implicitHeight
opacity: modelData.closed || previewHidden ? 0 : 1
scale: modelData.closed || previewHidden ? 0.7 : 1
Behavior on anchors.bottomMargin {
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Component.onCompleted: modelData.lock(this)
onClicked: modelData.close()
onPreviewHiddenChanged: {
if (initAnim.running && previewHidden)
initAnim.stop();
}
Anim {
id: initAnim
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
from: 0
properties: "opacity,scale"
target: toast
to: 1
Component.onCompleted: running = !toast.previewHidden
}
ParallelAnimation {
running: toast.modelData.closed
onFinished: toast.modelData.unlock(toast)
onStarted: toast.anchors.bottomMargin = toast.anchors.bottomMargin
Anim {
property: "opacity"
target: toast
to: 0
}
Anim {
property: "scale"
target: toast
to: 0.7
}
}
ToastItem {
id: toastInner
modelData: toast.modelData
}
}
}
-13
View File
@@ -1,13 +0,0 @@
import Quickshell.Io
JsonObject {
property Accents accents: Accents {
}
component Accents: JsonObject {
property string primary: "#4080ff"
property string primaryAlt: "#60a0ff"
property string warning: "#ff6b6b"
property string warningAlt: "#ff8787"
}
}
-13
View File
@@ -1,13 +0,0 @@
pragma Singleton
import Quickshell
Singleton {
readonly property AppearanceConf.Anim anim: Config.appearance.anim
readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
}
-114
View File
@@ -1,114 +0,0 @@
import Quickshell.Io
JsonObject {
property Anim anim: Anim {
}
property Deform deform: Deform {
}
property FontStuff font: FontStuff {
}
property Padding padding: Padding {
}
property Rounding rounding: Rounding {
}
property Spacing spacing: Spacing {
}
property Transparency transparency: Transparency {
}
component Anim: JsonObject {
property AnimCurves curves: AnimCurves {
}
property AnimDurations durations: AnimDurations {
}
property real mediaGifSpeedAdjustment: 300
property real sessionGifSpeed: 0.7
}
component AnimCurves: JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
}
component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale
property int large: 600 * scale
property int normal: 400 * scale
property real scale: 1
property int small: 200 * scale
}
component Deform: JsonObject {
property real scale: 1
}
component FontFamily: JsonObject {
property string clock: "Rubik"
property string material: "Material Symbols Rounded"
property string mono: "CaskaydiaCove NF"
property string sans: "Segoe UI Variable Text"
}
component FontSize: JsonObject {
property int extraLarge: 28 * scale
property int large: 18 * scale
property int larger: 16 * scale
property int medium: 14 * scale
property int normal: 13 * scale
property real scale: 1
property int small: 11 * scale
property int smaller: 12 * scale
}
component FontStuff: JsonObject {
property FontFamily family: FontFamily {
}
property FontSize size: FontSize {
}
}
component Padding: JsonObject {
property int extraLargeIncreased: 32 * scale
property int extraSmall: 4 * scale
property int large: 16 * scale
property int larger: 12 * scale
property int normal: 8 * scale
property real scale: 1
property int small: 5 * scale
property int smaller: 7 * scale
property int smallest: 2 * scale
}
component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale
property int large: 24 * scale
property int medium: 16 * scale
property int normal: 18 * scale
property real scale: 1
property int small: 12 * scale
property int smallest: 8 * scale
}
component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale
property int larger: 16 * scale
property int normal: 12 * scale
property real scale: 1
property int small: 8 * scale
property int smaller: 10 * scale
}
component Transparency: JsonObject {
property real base: 0.85
property bool enabled: false
property real layers: 0.4
}
}
-14
View File
@@ -1,14 +0,0 @@
import Quickshell.Io
import ZShell.Config
JsonObject {
property bool enabled: true
property int wallFadeDuration: MaterialEasing.standardTime
property real alignX: 0.5
property real alignY: 0.5
property real zoom: 1.0
property real sourceClipX: 0
property real sourceClipY: 0
property real sourceClipW: 0
property real sourceClipH: 0
}
-77
View File
@@ -1,77 +0,0 @@
import Quickshell.Io
JsonObject {
property bool autoHide: false
property int border: 8
property list<var> entries: [
{
id: "workspaces",
enabled: true
},
{
id: "media",
enabled: true
},
{
id: "resources",
enabled: true
},
{
id: "updates",
enabled: true
},
{
id: "spacer",
enabled: true
},
{
id: "activeWindow",
enabled: true
},
{
id: "spacer",
enabled: true
},
{
id: "hyprsunset",
enabled: true
},
{
id: "tray",
enabled: true
},
{
id: "network",
enabled: false
},
{
id: "clock",
enabled: true
},
{
id: "notifBell",
enabled: true
},
]
property int height: 34
property bool hideWhenNotif: false
property Popouts popouts: Popouts {
}
property int rounding: 8
property int smoothing: 32
property Tray tray: Tray {
}
component Popouts: JsonObject {
property bool activeWindow: true
property bool audio: true
property bool clock: true
property bool network: true
property bool resources: true
property bool tray: true
property bool upower: true
}
component Tray: JsonObject {
property int trayIconSize: 24
}
}
-13
View File
@@ -1,13 +0,0 @@
import Quickshell.Io
JsonObject {
property Presets presets: Presets {
}
property string schemeType: "vibrant"
component Presets: JsonObject {
property string accent: ""
property string name: ""
property string variant: ""
}
}
-474
View File
@@ -1,474 +0,0 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import ZShell
import QtQuick
Singleton {
id: root
property alias appearance: adapter.appearance
property alias background: adapter.background
property alias barConfig: adapter.barConfig
property alias colors: adapter.colors
property alias dashboard: adapter.dashboard
property alias dock: adapter.dock
property alias general: adapter.general
property alias launcher: adapter.launcher
property alias lock: adapter.lock
property alias notifs: adapter.notifs
property alias osd: adapter.osd
property alias overview: adapter.overview
property bool recentlySaved: false
property alias screenshot: adapter.screenshot
property alias services: adapter.services
property alias sidebar: adapter.sidebar
property alias utilities: adapter.utilities
function save(): void {
saveTimer.restart();
recentlySaved = true;
recentSaveCooldown.restart();
}
function saveNoToast(): void {
saveTimer.restart();
}
function serializeAppearance(): var {
return {
rounding: {
scale: appearance.rounding.scale
},
spacing: {
scale: appearance.spacing.scale
},
padding: {
scale: appearance.padding.scale
},
deform: {
scale: appearance.deform.scale
},
font: {
family: {
sans: appearance.font.family.sans,
mono: appearance.font.family.mono,
material: appearance.font.family.material,
clock: appearance.font.family.clock
},
size: {
scale: appearance.font.size.scale
}
},
anim: {
mediaGifSpeedAdjustment: appearance.anim.mediaGifSpeedAdjustment,
sessionGifSpeed: appearance.anim.sessionGifSpeed,
durations: {
scale: appearance.anim.durations.scale
}
},
transparency: {
enabled: appearance.transparency.enabled,
base: appearance.transparency.base,
layers: appearance.transparency.layers
}
};
}
function serializeBackground(): var {
return {
wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled,
alignX: background.alignX,
sourceClipX: background.sourceClipX,
sourceClipY: background.sourceClipY,
sourceClipW: background.sourceClipW,
sourceClipH: background.sourceClipH,
alignY: background.alignY,
zoom: background.zoom
};
}
function serializeBar(): var {
return {
autoHide: barConfig.autoHide,
hideWhenNotif: barConfig.hideWhenNotif,
rounding: barConfig.rounding,
border: barConfig.border,
smoothing: barConfig.smoothing,
height: barConfig.height,
tray: {
trayIconSize: barConfig.tray.trayIconSize
},
popouts: {
tray: barConfig.popouts.tray,
audio: barConfig.popouts.audio,
activeWindow: barConfig.popouts.activeWindow,
resources: barConfig.popouts.resources,
clock: barConfig.popouts.clock,
network: barConfig.popouts.network,
upower: barConfig.popouts.upower
},
entries: barConfig.entries
};
}
function serializeColors(): var {
return {
schemeType: colors.schemeType,
presets: {
name: colors.presets.name,
variant: colors.presets.variant,
accent: colors.presets.accent
}
};
}
function serializeConfig(): var {
return {
barConfig: serializeBar(),
lock: serializeLock(),
general: serializeGeneral(),
services: serializeServices(),
notifs: serializeNotifs(),
sidebar: serializeSidebar(),
utilities: serializeUtilities(),
dashboard: serializeDashboard(),
appearance: serializeAppearance(),
osd: serializeOsd(),
background: serializeBackground(),
launcher: serializeLauncher(),
colors: serializeColors(),
dock: serializeDock(),
screenshot: serializeScreenshot()
};
}
function serializeDashboard(): var {
return {
enabled: dashboard.enabled,
mediaUpdateInterval: dashboard.mediaUpdateInterval,
resourceUpdateInterval: dashboard.resourceUpdateInterval,
dragThreshold: dashboard.dragThreshold,
performance: {
showBattery: dashboard.performance.showBattery,
showGpu: dashboard.performance.showGpu,
showCpu: dashboard.performance.showCpu,
showMemory: dashboard.performance.showMemory,
showStorage: dashboard.performance.showStorage,
showNetwork: dashboard.performance.showNetwork
},
sizes: {
tabIndicatorHeight: dashboard.sizes.tabIndicatorHeight,
tabIndicatorSpacing: dashboard.sizes.tabIndicatorSpacing,
infoWidth: dashboard.sizes.infoWidth,
infoIconSize: dashboard.sizes.infoIconSize,
dateTimeWidth: dashboard.sizes.dateTimeWidth,
mediaWidth: dashboard.sizes.mediaWidth,
mediaProgressSweep: dashboard.sizes.mediaProgressSweep,
mediaProgressThickness: dashboard.sizes.mediaProgressThickness,
resourceProgessThickness: dashboard.sizes.resourceProgessThickness,
weatherWidth: dashboard.sizes.weatherWidth,
mediaCoverArtSize: dashboard.sizes.mediaCoverArtSize,
mediaVisualiserSize: dashboard.sizes.mediaVisualiserSize,
resourceSize: dashboard.sizes.resourceSize
}
};
}
function serializeDock(): var {
return {
enable: dock.enable,
height: dock.height,
hoverToReveal: dock.hoverToReveal,
pinnedApps: dock.pinnedApps,
pinnedOnStartup: dock.pinnedOnStartup,
ignoredAppRegexes: dock.ignoredAppRegexes
};
}
function serializeGeneral(): var {
return {
logo: general.logo,
wallpaperPath: general.wallpaperPath,
desktopIcons: general.desktopIcons,
dateFormat: general.dateFormat,
color: {
mode: general.color.mode,
smart: general.color.smart,
scheduleDark: general.color.scheduleDark,
scheduleHyprsunset: general.color.scheduleHyprsunset,
scheduleHyprsunsetStart: general.color.scheduleHyprsunsetStart,
hyprsunsetTemp: general.color.hyprsunsetTemp,
scheduleHyprsunsetEnd: general.color.scheduleHyprsunsetEnd,
schemeGeneration: general.color.schemeGeneration,
scheduleDarkStart: general.color.scheduleDarkStart,
scheduleDarkEnd: general.color.scheduleDarkEnd,
neovimColors: general.color.neovimColors
},
apps: {
terminal: general.apps.terminal,
audio: general.apps.audio,
playback: general.apps.playback,
explorer: general.apps.explorer
},
idle: {
timeouts: general.idle.timeouts
},
battery: {
popupThresholds: general.battery.popupThresholds,
critPerc: general.battery.critPerc
}
};
}
function serializeLauncher(): var {
return {
maxAppsShown: launcher.maxAppsShown,
maxWallpapers: launcher.maxWallpapers,
uwsm: launcher.uwsm,
actionPrefix: launcher.actionPrefix,
specialPrefix: launcher.specialPrefix,
useFuzzy: {
apps: launcher.useFuzzy.apps,
actions: launcher.useFuzzy.actions,
schemes: launcher.useFuzzy.schemes,
variants: launcher.useFuzzy.variants,
wallpapers: launcher.useFuzzy.wallpapers
},
sizes: {
itemWidth: launcher.sizes.itemWidth,
itemHeight: launcher.sizes.itemHeight,
wallpaperWidth: launcher.sizes.wallpaperWidth,
wallpaperHeight: launcher.sizes.wallpaperHeight
},
actions: launcher.actions
};
}
function serializeLock(): var {
return {
recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint,
showNotifContent: lock.showNotifContent,
showNotifIcon: lock.showNotifIcon,
maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount,
sizes: {
heightMult: lock.sizes.heightMult,
ratio: lock.sizes.ratio,
centerWidth: lock.sizes.centerWidth
}
};
}
function serializeNotifs(): var {
return {
expire: notifs.expire,
defaultExpireTimeout: notifs.defaultExpireTimeout,
appNotifCooldown: notifs.appNotifCooldown,
clearThreshold: notifs.clearThreshold,
expandThreshold: notifs.expandThreshold,
actionOnClick: notifs.actionOnClick,
groupPreviewNum: notifs.groupPreviewNum,
sizes: {
width: notifs.sizes.width,
image: notifs.sizes.image,
badge: notifs.sizes.badge
}
};
}
function serializeOsd(): var {
return {
enabled: osd.enabled,
hideDelay: osd.hideDelay,
enableBrightness: osd.enableBrightness,
enableMicrophone: osd.enableMicrophone,
allMonBrightness: osd.allMonBrightness,
sizes: {
sliderWidth: osd.sizes.sliderWidth,
sliderHeight: osd.sizes.sliderHeight
}
};
}
function serializeScreenshot(): var {
return {
enable_pp: screenshot.enable_pp,
mode: screenshot.mode,
radius: screenshot.radius,
shadow: screenshot.shadow,
rounding: screenshot.rounding,
shadow_blur: screenshot.shadow_blur,
shadow_color: screenshot.shadow_color,
shadow_offset_x: screenshot.shadow_offset_x,
shadow_offset_y: screenshot.shadow_offset_y
};
}
function serializeServices(): var {
return {
weatherLocation: services.weatherLocation,
updates: services.updates,
useFahrenheit: services.useFahrenheit,
ddcutilService: services.ddcutilService,
useTwelveHourClock: services.useTwelveHourClock,
gpuType: services.gpuType,
audioIncrement: services.audioIncrement,
brightnessIncrement: services.brightnessIncrement,
maxVolume: services.maxVolume,
defaultPlayer: services.defaultPlayer,
playerAliases: services.playerAliases,
visualizerBars: services.visualizerBars
};
}
function serializeSidebar(): var {
return {
enabled: sidebar.enabled,
sizes: {
width: sidebar.sizes.width
}
};
}
function serializeUtilities(): var {
return {
enabled: utilities.enabled,
maxToasts: utilities.maxToasts,
sizes: {
width: utilities.sizes.width,
toastWidth: utilities.sizes.toastWidth
},
toasts: {
configLoaded: utilities.toasts.configLoaded,
chargingChanged: utilities.toasts.chargingChanged,
gameModeChanged: utilities.toasts.gameModeChanged,
dndChanged: utilities.toasts.dndChanged,
audioOutputChanged: utilities.toasts.audioOutputChanged,
audioInputChanged: utilities.toasts.audioInputChanged,
capsLockChanged: utilities.toasts.capsLockChanged,
numLockChanged: utilities.toasts.numLockChanged,
kbLayoutChanged: utilities.toasts.kbLayoutChanged,
vpnChanged: utilities.toasts.vpnChanged,
nowPlaying: utilities.toasts.nowPlaying
},
vpn: {
enabled: utilities.vpn.enabled,
provider: utilities.vpn.provider
}
};
}
ElapsedTimer {
id: timer
}
Timer {
id: saveTimer
interval: 500
onTriggered: {
timer.restart();
try {
let config = {};
try {
config = JSON.parse(fileView.text());
} catch (e) {
config = {};
}
config = root.serializeConfig();
fileView.setText(JSON.stringify(config, null, 4));
} catch (e) {
Toaster.toast(qsTr("Failed to serialize config"), e.message, "settings_alert", Toast.Error);
}
}
}
Timer {
id: recentSaveCooldown
interval: 2000
onTriggered: {
root.recentlySaved = false;
}
}
FileView {
id: fileView
path: "/etc/zshell-greeter/config.json"
watchChanges: true
onFileChanged: {
if (!root.recentlySaved) {
timer.restart();
reload();
} else {
reload();
}
}
onLoadFailed: err => {
if (err !== FileViewError.FileNotFound)
Toaster.toast(qsTr("Failed to read config"), FileViewError.toString(err), "settings_alert", Toast.Warning);
}
onLoaded: {
try {
JSON.parse(text());
const elapsed = timer.elapsedMs();
if (adapter.utilities.toasts.configLoaded && !root.recentlySaved) {
Toaster.toast(qsTr("Config loaded"), qsTr("Config loaded in %1ms").arg(elapsed), "rule_settings");
} else if (adapter.utilities.toasts.configLoaded && root.recentlySaved) {
Toaster.toast(qsTr("Config saved"), qsTr("Config reloaded in %1ms").arg(elapsed), "settings_alert");
}
} catch (e) {
Toaster.toast(qsTr("Failed to load config"), e.message, "settings_alert", Toast.Error);
}
}
onSaveFailed: err => Toaster.toast(qsTr("Failed to save config"), FileViewError.toString(err), "settings_alert", Toast.Error)
JsonAdapter {
id: adapter
property AppearanceConf appearance: AppearanceConf {
}
property BackgroundConfig background: BackgroundConfig {
}
property BarConfig barConfig: BarConfig {
}
property Colors colors: Colors {
}
property DashboardConfig dashboard: DashboardConfig {
}
property DockConfig dock: DockConfig {
}
property General general: General {
}
property Launcher launcher: Launcher {
}
property LockConf lock: LockConf {
}
property NotifConfig notifs: NotifConfig {
}
property Osd osd: Osd {
}
property Overview overview: Overview {
}
property Screenshot screenshot: Screenshot {
}
property Services services: Services {
}
property SidebarConfig sidebar: SidebarConfig {
}
property UtilConfig utilities: UtilConfig {
}
}
}
}
-36
View File
@@ -1,36 +0,0 @@
import Quickshell.Io
JsonObject {
property int dragThreshold: 50
property bool enabled: true
property int mediaUpdateInterval: 500
property Performance performance: Performance {
}
property int resourceUpdateInterval: 1000
property Sizes sizes: Sizes {
}
component Performance: JsonObject {
property bool showBattery: true
property bool showCpu: true
property bool showGpu: true
property bool showMemory: true
property bool showNetwork: true
property bool showStorage: true
}
component Sizes: JsonObject {
readonly property int dateTimeWidth: 110
readonly property int infoIconSize: 25
readonly property int infoWidth: 200
readonly property int mediaCoverArtSize: 150
readonly property int mediaProgressSweep: 180
readonly property int mediaProgressThickness: 8
readonly property int mediaVisualiserSize: 80
readonly property int mediaWidth: 200
readonly property int resourceProgessThickness: 10
readonly property int resourceSize: 200
readonly property int tabIndicatorHeight: 3
readonly property int tabIndicatorSpacing: 5
readonly property int weatherWidth: 250
}
}
-10
View File
@@ -1,10 +0,0 @@
import Quickshell.Io
JsonObject {
property bool enable: false
property real height: 60
property bool hoverToReveal: true
property list<string> ignoredAppRegexes: []
property list<string> pinnedApps: ["org.kde.dolphin", "kitty",]
property bool pinnedOnStartup: false
}
-63
View File
@@ -1,63 +0,0 @@
import Quickshell.Io
import Quickshell
JsonObject {
property Apps apps: Apps {
}
property Battery battery: Battery {
}
property Color color: Color {
}
property string dateFormat: "ddd d MMM - hh:mm:ss"
property bool desktopIcons: false
property Idle idle: Idle {
}
property string logo: ""
property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers"
component Apps: JsonObject {
property list<string> audio: ["pavucontrol"]
property list<string> explorer: ["dolphin"]
property list<string> playback: ["mpv"]
property list<string> terminal: ["kitty"]
}
component Battery: JsonObject {
property int critPerc: 5
property list<var> popupThresholds: [
{
perc: 20,
name: qsTr("Low battery"),
message: qsTr("Battery is low"),
icon: "battery_android_frame_2"
},
]
}
component Color: JsonObject {
property int hyprsunsetTemp: 5000
property string mode: "dark"
property bool neovimColors: false
property bool scheduleDark: false
property int scheduleDarkEnd: 0
property int scheduleDarkStart: 0
property bool scheduleHyprsunset: false
property int scheduleHyprsunsetEnd: 0
property int scheduleHyprsunsetStart: 0
property bool schemeGeneration: true
property bool smart: false
}
component Idle: JsonObject {
property list<var> timeouts: [
{
name: "Lock",
timeout: 180,
idleAction: "lock"
},
{
name: "Screen",
timeout: 300,
idleAction: "dpms off",
activeAction: "dpms on"
}
]
}
}
-15
View File
@@ -1,15 +0,0 @@
import Quickshell.Io
JsonObject {
property list<var> timeouts: [
{
timeout: 180,
idleAction: "lock"
},
{
timeout: 300,
idleAction: "dpms off",
activeAction: "dpms on"
}
]
}
-109
View File
@@ -1,109 +0,0 @@
import Quickshell.Io
JsonObject {
property string actionPrefix: ">"
property list<var> actions: [
{
name: "Calculator",
icon: "calculate",
description: "Do simple math equations",
command: ["autocomplete", "calc"],
enabled: true,
dangerous: false
},
{
name: "Light",
icon: "light_mode",
description: "Change to light mode",
command: ["setMode", "light"],
enabled: true,
dangerous: false
},
{
name: "Dark",
icon: "dark_mode",
description: "Change to dark mode",
command: ["setMode", "dark"],
enabled: true,
dangerous: false
},
{
name: "Wallpaper",
icon: "image",
description: "Change the current wallpaper",
command: ["autocomplete", "wallpaper"],
enabled: true,
dangerous: false
},
{
name: "Variant",
icon: "colors",
description: "Change the current scheme variant",
command: ["autocomplete", "variant"],
enabled: true,
dangerous: false
},
{
name: "Shutdown",
icon: "power_settings_new",
description: "Shutdown the system",
command: ["systemctl", "poweroff"],
enabled: true,
dangerous: true
},
{
name: "Reboot",
icon: "cached",
description: "Reboot the system",
command: ["systemctl", "reboot"],
enabled: true,
dangerous: true
},
{
name: "Logout",
icon: "logout",
description: "Log out of the current session",
command: ["loginctl", "terminate-user", ""],
enabled: true,
dangerous: true
},
{
name: "Lock",
icon: "lock",
description: "Lock the current session",
command: ["loginctl", "lock-session"],
enabled: true,
dangerous: false
},
{
name: "Sleep",
icon: "bedtime",
description: "Suspend then hibernate",
command: ["systemctl", "suspend-then-hibernate"],
enabled: true,
dangerous: false
},
]
property int maxAppsShown: 10
property int maxWallpapers: 7
property Sizes sizes: Sizes {
}
property string specialPrefix: "@"
property UseFuzzy useFuzzy: UseFuzzy {
}
property bool uwsm: true
component Sizes: JsonObject {
property int itemHeight: 50
property int itemWidth: 600
property int wallpaperHeight: 200
property int wallpaperWidth: 280
}
component UseFuzzy: JsonObject {
property bool actions: false
property bool apps: false
property bool schemes: false
property bool variants: false
property bool wallpapers: false
}
}
-18
View File
@@ -1,18 +0,0 @@
import Quickshell.Io
JsonObject {
property int blurAmount: 40
property bool enableFprint: true
property int maxFprintTries: 3
property bool recolorLogo: false
property bool showNotifContent: false
property bool showNotifIcon: true
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int centerWidth: 600
property real heightMult: 0.7
property real ratio: 16 / 9
}
}
-26
View File
@@ -1,26 +0,0 @@
pragma Singleton
import Quickshell
Singleton {
id: root
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
readonly property int emphasizedAccelTime: 200 * scale
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
readonly property int emphasizedDecelTime: 400 * scale
readonly property int emphasizedTime: 500 * scale
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1]
readonly property int expressiveDefaultSpatialTime: 500 * scale
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1]
readonly property int expressiveEffectsTime: 200 * scale
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1]
readonly property int expressiveFastSpatialTime: 350 * scale
property real scale: Tokens.anim.durations.scale
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
readonly property int standardAccelTime: 200 * scale
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
readonly property int standardDecelTime: 250 * scale
readonly property int standardTime: 300 * scale
}
-20
View File
@@ -1,20 +0,0 @@
import Quickshell.Io
JsonObject {
property bool actionOnClick: false
property int appNotifCooldown: 0
property real clearThreshold: 0.3
property int defaultExpireTimeout: 5000
property int expandThreshold: 20
property bool expire: true
property int groupPreviewNum: 3
property bool openExpanded: false
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int badge: 20
property int image: 41
property int width: 400
}
}
-16
View File
@@ -1,16 +0,0 @@
import Quickshell.Io
JsonObject {
property bool allMonBrightness: false
property bool enableBrightness: true
property bool enableMicrophone: true
property bool enabled: true
property int hideDelay: 3000
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int sliderHeight: 150
property int sliderWidth: 30
}
}
-8
View File
@@ -1,8 +0,0 @@
import Quickshell.Io
JsonObject {
property int columns: 5
property bool enable: false
property int rows: 2
property real scale: 0.16
}
-13
View File
@@ -1,13 +0,0 @@
import Quickshell.Io
JsonObject {
property bool enable_pp: true
property string mode: "manual"
property real radius: 12.0
property bool rounding: false
property bool shadow: true
property real shadow_blur: 22.0
property list<int> shadow_color: [0, 0, 0, 160]
property real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0
}
-22
View File
@@ -1,22 +0,0 @@
import Quickshell.Io
import QtQuick
JsonObject {
property real audioIncrement: 0.1
property real brightnessIncrement: 0.1
property bool ddcutilService: false
property string defaultPlayer: "Spotify"
property string gpuType: ""
property real maxVolume: 1.0
property list<var> playerAliases: [
{
"from": "com.github.th_ch.youtube_music",
"to": "YT Music"
}
]
property bool updates: true
property bool useFahrenheit: false
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property int visualizerBars: 30
property string weatherLocation: ""
}
-11
View File
@@ -1,11 +0,0 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int width: 430
}
}
-7
View File
@@ -1,7 +0,0 @@
import Quickshell.Io
JsonObject {
property real base: 0.85
property bool enabled: false
property real layers: 0.4
}
-35
View File
@@ -1,35 +0,0 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int maxToasts: 4
property Sizes sizes: Sizes {
}
property Toasts toasts: Toasts {
}
property Vpn vpn: Vpn {
}
component Sizes: JsonObject {
property int toastWidth: 430
property int width: 430
}
component Toasts: JsonObject {
property bool audioInputChanged: true
property bool audioOutputChanged: true
property bool capsLockChanged: true
property bool chargingChanged: true
property bool configLoaded: true
property bool dndChanged: true
property bool gameModeChanged: true
property bool kbLayoutChanged: true
property bool kbLimit: true
property bool nowPlaying: false
property bool numLockChanged: true
property bool vpnChanged: true
}
component Vpn: JsonObject {
property bool enabled: false
property list<var> provider: ["netbird"]
}
}
-6
View File
@@ -1,6 +0,0 @@
import Quickshell.Io
JsonObject {
property string inactiveTextColor: "white"
property string textColor: "black"
}
-264
View File
@@ -1,264 +0,0 @@
pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Io
import QtQuick
import ZShell.Config
import qs.Components
Singleton {
id: root
property bool appleDisplayPresent: false
property list<var> ddcMonitors: []
property list<var> ddcServiceMon: []
readonly property list<Monitor> monitors: variants.instances
function decreaseBrightness(): void {
const monitor = getMonitor("active");
if (monitor)
monitor.setBrightness(monitor.brightness - Config.services.brightnessIncrement);
}
function getMonitor(query: string): var {
if (query === "active") {
return monitors.find(m => Hypr.monitorFor(m.modelData)?.focused);
}
if (query.startsWith("model:")) {
const model = query.slice(6);
return monitors.find(m => m.modelData.model === model);
}
if (query.startsWith("serial:")) {
const serial = query.slice(7);
return monitors.find(m => m.modelData.serialNumber === serial);
}
if (query.startsWith("id:")) {
const id = parseInt(query.slice(3), 10);
return monitors.find(m => Hypr.monitorFor(m.modelData)?.id === id);
}
return monitors.find(m => m.modelData.name === query);
}
function getMonitorForScreen(screen: ShellScreen): var {
return monitors.find(m => m.modelData === screen);
}
function increaseBrightness(): void {
const monitor = getMonitor("active");
if (monitor)
monitor.setBrightness(monitor.brightness + Config.services.brightnessIncrement);
}
onMonitorsChanged: {
ddcMonitors = [];
ddcServiceMon = [];
ddcServiceProc.running = true;
ddcProc.running = true;
}
Variants {
id: variants
model: Quickshell.screens
Monitor {
}
}
Process {
command: ["sh", "-c", "asdbctl get"]
running: true
stdout: StdioCollector {
onStreamFinished: root.appleDisplayPresent = text.trim().length > 0
}
}
Process {
id: ddcProc
command: ["ddcutil", "detect", "--brief"]
stdout: StdioCollector {
onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({
busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1],
connector: d.match(/DRM connector:\s+(.*)/)[1].replace(/^card\d+-/, "") // strip "card1-"
}))
}
}
Process {
id: ddcServiceProc
command: ["ddcutil-client", "detect"]
// running: true
stdout: StdioCollector {
onStreamFinished: {
const t = text.replace(/\r\n/g, "\n").trim();
const output = ("\n" + t).split(/\n(?=display:\s*\d+\s*\n)/).filter(b => b.startsWith("display:")).map(b => ({
display: Number(b.match(/^display:\s*(\d+)/m)?.[1] ?? -1),
name: (b.match(/^\s*product_name:\s*(.*)$/m)?.[1] ?? "").trim()
})).filter(d => d.display > 0);
root.ddcServiceMon = output;
}
}
}
CustomShortcut {
description: "Increase brightness"
name: "brightnessUp"
onPressed: root.increaseBrightness()
}
CustomShortcut {
description: "Decrease brightness"
name: "brightnessDown"
onPressed: root.decreaseBrightness()
}
IpcHandler {
function get(): real {
return getFor("active");
}
// Allows searching by active/model/serial/id/name
function getFor(query: string): real {
return root.getMonitor(query)?.brightness ?? -1;
}
function set(value: string): string {
return setFor("active", value);
}
// Handles brightness value like brightnessctl: 0.1, +0.1, 0.1-, 10%, +10%, 10%-
function setFor(query: string, value: string): string {
const monitor = root.getMonitor(query);
if (!monitor)
return "Invalid monitor: " + query;
let targetBrightness;
if (value.endsWith("%-")) {
const percent = parseFloat(value.slice(0, -2));
targetBrightness = monitor.brightness - (percent / 100);
} else if (value.startsWith("+") && value.endsWith("%")) {
const percent = parseFloat(value.slice(1, -1));
targetBrightness = monitor.brightness + (percent / 100);
} else if (value.endsWith("%")) {
const percent = parseFloat(value.slice(0, -1));
targetBrightness = percent / 100;
} else if (value.startsWith("+")) {
const increment = parseFloat(value.slice(1));
targetBrightness = monitor.brightness + increment;
} else if (value.endsWith("-")) {
const decrement = parseFloat(value.slice(0, -1));
targetBrightness = monitor.brightness - decrement;
} else if (value.includes("%") || value.includes("-") || value.includes("+")) {
return `Invalid brightness format: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`;
} else {
targetBrightness = parseFloat(value);
}
if (isNaN(targetBrightness))
return `Failed to parse value: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`;
monitor.setBrightness(targetBrightness);
return `Set monitor ${monitor.modelData.name} brightness to ${+monitor.brightness.toFixed(2)}`;
}
target: "brightness"
}
component Monitor: QtObject {
id: monitor
property real brightness
readonly property string busNum: root.ddcMonitors.find(m => m.connector === modelData.name)?.busNum ?? ""
readonly property string displayNum: root.ddcServiceMon.find(m => m.name === modelData.model)?.display ?? ""
readonly property Process initProc: Process {
stdout: StdioCollector {
onStreamFinished: {
if (monitor.isDdcService) {
const output = text.split("\n").filter(o => o.startsWith("vcp_current_value:"))[0].split(":")[1];
const val = parseInt(output.trim());
monitor.brightness = val / 100;
} else if (monitor.isAppleDisplay) {
const val = parseInt(text.trim());
monitor.brightness = val / 101;
} else {
const [, , , cur, max] = text.split(" ");
monitor.brightness = parseInt(cur) / parseInt(max);
}
}
}
}
readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay")
readonly property bool isDdc: root.ddcMonitors.some(m => m.connector === modelData.name)
readonly property bool isDdcService: Config.services.ddcutilService
required property ShellScreen modelData
property real queuedBrightness: NaN
readonly property Timer timer: Timer {
interval: 500
onTriggered: {
if (!isNaN(monitor.queuedBrightness)) {
monitor.setBrightness(monitor.queuedBrightness);
monitor.queuedBrightness = NaN;
}
}
}
function initBrightness(): void {
if (isDdcService)
initProc.command = ["ddcutil-client", "-d", displayNum, "getvcp", "10"];
else if (isAppleDisplay)
initProc.command = ["asdbctl", "get"];
else if (isDdc)
initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
else
initProc.command = ["sh", "-c", "echo a b c $(brightnessctl g) $(brightnessctl m)"];
initProc.running = true;
}
function setBrightness(value: real): void {
value = Math.max(0, Math.min(1, value));
const rounded = Math.round(value * 100);
if (Math.round(brightness * 100) === rounded)
return;
if ((isDdc || isDdcService) && timer.running) {
queuedBrightness = value;
return;
}
brightness = value;
if (isDdcService)
Quickshell.execDetached(["ddcutil-client", "-d", displayNum, "setvcp", "10", rounded]);
else if (isAppleDisplay)
Quickshell.execDetached(["asdbctl", "set", rounded]);
else if (isDdc)
Quickshell.execDetached(["ddcutil", "--disable-dynamic-sleep", "--sleep-multiplier", ".1", "--skip-ddc-checks", "-b", busNum, "setvcp", "10", rounded]);
else
Quickshell.execDetached(["brightnessctl", "s", `${rounded}%`]);
if (isDdc || isDdcService)
timer.restart();
}
Component.onCompleted: initBrightness()
onBusNumChanged: initBrightness()
onDisplayNumChanged: initBrightness()
}
}
-8
View File
@@ -149,15 +149,7 @@ Singleton {
target: "hypr"
}
CustomShortcut {
name: "refreshDevices"
onPressed: extras.refreshDevices()
onReleased: extras.refreshDevices()
}
HyprExtras {
id: extras
}
}
-83
View File
@@ -1,83 +0,0 @@
pragma Singleton
import ZShell.Config
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property bool isDefaultLogo: true
property string osId
property list<string> osIdLike
property string osLogo
property string osName
property string osPrettyName
readonly property string shell: Quickshell.env("SHELL").split("/").pop()
property string uptime
readonly property string user: Quickshell.env("USER")
readonly property string wm: Quickshell.env("XDG_CURRENT_DESKTOP") || Quickshell.env("XDG_SESSION_DESKTOP")
FileView {
id: osRelease
path: "/etc/os-release"
onLoaded: {
const lines = text().split("\n");
const fd = key => lines.find(l => l.startsWith(`${key}=`))?.split("=")[1].replace(/"/g, "") ?? "";
root.osName = fd("NAME");
root.osPrettyName = fd("PRETTY_NAME");
root.osId = fd("ID");
root.osIdLike = fd("ID_LIKE").split(" ");
const logo = Quickshell.iconPath(fd("LOGO"), true);
if (Config.general.logo) {
root.osLogo = Quickshell.iconPath(Config.general.logo, true) || "file://" + Paths.absolutePath(Config.general.logo);
root.isDefaultLogo = false;
} else if (logo) {
root.osLogo = logo;
root.isDefaultLogo = false;
}
}
}
Connections {
function onLogoChanged(): void {
osRelease.reload();
}
target: Config.general
}
Timer {
interval: 15000
repeat: true
running: true
onTriggered: fileUptime.reload()
}
FileView {
id: fileUptime
path: "/proc/uptime"
onLoaded: {
const up = parseInt(text().split(" ")[0] ?? 0);
const hours = Math.floor(up / 3600);
const minutes = Math.floor((up % 3600) / 60);
let str = "";
if (hours > 0)
str += `${hours} hour${hours === 1 ? "" : "s"}`;
if (minutes > 0 || !str)
str += `${str ? ", " : ""}${minutes} minute${minutes === 1 ? "" : "s"}`;
root.uptime = str;
}
}
}
+3 -4
View File
@@ -3,9 +3,8 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Components
import qs.Services
Item {
@@ -40,7 +39,7 @@ Item {
anchors.centerIn: parent
animate: true
color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
opacity: root.buffer ? 0 : 1
text: {
@@ -65,7 +64,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurface
elide: Text.ElideLeft
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Qt.AlignHCenter
opacity: root.greeter.echoResponse && root.buffer ? 1 : 0
@@ -3,8 +3,10 @@ pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
import ZShell
import ZShell.Config
import qs.Helpers
import qs.Paths
@@ -79,6 +81,35 @@ Singleton {
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1);
}
function reloadHyprRules(): void {
const blur = transparency.enabled ? 1 : 0;
const alpha = transparency.base - 0.03;
const rules = `
hl.layer_rule({
match = { namespace = "ZShell-Bar" },
blur = ${blur}
})
hl.layer_rule({
match = { namespace = "ZShell-Bar" },
ignore_alpha = ${alpha}
})
hl.layer_rule({
match = { namespace = "ZShell-Auth" },
blur = ${blur}
})
hl.layer_rule({
match = { namespace = "ZShell-Auth" },
ignore_alpha = ${alpha}
})
`;
Hypr.extras.message(`eval ${rules}`);
}
function setMode(mode: string): void {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
Config.general.color.mode = mode;
@@ -89,6 +120,24 @@ Singleton {
return Qt.rgba(c.g, c.r, c.b, c.a);
}
Component.onCompleted: debounceTimer.triggered()
Connections {
function onUsingLuaChanged(): void {
root.reloadHyprRules();
}
target: Hyprland
}
Connections {
function onConfigReloaded(): void {
root.reloadHyprRules();
}
target: Hypr
}
FileView {
path: "/etc/zshell-greeter/scheme.json"
watchChanges: true
@@ -97,6 +146,14 @@ Singleton {
onLoaded: root.load(text(), false)
}
Timer {
id: debounceTimer
interval: 300
onTriggered: root.reloadHyprRules()
}
ImageAnalyser {
id: analyser
@@ -224,9 +281,9 @@ Singleton {
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
}
component Transparency: QtObject {
readonly property real base: Math.max(0, Math.min(1, Appearance.transparency.base - (root.light ? 0.1 : 0)))
readonly property bool enabled: Appearance.transparency.enabled
readonly property real layers: Appearance.transparency.layers
readonly property real base: Math.max(0, Math.min(1, Config.appearance.transparency.base - (root.light ? 0.1 : 0)))
readonly property bool enabled: Config.appearance.transparency.enabled
readonly property real layers: Config.appearance.transparency.layers
onBaseChanged: debounceTimer.restart()
onEnabledChanged: debounceTimer.restart()
+4 -5
View File
@@ -2,9 +2,8 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Components
import qs.Services
ColumnLayout {
@@ -20,7 +19,7 @@ ColumnLayout {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.weight: 500
text: root.greeter.sessions.length > 0 ? qsTr("%1 session%2").arg(root.greeter.sessions.length).arg(root.greeter.sessions.length === 1 ? "" : "s") : qsTr("Sessions")
}
@@ -54,7 +53,7 @@ ColumnLayout {
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Sessions Found")
@@ -119,7 +118,7 @@ ColumnLayout {
Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
text: modelData.kind
}
+3 -3
View File
@@ -2,9 +2,9 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
ColumnLayout {
@@ -20,7 +20,7 @@ ColumnLayout {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.weight: 500
text: root.greeter.users.length > 0 ? qsTr("%1 user%2").arg(root.greeter.users.length).arg(root.greeter.users.length === 1 ? "" : "s") : qsTr("Users")
}
@@ -55,7 +55,7 @@ ColumnLayout {
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font.family: Appearance.font.family.mono
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Users Found")
+1 -1
View File
@@ -2,9 +2,9 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
ColumnLayout {
+1 -1
View File
@@ -33,7 +33,7 @@ Singleton {
function getIdentity(player: MprisPlayer): string {
if (!player)
return "";
const alias = Config.services.playerAliases.find(a => a.from === player.identity);
const alias = Config.services.playerAliases.values.find(a => a.from === player.identity);
return alias?.to ?? player.identity;
}
+1 -1
View File
@@ -74,7 +74,7 @@ RowLayout {
id: repeater
model: ScriptModel {
values: Config.bar.entries.filter(e => e.enabled)
values: Config.bar.entries.values.filter(e => e.enabled)
}
DelegateChooser {
+1 -1
View File
@@ -20,7 +20,7 @@ Searcher {
Variants {
id: variants
model: Config.launcher.actions.filter(a => (a.enabled ?? true) && (Config.launcher.enableDangerousActions || !(a.dangerous ?? false)))
model: Config.launcher.actions.values.filter(a => (a.enabled ?? true) && (Config.launcher.enableDangerousActions || !(a.dangerous ?? false)))
Action {
}
-162
View File
@@ -1,162 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell.Services.UPower
import QtQuick
import QtQuick.Layouts
import qs.Modules
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Services
ColumnLayout {
id: root
anchors.fill: parent
anchors.margins: Tokens.padding.large * 2
anchors.topMargin: Tokens.padding.large
spacing: Tokens.spacing.small
RowLayout {
Layout.fillHeight: false
Layout.fillWidth: true
spacing: Tokens.spacing.normal
CustomRect {
color: Colors.palette.m3primary
implicitHeight: prompt.implicitHeight + Tokens.padding.normal * 2
implicitWidth: prompt.implicitWidth + Tokens.padding.normal * 2
radius: Tokens.rounding.small
MonoText {
id: prompt
anchors.centerIn: parent
color: Colors.palette.m3onPrimary
font.pointSize: root.width > 400 ? Tokens.font.size.larger : Tokens.font.size.normal
text: ">"
}
}
MonoText {
Layout.fillWidth: true
elide: Text.ElideRight
font.pointSize: root.width > 400 ? Tokens.font.size.larger : Tokens.font.size.normal
text: "caelestiafetch.sh"
}
WrappedLoader {
Layout.fillHeight: true
active: !iconLoader.active
sourceComponent: OsLogo {
}
}
}
RowLayout {
Layout.fillHeight: false
Layout.fillWidth: true
spacing: height * 0.15
WrappedLoader {
id: iconLoader
Layout.fillHeight: true
active: root.width > 320
sourceComponent: OsLogo {
}
}
ColumnLayout {
Layout.bottomMargin: Tokens.padding.normal
Layout.fillWidth: true
Layout.leftMargin: iconLoader.active ? 0 : width * 0.1
Layout.topMargin: Tokens.padding.normal
spacing: Tokens.spacing.normal
WrappedLoader {
Layout.fillWidth: true
active: !batLoader.active && root.height > 200
sourceComponent: FetchText {
text: `OS : ${SystemInfo.osPrettyName || SysInfo.osName}`
}
}
WrappedLoader {
Layout.fillWidth: true
active: root.height > (batLoader.active ? 200 : 110)
sourceComponent: FetchText {
text: `WM : ${SystemInfo.wm}`
}
}
WrappedLoader {
Layout.fillWidth: true
active: !batLoader.active || root.height > 110
sourceComponent: FetchText {
text: `USER: ${SystemInfo.user}`
}
}
FetchText {
text: `UP : ${SystemInfo.uptime}`
}
WrappedLoader {
id: batLoader
Layout.fillWidth: true
active: UPower.displayDevice.isLaptopBattery
sourceComponent: FetchText {
text: `BATT: ${[UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state) ? "(+) " : ""}${Math.round(UPower.displayDevice.percentage * 100)}%`
}
}
}
}
WrappedLoader {
Layout.alignment: Qt.AlignHCenter
active: root.height > 180
sourceComponent: RowLayout {
spacing: Tokens.spacing.large
Repeater {
model: Math.max(0, Math.min(8, root.width / (Tokens.font.size.larger * 2 + Tokens.spacing.large)))
CustomRect {
required property int index
color: Colors.palette[`term${index}`]
implicitHeight: Tokens.font.size.larger * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.small
}
}
}
}
component FetchText: MonoText {
Layout.fillWidth: true
elide: Text.ElideRight
font.pointSize: root.width > 400 ? Tokens.font.size.larger : Tokens.font.size.normal
}
component MonoText: CustomText {
font.family: Appearance.font.family.mono
}
component OsLogo: ColoredIcon {
color: Colors.palette.m3primary
implicitSize: height
layer.enabled: Config.lock.recolorLogo || SystemInfo.isDefaultLogo
source: SystemInfo.osLogo
}
component WrappedLoader: Loader {
visible: active
}
}
+3 -2
View File
@@ -171,10 +171,11 @@ ListView {
item.held = true;
itemContent.x = Qt.binding(() => {
if (!root)
if (!root) {
return 0;
}
const maxOvershoot = Tokens.padding.extraExtraLarge;
const maxOvershoot = Tokens.padding.extraLargeIncreased;
const x = mouse.mouseX - item.pressPos.x;
return root.dampOvershoot(Math.abs(x), maxOvershoot) * Math.sign(x);
});
@@ -1,5 +1,3 @@
pragma ComponentBehavior: Bound
import QtQuick.Layouts
import ZShell.Config
import qs.Modules.Settings.Common
@@ -42,42 +40,24 @@ PageBase {
}
first: true
values: Config.bar.tray.statusIcons
values: Config.bar.tray.statusIcons.values
z: 1
onItemMoved: (from, to) => {
const icons = Config.bar.tray.statusIcons.slice();
const [moved] = icons.splice(from, 1);
icons.splice(to, 0, moved);
Config.bar.tray.statusIcons = icons;
}
onItemRemoved: index => {
const icons = Config.bar.tray.statusIcons.slice();
icons.splice(index, 1);
Config.bar.tray.statusIcons = icons;
}
onItemToggled: (index, checked) => {
const icons = Config.bar.tray.statusIcons.slice();
icons[index] = Object.assign({}, icons[index], {
enabled: checked
});
Config.bar.tray.statusIcons = icons;
}
onItemMoved: (from, to) => Config.bar.tray.statusIcons.move(from, to)
onItemRemoved: index => Config.bar.tray.statusIcons.remove(index)
onItemToggled: (index, checked) => Config.bar.tray.statusIcons.at(index).enabled = checked
}
DialogSelectButton {
id: addItemContainer
acceptLabel: qsTr("Add")
enabled: {
console.log(Object.keys(model).length);
return Object.keys(model).length > 0;
}
enabled: Object.keys(model).length > 0
header: qsTr("Add new entry")
icon: "add"
label: qsTr("Add entry")
model: {
const present = new Set(Config.bar.tray.statusIcons.map(item => item.id));
const present = new Set(Config.bar.tray.statusIcons.values.map(item => item.id));
return Object.keys(root.builtinIcons).filter(id => !present.has(id)).map(id => ({
id: id,
label: root.builtinIcons[id]
@@ -89,12 +69,10 @@ PageBase {
if (!selectedItem)
return;
const icons = Config.bar.tray.statusIcons.slice();
icons.push({
Config.bar.tray.statusIcons.insert({
id: selectedItem,
enabled: true
});
Config.bar.tray.statusIcons = icons;
}
}
@@ -106,6 +84,7 @@ PageBase {
ToggleRow {
checked: Config.bar.popouts.statusIcons
first: true
last: true
settingAnchor: "bar-status-popout-on-hover"
subtext: qsTr("Show a details popout when hovering the status icons")
text: qsTr("Popout on hover")
+1 -1
View File
@@ -56,7 +56,7 @@ CustomClippingRect {
model: ScriptModel {
id: model
values: Config.bar.tray.statusIcons.filter(e => e.enabled)
values: Config.bar.tray.statusIcons.values.filter(e => e.enabled)
}
DelegateChooser {
-40
View File
@@ -1,40 +0,0 @@
#include "Appearance.hpp"
Anim::Anim(QObject *parent) : ConfigSection(parent) {
m_mediaGifSpeedAdjustment = 300;
m_sessionGifSpeed = 0.7;
wireSignals();
}
Deform::Deform(QObject *parent) : ConfigSection(parent) {
m_scale = 1;
wireSignals();
}
FontFamily::FontFamily(QObject *parent) : ConfigSection(parent) {
m_clock = QStringLiteral("Rubik");
m_material = QStringLiteral("Material Symbols Rounded");
m_mono = QStringLiteral("SegoeUI Variable");
m_sans = QStringLiteral("SegoeUI Variable");
wireSignals();
}
Font::Font(QObject *parent) : ConfigSection(parent) {
m_family = new FontFamily(this);
wireSignals();
}
Transparency::Transparency(QObject *parent) : ConfigSection(parent) {
m_base = 0.75;
m_enabled = true;
m_layers = 0.4;
wireSignals();
}
Appearance::Appearance(QObject *parent) : ConfigSection(parent) {
m_anim = new Anim(this);
m_deform = new Deform(this);
m_font = new Font(this);
m_transparency = new Transparency(this);
wireSignals();
}
-78
View File
@@ -1,78 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
// NOTE: multiple classes per file is fine - moc processes every Q_OBJECT
// class it finds in a header, regardless of how many. Config.appearance.anim
// works purely through object composition (Appearance owns an Anim*), which
// has nothing to do with how the files are split.
class Anim : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(int, mediaGifSpeedAdjustment, MediaGifSpeedAdjustment)
CFG_PROPERTY(qreal, sessionGifSpeed, SessionGifSpeed)
public:
explicit Anim(QObject *parent = nullptr);
};
class Deform : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(qreal, scale, Scale)
public:
explicit Deform(QObject *parent = nullptr);
};
class FontFamily : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(QString, clock, Clock)
CFG_PROPERTY(QString, material, Material)
CFG_PROPERTY(QString, mono, Mono)
CFG_PROPERTY(QString, sans, Sans)
public:
explicit FontFamily(QObject *parent = nullptr);
};
class Font : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_SECTION(FontFamily, family, Family)
public:
explicit Font(QObject *parent = nullptr);
};
class Transparency : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(qreal, base, Base)
CFG_PROPERTY(bool, enabled, Enabled)
CFG_PROPERTY(qreal, layers, Layers)
public:
explicit Transparency(QObject *parent = nullptr);
};
class Appearance : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_SECTION(Anim, anim, Anim)
CFG_SECTION(Deform, deform, Deform)
CFG_SECTION(Font, font, Font)
CFG_SECTION(Transparency, transparency, Transparency)
public:
explicit Appearance(QObject *parent = nullptr);
};
-7
View File
@@ -1,7 +0,0 @@
#include "Background.hpp"
Background::Background(QObject *parent) : ConfigSection(parent) {
m_enabled = true;
m_wallFadeDuration = 300;
wireSignals();
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
class Background : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, enabled, Enabled)
CFG_PROPERTY(int, wallFadeDuration, WallFadeDuration)
public:
explicit Background(QObject *parent = nullptr);
};
-63
View File
@@ -1,63 +0,0 @@
#include "Bar.hpp"
namespace {
QVariantMap entry(bool enabled, const char *id) {
QVariantMap m;
m["enabled"] = enabled;
m["id"] = QString::fromLatin1(id);
return m;
}
}
Popouts::Popouts(QObject *parent) : ConfigSection(parent) {
m_statusIcons = true;
m_tray = true;
wireSignals();
}
Tray::Tray(QObject *parent) : ConfigSection(parent) {
m_recolorIcons = false;
m_showOnHover = true;
m_statusIcons = {
entry(true, "audio"),
entry(true, "microphone"),
entry(true, "bluetooth"),
entry(false, "network"),
entry(false, "wifi"),
entry(true, "power"),
};
m_trayIconSize = 24;
wireSignals();
}
Bar::Bar(QObject *parent) : ConfigSection(parent) {
m_autoHide = false;
m_border = 4;
m_entries = {
entry(true, "workspaces"),
entry(true, "audio"),
entry(true, "media"),
entry(true, "resources"),
entry(true, "updates"),
entry(false, "dash"),
entry(true, "spacer"),
entry(true, "activeWindow"),
entry(true, "spacer"),
entry(true, "hyprsunset"),
entry(true, "tray"),
entry(true, "statusIcons"),
entry(false, "network"),
entry(true, "upower"),
entry(true, "clock"),
entry(true, "notifBell"),
};
m_fullscreenReveal = true;
m_height = 24;
m_hideWhenNotif = true;
m_popouts = new Popouts(this);
m_revealDelay = 100;
m_rounding = 14;
m_smoothing = 32;
m_tray = new Tray(this);
wireSignals();
}
-52
View File
@@ -1,52 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
#include <QVariantList>
class Popouts : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, statusIcons, StatusIcons)
CFG_PROPERTY(bool, tray, Tray)
public:
explicit Popouts(QObject *parent = nullptr);
};
class Tray : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, recolorIcons, RecolorIcons)
CFG_PROPERTY(bool, showOnHover, ShowOnHover)
// Array of { enabled, id } objects. QML can't mutate array elements in
// place - assign the whole list back to change it, e.g.
// Config.bar.tray.statusIcons = updatedList
CFG_PROPERTY(QVariantList, statusIcons, StatusIcons)
CFG_PROPERTY(int, trayIconSize, TrayIconSize)
public:
explicit Tray(QObject *parent = nullptr);
};
class Bar : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, autoHide, AutoHide)
CFG_PROPERTY(int, border, Border)
// Array of { enabled, id } objects, in display order.
CFG_PROPERTY(QVariantList, entries, Entries)
CFG_PROPERTY(bool, fullscreenReveal, FullscreenReveal)
CFG_PROPERTY(int, height, Height)
CFG_PROPERTY(bool, hideWhenNotif, HideWhenNotif)
CFG_SECTION(Popouts, popouts, Popouts)
CFG_PROPERTY(int, revealDelay, RevealDelay)
CFG_PROPERTY(int, rounding, Rounding)
CFG_PROPERTY(int, smoothing, Smoothing)
CFG_SECTION(Tray, tray, Tray)
public:
explicit Bar(QObject *parent = nullptr);
};
+21 -20
View File
@@ -1,24 +1,25 @@
qml_module(ZShell-config
URI ZShell.Config
SOURCES
ConfigSection.hpp ConfigSection.cpp
Config.hpp Config.cpp
Tokens.hpp Tokens.cpp
Appearance.hpp Appearance.cpp
Background.hpp Background.cpp
Bar.hpp Bar.cpp
Clipboard.hpp Clipboard.cpp
Colors.hpp Colors.cpp
Dashboard.hpp Dashboard.cpp
Dock.hpp Dock.cpp
General.hpp General.cpp
Launcher.hpp Launcher.cpp
Lock.hpp Lock.cpp
Notifs.hpp Notifs.cpp
Osd.hpp Osd.cpp
Overview.hpp Overview.cpp
Screenshot.hpp Screenshot.cpp
Services.hpp Services.cpp
Sidebar.hpp Sidebar.cpp
Utilities.hpp Utilities.cpp
confignode.hpp confignode.cpp
configobject.hpp configobject.cpp
configlist.hpp configlist.cpp
config.hpp config.cpp
tokens.hpp
appearance.hpp
background.hpp
bar.hpp
clipboard.hpp
colors.hpp
dashboard.hpp
dock.hpp
general.hpp
launcher.hpp
lock.hpp
notifs.hpp
osd.hpp
screenshot.hpp
services.hpp
sidebar.hpp
utilities.hpp
)
-16
View File
@@ -1,16 +0,0 @@
#include "Clipboard.hpp"
ClipboardSizes::ClipboardSizes(QObject *parent) : ConfigSection(parent) {
m_itemHeight = 60;
m_minPreviewWidth = 200;
m_previewWidth = 800;
m_width = 500;
wireSignals();
}
Clipboard::Clipboard(QObject *parent) : ConfigSection(parent) {
m_enabled = true;
m_maxEntriesShown = 10;
m_sizes = new ClipboardSizes(this);
wireSignals();
}
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
class ClipboardSizes : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(int, itemHeight, ItemHeight)
CFG_PROPERTY(int, minPreviewWidth, MinPreviewWidth)
CFG_PROPERTY(int, previewWidth, PreviewWidth)
CFG_PROPERTY(int, width, Width)
public:
explicit ClipboardSizes(QObject *parent = nullptr);
};
class Clipboard : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, enabled, Enabled)
CFG_PROPERTY(int, maxEntriesShown, MaxEntriesShown)
CFG_SECTION(ClipboardSizes, sizes, Sizes)
public:
explicit Clipboard(QObject *parent = nullptr);
};
-14
View File
@@ -1,14 +0,0 @@
#include "Colors.hpp"
Presets::Presets(QObject *parent) : ConfigSection(parent) {
m_accent = QString();
m_name = QStringLiteral("Catppuccin");
m_variant = QStringLiteral("latte");
wireSignals();
}
Colors::Colors(QObject *parent) : ConfigSection(parent) {
m_presets = new Presets(this);
m_schemeType = QStringLiteral("fidelity");
wireSignals();
}
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
class Presets : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(QString, accent, Accent)
CFG_PROPERTY(QString, name, Name)
CFG_PROPERTY(QString, variant, Variant)
public:
explicit Presets(QObject *parent = nullptr);
};
class Colors : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_SECTION(Presets, presets, Presets)
CFG_PROPERTY(QString, schemeType, SchemeType)
public:
explicit Colors(QObject *parent = nullptr);
};
-86
View File
@@ -1,86 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
#include <QTimer>
#include <QtConcurrent/QtConcurrent>
#include <QFuture>
#include "Appearance.hpp"
#include "Background.hpp"
#include "Bar.hpp"
#include "Clipboard.hpp"
#include "Colors.hpp"
#include "Dashboard.hpp"
#include "Dock.hpp"
#include "General.hpp"
#include "Launcher.hpp"
#include "Lock.hpp"
#include "Notifs.hpp"
#include "Osd.hpp"
#include "Overview.hpp"
#include "Screenshot.hpp"
#include "Services.hpp"
#include "Sidebar.hpp"
#include "Utilities.hpp"
class QQmlEngine;
class QJSEngine;
// The central hub. Exposed to QML as a singleton, so `Config.appearance.
// anim.sessionGifSpeed` just works from anywhere without an import context
// juggle. Every top-level JSON key is a CFG_SECTION child object; the
// generic load()/save() logic lives entirely in ConfigSection, this class
// only adds: the file path, the debounced/atomic write, and the QML
// singleton factory.
class Config : public ConfigSection {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
CFG_SECTION(Appearance, appearance, Appearance)
CFG_SECTION(Background, background, Background)
CFG_SECTION(Bar, bar, Bar)
CFG_SECTION(Clipboard, clipboard, Clipboard)
CFG_SECTION(Colors, colors, Colors)
CFG_SECTION(Dashboard, dashboard, Dashboard)
CFG_SECTION(Dock, dock, Dock)
CFG_SECTION(General, general, General)
CFG_SECTION(Launcher, launcher, Launcher)
CFG_SECTION(Lock, lock, Lock)
CFG_SECTION(Notifs, notifs, Notifs)
CFG_SECTION(Osd, osd, Osd)
CFG_SECTION(Overview, overview, Overview)
CFG_SECTION(Screenshot, screenshot, Screenshot)
CFG_SECTION(Services, services, Services)
CFG_SECTION(Sidebar, sidebar, Sidebar)
CFG_SECTION(Utilities, utilities, Utilities)
public:
explicit Config(QObject* parent = nullptr);
static Config* create(QQmlEngine*, QJSEngine*);
// Reload from disk, discarding any unsaved in-memory changes.
// Synchronous for first load on startup, async for subsequent reloads.
Q_INVOKABLE void load();
// Force an immediate synchronous write, bypassing the debounce timer.
// Call this from your shell's shutdown handler so nothing gets lost.
Q_INVOKABLE void saveNow();
// Reload from disk asynchronously (for file change notifications).
Q_INVOKABLE void reloadAsync();
private Q_SLOTS:
void scheduleSave();
void flushAsync();
private:
QString filePath() const;
void writeAtomically(const QByteArray& data);
void loadSync();
void loadAsync();
QTimer m_saveTimer;
bool m_loading = false;
bool m_firstLoadDone = false;
QFuture<void> m_loadFuture;
};
-87
View File
@@ -1,87 +0,0 @@
#include "ConfigSection.hpp"
#include <QMetaType>
#include <QVariant>
namespace {
// Property index to start scanning from - skips QObject's own "objectName"
// property, which we never want in the JSON.
int firstOwnProperty() {
return QObject::staticMetaObject.propertyCount();
}
}
ConfigSection::ConfigSection(QObject *parent) : QObject(parent) {}
void ConfigSection::wireSignals() {
if (m_wired) return;
m_wired = true;
const QMetaObject *mo = metaObject();
const int slotIdx = mo->indexOfSlot("onPropertyChanged()");
Q_ASSERT(slotIdx != -1);
const QMetaMethod slot = mo->method(slotIdx);
const int start = firstOwnProperty();
for (int i = start; i < mo->propertyCount(); ++i) {
const QMetaProperty prop = mo->property(i);
if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
if (auto *child = qobject_cast<ConfigSection *>(prop.read(this).value<QObject *>())) {
// Child sections bubble their changes straight up as our
// own changed() - no need to also react to our own notify.
connect(child, &ConfigSection::changed, this, &ConfigSection::changed);
continue;
}
}
if (prop.hasNotifySignal())
connect(this, prop.notifySignal(), this, slot);
}
}
void ConfigSection::onPropertyChanged() {
Q_EMIT changed();
}
void ConfigSection::fromJson(const QJsonObject &obj) {
const QMetaObject *mo = metaObject();
const int start = firstOwnProperty();
for (int i = start; i < mo->propertyCount(); ++i) {
const QMetaProperty prop = mo->property(i);
const QLatin1String name(prop.name());
if (!obj.contains(name)) continue;
if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
if (auto *child = qobject_cast<ConfigSection *>(prop.read(this).value<QObject *>())) {
child->fromJson(obj.value(name).toObject());
continue;
}
}
if (prop.isWritable())
prop.write(this, obj.value(name).toVariant());
}
}
QJsonObject ConfigSection::toJson() const {
QJsonObject obj;
const QMetaObject *mo = metaObject();
const int start = firstOwnProperty();
for (int i = start; i < mo->propertyCount(); ++i) {
const QMetaProperty prop = mo->property(i);
if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
if (auto *child = qobject_cast<ConfigSection *>(prop.read(this).value<QObject *>())) {
obj.insert(QLatin1String(prop.name()), child->toJson());
continue;
}
}
obj.insert(QLatin1String(prop.name()), QJsonValue::fromVariant(prop.read(this)));
}
return obj;
}
-82
View File
@@ -1,82 +0,0 @@
#pragma once
#include <QObject>
#include <QJsonObject>
#include <QJsonValue>
#include <QMetaProperty>
#include <QMetaMethod>
// ---------------------------------------------------------------------------
// CFG_PROPERTY / CFG_SECTION
//
// These generate exactly the boilerplate you'd write by hand for a
// Q_PROPERTY with a backing member, getter, setter and NOTIFY signal - just
// in one line instead of ~8. Because everything they generate is a normal
// compiled getter/setter, there is *zero* extra runtime cost vs hand-written
// code: reading Config.appearance.anim.sessionGifSpeed from QML calls a
// plain inline getter. Reflection (QMetaObject) is only walked at load time,
// save time, and once at construction time to wire change signals - never
// on the read/write path itself.
//
// CFG_PROPERTY(type, name, Name)
// - type: C++ type (bool, int, qreal, QString, QVariantList, ...)
// - name: property name as it must appear in the JSON file (camelCase,
// or whatever the JSON actually uses - it MUST match exactly)
// - Name: same name, capitalized, used to build setName()/NameChanged()
//
// CFG_SECTION(Type, name, Name)
// - Declares a child ConfigSection* property (e.g. Anim inside Appearance)
// - The pointer itself is CONSTANT (never reassigned); the object it
// points to is what actually changes.
// ---------------------------------------------------------------------------
#define CFG_PROPERTY(type, name, Name) \
Q_PROPERTY(type name READ name WRITE set##Name NOTIFY Name##Changed) \
public: \
type name() const { return m_##name; } \
void set##Name(const type &value) { \
if (m_##name == value) return; \
m_##name = value; \
Q_EMIT Name##Changed(); \
} \
Q_SIGNALS: \
void Name##Changed(); \
private: \
type m_##name{};
#define CFG_SECTION(Type, name, Name) \
public: \
Q_PROPERTY(Type *name READ name CONSTANT) \
Type *name() const { return m_##name; } \
private: \
Type *m_##name = nullptr;
class ConfigSection : public QObject {
Q_OBJECT
public:
explicit ConfigSection(QObject *parent = nullptr);
// Populate this section (and all child sections) from JSON. Unknown
// keys in obj are ignored; missing keys keep their current value.
void fromJson(const QJsonObject &obj);
// Serialize this section (and all child sections) to JSON.
QJsonObject toJson() const;
Q_SIGNALS:
// Emitted whenever any property in this section, or any nested section,
// changes. Config listens on the root section to know a save is needed,
// without every leaf class needing to know about Config.
void changed();
protected:
// Call once, at the end of every subclass constructor, after all child
// ConfigSection* members have been constructed (new'd with `this` as
// parent). Sets up the reflection-based signal forwarding.
void wireSignals();
private Q_SLOTS:
void onPropertyChanged();
private:
bool m_wired = false;
};
-39
View File
@@ -1,39 +0,0 @@
#include "Dashboard.hpp"
Performance::Performance(QObject *parent) : ConfigSection(parent) {
m_enabled = true;
m_showBattery = false;
m_showCpu = true;
m_showGpu = true;
m_showMemory = true;
m_showNetwork = true;
m_showStorage = true;
m_showVram = true;
wireSignals();
}
DashboardSizes::DashboardSizes(QObject *parent) : ConfigSection(parent) {
m_dateTimeWidth = 110;
m_infoIconSize = 25;
m_infoWidth = 200;
m_mediaCoverArtSize = 150;
m_mediaProgressSweep = 180;
m_mediaProgressThickness = 8;
m_mediaVisualizerSize = 200;
m_mediaWidth = 200;
m_resourceProgessThickness = 10;
m_resourceSize = 200;
m_tabIndicatorHeight = 3;
m_tabIndicatorSpacing = 5;
m_weatherWidth = 250;
wireSignals();
}
Dashboard::Dashboard(QObject *parent) : ConfigSection(parent) {
m_enabled = true;
m_mediaUpdateInterval = 2000;
m_performance = new Performance(this);
m_resourceUpdateInterval = 1000;
m_sizes = new DashboardSizes(this);
wireSignals();
}
-56
View File
@@ -1,56 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
class Performance : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, enabled, Enabled)
CFG_PROPERTY(bool, showBattery, ShowBattery)
CFG_PROPERTY(bool, showCpu, ShowCpu)
CFG_PROPERTY(bool, showGpu, ShowGpu)
CFG_PROPERTY(bool, showMemory, ShowMemory)
CFG_PROPERTY(bool, showNetwork, ShowNetwork)
CFG_PROPERTY(bool, showStorage, ShowStorage)
CFG_PROPERTY(bool, showVram, ShowVram)
public:
explicit Performance(QObject *parent = nullptr);
};
class DashboardSizes : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(int, dateTimeWidth, DateTimeWidth)
CFG_PROPERTY(int, infoIconSize, InfoIconSize)
CFG_PROPERTY(int, infoWidth, InfoWidth)
CFG_PROPERTY(int, mediaCoverArtSize, MediaCoverArtSize)
CFG_PROPERTY(int, mediaProgressSweep, MediaProgressSweep)
CFG_PROPERTY(int, mediaProgressThickness, MediaProgressThickness)
CFG_PROPERTY(int, mediaVisualizerSize, MediaVisualizerSize)
CFG_PROPERTY(int, mediaWidth, MediaWidth)
CFG_PROPERTY(int, resourceProgessThickness, ResourceProgessThickness)
CFG_PROPERTY(int, resourceSize, ResourceSize)
CFG_PROPERTY(int, tabIndicatorHeight, TabIndicatorHeight)
CFG_PROPERTY(int, tabIndicatorSpacing, TabIndicatorSpacing)
CFG_PROPERTY(int, weatherWidth, WeatherWidth)
public:
explicit DashboardSizes(QObject *parent = nullptr);
};
class Dashboard : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, enabled, Enabled)
CFG_PROPERTY(int, mediaUpdateInterval, MediaUpdateInterval)
CFG_SECTION(Performance, performance, Performance)
CFG_PROPERTY(int, resourceUpdateInterval, ResourceUpdateInterval)
CFG_SECTION(DashboardSizes, sizes, Sizes)
public:
explicit Dashboard(QObject *parent = nullptr);
};
-15
View File
@@ -1,15 +0,0 @@
#include "Dock.hpp"
Dock::Dock(QObject *parent) : ConfigSection(parent) {
m_enable = false;
m_height = 80;
m_hoverToReveal = false;
m_ignoredAppRegexes = {};
m_pinnedApps = {
QStringLiteral("com.ayugram.desktop"),
QStringLiteral("com.obsproject.studio"),
QStringLiteral("librewolf"),
};
m_pinnedOnStartup = false;
wireSignals();
}
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
#include <QVariantList>
class Dock : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(bool, enable, Enable)
CFG_PROPERTY(int, height, Height)
CFG_PROPERTY(bool, hoverToReveal, HoverToReveal)
CFG_PROPERTY(QVariantList, ignoredAppRegexes, IgnoredAppRegexes)
CFG_PROPERTY(QVariantList, pinnedApps, PinnedApps)
CFG_PROPERTY(bool, pinnedOnStartup, PinnedOnStartup)
public:
explicit Dock(QObject *parent = nullptr);
};
-77
View File
@@ -1,77 +0,0 @@
#include "General.hpp"
namespace {
QVariantList strList(std::initializer_list<const char *> items) {
QVariantList list;
for (auto *item : items) list << QString::fromLatin1(item);
return list;
}
QVariantMap threshold(const char *icon, const char *message, const char *name, int perc) {
QVariantMap m;
m["icon"] = QString::fromLatin1(icon);
m["message"] = QString::fromLatin1(message);
m["name"] = QString::fromLatin1(name);
m["perc"] = perc;
return m;
}
}
Apps::Apps(QObject *parent) : ConfigSection(parent) {
m_archiver = strList({"ark"});
m_audio = strList({"pavucontrol"});
m_document = strList({"libreoffice"});
m_editor = strList({"kate"});
m_explorer = strList({"dolphin"});
m_image = strList({"imv"});
m_playback = strList({"vlc"});
m_terminal = strList({"wezterm"});
wireSignals();
}
Battery::Battery(QObject *parent) : ConfigSection(parent) {
m_critPerc = 5;
m_popupThresholds = {
threshold("battery_android_frame_2", "Battery low", "Low battery", 20),
threshold("battery_android_frame_2", "Battery lower", "Low battery", 15),
threshold("battery_android_frame_2", "Battery lowest", "Low battery", 10),
};
wireSignals();
}
ColorSettings::ColorSettings(QObject *parent) : ConfigSection(parent) {
m_hyprsunsetTemp = 2600;
m_mode = QStringLiteral("dark");
m_neovimColors = false;
m_scheduleDark = false;
m_scheduleDarkEnd = 600;
m_scheduleDarkStart = 1140;
m_scheduleHyprsunset = true;
m_scheduleHyprsunsetEnd = 570;
m_scheduleHyprsunsetStart = 1200;
m_schemeGeneration = true;
m_smart = false;
wireSignals();
}
Idle::Idle(QObject *parent) : ConfigSection(parent) {
QVariantMap lock;
lock["idleAction"] = QStringLiteral("lock");
lock["name"] = QStringLiteral("Lock");
lock["timeout"] = 180;
m_timeouts = { lock };
wireSignals();
}
General::General(QObject *parent) : ConfigSection(parent) {
m_apps = new Apps(this);
m_battery = new Battery(this);
m_color = new ColorSettings(this);
m_dateFormat = QStringLiteral("ddd d MMM - hh:mm:ss");
m_desktopIcons = true;
m_idle = new Idle(this);
m_logo = QString();
m_showOverFullscreen = true;
m_wallpaperPath = QStringLiteral("/mnt/IronWolf/SDImages/SWWW_Wals/");
wireSignals();
}
-82
View File
@@ -1,82 +0,0 @@
#pragma once
#include "ConfigSection.hpp"
#include <qqmlregistration.h>
#include <QVariantList>
class Apps : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(QVariantList, archiver, Archiver)
CFG_PROPERTY(QVariantList, audio, Audio)
CFG_PROPERTY(QVariantList, document, Document)
CFG_PROPERTY(QVariantList, editor, Editor)
CFG_PROPERTY(QVariantList, explorer, Explorer)
CFG_PROPERTY(QVariantList, image, Image)
CFG_PROPERTY(QVariantList, playback, Playback)
CFG_PROPERTY(QVariantList, terminal, Terminal)
public:
explicit Apps(QObject *parent = nullptr);
};
class Battery : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(int, critPerc, CritPerc)
// Array of { icon, message, name, perc } objects.
CFG_PROPERTY(QVariantList, popupThresholds, PopupThresholds)
public:
explicit Battery(QObject *parent = nullptr);
};
class ColorSettings : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_PROPERTY(int, hyprsunsetTemp, HyprsunsetTemp)
CFG_PROPERTY(QString, mode, Mode)
CFG_PROPERTY(bool, neovimColors, NeovimColors)
CFG_PROPERTY(bool, scheduleDark, ScheduleDark)
CFG_PROPERTY(int, scheduleDarkEnd, ScheduleDarkEnd)
CFG_PROPERTY(int, scheduleDarkStart, ScheduleDarkStart)
CFG_PROPERTY(bool, scheduleHyprsunset, ScheduleHyprsunset)
CFG_PROPERTY(int, scheduleHyprsunsetEnd, ScheduleHyprsunsetEnd)
CFG_PROPERTY(int, scheduleHyprsunsetStart, ScheduleHyprsunsetStart)
CFG_PROPERTY(bool, schemeGeneration, SchemeGeneration)
CFG_PROPERTY(bool, smart, Smart)
public:
explicit ColorSettings(QObject *parent = nullptr);
};
class Idle : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
// Array of { idleAction, name, timeout } objects.
CFG_PROPERTY(QVariantList, timeouts, Timeouts)
public:
explicit Idle(QObject *parent = nullptr);
};
class General : public ConfigSection {
Q_OBJECT
QML_UNCREATABLE("Instantiated internally by Config")
CFG_SECTION(Apps, apps, Apps)
CFG_SECTION(Battery, battery, Battery)
CFG_SECTION(ColorSettings, color, Color)
CFG_PROPERTY(QString, dateFormat, DateFormat)
CFG_PROPERTY(bool, desktopIcons, DesktopIcons)
CFG_SECTION(Idle, idle, Idle)
CFG_PROPERTY(QString, logo, Logo)
CFG_PROPERTY(bool, showOverFullscreen, ShowOverFullscreen)
CFG_PROPERTY(QString, wallpaperPath, WallpaperPath)
public:
explicit General(QObject *parent = nullptr);
};

Some files were not shown because too many files have changed in this diff Show More