Compare commits

..

6 commits

Author SHA1 Message Date
Benjamin Palko
11d0e4a838 try that out 2025-09-04 19:59:52 -04:00
Benjamin Palko
3074bcae38 remove menu stuff 2025-09-04 18:57:12 -04:00
Benjamin Palko
75f780753f bar margin 2025-09-04 17:26:10 -04:00
Benjamin Palko
37dfd1255a use window popup type to allow menu outside of parent window 2025-09-04 17:26:10 -04:00
Benjamin Palko
a40f7666dc rename pomodoro service 2025-09-04 17:25:04 -04:00
Benjamin Palko
4011f4b5c8 move bar to unified window setup 2025-09-04 17:25:04 -04:00
84 changed files with 999 additions and 1783 deletions

View file

@ -12,11 +12,13 @@ Run the shell
quickshell -c shell quickshell -c shell
``` ```
Run app launcher
```shell
quickshell -c launcher
```
## Dependencies ## Dependencies
- `quickshell` - `quickshell`
- `qt6-wayland`
- `app2unit` - `app2unit`
- `meson`
- `ninja`
- `python3`

View file

@ -2,12 +2,13 @@ import qs.config
import QtQuick import QtQuick
Text { Text {
font.family: Styling.lucide.font.family color: Theme.palette.basecontent
font.pixelSize: 16
color: Styling.theme.basecontent
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Styling.animations.speed.fast duration: 100
} }
} }
font.family: Theme.lucide.font.family
font.pixelSize: Dimensions.gpu.iconSize
font.bold: true
} }

View file

@ -2,6 +2,8 @@ pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Services.Mpris import Quickshell.Services.Mpris
@ -14,43 +16,35 @@ Loader {
active: player != null active: player != null
sourceComponent: ColumnLayout { sourceComponent: ColumnLayout {
spacing: Styling.layout.spacing.xl spacing: 12
implicitWidth: 800 implicitWidth: 800
StyledText { StyledText {
id: text id: text
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
font.pixelSize: Styling.typography.textSize.base
text: `${root.player.trackTitle} - ${root.player.trackArtist}` text: `${root.player.trackTitle} - ${root.player.trackArtist}`
font.pixelSize: Dimensions.mpris.fontSize
} }
RowLayout { RowLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
StyledIconButton { StyledIconButton {
id: backButton id: backButton
text: Icons.skipBack
text: Styling.lucide.icons.skipBack
onClicked: { onClicked: {
root.player.previous(); root.player.previous();
} }
} }
StyledIconButton { StyledIconButton {
id: playPauseButton id: playPauseButton
text: root.player.isPlaying ? Icons.pause : Icons.play
text: root.player.isPlaying ? Styling.lucide.icons.pause : Styling.lucide.icons.play
onClicked: { onClicked: {
root.player.isPlaying = !root.player.isPlaying; root.player.isPlaying = !root.player.isPlaying;
} }
} }
StyledIconButton { StyledIconButton {
id: forwardButton id: forwardButton
text: Icons.skipForward
text: Styling.lucide.icons.skipForward
onClicked: { onClicked: {
root.player.next(); root.player.next();
} }
@ -59,24 +53,21 @@ Loader {
StyledText { StyledText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
font.pixelSize: Styling.typography.textSize.sm
text: { text: {
function formatTime(num) { function formatTime(num) {
return Math.floor(num).toString().padStart(2, "0"); return Math.floor(num).toString().padStart(2, "0");
} }
return `${formatTime(root.player.position / 60)}:${formatTime(root.player.position % 60)} - ${formatTime(root.player.length / 60)}:${formatTime(root.player.length % 60)}`; return `${formatTime(root.player.position / 60)}:${formatTime(root.player.position % 60)} - ${formatTime(root.player.length / 60)}:${formatTime(root.player.length % 60)}`;
} }
font.pixelSize: Dimensions.mpris.fontSize
} }
StyledSlider { StyledSlider {
Layout.fillWidth: true
from: 0 from: 0
to: root.player.length ?? 0 to: root.player.length ?? 0
value: root.player.position value: root.player.position
implicitHeight: 6 implicitHeight: 6
Layout.fillWidth: true
onMoved: { onMoved: {
root.player.position = value; root.player.position = value;
} }

View file

@ -1,11 +1,12 @@
import qs.config
import qs.components import qs.components
import qs.constants
import qs.services import qs.services
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
RowLayout { RowLayout {
spacing: Styling.layout.spacing.xl spacing: 8
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
@ -13,7 +14,7 @@ RowLayout {
id: previousPlayerButton id: previousPlayerButton
visible: Mpris.players.length > 1 visible: Mpris.players.length > 1
text: Styling.lucide.icons.chevronLeft text: Icons.chevronLeft
onClicked: { onClicked: {
Mpris.previousPlayer(); Mpris.previousPlayer();
@ -21,7 +22,6 @@ RowLayout {
} }
StyledText { StyledText {
font.pixelSize: Styling.typography.textSize.xl
text: { text: {
if (!Mpris.active) { if (!Mpris.active) {
return "inactive"; return "inactive";
@ -36,13 +36,14 @@ RowLayout {
} }
return displayName; return displayName;
} }
font.pixelSize: 20
} }
StyledIconButton { StyledIconButton {
id: nextPlayerButton id: nextPlayerButton
visible: Mpris.players.length > 1 visible: Mpris.players.length > 1
text: Styling.lucide.icons.chevronRight text: Icons.chevronRight
onClicked: { onClicked: {
Mpris.nextPlayer(); Mpris.nextPlayer();

View file

@ -7,21 +7,19 @@ Button {
property alias border: rectangle.border property alias border: rectangle.border
property alias radius: rectangle.radius property alias radius: rectangle.radius
font.pixelSize: Styling.typography.textSize.base font.pixelSize: 14
font.family: Styling.typography.fontFamily padding: 6
verticalPadding: 6
horizontalPadding: 8
palette.button: hovered ? Styling.theme.primary : Styling.theme.base200 palette.button: hovered ? Theme.palette.primary : Theme.palette.base100
Behavior on palette.button { Behavior on palette.button {
ColorAnimation { ColorAnimation {
duration: Styling.animations.speed.normal duration: 100
} }
} }
palette.buttonText: hoverEnabled && hovered ? Styling.theme.primarycontent : Styling.theme.basecontent palette.buttonText: hoverEnabled && hovered ? Theme.palette.primarycontent : Theme.palette.basecontent
Behavior on palette.buttonText { Behavior on palette.buttonText {
ColorAnimation { ColorAnimation {
duration: Styling.animations.speed.normal duration: 100
} }
} }
@ -34,6 +32,6 @@ Button {
background: Rectangle { background: Rectangle {
id: rectangle id: rectangle
color: root.palette.button color: root.palette.button
radius: Styling.theme.radiusField radius: 8
} }
} }

View file

@ -1,107 +0,0 @@
pragma ComponentBehavior: Bound
import qs.config
import QtQuick
import QtQuick.Controls
import Quickshell.Widgets
ComboBox {
id: control
palette.button: Styling.theme.base200
palette.buttonText: Styling.theme.basecontent
palette.highlight: Styling.theme.primary
palette.highlightedText: Styling.theme.primarycontent
palette.text: Styling.theme.basecontent
palette.window: Styling.theme.base200
implicitHeight: 40
delegate: ItemDelegate {
id: delegate
required property var model
required property int index
width: control.width
contentItem: StyledText {
text: delegate.model[control.textRole]
color: delegate.highlighted ? palette.highlightedText : palette.buttonText
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: delegate.highlighted ? palette.highlight : palette.button
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
}
highlighted: control.highlightedIndex === index
}
contentItem: StyledText {
leftPadding: 12
text: control.displayText
color: control.hovered || control.down ? palette.highlightedText : palette.buttonText
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
color: control.hovered || control.down ? palette.highlight : palette.button
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
border.color: Styling.theme.base100
border.width: control.visualFocus ? 2 : 1
radius: Styling.theme.radiusSelector
}
popup: Popup {
palette: control.palette
y: control.height - 1
width: control.width
height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin)
padding: 1
contentItem: ClippingWrapperRectangle {
radius: Styling.theme.radiusField
ListView {
clip: true
implicitHeight: contentHeight + 2
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator {}
}
}
background: Rectangle {
color: palette.window
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
radius: Styling.theme.radiusField
}
}
}

View file

@ -1,29 +1,11 @@
import qs.config import qs.config
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import Quickshell.Widgets
Drawer { Drawer {
id: control id: control
dim: false dim: false
property bool focused: false background: Rectangle {
onVisibleChanged: {
focused = visible;
}
HyprlandFocusGrab {
active: control.focused
windows: [QsWindow.window]
onCleared: {
control.focused = false;
}
}
background: ClippingWrapperRectangle {
margin: 4
Component.onCompleted: { Component.onCompleted: {
if (control.edge == Qt.TopEdge) { if (control.edge == Qt.TopEdge) {
radius = 8; radius = 8;
@ -38,24 +20,6 @@ Drawer {
topRightRadius = 8; topRightRadius = 8;
} }
} }
color: Styling.theme.base300 color: Theme.palette.base200
Rectangle {
Component.onCompleted: {
if (control.edge == Qt.TopEdge) {
radius = 8;
} else if (control.edge == Qt.LeftEdge) {
topRightRadius = 8;
bottomRightRadius = 8;
} else if (control.edge == Qt.RightEdge) {
topLeftRadius = 8;
bottomLeftRadius = 8;
} else if (control.edge == Qt.BottomEdge) {
topLeftRadius = 8;
topRightRadius = 8;
}
}
color: Styling.theme.base100
}
} }
} }

View file

@ -5,14 +5,19 @@ import QtQuick.Controls
RoundButton { RoundButton {
id: control id: control
FontLoader {
id: loader
source: "../assets/lucide.woff"
}
property alias border: rect.border property alias border: rect.border
property color color: hovered ? Styling.theme.primarycontent : Styling.theme.basecontent property color color: hovered ? Theme.palette.primarycontent : Theme.palette.basecontent
property int rotation: 0 property int rotation: 0
font.family: Styling.lucide.font.family font.family: loader.font.family
font.pixelSize: 16 font.pixelSize: 18
radius: Styling.theme.radiusField radius: 8
padding: 8 padding: 6
HoverHandler { HoverHandler {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -22,35 +27,34 @@ RoundButton {
id: icon id: icon
font: control.font font: control.font
text: control.text text: control.text
verticalAlignment: Text.AlignVCenter
color: control.color color: control.color
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Styling.animations.speed.normal duration: 100
} }
} }
rotation: control.rotation rotation: control.rotation
Behavior on rotation { Behavior on rotation {
RotationAnimation { RotationAnimation {
duration: Styling.animations.speed.slow duration: 200
easing.type: Easing.OutQuad easing.type: Easing.InOutCubic
} }
} }
} }
background: Rectangle { background: Rectangle {
id: rect id: rect
border.color: control.hovered ? Styling.theme.base300 : Styling.theme.base200 border.color: control.hovered ? Theme.palette.primary : Theme.palette.base100
Behavior on border.color { Behavior on border.color {
ColorAnimation { ColorAnimation {
duration: Styling.animations.speed.normal duration: 100
} }
} }
border.width: 0 border.width: 2
color: control.hovered ? Styling.theme.primary : Styling.theme.base200 color: control.hovered ? Theme.palette.primary : Theme.palette.base100
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Styling.animations.speed.normal duration: 100
} }
} }
radius: control.radius radius: control.radius

View file

@ -1,17 +0,0 @@
import qs.config
import QtQuick
import Quickshell.Widgets
Image {
property alias radius: rectangle.radius
property alias skeletonColor: rectangle.color
ClippingRectangle {
id: rectangle
color: Styling.theme.base200
anchors.fill: parent
}
}

View file

@ -0,0 +1,16 @@
import qs.config
import QtQuick
import Quickshell.Widgets
WrapperRectangle {
id: root
margin: 8
radius: 8
color: Theme.palette.base100
Behavior on color {
ColorAnimation {
duration: 200
easing.type: Easing.InOutQuad
}
}
}

View file

@ -1,4 +1,3 @@
import qs.config
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
@ -15,7 +14,7 @@ ListView {
rebound: Transition { rebound: Transition {
NumberAnimation { NumberAnimation {
properties: "x,y" properties: "x,y"
duration: Styling.animations.speed.slow duration: 400
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: [0.2, 0, 0, 1, 1, 1] easing.bezierCurve: [0.2, 0, 0, 1, 1, 1]
} }

View file

@ -1,21 +0,0 @@
import qs.config
import QtQuick
import QtQuick.Controls
Pane {
id: pane
padding: 8
background: Rectangle {
color: "transparent"
border.width: Styling.theme.border
border.color: pane.hovered ? Styling.theme.accent : Styling.theme.basecontent
opacity: 0.33
Behavior on border.color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
radius: Styling.theme.radiusBox
}
}

View file

@ -1,74 +0,0 @@
import qs.config
import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
PanelWindow {
id: window
default property alias content: contentItem.data
property alias background: background
required property string name
property bool canFocus: true
property bool focused: false
property int padding: 4
WlrLayershell.namespace: `lux-${name}`
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.keyboardFocus: window.visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
onVisibleChanged: {
if (!canFocus)
return;
focused = visible;
}
HyprlandFocusGrab {
active: window.focused
windows: [window]
onCleared: {
if (!window.canFocus)
return;
window.focused = false;
}
}
color: "transparent"
Rectangle {
id: background
anchors.fill: parent
radius: Styling.theme.radiusBox
Behavior on radius {
NumberAnimation {
duration: Styling.animations.speed.normal
}
}
color: Styling.theme.base100
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
Behavior on opacity {
NumberAnimation {
duration: Styling.animations.speed.fast
}
}
border.width: 2
border.color: Styling.theme.base300
Behavior on border.color {
ColorAnimation {
duration: Styling.animations.speed.fast
}
}
}
Item {
id: contentItem
anchors.centerIn: parent
implicitWidth: parent.width - 2 * window.padding
implicitHeight: parent.height - 2 * window.padding
}
}

View file

@ -6,87 +6,33 @@ import Quickshell.Hyprland
PopupWindow { PopupWindow {
id: root id: root
required property Component content implicitWidth: contentItem.children.reduce((prev, child) => Math.max(prev, child.width), 0)
implicitHeight: contentItem.children.reduce((prev, child) => prev + child.height, 0)
implicitWidth: background.width
implicitHeight: background.height
color: "transparent" color: "transparent"
contentItem.focus: visible
function open() {
visible = true;
}
function close() {
visible = false;
}
// WlrLayershell.layer: WlrLayer.Top
// WlrLayershell.keyboardFocus: root.visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
HyprlandFocusGrab { HyprlandFocusGrab {
id: grab
active: root.visible active: root.visible
windows: [root] windows: [root]
onCleared: { onCleared: {
background.state = "closed"; root.close();
} }
} }
function toggle() { Rectangle {
background.state = background.state == "opened" ? "closed" : "opened";
}
StyledWrapperRectangle {
id: background id: background
anchors.fill: parent
margin: 16 color: Theme.palette.base200
focus: true radius: 8
onFocusChanged: {
if (!focus) {
grab.active = false;
}
}
Behavior on opacity {
NumberAnimation {
duration: Styling.animations.speed.normal
}
}
state: "closed"
states: [
State {
name: "closed"
PropertyChanges {
background {
opacity: 0
}
}
},
State {
name: "opened"
PropertyChanges {
background {
opacity: 1
}
}
}
]
transitions: [
Transition {
from: "closed"
to: "opened"
ScriptAction {
script: root.visible = true
}
},
Transition {
from: "opened"
to: "closed"
SequentialAnimation {
PauseAnimation {
duration: root.animationDuration
}
ScriptAction {
script: root.visible = false
}
}
}
]
Loader {
active: root.visible
sourceComponent: root.content
}
} }
} }

View file

@ -15,8 +15,8 @@ ProgressBar {
background: Rectangle { background: Rectangle {
implicitWidth: 200 implicitWidth: 200
implicitHeight: 6 implicitHeight: 6
color: Styling.theme.base100 color: Theme.palette.base100
radius: Styling.theme.radiusField radius: 8
} }
contentItem: Item { contentItem: Item {
@ -27,8 +27,8 @@ ProgressBar {
Rectangle { Rectangle {
width: control.visualPosition * parent.width width: control.visualPosition * parent.width
height: parent.height height: parent.height
radius: Styling.theme.radiusField radius: 8
color: Styling.theme.primary color: Theme.palette.primary
visible: !control.indeterminate visible: !control.indeterminate
} }
@ -41,14 +41,14 @@ ProgressBar {
Row { Row {
Rectangle { Rectangle {
id: rect id: rect
color: Styling.theme.primary color: Theme.palette.primary
width: 40 width: 40
height: control.height height: control.height
} }
XAnimator on x { XAnimator on x {
from: control.width + rect.width from: control.width + rect.width
to: -rect.width to: -rect.width
duration: Styling.animations.speed.verySlow duration: 1000
loops: Animation.Infinite loops: Animation.Infinite
running: control.indeterminate running: control.indeterminate
} }

View file

@ -1,12 +0,0 @@
import qs.config
import QtQuick
Rectangle {
radius: Styling.theme.radiusBox
color: Styling.theme.base200
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.normal
}
}
}

View file

@ -18,19 +18,19 @@ Slider {
implicitHeight: control.height implicitHeight: control.height
width: control.availableWidth width: control.availableWidth
height: implicitHeight height: implicitHeight
radius: Styling.theme.radiusField radius: 8
color: Styling.theme.base200 color: Theme.palette.base100
Rectangle { Rectangle {
width: control.visualPosition * parent.width width: control.visualPosition * parent.width
Behavior on width { Behavior on width {
NumberAnimation { NumberAnimation {
duration: Styling.animations.speed.fast duration: 75
} }
} }
height: parent.height height: parent.height
color: Styling.theme.primary color: Theme.palette.primary
radius: Styling.theme.radiusField radius: 8
} }
} }
handle: null handle: null

View file

@ -11,7 +11,7 @@ Switch {
text: control.text text: control.text
font: control.font font: control.font
opacity: enabled ? 1.0 : 0.3 opacity: enabled ? 1.0 : 0.3
color: Styling.theme.basecontent color: Theme.palette.basecontent
elide: Text.ElideRight elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
@ -29,9 +29,9 @@ Switch {
implicitHeight: 24 implicitHeight: 24
x: control.width - width - control.rightPadding x: control.width - width - control.rightPadding
y: parent.height / 2 - height / 2 y: parent.height / 2 - height / 2
radius: Styling.theme.radiusSelector radius: 16
color: "transparent" color: "transparent"
border.color: control.checked ? Styling.theme.primary : Styling.theme.basecontent border.color: control.checked ? Theme.palette.primary : Theme.palette.basecontent
border.width: 2 border.width: 2
Rectangle { Rectangle {
@ -40,15 +40,16 @@ Switch {
y: parent.height / 2 - height / 2 y: parent.height / 2 - height / 2
Behavior on x { Behavior on x {
NumberAnimation { NumberAnimation {
duration: Styling.animations.speed.fast duration: 100
} }
} }
width: parent.width / 2 - indicator.padding width: parent.width / 2 - indicator.padding
height: parent.height - indicator.padding height: parent.height - indicator.padding
radius: Styling.theme.radiusSelector radius: 16
color: control.checked ? Styling.theme.primary : Styling.theme.basecontent color: control.checked ? Theme.palette.primary : Theme.palette.basecontent
// border.color: control.checked ? (control.down ? "#17a81a" : "#21be2b") : "#999999"
} }
} }
background: Item {} background: null
} }

View file

@ -1,19 +0,0 @@
import QtQuick
import QtQuick.Controls
Container {
id: control
property alias orientation: view.orientation
contentItem: ListView {
id: view
model: control.contentModel
currentIndex: control.currentIndex
spacing: control.spacing
orientation: ListView.Horizontal
boundsBehavior: Flickable.StopAtBounds
}
background: Item {}
}

View file

@ -1,46 +0,0 @@
import qs.config
import QtQuick
import QtQuick.Controls
Button {
id: control
property alias radius: rectangle.radius
padding: 8
radius: Styling.theme.radiusField
contentItem: Text {
font.pixelSize: Styling.typography.textSize.base
font.family: Styling.typography.fontFamily
text: control.text
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: control.hovered ? Styling.theme.primarycontent : Styling.theme.basecontent
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.normal
}
}
}
background: Rectangle {
id: rectangle
color: control.hovered ? Styling.theme.primary : Styling.theme.base100
Behavior on color {
ColorAnimation {
duration: Styling.animations.speed.normal
}
}
opacity: control.checked || control.hovered ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation {
duration: Styling.animations.speed.normal
}
}
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
}
}

View file

@ -2,12 +2,9 @@ import qs.config
import QtQuick import QtQuick
Text { Text {
font.family: Styling.typography.fontFamily font.family: Theme.fontFamily
font.pixelSize: Styling.typography.textSize.base color: Theme.palette.basecontent
color: Styling.theme.basecontent ColorAnimation on color {
Behavior on color { duration: 100
ColorAnimation {
duration: Styling.animations.speed.fast
}
} }
} }

View file

@ -1,9 +1,8 @@
import qs.config import qs.config
import QtQuick
import QtQuick.Controls import QtQuick.Controls
TextField { TextField {
color: Styling.theme.basecontent color: Theme.palette.basecontent
background: Item {} background: null
} }

View file

@ -9,13 +9,11 @@ ToolTip {
contentItem: Text { contentItem: Text {
text: control.text text: control.text
font: control.font font: control.font
color: Styling.theme.basecontent color: Theme.palette.basecontent
} }
background: Rectangle { background: Rectangle {
radius: Styling.theme.radiusBox radius: 8
color: Styling.theme.base100 color: Theme.palette.base200
border.color: Styling.theme.base200
border.width: Styling.theme.border
} }
} }

View file

@ -0,0 +1,9 @@
import Quickshell
import Quickshell.Wayland
PanelWindow {
required property string name
WlrLayershell.namespace: `lux-${name}`
color: "transparent"
}

View file

@ -3,19 +3,12 @@ import QtQuick
import Quickshell.Widgets import Quickshell.Widgets
WrapperRectangle { WrapperRectangle {
margin: 8 radius: 8
radius: Styling.theme.radiusBox color: Theme.palette.base300
color: Styling.theme.base100
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Styling.animations.speed.fast duration: 200
} easing.type: Easing.InOutQuad
}
border.width: 2
border.color: Styling.theme.base100
Behavior on border.color {
ColorAnimation {
duration: Styling.animations.speed.fast
} }
} }
} }

View file

@ -1,13 +0,0 @@
import qs.components
import qs.config
import QtQuick
StyledComboBox {
id: control
currentIndex: Theme.themes.indexOf(Theme.currentTheme)
model: Theme.themes
onActivated: index => {
Theme.currentTheme = Theme.themes[index];
}
}

View file

@ -1,14 +0,0 @@
import QtQuick
Image {
asynchronous: true
fillMode: Image.PreserveAspectCrop
sourceSize.width: parent.width
sourceSize.height: parent.height
Behavior on scale {
NumberAnimation {
duration: 200
}
}
}

View file

@ -1,65 +0,0 @@
pragma ComponentBehavior: Bound
import qs.config
import qs.services
import QtQuick
import Qt.labs.folderlistmodel 2.9
import Quickshell.Widgets
ListView {
id: list
orientation: ListView.Horizontal
clip: true
spacing: 8
snapMode: ListView.SnapToItem
implicitWidth: 160 * 3
implicitHeight: 90
model: FolderListModel {
nameFilters: ["*.jpg"]
folder: `file://${Paths.expandTilde(Config.wallpaper.directory)}`
showDirs: false
}
delegate: Item {
id: delegate
required property url fileUrl
required property int index
property bool hovered: ListView.isCurrentItem
implicitWidth: 160
implicitHeight: 90
Rectangle {
color: Styling.theme.base200
anchors.fill: parent
radius: 12
scale: delegate.hovered ? 1.0 : 0.9
Behavior on scale {
NumberAnimation {
duration: Styling.animations.veryFast
}
}
Image {
id: image
asynchronous: true
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: delegate.fileUrl
}
MouseArea {
anchors.fill: image
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: list.currentIndex = delegate.index
onClicked: WallpaperService.currentWallpaper = delegate.fileUrl
}
}
}
}

View file

@ -1,39 +1,9 @@
pragma Singleton pragma Singleton
import Quickshell import Quickshell
import Quickshell.Io
Singleton { Singleton {
id: root id: root
readonly property alias powermenu: adapter.powermenu readonly property PowerMenu powermenu: PowerMenu {}
readonly property alias wallpaper: adapter.wallpaper
FileView {
path: `${Paths.config}/shell.json`
watchChanges: true
onFileChanged: reload()
// onAdapterUpdated: writeAdapter()
JsonAdapter {
id: adapter
property var powermenu: PowerMenu {}
property var wallpaper: Wallpaper {}
}
}
component PowerMenu: JsonObject {
property list<PowerMenuItem> actions
}
component PowerMenuItem: JsonObject {
property string text
property string command
}
component Wallpaper: JsonObject {
property string directory
}
} }

144
config/Dimensions.qml Normal file
View file

@ -0,0 +1,144 @@
pragma Singleton
import QtQuick
import Quickshell
Singleton {
id: root
property int radius: 8
property Bar bar: Bar {}
property Mpris mpris: Mpris {}
property Clock clock: Clock {}
property Pipewire pipewire: Pipewire {}
property Network network: Network {}
property Bluetooth bluetooth: Bluetooth {}
property Storage storage: Storage {}
property Memory memory: Memory {}
property Cpu cpu: Cpu {}
property Gpu gpu: Gpu {}
property Caffeine caffeine: Caffeine {}
property Notifications notifications: Notifications {}
property Workspace workspace: Workspace {}
property Tray tray: Tray {}
property TrayMenu trayMenu: TrayMenu {}
component Bar: QtObject {
property int spacing: 8
property int border: 2
property int height: 50
property int verticalMargins: 4
property int horizontalMargins: 8
property int verticalPadding: 2
property int horizontalPadding: 8
}
component Mpris: QtObject {
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Clock: QtObject {
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Pipewire: QtObject {
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Network: QtObject {
property int iconSize: 14
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Bluetooth: QtObject {
property int fontSize: 16
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Storage: QtObject {
property int iconSize: 14
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Memory: QtObject {
property int iconSize: 14
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Cpu: QtObject {
property int iconSize: 14
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Gpu: QtObject {
property int iconSize: 14
property int fontSize: 14
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Caffeine: QtObject {
property int fontSize: 16
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Notifications: QtObject {
property int fontSize: 16
property int height: 30
property int horizontalPadding: 8
property int verticalPadding: 6
}
component Workspace: QtObject {
property int spacing: 5
property int iconSize: 16
property int width: 30
property int height: 30
property int verticalPadding: 6
property int horizontalPadding: 7
}
component Tray: QtObject {
property int spacing: 5
property int iconSize: 18
property int width: 30
property int height: 30
property int verticalPadding: 6
property int horizontalPadding: 7
}
component TrayMenu: QtObject {
property int fontSize: 10
property int width: 30
property int height: 30
property int verticalPadding: 6
property int horizontalPadding: 7
}
}

View file

@ -1,45 +0,0 @@
pragma Singleton
import Quickshell
import Qt.labs.platform
Singleton {
id: root
readonly property url home: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
readonly property url pictures: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
readonly property url data: `${StandardPaths.standardLocations(StandardPaths.GenericDataLocation)[0]}/lux`
readonly property url state: `${StandardPaths.standardLocations(StandardPaths.GenericStateLocation)[0]}/lux`
readonly property url cache: `${StandardPaths.standardLocations(StandardPaths.GenericCacheLocation)[0]}/lux`
readonly property url config: `${StandardPaths.standardLocations(StandardPaths.GenericConfigLocation)[0]}/lux`
function stringify(path: url): string {
let str = path.toString();
if (str.startsWith("root:/"))
str = `file://${Quickshell.shellDir}/${str.slice(6)}`;
else if (str.startsWith("/"))
str = `file://${str}`;
return new URL(str).pathname;
}
function expandTilde(path: string): string {
return strip(path.replace("~", stringify(root.home)));
}
function shortenHome(path: string): string {
return path.replace(strip(root.home), "~");
}
function strip(path: url): string {
return stringify(path).replace("file://", "");
}
function mkdir(path: url): void {
Quickshell.execDetached(["mkdir", "-p", strip(path)]);
}
function copy(from: url, to: url): void {
Quickshell.execDetached(["cp", strip(from), strip(to)]);
}
}

18
config/PowerMenu.qml Normal file
View file

@ -0,0 +1,18 @@
import Quickshell
PersistentProperties {
property list<var> actions: [
{
text: "󰍃 Logout",
command: "hyprctl dispatch exit"
},
{
text: "󰜉 Reboot",
command: "systemctl reboot"
},
{
text: " Shutdown",
command: "systemctl poweroff"
}
]
}

View file

@ -1,132 +0,0 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
readonly property Animations animations: Animations {}
readonly property Typography typography: Typography {}
readonly property Layout layout: Layout {}
readonly property alias theme: theme
readonly property Lucide lucide: Lucide {}
component Animations: QtObject {
readonly property AnimationSpeed speed: AnimationSpeed {}
}
component AnimationSpeed: QtObject {
readonly property int veryFast: 50
readonly property int fast: 100
readonly property int normal: 200
readonly property int slow: 400
readonly property int verySlow: 1000
}
component Typography: QtObject {
readonly property string fontFamily: "JetBrainsMono Nerd Font"
readonly property FontSize textSize: FontSize {}
}
component FontSize: QtObject {
readonly property int sm: 12
readonly property int base: 14
readonly property int lg: 18
readonly property int xl: 24
}
component Layout: QtObject {
readonly property LayoutSpacing spacing: LayoutSpacing {}
}
component LayoutSpacing: QtObject {
readonly property int sm: 2
readonly property int base: 4
readonly property int lg: 8
readonly property int xl: 12
}
component Lucide: Item {
readonly property alias font: loader.font
readonly property LucideIcons icons: LucideIcons {}
FontLoader {
id: loader
source: "../assets/lucide.woff"
}
}
component LucideIcons: QtObject {
readonly property string activity: "\u{E038}"
readonly property string audioLines: "\u{E55F}"
readonly property string batteryCharging: "\u{E058}"
readonly property string batteryFull: "\u{E059}"
readonly property string batteryMedium: "\u{E05b}"
readonly property string batteryLow: "\u{E05a}"
readonly property string batteryWarning: "\u{E3b0}"
readonly property string bell: "\u{E05d}"
readonly property string bellRing: "\u{E224}"
readonly property string bluetooth: "\u{E060}"
readonly property string bluetoothConnected: "\u{E1b8}"
readonly property string brickWall: "\u{E586}"
readonly property string coffee: "\u{E09a}"
readonly property string chevronLeft: "\u{E072}"
readonly property string chevronRight: "\u{E073}"
readonly property string cpu: "\u{E0ad}"
readonly property string gpu: "\u{E66f}"
readonly property string hardDrive: "\u{E0f1}"
readonly property string layoutDashboard: "\u{E1C1}"
readonly property string memoryStick: "\u{E44a}"
readonly property string pause: "\u{E132}"
readonly property string play: "\u{E140}"
readonly property string search: "\u{E155}"
readonly property string skipBack: "\u{E163}"
readonly property string skipForward: "\u{E164}"
readonly property string square: "\u{E16B}"
readonly property string stop: "\u{E132}"
readonly property string swatchBook: "\u{E5A4}"
readonly property string wifiOff: "\u{E1af}"
readonly property string wifiLow: "\u{E5fd}"
readonly property string wifiHigh: "\u{E5fc}"
readonly property string wifi: "\u{E1ae}"
readonly property string triangle: "\u{E192}"
readonly property string triangleDashed: "\u{E642}"
}
FileView {
path: `${Paths.config}/themes/${Theme.currentTheme}.json`
watchChanges: true
onFileChanged: reload()
// when changes are made to properties in the adapter, save them
onAdapterUpdated: writeAdapter()
JsonAdapter {
id: theme
property color primary: "#605dff"
property color primarycontent: "#edf1fe"
property color secondary: "#f43098"
property color secondarycontent: "#f9e4f0"
property color accent: "#00d3bb"
property color accentcontent: "#084d49"
property color neutral: "#09090b"
property color neutralcontent: "#e4e4e7"
property color base100: "#1d232a"
property color base200: "#191e24"
property color base300: "#15191e"
property color basecontent: "#ecf9ff"
property color info: "#00bafe"
property color infocontent: "#042e49"
property color success: "#00d390"
property color successcontent: "#004c39"
property color warning: "#fcb700"
property color warningcontent: "#793205"
property color error: "#ff627d"
property color errorcontent: "#4d0218"
property int radiusSelector: 8
property int radiusField: 8
property int radiusBox: 8
property int border: 2
}
}
}

View file

@ -1,50 +1,39 @@
pragma Singleton pragma Singleton
import QtQuick import QtQuick
import Qt.labs.folderlistmodel 2.9
import Quickshell import Quickshell
import Quickshell.Io
Singleton { Singleton {
id: root id: root
property alias themes: cache.themes property var lucide: FontLoader {
property alias currentTheme: cache.current source: "../assets/lucide.woff"
property int currentThemeIndex: themes.indexOf(currentTheme)
FolderListModel {
id: model
nameFilters: ["*.json"]
folder: `${Paths.config}/themes/`
showDirs: false
onCountChanged: {
const arr = [];
for (let i = 0; i < count; i++) {
arr.push(get(i, "fileName").replace(".json", ""));
}
root.themes = arr;
}
} }
property string fontFamily: "JetBrainsMono Nerd Font"
property Palette palette: Palette {}
FileView { component Palette: QtObject {
path: `${Paths.cache}/theme.json` id: palette
watchChanges: true property color primary: "#1fb854"
onFileChanged: reload() property color primarycontent: "#000000"
property color secondary: "#1eb88e"
// when changes are made to properties in the adapter, save them property color secondarycontent: "#000c07"
onAdapterUpdated: writeAdapter() property color accent: "#1fb8ab"
property color accentcontent: "#010c0b"
JsonAdapter { property color neutral: "#19362d"
id: cache property color neutralcontent: "#cdd3d1"
property color base100: "#1b1717"
property string current: "dark" property color base200: "#161212"
property list<string> themes: ["dark"] property color base300: "#110d0d"
onThemesChanged: { property color basecontent: "#cac9c9"
if (!themes.includes(current)) { property color info: "#00b5ff"
current = themes[0]; property color infocontent: "#000000"
} property color success: "#00a96e"
} property color successcontent: "#000000"
} property color warning: "#ffbe00"
property color warningcontent: "#000000"
property color error: "#ff5861"
property color errorcontent: "#000000"
} }
} }

36
constants/Icons.qml Normal file
View file

@ -0,0 +1,36 @@
pragma Singleton
import Quickshell
Singleton {
property string batteryCharging: "\u{E058}"
property string batteryFull: "\u{E059}"
property string batteryMedium: "\u{E05b}"
property string batteryLow: "\u{E05a}"
property string batteryWarning: "\u{E3b0}"
property string bell: "\u{E05d}"
property string bellRing: "\u{E224}"
property string bluetooth: "\u{E060}"
property string bluetoothConnected: "\u{E1b8}"
property string brickWall: "\u{E586}"
property string coffee: "\u{E09a}"
property string chevronLeft: "\u{E072}"
property string chevronRight: "\u{E073}"
property string cpu: "\u{E0ad}"
property string gpu: "\u{E66f}"
property string hardDrive: "\u{E0f1}"
property string memoryStick: "\u{E44a}"
property string pause: "\u{E132}"
property string play: "\u{E140}"
property string search: "\u{E155}"
property string skipBack: "\u{E163}"
property string skipForward: "\u{E164}"
property string stop: "\u{E132}"
property string square: "\u{E16B}"
property string wifiOff: "\u{E1af}"
property string wifiLow: "\u{E5fd}"
property string wifiHigh: "\u{E5fc}"
property string wifi: "\u{E1ae}"
property string triangle: "\u{E192}"
property string triangleDashed: "\u{E642}"
}

View file

@ -1,11 +1,9 @@
import "bar" import "bar"
import "configuration"
import "drawers" import "drawers"
import "launcher" import "launcher"
import "pomodoro" import "pomodoro"
import "powermenu" import "powermenu"
import "storybook" import "storybook"
import "wallpaper"
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
@ -18,23 +16,6 @@ Variants {
required property ShellScreen modelData required property ShellScreen modelData
PanelWindow {
id: background
// visible: false
anchors.top: true
anchors.left: true
anchors.right: true
anchors.bottom: true
color: "transparent"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Background
Wallpaper {}
}
PanelWindow { PanelWindow {
id: exclusionZone id: exclusionZone
@ -43,7 +24,7 @@ Variants {
anchors.right: true anchors.right: true
implicitWidth: bar.width implicitWidth: bar.width
implicitHeight: bar.height + bar.anchors.margins implicitHeight: bar.height
color: "transparent" color: "transparent"
} }
@ -94,11 +75,14 @@ Variants {
} }
} }
} }
Configuration {}
Launcher {} Launcher {}
Pomodoro {} Pomodoro {}
PowerMenu {} PowerMenu {}
Storybook {} Storybook {
anchor.window: topWindow
anchor.rect.x: topWindow.width / 2 - width / 2
anchor.rect.y: topWindow.height / 4
}
Drawers {} Drawers {}
} }
} }

View file

@ -5,12 +5,6 @@ import Quickshell.Hyprland
Scope { Scope {
id: root id: root
LuxShortcut {
name: 'configuration'
description: 'Open the configuration screen'
onPressed: Visibility.configuration = !Visibility.configuration
}
LuxShortcut { LuxShortcut {
name: 'launcher' name: 'launcher'
description: 'Open the application launcher' description: 'Open the application launcher'

View file

@ -14,24 +14,19 @@ StyledWrapperRectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
anchors.margins: 6 anchors.leftMargin: Dimensions.bar.horizontalMargins
anchors.leftMargin: 10 anchors.rightMargin: Dimensions.bar.horizontalMargins
anchors.rightMargin: 10 anchors.topMargin: Dimensions.bar.verticalMargins
anchors.bottomMargin: Dimensions.bar.verticalMargins
border.width: 3 margin: 6
border.color: Styling.theme.base200
margin: 4
color: Styling.theme.base100
RowLayout { RowLayout {
uniformCellSizes: true
RowLayout { RowLayout {
id: leftbar id: leftbar
spacing: Styling.layout.spacing.base spacing: Dimensions.bar.spacing
SystemLogo { SystemLogo {
implicitSize: 22 implicitSize: 22
@ -47,7 +42,7 @@ StyledWrapperRectangle {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
spacing: Styling.layout.spacing.base spacing: Dimensions.bar.spacing
Mpris {} Mpris {}
} }
@ -57,7 +52,7 @@ StyledWrapperRectangle {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
spacing: Styling.layout.spacing.base spacing: Dimensions.bar.spacing
Pywal {} Pywal {}

View file

@ -1,13 +1,13 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
StyledIconButton { StyledIconButton {
id: root id: root
border.width: 2 border.color: Caffeine.enabled ? Theme.palette.secondary : root.hovered ? Theme.palette.primary : Theme.palette.base100
border.color: Caffeine.enabled ? Styling.theme.secondary : root.hovered ? Styling.theme.primary : Styling.theme.base300 text: Icons.coffee
text: Styling.lucide.icons.coffee
onClicked: { onClicked: {
Caffeine.toggle(); Caffeine.toggle();

View file

@ -1,10 +1,19 @@
import qs.components import qs.components
import qs.config
import qs.widgets
import Quickshell import Quickshell
StyledText { StyledLabel {
StyledText {
id: text
font.pixelSize: Dimensions.clock.fontSize
text: ` ${Qt.formatDateTime(clock.date, "hh:mm:ss AP")}` text: ` ${Qt.formatDateTime(clock.date, "hh:mm:ss AP")}`
SystemClock { SystemClock {
id: clock id: clock
precision: SystemClock.Seconds precision: SystemClock.Seconds
} }
}
} }

View file

@ -1,7 +1,9 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
import qs.utils import qs.utils
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
@ -15,23 +17,30 @@ StyledButton {
} }
contentItem: RowLayout { contentItem: RowLayout {
id: row
spacing: 0 spacing: 0
Ref { Ref {
service: SystemInfo service: SystemInfo
} }
StyledText {
id: icon
LucideIcon { font.family: Theme.lucide.font.family
text: Styling.lucide.icons.cpu font.pixelSize: Dimensions.cpu.iconSize
color: root.hovered ? Styling.theme.primarycontent : Styling.theme.basecontent font.bold: true
text: Icons.cpu
color: root.hovered ? Theme.palette.primarycontent : Theme.palette.basecontent
} }
StyledText { StyledText {
id: text id: text
font.pixelSize: Dimensions.cpu.fontSize
text: ` ${(SystemInfo.cpuPerc * 100).toFixed().toString().padStart(2, "_")}%` text: ` ${(SystemInfo.cpuPerc * 100).toFixed().toString().padStart(2, "_")}%`
color: root.hovered ? Styling.theme.primarycontent : Styling.theme.basecontent color: root.hovered ? Theme.palette.primarycontent : Theme.palette.basecontent
states: [ states: [
State { State {
name: "showTemp" name: "showTemp"

View file

@ -1,7 +1,9 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
import qs.utils import qs.utils
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
@ -11,27 +13,34 @@ StyledButton {
property bool showTemp: false property bool showTemp: false
onClicked: { onClicked: {
showTemp = !showTemp; root.showTemp = !root.showTemp;
} }
contentItem: RowLayout { contentItem: RowLayout {
id: row
spacing: 0 spacing: 0
Ref { Ref {
service: SystemInfo service: SystemInfo
} }
StyledText {
id: icon
LucideIcon { font.family: Theme.lucide.font.family
text: Styling.lucide.icons.gpu font.pixelSize: Dimensions.gpu.iconSize
color: root.hovered ? Styling.theme.primarycontent : Styling.theme.basecontent font.bold: true
text: Icons.gpu
color: root.hovered ? Theme.palette.primarycontent : Theme.palette.basecontent
} }
StyledText { StyledText {
id: text id: text
font.pixelSize: Dimensions.gpu.fontSize
text: ` ${(SystemInfo.gpuPerc * 100).toFixed().toString().padStart(2, "_")}%` text: ` ${(SystemInfo.gpuPerc * 100).toFixed().toString().padStart(2, "_")}%`
color: root.hovered ? Styling.theme.primarycontent : Styling.theme.basecontent color: root.hovered ? Theme.palette.primarycontent : Theme.palette.basecontent
states: [ states: [
State { State {
name: "temp" name: "temp"

View file

@ -1,21 +1,28 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
import qs.widgets
import qs.utils import qs.utils
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
RowLayout { StyledLabel {
RowLayout {
Ref { Ref {
service: SystemInfo service: SystemInfo
} }
StyledText {
LucideIcon { font.family: Theme.lucide.font.family
text: Styling.lucide.icons.memoryStick font.pixelSize: Dimensions.memory.iconSize
font.bold: true
text: Icons.memoryStick
} }
StyledText { StyledText {
id: text
font.pixelSize: Dimensions.memory.fontSize
text: ` ${(SystemInfo.memPerc * 100).toFixed()}%` text: ` ${(SystemInfo.memPerc * 100).toFixed()}%`
} }
}
} }

View file

@ -1,26 +1,32 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
import qs.utils import qs.utils
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
RowLayout { StyledLabel {
RowLayout {
Ref { Ref {
service: NetworkService service: NetworkService
} }
StyledText {
LucideIcon {
id: icon id: icon
text: Styling.lucide.icons.wifiOff
font.family: Theme.lucide.font.family
font.pixelSize: Dimensions.network.iconSize
font.bold: true
text: Icons.wifiOff
states: [ states: [
State { State {
name: "high" name: "high"
when: NetworkService.active?.strength > 50 when: NetworkService.active?.strength > 50
PropertyChanges { PropertyChanges {
icon { icon {
text: Styling.lucide.icons.wifi text: Icons.wifi
} }
} }
}, },
@ -29,7 +35,7 @@ RowLayout {
when: NetworkService.active?.strength > 25 when: NetworkService.active?.strength > 25
PropertyChanges { PropertyChanges {
icon { icon {
text: Styling.lucide.icons.wifiHigh text: Icons.wifiHigh
} }
} }
}, },
@ -38,7 +44,7 @@ RowLayout {
when: NetworkService.active?.strength > 0 when: NetworkService.active?.strength > 0
PropertyChanges { PropertyChanges {
icon { icon {
text: Styling.lucide.icons.wifiLow text: Icons.wifiLow
} }
} }
} }
@ -47,6 +53,8 @@ RowLayout {
StyledText { StyledText {
id: text id: text
font.pixelSize: Dimensions.network.fontSize
text: ` ${(NetworkService.active?.strength ?? 0).toFixed()}%` text: ` ${(NetworkService.active?.strength ?? 0).toFixed()}%`
} }
}
} }

View file

@ -28,7 +28,7 @@ StyledButton {
when: Pipewire.muted when: Pipewire.muted
PropertyChanges { PropertyChanges {
root { root {
palette.button: Styling.theme.error palette.button: Theme.palette.error
} }
} }
} }

View file

@ -1,5 +1,6 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Services.UPower import Quickshell.Services.UPower
@ -9,56 +10,50 @@ StyledButton {
property UPowerDevice laptopBattery: UPower.devices.values.find(device => device.isLaptopBattery) ?? null property UPowerDevice laptopBattery: UPower.devices.values.find(device => device.isLaptopBattery) ?? null
property bool isCritical: laptopBattery?.percentage < 0.10 property bool isCritical: laptopBattery?.percentage < 0.10
visible: laptopBattery
contentItem: RowLayout { contentItem: RowLayout {
spacing: 4 spacing: 4
LucideIcon { LucideIcon {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: { color: {
if (root.isCritical) { if (root.isCritical) {
return Styling.theme.error; return Theme.palette.error;
} }
if (root.hovered) { if (root.hovered) {
return Styling.theme.primarycontent; return Theme.palette.primarycontent;
} }
return Styling.theme.basecontent; return Theme.palette.basecontent;
} }
font.pixelSize: 16
text: { text: {
if (root.laptopBattery?.state == UPowerDeviceState.Charging) { if (root.laptopBattery?.state == UPowerDeviceState.Charging) {
return Styling.lucide.icons.batteryCharging; return Icons.batteryCharging;
} }
if (root.isCritical) { if (root.isCritical) {
return Styling.lucide.icons.batteryWarning; return Icons.batteryWarning;
} }
if (root.laptopBattery?.percentage < 0.33) { if (root.laptopBattery?.percentage < 0.33) {
return Styling.lucide.icons.batteryLow; return Icons.batteryLow;
} }
if (root.laptopBattery?.percentage < 0.66) { if (root.laptopBattery?.percentage < 0.66) {
return Styling.lucide.icons.batteryMedium; return Icons.batteryMedium;
} }
return Styling.lucide.icons.batteryFull; return Icons.batteryFull;
} }
} }
StyledText { StyledText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: { color: {
if (root.isCritical) { if (root.isCritical) {
return Styling.theme.error; return Theme.palette.error;
} }
if (root.hovered) { if (root.hovered) {
return Styling.theme.primarycontent; return Theme.palette.primarycontent;
} }
return Styling.theme.basecontent; return Theme.palette.basecontent;
} }
text: `${(root.laptopBattery?.percentage.toFixed(2) * 100)}%` text: `${(root.laptopBattery?.percentage.toFixed(2) * 100)}%`
} }
} }
visible: laptopBattery
} }

View file

@ -1,12 +1,12 @@
import qs.components import qs.components
import qs.config import qs.constants
import QtQuick import QtQuick
import Quickshell.Io import Quickshell.Io
StyledIconButton { StyledIconButton {
id: clickable id: clickable
text: Styling.lucide.icons.brickWall text: Icons.brickWall
onClicked: { onClicked: {
process.running = true; process.running = true;

View file

@ -1,23 +1,30 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
import qs.utils import qs.utils
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
RowLayout { StyledLabel {
RowLayout {
Ref { Ref {
service: SystemInfo service: SystemInfo
} }
StyledText {
id: icon
LucideIcon { font.family: Theme.lucide.font.family
text: Styling.lucide.icons.hardDrive font.pixelSize: Dimensions.storage.iconSize
font.bold: true
text: Icons.hardDrive
} }
StyledText { StyledText {
id: text id: text
font.pixelSize: Dimensions.storage.fontSize
text: ` ${(SystemInfo.storagePerc * 100).toFixed()}%` text: ` ${(SystemInfo.storagePerc * 100).toFixed()}%`
} }
}
} }

View file

@ -1,21 +1,21 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Bluetooth import Quickshell.Bluetooth
import Quickshell.Widgets import Quickshell.Widgets
StyledWrapperRectangle { StyledLabel {
id: root id: root
required property BluetoothDevice modelData required property BluetoothDevice modelData
RowLayout { RowLayout {
id: row id: row
spacing: Styling.layout.spacing.base spacing: 8
Loader { Loader {
active: root.modelData?.icon != undefined active: root.modelData?.icon != undefined

View file

@ -1,10 +1,10 @@
import qs.components import qs.components
import qs.config import qs.constants
StyledIconButton { StyledIconButton {
id: root id: root
text: Styling.lucide.icons.bluetooth text: Icons.bluetooth
onClicked: popup.toggle() onClicked: popup.toggle()

View file

@ -1,6 +1,8 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
@ -9,6 +11,10 @@ import Quickshell.Bluetooth
StyledPopupWindow { StyledPopupWindow {
id: root id: root
backgroundColor: Theme.palette.base300
margins: 16
radius: 8
content: ColumnLayout { content: ColumnLayout {
spacing: 8 spacing: 8
StyledWrapperRectangle { StyledWrapperRectangle {

View file

@ -2,20 +2,22 @@ pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Bluetooth import Quickshell.Bluetooth
import Quickshell.Widgets import Quickshell.Widgets
StyledWrapperRectangle { StyledLabel {
id: root id: root
required property BluetoothDevice modelData required property BluetoothDevice modelData
RowLayout { RowLayout {
id: row id: row
spacing: Styling.layout.spacing.base spacing: 8
Loader { Loader {
active: root.modelData?.icon != undefined active: root.modelData?.icon != undefined
@ -32,9 +34,12 @@ StyledWrapperRectangle {
Loader { Loader {
active: root.modelData.batteryAvailable active: root.modelData.batteryAvailable
sourceComponent: RowLayout { sourceComponent: RowLayout {
LucideIcon { StyledText {
id: icon id: icon
text: Styling.lucide.icons.batteryFull font.family: Theme.lucide.font.family
font.pixelSize: Dimensions.cpu.iconSize
font.bold: true
text: Icons.batteryFull
states: [ states: [
State { State {
name: "full" name: "full"
@ -45,7 +50,7 @@ StyledWrapperRectangle {
when: root.modelData.battery > 0.33 when: root.modelData.battery > 0.33
PropertyChanges { PropertyChanges {
icon { icon {
text: Styling.lucide.icons.batteryFull text: Icons.batteryFull
} }
} }
}, },
@ -54,7 +59,7 @@ StyledWrapperRectangle {
when: root.modelData.battery > 0.10 when: root.modelData.battery > 0.10
PropertyChanges { PropertyChanges {
icon { icon {
text: Styling.lucide.icons.batteryFull text: Icons.batteryFull
} }
} }
}, },
@ -63,8 +68,8 @@ StyledWrapperRectangle {
when: root.modelData.battery > 0.10 when: root.modelData.battery > 0.10
PropertyChanges { PropertyChanges {
icon { icon {
text: Styling.lucide.icons.batteryWarning text: Icons.batteryWarning
color: Styling.theme.error color: Theme.palette.error
} }
} }
} }
@ -76,7 +81,7 @@ StyledWrapperRectangle {
StyledButton { StyledButton {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: 'Disconnect' text: 'Disconnect'
palette.button: hovered ? Styling.theme.error : Styling.theme.base200 palette.button: hovered ? Theme.palette.error : Theme.palette.base200
onClicked: { onClicked: {
if (root.modelData.state != BluetoothDeviceState.Connected) { if (root.modelData.state != BluetoothDeviceState.Connected) {

View file

@ -2,20 +2,21 @@ pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config import qs.config
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Bluetooth import Quickshell.Bluetooth
import Quickshell.Widgets import Quickshell.Widgets
StyledWrapperRectangle { StyledLabel {
id: root id: root
required property BluetoothDevice modelData required property BluetoothDevice modelData
RowLayout { RowLayout {
id: row id: row
spacing: Styling.layout.spacing.base spacing: 8
Loader { Loader {
active: root.modelData?.icon != undefined active: root.modelData?.icon != undefined
@ -47,7 +48,7 @@ StyledWrapperRectangle {
hoverEnabled: root.modelData.state == BluetoothDeviceState.Disconnected hoverEnabled: root.modelData.state == BluetoothDeviceState.Disconnected
text: 'Unpair' text: 'Unpair'
palette.button: hovered ? Styling.theme.error : Styling.theme.base100 palette.button: hovered ? Theme.palette.error : Theme.palette.base100
onClicked: { onClicked: {
if (!hoverEnabled) { if (!hoverEnabled) {

View file

@ -1,5 +1,6 @@
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import QtQuick import QtQuick
import Quickshell.Hyprland import Quickshell.Hyprland
@ -8,7 +9,7 @@ StyledIconButton {
required property HyprlandWorkspace workspace required property HyprlandWorkspace workspace
text: Styling.lucide.icons.triangle text: Icons.triangle
font.bold: true font.bold: true
font.pixelSize: 17 font.pixelSize: 17
padding: 8 padding: 8
@ -22,7 +23,7 @@ StyledIconButton {
PropertyChanges { PropertyChanges {
root { root {
rotation: 180 rotation: 180
color: root.hovered ? Styling.theme.basecontent : Styling.theme.primary color: root.hovered ? Theme.palette.basecontent : Theme.palette.primary
} }
} }
}, },
@ -31,9 +32,9 @@ StyledIconButton {
when: root.workspace.active when: root.workspace.active
PropertyChanges { PropertyChanges {
root { root {
text: Styling.lucide.icons.triangleDashed text: Icons.triangleDashed
rotation: 180 rotation: 180
color: root.hovered ? Styling.theme.basecontent : Styling.theme.primary color: root.hovered ? Theme.palette.basecontent : Theme.palette.primary
} }
} }
} }

View file

@ -1,5 +1,6 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.config
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Hyprland import Quickshell.Hyprland
@ -7,7 +8,7 @@ import Quickshell.Hyprland
RowLayout { RowLayout {
id: root id: root
spacing: 4 spacing: Dimensions.workspace.spacing
Repeater { Repeater {

View file

@ -1,16 +1,18 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
StyledWrapperRectangle { StyledLabel {
id: root id: root
required property var modelData required property var modelData
margin: 16
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right

View file

@ -1,13 +1,19 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config
import qs.services import qs.services
import qs.widgets
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
StyledPopupWindow { StyledPopupWindow {
id: root id: root
backgroundColor: Theme.palette.base300
margins: 16
radius: 8
content: GridLayout { content: GridLayout {
columns: 2 columns: 2
@ -24,8 +30,9 @@ StyledPopupWindow {
onClicked: Notifications.clear() onClicked: Notifications.clear()
} }
StyledWrapperRectangle { StyledLabel {
Layout.columnSpan: 2 Layout.columnSpan: 2
color: Theme.palette.base200
StyledListView { StyledListView {
id: notifications id: notifications

View file

@ -1,5 +1,5 @@
import qs.components import qs.components
import qs.config import qs.constants
import qs.services import qs.services
import QtQuick import QtQuick
@ -10,13 +10,13 @@ StyledIconButton {
menu.toggle(); menu.toggle();
} }
text: Notifications.hasNotifications ? Styling.lucide.icons.bell : Styling.lucide.icons.bellRing text: Notifications.hasNotifications ? Icons.bell : Icons.bellRing
states: State { states: State {
when: Notifications.hasNotifications when: Notifications.hasNotifications
PropertyChanges { PropertyChanges {
root { root {
text: Styling.lucide.icons.bellRing text: Icons.bellRing
} }
} }
} }

View file

@ -1,5 +1,6 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.config
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
@ -7,10 +8,10 @@ import Quickshell.Services.SystemTray
RowLayout { RowLayout {
id: root id: root
spacing: 4 spacing: Dimensions.tray.spacing
Repeater { Repeater {
model: SystemTray.items.values model: SystemTray.items
Loader { Loader {
id: loader id: loader

View file

@ -5,11 +5,12 @@ import QtQuick
import Quickshell import Quickshell
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
import Quickshell.Widgets import Quickshell.Widgets
import "menu/"
StyledIconButton { StyledIconButton {
id: root id: root
required property SystemTrayItem trayItem property SystemTrayItem trayItem
onClicked: menu.toggle() onClicked: menu.toggle()
@ -27,7 +28,7 @@ StyledIconButton {
} }
} }
TrayMenu { Menu {
id: menu id: menu
anchor.item: root anchor.item: root

View file

@ -1,49 +0,0 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.config
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
StyledPopupWindow {
id: window
required property QsMenuOpener menuOpener
content: ColumnLayout {
spacing: 4
Repeater {
model: window.menuOpener.children.values
delegate: Loader {
id: loader
required property QsMenuEntry modelData
active: true
Component.onCompleted: {
console.log(modelData.text);
}
Layout.fillWidth: true
Layout.minimumWidth: 160
sourceComponent: modelData.isSeparator ? menuSeperator : menuItem
property Component menuSeperator: WrapperItem {
margin: 4
Rectangle {
implicitHeight: 2
color: Styling.theme.base200
}
}
property Component menuItem: StyledButton {
text: loader.modelData.text
onClicked: loader.modelData.triggered()
}
}
}
}
}

View file

@ -0,0 +1,43 @@
pragma ComponentBehavior: Bound
import qs.config
import qs.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
StyledPopupWindow {
id: window
backgroundColor: Theme.palette.base300
margins: 14
radius: 8
property QsMenuOpener menuOpener
content: ColumnLayout {
spacing: 8
Repeater {
model: window.menuOpener.children
delegate: Loader {
id: loader
required property QsMenuEntry modelData
active: true
Layout.fillWidth: true
Layout.minimumWidth: 160
sourceComponent: modelData.isSeparator ? menuSeperator : menuItem
property Component menuSeperator: Rectangle {
implicitHeight: 2
color: Theme.palette.base100
}
property Component menuItem: MenuItem {
menuEntry: loader.modelData
}
}
}
}
}

View file

@ -0,0 +1,12 @@
import qs.components
import Quickshell
StyledButton {
id: root
property QsMenuEntry menuEntry
text: root.menuEntry.text
onClicked: menuEntry.triggered()
}

View file

@ -1,88 +0,0 @@
import qs.components
import qs.config
import qs.services
import QtQuick
import QtQuick.Layouts
ColumnLayout {
anchors.fill: parent
spacing: Styling.layout.spacing.xl
StyledText {
text: "Speaker Settings"
}
StyledPane {
Layout.fillWidth: true
padding: 24
GridLayout {
Layout.fillWidth: true
columnSpacing: Styling.layout.spacing.xl
StyledText {
Layout.column: 1
Layout.row: 1
text: "Device"
}
StyledComboBox {
Layout.column: 2
Layout.row: 1
Layout.fillWidth: true
currentIndex: Pipewire.sinks.indexOf(Pipewire.sink)
model: Pipewire.sinks.map(sink => sink.nickname ?? sink.name)
onActivated: index => {
Pipewire.setSink(Pipewire.sinks[index]);
}
}
StyledText {
Layout.column: 1
Layout.row: 2
text: "Volume"
}
StyledSlider {
Layout.column: 2
Layout.row: 2
from: 0.0
to: 1.0
value: Pipewire.volume
onMoved: {
Pipewire.setVolume(value);
}
}
}
}
StyledText {
text: "Microphone Settings"
}
StyledPane {
Layout.fillWidth: true
padding: 24
GridLayout {
Layout.fillWidth: true
columnSpacing: Styling.layout.spacing.xl
StyledText {
Layout.column: 1
Layout.row: 2
text: "Microphones"
}
StyledComboBox {
Layout.column: 2
Layout.row: 2
Layout.fillWidth: true
currentIndex: Pipewire.sources.indexOf(Pipewire.source)
model: Pipewire.sources.map(source => source.nickname ?? source.name)
onActivated: index => {
Pipewire.setSource(Pipewire.sinks[index]);
}
}
}
}
}

View file

@ -1,128 +0,0 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.config
import qs.services
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Widgets
StyledPanelWindow {
id: window
name: "configuration"
visible: Visibility.configuration
implicitWidth: 1000
implicitHeight: 600
background.color: Styling.theme.base200
onFocusedChanged: {
Visibility.configuration = focused;
}
RowLayout {
anchors.fill: parent
StyledTabBar {
id: tabs
Layout.preferredWidth: 150
Layout.fillHeight: true
Layout.margins: 2
orientation: ListView.Vertical
spacing: 12
Repeater {
model: views.data
delegate: StyledTabButton {
id: tabButton
required property ConfigurationView modelData
anchors.left: parent.left
anchors.right: parent.right
required property int index
contentItem: RowLayout {
states: [
State {
when: tabButton.hovered
PropertyChanges {
tabIcon.color: Styling.theme.primarycontent
tabText.color: Styling.theme.primarycontent
}
}
]
LucideIcon {
id: tabIcon
text: tabButton.modelData.icon
font.pixelSize: Styling.typography.textSize.lg
}
StyledText {
id: tabText
text: tabButton.modelData.title
font.pixelSize: Styling.typography.textSize.lg
}
}
text: tabButton.modelData.title
onClicked: tabs.setCurrentIndex(index)
}
}
}
SwipeView {
id: view
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
orientation: Qt.Vertical
currentIndex: tabs.currentIndex
background: Rectangle {
color: Styling.theme.base100
radius: Styling.theme.radiusBox
}
Repeater {
model: views.data
delegate: ScrollView {
id: scrollView
required property ConfigurationView modelData
padding: 24
Loader {
anchors.fill: parent
active: scrollView.modelData.view
sourceComponent: scrollView.modelData.view
}
}
}
}
}
Item {
id: views
ConfigurationView {
icon: Styling.lucide.icons.audioLines
title: "Audio"
view: AudioView {}
}
ConfigurationView {
icon: Styling.lucide.icons.swatchBook
title: "Styling"
view: StylingView {}
}
}
component ConfigurationView: QtObject {
property string icon
property string title
property Component view
}
}

View file

@ -1,40 +0,0 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.composite
import qs.config
import qs.services
import QtQuick
import QtQuick.Layouts
import Qt.labs.folderlistmodel 2.9
import Quickshell.Widgets
ColumnLayout {
StyledPane {
GridLayout {
columnSpacing: Styling.layout.spacing.xl
StyledText {
text: "Theme"
font.pixelSize: Styling.typography.textSize.lg
}
ThemeComboBox {}
}
}
// StyledPane {
// WallpaperList {}
// }
StyledPane {
padding: 2
ClippingWrapperRectangle {
radius: Styling.theme.radiusBox
color: "transparent"
WallpaperList {}
}
}
}

View file

@ -1,22 +1,16 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.components.composite
import qs.services import qs.services
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Widgets
StyledDrawer { StyledDrawer {
id: root id: root
visible: Visibility.dashboard visible: Visibility.dashboard
onFocusedChanged: { StyledWrapperRectangle {
Visibility.dashboard = focused;
}
WrapperItem {
margin: 32 margin: 32
ColumnLayout { ColumnLayout {
spacing: 8 spacing: 8

View file

@ -4,6 +4,7 @@ import "items"
import "services" import "services"
import qs.components import qs.components
import qs.config import qs.config
import qs.widgets
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
@ -31,7 +32,7 @@ StyledListView {
highlightResizeDuration: 0 highlightResizeDuration: 0
highlight: Rectangle { highlight: Rectangle {
radius: 8 radius: 8
color: Styling.theme.primary color: Theme.palette.primary
} }
header: StyledText { header: StyledText {

View file

@ -1,29 +1,40 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import "services" import "services"
import qs.config
import qs.components import qs.components
import qs.constants
import qs.services import qs.services
import Quickshell.Hyprland
import Quickshell.Wayland
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Widgets
StyledPanelWindow { StyledWindow {
id: window id: root
name: "launcher" name: "launcher"
visible: Visibility.launcher visible: Visibility.launcher
onVisibleChanged: {
if (!visible) {
list.currentIndex = 0;
search.clear();
}
Visibility.launcher = focused;
}
implicitWidth: rect.width implicitWidth: rect.width
implicitHeight: rect.height implicitHeight: rect.height
WrapperItem { WlrLayershell.layer: WlrLayer.Top
WlrLayershell.keyboardFocus: root.visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
HyprlandFocusGrab {
active: root.visible
windows: [search]
onCleared: {
root.reset();
}
}
function reset() {
Visibility.launcher = false;
list.currentIndex = 0;
search.clear();
}
StyledWrapperRectangle {
id: rect id: rect
margin: 18 margin: 18
@ -34,13 +45,13 @@ StyledPanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
margin: 4
color: Styling.theme.base200
RowLayout { RowLayout {
LucideIcon { LucideIcon {
id: icon
Layout.leftMargin: 8 Layout.leftMargin: 8
text: Styling.lucide.icons.search text: Icons.search
} }
StyledTextField { StyledTextField {
@ -49,19 +60,19 @@ StyledPanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: 40 implicitHeight: 40
cursorVisible: true cursorVisible: true
focus: window.visible focus: root.visible
placeholderText: "Applications" placeholderText: "Applications"
placeholderTextColor: "grey" placeholderTextColor: "grey"
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
event.accepted = true; event.accepted = true;
Visibility.launcher = false; root.reset();
} }
Keys.onReturnPressed: event => { Keys.onReturnPressed: event => {
event.accepted = true; event.accepted = true;
Apps.launch(list.currentItem.modelData); Apps.launch(list.currentItem.modelData);
Visibility.launcher = false; root.reset();
} }
Keys.onUpPressed: event => { Keys.onUpPressed: event => {
event.accepted = true; event.accepted = true;

View file

@ -42,7 +42,8 @@ Item {
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
Layout.fillWidth: true Layout.fillWidth: true
color: root.active ? Styling.theme.primarycontent : Styling.theme.basecontent color: root.active ? Theme.palette.primarycontent : Theme.palette.basecontent
font.pixelSize: 14
font.bold: true font.bold: true
} }
@ -51,8 +52,8 @@ Item {
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
Layout.fillWidth: true Layout.fillWidth: true
color: root.active ? Styling.theme.primarycontent : Styling.theme.basecontent color: root.active ? Theme.palette.primarycontent : Theme.palette.basecontent
font.pixelSize: Styling.typography.textSize.sm font.pixelSize: 10
elide: Text.ElideRight elide: Text.ElideRight
} }
} }

View file

@ -2,13 +2,15 @@ pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
import qs.widgets import qs.widgets
import Quickshell.Hyprland
import Quickshell.Wayland
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Widgets
StyledPanelWindow { StyledWindow {
id: root id: root
name: "pomodoro" name: "pomodoro"
@ -16,11 +18,10 @@ StyledPanelWindow {
implicitWidth: rect.width implicitWidth: rect.width
implicitHeight: rect.height implicitHeight: rect.height
onFocusedChanged: { WlrLayershell.layer: WlrLayer.Top
Visibility.pomodoro = focused; WlrLayershell.keyboardFocus: root.visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
}
WrapperItem { StyledWrapperRectangle {
id: rect id: rect
leftMargin: 48 leftMargin: 48
@ -28,6 +29,14 @@ StyledPanelWindow {
topMargin: 24 topMargin: 24
bottomMargin: 24 bottomMargin: 24
HyprlandFocusGrab {
active: Visibility.pomodoro
windows: [root]
onCleared: {
Visibility.pomodoro = false;
}
}
ColumnLayout { ColumnLayout {
spacing: 22 spacing: 22
@ -45,10 +54,10 @@ StyledPanelWindow {
Circle { Circle {
id: circle id: circle
radius: 150 radius: 150
borderColor: Styling.theme.base100 borderColor: Theme.palette.base100
strokeColor: PomodoroService.state == "timer" ? Styling.theme.primary : Styling.theme.warning strokeColor: PomodoroService.state == "timer" ? Theme.palette.primary : Theme.palette.warning
strokeWidth: 12 strokeWidth: 12
fillColor: button.hovered ? Styling.theme.primary : "transparent" fillColor: button.hovered ? Theme.palette.primary : "transparent"
percentage: (PomodoroService.state == "timer" ? (PomodoroService.initialTime - PomodoroService.remainingTime) : PomodoroService.remainingTime) / PomodoroService.initialTime % 1 percentage: (PomodoroService.state == "timer" ? (PomodoroService.initialTime - PomodoroService.remainingTime) : PomodoroService.remainingTime) / PomodoroService.initialTime % 1
} }
@ -60,10 +69,10 @@ StyledPanelWindow {
radius: 9999 radius: 9999
focus: root.visible focus: root.visible
text: PomodoroService.running ? Styling.lucide.icons.square : Styling.lucide.icons.play text: PomodoroService.running ? Icons.square : Icons.play
font.pixelSize: 48 font.pixelSize: 48
background: Item {} background: undefined
onClicked: { onClicked: {
PomodoroService.toggle(); PomodoroService.toggle();
} }

View file

@ -3,12 +3,14 @@ pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config import qs.config
import qs.services import qs.services
import qs.widgets
import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Wayland
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell.Io
import Quickshell.Widgets
StyledPanelWindow { StyledWindow {
id: root id: root
name: "powermenu" name: "powermenu"
@ -16,18 +18,27 @@ StyledPanelWindow {
implicitWidth: rect.width implicitWidth: rect.width
implicitHeight: rect.height implicitHeight: rect.height
onFocusedChanged: { WlrLayershell.layer: WlrLayer.Top
Visibility.powermenu = focused; WlrLayershell.keyboardFocus: root.visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
}
Process { Process {
id: process id: process
} }
WrapperItem { StyledWrapperRectangle {
id: rect id: rect
color: Theme.palette.base300
margin: 14 margin: 14
radius: 8
HyprlandFocusGrab {
active: Visibility.powermenu
windows: [root]
onCleared: {
Visibility.powermenu = false;
}
}
StyledListView { StyledListView {
id: list id: list
@ -58,7 +69,7 @@ StyledPanelWindow {
model: Config.powermenu.actions model: Config.powermenu.actions
spacing: Styling.layout.spacing.base spacing: 8
implicitWidth: 220 implicitWidth: 220
implicitHeight: 185 implicitHeight: 185
@ -67,7 +78,7 @@ StyledPanelWindow {
highlightResizeDuration: 0 highlightResizeDuration: 0
highlight: Rectangle { highlight: Rectangle {
radius: 8 radius: 8
color: Styling.theme.primary color: Theme.palette.primary
} }
onCurrentItemChanged: { onCurrentItemChanged: {
@ -94,9 +105,9 @@ StyledPanelWindow {
} }
padding: 16 padding: 16
color: list.currentIndex == index ? Styling.theme.primarycontent : Styling.theme.basecontent color: list.currentIndex == index ? Theme.palette.primarycontent : Theme.palette.basecontent
text: modelData.text text: modelData.text
font.pixelSize: Styling.typography.textSize.lg font.pixelSize: 18
font.bold: true font.bold: true
} }
} }

View file

@ -1,150 +0,0 @@
import qs.components
import qs.components.composite
import qs.config
import qs.services
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
GridLayout {
flow: GridLayout.TopToBottom
columns: 2
rows: 10
ColumnLayout {
StyledText {
text: "ToolTip"
font.pixelSize: 18
}
StyledPane {
Button {
id: toolTipButton
text: "Hello world!"
StyledToolTip {
visible: toolTipButton.hovered
text: qsTr("Save the active project")
}
}
}
}
ColumnLayout {
StyledText {
text: "ProgressBar"
font.pixelSize: 18
}
StyledPane {
StyledProgressBar {
id: progressBar
indeterminate: true
implicitHeight: 10
from: 0
to: 100
value: 50
}
}
}
ColumnLayout {
StyledText {
text: "ListView"
font.pixelSize: 18
}
StyledPane {
StyledWrapperRectangle {
border.color: Styling.theme.base100
border.width: 2
StyledListView {
implicitWidth: 200
implicitHeight: 100
model: 10
delegate: StyledText {
text: "Hello world!"
}
}
}
}
}
ColumnLayout {
StyledText {
text: "Mpris Player Selector"
font.pixelSize: 18
}
StyledPane {
MprisPlayerSelector {}
}
}
ColumnLayout {
StyledText {
text: "Mpris Controller"
font.pixelSize: 18
}
StyledPane {
MprisController {
player: Mpris.active ?? null
}
}
}
ColumnLayout {
StyledText {
text: "Drawer"
font.pixelSize: 18
}
StyledPane {
RowLayout {
Button {
text: "Top"
onClicked: {
drawer.x = root.width / 2 - drawer.width / 2;
drawer.y = 0;
drawer.edge = Qt.TopEdge;
drawer.open();
}
}
Button {
text: "Left"
onClicked: {
drawer.y = root.height / 2 - drawer.height / 2;
drawer.x = 0;
drawer.edge = Qt.LeftEdge;
drawer.open();
}
}
Button {
text: "Right"
onClicked: {
drawer.y = root.height / 2 - drawer.height / 2;
drawer.x = 0;
drawer.edge = Qt.RightEdge;
drawer.open();
}
}
Button {
text: "Bottom"
onClicked: {
drawer.x = root.width / 2 - drawer.width / 2;
drawer.y = 0;
drawer.edge = Qt.BottomEdge;
drawer.open();
}
}
}
}
}
StyledDrawer {
id: drawer
edge: Qt.TopEdge
width: 400
height: 200
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "Close"
onClicked: drawer.close()
}
}
}

View file

@ -1,53 +0,0 @@
import qs.components
import qs.config
import QtQuick
import QtQuick.Layouts
ColumnLayout {
spacing: Styling.layout.spacing.xl
ColumnLayout {
StyledText {
text: "Button"
font.pixelSize: 18
}
StyledPane {
StyledButton {
text: "Button"
}
}
}
ColumnLayout {
StyledText {
text: "Icon Button"
font.pixelSize: 18
}
StyledPane {
StyledIconButton {
text: Styling.lucide.icons.square
}
}
}
ColumnLayout {
StyledText {
text: "Slider"
font.pixelSize: 18
}
StyledPane {
StyledSlider {
id: slider
from: 0
to: 100
value: 50
}
}
}
component FieldElement: QtObject {
property string title
property Component component
}
}

View file

@ -1,22 +0,0 @@
import qs.components
import QtQuick
import QtQuick.Layouts
GridLayout {
flow: GridLayout.TopToBottom
columns: 2
rows: 10
ColumnLayout {
StyledText {
text: "Switch"
font.pixelSize: 18
}
StyledPane {
StyledSwitch {
text: "Enable"
}
}
}
}

View file

@ -2,72 +2,191 @@ pragma ComponentBehavior: Bound
import qs.components import qs.components
import qs.config import qs.config
import qs.constants
import qs.services import qs.services
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
StyledPanelWindow { StyledPopupWindow {
id: root id: root
name: "storybook"
visible: Visibility.storybook visible: Visibility.storybook
implicitWidth: 500
implicitHeight: 600
background.color: Styling.theme.base200 GridLayout {
id: grid
onFocusedChanged: { flow: GridLayout.TopToBottom
Visibility.storybook = focused; columns: 2
} rows: 10
StyledTabBar { StyledText {
id: tabs Layout.columnSpan: grid.columns
anchors.top: parent.top Layout.alignment: Qt.AlignHCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: view.top
implicitHeight: 40
StyledTabButton {
text: "Fields"
onClicked: tabs.setCurrentIndex(0)
}
StyledTabButton {
text: "Selectors"
onClicked: tabs.setCurrentIndex(1)
}
StyledTabButton {
text: "Components" text: "Components"
onClicked: tabs.setCurrentIndex(2) font.pixelSize: 24
font.bold: true
font.underline: true
bottomPadding: 24
}
ColumnLayout {
StyledText {
text: "Icon Button"
font.pixelSize: 18
}
StyledIconButton {
text: Icons.square
} }
} }
SwipeView { ColumnLayout {
id: view StyledText {
text: "Switch"
anchors.top: tabs.bottom font.pixelSize: 18
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
currentIndex: tabs.currentIndex
background: Rectangle {
color: Styling.theme.base100
radius: Styling.theme.radiusBox
} }
ScrollView { StyledSwitch {
padding: 36 text: "Enable"
Fields {}
} }
ScrollView {
padding: 36
Selectors {}
} }
ScrollView {
padding: 36 ColumnLayout {
Components {} StyledText {
text: "ToolTip"
font.pixelSize: 18
}
Button {
id: toolTipButton
text: "Hello world!"
StyledToolTip {
visible: toolTipButton.hovered
text: qsTr("Save the active project")
}
}
}
ColumnLayout {
StyledText {
text: "Slider"
font.pixelSize: 18
}
StyledSlider {
id: slider
from: 0
to: 100
value: 50
}
}
ColumnLayout {
StyledText {
text: "ProgressBar"
font.pixelSize: 18
}
StyledProgressBar {
id: progressBar
indeterminate: true
implicitHeight: 10
from: 0
to: 100
value: 50
}
}
ColumnLayout {
StyledText {
text: "ListView"
font.pixelSize: 18
}
StyledWrapperRectangle {
border.color: Theme.palette.base100
border.width: 2
StyledListView {
implicitWidth: 200
implicitHeight: 100
model: 10
delegate: StyledText {
text: "Hello world!"
}
}
}
}
ColumnLayout {
StyledText {
text: "Mpris Player Selector"
font.pixelSize: 18
}
MprisPlayerSelector {}
}
ColumnLayout {
StyledText {
text: "Mpris Controller"
font.pixelSize: 18
}
MprisController {
player: Mpris.active ?? null
}
}
ColumnLayout {
StyledText {
text: "Drawer"
font.pixelSize: 18
}
RowLayout {
Button {
text: "Top"
onClicked: {
drawer.x = root.width / 2 - drawer.width / 2;
drawer.y = 0;
drawer.edge = Qt.TopEdge;
drawer.open();
}
}
Button {
text: "Left"
onClicked: {
drawer.y = root.height / 2 - drawer.height / 2;
drawer.x = 0;
drawer.edge = Qt.LeftEdge;
drawer.open();
}
}
Button {
text: "Right"
onClicked: {
drawer.y = root.height / 2 - drawer.height / 2;
drawer.x = 0;
drawer.edge = Qt.RightEdge;
drawer.open();
}
}
Button {
text: "Bottom"
onClicked: {
drawer.x = root.width / 2 - drawer.width / 2;
drawer.y = 0;
drawer.edge = Qt.BottomEdge;
drawer.open();
}
}
}
}
}
StyledDrawer {
id: drawer
edge: Qt.TopEdge
width: 400
height: 200
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "Close"
onClicked: drawer.close()
} }
} }
} }

View file

@ -1,35 +0,0 @@
import qs.services
import QtQuick
import QtQuick.Controls
StackView {
id: stack
property url wallpaper: WallpaperService.currentWallpaper
anchors.fill: parent
replaceEnter: Transition {
OpacityAnimator {
from: 0.0
to: 1.0
duration: 1000
}
}
replaceExit: Transition {
PauseAnimation {
duration: 1100
}
}
Component {
id: img
Image {}
}
Component.onCompleted: stack.replace(img, {
"source": stack.wallpaper
})
onWallpaperChanged: stack.replace(img, {
"source": stack.wallpaper
})
}

View file

@ -1,7 +1,7 @@
pragma Singleton pragma Singleton
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Io
Singleton { Singleton {
@ -16,14 +16,16 @@ Singleton {
function toggle() { function toggle() {
if (properties.enabled) { if (properties.enabled) {
process.signal(888);
properties.enabled = false; properties.enabled = false;
} else { } else {
properties.enabled = true; properties.enabled = true;
} }
} }
IdleInhibitor { Process {
id: inhibitor id: process
enabled: properties.enabled running: properties.enabled
command: ["sh", "-c", "systemd-inhibit --what=idle --who=Caffeine --why='Caffeine module is active' --mode=block sleep inf"]
} }
} }

View file

@ -10,16 +10,13 @@ Singleton {
readonly property PwNode sink: Pipewire.defaultAudioSink readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property PwNode source: Pipewire.defaultAudioSource readonly property PwNode source: Pipewire.defaultAudioSource
readonly property list<PwNode> sinks: Pipewire.nodes.values.filter(node => node.audio != null && node.isSink && !node.isStream)
readonly property list<PwNode> sources: Pipewire.nodes.values.filter(node => node.audio != null && !node.isSink && !node.isStream)
readonly property bool muted: sink?.audio?.muted ?? false readonly property bool muted: sink?.audio?.muted ?? false
readonly property real volume: sink?.audio?.volume ?? 0 readonly property real volume: sink?.audio?.volume ?? 0
function setVolume(volume: real): void { function setVolume(volume: real): void {
if (sink?.ready && sink?.audio) { if (sink?.ready && sink?.audio) {
sink.audio.muted = false; sink.audio.muted = false;
sink.audio.volume = Math.min(volume, 1.0); sink.audio.volume = volume;
} }
} }
@ -45,18 +42,6 @@ Singleton {
sink.audio.muted = !sink.audio.muted; sink.audio.muted = !sink.audio.muted;
} }
function setSink(node: PwNode) {
if (node.audio != null && node.isSink && !node.isStream) {
Pipewire.preferredDefaultAudioSink = node;
}
}
function setSource(node: PwNode) {
if (node.audio != null && !node.isSink && !node.isStream) {
Pipewire.preferredDefaultAudioSource = node;
}
}
PwObjectTracker { PwObjectTracker {
objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource] objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource]
} }

View file

@ -1,19 +1,27 @@
pragma Singleton pragma Singleton
import qs.widgets
import Quickshell import Quickshell
Singleton { Singleton {
property alias configuration: properties.configuration
property alias dashboard: properties.dashboard property alias dashboard: properties.dashboard
property alias launcher: properties.launcher property alias launcher: properties.launcher
property alias pomodoro: properties.pomodoro property alias pomodoro: properties.pomodoro
property alias powermenu: properties.powermenu property alias powermenu: properties.powermenu
property alias storybook: properties.storybook property alias storybook: properties.storybook
property StyledPopupWindow activePopup
function togglePopup(popup: StyledPopupWindow) {
if (activePopup && popup != activePopup) {
activePopup.state = "";
}
popup.state = popup.state == "opened" ? "" : "opened";
activePopup = popup;
}
PersistentProperties { PersistentProperties {
id: properties id: properties
property bool configuration
property bool dashboard property bool dashboard
property bool launcher property bool launcher
property bool pomodoro property bool pomodoro

View file

@ -1,27 +0,0 @@
pragma Singleton
import qs.config
import Quickshell
import Quickshell.Io
Singleton {
id: root
property alias currentWallpaper: properties.currentWallpaper
FileView {
path: `${Paths.cache}/wallpaper.json`
watchChanges: true
onFileChanged: reload()
// when changes are made to properties in the adapter, save them
onAdapterUpdated: writeAdapter()
JsonAdapter {
id: properties
property url currentWallpaper: ""
}
}
}

View file

@ -1,43 +0,0 @@
import QtQuick
import Quickshell.Io
Item {
id: root
required property string path
property bool recursive: true
property list<string> fileFilter: []
signal created(path: string)
signal modified(path: string)
signal deleted(path: string)
signal movedFrom(path: string)
signal movedTo(path: string)
Process {
running: true
command: ["bash", "-c", `inotifywait -m ${root.recursive ? "-r" : ""} ${root.path} -e modify,move,create,delete --format "%e-%w%f"`]
stderr: StdioCollector {
onStreamFinished: console.error(`DirectoryWatcher: ${this.text}`)
}
stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
const [action, path] = data.split("-");
if (path.endsWith("~") || root.fileFilter.length > 0 && !root.fileFilter.some(filter => path.endsWith(filter))) {
return;
}
if (action.includes("CREATE")) {
root.created(path);
} else if (action.includes("MODIFY")) {
root.modified(path);
} else if (action.includes("DELETE")) {
root.deleted(path);
} else if (action.includes("MOVED_FROM")) {
root.movedFrom(path);
} else if (action.includes("MOVED_TO")) {
root.movedTo(path);
}
}
}
}
}

View file

@ -22,8 +22,8 @@ Item {
ShapePath { ShapePath {
id: fill id: fill
fillColor: Styling.theme.base100 fillColor: Theme.palette.base100
strokeColor: Styling.theme.base200 strokeColor: Theme.palette.base200
strokeWidth: 8 strokeWidth: 8
PathAngleArc { PathAngleArc {

View file

@ -0,0 +1,102 @@
import qs.services
import QtQuick
import Quickshell
import Quickshell.Widgets
PopupWindow {
id: root
property bool opened: false
property int animationDuration: 200
property alias margins: background.margin
property alias backgroundColor: background.color
property alias radius: background.radius
property alias state: background.state
required property Component content
color: "transparent"
function toggle() {
Visibility.togglePopup(this);
}
implicitWidth: background.width
Behavior on implicitWidth {
NumberAnimation {
duration: 100
}
}
implicitHeight: background.height
Behavior on implicitHeight {
NumberAnimation {
duration: 100
}
}
Timer {
id: timer
interval: 750
onTriggered: {
if (!root.visible) {
return;
}
root.toggle();
}
}
WrapperMouseArea {
hoverEnabled: true
onExited: {
timer.start();
}
onEntered: {
timer.stop();
}
WrapperRectangle {
id: background
opacity: 0
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
}
}
states: State {
name: "opened"
when: root.opened
PropertyChanges {
background {
opacity: 1
}
}
}
transitions: [
Transition {
from: ""
to: "opened"
ScriptAction {
script: root.visible = true
}
},
Transition {
from: "opened"
to: ""
SequentialAnimation {
PauseAnimation {
duration: root.animationDuration
}
ScriptAction {
script: root.visible = false
}
}
}
]
Loader {
active: root.visible
sourceComponent: root.content
}
}
}
}