mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-11 00:47:22 +00:00
Expose layout requests and state through shell settings so Folio can offer alternatives for the current tiled workspace. Keep KWin as the owner of layout changes, snapshot request serials so requests are not dropped, and hide the edge menu when fewer than two windows are tiled.
177 lines
No EOL
10 KiB
Bash
177 lines
No EOL
10 KiB
Bash
#!/usr/bin/env bash
|
|
# SPDX-FileCopyrightText: 2026 Marco Allegretti <mightymarco4@gmail.com>
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
|
effect_qml="$repo_root/kwin/effects/shift-tile-preview/contents/ui/main.qml"
|
|
effect_metadata="$repo_root/kwin/effects/shift-tile-preview/metadata.json"
|
|
effects_cmake="$repo_root/kwin/effects/CMakeLists.txt"
|
|
tiling_script="$repo_root/kwin/scripts/shift-tiling/contents/ui/main.qml"
|
|
decoration_qml="$repo_root/kwin/decorations/org.shift.decoration/contents/ui/main.qml"
|
|
running_apps_panel="$repo_root/containments/homescreens/folio/qml/RunningAppsPanel.qml"
|
|
folio_main="$repo_root/containments/homescreens/folio/qml/main.qml"
|
|
env_config="$repo_root/envmanager/config.h"
|
|
|
|
require_line() {
|
|
local file="$1"
|
|
local needle="$2"
|
|
|
|
if ! grep -Fq -- "$needle" "$file"; then
|
|
echo "Missing dynamic tile motion invariant in ${file#$repo_root/}: $needle" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
reject_line() {
|
|
local file="$1"
|
|
local needle="$2"
|
|
|
|
if grep -Fq -- "$needle" "$file"; then
|
|
echo "Rejected dynamic tile motion pattern in ${file#$repo_root/}: $needle" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
require_line "$effects_cmake" "add_kwin_effect(shift-tile-preview shift-tile-preview)"
|
|
require_line "$effect_metadata" '"Id": "shift-tile-preview"'
|
|
require_line "$effect_metadata" '"X-Plasma-API": "declarativescript"'
|
|
|
|
require_line "$env_config" '{"shift-tilingEnabled", convergenceModeEnabled}'
|
|
require_line "$env_config" '{"shift-tile-previewEnabled", convergenceModeEnabled}'
|
|
require_line "$env_config" '"shift-tile-preview"'
|
|
require_line "$env_config" '"shift-tiling"'
|
|
|
|
require_line "$effect_qml" "KWinComponents.SceneEffect"
|
|
require_line "$effect_qml" "visible: false"
|
|
require_line "$effect_qml" "ShellSettings.Settings.dynamicTilingEnabled"
|
|
require_line "$effect_qml" "interactiveMoveResizeStarted.connect"
|
|
require_line "$effect_qml" "interactiveMoveResizeStepped.connect"
|
|
require_line "$effect_qml" "interactiveMoveResizeFinished.connect"
|
|
require_line "$effect_qml" "Behavior on x"
|
|
require_line "$effect_qml" "Behavior on y"
|
|
require_line "$effect_qml" "Behavior on width"
|
|
require_line "$effect_qml" "Behavior on height"
|
|
require_line "$effect_qml" "showPreview(\"swap\""
|
|
require_line "$effect_qml" "showPreview(\"float\""
|
|
require_line "$effect_qml" "showPreview(\"restore\""
|
|
|
|
require_line "$tiling_script" "readonly property int maxWindowsPerPage: 4"
|
|
require_line "$tiling_script" "readonly property real stablePrimaryRatio: 0.58"
|
|
require_line "$tiling_script" "readonly property var layoutModes: [\"master\", \"columns\", \"rows\"]"
|
|
require_line "$tiling_script" "property int lastWindowRequestSerial: -1"
|
|
require_line "$tiling_script" "property int lastLayoutRequestSerial: -1"
|
|
require_line "$tiling_script" "lastWindowRequestSerial = ShellSettings.Settings.dynamicTilingWindowRequestSerial"
|
|
require_line "$tiling_script" "lastLayoutRequestSerial = ShellSettings.Settings.dynamicTilingLayoutRequestSerial"
|
|
require_line "$tiling_script" "function desktopKey(desktop)"
|
|
require_line "$tiling_script" "function desktopForWindow(win)"
|
|
require_line "$tiling_script" "function normalizeWindowDesktopScope(win)"
|
|
require_line "$tiling_script" "function layoutKeyFor(outputName, desktop)"
|
|
require_line "$tiling_script" "function desktopForLayoutKey(outputName)"
|
|
require_line "$tiling_script" "function layoutKeyForWindow(win)"
|
|
require_line "$tiling_script" "function assignWindowToDesktop(win, desktop)"
|
|
require_line "$tiling_script" "function targetLayoutForNewWindow(win)"
|
|
require_line "$tiling_script" "function leafIndexForWindow(outputName, win)"
|
|
require_line "$tiling_script" "function leafForSlot(outputName, preferredIndex)"
|
|
require_line "$tiling_script" "property var lastFocusedWindowKeys: ({})"
|
|
require_line "$tiling_script" "function rememberFocusedWindow(win)"
|
|
require_line "$tiling_script" "function replacementLeafForLayout(outputName, fallbackIndex)"
|
|
require_line "$tiling_script" "function swapWindowBetweenLayouts(win, sourceName, targetName)"
|
|
require_line "$tiling_script" "windowCountForLayout(currentKey) < maxWindowsPerPage"
|
|
require_line "$tiling_script" "windowCountForLayout(targetName) >= maxWindowsPerPage && swapWindowBetweenLayouts(win, currentName, targetName)"
|
|
require_line "$tiling_script" "KWinComponents.Workspace.currentDesktop = target.desktop"
|
|
require_line "$tiling_script" "assignWindowToDesktop(displacedWindow, sourceDesktop)"
|
|
require_line "$tiling_script" "const targetLeaf = replacementLeafForLayout(targetName, sourceIndex)"
|
|
require_line "$tiling_script" "function onActiveWindowChanged()"
|
|
require_line "$tiling_script" "root.rememberFocusedWindow(KWinComponents.Workspace.activeWindow)"
|
|
require_line "$tiling_script" "win.desktops = [desktop]"
|
|
require_line "$tiling_script" "win.desktopsChanged.connect"
|
|
require_line "$tiling_script" "function dynamicTilingOwnsWindow(win)"
|
|
require_line "$tiling_script" "function normalizeNativeWindowOperationState(win)"
|
|
require_line "$tiling_script" "function handleNativeWindowOperationStateChange(win)"
|
|
require_line "$tiling_script" "function adoptWindow(win)"
|
|
require_line "$tiling_script" "win.maximizeModeChanged.connect"
|
|
require_line "$tiling_script" "win.fullScreenChanged.connect"
|
|
require_line "$tiling_script" "win.noBorderChanged.connect"
|
|
require_line "$tiling_script" "win.keepAboveChanged.connect"
|
|
require_line "$tiling_script" "win.keepBelowChanged.connect"
|
|
require_line "$tiling_script" "property bool dragNativeResize: false"
|
|
require_line "$tiling_script" "if (dragNativeResize && dragSourceScreen && dragSourceIndex >= 0)"
|
|
require_line "$tiling_script" "function orderedWindowsForScreen(outputName)"
|
|
require_line "$tiling_script" "function buildStableStack(windows, startIndex)"
|
|
require_line "$tiling_script" "function buildLinearLayout(windows, startIndex, orientation)"
|
|
require_line "$tiling_script" "function layoutModeForScreen(outputName)"
|
|
require_line "$tiling_script" "function buildStableLayout(windows, mode)"
|
|
require_line "$tiling_script" "function setStableLayout(outputName, windows)"
|
|
require_line "$tiling_script" "function cycleLayoutMode(outputName)"
|
|
require_line "$tiling_script" "function swapDraggedWindow(target)"
|
|
require_line "$tiling_script" "showDragOutline(\"swap\", target.rect)"
|
|
require_line "$tiling_script" "swapDraggedWindow(dragSwapTarget)"
|
|
require_line "$tiling_script" "setStableLayout(outputName, windows)"
|
|
require_line "$tiling_script" "setStableLayout(outputName, remaining)"
|
|
require_line "$tiling_script" "retileScreen(name)"
|
|
require_line "$tiling_script" "markLayoutChanged(transaction, targetName)"
|
|
require_line "$tiling_script" "function promoteWindow(win)"
|
|
require_line "$tiling_script" "setStableLayout(outputName, reordered)"
|
|
require_line "$tiling_script" "name: \"SHIFT Tiling Promote Primary\""
|
|
require_line "$tiling_script" "sequence: \"Meta+Shift+Return\""
|
|
require_line "$tiling_script" "name: \"SHIFT Tiling Cycle Layout\""
|
|
require_line "$tiling_script" "sequence: \"Meta+Shift+T\""
|
|
|
|
require_line "$decoration_qml" "readonly property bool windowMenuAllowed: !ShellSettings.Settings.convergenceModeEnabled"
|
|
require_line "$decoration_qml" "return root.windowMenuAllowed;"
|
|
require_line "$decoration_qml" "enabled: !root.windowMenuAllowed"
|
|
require_line "$decoration_qml" "acceptedButtons: Qt.RightButton"
|
|
require_line "$decoration_qml" "readonly property int frameThickness: decoration.client.maximized ? 0 : normalCornerRadius"
|
|
require_line "$decoration_qml" "borders.left = normalCornerRadius;"
|
|
require_line "$decoration_qml" "borders.right = normalCornerRadius;"
|
|
require_line "$decoration_qml" "borders.bottom = normalCornerRadius;"
|
|
require_line "$decoration_qml" "PathArc { x: root.width - root.cornerRadius; y: root.height; radiusX: root.cornerRadius; radiusY: root.cornerRadius }"
|
|
require_line "$decoration_qml" "PathArc { x: 0; y: root.height - root.cornerRadius; radiusX: root.cornerRadius; radiusY: root.cornerRadius }"
|
|
|
|
require_line "$folio_main" "readonly property int layoutMenuWindowCount: Math.max(0, ShellSettings.Settings.dynamicTilingLayoutWindowCount)"
|
|
require_line "$folio_main" "&& layoutMenuWindowCount >= 2"
|
|
require_line "$folio_main" "if (layoutMenuEnabled) {"
|
|
require_line "$folio_main" "regions.push(rightEdgeRegion)"
|
|
require_line "$folio_main" "if (layoutMenuEnabled && layoutMenuOpen)"
|
|
require_line "$folio_main" "rightEdgeHovered = false"
|
|
require_line "$folio_main" "layoutMenuHovered = false"
|
|
require_line "$folio_main" "const insideEdge = layoutMenuEnabled"
|
|
require_line "$folio_main" "width: convergenceChrome.layoutMenuEnabled ? convergenceChrome.rightEdgeHotzoneWidth : 0"
|
|
|
|
running_panel_group_disabled_count="$(grep -F "groupMode: TaskManager.TasksModel.GroupDisabled" "$running_apps_panel" | wc -l)"
|
|
if [[ "$running_panel_group_disabled_count" -ne 2 ]]; then
|
|
echo "Expected the Folio Running panel to disable grouping for both task models; found $running_panel_group_disabled_count" >&2
|
|
exit 1
|
|
fi
|
|
reject_line "$running_apps_panel" "groupMode: TaskManager.TasksModel.GroupApplications"
|
|
|
|
if awk '
|
|
/ShellSettings\.Settings\.requestDynamicTilingWindowAction\(taskCard\.windowId, action\)/ { seen = 1; next }
|
|
seen && /return/ { exit 0 }
|
|
seen && /tasksModel\.requestVirtualDesktops\(taskCard\.modelIndex, \[desktopId\]\)/ { exit 1 }
|
|
END { exit 1 }
|
|
' "$running_apps_panel"; then
|
|
echo "Expected the Folio Running panel desktop drop to fall through to TaskManager after notifying dynamic tiling" >&2
|
|
exit 1
|
|
fi
|
|
|
|
reject_line "$tiling_script" "targetKey = lastLeafKey(rootNode)"
|
|
reject_line "$tiling_script" "function lastLeafKey(node)"
|
|
reject_line "$tiling_script" "win.output.name !== outputName"
|
|
reject_line "$tiling_script" "const targetName = output.name"
|
|
reject_line "$tiling_script" "const desktop = win.desktops[0]"
|
|
reject_line "$tiling_script" "setScreenLayout(outputName, splitLeaf(rootNode, targetKey"
|
|
reject_line "$tiling_script" "function moveWindowToSplit(target)"
|
|
reject_line "$tiling_script" "function splitLeaf(node, targetKey"
|
|
reject_line "$tiling_script" "function tileInsertDirection(cursor, rect)"
|
|
reject_line "$tiling_script" "showDragOutline(\"insert\""
|
|
reject_line "$tiling_script" "KWinComponents.Workspace.showOutline(dragOutlineRect)"
|
|
reject_line "$tiling_script" "KWinComponents.Workspace.hideOutline()"
|
|
reject_line "$tiling_script" "property int lastWindowRequestSerial: ShellSettings.Settings.dynamicTilingWindowRequestSerial"
|
|
reject_line "$tiling_script" "property int lastLayoutRequestSerial: ShellSettings.Settings.dynamicTilingLayoutRequestSerial"
|
|
reject_line "$effect_qml" "effect.visible = true"
|
|
|
|
printf '%s\n' 'dynamic-tiles-motion-ok' |