feat: move to DankMaterialShell, remove quickshell config updates

This commit is contained in:
2026-06-14 15:12:24 +02:00
parent 940ee3e988
commit e74b162b91
27 changed files with 34 additions and 605 deletions
+2 -11
View File
@@ -11,17 +11,7 @@ For my neovim config, see [here](https://git.janishutz.com/janishutz/nvim)
## Features
- QuickShell-based status bar and QuickActions menu (Coming soon)
- QuickShell-based
- Astal4 based Status Bar and Quick Actions menu
- System info
- Hyprland info
- Date & Time
- Bluetooth picker
- some networking settings (more coming later)
- Audio and brightness control
- battery monitoring
- Logout, Reboot, Shutdown, etc
- DankMaterialShell
- `hyprlauncher` for the app launcher
- Wlogout config
- Automatically generated GTK Theme and theming of the rest of the desktop using `matugen`
@@ -33,6 +23,7 @@ For my neovim config, see [here](https://git.janishutz.com/janishutz/nvim)
- mpv config
- zathura configs
- yazi configs with links to various directories I use commonly plus a few plugins and themes that are applied by the script
- Custom QuickShell-based status bar and QuickActions menu (Postponed until further notice)
## TODOs
+11 -15
View File
@@ -1,10 +1,6 @@
[config]
version_check = false
[templates.m3colors]
input_path = '~/.config/matugen/templates/colors.json'
output_path = '~/.local/state/quickshell/user/generated/colors.json'
[templates.hyprland]
input_path = '~/.config/matugen/templates/hyprland/colors.lua'
output_path = '~/.config/hypr/hyprland/colors.lua'
@@ -13,14 +9,14 @@ output_path = '~/.config/hypr/hyprland/colors.lua'
input_path = '~/.config/matugen/templates/hyprland/hyprlock-colors.conf'
output_path = '~/.config/hypr/hyprlock/colors.conf'
[templates.gtk3]
input_path = '~/.config/matugen/templates/gtk-3.0/gtk.css'
output_path = '~/.config/gtk-3.0/gtk.css'
[templates.gtk4]
input_path = '~/.config/matugen/templates/gtk-4.0/gtk.css'
output_path = '~/.config/gtk-4.0/gtk.css'
[templates.wlogout]
input_path = '~/.config/matugen/templates/wlogout/style.css'
output_path = '~/.config/wlogout/style.css'
# [templates.gtk3]
# input_path = '~/.config/matugen/templates/gtk-3.0/gtk.css'
# output_path = '~/.config/gtk-3.0/gtk.css'
#
# [templates.gtk4]
# input_path = '~/.config/matugen/templates/gtk-4.0/gtk.css'
# output_path = '~/.config/gtk-4.0/gtk.css'
#
# [templates.wlogout]
# input_path = '~/.config/matugen/templates/wlogout/style.css'
# output_path = '~/.config/wlogout/style.css'
-4
View File
@@ -1,4 +0,0 @@
# Quickshell Status bar
Many of the components were taken and adapted from the [End-4 configs](https://github.com/end-4/dots-hyprland).
All of the files in `utils/` are taken from the End-4 configs
-35
View File
@@ -1,35 +0,0 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import "./widgets/"
import "../config"
Scope {
Variants {
model: Quickshell.screens
PanelWindow {
id: barRoot
property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
color: Appearance.colors.m3background
implicitHeight: 40
RowLayout {
anchors.fill: parent
spacing: 0
Clock {}
}
}
}
}
-63
View File
@@ -1,63 +0,0 @@
import QtQuick
import QtQuick.Layouts
import "../config/"
// Mostly from here: https://github.com/ChrisTitusTech/quickshell/blob/main/bar/BarBlock.qml
Rectangle {
id: root
radius: Appearance.rounding.barItems
Layout.preferredWidth: contentContainer.implicitWidth + 20
Layout.preferredHeight: 30
property Item content: Text {
text: "No content"
}
property bool hoverEnabled: false
property Item mouseArea: mouseArea
property bool dim: false
property var onClicked: function () {}
property int leftPadding
property int rightPadding
// Background color
color: {
if (mouseArea.containsMouse)
return Appearance.colors.m3primary;
return Appearance.colors.m3background;
}
states: [
State {
when: mouseArea.containsMouse
PropertyChanges {
target: root
}
}
]
Behavior on color {
ColorAnimation {
duration: 200
}
}
Item {
// Contents of the bar block
id: contentContainer
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
anchors.centerIn: parent
children: content
}
MouseArea {
enabled: root.hoverEnabled
id: mouseArea
anchors.fill: root
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: root.onClicked()
}
}
-2
View File
@@ -1,2 +0,0 @@
Bar 1.0 Bar.qml
WidgetWrapper 1.0 WidgetWrapper.qml
@@ -1,4 +0,0 @@
import QtQuick
import Quickshell
Scope {}
-10
View File
@@ -1,10 +0,0 @@
import QtQuick
import QtQuick.Layouts
import "."
import "../../config"
Text {
text: `${ClockHandler.date}, ${ClockHandler.time}`
color: Appearance.colors.m3onBackground
Layout.leftMargin: 10
}
@@ -1,30 +0,0 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
property string time
property string date
Process {
id: dateProc
command: ["date", "+%a %e %b|%T"]
running: true
stdout: SplitParser {
onRead: data => {
date = data.split("|")[0];
time = data.split("|")[1];
}
}
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: dateProc.running = true
}
}
-6
View File
@@ -1,6 +0,0 @@
import Quickshell
import QtQuick
Scope {
id: root
}
-2
View File
@@ -1,2 +0,0 @@
Clock 1.0 Clock.qml
singleton ClockHandler 1.0 ClockHandler.qml
-114
View File
@@ -1,114 +0,0 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
property QtObject colors
property QtObject fonts
property QtObject fontsizes
property QtObject rounding
// From end-4 configs
colors: QtObject {
property color m3background: "#141313"
property color m3onBackground: "#e6e1e1"
property color m3surface: "#141313"
property color m3surfaceDim: "#141313"
property color m3surfaceBright: "#3a3939"
property color m3surfaceContainerLowest: "#0f0e0e"
property color m3surfaceContainerLow: "#1c1b1c"
property color m3surfaceContainer: "#201f20"
property color m3surfaceContainerHigh: "#2b2a2a"
property color m3surfaceContainerHighest: "#363435"
property color m3onSurface: "#e6e1e1"
property color m3surfaceVariant: "#49464a"
property color m3onSurfaceVariant: "#cbc5ca"
property color m3inverseSurface: "#e6e1e1"
property color m3inverseOnSurface: "#313030"
property color m3outline: "#948f94"
property color m3outlineVariant: "#49464a"
property color m3shadow: "#000000"
property color m3scrim: "#000000"
property color m3surfaceTint: "#cbc4cb"
property color m3primary: "#cbc4cb"
property color m3onPrimary: "#322f34"
property color m3primaryContainer: "#2d2a2f"
property color m3onPrimaryContainer: "#bcb6bc"
property color m3inversePrimary: "#615d63"
property color m3secondary: "#cac5c8"
property color m3onSecondary: "#323032"
property color m3secondaryContainer: "#4d4b4d"
property color m3onSecondaryContainer: "#ece6e9"
property color m3tertiary: "#d1c3c6"
property color m3onTertiary: "#372e30"
property color m3tertiaryContainer: "#31292b"
property color m3onTertiaryContainer: "#c1b4b7"
property color m3error: "#ffb4ab"
property color m3onError: "#690005"
property color m3errorContainer: "#93000a"
property color m3onErrorContainer: "#ffdad6"
property color m3primaryFixed: "#e7e0e7"
property color m3primaryFixedDim: "#cbc4cb"
property color m3onPrimaryFixed: "#1d1b1f"
property color m3onPrimaryFixedVariant: "#49454b"
property color m3secondaryFixed: "#e6e1e4"
property color m3secondaryFixedDim: "#cac5c8"
property color m3onSecondaryFixed: "#1d1b1d"
property color m3onSecondaryFixedVariant: "#484648"
property color m3tertiaryFixed: "#eddfe1"
property color m3tertiaryFixedDim: "#d1c3c6"
property color m3onTertiaryFixed: "#211a1c"
property color m3onTertiaryFixedVariant: "#4e4447"
property color m3success: "#B5CCBA"
property color m3onSuccess: "#213528"
property color m3successContainer: "#374B3E"
property color m3onSuccessContainer: "#D1E9D6"
}
rounding: QtObject {
property int barItems: 10
}
function reapplyTheme() {
themeFileView.reload();
}
function applyTheme(fileContent) {
const json = JSON.parse(fileContent);
for (const key in json) {
if (json.hasOwnProperty(key)) {
// Convert snake_case to CamelCase
const camelCaseKey = key.replace(/_([a-z])/g, g => g[1].toUpperCase());
const m3Key = `${camelCaseKey}`;
root.colors[m3Key] = json[key];
}
}
}
Timer {
id: delayedFileRead
interval: 1000
repeat: false
running: false
onTriggered: {
root.applyTheme(themeFileView.text());
}
}
FileView {
id: themeFileView
path: Qt.resolvedUrl(Paths.materialColorsPath)
watchChanges: true
onFileChanged: {
this.reload();
delayedFileRead.start();
}
onLoadedChanged: {
const fileContent = themeFileView.text();
root.applyTheme(fileContent);
}
}
}
-14
View File
@@ -1,14 +0,0 @@
pragma Singleton
import Quickshell
import QtCore
import "../utils/"
Singleton {
readonly property string home: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
readonly property string config: StandardPaths.standardLocations(StandardPaths.ConfigLocation)[0]
readonly property string state: StandardPaths.standardLocations(StandardPaths.StateLocation)[0]
readonly property string cache: StandardPaths.standardLocations(StandardPaths.CacheLocation)[0]
property string materialColorsPath: FileUtils.trimFileProtocol(`${Paths.state}/user/generated/colors.json`)
}
-2
View File
@@ -1,2 +0,0 @@
singleton Appearance 1.0 Appearance.qml
singleton Paths 1.0 Paths.qml
-95
View File
@@ -1,95 +0,0 @@
// From https://github.com/quickshell-mirror/quickshell-examples/blob/master/volume-osd/shell.qml
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Pipewire
import Quickshell.Widgets
import "../config/"
Scope {
id: root
// Bind the pipewire node so its volume will be tracked
PwObjectTracker {
objects: [Pipewire.defaultAudioSink]
}
Connections {
target: Pipewire.defaultAudioSink?.audio
function onVolumeChanged() {
root.shouldShowOsd = true;
hideTimer.restart();
}
}
property bool shouldShowOsd: false
Timer {
id: hideTimer
interval: 1000
onTriggered: root.shouldShowOsd = false
}
// The OSD window will be created and destroyed based on shouldShowOsd.
// PanelWindow.visible could be set instead of using a loader, but using
// a loader will reduce the memory overhead when the window isn't open.
LazyLoader {
active: root.shouldShowOsd
PanelWindow {
// Since the panel's screen is unset, it will be picked by the compositor
// when the window is created. Most compositors pick the current active monitor.
anchors.bottom: true
margins.bottom: screen.height / 5
exclusiveZone: 0
implicitWidth: 300
implicitHeight: 50
color: "transparent"
// An empty click mask prevents the window from blocking mouse events.
mask: Region {}
Rectangle {
anchors.fill: parent
radius: height / 2
color: Appearance.colors.m3background
RowLayout {
anchors {
fill: parent
leftMargin: 10
rightMargin: 15
}
IconImage {
implicitSize: 30
source: Quickshell.iconPath("audio-volume-high-symbolic")
}
Rectangle {
// Stretches to fill all left-over space
Layout.fillWidth: true
implicitHeight: 10
radius: 20
color: "#50ffffff"
Rectangle {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0)
radius: parent.radius
}
}
}
}
}
}
}
-1
View File
@@ -1 +0,0 @@
Volume 1.0 Volume.qml
-15
View File
@@ -1,15 +0,0 @@
import Quickshell
import QtQuick
import "config"
import "bar"
import "osd"
ShellRoot {
Component.onCompleted: {
// Load themes
Appearance.reapplyTheme();
}
Bar {}
Volume {}
}
-72
View File
@@ -1,72 +0,0 @@
pragma Singleton
import Quickshell
Singleton {
id: root
/**
* Trims the File protocol off the input string
* @param {string} str
* @returns {string}
*/
function trimFileProtocol(str) {
let s = str;
if (typeof s !== "string") s = str.toString(); // Convert to string if it's an url or whatever
return s.startsWith("file://") ? s.slice(7) : s;
}
/**
* Extracts the file name from a file path
* @param {string} str
* @returns {string}
*/
function fileNameForPath(str) {
if (typeof str !== "string") return "";
const trimmed = trimFileProtocol(str);
return trimmed.split(/[\\/]/).pop();
}
/**
* Extracts the folder name from a directory path
* @param {string} str
* @returns {string}
*/
function folderNameForPath(str) {
if (typeof str !== "string") return "";
const trimmed = trimFileProtocol(str);
// Remove trailing slash if present
const noTrailing = trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
if (!noTrailing) return "";
return noTrailing.split(/[\\/]/).pop();
}
/**
* Removes the file extension from a file path or name
* @param {string} str
* @returns {string}
*/
function trimFileExt(str) {
if (typeof str !== "string") return "";
const trimmed = trimFileProtocol(str);
const lastDot = trimmed.lastIndexOf(".");
if (lastDot > -1 && lastDot > trimmed.lastIndexOf("/")) {
return trimmed.slice(0, lastDot);
}
return trimmed;
}
/**
* Returns the parent directory of a given file path
* @param {string} str
* @returns {string}
*/
function parentDirectory(str) {
if (typeof str !== "string") return "";
const trimmed = trimFileProtocol(str);
const parts = trimmed.split(/[\\/]/);
if (parts.length <= 1) return "";
parts.pop();
return parts.join("/");
}
}
-1
View File
@@ -1 +0,0 @@
singleton FileUtils 1.0 FileUtils.qml
-87
View File
@@ -1,87 +0,0 @@
{
"$schema": "/etc/xdg/swaync/configSchema.json",
"ignore-gtk-theme": true,
"positionX": "right",
"positionY": "top",
"layer": "overlay",
"control-center-layer": "top",
"layer-shell": true,
"layer-shell-cover-screen": true,
"cssPriority": "user",
"control-center-margin-top": 0,
"control-center-margin-bottom": 0,
"control-center-margin-right": 0,
"control-center-margin-left": 0,
"notification-2fa-action": true,
"notification-inline-replies": false,
"notification-body-image-height": 100,
"notification-body-image-width": 200,
"timeout": 10,
"timeout-low": 5,
"timeout-critical": 0,
"fit-to-screen": true,
"relative-timestamps": true,
"control-center-width": 500,
"control-center-height": 600,
"notification-window-width": 500,
"keyboard-shortcuts": true,
"notification-grouping": true,
"image-visibility": "when-available",
"transition-time": 200,
"hide-on-clear": false,
"hide-on-action": true,
"text-empty": "No Notifications",
"notification-visibility": {
"example-name": {
"state": "muted",
"urgency": "Normal",
"app-name": "example.app.id"
}
},
"widgets": [
"inhibitors",
"title",
"dnd",
"notifications"
],
"widget-config": {
"notifications": {
"vexpand": true
},
"inhibitors": {
"text": "Inhibitors",
"button-text": "Clear All",
"clear-all-button": true
},
"title": {
"text": "Notifications",
"clear-all-button": true,
"button-text": "Clear All"
},
"dnd": {
"text": "Do Not Disturb"
},
"label": {
"max-lines": 5,
"text": "Label Text"
},
"mpris": {
"blacklist": [],
"autohide": false,
"show-album-art": "always",
"loop-carousel": false
},
"buttons-grid": {
"buttons-per-row": 7,
"actions": [
{
"label": "直",
"type": "toggle",
"active": true,
"command": "sh -c '[[ $SWAYNC_TOGGLE_STATE == true ]] && nmcli radio wifi on || nmcli radio wifi off'",
"update-command": "sh -c '[[ $(nmcli radio wifi) == \"enabled\" ]] && echo true || echo false'"
}
]
}
}
}
-22
View File
@@ -1,22 +0,0 @@
# NOTE: Bar will be rewritten in QuickShell likely
import decman
from decman.plugins import pacman, aur
class LoginManager(decman.Module):
def __init__(self):
"""Base packages that should never be uninstalled"""
super().__init__("base")
@pacman.packages
def pkgs(self) -> set[str]:
return {
# "quickshell",
"playerctl",
"playerctl",
}
@aur.packages
def aurpkgs(self) -> set[str]:
# TODO: Rewrite bar in quickshell
return {"aylurs-gtk-shell"}
+1
View File
@@ -21,6 +21,7 @@ class LoginManager(decman.Module):
return {"gdm"}
# TODO: Config files for laptop and desktop
# TODO: Consider dms greetd for desktop
@systemd.units
def units(self) -> set[str]:
+20
View File
@@ -0,0 +1,20 @@
# NOTE: Bar may eventually be replaced by custom bar
import decman
from decman.plugins import pacman
class DesktopShell(decman.Module):
def __init__(self):
"""DesktopShell"""
super().__init__("base")
@pacman.packages
def pkgs(self) -> set[str]:
return {"dms-shell", "cava", "matugen"}
# TODO: Copy the config, once done
def directories(self) -> dict[str, decman.Directory]:
return { "~/.config/matugen": decman.Directory("./config/matugen") }
def files(self) -> dict[str, decman.File]:
return { "~/.config/matugen": decman.File("") }