2 Commits
Author SHA1 Message Date
AramJonghu 5a01c4b472 chore: added missing dependency to pyproject
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 22s
Python / verify (pull_request) Failing after 19s
Python / static (pull_request) Failing after 22s
C++ / build (pull_request) Canceled after 9m5s
C++ / clang-tidy (pull_request) Canceled after 9m3s
Rust / build (pull_request) Canceled after 8m57s
Rust / fmt (pull_request) Canceled after 8m41s
Rust / clippy (pull_request) Canceled after 8m39s
2026-08-15 22:01:22 +02:00
AramJonghu 9271fa47bc ci(python): rework of jobs + fix import issue 2026-08-15 22:01:22 +02:00
109 changed files with 1867 additions and 5162 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ on:
jobs: jobs:
static: static:
runs-on: debian runs-on: debian
container: node:26-trixie-slim container: python:3.13-slim
steps: steps:
- name: Checkout - name: Checkout
@@ -36,7 +36,7 @@ jobs:
verify: verify:
if: always() if: always()
runs-on: debian runs-on: debian
container: node:26-trixie-slim container: python:3.13-slim
steps: steps:
- name: Checkout - name: Checkout
+1 -1
View File
@@ -15,7 +15,7 @@ CustomRect {
property color activeOnColor property color activeOnColor
property bool checked property bool checked
property real checkedRadius: Tokens.rounding.medium property real checkedRadius: Tokens.rounding.medium
property real defaultRadius: Math.min(width, height) / 2 property real defaultRadius: Tokens.rounding.large
property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1) property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledOnColor: Qt.alpha(Colors.palette.m3onSurface, 0.38) property color disabledOnColor: Qt.alpha(Colors.palette.m3onSurface, 0.38)
property bool fillWidth property bool fillWidth
-2
View File
@@ -34,7 +34,6 @@ BusyIndicator {
anchors.fill: parent anchors.fill: parent
bgColor: root.bgColor bgColor: root.bgColor
fgColor: root.fgColor fgColor: root.fgColor
hasEndIndicator: false
padding: root.padding padding: root.padding
rotation: manager.rotation rotation: manager.rotation
startAngle: manager.startFraction * 360 startAngle: manager.startFraction * 360
@@ -54,7 +53,6 @@ BusyIndicator {
Anim { Anim {
duration: manager.completeEndDuration * Tokens.anim.durations.scale duration: manager.completeEndDuration * Tokens.anim.durations.scale
properties: "opacity,internalStrokeWidth" properties: "opacity,internalStrokeWidth"
type: Anim.DefaultEffects
} }
} }
+2 -5
View File
@@ -13,13 +13,11 @@ Slider {
property bool animateWave property bool animateWave
property color bgColor: enabled ? Colors.palette.m3secondaryContainer : Qt.alpha(Colors.palette.m3onSurface, 0.1) property color bgColor: enabled ? Colors.palette.m3secondaryContainer : Qt.alpha(Colors.palette.m3onSurface, 0.1)
readonly property bool dragging: mouse.pressed
property color fgColor: enabled ? Colors.palette.m3primary : Qt.alpha(Colors.palette.m3onSurface, 0.38) property color fgColor: enabled ? Colors.palette.m3primary : Qt.alpha(Colors.palette.m3onSurface, 0.38)
property real filledWidth property real filledWidth
property alias inset: inset property alias inset: inset
property color insetColor: enabled ? (inset.attached ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onPrimary) : Colors.palette.m3onSurface property color insetColor: enabled ? (inset.attached ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onPrimary) : Colors.palette.m3onSurface
property string insetIcon: "" property string insetIcon: ""
property bool interactionOnMove: true
property real pos: visualPosition property real pos: visualPosition
property int radius: Tokens.rounding.small property int radius: Tokens.rounding.small
property int waveDuration: 1000 property int waveDuration: 1000
@@ -141,7 +139,7 @@ Slider {
CAnim { CAnim {
} }
} }
NumberAnimation on waveProgress { Anim on waveProgress {
duration: root.waveDuration duration: root.waveDuration
easing.type: Easing.Linear easing.type: Easing.Linear
from: 0 from: 0
@@ -188,8 +186,7 @@ Slider {
onPositionChanged: e => { onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width; dragMovement = (e.x - pressStartX) / width;
if (root.interactionOnMove) root.interaction(posBinding.value);
root.interaction(posBinding.value);
} }
onPressed: e => { onPressed: e => {
widthBehavior.enabled = false; widthBehavior.enabled = false;
-2
View File
@@ -23,7 +23,6 @@ TextFieldBase {
property string leadingIcon property string leadingIcon
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0 readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
property int radius: Tokens.rounding.small property int radius: Tokens.rounding.small
property alias sLayer: stateLayer
readonly property real smallFontScale: smallFontSize / font.pointSize readonly property real smallFontScale: smallFontSize / font.pointSize
property int smallFontSize: Tokens.font.size.small property int smallFontSize: Tokens.font.size.small
property string supportingText property string supportingText
@@ -146,7 +145,6 @@ TextFieldBase {
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.larger
text: root.leadingIcon text: root.leadingIcon
} }
} }
+130 -148
View File
@@ -18,14 +18,13 @@ Item {
property real leftFadeStrength: overflowing && leftFadeEnabled ? fadeStrengthMoving : fadeStrengthIdle property real leftFadeStrength: overflowing && leftFadeEnabled ? fadeStrengthMoving : fadeStrengthIdle
property int leftFadeWidth: 28 property int leftFadeWidth: 28
property bool marqueeEnabled: true property bool marqueeEnabled: true
readonly property bool overflowing: metrics.width > content.width readonly property bool overflowing: metrics.width > root.width
property int pauseMs: 1200 property int pauseMs: 1200
property real pixelsPerSecond: 40 property real pixelsPerSecond: 40
property real rightFadeStrength: overflowing ? fadeStrengthMoving : fadeStrengthIdle property real rightFadeStrength: overflowing ? fadeStrengthMoving : fadeStrengthIdle
property int rightFadeWidth: 28 property int rightFadeWidth: 28
property bool sliding: false property bool sliding: false
property alias text: elideText.text property alias text: elideText.text
property bool vertical: false
function durationForDistance(px): int { function durationForDistance(px): int {
return Math.max(1, Math.round(Math.abs(px) / root.pixelsPerSecond * 1000)); return Math.max(1, Math.round(Math.abs(px) / root.pixelsPerSecond * 1000));
@@ -42,9 +41,8 @@ Item {
} }
} }
clip: false clip: true
implicitHeight: vertical ? elideText.implicitWidth : elideText.implicitHeight implicitHeight: elideText.implicitHeight
implicitWidth: vertical ? elideText.implicitHeight : elideText.implicitWidth
Behavior on leftFadeStrength { Behavior on leftFadeStrength {
Anim { Anim {
@@ -55,187 +53,171 @@ Item {
} }
} }
onHeightChanged: resetMarquee()
onTextChanged: resetMarquee() onTextChanged: resetMarquee()
onVerticalChanged: resetMarquee()
onVisibleChanged: if (!visible) onVisibleChanged: if (!visible)
resetMarquee() resetMarquee()
onWidthChanged: 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 { Item {
id: content id: marqueeViewport
anchors.centerIn: parent anchors.fill: parent
height: root.vertical ? root.width : root.height clip: true
width: root.vertical ? root.height : root.width layer.enabled: true
visible: root.overflowing
transform: Rotation { layer.effect: OpacityMask {
angle: root.vertical ? -90 : 0 maskSource: rightFadeMask
origin.x: content.width / 2
origin.y: content.height / 2
}
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: content.width
} }
Item { Item {
id: marqueeViewport id: strip
anchors.fill: parent anchors.verticalCenter: parent.verticalCenter
clip: false height: t1.implicitHeight
layer.enabled: true width: t1.width + root.gap + t2.width
visible: root.overflowing x: 0
layer.effect: OpacityMask { CustomText {
maskSource: rightFadeMask id: t1
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: elideText.text
} }
Item { CustomText {
id: strip id: t2
anchors.verticalCenter: parent.verticalCenter animate: root.animate
height: t1.implicitHeight animateProp: "opacity"
width: t1.width + root.gap + t2.width color: root.color
x: 0 font.pointSize: elideText.font.pointSize
text: t1.text
x: t1.width + root.gap
}
}
CustomText { SequentialAnimation {
id: t1 id: marqueeAnim
animate: root.animate running: false
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: elideText.text
}
CustomText { onFinished: pauseTimer.restart()
id: t2
animate: root.animate ScriptAction {
animateProp: "opacity" script: {
color: root.color root.sliding = true;
font.pointSize: elideText.font.pointSize root.leftFadeEnabled = true;
text: t1.text
x: t1.width + root.gap
} }
} }
SequentialAnimation { Anim {
id: marqueeAnim duration: root.durationForDistance(t1.width)
easing.bezierCurve: Easing.Linear
easing.type: Easing.Linear
from: 0
property: "x"
target: strip
to: -t1.width
}
running: false ScriptAction {
script: {
onFinished: pauseTimer.restart() root.leftFadeEnabled = false;
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 { Anim {
id: pauseTimer 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)
}
interval: root.pauseMs ScriptAction {
repeat: false script: {
running: true root.sliding = false;
strip.x = 0;
onTriggered: {
if (root.marqueeEnabled)
marqueeAnim.start();
} }
} }
} }
Rectangle { Timer {
id: rightFadeMask id: pauseTimer
readonly property real fadeStartPos: { interval: root.pauseMs
const w = Math.max(1, width); repeat: false
return Math.max(0, Math.min(1, (w - root.rightFadeWidth) / w)); running: true
onTriggered: {
if (root.marqueeEnabled)
marqueeAnim.start();
} }
readonly property real leftFadeEndPos: { }
const w = Math.max(1, width); }
return Math.max(0, Math.min(1, root.leftFadeWidth / w));
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
} }
anchors.fill: marqueeViewport GradientStop {
layer.enabled: true color: Qt.rgba(1, 1, 1, 1.0)
visible: false position: rightFadeMask.leftFadeEndPos
}
gradient: Gradient { GradientStop {
orientation: Gradient.Horizontal color: Qt.rgba(1, 1, 1, 1.0)
position: rightFadeMask.fadeStartPos
}
GradientStop { GradientStop {
color: Qt.rgba(1, 1, 1, 1.0 - root.leftFadeStrength) color: Qt.rgba(1, 1, 1, 1.0 - root.rightFadeStrength)
position: 0.0 position: 1.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
}
} }
} }
} }
+4 -4
View File
@@ -126,7 +126,7 @@ MouseArea {
CustomRect { CustomRect {
id: item id: item
readonly property bool active: modelData === root?.active readonly property bool active: modelData === root.active
required property int index required property int index
required property MenuItem modelData required property MenuItem modelData
@@ -163,19 +163,19 @@ MouseArea {
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: item.modelData?.icon ?? "" text: item.modelData.icon
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true Layout.fillWidth: true
color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
text: item.modelData?.text ?? "" text: item.modelData.text
} }
Loader { Loader {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
active: item.modelData?.trailingIcon.length > 0 active: item.modelData.trailingIcon.length > 0
asynchronous: true asynchronous: true
visible: active visible: active
+2 -33
View File
@@ -11,46 +11,15 @@ Variants {
required property ShellScreen modelData required property ShellScreen modelData
function rebuild(): void {
loader.active = false;
loader.active = true;
}
LazyLoader {
id: loader
active: true
Drawer {
screen: scope.modelData
}
}
Connections {
function onConfigPositionChanged(): void {
Qt.callLater(scope.rebuild);
}
target: (loader.item as Drawer)?.geometry ?? null
}
}
component Drawer: Scope {
id: drawer
readonly property alias geometry: content.geometry
required property ShellScreen screen
Exclusions { Exclusions {
bar: content.bar bar: content.bar
geometry: content.geometry screen: scope.modelData
screen: drawer.screen
} }
Windows { Windows {
id: content id: content
screen: drawer.screen screen: scope.modelData
} }
} }
} }
-55
View File
@@ -1,55 +0,0 @@
import QtQuick
import qs.Modules.Bar as Bar
QtObject {
id: root
required property Bar.BarLoader bar
readonly property real barClamped: bar.clampedExtent
readonly property real barExtent: bar.extent
readonly property bool barOnBottom: position === "bottom"
readonly property bool barOnLeft: position === "left"
readonly property bool barOnTop: position === "top"
required property string configPosition
readonly property bool horizontal: position !== "left"
readonly property string position: configPosition === "top" || configPosition === "bottom" ? configPosition : "left"
required property var win
function axisPos(x: real, y: real): real {
return horizontal ? x : y;
}
function barContains(x: real, y: real, clamped = false): bool {
const extent = clamped ? barClamped : barExtent;
if (barOnTop)
return y < extent;
if (barOnBottom)
return y > win.height - extent;
return x < extent;
}
function insetBottom(border: real, clamped = false): real {
return barOnBottom ? (clamped ? barClamped : barExtent) : border;
}
function insetLeft(border: real, clamped = false): real {
return barOnLeft ? (clamped ? barClamped : barExtent) : border;
}
function insetTop(border: real, clamped = false): real {
return barOnTop ? (clamped ? barClamped : barExtent) : border;
}
function inwardDrag(dragX: real, dragY: real): real {
if (barOnTop)
return dragY;
if (barOnBottom)
return -dragY;
return dragX;
}
onConfigPositionChanged: {
if (!["left", "top", "bottom"].includes(configPosition))
console.warn(`Invalid bar position '${configPosition}', falling back to left`);
}
}
+2 -11
View File
@@ -1,7 +1,6 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Wayland
import QtQuick import QtQuick
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
@@ -10,24 +9,19 @@ Scope {
id: root id: root
required property Item bar required property Item bar
required property EdgeGeometry geometry
required property ShellScreen screen required property ShellScreen screen
ExclusionZone { ExclusionZone {
id: top id: top
WlrLayershell.layer: WlrLayer.Overlay
anchors.left: true
anchors.right: true
anchors.top: true anchors.top: true
hasBar: root.geometry.barOnTop exclusiveZone: root.bar.exclusiveZone
} }
ExclusionZone { ExclusionZone {
id: left id: left
anchors.left: true anchors.left: true
hasBar: root.geometry.barOnLeft
} }
ExclusionZone { ExclusionZone {
@@ -40,7 +34,6 @@ Scope {
id: bottom id: bottom
anchors.bottom: true anchors.bottom: true
hasBar: root.geometry.barOnBottom
} }
Timer { Timer {
@@ -51,9 +44,7 @@ Scope {
} }
component ExclusionZone: CustomWindow { component ExclusionZone: CustomWindow {
property bool hasBar exclusiveZone: Config.bar.border
exclusiveZone: hasBar ? root.bar.exclusiveZone : Config.bar.border
implicitHeight: 1 implicitHeight: 1
implicitWidth: 1 implicitWidth: 1
name: "Bar-Exclusion" name: "Bar-Exclusion"
+24 -42
View File
@@ -3,7 +3,7 @@ import QtQuick
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Popouts import qs.Modules as BarPopouts
Item { Item {
id: root id: root
@@ -12,47 +12,43 @@ Item {
required property real borderThickness required property real borderThickness
property bool dashboardShortcutActive property bool dashboardShortcutActive
required property Drawing drawing required property Drawing drawing
required property EdgeGeometry geometry
property bool osdShortcutActive property bool osdShortcutActive
required property Panels panels required property Panels panels
required property Wrapper popouts required property BarPopouts.Wrapper popouts
required property ShellScreen screen required property ShellScreen screen
property bool singleGestureTriggered: false property bool singleGestureTriggered: false
property bool utilitiesShortcutActive property bool utilitiesShortcutActive
required property PersistentProperties visibilities required property PersistentProperties visibilities
function inBottomPanel(panel: Item, x: real, y: real): bool { function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); return y > root.height - panel.height - Config.bar.border && withinPanelWidth(panel, x, y);
return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y);
} }
function inLeftPanel(panel: Item, x: real, y: real): bool { function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y); return x < panel.x + panel.width + Config.bar.border && withinPanelHeight(panel, x, y);
}
function inPopoutArea(x: real, y: real): bool {
const panel = panels.popoutsWrapper;
if (!geometry.horizontal)
return inLeftPanel(panel, x, y);
return withinPanelWidth(panel, x, y) && withinPanelHeight(panel, x, y);
} }
function inRightPanel(panel: Item, x: real, y: real): bool { function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y); return x > panel.x - Config.bar.border && withinPanelHeight(panel, x, y);
} }
function inTopPanel(panel: Item, x: real, y: real): bool { function inTopPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); return y < bar.implicitHeight + panel.height && withinPanelWidth(panel, x, y);
return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y); }
function onWheel(event: WheelEvent): void {
if (event.x < bar.implicitWidth) {
bar.handleWheel(event.y, event.angleDelta);
}
} }
function withinPanelHeight(panel: Item, x: real, y: real): bool { function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + geometry.insetTop(borderThickness); const panelY = panel.y + bar.implicitHeight;
return y >= panelY && y <= panelY + panel.height; return y >= panelY && y <= panelY + panel.height;
} }
function withinPanelWidth(panel: Item, x: real, y: real): bool { function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x + geometry.insetLeft(borderThickness); const panelX = panel.x + root.borderThickness;
return x >= panelX && x <= panelX + panel.width; return x >= panelX && x <= panelX + panel.width;
} }
@@ -85,7 +81,7 @@ Item {
if (root.singleGestureTriggered) if (root.singleGestureTriggered)
return; return;
if (root.geometry.insetTop(root.borderThickness) > centroid.pressPosition.y) { if (centroid.pressPosition.y < root.bar.implicitHeight) {
if (dragY > 20) { if (dragY > 20) {
root.visibilities.settings = true; root.visibilities.settings = true;
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
@@ -95,10 +91,10 @@ Item {
} }
} }
if (centroid.pressPosition.y > root.screen.height - root.geometry.insetBottom(root.borderThickness) && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50) if (centroid.pressPosition.y > root.screen.height - Config.bar.border && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
root.visibilities.clipboard = true; root.visibilities.clipboard = true;
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.geometry.insetTop(root.borderThickness) && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher) if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher)
if (dragY < -10) { if (dragY < -10) {
root.visibilities.dock = true; root.visibilities.dock = true;
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
@@ -162,19 +158,6 @@ Item {
} }
} }
Timer {
id: unhideTimer
interval: Config.bar.revealDelay
repeat: false
running: false
onTriggered: {
if (hoverHandler.hovered)
root.bar.isHovered = true;
}
}
HoverHandler { HoverHandler {
id: hoverHandler id: hoverHandler
@@ -191,7 +174,7 @@ Item {
root.popouts.hasCurrent = false; root.popouts.hasCurrent = false;
} }
if (Config.bar.autoHide || root.bar.fullscreen) if (Config.bar.autoHide)
root.bar.isHovered = false; root.bar.isHovered = false;
} }
} }
@@ -210,12 +193,9 @@ Item {
return; return;
} }
if (!root.visibilities.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true)) if (!root.visibilities.bar && Config.bar.autoHide && y < root.bar.implicitHeight)
root.bar.isHovered = true; root.bar.isHovered = true;
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
if (root.panels.sidebar.offsetScale === 1) { if (root.panels.sidebar.offsetScale === 1) {
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y); const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
@@ -236,11 +216,11 @@ Item {
} }
} }
if (Config.dock.enabled && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y)) if (Config.dock.enable && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.visibilities.dock = true; root.visibilities.dock = true;
if (root.geometry.barContains(x, y)) if (y < root.bar.implicitHeight)
root.bar.checkPopout(root.geometry.axisPos(x, y)); root.bar.checkPopout(x);
} }
} }
@@ -318,6 +298,8 @@ Item {
root.visibilities.sidebar = false; root.visibilities.sidebar = false;
root.panels.popouts.hasCurrent = false; root.panels.popouts.hasCurrent = false;
root.visibilities.launcher = false; root.visibilities.launcher = false;
} else {
Config.save();
} }
} }
+16 -19
View File
@@ -1,7 +1,7 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import qs.Components import qs.Components
import qs.Modules.Bar.Popouts as Popouts import qs.Modules as Modules
import qs.Modules.Notifications as Notifications import qs.Modules.Notifications as Notifications
import qs.Modules.Notifications.Sidebar as Sidebar import qs.Modules.Notifications.Sidebar as Sidebar
import qs.Modules.Notifications.Sidebar.Utils as Utils import qs.Modules.Notifications.Sidebar.Utils as Utils
@@ -27,7 +27,6 @@ Item {
readonly property alias dock: dock readonly property alias dock: dock
readonly property alias drawing: drawing readonly property alias drawing: drawing
required property var drawingItem required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher readonly property alias launcher: launcher
readonly property alias notifications: notifications readonly property alias notifications: notifications
readonly property alias osd: osd readonly property alias osd: osd
@@ -44,26 +43,24 @@ Item {
readonly property alias utilities: utilities readonly property alias utilities: utilities
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: geometry.insetLeft(borderThickness)
anchors.margins: borderThickness anchors.margins: borderThickness
anchors.topMargin: geometry.insetTop(borderThickness) anchors.topMargin: bar.implicitHeight
Item { Item {
id: resourcesWrapper id: resourcesWrapper
anchors.bottom: root.geometry.position === "bottom" ? parent.bottom : undefined
anchors.left: parent.left anchors.left: parent.left
anchors.top: root.geometry.position !== "bottom" ? parent.top : undefined anchors.top: parent.top
clip: true clip: true
implicitHeight: root.geometry.horizontal ? resources.implicitHeight * (1 - resources.offsetScale) : resources.implicitHeight implicitHeight: resources.implicitHeight * (1 - resources.offsetScale)
implicitWidth: root.geometry.horizontal ? resources.implicitWidth : resources.implicitWidth * (1 - resources.offsetScale) implicitWidth: resources.implicitWidth
Resources.Wrapper { Resources.Wrapper {
id: resources id: resources
position: root.geometry.position anchors.left: parent.left
anchors.top: parent.top
visibilities: root.visibilities visibilities: root.visibilities
} }
} }
@@ -99,11 +96,11 @@ Item {
visibilities: root.visibilities visibilities: root.visibilities
} }
Popouts.ClipWrapper { Modules.ClipWrapper {
id: popouts id: popouts
anchors.top: parent.top
borderThickness: root.borderThickness borderThickness: root.borderThickness
position: root.geometry.position
screen: root.screen screen: root.screen
} }
@@ -150,13 +147,11 @@ Item {
property real offsetScale: dashboard.shouldBeActive ? 0 : 1 property real offsetScale: dashboard.shouldBeActive ? 0 : 1
anchors.bottom: root.geometry.barOnLeft || root.geometry.barOnBottom ? parent.bottom : undefined anchors.right: parent.right
anchors.left: root.geometry.barOnLeft ? parent.left : undefined anchors.top: parent.top
anchors.right: root.geometry.barOnLeft ? undefined : parent.right
anchors.top: root.geometry.barOnBottom || root.geometry.barOnLeft ? undefined : parent.top
clip: true clip: true
implicitHeight: root.geometry.horizontal ? dashboard.implicitHeight * (1 - offsetScale) : dashboard.implicitHeight implicitHeight: dashboard.implicitHeight * (1 - offsetScale)
implicitWidth: root.geometry.horizontal ? dashboard.implicitWidth : dashboard.implicitWidth * (1 - offsetScale) implicitWidth: dashboard.implicitWidth
Behavior on offsetScale { Behavior on offsetScale {
Anim { Anim {
@@ -168,8 +163,10 @@ Item {
Dashboard.Wrapper { Dashboard.Wrapper {
id: dashboard id: dashboard
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: (-implicitHeight - 5) * offsetScale
offsetScale: dashboardWrapper.offsetScale offsetScale: dashboardWrapper.offsetScale
position: root.geometry.position
visibilities: root.visibilities visibilities: root.visibilities
} }
} }
+8 -13
View File
@@ -7,18 +7,17 @@ import qs.Modules.Bar as Bar
Region { Region {
id: root id: root
// required property Bar.BarLoader bar required property Bar.BarLoader bar
readonly property real borderThickness: win.borderThickness readonly property real borderThickness: win.borderThickness
required property EdgeGeometry geometry
readonly property alias menuPopoutRegion: menuPopoutRegion readonly property alias menuPopoutRegion: menuPopoutRegion
required property Panels panels required property Panels panels
required property var win required property var win
height: win.height - geometry.insetTop(borderThickness, true) - geometry.insetBottom(borderThickness, true) - win.dragMaskPadding * 2 height: win.height - bar.implicitHeight - win.borderThickness - win.dragMaskPadding * 2
intersection: Intersection.Xor intersection: Intersection.Xor
width: win.width - geometry.insetLeft(borderThickness, true) * 2 - win.dragMaskPadding * 2 width: win.width - win.borderThickness * 2 - win.dragMaskPadding * 2
x: geometry.insetLeft(borderThickness, true) + win.dragMaskPadding x: win.borderThickness + win.dragMaskPadding
y: geometry.insetTop(borderThickness, true) + win.dragMaskPadding y: bar.implicitHeight + win.dragMaskPadding
R { R {
panel: root.panels.dashboardWrapper panel: root.panels.dashboardWrapper
@@ -42,21 +41,17 @@ Region {
} }
R { R {
height: panel.height + root.geometry.insetTop(root.borderThickness)
panel: root.panels.notifications panel: root.panels.notifications
y: 0
} }
R { R {
height: panel.height * (1 - root.panels.utilities.offsetScale) + root.geometry.insetBottom(root.borderThickness) height: panel.height * (1 - root.panels.utilities.offsetScale) + root.borderThickness
panel: root.panels.utilities panel: root.panels.utilities
y: root.win.height - height y: root.win.height - height
} }
R { R {
height: root.geometry.horizontal ? panel.height * (1 - root.panels.popoutsWrapper.offsetScale) : panel.height
panel: root.panels.popoutsWrapper panel: root.panels.popoutsWrapper
width: root.geometry.horizontal ? panel.width : panel.width * (1 - root.panels.popoutsWrapper.offsetScale)
} }
R { R {
@@ -87,7 +82,7 @@ Region {
height: panel.height height: panel.height
intersection: Intersection.Subtract intersection: Intersection.Subtract
width: panel.width width: panel.width
x: panel.x + root.geometry.insetLeft(root.borderThickness) x: panel.x + root.borderThickness
y: panel.y + root.geometry.insetTop(root.borderThickness) y: panel.y + root.bar.implicitHeight
} }
} }
+28 -59
View File
@@ -11,7 +11,6 @@ import ZShell.Blobs
import qs.Daemons import qs.Daemons
import qs.Components import qs.Components
import qs.Modules.Bar import qs.Modules.Bar
import qs.Modules.Bar.Popouts.Network as N
import ZShell.Config import ZShell.Config
import qs.Helpers import qs.Helpers
import qs.Drawers import qs.Drawers
@@ -34,7 +33,6 @@ CustomWindow {
return 100; return 100;
} }
property real fsTransitionProg: hasFullscreen ? 1 : 0 property real fsTransitionProg: hasFullscreen ? 1 : 0
readonly property alias geometry: geometry
readonly property bool hasFullscreen: { readonly property bool hasFullscreen: {
if (hasSpecialWorkspace) { if (hasSpecialWorkspace) {
const specialName = monitor?.lastIpcObject.specialWorkspace?.name; const specialName = monitor?.lastIpcObject.specialWorkspace?.name;
@@ -52,7 +50,7 @@ CustomWindow {
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen) readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
property var root: Quickshell.shellDir property var root: Quickshell.shellDir
readonly property real sdfBorderOffset: 2 * fsTransitionProg readonly property real sdfBorderOffset: 2 * fsTransitionProg
readonly property real shadowOpacity: 0.7 * (1 - (bar.isHovered ? 0 : fsTransitionProg)) readonly property real shadowOpacity: 0.7 * (1 - fsTransitionProg)
property color surfaceColor: Colors.tPalette.m3surface property color surfaceColor: Colors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
@@ -60,7 +58,7 @@ CustomWindow {
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent" color: "transparent"
contentItem.focus: true contentItem.focus: true
mask: visibilities.isDrawing ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions) mask: visibilities.isDrawing ? null : (hasFullscreen ? emptyRegion : regions)
name: "Bar" name: "Bar"
Behavior on fsTransitionProg { Behavior on fsTransitionProg {
@@ -97,21 +95,13 @@ CustomWindow {
panels.popouts.hasCurrent = false; panels.popouts.hasCurrent = false;
} }
EdgeGeometry {
id: geometry
bar: bar
configPosition: Config.bar.position
win: root
}
Region { Region {
id: emptyRegion id: emptyRegion
height: panels.notifications.height height: panels.notifications.height
width: panels.notifications.width width: panels.notifications.width
x: panels.notifications.x + geometry.insetLeft(root.borderThickness) x: panels.notifications.x + root.borderThickness
y: panels.notifications.y + geometry.insetTop(root.borderThickness) y: panels.notifications.y + bar.implicitHeight
Region { Region {
height: panels.osd.height height: panels.osd.height
@@ -119,18 +109,12 @@ CustomWindow {
x: root.width - width x: root.width - width
y: panels.osdWrapper.y + bar.implicitHeight y: panels.osdWrapper.y + bar.implicitHeight
} }
Region {
height: geometry.horizontal ? 1 : root.screen.height
width: geometry.horizontal ? root.screen.width : 1
y: geometry.position === "bottom" ? root.screen.height - 1 : 0
}
} }
Regions { Regions {
id: regions id: regions
geometry: geometry bar: bar
panels: panels panels: panels
win: root win: root
} }
@@ -145,17 +129,7 @@ CustomWindow {
HyprlandFocusGrab { HyprlandFocusGrab {
id: focusGrab id: focusGrab
active: { active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu") && (!Config.bar.tray.showOnHover || (panels.popouts.current as StackView)?.depth > 1))
const v = visibilities;
const conf = Config;
if (!N.PopupManager.closed && panels.popouts.hasCurrent)
return true;
if ((v.launcher && conf.launcher.enabled) || (v.sidebar && conf.sidebar.enabled) || (v.resources && conf.dashboard.performance.enabled) || (v.dashboard && conf.dashboard.enabled) || v.settings || (v.clipboard && conf.clipboard.enabled) || (v.dock && conf.dock.enabled))
return true;
if (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu") && (!conf.bar.tray.showOnHover || (panels.popouts.current as StackView)?.depth > 1))
return true;
return false;
}
windows: [root] windows: [root]
onCleared: { onCleared: {
@@ -219,10 +193,10 @@ CustomWindow {
BlobInvertedRect { BlobInvertedRect {
anchors.fill: parent anchors.fill: parent
anchors.margins: -50 anchors.margins: -50
borderBottom: geometry.insetBottom(root.borderThickness) - anchors.margins - root.sdfBorderOffset borderBottom: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderLeft: geometry.insetLeft(root.borderThickness) - anchors.margins - root.sdfBorderOffset borderLeft: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderRight: root.borderThickness - anchors.margins - root.sdfBorderOffset borderRight: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderTop: geometry.insetTop(root.borderThickness) - anchors.margins - root.sdfBorderOffset borderTop: bar.implicitHeight - anchors.margins - root.sdfBorderOffset
group: blobGroup group: blobGroup
radius: root.borderRounding radius: root.borderRounding
} }
@@ -230,15 +204,15 @@ CustomWindow {
PanelBg { PanelBg {
id: dashBg id: dashBg
property real extraExtent: 0.2 property real extraHeight: 0.2
deformAmount: 0.06 deformAmount: 0.06
implicitHeight: panels.dashboard.height * (geometry.horizontal ? 1 + extraExtent : 1) implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width * (geometry.horizontal ? 1 : 1 + extraExtent) implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper panel: panels.dashboardWrapper
radius: Tokens.rounding.normal radius: Tokens.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.dashboard.width * extraExtent) x: panels.dashboardWrapper.x + panels.dashboard.x + root.borderThickness
y: panels.dashboardWrapper.y + panels.dashboard.y + geometry.insetTop(root.borderThickness) - (geometry.horizontal && geometry.position !== "bottom" ? panels.dashboard.height * extraExtent : 0) y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight
} }
PanelBg { PanelBg {
@@ -250,7 +224,7 @@ CustomWindow {
implicitHeight: panels.launcher.height * (1 + extraHeight) implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher panel: panels.launcher
radius: Tokens.rounding.smallest + 5 radius: Tokens.rounding.smallest + 5
y: panels.launcher.y + geometry.insetTop(root.borderThickness) y: panels.launcher.y + bar.implicitHeight
} }
PanelBg { PanelBg {
@@ -271,7 +245,8 @@ CustomWindow {
implicitWidth: panels.osd.width implicitWidth: panels.osd.width
panel: panels.osdWrapper panel: panels.osdWrapper
radius: 20 radius: 20
x: panels.osdWrapper.x + panels.osd.x + geometry.insetLeft(root.borderThickness) x: panels.osdWrapper.x + panels.osd.x + root.borderThickness
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
} }
PanelBg { PanelBg {
@@ -293,17 +268,17 @@ CustomWindow {
PanelBg { PanelBg {
id: popoutBg id: popoutBg
property real extraExtent: 0.2 property real extraHeight: 0.2
deformAmount: panels.popouts.currentName.startsWith("traymenu") ? 0.15 : 0.08 deformAmount: panels.popouts.currentName.startsWith("traymenu") ? 0.15 : 0.08
implicitHeight: panels.popouts.height * (geometry.horizontal ? 1 + extraExtent : 1) implicitHeight: panels.popouts.height * (1 + extraHeight)
implicitWidth: panels.popouts.width * (geometry.horizontal ? 1 : 1 + extraExtent) implicitWidth: panels.popouts.width
panel: panels.popoutsWrapper panel: panels.popoutsWrapper
radius: panels.popouts.current?.panelRadius ?? Tokens.rounding.normal radius: panels.popouts.current?.panelRadius ?? Tokens.rounding.normal
x: panels.popoutsWrapper.x + panels.popouts.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.popouts.width * extraExtent) x: panels.popoutsWrapper.x + panels.popouts.x + root.borderThickness
y: panels.popoutsWrapper.y + panels.popouts.y + geometry.insetTop(root.borderThickness) - (geometry.barOnTop ? panels.popouts.height * extraExtent : 0) y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
Behavior on extraExtent { Behavior on extraHeight {
Anim { Anim {
} }
} }
@@ -317,8 +292,8 @@ CustomWindow {
implicitWidth: panels.resources.width implicitWidth: panels.resources.width
panel: panels.resourcesWrapper panel: panels.resourcesWrapper
radius: Tokens.rounding.large radius: Tokens.rounding.large
x: panels.resourcesWrapper.x + panels.resources.x + geometry.insetLeft(root.borderThickness) x: panels.resourcesWrapper.x + panels.resources.x + root.borderThickness
y: panels.resourcesWrapper.y + panels.resources.y + geometry.insetTop(root.borderThickness) y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
} }
PanelBg { PanelBg {
@@ -396,7 +371,6 @@ CustomWindow {
borderThickness: root.borderLayoutThickness borderThickness: root.borderLayoutThickness
drawing: drawing drawing: drawing
enabled: true enabled: true
geometry: geometry
panels: panels panels: panels
popouts: panels.popouts popouts: panels.popouts
screen: root.screen screen: root.screen
@@ -408,7 +382,6 @@ CustomWindow {
bar: bar bar: bar
borderThickness: root.borderThickness borderThickness: root.borderThickness
drawingItem: drawing drawingItem: drawing
geometry: geometry
screen: root.screen screen: root.screen
visibilities: visibilities visibilities: visibilities
@@ -450,18 +423,14 @@ CustomWindow {
BarLoader { BarLoader {
id: bar id: bar
anchors.bottom: geometry.barOnBottom ? parent.bottom : undefined
anchors.left: parent.left anchors.left: parent.left
anchors.top: geometry.barOnBottom ? undefined : parent.top anchors.right: parent.right
enabled: !visibilities.isDrawing enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen fullscreen: root.hasFullscreen
height: geometry.horizontal ? implicitHeight : parent.height
popouts: panels.popouts popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper popoutsWrapper: panels.popoutsWrapper
position: geometry.position
screen: root.screen screen: root.screen
visibilities: visibilities visibilities: visibilities
width: geometry.horizontal ? parent.width : implicitWidth
} }
} }
@@ -474,7 +443,7 @@ CustomWindow {
implicitHeight: panel.height implicitHeight: panel.height
implicitWidth: panel.width implicitWidth: panel.width
radius: Tokens.rounding.smallest radius: Tokens.rounding.smallest
x: panel.x + geometry.insetLeft(root.borderThickness) x: panel.x + root.borderThickness
y: panel.y + geometry.insetTop(root.borderThickness) y: panel.y + bar.implicitHeight
} }
} }
+33
View File
@@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Effects
Item {
id: root
property real radius
Rectangle {
id: shadowRect
anchors.fill: root
color: "black"
layer.enabled: true
radius: root.radius
visible: false
}
MultiEffect {
id: effects
anchors.fill: shadowRect
autoPaddingEnabled: true
maskEnabled: true
maskInverted: true
maskSource: shadowRect
shadowBlur: 2.0
shadowColor: "black"
shadowEnabled: true
shadowOpacity: 1
source: shadowRect
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Controls
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -10,6 +11,6 @@ TextInput {
font { font {
family: Config.appearance?.font.family.sans ?? "sans-serif" family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting hintingPreference: Font.PreferFullHinting
pixelSize: Tokens?.font.size.normal ?? 15 pixelSize: Config.appearance?.font.size.normal ?? 15
} }
} }
+70
View File
@@ -0,0 +1,70 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Components
import ZShell.Config
Singleton {
id: root
property list<var> apps: {
var map = new Map();
const pinnedApps = Config.dock?.pinnedApps ?? [];
for (const appId of pinnedApps) {
if (!map.has(appId.toLowerCase()))
map.set(appId.toLowerCase(), ({
pinned: true,
toplevels: []
}));
}
if (pinnedApps.length > 0) {
map.set("SEPARATOR", {
pinned: false,
toplevels: []
});
}
var values = [];
for (const [key, value] of map) {
values.push(appEntryComp.createObject(null, {
appId: key,
toplevels: value.toplevels,
pinned: value.pinned
}));
}
return values;
}
function isPinned(appId) {
return Config.dock.pinnedApps.indexOf(appId) !== -1;
}
function togglePin(appId) {
if (root.isPinned(appId)) {
Config.dock.pinnedApps = Config.dock.pinnedApps.filter(id => id !== appId);
} else {
Config.dock.pinnedApps = Config.dock.pinnedApps.concat([appId]);
}
}
Component {
id: appEntryComp
TaskbarAppEntry {
}
}
component TaskbarAppEntry: QtObject {
id: wrapper
required property string appId
required property bool pinned
required property list<var> toplevels
}
}
+63
View File
@@ -0,0 +1,63 @@
// FetchPresets.qml
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
property var parsedPresets: ({})
readonly property var presets: parsedPresets
property bool ready: false
function accents(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.accents ?? [];
}
function defaultAccent(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.default_accent ?? "";
}
function modes(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.modes ?? [];
}
function presetNames() {
return Object.keys(parsedPresets);
}
function variantNames(presetName) {
const preset = parsedPresets[presetName];
if (!preset || !preset.variants)
return [];
return Object.keys(preset.variants);
}
Process {
command: ["zshell-cli", "scheme", "list-presets", "--json"]
running: true
stdout: StdioCollector {
onStreamFinished: {
try {
const parsed = JSON.parse(text);
root.parsedPresets = parsed.presets ?? {};
root.ready = true;
} catch (e) {
console.error("Failed to parse presets JSON:", e);
}
}
}
}
}
+12 -51
View File
@@ -1,8 +1,9 @@
pragma Singleton pragma Singleton
import QtQuick import ZShell.Config
import Quickshell import Quickshell
import Quickshell.Services.Notifications import Quickshell.Services.Notifications
import QtQuick
Singleton { Singleton {
id: root id: root
@@ -77,6 +78,16 @@ Singleton {
"99": "thunderstorm" "99": "thunderstorm"
}) })
function getAppCategoryIcon(name: string, fallback: string): string {
const categories = DesktopEntries.heuristicLookup(name)?.categories;
if (categories)
for (const [key, value] of Object.entries(categoryIcons))
if (categories.includes(key))
return value;
return fallback;
}
function getAppIcon(name: string, fallback: string): string { function getAppIcon(name: string, fallback: string): string {
const icon = DesktopEntries.heuristicLookup(name)?.icon; const icon = DesktopEntries.heuristicLookup(name)?.icon;
if (fallback !== "undefined") if (fallback !== "undefined")
@@ -84,15 +95,6 @@ Singleton {
return Quickshell.iconPath(icon); return Quickshell.iconPath(icon);
} }
function getBatteryIcon(percentage: real, charging = false): string {
if (percentage === 1)
return charging ? "battery_charging_full" : "battery_full";
let level = Math.floor(percentage * 7);
if (charging && (level === 4 || level === 1))
level--;
return charging ? `battery_charging_${(level + 3) * 10}` : `battery_${level}_bar`;
}
function getBluetoothIcon(icon: string): string { function getBluetoothIcon(icon: string): string {
if (icon.includes("headset") || icon.includes("headphones")) if (icon.includes("headset") || icon.includes("headphones"))
return "headphones"; return "headphones";
@@ -166,32 +168,6 @@ Singleton {
return "chat"; return "chat";
} }
function getSpecialWsIcon(name: string): string {
name = name.toLowerCase().slice("special:".length);
if (name === "special")
return "star";
if (name === "communication")
return "forum";
if (name === "music")
return "music_cast";
if (name === "todo")
return "checklist";
if (name === "sysmon")
return "monitor_heart";
return name[0].toUpperCase();
}
function getTrayIcon(id: string, icon: string): string {
if (icon.includes("?path=")) {
const [name, path] = icon.split("?path=");
const file = name.slice(name.lastIndexOf("/") + 1);
const themed = Quickshell.iconPath(file, true);
icon = themed ? themed : Qt.resolvedUrl(`${path}/${file}`);
}
return icon;
}
function getVolumeIcon(volume: real, isMuted: bool): string { function getVolumeIcon(volume: real, isMuted: bool): string {
if (isMuted) if (isMuted)
return "no_sound"; return "no_sound";
@@ -207,19 +183,4 @@ Singleton {
return weatherIcons[code]; return weatherIcons[code];
return "air"; return "air";
} }
function matchIconConfig(name: string, iconConfig: var): bool {
if (!iconConfig.icon)
return false;
if (iconConfig.regex) {
const re = new RegExp(iconConfig.regex, iconConfig.flags ?? "");
if (re.test(name))
return true;
} else if (iconConfig.name === name) {
return true;
}
return false;
}
} }
-114
View File
@@ -1,114 +0,0 @@
pragma Singleton
import Quickshell
import Quickshell.Networking
import QtQuick
Singleton {
id: root
property bool active: false
readonly property list<Network> connectedNetworks: networks.filter(n => n.connected)
property Network connectingNetwork
readonly property list<NetworkDevice> devices: Networking.devices.values
property Network failedNetwork
readonly property list<Network> knownNetworks: networks.filter(n => n.known && !n.connected)
readonly property list<Network> networks: {
const list = [];
for (const d of wifiDevices) {
for (const n of d.networks.values) {
if (!list.includes(n))
list.push(n);
}
}
return list;
}
property bool scanning: false
readonly property list<Network> unknownNetworks: networks.filter(n => !n.known)
readonly property list<WifiDevice> wifiDevices: devices.filter(d => wifiDevice(d))
readonly property bool wifiEnabled: Networking.wifiEnabled
signal networkConnected
function isSecure(security): bool {
return (security === WifiSecurityType.WpaPsk || security === WifiSecurityType.Wpa2Psk || security === WifiSecurityType.Sae);
}
function requestConnect(network: Network, secret = undefined): void {
connectingNetwork = network;
if (secret === undefined || secret === "") {
network.connect();
} else {
(network as WifiNetwork).connectWithPsk(secret);
}
}
function setScan(value: bool): void {
for (const d of wifiDevices) {
d.scannerEnabled = value;
}
}
function setWifi(value: bool): void {
Networking.wifiEnabled = value;
}
function wifiDevice(dev): bool {
return dev.type === DeviceType.Wifi;
}
onActiveChanged: {
for (const d of wifiDevices)
d.scannerEnabled = active;
}
Timer {
id: flushFailed
interval: 5000
running: root.failedNetwork
onTriggered: {
root.failedNetwork = null;
}
}
Timer {
id: checkConnection
interval: 500
repeat: true
running: root.connectingNetwork
onTriggered: {
var completedNetwork;
switch (root.connectingNetwork.state) {
case ConnectionState.Connecting:
break;
case ConnectionState.Connected:
completedNetwork = root.connectingNetwork;
root.networkConnected();
break;
case ConnectionState.Disconnected:
root.failedNetwork = root.connectingNetwork;
completedNetwork = root.connectingNetwork;
break;
}
if (completedNetwork) {
root.connectingNetwork = null;
}
}
}
Timer {
interval: 10000
running: checkConnection.running
onTriggered: {
root.failedNetwork = root.connectingNetwork;
root.connectingNetwork = null;
}
}
}
+1
View File
@@ -73,6 +73,7 @@ Singleton {
Config.dock.pinnedApps = pinnedApps; Config.dock.pinnedApps = pinnedApps;
root.unpinnedOrder = visibleUnpinned.concat(root.unpinnedOrder.map(normalizeId).filter(id => !pinnedApps.includes(id) && !visibleUnpinned.includes(id))); root.unpinnedOrder = visibleUnpinned.concat(root.unpinnedOrder.map(normalizeId).filter(id => !pinnedApps.includes(id) && !visibleUnpinned.includes(id)));
Config.saveNoToast();
} }
function isPinned(appId) { function isPinned(appId) {
+41 -8
View File
@@ -11,8 +11,8 @@ Singleton {
id: root id: root
property int availableUpdates: 0 property int availableUpdates: 0
property string cmd: ""
property bool commandReady property bool commandReady
property bool hasHelper
property bool loaded property bool loaded
property double now: Date.now() property double now: Date.now()
property var updates: ({}) property var updates: ({})
@@ -38,12 +38,18 @@ Singleton {
} }
function performPackageUpdate(pkg: string): void { function performPackageUpdate(pkg: string): void {
pkgUpdateProc.command = ["pkexec", "pacman", "--noconfirm", "-Sy", pkg]; if (root.cmd === "pacman")
pkgUpdateProc.command = ["pkexec", root.cmd, "--noconfirm", "-Sy", pkg];
else
pkgUpdateProc.command = [root.cmd, "--noconfirm", "--sudo", "pkexec", "-Sy", pkg];
pkgUpdateProc.running = true; pkgUpdateProc.running = true;
} }
function performSystemUpdate(): void { function performSystemUpdate(): void {
sysUpdateProc.command = ["pkexec", "pacman", "--noconfirm", "-Syu"]; if (root.cmd === "pacman")
sysUpdateProc.command = ["pkexec", root.cmd, "--noconfirm", "-Syu"];
else
sysUpdateProc.command = [root.cmd, "--noconfirm", "--sudo", "pkexec", "-Syu"];
sysUpdateProc.running = true; sysUpdateProc.running = true;
} }
@@ -81,7 +87,7 @@ Singleton {
Process { Process {
id: cmdDetect id: cmdDetect
command: ["sh", "-c", "command -v checkupdates"] command: ["sh", "-c", "command -v checkupdates || command -v yay || command -v paru"]
running: true running: true
stdout: StdioCollector { stdout: StdioCollector {
@@ -90,20 +96,47 @@ Singleton {
let helper; let helper;
if (cmd.length > 0) { if (cmd.length > 0) {
helper = true; helper = cmd.split("/").pop();
} else { } else {
helper = false; helper = "pacman";
}
if (helper === "checkupdates") {
updatesProc.command = [helper];
} else {
updatesProc.command = [helper, "-Qu"];
} }
root.hasHelper = helper;
root.commandReady = true; root.commandReady = true;
} }
} }
} }
Process {
id: updateCmdDetect
command: ["sh", "-c", "command -v yay || command -v paru"]
running: true
stdout: StdioCollector {
onStreamFinished: {
const cmd = this.text.trim();
let helper;
if (cmd.length > 0) {
helper = cmd.split("/").pop();
} else {
helper = "pacman";
}
root.cmd = helper;
}
}
}
Process { Process {
id: updatesProc id: updatesProc
command: root.hasHelper ? ["checkupdates"] : [] command: []
running: false running: false
stdout: StdioCollector { stdout: StdioCollector {
+1 -4
View File
@@ -17,10 +17,7 @@ Singleton {
watchChanges: true watchChanges: true
onAdapterUpdated: writeAdapter() onAdapterUpdated: writeAdapter()
onFileChanged: { onFileChanged: reload()
reload();
Wallpapers.previewColorLock = false;
}
JsonAdapter { JsonAdapter {
id: adapter id: adapter
+6 -28
View File
@@ -6,7 +6,7 @@ import Quickshell.Io
import QtQuick import QtQuick
import ZShell.Models import ZShell.Models
import ZShell.Config import ZShell.Config
import qs.Services import qs.Modules
import qs.Helpers import qs.Helpers
import qs.Paths import qs.Paths
@@ -17,8 +17,6 @@ Searcher {
property alias crops: adapter.monitorCrops property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent readonly property string current: showPreview ? previewPath : actualCurrent
property alias monitorCrops: monitorCrops property alias monitorCrops: monitorCrops
property bool pendingPreviewClear
property bool previewColorLock
property string previewPath property string previewPath
property bool recentlyChanged property bool recentlyChanged
property bool showPreview: false property bool showPreview: false
@@ -36,10 +34,9 @@ Searcher {
function preview(path: string): void { function preview(path: string): void {
previewPath = path; previewPath = path;
showPreview = true;
if (Config.general.color.schemeGeneration) if (Config.general.color.schemeGeneration)
previewColorsProc.running = true; Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${previewPath}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
showPreview = true;
} }
function setCrop(screen: string, rect: rect, zoom: real): void { function setCrop(screen: string, rect: rect, zoom: real): void {
@@ -55,6 +52,7 @@ Searcher {
height: rect.height, height: rect.height,
zoom: zoom zoom: zoom
}; };
// root.crops = updated;
} }
function setWallpaper(path: string): void { function setWallpaper(path: string): void {
@@ -68,10 +66,8 @@ Searcher {
function stopPreview(): void { function stopPreview(): void {
showPreview = false; showPreview = false;
if (previewColorLock) if (Config.general.color.schemeGeneration)
pendingPreviewClear = true; Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
else
Colors.showPreview = false;
} }
extraOpts: useFuzzy ? ({}) : ({ extraOpts: useFuzzy ? ({}) : ({
@@ -81,11 +77,6 @@ Searcher {
list: wallpapers.entries list: wallpapers.entries
useFuzzy: true useFuzzy: true
onPreviewColorLockChanged: {
if (!previewColorLock && pendingPreviewClear)
Colors.showPreview = false;
}
IpcHandler { IpcHandler {
function set(path: string): void { function set(path: string): void {
root.setWallpaper(path); root.setWallpaper(path);
@@ -127,17 +118,4 @@ Searcher {
path: Config.general.wallpaperPath path: Config.general.wallpaperPath
recursive: true recursive: true
} }
Process {
id: previewColorsProc
command: ["zshell-cli", "scheme", "generate", "-p", "-i", root.previewPath]
stdout: StdioCollector {
onStreamFinished: {
Colors.load(text, true);
Colors.showPreview = true;
}
}
}
} }
+40 -52
View File
@@ -3,29 +3,24 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Modules
import ZShell.Config import ZShell.Config
import qs.Modules.Bar.Components import qs.Modules.SysTray
import qs.Modules.Bar.Popouts import qs.Modules.Network
import qs.Modules.Updates
GridLayout { RowLayout {
id: root id: root
required property bool fullscreen required property bool fullscreen
required property bool horizontal
required property bool isHovered
required property Wrapper popouts required property Wrapper popouts
required property ClipWrapper popoutsWrapper required property ClipWrapper popoutsWrapper
required property ShellScreen screen required property ShellScreen screen
readonly property int vPadding: 6 readonly property int vPadding: 6
required property PersistentProperties visibilities required property PersistentProperties visibilities
function axisCenterOf(item: Item): real { function checkPopout(x: real): void {
const c = item.mapToItem(root, item.implicitWidth / 2, item.implicitHeight / 2); const ch = childAt(x, height / 2) as EntryWrapper;
return horizontal ? c.x : c.y;
}
function checkPopout(pos: real): void {
const ch = (horizontal ? childAt(pos, height / 2) : childAt(width / 2, pos)) as EntryWrapper;
const id = ch?.entryId; const id = ch?.entryId;
if (!ch || id === undefined) { if (!ch || id === undefined) {
@@ -39,21 +34,26 @@ GridLayout {
if (id === "statusIcons" && Config.bar.popouts.statusIcons) { if (id === "statusIcons" && Config.bar.popouts.statusIcons) {
const items = (ch.item as StatusIcons).items; const items = (ch.item as StatusIcons).items;
const icon = horizontal ? items.childAt(mapToItem(items, pos, 0).x, items.height / 2) : items.childAt(items.width / 2, mapToItem(items, 0, pos).y); const localX = mapToItem(items, x, 0).x;
const icon = items.childAt(localX, items.height / 2);
if (icon) { if (icon) {
popouts.currentName = icon.name; popouts.currentName = icon.name;
popouts.currentCenter = Qt.binding(() => axisCenterOf(icon)); popouts.currentCenter = Qt.binding(() => icon.mapToItem(root, icon.implicitWidth / 2, 0).x);
popouts.hasCurrent = true; popouts.hasCurrent = true;
} }
} else if (id === "tray" && Config.bar.popouts.tray && Config.bar.tray.showOnHover) { } else if (id === "tray" && Config.bar.popouts.tray && Config.bar.tray.showOnHover) {
const tray = ch.item as TrayIcons; const tray = ch.item as TrayIcons;
const layout = tray.layout; const layout = tray.layout;
const trayItem = horizontal ? layout.childAt(mapToItem(layout, pos, 0).x, layout.height / 2) : layout.childAt(tray.width / 2, mapToItem(layout, 0, pos).y); const localX = mapToItem(layout, x, 0).x;
const trayItem = layout.childAt(localX, layout.height / 2);
if (trayItem) { if (trayItem) {
const idx = trayItem.index; const idx = trayItem.index;
popouts.currentName = `traymenu${idx}`; popouts.currentName = `traymenu${idx}`;
popouts.currentCenter = Qt.binding(() => axisCenterOf(trayItem)); popouts.currentCenter = Qt.binding(() => {
const it = tray.items.itemAt(idx);
return it ? it.mapToItem(root, it.implicitWidth / 2, 0).x : popouts.currentCenter;
});
popouts.hasCurrent = true; popouts.hasCurrent = true;
} }
@@ -63,19 +63,12 @@ GridLayout {
if (id === "updates") { if (id === "updates") {
popouts.currentName = "updates"; popouts.currentName = "updates";
popouts.currentCenter = Qt.binding(() => axisCenterOf(ch.item as Item)); popouts.currentCenter = Qt.binding(() => ch.item.mapToItem(root, (ch.item as Item).implicitWidth / 2, 0).x);
popouts.hasCurrent = true; popouts.hasCurrent = true;
} }
} }
function entryAt(pos: real): string { spacing: Tokens.spacing.small
const ch = (horizontal ? childAt(pos, height / 2) : childAt(width / 2, pos)) as EntryWrapper;
return ch?.entryId ?? "";
}
columnSpacing: Tokens.spacing.small
columns: horizontal ? -1 : 1
rowSpacing: Tokens.spacing.small
Repeater { Repeater {
id: repeater id: repeater
@@ -92,8 +85,7 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
HyprsunsetWidget { HyprsunsetWidget {
horizontal: root.horizontal visible: !root.fullscreen
visible: !root.fullscreen || root.isHovered
} }
} }
} }
@@ -102,8 +94,7 @@ GridLayout {
roleValue: "spacer" roleValue: "spacer"
delegate: EntryWrapper { delegate: EntryWrapper {
Layout.fillHeight: !root.horizontal Layout.fillWidth: true
Layout.fillWidth: root.horizontal
} }
} }
@@ -112,9 +103,8 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
Workspaces { Workspaces {
horizontal: root.horizontal
screen: root.screen screen: root.screen
visible: !root.fullscreen || root.isHovered visible: !root.fullscreen
} }
} }
} }
@@ -124,10 +114,9 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
TrayIcons { TrayIcons {
horizontal: root.horizontal
loader: root loader: root
popouts: root.popouts popouts: root.popouts
visible: !root.fullscreen || root.isHovered visible: !root.fullscreen
} }
} }
} }
@@ -137,8 +126,6 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
StatusIcons { StatusIcons {
horizontal: root.horizontal
visible: !root.fullscreen || root.isHovered
} }
} }
} }
@@ -148,9 +135,8 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
Resources { Resources {
horizontal: root.horizontal
visibilities: root.visibilities visibilities: root.visibilities
visible: !root.fullscreen || root.isHovered visible: !root.fullscreen
} }
} }
} }
@@ -160,8 +146,7 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
UpdatesWidget { UpdatesWidget {
horizontal: root.horizontal visible: !root.fullscreen
visible: !root.fullscreen || root.isHovered
} }
} }
} }
@@ -171,10 +156,9 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
NotifBell { NotifBell {
horizontal: root.horizontal
popouts: root.popouts popouts: root.popouts
visibilities: root.visibilities visibilities: root.visibilities
visible: !root.fullscreen || root.isHovered visible: !root.fullscreen
} }
} }
} }
@@ -184,11 +168,10 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
Clock { Clock {
horizontal: root.horizontal
loader: root loader: root
popouts: root.popouts popouts: root.popouts
visibilities: root.visibilities visibilities: root.visibilities
visible: !root.fullscreen || root.isHovered visible: !root.fullscreen
} }
} }
} }
@@ -199,8 +182,16 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
WindowTitle { WindowTitle {
bar: root bar: root
horizontal: root.horizontal visible: !root.fullscreen
visible: !root.fullscreen || root.isHovered }
}
}
DelegateChoice {
roleValue: "network"
delegate: EntryWrapper {
NetworkWidget {
} }
} }
} }
@@ -210,8 +201,7 @@ GridLayout {
delegate: EntryWrapper { delegate: EntryWrapper {
MediaWidget { MediaWidget {
horizontal: root.horizontal visible: !root.fullscreen
visible: !root.fullscreen || root.isHovered
} }
} }
} }
@@ -224,11 +214,9 @@ GridLayout {
default property Item item default property Item item
required property var modelData required property var modelData
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter Layout.alignment: Qt.AlignVCenter
Layout.bottomMargin: !root.horizontal && index === repeater.count - 1 ? root.vPadding : 0 Layout.leftMargin: index === 0 ? root.vPadding : (entryId === "statusIcons") ? -root.spacing + Tokens.spacing.extraSmall : 0
Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && root.horizontal) ? -root.rowSpacing + Tokens.spacing.extraSmall : 0 Layout.rightMargin: index === repeater.count - 1 ? root.vPadding : 0
Layout.rightMargin: root.horizontal && index === repeater.count - 1 ? root.vPadding : 0
Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && !root.horizontal) ? -root.columnSpacing + Tokens.spacing.extraSmall : 0
children: item children: item
implicitHeight: item?.implicitHeight ?? 0 implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0 implicitWidth: item?.implicitWidth ?? 0
+21 -29
View File
@@ -1,48 +1,45 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Hyprland
import QtQuick import QtQuick
import ZShell.Config import QtQuick.Layouts
import qs.Modules.Bar.Popouts
import qs.Components import qs.Components
import qs.Modules
import ZShell.Config
import qs.Helpers
import qs.Modules.SysTray
import qs.Modules.SysTray.Widgets
import qs.Modules.Network
Item { Item {
id: root id: root
readonly property int clampedExtent: Math.max(Config.bar.border, extent) readonly property int contentHeight: Config.bar.height + padding * 2
readonly property int contentThickness: Math.floor(Math.max(Config.bar.height, Tokens.bar.innerSize * (horizontal ? 1 : 1.5))) + padding * 2 readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentHeight
readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentThickness
property real extent: fullscreen ? 0 : Config.bar.border
required property bool fullscreen required property bool fullscreen
readonly property bool horizontal: position !== "left"
property bool isHovered property bool isHovered
readonly property int padding: Math.max(Tokens.padding.smaller, Config.bar.border) readonly property int padding: Math.max(Tokens.padding.smaller, Config.bar.border)
required property Wrapper popouts required property Wrapper popouts
required property ClipWrapper popoutsWrapper required property ClipWrapper popoutsWrapper
required property string position
required property ShellScreen screen required property ShellScreen screen
readonly property bool shouldBeVisible: (!fullscreen && (!Config.bar.autoHide || visibilities.bar)) || isHovered readonly property bool shouldBeVisible: !fullscreen && (!Config.bar.autoHide || visibilities.bar || isHovered)
readonly property int vPadding: 6 readonly property int vPadding: 6
required property PersistentProperties visibilities required property PersistentProperties visibilities
function checkPopout(pos: real): void { function checkPopout(x: real): void {
(content.item as Bar)?.checkPopout(pos); content.item?.checkPopout(x);
} }
function entryAt(pos: real): string { implicitHeight: fullscreen ? 0 : Config.bar.border
return (content.item as Bar)?.entryAt(pos) ?? ""; visible: height > Config.bar.border
}
implicitHeight: extent
implicitWidth: extent
visible: extent > Config.bar.border
states: State { states: State {
name: "visible" name: "visible"
when: root.shouldBeVisible when: root.shouldBeVisible
PropertyChanges { PropertyChanges {
root.extent: root.contentThickness root.implicitHeight: root.contentHeight
} }
} }
transitions: [ transitions: [
@@ -51,7 +48,7 @@ Item {
to: "visible" to: "visible"
Anim { Anim {
property: "extent" property: "implicitHeight"
target: root target: root
} }
}, },
@@ -60,7 +57,7 @@ Item {
to: "" to: ""
Anim { Anim {
property: "extent" property: "implicitHeight"
target: root target: root
} }
} }
@@ -70,18 +67,13 @@ Item {
id: content id: content
active: root.shouldBeVisible || root.visible active: root.shouldBeVisible || root.visible
anchors.bottom: root.position !== "bottom" ? parent.bottom : undefined anchors.bottom: parent.bottom
anchors.left: root.horizontal ? parent.left : undefined anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.top: root.position !== "top" ? parent.top : undefined
height: root.contentThickness
width: root.contentThickness
sourceComponent: Bar { sourceComponent: Bar {
fullscreen: root.fullscreen fullscreen: root.fullscreen
height: root.contentThickness height: root.contentHeight
horizontal: root.horizontal
isHovered: root.isHovered
popouts: root.popouts popouts: root.popouts
popoutsWrapper: root.popoutsWrapper popoutsWrapper: root.popoutsWrapper
screen: root.screen screen: root.screen
-157
View File
@@ -1,157 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Modules.Bar.Popouts
import qs.Modules.Bar.Components.Common
import qs.Helpers
import qs.Components
import qs.Services
WidgetBase {
id: root
readonly property color contentColor: visibilities.dashboard ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
readonly property string effectiveFormat: {
let fmt = Config.general.dateFormat;
if (!root.horizontal)
fmt = fmt.replace("dddd", "ddd");
return fmt;
}
readonly property var formatParts: {
const segments = root.effectiveFormat.split(" - ");
let datePart = "";
let timePart = "";
if (segments.length > 1) {
datePart = segments[0];
timePart = segments[1];
} else if (/[hms]/.test(segments[0])) {
timePart = segments[0];
} else {
datePart = segments[0];
}
const dateTokens = datePart.trim().length ? datePart.trim().split(/\s+/) : [];
const timeTokens = timePart.trim().length ? timePart.trim().split(":") : [];
return {
dateTokens: dateTokens,
timeTokens: timeTokens
};
}
required property GridLayout loader
required property Wrapper popouts
readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.normal * 2 : verticalColumn.implicitHeight + Tokens.padding.larger * 2
required property PersistentProperties visibilities
color: visibilities.dashboard ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : size
implicitWidth: horizontal ? size : baseSize
radius: Tokens.rounding.full
CustomText {
id: timeText
anchors.centerIn: parent
color: root.contentColor
font.family: Config.appearance.font.family.mono // qmllint disable incompatible-type
height: implicitHeight
text: Time.dateStr
visible: root.horizontal
Behavior on color {
CAnim {
}
}
}
ColumnLayout {
id: verticalColumn
anchors.centerIn: parent
spacing: Tokens.padding.small ?? 2
visible: !root.horizontal
width: root.baseSize
Repeater {
model: root.formatParts.dateTokens
CustomText {
required property string modelData
Layout.alignment: Qt.AlignHCenter
color: root.contentColor
font.family: Config.appearance.font.family.mono // qmllint disable incompatible-type
text: Qt.formatDateTime(Time.date, modelData)
Behavior on color {
CAnim {
}
}
}
}
Rectangle {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: Tokens.padding.small ?? 2
Layout.topMargin: Tokens.padding.small ?? 2
color: root.contentColor
height: 1
implicitWidth: root.baseSize * 0.5
opacity: 0.4
radius: 1
visible: root.formatParts.dateTokens.length > 0 && root.formatParts.timeTokens.length > 0
}
Repeater {
model: root.formatParts.timeTokens
CustomText {
required property string modelData
Layout.alignment: Qt.AlignHCenter
color: root.contentColor
font.family: Config.appearance.font.family.mono // qmllint disable incompatible-type
text: {
if (modelData.includes("h"))
return Time.hourStr;
if (modelData.includes("m"))
return Time.minuteStr;
if (modelData.includes("s"))
return Time.secondStr;
return "";
}
Behavior on color {
CAnim {
}
}
}
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: root.contentColor
font.family: Config.appearance.font.family.mono // qmllint disable incompatible-type
text: Qt.formatDateTime(Time.date, "AP")
visible: Config.services.useTwelveHourClock && root.formatParts.timeTokens.length > 0
Behavior on color {
CAnim {
}
}
}
}
StateLayer {
acceptedButtons: Qt.LeftButton
color: root.contentColor
onClicked: {
root.visibilities.dashboard = !root.visibilities.dashboard;
}
}
}
@@ -1,16 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Components
import qs.Services
CustomRect {
id: root
readonly property int baseSize: horizontal ? Math.max(Config.bar.height, Tokens.bar.innerSize) : Math.max(Config.bar.height, Math.floor(Tokens.bar.innerSize * 1.5))
required property bool horizontal
color: Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : width
implicitWidth: horizontal ? height : baseSize
radius: Tokens.rounding.full
}
-92
View File
@@ -1,92 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Components.Common
import qs.Helpers
import qs.Services
WidgetBase {
id: root
readonly property string currentMedia: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
readonly property int textWidth: Math.min(metrics.width, 200)
implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.normal * 2
implicitWidth: horizontal ? layout.implicitWidth + Tokens.padding.normal * 2 : baseSize
radius: Tokens.rounding.full
Behavior on implicitHeight {
enabled: !root.horizontal
Anim {
}
}
Behavior on implicitWidth {
enabled: root.horizontal
Anim {
}
}
TextMetrics {
id: metrics
font: mediatext.font
text: mediatext.text
}
GridLayout {
id: layout
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.normal
anchors.fill: parent
anchors.leftMargin: root.horizontal ? Tokens.padding.normal : 0
anchors.rightMargin: root.horizontal ? Tokens.padding.normal : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.normal
columns: root.horizontal ? -1 : 1
Behavior on implicitWidth {
Anim {
}
}
MaterialIcon {
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
animate: true
color: Players.active?.isPlaying ? Colors.palette.m3primary : Colors.palette.m3onSurface
font.pointSize: Tokens.font.size.larger
text: Players.active?.isPlaying ? "music_note" : "music_off"
}
MarqueeText {
id: mediatext
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
animate: true
color: Players.active?.isPlaying ? Colors.palette.m3primary : Colors.palette.m3onSurface
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Text.AlignHCenter
implicitHeight: root.horizontal ? root.implicitHeight : root.textWidth
implicitWidth: root.horizontal ? root.textWidth : root.implicitWidth
marqueeEnabled: false
pauseMs: 4000
text: root.currentMedia
vertical: !root.horizontal
}
}
CustomMouseArea {
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
if (!containsMouse) {
mediatext.marqueeEnabled = false;
} else {
mediatext.marqueeEnabled = true;
mediatext.anim.start();
}
}
}
}
@@ -1,14 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
import qs.Components
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
animate: true
color: Colors.palette.m3onSurface // Network.connected ? root.textColor : Colors.palette.m3error
fill: 1
font.pointSize: Tokens.font.size.larger
text: "bluetooth"
}
@@ -1,14 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
import qs.Components
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
animate: true
color: Colors.palette.m3onSurface // Network.connected ? root.textColor : Colors.palette.m3error
fill: 1
font.pointSize: Tokens.font.size.larger
text: "android_wifi_4_bar"
}
-41
View File
@@ -1,41 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Components.Common
import qs.Helpers
import qs.Services
WidgetBase {
id: root
property int countUpdates: Updates.availableUpdates
property color textColor: Colors.palette.m3onSurface
color: Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : content.implicitHeight + Tokens.spacing.small
implicitWidth: horizontal ? content.implicitWidth + Tokens.spacing.small * 2 : baseSize
radius: Tokens.rounding.full
GridLayout {
id: content
anchors.centerIn: parent
columnSpacing: Tokens.spacing.small
columns: root.horizontal ? -1 : 1
rowSpacing: -Tokens.spacing.extraSmall / 2
MaterialIcon {
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
font.pointSize: root.horizontal ? Tokens.font.size.larger : Tokens.font.size.large
text: "package_2"
}
CustomText {
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
color: root.textColor
font.pointSize: Tokens.font.size.normal
text: root.countUpdates
}
}
}
-98
View File
@@ -1,98 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
Item {
id: root
required property var bar
property color color: Colors.palette.m3primary
property Title current: text1
required property bool horizontal
// readonly property int maxWidth: 300
readonly property int maxSize: {
const otherModules = bar.children.filter(c => c.enabled && c.entryId && c.item !== this && c.entryId !== "spacer");
const otherSize = otherModules.reduce((acc, curr) => acc + (horizontal ? (curr.item?.nonAnimWidth ?? curr.width ?? 0) : (curr.item.nonAnimHeight ?? curr.height ?? 0)), 0);
return horizontal ? (bar.width - otherSize - bar.columnSpacing * (bar.children.length - 1) - bar.vPadding * 2) : (bar.height - otherSize - bar.rowSpacing * (bar.children.length - 1) - bar.vPadding * 4);
}
clip: true
implicitHeight: horizontal ? current.implicitHeight : current.implicitWidth + current.anchors.topMargin
implicitWidth: horizontal ? current.implicitWidth + current.anchors.leftMargin : current.implicitHeight + current.anchors.topMargin
Behavior on implicitHeight {
enabled: !root.horizontal
Anim {
type: Anim.DefaultEffects
}
}
Behavior on implicitWidth {
enabled: root.horizontal
Anim {
type: Anim.DefaultEffects
}
}
Title {
id: text1
}
Title {
id: text2
}
TextMetrics {
id: metrics
elide: Qt.ElideRight
elideWidth: root.maxSize - (root.horizontal ? root.current.anchors.leftMargin : 0)
font.family: "Rubik"
font.pointSize: Tokens.font.size.normal
text: Hypr.activeToplevel?.title ?? qsTr("Desktop")
onElideWidthChanged: root.current.text = elidedText
onTextChanged: {
const next = root.current === text1 ? text2 : text1;
next.text = elidedText;
root.current = next;
}
}
component Title: CustomText {
id: text
anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter
anchors.left: root.horizontal ? parent.left : undefined
anchors.leftMargin: root.horizontal ? Tokens.spacing.small : 0
anchors.top: root.horizontal ? undefined : parent.top
anchors.topMargin: root.horizontal ? 0 : Tokens.spacing.small
anchors.verticalCenter: root.horizontal ? parent.verticalCenter : undefined
color: root.color
font.family: metrics.font.family
font.pointSize: metrics.font.pointSize
height: root.horizontal ? implicitHeight : implicitWidth
opacity: root.current === this ? 1 : 0
width: root.horizontal ? implicitWidth : implicitHeight
Behavior on opacity {
Anim {
}
}
transform: [
Translate {
x: !root.horizontal ? -text.implicitWidth + text.implicitHeight : 0
},
Rotation {
angle: root.horizontal ? 0 : 270
origin.x: text.implicitHeight / 2
origin.y: text.implicitHeight / 2
}
]
}
}
-385
View File
@@ -1,385 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQml.Models
import Quickshell
import Quickshell.Bluetooth
import ZShell.Components
import ZShell.Config
import qs.Services
import qs.Components
import qs.Helpers
CustomClippingRect {
id: root
readonly property list<var> btConnected: Bluetooth.devices.values.filter(d => d.connected)
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
implicitWidth: 500
ColumnLayout {
id: layout
anchors.fill: parent
anchors.margins: Tokens.padding.larger
spacing: Tokens.spacing.extraSmall / 2
CustomText {
Layout.rightMargin: Tokens.padding.extraSmall
Layout.topMargin: Tokens.padding.normal
font.pointSize: Tokens.font.size.large
text: qsTr("Bluetooth")
}
Toggle {
checked: Bluetooth.defaultAdapter?.enabled ?? false // qmllint disable unresolved-type
first: true
subtext: qsTr("Toggle bluetooth device state")
text: checked ? qsTr("Enabled") : qsTr("Disabled")
onToggled: {
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
if (adapter)
adapter.enabled = checked;
}
}
Toggle {
checked: Bluetooth.defaultAdapter?.discovering ?? false // qmllint disable unresolved-type
last: root.btConnected.length === 0
subtext: qsTr("Enable scanning for new devices")
text: qsTr("Discover")
onToggled: {
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
if (adapter)
adapter.discovering = checked;
}
}
ListView {
id: rows
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
interactive: false
section.property: "connected"
spacing: Tokens.spacing.extraSmall / 2
add: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
}
delegate: DelegateChooser {
role: "isDivider"
DelegateChoice {
roleValue: true
delegate: StatusDivider {
}
}
DelegateChoice {
delegate: BluetoothItem {
list: rows
width: rows.width
}
}
}
displaced: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
model: ScriptModel {
objectProp: "address"
values: {
const byName = (a, b) => a.name.localeCompare(b.name);
return [...[...Bluetooth.devices.values].filter(d => d.connected).sort(byName).slice(0, 5),
{
isDivider: true
},
...[...Bluetooth.devices.values].filter(d => !d.connected).sort(byName).slice(0, 5)];
}
}
move: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
}
section.delegate: Rectangle {
required property string section
color: "transparent"
height: childrenRect.height
width: ListView.view.width
CustomText {
text: parent.section
}
}
}
}
component BluetoothItem: ConnectedRect {
id: btItem
readonly property int connectedCount: root.btConnected.length
readonly property bool connecting: Network.connectingNetwork === modelData
property int horizontalPadding: Tokens.padding.largeIncreased
readonly property bool inConnectedGroup: index < connectedCount
required property int index
required property ListView list
readonly property bool loading: modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting // qmllint disable unresolved-type
required property BluetoothDevice modelData
property int verticalPadding: Tokens.padding.smaller
first: !inConnectedGroup && index === connectedCount + 1
implicitHeight: rowLayout.implicitHeight + verticalPadding * 2
last: inConnectedGroup ? index === connectedCount - 1 : index === list.count - 1
radius: Tokens.rounding.small
Timer {
interval: 50
running: btItem.ListView.delayRemove
onTriggered: btItem.ListView.delayRemove = false
}
Connections {
function onConnectedChanged(): void {
if (!btItem.modelData.connected)
btItem.ListView.delayRemove = true;
}
function onPairedChanged(): void {
if (btItem.modelData.paired)
btItem.modelData.connect();
}
target: btItem.modelData
}
RowLayout {
id: rowLayout
anchors.fill: parent
anchors.leftMargin: btItem.horizontalPadding
anchors.rightMargin: btItem.horizontalPadding
Column {
id: column
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Tokens.spacing.extraSmall
Layout.rightMargin: Tokens.spacing.extraSmall
elide: Text.ElideRight
text: btItem.modelData.name
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font.pointSize: Tokens.font.size.small
text: btItem.modelData.connected ? (btItem.modelData.batteryAvailable ? qsTr("%1 battery left").arg(Math.round(btItem.modelData.battery * 100) + "%") : qsTr("Battery status unknown")) : btItem.modelData.address
}
}
Item {
Layout.alignment: Qt.AlignRight
implicitHeight: icon.height
implicitWidth: icon.width
MaterialIcon {
id: icon
font.pointSize: Tokens.font.size.large
opacity: btItem.connecting ? 0 : 1
text: btItem.modelData.batteryAvailable ? Icons.getBatteryIcon(btItem.modelData.battery) : "battery_unknown"
visible: btItem.modelData.state === BluetoothDeviceState.Connected // qmllint disable unresolved-type
Behavior on opacity {
Anim {
}
}
}
Loader {
active: opacity > 0
anchors.fill: parent
opacity: btItem.connecting ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
running: true
}
}
}
ButtonRow {
spacing: Tokens.spacing.small
IconTextButton {
font.pointSize: Tokens.font.size.small
icon: btItem.modelData.connected ? "link_off" : btItem.modelData.paired ? "link" : "add_link"
text: btItem.modelData.connected ? qsTr("Disconnect") : btItem.modelData.paired ? qsTr("Connect") : qsTr("Pair")
onClicked: {
if (btItem.modelData.connected)
btItem.modelData.disconnect();
else if (btItem.modelData.paired)
btItem.modelData.connect();
else if (!btItem.modelData.paired)
btItem.modelData.pair();
else if (btItem.modelData.pairing)
btItem.modelData.cancelPair();
}
}
}
}
}
component ConnectedRect: CustomRect {
id: bg
property bool first
property bool last
bottomLeftRadius: last ? Tokens.rounding.large : Tokens.rounding.extraSmall
bottomRightRadius: last ? Tokens.rounding.large : Tokens.rounding.extraSmall
color: Colors.tPalette.m3surfaceContainer
topLeftRadius: first ? Tokens.rounding.large : Tokens.rounding.extraSmall
topRightRadius: first ? Tokens.rounding.large : Tokens.rounding.extraSmall
Behavior on bottomLeftRadius {
Anim {
}
}
Behavior on bottomRightRadius {
Anim {
}
}
Behavior on topLeftRadius {
Anim {
}
}
Behavior on topRightRadius {
Anim {
}
}
}
component StatusDivider: CustomText {
bottomPadding: Tokens.spacing.extraSmall
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.smaller
text: {
const devices = Bluetooth.devices.values; // qmllint disable unresolved-type
const connectedCount = devices.filter(d => d.connected).length;
let available = qsTr("%1 device%2 available").arg(devices.length - connectedCount).arg(devices.length === 1 ? "" : "s");
return available;
}
topPadding: Tokens.spacing.small
}
component Toggle: CustomSwitch {
id: toggleItem
readonly property alias bg: bg
property alias first: bg.first
property alias last: bg.last
property string subtext
Layout.fillWidth: true
cLayer: 2
horizontalPadding: Tokens.padding.largeIncreased
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + verticalPadding * 2
implicitWidth: implicitContentWidth + implicitIndicatorWidth + horizontalPadding * 2
indicator.anchors.right: right
indicator.anchors.rightMargin: toggleItem.horizontalPadding
indicator.anchors.verticalCenter: verticalCenter
verticalPadding: Tokens.padding.normal
background: ConnectedRect {
id: bg
StateLayer {
id: stateLayer
manualPressOverride: toggleItem.pressed
}
}
contentItem: Item {
anchors.left: parent.left
anchors.leftMargin: toggleItem.horizontalPadding
anchors.right: toggleItem.indicator.left
anchors.rightMargin: Tokens.spacing.normal
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
Column {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 0
CustomText {
id: label
anchors.left: parent.left
anchors.right: parent.right
elide: Text.ElideRight
font: {
const f = Qt.font(label.font);
f.pointSize = Tokens.font.size.smaller;
return f;
}
text: toggleItem.text
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font: {
const f = Qt.font(subtext.font);
f.pointSize = Tokens.font.size.small;
return f;
}
text: toggleItem.subtext
visible: toggleItem.subtext
}
}
}
onPressed: stateLayer.press(stateLayer.mouseX, stateLayer.mouseY)
}
}
-87
View File
@@ -1,87 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Components
import ZShell.Config
Item {
id: root
required property real borderThickness
readonly property alias content: content
readonly property bool horizontal: position !== "left"
// property real offsetScale: y > 0 || content.hasCurrent ? 0 : 1
property real offsetScale: {
if (content.hasCurrent)
return 0;
if (position === "top")
return y > 0 ? 0 : 1;
if (position === "bottom")
return 1;
return x > 0 ? 0 : 1;
}
required property string position
required property ShellScreen screen
anchors.bottom: position === "bottom" ? parent.bottom : undefined
clip: true
implicitHeight: horizontal ? content.implicitHeight * (1 - offsetScale) : content.implicitHeight
implicitWidth: horizontal ? content.implicitWidth : content.implicitWidth * (1 - offsetScale)
visible: width > 0 && height > 0
x: {
if (!horizontal)
return 0;
const off = content.currentCenter - borderThickness - content.nonAnimWidth / 2;
const diff = parent.width - Math.floor(off + content.nonAnimWidth);
if (diff < 0)
return off + diff;
return Math.floor(Math.max(off, 0));
}
y: {
if (horizontal)
return 0;
const off = content.currentCenter - borderThickness - content.nonAnimHeight / 2;
const diff = parent.height - Math.floor(off + content.nonAnimHeight);
if (diff < 0)
return off + diff;
return Math.max(off, 0);
}
Behavior on offsetScale {
Anim {
}
}
Behavior on x {
enabled: !root.horizontal || root.offsetScale < 1
Anim {
duration: content.animLength
easing.bezierCurve: content.animCurve
}
}
Behavior on y {
enabled: root.horizontal || root.offsetScale < 1
Anim {
duration: content.animLength
easing.bezierCurve: content.animCurve
}
}
Wrapper {
id: content
anchors.bottom: root.position === "bottom" ? parent.bottom : undefined
anchors.bottomMargin: (-implicitHeight - 5) * root.offsetScale
anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined
anchors.left: root.horizontal ? undefined : parent.left
anchors.leftMargin: (-implicitWidth - 5) * root.offsetScale
anchors.top: root.position === "top" ? parent.top : undefined
anchors.topMargin: (-implicitHeight - 5) * root.offsetScale
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
offsetScale: root.offsetScale
screen: root.screen
}
}
@@ -1,71 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
import qs.Helpers
import qs.Components
CustomRect {
id: root
readonly property bool closing: PopupManager.closed
readonly property url currentPopup: PopupManager.currentPopup
property bool shouldBeVisible: loader.status === Loader.Ready
color: Qt.alpha(Colors.palette.m3shadow, 0.3)
opacity: shouldBeVisible && !closing ? 1 : 0
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
onVisibleChanged: {
if (!visible) {
PopupManager.requestClose();
PopupManager.currentPopup = "";
}
}
CustomMouseArea {
anchors.fill: parent
hoverEnabled: true
preventStealing: true
propagateComposedEvents: false
onClicked: {
const insideItemWidth = mouseX < loader.x + loader.item.width && mouseX > loader.x;
const insideItemHeight = mouseY < loader.y + loader.item.height && mouseY > loader.y;
if (insideItemHeight && insideItemWidth)
return;
PopupManager.requestClose();
}
}
Loader {
id: loader
anchors.centerIn: parent
Connections {
function onCurrentPopupChanged(): void {
if (PopupManager.currentPopup) {
loader.setSource(PopupManager.currentPopup, PopupManager.currentPopupProps);
} else {
loader.source = null;
}
}
target: PopupManager
}
}
Connections {
function onNetworkConnected(): void {
PopupManager.requestClose();
}
target: Network
}
}
-103
View File
@@ -1,103 +0,0 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Services
import qs.Components
import qs.Helpers
CustomClippingRect {
id: root
required property var network
color: Colors.palette.m3surfaceContainer
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
implicitWidth: layout.implicitWidth + layout.anchors.margins * 2
radius: Tokens.rounding.large
ColumnLayout {
id: layout
anchors.fill: parent
anchors.margins: Tokens.padding.extraLarge
spacing: Tokens.spacing.normal
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
font.pointSize: Tokens.font.size.extraLarge * 2
text: "lock"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant
text: qsTr("Connect to %1").arg(root.network.name)
}
CustomTextField {
id: passwordField
Layout.fillWidth: true
Layout.preferredWidth: 400
echoMode: CustomTextField.Password
errorText: qsTr("Failed to connect with given password")
isError: Network.failedNetwork
leadingIcon: "lock"
passwordMaskDelay: 5000
placeholderText: qsTr("Input password")
}
ButtonRow {
id: buttonRow
Layout.fillWidth: true
spacing: Tokens.spacing.smaller
IconTextButton {
fillWidth: true
font.pointSize: Tokens.font.size.normal
icon: "close"
inactiveColor: Colors.layer(Colors.palette.m3surfaceContainerHighest, 2)
inactiveOnColor: Colors.palette.m3onSurfaceVariant
isRound: true
isToggle: false
shapeMorph: true
text: "Cancel"
onClicked: PopupManager.requestClose()
}
IconTextButton {
fillWidth: true
font.pointSize: Tokens.font.size.normal
icon: Network.connectingNetwork ? "" : "check"
inactiveColor: Network.connectingNetwork ? Colors.palette.m3primaryContainer : Network.failedNetwork ? Colors.palette.m3error : Colors.palette.m3primary
inactiveOnColor: Network.connectingNetwork ? Colors.palette.m3onPrimaryContainer : Network.failedNetwork ? Colors.palette.m3onError : Colors.palette.m3onPrimary
isRound: true
isToggle: false
shapeMorph: true
text: Network.connectingNetwork ? "" : "Apply"
onClicked: Network.requestConnect(root.network, passwordField.text)
Loader {
active: opacity > 0
anchors.centerIn: parent
opacity: Network.connectingNetwork ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
implicitSize: buttonRow.height - Tokens.padding.normal
running: true
}
}
}
}
}
}
@@ -1,22 +0,0 @@
pragma Singleton
import Quickshell
import QtQuick
Singleton {
id: root
property bool closed: true
property url currentPopup: ""
property var currentPopupProps: ({})
function requestClose(): void {
closed = true;
}
function requestOpen(source: url, properties = {}): void {
currentPopupProps = properties;
currentPopup = source;
closed = false;
}
}
-362
View File
@@ -1,362 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Components
import ZShell.Config
import qs.Services
import qs.Modules.Bar.Popouts.Network
import qs.Components
import qs.Helpers
CustomClippingRect {
id: root
required property var wrapper
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: networkPopContent.height + networkPopContent.anchors.margins * 2
implicitWidth: 500 + 8 * 2
radius: (20 - Tokens.padding.small) * Tokens.rounding.scale
Component.onCompleted: Network.active = true
Component.onDestruction: Network.active = false
ColumnLayout {
id: networkPopContent
anchors.left: parent.left
anchors.margins: Tokens.padding.large
anchors.right: parent.right
anchors.top: parent.top
spacing: Tokens.spacing.extraSmall / 2
CustomText {
Layout.preferredHeight: visible ? implicitHeight : 0
Layout.rightMargin: Tokens.padding.extraSmall
font.pointSize: Tokens.font.size.large
text: qsTr("Wifi")
}
Toggle {
Layout.preferredHeight: visible ? implicitHeight : 0
checked: Network.wifiEnabled
first: true
last: Network.connectedNetworks.length === 0
subtext: qsTr("Toggle WiFi device state")
text: checked ? qsTr("Enabled") : qsTr("Disabled")
onToggled: Network.setWifi(checked)
}
ListView {
id: rows
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
interactive: false
section.property: "connected"
spacing: Tokens.spacing.extraSmall / 2
add: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
}
delegate: DelegateChooser {
role: "isDivider"
DelegateChoice {
roleValue: true
delegate: StatusDivider {
}
}
DelegateChoice {
delegate: NetworkItem {
list: rows
width: rows.width
}
}
}
displaced: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
model: ScriptModel {
values: {
const byStrengthName = (a, b) => b.signalStrength - a.signalStrength || a.name.localeCompare(b.name);
const cNetworks = [...Network.connectedNetworks];
const kNetworks = [...Network.knownNetworks];
const uNetworks = [...Network.unknownNetworks];
return [...cNetworks.sort(byStrengthName), ...(kNetworks.length > 0 ? [
{
isDivider: true,
type: "known"
},
...kNetworks.sort(byStrengthName)] : []), ...(uNetworks.length > 0 ? [
{
isDivider: true,
type: "unknown"
},
...uNetworks.sort(byStrengthName)] : [])];
}
}
move: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
}
section.delegate: Rectangle {
required property string section
color: "transparent"
height: childrenRect.height
width: ListView.view.width
CustomText {
text: parent.section
}
}
}
}
NetworkOverlay {
anchors.fill: parent
}
component ConnectedRect: CustomRect {
id: bg
property bool first
property bool last
bottomLeftRadius: last ? Tokens.rounding.large : Tokens.rounding.extraSmall
bottomRightRadius: last ? Tokens.rounding.large : Tokens.rounding.extraSmall
color: Colors.tPalette.m3surfaceContainer
topLeftRadius: first ? Tokens.rounding.large : Tokens.rounding.extraSmall
topRightRadius: first ? Tokens.rounding.large : Tokens.rounding.extraSmall
}
component NetworkItem: ConnectedRect {
id: networkItem
readonly property int connectedCount: Network.connectedNetworks.length
readonly property bool connecting: Network.connectingNetwork === modelData
property int horizontalPadding: Tokens.padding.largeIncreased
readonly property bool inConnectedGroup: index < connectedCount
// sectioning first / last item in list reminder
// if index === network.connectedNetworks.length - 1
// if index === network.connectedNetworks.length - 1 + network.knownNetworks.length - 2
// if index === network.connectedNetworks.length - 1 + network.knownNetworks.length
// list.model.values.length - 1
required property int index
readonly property bool isSecure: Network.isSecure(modelData.security)
required property ListView list
required property var modelData
property int verticalPadding: Tokens.padding.smaller
Layout.fillWidth: true
first: !inConnectedGroup && index === connectedCount + 1
implicitHeight: rowLayout.implicitHeight + verticalPadding * 2
last: inConnectedGroup ? index === connectedCount - 1 : index === list.count - 1
radius: Tokens.rounding.small
RowLayout {
id: rowLayout
Layout.preferredHeight: visible ? rowLayout.implicitHeight + Tokens.padding.smaller * 2 : 0
anchors.fill: parent
anchors.leftMargin: networkItem.horizontalPadding
anchors.rightMargin: networkItem.horizontalPadding
Column {
id: column
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Tokens.spacing.extraSmall
Layout.rightMargin: Tokens.spacing.extraSmall
color: networkItem.modelData.active ? Colors.palette.m3primary : Colors.palette.m3onSurface
elide: Text.ElideRight
text: networkItem.modelData.name
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font.pointSize: Tokens.font.size.small
text: networkItem.modelData.connected ? qsTr("Connected: %1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%") : qsTr("%1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%")
}
}
Item {
Layout.alignment: Qt.AlignRight
implicitHeight: icon.height
implicitWidth: icon.width
MaterialIcon {
id: icon
color: networkItem.modelData.active ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.large
opacity: networkItem.connecting ? 0 : 1
text: Icons.getNetworkIcon(networkItem.modelData.signalStrength * 100, networkItem.isSecure)
Behavior on opacity {
Anim {
}
}
}
Loader {
active: opacity > 0
anchors.fill: parent
opacity: networkItem.connecting ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
running: true
}
}
}
}
StateLayer {
color: networkItem.modelData.active ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
onClicked: {
if (networkItem.isSecure && !networkItem.known) {
PopupManager.requestOpen(Qt.resolvedUrl("Network/PSKOverlay.qml"), {
network: networkItem.modelData
});
} else {
Network.requestConnect(networkItem.modelData);
}
}
}
}
component StatusDivider: CustomText {
required property var modelData
bottomPadding: Tokens.spacing.extraSmall
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.smaller
text: {
if (modelData.type === "known") {
const count = Network.knownNetworks.filter(n => !n.connected).length;
return qsTr("%1 known network%2").arg(count).arg(count === 1 ? "" : "s");
}
if (modelData.type === "unknown") {
const count = Network.unknownNetworks.filter(n => !n.connected).length;
return qsTr("%1 network%2 available").arg(count).arg(count === 1 ? "" : "s");
}
return qsTr("object unknown");
}
topPadding: Tokens.spacing.small
}
component Toggle: CustomSwitch {
id: toggleItem
readonly property alias bg: bg
property alias first: bg.first
property alias last: bg.last
property string subtext
Layout.fillWidth: true
cLayer: 2
horizontalPadding: Tokens.padding.largeIncreased
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + verticalPadding * 2
implicitWidth: implicitContentWidth + implicitIndicatorWidth + horizontalPadding * 2
indicator.anchors.right: right
indicator.anchors.rightMargin: toggleItem.horizontalPadding
indicator.anchors.verticalCenter: verticalCenter
verticalPadding: Tokens.padding.normal
background: ConnectedRect {
id: bg
StateLayer {
id: stateLayer
manualPressOverride: toggleItem.pressed
}
}
contentItem: Item {
anchors.left: parent.left
anchors.leftMargin: toggleItem.horizontalPadding
anchors.right: toggleItem.indicator.left
anchors.rightMargin: Tokens.spacing.normal
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
Column {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 0
CustomText {
id: label
anchors.left: parent.left
anchors.right: parent.right
elide: Text.ElideRight
font: {
const f = Qt.font(label.font);
f.pointSize = Tokens.font.size.smaller;
return f;
}
text: toggleItem.text
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font: {
const f = Qt.font(subtext.font);
f.pointSize = Tokens.font.size.small;
return f;
}
text: toggleItem.subtext
visible: toggleItem.subtext
}
}
}
onPressed: stateLayer.press(stateLayer.mouseX, stateLayer.mouseY)
}
}
+56
View File
@@ -0,0 +1,56 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Components
import ZShell.Config
Item {
id: root
required property real borderThickness
readonly property alias content: content
property real offsetScale: y > 0 || content.hasCurrent ? 0 : 1
required property ShellScreen screen
clip: true
implicitHeight: content.implicitHeight * (1 - offsetScale)
implicitWidth: content.implicitWidth
visible: width > 0 && height > 0
x: {
const off = content.currentCenter - borderThickness - content.nonAnimWidth / 2;
const diff = parent.width - Math.floor(off + content.nonAnimWidth);
if (diff < 0)
return off + diff;
return Math.floor(Math.max(off, 0));
}
Behavior on offsetScale {
Anim {
}
}
Behavior on x {
enabled: root.offsetScale < 1
Anim {
duration: content.animLength
easing.bezierCurve: content.animCurve
}
}
Behavior on y {
Anim {
duration: content.animLength
easing.bezierCurve: content.animCurve
}
}
Wrapper {
id: content
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: (-implicitHeight - 5) * root.offsetScale
offsetScale: root.offsetScale
screen: root.screen
}
}
+44
View File
@@ -0,0 +1,44 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Modules
import qs.Helpers
import qs.Components
import qs.Services
CustomRect {
id: root
required property RowLayout loader
required property Wrapper popouts
required property PersistentProperties visibilities
color: visibilities.dashboard ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: timeText.contentWidth + Tokens.padding.normal * 2
radius: Tokens.rounding.full
CustomText {
id: timeText
anchors.centerIn: parent
color: root.visibilities.dashboard ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font: Config.appearance.font.family.mono
text: Time.dateStr
Behavior on color {
CAnim {
}
}
}
StateLayer {
acceptedButtons: Qt.LeftButton
color: root.visibilities.dashboard ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
onClicked: {
root.visibilities.dashboard = !root.visibilities.dashboard;
}
}
}
@@ -5,6 +5,10 @@ import Quickshell.Services.SystemTray
import QtQuick import QtQuick
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Modules.WSOverview
import qs.Modules.Network
import qs.Modules.SysTray.Popouts
import qs.Modules.Updates
Item { Item {
id: root id: root
@@ -80,13 +84,6 @@ Item {
} }
} }
Popout {
name: "bluetooth"
sourceComponent: BluetoothPopout {
}
}
Popout { Popout {
name: "updates" name: "updates"
@@ -103,10 +100,10 @@ Item {
readonly property bool shouldBeActive: root.popouts.currentName === name readonly property bool shouldBeActive: root.popouts.currentName === name
active: false active: false
anchors.centerIn: opacity === 1 ? undefined : parent // anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: parent.horizontalCenter // anchors.top: parent.top
anchors.top: opacity === 1 ? parent.top : undefined // anchors.topMargin: 5
anchors.topMargin: 5 anchors.centerIn: parent
opacity: 0 opacity: 0
scale: 0.8 scale: 0.8
+1 -1
View File
@@ -41,7 +41,7 @@ Item {
Item { Item {
id: view id: view
readonly property int currentIndex: root.dashState?.currentTab readonly property int currentIndex: root.state.currentTab
readonly property Item currentItem: row.children[currentIndex] readonly property Item currentItem: row.children[currentIndex]
anchors.fill: parent anchors.fill: parent
+151 -144
View File
@@ -1,12 +1,10 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Services.Mpris
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Shapes import QtQuick.Shapes
import QtQuick.Effects import QtQuick.Effects
import ZShell.Components
import ZShell.Services import ZShell.Services
import ZShell.Config import ZShell.Config
import qs.Daemons import qs.Daemons
@@ -16,7 +14,7 @@ import qs.Services
Item { Item {
id: root id: root
readonly property bool hasUnknownLength: (Players.active?.length ?? 0) > 2147483647
property real playerProgress: { property real playerProgress: {
const active = Players.active; const active = Players.active;
return active?.length ? (active.position % active.length) / active.length : 0; return active?.length ? (active.position % active.length) / active.length : 0;
@@ -25,19 +23,7 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
implicitHeight: layout.implicitHeight implicitHeight: cover.height + rowHeight * 2
function lengthStr(length: int): string {
if (length < 0)
return "-1:-1";
const hours = Math.floor(length / 3600);
const mins = Math.floor((length % 3600) / 60);
const secs = Math.floor(length % 60).toString().padStart(2, "0");
if (hours > 0)
return `${hours}:${mins.toString().padStart(2, "0")}:${secs}`;
return `${mins}:${secs}`;
}
Behavior on playerProgress { Behavior on playerProgress {
Anim { Anim {
@@ -46,10 +32,11 @@ Item {
} }
Timer { Timer {
running: Players.active?.isPlaying ?? false
interval: Config.dashboard.mediaUpdateInterval interval: Config.dashboard.mediaUpdateInterval
triggeredOnStart: true
repeat: true repeat: true
running: Players.active?.isPlaying ?? false
triggeredOnStart: true
onTriggered: Players.active?.positionChanged() onTriggered: Players.active?.positionChanged()
} }
@@ -85,7 +72,7 @@ Item {
const n = values.length; const n = values.length;
if (n < 2) if (n < 2)
return ""; return "";
const h = layout.height + shape.strokeWidth / 2; const h = layout.height + shape.strokeWidth / 2;
const w = layout.width + shape.strokeWidth; const w = layout.width + shape.strokeWidth;
@@ -139,11 +126,58 @@ Item {
} }
} }
Shape {
preferredRendererType: Shape.CurveRenderer
ShapePath {
capStyle: Tokens.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
strokeWidth: Config.dashboard.sizes.mediaProgressThickness
Behavior on strokeColor {
CAnim {
}
}
PathAngleArc {
centerX: cover.x + cover.width / 2
centerY: cover.y + cover.height / 2
radiusX: (cover.width + Config.dashboard.sizes.mediaProgressThickness) / 2 + Tokens.spacing.small
radiusY: (cover.height + Config.dashboard.sizes.mediaProgressThickness) / 2 + Tokens.spacing.small
startAngle: -90 - Config.dashboard.sizes.mediaProgressSweep / 2
sweepAngle: Config.dashboard.sizes.mediaProgressSweep
}
}
ShapePath {
capStyle: Tokens.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: Colors.palette.m3primary
strokeWidth: Config.dashboard.sizes.mediaProgressThickness
Behavior on strokeColor {
CAnim {
}
}
PathAngleArc {
centerX: cover.x + cover.width / 2
centerY: cover.y + cover.height / 2
radiusX: (cover.width + Config.dashboard.sizes.mediaProgressThickness) / 2 + Tokens.spacing.small
radiusY: (cover.height + Config.dashboard.sizes.mediaProgressThickness) / 2 + Tokens.spacing.small
startAngle: -90 - Config.dashboard.sizes.mediaProgressSweep / 2
sweepAngle: Config.dashboard.sizes.mediaProgressSweep * root.playerProgress
}
}
}
RowLayout { RowLayout {
id: layout id: layout
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
implicitHeight: root.implicitHeight
CustomClippingRect { CustomClippingRect {
id: cover id: cover
@@ -177,174 +211,147 @@ Item {
} }
} }
ColumnLayout { CustomRect {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: Tokens.spacing.large
Layout.rightMargin: Tokens.spacing.large
Layout.preferredHeight: childrenRect.height Layout.preferredHeight: childrenRect.height
spacing: Tokens.spacing.normal
MarqueeText { MarqueeText {
id: title id: title
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
color: Colors.palette.m3primary color: Colors.palette.m3primary
font.pointSize: Tokens.font.size.normal font.pointSize: Tokens.font.size.normal
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
pauseMs: 4000 pauseMs: 4000
text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title") text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
implicitWidth: parent.width - Tokens.padding.large * 4 width: parent.width - Tokens.padding.large * 4
} }
MarqueeText { MarqueeText {
id: album id: album
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: title.bottom
anchors.topMargin: Tokens.spacing.small
color: Colors.palette.m3outline color: Colors.palette.m3outline
font.pointSize: Tokens.font.size.small font.pointSize: Tokens.font.size.small
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
pauseMs: 4000 pauseMs: 4000
text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album") text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album")
implicitWidth: parent.width - Tokens.padding.large * 4 width: parent.width - Tokens.padding.large * 4
} }
MarqueeText { MarqueeText {
id: artist id: artist
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: album.bottom
anchors.topMargin: Tokens.spacing.small
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
pauseMs: 4000 pauseMs: 4000
text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist") text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist")
implicitWidth: parent.width - Tokens.padding.large * 4 width: parent.width - Tokens.padding.large * 4
} }
RowLayout { RowLayout {
id: positionSliderLayout id: controls
Layout.topMargin: Tokens.spacing.extraLargeIncreased
Layout.fillWidth: true anchors.horizontalCenter: parent.horizontalCenter
anchors.top: artist.bottom
anchors.topMargin: Tokens.spacing.smaller
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
TextMetrics { Control {
id: timeMetrics function onClicked(): void {
Players.active?.previous();
text: Players.active ? root.lengthStr(Math.max(Players.active.position, root.hasUnknownLength ? 0 : Players.active.length)).replace(/[1-9]/g, "0") : "00:00"
}
CustomText {
id: positionLabel
Layout.preferredWidth: timeMetrics.width
text: root.lengthStr(Players.active?.position ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
}
CustomSlider {
id: positionSlider
Layout.fillWidth: true
value: Players.active ? Players.active.position / (Players.active.length || 1) : 0
enabled: (Players.active?.canSeek ?? false) && !root.hasUnknownLength
wavy: true
animateWave: Players.active?.isPlaying ?? false
waveFrequency: 5
waveDuration: 2000
interactionOnMove: false
onInteraction: value => {
const active = Players.active;
if (active?.canSeek && active?.positionSupported)
active.position = value * active.length;
} }
Binding {
target: positionLabel
property: "text"
value: root.lengthStr(positionSlider.pos * (Players.active?.length ?? 0))
when: positionSlider.dragging
}
}
CustomText { canUse: Players.active?.canGoPrevious ?? false
Layout.preferredWidth: timeMetrics.width
text: root.hasUnknownLength ? "--:--" : root.lengthStr(Players.active?.length ?? -1)
color: Colors.palette.m3onSurfaceVariant
font: timeMetrics.font
horizontalAlignment: Text.AlignHCenter
}
}
ButtonRow {
Layout.topMargin: Tokens.spacing.largeIncreased
Layout.fillWidth: true
spacing: Tokens.spacing.extraSmall
Layout.leftMargin: positionSliderLayout.implicitWidth / 4
Layout.rightMargin: positionSliderLayout.implicitWidth / 4
IconButton {
type: IconButton.Tonal
icon: "shuffle"
isRound: true
shapeMorph: true
checked: Players.active?.shuffle ?? false
enabled: Players.active?.shuffleSupported
onClicked: Players.active.shuffle = !Players.active?.shuffle
implicitWidth: Math.round(implicitHeight * 0.9)
}
IconButton {
id: previousBtn
type: IconButton.Tonal
icon: "skip_previous" icon: "skip_previous"
isRound: true
shapeMorph: true
enabled: Players.active?.canGoPrevious
onClicked: Players.active?.previous()
} }
IconButton { Control {
id: playPauseBtn function onClicked(): void {
Players.active?.togglePlaying();
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
isRound: true
shapeMorph: true
fillWidth: true
checked: Players.active?.isPlaying ?? false
enabled: Players.active?.canTogglePlaying
onClicked: Players.active?.togglePlaying()
}
IconButton {
id: nextBtn
type: IconButton.Tonal
icon: "skip_next"
isRound: true
shapeMorph: true
enabled: Players.active?.canGoNext
onClicked: Players.active?.next()
}
IconButton {
type: IconButton.Tonal
icon: Players.active?.loopState === MprisLoopState.Track ? "repeat_one" : "repeat"
isRound: true
shapeMorph: true
checked: Players.active?.loopState === MprisLoopState.Track || Players.active?.loopState === MprisLoopState.Playlist
enabled: Players.active?.loopSupported
onClicked: {
const state = Players.active.loopState;
if (state === MprisLoopState.None)
Players.active.loopState = MprisLoopState.Track;
else if (state === MprisLoopState.Track)
Players.active.loopState = MprisLoopState.Playlist;
else
Players.active.loopState = MprisLoopState.None;
} }
implicitWidth: Math.round(implicitHeight * 0.9)
canUse: Players.active?.canTogglePlaying ?? false
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
}
Control {
function onClicked(): void {
Players.active?.next();
}
canUse: Players.active?.canGoNext ?? false
icon: "skip_next"
} }
} }
} }
} }
component Control: CustomRect {
id: control
required property bool canUse
required property string icon
property int level: 1
property string set_color: "Secondary"
function onClicked(): void {
}
Layout.preferredWidth: implicitWidth + (controlState.pressed ? Tokens.padding.normal * 2 : 0)
color: canUse ? Colors.palette[`m3${set_color.toLowerCase()}`] : Colors.palette[`m3${set_color.toLowerCase()}Container`]
implicitHeight: implicitWidth
implicitWidth: Math.max(icon.implicitHeight, icon.implicitHeight) + Tokens.padding.small
radius: Tokens.rounding.full
Behavior on Layout.preferredWidth {
Anim {
duration: Tokens.anim.durations.expressiveFastSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
}
}
Behavior on radius {
Anim {
duration: Tokens.anim.durations.expressiveFastSpatial
easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
}
}
Elevation {
anchors.fill: parent
level: controlState.containsMouse && !controlState.pressed ? control.level + 1 : control.level
radius: parent.radius
z: -1
}
StateLayer {
id: controlState
color: control.canUse ? Colors.palette[`m3on${control.set_color}`] : Colors.palette[`m3on${control.set_color}Container`]
enabled: control.canUse
onClicked: {
control.onClicked();
}
// radius: Tokens.rounding.full
}
MaterialIcon {
id: icon
anchors.centerIn: parent
anchors.verticalCenterOffset: font.pointSize * 0.05
animate: true
color: control.canUse ? Colors.palette[`m3on${control.set_color}`] : Colors.palette[`m3on${control.set_color}Container`]
fill: control.canUse ? 1 : 0
font.pointSize: Tokens.font.size.large
text: control.icon
}
}
} }
+2 -12
View File
@@ -16,16 +16,9 @@ Item {
} }
readonly property real nonAnimHeight: state === "visible" ? (content.item?.nonAnimHeight ?? 0) : 0 readonly property real nonAnimHeight: state === "visible" ? (content.item?.nonAnimHeight ?? 0) : 0
required property real offsetScale required property real offsetScale
required property string position
readonly property bool shouldBeActive: root.visibilities.dashboard && Config.dashboard.enabled readonly property bool shouldBeActive: root.visibilities.dashboard && Config.dashboard.enabled
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.bottom: position === "bottom" ? parent.bottom : undefined
anchors.bottomMargin: position === "bottom" ? (-implicitHeight - 5) * offsetScale : 0
anchors.left: position === "left" ? parent.left : undefined
anchors.leftMargin: position === "left" ? (-implicitWidth - 5) * offsetScale : 0
anchors.top: position === "top" ? parent.top : undefined
anchors.topMargin: position === "top" ? (-implicitHeight - 5) * offsetScale : 0
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
implicitWidth: content.implicitWidth || 854 implicitWidth: content.implicitWidth || 854
opacity: 1 - offsetScale opacity: 1 - offsetScale
@@ -35,11 +28,8 @@ Item {
id: content id: content
active: root.shouldBeActive || root.visible active: root.shouldBeActive || root.visible
anchors.bottom: root.position === "top" ? parent.bottom : undefined anchors.bottom: parent.bottom
anchors.horizontalCenter: root.position !== "left" ? parent.horizontalCenter : undefined anchors.horizontalCenter: parent.horizontalCenter
anchors.right: root.position !== "left" ? undefined : parent.right
anchors.top: root.position === "bottom" ? parent.top : undefined
anchors.verticalCenter: root.position === "left" ? undefined : parent.verticalCenter
sourceComponent: Content { sourceComponent: Content {
dashState: root.dashState dashState: root.dashState
+1 -1
View File
@@ -12,7 +12,7 @@ Item {
property real offsetScale: shouldBeActive ? 0 : 1 property real offsetScale: shouldBeActive ? 0 : 1
required property var panels required property var panels
required property ShellScreen screen required property ShellScreen screen
readonly property bool shouldBeActive: visibilities.dock && Config.dock.enabled readonly property bool shouldBeActive: visibilities.dock && Config.dock.enable
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.bottomMargin: (-implicitHeight - 5) * offsetScale anchors.bottomMargin: (-implicitHeight - 5) * offsetScale
@@ -1,16 +1,17 @@
import QtQuick import QtQuick
import ZShell.Config
import qs.Components import qs.Components
import qs.Modules.Bar.Components.Common
import qs.Helpers import qs.Helpers
import ZShell.Config
import qs.Services import qs.Services
WidgetBase { CustomRect {
id: root id: root
property bool tempEnabled: Hyprsunset.enabled property bool tempEnabled: Hyprsunset.enabled
color: root.tempEnabled ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer color: root.tempEnabled ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.full radius: Tokens.rounding.full
StateLayer { StateLayer {
@@ -25,7 +26,6 @@ WidgetBase {
animate: true animate: true
color: root.tempEnabled ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: root.tempEnabled ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
fill: root.tempEnabled ? 1 : 0 fill: root.tempEnabled ? 1 : 0
font.pointSize: Tokens.font.size.larger
text: root.tempEnabled ? "lightbulb" : "light_off" text: root.tempEnabled ? "lightbulb" : "light_off"
Behavior on fill { Behavior on fill {
+31 -82
View File
@@ -1,36 +1,28 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick
import Quickshell import Quickshell
import ZShell.Config import QtQuick
import qs.Components
import qs.Services
import qs.Modules.Launcher.Items
import qs.Modules.Launcher.Services import qs.Modules.Launcher.Services
import qs.Modules.Launcher.Items
import qs.Components
import ZShell.Config
import qs.Services
CustomListView { CustomListView {
id: root id: root
readonly property string displayState: stateForText(displayText)
property string displayText
readonly property string requestedState: stateForText(search.text)
required property SearchBar search required property SearchBar search
required property PersistentProperties visibilities required property PersistentProperties visibilities
function resultsForText(text: string): var { highlightFollowsCurrentItem: false
switch (stateForText(text)) { highlightRangeMode: ListView.ApplyRange
case "actions": implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
return Actions.query(text); orientation: Qt.Vertical
case "calc": preferredHighlightBegin: 0
return [0]; preferredHighlightEnd: height
case "variant": spacing: Tokens.spacing.small
return SchemeVariants.query(text); state: {
default: const text = search.text;
return Apps.search(text);
}
}
function stateForText(text: string): string {
const prefix = Config.launcher.actionPrefix; const prefix = Config.launcher.actionPrefix;
if (text.startsWith(prefix)) { if (text.startsWith(prefix)) {
for (const action of ["calc", "scheme", "variant"]) for (const action of ["calc", "scheme", "variant"])
@@ -42,20 +34,6 @@ CustomListView {
return "apps"; return "apps";
} }
function syncDisplayText(): void {
if (visibilities.launcher && requestedState === displayState)
displayText = search.text;
}
highlightFollowsCurrentItem: false
highlightRangeMode: ListView.ApplyRange
implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
orientation: Qt.Vertical
preferredHighlightBegin: 0
preferredHighlightEnd: height
spacing: Tokens.spacing.small
state: visibilities.launcher ? requestedState : displayState
verticalLayoutDirection: ListView.BottomToTop verticalLayoutDirection: ListView.BottomToTop
CustomScrollBar.vertical: CustomScrollBar { CustomScrollBar.vertical: CustomScrollBar {
@@ -66,21 +44,19 @@ CustomListView {
Anim { Anim {
from: 0 from: 0
property: "opacity" properties: "opacity,scale"
to: 1 to: 1
type: Anim.DefaultEffects
} }
} }
addDisplaced: Transition { addDisplaced: Transition {
Anim { Anim {
duration: Tokens.anim.durations.small
property: "y" property: "y"
type: Anim.StandardSmall
} }
Anim { Anim {
property: "opacity" properties: "opacity,scale"
to: 1 to: 1
type: Anim.DefaultEffects
} }
} }
displaced: Transition { displaced: Transition {
@@ -89,9 +65,8 @@ CustomListView {
} }
Anim { Anim {
property: "opacity" properties: "opacity,scale"
to: 1 to: 1
type: Anim.DefaultEffects
} }
} }
highlight: CustomRect { highlight: CustomRect {
@@ -110,7 +85,7 @@ CustomListView {
} }
} }
model: ScriptModel { model: ScriptModel {
values: root.resultsForText(root.displayText) id: model
onValuesChanged: root.currentIndex = 0 onValuesChanged: root.currentIndex = 0
} }
@@ -120,9 +95,8 @@ CustomListView {
} }
Anim { Anim {
property: "opacity" properties: "opacity,scale"
to: 1 to: 1
type: Anim.DefaultEffects
} }
} }
remove: Transition { remove: Transition {
@@ -130,9 +104,8 @@ CustomListView {
Anim { Anim {
from: 1 from: 1
property: "opacity" properties: "opacity,scale"
to: 0 to: 0
type: Anim.DefaultEffects
} }
} }
states: [ states: [
@@ -140,6 +113,7 @@ CustomListView {
name: "apps" name: "apps"
PropertyChanges { PropertyChanges {
model.values: Apps.search(search.text)
root.delegate: appItem root.delegate: appItem
} }
}, },
@@ -147,6 +121,7 @@ CustomListView {
name: "actions" name: "actions"
PropertyChanges { PropertyChanges {
model.values: Actions.query(search.text)
root.delegate: actionItem root.delegate: actionItem
} }
}, },
@@ -154,6 +129,7 @@ CustomListView {
name: "calc" name: "calc"
PropertyChanges { PropertyChanges {
model.values: [0]
root.delegate: calcItem root.delegate: calcItem
} }
}, },
@@ -161,6 +137,7 @@ CustomListView {
name: "variant" name: "variant"
PropertyChanges { PropertyChanges {
model.values: SchemeVariants.query(search.text)
root.delegate: variantItem root.delegate: variantItem
} }
} }
@@ -170,7 +147,7 @@ CustomListView {
ParallelAnimation { ParallelAnimation {
Anim { Anim {
duration: Tokens.anim.durations.small duration: Tokens.anim.durations.small
easing.bezierCurve: Tokens.anim.curves.standardAccel easing.bezierCurve: Tokens.anim.curves.expressiveEffects
from: 1 from: 1
property: "opacity" property: "opacity"
target: root target: root
@@ -179,7 +156,7 @@ CustomListView {
Anim { Anim {
duration: Tokens.anim.durations.small duration: Tokens.anim.durations.small
easing.bezierCurve: Tokens.anim.curves.standardAccel easing.bezierCurve: Tokens.anim.curves.expressiveEffects
from: 1 from: 1
property: "scale" property: "scale"
target: root target: root
@@ -188,24 +165,14 @@ CustomListView {
} }
PropertyAction { PropertyAction {
property: "delegate" properties: "values,delegate"
target: root targets: [model, root]
value: null
}
ScriptAction {
script: root.displayText = root.search.text
}
PropertyAction {
property: "delegate"
target: root
} }
ParallelAnimation { ParallelAnimation {
Anim { Anim {
duration: Tokens.anim.durations.small duration: Tokens.anim.durations.small
easing.bezierCurve: Tokens.anim.curves.standardDecel easing.bezierCurve: Tokens.anim.curves.expressiveEffects
from: 0 from: 0
property: "opacity" property: "opacity"
target: root target: root
@@ -214,7 +181,7 @@ CustomListView {
Anim { Anim {
duration: Tokens.anim.durations.small duration: Tokens.anim.durations.small
easing.bezierCurve: Tokens.anim.curves.standardDecel easing.bezierCurve: Tokens.anim.curves.expressiveEffects
from: 0.9 from: 0.9
property: "scale" property: "scale"
target: root target: root
@@ -230,8 +197,6 @@ CustomListView {
} }
} }
Component.onCompleted: displayText = search.text
Component { Component {
id: appItem id: appItem
@@ -263,20 +228,4 @@ CustomListView {
list: root list: root
} }
} }
Connections {
function onTextChanged() {
root.syncDisplayText();
}
target: root.search
}
Connections {
function onLauncherChanged() {
root.syncDisplayText();
}
target: root.visibilities
}
} }
-53
View File
@@ -1,53 +0,0 @@
import Quickshell
import QtQuick
import ZShell.Components
import ZShell.Config
import qs.Modules.Launcher.Items
import qs.Components
import qs.Helpers
CarouselView {
id: root
required property var panels
readonly property var screen: QsWindow.window?.screen
required property SearchBar search
readonly property var values: Wallpapers.query(root.search.text.split(" ").slice(1).join(" "))
required property var visibilities
currentIndex: {
const idx = Wallpapers.list.findIndex(w => w.path === Wallpapers.actualCurrent);
return idx >= 0 ? idx : 0;
}
delegateProperties: ({
visibilities: root.visibilities
})
focalWidth: Config.launcher.sizes.wallpaperWidth
glideDuration: 250
glideEasingType: Easing.OutCubic
implicitHeight: tileHeight + Tokens.spacing.small / 2 + Tokens.padding.large + Tokens.padding.normal
itemSpacing: Tokens.spacing.small
maxWidth: {
if (!screen)
return 0;
const barMargins = Config.bar.border * 2;
let margins = Math.round(screen.width / 6);
if (visibilities.utilities || visibilities.sidebar)
margins = Config.sidebar.sizes.width;
return screen.width - (barMargins + margins) * 2 - Config.bar.rounding * 4;
}
minEdgeWidth: 56
model: values
tileHeight: focalWidth / 16 * 9
delegate: Component {
WallpaperTile {
}
}
Component.onDestruction: Wallpapers.stopPreview()
onCurrentItemChanged: {
if (currentItem)
Wallpapers.preview(currentItem.modelData.path);
}
}
+1 -1
View File
@@ -79,7 +79,7 @@ Item {
if (currentItem) { if (currentItem) {
if (list.showWallpapers) { if (list.showWallpapers) {
if (Colors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent) if (Colors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent)
Wallpapers.previewColorLock = true; Wallpapers.previewColourLock = true;
Wallpapers.setWallpaper(currentItem.modelData.path); Wallpapers.setWallpaper(currentItem.modelData.path);
root.visibilities.launcher = false; root.visibilities.launcher = false;
} else if (text.startsWith(Config.launcher.actionPrefix)) { } else if (text.startsWith(Config.launcher.actionPrefix)) {
+2 -2
View File
@@ -110,9 +110,9 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
width: root.implicitWidth
sourceComponent: CarouselView { sourceComponent: WallpaperList {
content: root.content
panels: root.panels panels: root.panels
search: root.search search: root.search
visibilities: root.visibilities visibilities: root.visibilities
+96
View File
@@ -0,0 +1,96 @@
import ZShell.Models
import Quickshell
import QtQuick
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Modules
import qs.Services
Item {
id: root
required property FileSystemEntry modelData
required property PersistentProperties visibilities
implicitHeight: image.height + label.height + Tokens.spacing.small / 2 + Tokens.padding.large + Tokens.padding.normal
implicitWidth: image.width + Tokens.padding.larger * 2
opacity: 0
scale: 0.5
z: PathView.z ?? 0
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Component.onCompleted: {
scale = Qt.binding(() => PathView.isCurrentItem ? 1 : PathView.onPath ? 0.8 : 0);
opacity = Qt.binding(() => PathView.onPath ? 1 : 0);
}
StateLayer {
radius: Tokens.rounding.normal
onClicked: {
Wallpapers.setWallpaper(root.modelData.path);
root.visibilities.launcher = false;
}
}
Elevation {
anchors.fill: image
level: 4
opacity: root.PathView.isCurrentItem ? 1 : 0
radius: image.radius
Behavior on opacity {
Anim {
}
}
}
CustomClippingRect {
id: image
anchors.horizontalCenter: parent.horizontalCenter
color: Colors.tPalette.m3surfaceContainer
implicitHeight: implicitWidth / 16 * 9
implicitWidth: Config.launcher.sizes.wallpaperWidth
radius: Tokens.rounding.small
y: Tokens.padding.large
MaterialIcon {
anchors.centerIn: parent
color: Colors.tPalette.m3outline
font.pointSize: Tokens.font.size.extraLarge * 2
font.weight: 600
text: "image"
}
CachingImage {
anchors.fill: parent
cache: true
path: root.modelData.path
smooth: !root.PathView.view.moving
}
}
CustomText {
id: label
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: image.bottom
anchors.topMargin: Tokens.spacing.small / 2
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Text.AlignHCenter
renderType: Text.QtRendering
text: root.modelData.relativePath
width: image.width - Tokens.padding.normal * 2
}
}
-58
View File
@@ -1,58 +0,0 @@
import Quickshell
import QtQuick
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
Item {
id: root
required property bool isCurrent
required property var modelData // FileSystemEntry-like {path, relativePath}
required property real tileHeight
required property real tileWidth
required property PersistentProperties visibilities
signal requestActivate
implicitHeight: tileHeight + Tokens.padding.large * 2
implicitWidth: tileWidth
CustomClippingRect {
id: image
anchors.horizontalCenter: parent.horizontalCenter
color: Colors.tPalette.m3surfaceContainer
implicitHeight: root.tileHeight
implicitWidth: root.tileWidth
radius: Tokens.rounding.small
y: Tokens.padding.large
MaterialIcon {
anchors.centerIn: parent
color: Colors.tPalette.m3outline
font.pointSize: Tokens.font.size.extraLarge * 2
font.weight: 600
text: "image"
}
CachingImage {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
cache: true
fillMode: Image.PreserveAspectCrop
height: root.tileHeight
path: root.modelData ? root.modelData.path : ""
smooth: root.isCurrent
sourceSize.height: root.tileHeight
sourceSize.width: root.tileHeight * (16 / 9)
width: root.tileHeight * (16 / 9)
}
StateLayer {
onClicked: root.requestActivate()
}
}
}
@@ -82,6 +82,7 @@ Searcher {
list.visibilities.launcher = false; list.visibilities.launcher = false;
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--scheme", variant]); Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--scheme", variant]);
Config.colors.schemeType = variant; Config.colors.schemeType = variant;
Config.save();
} }
} }
} }
+93
View File
@@ -0,0 +1,93 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import qs.Components
import qs.Helpers
import ZShell.Config
import qs.Modules.Launcher.Items
PathView {
id: root
required property var content
readonly property int itemWidth: Config.launcher.sizes.wallpaperWidth * 0.9 + Tokens.padding.larger * 2
readonly property int numItems: {
const screen = QsWindow.window?.screen;
if (!screen)
return 0;
// Screen width - 4x outer rounding - 2x max side thickness (cause centered)
const barMargins = panels.bar.implicitWidth;
let outerMargins = 0;
if ((visibilities.utilities || visibilities.sidebar) && panels.utilities.implicitWidth > outerMargins)
outerMargins = panels.utilities.implicitWidth;
const maxWidth = screen.width - Config.bar.rounding * 4 - (barMargins + outerMargins) * 2;
if (maxWidth <= 0)
return 0;
const maxItemsOnScreen = Math.floor(maxWidth / itemWidth);
const visible = Math.min(maxItemsOnScreen, Config.launcher.maxWallpapers, scriptModel.values.length);
if (visible === 2)
return 1;
if (visible > 1 && visible % 2 === 0)
return visible - 1;
return visible;
}
required property var panels
required property SearchBar search
required property var visibilities
cacheItemCount: 4
highlightRangeMode: PathView.StrictlyEnforceRange
implicitWidth: Math.min(numItems, count) * itemWidth
pathItemCount: numItems
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
snapMode: PathView.SnapToItem
delegate: WallpaperItem {
visibilities: root.visibilities
}
model: ScriptModel {
id: scriptModel
readonly property string search: root.search.text.split(" ").slice(1).join(" ")
values: Wallpapers.query(search)
onValuesChanged: root.currentIndex = search ? 0 : values.findIndex(w => w.path === Wallpapers.actualCurrent)
}
path: Path {
startY: root.height / 2
PathAttribute {
name: "z"
value: 0
}
PathLine {
relativeY: 0
x: root.width / 2
}
PathAttribute {
name: "z"
value: 1
}
PathLine {
relativeY: 0
x: root.width
}
}
Component.onCompleted: currentIndex = Wallpapers.list.findIndex(w => w.path === Wallpapers.actualCurrent)
Component.onDestruction: Wallpapers.stopPreview()
onCurrentItemChanged: {
if (currentItem)
Wallpapers.preview(currentItem.modelData.path);
}
}
+77
View File
@@ -0,0 +1,77 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Daemons
import ZShell.Config
import qs.Helpers
import qs.Services
CustomRect {
id: root
readonly property string currentMedia: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
readonly property int textWidth: Math.min(metrics.width, 200)
color: Colors.tPalette.m3surfaceContainer
implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: layout.implicitWidth + Tokens.padding.normal * 2
radius: Tokens.rounding.full
Behavior on implicitWidth {
Anim {
}
}
TextMetrics {
id: metrics
font: mediatext.font
text: mediatext.text
}
RowLayout {
id: layout
anchors.centerIn: parent
Behavior on implicitWidth {
Anim {
}
}
MaterialIcon {
animate: true
color: Players.active?.isPlaying ? Colors.palette.m3primary : Colors.palette.m3onSurface
font.pointSize: Tokens.font.size.larger
text: Players.active?.isPlaying ? "music_note" : "music_off"
}
MarqueeText {
id: mediatext
Layout.preferredWidth: root.textWidth
animate: true
color: Players.active?.isPlaying ? Colors.palette.m3primary : Colors.palette.m3onSurface
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Text.AlignHCenter
marqueeEnabled: false
pauseMs: 4000
text: root.currentMedia
width: root.textWidth
CustomMouseArea {
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
if (!containsMouse) {
mediatext.marqueeEnabled = false;
} else {
mediatext.marqueeEnabled = true;
mediatext.anim.start();
}
}
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Networking
import QtQuick
import QtQuick.Layouts
import qs.Components
import ZShell.Config
import qs.Modules
import qs.Helpers as Helpers
Item {
id: root
required property var wrapper
ColumnLayout {
id: layout
spacing: 8
Repeater {
model: Helpers.Network.devices
CustomRadioButton {
id: network
required property NetworkDevice modelData
checked: Helpers.Network.activeDevice?.name === modelData.name
text: modelData.description
visible: modelData.name !== "lo"
}
}
}
}
@@ -1,19 +1,19 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Config import ZShell.Config
import qs.Modules.Bar.Components.Common
import qs.Modules.Bar.Popouts
import qs.Daemons import qs.Daemons
import qs.Components import qs.Components
import qs.Services import qs.Services
WidgetBase { CustomRect {
id: root id: root
required property Wrapper popouts required property Wrapper popouts
required property PersistentProperties visibilities required property PersistentProperties visibilities
color: visibilities.sidebar ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer color: visibilities.sidebar ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: implicitHeight
radius: Tokens.rounding.full radius: Tokens.rounding.full
MaterialIcon { MaterialIcon {
@@ -22,6 +22,7 @@ WidgetBase {
anchors.centerIn: parent anchors.centerIn: parent
color: root.visibilities.sidebar ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: root.visibilities.sidebar ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
fill: root.visibilities.sidebar ? 1 : 0 fill: root.visibilities.sidebar ? 1 : 0
font.family: "Material Symbols Rounded"
font.pointSize: Tokens.font.size.larger font.pointSize: Tokens.font.size.larger
text: NotifServer.list.length ? "\uf4fe" : "\ue7f4" text: NotifServer.list.length ? "\uf4fe" : "\ue7f4"
+5 -1
View File
@@ -464,7 +464,11 @@ CustomRect {
enabled: root.expanded enabled: root.expanded
fillWidth: true fillWidth: true
font.pointSize: Tokens.font.size.small font: {
const f = Qt.font(font);
f.pointSize = Tokens.font.size.small;
return f;
}
implicitWidth: label.implicitWidth implicitWidth: label.implicitWidth
inactiveColor: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3secondary : Colors.layer(Colors.palette.m3surfaceContainerHighest, 2) inactiveColor: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3secondary : Colors.layer(Colors.palette.m3surfaceContainerHighest, 2)
inactiveOnColor: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3onSecondary : Colors.palette.m3onSurfaceVariant inactiveOnColor: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3onSecondary : Colors.palette.m3onSurfaceVariant
@@ -5,14 +5,13 @@ import qs.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
GridLayout { RowLayout {
id: root id: root
property color accentColor: warning ? Colors.palette.m3error : mainColor property color accentColor: warning ? Colors.palette.m3error : mainColor
property real animatedPercentage: 0 property real animatedPercentage: 0
readonly property real arcStartAngle: 0.75 * Math.PI readonly property real arcStartAngle: 0.75 * Math.PI
readonly property real arcSweep: 1.5 * Math.PI readonly property real arcSweep: 1.5 * Math.PI
required property bool horizontal
property string icon property string icon
required property color iconColor required property color iconColor
required property color mainColor required property color mainColor
@@ -22,9 +21,8 @@ GridLayout {
property bool warning: percentage * 100 >= warningThreshold property bool warning: percentage * 100 >= warningThreshold
property int warningThreshold: 80 property int warningThreshold: 80
columnSpacing: Tokens.spacing.smaller
columns: horizontal ? -1 : 1
percentage: 0 percentage: 0
spacing: Tokens.spacing.smaller
Behavior on animatedPercentage { Behavior on animatedPercentage {
Anim { Anim {
@@ -49,8 +47,8 @@ GridLayout {
} }
CustomClippingRect { CustomClippingRect {
Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.small : 4 Layout.preferredHeight: root.height - Tokens.padding.small
Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth Layout.preferredWidth: 4
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2) color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -59,11 +57,9 @@ GridLayout {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: root.horizontal ? parent.right : undefined anchors.right: parent.right
anchors.top: root.horizontal ? undefined : parent.top
color: root.mainColor color: root.mainColor
implicitHeight: root.horizontal ? Math.ceil(root.percentage * parent.height) : 0 implicitHeight: Math.ceil(root.percentage * parent.height)
implicitWidth: root.horizontal ? 0 : Math.ceil(root.percentage * parent.width)
// Behavior on implicitHeight { // Behavior on implicitHeight {
// Anim { // Anim {
@@ -6,18 +6,18 @@ import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import ZShell.Services import ZShell.Services
import qs.Services import qs.Services
import qs.Modules.Bar.Components.Common import qs.Modules
import qs.Components import qs.Components
WidgetBase { CustomRect {
id: root id: root
required property PersistentProperties visibilities required property PersistentProperties visibilities
clip: true clip: true
color: visibilities.resources ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer color: visibilities.resources ? Colors.palette.m3primary : Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : gridLayout.implicitHeight + Tokens.padding.normal * 2 implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: horizontal ? gridLayout.implicitWidth + Tokens.padding.larger * 2 : baseSize implicitWidth: rowLayout.implicitWidth + Tokens.padding.larger * 2
radius: Tokens.rounding.full radius: Tokens.rounding.full
StateLayer { StateLayer {
@@ -26,14 +26,13 @@ WidgetBase {
onClicked: root.visibilities.resources = !root.visibilities.resources onClicked: root.visibilities.resources = !root.visibilities.resources
} }
GridLayout { RowLayout {
id: gridLayout id: rowLayout
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: root.horizontal ? 0 : 1 anchors.horizontalCenterOffset: -2
anchors.verticalCenterOffset: root.horizontal ? 0 : -3 implicitHeight: root.implicitHeight
columnSpacing: Tokens.spacing.smaller spacing: Tokens.spacing.smaller
columns: root.horizontal ? -1 : 1
ServiceRef { ServiceRef {
service: Gpu service: Gpu
@@ -49,9 +48,7 @@ WidgetBase {
Resource { Resource {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: root.horizontal Layout.fillHeight: true
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "memory" icon: "memory"
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary
@@ -60,9 +57,7 @@ WidgetBase {
} }
Resource { Resource {
Layout.fillHeight: root.horizontal Layout.fillHeight: true
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "memory_alt" icon: "memory_alt"
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3secondary mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3secondary
@@ -71,9 +66,7 @@ WidgetBase {
} }
Resource { Resource {
Layout.fillHeight: root.horizontal Layout.fillHeight: true
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "gamepad" icon: "gamepad"
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3tertiary mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3tertiary
@@ -81,9 +74,7 @@ WidgetBase {
} }
Resource { Resource {
Layout.fillHeight: root.horizontal Layout.fillHeight: true
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "developer_board" icon: "developer_board"
iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface iconColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary mainColor: root.visibilities.resources ? Colors.palette.m3onPrimary : Colors.palette.m3primary
+2 -12
View File
@@ -10,16 +10,10 @@ Item {
readonly property real nonAnimHeight: content.item?.nonAnimHeight ?? 0 readonly property real nonAnimHeight: content.item?.nonAnimHeight ?? 0
property real offsetScale: shouldBeActive ? 0 : 1 property real offsetScale: shouldBeActive ? 0 : 1
required property string position
readonly property bool shouldBeActive: root.visibilities.resources readonly property bool shouldBeActive: root.visibilities.resources
required property PersistentProperties visibilities required property PersistentProperties visibilities
anchors.bottom: position === "bottom" ? parent.bottom : undefined anchors.topMargin: (-implicitHeight - 5) * offsetScale
anchors.bottomMargin: position === "bottom" ? (-implicitHeight - 5) * offsetScale : 0
anchors.left: parent.left
anchors.leftMargin: position === "left" ? (-implicitWidth - 5) * offsetScale : 0
anchors.top: position !== "bottom" ? parent.top : undefined
anchors.topMargin: position === "top" ? (-implicitHeight - 5) * offsetScale : 0
clip: true clip: true
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
implicitWidth: content.implicitWidth || 854 // Hard coded fallback for first open implicitWidth: content.implicitWidth || 854 // Hard coded fallback for first open
@@ -37,11 +31,7 @@ Item {
id: content id: content
active: root.shouldBeActive || root.visible active: root.shouldBeActive || root.visible
anchors.bottom: root.position === "top" ? parent.bottom : undefined anchors.centerIn: parent
anchors.horizontalCenter: root.position !== "left" ? parent.horizontalCenter : undefined
anchors.right: root.position !== "left" ? undefined : parent.right
anchors.top: root.position === "bottom" ? parent.top : undefined
anchors.verticalCenter: root.position === "left" ? undefined : parent.verticalCenter
sourceComponent: Content { sourceComponent: Content {
wrapper: root wrapper: root
+2 -6
View File
@@ -2,8 +2,8 @@ pragma Singleton
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config
import qs.Components import qs.Components
import ZShell.Config
import qs.Modules.Settings.Common import qs.Modules.Settings.Common
import qs.Modules.Settings.Pages import qs.Modules.Settings.Pages
import qs.Modules.Settings.Pages.Wallpaper import qs.Modules.Settings.Pages.Wallpaper
@@ -45,11 +45,7 @@ QtObject {
// Connectivity // Connectivity
Component { Component {
StackPage { PlaceholderComp {
Component {
NetworkPage {
}
}
} }
}, },
Component { Component {
-240
View File
@@ -1,240 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Config
import qs.Modules.Settings.Common
import qs.Modules.Bar.Popouts.Network
import qs.Components
import qs.Helpers
import qs.Services
PageBase {
id: root
title: qsTr("Network")
ColumnLayout {
id: networkContent
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
spacing: Tokens.spacing.extraSmall / 2
width: root.cappedWidth
Toggle {
Layout.preferredHeight: visible ? implicitHeight : 0
checked: Network.wifiEnabled
first: true
last: connected.count === 0
subtext: qsTr("Toggle WiFi device state")
text: checked ? qsTr("Enabled") : qsTr("Disabled")
onToggled: Network.setWifi(checked)
}
Repeater {
id: connected
visible: Network.connectedNetworks.length > 0
model: ScriptModel {
values: [...Network.connectedNetworks].sort((a, b) => {
return b.signalStrength - a.signalStrength;
})
}
NetworkItem {
first: false
known: true
repeater: connected
}
}
}
component NetworkItem: ConnectedRect {
id: networkItem
readonly property bool connecting: Network.connectingNetwork === modelData
property int horizontalPadding: Tokens.padding.largeIncreased
required property int index
readonly property bool isSecure: Network.isSecure(modelData.security)
required property bool known
required property var modelData
required property Repeater repeater
Layout.fillWidth: true
Layout.preferredHeight: visible ? rowLayout.implicitHeight + Tokens.padding.smaller * 2 : 0
first: index === 0
last: index === (repeater.count - 1)
radius: Tokens.rounding.small
RowLayout {
id: rowLayout
anchors.fill: parent
anchors.leftMargin: networkItem.horizontalPadding
anchors.rightMargin: networkItem.horizontalPadding
Column {
id: column
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Tokens.spacing.extraSmall
Layout.rightMargin: Tokens.spacing.extraSmall
color: networkItem.modelData.active ? Colors.palette.m3primary : Colors.palette.m3onSurface
elide: Text.ElideRight
text: networkItem.modelData.name
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font.pointSize: Tokens.font.size.small
text: qsTr("%1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%")
}
}
Item {
Layout.alignment: Qt.AlignRight
implicitHeight: icon.height
implicitWidth: icon.width
MaterialIcon {
id: icon
color: networkItem.modelData.active ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.large
opacity: networkItem.connecting ? 0 : 1
text: Icons.getNetworkIcon(networkItem.modelData.signalStrength * 100, networkItem.isSecure)
Behavior on opacity {
Anim {
}
}
}
Loader {
active: opacity > 0
anchors.fill: parent
opacity: networkItem.connecting ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
running: true
}
}
}
}
StateLayer {
color: networkItem.modelData.active ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
onClicked: {
if (networkItem.isSecure && !networkItem.known) {
PopupManager.requestOpen(Qt.resolvedUrl("Network/PSKOverlay.qml"), {
network: networkItem.modelData
});
} else {
Network.requestConnect(networkItem.modelData);
}
}
}
}
component Spacer: Item {
id: spacer
required property Repeater repeater
Layout.preferredHeight: repeater.count > 0 ? Tokens.spacing.extraSmall : 0
}
component Toggle: CustomSwitch {
id: toggleItem
readonly property alias bg: bg
property alias first: bg.first
property alias last: bg.last
property string subtext
Layout.fillWidth: true
cLayer: 2
horizontalPadding: Tokens.padding.largeIncreased
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + verticalPadding * 2
implicitWidth: implicitContentWidth + implicitIndicatorWidth + horizontalPadding * 2
indicator.anchors.right: right
indicator.anchors.rightMargin: toggleItem.horizontalPadding
indicator.anchors.verticalCenter: verticalCenter
verticalPadding: Tokens.padding.normal
background: ConnectedRect {
id: bg
StateLayer {
id: stateLayer
manualPressOverride: toggleItem.pressed
}
}
contentItem: Item {
anchors.left: parent.left
anchors.leftMargin: toggleItem.horizontalPadding
anchors.right: toggleItem.indicator.left
anchors.rightMargin: Tokens.spacing.normal
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
Column {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 0
CustomText {
id: label
anchors.left: parent.left
anchors.right: parent.right
elide: Text.ElideRight
font: {
const f = Qt.font(label.font);
f.pointSize = Tokens.font.size.smaller;
return f;
}
text: toggleItem.text
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font: {
const f = Qt.font(subtext.font);
f.pointSize = Tokens.font.size.small;
return f;
}
text: toggleItem.subtext
visible: toggleItem.subtext
}
}
}
onPressed: stateLayer.press(stateLayer.mouseX, stateLayer.mouseY)
}
}
@@ -8,9 +8,7 @@ PageBase {
readonly property var builtinIcons: ({ readonly property var builtinIcons: ({
audio: qsTr("Speakers"), audio: qsTr("Speakers"),
microphone: qsTr("Microphone"), microphone: qsTr("Microphone"),
power: qsTr("Battery"), power: qsTr("Battery")
network: qsTr("Network"),
bluetooth: qsTr("Bluetooth")
}) })
isSubPage: true isSubPage: true
@@ -93,15 +91,5 @@ PageBase {
onToggled: Config.bar.popouts.statusIcons = checked onToggled: Config.bar.popouts.statusIcons = checked
} }
ToggleRow {
checked: Config.bar.popouts.network
last: true
settingAnchor: "bar-status-network-popout"
subtext: qsTr("Show a details popout when hovering the network icon")
text: qsTr("Network popout on hover")
onToggled: Config.bar.popouts.network = checked
}
} }
} }
+1 -50
View File
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import ZShell.Config
import qs.Components
import qs.Modules.Settings.Common import qs.Modules.Settings.Common
PageBase { PageBase {
@@ -26,6 +25,7 @@ PageBase {
ToggleRow { ToggleRow {
checked: Config.bar.autoHide checked: Config.bar.autoHide
first: true first: true
last: true
settingAnchor: "bar-autohide" settingAnchor: "bar-autohide"
subtext: qsTr("Hide the bar, reveal on hover") subtext: qsTr("Hide the bar, reveal on hover")
text: qsTr("Auto hide") text: qsTr("Auto hide")
@@ -33,55 +33,6 @@ PageBase {
onToggled: Config.bar.autoHide = checked onToggled: Config.bar.autoHide = checked
} }
SelectRow {
active: Config.bar.position === "top" ? menuItems[0] : Config.bar.position === "left" ? menuItems[1] : menuItems[2]
settingAnchor: "bar-position"
subtext: qsTr("Change which edge the bar appears on")
text: qsTr("Position")
menuItems: [
MenuItem {
icon: "build"
text: qsTr("Top")
value: "top"
},
MenuItem {
icon: "rotate_auto"
text: qsTr("Left")
value: "left"
},
MenuItem {
icon: "rotate_auto"
text: qsTr("Bottom")
value: "bottom"
}
]
onSelected: item => Config.bar.position = item.value
}
ToggleRow {
checked: Config.bar.fullscreenReveal
settingAnchor: "bar-fullscreen-reveal"
subtext: qsTr("Hover top edge to show bar in fullscreen")
text: qsTr("Reveal on edge")
onToggled: Config.bar.fullscreenReveal = checked
}
SpinRow {
from: 0
last: true
settingAnchor: "bar-reveal-delay"
stepSize: 100
subtext: qsTr("The delay before bar is revealed when cursor is at edge")
text: qsTr("Reveal delay")
to: 2000
value: Config.bar.revealDelay
onMoved: v => Config.bar.revealDelay = v
}
// Components // Components
SectionHeader { SectionHeader {
text: qsTr("Components") text: qsTr("Components")
+4 -1
View File
@@ -70,7 +70,10 @@ PageBase {
} }
] ]
onSelected: item => Config.screenshot.mode = item.value onSelected: item => {
Config.screenshot.mode = item.value;
Config.save();
}
} }
SectionHeader { SectionHeader {
@@ -40,19 +40,40 @@ PageBase {
id: localWalls id: localWalls
model: { model: {
const walls = [...Wallpapers.list]; const walls = Wallpapers.list;
var baseDir = Paths.wallsdir; var baseDir = Paths.wallsdir;
walls.sort((a, b) => ((a.parentDir === baseDir) - (b.parentDir === baseDir)) || a.name.localeCompare(b.name)); const categories = {};
while (walls.length < 4) const list = [];
walls.push(null); for (const w of walls) {
var parentDir = w.parentDir;
if (!parentDir.endsWith("/"))
parentDir = parentDir + "/";
return walls; if (!baseDir.endsWith("/"))
baseDir = baseDir + "/";
if (parentDir !== baseDir) {
const category = Wallpapers.getCategoryFor(w);
if (category && (!(category in categories) || categories[category].name.localeCompare(w.name) > 0))
categories[category] = w;
} else {
list.push(w);
}
}
list.push(...Object.values(categories));
list.sort((a, b) => ((a.parentDir === baseDir) - (b.parentDir === baseDir)) || a.name.localeCompare(b.name));
while (list.length < 4)
list.push(null);
return list;
} }
WallItem { WallItem {
required property FileSystemEntry modelData required property FileSystemEntry modelData
enabled: modelData enabled: modelData
// Empty placeholders for sizing
opacity: modelData ? 1 : 0 opacity: modelData ? 1 : 0
source: String(modelData?.path ?? "") source: String(modelData?.path ?? "")
+2
View File
@@ -104,6 +104,7 @@ PageBase {
onApplySettings: (start, end) => { onApplySettings: (start, end) => {
Config.general.color.scheduleDarkStart = start; Config.general.color.scheduleDarkStart = start;
Config.general.color.scheduleDarkEnd = end; Config.general.color.scheduleDarkEnd = end;
Config.save();
ModeScheduler.checkStartup(); ModeScheduler.checkStartup();
PopupManager.requestClose(); PopupManager.requestClose();
} }
@@ -145,6 +146,7 @@ PageBase {
onApplySettings: (start, end) => { onApplySettings: (start, end) => {
Config.general.color.scheduleHyprsunsetStart = start; Config.general.color.scheduleHyprsunsetStart = start;
Config.general.color.scheduleHyprsunsetEnd = end; Config.general.color.scheduleHyprsunsetEnd = end;
Config.save();
Hyprsunset.checkStartup(); Hyprsunset.checkStartup();
PopupManager.requestClose(); PopupManager.requestClose();
} }
+2 -4
View File
@@ -1,16 +1,14 @@
pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import qs.Helpers
Item { Item {
id: root id: root
property real offsetScale: shouldBeActive ? 0 : 1 property real offsetScale: shouldBeActive ? 0 : 1
required property var panels required property var panels
property string queuedMode: "wallpaper"
required property ShellScreen screen required property ShellScreen screen
readonly property bool shouldBeActive: visibilities.settings readonly property bool shouldBeActive: visibilities.settings
required property PersistentProperties visibilities required property PersistentProperties visibilities
@@ -39,7 +37,7 @@ Item {
sourceComponent: Content { sourceComponent: Content {
sState.animatingContainer: content.opacity < 1 sState.animatingContainer: content.opacity < 1
sState.currentPageIdx: ["wallpaper"].indexOf(root.queuedMode) sState.currentPageIdx: ["wallpaper"][0]
sState.screen: root.screen sState.screen: root.screen
} }
} }
@@ -4,7 +4,7 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Services.UPower import Quickshell.Services.UPower
import ZShell.Config import ZShell.Config
import qs.Modules.Bar.Components.Common import qs.Modules.SysTray.Common
import qs.Helpers import qs.Helpers
import qs.Components import qs.Components
import qs.Services import qs.Services
@@ -3,14 +3,13 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Components.StatusIcons import qs.Modules.SysTray.Widgets
import qs.Modules.Bar.Components.Common import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
WidgetBase { CustomClippingRect {
id: root id: root
readonly property int firstPresent: { readonly property int firstPresent: {
@@ -22,7 +21,7 @@ WidgetBase {
} }
// Index of the first/last entry that isn't collapsed, for edge margin gating // Index of the first/last entry that isn't collapsed, for edge margin gating
readonly property alias items: grid readonly property alias items: row
readonly property int lastPresent: { readonly property int lastPresent: {
const values = model.values; const values = model.values;
for (let i = values.length - 1; i >= 0; i--) for (let i = values.length - 1; i >= 0; i--)
@@ -30,7 +29,6 @@ WidgetBase {
return i; return i;
return -1; return -1;
} }
readonly property real size: horizontal ? grid.implicitWidth + Tokens.padding.small * 2 : grid.implicitHeight + Tokens.padding.small * 2
readonly property int spacing: Tokens.spacing.normal / 2 readonly property int spacing: Tokens.spacing.normal / 2
// Entries that can shrink to nothing, spacing included // Entries that can shrink to nothing, spacing included
@@ -40,36 +38,19 @@ WidgetBase {
return false; return false;
} }
bottomLeftRadius: horizontal ? Tokens.rounding.smallest / 2 : Tokens.rounding.full bottomLeftRadius: Tokens.rounding.smallest / 2
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : size implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: horizontal ? size : baseSize implicitWidth: row.implicitWidth + Tokens.padding.small * 2
radius: Tokens.rounding.full radius: Tokens.rounding.full
topLeftRadius: Tokens.rounding.smallest / 2 topLeftRadius: Tokens.rounding.smallest / 2
topRightRadius: horizontal ? Tokens.rounding.full : Tokens.rounding.smallest / 2
Behavior on implicitHeight { RowLayout {
enabled: !root.horizontal id: row
Anim {
}
}
Behavior on implicitWidth {
enabled: root.horizontal
Anim {
}
}
GridLayout {
id: grid
anchors.bottomMargin: root.horizontal ? 0 : Tokens.padding.small
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: root.horizontal ? Tokens.padding.small : 0 anchors.leftMargin: Tokens.padding.small
anchors.rightMargin: root.horizontal ? Tokens.padding.small : 0 anchors.rightMargin: Tokens.padding.small
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.small
columns: root.horizontal ? -1 : 1
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
@@ -88,7 +69,6 @@ WidgetBase {
name: "audio" name: "audio"
AudioWidget { AudioWidget {
horizontal: root.horizontal
} }
} }
} }
@@ -100,29 +80,6 @@ WidgetBase {
name: "audio" name: "audio"
MicWidget { MicWidget {
horizontal: root.horizontal
}
}
}
DelegateChoice {
roleValue: "bluetooth"
delegate: EntryWrapper {
name: "bluetooth"
BluetoothWidget {
}
}
}
DelegateChoice {
roleValue: "network"
delegate: EntryWrapper {
name: "network"
NetworkWidget {
} }
} }
} }
@@ -152,10 +109,8 @@ WidgetBase {
property real rightGap: present && index !== root.firstPresent ? margin : 0 property real rightGap: present && index !== root.firstPresent ? margin : 0
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: root.horizontal ? 0 : Math.round(rightGap) Layout.leftMargin: Math.round(leftGap)
Layout.leftMargin: root.horizontal ? Math.round(leftGap) : 0 Layout.rightMargin: Math.round(rightGap)
Layout.rightMargin: root.horizontal ? Math.round(rightGap) : 0
Layout.topMargin: root.horizontal ? 0 : Math.round(leftGap)
children: item children: item
implicitHeight: item?.implicitHeight ?? 0 implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0 implicitWidth: item?.implicitWidth ?? 0
@@ -3,37 +3,33 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
import ZShell.Config
import qs.Components import qs.Components
import qs.Modules.Bar.Components.Common import ZShell.Config
import qs.Modules.Bar.Components.Tray import qs.Modules.SysTray.Widgets
import qs.Modules.Bar.Popouts import qs.Modules
import qs.Services import qs.Services
WidgetBase { CustomClippingRect {
id: root id: root
readonly property alias items: repeater readonly property alias items: repeater
readonly property alias layout: sysGrid readonly property alias layout: sysRow
required property GridLayout loader required property RowLayout loader
readonly property int padding: Tokens.padding.small readonly property int padding: Tokens.padding.small
required property Wrapper popouts required property Wrapper popouts
readonly property real size: horizontal ? sysGrid.implicitWidth + Tokens.padding.small : sysGrid.implicitHeight + Tokens.padding.small
bottomLeftRadius: horizontal ? Tokens.rounding.full : Tokens.rounding.smallest / 2
bottomRightRadius: Tokens.rounding.smallest / 2 bottomRightRadius: Tokens.rounding.smallest / 2
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitHeight: horizontal ? baseSize : size implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: horizontal ? size : baseSize implicitWidth: sysRow.implicitWidth + Tokens.padding.small * 2
radius: Tokens.rounding.full radius: Tokens.rounding.full
topRightRadius: horizontal ? Tokens.rounding.smallest / 2 : Tokens.rounding.full topRightRadius: Tokens.rounding.smallest / 2
GridLayout { RowLayout {
id: sysGrid id: sysRow
anchors.centerIn: parent anchors.centerIn: parent
columns: root.horizontal ? -1 : 1 spacing: 0
rowSpacing: -2
Repeater { Repeater {
id: repeater id: repeater
@@ -1,9 +1,10 @@
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick import QtQuick
import QtQuick.VectorImage
import Quickshell import Quickshell
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Popouts import qs.Modules
import qs.Components import qs.Components
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -12,10 +13,10 @@ Item {
id: root id: root
property bool current: popouts.currentName.startsWith(`traymenu${ind}`) && popouts.hasCurrent property bool current: popouts.currentName.startsWith(`traymenu${ind}`) && popouts.hasCurrent
readonly property real dpr: Hypr.monitorFor(loader?.screen).scale readonly property real dpr: Hypr.monitorFor(loader.screen).scale
required property int ind required property int ind
required property SystemTrayItem item required property SystemTrayItem item
required property GridLayout loader required property RowLayout loader
required property Wrapper popouts required property Wrapper popouts
function resolveIcon(app: string, icon: string): string { function resolveIcon(app: string, icon: string): string {
+144
View File
@@ -0,0 +1,144 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell.Services.SystemTray
import qs.Components
import ZShell.Config
import qs.Modules.SysTray.Widgets
import qs.Modules
import qs.Services
RowLayout {
id: root
readonly property alias items: repeater
required property RowLayout loader
required property Wrapper popouts
function closestRowChild(row, x) {
let child = row.childAt(x, row.height / 2);
if (child)
return child;
let closest = null;
let closestDistance = Infinity;
for (let i = 0; i < row.children.length; ++i) {
let c = row.children[i];
if (!c.visible || c.width <= 0)
continue;
let centerX = c.x + c.width / 2;
let dist = Math.abs(x - centerX);
if (dist < closestDistance) {
closestDistance = dist;
closest = c;
}
}
return closest;
}
function getHoveredSubItem(localX, localY) {
let modPos = mapToItem(sysTrayMod, localX, localY);
if (sysTrayMod.contains(Qt.point(modPos.x, modPos.y))) {
let modRowPos = sysTrayMod.mapToItem(sysIcons, modPos.x, modPos.y);
let child = closestRowChild(sysIcons, modRowPos.x);
if (child) {
if (child.objectName === "audio" && Config.bar.popouts.audio)
return {
id: "audio",
item: child
};
if (child.objectName === "microphone" && Config.bar.popouts.audio)
return {
id: "audio",
item: child
};
if (child.objectName === "upower" && Config.bar.popouts.upower)
return {
id: "upower",
item: child
};
}
}
let trayPos = mapToItem(sysTray, localX, localY);
if (Config.bar.tray.showOnHover && sysTray.contains(Qt.point(trayPos.x, trayPos.y))) {
let trayRowPos = sysTray.mapToItem(sysRow, trayPos.x, trayPos.y);
let child = sysRow.childAt(trayRowPos.x, trayRowPos.y);
if (child && child.hasOwnProperty("ind")) {
return {
id: `traymenu${child.ind}`,
item: child
};
}
}
return null;
}
height: Config.bar.height + Tokens.padding.smallest * 2
spacing: Tokens.padding.small
width: sysTray.implicitWidth + sysTrayMod.implicitWidth + Tokens.padding.small
CustomClippingRect {
id: sysTray
Layout.fillHeight: true
bottomRightRadius: Tokens.rounding.smallest / 2
color: Colors.tPalette.m3surfaceContainer
implicitWidth: sysRow.width + Tokens.padding.small * 2
radius: Tokens.rounding.full
topRightRadius: Tokens.rounding.smallest / 2
Row {
id: sysRow
anchors.centerIn: parent
spacing: 0
Repeater {
id: repeater
model: SystemTray.items
TrayItem {
id: trayItem
required property int index
required property SystemTrayItem modelData
implicitHeight: 34
implicitWidth: 34
ind: index
item: modelData
loader: root.loader
popouts: root.popouts
}
}
}
}
CustomClippingRect {
id: sysTrayMod
Layout.fillHeight: true
bottomLeftRadius: Tokens.rounding.smallest / 2
color: Colors.tPalette.m3surfaceContainer
implicitWidth: sysIcons.implicitWidth + Tokens.padding.smaller + Tokens.padding.normal
radius: Tokens.rounding.full
topLeftRadius: Tokens.rounding.smallest / 2
StatusIcons {
id: sysIcons
anchors.fill: parent
anchors.leftMargin: Tokens.padding.smaller
anchors.rightMargin: Tokens.padding.normal
}
}
}
@@ -1,15 +1,16 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import Quickshell.Io
import Quickshell.Services.Pipewire
import qs.Daemons import qs.Daemons
import qs.Modules
import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
MaterialIcon { MaterialIcon {
id: speaker id: speaker
required property bool horizontal
animate: true animate: true
color: Audio.muted ? Colors.palette.m3error : Colors.palette.m3onSurface color: Audio.muted ? Colors.palette.m3error : Colors.palette.m3onSurface
fill: 1 fill: 1
@@ -1,15 +1,16 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import Quickshell.Io
import Quickshell.Services.Pipewire
import qs.Daemons import qs.Daemons
import qs.Modules
import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
MaterialIcon { MaterialIcon {
id: mic id: mic
required property bool horizontal
animate: true animate: true
color: (Audio.sourceMuted ?? false) ? Colors.palette.m3error : Colors.palette.m3onSurface color: (Audio.sourceMuted ?? false) ? Colors.palette.m3error : Colors.palette.m3onSurface
fill: 1 fill: 1
@@ -1,8 +1,7 @@
import Quickshell.Services.UPower import Quickshell.Services.UPower
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config import qs.Modules.SysTray.Common
import qs.Modules.Bar.Components.Common
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
@@ -13,6 +12,7 @@ Item {
implicitHeight: currentItem?.implicitHeight ?? 0 implicitHeight: currentItem?.implicitHeight ?? 0
implicitWidth: currentItem?.implicitWidth ?? 0 implicitWidth: currentItem?.implicitWidth ?? 0
visible: opacity > 0
Behavior on Layout.preferredHeight { Behavior on Layout.preferredHeight {
Anim { Anim {
@@ -48,12 +48,12 @@ Item {
Loader { Loader {
id: peripheralIconLoader id: peripheralIconLoader
active: (Battery.lowestPeripheral?.isPresent ?? false) && !batteryIconLoader.active active: Battery.lowestPeripheral.isPresent && !batteryIconLoader.active
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: BatteryIcon { sourceComponent: BatteryIcon {
devState: Battery.lowestPeripheral?.state ?? "" devState: Battery.lowestPeripheral.state
percentage: Battery.lowestPeripheral?.percentage ?? 0 percentage: Battery.lowestPeripheral.percentage
} }
} }
@@ -67,7 +67,6 @@ Item {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
animate: true animate: true
fill: 1 fill: 1
font.pointSize: Tokens.font.size.larger
text: { text: {
if (PowerProfiles.profile === PowerProfile.PowerSaver) if (PowerProfiles.profile === PowerProfile.PowerSaver)
return "energy_savings_leaf"; return "energy_savings_leaf";
@@ -78,6 +78,8 @@ CustomClippingRect {
required property var modelData required property var modelData
readonly property list<string> sections: modelData.update.split(" ") readonly property list<string> sections: modelData.update.split(" ")
// anchors.left: parent.left
// anchors.right: parent.right
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitHeight: root.itemHeight implicitHeight: root.itemHeight
implicitWidth: 600 implicitWidth: 600
+37
View File
@@ -0,0 +1,37 @@
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Modules
import qs.Helpers
import ZShell.Config
import qs.Services
CustomRect {
id: root
property int countUpdates: Updates.availableUpdates
property color textColor: Colors.palette.m3onSurface
color: Colors.tPalette.m3surfaceContainer
implicitHeight: Config.bar.height + Tokens.padding.smallest * 2
implicitWidth: contentRow.implicitWidth + Tokens.spacing.small * 2
radius: Tokens.rounding.full
RowLayout {
id: contentRow
anchors.centerIn: parent
spacing: Tokens.spacing.small
MaterialIcon {
font.pointSize: Tokens.font.size.larger
text: "package_2"
}
CustomText {
color: root.textColor
font.pointSize: Tokens.font.size.normal
text: root.countUpdates
}
}
}
+86
View File
@@ -0,0 +1,86 @@
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Modules
import qs.Helpers
import qs.Services
Item {
id: root
required property ShellScreen screen
required property Item wrapper
implicitHeight: layout.implicitHeight + 16
implicitWidth: layout.implicitWidth + 16
GridLayout {
id: layout
anchors.centerIn: parent
columnSpacing: 8
rowSpacing: 8
Repeater {
model: Hypr.workspaces
CustomRect {
id: workspacePreview
required property HyprlandWorkspace modelData
Layout.preferredHeight: 180 + 10
Layout.preferredWidth: 320 + 10
border.color: "white"
border.width: 1
radius: Tokens.rounding.smallest
Repeater {
model: workspacePreview.modelData.toplevels
Item {
id: preview
property rect appPosition: {
let {
at: [cx, cy],
size: [cw, ch]
} = modelData.lastIpcObject;
cx -= modelData.monitor.x;
cy -= modelData.monitor.y;
return Qt.rect((cx / 8), (cy / 8), (cw / 8), (ch / 8));
}
required property HyprlandToplevel modelData
anchors.fill: parent
anchors.margins: 5
CustomRect {
border.color: Colors.tPalette.m3outline
border.width: 1
implicitHeight: preview.appPosition.height
implicitWidth: preview.appPosition.width
radius: Tokens.rounding.smallest / 2
x: preview.appPosition.x
y: preview.appPosition.y - 3.4
ScreencopyView {
id: previewCopy
anchors.fill: parent
captureSource: preview.modelData.wayland
live: true
}
}
}
}
}
}
}
}
+79 -22
View File
@@ -5,46 +5,103 @@ import Quickshell.Hyprland
import QtQuick import QtQuick
import qs.Components import qs.Components
import qs.Helpers import qs.Helpers
import ZShell.Config
import ZShell.Internal import ZShell.Internal
Item { Item {
id: root id: root
property bool completed
property real cropHeight: displayData?.height ?? 1.0
property real cropWidth: displayData?.width ?? 1.0
property real cropX: displayData?.x ?? 0.0
property real cropY: displayData?.y ?? 0.0
property WallpaperImage current
readonly property var displayData: Wallpapers.getCrop(screen.name) readonly property var displayData: Wallpapers.getCrop(screen.name)
required property ShellScreen screen required property ShellScreen screen
property size screenResolution: Qt.size(screen.width * screenScale, screen.height * screenScale) property size screenResolution: Qt.size(screen.width * screenScale, screen.height * screenScale)
property real screenScale: Hyprland.monitorFor(screen).scale property real screenScale: Hyprland.monitorFor(screen).scale
property string source: Wallpapers.current
anchors.fill: parent anchors.fill: parent
Component.onCompleted: Hyprland.refreshMonitors() Component.onCompleted: {
Hyprland.refreshMonitors();
WallpaperImage { if (source)
id: img Qt.callLater(() => {
current = imgComp.createObject(this, {
source
});
completed = true;
});
}
onSourceChanged: {
if (!source)
current = null;
else
current = imgComp.createObject(this, {
source: source
});
}
anchors.fill: parent Component {
cropHeight: root.displayData?.height ?? 1.0 id: imgComp
cropWidth: root.displayData?.width ?? 1.0
cropX: root.displayData?.x ?? 0.0
cropY: root.displayData?.y ?? 0.0
fadeDuration: 150
screenResolution: root.screenResolution
source: Wallpapers.current
Behavior on cropHeight { WallpaperImage {
Anim { id: img
anchors.fill: parent
cropHeight: root.cropHeight
cropWidth: root.cropWidth
cropX: root.cropX
cropY: root.cropY
opacity: 0
screenResolution: root.screenResolution
source: root.source
Behavior on cropHeight {
Anim {
id: heightAnim
}
} }
} Behavior on cropWidth {
Behavior on cropWidth { Anim {
Anim { id: widthAnim
}
} }
} Behavior on cropX {
Behavior on cropX { Anim {
Anim { id: xAnim
}
} }
} Behavior on cropY {
Behavior on cropY { Anim {
Anim { id: yAnim
}
}
Anim on opacity {
id: anim
from: 0
running: false
to: 1
type: Anim.SlowEffects
}
onStatusChanged: {
if (status === Image.Ready) {
anim.start();
}
}
Timer {
id: destroyTimer
interval: anim.duration * 2
running: root.current !== img && root.current?.status === Image.Ready
onTriggered: Qt.callLater(() => img.destroy())
} }
} }
} }
+75
View File
@@ -0,0 +1,75 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Components
import ZShell.Config
import qs.Helpers
import qs.Services
Item {
id: root
required property var bar
property color colour: Colors.palette.m3primary
property Title current: text1
// readonly property int maxWidth: 300
readonly property int maxWidth: {
const otherModules = bar.children.filter(c => c.enabled && c.id && c.item !== this && c.id !== "spacer");
const otherWidth = otherModules.reduce((acc, curr) => {
return acc + (curr.item?.nonAnimWidth ?? curr.width ?? 0);
}, 0);
return bar.width - otherWidth - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2;
}
clip: true
implicitHeight: current.implicitHeight
implicitWidth: Math.min(current.implicitWidth, root.maxWidth)
Behavior on implicitWidth {
Anim {
}
}
Title {
id: text1
}
Title {
id: text2
}
TextMetrics {
id: metrics
elide: Qt.ElideRight
elideWidth: root.maxWidth
font.family: "Rubik"
font.pointSize: Tokens.font.size.normal
text: Hypr.activeToplevel?.title ?? qsTr("Desktop")
onElideWidthChanged: root.current.text = elidedText
onTextChanged: {
const next = root.current === text1 ? text2 : text1;
next.text = elidedText;
root.current = next;
}
}
component Title: CustomText {
id: text
anchors.leftMargin: 7
anchors.verticalCenter: parent.verticalCenter
color: root.colour
font.family: metrics.font.family
font.pointSize: metrics.font.pointSize
height: implicitHeight
opacity: root.current === this ? 1 : 0
width: implicitWidth
Behavior on opacity {
Anim {
}
}
}
}
@@ -2,10 +2,10 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import ZShell.Config import ZShell.Config
import qs.Modules.Bar.Components.Workspaces
import qs.Components import qs.Components
import qs.Services import qs.Services
@@ -14,57 +14,41 @@ Item {
property real activeWorkspaceMargin: Math.ceil(Tokens.padding.small / 2) property real activeWorkspaceMargin: Math.ceil(Tokens.padding.small / 2)
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1 readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
required property bool horizontal
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
required property ShellScreen screen required property ShellScreen screen
readonly property real shortSize: Math.max(Config.bar.height, Tokens.bar.innerSize) property int workspaceButtonWidth: bgRect.implicitHeight - root.activeWorkspaceMargin * 2
readonly property real size: (workspaceButtonWidth * workspacesShown) + activeWorkspaceMargin * 2
property int workspaceButtonWidth: (horizontal ? bgRect.implicitHeight : bgRect.implicitWidth) - root.activeWorkspaceMargin * 2
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor) readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor)
readonly property int workspacesShown: workspaces.length readonly property int workspacesShown: workspaces.length
implicitHeight: horizontal ? shortSize : size height: implicitHeight
implicitWidth: horizontal ? size : shortSize implicitHeight: Config.bar.height + Tokens.padding.smaller * 2
implicitWidth: (root.workspaceButtonWidth * root.workspacesShown) + root.activeWorkspaceMargin * 2
Behavior on implicitHeight {
enabled: !root.horizontal
Anim {
}
}
Behavior on implicitWidth { Behavior on implicitWidth {
enabled: root.horizontal
Anim { Anim {
} }
} }
CustomRect { CustomRect {
// qmllint disable Quick.anchor-combinations
id: bgRect id: bgRect
anchors.bottom: root.horizontal ? undefined : parent.bottom anchors.left: parent.left
anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter anchors.right: parent.right
anchors.left: root.horizontal ? parent.left : undefined anchors.verticalCenter: parent.verticalCenter
anchors.right: root.horizontal ? parent.right : undefined
anchors.top: root.horizontal ? undefined : parent.top
anchors.verticalCenter: root.horizontal ? parent.verticalCenter : undefined
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitHeight: root.horizontal ? root.implicitHeight : 0 implicitHeight: root.implicitHeight - ((Tokens.padding.small - 1) * 2)
implicitWidth: root.horizontal ? 0 : root.implicitWidth
radius: height / 2 radius: height / 2
// qmllint enable Quick.anchor-combinations
Grid { Grid {
id: grid id: grid
anchors.fill: parent anchors.fill: parent
anchors.margins: root.activeWorkspaceMargin anchors.margins: root.activeWorkspaceMargin
columnSpacing: 0 columnSpacing: 0
columns: root.horizontal ? root.workspacesShown : 1 columns: root.workspacesShown
rowSpacing: 0 rowSpacing: 0
rows: root.horizontal ? 1 : root.workspacesShown rows: 1
Repeater { Repeater {
model: root.workspaces model: root.workspaces
@@ -90,7 +74,7 @@ Item {
color: Colors.palette.m3onSecondaryContainer color: Colors.palette.m3onSecondaryContainer
elide: Text.ElideRight elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: button.modelData?.name ?? "" text: button.modelData.name
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
} }
@@ -110,15 +94,13 @@ Item {
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
property real indicatorThickness: root.workspaceButtonWidth property real indicatorThickness: root.workspaceButtonWidth
anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenter: root.horizontal ? parent.verticalCenter : undefined
clip: true clip: true
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: root.horizontal ? indicatorThickness : indicatorLength implicitHeight: indicatorThickness
implicitWidth: root.horizontal ? indicatorLength : indicatorThickness implicitWidth: indicatorLength
radius: Tokens.rounding.full radius: Tokens.rounding.full
x: root.horizontal ? indicatorPosition : 0 x: indicatorPosition
y: root.horizontal ? 0 : indicatorPosition
AnimatedTabIndexPair { AnimatedTabIndexPair {
id: idxPair id: idxPair
@@ -132,8 +114,7 @@ Item {
implicitWidth: grid.width implicitWidth: grid.width
source: grid source: grid
sourceColor: Colors.palette.m3onSurface sourceColor: Colors.palette.m3onSurface
x: root.horizontal ? -indicator.x + 3 : 0 x: -indicator.x + 3
y: root.horizontal ? 0 : -indicator.y + 3
} }
} }
} }
@@ -54,7 +54,7 @@ Item {
Comp { Comp {
id: content id: content
anchors.fill: parent anchors.centerIn: parent
shouldBeActive: root.hasCurrent shouldBeActive: root.hasCurrent
sourceComponent: Content { sourceComponent: Content {
-1
View File
@@ -4,7 +4,6 @@ qml_module(ZShell-components
lazylistview.hpp lazylistview.cpp lazylistview.hpp lazylistview.cpp
wavyline.hpp wavyline.cpp wavyline.hpp wavyline.cpp
buttonrow.hpp buttonrow.cpp buttonrow.hpp buttonrow.cpp
carouselview.hpp carouselview.cpp
LIBRARIES LIBRARIES
Qt::Quick Qt::Quick
) )
File diff suppressed because it is too large Load Diff
-255
View File
@@ -1,255 +0,0 @@
#pragma once
#include <QQmlParserStatus>
#include <QElapsedTimer>
#include <QEasingCurve>
#include <QList>
#include <QPointF>
#include <QQuickItem>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QVariantAnimation>
#include <QVariantList>
#include <QVector>
#include <qtypes.h>
#include <qvectornd.h>
namespace ZShell::components {
class CarouselView : public QQuickItem {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(
QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY
delegateChanged)
Q_PROPERTY(QVariantList model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(
qreal focalWidth READ focalWidth WRITE setFocalWidth NOTIFY
focalWidthChanged)
Q_PROPERTY(
qreal minEdgeWidth READ minEdgeWidth WRITE setMinEdgeWidth NOTIFY
minEdgeWidthChanged)
Q_PROPERTY(
qreal itemSpacing READ itemSpacing WRITE setItemSpacing NOTIFY
itemSpacingChanged)
Q_PROPERTY(
qreal tileHeight READ tileHeight WRITE setTileHeight NOTIFY
tileHeightChanged)
Q_PROPERTY(
qreal maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged)
Q_PROPERTY(
int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY
currentIndexChanged)
Q_PROPERTY(
QQuickItem* currentItem READ currentItem NOTIFY currentItemChanged)
Q_PROPERTY(
int glideDuration READ glideDuration WRITE setGlideDuration NOTIFY
glideDurationChanged)
Q_PROPERTY(
QEasingCurve::Type glideEasingType READ glideEasingType WRITE
setGlideEasingType NOTIFY glideEasingTypeChanged)
Q_PROPERTY(
QVariantMap delegateProperties READ delegateProperties WRITE
setDelegateProperties NOTIFY delegatePropertiesChanged)
public:
explicit CarouselView(QQuickItem* parent = nullptr);
~CarouselView() override;
QQmlComponent* delegate() const { return m_delegate; }
void setDelegate(QQmlComponent* c);
QVariantList model() const { return m_model; }
void setModel(const QVariantList& m);
qreal focalWidth() const { return m_focalWidth; }
void setFocalWidth(qreal v);
qreal minEdgeWidth() const { return m_minEdgeWidth; }
void setMinEdgeWidth(qreal v);
qreal itemSpacing() const { return m_itemSpacing; }
void setItemSpacing(qreal v);
qreal tileHeight() const { return m_tileHeight; }
void setTileHeight(qreal v);
qreal maxWidth() const { return m_maxWidth; }
void setMaxWidth(qreal v);
int currentIndex() const { return m_currentRealIndex; }
void setCurrentIndex(int idx);
QQuickItem* currentItem() const { return m_currentItem; }
int glideDuration() const { return m_glideDuration; }
void setGlideDuration(int v);
QEasingCurve::Type glideEasingType() const { return m_glideEasingType; }
void setGlideEasingType(QEasingCurve::Type t);
QVariantMap delegateProperties() const { return m_delegateProperties; }
void setDelegateProperties(const QVariantMap& v);
Q_INVOKABLE void goToIndex(int realIndex);
Q_INVOKABLE void incrementCurrentIndex();
Q_INVOKABLE void decrementCurrentIndex();
signals:
void delegateChanged();
void modelChanged();
void focalWidthChanged();
void minEdgeWidthChanged();
void itemSpacingChanged();
void tileHeightChanged();
void maxWidthChanged();
void currentIndexChanged();
void currentItemChanged();
void glideDurationChanged();
void glideEasingTypeChanged();
void delegatePropertiesChanged();
void activated(int realIndex, QVariant modelData);
void previewIndexChanged(int realIndex, QVariant modelData);
protected:
void classBegin() override;
void componentComplete() override;
bool childMouseEventFilter(QQuickItem* item, QEvent* event) override;
void geometryChange(const QRectF& newGeo, const QRectF& oldGeo) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseUngrabEvent() override;
void wheelEvent(QWheelEvent* event) override;
private slots:
void handleDelegateActivate();
private:
struct Arrangement {
int n = 0;
qreal width = 0;
qreal outerSpacing = 0;
QVector<qreal> sizes;
QVector<qreal> centers;
qreal effectiveExtraWidth = 0;
};
struct Slot {
QQuickItem* item = nullptr;
int virtualIndex = 0;
int realIndex = -1;
bool active = false;
};
struct VelocitySample {
qint64 t;
qreal x;
};
void rebuildArrangement();
void relayout();
Arrangement arrangementForCount(int n) const;
int layoutCapacityForWidth(qreal width) const;
qreal targetWidthForMaxWidth(qreal maxWidth) const;
void updateSlotContent(Slot& slot, int virtualIndex);
void positionSlot(const Slot& slot) const;
void applyPropertiesToItem(QQuickItem* item, int realIndex) const;
qreal pitch() const { return m_focalWidth + m_itemSpacing; }
qreal contentXForIndex(qreal index) const;
int indexForContentX(qreal x) const;
qreal snapTargetX(qreal x) const;
void snapToNearest();
void glideTo(qreal dest);
void cancelAnimations();
qreal keylineSize(int io) const;
qreal sampleSize(qreal childLoc) const;
qreal continuousIndex() const;
int realIndexOf(int virtualIndex) const;
void updateCurrentIndexFromContentX(bool emitPreview);
void updateCurrentItem();
void wrapContentIfNeeded();
void centerInstantlyOnReal(int realIndex);
QQuickItem* acquireItem();
void releaseAllItems();
void addVelocitySample(qint64 t, qreal x);
qreal sampledVelocity() const;
void notifyCurrentIndexChanged();
void notifyCurrentItemChanged();
void notifyPreviewIndexChanged(int realIndex);
QQmlComponent* m_delegate = nullptr;
QVariantList m_model;
QVariantMap m_delegateProperties;
qreal m_focalWidth = 0;
qreal m_minEdgeWidth = 56;
qreal m_itemSpacing = 8;
qreal m_tileHeight = 0;
qreal m_maxWidth = 0;
Arrangement m_arrangement;
int m_layoutCapacity = 0;
qreal m_contentX = 0;
int m_currentVirtualIndex = 0;
int m_currentRealIndex = -1;
int m_previewRealIndex = -1;
QQuickItem* m_currentItem = nullptr;
int m_centerBlockStart = 0;
QList<Slot> m_slots;
int m_glideDuration = 250;
QEasingCurve::Type m_glideEasingType = QEasingCurve::OutCubic;
QVariantAnimation* m_glideAnim = nullptr;
bool m_dragging = false;
bool m_dragActive = false;
QPointF m_pressPos;
qreal m_pressContentX = 0;
QElapsedTimer m_dragTimer;
QVector<VelocitySample> m_velocitySamples;
static constexpr qint64 kVelocitySampleWindowMs = 80;
QVariantAnimation* m_flickAnim = nullptr;
bool m_completed = false;
int m_pendingCurrentIndex = -1;
bool m_initializedLayout = false;
bool m_relayoutQueued = false;
};
} // namespace ZShell::components
-3
View File
@@ -2,8 +2,6 @@
#include "configlist.hpp" #include "configlist.hpp"
#include "configobject.hpp" #include "configobject.hpp"
#include <qcontainerfwd.h>
#include <qhashfunctions.h>
#include <qqmlregistration.h> #include <qqmlregistration.h>
namespace ZShell::config { namespace ZShell::config {
@@ -63,7 +61,6 @@ class Bar : public ConfigObject {
CFG_PROPERTY(int, revealDelay, 100) CFG_PROPERTY(int, revealDelay, 100)
CFG_PROPERTY(int, rounding, 14) CFG_PROPERTY(int, rounding, 14)
CFG_PROPERTY(int, smoothing, 32) CFG_PROPERTY(int, smoothing, 32)
CFG_PROPERTY(QString, position, QStringLiteral("top"))
CONFIG_SUBOBJECT(Tray, tray) CONFIG_SUBOBJECT(Tray, tray)
CONFIG_LIST( CONFIG_LIST(
EntryList, EntryList,
+19 -40
View File
@@ -61,8 +61,6 @@ Config::Config(QObject* parent)
m_reloadTimer.setInterval(50); m_reloadTimer.setInterval(50);
connect(&m_reloadTimer, &QTimer::timeout, this, &Config::reloadAsync); connect(&m_reloadTimer, &QTimer::timeout, this, &Config::reloadAsync);
connectAutoSave(this);
connect( connect(
&m_watcher, &m_watcher,
&QFileSystemWatcher::directoryChanged, &QFileSystemWatcher::directoryChanged,
@@ -95,31 +93,15 @@ QString Config::filePath() const {
void Config::loadSync() { void Config::loadSync() {
m_loading = true; m_loading = true;
QFile f(filePath()); QFile f(filePath());
QJsonObject before;
bool existed = false;
if (f.open(QIODevice::ReadOnly)) { if (f.open(QIODevice::ReadOnly)) {
const auto doc = QJsonDocument::fromJson(f.readAll()); const auto doc = QJsonDocument::fromJson(f.readAll());
if (doc.isObject()) { if (doc.isObject()) loadFromJson(QJsonValue(doc.object()));
before = doc.object();
existed = true;
} else {
qInfo() << "Config: existing config at" << filePath()
<< "is empty or not a valid JSON object - using defaults";
}
} else { } else {
qInfo() << "Config: no existing config at" << filePath() qInfo() << "Config: no existing config at" << filePath()
<< "- using defaults"; << "- using defaults";
} }
loadFromJson(QJsonValue(before));
m_loading = false; m_loading = false;
const auto after = toJson().toObject();
if (!existed || after != before) saveNow();
} }
void Config::updateWatch() { void Config::updateWatch() {
@@ -164,21 +146,27 @@ void Config::loadAsync() {
if (f.open(QIODevice::ReadOnly)) { if (f.open(QIODevice::ReadOnly)) {
const auto doc = QJsonDocument::fromJson(f.readAll()); const auto doc = QJsonDocument::fromJson(f.readAll());
const bool valid = doc.isObject();
const QJsonObject before = valid ? doc.object() : QJsonObject();
QMetaObject::invokeMethod( if (doc.isObject()) {
this, QMetaObject::invokeMethod(
[this, valid, before]() { this,
loadFromJson(QJsonValue(before)); [this, doc]() {
m_loading = false; loadFromJson(QJsonValue(doc.object()));
m_loading = false;
const auto after = toJson().toObject(); if (m_reloadPending) m_reloadTimer.start();
if (!valid || after != before) saveNow(); },
Qt::QueuedConnection);
} else {
QMetaObject::invokeMethod(
this,
[this]() {
m_loading = false;
if (m_reloadPending) m_reloadTimer.start(); if (m_reloadPending) m_reloadTimer.start();
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
}
} else { } else {
qInfo() << "Config: failed to reload from" << filePath() qInfo() << "Config: failed to reload from" << filePath()
<< "- using in-memory values"; << "- using in-memory values";
@@ -244,15 +232,6 @@ void Config::saveNow() {
flushAsync(); flushAsync();
} }
void Config::connectAutoSave(ConfigNode* node) {
connect(node, &ConfigNode::propertiesChanged, this, [this] {
scheduleSave();
});
for (auto* child : node->childNodes())
connectAutoSave(child);
}
bool Config::writeAtomically(const QByteArray& data) { bool Config::writeAtomically(const QByteArray& data) {
QSaveFile f(filePath()); QSaveFile f(filePath());
-1
View File
@@ -90,7 +90,6 @@ class Config : public ConfigObject {
void updateWatch(); void updateWatch();
void loadSync(); void loadSync();
void loadAsync(); void loadAsync();
void connectAutoSave(ConfigNode* node);
QTimer m_saveTimer; QTimer m_saveTimer;
QTimer m_reloadTimer; QTimer m_reloadTimer;
+5 -14
View File
@@ -79,14 +79,13 @@ void ConfigList::loadFromJson(const QJsonValue& json) {
if (!json.isArray()) { if (!json.isArray()) {
qCWarning( qCWarning(
lcConfig, lcConfig,
"Option '%s' must be a list, resetting to default", "Option '%s' must be a list, ignoring",
qUtf8Printable(propertyPath())); qUtf8Printable(propertyPath()));
m_rejectedJson = json;
resetToDefaults();
materializeDefaults();
return; return;
} }
m_rejectedJson = QJsonValue::Undefined;
populate(json.toArray()); populate(json.toArray());
m_loaded = true; m_loaded = true;
} }
@@ -94,11 +93,12 @@ void ConfigList::loadFromJson(const QJsonValue& json) {
QJsonValue ConfigList::toJson() const { QJsonValue ConfigList::toJson() const {
if (m_loaded) return elementsToJson(); if (m_loaded) return elementsToJson();
return QJsonValue::Undefined; return m_rejectedJson;
} }
void ConfigList::clearLoadedKeys() { void ConfigList::clearLoadedKeys() {
m_loaded = false; m_loaded = false;
m_rejectedJson = QJsonValue::Undefined;
} }
QStringList ConfigList::unknownKeys() const { QStringList ConfigList::unknownKeys() const {
@@ -113,15 +113,6 @@ QStringList ConfigList::unknownKeys() const {
return keys; return keys;
} }
void ConfigList::materializeDefaults() {
for (auto* const item : m_items)
item->materializeDefaults();
if (m_global) return;
m_loaded = true;
}
void ConfigList::resyncFromGlobal() { void ConfigList::resyncFromGlobal() {
syncValuesFromGlobal(); syncValuesFromGlobal();
} }
+5 -3
View File
@@ -33,7 +33,6 @@ class ConfigList : public ConfigNode {
[[nodiscard]] QJsonValue toJson() const override; [[nodiscard]] QJsonValue toJson() const override;
void clearLoadedKeys() override; void clearLoadedKeys() override;
[[nodiscard]] QStringList unknownKeys() const override; [[nodiscard]] QStringList unknownKeys() const override;
void materializeDefaults() override;
void resyncFromGlobal() override; void resyncFromGlobal() override;
signals: signals:
@@ -72,6 +71,7 @@ class ConfigList : public ConfigNode {
QJsonArray m_defaults; QJsonArray m_defaults;
QList<ConfigObject*> m_items; QList<ConfigObject*> m_items;
bool m_loaded = false; bool m_loaded = false;
QJsonValue m_rejectedJson = QJsonValue::Undefined;
}; };
} // namespace ZShell::config } // namespace ZShell::config
@@ -103,6 +103,7 @@ class ConfigList : public ConfigNode {
} \ } \
}; };
#define CONFIG_LIST(Type, name, ...) \ #define CONFIG_LIST(Type, name, ...) \
Q_PROPERTY(ZShell::config::Type* name READ name CONSTANT) \ Q_PROPERTY(ZShell::config::Type* name READ name CONSTANT) \
\ \
@@ -114,8 +115,6 @@ class ConfigList : public ConfigNode {
private: \ private: \
Type* m_##name = new Type(this __VA_OPT__(, __VA_ARGS__)); Type* m_##name = new Type(this __VA_OPT__(, __VA_ARGS__));
#define LIST_ENTRY(id, enabled) \
vmap({{"id", QString::fromUtf8(#id)}, {"enabled", enabled}})
namespace ZShell::config { namespace ZShell::config {
@@ -137,3 +136,6 @@ class ListEntry : public ConfigObject {
CONFIG_LIST_TYPE(ListEntry, EntryList) CONFIG_LIST_TYPE(ListEntry, EntryList)
} // namespace ZShell::config } // namespace ZShell::config
#define LIST_ENTRY(id, enabled) \
vmap({{"id", QString::fromUtf8(#id)}, {"enabled", enabled}})
-2
View File
@@ -26,8 +26,6 @@ class ConfigNode : public QObject {
[[nodiscard]] virtual QStringList unknownKeys() const = 0; [[nodiscard]] virtual QStringList unknownKeys() const = 0;
[[nodiscard]] virtual QList<ConfigNode*> childNodes() const; [[nodiscard]] virtual QList<ConfigNode*> childNodes() const;
virtual void materializeDefaults() = 0;
void syncFromGlobal(ConfigNode* global); void syncFromGlobal(ConfigNode* global);
virtual void resyncFromGlobal() = 0; virtual void resyncFromGlobal() = 0;

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