Reduce dynamic tile drag jitter

This commit is contained in:
Marco Allegretti 2026-05-19 10:00:43 +02:00
parent c6bd37dfc3
commit 7c51f76cc0
3 changed files with 39 additions and 25 deletions

View file

@ -16,6 +16,7 @@ KWinComponents.SceneEffect {
readonly property int floatEscapeMargin: 32 readonly property int floatEscapeMargin: 32
readonly property int previewAnimationDuration: 180 readonly property int previewAnimationDuration: 180
readonly property int previewFadeDuration: 110 readonly property int previewFadeDuration: 110
readonly property real insertIntentDeadZone: 0.18
property var dragConnectedWindows: ({}) property var dragConnectedWindows: ({})
property var draggingWindow: null property var draggingWindow: null
@ -101,26 +102,25 @@ KWinComponents.SceneEffect {
} }
function insertDirection(cursor, geometry) { function insertDirection(cursor, geometry) {
const leftDistance = Math.abs(cursor.x - geometry.x); const relativeX = (cursor.x - geometry.x) / geometry.width;
const rightDistance = Math.abs((geometry.x + geometry.width) - cursor.x); const relativeY = (cursor.y - geometry.y) / geometry.height;
const topDistance = Math.abs(cursor.y - geometry.y); const fromCenterX = relativeX - 0.5;
const bottomDistance = Math.abs((geometry.y + geometry.height) - cursor.y); const fromCenterY = relativeY - 0.5;
const nearestDistance = Math.min(leftDistance, rightDistance, topDistance, bottomDistance); if (Math.abs(fromCenterX) < insertIntentDeadZone && Math.abs(fromCenterY) < insertIntentDeadZone) {
return "";
}
if (nearestDistance === leftDistance) { if (Math.abs(fromCenterX) >= Math.abs(fromCenterY)) {
return "left"; return fromCenterX < 0 ? "left" : "right";
} }
if (nearestDistance === rightDistance) { return fromCenterY < 0 ? "up" : "down";
return "right";
}
if (nearestDistance === topDistance) {
return "up";
}
return "down";
} }
function previewRectFor(cursor, targetGeometry) { function previewRectFor(cursor, targetGeometry) {
const direction = insertDirection(cursor, targetGeometry); const direction = insertDirection(cursor, targetGeometry);
if (direction === "") {
return null;
}
const orientation = direction === "left" || direction === "right" ? "vertical" : "horizontal"; const orientation = direction === "left" || direction === "right" ? "vertical" : "horizontal";
const splitGeometries = splitRect(targetGeometry, orientation); const splitGeometries = splitRect(targetGeometry, orientation);
if (direction === "left" || direction === "up") { if (direction === "left" || direction === "up") {
@ -208,8 +208,11 @@ KWinComponents.SceneEffect {
const cursor = KWinComponents.Workspace.cursorPos; const cursor = KWinComponents.Workspace.cursorPos;
const targetGeometry = findTileAtCursor(cursor, window); const targetGeometry = findTileAtCursor(cursor, window);
if (targetGeometry) { if (targetGeometry) {
showPreview("insert", previewRectFor(cursor, targetGeometry), window.output.name); const insertGeometry = previewRectFor(cursor, targetGeometry);
return; if (validRect(insertGeometry)) {
showPreview("insert", insertGeometry, window.output.name);
return;
}
} }
if (outsideWorkArea(window, cursor)) { if (outsideWorkArea(window, cursor)) {

View file

@ -27,6 +27,7 @@ Item {
readonly property int outerGap: 8 readonly property int outerGap: 8
readonly property int innerGap: 8 // half applied to each edge 4px per tile readonly property int innerGap: 8 // half applied to each edge 4px per tile
readonly property real stablePrimaryRatio: 0.58 readonly property real stablePrimaryRatio: 0.58
readonly property real insertIntentDeadZone: 0.18
// State // State
@ -540,6 +541,7 @@ Item {
if (win.maximizable) win.setMaximize(false, false); if (win.maximizable) win.setMaximize(false, false);
win.noBorder = false; win.noBorder = false;
retileScreen(targetName);
scheduleRetile(targetName); scheduleRetile(targetName);
} }
@ -569,6 +571,7 @@ Item {
// (Same pattern as convergentwindows constrainAfterRestoreTimer.) // (Same pattern as convergentwindows constrainAfterRestoreTimer.)
if (win.maximizable) win.setMaximize(false, false); if (win.maximizable) win.setMaximize(false, false);
win.noBorder = false; win.noBorder = false;
retileScreen(name);
scheduleRetile(name); scheduleRetile(name);
} }
@ -801,16 +804,18 @@ Item {
function tileInsertDirection(cursor, rect) { function tileInsertDirection(cursor, rect) {
if (!validRect(rect)) return ""; if (!validRect(rect)) return "";
const left = Math.abs(cursor.x - rect.x); const relativeX = (cursor.x - rect.x) / rect.width;
const right = Math.abs((rect.x + rect.width) - cursor.x); const relativeY = (cursor.y - rect.y) / rect.height;
const top = Math.abs(cursor.y - rect.y); const fromCenterX = relativeX - 0.5;
const bottom = Math.abs((rect.y + rect.height) - cursor.y); const fromCenterY = relativeY - 0.5;
const nearest = Math.min(left, right, top, bottom); if (Math.abs(fromCenterX) < insertIntentDeadZone && Math.abs(fromCenterY) < insertIntentDeadZone) {
return "";
}
if (nearest === left) return "left"; if (Math.abs(fromCenterX) >= Math.abs(fromCenterY)) {
if (nearest === right) return "right"; return fromCenterX < 0 ? "left" : "right";
if (nearest === top) return "up"; }
return "down"; return fromCenterY < 0 ? "up" : "down";
} }
function previewInsertRect(win, target, position) { function previewInsertRect(win, target, position) {

View file

@ -53,14 +53,20 @@ require_line "$effect_qml" "Behavior on height"
require_line "$effect_qml" "showPreview(\"insert\"" require_line "$effect_qml" "showPreview(\"insert\""
require_line "$effect_qml" "showPreview(\"float\"" require_line "$effect_qml" "showPreview(\"float\""
require_line "$effect_qml" "showPreview(\"restore\"" require_line "$effect_qml" "showPreview(\"restore\""
require_line "$effect_qml" "readonly property real insertIntentDeadZone: 0.18"
require_line "$effect_qml" "if (Math.abs(fromCenterX) < insertIntentDeadZone && Math.abs(fromCenterY) < insertIntentDeadZone)"
require_line "$tiling_script" "readonly property real stablePrimaryRatio: 0.58" require_line "$tiling_script" "readonly property real stablePrimaryRatio: 0.58"
require_line "$tiling_script" "readonly property real insertIntentDeadZone: 0.18"
require_line "$tiling_script" "function orderedWindowsForScreen(outputName)" require_line "$tiling_script" "function orderedWindowsForScreen(outputName)"
require_line "$tiling_script" "function buildStableStack(windows, startIndex)" require_line "$tiling_script" "function buildStableStack(windows, startIndex)"
require_line "$tiling_script" "function buildStableLayout(windows)" require_line "$tiling_script" "function buildStableLayout(windows)"
require_line "$tiling_script" "function setStableLayout(outputName, windows)" require_line "$tiling_script" "function setStableLayout(outputName, windows)"
require_line "$tiling_script" "setStableLayout(outputName, windows)" require_line "$tiling_script" "setStableLayout(outputName, windows)"
require_line "$tiling_script" "setStableLayout(outputName, remaining)" require_line "$tiling_script" "setStableLayout(outputName, remaining)"
require_line "$tiling_script" "retileScreen(name)"
require_line "$tiling_script" "retileScreen(targetName)"
require_line "$tiling_script" "if (Math.abs(fromCenterX) < insertIntentDeadZone && Math.abs(fromCenterY) < insertIntentDeadZone)"
reject_line "$tiling_script" "targetKey = lastLeafKey(rootNode)" reject_line "$tiling_script" "targetKey = lastLeafKey(rootNode)"
reject_line "$tiling_script" "setScreenLayout(outputName, splitLeaf(rootNode, targetKey" reject_line "$tiling_script" "setScreenLayout(outputName, splitLeaf(rootNode, targetKey"