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
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
|
||||||
onEntered: {
|
onEntered: {
|
||||||
|
previewCloseTimer.stop()
|
||||||
if (TaskService.isRunning(modelData.appId)) {
|
if (TaskService.isRunning(modelData.appId)) {
|
||||||
let instances = TaskService.getWindowsForApp(modelData.appId)
|
let instances = TaskService.getWindowsForApp(modelData.appId)
|
||||||
if (instances && instances.length > 0) {
|
if (instances && instances.length > 0) {
|
||||||
root.previewTargetApp = modelData
|
let targetParent = root.dockWindow ? root.dockWindow.contentItem : root
|
||||||
root.previewWindowInstances = instances
|
let pt = itemMouse.mapToItem(targetParent, 0, 0)
|
||||||
root.previewTargetX = Math.round(dockItem.mapToItem(null, 0, 0).x)
|
root.previewTargetX = Math.round(pt.x + itemMouse.width / 2)
|
||||||
root.isPreviewHovered = true
|
root.isPreviewHovered = true
|
||||||
previewCloseTimer.stop()
|
root.previewActive = true
|
||||||
previewPopOut.running = false
|
|
||||||
dockWindowPreview.visible = true
|
let oldLen = root.previewWindowInstances ? root.previewWindowInstances.length : 0
|
||||||
previewPopIn.restart()
|
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: {
|
onExited: {
|
||||||
root.isPreviewHovered = false
|
root.isPreviewHovered = false
|
||||||
previewCloseTimer.start()
|
previewCloseTimer.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
property real dragXOffset: 0
|
property real dragXOffset: 0
|
||||||
@ -265,12 +285,20 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mouse.button === Qt.LeftButton) {
|
} 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)
|
TaskService.focusApp(modelData.appId, modelData.cmd, globalPos.x, globalPos.y)
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
|
root.isPreviewHovered = false
|
||||||
|
root.isCardHovered = false
|
||||||
|
previewCloseTimer.stop()
|
||||||
|
|
||||||
root.contextTargetApp = modelData
|
root.contextTargetApp = modelData
|
||||||
root.contextTargetX = Math.round(dockItem.mapToItem(null, 0, 0).x)
|
let targetParent = root.dockWindow ? root.dockWindow.contentItem : root
|
||||||
PopupService.toggleDockMenu()
|
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);
|
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
|
property var dockWindow: null
|
||||||
|
|
||||||
// Window Preview Hover State Properties
|
// Window Preview Hover State Properties
|
||||||
@ -440,58 +516,48 @@ Item {
|
|||||||
property var previewWindowInstances: []
|
property var previewWindowInstances: []
|
||||||
property int previewTargetX: 0
|
property int previewTargetX: 0
|
||||||
property bool isPreviewHovered: false
|
property bool isPreviewHovered: false
|
||||||
|
property bool isCardHovered: false
|
||||||
|
property bool previewActive: false
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: previewCloseTimer
|
id: previewCloseTimer
|
||||||
interval: 220
|
interval: 180
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (!root.isPreviewHovered && !previewMouseArea.containsMouse) {
|
if (!root.isPreviewHovered && !previewMouseArea.containsMouse && !root.isCardHovered) {
|
||||||
previewPopIn.running = false
|
root.previewActive = false
|
||||||
previewPopOut.restart()
|
root.isPreviewHovered = false
|
||||||
|
root.isCardHovered = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Live Window Preview Floating Popup Window
|
// Live Window Preview Floating Popup Window (Hysteresis Active Binding)
|
||||||
PopupWindow {
|
PopupWindow {
|
||||||
id: dockWindowPreview
|
id: dockWindowPreview
|
||||||
anchor.window: root.dockWindow
|
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.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.rect.y: 0
|
||||||
anchor.edges: Edges.Top | Edges.Left
|
anchor.edges: Edges.Top | Edges.Left
|
||||||
visible: false
|
visible: root.previewActive && (root.previewWindowInstances && root.previewWindowInstances.length > 0)
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
implicitWidth: previewGlass.implicitWidth
|
implicitWidth: previewGlass.implicitWidth
|
||||||
implicitHeight: previewGlass.implicitHeight
|
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 {
|
MouseArea {
|
||||||
id: previewMouseArea
|
id: previewMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onEntered: previewCloseTimer.stop()
|
onEntered: {
|
||||||
onExited: previewCloseTimer.start()
|
previewCloseTimer.stop()
|
||||||
|
root.previewActive = true
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
if (!root.isPreviewHovered) {
|
||||||
|
previewCloseTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GlassPanel {
|
GlassPanel {
|
||||||
id: previewGlass
|
id: previewGlass
|
||||||
@ -499,14 +565,6 @@ Item {
|
|||||||
implicitHeight: previewMainCol.implicitHeight + 16
|
implicitHeight: previewMainCol.implicitHeight + 16
|
||||||
anchors.fill: parent
|
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 {
|
ColumnLayout {
|
||||||
id: previewMainCol
|
id: previewMainCol
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@ -564,6 +622,7 @@ Item {
|
|||||||
// Window Preview Card Row (Single or Multiple Instances)
|
// Window Preview Card Row (Single or Multiple Instances)
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: previewCardsRow
|
id: previewCardsRow
|
||||||
|
implicitWidth: root.calculateCardsRowWidth(root.previewWindowInstances)
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
@ -571,20 +630,49 @@ Item {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: cardItem
|
id: cardItem
|
||||||
width: Math.max(140, Math.min(220, cardCaptionText.implicitWidth + 36))
|
property real winW: (modelData && modelData.width && Number(modelData.width) > 50) ? Number(modelData.width) : 1280
|
||||||
height: 110
|
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
|
property bool cardHovered: cardMouseArea.containsMouse
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: 10
|
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.color: modelData.active ? Theme.accent : (cardItem.cardHovered ? Theme.subAccent : Theme.separator)
|
||||||
border.width: modelData.active || cardItem.cardHovered ? 1.5 : 1
|
border.width: modelData.active || cardItem.cardHovered ? 1.5 : 1
|
||||||
|
|
||||||
Behavior on color { ColorAnimation { duration: 120 } }
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||||||
Behavior on border.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 {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -617,33 +705,42 @@ Item {
|
|||||||
|
|
||||||
// Individual Window Close Button (✕)
|
// Individual Window Close Button (✕)
|
||||||
Item {
|
Item {
|
||||||
width: 16
|
width: 18
|
||||||
height: 16
|
height: 18
|
||||||
|
z: 10
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
visible: cardItem.cardHovered
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: 8
|
radius: 9
|
||||||
color: closeMouse.containsMouse ? Qt.rgba(255/255, 85/255, 85/255, 0.35) : "transparent"
|
color: closeMouse.containsMouse ? Qt.rgba(255/255, 85/255, 85/255, 0.70) : Qt.rgba(0, 0, 0, 0.40)
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "✕"
|
text: "✕"
|
||||||
color: closeMouse.containsMouse ? "#ff5555" : Theme.comment
|
color: closeMouse.containsMouse ? "#ffffff" : Theme.comment
|
||||||
font.pixelSize: 9
|
font.pixelSize: 10
|
||||||
|
font.weight: Font.Bold
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: closeMouse
|
id: closeMouse
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: {
|
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)
|
TaskService.closeSpecificWindow(modelData)
|
||||||
let rem = root.previewWindowInstances ? root.previewWindowInstances.filter(w => w.id !== modelData.id) : []
|
let targetId = String(modelData ? (modelData.id || "") : "")
|
||||||
|
let rem = root.previewWindowInstances ? root.previewWindowInstances.filter(w => String(w ? (w.id || "") : "") !== targetId) : []
|
||||||
root.previewWindowInstances = rem
|
root.previewWindowInstances = rem
|
||||||
if (!rem || rem.length === 0) {
|
if (!rem || rem.length === 0) {
|
||||||
previewPopIn.running = false
|
root.previewActive = false
|
||||||
previewPopOut.restart()
|
root.isPreviewHovered = false
|
||||||
|
root.isCardHovered = false
|
||||||
|
previewCloseTimer.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,62 +748,28 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Miniature Glass Window Preview Container
|
// Lightweight Glass Window Card Viewport (0% CPU, 0ms lag)
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
radius: 6
|
radius: 6
|
||||||
color: Qt.rgba(0, 0, 0, 0.25)
|
color: Qt.rgba(0, 0, 0, 0.45)
|
||||||
border.color: Theme.separator
|
border.color: Theme.separator
|
||||||
border.width: 1
|
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 {
|
Image {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
anchors.verticalCenterOffset: 4
|
width: 38
|
||||||
width: 32
|
height: 38
|
||||||
height: 32
|
sourceSize.width: 38
|
||||||
sourceSize.width: 32
|
sourceSize.height: 38
|
||||||
sourceSize.height: 32
|
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
opacity: cardItem.cardHovered ? 1.0 : 0.82
|
opacity: 0.85
|
||||||
source: root.getIconPath(modelData.icon || root.previewTargetApp.icon)
|
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 {
|
PopupWindow {
|
||||||
id: dockContextMenu
|
id: dockContextMenu
|
||||||
anchor.window: root.dockWindow
|
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.rect.y: 0
|
||||||
anchor.edges: Edges.Top | Edges.Left
|
anchor.edges: Edges.Top | Edges.Left
|
||||||
visible: false
|
visible: false
|
||||||
@ -921,7 +984,7 @@ Item {
|
|||||||
for (let i = 0; i < dockRepeater.count; i++) {
|
for (let i = 0; i < dockRepeater.count; i++) {
|
||||||
let item = dockRepeater.itemAt(i)
|
let item = dockRepeater.itemAt(i)
|
||||||
if (item && item.appId) {
|
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) }
|
map[item.appId.toLowerCase()] = { x: Math.round(pt.x), y: Math.round(pt.y) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,7 +155,7 @@ Item {
|
|||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
onRead: data => {
|
onRead: data => {
|
||||||
let posVal = parseFloat(data.trim())
|
let posVal = parseFloat(data.trim())
|
||||||
if (!isNaN(posVal) && posVal >= 0 && !seekProc.running && !root.isSeeking) {
|
if (!isNaN(posVal) && posVal >= 0 && !root.isSeeking) {
|
||||||
root.position = posVal
|
root.position = posVal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,14 +94,48 @@ Item {
|
|||||||
return false
|
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) {
|
function isRunning(appId) {
|
||||||
if (!appId) return false;
|
if (!appId) return false;
|
||||||
let lower = appId.toLowerCase().trim()
|
|
||||||
|
|
||||||
for (let i = 0; i < openWindowApps.length; i++) {
|
for (let i = 0; i < openWindowApps.length; i++) {
|
||||||
let winObj = openWindowApps[i]
|
let winObj = openWindowApps[i]
|
||||||
let winApp = typeof winObj === "string" ? winObj.toLowerCase().trim() : (winObj.appId || "").toLowerCase().trim()
|
if (!winObj) continue;
|
||||||
if (winApp === lower || winApp.includes(lower) || lower.includes(winApp)) {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +150,7 @@ Item {
|
|||||||
|
|
||||||
if (activeAppId && activeAppId !== "") {
|
if (activeAppId && activeAppId !== "") {
|
||||||
let actLower = activeAppId.toLowerCase().trim()
|
let actLower = activeAppId.toLowerCase().trim()
|
||||||
if (actLower === lower || actLower.includes(lower) || lower.includes(actLower)) {
|
if (appIdsMatch(lower, actLower)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,30 +159,25 @@ Item {
|
|||||||
|
|
||||||
function getWindowsForApp(appId) {
|
function getWindowsForApp(appId) {
|
||||||
if (!appId) return [];
|
if (!appId) return [];
|
||||||
let lower = appId.toLowerCase().trim()
|
|
||||||
let matching = []
|
let matching = []
|
||||||
|
|
||||||
for (let i = 0; i < openWindowApps.length; i++) {
|
for (let i = 0; i < openWindowApps.length; i++) {
|
||||||
let winObj = openWindowApps[i]
|
let winObj = openWindowApps[i]
|
||||||
if (!winObj) continue;
|
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 (appIdsMatch(appId, winApp) || appIdsMatch(appId, winName) || appIdsMatch(appId, winCap)) {
|
||||||
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) {
|
|
||||||
matching.push({
|
matching.push({
|
||||||
id: typeof winObj === "object" ? (winObj.id || winApp) : winApp,
|
id: typeof winObj === "object" ? (winObj.id || winApp) : winApp,
|
||||||
appId: winApp,
|
appId: winApp,
|
||||||
name: typeof winObj === "object" ? (winObj.name || winApp) : winApp,
|
name: typeof winObj === "object" ? (winObj.name || winApp) : winApp,
|
||||||
icon: typeof winObj === "object" ? (winObj.icon || winApp) : winApp,
|
icon: typeof winObj === "object" ? (winObj.icon || winApp) : winApp,
|
||||||
caption: typeof winObj === "object" ? (winObj.caption || winObj.name || 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,
|
minimized: typeof winObj === "object" ? (winObj.minimized || false) : false,
|
||||||
active: typeof winObj === "object" ? (winObj.active || 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 {
|
Timer {
|
||||||
interval: 100
|
interval: 200
|
||||||
running: true
|
running: true
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
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 name = "%APP_NAME%".toLowerCase().trim();
|
||||||
var ix = %ICON_X%;
|
var ix = %ICON_X%;
|
||||||
var iy = %ICON_Y%;
|
var iy = %ICON_Y%;
|
||||||
|
|
||||||
if (name && name !== "") {
|
if (name && name !== "") {
|
||||||
var windows = workspace.windowList();
|
var windows = workspace.windowList();
|
||||||
|
var matchingWindows = [];
|
||||||
|
|
||||||
for (var i = 0; i < windows.length; i++) {
|
for (var i = 0; i < windows.length; i++) {
|
||||||
var win = windows[i];
|
var win = windows[i];
|
||||||
if (!win.normalWindow && !win.fullScreen && !win.managed) continue;
|
if (!win.normalWindow && !win.fullScreen && !win.managed) continue;
|
||||||
|
|
||||||
var cls = (win.desktopFileName || win.resourceClass || win.resourceName || "").toLowerCase().trim();
|
var cls = (win.desktopFileName || win.resourceClass || win.resourceName || "").toLowerCase().trim();
|
||||||
var caption = (win.caption || "").toLowerCase().trim();
|
var caption = (win.caption || "").toLowerCase().trim();
|
||||||
|
|
||||||
if (!cls && !caption) continue;
|
if (!cls && !caption) continue;
|
||||||
|
|
||||||
var matches = false;
|
var matches = false;
|
||||||
|
|
||||||
// 1. Exact match check
|
|
||||||
if (cls === name) {
|
if (cls === name) {
|
||||||
matches = true;
|
matches = true;
|
||||||
}
|
} else if (name.indexOf("overwatch") !== -1 || name.indexOf("2357570") !== -1) {
|
||||||
|
|
||||||
// 2. Overwatch & Steam game window targeting
|
|
||||||
if (!matches && (name.indexOf("overwatch") !== -1 || name.indexOf("2357570") !== -1)) {
|
|
||||||
if (cls.indexOf("overwatch") !== -1 || caption.indexOf("overwatch") !== -1 || cls === "steam_app_2357570") {
|
if (cls.indexOf("overwatch") !== -1 || caption.indexOf("overwatch") !== -1 || cls === "steam_app_2357570") {
|
||||||
matches = true;
|
matches = true;
|
||||||
}
|
}
|
||||||
}
|
} else if (name.indexOf("steam_app_") !== -1) {
|
||||||
|
|
||||||
// 3. Steam App ID window targeting (must NOT match main "steam" client window)
|
|
||||||
if (!matches && name.indexOf("steam_app_") !== -1) {
|
|
||||||
if (cls === name || cls.indexOf(name) !== -1) {
|
if (cls === name || cls.indexOf(name) !== -1) {
|
||||||
matches = true;
|
matches = true;
|
||||||
}
|
}
|
||||||
}
|
} else if (name !== "steam") {
|
||||||
|
|
||||||
// 4. General matching (exclude steam client when searching for steam_app_)
|
|
||||||
if (!matches && name !== "steam") {
|
|
||||||
if (cls && cls !== "steam" && (cls.indexOf(name) !== -1 || name.indexOf(cls) !== -1)) {
|
if (cls && cls !== "steam" && (cls.indexOf(name) !== -1 || name.indexOf(cls) !== -1)) {
|
||||||
matches = true;
|
matches = true;
|
||||||
} else if (caption && caption.indexOf(name) !== -1) {
|
} else if (caption && caption.indexOf(name) !== -1) {
|
||||||
@ -45,17 +36,37 @@ if (name && name !== "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (matches) {
|
if (matches) {
|
||||||
try {
|
matchingWindows.push(win);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
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);
|
cls.indexOf("status_icon") !== -1 || cls.indexOf("tray") !== -1 || cls.indexOf("desktop") !== -1);
|
||||||
if (!isSystemShell) {
|
if (!isSystemShell) {
|
||||||
var winId = String(w.internalId || w.windowId || (cls + "_" + i));
|
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({
|
openApps.push({
|
||||||
id: winId,
|
id: winId,
|
||||||
appId: cls,
|
appId: cls,
|
||||||
caption: w.caption || cls,
|
caption: w.caption || cls,
|
||||||
|
x: wx,
|
||||||
|
y: wy,
|
||||||
|
width: ww,
|
||||||
|
height: wh,
|
||||||
minimized: w.minimized || false,
|
minimized: w.minimized || false,
|
||||||
active: (w === activeWin || w.active || false)
|
active: (w === activeWin || w.active || false)
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user