1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

niri: improve toplevel sorting

This commit is contained in:
bbedward
2025-10-23 10:33:24 -04:00
parent beab1a7b01
commit 4e43c797e2
2 changed files with 186 additions and 103 deletions

View File

@@ -37,7 +37,7 @@ Singleton {
property bool matugenSuppression: false property bool matugenSuppression: false
property bool configGenerationPending: false property bool configGenerationPending: false
signal windowUrgentChanged() signal windowUrgentChanged
Component.onCompleted: fetchOutputs() Component.onCompleted: fetchOutputs()
@@ -140,28 +140,30 @@ Singleton {
} }
function fetchOutputs() { function fetchOutputs() {
if (!CompositorService.isNiri) return if (!CompositorService.isNiri)
return
Proc.runCommand("niri-fetch-outputs", ["niri", "msg", "-j", "outputs"], (output, exitCode) => { Proc.runCommand("niri-fetch-outputs", ["niri", "msg", "-j", "outputs"], (output, exitCode) => {
if (exitCode !== 0) { if (exitCode !== 0) {
console.warn("NiriService: Failed to fetch outputs, exit code:", exitCode) console.warn("NiriService: Failed to fetch outputs, exit code:", exitCode)
return return
} }
try { try {
const outputsData = JSON.parse(output) const outputsData = JSON.parse(output)
outputs = outputsData outputs = outputsData
console.log("NiriService: Loaded", Object.keys(outputsData).length, "outputs") console.log("NiriService: Loaded", Object.keys(outputsData).length, "outputs")
updateDisplayScales() updateDisplayScales()
if (windows.length > 0) { if (windows.length > 0) {
windows = sortWindowsByLayout(windows) windows = sortWindowsByLayout(windows)
} }
} catch (e) { } catch (e) {
console.warn("NiriService: Failed to parse outputs:", e) console.warn("NiriService: Failed to parse outputs:", e)
} }
}) })
} }
function updateDisplayScales() { function updateDisplayScales() {
if (!outputs || Object.keys(outputs).length === 0) return if (!outputs || Object.keys(outputs).length === 0)
return
const scales = {} const scales = {}
for (const outputName in outputs) { for (const outputName in outputs) {
@@ -176,48 +178,48 @@ Singleton {
function sortWindowsByLayout(windowList) { function sortWindowsByLayout(windowList) {
return [...windowList].sort((a, b) => { return [...windowList].sort((a, b) => {
const aWorkspace = workspaces[a.workspace_id] const aWorkspace = workspaces[a.workspace_id]
const bWorkspace = workspaces[b.workspace_id] const bWorkspace = workspaces[b.workspace_id]
if (aWorkspace && bWorkspace) { if (aWorkspace && bWorkspace) {
const aOutput = aWorkspace.output const aOutput = aWorkspace.output
const bOutput = bWorkspace.output const bOutput = bWorkspace.output
const aOutputInfo = outputs[aOutput] const aOutputInfo = outputs[aOutput]
const bOutputInfo = outputs[bOutput] const bOutputInfo = outputs[bOutput]
if (aOutputInfo && bOutputInfo && aOutputInfo.logical && bOutputInfo.logical) { if (aOutputInfo && bOutputInfo && aOutputInfo.logical && bOutputInfo.logical) {
if (aOutputInfo.logical.x !== bOutputInfo.logical.x) { if (aOutputInfo.logical.x !== bOutputInfo.logical.x) {
return aOutputInfo.logical.x - bOutputInfo.logical.x return aOutputInfo.logical.x - bOutputInfo.logical.x
} }
if (aOutputInfo.logical.y !== bOutputInfo.logical.y) { if (aOutputInfo.logical.y !== bOutputInfo.logical.y) {
return aOutputInfo.logical.y - bOutputInfo.logical.y return aOutputInfo.logical.y - bOutputInfo.logical.y
} }
} }
if (aOutput === bOutput && aWorkspace.idx !== bWorkspace.idx) { if (aOutput === bOutput && aWorkspace.idx !== bWorkspace.idx) {
return aWorkspace.idx - bWorkspace.idx return aWorkspace.idx - bWorkspace.idx
} }
} }
if (a.workspace_id === b.workspace_id && a.layout && b.layout) { if (a.workspace_id === b.workspace_id && a.layout && b.layout) {
if (a.layout.pos_in_scrolling_layout && b.layout.pos_in_scrolling_layout) { if (a.layout.pos_in_scrolling_layout && b.layout.pos_in_scrolling_layout) {
const aPos = a.layout.pos_in_scrolling_layout const aPos = a.layout.pos_in_scrolling_layout
const bPos = b.layout.pos_in_scrolling_layout const bPos = b.layout.pos_in_scrolling_layout
if (aPos.length > 1 && bPos.length > 1) { if (aPos.length > 1 && bPos.length > 1) {
if (aPos[0] !== bPos[0]) { if (aPos[0] !== bPos[0]) {
return aPos[0] - bPos[0] return aPos[0] - bPos[0]
} }
if (aPos[1] !== bPos[1]) { if (aPos[1] !== bPos[1]) {
return aPos[1] - bPos[1] return aPos[1] - bPos[1]
} }
} }
} }
} }
return a.id - b.id return a.id - b.id
}) })
} }
function handleNiriEvent(event) { function handleNiriEvent(event) {
@@ -354,7 +356,8 @@ Singleton {
} }
function handleWindowOpenedOrChanged(data) { function handleWindowOpenedOrChanged(data) {
if (!data.window) return if (!data.window)
return
const window = data.window const window = data.window
const existingIndex = windows.findIndex(w => w.id === window.id) const existingIndex = windows.findIndex(w => w.id === window.id)
@@ -370,7 +373,8 @@ Singleton {
} }
function handleWindowLayoutsChanged(data) { function handleWindowLayoutsChanged(data) {
if (!data.changes) return if (!data.changes)
return
const updatedWindows = [...windows] const updatedWindows = [...windows]
let hasChanges = false let hasChanges = false
@@ -380,7 +384,8 @@ Singleton {
const layoutData = change[1] const layoutData = change[1]
const windowIndex = updatedWindows.findIndex(w => w.id === windowId) const windowIndex = updatedWindows.findIndex(w => w.id === windowId)
if (windowIndex < 0) continue if (windowIndex < 0)
continue
const updatedWindow = {} const updatedWindow = {}
for (var prop in updatedWindows[windowIndex]) { for (var prop in updatedWindows[windowIndex]) {
@@ -391,14 +396,16 @@ Singleton {
hasChanges = true hasChanges = true
} }
if (!hasChanges) return if (!hasChanges)
return
windows = sortWindowsByLayout(updatedWindows) windows = sortWindowsByLayout(updatedWindows)
windowsChanged() windowsChanged()
} }
function handleOutputsChanged(data) { function handleOutputsChanged(data) {
if (!data.outputs) return if (!data.outputs)
return
outputs = data.outputs outputs = data.outputs
updateDisplayScales() updateDisplayScales()
windows = sortWindowsByLayout(windows) windows = sortWindowsByLayout(windows)
@@ -442,7 +449,8 @@ Singleton {
function handleWorkspaceUrgencyChanged(data) { function handleWorkspaceUrgencyChanged(data) {
const ws = root.workspaces[data.id] const ws = root.workspaces[data.id]
if (!ws) return if (!ws)
return
ws.is_urgent = data.urgent ws.is_urgent = data.urgent
@@ -465,41 +473,86 @@ Singleton {
} }
function send(request) { function send(request) {
if (!CompositorService.isNiri || !requestSocket.connected) return false if (!CompositorService.isNiri || !requestSocket.connected)
return false
requestSocket.send(request) requestSocket.send(request)
return true return true
} }
function doScreenTransition() { function doScreenTransition() {
return send({"Action": {"DoScreenTransition": {"delay_ms": 0}}}) return send({
"Action": {
"DoScreenTransition": {
"delay_ms": 0
}
}
})
} }
function toggleOverview() { function toggleOverview() {
return send({"Action": {"ToggleOverview": {}}}) return send({
"Action": {
"ToggleOverview": {}
}
})
} }
function switchToWorkspace(workspaceIndex) { function switchToWorkspace(workspaceIndex) {
return send({"Action": {"FocusWorkspace": {"reference": {"Index": workspaceIndex}}}}) return send({
"Action": {
"FocusWorkspace": {
"reference": {
"Index": workspaceIndex
}
}
}
})
} }
function focusWindow(windowId) { function focusWindow(windowId) {
return send({"Action": {"FocusWindow": {"id": windowId}}}) return send({
"Action": {
"FocusWindow": {
"id": windowId
}
}
})
} }
function powerOffMonitors() { function powerOffMonitors() {
return send({"Action": {"PowerOffMonitors": {}}}) return send({
"Action": {
"PowerOffMonitors": {}
}
})
} }
function powerOnMonitors() { function powerOnMonitors() {
return send({"Action": {"PowerOnMonitors": {}}}) return send({
"Action": {
"PowerOnMonitors": {}
}
})
} }
function cycleKeyboardLayout() { function cycleKeyboardLayout() {
return send({"Action": {"SwitchLayout": {"layout": "Next"}}}) return send({
"Action": {
"SwitchLayout": {
"layout": "Next"
}
}
})
} }
function quit() { function quit() {
return send({"Action": {"Quit": {"skip_confirmation": true}}}) return send({
"Action": {
"Quit": {
"skip_confirmation": true
}
}
})
} }
function getCurrentOutputWorkspaceNumbers() { function getCurrentOutputWorkspaceNumbers() {
@@ -526,13 +579,17 @@ Singleton {
} }
function findNiriWindow(toplevel) { function findNiriWindow(toplevel) {
if (!toplevel.appId) return null if (!toplevel.appId)
return null
for (var j = 0; j < windows.length; j++) { for (var j = 0; j < windows.length; j++) {
const niriWindow = windows[j] const niriWindow = windows[j]
if (niriWindow.app_id === toplevel.appId) { if (niriWindow.app_id === toplevel.appId) {
if (!niriWindow.title || niriWindow.title === toplevel.title) { if (!niriWindow.title || niriWindow.title === toplevel.title) {
return {"niriIndex": j, "niriWindow": niriWindow} return {
"niriIndex": j,
"niriWindow": niriWindow
}
} }
} }
} }
@@ -549,35 +606,47 @@ Singleton {
for (const niriWindow of sortWindowsByLayout(windows)) { for (const niriWindow of sortWindowsByLayout(windows)) {
let bestMatch = null let bestMatch = null
let bestScore = -1
for (const toplevel of toplevels) { for (const toplevel of toplevels) {
if (usedToplevels.has(toplevel)) continue if (usedToplevels.has(toplevel))
continue
if (toplevel.appId === niriWindow.app_id) { if (toplevel.appId === niriWindow.app_id) {
if (niriWindow.title && toplevel.title === niriWindow.title) { let score = 1
bestMatch = toplevel
break if (niriWindow.title && toplevel.title) {
if (toplevel.title === niriWindow.title) {
score = 3
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
score = 2
}
} }
if (!niriWindow.title && !bestMatch) {
if (score > bestScore) {
bestScore = score
bestMatch = toplevel bestMatch = toplevel
if (score === 3)
break
} }
} }
} }
if (!bestMatch) continue if (!bestMatch)
continue
usedToplevels.add(bestMatch) usedToplevels.add(bestMatch)
const enrichedToplevel = { const enrichedToplevel = {
appId: bestMatch.appId, "appId": bestMatch.appId,
title: bestMatch.title, "title": bestMatch.title,
activated: bestMatch.activated, "activated": bestMatch.activated,
niriWindowId: niriWindow.id, "niriWindowId": niriWindow.id,
niriWorkspaceId: niriWindow.workspace_id, "niriWorkspaceId": niriWindow.workspace_id,
activate: function() { "activate": function () {
return NiriService.focusWindow(niriWindow.id) return NiriService.focusWindow(niriWindow.id)
}, },
close: function() { "close": function () {
if (bestMatch.close) { if (bestMatch.close) {
return bestMatch.close() return bestMatch.close()
} }
@@ -614,7 +683,8 @@ Singleton {
} }
} }
if (currentWorkspaceId === null) return toplevels if (currentWorkspaceId === null)
return toplevels
const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId) const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId)
const usedToplevels = new Set() const usedToplevels = new Set()
@@ -622,35 +692,47 @@ Singleton {
for (const niriWindow of workspaceWindows) { for (const niriWindow of workspaceWindows) {
let bestMatch = null let bestMatch = null
let bestScore = -1
for (const toplevel of toplevels) { for (const toplevel of toplevels) {
if (usedToplevels.has(toplevel)) continue if (usedToplevels.has(toplevel))
continue
if (toplevel.appId === niriWindow.app_id) { if (toplevel.appId === niriWindow.app_id) {
if (niriWindow.title && toplevel.title === niriWindow.title) { let score = 1
bestMatch = toplevel
break if (niriWindow.title && toplevel.title) {
if (toplevel.title === niriWindow.title) {
score = 3
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
score = 2
}
} }
if (!niriWindow.title && !bestMatch) {
if (score > bestScore) {
bestScore = score
bestMatch = toplevel bestMatch = toplevel
if (score === 3)
break
} }
} }
} }
if (!bestMatch) continue if (!bestMatch)
continue
usedToplevels.add(bestMatch) usedToplevels.add(bestMatch)
const enrichedToplevel = { const enrichedToplevel = {
appId: bestMatch.appId, "appId": bestMatch.appId,
title: bestMatch.title, "title": bestMatch.title,
activated: bestMatch.activated, "activated": bestMatch.activated,
niriWindowId: niriWindow.id, "niriWindowId": niriWindow.id,
niriWorkspaceId: niriWindow.workspace_id, "niriWorkspaceId": niriWindow.workspace_id,
activate: function() { "activate": function () {
return NiriService.focusWindow(niriWindow.id) return NiriService.focusWindow(niriWindow.id)
}, },
close: function() { "close": function () {
if (bestMatch.close) { if (bestMatch.close) {
return bestMatch.close() return bestMatch.close()
} }
@@ -672,8 +754,10 @@ Singleton {
function generateNiriLayoutConfig() { function generateNiriLayoutConfig() {
const niriSocket = Quickshell.env("NIRI_SOCKET") const niriSocket = Quickshell.env("NIRI_SOCKET")
if (!niriSocket || niriSocket.length === 0) return if (!niriSocket || niriSocket.length === 0)
if (configGenerationPending) return return
if (configGenerationPending)
return
configGenerationPending = true configGenerationPending = true
configGenerationDebounce.restart() configGenerationDebounce.restart()
@@ -696,7 +780,6 @@ Singleton {
width 2 width 2
} }
} }
window-rule { window-rule {
geometry-corner-radius ${cornerRadius} geometry-corner-radius ${cornerRadius}
clip-to-geometry true clip-to-geometry true
@@ -727,4 +810,4 @@ window-rule {
writeBindsProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp --no-preserve=mode "${sourceBindsPath}" "${bindsPath}"`] writeBindsProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp --no-preserve=mode "${sourceBindsPath}" "${bindsPath}"`]
writeBindsProcess.running = true writeBindsProcess.running = true
} }
} }

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell