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:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
|||||||
Reference in New Issue
Block a user