Round normal window decorations

Reserve side and bottom decoration borders for normal windows and draw a rounded decoration frame so windows match the convergence workspace shape. Add static guards for the decoration and existing workspace-frame corner geometry.
This commit is contained in:
Marco Allegretti 2026-05-23 08:53:02 +02:00
parent d03f3585a3
commit 36d9004473
3 changed files with 47 additions and 4 deletions

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: EUPL-1.2 // SPDX-License-Identifier: EUPL-1.2
import QtQuick import QtQuick
import QtQuick.Shapes
import org.kde.kwin.decoration import org.kde.kwin.decoration
import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
@ -20,7 +21,9 @@ Decoration {
readonly property int btnSize: 16 readonly property int btnSize: 16
readonly property int btnSpacing: 8 readonly property int btnSpacing: 8
readonly property int btnSideMargin: 12 readonly property int btnSideMargin: 12
readonly property int cornerRadius: decoration.client.maximized ? 0 : 8 readonly property int normalCornerRadius: 8
readonly property int cornerRadius: decoration.client.maximized ? 0 : normalCornerRadius
readonly property int frameThickness: decoration.client.maximized ? 0 : normalCornerRadius
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast) readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
readonly property bool windowMenuAllowed: !ShellSettings.Settings.convergenceModeEnabled readonly property bool windowMenuAllowed: !ShellSettings.Settings.convergenceModeEnabled
|| ShellSettings.Settings.gamingModeEnabled || ShellSettings.Settings.gamingModeEnabled
@ -28,9 +31,9 @@ Decoration {
Component.onCompleted: { Component.onCompleted: {
borders.top = barHeight; borders.top = barHeight;
borders.left = 0; borders.left = normalCornerRadius;
borders.right = 0; borders.right = normalCornerRadius;
borders.bottom = 0; borders.bottom = normalCornerRadius;
// Keep titlebar controls available for maximized windows in desktop // Keep titlebar controls available for maximized windows in desktop
// convergence mode. Mobile mode uses noBorder=true and bypasses this. // convergence mode. Mobile mode uses noBorder=true and bypasses this.
@ -45,6 +48,36 @@ Decoration {
deco: decoration deco: decoration
} }
readonly property color frameColor: decoration.client.active ? root.activeBar : root.inactiveBar
Shape {
anchors.fill: parent
visible: !decoration.client.maximized
ShapePath {
fillColor: root.frameColor
fillRule: ShapePath.OddEvenFill
strokeWidth: 0
startX: root.cornerRadius
startY: 0
PathLine { x: root.width - root.cornerRadius; y: 0 }
PathArc { x: root.width; y: root.cornerRadius; radiusX: root.cornerRadius; radiusY: root.cornerRadius }
PathLine { x: root.width; y: root.height - root.cornerRadius }
PathArc { x: root.width - root.cornerRadius; y: root.height; radiusX: root.cornerRadius; radiusY: root.cornerRadius }
PathLine { x: root.cornerRadius; y: root.height }
PathArc { x: 0; y: root.height - root.cornerRadius; radiusX: root.cornerRadius; radiusY: root.cornerRadius }
PathLine { x: 0; y: root.cornerRadius }
PathArc { x: root.cornerRadius; y: 0; radiusX: root.cornerRadius; radiusY: root.cornerRadius }
PathMove { x: root.frameThickness; y: root.barHeight }
PathLine { x: root.width - root.frameThickness; y: root.barHeight }
PathLine { x: root.width - root.frameThickness; y: root.height - root.frameThickness }
PathLine { x: root.frameThickness; y: root.height - root.frameThickness }
PathLine { x: root.frameThickness; y: root.barHeight }
}
}
// Faint window outline // Faint window outline
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent

View file

@ -53,6 +53,10 @@ require_line "$folio_main" "readonly property real workAreaY: topReservedHeight
require_line "$folio_main" "readonly property real workAreaWidth: Math.max(0, width - frameThickness * 2)" require_line "$folio_main" "readonly property real workAreaWidth: Math.max(0, width - frameThickness * 2)"
require_line "$folio_main" "readonly property real workAreaHeight: Math.max(0, height - topReservedHeight - bottomReservedHeight - frameThickness * 2)" require_line "$folio_main" "readonly property real workAreaHeight: Math.max(0, height - topReservedHeight - bottomReservedHeight - frameThickness * 2)"
require_line "$folio_main" "fillRule: ShapePath.OddEvenFill" require_line "$folio_main" "fillRule: ShapePath.OddEvenFill"
require_line "$folio_main" "PathLine { x: workspaceFrame.width; y: workspaceFrame.height - workspaceFrame.bottomReservedHeight }"
require_line "$folio_main" "PathLine { x: 0; y: workspaceFrame.height - workspaceFrame.bottomReservedHeight }"
require_line "$folio_main" "PathArc { x: workspaceFrame.workAreaX + workspaceFrame.workAreaWidth - workspaceFrame.frameRadius; y: workspaceFrame.workAreaY + workspaceFrame.workAreaHeight; radiusX: workspaceFrame.frameRadius; radiusY: workspaceFrame.frameRadius }"
require_line "$folio_main" "PathArc { x: workspaceFrame.workAreaX; y: workspaceFrame.workAreaY + workspaceFrame.workAreaHeight - workspaceFrame.frameRadius; radiusX: workspaceFrame.frameRadius; radiusY: workspaceFrame.frameRadius }"
require_line "$folio_main" "readonly property real popupTopY: MobileShell.Constants.topPanelHeight" require_line "$folio_main" "readonly property real popupTopY: MobileShell.Constants.topPanelHeight"
require_line "$folio_main" "+ MobileShell.Constants.convergenceWorkspaceFrameThickness" require_line "$folio_main" "+ MobileShell.Constants.convergenceWorkspaceFrameThickness"
require_line "$folio_main" "readonly property real popupBottomY: parent.height" require_line "$folio_main" "readonly property real popupBottomY: parent.height"

View file

@ -118,6 +118,12 @@ require_line "$decoration_qml" "readonly property bool windowMenuAllowed: !Shell
require_line "$decoration_qml" "return root.windowMenuAllowed;" require_line "$decoration_qml" "return root.windowMenuAllowed;"
require_line "$decoration_qml" "enabled: !root.windowMenuAllowed" require_line "$decoration_qml" "enabled: !root.windowMenuAllowed"
require_line "$decoration_qml" "acceptedButtons: Qt.RightButton" 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 }"
reject_line "$tiling_script" "targetKey = lastLeafKey(rootNode)" reject_line "$tiling_script" "targetKey = lastLeafKey(rootNode)"
reject_line "$tiling_script" "function lastLeafKey(node)" reject_line "$tiling_script" "function lastLeafKey(node)"