summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/OsdConfig.qml14
-rw-r--r--modules/launcher/Background.qml2
-rw-r--r--modules/launcher/Content.qml1
-rw-r--r--modules/launcher/Launcher.qml2
-rw-r--r--modules/launcher/Wrapper.qml2
-rw-r--r--modules/osd/Background.qml61
-rw-r--r--modules/osd/Content.qml42
-rw-r--r--modules/osd/Osd.qml89
-rw-r--r--modules/osd/Wrapper.qml62
-rw-r--r--services/Apps.qml2
-rw-r--r--services/Audio.qml25
-rw-r--r--shell.qml2
-rw-r--r--widgets/VerticalSlider.qml130
13 files changed, 427 insertions, 7 deletions
diff --git a/config/OsdConfig.qml b/config/OsdConfig.qml
new file mode 100644
index 0000000..1bfdbd6
--- /dev/null
+++ b/config/OsdConfig.qml
@@ -0,0 +1,14 @@
+pragma Singleton
+
+import Quickshell
+import QtQuick
+
+Singleton {
+ readonly property int hideDelay: 1500
+ readonly property Sizes sizes: Sizes {}
+
+ component Sizes: QtObject {
+ readonly property int sliderWidth: 30
+ readonly property int sliderHeight: 150
+ }
+}
diff --git a/modules/launcher/Background.qml b/modules/launcher/Background.qml
index 8cfaac8..0e9f7dd 100644
--- a/modules/launcher/Background.qml
+++ b/modules/launcher/Background.qml
@@ -11,8 +11,6 @@ Shape {
readonly property int roundingY: Math.min(rounding, realWrapperHeight / 2)
readonly property real wrapperHeight: realWrapperHeight - 1 // Pixel issues :sob:
- anchors.bottom: parent.bottom
-
preferredRendererType: Shape.CurveRenderer
opacity: Appearance.transparency.enabled ? Appearance.transparency.base : 1
diff --git a/modules/launcher/Content.qml b/modules/launcher/Content.qml
index 9759abb..315835e 100644
--- a/modules/launcher/Content.qml
+++ b/modules/launcher/Content.qml
@@ -33,7 +33,6 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: search.top
anchors.bottomMargin: root.spacing
- anchors.margins: root.padding
ContentList {
id: list
diff --git a/modules/launcher/Launcher.qml b/modules/launcher/Launcher.qml
index bbd2f91..34e592a 100644
--- a/modules/launcher/Launcher.qml
+++ b/modules/launcher/Launcher.qml
@@ -34,6 +34,7 @@ Scope {
id: bg
anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
wrapperWidth: wrapper.width
realWrapperHeight: Math.min(wrapper.height, content.height)
@@ -43,6 +44,7 @@ Scope {
id: wrapper
anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
implicitWidth: content.width + bg.rounding * 2
diff --git a/modules/launcher/Wrapper.qml b/modules/launcher/Wrapper.qml
index 132a130..d221a0b 100644
--- a/modules/launcher/Wrapper.qml
+++ b/modules/launcher/Wrapper.qml
@@ -8,8 +8,6 @@ Item {
required property real contentHeight
property bool shouldBeVisible
- anchors.bottom: parent.bottom
-
height: 0
clip: true
diff --git a/modules/osd/Background.qml b/modules/osd/Background.qml
new file mode 100644
index 0000000..e0383f5
--- /dev/null
+++ b/modules/osd/Background.qml
@@ -0,0 +1,61 @@
+import "root:/config"
+import QtQuick
+import QtQuick.Shapes
+
+Shape {
+ id: root
+
+ required property real realWrapperWidth
+ required property real wrapperHeight
+ readonly property int rounding: Appearance.rounding.large
+ readonly property int roundingX: Math.min(rounding, realWrapperWidth / 2)
+ readonly property real wrapperWidth: realWrapperWidth - 1 // Pixel issues :sob:
+
+ preferredRendererType: Shape.CurveRenderer
+ opacity: Appearance.transparency.enabled ? Appearance.transparency.base : 1
+
+ ShapePath {
+ strokeWidth: -1
+ fillColor: Appearance.colours.m3surfaceContainer
+
+ startX: root.wrapperWidth
+
+ PathArc {
+ relativeX: -root.roundingX
+ relativeY: root.rounding
+ radiusX: root.roundingX
+ radiusY: root.rounding
+ }
+ PathLine {
+ x: root.roundingX
+ relativeY: 0
+ }
+ PathArc {
+ relativeX: -root.roundingX
+ relativeY: root.rounding
+ radiusX: root.roundingX
+ radiusY: root.rounding
+ direction: PathArc.Counterclockwise
+ }
+ PathLine {
+ y: root.wrapperHeight - root.rounding * 2
+ }
+ PathArc {
+ relativeX: root.roundingX
+ relativeY: root.rounding
+ radiusX: root.roundingX
+ radiusY: root.rounding
+ direction: PathArc.Counterclockwise
+ }
+ PathLine {
+ x: root.wrapperWidth - root.roundingX
+ relativeY: 0
+ }
+ PathArc {
+ relativeX: root.roundingX
+ relativeY: root.rounding
+ radiusX: root.roundingX
+ radiusY: root.rounding
+ }
+ }
+}
diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml
new file mode 100644
index 0000000..daab4e2
--- /dev/null
+++ b/modules/osd/Content.qml
@@ -0,0 +1,42 @@
+import "root:/widgets"
+import "root:/services"
+import "root:/config"
+import Quickshell
+import QtQuick
+
+Column {
+ id: root
+
+ required property ShellScreen screen
+
+ padding: Appearance.padding.large
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+
+ spacing: Appearance.spacing.normal
+
+ VerticalSlider {
+ icon: {
+ if (Audio.muted)
+ return "no_sound";
+ if (value >= 0.5)
+ return "volume_up";
+ if (value > 0)
+ return "volume_down";
+ return "volume_mute";
+ }
+ value: Audio.volume
+ onMoved: Audio.setVolume(value)
+
+ implicitWidth: OsdConfig.sizes.sliderWidth
+ implicitHeight: OsdConfig.sizes.sliderHeight
+ }
+
+ VerticalSlider {
+ icon: "brightness_6"
+
+ implicitWidth: OsdConfig.sizes.sliderWidth
+ implicitHeight: OsdConfig.sizes.sliderHeight
+ }
+}
diff --git a/modules/osd/Osd.qml b/modules/osd/Osd.qml
new file mode 100644
index 0000000..4d744f6
--- /dev/null
+++ b/modules/osd/Osd.qml
@@ -0,0 +1,89 @@
+import "root:/widgets"
+import "root:/services"
+import "root:/config"
+import Quickshell
+import QtQuick
+
+Scope {
+ id: root
+
+ property bool osdVisible
+
+ function show(): void {
+ root.osdVisible = true;
+ timer.restart();
+ }
+
+ Connections {
+ target: Audio
+
+ function onMutedChanged(): void {
+ root.show();
+ }
+
+ function onVolumeChanged(): void {
+ root.show();
+ }
+ }
+
+ Timer {
+ id: timer
+
+ interval: OsdConfig.hideDelay
+ onTriggered: root.osdVisible = false
+ }
+
+ Variants {
+ model: Quickshell.screens
+
+ LazyLoader {
+ loading: true
+
+ required property ShellScreen modelData
+
+ StyledWindow {
+ id: win
+
+ screen: parent.modelData
+ name: "osd"
+ visible: wrapper.shouldBeVisible
+
+ mask: Region {
+ item: wrapper
+ }
+
+ anchors.left: true
+ anchors.right: true
+ height: wrapper.height
+
+ Background {
+ id: bg
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+
+ wrapperHeight: wrapper.height
+ realWrapperWidth: Math.min(wrapper.width, content.width)
+ }
+
+ Wrapper {
+ id: wrapper
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+
+ implicitHeight: content.height + bg.rounding * 2
+
+ osdVisible: root.osdVisible
+ contentWidth: content.width
+
+ Content {
+ id: content
+
+ screen: parent.modelData
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/osd/Wrapper.qml b/modules/osd/Wrapper.qml
new file mode 100644
index 0000000..92bd3c2
--- /dev/null
+++ b/modules/osd/Wrapper.qml
@@ -0,0 +1,62 @@
+import "root:/config"
+import QtQuick
+
+Item {
+ id: root
+
+ required property bool osdVisible
+ required property real contentWidth
+ property bool shouldBeVisible
+
+ width: 0
+ clip: true
+
+ states: State {
+ name: "visible"
+ when: root.osdVisible
+
+ PropertyChanges {
+ root.width: contentWidth
+ root.shouldBeVisible: true
+ }
+ }
+
+ transitions: [
+ Transition {
+ from: ""
+ to: "visible"
+
+ SequentialAnimation {
+ PropertyAction {
+ target: root
+ property: "shouldBeVisible"
+ }
+ NumberAnimation {
+ target: root
+ property: "width"
+ duration: Appearance.anim.durations.large
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
+ }
+ }
+ },
+ Transition {
+ from: "visible"
+ to: ""
+
+ SequentialAnimation {
+ NumberAnimation {
+ target: root
+ property: "width"
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.emphasizedAccel
+ }
+ PropertyAction {
+ target: root
+ property: "shouldBeVisible"
+ }
+ }
+ }
+ ]
+}
diff --git a/services/Apps.qml b/services/Apps.qml
index 453030d..f964be2 100644
--- a/services/Apps.qml
+++ b/services/Apps.qml
@@ -1,8 +1,6 @@
pragma Singleton
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
-import "root:/widgets"
-import "root:/config"
import Quickshell
import Quickshell.Io
diff --git a/services/Audio.qml b/services/Audio.qml
new file mode 100644
index 0000000..c91adbc
--- /dev/null
+++ b/services/Audio.qml
@@ -0,0 +1,25 @@
+pragma Singleton
+
+import Quickshell
+import Quickshell.Services.Pipewire
+
+Singleton {
+ id: root
+
+ readonly property PwNode sink: Pipewire.defaultAudioSink
+ readonly property PwNode source: Pipewire.defaultAudioSource
+
+ readonly property bool muted: sink?.audio?.muted ?? false
+ readonly property real volume: sink?.audio?.volume ?? 0
+
+ function setVolume(volume: real): void {
+ if (sink?.ready && sink?.audio) {
+ sink.audio.muted = false;
+ sink.audio.volume = volume;
+ }
+ }
+
+ PwObjectTracker {
+ objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource]
+ }
+}
diff --git a/shell.qml b/shell.qml
index 93bc3a1..38054f5 100644
--- a/shell.qml
+++ b/shell.qml
@@ -1,8 +1,10 @@
import "modules/bar"
import "modules/launcher"
+import "modules/osd"
import Quickshell
ShellRoot {
Bar {}
Launcher {}
+ Osd {}
}
diff --git a/widgets/VerticalSlider.qml b/widgets/VerticalSlider.qml
new file mode 100644
index 0000000..1dc9ac4
--- /dev/null
+++ b/widgets/VerticalSlider.qml
@@ -0,0 +1,130 @@
+import "root:/widgets"
+import "root:/config"
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Effects
+
+Slider {
+ id: root
+
+ required property string icon
+
+ orientation: Qt.Vertical
+
+ background: StyledRect {
+ color: Appearance.alpha(Appearance.colours.m3surfaceContainerHigh, true)
+ radius: Appearance.rounding.full
+
+ StyledRect {
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ y: root.handle.y
+ implicitHeight: parent.height - y
+
+ color: Appearance.alpha(Appearance.colours.m3secondary, true)
+ radius: Appearance.rounding.full
+ }
+ }
+
+ handle: Item {
+ id: handle
+
+ property bool moving
+
+ y: root.visualPosition * (root.availableHeight - height)
+ implicitWidth: root.width
+ implicitHeight: root.width
+
+ RectangularShadow {
+ anchors.fill: parent
+ radius: rect.radius
+ color: Appearance.colours.m3shadow
+ blur: 5
+ spread: 0
+ }
+
+ StyledRect {
+ id: rect
+
+ anchors.fill: parent
+
+ color: Appearance.alpha(Appearance.colours.m3inverseSurface, true)
+ radius: Appearance.rounding.full
+
+ MaterialIcon {
+ id: icon
+
+ animate: true
+ text: root.icon
+ color: Appearance.colours.m3onPrimary
+ anchors.centerIn: parent
+
+ states: State {
+ name: "value"
+ when: handle.moving
+
+ PropertyChanges {
+ icon.animate: false
+ icon.text: Math.round(root.value * 100)
+ icon.font.pointSize: Appearance.font.size.small
+ icon.font.family: Appearance.font.family.sans
+ }
+ }
+
+ transitions: Transition {
+ from: "*"
+ to: "*"
+
+ NumberAnimation {
+ target: icon
+ property: "scale"
+ from: 1
+ to: 0
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standardAccel
+ }
+ PropertyAction {
+ target: icon
+ properties: "animate,text,font.pointSize,font.family"
+ }
+ NumberAnimation {
+ target: icon
+ property: "scale"
+ from: 0
+ to: 1
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standardDecel
+ }
+ }
+ }
+ }
+ }
+
+ onPressedChanged: handle.moving = pressed
+
+ onValueChanged: {
+ handle.moving = true;
+ stateChangeDelay.restart();
+ }
+
+ Timer {
+ id: stateChangeDelay
+
+ interval: 500
+ onTriggered: {
+ if (!root.pressed)
+ handle.moving = false;
+ }
+ }
+
+ Behavior on value {
+ NumberAnimation {
+ duration: Appearance.anim.durations.large
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ }
+}