diff --git a/Services/NiriService.qml b/Services/NiriService.qml index 3fe4af7a..69728a3d 100644 --- a/Services/NiriService.qml +++ b/Services/NiriService.qml @@ -6,6 +6,9 @@ import qs.Commons Item { id: root + // Sorts floating windows after scrolling ones + property int floatingWindowPosition: Number.MAX_SAFE_INTEGER + // Properties that match the facade interface property ListModel workspaces: ListModel {} property var windows: [] @@ -91,41 +94,7 @@ Item { onRead: function (line) { try { const windowsData = JSON.parse(line) - const windowsList = [] - - for (const win of windowsData) { - var output = null - for (var i = 0; i < workspaces.count; i++) { - if (workspaces.get(i).id === win.workspace_id) { - output = workspaces.get(i).output - break - } - } - - windowsList.push({ - "id": win.id, - "title": win.title || "", - "appId": win.app_id || "", - "workspaceId": win.workspace_id || null, - "isFocused": win.is_focused === true, - "output": output - }) - } - - windowsList.sort((a, b) => a.id - b.id) - windows = windowsList - windowListChanged() - - // Update focused window index - focusedWindowIndex = -1 - for (var i = 0; i < windowsList.length; i++) { - if (windowsList[i].isFocused) { - focusedWindowIndex = i - break - } - } - - activeWindowChanged() + recollectWindows(windowsData) } catch (e) { Logger.error("NiriService", "Failed to parse windows:", e, line) } @@ -141,52 +110,106 @@ Item { stdout: SplitParser { onRead: data => { - try { - const event = JSON.parse(data.trim()) + try { + const event = JSON.parse(data.trim()) - if (event.WorkspacesChanged) { - updateWorkspaces() - } else if (event.WindowOpenedOrChanged) { - handleWindowOpenedOrChanged(event.WindowOpenedOrChanged) - } else if (event.WindowClosed) { - handleWindowClosed(event.WindowClosed) - } else if (event.WindowsChanged) { - handleWindowsChanged(event.WindowsChanged) - } else if (event.WorkspaceActivated) { - updateWorkspaces() - } else if (event.WindowFocusChanged) { - handleWindowFocusChanged(event.WindowFocusChanged) - } - // Removed OverviewOpenedOrClosed handling - } catch (e) { - Logger.error("NiriService", "Error parsing event stream:", e, data) - } - } + if (event.WorkspacesChanged) { + updateWorkspaces() + } else if (event.WindowOpenedOrChanged) { + handleWindowOpenedOrChanged(event.WindowOpenedOrChanged) + } else if (event.WindowClosed) { + handleWindowClosed(event.WindowClosed) + } else if (event.WindowsChanged) { + handleWindowsChanged(event.WindowsChanged) + } else if (event.WorkspaceActivated) { + updateWorkspaces() + } else if (event.WindowFocusChanged) { + handleWindowFocusChanged(event.WindowFocusChanged) + } else if (event.WindowLayoutsChanged) { + handleWindowLayoutsChanged(event.WindowLayoutsChanged) + } + } catch (e) { + Logger.error("NiriService", "Error parsing event stream:", e, data) + } + } } } + // Utility functions + function getWindowPosition(layout) { + if (layout.pos_in_scrolling_layout) { + return { + x: layout.pos_in_scrolling_layout[0], + y: layout.pos_in_scrolling_layout[1] + } + } else { + return { + x: floatingWindowPosition, + y: floatingWindowPosition + } + } + } + + function getWindowOutput(win) { + for (var i = 0; i < workspaces.count; i++) { + if (workspaces.get(i).id === win.workspace_id) { + return workspaces.get(i).output + } + } + return null + } + + function getWindowData(win) { + return { + "id": win.id, + "title": win.title || "", + "appId": win.app_id || "", + "workspaceId": win.workspace_id || null, + "isFocused": win.is_focused === true, + "output": getWindowOutput(win) || "", + "position": getWindowPosition(win.layout) + } + } + + // Sort windows + // 1. by workspace ID + // 2. by position X + // 3. by position Y + function compareWindows(a, b) { + if (a.workspaceId !== b.workspaceId) { + return a.workspaceId - b.workspaceId + } + if (a.position.x !== b.position.x) { + return a.position.x - b.position.x + } + return a.position.y - b.position.y + } + + function recollectWindows(windowsData) { + const windowsList = [] + for (const win of windowsData) { + windowsList.push(getWindowData(win)) + } + windowsList.sort(compareWindows) + windows = windowsList + windowListChanged() + + focusedWindowIndex = -1 + for (var i = 0; i < windowsList.length; i++) { + if (windowsList[i].isFocused) { + focusedWindowIndex = i + break + } + } + activeWindowChanged() + } + // Event handlers function handleWindowOpenedOrChanged(eventData) { try { const windowData = eventData.window const existingIndex = windows.findIndex(w => w.id === windowData.id) - - var output = null - for (var i = 0; i < workspaces.count; i++) { - if (workspaces.get(i).id === windowData.workspace_id) { - output = workspaces.get(i).output - break - } - } - - const newWindow = { - "id": windowData.id, - "title": windowData.title || "", - "appId": windowData.app_id || "", - "workspaceId": windowData.workspace_id || null, - "isFocused": windowData.is_focused === true, - "output": output - } + const newWindow = getWindowData(windowData) if (existingIndex >= 0) { // Update existing window @@ -194,7 +217,7 @@ Item { } else { // Add new window windows.push(newWindow) - windows.sort((a, b) => a.id - b.id) + windows.sort(compareWindows) } // Update focused window index if this window is focused @@ -204,6 +227,7 @@ Item { // Only emit activeWindowChanged if the focused window actually changed if (oldFocusedIndex !== focusedWindowIndex) { + windows[oldFocusedIndex].isFocused = false activeWindowChanged() } } @@ -241,41 +265,7 @@ Item { function handleWindowsChanged(eventData) { try { const windowsData = eventData.windows - const windowsList = [] - - for (const win of windowsData) { - var output = "" - for (var i = 0; i < workspaces.count; i++) { - if (workspaces.get(i).id === win.workspace_id) { - output = workspaces.get(i).output - break - } - } - - windowsList.push({ - "id": win.id, - "title": win.title || "", - "appId": win.app_id || "", - "workspaceId": win.workspace_id || null, - "isFocused": win.is_focused === true, - "output": output - }) - } - - windowsList.sort((a, b) => a.id - b.id) - windows = windowsList - windowListChanged() - - // Update focused window index - focusedWindowIndex = -1 - for (var i = 0; i < windowsList.length; i++) { - if (windowsList[i].isFocused) { - focusedWindowIndex = i - break - } - } - - activeWindowChanged() + recollectWindows(windowsData) } catch (e) { Logger.error("NiriService", "Error handling WindowsChanged:", e) } @@ -307,6 +297,23 @@ Item { } } + function handleWindowLayoutsChanged(eventData) { + try { + for (const [windowId, layout] of eventData.changes) { + const window = windows.find(w => w.id === windowId) + if (window) { + window.position = getWindowPosition(layout) + } + } + + windows.sort(compareWindows) + + windowListChanged() + } catch (e) { + Logger.error("NiriService", "Error handling WindowLayoutChanged:", e) + } + } + // Public functions function switchToWorkspace(workspaceId) { try {