1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 07:52:50 -05:00

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

This commit is contained in:
bbedward
2025-08-20 21:20:39 -04:00
6 changed files with 291 additions and 35 deletions

View File

@@ -23,7 +23,7 @@ DankModal {
function executePowerAction(action) { function executePowerAction(action) {
switch (action) { switch (action) {
case "logout": case "logout":
NiriService.quit() CompositorService.logout()
break break
case "suspend": case "suspend":
Quickshell.execDetached(["systemctl", "suspend"]) Quickshell.execDetached(["systemctl", "suspend"])

View File

@@ -1187,7 +1187,7 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
logoutDialog.close() logoutDialog.close()
NiriService.quit() CompositorService.logout()
} }
} }
} }

View File

@@ -378,11 +378,28 @@ Item {
} }
StyledText { StyledText {
text: "(Hyprland Soon™)" text: "&"
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
StyledText {
text: `<a href="https://github.com/hyprwm/Hyprland" style="text-decoration:none; color:${Theme.primary};">hyprland</a>`
font.pixelSize: Theme.fontSizeMedium
linkColor: Theme.primary
textFormat: Text.RichText
color: Theme.surfaceVariantText
onLinkActivated: url => Qt.openUrlExternally(url)
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
acceptedButtons: Qt.NoButton
propagateComposedEvents: true
}
}
} }
StyledText { StyledText {

View File

@@ -24,35 +24,47 @@ Rectangle {
} }
function getDisplayWorkspaces() { function getDisplayWorkspaces() {
if (!CompositorService.isNiri if (CompositorService.isNiri) {
|| NiriService.allWorkspaces.length === 0) if (NiriService.allWorkspaces.length === 0)
return [1, 2] return [1, 2]
if (!root.screenName) if (!root.screenName)
return NiriService.getCurrentOutputWorkspaceNumbers() return NiriService.getCurrentOutputWorkspaceNumbers()
var displayWorkspaces = [] var displayWorkspaces = []
for (var i = 0; i < NiriService.allWorkspaces.length; i++) { for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
var ws = NiriService.allWorkspaces[i] var ws = NiriService.allWorkspaces[i]
if (ws.output === root.screenName) if (ws.output === root.screenName)
displayWorkspaces.push(ws.idx + 1) displayWorkspaces.push(ws.idx + 1)
}
return displayWorkspaces.length > 0 ? displayWorkspaces : [1, 2]
} else if (CompositorService.isHyprland) {
var workspaces = HyprlandService.getWorkspaceDisplayNumbers()
return workspaces.length > 0 ? workspaces : [1]
} }
return displayWorkspaces.length > 0 ? displayWorkspaces : [1, 2]
return [1, 2]
} }
function getDisplayActiveWorkspace() { function getDisplayActiveWorkspace() {
if (!CompositorService.isNiri if (CompositorService.isNiri) {
|| NiriService.allWorkspaces.length === 0) if (NiriService.allWorkspaces.length === 0)
return 1
if (!root.screenName)
return NiriService.getCurrentWorkspaceNumber()
for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
var ws = NiriService.allWorkspaces[i]
if (ws.output === root.screenName && ws.is_active)
return ws.idx + 1
}
return 1 return 1
} else if (CompositorService.isHyprland) {
if (!root.screenName) var activeWs = HyprlandService.getCurrentWorkspaceNumber()
return NiriService.getCurrentWorkspaceNumber() return activeWs
for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
var ws = NiriService.allWorkspaces[i]
if (ws.output === root.screenName && ws.is_active)
return ws.idx + 1
} }
return 1 return 1
} }
@@ -68,7 +80,7 @@ Rectangle {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, return Qt.rgba(baseColor.r, baseColor.g, baseColor.b,
baseColor.a * Theme.widgetTransparency) baseColor.a * Theme.widgetTransparency)
} }
visible: CompositorService.isNiri visible: CompositorService.isNiri || CompositorService.isHyprland
Connections { Connections {
function onAllWorkspacesChanged() { function onAllWorkspacesChanged() {
@@ -84,6 +96,28 @@ Rectangle {
} }
target: NiriService target: NiriService
enabled: CompositorService.isNiri
}
Connections {
function onWorkspacesUpdated() {
root.workspaceList
= SettingsData.showWorkspacePadding ? root.padWorkspaces(
root.getDisplayWorkspaces(
)) : root.getDisplayWorkspaces()
root.currentWorkspace = root.getDisplayActiveWorkspace()
}
function onFocusedWorkspaceUpdated() {
root.currentWorkspace = root.getDisplayActiveWorkspace()
}
function onFocusedMonitorUpdated() {
root.currentWorkspace = root.getDisplayActiveWorkspace()
}
target: HyprlandService
enabled: CompositorService.isHyprland
} }
@@ -112,12 +146,22 @@ Rectangle {
property bool isHovered: mouseArea.containsMouse property bool isHovered: mouseArea.containsMouse
property int sequentialNumber: index + 1 property int sequentialNumber: index + 1
property var workspaceData: { property var workspaceData: {
if (isPlaceholder || !CompositorService.isNiri) if (isPlaceholder)
return null return null
for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
var ws = NiriService.allWorkspaces[i] if (CompositorService.isNiri) {
if (ws.idx + 1 === modelData) for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
return ws var ws = NiriService.allWorkspaces[i]
if (ws.idx + 1 === modelData)
return ws
}
} else if (CompositorService.isHyprland) {
var hyprWorkspaces = HyprlandService.getWorkspacesForMonitor(root.screenName)
for (var j = 0; j < hyprWorkspaces.length; j++) {
var hws = hyprWorkspaces[j]
if (hws.id === modelData)
return hws
}
} }
return null return null
} }
@@ -140,8 +184,13 @@ Rectangle {
cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor
enabled: !isPlaceholder enabled: !isPlaceholder
onClicked: { onClicked: {
if (!isPlaceholder) if (!isPlaceholder) {
NiriService.switchToWorkspace(modelData - 1) if (CompositorService.isNiri) {
NiriService.switchToWorkspace(modelData - 1)
} else if (CompositorService.isHyprland) {
HyprlandService.switchToWorkspace(modelData)
}
}
} }
} }

View File

@@ -6,6 +6,7 @@ import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland
Singleton { Singleton {
id: root id: root
@@ -19,6 +20,7 @@ Singleton {
readonly property string niriSocket: Quickshell.env("NIRI_SOCKET") readonly property string niriSocket: Quickshell.env("NIRI_SOCKET")
property bool useNiriSorting: isNiri && NiriService property bool useNiriSorting: isNiri && NiriService
property bool useHyprlandSorting: isHyprland && HyprlandService
// Unified sorted toplevels - automatically chooses sorting based on compositor // Unified sorted toplevels - automatically chooses sorting based on compositor
property var sortedToplevels: { property var sortedToplevels: {
@@ -31,12 +33,16 @@ Singleton {
return NiriService.sortToplevels(ToplevelManager.toplevels.values) return NiriService.sortToplevels(ToplevelManager.toplevels.values)
} }
// For non-niri compositors or when niri isn't ready yet, return unsorted toplevels // Use Hyprland sorting when both compositor is Hyprland AND hyprland service is ready
if (useHyprlandSorting) {
return HyprlandService.sortToplevels(ToplevelManager.toplevels.values)
}
// For other compositors or when services aren't ready yet, return unsorted toplevels
return ToplevelManager.toplevels.values return ToplevelManager.toplevels.values
} }
Component.onCompleted: { Component.onCompleted: {
console.log("CompositorService: Starting detection...")
detectCompositor() detectCompositor()
} }
@@ -46,7 +52,7 @@ Singleton {
isHyprland = true isHyprland = true
isNiri = false isNiri = false
compositor = "hyprland" compositor = "hyprland"
console.log("CompositorService: Detected Hyprland with signature:", hyprlandSignature) console.log("CompositorService: Detected Hyprland")
return return
} }
@@ -81,4 +87,12 @@ Singleton {
} }
} }
} }
function logout() {
if (isNiri) {
NiriService.quit()
return
}
Hyprland.dispatch("exit")
}
} }

View File

@@ -0,0 +1,176 @@
pragma Singleton
pragma ComponentBehavior
import QtQuick
import Quickshell
import Quickshell.Hyprland
Singleton {
id: root
property bool hyprlandAvailable: {
const signature = Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
return signature && signature.length > 0
}
property var allWorkspaces: hyprlandAvailable && Hyprland.workspaces ? Hyprland.workspaces.values : []
property var focusedWorkspace: hyprlandAvailable ? Hyprland.focusedWorkspace : null
property var monitors: hyprlandAvailable ? Hyprland.monitors : []
property var focusedMonitor: hyprlandAvailable ? Hyprland.focusedMonitor : null
function getWorkspacesForMonitor(monitorName) {
if (!hyprlandAvailable) return []
const workspaces = Hyprland.workspaces ? Hyprland.workspaces.values : []
if (!workspaces || workspaces.length === 0) return []
// If no monitor name specified, return all workspaces
if (!monitorName) {
const allWorkspacesCopy = []
for (let i = 0; i < workspaces.length; i++) {
const workspace = workspaces[i]
if (workspace) {
allWorkspacesCopy.push(workspace)
}
}
allWorkspacesCopy.sort((a, b) => a.id - b.id)
return allWorkspacesCopy
}
const filtered = []
for (let i = 0; i < workspaces.length; i++) {
const workspace = workspaces[i]
if (workspace && workspace.monitor && workspace.monitor.name === monitorName) {
filtered.push(workspace)
}
}
// Sort by workspace ID
filtered.sort((a, b) => a.id - b.id)
return filtered
}
function getCurrentWorkspaceForMonitor(monitorName) {
if (!hyprlandAvailable) return null
// If no monitor name specified, return the globally focused workspace
if (!monitorName) {
return focusedWorkspace
}
if (focusedMonitor && focusedMonitor.name === monitorName) {
return focusedWorkspace
}
const monitorWorkspaces = getWorkspacesForMonitor(monitorName)
for (let i = 0; i < monitorWorkspaces.length; i++) {
const ws = monitorWorkspaces[i]
if (ws && ws.active) {
return ws
}
}
return null
}
function switchToWorkspace(workspaceId) {
if (!hyprlandAvailable) return
Hyprland.dispatch(`workspace ${workspaceId}`)
}
function switchToWorkspaceByName(workspaceName) {
if (!hyprlandAvailable) return
Hyprland.dispatch(`workspace name:${workspaceName}`)
}
function moveToWorkspace(workspaceId) {
if (!hyprlandAvailable) return
Hyprland.dispatch(`movetoworkspace ${workspaceId}`)
}
function createWorkspace(workspaceId) {
if (!hyprlandAvailable) return
Hyprland.dispatch(`workspace ${workspaceId}`)
}
function getWorkspaceDisplayNumbers() {
if (!hyprlandAvailable) return [1, 2, 3, 4]
// Get all existing workspaces from Hyprland.workspaces.values
const workspaces = Hyprland.workspaces ? Hyprland.workspaces.values : []
if (!workspaces || workspaces.length === 0) {
// If no workspaces detected, show at least current + a few more
const current = getCurrentWorkspaceNumber()
return [Math.max(1, current - 1), current, current + 1, current + 2].filter((ws, i, arr) => arr.indexOf(ws) === i && ws > 0)
}
// Get workspace IDs and ensure we show a reasonable range
const numbers = []
let maxId = 0
for (let i = 0; i < workspaces.length; i++) {
const ws = workspaces[i]
if (ws && ws.id > 0) {
numbers.push(ws.id)
maxId = Math.max(maxId, ws.id)
}
}
// Always ensure we have at least one workspace beyond the highest
// to allow easy navigation to new workspaces
if (maxId > 0 && numbers.indexOf(maxId + 1) === -1) {
numbers.push(maxId + 1)
}
return numbers.sort((a, b) => a - b)
}
function getCurrentWorkspaceNumber() {
if (!hyprlandAvailable) return 1
// Use the focused workspace directly
const focused = Hyprland.focusedWorkspace
return focused ? focused.id : 1
}
function sortToplevels(toplevels) {
if (!hyprlandAvailable || !toplevels) return []
// Create a copy of the array since the original might be readonly
const sortedArray = Array.from(toplevels)
return sortedArray.sort((a, b) => {
if (a.workspace && b.workspace) {
if (a.workspace.monitor && b.workspace.monitor) {
const monitorCompare = a.workspace.monitor.name.localeCompare(b.workspace.monitor.name)
if (monitorCompare !== 0) return monitorCompare
}
const workspaceCompare = a.workspace.id - b.workspace.id
if (workspaceCompare !== 0) return workspaceCompare
}
return 0
})
}
// Signals for workspace changes that WorkspaceSwitcher can connect to
signal workspacesUpdated()
signal focusedWorkspaceUpdated()
signal focusedMonitorUpdated()
// Monitor changes to properties and emit our signals
onAllWorkspacesChanged: workspacesUpdated()
onFocusedWorkspaceChanged: focusedWorkspaceUpdated()
onFocusedMonitorChanged: focusedMonitorUpdated()
Component.onCompleted: {
if (hyprlandAvailable) {
console.log("HyprlandService: Initialized with Hyprland support")
}
}
}