Compare commits

..

No commits in common. "master" and "new" have entirely different histories.
master ... new

46 changed files with 1676 additions and 1046 deletions

View File

@ -1,18 +0,0 @@
//@ pragma UseQApplication
import Quickshell
import QtQuick
import qs.modules.Bar
import qs.modules.ipc
import qs.modules.wallpaper
import qs.modules.widgets.wallpicker
import qs.modules.notifications
ShellRoot {
id: root
Bar {}
Ipc {}
Wallpaper {}
WallPicker {}
Notification {}
}

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
Colors.qml
/settings/config.json
.qmlls.ini

View File

@ -1 +0,0 @@
/run/user/1000/quickshell/vfs/1b52f593d70d8ccef6fc467cf768a2ef/.qmlls.ini

View File

@ -1,26 +0,0 @@
pragma Singleton
import QtQuick
QtObject {
// --- The Backgrounds (Darkest to Lightest) ---
readonly property string base00: "#1e1e2e" // Default Background
readonly property string base01: "#181825" // Lighter Background (Status bars, panels)
readonly property string base02: "#313244" // Selection Background
readonly property string base03: "#45475a" // Comments, Invisibles, line highlighting
// --- The Foregrounds (Darkest to Lightest) ---
readonly property string base04: "#585b70" // Dark Foreground (Used for status bars)
readonly property string base05: "#cdd6f4" // Default Foreground, Caret
readonly property string base06: "#f5e0dc" // Light Foreground (Rarely used)
readonly property string base07: "#b4befe" // Lightest Foreground
// --- The Accent Colors ---
readonly property string base08: "#f38ba8" // Red (Variables, errors)
readonly property string base09: "#fab387" // Orange (Integers, booleans, constants)
readonly property string base0A: "#f9e2af" // Yellow (Classes, search text bg, warnings)
readonly property string base0B: "#a6e3a1" // Green (Strings, success states)
readonly property string base0C: "#94e2d5" // Cyan (Support, regex, escape chars)
readonly property string base0D: "#89b4fa" // Blue (Functions, methods, headings)
readonly property string base0E: "#cba6f7" // Purple/Mauve (Keywords, storage, selectors)
readonly property string base0F: "#f2cdcd" // Brown/Flamingo (Deprecated, embedded tags)
}

View File

@ -1,67 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import Quickshell.Widgets
import QtQuick.Layouts
import qs.settings
import qs
Variants {
model: Quickshell.screens
delegate: PanelWindow {
id: root
required property ShellScreen modelData
aboveWindows: true
screen: modelData
anchors {
top: true
left: true
right: true
}
margins {
top: Settings.config.floating ? Settings.config.margins : 0
left: Settings.config.floating ? Settings.config.margins : 0
right: Settings.config.floating ? Settings.config.margins : 0
}
implicitHeight: Settings.config.barHeight
color: "transparent"
Rectangle {
id: bar
anchors.fill: parent
radius: Settings.config.floating ? Settings.config.rounding * 2 : 0
color: Colors.base00
RowLayout {
id: left
spacing: Settings.config.barSpacing
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
Ws {
barScreen: root.modelData
}
MPris {}
Title {}
}
RowLayout {
id: center
spacing: Settings.config.barSpacing
anchors {
centerIn: parent
}
Clock {}
}
RowLayout {
id: right
spacing: Settings.config.barSpacing
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
rightMargin: Settings.config.floating ? Settings.config.barmargins : 10
}
StatusIcons {}
Tray {}
}
}
}
}

View File

@ -1,39 +0,0 @@
import Quickshell
import Quickshell.Widgets
import QtQuick
import Qt5Compat.GraphicalEffects
import qs.settings
import qs
import qs.widgets
WrapperRectangle {
id: root
margin: Settings.config.barmargins
layer {
enabled: true
effect: DropShadow {
color: "#111111"
radius: 4
verticalOffset: 2
horizontalOffset: 2
samples: 18
}
}
color: Colors.base02
radius: Settings.config.rounding
implicitWidth: clockText.implicitWidth + 20
implicitHeight: Settings.config.barHeight - margin * 2
SystemClock {
id: clock
precision: SystemClock.Minutes
}
child: Item {
id: textWrap
CText {
id: clockText
text: Qt.formatDateTime(clock.date, "hh:mm")
anchors.centerIn: parent
}
}
}

View File

@ -1,55 +0,0 @@
import Quickshell
import Quickshell.Services.Mpris
import Quickshell.Widgets
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Layouts
import qs
import qs.settings
import qs.widgets
WrapperRectangle {
id: root
margin: Settings.config.barmargins
layer {
enabled: true
effect: DropShadow {
color: "#111111"
radius: 4
verticalOffset: 2
horizontalOffset: 2
samples: 18
}
}
color: Colors.base02
implicitWidth: songLayout.implicitWidth + 20
implicitHeight: Settings.config.barHeight - margin * 2
radius: Settings.config.rounding
property var spotify: root.getSpotify()
visible: getSpotify() == null ? false : true
function getSpotify() {
for (var i = 0; i < Mpris.players.values.length; i++) {
if (Mpris.players.values[i].identity == "Spotify" || Mpris.players.values[i] == "spotify") {
return Mpris.players.values[i];
} else {
return null;
}
}
return null;
}
child: Item {
RowLayout {
id: songLayout
anchors.centerIn: parent
CText {
id: playingSong
Layout.maximumWidth: 400
text: root.spotify == null ? "" : root.spotify.trackTitle + " - " + root.spotify.trackArtist
elide: Text.ElideRight
}
}
}
}

View File

@ -1,80 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import Niri
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import qs
import qs.settings
import qs.widgets
Rectangle {
id: wsWrap
Niri {
id: niri
Component.onCompleted: connect()
onConnected: console.log("Connected to niri")
onErrorOccurred: function (error) {
console.error("Error:", error);
}
}
required property ShellScreen barScreen
color: "transparent"
radius: Settings.config.rounding
implicitWidth: wsLayout.implicitWidth + 6
implicitHeight: wsLayout.implicitHeight + 6
RowLayout {
id: wsLayout
spacing: 6
anchors.centerIn: parent
Repeater {
id: wsRep
model: niri.workspaces
delegate: Rectangle {
id: wsRect
layer {
enabled: true
effect: DropShadow {
color: Colors.base01
radius: 8
verticalOffset: 1
horizontalOffset: 1
samples: 18
}
}
implicitWidth: modelData.isFocused ? Settings.config.barHeight * 1.5 : Settings.config.barHeight / 2 + 10
implicitHeight: Settings.config.barHeight / 1.5
visible: modelData.id < 0 ? false : modelData.output == wsWrap.barScreen.name
required property var modelData
color: modelData.isFocused ? Colors.base0D : Colors.base02
radius: Settings.config.rounding
CText {
id: wsText
anchors.centerIn: parent
text: wsRect.modelData.index
color: parent.modelData.isFocused ? Colors.base01 : Colors.base07
opacity: parent.modelData.isFocused ? 1 : 0.5
}
Behavior on implicitWidth {
NumberAnimation {
easing {
type: Easing.OutBack
overshoot: 2
}
duration: 400
}
}
MouseArea {
id: mouseHandler
acceptedButtons: Qt.LeftButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
niri.focusWorkspace(wsRect.modelData.index);
}
}
}
}
}
}

View File

@ -1,103 +0,0 @@
import Quickshell.Services.UPower
import Quickshell.Services.Pipewire
import Quickshell.Widgets
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Layouts
import qs
import qs.settings
import qs.widgets
WrapperRectangle {
id: root
margin: Settings.config.barmargins
layer {
enabled: true
effect: DropShadow {
color: "#111111"
radius: 4
verticalOffset: 2
horizontalOffset: 2
samples: 18
}
}
color: Colors.base02
implicitWidth: iconLayout.implicitWidth + 14
implicitHeight: Settings.config.barHeight - margin * 2
radius: Settings.config.rounding
property var battery: UPower.displayDevice.isLaptopBattery ? UPower.displayDevice : null
property var percentage: UPower.displayDevice.isLaptopBattery ? UPower.displayDevice.percentage : null
property bool charging: UPower.displayDevice.isLaptopBattery ? UPower.displayDevice.state == UPowerDeviceState.Charging : null
property bool hasBattery: UPower.displayDevice.isLaptopBattery
property var audio: Pipewire.ready ? Pipewire.defaultAudioSink : ""
property var audioPercentage: Pipewire.ready ? Pipewire.defaultAudioSink.audio.volume : 0
property bool audioMute: Pipewire.ready ? Pipewire.defaultAudioSink.audio.muted : false
function getBatteryIcon() {
if (charging) {
return "\uf250";
}
if (percentage <= 0.12) {
return "\uf251";
}
if (percentage <= 0.24) {
return "\uf257";
}
if (percentage <= 0.36) {
return "\uf256";
}
if (percentage <= 0.48) {
return "\uf255";
}
if (percentage <= 0.60) {
return "\uf254";
}
if (percentage <= 0.72) {
return "\uf253";
}
if (percentage <= 0.84) {
return "\uf252";
}
if (percentage >= 0.84) {
return "\uf24f";
} else {
return "";
}
}
function getVolumeIcon() {
if (audioMute) {
return "\ue04f";
}
if (audioPercentage <= 0.33) {
return "\ue04e";
}
if (audioPercentage <= 0.66) {
return "\ue04d";
}
if (audioPercentage >= 0.66) {
return "\ue050";
} else {
return "";
}
}
child: Item {
RowLayout {
id: iconLayout
anchors.centerIn: parent
CIcon {
id: batteryIcon
Layout.leftMargin: 2
visible: root.hasBattery
text: root.getBatteryIcon()
}
CIcon {
id: volIcon
text: root.getVolumeIcon()
PwObjectTracker {
id: audioTracker
objects: Pipewire.ready ? Pipewire.defaultAudioSink : []
}
}
}
}
}

View File

@ -1,43 +0,0 @@
import Quickshell
import Qt5Compat.GraphicalEffects
import Quickshell.Wayland
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import qs
import qs.widgets
import qs.settings
WrapperRectangle {
id: root
margin: Settings.config.barmargins
layer {
enabled: true
effect: DropShadow {
color: "#111111"
radius: 4
verticalOffset: 2
horizontalOffset: 2
samples: 18
}
}
property var activeWindow: ToplevelManager.activeToplevel
property bool active: activeWindow ? activeWindow.activated ? true : false : false
radius: Settings.config.rounding
color: active ? Colors.base02 : "transparent"
implicitWidth: titleText.width + 40
implicitHeight: Settings.config.barHeight - margin * 2
child: Item {
RowLayout {
anchors.centerIn: parent
CText {
id: titleText
Layout.maximumWidth: 250
text: root.activeWindow ? root.activeWindow.activated ? root.activeWindow.title : "" : ""
elide: Text.ElideRight // Allows wrapping
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
}
}
Layout.alignment: Qt.AlignHCenter
}
}

View File

@ -1,39 +0,0 @@
import Quickshell.Services.SystemTray
import Quickshell.Widgets
import QtQuick
import Qt5Compat.GraphicalEffects
import qs
import qs.settings
import QtQuick.Layouts
WrapperRectangle {
id: root
margin: Settings.config.barmargins
layer {
enabled: true
effect: DropShadow {
color: "#111111"
radius: 4
verticalOffset: 2
horizontalOffset: 2
samples: 18
}
}
implicitWidth: trayRow.implicitWidth + 14
implicitHeight: Settings.config.barHeight - margin * 2
visible: trayRep.count > 0
color: Colors.base02
radius: Settings.config.rounding
child: Item {
RowLayout {
id: trayRow
anchors.centerIn: parent
Repeater {
id: trayRep
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
model: SystemTray.items
delegate: TrayItem {}
}
}
}
}

View File

@ -1,76 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Widgets
import Quickshell.Hyprland
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Layouts
import qs
import qs.settings
import qs.widgets
WrapperRectangle {
id: wsWrap
margin: Settings.config.barmargins
leftMargin: margin * 2
required property ShellScreen barScreen
color: "transparent"
radius: Settings.config.rounding
implicitWidth: wsLayout.implicitWidth + 6
implicitHeight: Settings.config.barHeight - margin * 2
child: Item {
RowLayout {
id: wsLayout
spacing: 6
anchors.centerIn: parent
Repeater {
id: wsRep
model: Hyprland.workspaces
delegate: Rectangle {
id: wsRect
layer {
enabled: true
effect: DropShadow {
color: "#111111"
radius: 0
verticalOffset: 2
horizontalOffset: 2
samples: 16
}
}
implicitWidth: modelData.focused ? Settings.config.barHeight * 1.5 : Settings.config.barHeight / 2 + 10
implicitHeight: Settings.config.barHeight - wsWrap.margin * 2
visible: modelData.id < 0 ? false : modelData.monitor?.name == wsWrap.barScreen.name
required property var modelData
color: modelData.focused ? Colors.base0D : Colors.base02
radius: Settings.config.rounding
CText {
id: wsText
anchors.centerIn: parent
text: wsRect.modelData.id
opacity: 1
color: parent.modelData.focused ? Colors.base00 : Colors.base05
}
Behavior on implicitWidth {
NumberAnimation {
easing {
type: Easing.OutBack
overshoot: 2
}
duration: 400
}
}
MouseArea {
id: mouseHandler
acceptedButtons: Qt.LeftButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
wsRect.modelData.activate();
}
}
}
}
}
}
}

69
modules/bar/Bar.qml Normal file
View File

@ -0,0 +1,69 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs
import qs.settings
Variants {
model: Quickshell.screens
delegate: PanelWindow {
id: root
required property var modelData
implicitHeight: Settings.config.barHeight
aboveWindows: true
screen: modelData
margins {
top: Settings.config.floating ? Settings.config.margins : 0
left: Settings.config.floating ? Settings.config.margins : 0
right: Settings.config.floating ? Settings.config.margins : 0
}
anchors {
top: true
left: true
right: true
}
color: "transparent"
Rectangle {
id: container
implicitHeight: Settings.config.barHeight
anchors.fill: parent
color: Qt.rgba(Colors.surface.r, Colors.surface.g, Colors.surface.b, Settings.config.translucency)
radius: Settings.config.floating ? Settings.config.barHeight / 2 : 0
RowLayout {
id: leftStuff
spacing: 10
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
Workspaces {
Layout.leftMargin: Settings.config.floating ? 5 : 20
property var screen: root.modelData
}
Title {}
Mpris {}
}
Row {
id: centerStuff
anchors.centerIn: parent
Clock {}
}
RowLayout {
id: rightStuff
spacing: 10
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
SysTray {}
StatusIcons {
margin: Layout.rightMargin
barWindow: root
barContainer: container
Layout.rightMargin: Settings.config.floating ? 5 : 20
}
}
}
}
}

39
modules/bar/Clock.qml Normal file
View File

@ -0,0 +1,39 @@
import Quickshell
import QtQuick
import QtQuick.Controls
import qs
import qs.settings
import qs.reusables
Rectangle {
id: container
radius: implicitHeight / 2
color: formatSwitch.containsMouse ? Colors.primaryContainer : Colors.surfaceContainer
anchors.verticalCenter: parent.verticalCenter
implicitHeight: Settings.config.barHeight - 10
implicitWidth: root.implicitWidth + 20
Item {
id: root
anchors.centerIn: parent
implicitWidth: clockText.implicitWidth
implicitHeight: Settings.config.barHeight
SystemClock {
id: clock
precision: SystemClock.Minutes
}
CustomText {
id: clockText
property bool showFull
anchors.centerIn: parent
text: showFull ? Qt.formatDateTime(clock.date, "hh:mm / dd.MM.yy") : Qt.formatDateTime(clock.date, "hh:mm")
}
}
MouseArea {
id: formatSwitch
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: clockText.showFull = !clockText.showFull
}
}

62
modules/bar/Mpris.qml Normal file
View File

@ -0,0 +1,62 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Services.Mpris
import qs
import qs.settings
import qs.reusables
Rectangle {
id: root
visible: root.spotify != null
radius: implicitHeight / 2
implicitHeight: Settings.config.barHeight - 10
color: clickHandler.containsMouse ? Colors.primaryContainer : Colors.surfaceContainer
implicitWidth: statusRow.width + 20
property var spotify: root.getSpotify()
function getSpotify() {
for (let i = 0; i < Mpris.players.values.length; i++) {
if (Mpris.players.values[i].identity.toLowerCase() === "spotify") {
return Mpris.players.values[i];
}
}
return null;
}
RowLayout {
id: statusRow
spacing: 5
anchors.centerIn: parent
property var combinedText: root.spotify != null ? root.spotify.trackArtist + " - " + root.spotify.trackTitle : ""
property var status: root.spotify != null ? !root.spotify.isPlaying ? "play_arrow" : "pause" : ""
CustomText {
id: mprisText
Layout.maximumWidth: 300
Layout.topMargin: 2
text: root.spotify != null ? parent.combinedText : ""
elide: Text.ElideRight
}
CustomIcon {
id: mprisStatus
Layout.topMargin: 2
text: root.spotify != null ? parent.status : ""
}
}
MouseArea {
id: clickHandler
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onDoubleClicked: mouse => {
if (mouse.button == Qt.LeftButton) {
root.spotify.next();
}
}
onClicked: mouse => {
if (mouse.button == Qt.RightButton) {
root.spotify.togglePlaying();
}
}
}
}

View File

@ -0,0 +1,28 @@
import QtQuick
import qs.modules.widgets.settingsapp
import qs.settings
import qs.reusables
import qs
Rectangle {
id: root
radius: implicitHeight / 2
color: pavuArea.containsMouse ? Colors.primaryContainer : Colors.surfaceContainer
implicitWidth: Settings.config.barHeight - 8
implicitHeight: Settings.config.barHeight - 10
CustomIcon {
id: volumeIcon
anchors.centerIn: parent
text: "settings"
}
MouseArea {
id: pavuArea
anchors.fill: root
onClicked: {
Settings.config.settingsShown = true;
}
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}

134
modules/bar/StatusIcons.qml Normal file
View File

@ -0,0 +1,134 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Io
import Quickshell.Services.Pipewire
import Quickshell.Networking
import Quickshell.Services.UPower
import QtQuick
import QtQuick.Layouts
import qs
import qs.reusables
import qs.settings
import "functions.js" as Fn
Rectangle {
id: root
implicitWidth: statusLayout.implicitWidth + 20
implicitHeight: Settings.config.barHeight - 10
color: Colors.surfaceContainer
radius: implicitHeight / 2
required property var barWindow
required property var barContainer
required property var margin
property var sink: Pipewire.defaultAudioSink
property var sinkReady: Pipewire.defaultAudioSink.ready
property var bat: UPower.displayDevice
property var perc: UPower.displayDevice.percentage
property var vol: Math.floor(Pipewire.defaultAudioSink.audio.volume * 100)
Process {
id: lowBat
running: false
command: ["sh", "-c", "notify-send", "'Low battery!'", "'Plug in your device!'"]
}
PopupWindow {
id: batPopup
property string popupText
implicitWidth: root.width + 5
implicitHeight: 30
anchor.window: root.barWindow
anchor.rect.y: root.barContainer.height + 5
anchor.rect.x: root.barContainer.width - root.width - root.margin
color: "transparent"
Rectangle {
anchors.fill: parent
color: Colors.surfaceContainer
border.width: 5
border.color: Colors.surface
radius: Settings.config.floating ? height / 2 : Settings.config.screenCornerRadius
CustomText {
anchors.centerIn: parent
text: batPopup.popupText
}
}
}
RowLayout {
id: statusLayout
anchors.centerIn: parent
spacing: 10
CustomIcon {
id: volumeIcon
Layout.alignment: Qt.AlignVCenter
PwObjectTracker {
objects: Pipewire.ready ? Pipewire.defaultAudioSink : []
}
text: Fn.getVolumeIcon()
MouseArea {
id: pavuArea
Process {
id: pavuLauncher
command: ["sh", "-c", "pavucontrol"]
}
anchors.fill: parent
onClicked: pavuLauncher.exec(pavuLauncher.command)
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
batPopup.visible = true;
batPopup.popupText = "Vol " + root.vol + "%";
}
onExited: {
batPopup.visible = false;
}
}
}
Repeater {
id: netRepeater
Layout.alignment: Qt.AlignVCenter
model: Networking.devices
delegate: CustomIcon {
id: netIcon
Layout.alignment: Qt.AlignVCenter
required property var modelData
text: Fn.getIcon(modelData)
}
}
CustomIcon {
id: batIcon
Layout.alignment: Qt.AlignVCenter
visible: UPower.displayDevice.isLaptopBattery
text: Fn.getBatteryIcon(root.perc)
MouseArea {
id: batHover
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onEntered: {
batPopup.visible = true;
batPopup.popupText = "Battery: " + Math.floor(UPower.displayDevice.percentage * 100) + "%";
}
onExited: {
batPopup.visible = false;
}
}
}
CustomIcon {
id: settingsIcon
text: "settings"
MouseArea {
id: settingsArea
anchors.fill: settingsIcon
onClicked: {
Settings.config.settingsShown = true;
}
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
}

25
modules/bar/SysTray.qml Normal file
View File

@ -0,0 +1,25 @@
import Quickshell.Services.SystemTray
import QtQuick
import qs.settings
import qs
Rectangle {
id: root
implicitWidth: trayRow.implicitWidth + 8
implicitHeight: Settings.config.barHeight - 10
radius: implicitHeight / 2
color: Colors.surfaceContainer
readonly property var count: trayRepeater.count
visible: trayRepeater.count > 0
Row {
id: trayRow
spacing: 5
anchors.centerIn: parent
Repeater {
id: trayRepeater
model: SystemTray.items
delegate: TrayItem {}
}
}
}

48
modules/bar/Title.qml Normal file
View File

@ -0,0 +1,48 @@
import QtQuick
import Quickshell.Wayland
import Quickshell.Widgets
import Quickshell
import qs
import qs.settings
import qs.reusables
import QtQuick.Layouts
Rectangle {
id: container
radius: implicitHeight / 2
color: Colors.surfaceContainer
implicitWidth: root.implicitWidth
implicitHeight: Settings.config.barHeight - 10
Item {
id: root
anchors.centerIn: parent
readonly property var activeWindow: ToplevelManager.activeToplevel
implicitWidth: titleLayout.implicitWidth
implicitHeight: titleText.implicitHeight
RowLayout {
id: titleLayout
anchors.centerIn: parent
anchors.fill: parent
spacing: 5
ClippingWrapperRectangle {
radius: 30
Layout.leftMargin: 10
IconImage {
id: icon
source: root.activeWindow ? root.activeWindow.activated ? Quickshell.iconPath(root.activeWindow.appId, "") : "" : ""
implicitSize: root.activeWindow ? root.activeWindow.activated ? 16 : 0 : 0
}
}
CustomText {
id: titleText
Layout.rightMargin: 10
Layout.maximumWidth: 300
text: root.activeWindow ? root.activeWindow.activated ? root.activeWindow.title : "Desktop" : "Desktop"
onTextChanged: {}
elide: Text.ElideRight
}
}
}
}

View File

@ -2,24 +2,16 @@ import QtQuick
import Quickshell import Quickshell
import Quickshell.Services.SystemTray import Quickshell.Services.SystemTray
import Quickshell.Widgets import Quickshell.Widgets
import qs
MouseArea { MouseArea {
id: root id: root
property var bar: root.QsWindow.window
required property SystemTrayItem modelData required property SystemTrayItem modelData
implicitWidth: trayIcon.implicitWidth
implicitHeight: trayIcon.implicitHeight
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
IconImage { implicitWidth: 16
id: trayIcon implicitHeight: 16
implicitSize: 16
source: parent.modelData.icon
}
QsMenuAnchor {
id: menu
menu: root.modelData.hasMenu ? root.modelData.menu : null
anchor.item: root
}
onClicked: event => { onClicked: event => {
if (event.button === Qt.LeftButton) { if (event.button === Qt.LeftButton) {
modelData.activate(); modelData.activate();
@ -27,4 +19,18 @@ MouseArea {
menu.open(); menu.open();
} }
} }
QsMenuAnchor {
id: menu
menu: root.modelData.hasMenu ? root.modelData.menu : null
anchor.item: root
}
IconImage {
id: trayIcon
width: parent.implicitWidth
height: parent.implicitHeight
source: root.modelData.icon
anchors.centerIn: parent
}
} }

View File

@ -0,0 +1,70 @@
pragma ComponentBehavior: Bound
import Quickshell.Hyprland
import QtQuick
import QtQuick.Layouts
import qs
import qs.settings
import qs.reusables
Rectangle {
id: root
color: Colors.surfaceContainer
implicitWidth: workspaceRow.implicitWidth + 10
implicitHeight: Settings.config.barHeight - 10
radius: Settings.config.barHeight / 2
property var screen: screen
Row {
id: workspaceRow
anchors.centerIn: parent
spacing: 0 // Slightly increase spacing between workspace buttons
Repeater {
id: wsRepeater
model: Hyprland.workspaces
Rectangle {
id: workspaceNumber
radius: 20
property bool isOnMon: {
if (!modelData)
return false;
if (!modelData.monitor)
return false;
if (!root.screen)
return false;
return modelData.monitor.name === root.screen.name;
}
required property var modelData
width: 20
height: Settings.config.barHeight - 10
color: "transparent"
Behavior on width {
NumberAnimation {
duration: 200
easing.type: Easing.Linear
}
}
CustomText {
anchors.centerIn: workspaceNumber
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: parent.modelData.id
opacity: workspaceNumber.modelData.focused ? 1 : 0.5
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onClicked: {
parent.modelData.activate();
}
}
}
}
}
}

91
modules/bar/functions.js Normal file
View File

@ -0,0 +1,91 @@
function getIcon(device) {
if (device.type === DeviceType.Wifi) {
for (var i = 0; i < device.networks.values.length; i++) {
var net = device.networks.values[i];
if (net.connected) {
if (net.signalStrength <= 0.20)
return "\uf0b0";
//signa_wifi_0_bar
if (net.signalStrength <= 0.40)
return "\uebe4";
//network_wifi_1_bar
if (net.signalStrength <= 0.60)
return "\uebd6";
//network_wifi_2_bar
if (net.signalStrength <= 0.80)
return "\uebe1";
//network_wifi_3_bar
if (net.signalStrength >= 0.80)
return "\ue1d8";
// signal_wifi_4_bar
}
}
return "\ue1da";
} else if (device.connected) {
return "settings_ethernet";
}
return "\ue1da";
// signal_wifi_off
}
function getBatteryIcon(perc) {
if (UPower.displayDevice.state == UPowerDeviceState.Charging) {
return "battery_android_frame_bolt";
}
if (perc <= 0.16) {
lowBat.running = true;
return "battery_android_alert";
}
if (perc < 0.32) {
return "battery_android_frame_2";
}
if (perc < 0.48) {
return "battery_android_frame_3";
}
if (perc < 0.74) {
return "battery_android_frame_4";
}
if (perc < 0.9) {
return "battery_android_frame_5";
}
if (perc > 0.9) {
return "battery_android_frame_full";
}
}
function getStatus(device) {
if (device.type === DeviceType.Wifi) {
for (var i = 0; i < device.networks.values.length; i++) {
var net = device.networks.values[i];
if (net.connected) {
return net.name;
}
}
return "Disconnected";
}
return device.connected ? "Connected" : "Disconnected";
}
// pipewire function
function getVolumeIcon() {
// Safety check: if Pipewire is dead or sink is missing
if (!sink)
return "volume_off";
// If muted, show the hush icon
if (sink.audio.muted)
return "volume_off";
// Volume is usually 0.0 to 1.0 (0% to 100%)
const vol = sink.audio.volume;
if (vol <= 0.25)
return "volume_mute";
if (vol < 0.75)
return "volume_down";
if (vol <= 1.00)
return "volume_up";
// If it's loud, prepare the ears!
return "volume_up";
}

View File

@ -1,14 +1,27 @@
import Quickshell
import QtQuick import QtQuick
import Quickshell.Io import Quickshell.Io
import qs
import qs.settings import qs.settings
import QtQuick.Dialogs
import Quickshell
Item { Item {
FontDialog {
id: fontPicker
flags: FontDialog.NoButtons
}
IpcHandler { IpcHandler {
id: ipcHandler
target: "settings" target: "settings"
function toggleWall() { function setFont(newFont: string): void {
Settings.config.wallswitchershown = !Settings.config.wallswitchershown; Settings.config.font = newFont;
}
function gen(toggle: bool): void {
Settings.config.generateScheme = toggle;
}
function reload(hard: bool): void {
Quickshell.reload(hard);
console.log("reloaded!");
} }
} }
} }

View File

@ -26,7 +26,7 @@ Variants {
item: notifList item: notifList
} }
implicitHeight: notifList.contentHeight + 20 implicitHeight: notifList.contentHeight + 20
implicitWidth: modelData.width / 6 implicitWidth: modelData.width / 8
layer: WlrLayer.Overlay layer: WlrLayer.Overlay
exclusionMode: ExclusionMode.Ignore exclusionMode: ExclusionMode.Ignore

View File

@ -2,88 +2,73 @@ import QtQuick
import qs.settings import qs.settings
import QtQuick.Layouts import QtQuick.Layouts
import qs import qs
import Quickshell import qs.modules.bar
import qs.widgets import qs.reusables
import Quickshell.Widgets import Quickshell.Widgets
Rectangle { Rectangle {
id: notifyItem id: notifyItem
required property var modelData required property var modelData
implicitWidth: ListView.view ? ListView.view.width : 500 implicitWidth: ListView.view ? ListView.view.width : 300
implicitHeight: fullLayout.implicitHeight + 40 implicitHeight: fullLayout.implicitHeight + 20
color: dismissArea.containsMouse ? Colors.base02 : Colors.base00 color: dismissArea.containsMouse ? Colors.primaryContainer : Colors.surfaceContainer
radius: Settings.config.rounding radius: 22
border.width: 2 border.width: 1
border.color: Colors.base0D border.color: Colors.outline
Timer { Timer {
id: dismissTimer id: dismissTimer
interval: 5000 interval: 5000
running: true running: true
onTriggered: notifyItem.modelData.expire() onTriggered: {
parent.modelData.expire();
} }
}
RowLayout { RowLayout {
id: fullLayout id: fullLayout
anchors.margins: 20 anchors.margins: 10
anchors.fill: parent anchors.fill: parent
spacing: 10 spacing: 10
ClippingWrapperRectangle {
id: notiIcon
radius: notifyItem.radius - notifyItem.radius / 3
implicitWidth: 64
color: "transparent"
implicitHeight: 64
visible: notifyItem.modelData.image !== ""
IconImage {
source: notifyItem.modelData.image
visible: notifyItem.modelData.image !== ""
implicitSize: 64
asynchronous: true
}
}
ColumnLayout { ColumnLayout {
id: textLayout id: textLayout
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
spacing: 0 spacing: 2
// New RowLayout to hold the Icon and App Name together CustomText {
RowLayout {
id: iconTextLayout
spacing: 8
ClippingWrapperRectangle {
id: notiIconWrapper
radius: notifyItem.radius - notifyItem.radius / 3
implicitWidth: notiIcon.implicitSize
implicitHeight: notiIcon.implicitSize
color: "transparent"
child: IconImage {
id: notiIcon
// Keep your existing source logic
source: notifyItem.modelData.image !== "" ? notifyItem.modelData.image : Quickshell.iconPath("/usr/share/icons/Papirus/24x24/panel/notifications.svg")
implicitSize: 22 // Slightly smaller to match text height
asynchronous: true
}
}
CText {
id: appName
text: notifyItem.modelData.appName
opacity: 0.5
font.pixelSize: 10
}
}
ColumnLayout {
spacing: 0
Layout.alignment: Qt.AlignHCenter
Layout.leftMargin: notiIcon.implicitWidth + iconTextLayout.spacing
CText {
id: summary id: summary
text: notifyItem.modelData.summary text: notifyItem.modelData.summary
font.bold: true font.bold: true
elide: Text.ElideRight elide: Text.ElideRight
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 5 onTextChanged: {
dismissTimer.restart();
}
} }
CText { CustomText {
text: notifyItem.modelData.body text: notifyItem.modelData.body
font.pixelSize: Settings.config.fontSize - 2 font.pixelSize: Settings.config.fontSize - 2
maximumLineCount: 1 maximumLineCount: 2
opacity: 0.3
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
elide: Text.ElideRight elide: Text.ElideRight
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
}
} }
} }
} }

View File

@ -0,0 +1,224 @@
import QtQuick
import QtQuick.Shapes
import qs
import qs.settings
/**
* ScreenCorners - Shape component for rendering screen corners
*
* Renders concave corners at the screen edges to create a rounded screen effect.
* Self-contained Shape component (no shadows).
*/
Item {
id: root
anchors.fill: parent
// Wrapper with layer caching to reduce GPU tessellation overhead
Item {
anchors.fill: parent
// Cache the Shape to a texture to prevent continuous re-tessellation
layer.enabled: true
Shape {
id: cornersShape
anchors.fill: parent
preferredRendererType: Shape.CurveRenderer
enabled: false // Disable mouse input
ShapePath {
id: cornersPath
// Corner configuration
readonly property color cornerColor: Settings.config.floating ? "black" : Qt.rgba(Colors.surface.r, Colors.surface.g, Colors.surface.b, Settings.config.translucency)
readonly property real cornerRadius: Settings.config.screenCornerRadius
readonly property real cornerSize: Settings.config.screenCornerRadius
// Determine margins based on bar position
readonly property real topMargin: Settings.config.floating ? 0 : Settings.config.barHeight
readonly property real bottomMargin: 0
readonly property real leftMargin: 0
readonly property real rightMargin: 0
// Screen dimensions
readonly property real screenWidth: cornersShape.width
readonly property real screenHeight: cornersShape.height
// Only show screen corners if enabled and appropriate conditions are met
readonly property bool shouldShow: Settings.config.showScreenCorners
// ShapePath configuration
strokeWidth: -1 // No stroke, fill only
fillColor: shouldShow ? cornerColor : "transparent"
// Smooth color animation (disabled during theme transitions to sync with Color.qml)
// ========== PATH DEFINITION ==========
// Draws 4 separate corner squares at screen edges
// Each corner square has a concave arc on the inner diagonal
// ========== TOP-LEFT CORNER ==========
// Arc is at the bottom-right of this square (inner diagonal)
// Start at top-left screen corner
startX: leftMargin
startY: topMargin
// Top edge (moving right)
PathLine {
relativeX: cornersPath.cornerSize
relativeY: 0
}
// Right edge (moving down toward arc)
PathLine {
relativeX: 0
relativeY: cornersPath.cornerSize - cornersPath.cornerRadius
}
// Concave arc (bottom-right corner of square, curving inward toward screen center)
PathArc {
relativeX: -cornersPath.cornerRadius
relativeY: cornersPath.cornerRadius
radiusX: cornersPath.cornerRadius
radiusY: cornersPath.cornerRadius
direction: PathArc.Counterclockwise
}
// Bottom edge (moving left)
PathLine {
relativeX: -(cornersPath.cornerSize - cornersPath.cornerRadius)
relativeY: 0
}
// Left edge (moving up) - closes back to start
PathLine {
relativeX: 0
relativeY: -cornersPath.cornerSize
}
// ========== TOP-RIGHT CORNER ==========
// Arc is at the bottom-left of this square (inner diagonal)
PathMove {
x: cornersPath.screenWidth - cornersPath.rightMargin - cornersPath.cornerSize
y: cornersPath.topMargin
}
// Top edge (moving right)
PathLine {
relativeX: cornersPath.cornerSize
relativeY: 0
}
// Right edge (moving down)
PathLine {
relativeX: 0
relativeY: cornersPath.cornerSize
}
// Bottom edge (moving left toward arc)
PathLine {
relativeX: -(cornersPath.cornerSize - cornersPath.cornerRadius)
relativeY: 0
}
// Concave arc (bottom-left corner of square, curving inward toward screen center)
PathArc {
relativeX: -cornersPath.cornerRadius
relativeY: -cornersPath.cornerRadius
radiusX: cornersPath.cornerRadius
radiusY: cornersPath.cornerRadius
direction: PathArc.Counterclockwise
}
// Left edge (moving up) - closes back to start
PathLine {
relativeX: 0
relativeY: -(cornersPath.cornerSize - cornersPath.cornerRadius)
}
// ========== BOTTOM-LEFT CORNER ==========
// Arc is at the top-right of this square (inner diagonal)
PathMove {
x: cornersPath.leftMargin
y: cornersPath.screenHeight - cornersPath.bottomMargin - cornersPath.cornerSize
}
// Top edge (moving right toward arc)
PathLine {
relativeX: cornersPath.cornerSize - cornersPath.cornerRadius
relativeY: 0
}
// Concave arc (top-right corner of square, curving inward toward screen center)
PathArc {
relativeX: cornersPath.cornerRadius
relativeY: cornersPath.cornerRadius
radiusX: cornersPath.cornerRadius
radiusY: cornersPath.cornerRadius
direction: PathArc.Counterclockwise
}
// Right edge (moving down)
PathLine {
relativeX: 0
relativeY: cornersPath.cornerSize - cornersPath.cornerRadius
}
// Bottom edge (moving left)
PathLine {
relativeX: -cornersPath.cornerSize
relativeY: 0
}
// Left edge (moving up) - closes back to start
PathLine {
relativeX: 0
relativeY: -cornersPath.cornerSize
}
// ========== BOTTOM-RIGHT CORNER ==========
// Arc is at the top-left of this square (inner diagonal)
// Start at bottom-right of square (different from other corners!)
PathMove {
x: cornersPath.screenWidth - cornersPath.rightMargin
y: cornersPath.screenHeight - cornersPath.bottomMargin
}
// Bottom edge (moving left)
PathLine {
relativeX: -cornersPath.cornerSize
relativeY: 0
}
// Left edge (moving up toward arc)
PathLine {
relativeX: 0
relativeY: -(cornersPath.cornerSize - cornersPath.cornerRadius)
}
// Concave arc (top-left corner of square, curving inward toward screen center)
PathArc {
relativeX: cornersPath.cornerRadius
relativeY: -cornersPath.cornerRadius
radiusX: cornersPath.cornerRadius
radiusY: cornersPath.cornerRadius
direction: PathArc.Counterclockwise
}
// Top edge (moving right)
PathLine {
relativeX: cornersPath.cornerSize - cornersPath.cornerRadius
relativeY: 0
}
// Right edge (moving down) - closes back to start
PathLine {
relativeX: 0
relativeY: cornersPath.cornerSize
}
}
}
}
}

View File

@ -0,0 +1,32 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import Quickshell.Wayland
import qs.settings
Variants {
id: root
model: Quickshell.screens
delegate: WlrLayershell {
id: wpShell
aboveWindows: false
exclusionMode: ExclusionMode.Ignore
required property var modelData
screen: modelData
anchors {
left: true
top: true
bottom: true
right: true
}
layer: WlrLayer.Background
Image {
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: Settings.config.currentWall
}
ScreenCorners {}
}
}

View File

@ -1,51 +0,0 @@
import Quickshell
import Quickshell.Widgets
import QtQuick
import Qt5Compat.GraphicalEffects
import Quickshell.Io
import qs
import qs.settings
import qs.widgets
Item {
id: root
property int gaps: 10
implicitWidth: wrapper.width + gaps
implicitHeight: wrapper.height + gaps
ClippingWrapperRectangle {
id: wrapper
layer {
enabled: true
effect: DropShadow {
color: "#111111"
horizontalOffset: 7
verticalOffset: 8
radius: 12
samples: 14
}
}
SystemClock {
id: clock
precision: SystemClock.Minutes
}
color: Colors.base01
radius: Settings.config.rounding
anchors.centerIn: parent
margin: 10
rightMargin: 15
child: Column {
id: dataLayout
spacing: 0
anchors.margins: 0
CText {
text: Qt.formatDateTime(clock.date, "hh:mm")
font.pixelSize: 48
}
CText {
text: Qt.formatDateTime(clock.date, "dd.MM.yy")
opacity: 0.6
font.pixelSize: 24
}
}
}
}

View File

@ -1,218 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell.Services.Mpris
import Quickshell.Widgets
import QtQuick
import Qt5Compat.GraphicalEffects
import QtQuick.Layouts
import QtQuick.Controls
import qs
import qs.settings
import qs.widgets
Rectangle {
id: root
color: Colors.base00
radius: Settings.config.rounding
implicitWidth: 600
implicitHeight: 200
visible: getSpotify() != null
layer {
enabled: true
effect: DropShadow {
color: "#111111"
horizontalOffset: 7
verticalOffset: 8
radius: 12
samples: 14
}
}
MouseArea {
id: hoverDetect
hoverEnabled: true
anchors.fill: parent
onExited: title.x = 0
}
function getSpotify() {
for (var i = 0; i < Mpris.players.values.length; i++) {
if (Mpris.players.values[i].identity == "Spotify" || Mpris.players.values[i] == "spotify") {
return Mpris.players.values[i];
} else {
return null;
}
}
return null;
}
property var spotify: getSpotify() != null ? getSpotify() : null
property var title: getSpotify() != null ? getSpotify().trackTitle : ""
property var album: getSpotify() != null ? getSpotify().trackAlbum : ""
property var art: getSpotify() != null ? getSpotify().trackArtUrl : ""
property var artist: getSpotify() != null ? getSpotify().trackArtist : ""
ClippingWrapperRectangle {
id: songWrapper
radius: Settings.config.rounding / 1.5
anchors.margins: 8
margin: 0
anchors.fill: parent
color: Colors.base00
RowLayout {
id: songLayout
spacing: 10
ClippingWrapperRectangle {
id: coverRounder
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: songCover.sourceSize.width
radius: Settings.config.rounding / 1.5
Image {
id: songCover
source: root.art
sourceSize {
width: 180
height: 180
}
}
}
WrapperRectangle {
color: Colors.base01
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignTop
radius: Settings.config.rounding / 1.5
margin: 20
child: ColumnLayout {
id: songInfo
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 20
Layout.topMargin: 2
Item {
id: titleContainer
Layout.fillWidth: true
x: 0
implicitHeight: title.implicitHeight
clip: true // Keeps the text inside this "window"
CText {
id: title
Layout.maximumWidth: 300
text: root.title
font.pixelSize: 30
SequentialAnimation on x {
id: scrollAnimation
running: hoverDetect.containsMouse && title.width > titleContainer.width
loops: Animation.Infinite
// Scroll to the end
NumberAnimation {
to: titleContainer.width - title.width
duration: Math.max(2000, (title.width - titleContainer.width) * 30)
easing.type: Easing.InOutQuad
}
// Scroll back to the start
NumberAnimation {
to: 0
duration: Math.max(2000, (title.width - titleContainer.width) * 30)
easing.type: Easing.InOutQuad
}
}
}
}
CText {
id: album
text: root.album + " - " + root.artist
opacity: 0.6
Layout.maximumWidth: 250
Layout.alignment: Qt.AlignTop
elide: Text.ElideRight
}
ProgressBar {
id: songProgress
FrameAnimation {
// only emit the signal when the position is actually changing.
running: root.spotify ? root.spotify.playbackState == MprisPlaybackState.Playing || root.visible : false
// emit the positionChanged signal every frame.
onTriggered: root.spotify.positionChanged()
}
implicitWidth: 200
implicitHeight: 10
from: 0
to: root.spotify != null ? root.spotify.length : 0
value: root.spotify != null ? root.spotify.position : 0
background: Rectangle {
implicitWidth: 200
implicitHeight: 6
color: Colors.base02
radius: Settings.config.rounding
}
contentItem: Item {
implicitWidth: 200
implicitHeight: 4
// Progress indicator for determinate state.
Rectangle {
width: songProgress.visualPosition * parent.width
height: parent.height
radius: Settings.config.rounding
color: Colors.base07
visible: !songProgress.indeterminate
}
}
}
RowLayout {
id: playerControls
Layout.maximumWidth: 200
CIcon {
id: previous
text: "\ue045"
Layout.alignment: Qt.AlignLeft
MouseArea {
id: prevHandler
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onClicked: {
root.spotify.previous();
title.x = 0;
}
}
}
CIcon {
id: pause
text: root.spotify ? root.spotify.isPlaying ? "\ue034" : "\ue037" : ""
Layout.alignment: Qt.AlignHCenter
MouseArea {
id: pauseHandler
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onClicked: {
root.spotify.togglePlaying();
title.x = 0;
}
}
}
CIcon {
id: next
Layout.alignment: Qt.AlignRight
text: "\ue044"
MouseArea {
id: nextHandler
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onClicked: {
root.spotify.next();
title.x = 0;
}
}
}
}
}
}
}
}
}

View File

@ -1,42 +0,0 @@
import Quickshell
import QtQuick
import qs
import qs.settings
import Quickshell.Wayland
Variants {
model: Quickshell.screens
delegate: WlrLayershell {
id: wallpaperShell
exclusionMode: ExclusionMode.Ignore
aboveWindows: false
required property ShellScreen modelData
layer: WlrLayer.Background
anchors {
top: true
bottom: true
left: true
right: true
}
Image {
id: wallpaper
source: Settings.config.currentWall ? Settings.config.currentWall : ""
anchors.fill: parent
}
DesktopClock {
anchors {
bottom: parent.bottom
right: parent.right
margins: 25
}
}
PlayerWidget {
anchors {
top: parent.top
left: parent.left
margins: 35
topMargin: Settings.config.barHeight + 35 + (Settings.config.floating ? Settings.config.margins : 0)
}
}
}
}

View File

@ -0,0 +1,288 @@
import QtQuick
import qs
import qs.reusables
import QtQuick.Layouts
import qs.settings
import Quickshell.Widgets
import QtQuick.Dialogs
ClippingWrapperRectangle {
id: root
FontDialog {
id: fontPicker
title: "qs-fontpicker"
flags: FontDialog.NoButtons | FontDialog.MonospacedFonts
onAccepted: {
Settings.config.font = selectedFont.family;
Settings.config.fontSize = selectedFont.pointSize;
Settings.config.fontWeight = selectedFont.weight;
}
onSelectedFontChanged: {
Settings.config.font = selectedFont.family;
Settings.config.fontSize = selectedFont.pointSize;
Settings.config.fontWeight = selectedFont.weight;
}
}
anchors.centerIn: parent
clip: true
color: Colors.surfaceContainerHigh
border.color: Colors.primary
border.width: 1
radius: 12
margin: 20
ColumnLayout {
id: verticalLayout
anchors.margins: 20
anchors.fill: parent
ClippingWrapperRectangle {
id: fontWrapper
Layout.fillWidth: true
leftMargin: 10
rightMargin: 15
implicitWidth: parent.implicitWidth - 20
topLeftRadius: 12
topRightRadius: 12
bottomRightRadius: 4
bottomLeftRadius: 4
color: Colors.primaryContainer
implicitHeight: 30
child: RowLayout {
id: fontLayout
spacing: 40
CustomText {
id: fontText
text: "Current font"
Layout.fillWidth: true
}
CustomButton {
id: fontPickerButton
customText: Settings.config.font
implicitHeight: fontWrapper.implicitHeight - 10
onClicked: fontPicker.open()
}
}
}
ClippingWrapperRectangle {
id: floatingWrapper
Layout.fillWidth: true
leftMargin: 10
rightMargin: 20
implicitHeight: 30
bottomLeftRadius: 4
bottomRightRadius: 4
topRightRadius: 4
topLeftRadius: 4
color: Colors.primaryContainer
child: RowLayout {
id: floatingToggleLayout
spacing: 40
CustomText {
id: floatingToggleText
text: "Top bar floating"
Layout.fillWidth: true
}
CustomSwitch {
checked: Settings.config.floating
implicitHeight: floatingWrapper.implicitHeight - 10
implicitWidth: 34
onClicked: {
Settings.config.floating = checked;
}
}
}
}
ClippingWrapperRectangle {
id: screenCornerWrapper
Layout.fillWidth: true
leftMargin: 10
rightMargin: 20
implicitHeight: 30
bottomLeftRadius: 4
bottomRightRadius: 4
topRightRadius: 4
topLeftRadius: 4
color: Colors.primaryContainer
child: RowLayout {
id: screenCornerLayout
spacing: 40
CustomText {
id: screenCornerText
text: "Show screen corners"
Layout.fillWidth: true
}
CustomSwitch {
checked: Settings.config.showScreenCorners
implicitHeight: floatingWrapper.implicitHeight - 10
implicitWidth: 34
onClicked: {
Settings.config.showScreenCorners = checked;
}
}
}
}
ClippingWrapperRectangle {
id: screenCornerRadiusWrapper
Layout.fillWidth: true
leftMargin: 10
rightMargin: 15
implicitHeight: 30
bottomLeftRadius: 4
bottomRightRadius: 4
topRightRadius: 4
topLeftRadius: 4
color: Colors.primaryContainer
child: RowLayout {
id: screenCornerRadiusLayout
spacing: 5
CustomText {
id: screenCornerRadiusText
text: "Screen corner radius"
Layout.fillWidth: true
}
CustomButton {
implicitHeight: screenCornerRadiusWrapper.implicitHeight - 10
implicitWidth: 20
onClicked: {
Settings.config.screenCornerRadius = Settings.config.screenCornerRadius + 1;
}
customText: "+"
Layout.rightMargin: 0
}
CustomText {
text: Settings.config.screenCornerRadius
}
CustomButton {
implicitHeight: barHeightWrapper.implicitHeight - 10
implicitWidth: 20
onClicked: {
Settings.config.screenCornerRadius = Settings.config.screenCornerRadius - 1;
}
customText: "-"
Layout.rightMargin: 0
}
}
}
ClippingWrapperRectangle {
id: barHeightWrapper
Layout.fillWidth: true
leftMargin: 10
rightMargin: 15
implicitHeight: 30
bottomLeftRadius: 4
bottomRightRadius: 4
topRightRadius: 4
topLeftRadius: 4
color: Colors.primaryContainer
child: RowLayout {
id: barControlsLayout
spacing: 5
CustomText {
id: barControlsText
text: "Bar height"
Layout.fillWidth: true
}
CustomButton {
implicitHeight: barHeightWrapper.implicitHeight - 10
implicitWidth: 20
onClicked: {
Settings.config.barHeight = Settings.config.barHeight + 1;
}
customText: "+"
Layout.rightMargin: 0
}
CustomText {
text: Settings.config.barHeight
}
CustomButton {
implicitHeight: barHeightWrapper.implicitHeight - 10
implicitWidth: 20
onClicked: {
Settings.config.barHeight = Settings.config.barHeight - 1;
}
customText: "-"
Layout.rightMargin: 0
}
}
}
ClippingWrapperRectangle {
id: barMarginWrapper
Layout.fillWidth: true
leftMargin: 10
rightMargin: 15
implicitHeight: 30
bottomLeftRadius: 4
bottomRightRadius: 4
topRightRadius: 4
topLeftRadius: 4
color: Colors.primaryContainer
child: RowLayout {
id: barMarginLayout
spacing: 5
CustomText {
id: barMarginText
text: "Bar margins"
Layout.fillWidth: true
}
CustomButton {
implicitHeight: barMarginWrapper.implicitHeight - 10
implicitWidth: 20
onClicked: {
Settings.config.margins = Settings.config.margins + 1;
}
customText: "+"
}
CustomText {
text: Settings.config.margins
}
CustomButton {
implicitHeight: barHeightWrapper.implicitHeight - 10
implicitWidth: 20
onClicked: {
Settings.config.margins = Settings.config.margins - 1;
}
customText: "-"
}
}
}
ClippingWrapperRectangle {
id: translucencyWrapper
leftMargin: 10
rightMargin: 10
implicitHeight: 30
bottomLeftRadius: 12
bottomRightRadius: 12
topRightRadius: 4
topLeftRadius: 4
color: Colors.primaryContainer
Layout.fillWidth: true
RowLayout {
id: translucencyLayout
CustomText {
id: translucencyText
text: "Bar trasnlucency"
Layout.fillWidth: true
}
CustomSlider {
from: 0.0
to: 1.0
value: Settings.config.translucency
implicitWidth: 100
implicitHeight: translucencyWrapper.implicitHeight - 10
onMoved: {
Settings.config.translucency = position;
}
}
}
}
Item {
id: spring
Layout.fillHeight: true
}
}
}

View File

@ -0,0 +1,62 @@
import qs.reusables
import qs.settings
import QtQuick
import QtQuick.Layouts
import qs
import QtQuick.Dialogs
Rectangle {
id: fontButton
FontDialog {
id: fontPicker
title: "qs-fontpicker"
flags: FontDialog.NoButtons | FontDialog.MonospacedFonts
onAccepted: {
Settings.config.font = selectedFont.family;
Settings.config.fontSize = selectedFont.pointSize;
}
onSelectedFontChanged: {
Settings.config.font = selectedFont.family;
Settings.config.fontSize = selectedFont.pointSize;
}
}
radius: 14
color: Colors.surfaceContainerHigh
implicitHeight: fontLayout.implicitHeight + 10
implicitWidth: fontLayout.implicitWidth + 10
RowLayout {
id: fontLayout
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
clip: true
spacing: 10
CustomText {
id: fontText
font.bold: true
text: "current font: " + Settings.config.font
}
Rectangle {
id: fontSelector
Layout.rightMargin: 5
implicitWidth: fontSelectorText.implicitWidth + 20
implicitHeight: 20
color: Colors.surfaceContainerHigh
border.width: 2
border.color: Colors.outline
radius: 12
CustomText {
id: fontSelectorText
anchors.centerIn: parent
text: "Pick Font"
}
MouseArea {
id: fontPickerOpener
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onClicked: fontPicker.open()
}
}
}
}

View File

@ -0,0 +1,99 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import QtQuick.Dialogs
import Quickshell.Widgets
import qs
import qs.settings
import qs.reusables
FloatingWindow {
id: root
visible: Settings.config.settingsShown
onClosed: {
Settings.config.settingsShown = false;
}
color: Colors.surfaceContainer
title: "qs-settings"
implicitWidth: 800
implicitHeight: 600
ListModel {
id: pageModel
ListElement {
text: "welcomePage"
source: "WelcomePage.qml"
}
ListElement {
text: "Appearance"
source: "Appearance.qml"
}
ListElement {
text: "Matugen"
source: "Matugen.qml"
}
ListElement {
text: "test"
source: "WelcomePage.qml"
}
}
RowLayout {
id: windowLayout
clip: true
anchors.fill: parent
spacing: 10
ClippingWrapperRectangle {
id: pageWrapper
Layout.margins: 20
Layout.fillHeight: true
Layout.preferredWidth: 200
Layout.rightMargin: 0
margin: 20
color: Colors.surfaceContainer
radius: 12
ListView {
id: pageView
clip: true
model: pageModel
delegate: pageDelegate
Component {
id: pageDelegate
Rectangle {
radius: 24
color: ListView.isCurrentItem ? Colors.primaryContainer : "transparent"
implicitHeight: 30
implicitWidth: pageWrapper.width - 50
required property var modelData
required property int index
CustomText {
id: delegateText
leftPadding: 10
anchors.verticalCenter: parent.verticalCenter
text: parent.modelData.text
}
MouseArea {
id: clickHandler
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onClicked: {
contentLoader.source = parent.modelData.source;
console.log(parent.index);
pageView.currentIndex = parent.index;
}
}
}
}
}
}
Loader {
id: contentLoader
Layout.leftMargin: 0
Layout.fillHeight: true
Layout.fillWidth: true
Layout.margins: 20
source: pageModel.get(pageView.currentIndex).source
}
}
}

View File

@ -0,0 +1,13 @@
import QtQuick
import qs
import qs.reusables
Item {
id: root
implicitWidth: 300
implicitHeight: 300
CustomText {
text: "I am the Matugen page"
}
}

View File

@ -0,0 +1,35 @@
import QtQuick
import qs
import qs.reusables
import Quickshell.Widgets
import QtQuick.Layouts
ClippingWrapperRectangle {
id: root
anchors.centerIn: parent
clip: true
color: Colors.surfaceContainerHigh
border.width: 1
border.color: Colors.primary
radius: 12
margin: 20
ColumnLayout {
id: pageLayout
spacing: 0
anchors.top: parent.top
CustomText {
topPadding: 10
text: "Welcome!"
font.pixelSize: 24
font.bold: true
}
CustomText {
text: "we love women here"
}
Item {
id: sping
Layout.fillHeight: true
}
}
}

View File

@ -1,73 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import QtQuick
import Qt.labs.folderlistmodel 2.10
import qs
import qs.settings
Loader {
active: Settings.config.wallswitchershown
sourceComponent: root
Component {
id: root
FloatingWindow {
implicitWidth: 700
title: "qs-wallpicker"
implicitHeight: 600
color: Colors.base00
visible: Settings.config.wallswitchershown
onClosed: Settings.config.wallswitchershown = false
Rectangle {
id: container
radius: Settings.config.rounding
anchors {
fill: parent
margins: 8
}
color: Colors.base02
FolderListModel {
id: wpModel
folder: "file:///home/lucy/.walls/"
nameFilters: ["*.png"]
}
Component {
id: wallDelegate
Rectangle {
id: wpPreview
required property var filePath
implicitWidth: 80
implicitHeight: 60
color: "transparent"
Image {
asynchronous: true
anchors.fill: parent
source: wpPreview.filePath ? wpPreview.filePath : null
}
MouseArea {
id: updater
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
Settings.config.currentWall = wpPreview.filePath;
}
}
}
}
GridView {
id: wallLayout
anchors.centerIn: parent
anchors.margins: 20
anchors.leftMargin: 40
anchors.fill: parent
clip: true
model: wpModel
delegate: wallDelegate
}
}
}
}
}

View File

@ -0,0 +1,134 @@
pragma ComponentBehavior: Bound
import QtQuick.Dialogs
import Quickshell
import qs.settings
import Quickshell.Widgets
import QtQuick
import qs.reusables
import Qt.labs.folderlistmodel 2.10
import Quickshell.Io
import qs
import Quickshell.Hyprland
import QtQuick.Layouts
FloatingWindow {
id: root
implicitHeight: 600
implicitWidth: 900
title: "qs-wallswitcher"
visible: Settings.config.wallSwitcherShown
color: Colors.surface
onClosed: {
Settings.config.wallSwitcherShown = false;
}
Process {
id: wallustRunner
property string cmd: "wallust run " + Settings.config.currentWall
command: ["sh", "-c", cmd]
}
GlobalShortcut {
name: "showWallSwitcher"
onPressed: {
Settings.config.wallSwitcherShown = true;
}
}
ColumnLayout {
id: windowLayout
spacing: 5
anchors.fill: parent
anchors.margins: 5
Rectangle {
id: textWrapper
Layout.fillWidth: true
Layout.margins: 10
Layout.alignment: Qt.AlignCenter
Layout.bottomMargin: 0
topLeftRadius: 12
topRightRadius: 12
bottomLeftRadius: 4
bottomRightRadius: 4
implicitHeight: 30
color: Colors.surfaceContainerLow
CustomText {
id: titleText
font.bold: true
anchors.centerIn: textWrapper
text: "Wallpapers in " + Settings.config.wallDir
}
}
Rectangle {
id: innerWindow
Layout.topMargin: 0
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 10
topLeftRadius: 4
topRightRadius: 4
bottomLeftRadius: 12
bottomRightRadius: 12
color: Colors.surfaceContainerLow
GridView {
id: gridRoot
anchors.margins: 20
property var columns: Math.floor(gridRoot.width / cellWidth)
property var usedWidth: columns * cellWidth
property var emptySpace: width - usedWidth
property var rows: Math.floor(gridRoot.height / cellHeight)
property var usedHeight: rows * cellHeight
property var emptyHeight: height - usedHeight
clip: true
boundsBehavior: Flickable.StopAtBounds
snapMode: GridView.SnapToRow
cellWidth: 130
cellHeight: 90
anchors.fill: innerWindow
anchors.centerIn: innerWindow
leftMargin: emptySpace / 2
model: folderModel
delegate: fileDelegate
FolderListModel {
id: folderModel
folder: Settings.config.wallDir
nameFilters: ["*.png", "*.jpg"]
}
FontDialog {
id: fontPicker
}
Component {
id: fileDelegate
ClippingWrapperRectangle {
id: imageRounder
implicitHeight: 80
implicitWidth: 120
color: "transparent"
required property string filePath
radius: 12
child: Image {
id: wallPreview
asynchronous: true
source: imageRounder.filePath
sourceSize.width: 120
sourceSize.height: 80
MouseArea {
id: wallpaperSetter
acceptedButtons: Qt.LeftButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
Settings.config.currentWall = imageRounder.filePath;
if (Settings.config.generateScheme) {
wallustRunner.running = true;
}
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,21 @@
import QtQuick
import qs
import QtQuick.Controls.Basic
Button {
id: control
property bool showText: false
property string customText
contentItem: CustomText {
id: contentText
text: control.customText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
id: contentBackground
radius: parent.implicitHeight / 2
color: Colors.surfaceContainerHigh
}
}

View File

@ -4,9 +4,9 @@ import qs.settings
Text { Text {
id: root id: root
color: Colors.base05 color: Colors.onSurfaceColor
property real iconSize: 18 property real iconSize: 18
property var fill: true property real fill: 0
renderType: Text.NativeRendering renderType: Text.NativeRendering
font { font {
hintingPreference: Font.PreferNoHinting hintingPreference: Font.PreferNoHinting
@ -14,7 +14,7 @@ Text {
pixelSize: iconSize pixelSize: iconSize
weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill
variableAxes: { variableAxes: {
"FILL": fill, "FILL": fill.toFixed(1),
"opsz": iconSize "opsz": iconSize
} }
} }

View File

@ -0,0 +1,34 @@
import QtQuick
import QtQuick.Controls.Basic
import qs
Slider {
id: control
background: Rectangle {
x: control.leftPadding
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: control.implicitWidth
implicitHeight: 4
width: control.availableWidth
height: implicitHeight
radius: 2
color: Colors.surfaceContainerLow
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: Colors.primary
radius: 2
}
}
handle: Rectangle {
x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: control.implicitHeight - 2
implicitHeight: control.implicitHeight - 2
radius: 13
color: control.pressed ? Colors.primary : Colors.surfaceContainerLowest
}
}

View File

@ -0,0 +1,25 @@
import QtQuick
import QtQuick.Controls.Basic
import qs
Switch {
id: control
indicator: Rectangle {
anchors.verticalCenter: parent.verticalCenter
x: control.leftPadding
y: parent.height / 2 - height / 2
implicitWidth: parent.implicitWidth
implicitHeight: parent.implicitHeight
radius: 26
color: Colors.surfaceContainerLow
Rectangle {
implicitHeight: parent.implicitHeight - 6
implicitWidth: parent.implicitWidth / 2 - 3
x: control.checked ? parent.width - width - 3 : 3
anchors.verticalCenter: parent.verticalCenter
radius: 24
color: control.down ? Colors.surfaceContainerHighest : Colors.primary
}
}
}

View File

@ -3,8 +3,8 @@ import qs
import qs.settings import qs.settings
Text { Text {
color: Colors.onSurfaceColor
font.family: Settings.config.font font.family: Settings.config.font
font.pixelSize: Settings.config.fontSize font.pixelSize: Settings.config.fontSize
color: Colors.base05 font.weight: Settings.config.fontWeight
font.weight: 700
} }

View File

@ -0,0 +1,24 @@
import QtQuick
import QtQuick.Controls.Basic
Switch {
id: control
indicator: Rectangle {
implicitWidth: 48
implicitHeight: 26
x: control.leftPadding
y: parent.height / 2 - height / 2
radius: 13
border.color: control.checked ? "#17a81a" : "#cccccc"
Rectangle {
x: control.checked ? parent.width - width : 0
width: 26
height: 26
radius: 13
color: control.down ? "#cccccc" : "#ffffff"
}
}
}

View File

@ -1,31 +1,38 @@
pragma ComponentBehavior: Bound
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
Singleton { Singleton {
id: root id: root
property alias config: settingsAdapter property alias config: settingsAdapter
property alias currentWall: settingsAdapter.currentWall
onConfigChanged: settingsView.writeAdapter()
FileView { FileView {
id: settingsView id: settingsView
path: "file:///home/lucy/.config/quickshell/settings/settings.json"
onFileChanged: reload()
onAdapterUpdated: writeAdapter()
path: "/home/lucy/.config/qs.json"
watchChanges: true watchChanges: true
onAdapterUpdated: writeAdapter()
onFileChanged: reload()
adapter: JsonAdapter { adapter: JsonAdapter {
id: settingsAdapter id: settingsAdapter
property int barHeight property var margins: 20
property int rounding property var currentWall: ""
property bool floating property var barHeight: 30
property string font property var font: ""
property int fontSize property var fontSize: 14
property int margins property var wallDir: "file:///home/lucy/.walls/"
property var currentWall property bool floating: false
property bool wallswitchershown property var generateScheme: true
property int barmargins property bool wallSwitcherShown: false
property int barSpacing property int fontWeight: 600
property bool showScreenCorners: true
property int screenCornerRadius: 10
property double translucency: 1
property bool blackScreenCorners: true
property bool settingsShown: true
} }
} }
} }

View File

@ -1,12 +0,0 @@
{
"barHeight": 32,
"barSpacing": 10,
"barmargins": 6,
"currentWall": "/home/lucy/.walls/frierensuff.png",
"floating": true,
"font": "Google Sans",
"fontSize": 13,
"margins": 10,
"rounding": 26,
"wallswitchershown": false
}

View File

@ -1,17 +1,19 @@
//@ pragma UseQApplication //@ pragma UseQApplication
import Quickshell import Quickshell
import QtQuick import QtQuick
import qs.modules.Bar
import qs.modules.ipc import qs.modules.ipc
import qs.modules.wallpaper import qs.modules.bar
import qs.modules.widgets.wallpicker import qs.modules.overlays
import qs.modules.notifications import qs.modules.notifications
import qs.modules.widgets.wallswitcher
import qs.modules.widgets.settingsapp
ShellRoot { ShellRoot {
id: root id: root
Ipc {}
Bar {}
Wallpaper {} Wallpaper {}
WallPicker {} Bar {}
Notification {} Notification {}
WallSwitcher {}
Ipc {}
MainWindow {}
} }