24 Commits

Author SHA1 Message Date
zach 04fb534a05 scroll deceleration env
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-06 21:21:32 +02:00
zach 50e99501de switches and popouts
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
2026-06-06 21:13:44 +02:00
zach 65c56bc598 updated components
Python / lint-format (pull_request) Successful in 41s
Python / test (pull_request) Successful in 1m9s
Lint & Format (Rust) / lint-format (pull_request) Successful in 2m42s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 20s
2026-06-06 00:49:19 +02:00
zach 82518006c3 revert toaster enum 2026-06-05 19:45:43 +02:00
zach 9cefdf509c cmake build hotfix 2026-06-05 12:31:37 +02:00
zach 27924aca37 disable incompatible-type for qmllint in clock 2026-06-04 23:22:28 +02:00
zach b4716d25c0 nodiscard 2026-06-04 22:57:45 +02:00
zach d8f047dbc9 nodiscard 2026-06-04 22:38:17 +02:00
zach 91b50b312d open launcher with gesture from bottom border 2026-06-04 19:01:02 +02:00
zach 3e933a8b78 remove logging 2026-06-04 18:56:42 +02:00
zach e127928126 Merge pull request 'Rewrite the manager responsible for handling automatic hyprsunset temperature activation as a qml plugin' (#117) from hyprsunset-manager-rewrite into main
Reviewed-on: #117
2026-06-04 15:05:45 +02:00
zach 94f2cf076c fix applying end() on init
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-04 15:05:18 +02:00
zach 9168b6e893 Merge branch 'main' into hyprsunset-manager-rewrite
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-04 14:56:53 +02:00
zach a477fb2e22 fix namespaces and logging names 2026-06-04 14:56:20 +02:00
zach 6586cc2788 do not use apply() in initializer
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 23:21:07 +02:00
zach 0be98a64ac Merge branch 'main' into hyprsunset-manager-rewrite
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 54s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 18:02:27 +02:00
zach b6e7ee7a54 Merge pull request 'lid behavior watcher to lock session' (#115) from lid-switch-behavior into main
Reviewed-on: #115
2026-06-03 18:02:21 +02:00
AramJonghu 59789ab8d3 unused imports in shell.qml
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 18:01:10 +02:00
AramJonghu a128c0fa40 removal reduntant config option and settings, unused lines in Lock.qml
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 52s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 17:59:03 +02:00
AramJonghu 6f856e2162 fix typo
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-03 17:47:12 +02:00
AramJonghu a19701222b forgotton import and removal LidService
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 26s
Python / test (pull_request) Successful in 41s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 17:43:58 +02:00
AramJonghu 0d8f558f66 Took caelestia lid logic (prepare to sleep) instead
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-03 17:41:29 +02:00
AramJonghu ed28d8b56a Took caelestia lid logic (prepare to sleep) instead 2026-06-03 17:41:17 +02:00
zach d246ba1800 rewrite the manager responsible for handling automatic hyprsunset temperature activation as a qml plugin
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-06-02 16:09:48 +02:00
65 changed files with 2206 additions and 1276 deletions
+58 -3
View File
@@ -2,7 +2,62 @@ import QtQuick
import qs.Config import qs.Config
NumberAnimation { NumberAnimation {
duration: Appearance.anim.durations.normal enum Type {
easing.bezierCurve: Appearance.anim.curves.standard StandardSmall = 0,
easing.type: Easing.BezierSpline Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: Anim.DefaultSpatial
duration: {
if (type < Anim.StandardSmall || type > Anim.SlowEffects)
return Appearance.anim.durations.normal;
if (type === Anim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.durations.large;
if (type === Anim.FastEffects)
return Appearance.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Appearance.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.standard;
}
} }
+156 -33
View File
@@ -1,51 +1,174 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Templates import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import qs.Config import qs.Config
Slider { Slider {
id: root id: root
property color color: DynamicColors.palette.m3primary property bool animateWave
property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property real filledWidth
property real pos: visualPosition
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
background: Item { signal interaction(v: real)
CustomRect {
anchors.bottom: parent.bottom implicitHeight: 12
anchors.bottomMargin: root.implicitHeight / 6 implicitWidth: 200
anchors.left: parent.left
anchors.top: parent.top contentItem: Item {
anchors.topMargin: root.implicitHeight / 6 anchors.fill: parent
bottomRightRadius: root.implicitHeight / 6
color: root.color
implicitWidth: root.handle.x - root.implicitHeight / 6
radius: root.implicitHeight / 6
topRightRadius: root.implicitHeight / 6
}
CustomRect { CustomRect {
anchors.bottom: parent.bottom id: remaining
anchors.bottomMargin: root.implicitHeight / 6
anchors.left: handle.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 6
bottomLeftRadius: root.implicitHeight / 6
color: DynamicColors.tPalette.m3surfaceContainerHighest
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 6
radius: root.implicitHeight / 6
topLeftRadius: root.implicitHeight / 6
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: root.color bottomLeftRadius: Appearance.rounding.extraSmall / 2
implicitHeight: root.implicitHeight color: root.bgColor
implicitWidth: root.implicitHeight / 4.5 implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: Appearance.rounding.small
topLeftRadius: Appearance.rounding.extraSmall / 2
}
CustomRect {
anchors.right: parent.right
anchors.rightMargin: 4 * remaining.opacity
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Appearance.rounding.full radius: Appearance.rounding.full
x: root.visualPosition * root.availableWidth - implicitWidth / 2 }
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
const mult = parent.height <= 12 ? 3 : 1.2;
const pressMult = parent.height <= 12 ? 4 : 1.5;
return parent.height * (mouse.pressed ? pressMult : mult);
}
implicitWidth: 4
radius: Appearance.rounding.full
Behavior on implicitHeight {
Anim {
type: Anim.FastSpatial
}
}
}
Loader {
id: filled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: root.wavy ? waveComp : lineComp
}
Component {
id: lineComp
CustomRect {
bottomRightRadius: Appearance.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: root.filledWidth
radius: Appearance.rounding.small
topRightRadius: Appearance.rounding.extraSmall / 2
}
}
Component {
id: waveComp
WavyLine {
color: root.fgColor
frequency: root.waveFrequency
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: !root.animateWave
running: true
to: 1
}
}
}
}
Behavior on filledWidth {
id: widthBehavior
Anim {
}
}
Component.onCompleted: filledWidth = Qt.binding(() => (width - handle.implicitWidth - handle.anchors.leftMargin) * pos)
Binding {
id: posBinding
property: "pos"
target: root
value: ZUtils.clamp(mouse.pressStartPos + mouse.dragMovement, 0, 1)
when: mouse.pressed
}
MouseArea { MouseArea {
acceptedButtons: Qt.NoButton id: mouse
anchors.fill: parent
cursorShape: Qt.PointingHandCursor property real dragMovement
property real pressStartPos
property real pressStartX
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: handle.implicitHeight
preventStealing: true
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
pressStartX = e.x;
pressStartPos = root.visualPosition;
}
onReleased: e => {
root.interaction(posBinding.value);
widthBehavior.enabled = true;
dragMovement = 0;
} }
} }
} }
+35 -64
View File
@@ -1,7 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Config import qs.Config
import qs.Helpers
Row { Row {
id: root id: root
@@ -12,62 +11,46 @@ Row {
} }
property alias active: menu.active property alias active: menu.active
property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer property color colour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property bool disabled property bool disabled
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1) property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38) property color disabledTextColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded property alias expanded: menu.expanded
property string fallbackIcon property string fallbackIcon
property string fallbackText property string fallbackText
property real horizontalPadding: Appearance.padding.normal property real horizontalPadding: Appearance.padding.larger
property alias iconLabel: iconLabel readonly property alias iconLabel: iconLabel
property alias label: label readonly property alias label: label
property alias menu: menu readonly property alias menu: menu
property alias menuItems: menu.items property alias menuItems: menu.items
property bool menuOnTop property bool menuOnTop
property alias stateLayer: stateLayer property real minLeftWidth
property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer readonly property alias stateLayer: stateLayer
property color textColour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: CustomSplitButton.Filled property int type: CustomSplitButton.Filled
property real verticalPadding: Appearance.padding.smaller property real verticalPadding: Appearance.padding.small
function closeDropdown(): void { spacing: Math.floor(Appearance.spacing.extraSmall)
SettingsDropdowns.close(menu);
}
function openDropdown(): void {
SettingsDropdowns.open(menu, root);
}
function toggleDropdown(): void {
SettingsDropdowns.toggle(menu, root);
}
spacing: Math.floor(Appearance.spacing.small / 2)
onExpandedChanged: {
if (!expanded)
SettingsDropdowns.forget(menu);
}
CustomRect { CustomRect {
bottomRightRadius: Appearance.rounding.small / 2 bottomRightRadius: Appearance.rounding.small / 2
color: !root.enabled ? root.disabledColor : root.color color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandBtn.implicitHeight implicitHeight: expandBtn.implicitHeight
implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2 implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2)
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topRightRadius: Appearance.rounding.small / 2 topRightRadius: Appearance.rounding.small / 2
StateLayer { StateLayer {
id: stateLayer id: stateLayer
function onClicked(): void { bottomRightRadius: parent.bottomRightRadius
root.active?.clicked(); color: root.textColour
} disabled: root.disabled
topRightRadius: parent.topRightRadius
color: root.textColor onClicked: root.active?.clicked()
disabled: !root.enabled
rect.bottomRightRadius: parent.bottomRightRadius
rect.topRightRadius: parent.topRightRadius
} }
RowLayout { RowLayout {
@@ -82,7 +65,7 @@ Row {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
animate: true animate: true
color: !root.enabled ? root.disabledTextColor : root.textColor color: root.disabled ? root.disabledTextColour : root.textColour
fill: 1 fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon text: root.active?.activeIcon ?? root.fallbackIcon
} }
@@ -94,12 +77,12 @@ Row {
Layout.preferredWidth: implicitWidth Layout.preferredWidth: implicitWidth
animate: true animate: true
clip: true clip: true
color: !root.enabled ? root.disabledTextColor : root.textColor color: root.disabled ? root.disabledTextColour : root.textColour
text: root.active?.activeText ?? root.fallbackText text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth { Behavior on Layout.preferredWidth {
Anim { Anim {
easing.bezierCurve: Appearance.anim.curves.emphasized type: Anim.Emphasized
} }
} }
} }
@@ -112,7 +95,7 @@ Row {
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2 property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad bottomLeftRadius: rad
color: !root.enabled ? root.disabledColor : root.color color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2 implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -126,14 +109,12 @@ Row {
StateLayer { StateLayer {
id: expandStateLayer id: expandStateLayer
function onClicked(): void { color: root.textColour
root.toggleDropdown(); disabled: root.disabled
}
color: root.textColor
disabled: !root.enabled
rect.bottomLeftRadius: parent.bottomLeftRadius rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius rect.topLeftRadius: parent.topLeftRadius
onClicked: root.expanded = !root.expanded
} }
MaterialIcon { MaterialIcon {
@@ -141,7 +122,7 @@ Row {
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4) anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
color: !root.enabled ? root.disabledTextColor : root.textColor color: root.disabled ? root.disabledTextColour : root.textColour
rotation: root.expanded ? 180 : 0 rotation: root.expanded ? 180 : 0
text: "expand_more" text: "expand_more"
@@ -154,24 +135,14 @@ Row {
} }
} }
} }
}
Menu { Menu {
id: menu id: menu
anchors.bottomMargin: Appearance.spacing.small attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom
anchors.right: parent.right attachTo: expandBtn
anchors.top: parent.bottom marginY: Appearance.spacing.small * (root.menuOnTop ? -1 : 1)
anchors.topMargin: Appearance.spacing.small thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top
states: State {
when: root.menuOnTop
AnchorChanges {
anchors.bottom: expandBtn.top
anchors.top: undefined
target: menu
}
}
}
} }
} }
+6 -6
View File
@@ -9,7 +9,6 @@ Item {
property alias active: splitButton.active property alias active: splitButton.active
property alias buttonAlias: splitButton property alias buttonAlias: splitButton
property bool enabled: true
property alias expanded: splitButton.expanded property alias expanded: splitButton.expanded
property int expandedZ: 100 property int expandedZ: 100
required property string label required property string label
@@ -25,7 +24,7 @@ Item {
implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2 implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0 opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8 scale: shouldBeActive ? 1 : 0.8
z: root.expanded ? expandedZ : -1 z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity { Behavior on opacity {
Anim { Anim {
@@ -50,7 +49,6 @@ Item {
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: root.label text: root.label
z: root.expanded ? root.expandedZ : -1
} }
CustomSplitButton { CustomSplitButton {
@@ -58,14 +56,16 @@ Item {
enabled: root.enabled enabled: root.enabled
type: CustomSplitButton.Filled type: CustomSplitButton.Filled
z: root.expanded ? root.expandedZ : -1 z: 2
menu.onItemSelected: item => { menu.onItemSelected: item => {
root.selected(item); root.selected(item);
splitButton.closeDropdown(); // splitButton.closeDropdown();
} }
stateLayer.onClicked: { stateLayer.onClicked: {
splitButton.toggleDropdown(); // splitButton.toggleDropdown();
splitButton.expanded = !splitButton.expanded;
console.log(root.z);
} }
} }
} }
+14 -12
View File
@@ -1,6 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Templates
import QtQuick.Shapes import QtQuick.Shapes
import QtQuick.Templates
import qs.Config import qs.Config
Switch { Switch {
@@ -13,26 +13,28 @@ Switch {
indicator: CustomRect { indicator: CustomRect {
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: 13 + 7 * 2 implicitHeight: Appearance.font.size.medium + Appearance.padding.normal * 2
implicitWidth: implicitHeight * 1.7 implicitWidth: implicitHeight * 1.7
radius: Appearance.rounding.full radius: Appearance.rounding.full
CustomRect { CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1) color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - 10 implicitHeight: parent.implicitHeight - Appearance.padding.extraSmall
implicitWidth: nonAnimWidth implicitWidth: nonAnimWidth
radius: Appearance.rounding.full radius: Appearance.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2 x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.extraSmall / 2 : Appearance.padding.extraSmall / 2
Behavior on implicitWidth { Behavior on implicitWidth {
Anim { Anim {
type: Anim.FastSpatial
} }
} }
Behavior on x { Behavior on x {
Anim { Anim {
type: Anim.FastSpatial
} }
} }
@@ -44,6 +46,7 @@ Switch {
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.DefaultEffects
} }
} }
} }
@@ -63,14 +66,14 @@ Switch {
} }
property point end2: { property point end2: {
if (root.pressed) if (root.pressed)
return Qt.point(width, height / 2); return Qt.point(width * 0.8, height / 2);
if (root.checked) if (root.checked)
return Qt.point(width * 0.85, height * 0.2); return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15); return Qt.point(width * 0.85, height * 0.15);
} }
property point start1: { property point start1: {
if (root.pressed) if (root.pressed)
return Qt.point(width * 0.1, height / 2); return Qt.point(width * 0.2, height / 2);
if (root.checked) if (root.checked)
return Qt.point(width * 0.15, height / 2); return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15); return Qt.point(width * 0.15, height * 0.15);
@@ -88,7 +91,7 @@ Switch {
anchors.centerIn: parent anchors.centerIn: parent
asynchronous: true asynchronous: true
height: parent.implicitHeight - Appearance.padding.small * 2 height: parent.implicitHeight - Appearance.padding.larger
preferredRendererType: Shape.CurveRenderer preferredRendererType: Shape.CurveRenderer
width: height width: height
@@ -110,7 +113,7 @@ Switch {
} }
ShapePath { ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap capStyle: ShapePath.RoundCap
fillColor: "transparent" fillColor: "transparent"
startX: icon.start1.x startX: icon.start1.x
startY: icon.start1.y startY: icon.start1.y
@@ -148,8 +151,7 @@ Switch {
} }
component PropAnim: PropertyAnimation { component PropAnim: PropertyAnimation {
duration: MaterialEasing.expressiveEffectsTime duration: Appearance.anim.durations.expressiveFastSpatial
easing.bezierCurve: MaterialEasing.expressiveEffects easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
easing.type: Easing.BezierSpline
} }
} }
+2 -1
View File
@@ -1,6 +1,6 @@
import qs.Config
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import qs.Config
RectangularShadow { RectangularShadow {
property real dp: [0, 1, 3, 6, 8, 12][level] property real dp: [0, 1, 3, 6, 8, 12][level]
@@ -13,6 +13,7 @@ RectangularShadow {
Behavior on dp { Behavior on dp {
Anim { Anim {
type: Anim.SlowEffects
} }
} }
} }
+98 -38
View File
@@ -2,49 +2,107 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import qs.Config import qs.Config
import qs.Drawers
Elevation { MouseArea {
id: root id: root
enum Side {
Top,
Bottom,
Left,
Right
}
property MenuItem active: items[0] ?? null property MenuItem active: items[0] ?? null
property int attachSideX: Menu.Right
property int attachSideY: Menu.Bottom
required property Item attachTo
property bool expanded property bool expanded
property list<MenuItem> items property list<MenuItem> items
property real marginX
property real marginY
property int thisSideX: Menu.Right
property int thisSideY: Menu.Top
signal itemSelected(item: MenuItem) signal itemSelected(item: MenuItem)
implicitHeight: root.expanded ? column.implicitHeight + Appearance.padding.small * 2 : 0 anchors.fill: parent
implicitWidth: Math.max(200, column.implicitWidth) enabled: expanded
level: 2 layer.enabled: opacity < 1
opacity: root.expanded ? 1 : 0 opacity: expanded ? 1 : 0
radius: Appearance.rounding.normal parent: {
const win = QsWindow.window;
const contentWin = win as Windows;
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
Behavior on implicitHeight {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity { Behavior on opacity {
Anim { Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial type: Anim.DefaultEffects
} }
} }
CustomClippingRect { onClicked: expanded = false
TransformWatcher {
id: watcher
a: root.parent
b: root.attachTo
}
Elevation {
id: menu
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Appearance.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideX === Menu.Left ? 0 : item.width;
if (root.thisSideX === Menu.Right)
off -= width;
return item.mapToItem(root.parent, off, 0).x + root.marginX;
}
y: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideY === Menu.Top ? 0 : item.height;
if (root.thisSideY === Menu.Bottom)
off -= height;
return item.mapToItem(root.parent, 0, off).y + root.marginY;
}
transform: Scale {
origin.y: root.thisSideY === Menu.Bottom ? menu.height : 0
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {
}
}
}
CustomRect {
anchors.fill: parent anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainer color: DynamicColors.palette.m3surfaceContainerLow
radius: parent.radius radius: parent.radius
ColumnLayout { ColumnLayout {
id: column id: column
anchors.left: parent.left anchors.fill: parent
anchors.right: parent.right anchors.margins: Appearance.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter spacing: Appearance.spacing.extraSmall
spacing: 5
Repeater { Repeater {
id: repeater
model: root.items model: root.items
CustomRect { CustomRect {
@@ -55,25 +113,25 @@ Elevation {
required property MenuItem modelData required property MenuItem modelData
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2 color: Qt.alpha(DynamicColors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2 implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.larger * 2
radius: Appearance.rounding.small
CustomRect { Behavior on radius {
anchors.fill: parent Anim {
anchors.leftMargin: Appearance.padding.small }
anchors.rightMargin: Appearance.padding.small
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
radius: Appearance.rounding.normal - Appearance.padding.small
StateLayer {
function onClicked(): void {
root.itemSelected(item.modelData);
root.active = item.modelData;
root.expanded = false;
} }
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface StateLayer {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
disabled: !root.expanded disabled: !root.expanded
onClicked: {
root.itemSelected(item.modelData);
root.active = item.modelData;
item.modelData.clicked();
root.expanded = false;
} }
} }
@@ -81,29 +139,30 @@ Elevation {
id: menuOptionRow id: menuOptionRow
anchors.fill: parent anchors.fill: parent
anchors.margins: Appearance.padding.normal anchors.margins: Appearance.padding.larger
spacing: Appearance.spacing.small spacing: Appearance.spacing.small
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.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 ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.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
visible: active visible: active
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.trailingIcon text: item.modelData.trailingIcon
} }
} }
@@ -113,3 +172,4 @@ Elevation {
} }
} }
} }
}
+167 -62
View File
@@ -1,15 +1,53 @@
import qs.Config
import QtQuick import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import qs.Config
MouseArea { MouseArea {
id: root id: root
property color color: DynamicColors.palette.m3onSurface property alias bottomLeftRadius: base.bottomLeftRadius
property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius
property alias color: base.color
property bool disabled property bool disabled
property real radius: parent?.radius ?? 0 readonly property real endRadius: {
property alias rect: hoverLayer const d1 = distSq(0, 0);
const d2 = distSq(width, 0);
const d3 = distSq(0, height);
const d4 = distSq(width, height);
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
property alias radius: base.radius
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
function onClicked(): void { function clamp(r: real): real {
return Math.max(0, Math.min(r, width / 2, height / 2));
}
function distSq(x: real, y: real): real {
return (pressX - x) ** 2 + (pressY - y) ** 2;
}
function press(x: real, y: real): void {
pressX = x;
pressY = y;
fadeAnim.complete();
circleRadius = 0;
circle.opacity = 0.1;
rippleAnim.restart();
endRadiusAtPress = endRadius;
} }
anchors.fill: parent anchors.fill: parent
@@ -17,79 +55,146 @@ MouseArea {
enabled: !disabled enabled: !disabled
hoverEnabled: true hoverEnabled: true
onClicked: event => !disabled && onClicked(event) Behavior on stateOpacity {
onPressed: event => { Anim {
if (disabled) type: Anim.DefaultEffects
return; }
rippleAnim.x = event.x;
rippleAnim.y = event.y;
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
} }
SequentialAnimation { onCircleRadiusChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onClicked: event => !disabled && onClicked(event)
onManualPressOverrideChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onPressed: e => press(e.x, e.y)
onPressedChanged: {
if (!(pressed || manualPressOverride) && !rippleAnim.running && circle.opacity > 0)
fadeAnim.start();
}
Anim {
id: rippleAnim id: rippleAnim
property real radius alwaysRunToEnd: true
property real x duration: Appearance.anim.durations.expressiveSlowEffects * 2
property real y easing.bezierCurve: Appearance.anim.curves.standard
property: "circleRadius"
PropertyAction { target: root
property: "x" to: root.endRadius
target: ripple
value: rippleAnim.x
}
PropertyAction {
property: "y"
target: ripple
value: rippleAnim.y
}
PropertyAction {
property: "opacity"
target: ripple
value: 0.08
} }
Anim { Anim {
easing.bezierCurve: MaterialEasing.standardDecel id: fadeAnim
from: 0
properties: "implicitWidth,implicitHeight"
target: ripple
to: rippleAnim.radius * 2
}
Anim {
property: "opacity" property: "opacity"
target: ripple target: circle
to: 0 to: 0
type: Anim.SlowEffects
} }
}
CustomClippingRect {
id: hoverLayer
anchors.fill: parent
border.pixelAligned: false
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
CustomRect { CustomRect {
id: ripple id: base
border.pixelAligned: false anchors.fill: parent
color: root.color bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: DynamicColors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
}
Shape {
id: circle
anchors.fill: parent
opacity: 0 opacity: 0
radius: Appearance.rounding.full preferredRendererType: Shape.CurveRenderer
transform: Translate { ShapePath {
x: -ripple.width / 2 fillColor: base.color
y: -ripple.height / 2 startX: root.clamp(base.topLeftRadius)
startY: 0
strokeColor: "transparent"
strokeWidth: 0
fillGradient: RadialGradient {
centerRadius: root.circleRadius
centerX: root.pressX
centerY: root.pressY
focalX: centerX
focalY: centerY
GradientStop {
color: Qt.alpha(base.color, 1)
position: 0
}
GradientStop {
color: Qt.alpha(base.color, 1)
position: ZUtils.clamp(1 - 0.2 * root.endRadius / root.circleRadius, 0.01, 0.99)
}
GradientStop {
color: Qt.alpha(base.color, ZUtils.clamp((root.circleRadius / root.endRadius - 0.9) / 0.1, 0, 1))
position: 1
}
}
PathLine {
x: root.width - root.clamp(base.topRightRadius)
y: 0
}
PathArc {
radiusX: root.clamp(base.topRightRadius)
radiusY: root.clamp(base.topRightRadius)
relativeX: root.clamp(base.topRightRadius)
relativeY: root.clamp(base.topRightRadius)
}
PathLine {
x: root.width
y: root.height - root.clamp(base.bottomRightRadius)
}
PathArc {
radiusX: root.clamp(base.bottomRightRadius)
radiusY: root.clamp(base.bottomRightRadius)
relativeX: -root.clamp(base.bottomRightRadius)
relativeY: root.clamp(base.bottomRightRadius)
}
PathLine {
x: root.clamp(base.bottomLeftRadius)
y: root.height
}
PathArc {
radiusX: root.clamp(base.bottomLeftRadius)
radiusY: root.clamp(base.bottomLeftRadius)
relativeX: -root.clamp(base.bottomLeftRadius)
relativeY: -root.clamp(base.bottomLeftRadius)
}
PathLine {
x: 0
y: root.clamp(base.topLeftRadius)
}
PathArc {
radiusX: root.clamp(base.topLeftRadius)
radiusY: root.clamp(base.topLeftRadius)
x: root.clamp(base.topLeftRadius)
y: 0
} }
} }
} }
-2
View File
@@ -7,8 +7,6 @@ Singleton {
readonly property AppearanceConf.Deform deform: Config.appearance.deform readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding readonly property AppearanceConf.Padding padding: Config.appearance.padding
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
+16 -4
View File
@@ -28,9 +28,13 @@ JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1] property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1] property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1] property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1] property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1] property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1] property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1] property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1] property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1] property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
@@ -38,7 +42,9 @@ JsonObject {
component AnimDurations: JsonObject { component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale property int extraLarge: 1000 * scale
property int large: 600 * scale property int large: 600 * scale
property int normal: 400 * scale property int normal: 400 * scale
@@ -57,7 +63,8 @@ JsonObject {
component FontSize: JsonObject { component FontSize: JsonObject {
property int extraLarge: 28 * scale property int extraLarge: 28 * scale
property int large: 18 * scale property int large: 18 * scale
property int larger: 15 * scale property int larger: 16 * scale
property int medium: 14 * scale
property int normal: 13 * scale property int normal: 13 * scale
property real scale: 1 property real scale: 1
property int small: 11 * scale property int small: 11 * scale
@@ -70,23 +77,28 @@ JsonObject {
} }
} }
component Padding: JsonObject { component Padding: JsonObject {
property int large: 15 * scale property int extraLargeIncreased: 32 * scale
property int larger: 13 * scale property int extraSmall: 4 * scale
property int normal: 9 * scale property int large: 16 * scale
property int larger: 12 * scale
property int normal: 8 * scale
property real scale: 1 property real scale: 1
property int small: 5 * scale property int small: 5 * scale
property int smaller: 7 * scale property int smaller: 7 * scale
property int smallest: 2 * scale property int smallest: 2 * scale
} }
component Rounding: JsonObject { component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale property int full: 1000 * scale
property int large: 24 * scale property int large: 24 * scale
property int medium: 16 * scale
property int normal: 18 * scale property int normal: 18 * scale
property real scale: 1 property real scale: 1
property int small: 12 * scale property int small: 12 * scale
property int smallest: 8 * scale property int smallest: 8 * scale
} }
component Spacing: JsonObject { component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale property int large: 20 * scale
property int larger: 16 * scale property int larger: 16 * scale
property int normal: 12 * scale property int normal: 12 * scale
-1
View File
@@ -253,7 +253,6 @@ Singleton {
return { return {
recolorLogo: lock.recolorLogo, recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint, enableFprint: lock.enableFprint,
lidWatch: lock.lidWatch,
showNotifContent: lock.showNotifContent, showNotifContent: lock.showNotifContent,
showNotifIcon: lock.showNotifIcon, showNotifIcon: lock.showNotifIcon,
maxFprintTries: lock.maxFprintTries, maxFprintTries: lock.maxFprintTries,
-1
View File
@@ -4,7 +4,6 @@ JsonObject {
property int blurAmount: 40 property int blurAmount: 40
property bool enableFprint: true property bool enableFprint: true
property int maxFprintTries: 3 property int maxFprintTries: 3
property bool lidWatch: false
property bool recolorLogo: false property bool recolorLogo: false
property bool showNotifContent: false property bool showNotifContent: false
property bool showNotifIcon: true property bool showNotifIcon: true
-18
View File
@@ -1,18 +0,0 @@
import Quickshell
import ZShell
import QtQuick
Scope {
id: root
signal requestLock
Connections {
function onStateChanged(): void {
if (LidWatcher.state === LidWatcher.Closed)
root.requestLock();
}
target: LidWatcher
}
}
+25
View File
@@ -0,0 +1,25 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Variants {
model: Quickshell.screens
Scope {
id: scope
required property ShellScreen modelData
Exclusions {
bar: content.bar
screen: scope.modelData
}
Windows {
id: content
screen: scope.modelData
}
}
}
+7 -4
View File
@@ -59,7 +59,7 @@ Item {
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0 dragThreshold: 0
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1 maximumPointCount: 1
minimumPointCount: 1 minimumPointCount: 1
target: null target: null
@@ -69,14 +69,17 @@ Item {
root.singleGestureTriggered = false; root.singleGestureTriggered = false;
} }
onCentroidChanged: { onCentroidChanged: {
if (root.singleGestureTriggered)
return;
const x = centroid.position.x; const x = centroid.position.x;
const y = centroid.position.y; const y = centroid.position.y;
const dragX = x - centroid.pressPosition.x; const dragX = x - centroid.pressPosition.x;
const dragY = y - centroid.pressPosition.y; const dragY = y - centroid.pressPosition.y;
if (centroid.pressPosition.y >= root.screen.height - Config.barConfig.border && dragY < -200)
root.visibilities.launcher = true;
if (root.singleGestureTriggered)
return;
if (centroid.pressPosition.y < root.bar.implicitHeight) { if (centroid.pressPosition.y < root.bar.implicitHeight) {
if (dragY > 20) { if (dragY > 20) {
root.visibilities.settings = true; root.visibilities.settings = true;
+11 -26
View File
@@ -9,29 +9,17 @@ import Quickshell.Hyprland
import ZShell.Blobs import ZShell.Blobs
import qs.Daemons import qs.Daemons
import qs.Components import qs.Components
import qs.Modules
import qs.Modules.Bar import qs.Modules.Bar
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
import qs.Drawers import qs.Drawers
Variants {
model: Quickshell.screens
Scope {
id: scope
required property var modelData
Exclusions {
bar: bar
screen: scope.modelData
}
CustomWindow { CustomWindow {
id: win id: root
readonly property alias bar: bar
readonly property bool hasFullscreen: Hypr.monitorFor(screen)?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2) readonly property bool hasFullscreen: Hypr.monitorFor(screen)?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2)
readonly property alias interactionWrapper: interactions
property var root: Quickshell.shellDir property var root: Quickshell.shellDir
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
@@ -40,7 +28,6 @@ Variants {
contentItem.focus: true contentItem.focus: true
mask: visibilities.isDrawing ? null : region mask: visibilities.isDrawing ? null : region
name: "Bar" name: "Bar"
screen: scope.modelData
contentItem.Keys.onEscapePressed: { contentItem.Keys.onEscapePressed: {
if (Config.barConfig.autoHide) if (Config.barConfig.autoHide)
@@ -62,10 +49,10 @@ Variants {
Region { Region {
id: region id: region
height: win.height - bar.implicitHeight - Config.barConfig.border height: root.height - bar.implicitHeight - Config.barConfig.border
intersection: Intersection.Xor intersection: Intersection.Xor
regions: popoutRegions.instances regions: popoutRegions.instances
width: win.width - Config.barConfig.border * 2 width: root.width - Config.barConfig.border * 2
x: Config.barConfig.border x: Config.barConfig.border
y: bar.implicitHeight y: bar.implicitHeight
} }
@@ -97,7 +84,7 @@ Variants {
id: focusGrab id: focusGrab
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu")) active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
windows: [win] windows: [root]
onCleared: { onCleared: {
visibilities.launcher = false; visibilities.launcher = false;
@@ -125,7 +112,7 @@ Variants {
property bool settings property bool settings
property bool sidebar property bool sidebar
Component.onCompleted: Visibilities.load(scope.modelData, this) Component.onCompleted: Visibilities.load(root.screen, this)
} }
IpcHandler { IpcHandler {
@@ -336,7 +323,7 @@ Variants {
} }
Interactions { Interactions {
id: mouseArea id: interactions
anchors.fill: parent anchors.fill: parent
bar: bar bar: bar
@@ -345,7 +332,7 @@ Variants {
input: inputLoader.item input: inputLoader.item
panels: panels panels: panels
popouts: panels.popouts popouts: panels.popouts
screen: scope.modelData screen: root.screen
visibilities: visibilities visibilities: visibilities
z: 1 z: 1
@@ -354,7 +341,7 @@ Variants {
bar: bar bar: bar
drawingItem: drawingLoader.item drawingItem: drawingLoader.item
screen: scope.modelData screen: root.screen
visibilities: visibilities visibilities: visibilities
dashboard.transform: Matrix4x4 { dashboard.transform: Matrix4x4 {
@@ -396,12 +383,10 @@ Variants {
anchors.right: parent.right anchors.right: parent.right
popouts: panels.popouts popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper popoutsWrapper: panels.popoutsWrapper
screen: scope.modelData screen: root.screen
visibilities: visibilities visibilities: visibilities
} }
} }
}
}
component PanelBg: BlobRect { component PanelBg: BlobRect {
property real deformAmount: 0.15 property real deformAmount: 0.15
+11 -59
View File
@@ -2,12 +2,13 @@ pragma Singleton
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Services
import qs.Config import qs.Config
Singleton { Singleton {
id: root id: root
property bool enabled readonly property bool enabled: service.enabled
readonly property int end: Config.general.color.scheduleHyprsunsetEnd readonly property int end: Config.general.color.scheduleHyprsunsetEnd
property bool manualToggle: false property bool manualToggle: false
readonly property int start: Config.general.color.scheduleHyprsunsetStart readonly property int start: Config.general.color.scheduleHyprsunsetStart
@@ -17,69 +18,20 @@ Singleton {
if (!Config.general.color.scheduleHyprsunset) if (!Config.general.color.scheduleHyprsunset)
return; return;
var now = new Date(); service.apply();
if (now.getHours() >= root.start || now.getHours() < root.end) {
root.startNightLight(root.temp);
} else {
root.stopNightLight();
}
}
function startNightLight(temp: int): void {
Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", `${temp}`]);
root.enabled = true;
}
function stopNightLight(): void {
Quickshell.execDetached(["hyprctl", "hyprsunset", "identity"]);
root.enabled = false;
} }
function toggleNightLight(): void { function toggleNightLight(): void {
if (enabled) service.manualToggle = true;
stopNightLight(); service.toggle();
else
startNightLight(temp);
} }
onManualToggleChanged: { HyprsunsetManager {
if (root.manualToggle) id: service
manualTimer.start();
}
Timer { activeAuto: Config.general.color.scheduleHyprsunset
id: manualTimer endTime: root.end
startTime: root.start
interval: 60000 * 60 temp: root.temp
repeat: false
running: false
onTriggered: {
root.manualToggle = false;
}
}
Timer {
interval: 5000
repeat: true
running: true
triggeredOnStart: true
onTriggered: {
if (!Config.general.color.scheduleHyprsunset)
return;
if (root.manualToggle)
return;
var now = new Date();
if (now.getHours() >= root.start || now.getHours() < root.end) {
if (!root.enabled)
root.startNightLight(root.temp);
} else {
if (root.enabled)
root.stopNightLight();
}
}
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ CustomRect {
anchors.centerIn: parent anchors.centerIn: parent
color: root.visibilities.dashboard ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface color: root.visibilities.dashboard ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
font: Appearance.font.family.mono font: Appearance.font.family.mono // qmllint disable incompatible-type
text: Time.dateStr text: Time.dateStr
Behavior on color { Behavior on color {
+5
View File
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import ZShell.Internal
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
@@ -29,6 +30,10 @@ Scope {
Quickshell.execDetached(action); Quickshell.execDetached(action);
} }
LidWatcher {
onAboutToSleep: root.lock.lock.locked = true
}
Variants { Variants {
model: Config.general.idle.timeouts model: Config.general.idle.timeouts
-11
View File
@@ -9,7 +9,6 @@ import qs.Components
Scope { Scope {
id: root id: root
required property var lid
property alias lock: lock property alias lock: lock
property int seenOnce: 0 property int seenOnce: 0
@@ -19,16 +18,6 @@ Scope {
signal requestLock signal requestLock
signal unlock signal unlock
onRequestLock: lock.locked = true
onUnlock: lock.locked = false
Connections {
target: root.lid
function onRequestLock(): void {
lock.locked = true
}
}
LockSurface { LockSurface {
id: lockSurface id: lockSurface
+1 -2
View File
@@ -62,6 +62,7 @@ SettingsPage {
SettingsSection { SettingsSection {
sectionId: "Color" sectionId: "Color"
z: 1
SettingsHeader { SettingsHeader {
name: "Color" name: "Color"
@@ -105,7 +106,6 @@ SettingsPage {
active: root.schemeTypeItem(menuItems, Config.colors.schemeType) active: root.schemeTypeItem(menuItems, Config.colors.schemeType)
enabled: Config.general.color.schemeGeneration enabled: Config.general.color.schemeGeneration
label: qsTr("Scheme type") label: qsTr("Scheme type")
z: 2
menuItems: [ menuItems: [
MenuItem { MenuItem {
@@ -250,7 +250,6 @@ SettingsPage {
SettingsSection { SettingsSection {
sectionId: "Default Apps" sectionId: "Default Apps"
z: -1
SettingsHeader { SettingsHeader {
name: "Default Apps" name: "Default Apps"
@@ -60,17 +60,6 @@ SettingsPage {
Separator { Separator {
} }
SettingSwitch {
name: "Laptop lid watch to lock session"
object: Config.lock
setting: "lidWatch"
shouldBeActive: Battery.isLaptop
}
Separator {
shouldBeActive: Battery.isLaptop
}
SettingSpinBox { SettingSpinBox {
min: 0 min: 0
name: "Blur amount" name: "Blur amount"
@@ -141,14 +141,14 @@ Item {
id: zoomSlider id: zoomSlider
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 30 Layout.preferredHeight: Appearance.padding.larger * 3
from: 1.0 from: 1.0
implicitHeight: 30 implicitHeight: Appearance.padding.larger * 3
to: 5.0 to: 5.0
value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0 value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0
onMoved: { onInteraction: value => {
delegate.zoomClipRect(value); delegate.zoomClipRect(1 + (value * 4));
wrapper.changesMade = true; wrapper.changesMade = true;
} }
} }
+31 -31
View File
@@ -19,7 +19,7 @@ Item {
required property var wrapper required property var wrapper
implicitHeight: vol.implicitHeight + Appearance.padding.small * 2 implicitHeight: vol.implicitHeight + Appearance.padding.small * 2
implicitWidth: 400 + Appearance.padding.small * 2 implicitWidth: 600 + Appearance.padding.small * 2
CustomRect { CustomRect {
anchors.left: parent.left anchors.left: parent.left
@@ -52,7 +52,7 @@ Item {
CustomRect { CustomRect {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 50 + Appearance.spacing.smaller * 2 Layout.preferredHeight: 65 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding radius: root.rounding
@@ -62,15 +62,15 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Appearance.padding.normal anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top anchors.top: parent.top
implicitWidth: childrenRect.width implicitWidth: childrenRect.width
CustomRect { CustomRect {
anchors.centerIn: parent anchors.centerIn: parent
color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 40 implicitHeight: 54
implicitWidth: 40 implicitWidth: 54
radius: Appearance.rounding.full radius: Appearance.rounding.full
MaterialIcon { MaterialIcon {
@@ -78,7 +78,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
animate: true animate: true
color: Audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary color: Audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary
font.pointSize: 22 font.pointSize: Appearance.font.size.extraLarge
text: Audio.muted ? "volume_off" : "volume_up" text: Audio.muted ? "volume_off" : "volume_up"
} }
@@ -98,7 +98,7 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest anchors.bottomMargin: Appearance.padding.smallest
anchors.left: sinkIcon.right anchors.left: sinkIcon.right
anchors.leftMargin: Appearance.spacing.normal anchors.leftMargin: Appearance.spacing.larger
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top anchors.top: parent.top
@@ -122,14 +122,14 @@ Item {
} }
CustomMouseArea { CustomMouseArea {
Layout.bottomMargin: 5 Layout.bottomMargin: Appearance.padding.normal
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: Appearance.padding.larger * 3
CustomSlider { CustomSlider {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary fgColor: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: parent.height implicitHeight: parent.height
value: Audio.volume value: Audio.volume
@@ -138,7 +138,7 @@ Item {
} }
} }
onMoved: Audio.setVolume(value) onInteraction: value => Audio.setVolume(value)
} }
} }
} }
@@ -146,7 +146,7 @@ Item {
CustomClippingRect { CustomClippingRect {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 50 + Appearance.spacing.smaller * 2 Layout.preferredHeight: 65 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding radius: root.rounding
@@ -178,15 +178,15 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Appearance.padding.normal anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top anchors.top: parent.top
implicitWidth: childrenRect.width implicitWidth: childrenRect.width
CustomRect { CustomRect {
anchors.centerIn: parent anchors.centerIn: parent
color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 40 implicitHeight: 54
implicitWidth: 40 implicitWidth: 54
radius: Appearance.rounding.full radius: Appearance.rounding.full
MaterialIcon { MaterialIcon {
@@ -194,7 +194,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
animate: true animate: true
color: Audio.sourceMuted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary color: Audio.sourceMuted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary
font.pointSize: 22 font.pointSize: Appearance.font.size.extraLarge
text: Audio.sourceMuted ? "mic_off" : "mic" text: Audio.sourceMuted ? "mic_off" : "mic"
} }
@@ -214,7 +214,7 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest anchors.bottomMargin: Appearance.padding.smallest
anchors.left: sourceIcon.right anchors.left: sourceIcon.right
anchors.leftMargin: Appearance.spacing.normal anchors.leftMargin: Appearance.spacing.larger
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top anchors.top: parent.top
@@ -238,14 +238,14 @@ Item {
} }
CustomMouseArea { CustomMouseArea {
Layout.bottomMargin: 5 Layout.bottomMargin: Appearance.padding.normal
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: Appearance.padding.larger * 3
CustomSlider { CustomSlider {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary fgColor: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: parent.height implicitHeight: parent.height
value: Audio.sourceVolume value: Audio.sourceVolume
@@ -254,7 +254,7 @@ Item {
} }
} }
onMoved: Audio.setSourceVolume(value) onInteraction: value => Audio.setSourceVolume(value)
} }
} }
} }
@@ -280,7 +280,7 @@ Item {
required property var modelData required property var modelData
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 50 + Appearance.spacing.smaller * 2 Layout.preferredHeight: 65 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding radius: root.rounding
@@ -312,15 +312,15 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Appearance.padding.normal anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top anchors.top: parent.top
implicitWidth: childrenRect.width implicitWidth: childrenRect.width
CustomRect { CustomRect {
anchors.centerIn: parent anchors.centerIn: parent
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 40 implicitHeight: 54
implicitWidth: 40 implicitWidth: 54
radius: Appearance.rounding.full radius: Appearance.rounding.full
MaterialIcon { MaterialIcon {
@@ -329,7 +329,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
animate: true animate: true
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary color: appBox.modelData.audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary
font.pointSize: 22 font.pointSize: Appearance.font.size.extraLarge
text: appBox.modelData.audio.muted ? "volume_off" : "volume_up" text: appBox.modelData.audio.muted ? "volume_off" : "volume_up"
} }
@@ -356,7 +356,7 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest anchors.bottomMargin: Appearance.padding.smallest
anchors.left: appBoxIcon.right anchors.left: appBoxIcon.right
anchors.leftMargin: Appearance.spacing.normal anchors.leftMargin: Appearance.spacing.larger
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top anchors.top: parent.top
@@ -381,18 +381,18 @@ Item {
} }
CustomMouseArea { CustomMouseArea {
Layout.bottomMargin: 5 Layout.bottomMargin: Appearance.padding.normal
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: Appearance.padding.larger * 3
CustomSlider { CustomSlider {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary fgColor: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: parent.height implicitHeight: parent.height
value: appBox.modelData.audio.volume value: appBox.modelData.audio.volume
onMoved: { onInteraction: value => {
Audio.setStreamVolume(appBox.modelData, value); Audio.setStreamVolume(appBox.modelData, value);
} }
} }
-2
View File
@@ -28,8 +28,6 @@ Item {
spacing: 1 spacing: 1
Component.onCompleted: console.log(Battery.isLaptop)
CustomRect { CustomRect {
id: track id: track
+4 -4
View File
@@ -18,13 +18,13 @@ public:
explicit BlobGroup(QObject* parent = nullptr); explicit BlobGroup(QObject* parent = nullptr);
~BlobGroup() override; ~BlobGroup() override;
qreal smoothing() const { [[nodiscard]] qreal smoothing() const {
return m_smoothing; return m_smoothing;
} }
void setSmoothing(qreal s); void setSmoothing(qreal s);
QColor color() const { [[nodiscard]] QColor color() const {
return m_color; return m_color;
} }
@@ -36,11 +36,11 @@ void removeShape(BlobShape* shape);
void setInvertedRect(BlobInvertedRect* rect); void setInvertedRect(BlobInvertedRect* rect);
void clearInvertedRect(BlobInvertedRect* rect); void clearInvertedRect(BlobInvertedRect* rect);
const QList<BlobShape*>& shapes() const { [[nodiscard]] const QList<BlobShape*>& shapes() const {
return m_shapes; return m_shapes;
} }
BlobInvertedRect* invertedRect() const { [[nodiscard]] BlobInvertedRect* invertedRect() const {
return m_invertedRect; return m_invertedRect;
} }
+5 -5
View File
@@ -16,25 +16,25 @@ public:
explicit BlobInvertedRect(QQuickItem* parent = nullptr); explicit BlobInvertedRect(QQuickItem* parent = nullptr);
~BlobInvertedRect() override; ~BlobInvertedRect() override;
qreal borderLeft() const { [[nodiscard]] qreal borderLeft() const {
return m_borderLeft; return m_borderLeft;
} }
void setBorderLeft(qreal v); void setBorderLeft(qreal v);
qreal borderRight() const { [[nodiscard]] qreal borderRight() const {
return m_borderRight; return m_borderRight;
} }
void setBorderRight(qreal v); void setBorderRight(qreal v);
qreal borderTop() const { [[nodiscard]] qreal borderTop() const {
return m_borderTop; return m_borderTop;
} }
void setBorderTop(qreal v); void setBorderTop(qreal v);
qreal borderBottom() const { [[nodiscard]] qreal borderBottom() const {
return m_borderBottom; return m_borderBottom;
} }
@@ -47,7 +47,7 @@ void borderTopChanged();
void borderBottomChanged(); void borderBottomChanged();
protected: protected:
bool isInvertedRect() const override { [[nodiscard]] bool isInvertedRect() const override {
return true; return true;
} }
+2 -2
View File
@@ -21,8 +21,8 @@ struct BlobRectData {
class BlobMaterial : public QSGMaterial { class BlobMaterial : public QSGMaterial {
public: public:
QSGMaterialType* type() const override; [[nodiscard]] QSGMaterialType* type() const override;
QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override; [[nodiscard]] QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override;
int compare(const QSGMaterial* other) const override; int compare(const QSGMaterial* other) const override;
float m_paddedX = 0; float m_paddedX = 0;
+7 -7
View File
@@ -24,7 +24,7 @@ public:
explicit BlobRect(QQuickItem* parent = nullptr); explicit BlobRect(QQuickItem* parent = nullptr);
~BlobRect() override; ~BlobRect() override;
qreal stiffness() const { [[nodiscard]] qreal stiffness() const {
return m_stiffness; return m_stiffness;
} }
@@ -35,7 +35,7 @@ void setStiffness(qreal s) {
} }
} }
qreal damping() const { [[nodiscard]] qreal damping() const {
return m_damping; return m_damping;
} }
@@ -46,7 +46,7 @@ void setDamping(qreal d) {
} }
} }
qreal deformScale() const { [[nodiscard]] qreal deformScale() const {
return m_deformScale; return m_deformScale;
} }
@@ -62,25 +62,25 @@ QQmlListProperty<BlobRect> exclude();
bool isExcluded(const BlobShape* other) const override; bool isExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override; void cornerRadii(float out[4]) const override;
qreal topLeftRadius() const { [[nodiscard]] qreal topLeftRadius() const {
return m_topLeftRadius; return m_topLeftRadius;
} }
void setTopLeftRadius(qreal r); void setTopLeftRadius(qreal r);
qreal topRightRadius() const { [[nodiscard]] qreal topRightRadius() const {
return m_topRightRadius; return m_topRightRadius;
} }
void setTopRightRadius(qreal r); void setTopRightRadius(qreal r);
qreal bottomLeftRadius() const { [[nodiscard]] qreal bottomLeftRadius() const {
return m_bottomLeftRadius; return m_bottomLeftRadius;
} }
void setBottomLeftRadius(qreal r); void setBottomLeftRadius(qreal r);
qreal bottomRightRadius() const { [[nodiscard]] qreal bottomRightRadius() const {
return m_bottomRightRadius; return m_bottomRightRadius;
} }
+6 -7
View File
@@ -21,23 +21,23 @@ public:
explicit BlobShape(QQuickItem* parent = nullptr); explicit BlobShape(QQuickItem* parent = nullptr);
~BlobShape() override = default; ~BlobShape() override = default;
BlobGroup* group() const { [[nodiscard]] BlobGroup* group() const {
return m_group; return m_group;
} }
void setGroup(BlobGroup* g); void setGroup(BlobGroup* g);
qreal radius() const { [[nodiscard]] qreal radius() const {
return m_radius; return m_radius;
} }
void setRadius(qreal r); void setRadius(qreal r);
QMatrix4x4 deformMatrix() const { [[nodiscard]] QMatrix4x4 deformMatrix() const {
return m_centeredDeformMatrix; return m_centeredDeformMatrix;
} }
QMatrix4x4 rawDeformMatrix() const { [[nodiscard]] QMatrix4x4 rawDeformMatrix() const {
return m_deformMatrix; return m_deformMatrix;
} }
@@ -53,7 +53,7 @@ void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) overri
void updatePolish() override; void updatePolish() override;
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override; QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
virtual bool isInvertedRect() const { [[nodiscard]] virtual bool isInvertedRect() const {
return false; return false;
} }
@@ -72,10 +72,9 @@ void updateCenteredDeformMatrix();
BlobGroup* m_group = nullptr; BlobGroup* m_group = nullptr;
qreal m_radius = 0; qreal m_radius = 0;
QMatrix4x4 m_deformMatrix; // identity by default QMatrix4x4 m_deformMatrix;
QMatrix4x4 m_centeredDeformMatrix; QMatrix4x4 m_centeredDeformMatrix;
// Cached data from updatePolish
float m_cachedPaddedX = 0; float m_cachedPaddedX = 0;
float m_cachedPaddedY = 0; float m_cachedPaddedY = 0;
float m_cachedPaddedW = 0; float m_cachedPaddedW = 0;
+1 -1
View File
@@ -45,7 +45,7 @@ qml_module(ZShell
requests.hpp requests.cpp requests.hpp requests.cpp
toaster.hpp toaster.cpp toaster.hpp toaster.cpp
qalculator.hpp qalculator.cpp qalculator.hpp qalculator.cpp
lidwatcher.hpp lidwatcher.cpp zutils.hpp zutils.cpp
LIBRARIES LIBRARIES
Qt::Gui Qt::Gui
Qt::Quick Qt::Quick
+1
View File
@@ -2,6 +2,7 @@ qml_module(ZShell-components
URI ZShell.Components URI ZShell.Components
SOURCES SOURCES
lazylistview.hpp lazylistview.cpp lazylistview.hpp lazylistview.cpp
wavyline.hpp wavyline.cpp
LIBRARIES LIBRARIES
Qt::Quick Qt::Quick
) )
+255
View File
@@ -0,0 +1,255 @@
#include "wavyline.hpp"
#include <qpainter.h>
#include <qpainterpath.h>
namespace ZShell::controls {
WavyLine::WavyLine(QQuickItem* parent)
: QQuickPaintedItem(parent)
, m_lineWidth(4)
, m_amplitudeMultiplier(0.5)
, m_frequency(6)
, m_startX(0)
, m_fullLength(0)
, m_color(Qt::white)
, m_waveProgress(0)
, m_pathType(Linear)
, m_startAngle(0)
, m_fullAngle(360)
, m_radius(-1)
, m_value(1)
, m_startAngleRad(0)
, m_fullAngleRad(2 * M_PI) {
setAntialiasing(true);
}
int WavyLine::lineWidth() const {
return m_lineWidth;
}
void WavyLine::setLineWidth(int lineWidth) {
if (m_lineWidth != lineWidth) {
m_lineWidth = lineWidth;
emit lineWidthChanged();
update();
}
}
qreal WavyLine::amplitudeMultiplier() const {
return m_amplitudeMultiplier;
}
void WavyLine::setAmplitudeMultiplier(qreal amplitudeMultiplier) {
if (!qFuzzyCompare(m_amplitudeMultiplier + 1.0, amplitudeMultiplier + 1.0)) {
m_amplitudeMultiplier = amplitudeMultiplier;
emit amplitudeMultiplierChanged();
update();
}
}
int WavyLine::frequency() const {
return m_frequency;
}
void WavyLine::setFrequency(int frequency) {
if (m_frequency != frequency) {
m_frequency = frequency;
emit frequencyChanged();
update();
}
}
qreal WavyLine::startX() const {
return m_startX;
}
void WavyLine::setStartX(qreal startX) {
if (!qFuzzyCompare(m_startX + 1.0, startX + 1.0)) {
m_startX = startX;
emit startXChanged();
update();
}
}
qreal WavyLine::fullLength() const {
return m_fullLength;
}
void WavyLine::setFullLength(qreal fullLength) {
if (!qFuzzyCompare(m_fullLength + 1.0, fullLength + 1.0)) {
m_fullLength = fullLength;
emit fullLengthChanged();
update();
}
}
QColor WavyLine::color() const {
return m_color;
}
void WavyLine::setColor(const QColor& color) {
if (m_color != color) {
m_color = color;
emit colorChanged();
update();
}
}
qreal WavyLine::waveProgress() const {
return m_waveProgress;
}
void WavyLine::setWaveProgress(qreal progress) {
if (!qFuzzyCompare(m_waveProgress + 1.0, progress + 1.0)) {
m_waveProgress = progress;
emit waveProgressChanged();
update();
}
}
WavyLine::PathType WavyLine::pathType() const {
return m_pathType;
}
void WavyLine::setPathType(PathType pathType) {
if (m_pathType != pathType) {
m_pathType = pathType;
emit pathTypeChanged();
update();
}
}
qreal WavyLine::startAngle() const {
return m_startAngle;
}
void WavyLine::setStartAngle(qreal startAngle) {
if (!qFuzzyCompare(m_startAngle + 1.0, startAngle + 1.0)) {
m_startAngle = startAngle;
m_startAngleRad = startAngle * M_PI / 180.0;
emit startAngleChanged();
update();
}
}
qreal WavyLine::fullAngle() const {
return m_fullAngle;
}
void WavyLine::setFullAngle(qreal fullAngle) {
if (!qFuzzyCompare(m_fullAngle + 1.0, fullAngle + 1.0)) {
m_fullAngle = fullAngle;
m_fullAngleRad = fullAngle * M_PI / 180.0;
emit fullAngleChanged();
update();
}
}
qreal WavyLine::radius() const {
return m_radius;
}
void WavyLine::setRadius(qreal radius) {
if (!qFuzzyCompare(m_radius + 1.0, radius + 1.0)) {
m_radius = radius;
emit radiusChanged();
update();
}
}
qreal WavyLine::value() const {
return m_value;
}
void WavyLine::setValue(qreal value) {
if (!qFuzzyCompare(m_value + 1.0, value + 1.0)) {
m_value = value;
emit valueChanged();
update();
}
}
void WavyLine::paint(QPainter* painter) {
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QPen(m_color, m_lineWidth, Qt::SolidLine, Qt::RoundCap));
if (m_pathType == Arc) {
paintArc(painter);
} else {
paintLinear(painter);
}
}
void WavyLine::paintLinear(QPainter* painter) {
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto phase = m_waveProgress * 2 * M_PI;
const auto centerY = height() / 2;
const auto len = m_fullLength > 0 ? m_fullLength : 1;
const auto start = m_lineWidth / 2.0;
const auto fullEnd = width() - m_lineWidth / 2.0;
const auto drawEnd = start + (fullEnd - start) * m_value;
QPainterPath path;
bool first = true;
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
const auto theta = m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
const auto waveY = centerY + amplitude * qSin(theta);
if (first) {
path.moveTo(x, waveY);
first = false;
} else {
path.lineTo(x, waveY);
}
}
painter->drawPath(path);
}
void WavyLine::paintArc(QPainter* painter) {
if (m_fullAngleRad <= 0) {
return;
}
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto cx = width() / 2.0;
const auto cy = height() / 2.0;
const auto radius = m_radius > 0 ? m_radius : (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
if (radius <= 0) {
return;
}
const auto phase = m_waveProgress * 2 * M_PI;
const auto arcLen = radius * m_fullAngleRad;
const auto len = m_fullLength > 0 ? m_fullLength : arcLen;
const auto drawAngleRad = m_fullAngleRad * m_value;
if (drawAngleRad <= 0) {
return;
}
const auto N = qMax(64, qCeil(radius * drawAngleRad));
const auto dTheta = drawAngleRad / N;
QPainterPath path;
for (int i = 0; i <= N; ++i) {
const auto theta = m_startAngleRad + i * dTheta;
const auto s = i * dTheta * radius;
const auto phi = m_frequency * 2 * M_PI * (s + m_startX) / len + phase;
const auto r = radius + amplitude * qSin(phi);
const auto px = cx + r * qCos(theta);
const auto py = cy + r * qSin(theta);
if (i == 0) {
path.moveTo(px, py);
} else {
path.lineTo(px, py);
}
}
painter->drawPath(path);
}
} // namespace ZShell::controls
+107
View File
@@ -0,0 +1,107 @@
#pragma once
#include <qqmlintegration.h>
#include <qquickpainteditem.h>
namespace ZShell::controls {
class WavyLine : public QQuickPaintedItem {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged FINAL)
Q_PROPERTY(qreal amplitudeMultiplier READ amplitudeMultiplier WRITE setAmplitudeMultiplier NOTIFY
amplitudeMultiplierChanged FINAL)
Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged FINAL)
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged FINAL)
Q_PROPERTY(qreal fullLength READ fullLength WRITE setFullLength NOTIFY fullLengthChanged FINAL)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
Q_PROPERTY(qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY waveProgressChanged FINAL)
Q_PROPERTY(PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged FINAL)
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL)
Q_PROPERTY(qreal fullAngle READ fullAngle WRITE setFullAngle NOTIFY fullAngleChanged FINAL)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
public:
enum PathType {
Linear,
Arc
};
Q_ENUM(PathType)
explicit WavyLine(QQuickItem* parent = nullptr);
[[nodiscard]] int lineWidth() const;
void setLineWidth(int lineWidth);
[[nodiscard]] qreal amplitudeMultiplier() const;
void setAmplitudeMultiplier(qreal amplitudeMultiplier);
[[nodiscard]] int frequency() const;
void setFrequency(int frequency);
[[nodiscard]] qreal startX() const;
void setStartX(qreal startX);
[[nodiscard]] qreal fullLength() const;
void setFullLength(qreal fullLength);
[[nodiscard]] QColor color() const;
void setColor(const QColor& color);
[[nodiscard]] qreal waveProgress() const;
void setWaveProgress(qreal progress);
[[nodiscard]] PathType pathType() const;
void setPathType(PathType pathType);
[[nodiscard]] qreal startAngle() const;
void setStartAngle(qreal startAngle);
[[nodiscard]] qreal fullAngle() const;
void setFullAngle(qreal fullAngle);
[[nodiscard]] qreal radius() const;
void setRadius(qreal radius);
[[nodiscard]] qreal value() const;
void setValue(qreal value);
void paint(QPainter* painter) override;
signals:
void lineWidthChanged();
void amplitudeMultiplierChanged();
void frequencyChanged();
void startXChanged();
void fullLengthChanged();
void colorChanged();
void waveProgressChanged();
void pathTypeChanged();
void startAngleChanged();
void fullAngleChanged();
void radiusChanged();
void valueChanged();
private:
void paintLinear(QPainter* painter);
void paintArc(QPainter* painter);
int m_lineWidth;
qreal m_amplitudeMultiplier;
int m_frequency;
qreal m_startX;
qreal m_fullLength;
QColor m_color;
qreal m_waveProgress;
PathType m_pathType;
qreal m_startAngle;
qreal m_fullAngle;
qreal m_radius;
qreal m_value;
qreal m_startAngleRad;
qreal m_fullAngleRad;
};
} // namespace ZShell::controls
+1
View File
@@ -9,6 +9,7 @@ qml_module(ZShell-internal
sparklineitem.hpp sparklineitem.cpp sparklineitem.hpp sparklineitem.cpp
arcgauge.hpp arcgauge.cpp arcgauge.hpp arcgauge.cpp
wallpaperimage.hpp wallpaperimage.cpp wallpaperimage.hpp wallpaperimage.cpp
lidwatcher.hpp lidwatcher.cpp
LIBRARIES LIBRARIES
Qt::Gui Qt::Gui
Qt::Quick Qt::Quick
+1 -1
View File
@@ -58,4 +58,4 @@ qreal m_sweepAngle = 1.5 * M_PI;
qreal m_lineWidth = 10.0; qreal m_lineWidth = 10.0;
}; };
} // namespace ZShell::internal } // namespace ZShell::Internal
@@ -19,7 +19,8 @@ class CachingImageManager : public QObject {
public: public:
explicit CachingImageManager(QObject* parent = nullptr) explicit CachingImageManager(QObject* parent = nullptr)
: QObject(parent) : QObject(parent)
, m_item(nullptr) {} , m_item(nullptr) {
}
[[nodiscard]] QQuickItem* item() const; [[nodiscard]] QQuickItem* item() const;
void setItem(QQuickItem* item); void setItem(QQuickItem* item);
+2 -2
View File
@@ -2,7 +2,7 @@
#include <algorithm> #include <algorithm>
namespace caelestia::internal { namespace ZShell::internal {
CircularBuffer::CircularBuffer(QObject* parent) CircularBuffer::CircularBuffer(QObject* parent)
: QObject(parent) { : QObject(parent) {
@@ -92,4 +92,4 @@ qreal CircularBuffer::at(int index) const {
return m_data[actualIndex]; return m_data[actualIndex];
} }
} // namespace caelestia::internal } // namespace ZShell::internal
+2 -2
View File
@@ -4,7 +4,7 @@
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qvector.h> #include <qvector.h>
namespace caelestia::internal { namespace ZShell::internal {
class CircularBuffer : public QObject { class CircularBuffer : public QObject {
Q_OBJECT Q_OBJECT
@@ -41,4 +41,4 @@ int m_count = 0;
int m_capacity = 0; int m_capacity = 0;
}; };
} // namespace caelestia::internal } // namespace ZShell::internal
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <qeasingcurve.h> #include <qeasingcurve.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
+86
View File
@@ -0,0 +1,86 @@
#include "lidwatcher.hpp"
#include <QtDBus/qdbusconnection.h>
#include <QtDBus/qdbuserror.h>
#include <QtDBus/qdbusinterface.h>
#include <QtDBus/qdbusreply.h>
#include <qloggingcategory.h>
Q_LOGGING_CATEGORY(lcLidWatcher, "ZShell.internal.logindmanager", QtInfoMsg)
namespace ZShell::internal {
LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
auto bus = QDBusConnection::systemBus();
if (!bus.isConnected()) {
qCWarning(lcLidWatcher)
<< "Failed to connect to system bus:" << bus.lastError().message();
return;
}
bool ok = bus.connect("org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"PrepareForSleep",
this,
SLOT(handlePrepareForSleep(bool)));
if (!ok) {
qCWarning(lcLidWatcher)
<< "Failed to connect to PrepareForSleep signal:"
<< bus.lastError().message();
}
QDBusInterface login1("org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
bus);
const QDBusReply<QDBusObjectPath> reply = login1.call("GetSession", "auto");
if (!reply.isValid()) {
qCWarning(lcLidWatcher) << "Failed to get session path";
return;
}
const auto sessionPath = reply.value().path();
ok = bus.connect("org.freedesktop.login1",
sessionPath,
"org.freedesktop.login1.Session",
"Lock",
this,
SLOT(handleLockRequested()));
if (!ok) {
qCWarning(lcLidWatcher)
<< "Failed to connect to Lock signal:" << bus.lastError().message();
}
ok = bus.connect("org.freedesktop.login1",
sessionPath,
"org.freedesktop.login1.Session",
"Unlock",
this,
SLOT(handleUnlockRequested()));
if (!ok) {
qCWarning(lcLidWatcher) << "Failed to connect to Unlock signal:"
<< bus.lastError().message();
}
}
void LidWatcher::handlePrepareForSleep(bool sleep) {
if (sleep) {
emit aboutToSleep();
} else {
emit resumed();
}
}
void LidWatcher::handleLockRequested() {
emit lockRequested();
}
void LidWatcher::handleUnlockRequested() {
emit unlockRequested();
}
} // namespace ZShell::internal
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <qobject.h>
#include <qqmlintegration.h>
namespace ZShell::internal {
class LidWatcher : public QObject {
Q_OBJECT
QML_ELEMENT
public:
explicit LidWatcher(QObject* parent = nullptr);
signals:
void aboutToSleep();
void resumed();
void lockRequested();
void unlockRequested();
private slots:
void handlePrepareForSleep(bool sleep);
void handleLockRequested();
void handleUnlockRequested();
};
} // namespace ZShell::internal
+2 -2
View File
@@ -4,7 +4,7 @@
#include <qpainterpath.h> #include <qpainterpath.h>
#include <qpen.h> #include <qpen.h>
namespace caelestia::internal { namespace ZShell::internal {
SparklineItem::SparklineItem(QQuickItem* parent) SparklineItem::SparklineItem(QQuickItem* parent)
: QQuickPaintedItem(parent) { : QQuickPaintedItem(parent) {
@@ -212,4 +212,4 @@ void SparklineItem::setLineWidth(qreal width) {
update(); update();
} }
} // namespace caelestia::internal } // namespace ZShell::internal
+2 -2
View File
@@ -7,7 +7,7 @@
#include "circularbuffer.hpp" #include "circularbuffer.hpp"
namespace caelestia::internal { namespace ZShell::internal {
class SparklineItem : public QQuickPaintedItem { class SparklineItem : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
@@ -87,4 +87,4 @@ int m_historyLength = 30;
qreal m_lineWidth = 2.0; qreal m_lineWidth = 2.0;
}; };
} // namespace caelestia::internal } // namespace ZShell::internal
+4 -3
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <qabstractitemmodel.h> #include <qabstractitemmodel.h>
#include <qdir.h> #include <qdir.h>
#include <qfilesystemwatcher.h> #include <qfilesystemwatcher.h>
@@ -85,9 +86,9 @@ public:
explicit FileSystemModel(QObject* parent = nullptr); explicit FileSystemModel(QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override; [[nodiscard]] int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] QString path() const; [[nodiscard]] QString path() const;
void setPath(const QString& path); void setPath(const QString& path);
+1
View File
@@ -9,6 +9,7 @@ qml_module(ZShell-services
cavaprovider.hpp cavaprovider.cpp cavaprovider.hpp cavaprovider.cpp
desktopmodel.hpp desktopmodel.cpp desktopmodel.hpp desktopmodel.cpp
desktopstatemanager.hpp desktopstatemanager.cpp desktopstatemanager.hpp desktopstatemanager.cpp
hyprsunsetmanager.hpp hyprsunsetmanager.cpp
LIBRARIES LIBRARIES
Qt6::Core Qt6::Core
Qt6::Qml Qt6::Qml
+1 -1
View File
@@ -59,7 +59,7 @@ quint32 readChunk(double* out, quint32 count = 0);
private: private:
explicit AudioCollector(QObject* parent = nullptr); explicit AudioCollector(QObject* parent = nullptr);
~AudioCollector(); ~AudioCollector() override;
std::jthread m_thread; std::jthread m_thread;
std::vector<float> m_buffer1; std::vector<float> m_buffer1;
+2 -3
View File
@@ -11,11 +11,10 @@ Q_OBJECT
public: public:
explicit AudioProcessor(QObject* parent = nullptr); explicit AudioProcessor(QObject* parent = nullptr);
~AudioProcessor(); ~AudioProcessor() override;
void init(); void init();
public slots:
void start(); void start();
void stop(); void stop();
@@ -31,7 +30,7 @@ Q_OBJECT
public: public:
explicit AudioProvider(QObject* parent = nullptr); explicit AudioProvider(QObject* parent = nullptr);
~AudioProvider(); ~AudioProvider() override;
protected: protected:
AudioProcessor* m_processor; AudioProcessor* m_processor;
+1 -1
View File
@@ -11,7 +11,7 @@ Q_OBJECT
public: public:
explicit BeatProcessor(QObject* parent = nullptr); explicit BeatProcessor(QObject* parent = nullptr);
~BeatProcessor(); ~BeatProcessor() override;
signals: signals:
void beat(smpl_t bpm); void beat(smpl_t bpm);
+1 -1
View File
@@ -11,7 +11,7 @@ Q_OBJECT
public: public:
explicit CavaProcessor(QObject* parent = nullptr); explicit CavaProcessor(QObject* parent = nullptr);
~CavaProcessor(); ~CavaProcessor() override;
void setBars(int bars); void setBars(int bars);
+5 -4
View File
@@ -4,6 +4,7 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QQmlEngine> #include <QQmlEngine>
#include <cstdint>
namespace ZShell::services { namespace ZShell::services {
@@ -30,9 +31,9 @@ enum DesktopRoles {
explicit DesktopModel(QObject *parent = nullptr); explicit DesktopModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void loadDirectory(const QString &path); Q_INVOKABLE void loadDirectory(const QString &path);
Q_INVOKABLE void moveIcon(int index, int newX, int newY); Q_INVOKABLE void moveIcon(int index, int newX, int newY);
@@ -43,4 +44,4 @@ QList<DesktopItem> m_items;
void saveCurrentLayout(); void saveCurrentLayout();
}; };
} // namespace ZShell::services } // namespace ZShell::Services
@@ -18,7 +18,7 @@ Q_INVOKABLE void saveLayout(const QVariantMap& layout);
Q_INVOKABLE QVariantMap getLayout(); Q_INVOKABLE QVariantMap getLayout();
private: private:
QString getConfigFilePath() const; [[nodiscard]] QString getConfigFilePath() const;
}; };
} // namespace ZShell::services } // namespace ZShell::services
@@ -0,0 +1,147 @@
#include "hyprsunsetmanager.hpp"
#include <qlogging.h>
#include <qobject.h>
#include <qprocess.h>
#include <qtimer.h>
namespace ZShell::services {
HyprsunsetManager::HyprsunsetManager(QObject* parent) : QObject(parent) {
connect(&m_timer, &QTimer::timeout, this, &HyprsunsetManager::apply);
connect(&m_manualTimer, &QTimer::timeout, this, [this] {
m_manualToggle = false;
emit manualToggleChanged();
apply();
});
connect(&m_startCooldown, &QTimer::timeout, this, [this] {
m_startAllowed = true;
apply();
});
m_startCooldown.start(2000);
m_manualTimer.setSingleShot(true);
m_timer.start(60000);
m_process.setStandardInputFile(QProcess::nullDevice());
m_process.setStandardOutputFile(QProcess::nullDevice());
}
int HyprsunsetManager::startTime() const {
return m_startTime;
}
int HyprsunsetManager::endTime() const {
return m_endTime;
}
bool HyprsunsetManager::manualToggle() const {
return m_manualToggle;
}
bool HyprsunsetManager::enabled() const {
return m_enabled;
}
bool HyprsunsetManager::activeAuto() const {
return m_activeAuto;
}
int HyprsunsetManager::temp() const {
return m_temp;
}
void HyprsunsetManager::setActiveAuto(bool activeAuto) {
if (activeAuto == m_activeAuto)
return;
m_activeAuto = activeAuto;
emit activeAutoChanged();
}
void HyprsunsetManager::setManualToggle(bool toggle) {
if (toggle == m_manualToggle)
return;
m_manualToggle = toggle;
emit manualToggleChanged();
m_manualTimer.start(60 * 60 * 1000);
}
void HyprsunsetManager::setEndTime(const int& time) {
if (time == m_endTime)
return;
m_endTime = time;
emit endTimeChanged();
apply();
}
void HyprsunsetManager::setStartTime(const int& time) {
if (time == m_startTime)
return;
m_startTime = time;
emit startTimeChanged();
apply();
}
void HyprsunsetManager::setTemp(const int& temp) {
if (temp == m_temp)
return;
m_temp = temp;
emit tempChanged();
apply();
}
void HyprsunsetManager::toggle() {
if (m_enabled) {
end();
} else {
start();
}
}
void HyprsunsetManager::start() {
if (m_enabled && m_initialized)
return;
m_initialized = true;
m_enabled = true;
emit enabledChanged();
m_process.setProgram("hyprctl");
m_process.setArguments({"hyprsunset", "temperature", QString::number(m_temp)});
m_process.startDetached();
}
void HyprsunsetManager::end() {
if (!m_enabled && m_initialized)
return;
m_initialized = true;
m_enabled = false;
emit enabledChanged();
m_process.setProgram("hyprctl");
m_process.setArguments({"hyprsunset", "identity"});
m_process.startDetached();
}
void HyprsunsetManager::apply() {
if (m_manualToggle || !m_activeAuto || !m_startAllowed)
return;
const auto current = QTime::currentTime().hour();
if (current >= m_startTime || current < m_endTime) {
start();
} else {
end();
}
}
};
@@ -0,0 +1,66 @@
#pragma once
#include <QObject>
#include <QTime>
#include <QProcess>
#include <QTimer>
#include <qqmlintegration.h>
#include <qtmetamacros.h>
namespace ZShell::services {
class HyprsunsetManager : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged)
Q_PROPERTY(bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY activeAutoChanged)
Q_PROPERTY(bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY manualToggleChanged)
public:
explicit HyprsunsetManager(QObject* parent = nullptr);
[[nodiscard]] int startTime() const;
[[nodiscard]] int endTime() const;
[[nodiscard]] bool enabled() const;
[[nodiscard]] int temp() const;
[[nodiscard]] bool activeAuto() const;
[[nodiscard]] bool manualToggle() const;
Q_INVOKABLE void toggle();
Q_INVOKABLE void apply();
void setStartTime(const int& time);
void setEndTime(const int& time);
void setTemp(const int& temp);
void setActiveAuto(bool activeAuto);
void setManualToggle(bool toggle);
signals:
void enabledChanged();
void startTimeChanged();
void activeAutoChanged();
void endTimeChanged();
void tempChanged();
void manualToggleChanged();
private:
int m_startTime;
int m_endTime;
bool m_enabled = false;
bool m_manualToggle = false;
bool m_activeAuto;
bool m_startAllowed = false;
bool m_initialized = false;
QTimer m_startCooldown;
int m_temp;
QProcess m_process;
QTimer m_timer;
QTimer m_manualTimer;
void start();
void end();
};
};
-121
View File
@@ -1,121 +0,0 @@
#include "lidwatcher.hpp"
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusReply>
#include <QtDBus/QDBusServiceWatcher>
#include <QLoggingCategory>
#include <QTimer>
Q_LOGGING_CATEGORY(lcLidWatcher, "ZShell.lidwatcher", QtInfoMsg)
namespace ZShell {
static constexpr auto kLogin1Service = "org.freedesktop.login1";
static constexpr auto kLogin1Path = "/org/freedesktop/login1";
static constexpr auto kLogin1Interface = "org.freedesktop.login1.Manager";
static constexpr auto kDBusPropertiesInterface =
"org.freedesktop.DBus.Properties";
LidWatcher::LidWatcher(QObject* parent)
: QObject(parent)
, m_watcher(nullptr)
, m_pollTimer(nullptr)
, m_available(false)
{
QDBusConnection bus = QDBusConnection::systemBus();
if (!bus.isConnected()) {
qCWarning(lcLidWatcher) << "system bus unavailable";
emit availableChanged();
return;
}
m_pollTimer = new QTimer(this);
m_pollTimer->setInterval(5000);
connect(m_pollTimer, &QTimer::timeout, this, &LidWatcher::queryLidState);
m_watcher = new QDBusServiceWatcher(
kLogin1Service,
bus,
QDBusServiceWatcher::WatchForRegistration,
this);
connect(m_watcher, &QDBusServiceWatcher::serviceRegistered,
this, &LidWatcher::tryConnect);
tryConnect();
}
bool LidWatcher::available() const {
return m_available;
}
LidWatcher::LidState LidWatcher::state() const {
return m_state;
}
void LidWatcher::tryConnect() {
QDBusConnection bus = QDBusConnection::systemBus();
const auto connected = bus.connect(
QString(),
kLogin1Path,
kDBusPropertiesInterface,
"PropertiesChanged",
this,
SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
queryLidState();
if (connected) {
m_pollTimer->stop();
qCInfo(lcLidWatcher) << "login1 PropertiesChanged connected";
} else {
qCWarning(lcLidWatcher)
<< "login1 PropertiesChanged unavailable, polling";
if (!m_pollTimer->isActive()) {
m_pollTimer->start();
}
}
if (!m_available) {
m_available = true;
emit availableChanged();
}
}
void LidWatcher::queryLidState() {
auto msg = QDBusMessage::createMethodCall(kLogin1Service,
kLogin1Path,
kDBusPropertiesInterface,
"Get");
msg << kLogin1Interface << "LidClosed";
const QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
if (!reply.isValid()) {
qCWarning(lcLidWatcher)
<< "cannot query LidClosed:" << reply.error().message();
return;
}
const auto newState = reply.value().toBool() ? Closed : Opened;
if (m_state != newState) {
m_state = newState;
emit stateChanged();
}
}
void LidWatcher::onPropertiesChanged(const QString& interface,
const QVariantMap& changed,
const QStringList& ) {
if (interface != kLogin1Interface) {
return;
}
if (!changed.contains("LidClosed")) {
return;
}
const auto newState = changed.value("LidClosed").toBool() ? Closed : Opened;
if (m_state != newState) {
m_state = newState;
emit stateChanged();
}
}
} // namespace ZShell
-50
View File
@@ -1,50 +0,0 @@
#pragma once
#include <QObject>
#include <QStringList>
#include <QVariantMap>
#include <cstdint>
#include <qqmlintegration.h>
class QDBusServiceWatcher;
class QTimer;
namespace ZShell {
class LidWatcher : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
Q_PROPERTY(LidState state READ state NOTIFY stateChanged)
public:
enum LidState : std::uint8_t{ Opened, Closed };
Q_ENUM(LidState)
explicit LidWatcher(QObject* parent = nullptr);
[[nodiscard]] bool available() const;
[[nodiscard]] LidState state() const;
private:
void tryConnect();
void queryLidState();
QDBusServiceWatcher* m_watcher;
QTimer* m_pollTimer;
bool m_available;
LidState m_state = Opened;
private Q_SLOTS:
void onPropertiesChanged(const QString& interface,
const QVariantMap& changed,
const QStringList& invalidated);
Q_SIGNALS:
void availableChanged();
void stateChanged();
};
} // namespace ZShell
+1 -1
View File
@@ -13,7 +13,7 @@ QML_SINGLETON
public: public:
explicit Qalculator(QObject* parent = nullptr); explicit Qalculator(QObject* parent = nullptr);
Q_INVOKABLE QString eval(const QString& expr, bool printExpr = true) const; Q_INVOKABLE [[nodiscard]] QString eval(const QString& expr, bool printExpr = true) const;
}; };
} // namespace ZShell } // namespace ZShell
+3 -3
View File
@@ -29,9 +29,9 @@ Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValu
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed); Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
// clang-format on // clang-format on
Q_INVOKABLE bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const; Q_INVOKABLE [[nodiscard]] bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const;
Q_INVOKABLE bool deleteFile(const QUrl& path) const; Q_INVOKABLE [[nodiscard]] bool deleteFile(const QUrl& path) const;
Q_INVOKABLE QString toLocalFile(const QUrl& url) const; Q_INVOKABLE [[nodiscard]] QString toLocalFile(const QUrl& url) const;
private: private:
bool loadSourceImage(const QUrl& source, QImage& image) const; bool loadSourceImage(const QUrl& source, QImage& image) const;
+145
View File
@@ -0,0 +1,145 @@
#include "zutils.hpp"
#include <QtConcurrent/qtconcurrentrun.h>
#include <QtQuick/qquickitemgrabresult.h>
#include <QtQuick/qquickwindow.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qfuturewatcher.h>
#include <qloggingcategory.h>
#include <qqmlengine.h>
Q_LOGGING_CATEGORY(lcZUtils, "ZShell.cutils", QtInfoMsg)
namespace ZShell {
void ZUtils::saveItem(QQuickItem* target, const QUrl& path) {
this->saveItem(target, path, QRect(), QJSValue(), QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect) {
this->saveItem(target, path, rect, QJSValue(), QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved) {
this->saveItem(target, path, QRect(), onSaved, QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
this->saveItem(target, path, QRect(), onSaved, onFailed);
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
this->saveItem(target, path, rect, onSaved, QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed) {
if (!target) {
qCWarning(lcZUtils) << "saveItem: a target is required";
return;
}
if (!path.isLocalFile()) {
qCWarning(lcZUtils) << "saveItem:" << path << "is not a local file";
return;
}
if (!target->window()) {
qCWarning(lcZUtils) << "saveItem: unable to save target" << target << "without a window";
return;
}
auto scaledRect = rect;
const qreal scale = target->window()->devicePixelRatio();
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
scaledRect =
QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale).toRect();
}
const QSharedPointer<const QQuickItemGrabResult> grabResult = target->grabToImage();
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this,
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
const auto future = QtConcurrent::run([=]() {
QImage image = grabResult->image();
if (scaledRect.isValid()) {
image = image.copy(scaledRect);
}
const QString file = path.toLocalFile();
const QString parent = QFileInfo(file).absolutePath();
return QDir().mkpath(parent) && image.save(file);
});
auto* watcher = new QFutureWatcher<bool>(this);
auto* engine = qmlEngine(this);
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) {
if (onSaved.isCallable()) {
QJSValueList args = { QJSValue(path.toLocalFile()) };
if (engine) {
args << engine->toScriptValue(QVariant::fromValue(path));
}
onSaved.call(args);
}
} else {
qCWarning(lcZUtils) << "saveItem: failed to save" << path;
if (onFailed.isCallable()) {
if (engine) {
onFailed.call({ engine->toScriptValue(QVariant::fromValue(path)) });
} else {
onFailed.call();
}
}
}
watcher->deleteLater();
});
watcher->setFuture(future);
});
}
bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
if (!source.isLocalFile()) {
qCWarning(lcZUtils) << "copyFile: source" << source << "is not a local file";
return false;
}
if (!target.isLocalFile()) {
qCWarning(lcZUtils) << "copyFile: target" << target << "is not a local file";
return false;
}
if (overwrite && QFile::exists(target.toLocalFile())) {
if (!QFile::remove(target.toLocalFile())) {
qCWarning(lcZUtils) << "copyFile: overwrite was specified but failed to remove" << target.toLocalFile();
return false;
}
}
return QFile::copy(source.toLocalFile(), target.toLocalFile());
}
bool ZUtils::deleteFile(const QUrl& path) {
if (!path.isLocalFile()) {
qCWarning(lcZUtils) << "deleteFile: path" << path << "is not a local file";
return false;
}
return QFile::remove(path.toLocalFile());
}
QString ZUtils::toLocalFile(const QUrl& url) {
if (!url.isLocalFile()) {
qCWarning(lcZUtils) << "toLocalFile: given url is not a local file" << url;
return QString();
}
return url.toLocalFile();
}
qreal ZUtils::clamp(qreal value, qreal min, qreal max) {
return qBound(min, value, max);
}
} // namespace ZShell
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <QtQuick/qquickitem.h>
#include <qobject.h>
#include <qqmlintegration.h>
namespace ZShell {
class ZUtils : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
// clang-format on
Q_INVOKABLE static bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true);
Q_INVOKABLE static bool deleteFile(const QUrl& path);
Q_INVOKABLE static QString toLocalFile(const QUrl& url);
Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);
};
} // namespace ZShell
-7
View File
@@ -326,13 +326,6 @@ export const settingsIndex = [
section: "Lockscreen", section: "Lockscreen",
keywords: ["notification", "hide", "icon"], keywords: ["notification", "hide", "icon"],
}, },
{
name: "Laptop lid watch to lock session",
category: "lockscreen",
categoryName: "Lockscreen",
section: "Lockscreen",
keywords: ["lid", "lock", "watch", "session", "laptop"],
},
{ {
name: "Blur amount", name: "Blur amount",
category: "lockscreen", category: "lockscreen",
+2 -15
View File
@@ -4,16 +4,15 @@
//@ pragma Env QSG_NO_VSYNC=1 //@ pragma Env QSG_NO_VSYNC=1
//@ pragma Env QS_NO_RELOAD_POPUP=1 //@ pragma Env QS_NO_RELOAD_POPUP=1
//@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round //@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round
//@ pragma DefaultEnv QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000
//@ pragma DropExpensiveFonts //@ pragma DropExpensiveFonts
import Quickshell import Quickshell
import Quickshell.Services.UPower import Quickshell.Services.UPower
import QtQuick
import qs.Modules import qs.Modules
import qs.Modules.Wallpaper import qs.Modules.Wallpaper
import qs.Modules.Lock import qs.Modules.Lock
import qs.Drawers import qs.Drawers
import qs.Helpers import qs.Helpers
import qs.Config
import qs.Modules.Polkit import qs.Modules.Polkit
import qs.Daemons import qs.Daemons
@@ -24,7 +23,7 @@ ShellRoot {
settings.watchFiles: true settings.watchFiles: true
Windows { Drawers {
} }
Wallpaper { Wallpaper {
@@ -35,8 +34,6 @@ ShellRoot {
Lock { Lock {
id: lock id: lock
lid: lid
} }
Shortcuts { Shortcuts {
@@ -49,16 +46,6 @@ ShellRoot {
Polkit { Polkit {
} }
LazyLoader {
id: lid
activeAsync: Config.lock.lidWatch && Battery.isLaptop
component: LidService {
onRequestLock: lock.lock.requestLock()
}
}
LazyLoader { LazyLoader {
activeAsync: root.laptop activeAsync: root.laptop