2023-03-26 06:07:18 +00:00
|
|
|
// SPDX-FileCopyrightText: 2023 Plata Hill <plata.hill@kdemail.net>
|
|
|
|
|
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
|
|
|
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
|
|
|
|
|
|
import QtQuick
|
2023-09-27 05:53:45 +00:00
|
|
|
import org.kde.kwin as KWinComponents
|
2023-03-29 06:49:09 +00:00
|
|
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
2023-03-26 06:07:18 +00:00
|
|
|
|
2023-11-13 06:50:44 +00:00
|
|
|
Loader {
|
2023-03-26 06:07:18 +00:00
|
|
|
id: root
|
|
|
|
|
|
2024-11-13 17:37:09 +00:00
|
|
|
property var currentWindow
|
|
|
|
|
|
2026-04-18 07:48:58 +00:00
|
|
|
// Window that needs geometry clamping after un-maximize in convergence
|
|
|
|
|
// mode. Set in onMaximizedChanged and consumed by the timer below.
|
|
|
|
|
property var pendingConstrainWindow: null
|
|
|
|
|
|
|
|
|
|
// After a window is un-maximized in convergence mode, the dockSpaceReserver
|
|
|
|
|
// LayerShell surface needs one Wayland roundtrip to (re)commit its exclusive
|
|
|
|
|
// zone so that KWin updates MaximizeArea. We wait 200 ms — well within the
|
|
|
|
|
// dock slide-in animation — then clamp the window bottom to MaximizeArea so
|
|
|
|
|
// it cannot overlap the dock.
|
|
|
|
|
Timer {
|
|
|
|
|
id: constrainAfterRestoreTimer
|
|
|
|
|
interval: 200
|
|
|
|
|
onTriggered: {
|
|
|
|
|
const window = root.pendingConstrainWindow
|
|
|
|
|
root.pendingConstrainWindow = null
|
|
|
|
|
if (!window || window.deleted || !window.normalWindow) return
|
|
|
|
|
if (!ShellSettings.Settings.convergenceModeEnabled) return
|
|
|
|
|
|
|
|
|
|
const output = window.output
|
|
|
|
|
const desktop = window.desktops[0]
|
|
|
|
|
if (!desktop) return
|
|
|
|
|
|
|
|
|
|
const maxRect = KWinComponents.Workspace.clientArea(
|
|
|
|
|
KWinComponents.Workspace.MaximizeArea, output, desktop)
|
|
|
|
|
const geo = window.frameGeometry
|
|
|
|
|
const maxBottom = maxRect.y + maxRect.height
|
|
|
|
|
|
|
|
|
|
if (geo.y + geo.height > maxBottom) {
|
|
|
|
|
// Clip the bottom edge to MaximizeArea; preserve top position
|
|
|
|
|
// and width. Ensure height is at least 100px to avoid
|
|
|
|
|
// pathological cases where the window starts above maxRect.
|
|
|
|
|
const newH = Math.max(100, maxBottom - geo.y)
|
|
|
|
|
window.frameGeometry = Qt.rect(geo.x, geo.y, geo.width, newH)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-28 19:06:59 +00:00
|
|
|
function run(window) {
|
2024-03-07 21:04:06 +00:00
|
|
|
// HACK: don't maximize xwaylandvideobridge
|
|
|
|
|
// see: https://invent.kde.org/plasma/plasma-mobile/-/issues/324
|
|
|
|
|
if (window.resourceClass === 'xwaylandvideobridge') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!window.normalWindow) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-04 17:26:50 +00:00
|
|
|
if (ShellSettings.Settings.convergenceModeEnabled) {
|
|
|
|
|
window.noBorder = false;
|
|
|
|
|
} else {
|
2024-03-04 19:30:37 +00:00
|
|
|
if (!window.fullScreen) {
|
|
|
|
|
const output = window.output;
|
|
|
|
|
const desktop = window.desktops[0]; // assume it's the first desktop that the window is on
|
2024-11-15 05:48:25 +00:00
|
|
|
if (desktop === undefined) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-03-04 19:30:37 +00:00
|
|
|
const maximizeRect = KWinComponents.Workspace.clientArea(KWinComponents.Workspace.MaximizeArea, output, desktop);
|
2024-07-27 03:47:44 +00:00
|
|
|
|
|
|
|
|
// set the window to the maximized size and position instantly, avoiding race condition
|
2024-03-04 19:30:37 +00:00
|
|
|
// between maximizing and window decorations being turned off (changing window height)
|
|
|
|
|
// see: https://invent.kde.org/teams/plasma-mobile/issues/-/issues/256
|
|
|
|
|
window.frameGeometry = maximizeRect;
|
|
|
|
|
}
|
2024-03-04 17:26:50 +00:00
|
|
|
|
|
|
|
|
// turn off window decorations
|
2023-10-28 19:06:59 +00:00
|
|
|
window.noBorder = true;
|
2024-03-04 17:26:50 +00:00
|
|
|
|
2024-03-04 19:30:37 +00:00
|
|
|
if (!window.fullScreen) {
|
|
|
|
|
// run maximize after to ensure the state is maximized
|
|
|
|
|
window.setMaximize(true, true);
|
|
|
|
|
}
|
2023-03-29 06:49:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-13 17:37:09 +00:00
|
|
|
Connections {
|
|
|
|
|
target: currentWindow
|
|
|
|
|
|
|
|
|
|
function onFullScreenChanged() {
|
|
|
|
|
currentWindow.interactiveMoveResizeFinished.connect((currentWindow) => {
|
|
|
|
|
root.run(currentWindow);
|
|
|
|
|
});
|
|
|
|
|
root.run(currentWindow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onMaximizedChanged() {
|
|
|
|
|
if (!currentWindow.maximizable) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
currentWindow.interactiveMoveResizeFinished.connect((currentWindow) => {
|
|
|
|
|
root.run(currentWindow);
|
|
|
|
|
});
|
|
|
|
|
root.run(currentWindow);
|
2026-04-18 07:48:58 +00:00
|
|
|
// Schedule a deferred geometry clamp so that the restored window
|
|
|
|
|
// doesn't overlap the dock after the dockSpaceReserver exclusive
|
|
|
|
|
// zone is re-committed over a Wayland roundtrip.
|
|
|
|
|
if (ShellSettings.Settings.convergenceModeEnabled
|
|
|
|
|
&& ShellSettings.Settings.autoHidePanelsEnabled) {
|
|
|
|
|
root.pendingConstrainWindow = currentWindow
|
|
|
|
|
constrainAfterRestoreTimer.restart()
|
|
|
|
|
}
|
2024-11-13 17:37:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 06:49:09 +00:00
|
|
|
Connections {
|
|
|
|
|
target: ShellSettings.Settings
|
|
|
|
|
|
|
|
|
|
function onConvergenceModeEnabledChanged() {
|
2023-10-28 19:06:59 +00:00
|
|
|
const windows = KWinComponents.Workspace.windows;
|
2023-03-29 06:49:09 +00:00
|
|
|
|
2023-10-28 19:06:59 +00:00
|
|
|
for (let i = 0; i < windows.length; i++) {
|
|
|
|
|
if (windows[i].normalWindow) {
|
|
|
|
|
root.run(windows[i]);
|
2023-03-29 06:49:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-26 06:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Connections {
|
2023-09-30 16:27:58 +00:00
|
|
|
target: KWinComponents.Workspace
|
2023-03-26 06:07:18 +00:00
|
|
|
|
2023-10-28 19:06:59 +00:00
|
|
|
function onWindowAdded(window) {
|
|
|
|
|
if (window.normalWindow) {
|
|
|
|
|
window.interactiveMoveResizeFinished.connect((window) => {
|
|
|
|
|
root.run(window);
|
2023-03-26 06:07:18 +00:00
|
|
|
});
|
2023-10-28 19:06:59 +00:00
|
|
|
root.run(window);
|
2023-03-26 06:07:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-13 17:37:09 +00:00
|
|
|
function onWindowActivated(window) {
|
|
|
|
|
if (window.normalWindow) {
|
|
|
|
|
currentWindow = window;
|
|
|
|
|
window.interactiveMoveResizeFinished.connect((window) => {
|
|
|
|
|
root.run(window);
|
|
|
|
|
});
|
|
|
|
|
root.run(window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-26 06:07:18 +00:00
|
|
|
function onScreensChanged() {
|
|
|
|
|
// Windows are moved from the external screen
|
|
|
|
|
// to the internal screen if the external screen
|
|
|
|
|
// is disconnected.
|
2023-10-28 19:06:59 +00:00
|
|
|
const windows = KWinComponents.Workspace.windows;
|
2023-03-26 06:07:18 +00:00
|
|
|
|
2023-10-28 19:06:59 +00:00
|
|
|
for (var i = 0; i < windows.length; i++) {
|
|
|
|
|
if (windows[i].normalWindow) {
|
|
|
|
|
root.run(windows[i]);
|
2023-03-26 06:07:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|