mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-07 05:55:37 -05:00
dock: re-work to separate pins from all open windows
This commit is contained in:
@@ -20,19 +20,45 @@ Item {
|
||||
property point dragOffset: Qt.point(0, 0)
|
||||
property int targetIndex: -1
|
||||
property int originalIndex: -1
|
||||
property bool showWindowTitle: false
|
||||
property string windowTitle: ""
|
||||
property bool isHovered: mouseArea.containsMouse && !dragging
|
||||
property bool showTooltip: mouseArea.containsMouse && !dragging
|
||||
property string tooltipText: {
|
||||
if (!appData)
|
||||
return "";
|
||||
|
||||
// For window type, show app name + window title
|
||||
if (appData.type === "window" && showWindowTitle) {
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId);
|
||||
var appName = desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId;
|
||||
return appName + (windowTitle ? " • " + windowTitle : "");
|
||||
}
|
||||
// For pinned apps, just show app name
|
||||
if (!appData.appId)
|
||||
return "";
|
||||
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId);
|
||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId;
|
||||
}
|
||||
|
||||
width: 40
|
||||
height: 40
|
||||
onIsHoveredChanged: {
|
||||
if (isHovered) {
|
||||
exitAnimation.stop();
|
||||
if (!bounceAnimation.running)
|
||||
bounceAnimation.restart();
|
||||
|
||||
property bool isHovered: mouseArea.containsMouse && !dragging
|
||||
|
||||
transform: Translate {
|
||||
id: translateY
|
||||
y: 0
|
||||
} else {
|
||||
bounceAnimation.stop();
|
||||
exitAnimation.restart();
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: bounceAnimation
|
||||
|
||||
running: false
|
||||
|
||||
NumberAnimation {
|
||||
@@ -52,10 +78,12 @@ Item {
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Anims.emphasizedDecel
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: exitAnimation
|
||||
|
||||
running: false
|
||||
target: translateY
|
||||
property: "y"
|
||||
@@ -65,18 +93,6 @@ Item {
|
||||
easing.bezierCurve: Anims.emphasizedDecel
|
||||
}
|
||||
|
||||
onIsHoveredChanged: {
|
||||
if (isHovered) {
|
||||
exitAnimation.stop()
|
||||
if (!bounceAnimation.running) {
|
||||
bounceAnimation.restart()
|
||||
}
|
||||
} else {
|
||||
bounceAnimation.stop()
|
||||
exitAnimation.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
@@ -89,165 +105,133 @@ Item {
|
||||
|
||||
Timer {
|
||||
id: longPressTimer
|
||||
|
||||
interval: 500
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (appData && appData.isPinned) {
|
||||
longPressing = true
|
||||
}
|
||||
if (appData && appData.isPinned)
|
||||
longPressing = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: -20
|
||||
hoverEnabled: true
|
||||
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
|
||||
onPressed: mouse => {
|
||||
if (mouse.button === Qt.LeftButton && appData
|
||||
&& appData.isPinned) {
|
||||
dragStartPos = Qt.point(mouse.x, mouse.y)
|
||||
longPressTimer.start()
|
||||
onPressed: (mouse) => {
|
||||
if (mouse.button === Qt.LeftButton && appData && appData.isPinned) {
|
||||
dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||
longPressTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
longPressTimer.stop()
|
||||
onReleased: (mouse) => {
|
||||
longPressTimer.stop();
|
||||
if (longPressing) {
|
||||
if (dragging && targetIndex >= 0
|
||||
&& targetIndex !== originalIndex && dockApps) {
|
||||
dockApps.movePinnedApp(originalIndex, targetIndex)
|
||||
}
|
||||
if (dragging && targetIndex >= 0 && targetIndex !== originalIndex && dockApps)
|
||||
dockApps.movePinnedApp(originalIndex, targetIndex);
|
||||
|
||||
longPressing = false
|
||||
dragging = false
|
||||
dragOffset = Qt.point(0, 0)
|
||||
targetIndex = -1
|
||||
originalIndex = -1
|
||||
longPressing = false;
|
||||
dragging = false;
|
||||
dragOffset = Qt.point(0, 0);
|
||||
targetIndex = -1;
|
||||
originalIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
onPositionChanged: (mouse) => {
|
||||
if (longPressing && !dragging) {
|
||||
var distance = Math.sqrt(
|
||||
Math.pow(mouse.x - dragStartPos.x,
|
||||
2) + Math.pow(mouse.y - dragStartPos.y,
|
||||
2))
|
||||
var distance = Math.sqrt(Math.pow(mouse.x - dragStartPos.x, 2) + Math.pow(mouse.y - dragStartPos.y, 2));
|
||||
if (distance > 5) {
|
||||
dragging = true
|
||||
targetIndex = index
|
||||
originalIndex = index
|
||||
dragging = true;
|
||||
targetIndex = index;
|
||||
originalIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
if (dragging) {
|
||||
dragOffset = Qt.point(mouse.x - dragStartPos.x,
|
||||
mouse.y - dragStartPos.y)
|
||||
|
||||
dragOffset = Qt.point(mouse.x - dragStartPos.x, mouse.y - dragStartPos.y);
|
||||
if (dockApps) {
|
||||
var threshold = 40
|
||||
var newTargetIndex = targetIndex
|
||||
|
||||
if (dragOffset.x > threshold
|
||||
&& targetIndex < dockApps.pinnedAppCount - 1) {
|
||||
newTargetIndex = targetIndex + 1
|
||||
} else if (dragOffset.x < -threshold
|
||||
&& targetIndex > 0) {
|
||||
newTargetIndex = targetIndex - 1
|
||||
}
|
||||
|
||||
var threshold = 40;
|
||||
var newTargetIndex = targetIndex;
|
||||
if (dragOffset.x > threshold && targetIndex < dockApps.pinnedAppCount - 1)
|
||||
newTargetIndex = targetIndex + 1;
|
||||
else if (dragOffset.x < -threshold && targetIndex > 0)
|
||||
newTargetIndex = targetIndex - 1;
|
||||
if (newTargetIndex !== targetIndex) {
|
||||
targetIndex = newTargetIndex
|
||||
dragStartPos = Qt.point(mouse.x, mouse.y)
|
||||
targetIndex = newTargetIndex;
|
||||
dragStartPos = Qt.point(mouse.x, mouse.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: mouse => {
|
||||
onClicked: (mouse) => {
|
||||
if (!appData || longPressing)
|
||||
return
|
||||
return ;
|
||||
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
var windowCount = appData.windows ? appData.windows.count : 0
|
||||
|
||||
if (windowCount === 0) {
|
||||
// Handle based on type
|
||||
if (appData.type === "pinned") {
|
||||
// Launch the pinned app
|
||||
if (appData && appData.appId) {
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId)
|
||||
if (desktopEntry) {
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId);
|
||||
if (desktopEntry)
|
||||
AppUsageHistoryData.addAppUsage({
|
||||
"id": appData.appId,
|
||||
"name": desktopEntry.name
|
||||
|| appData.appId,
|
||||
"icon": desktopEntry.icon
|
||||
|| "",
|
||||
"exec": desktopEntry.exec
|
||||
|| "",
|
||||
"comment": desktopEntry.comment
|
||||
|| ""
|
||||
})
|
||||
"name": desktopEntry.name || appData.appId,
|
||||
"icon": desktopEntry.icon || "",
|
||||
"exec": desktopEntry.exec || "",
|
||||
"comment": desktopEntry.comment || ""
|
||||
});
|
||||
|
||||
Quickshell.execDetached(["gtk-launch", appData.appId]);
|
||||
}
|
||||
Quickshell.execDetached(["gtk-launch", appData.appId])
|
||||
}
|
||||
} else if (windowCount === 1) {
|
||||
var window = appData.windows.get(0)
|
||||
NiriService.focusWindow(window.id)
|
||||
} else {
|
||||
windowsMenu.showForButton(root, appData, 40)
|
||||
} else if (appData.type === "window") {
|
||||
// Focus the specific window
|
||||
if (appData.windowId)
|
||||
NiriService.focusWindow(appData.windowId);
|
||||
|
||||
}
|
||||
} else if (mouse.button === Qt.MiddleButton) {
|
||||
if (appData && appData.appId) {
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId)
|
||||
if (desktopEntry) {
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId);
|
||||
if (desktopEntry)
|
||||
AppUsageHistoryData.addAppUsage({
|
||||
"id": appData.appId,
|
||||
"name": desktopEntry.name
|
||||
|| appData.appId,
|
||||
"icon": desktopEntry.icon
|
||||
|| "",
|
||||
"exec": desktopEntry.exec
|
||||
|| "",
|
||||
"comment": desktopEntry.comment
|
||||
|| ""
|
||||
})
|
||||
}
|
||||
Quickshell.execDetached(["gtk-launch", appData.appId])
|
||||
"name": desktopEntry.name || appData.appId,
|
||||
"icon": desktopEntry.icon || "",
|
||||
"exec": desktopEntry.exec || "",
|
||||
"comment": desktopEntry.comment || ""
|
||||
});
|
||||
|
||||
Quickshell.execDetached(["gtk-launch", appData.appId]);
|
||||
}
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
if (contextMenu) {
|
||||
contextMenu.showForButton(root, appData, 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contextMenu)
|
||||
contextMenu.showForButton(root, appData, 40);
|
||||
|
||||
property bool showTooltip: mouseArea.containsMouse && !dragging
|
||||
property string tooltipText: {
|
||||
if (!appData || !appData.appId)
|
||||
return ""
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId)
|
||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconImage {
|
||||
id: iconImg
|
||||
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.centerIn: parent
|
||||
source: {
|
||||
if (!appData || !appData.appId)
|
||||
return ""
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId)
|
||||
return "";
|
||||
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId);
|
||||
if (desktopEntry && desktopEntry.icon) {
|
||||
var iconPath = Quickshell.iconPath(
|
||||
desktopEntry.icon,
|
||||
SettingsData.iconTheme === "System Default" ? "" : SettingsData.iconTheme)
|
||||
return iconPath
|
||||
var iconPath = Quickshell.iconPath(desktopEntry.icon, SettingsData.iconTheme === "System Default" ? "" : SettingsData.iconTheme);
|
||||
return iconPath;
|
||||
}
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
smooth: true
|
||||
mipmap: true
|
||||
@@ -270,44 +254,50 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
if (!appData || !appData.appId)
|
||||
return "?"
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId)
|
||||
if (desktopEntry && desktopEntry.name) {
|
||||
return desktopEntry.name.charAt(0).toUpperCase()
|
||||
}
|
||||
return appData.appId.charAt(0).toUpperCase()
|
||||
return "?";
|
||||
|
||||
var desktopEntry = DesktopEntries.byId(appData.appId);
|
||||
if (desktopEntry && desktopEntry.name)
|
||||
return desktopEntry.name.charAt(0).toUpperCase();
|
||||
|
||||
return appData.appId.charAt(0).toUpperCase();
|
||||
}
|
||||
font.pixelSize: 14
|
||||
color: Theme.primary
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
// Indicator for running/focused state
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: -2
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
model: appData && appData.windows ? Math.min(appData.windows.count, 4) : 0
|
||||
|
||||
Rectangle {
|
||||
width: appData && appData.windows && appData.windows.count <= 3 ? 5 : 3
|
||||
width: 8
|
||||
height: 2
|
||||
radius: 1
|
||||
visible: appData && (appData.isRunning || appData.type === "window")
|
||||
color: {
|
||||
if (!appData || !appData.windows || appData.windows.count === 0)
|
||||
return "transparent"
|
||||
var window = appData.windows.get(index)
|
||||
return window
|
||||
&& window.id == NiriService.focusedWindowId ? Theme.primary : Qt.rgba(
|
||||
Theme.surfaceText.r,
|
||||
Theme.surfaceText.g,
|
||||
Theme.surfaceText.b,
|
||||
0.6)
|
||||
}
|
||||
if (!appData)
|
||||
return "transparent";
|
||||
|
||||
// For window type, check if focused
|
||||
if (appData.type === "window" && appData.isFocused)
|
||||
return Theme.primary;
|
||||
|
||||
// For running apps, show dimmer indicator
|
||||
if (appData.isRunning || appData.type === "window")
|
||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6);
|
||||
|
||||
return "transparent";
|
||||
}
|
||||
}
|
||||
|
||||
transform: Translate {
|
||||
id: translateY
|
||||
|
||||
y: 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,84 +48,62 @@ Item {
|
||||
clear()
|
||||
|
||||
var items = []
|
||||
// Use ordered app IDs if available from Niri, fallback to unordered
|
||||
var runningApps = NiriService.niriAvailable && NiriService.getRunningAppIdsOrdered
|
||||
? NiriService.getRunningAppIdsOrdered()
|
||||
: NiriService.getRunningAppIds()
|
||||
var pinnedApps = [...(SessionData.pinnedApps || [])]
|
||||
var addedApps = new Set()
|
||||
|
||||
// First section: Pinned apps (always visible, not representing running windows)
|
||||
pinnedApps.forEach(appId => {
|
||||
var lowerAppId = appId.toLowerCase()
|
||||
if (!addedApps.has(lowerAppId)) {
|
||||
var windows = NiriService.getWindowsByAppId(
|
||||
appId)
|
||||
items.push({
|
||||
"type": "pinned",
|
||||
"appId": appId,
|
||||
"windows": windows,
|
||||
"windowId": -1, // Use -1 instead of null to avoid ListModel warnings
|
||||
"windowTitle": "",
|
||||
"workspaceId": -1, // Use -1 instead of null
|
||||
"isPinned": true,
|
||||
"isRunning": windows.length > 0
|
||||
"isRunning": false,
|
||||
"isFocused": false
|
||||
})
|
||||
addedApps.add(lowerAppId)
|
||||
}
|
||||
})
|
||||
|
||||
root.pinnedAppCount = pinnedApps.length
|
||||
var appUsageRanking = AppUsageHistoryData.appUsageRanking || {}
|
||||
|
||||
var unpinnedApps = []
|
||||
var unpinnedAppsSet = new Set()
|
||||
|
||||
// First: Add ALL currently running apps that aren't pinned
|
||||
// They come pre-ordered from NiriService if Niri is available
|
||||
runningApps.forEach(appId => {
|
||||
var lowerAppId = appId.toLowerCase()
|
||||
if (!addedApps.has(lowerAppId)) {
|
||||
unpinnedApps.push(appId)
|
||||
unpinnedAppsSet.add(lowerAppId)
|
||||
}
|
||||
})
|
||||
|
||||
// Then: Fill remaining slots up to 3 with recently used apps
|
||||
var remainingSlots = Math.max(0, 3 - unpinnedApps.length)
|
||||
if (remainingSlots > 0) {
|
||||
// Sort recent apps by usage
|
||||
var recentApps = []
|
||||
for (var appId in appUsageRanking) {
|
||||
var lowerAppId = appId.toLowerCase()
|
||||
if (!addedApps.has(lowerAppId) && !unpinnedAppsSet.has(
|
||||
lowerAppId)) {
|
||||
recentApps.push({
|
||||
"appId": appId,
|
||||
"lastUsed": appUsageRanking[appId].lastUsed
|
||||
|| 0
|
||||
})
|
||||
}
|
||||
}
|
||||
recentApps.sort((a, b) => b.lastUsed - a.lastUsed)
|
||||
|
||||
var recentToAdd = Math.min(remainingSlots, recentApps.length)
|
||||
for (var i = 0; i < recentToAdd; i++) {
|
||||
unpinnedApps.push(recentApps[i].appId)
|
||||
}
|
||||
}
|
||||
if (pinnedApps.length > 0 && unpinnedApps.length > 0) {
|
||||
// Add separator between pinned and running if both exist
|
||||
if (pinnedApps.length > 0 && NiriService.windows.length > 0) {
|
||||
items.push({
|
||||
"type": "separator",
|
||||
"appId": "__SEPARATOR__",
|
||||
"windows": [],
|
||||
"windowId": -1, // Use -1 instead of null
|
||||
"windowTitle": "",
|
||||
"workspaceId": -1, // Use -1 instead of null
|
||||
"isPinned": false,
|
||||
"isRunning": false
|
||||
"isRunning": false,
|
||||
"isFocused": false
|
||||
})
|
||||
}
|
||||
unpinnedApps.forEach(appId => {
|
||||
var windows = NiriService.getWindowsByAppId(
|
||||
appId)
|
||||
|
||||
// Second section: Running windows (sorted by display->workspace->position)
|
||||
// NiriService.windows is already sorted by sortWindowsByLayout
|
||||
NiriService.windows.forEach(window => {
|
||||
// Limit window title length for tooltip
|
||||
var title = window.title || "(Unnamed)"
|
||||
if (title.length > 50) {
|
||||
title = title.substring(0, 47) + "..."
|
||||
}
|
||||
|
||||
// Check if this window is focused - compare as numbers
|
||||
var isFocused = window.id == NiriService.focusedWindowId
|
||||
|
||||
items.push({
|
||||
"appId": appId,
|
||||
"windows": windows,
|
||||
"type": "window",
|
||||
"appId": window.app_id || "",
|
||||
"windowId": window.id || -1,
|
||||
"windowTitle": title,
|
||||
"workspaceId": window.workspace_id || -1,
|
||||
"isPinned": false,
|
||||
"isRunning": windows.length > 0
|
||||
"isRunning": true,
|
||||
"isFocused": isFocused
|
||||
})
|
||||
})
|
||||
|
||||
items.forEach(item => {
|
||||
append(item)
|
||||
})
|
||||
@@ -136,11 +114,11 @@ Item {
|
||||
id: delegateItem
|
||||
property alias dockButton: button
|
||||
|
||||
width: model.appId === "__SEPARATOR__" ? 16 : 40
|
||||
width: model.type === "separator" ? 16 : 40
|
||||
height: 40
|
||||
|
||||
Rectangle {
|
||||
visible: model.appId === "__SEPARATOR__"
|
||||
visible: model.type === "separator"
|
||||
width: 2
|
||||
height: 20
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
||||
@@ -150,7 +128,7 @@ Item {
|
||||
|
||||
DockAppButton {
|
||||
id: button
|
||||
visible: model.appId !== "__SEPARATOR__"
|
||||
visible: model.type !== "separator"
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: 40
|
||||
@@ -161,6 +139,10 @@ Item {
|
||||
windowsMenu: root.windowsMenu
|
||||
dockApps: root
|
||||
index: model.index
|
||||
|
||||
// Override tooltip for windows to show window title
|
||||
showWindowTitle: model.type === "window"
|
||||
windowTitle: model.windowTitle || ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,7 +156,11 @@ Item {
|
||||
function onWindowOpenedOrChanged() {
|
||||
dockModel.updateModel()
|
||||
}
|
||||
function onFocusedWindowIdChanged() {
|
||||
dockModel.updateModel()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: SessionData
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
@@ -12,24 +13,18 @@ Rectangle {
|
||||
readonly property int maxNormalWidth: 456
|
||||
readonly property int maxCompactWidth: 288
|
||||
|
||||
width: compactMode ? Math.min(baseWidth,
|
||||
maxCompactWidth) : Math.min(baseWidth,
|
||||
maxNormalWidth)
|
||||
width: compactMode ? Math.min(baseWidth, maxCompactWidth) : Math.min(baseWidth, maxNormalWidth)
|
||||
height: 30
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (!FocusedWindowService.focusedAppName
|
||||
&& !FocusedWindowService.focusedWindowTitle)
|
||||
return "transparent"
|
||||
if (!NiriService.focusedWindowTitle)
|
||||
return "transparent";
|
||||
|
||||
const baseColor = mouseArea.containsMouse ? Theme.primaryHover : Theme.surfaceTextHover
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b,
|
||||
baseColor.a * Theme.widgetTransparency)
|
||||
const baseColor = mouseArea.containsMouse ? Theme.primaryHover : Theme.surfaceTextHover;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
clip: true
|
||||
visible: FocusedWindowService.niriAvailable
|
||||
&& (FocusedWindowService.focusedAppName
|
||||
|| FocusedWindowService.focusedWindowTitle)
|
||||
visible: NiriService.niriAvailable && NiriService.focusedWindowTitle
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
@@ -40,7 +35,19 @@ Rectangle {
|
||||
StyledText {
|
||||
id: appText
|
||||
|
||||
text: FocusedWindowService.focusedAppName || ""
|
||||
text: {
|
||||
if (!NiriService.focusedWindowId)
|
||||
return "";
|
||||
|
||||
var window = NiriService.windows.find((w) => {
|
||||
return w.id == NiriService.focusedWindowId;
|
||||
});
|
||||
if (!window || !window.app_id)
|
||||
return "";
|
||||
|
||||
var desktopEntry = DesktopEntries.byId(window.app_id);
|
||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : window.app_id;
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
@@ -62,7 +69,7 @@ Rectangle {
|
||||
StyledText {
|
||||
id: titleText
|
||||
|
||||
text: FocusedWindowService.focusedWindowTitle || ""
|
||||
text: NiriService.focusedWindowTitle || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
@@ -72,6 +79,7 @@ Rectangle {
|
||||
width: Math.min(implicitWidth, compactMode ? 280 : 250)
|
||||
visible: text.length > 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -86,6 +94,7 @@ Rectangle {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
@@ -93,5 +102,7 @@ Rectangle {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,10 +13,8 @@ Rectangle {
|
||||
property var parentScreen
|
||||
property var hoveredItem: null
|
||||
property var topBar: null
|
||||
|
||||
// The visual root for this window
|
||||
property Item windowRoot: (Window.window ? Window.window.contentItem : null)
|
||||
|
||||
readonly property int windowCount: NiriService.windows.length
|
||||
readonly property int calculatedWidth: windowCount > 0 ? windowCount * 24 + (windowCount - 1) * Theme.spacingXS + Theme.spacingS * 2 : 0
|
||||
|
||||
@@ -27,35 +25,37 @@ Rectangle {
|
||||
clip: false
|
||||
color: {
|
||||
if (windowCount === 0)
|
||||
return "transparent"
|
||||
return "transparent";
|
||||
|
||||
const baseColor = Theme.secondaryHover
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b,
|
||||
baseColor.a * Theme.widgetTransparency)
|
||||
const baseColor = Theme.secondaryHover;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
Row {
|
||||
id: windowRow
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Repeater {
|
||||
id: windowRepeater
|
||||
|
||||
model: NiriService.windows
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
property bool isFocused: String(modelData.id) === String(FocusedWindowService.focusedWindowId)
|
||||
|
||||
property bool isFocused: String(modelData.id) === String(NiriService.focusedWindowId)
|
||||
property string appId: modelData.app_id || ""
|
||||
property string windowTitle: modelData.title || "(Unnamed)"
|
||||
property int windowId: modelData.id
|
||||
property string tooltipText: {
|
||||
var appName = "Unknown"
|
||||
var appName = "Unknown";
|
||||
if (appId) {
|
||||
var desktopEntry = DesktopEntries.byId(appId)
|
||||
appName = desktopEntry && desktopEntry.name ? desktopEntry.name : appId
|
||||
var desktopEntry = DesktopEntries.byId(appId);
|
||||
appName = desktopEntry && desktopEntry.name ? desktopEntry.name : appId;
|
||||
}
|
||||
return appName + (windowTitle ? " • " + windowTitle : "")
|
||||
return appName + (windowTitle ? " • " + windowTitle : "");
|
||||
}
|
||||
|
||||
width: 24
|
||||
@@ -65,11 +65,10 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (isFocused) {
|
||||
return mouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||
} else {
|
||||
return mouseArea.containsMouse ? Qt.rgba(Theme.primaryHover.r, Theme.primaryHover.g, Theme.primaryHover.b, 0.1) : "transparent"
|
||||
}
|
||||
if (isFocused)
|
||||
return mouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2);
|
||||
else
|
||||
return mouseArea.containsMouse ? Qt.rgba(Theme.primaryHover.r, Theme.primaryHover.g, Theme.primaryHover.b, 0.1) : "transparent";
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
@@ -77,25 +76,28 @@ Rectangle {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// App icon
|
||||
IconImage {
|
||||
id: iconImg
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 18
|
||||
height: 18
|
||||
source: {
|
||||
if (!appId) return ""
|
||||
var desktopEntry = DesktopEntries.byId(appId)
|
||||
if (!appId)
|
||||
return "";
|
||||
|
||||
var desktopEntry = DesktopEntries.byId(appId);
|
||||
if (desktopEntry && desktopEntry.icon) {
|
||||
var iconPath = Quickshell.iconPath(
|
||||
desktopEntry.icon,
|
||||
SettingsData.iconTheme === "System Default" ? "" : SettingsData.iconTheme)
|
||||
return iconPath
|
||||
var iconPath = Quickshell.iconPath(desktopEntry.icon, SettingsData.iconTheme === "System Default" ? "" : SettingsData.iconTheme);
|
||||
return iconPath;
|
||||
}
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
smooth: true
|
||||
mipmap: true
|
||||
@@ -108,12 +110,14 @@ Rectangle {
|
||||
anchors.centerIn: parent
|
||||
visible: !iconImg.visible
|
||||
text: {
|
||||
if (!appId) return "?"
|
||||
var desktopEntry = DesktopEntries.byId(appId)
|
||||
if (desktopEntry && desktopEntry.name) {
|
||||
return desktopEntry.name.charAt(0).toUpperCase()
|
||||
}
|
||||
return appId.charAt(0).toUpperCase()
|
||||
if (!appId)
|
||||
return "?";
|
||||
|
||||
var desktopEntry = DesktopEntries.byId(appId);
|
||||
if (desktopEntry && desktopEntry.name)
|
||||
return desktopEntry.name.charAt(0).toUpperCase();
|
||||
|
||||
return appId.charAt(0).toUpperCase();
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
@@ -122,40 +126,47 @@ Rectangle {
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
NiriService.focusWindow(windowId)
|
||||
NiriService.focusWindow(windowId);
|
||||
}
|
||||
onEntered: {
|
||||
root.hoveredItem = delegateItem
|
||||
var globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height)
|
||||
tooltipLoader.active = true
|
||||
root.hoveredItem = delegateItem;
|
||||
var globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height);
|
||||
tooltipLoader.active = true;
|
||||
if (tooltipLoader.item) {
|
||||
var tooltipY = Theme.barHeight + SettingsData.topBarSpacing + Theme.spacingXS
|
||||
tooltipLoader.item.showTooltip(delegateItem.tooltipText, globalPos.x, tooltipY, root.parentScreen)
|
||||
var tooltipY = Theme.barHeight + SettingsData.topBarSpacing + Theme.spacingXS;
|
||||
tooltipLoader.item.showTooltip(delegateItem.tooltipText, globalPos.x, tooltipY, root.parentScreen);
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
if (root.hoveredItem === delegateItem) {
|
||||
root.hoveredItem = null
|
||||
if (tooltipLoader.item) {
|
||||
tooltipLoader.item.hideTooltip()
|
||||
}
|
||||
tooltipLoader.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
root.hoveredItem = null;
|
||||
if (tooltipLoader.item)
|
||||
tooltipLoader.item.hideTooltip();
|
||||
|
||||
tooltipLoader.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: tooltipLoader
|
||||
|
||||
active: false
|
||||
|
||||
sourceComponent: RunningAppsTooltip {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property bool niriAvailable: false
|
||||
property string focusedAppId: ""
|
||||
property string focusedAppName: ""
|
||||
property string focusedWindowTitle: ""
|
||||
property int focusedWindowId: -1
|
||||
|
||||
function updateFromNiriData() {
|
||||
if (!root.niriAvailable) {
|
||||
clearFocusedWindow()
|
||||
return
|
||||
}
|
||||
|
||||
let focusedWindow = NiriService.windows.find(w => w.is_focused)
|
||||
|
||||
if (focusedWindow) {
|
||||
root.focusedAppId = focusedWindow.app_id || ""
|
||||
root.focusedWindowTitle = focusedWindow.title || ""
|
||||
root.focusedAppName = getDisplayName(focusedWindow.app_id || "")
|
||||
root.focusedWindowId = parseInt(focusedWindow.id) || -1
|
||||
} else {
|
||||
setWorkspaceFallback()
|
||||
}
|
||||
}
|
||||
|
||||
function clearFocusedWindow() {
|
||||
root.focusedAppId = ""
|
||||
root.focusedAppName = ""
|
||||
root.focusedWindowTitle = ""
|
||||
root.focusedWindowId = -1
|
||||
}
|
||||
|
||||
function setWorkspaceFallback() {
|
||||
if (NiriService.focusedWorkspaceIndex >= 0 && NiriService.allWorkspaces.length > 0) {
|
||||
const workspace = NiriService.allWorkspaces[NiriService.focusedWorkspaceIndex]
|
||||
if (workspace) {
|
||||
root.focusedAppId = "niri"
|
||||
root.focusedAppName = "niri"
|
||||
if (workspace.name && workspace.name.length > 0) {
|
||||
root.focusedWindowTitle = workspace.name
|
||||
} else {
|
||||
root.focusedWindowTitle = "workspace " + workspace.idx
|
||||
}
|
||||
root.focusedWindowId = -1
|
||||
} else {
|
||||
clearFocusedWindow()
|
||||
}
|
||||
} else {
|
||||
clearFocusedWindow()
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplayName(appId) {
|
||||
if (!appId)
|
||||
return ""
|
||||
const desktopEntry = DesktopEntries.byId(appId)
|
||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : ""
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.niriAvailable = NiriService.niriAvailable
|
||||
NiriService.onNiriAvailableChanged.connect(() => {
|
||||
root.niriAvailable = NiriService.niriAvailable
|
||||
if (root.niriAvailable)
|
||||
updateFromNiriData()
|
||||
})
|
||||
if (root.niriAvailable)
|
||||
updateFromNiriData()
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onFocusedWindowIdChanged() {
|
||||
const focusedWindowId = NiriService.focusedWindowId
|
||||
if (!focusedWindowId) {
|
||||
setWorkspaceFallback()
|
||||
return
|
||||
}
|
||||
|
||||
const focusedWindow = NiriService.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 {
|
||||
setWorkspaceFallback()
|
||||
}
|
||||
}
|
||||
|
||||
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: NiriService
|
||||
}
|
||||
}
|
||||
@@ -307,6 +307,17 @@ Singleton {
|
||||
|
||||
function handleWindowsChanged(data) {
|
||||
windows = sortWindowsByLayout(data.windows)
|
||||
|
||||
// Extract focused window from initial state
|
||||
var focusedWindow = windows.find(w => w.is_focused)
|
||||
if (focusedWindow) {
|
||||
focusedWindowId = String(focusedWindow.id)
|
||||
focusedWindowIndex = windows.findIndex(w => w.id === focusedWindow.id)
|
||||
} else {
|
||||
focusedWindowId = ""
|
||||
focusedWindowIndex = -1
|
||||
}
|
||||
|
||||
updateFocusedWindow()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user