1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

Merge branch 'master' of github.com:bbedward/DankMaterialShell

This commit is contained in:
bbedward
2025-09-06 10:06:15 -04:00
4 changed files with 370 additions and 133 deletions

View File

@@ -20,6 +20,17 @@ DankModal {
if (modalKeyboardController && notificationListRef) {
modalKeyboardController.listView = notificationListRef
modalKeyboardController.rebuildFlatNavigation()
Qt.callLater(() => {
modalKeyboardController.keyboardNavigationActive = true
modalKeyboardController.selectedFlatIndex = 0
modalKeyboardController.updateSelectedIdFromIndex()
if (notificationListRef) {
notificationListRef.keyboardActive = true
}
modalKeyboardController.selectionVersion++
modalKeyboardController.ensureVisible()
})
}
}

View File

@@ -5,6 +5,7 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import qs.Common
import qs.Modals.Common
import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets
@@ -22,9 +23,20 @@ PanelWindow {
property var targetScreen: null
property var modelData: null
property bool animatingOut: false
property bool confirmationDialogOpen: false
property string pendingAction: ""
property string lastSavedFileContent: ""
function hasFileChanges() {
if (!root.currentFileUrl.toString()) {
return root.hasUnsavedChanges || SessionData.notepadContent.length > 0
}
return SessionData.notepadContent !== root.lastSavedFileContent
}
function show() {
notepadVisible = true
Qt.callLater(() => textArea.forceActiveFocus())
}
function hide() {
@@ -111,10 +123,10 @@ PanelWindow {
}
StyledText {
text: (root.hasUnsavedChanges ? "● " : "") + (root.currentFileName || qsTr("Untitled"))
text: (hasFileChanges() ? "● " : "") + (root.currentFileName || qsTr("Untitled"))
font.pixelSize: Theme.fontSizeSmall
color: root.hasUnsavedChanges ? Theme.primary : Theme.surfaceTextMedium
visible: root.currentFileName !== "" || root.hasUnsavedChanges
color: hasFileChanges() ? Theme.primary : Theme.surfaceTextMedium
visible: root.currentFileName !== "" || hasFileChanges()
elide: Text.ElideMiddle
maximumLineCount: 1
width: parent.width - Theme.spacingM
@@ -203,16 +215,29 @@ PanelWindow {
break
case Qt.Key_O:
event.accepted = true
root.fileDialogOpen = true
loadBrowser.open()
if (hasFileChanges()) {
root.pendingAction = "open"
root.confirmationDialogOpen = true
confirmationDialog.open()
} else {
root.fileDialogOpen = true
loadBrowser.open()
}
break
case Qt.Key_N:
event.accepted = true
textArea.text = ""
SessionData.notepadContent = ""
root.currentFileName = ""
root.currentFileUrl = ""
root.hasUnsavedChanges = false
if (hasFileChanges()) {
root.pendingAction = "new"
root.confirmationDialogOpen = true
confirmationDialog.open()
} else {
textArea.text = ""
SessionData.notepadContent = ""
root.currentFileName = ""
root.currentFileUrl = ""
root.hasUnsavedChanges = false
root.lastSavedFileContent = ""
}
break
case Qt.Key_A:
event.accepted = true
@@ -244,7 +269,7 @@ PanelWindow {
iconName: "save"
iconSize: Theme.iconSize - 2
iconColor: Theme.primary
enabled: root.hasUnsavedChanges || SessionData.notepadContent.length > 0
enabled: hasFileChanges() || SessionData.notepadContent.length > 0
onClicked: {
root.fileDialogOpen = true
saveBrowser.open()
@@ -265,8 +290,14 @@ PanelWindow {
iconSize: Theme.iconSize - 2
iconColor: Theme.secondary
onClicked: {
root.fileDialogOpen = true
loadBrowser.open()
if (hasFileChanges()) {
root.pendingAction = "open"
root.confirmationDialogOpen = true
confirmationDialog.open()
} else {
root.fileDialogOpen = true
loadBrowser.open()
}
}
}
StyledText {
@@ -284,11 +315,18 @@ PanelWindow {
iconSize: Theme.iconSize - 2
iconColor: Theme.surfaceText
onClicked: {
textArea.text = ""
SessionData.notepadContent = ""
root.currentFileName = ""
root.currentFileUrl = ""
root.hasUnsavedChanges = false
if (hasFileChanges()) {
root.pendingAction = "new"
root.confirmationDialogOpen = true
confirmationDialog.open()
} else {
textArea.text = ""
SessionData.notepadContent = ""
root.currentFileName = ""
root.currentFileUrl = ""
root.hasUnsavedChanges = false
root.lastSavedFileContent = ""
}
}
}
StyledText {
@@ -318,9 +356,9 @@ PanelWindow {
}
StyledText {
text: saveTimer.running ? qsTr("Auto-saving...") : (root.hasUnsavedChanges ? qsTr("Unsaved changes") : qsTr("Auto-saved"))
text: saveTimer.running ? qsTr("Auto-saving...") : (hasFileChanges() ? qsTr("Unsaved changes") : qsTr("Auto-saved"))
font.pixelSize: Theme.fontSizeSmall
color: root.hasUnsavedChanges ? Theme.warning : (saveTimer.running ? Theme.primary : Theme.surfaceTextMedium)
color: hasFileChanges() ? Theme.warning : (saveTimer.running ? Theme.primary : Theme.surfaceTextMedium)
opacity: SessionData.notepadContent.length > 0 ? 1 : 0
}
}
@@ -344,9 +382,6 @@ PanelWindow {
repeat: false
onTriggered: {
animatingOut = false
currentFileName = ""
currentFileUrl = ""
hasUnsavedChanges = false
}
}
@@ -372,6 +407,7 @@ PanelWindow {
onExited: (exitCode) => {
if (exitCode === 0) {
root.hasUnsavedChanges = false
root.lastSavedFileContent = SessionData.notepadContent
} else {
console.warn("Notepad: Failed to save file, exit code:", exitCode)
}
@@ -385,6 +421,7 @@ PanelWindow {
onStreamFinished: {
SessionData.notepadContent = text
root.hasUnsavedChanges = false
root.lastSavedFileContent = text
}
}
@@ -404,7 +441,9 @@ PanelWindow {
fileExtensions: ["*.txt", "*.md", "*.*"]
allowStacking: true
saveMode: true
defaultFileName: "note.txt"
defaultFileName: root.currentFileName || "note.txt"
WlrLayershell.layer: WlrLayershell.Overlay
onFileSelected: (path) => {
root.fileDialogOpen = false
@@ -416,6 +455,25 @@ PanelWindow {
root.currentFileUrl = fileUrl
saveToFile(fileUrl)
// Handle pending action after save
if (root.pendingAction === "new") {
Qt.callLater(() => {
textArea.text = ""
SessionData.notepadContent = ""
root.currentFileName = ""
root.currentFileUrl = ""
root.hasUnsavedChanges = false
root.lastSavedFileContent = ""
})
} else if (root.pendingAction === "open") {
Qt.callLater(() => {
root.fileDialogOpen = true
loadBrowser.open()
})
}
root.pendingAction = ""
close()
}
@@ -433,6 +491,8 @@ PanelWindow {
fileExtensions: ["*.txt", "*.md", "*.*"]
allowStacking: true
WlrLayershell.layer: WlrLayershell.Overlay
onFileSelected: (path) => {
root.fileDialogOpen = false
const cleanPath = path.toString().replace(/^file:\/\//, '')
@@ -450,4 +510,162 @@ PanelWindow {
root.fileDialogOpen = false
}
}
DankModal {
id: confirmationDialog
width: 400
height: 180
shouldBeVisible: false
allowStacking: true
onBackgroundClicked: {
close()
root.confirmationDialogOpen = false
}
content: Component {
FocusScope {
anchors.fill: parent
focus: true
Keys.onEscapePressed: event => {
confirmationDialog.close()
root.confirmationDialogOpen = false
event.accepted = true
}
Column {
anchors.centerIn: parent
width: parent.width - Theme.spacingM * 2
spacing: Theme.spacingM
Row {
width: parent.width
Column {
width: parent.width - 40
spacing: Theme.spacingXS
StyledText {
text: qsTr("Unsaved Changes")
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
StyledText {
text: root.pendingAction === "new" ?
qsTr("You have unsaved changes. Save before creating a new file?") :
qsTr("You have unsaved changes. Save before opening a file?")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium
width: parent.width
wrapMode: Text.Wrap
}
}
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
onClicked: {
confirmationDialog.close()
root.confirmationDialogOpen = false
}
}
}
Item {
width: parent.width
height: 40
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Rectangle {
width: Math.max(80, discardText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: discardArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
StyledText {
id: discardText
anchors.centerIn: parent
text: qsTr("Don't Save")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
MouseArea {
id: discardArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
confirmationDialog.close()
root.confirmationDialogOpen = false
if (root.pendingAction === "new") {
textArea.text = ""
SessionData.notepadContent = ""
root.currentFileName = ""
root.currentFileUrl = ""
root.hasUnsavedChanges = false
root.lastSavedFileContent = ""
} else if (root.pendingAction === "open") {
root.fileDialogOpen = true
loadBrowser.open()
}
root.pendingAction = ""
}
}
}
Rectangle {
width: Math.max(70, saveAsText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: saveAsArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
StyledText {
id: saveAsText
anchors.centerIn: parent
text: qsTr("Save")
font.pixelSize: Theme.fontSizeMedium
color: Theme.background
font.weight: Font.Medium
}
MouseArea {
id: saveAsArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
confirmationDialog.close()
root.confirmationDialogOpen = false
root.fileDialogOpen = true
saveBrowser.open()
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
}
}
}
}

View File

@@ -138,6 +138,22 @@ QtObject {
ensureVisible()
}
function selectNextWrapping() {
keyboardNavigationActive = true
if (flatNavigation.length === 0)
return
// Re-enable auto-scrolling when arrow keys are used
if (listView && listView.enableAutoScroll) {
listView.enableAutoScroll()
}
selectedFlatIndex = (selectedFlatIndex + 1) % flatNavigation.length
updateSelectedIdFromIndex()
selectionVersion++
ensureVisible()
}
function selectPrevious() {
keyboardNavigationActive = true
if (flatNavigation.length === 0)
@@ -408,6 +424,9 @@ QtObject {
} else if (event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) {
clearSelected()
event.accepted = true
} else if (event.key === Qt.Key_Tab) {
selectNextWrapping()
event.accepted = true
} else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) {
const actionIndex = event.key - Qt.Key_1
executeAction(actionIndex)

207
flake.nix
View File

@@ -3,18 +3,28 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
quickshell.url = "git+https://git.outfoxxed.me/quickshell/quickshell";
quickshell.inputs.nixpkgs.follows = "nixpkgs";
dms-cli.url = "github:AvengeMedia/danklinux";
dms-cli.inputs.nixpkgs.follows = "nixpkgs";
quickshell = {
url = "git+https://git.outfoxxed.me/quickshell/quickshell";
inputs.nixpkgs.follows = "nixpkgs";
};
dgop = {
url = "github:AvengeMedia/dgop";
inputs.nixpkgs.follows = "nixpkgs";
};
dms-cli = {
url = "github:AvengeMedia/danklinux";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, quickshell, dms-cli, ... }:
outputs = { self, nixpkgs, quickshell, dgop, dms-cli, ... }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
forEachSystem = fn:
nixpkgs.lib.genAttrs
nixpkgs.lib.platforms.linux
(system: fn system nixpkgs.legacyPackages.${system});
in {
packages.${system} = {
packages = forEachSystem (system: pkgs: rec {
dankMaterialShell = pkgs.stdenvNoCC.mkDerivation {
name = "dankMaterialShell";
src = ./.;
@@ -26,7 +36,7 @@
};
default = self.packages.${system}.dankMaterialShell;
};
});
homeModules.dankMaterialShell = { config, pkgs, lib, ... }:
let cfg = config.programs.dankMaterialShell;
@@ -40,14 +50,60 @@
lib.mkEnableOption "DankMaterialShell systemd startup";
enableSpawn =
lib.mkEnableOption "DankMaterialShell Niri spawn-at-startup";
enableSystemMonitoring = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add needed dependencies to use system monitoring widgets";
};
enableClipboard = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add needed dependencies to use the clipboard widget";
};
enableVPN = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add needed dependencies to use the VPN widget";
};
enableBrigthnessControl = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add needed dependencies to have brightness/backlight support";
};
enableNightMode = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add needed dependencies to have night mode support";
};
enableDynamicTheming = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add needed dependencies to have dynamic theming support";
};
enableAudioWavelenght = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add needed dependencies to have audio wavelenght support";
};
enableCalendarEvents = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Add calendar events support via khal";
};
quickshell = {
package = lib.mkPackageOption pkgs "quickshell" {
default = quickshell.packages.${pkgs.system}.quickshell;
nullable = false;
};
};
};
config = lib.mkIf cfg.enable {
programs.quickshell = {
enable = true;
package = quickshell.packages.${system}.quickshell;
package = cfg.quickshell.package;
configs.DankMaterialShell = "${
self.packages.${system}.dankMaterialShell
self.packages.${pkgs.system}.dankMaterialShell
}/etc/xdg/quickshell/DankMaterialShell";
activeConfig = lib.mkIf cfg.enableSystemd "DankMaterialShell";
systemd = lib.mkIf cfg.enableSystemd {
@@ -58,123 +114,56 @@
programs.niri.settings = lib.mkMerge [
(lib.mkIf cfg.enableKeybinds {
binds = {
"Mod+Space".action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"spotlight"
"toggle"
];
"Mod+V".action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"clipboard"
"toggle"
];
"Mod+M".action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"processlist"
"toggle"
];
"Mod+Comma".action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"settings"
"toggle"
];
"Super+Alt+L".action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"lock"
"lock"
];
binds = with config.lib.niri.actions; let
quickShellIpc = spawn "${cfg.quickshell.package}/bin/qs" "-c" "DankMaterialShell" "ipc" "call";
in {
"Mod+Space".action = quickShellIpc "spotlight" "toggle";
"Mod+V".action = quickShellIpc "clipboard" "toggle";
"Mod+M".action = quickShellIpc "processlist" "toggle";
"Mod+Comma".action = quickShellIpc "settings" "toggle";
"Super+Alt+L".action = quickShellIpc "lock" "lock";
"XF86AudioRaiseVolume" = {
allow-when-locked = true;
action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"audio"
"increment"
"3"
];
action = quickShellIpc "audio" "increment" "3";
};
"XF86AudioLowerVolume" = {
allow-when-locked = true;
action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"audio"
"decrement"
"3"
];
action = quickShellIpc "audio" "decrement" "3";
};
"XF86AudioMute" = {
allow-when-locked = true;
action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"audio"
"mute"
];
action = quickShellIpc "audio" "mute";
};
"XF86AudioMicMute" = {
allow-when-locked = true;
action.spawn = [
"qs"
"-c"
"DankMaterialShell"
"ipc"
"call"
"audio"
"micmute"
];
action = quickShellIpc "audio" "micmute";
};
};
})
(lib.mkIf (cfg.enableSpawn) {
spawn-at-startup =
[{ command = [ "qs" "-c" "DankMaterialShell" ]; }];
[{ command = [ "${cfg.quickshell.package}/bin/qs" "-c" "DankMaterialShell" ]; }];
})
];
home.packages = with pkgs; [
material-symbols
inter
fira-code
cava
wl-clipboard
cliphist
ddcutil
libsForQt5.qt5ct
kdePackages.qt6ct
matugen
dms-cli.packages.${system}.default
];
home.packages = [
pkgs.material-symbols
pkgs.inter
pkgs.fira-code
pkgs.ddcutil
pkgs.libsForQt5.qt5ct
pkgs.kdePackages.qt6ct
dms-cli.packages.${pkgs.system}.default
]
++ lib.optional cfg.enableSystemMonitoring dgop.packages.${pkgs.system}.dgop
++ lib.optionals cfg.enableClipboard [pkgs.cliphist pkgs.wl-clipboard]
++ lib.optionals cfg.enableVPN [pkgs.glib pkgs.networkmanager]
++ lib.optional cfg.enableBrigthnessControl pkgs.brightnessctl
++ lib.optional cfg.enableNightMode pkgs.gammastep
++ lib.optional cfg.enableDynamicTheming pkgs.matugen
++ lib.optional cfg.enableAudioWavelenght pkgs.cava
++ lib.optional cfg.enableCalendarEvents pkgs.khal;
};
};
};