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

compositor service & use toplevels instead of niri data

This commit is contained in:
bbedward
2025-08-20 17:31:10 -04:00
parent 835d46a7af
commit be4c09e56d
13 changed files with 236 additions and 537 deletions

View File

@@ -16,24 +16,18 @@ PanelWindow {
property var modelData
property var contextMenu
property var windowsMenu
property bool autoHide: SettingsData.dockAutoHide
property real backgroundTransparency: SettingsData.dockTransparency
property bool contextMenuOpen: (contextMenu && contextMenu.visible
&& contextMenu.screen === modelData)
|| (windowsMenu && windowsMenu.visible
&& windowsMenu.screen === modelData)
property bool windowIsFullscreen: {
if (!NiriService.focusedWindowId || !NiriService.niriAvailable)
return false
var focusedWindow = NiriService.windows.find(
w => w.id === NiriService.focusedWindowId)
if (!focusedWindow)
if (!ToplevelManager.activeToplevel)
return false
var activeWindow = ToplevelManager.activeToplevel
var fullscreenApps = ["vlc", "mpv", "kodi", "steam", "lutris", "wine", "dosbox"]
return fullscreenApps.some(app => focusedWindow.app_id
&& focusedWindow.app_id.toLowerCase(
return fullscreenApps.some(app => activeWindow.appId
&& activeWindow.appId.toLowerCase(
).includes(app))
}
property bool reveal: (!autoHide || dockMouseArea.containsMouse
@@ -142,7 +136,6 @@ PanelWindow {
anchors.bottomMargin: 4
contextMenu: dock.contextMenu
windowsMenu: dock.windowsMenu
}
}

View File

@@ -11,7 +11,6 @@ Item {
property var appData
property var contextMenu: null
property var windowsMenu: null
property var dockApps: null
property int index: -1
property bool longPressing: false
@@ -203,9 +202,10 @@ Item {
["gtk-launch", appData.appId])
}
} else if (appData.type === "window") {
// Focus the specific window
if (appData.windowId)
NiriService.focusWindow(appData.windowId)
// Focus the specific window using toplevel
if (appData.toplevelObject) {
appData.toplevelObject.activate()
}
}
} else if (mouse.button === Qt.MiddleButton) {
if (appData && appData.appId) {
@@ -301,8 +301,8 @@ Item {
if (!appData)
return "transparent"
// For window type, check if focused
if (appData.type === "window" && appData.isFocused)
// For window type, check if focused using reactive property
if (appData.type === "window" && appData.toplevelObject && appData.toplevelObject.activated)
return Theme.primary
// For running apps, show dimmer indicator

View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Common
import qs.Services
import qs.Widgets
@@ -9,7 +10,6 @@ Item {
id: root
property var contextMenu: null
property var windowsMenu: null
property bool requestDockShow: false
property int pinnedAppCount: 0
@@ -62,15 +62,17 @@ Item {
"isPinned"// Use -1 instead of null
: true,
"isRunning": false,
"isFocused": false
})
})
root.pinnedAppCount = pinnedApps.length
// Get sorted toplevels from CompositorService
var sortedToplevels = CompositorService.sortedToplevels
// Add separator between pinned and running if both exist
if (pinnedApps.length > 0
&& NiriService.windows.length > 0) {
&& sortedToplevels.length > 0) {
items.push({
"type": "separator",
"appId": "__SEPARATOR__",
@@ -85,33 +87,25 @@ Item {
})
}
// Second section: Running windows (sorted by display->workspace->position)
// NiriService.windows is already sorted by sortWindowsByLayout
NiriService.windows.forEach(window => {
// Limit window title length for tooltip
var title = window.title
|| "(Unnamed)"
if (title.length > 50) {
title = title.substring(
0, 47) + "..."
}
// Check if this window is focused - compare as numbers
var isFocused = window.id
== NiriService.focusedWindowId
items.push({
"type": "window",
"appId": window.app_id
|| "",
"windowId": window.id || -1,
"windowTitle": title,
"workspaceId": window.workspace_id || -1,
"isPinned": false,
"isRunning": true,
"isFocused": isFocused
})
})
// Second section: Running windows (sorted using Theme.sortToplevels)
sortedToplevels.forEach(toplevel => {
// Limit window title length for tooltip
var title = toplevel.title || "(Unnamed)"
if (title.length > 50) {
title = title.substring(0, 47) + "..."
}
items.push({
"type": "window",
"appId": toplevel.appId || "",
"windowId": -1, // Toplevel doesn't have numeric ID
"windowTitle": title,
"workspaceId": -1, // Will be handled by sorting
"isPinned": false,
"isRunning": true,
"toplevelObject": toplevel
})
})
items.forEach(item => {
append(item)
@@ -146,7 +140,6 @@ Item {
appData: model
contextMenu: root.contextMenu
windowsMenu: root.windowsMenu
dockApps: root
index: model.index
@@ -159,17 +152,12 @@ Item {
}
Connections {
target: NiriService
function onWindowsChanged() {
dockModel.updateModel()
}
function onWindowOpenedOrChanged() {
dockModel.updateModel()
}
function onFocusedWindowIdChanged() {
target: CompositorService
function onSortedToplevelsChanged() {
dockModel.updateModel()
}
}
Connections {
target: SessionData

View File

@@ -187,59 +187,7 @@ PanelWindow {
}
Rectangle {
visible: !!(root.appData && root.appData.windows
&& root.appData.windows.count > 0)
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
}
Repeater {
model: root.appData
&& root.appData.windows ? root.appData.windows : null
Rectangle {
required property var model
width: menuColumn.width
height: 28
radius: Theme.cornerRadius
color: windowArea.containsMouse ? Qt.rgba(
Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.12) : "transparent"
StyledText {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
text: model.title || "Untitled Window"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
MouseArea {
id: windowArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
NiriService.focusWindow(model.id)
root.close()
}
}
}
}
Rectangle {
visible: !!(root.appData && root.appData.windows
&& root.appData.windows.count > 1)
visible: root.appData && root.appData.type === "window"
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g,
@@ -247,16 +195,23 @@ PanelWindow {
}
Rectangle {
visible: !!(root.appData && root.appData.windows
&& root.appData.windows.count > 1)
visible: root.appData && root.appData.type === "window"
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
}
Rectangle {
visible: root.appData && root.appData.type === "window"
width: parent.width
height: 28
radius: Theme.cornerRadius
color: closeAllArea.containsMouse ? Qt.rgba(
Theme.error.r,
Theme.error.g,
Theme.error.b,
0.12) : "transparent"
color: closeArea.containsMouse ? Qt.rgba(
Theme.error.r,
Theme.error.g,
Theme.error.b,
0.12) : "transparent"
StyledText {
anchors.left: parent.left
@@ -264,25 +219,22 @@ PanelWindow {
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
text: "Close All Windows"
text: "Close Window"
font.pixelSize: Theme.fontSizeSmall
color: closeAllArea.containsMouse ? Theme.error : Theme.surfaceText
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
MouseArea {
id: closeAllArea
id: closeArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!root.appData || !root.appData.windows)
return
for (var i = 0; i < root.appData.windows.count; i++) {
var window = root.appData.windows.get(i)
NiriService.closeWindow(window.id)
if (root.appData && root.appData.toplevelObject) {
root.appData.toplevelObject.close()
}
root.close()
}

View File

@@ -1,215 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
PanelWindow {
id: root
property bool showWindowsMenu: false
property var appData: null
property var anchorItem: null
property real dockVisibleHeight: 40
property int margin: 10
function showForButton(button, data, dockHeight) {
anchorItem = button
appData = data
dockVisibleHeight = dockHeight || 40
var dockWindow = button.Window.window
if (dockWindow) {
for (var i = 0; i < Quickshell.screens.length; i++) {
var s = Quickshell.screens[i]
if (dockWindow.x >= s.x && dockWindow.x < s.x + s.width) {
root.screen = s
break
}
}
}
showWindowsMenu = true
}
function close() {
showWindowsMenu = false
}
screen: Quickshell.screens[0]
visible: showWindowsMenu
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
property point anchorPos: Qt.point(screen.width / 2, screen.height - 100)
onAnchorItemChanged: updatePosition()
onVisibleChanged: if (visible)
updatePosition()
function updatePosition() {
if (!anchorItem) {
anchorPos = Qt.point(screen.width / 2, screen.height - 100)
return
}
var dockWindow = anchorItem.Window.window
if (!dockWindow) {
anchorPos = Qt.point(screen.width / 2, screen.height - 100)
return
}
var buttonPosInDock = anchorItem.mapToItem(dockWindow.contentItem, 0, 0)
var actualDockHeight = root.dockVisibleHeight // fallback
function findDockBackground(item) {
if (item.objectName === "dockBackground") {
return item
}
for (var i = 0; i < item.children.length; i++) {
var found = findDockBackground(item.children[i])
if (found)
return found
}
return null
}
var dockBackground = findDockBackground(dockWindow.contentItem)
if (dockBackground) {
actualDockHeight = dockBackground.height
}
var dockBottomMargin = 16 // The dock has bottom margin
var buttonScreenY = root.screen.height - actualDockHeight - dockBottomMargin - 20
var dockContentWidth = dockWindow.width
var screenWidth = root.screen.width
var dockLeftMargin = Math.round((screenWidth - dockContentWidth) / 2)
var buttonScreenX = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2
anchorPos = Qt.point(buttonScreenX, buttonScreenY)
}
Rectangle {
id: menuContainer
width: Math.min(600, Math.max(
250,
windowColumn.implicitWidth + Theme.spacingS * 2))
height: Math.max(60, windowColumn.implicitHeight + Theme.spacingS * 2)
x: {
var left = 10
var right = root.width - width - 10
var want = root.anchorPos.x - width / 2
return Math.max(left, Math.min(right, want))
}
y: Math.max(10, root.anchorPos.y - height + 30)
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08)
border.width: 1
opacity: showWindowsMenu ? 1 : 0
scale: showWindowsMenu ? 1 : 0.85
Rectangle {
anchors.fill: parent
anchors.topMargin: 4
anchors.leftMargin: 2
anchors.rightMargin: -2
anchors.bottomMargin: -4
radius: parent.radius
color: Qt.rgba(0, 0, 0, 0.15)
z: parent.z - 1
}
Column {
id: windowColumn
width: parent.width - Theme.spacingS * 2
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Theme.spacingS
spacing: 1
Repeater {
model: root.appData
&& root.appData.windows ? root.appData.windows : null
Rectangle {
required property var model
width: windowColumn.width
height: 32
radius: Theme.cornerRadius
color: windowArea.containsMouse ? Qt.rgba(
Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.12) : "transparent"
StyledText {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
text: model.title || "Untitled Window"
font.pixelSize: Theme.fontSizeSmall
color: model.is_focused ? Theme.primary : Theme.surfaceText
font.weight: model.is_focused ? Font.Medium : Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
MouseArea {
id: windowArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
NiriService.focusWindow(model.id)
root.close()
}
}
}
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
}
MouseArea {
anchors.fill: parent
z: -1
hoverEnabled: false
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
root.close()
}
}
}

View File

@@ -1,4 +1,5 @@
import Quickshell
import Quickshell.Wayland
import QtQuick
import qs.Common
import qs.Services
@@ -12,6 +13,7 @@ Rectangle {
readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2
readonly property int maxNormalWidth: 456
readonly property int maxCompactWidth: 288
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
width: compactMode ? Math.min(baseWidth,
maxCompactWidth) : Math.min(baseWidth,
@@ -19,7 +21,7 @@ Rectangle {
height: 30
radius: Theme.cornerRadius
color: {
if (!NiriService.focusedWindowTitle)
if (!activeWindow || !activeWindow.title)
return "transparent"
const baseColor = mouseArea.containsMouse ? Theme.primaryHover : Theme.surfaceTextHover
@@ -27,7 +29,7 @@ Rectangle {
baseColor.a * Theme.widgetTransparency)
}
clip: true
visible: NiriService.niriAvailable && NiriService.focusedWindowTitle
visible: activeWindow && activeWindow.title
Row {
id: contentRow
@@ -39,18 +41,12 @@ Rectangle {
id: appText
text: {
if (!NiriService.focusedWindowId)
if (!activeWindow || !activeWindow.appId)
return ""
var window = NiriService.windows.find(w => {
return w.id == NiriService.focusedWindowId
})
if (!window || !window.app_id)
return ""
var desktopEntry = DesktopEntries.byId(window.app_id)
var desktopEntry = DesktopEntries.byId(activeWindow.appId)
return desktopEntry
&& desktopEntry.name ? desktopEntry.name : window.app_id
&& desktopEntry.name ? desktopEntry.name : activeWindow.appId
}
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
@@ -74,7 +70,7 @@ Rectangle {
id: titleText
text: {
var title = NiriService.focusedWindowTitle || ""
var title = activeWindow && activeWindow.title ? activeWindow.title : ""
var appName = appText.text
if (!title || !appName)

View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
@@ -15,7 +16,8 @@ Rectangle {
property var topBar: null
// The visual root for this window
property Item windowRoot: (Window.window ? Window.window.contentItem : null)
readonly property int windowCount: NiriService.windows.length
readonly property var sortedToplevels: CompositorService.sortedToplevels
readonly property int windowCount: sortedToplevels.length
readonly property int calculatedWidth: {
if (windowCount === 0)
return 0
@@ -50,16 +52,15 @@ Rectangle {
Repeater {
id: windowRepeater
model: NiriService.windows
model: sortedToplevels
delegate: Item {
id: delegateItem
property bool isFocused: String(modelData.id) === String(
NiriService.focusedWindowId)
property string appId: modelData.app_id || ""
property bool isFocused: modelData.activated
property string appId: modelData.appId || ""
property string windowTitle: modelData.title || "(Unnamed)"
property int windowId: modelData.id
property var toplevelObject: modelData
property string tooltipText: {
var appName = "Unknown"
if (appId) {
@@ -176,7 +177,9 @@ Rectangle {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
NiriService.focusWindow(windowId)
if (toplevelObject) {
toplevelObject.activate()
}
}
onEntered: {
root.hoveredItem = delegateItem

View File

@@ -24,7 +24,7 @@ Rectangle {
}
function getDisplayWorkspaces() {
if (!NiriService.niriAvailable
if (!CompositorService.isNiri
|| NiriService.allWorkspaces.length === 0)
return [1, 2]
@@ -41,7 +41,7 @@ Rectangle {
}
function getDisplayActiveWorkspace() {
if (!NiriService.niriAvailable
if (!CompositorService.isNiri
|| NiriService.allWorkspaces.length === 0)
return 1
@@ -68,7 +68,7 @@ Rectangle {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b,
baseColor.a * Theme.widgetTransparency)
}
visible: NiriService.niriAvailable
visible: CompositorService.isNiri
Connections {
function onAllWorkspacesChanged() {
@@ -83,16 +83,10 @@ Rectangle {
root.currentWorkspace = root.getDisplayActiveWorkspace()
}
function onNiriAvailableChanged() {
if (NiriService.niriAvailable) {
root.workspaceList = SettingsData.showWorkspacePadding ? root.padWorkspaces(root.getDisplayWorkspaces()) : root.getDisplayWorkspaces()
root.currentWorkspace = root.getDisplayActiveWorkspace()
}
}
target: NiriService
}
Connections {
function onShowWorkspacePaddingChanged() {
var baseList = root.getDisplayWorkspaces()
@@ -118,7 +112,7 @@ Rectangle {
property bool isHovered: mouseArea.containsMouse
property int sequentialNumber: index + 1
property var workspaceData: {
if (isPlaceholder || !NiriService.niriAvailable)
if (isPlaceholder || !CompositorService.isNiri)
return null
for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
var ws = NiriService.allWorkspaces[i]