Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f6847ce0e | ||
|
|
e5e52802de | ||
|
|
42f02f4dd2 | ||
|
|
9df3a64815 | ||
|
|
9370284c6b | ||
|
|
5598f4f435 | ||
|
|
7e02c87b91 | ||
|
|
d3e61efd86 | ||
|
|
de95b6c8f9 | ||
|
|
4ff4afa279 | ||
|
|
8a83181f11 |
@@ -40,7 +40,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
|
||||
sourceComponent: BatteryIcon {
|
||||
devState: Battery.deviceStateString
|
||||
devState: Battery.deviceStateString.toLowerCase()
|
||||
percentage: Battery.currentPerc
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ ColumnLayout {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Colors.palette.m3secondary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.family: Config.appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: Time.hourStr
|
||||
}
|
||||
@@ -38,7 +38,7 @@ ColumnLayout {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Colors.palette.m3primary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.family: Config.appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: ":"
|
||||
}
|
||||
@@ -47,7 +47,7 @@ ColumnLayout {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Colors.palette.m3secondary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.family: Config.appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: Time.minuteStr
|
||||
}
|
||||
@@ -58,7 +58,7 @@ ColumnLayout {
|
||||
Layout.topMargin: -Tokens.padding.large * 2
|
||||
color: Colors.palette.m3tertiary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.mono
|
||||
font.family: Config.appearance.font.family.mono
|
||||
font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale)
|
||||
text: Time.format("dddd, d MMMM yyyy")
|
||||
}
|
||||
@@ -226,7 +226,7 @@ ColumnLayout {
|
||||
anchors.right: parent.right
|
||||
animateProp: "opacity"
|
||||
color: Colors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.family: Config.appearance.font.family.mono
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
lineHeight: 1.2
|
||||
opacity: shouldBeVisible && !message.msg ? 1 : 0
|
||||
@@ -295,7 +295,7 @@ ColumnLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: Colors.palette.m3error
|
||||
font.family: Appearance.font.family.mono
|
||||
font.family: Config.appearance.font.family.mono
|
||||
font.pointSize: Tokens.font.size.small
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
opacity: 0
|
||||
|
||||
@@ -41,7 +41,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
color: root.pam.passwd.active ? Colors.palette.m3secondary : Colors.palette.m3outline
|
||||
font.family: Appearance.font.family.mono
|
||||
font.family: Config.appearance.font.family.mono
|
||||
font.pointSize: Tokens.font.size.normal
|
||||
opacity: root.buffer ? 0 : 1
|
||||
text: {
|
||||
|
||||
@@ -24,7 +24,7 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
color: Colors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.font.family.mono
|
||||
font.family: Config.appearance.font.family.mono
|
||||
font.weight: 500
|
||||
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
|
||||
}
|
||||
@@ -66,7 +66,7 @@ ColumnLayout {
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: Colors.palette.m3outlineVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.family: Config.appearance.font.family.mono
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font.weight: 500
|
||||
text: qsTr("No Notifications")
|
||||
|
||||
@@ -1,101 +1,184 @@
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Networking as QSNetwork
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Networking as QSNetwork
|
||||
import ZShell.Components
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
import qs.Helpers
|
||||
import qs.Daemons
|
||||
import qs.Services
|
||||
import qs.Modules.Settings
|
||||
import qs.Modules.Bar.Popouts as BarPopouts
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
required property Item popouts
|
||||
required property var visibilities
|
||||
readonly property bool needExtraRow: quickToggles.length > 6
|
||||
required property BarPopouts.Wrapper popouts
|
||||
readonly property var quickToggles: {
|
||||
const seenIds = new Set();
|
||||
|
||||
return Config.utilities.quickToggles.values.filter(item => {
|
||||
if (!item.enabled)
|
||||
return false;
|
||||
|
||||
if (seenIds.has(item.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.id === "bluetooth" && !Bluetooth.defaultAdapter)
|
||||
return false;
|
||||
|
||||
if (item.id === "wifi" && !QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi))
|
||||
return false;
|
||||
|
||||
seenIds.add(item.id);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
readonly property int splitIndex: Math.ceil(quickToggles.length / 2)
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
Layout.fillWidth: true
|
||||
color: Colors.tPalette.m3surfaceContainer
|
||||
implicitHeight: layout.implicitHeight + 18 * 2
|
||||
implicitHeight: layout.implicitHeight + Tokens.padding.extraLargeIncreased
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
ButtonRow {
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
anchors.margins: Tokens.padding.large
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
Toggle {
|
||||
checked: Network.wifiEnabled
|
||||
icon: Network.wifiEnabled ? "wifi" : "wifi_off"
|
||||
visible: QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi)
|
||||
|
||||
onClicked: Network.toggleWifi()
|
||||
CustomText {
|
||||
font.pointSize: Tokens.font.size.normal
|
||||
text: qsTr("Quick Toggles")
|
||||
}
|
||||
|
||||
Toggle {
|
||||
id: toggle
|
||||
|
||||
checked: !NotifServer.dnd
|
||||
icon: NotifServer.dnd ? "notifications_off" : "notifications"
|
||||
|
||||
onClicked: NotifServer.dnd = !NotifServer.dnd
|
||||
QuickToggleRow {
|
||||
model: root.needExtraRow ? root.quickToggles.slice(0, root.splitIndex) : root.quickToggles
|
||||
}
|
||||
|
||||
Toggle {
|
||||
checked: !Audio.sourceMuted
|
||||
icon: Audio.sourceMuted ? "mic_off" : "mic"
|
||||
|
||||
onClicked: {
|
||||
const audio = Audio.source?.audio;
|
||||
if (audio)
|
||||
audio.muted = !audio.muted;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
checked: !Audio.muted
|
||||
icon: Audio.muted ? "volume_off" : "volume_up"
|
||||
|
||||
onClicked: {
|
||||
const audio = Audio.sink?.audio;
|
||||
if (audio)
|
||||
audio.muted = !audio.muted;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
checked: Bluetooth.defaultAdapter?.enabled ?? false
|
||||
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||
visible: Bluetooth.defaultAdapter ?? false
|
||||
|
||||
onClicked: {
|
||||
const adapter = Bluetooth.defaultAdapter;
|
||||
if (adapter)
|
||||
adapter.enabled = !adapter.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
checked: GameMode.enabled
|
||||
icon: GameMode.enabled ? "videogame_asset" : "videogame_asset_off"
|
||||
|
||||
onClicked: GameMode.enabled = !GameMode.enabled
|
||||
QuickToggleRow {
|
||||
model: root.needExtraRow ? root.quickToggles.slice(root.splitIndex) : []
|
||||
visible: root.needExtraRow
|
||||
}
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "toggle-dnd"
|
||||
component QuickToggleRow: ButtonRow {
|
||||
property alias model: repeater.model
|
||||
|
||||
onPressed: {
|
||||
toggle.clicked();
|
||||
Layout.fillWidth: true
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
delegate: DelegateChooser {
|
||||
role: "id"
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "wifi"
|
||||
|
||||
delegate: Toggle {
|
||||
checked: Network.wifiEnabled
|
||||
icon: checked ? "wifi" : "wifi_off"
|
||||
|
||||
onClicked: Network.toggleWifi()
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "bluetooth"
|
||||
|
||||
delegate: Toggle {
|
||||
checked: Bluetooth.defaultAdapter?.enabled ?? false // qmllint disable unresolved-type
|
||||
icon: "bluetooth"
|
||||
|
||||
onClicked: {
|
||||
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
|
||||
if (adapter)
|
||||
adapter.enabled = !adapter.enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "mic"
|
||||
|
||||
delegate: Toggle {
|
||||
checked: !Audio.sourceMuted
|
||||
icon: checked ? "mic" : "mic_off"
|
||||
|
||||
onClicked: {
|
||||
const audio = Audio.source?.audio;
|
||||
if (audio)
|
||||
audio.muted = !audio.muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "audio"
|
||||
|
||||
delegate: Toggle {
|
||||
checked: !Audio.muted
|
||||
icon: checked ? "volume_up" : "volume_off"
|
||||
|
||||
onClicked: {
|
||||
const audio = Audio.sink?.audio;
|
||||
if (audio)
|
||||
audio.muted = !audio.muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "gameMode"
|
||||
|
||||
delegate: Toggle {
|
||||
checked: GameMode.enabled
|
||||
icon: "gamepad"
|
||||
|
||||
onClicked: GameMode.enabled = !GameMode.enabled
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "dnd"
|
||||
|
||||
delegate: Toggle {
|
||||
checked: NotifServer.dnd
|
||||
icon: "notifications_off"
|
||||
|
||||
onClicked: NotifServer.dnd = !NotifServer.dnd
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "settings"
|
||||
|
||||
delegate: Toggle {
|
||||
icon: "settings"
|
||||
inactiveOnColor: Colors.palette.m3onSurfaceVariant
|
||||
isToggle: false
|
||||
|
||||
onClicked: {
|
||||
root.visibilities.sidebar = false;
|
||||
root.visibilities.settings = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Toggle: IconButton {
|
||||
fillWidth: true
|
||||
inactiveColor: Colors.layer(Colors.palette.m3surfaceContainerHighest, 2)
|
||||
isRound: true
|
||||
isToggle: true
|
||||
shapeMorph: true
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Modules.Bar.Popouts as BarPopouts
|
||||
import qs.Modules.Notifications.Sidebar.Utils.Cards
|
||||
import ZShell.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Item popouts
|
||||
required property BarPopouts.Wrapper popouts
|
||||
required property PersistentProperties props
|
||||
required property var visibilities
|
||||
|
||||
@@ -30,6 +31,7 @@ Item {
|
||||
}
|
||||
|
||||
Toggles {
|
||||
Layout.fillWidth: true
|
||||
popouts: root.popouts
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.Components
|
||||
import ZShell.Config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Bar.Popouts as BarPopouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real offsetScale: shouldBeActive ? 0 : 1
|
||||
required property Item popouts
|
||||
required property BarPopouts.Wrapper popouts
|
||||
readonly property PersistentProperties props: PersistentProperties {
|
||||
property string recordingConfirmDelete
|
||||
property bool recordingListExpanded: false
|
||||
|
||||
@@ -130,7 +130,7 @@ Scope {
|
||||
Layout.preferredWidth: Math.min(600, contentWidth)
|
||||
font.bold: true
|
||||
font.pointSize: 16
|
||||
text: polkitAgent.flow?.message
|
||||
text: polkitAgent.flow?.message ?? ""
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ Scope {
|
||||
id: showPassCheckbox
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
checked: polkitAgent.flow?.responseVisible
|
||||
checked: polkitAgent.flow?.responseVisible ?? false
|
||||
text: "Show Password"
|
||||
|
||||
onCheckedChanged: {
|
||||
|
||||
@@ -9,6 +9,16 @@ import qs.Modules.Settings.Common
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
readonly property var builtinIcons: ({
|
||||
audio: qsTr("Toggle audio"),
|
||||
mic: qsTr("Toggle microphone"),
|
||||
settings: qsTr("Open settings"),
|
||||
dnd: qsTr("Toggle Do-Not-Disturb"),
|
||||
gameMode: qsTr("Toggle game mode"),
|
||||
bluetooth: qsTr("Toggle bluetooth"),
|
||||
wifi: qsTr("Toggle Wi-Fi")
|
||||
})
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Sidebar")
|
||||
|
||||
@@ -44,5 +54,59 @@ PageBase {
|
||||
|
||||
onMoved: v => Config.sidebar.dragThreshold = v
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Quick toggles")
|
||||
}
|
||||
|
||||
ListEditor {
|
||||
function labelFor(item: var): string {
|
||||
const prettyName = root.builtinIcons[item.id];
|
||||
if (prettyName)
|
||||
return prettyName;
|
||||
const label = item.id.replace(/([A-Z])/g, " $1");
|
||||
return label.charAt(0).toUpperCase() + label.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
function toggledFor(item: var): bool {
|
||||
return item.enabled;
|
||||
}
|
||||
|
||||
first: true
|
||||
values: Config.utilities.quickToggles.values
|
||||
z: 1
|
||||
|
||||
onItemMoved: (from, to) => Config.utilities.quickToggles.move(from, to)
|
||||
onItemRemoved: index => Config.utilities.quickToggles.remove(index)
|
||||
onItemToggled: (index, checked) => Config.utilities.quickToggles.at(index).enabled = checked
|
||||
}
|
||||
|
||||
DialogSelectButton {
|
||||
id: addItemContainer
|
||||
|
||||
acceptLabel: qsTr("Add")
|
||||
enabled: Object.keys(model).length > 0
|
||||
header: qsTr("Add new entry")
|
||||
icon: "add"
|
||||
label: qsTr("Add entry")
|
||||
model: {
|
||||
const present = new Set(Config.utilities.quickToggles.values.map(item => item.id));
|
||||
return Object.keys(root.builtinIcons).filter(id => !present.has(id)).map(id => ({
|
||||
id: id,
|
||||
label: root.builtinIcons[id]
|
||||
}));
|
||||
}
|
||||
rootParent: root.flickable
|
||||
|
||||
onAccepted: {
|
||||
if (!selectedItem)
|
||||
return;
|
||||
|
||||
Config.utilities.quickToggles.insert({
|
||||
id: selectedItem,
|
||||
enabled: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "configobject.hpp"
|
||||
#include "configlist.hpp"
|
||||
|
||||
#include <QVariantList>
|
||||
#include <QStringList>
|
||||
#include <qqmlintegration.h>
|
||||
@@ -58,6 +61,16 @@ class Utilities : public ConfigObject {
|
||||
CONFIG_SUBOBJECT(UtilitiesSizes, sizes)
|
||||
CONFIG_SUBOBJECT(Toasts, toasts)
|
||||
CONFIG_SUBOBJECT(Vpn, vpn)
|
||||
CONFIG_LIST(
|
||||
EntryList,
|
||||
quickToggles,
|
||||
{LIST_ENTRY(dnd, true),
|
||||
LIST_ENTRY(wifi, true),
|
||||
LIST_ENTRY(settings, true),
|
||||
LIST_ENTRY(bluetooth, true),
|
||||
LIST_ENTRY(mic, true),
|
||||
LIST_ENTRY(audio, true),
|
||||
LIST_ENTRY(gameMode, true)})
|
||||
|
||||
public:
|
||||
explicit Utilities(QObject* parent = nullptr)
|
||||
|
||||
@@ -15,7 +15,6 @@ It includes a configurable top bar, launcher, notifications daemon + sidebar, wa
|
||||
## Highlights
|
||||
|
||||
- Multi-window shell layout driven by `Drawers/Windows.qml` and panel/interactions wrappers
|
||||
- Configurable bar entries and popouts (audio, tray, network, power, resources, clock, active window)
|
||||
- Launcher with app DB frequency tracking, action commands, fuzzy search modes, and wallpaper/scheme flows
|
||||
- Notification server with persistence (`~/.local/state/zshell/notifs.json`) and sidebar UI
|
||||
- Dynamic Material 3 palette generation from wallpaper, optional template rendering, terminal sequence application
|
||||
@@ -47,16 +46,25 @@ Core requirements:
|
||||
- Hyprland (Wayland session integration)
|
||||
- Python 3 for scheme/wallpaper tooling
|
||||
|
||||
Make sure to have the newest Quickshell version! As of writing, version `0.2.0.r136.gfb08ece-1`.
|
||||
Here's a list of package dependencies, names are from arch-packages so they
|
||||
might differ slightly depending on your distro.
|
||||
|
||||
```
|
||||
python python-pillow python-materialyoucolor libnotify cava
|
||||
app2unit wl-clipboard dconf cliphist python-typer qt6-canvaspainter
|
||||
python-build python-installer python-hatch python-hatch-vcs cmake ninja
|
||||
```
|
||||
|
||||
Make sure to have the newest Quickshell version! As of writing, version `0.3.1.r1.g0fed22a-1`.
|
||||
|
||||
Used by major features (install as needed for your setup):
|
||||
|
||||
- `app2unit` (launcher app execution)
|
||||
- `nmcli` (network integration)
|
||||
- `brightnessctl`, `ddcutil` (brightness controls)
|
||||
- `wl-copy`, `swappy` (picker/screenshot flow)
|
||||
- `libqalculate` (launcher calculator)
|
||||
- `wl-copy` (clipboard integration)
|
||||
- `PipeWire` + audio stack + `aubio`/`cava` paths for media visualization
|
||||
- `libqalculate` (launcher calculator)
|
||||
- `gsettings` (optional GTK dark/light mode sync)
|
||||
|
||||
## Build and Install
|
||||
@@ -180,7 +188,7 @@ Important state/cache files:
|
||||
- `~/.local/state/zshell/apps.sqlite`
|
||||
- `~/.cache/zshell/`
|
||||
|
||||
Config is hot-reloaded and saved through `Config/Config.qml` serializers. Top-level sections include:
|
||||
Config is hot-reloaded and saved through the `Config` singleton. Top-level sections include:
|
||||
|
||||
- `general`
|
||||
- `appearance`
|
||||
@@ -265,7 +273,6 @@ Note: Template rendering (Jinja2) applies generated colors to `~/.config/zshell/
|
||||
### `screenshot` — area picker
|
||||
|
||||
- `start` — open interactive area picker
|
||||
- `start-freeze` — freeze screen then pick
|
||||
|
||||
### `wallpaper` — wallpaper management
|
||||
|
||||
|
||||
Reference in New Issue
Block a user