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:
133
Services/FocusedWindowService.qml
Normal file
133
Services/FocusedWindowService.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
81
Widgets/TopBar/FocusedAppWidget.qml
Normal file
81
Widgets/TopBar/FocusedAppWidget.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user