From c47ecf8fb0814c4bdeaa28226d9538044f9c5751 Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 21 Jul 2025 17:36:15 -0400 Subject: [PATCH] focusedwindow: fix various issues with workspaces --- Services/FocusedWindowService.qml | 58 +++++++++++++++------- Services/NiriWorkspaceService.qml | 80 +++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 18 deletions(-) diff --git a/Services/FocusedWindowService.qml b/Services/FocusedWindowService.qml index ca56a035..5165d770 100644 --- a/Services/FocusedWindowService.qml +++ b/Services/FocusedWindowService.qml @@ -14,17 +14,33 @@ Singleton { property string focusedWindowTitle: "" property int focusedWindowId: -1 + + function updateFromNiriData() { + if (!root.niriAvailable) { + clearFocusedWindow(); + return; + } + + const focusedWindowId = NiriWorkspaceService.focusedWindowId; + if (!focusedWindowId) { + clearFocusedWindow(); + return; + } + + const focusedWindow = NiriWorkspaceService.windows.find(w => w.id === focusedWindowId); + if (focusedWindow) { + root.focusedAppId = focusedWindow.app_id || ""; + root.focusedWindowTitle = focusedWindow.title || ""; + root.focusedAppName = getDisplayName(focusedWindow.app_id || ""); + root.focusedWindowId = parseInt(focusedWindow.id) || -1; + } else { + clearFocusedWindow(); + } + } + function loadInitialFocusedWindow() { if (root.niriAvailable) focusedWindowQuery.running = true; - - } - - function updateFocusedWindowData() { - if (root.niriAvailable && root.focusedWindowId !== -1) - focusedWindowQuery.running = true; - else - clearFocusedWindow(); } function clearFocusedWindow() { @@ -38,7 +54,6 @@ Singleton { if (!appId) return ""; - // Common app_id to display name mappings const appNames = { "com.mitchellh.ghostty": "Ghostty", "org.mozilla.firefox": "Firefox", @@ -59,12 +74,10 @@ Singleton { "slack": "Slack", "zoom": "Zoom" }; - // Return mapped name or clean up the app_id if (appNames[appId]) return appNames[appId]; - // Try to extract a clean name from the app_id - // Remove common prefixes and make first letter uppercase + let cleanName = appId.replace(/^(org\.|com\.|net\.|io\.)/, '').replace(/\./g, ' ').split(' ').map((word) => { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' '); @@ -72,9 +85,7 @@ Singleton { } Component.onCompleted: { - // Use the availability from NiriWorkspaceService to avoid duplicate checks root.niriAvailable = NiriWorkspaceService.niriAvailable; - // Connect to workspace service events NiriWorkspaceService.onNiriAvailableChanged.connect(() => { root.niriAvailable = NiriWorkspaceService.niriAvailable; if (root.niriAvailable) @@ -86,21 +97,32 @@ Singleton { } - // Listen to window focus changes from NiriWorkspaceService Connections { function onFocusedWindowIdChanged() { root.focusedWindowId = parseInt(NiriWorkspaceService.focusedWindowId) || -1; - updateFocusedWindowData(); + updateFromNiriData(); } function onFocusedWindowTitleChanged() { root.focusedWindowTitle = NiriWorkspaceService.focusedWindowTitle; } + function onWindowsChanged() { + updateFromNiriData(); + } + + function onWindowOpenedOrChanged(windowData) { + if (windowData.is_focused) { + root.focusedAppId = windowData.app_id || ""; + root.focusedWindowTitle = windowData.title || ""; + root.focusedAppName = getDisplayName(windowData.app_id || ""); + root.focusedWindowId = parseInt(windowData.id) || -1; + } + } + target: NiriWorkspaceService } - // Process to get focused window info Process { id: focusedWindowQuery @@ -125,7 +147,7 @@ Singleton { } } } - } + } diff --git a/Services/NiriWorkspaceService.qml b/Services/NiriWorkspaceService.qml index f2267a12..e5fddf31 100644 --- a/Services/NiriWorkspaceService.qml +++ b/Services/NiriWorkspaceService.qml @@ -24,6 +24,8 @@ Singleton { // Overview state property bool inOverview: false + signal windowOpenedOrChanged(var windowData) + // Feature availability property bool niriAvailable: false @@ -75,9 +77,57 @@ Singleton { } } + // Load initial windows data + Process { + id: initialWindowsQuery + command: ["niri", "msg", "-j", "windows"] + running: false + + stdout: StdioCollector { + onStreamFinished: { + if (text && text.trim()) { + try { + const windowsData = JSON.parse(text.trim()) + if (windowsData && windowsData.windows) { + handleWindowsChanged(windowsData) + console.log("NiriWorkspaceService: Loaded", windowsData.windows.length, "initial windows") + } + } catch (e) { + console.warn("NiriWorkspaceService: Failed to parse initial windows data:", e) + } + } + } + } + } + + // Load initial focused window data + Process { + id: initialFocusedWindowQuery + command: ["niri", "msg", "-j", "focused-window"] + running: false + + stdout: StdioCollector { + onStreamFinished: { + if (text && text.trim()) { + try { + const focusedData = JSON.parse(text.trim()) + if (focusedData && focusedData.id) { + handleWindowFocusChanged({ id: focusedData.id }) + console.log("NiriWorkspaceService: Loaded initial focused window:", focusedData.id) + } + } catch (e) { + console.warn("NiriWorkspaceService: Failed to parse initial focused window data:", e) + } + } + } + } + } + function loadInitialWorkspaceData() { console.log("NiriWorkspaceService: Loading initial workspace data...") initialDataQuery.running = true + initialWindowsQuery.running = true + initialFocusedWindowQuery.running = true } // Event stream for real-time updates @@ -117,6 +167,8 @@ Singleton { handleWindowClosed(event.WindowClosed) } else if (event.WindowFocusChanged) { handleWindowFocusChanged(event.WindowFocusChanged) + } else if (event.WindowOpenedOrChanged) { + handleWindowOpenedOrChanged(event.WindowOpenedOrChanged) } else if (event.OverviewOpenedOrClosed) { handleOverviewChanged(event.OverviewOpenedOrClosed) } @@ -192,6 +244,34 @@ Singleton { updateFocusedWindow() } + function handleWindowOpenedOrChanged(data) { + if (!data.window) return; + + const window = data.window; + const existingIndex = windows.findIndex(w => w.id === window.id); + + if (existingIndex >= 0) { + // Update existing window - create new array to trigger property change + let updatedWindows = [...windows]; + updatedWindows[existingIndex] = window; + windows = updatedWindows.sort((a, b) => a.id - b.id); + } else { + // Add new window + windows = [...windows, window].sort((a, b) => a.id - b.id); + } + + // Update focused window if this window is focused + if (window.is_focused) { + focusedWindowId = window.id; + focusedWindowIndex = windows.findIndex(w => w.id === window.id); + } + + updateFocusedWindow(); + + // Emit signal for other services to listen to + windowOpenedOrChanged(window); + } + function handleOverviewChanged(data) { inOverview = data.is_open }