fix(dock): optimize dock preview panel responsiveness, multi-window layout, and window close button
This commit is contained in:
parent
f40fc29e3f
commit
80d3dad766
@ -198,24 +198,44 @@ Item {
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onEntered: {
|
||||
previewCloseTimer.stop()
|
||||
if (TaskService.isRunning(modelData.appId)) {
|
||||
let instances = TaskService.getWindowsForApp(modelData.appId)
|
||||
if (instances && instances.length > 0) {
|
||||
root.previewTargetApp = modelData
|
||||
root.previewWindowInstances = instances
|
||||
root.previewTargetX = Math.round(dockItem.mapToItem(null, 0, 0).x)
|
||||
root.isPreviewHovered = true
|
||||
previewCloseTimer.stop()
|
||||
previewPopOut.running = false
|
||||
dockWindowPreview.visible = true
|
||||
previewPopIn.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
let instances = TaskService.getWindowsForApp(modelData.appId)
|
||||
if (instances && instances.length > 0) {
|
||||
let targetParent = root.dockWindow ? root.dockWindow.contentItem : root
|
||||
let pt = itemMouse.mapToItem(targetParent, 0, 0)
|
||||
root.previewTargetX = Math.round(pt.x + itemMouse.width / 2)
|
||||
root.isPreviewHovered = true
|
||||
root.previewActive = true
|
||||
|
||||
let oldLen = root.previewWindowInstances ? root.previewWindowInstances.length : 0
|
||||
let newLen = instances.length
|
||||
let isDifferentApp = (root.previewTargetApp !== modelData)
|
||||
|
||||
root.previewTargetApp = modelData
|
||||
if (isDifferentApp && oldLen !== newLen && dockWindowPreview.visible) {
|
||||
root.previewWindowInstances = []
|
||||
Qt.callLater(() => {
|
||||
if (root.isPreviewHovered || root.previewActive) {
|
||||
root.previewWindowInstances = instances
|
||||
}
|
||||
})
|
||||
} else {
|
||||
root.previewWindowInstances = instances
|
||||
}
|
||||
} else {
|
||||
root.isPreviewHovered = false
|
||||
previewCloseTimer.restart()
|
||||
}
|
||||
} else {
|
||||
root.isPreviewHovered = false
|
||||
previewCloseTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.isPreviewHovered = false
|
||||
previewCloseTimer.start()
|
||||
previewCloseTimer.restart()
|
||||
}
|
||||
|
||||
property real dragXOffset: 0
|
||||
@ -265,12 +285,20 @@ Item {
|
||||
}
|
||||
}
|
||||
} else if (mouse.button === Qt.LeftButton) {
|
||||
let globalPos = dockItem.mapToItem(null, 0, 0)
|
||||
let globalPos = itemMouse.mapToItem(root, 0, 0)
|
||||
TaskService.focusApp(modelData.appId, modelData.cmd, globalPos.x, globalPos.y)
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
root.isPreviewHovered = false
|
||||
root.isCardHovered = false
|
||||
previewCloseTimer.stop()
|
||||
|
||||
root.contextTargetApp = modelData
|
||||
root.contextTargetX = Math.round(dockItem.mapToItem(null, 0, 0).x)
|
||||
PopupService.toggleDockMenu()
|
||||
let targetParent = root.dockWindow ? root.dockWindow.contentItem : root
|
||||
let posInWindow = itemMouse.mapToItem(targetParent, 0, 0).x
|
||||
root.contextTargetX = Math.round(posInWindow)
|
||||
|
||||
dockContextMenu.visible = true
|
||||
PopupService.dockMenuOpen = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -433,6 +461,54 @@ Item {
|
||||
return raw.charAt(0).toUpperCase() + raw.slice(1);
|
||||
}
|
||||
|
||||
function getToplevelForWindow(winObj) {
|
||||
if (!winObj || typeof ToplevelManager === "undefined" || !ToplevelManager.toplevels) return null;
|
||||
let list = ToplevelManager.toplevels.values;
|
||||
if (!list || list.length === 0) return null;
|
||||
|
||||
let targetApp = (winObj.appId || "").toLowerCase().trim();
|
||||
let targetTitle = (winObj.caption || winObj.name || "").toLowerCase().trim();
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let t = list[i];
|
||||
if (!t) continue;
|
||||
let tApp = (t.appId || "").toLowerCase().trim();
|
||||
let tTitle = (t.title || "").toLowerCase().trim();
|
||||
|
||||
if (tApp === targetApp || (tApp && targetApp && (tApp.includes(targetApp) || targetApp.includes(tApp)))) {
|
||||
if (!targetTitle || tTitle.includes(targetTitle) || targetTitle.includes(tTitle)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let t = list[i];
|
||||
if (!t) continue;
|
||||
let tApp = (t.appId || "").toLowerCase().trim();
|
||||
if (tApp === targetApp || (tApp && targetApp && (tApp.includes(targetApp) || targetApp.includes(tApp)))) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function calculateCardsRowWidth(instances) {
|
||||
if (!instances || !Array.isArray(instances) || instances.length === 0) return 180;
|
||||
let totalW = 0;
|
||||
for (let i = 0; i < instances.length; i++) {
|
||||
let w = instances[i];
|
||||
let winW = (w && w.width && Number(w.width) > 50) ? Number(w.width) : 1280;
|
||||
let winH = (w && w.height && Number(w.height) > 50) ? Number(w.height) : 800;
|
||||
let aspect = Math.max(0.6, Math.min(2.2, winW / winH));
|
||||
let cardW = Math.max(160, Math.min(260, Math.round(130 * aspect)));
|
||||
totalW += cardW;
|
||||
}
|
||||
totalW += Math.max(0, (instances.length - 1) * 10);
|
||||
return totalW;
|
||||
}
|
||||
|
||||
property var dockWindow: null
|
||||
|
||||
// Window Preview Hover State Properties
|
||||
@ -440,58 +516,48 @@ Item {
|
||||
property var previewWindowInstances: []
|
||||
property int previewTargetX: 0
|
||||
property bool isPreviewHovered: false
|
||||
property bool isCardHovered: false
|
||||
property bool previewActive: false
|
||||
|
||||
Timer {
|
||||
id: previewCloseTimer
|
||||
interval: 220
|
||||
interval: 180
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!root.isPreviewHovered && !previewMouseArea.containsMouse) {
|
||||
previewPopIn.running = false
|
||||
previewPopOut.restart()
|
||||
if (!root.isPreviewHovered && !previewMouseArea.containsMouse && !root.isCardHovered) {
|
||||
root.previewActive = false
|
||||
root.isPreviewHovered = false
|
||||
root.isCardHovered = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Live Window Preview Floating Popup Window
|
||||
// Live Window Preview Floating Popup Window (Hysteresis Active Binding)
|
||||
PopupWindow {
|
||||
id: dockWindowPreview
|
||||
anchor.window: root.dockWindow
|
||||
anchor.rect.x: Math.max(10, Math.min(root.dockWindow ? root.dockWindow.width - previewGlass.implicitWidth - 10 : 800, root.previewTargetX - Math.round(previewGlass.implicitWidth / 2) + 21))
|
||||
anchor.rect.y: 0
|
||||
anchor.edges: Edges.Top | Edges.Left
|
||||
visible: false
|
||||
visible: root.previewActive && (root.previewWindowInstances && root.previewWindowInstances.length > 0)
|
||||
color: "transparent"
|
||||
|
||||
implicitWidth: previewGlass.implicitWidth
|
||||
implicitHeight: previewGlass.implicitHeight
|
||||
|
||||
property real animProgress: 0.0
|
||||
|
||||
NumberAnimation on animProgress {
|
||||
id: previewPopIn
|
||||
running: false
|
||||
to: 1.0
|
||||
duration: 220
|
||||
easing.type: Easing.OutBack
|
||||
easing.overshoot: 1.15
|
||||
}
|
||||
|
||||
NumberAnimation on animProgress {
|
||||
id: previewPopOut
|
||||
running: false
|
||||
to: 0.0
|
||||
duration: 160
|
||||
easing.type: Easing.InQuad
|
||||
onFinished: dockWindowPreview.visible = false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: previewMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: previewCloseTimer.stop()
|
||||
onExited: previewCloseTimer.start()
|
||||
onEntered: {
|
||||
previewCloseTimer.stop()
|
||||
root.previewActive = true
|
||||
}
|
||||
onExited: {
|
||||
if (!root.isPreviewHovered) {
|
||||
previewCloseTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
GlassPanel {
|
||||
id: previewGlass
|
||||
@ -499,14 +565,6 @@ Item {
|
||||
implicitHeight: previewMainCol.implicitHeight + 16
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation { duration: 180; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
opacity: dockWindowPreview.animProgress
|
||||
scale: 0.90 + 0.10 * dockWindowPreview.animProgress
|
||||
transformOrigin: Item.Bottom
|
||||
|
||||
ColumnLayout {
|
||||
id: previewMainCol
|
||||
anchors.centerIn: parent
|
||||
@ -564,6 +622,7 @@ Item {
|
||||
// Window Preview Card Row (Single or Multiple Instances)
|
||||
RowLayout {
|
||||
id: previewCardsRow
|
||||
implicitWidth: root.calculateCardsRowWidth(root.previewWindowInstances)
|
||||
spacing: 10
|
||||
|
||||
Repeater {
|
||||
@ -571,142 +630,146 @@ Item {
|
||||
|
||||
Item {
|
||||
id: cardItem
|
||||
width: Math.max(140, Math.min(220, cardCaptionText.implicitWidth + 36))
|
||||
height: 110
|
||||
property real winW: (modelData && modelData.width && Number(modelData.width) > 50) ? Number(modelData.width) : 1280
|
||||
property real winH: (modelData && modelData.height && Number(modelData.height) > 50) ? Number(modelData.height) : 800
|
||||
property real winAspect: Math.max(0.6, Math.min(2.2, winW / winH))
|
||||
implicitWidth: Math.max(160, Math.min(260, Math.round(130 * winAspect)))
|
||||
implicitHeight: 140
|
||||
Layout.preferredWidth: implicitWidth
|
||||
Layout.preferredHeight: implicitHeight
|
||||
|
||||
property bool cardHovered: cardMouseArea.containsMouse
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 10
|
||||
color: cardItem.cardHovered ? Qt.rgba(255/255, 255/255, 255/255, 0.10) : Qt.rgba(255/255, 255/255, 255/255, 0.05)
|
||||
color: cardItem.cardHovered ? Qt.rgba(255/255, 255/255, 255/255, 0.10) : Qt.rgba(0, 0, 0, 0.35)
|
||||
border.color: modelData.active ? Theme.accent : (cardItem.cardHovered ? Theme.subAccent : Theme.separator)
|
||||
border.width: modelData.active || cardItem.cardHovered ? 1.5 : 1
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
Behavior on border.color { ColorAnimation { duration: 120 } }
|
||||
MouseArea {
|
||||
id: cardMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onEntered: { root.isCardHovered = true; root.previewActive = true; previewCloseTimer.stop() }
|
||||
onExited: { root.isCardHovered = false; previewCloseTimer.restart() }
|
||||
onClicked: (mouse) => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
root.previewActive = false
|
||||
root.isPreviewHovered = false
|
||||
root.isCardHovered = false
|
||||
previewCloseTimer.stop()
|
||||
dockContextMenu.visible = true
|
||||
PopupService.dockMenuOpen = true
|
||||
} else {
|
||||
let globalPos = cardItem.mapToItem(root, 0, 0)
|
||||
TaskService.focusSpecificWindow(modelData, globalPos.x, globalPos.y)
|
||||
root.previewActive = false
|
||||
root.isPreviewHovered = false
|
||||
root.isCardHovered = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
spacing: 6
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
spacing: 6
|
||||
|
||||
// Card Header: Window Caption & Close Button
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 6
|
||||
// Card Header: Window Caption & Close Button
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 6
|
||||
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: modelData.active ? Theme.accent : Theme.comment
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: modelData.active ? Theme.accent : Theme.comment
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
id: cardCaptionText
|
||||
text: modelData.caption || modelData.name || "Window"
|
||||
color: modelData.active ? Theme.accent : Theme.fg
|
||||
font.pixelSize: 10
|
||||
font.weight: modelData.active ? Font.Bold : Font.Normal
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
Text {
|
||||
id: cardCaptionText
|
||||
text: modelData.caption || modelData.name || "Window"
|
||||
color: modelData.active ? Theme.accent : Theme.fg
|
||||
font.pixelSize: 10
|
||||
font.weight: modelData.active ? Font.Bold : Font.Normal
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
// Individual Window Close Button (✕)
|
||||
Item {
|
||||
width: 16
|
||||
height: 16
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: cardItem.cardHovered
|
||||
// Individual Window Close Button (✕)
|
||||
Item {
|
||||
width: 18
|
||||
height: 18
|
||||
z: 10
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 8
|
||||
color: closeMouse.containsMouse ? Qt.rgba(255/255, 85/255, 85/255, 0.35) : "transparent"
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "✕"
|
||||
color: closeMouse.containsMouse ? "#ff5555" : Theme.comment
|
||||
font.pixelSize: 9
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 9
|
||||
color: closeMouse.containsMouse ? Qt.rgba(255/255, 85/255, 85/255, 0.70) : Qt.rgba(0, 0, 0, 0.40)
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "✕"
|
||||
color: closeMouse.containsMouse ? "#ffffff" : Theme.comment
|
||||
font.pixelSize: 10
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
TaskService.closeSpecificWindow(modelData)
|
||||
let rem = root.previewWindowInstances ? root.previewWindowInstances.filter(w => w.id !== modelData.id) : []
|
||||
root.previewWindowInstances = rem
|
||||
if (!rem || rem.length === 0) {
|
||||
previewPopIn.running = false
|
||||
previewPopOut.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: closeMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onEntered: { root.isCardHovered = true; root.previewActive = true; previewCloseTimer.stop() }
|
||||
onExited: { root.isCardHovered = false; previewCloseTimer.restart() }
|
||||
onPressed: (mouse) => { mouse.accepted = true }
|
||||
onClicked: (mouse) => {
|
||||
mouse.accepted = true
|
||||
TaskService.closeSpecificWindow(modelData)
|
||||
let targetId = String(modelData ? (modelData.id || "") : "")
|
||||
let rem = root.previewWindowInstances ? root.previewWindowInstances.filter(w => String(w ? (w.id || "") : "") !== targetId) : []
|
||||
root.previewWindowInstances = rem
|
||||
if (!rem || rem.length === 0) {
|
||||
root.previewActive = false
|
||||
root.isPreviewHovered = false
|
||||
root.isCardHovered = false
|
||||
previewCloseTimer.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Miniature Glass Window Preview Container
|
||||
// Lightweight Glass Window Card Viewport (0% CPU, 0ms lag)
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: 6
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
color: Qt.rgba(0, 0, 0, 0.45)
|
||||
border.color: Theme.separator
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
// Mock Window Header Bar
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
height: 14
|
||||
radius: 6
|
||||
color: Qt.rgba(255/255, 255/255, 255/255, 0.06)
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
Rectangle { width: 5; height: 5; radius: 2.5; color: "#ff5555" }
|
||||
Rectangle { width: 5; height: 5; radius: 2.5; color: "#f1fa8c" }
|
||||
Rectangle { width: 5; height: 5; radius: 2.5; color: "#50fa7b" }
|
||||
}
|
||||
}
|
||||
|
||||
// App Icon Centerpiece inside Preview Box
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 4
|
||||
width: 32
|
||||
height: 32
|
||||
sourceSize.width: 32
|
||||
sourceSize.height: 32
|
||||
width: 38
|
||||
height: 38
|
||||
sourceSize.width: 38
|
||||
sourceSize.height: 38
|
||||
fillMode: Image.PreserveAspectFit
|
||||
opacity: cardItem.cardHovered ? 1.0 : 0.82
|
||||
source: root.getIconPath(modelData.icon || root.previewTargetApp.icon)
|
||||
opacity: 0.85
|
||||
source: root.getIconPath(modelData.icon || (root.previewTargetApp ? root.previewTargetApp.icon : ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse Area to Focus Specific Window
|
||||
MouseArea {
|
||||
id: cardMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
let globalPos = cardItem.mapToItem(null, 0, 0)
|
||||
TaskService.focusSpecificWindow(modelData, globalPos.x, globalPos.y)
|
||||
previewPopIn.running = false
|
||||
previewPopOut.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -720,7 +783,7 @@ Item {
|
||||
PopupWindow {
|
||||
id: dockContextMenu
|
||||
anchor.window: root.dockWindow
|
||||
anchor.rect.x: Math.round(root.contextTargetX)
|
||||
anchor.rect.x: Math.max(10, Math.min(root.dockWindow ? root.dockWindow.width - dockCtxGlass.implicitWidth - 10 : 800, root.contextTargetX - Math.round(dockCtxGlass.implicitWidth / 2) + 21))
|
||||
anchor.rect.y: 0
|
||||
anchor.edges: Edges.Top | Edges.Left
|
||||
visible: false
|
||||
@ -921,7 +984,7 @@ Item {
|
||||
for (let i = 0; i < dockRepeater.count; i++) {
|
||||
let item = dockRepeater.itemAt(i)
|
||||
if (item && item.appId) {
|
||||
let pt = item.mapToItem(null, 0, 0)
|
||||
let pt = item.mapToItem(root, 0, 0)
|
||||
map[item.appId.toLowerCase()] = { x: Math.round(pt.x), y: Math.round(pt.y) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ Item {
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
let posVal = parseFloat(data.trim())
|
||||
if (!isNaN(posVal) && posVal >= 0 && !seekProc.running && !root.isSeeking) {
|
||||
if (!isNaN(posVal) && posVal >= 0 && !root.isSeeking) {
|
||||
root.position = posVal
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,14 +94,48 @@ Item {
|
||||
return false
|
||||
}
|
||||
|
||||
function appIdsMatch(id1, id2) {
|
||||
if (!id1 || !id2) return false;
|
||||
let a = id1.toLowerCase().trim()
|
||||
let b = id2.toLowerCase().trim()
|
||||
|
||||
if (a === b) return true;
|
||||
|
||||
let aliases = [
|
||||
["equibop", "discord", "equicord", "vencord", "webcord"],
|
||||
["code", "vscode", "visual-studio-code", "code-oss"],
|
||||
["org.kde.konsole", "konsole", "terminal"],
|
||||
["org.kde.dolphin", "dolphin"],
|
||||
["feishin", "org.jeffvli.feishin", "io.github.jeffvli.feishin"],
|
||||
["zen-alpha", "zen", "zen-browser"],
|
||||
["com.antigravity.app", "antigravity"]
|
||||
]
|
||||
|
||||
for (let i = 0; i < aliases.length; i++) {
|
||||
let grp = aliases[i]
|
||||
let aIn = grp.some(x => a.includes(x))
|
||||
let bIn = grp.some(x => b.includes(x))
|
||||
if (aIn && bIn) return true;
|
||||
}
|
||||
|
||||
if (!a.startsWith("steam_app_") && !b.startsWith("steam_app_") && a !== "steam" && b !== "steam") {
|
||||
if (a.includes(b) || b.includes(a)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isRunning(appId) {
|
||||
if (!appId) return false;
|
||||
let lower = appId.toLowerCase().trim()
|
||||
|
||||
for (let i = 0; i < openWindowApps.length; i++) {
|
||||
let winObj = openWindowApps[i]
|
||||
let winApp = typeof winObj === "string" ? winObj.toLowerCase().trim() : (winObj.appId || "").toLowerCase().trim()
|
||||
if (winApp === lower || winApp.includes(lower) || lower.includes(winApp)) {
|
||||
if (!winObj) continue;
|
||||
let winApp = typeof winObj === "string" ? winObj : (winObj.appId || "")
|
||||
let winName = typeof winObj === "object" ? (winObj.name || "") : ""
|
||||
let winCap = typeof winObj === "object" ? (winObj.caption || "") : ""
|
||||
|
||||
if (appIdsMatch(appId, winApp) || appIdsMatch(appId, winName) || appIdsMatch(appId, winCap)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -116,7 +150,7 @@ Item {
|
||||
|
||||
if (activeAppId && activeAppId !== "") {
|
||||
let actLower = activeAppId.toLowerCase().trim()
|
||||
if (actLower === lower || actLower.includes(lower) || lower.includes(actLower)) {
|
||||
if (appIdsMatch(lower, actLower)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -125,30 +159,25 @@ Item {
|
||||
|
||||
function getWindowsForApp(appId) {
|
||||
if (!appId) return [];
|
||||
let lower = appId.toLowerCase().trim()
|
||||
let matching = []
|
||||
|
||||
for (let i = 0; i < openWindowApps.length; i++) {
|
||||
let winObj = openWindowApps[i]
|
||||
if (!winObj) continue;
|
||||
let winApp = typeof winObj === "string" ? winObj.toLowerCase().trim() : (winObj.appId || "").toLowerCase().trim()
|
||||
let winApp = typeof winObj === "string" ? winObj : (winObj.appId || "")
|
||||
let winName = typeof winObj === "object" ? (winObj.name || "") : ""
|
||||
let winCap = typeof winObj === "object" ? (winObj.caption || "") : ""
|
||||
|
||||
let isMatch = false
|
||||
if (winApp === lower) {
|
||||
isMatch = true
|
||||
} else if (!winApp.startsWith("steam_app_") && !lower.startsWith("steam_app_") && lower !== "steam") {
|
||||
if (winApp.includes(lower) || lower.includes(winApp)) {
|
||||
isMatch = true
|
||||
}
|
||||
}
|
||||
|
||||
if (isMatch) {
|
||||
if (appIdsMatch(appId, winApp) || appIdsMatch(appId, winName) || appIdsMatch(appId, winCap)) {
|
||||
matching.push({
|
||||
id: typeof winObj === "object" ? (winObj.id || winApp) : winApp,
|
||||
appId: winApp,
|
||||
name: typeof winObj === "object" ? (winObj.name || winApp) : winApp,
|
||||
icon: typeof winObj === "object" ? (winObj.icon || winApp) : winApp,
|
||||
caption: typeof winObj === "object" ? (winObj.caption || winObj.name || winApp) : winApp,
|
||||
width: typeof winObj === "object" ? (winObj.width || 1280) : 1280,
|
||||
height: typeof winObj === "object" ? (winObj.height || 800) : 800,
|
||||
preview: typeof winObj === "object" ? (winObj.preview || "") : "",
|
||||
minimized: typeof winObj === "object" ? (winObj.minimized || false) : false,
|
||||
active: typeof winObj === "object" ? (winObj.active || false) : false
|
||||
})
|
||||
@ -345,9 +374,9 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// 100ms High-speed State Reader Timer
|
||||
// 200ms Lightweight State Reader Timer
|
||||
Timer {
|
||||
interval: 100
|
||||
interval: 200
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
|
||||
@ -1,42 +1,33 @@
|
||||
// KWin JavaScript Script: Focus, Restore, or Minimize Application Window
|
||||
// KWin JavaScript Script: Focus, Restore, or Cycle Application Windows
|
||||
var name = "%APP_NAME%".toLowerCase().trim();
|
||||
var ix = %ICON_X%;
|
||||
var iy = %ICON_Y%;
|
||||
|
||||
if (name && name !== "") {
|
||||
var windows = workspace.windowList();
|
||||
var matchingWindows = [];
|
||||
|
||||
for (var i = 0; i < windows.length; i++) {
|
||||
var win = windows[i];
|
||||
if (!win.normalWindow && !win.fullScreen && !win.managed) continue;
|
||||
|
||||
var cls = (win.desktopFileName || win.resourceClass || win.resourceName || "").toLowerCase().trim();
|
||||
var caption = (win.caption || "").toLowerCase().trim();
|
||||
|
||||
if (!cls && !caption) continue;
|
||||
|
||||
var matches = false;
|
||||
|
||||
// 1. Exact match check
|
||||
if (cls === name) {
|
||||
matches = true;
|
||||
}
|
||||
|
||||
// 2. Overwatch & Steam game window targeting
|
||||
if (!matches && (name.indexOf("overwatch") !== -1 || name.indexOf("2357570") !== -1)) {
|
||||
} else if (name.indexOf("overwatch") !== -1 || name.indexOf("2357570") !== -1) {
|
||||
if (cls.indexOf("overwatch") !== -1 || caption.indexOf("overwatch") !== -1 || cls === "steam_app_2357570") {
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Steam App ID window targeting (must NOT match main "steam" client window)
|
||||
if (!matches && name.indexOf("steam_app_") !== -1) {
|
||||
} else if (name.indexOf("steam_app_") !== -1) {
|
||||
if (cls === name || cls.indexOf(name) !== -1) {
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. General matching (exclude steam client when searching for steam_app_)
|
||||
if (!matches && name !== "steam") {
|
||||
} else if (name !== "steam") {
|
||||
if (cls && cls !== "steam" && (cls.indexOf(name) !== -1 || name.indexOf(cls) !== -1)) {
|
||||
matches = true;
|
||||
} else if (caption && caption.indexOf(name) !== -1) {
|
||||
@ -45,17 +36,37 @@ if (name && name !== "") {
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
try {
|
||||
win.setMinimizeIconGeometry(Qt.rect(ix, iy, 42, 42));
|
||||
} catch(e) {}
|
||||
|
||||
if (workspace.activeWindow === win && !win.minimized) {
|
||||
win.minimized = true;
|
||||
} else {
|
||||
win.minimized = false;
|
||||
workspace.activeWindow = win;
|
||||
}
|
||||
break;
|
||||
matchingWindows.push(win);
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingWindows.length === 1) {
|
||||
var targetWin = matchingWindows[0];
|
||||
try { targetWin.setMinimizeIconGeometry(Qt.rect(ix, iy, 42, 42)); } catch(e) {}
|
||||
|
||||
if (workspace.activeWindow === targetWin && !targetWin.minimized) {
|
||||
targetWin.minimized = true;
|
||||
} else {
|
||||
targetWin.minimized = false;
|
||||
workspace.activeWindow = targetWin;
|
||||
}
|
||||
} else if (matchingWindows.length > 1) {
|
||||
var activeIdx = -1;
|
||||
for (var j = 0; j < matchingWindows.length; j++) {
|
||||
if (workspace.activeWindow === matchingWindows[j] && !matchingWindows[j].minimized) {
|
||||
activeIdx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var nextIdx = 0;
|
||||
if (activeIdx !== -1) {
|
||||
nextIdx = (activeIdx + 1) % matchingWindows.length;
|
||||
}
|
||||
|
||||
var winToActivate = matchingWindows[nextIdx];
|
||||
try { winToActivate.setMinimizeIconGeometry(Qt.rect(ix, iy, 42, 42)); } catch(e) {}
|
||||
winToActivate.minimized = false;
|
||||
workspace.activeWindow = winToActivate;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,10 +30,20 @@ function updateWindows() {
|
||||
cls.indexOf("status_icon") !== -1 || cls.indexOf("tray") !== -1 || cls.indexOf("desktop") !== -1);
|
||||
if (!isSystemShell) {
|
||||
var winId = String(w.internalId || w.windowId || (cls + "_" + i));
|
||||
var fg = w.frameGeometry || w.clientGeometry || w.bufferGeometry || {};
|
||||
var wx = Math.round(fg.x !== undefined ? fg.x : (w.x || 0));
|
||||
var wy = Math.round(fg.y !== undefined ? fg.y : (w.y || 0));
|
||||
var ww = Math.round(fg.width !== undefined ? fg.width : (w.width || 800));
|
||||
var wh = Math.round(fg.height !== undefined ? fg.height : (w.height || 600));
|
||||
|
||||
openApps.push({
|
||||
id: winId,
|
||||
appId: cls,
|
||||
caption: w.caption || cls,
|
||||
x: wx,
|
||||
y: wy,
|
||||
width: ww,
|
||||
height: wh,
|
||||
minimized: w.minimized || false,
|
||||
active: (w === activeWin || w.active || false)
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user