1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 14:05:38 -05:00

Focused window service

This commit is contained in:
bbedward
2025-07-13 11:37:33 -04:00
parent 7981bbdbc2
commit 755f13e06b
5 changed files with 221 additions and 1 deletions

View File

@@ -0,0 +1,133 @@
import QtQuick
import Quickshell
import Quickshell.Io
pragma Singleton
pragma ComponentBehavior: Bound
Singleton {
id: root
property bool niriAvailable: false
property string focusedAppId: ""
property string focusedAppName: ""
property string focusedWindowTitle: ""
property int focusedWindowId: -1
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) {
loadInitialFocusedWindow()
}
})
if (root.niriAvailable) {
loadInitialFocusedWindow()
}
}
// Listen to window focus changes from NiriWorkspaceService
Connections {
target: NiriWorkspaceService
function onFocusedWindowIdChanged() {
root.focusedWindowId = NiriWorkspaceService.focusedWindowId
updateFocusedWindowData()
}
function onFocusedWindowTitleChanged() {
root.focusedWindowTitle = NiriWorkspaceService.focusedWindowTitle
}
}
// Process to get focused window info
Process {
id: focusedWindowQuery
command: ["niri", "msg", "--json", "focused-window"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
try {
const windowData = JSON.parse(text.trim())
root.focusedAppId = windowData.app_id || ""
root.focusedWindowTitle = windowData.title || ""
root.focusedAppName = getDisplayName(windowData.app_id || "")
root.focusedWindowId = windowData.id || -1
} catch (e) {
console.warn("FocusedWindowService: Failed to parse focused window data:", e)
clearFocusedWindow()
}
} 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() {
root.focusedAppId = ""
root.focusedAppName = ""
root.focusedWindowTitle = ""
}
// Convert app_id to a more user-friendly display name
function getDisplayName(appId) {
if (!appId) return ""
// Common app_id to display name mappings
const appNames = {
"com.mitchellh.ghostty": "Ghostty",
"org.mozilla.firefox": "Firefox",
"org.gnome.Nautilus": "Files",
"org.gnome.TextEditor": "Text Editor",
"com.google.Chrome": "Chrome",
"org.telegram.desktop": "Telegram",
"com.spotify.Client": "Spotify",
"org.kde.konsole": "Konsole",
"org.gnome.Terminal": "Terminal",
"code": "VS Code",
"code-oss": "VS Code",
"org.mozilla.Thunderbird": "Thunderbird",
"org.libreoffice.LibreOffice": "LibreOffice",
"org.gimp.GIMP": "GIMP",
"org.blender.Blender": "Blender",
"discord": "Discord",
"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 => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
return cleanName
}
}

View File

@@ -13,3 +13,4 @@ singleton LauncherService 1.0 LauncherService.qml
singleton NiriWorkspaceService 1.0 NiriWorkspaceService.qml singleton NiriWorkspaceService 1.0 NiriWorkspaceService.qml
singleton CalendarService 1.0 CalendarService.qml singleton CalendarService 1.0 CalendarService.qml
singleton UserInfoService 1.0 UserInfoService.qml singleton UserInfoService 1.0 UserInfoService.qml
singleton FocusedWindowService 1.0 FocusedWindowService.qml

View File

@@ -0,0 +1,81 @@
import QtQuick
import "../../Common"
import "../../Services"
Rectangle {
id: root
width: Math.max(contentRow.implicitWidth + Theme.spacingS * 2, 60)
height: 30
radius: Theme.cornerRadius
color: mouseArea.containsMouse ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
visible: FocusedWindowService.niriAvailable && (FocusedWindowService.focusedAppName || FocusedWindowService.focusedWindowTitle)
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Row {
id: contentRow
anchors.centerIn: parent
spacing: Theme.spacingS
Text {
id: appText
text: FocusedWindowService.focusedAppName || ""
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
// Limit app name width
elide: Text.ElideRight
maximumLineCount: 1
width: Math.min(implicitWidth, 120)
}
Text {
text: "•"
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
anchors.verticalCenter: parent.verticalCenter
visible: appText.text && titleText.text
}
Text {
id: titleText
text: FocusedWindowService.focusedWindowTitle || ""
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
// Limit title width - increased for longer titles
elide: Text.ElideRight
maximumLineCount: 1
width: Math.min(implicitWidth, 350)
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
// Non-interactive widget - just provides hover state for visual feedback
}
// Smooth width animation when the text changes
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}

View File

@@ -156,6 +156,10 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
screenName: topBar.screenName screenName: topBar.screenName
} }
FocusedAppWidget {
anchors.verticalCenter: parent.verticalCenter
}
} }
ClockWidget { ClockWidget {

View File

@@ -1,6 +1,7 @@
TopBar 1.0 TopBar.qml TopBar 1.0 TopBar.qml
LauncherButton 1.0 LauncherButton.qml LauncherButton 1.0 LauncherButton.qml
WorkspaceSwitcher 1.0 WorkspaceSwitcher.qml WorkspaceSwitcher 1.0 WorkspaceSwitcher.qml
FocusedAppWidget 1.0 FocusedAppWidget.qml
ClockWidget 1.0 ClockWidget.qml ClockWidget 1.0 ClockWidget.qml
MediaWidget 1.0 MediaWidget.qml MediaWidget 1.0 MediaWidget.qml
WeatherWidget 1.0 WeatherWidget.qml WeatherWidget 1.0 WeatherWidget.qml