import QtQuick import qs.Common import qs.Services Item { id: root required property var barWindow required property var axis required property var barConfig anchors.fill: parent anchors.left: parent.left anchors.top: parent.top readonly property bool gothEnabled: barConfig?.gothCornersEnabled ?? false anchors.leftMargin: -(gothEnabled && axis.isVertical && axis.edge === "right" ? barWindow._wingR : 0) anchors.rightMargin: -(gothEnabled && axis.isVertical && axis.edge === "left" ? barWindow._wingR : 0) anchors.topMargin: -(gothEnabled && !axis.isVertical && axis.edge === "bottom" ? barWindow._wingR : 0) anchors.bottomMargin: -(gothEnabled && !axis.isVertical && axis.edge === "top" ? barWindow._wingR : 0) readonly property real dpr: CompositorService.getScreenScale(barWindow.screen) function requestRepaint() { debounceTimer.restart(); } MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton z: -999 onClicked: { const activePopout = PopoutManager.getActivePopout(barWindow.screen); if (activePopout) { if (activePopout.dashVisible !== undefined) { activePopout.dashVisible = false; } else if (activePopout.notificationHistoryVisible !== undefined) { activePopout.notificationHistoryVisible = false; } else { activePopout.close(); } } TrayMenuManager.closeAllMenus(); } } Timer { id: debounceTimer interval: 50 repeat: false onTriggered: { barShape.requestPaint(); barTint.requestPaint(); barBorder.requestPaint(); } } Canvas { id: barShape anchors.fill: parent antialiasing: true renderTarget: Canvas.FramebufferObject renderStrategy: Canvas.Cooperative readonly property real correctWidth: Theme.px(root.width, dpr) readonly property real correctHeight: Theme.px(root.height, dpr) canvasSize: Qt.size(correctWidth, correctHeight) property real wing: (barConfig?.gothCornersEnabled ?? false) ? Theme.px(barWindow._wingR, dpr) : 0 property real rt: (barConfig?.squareCorners ?? false) ? 0 : Theme.px(Theme.cornerRadius, dpr) onWingChanged: root.requestRepaint() onRtChanged: root.requestRepaint() onCorrectWidthChanged: root.requestRepaint() onCorrectHeightChanged: root.requestRepaint() onVisibleChanged: if (visible) root.requestRepaint() Component.onCompleted: root.requestRepaint() Connections { target: root function onDprChanged() { root.requestRepaint(); } } Connections { target: barWindow function on_BgColorChanged() { root.requestRepaint(); } function onGothCornersEnabledChanged() { root.requestRepaint(); } function onWingtipsRadiusChanged() { root.requestRepaint(); } } Connections { target: Theme function onIsLightModeChanged() { root.requestRepaint(); } function onSurfaceContainerChanged() { root.requestRepaint(); } } onPaint: { const ctx = getContext("2d"); const W = barWindow.isVertical ? correctHeight : correctWidth; const H_raw = barWindow.isVertical ? correctWidth : correctHeight; const R = wing; const RT = rt; const H = H_raw - (R > 0 ? R : 0); const barPos = barConfig?.position ?? 0; const isTop = barPos === SettingsData.Position.Top; const isBottom = barPos === SettingsData.Position.Bottom; const isLeft = barPos === SettingsData.Position.Left; const isRight = barPos === SettingsData.Position.Right; function drawTopPath() { ctx.beginPath(); ctx.moveTo(RT, 0); ctx.lineTo(W - RT, 0); ctx.arcTo(W, 0, W, RT, RT); ctx.lineTo(W, H); if (R > 0) { ctx.lineTo(W, H + R); ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true); ctx.lineTo(R, H); ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true); ctx.lineTo(0, H + R); } else { ctx.lineTo(W, H - RT); ctx.arcTo(W, H, W - RT, H, RT); ctx.lineTo(RT, H); ctx.arcTo(0, H, 0, H - RT, RT); } ctx.lineTo(0, RT); ctx.arcTo(0, 0, RT, 0, RT); ctx.closePath(); } ctx.reset(); ctx.clearRect(0, 0, W, H_raw); ctx.save(); if (isBottom) { ctx.translate(W, H_raw); ctx.rotate(Math.PI); } else if (isLeft) { ctx.translate(0, W); ctx.rotate(-Math.PI / 2); } else if (isRight) { ctx.translate(H_raw, 0); ctx.rotate(Math.PI / 2); } drawTopPath(); ctx.restore(); ctx.fillStyle = barWindow._bgColor; ctx.fill(); } } Canvas { id: barTint anchors.fill: parent antialiasing: true renderTarget: Canvas.FramebufferObject renderStrategy: Canvas.Cooperative readonly property real correctWidth: Theme.px(root.width, dpr) readonly property real correctHeight: Theme.px(root.height, dpr) canvasSize: Qt.size(correctWidth, correctHeight) property real wing: (barConfig?.gothCornersEnabled ?? false) ? Theme.px(barWindow._wingR, dpr) : 0 property real rt: (barConfig?.squareCorners ?? false) ? 0 : Theme.px(Theme.cornerRadius, dpr) property real alphaTint: (barWindow._bgColor?.a ?? 1) < 0.99 ? (Theme.stateLayerOpacity ?? 0) : 0 onWingChanged: root.requestRepaint() onRtChanged: root.requestRepaint() onAlphaTintChanged: root.requestRepaint() onCorrectWidthChanged: root.requestRepaint() onCorrectHeightChanged: root.requestRepaint() onVisibleChanged: if (visible) root.requestRepaint() Component.onCompleted: root.requestRepaint() Connections { target: root function onDprChanged() { root.requestRepaint(); } } Connections { target: barWindow function on_BgColorChanged() { root.requestRepaint(); } function onGothCornersEnabledChanged() { root.requestRepaint(); } function onWingtipsRadiusChanged() { root.requestRepaint(); } } Connections { target: Theme function onIsLightModeChanged() { root.requestRepaint(); } function onSurfaceChanged() { root.requestRepaint(); } } onPaint: { const ctx = getContext("2d"); const W = barWindow.isVertical ? correctHeight : correctWidth; const H_raw = barWindow.isVertical ? correctWidth : correctHeight; const R = wing; const RT = rt; const H = H_raw - (R > 0 ? R : 0); const barPos = barConfig?.position ?? 0; const isTop = barPos === SettingsData.Position.Top; const isBottom = barPos === SettingsData.Position.Bottom; const isLeft = barPos === SettingsData.Position.Left; const isRight = barPos === SettingsData.Position.Right; function drawTopPath() { ctx.beginPath(); ctx.moveTo(RT, 0); ctx.lineTo(W - RT, 0); ctx.arcTo(W, 0, W, RT, RT); ctx.lineTo(W, H); if (R > 0) { ctx.lineTo(W, H + R); ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true); ctx.lineTo(R, H); ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true); ctx.lineTo(0, H + R); } else { ctx.lineTo(W, H - RT); ctx.arcTo(W, H, W - RT, H, RT); ctx.lineTo(RT, H); ctx.arcTo(0, H, 0, H - RT, RT); } ctx.lineTo(0, RT); ctx.arcTo(0, 0, RT, 0, RT); ctx.closePath(); } ctx.reset(); ctx.clearRect(0, 0, W, H_raw); ctx.save(); if (isBottom) { ctx.translate(W, H_raw); ctx.rotate(Math.PI); } else if (isLeft) { ctx.translate(0, W); ctx.rotate(-Math.PI / 2); } else if (isRight) { ctx.translate(H_raw, 0); ctx.rotate(Math.PI / 2); } drawTopPath(); ctx.restore(); ctx.fillStyle = Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, alphaTint); ctx.fill(); } } Canvas { id: barBorder anchors.fill: parent visible: barConfig?.borderEnabled ?? false renderTarget: Canvas.FramebufferObject renderStrategy: Canvas.Cooperative readonly property real correctWidth: Theme.px(root.width, dpr) readonly property real correctHeight: Theme.px(root.height, dpr) canvasSize: Qt.size(correctWidth, correctHeight) property real wing: (barConfig?.gothCornersEnabled ?? false) ? Theme.px(barWindow._wingR, dpr) : 0 property real rt: (barConfig?.squareCorners ?? false) ? 0 : Theme.px(Theme.cornerRadius, dpr) property bool borderEnabled: barConfig?.borderEnabled ?? false antialiasing: rt > 0 || wing > 0 onWingChanged: root.requestRepaint() onRtChanged: root.requestRepaint() onBorderEnabledChanged: root.requestRepaint() onCorrectWidthChanged: root.requestRepaint() onCorrectHeightChanged: root.requestRepaint() onVisibleChanged: if (visible) root.requestRepaint() Component.onCompleted: root.requestRepaint() Connections { target: root function onDprChanged() { root.requestRepaint(); } } Connections { target: Theme function onIsLightModeChanged() { root.requestRepaint(); } function onSurfaceTextChanged() { root.requestRepaint(); } function onPrimaryChanged() { root.requestRepaint(); } function onSecondaryChanged() { root.requestRepaint(); } function onOutlineChanged() { root.requestRepaint(); } } Connections { target: barWindow function onGothCornersEnabledChanged() { root.requestRepaint(); } function onWingtipsRadiusChanged() { root.requestRepaint(); } } onPaint: { if (!borderEnabled) return; const ctx = getContext("2d"); const W = barWindow.isVertical ? correctHeight : correctWidth; const H_raw = barWindow.isVertical ? correctWidth : correctHeight; const R = wing; const RT = rt; const H = H_raw - (R > 0 ? R : 0); const barPos = barConfig?.position ?? 0; const isTop = barPos === SettingsData.Position.Top; const isBottom = barPos === SettingsData.Position.Bottom; const isLeft = barPos === SettingsData.Position.Left; const isRight = barPos === SettingsData.Position.Right; const spacing = barConfig?.spacing ?? 4; ctx.reset(); ctx.clearRect(0, 0, W, H_raw); ctx.save(); if (isBottom) { ctx.translate(W, H_raw); ctx.rotate(Math.PI); } else if (isLeft) { ctx.translate(0, W); ctx.rotate(-Math.PI / 2); } else if (isRight) { ctx.translate(H_raw, 0); ctx.rotate(Math.PI / 2); } const uiThickness = Math.max(1, barConfig?.borderThickness ?? 1); const devThickness = Math.max(1, Math.round(Theme.px(uiThickness, dpr))); const key = barConfig?.borderColor || "surfaceText"; const base = (key === "surfaceText") ? Theme.surfaceText : (key === "primary") ? Theme.primary : Theme.secondary; const color = Theme.withAlpha(base, barConfig?.borderOpacity ?? 1.0); ctx.strokeStyle = color; ctx.lineWidth = devThickness * 2; ctx.lineJoin = "round"; ctx.lineCap = "butt"; function drawFullShape() { ctx.beginPath(); ctx.moveTo(RT, 0); ctx.lineTo(W - RT, 0); ctx.arcTo(W, 0, W, RT, RT); ctx.lineTo(W, H); if (R > 0) { ctx.lineTo(W, H + R); ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true); ctx.lineTo(R, H); ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true); ctx.lineTo(0, H + R); } else { ctx.lineTo(W, H - RT); ctx.arcTo(W, H, W - RT, H, RT); ctx.lineTo(RT, H); ctx.arcTo(0, H, 0, H - RT, RT); } ctx.lineTo(0, RT); ctx.arcTo(0, 0, RT, 0, RT); ctx.closePath(); } drawFullShape(); ctx.clip(); if (spacing > 0) { drawFullShape(); } else { ctx.beginPath(); if (R > 0) { ctx.moveTo(W, H + R); ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true); ctx.lineTo(R, H); ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true); } else { ctx.moveTo(W, H - RT); ctx.arcTo(W, H, W - RT, H, RT); ctx.lineTo(RT, H); ctx.arcTo(0, H, 0, H - RT, RT); } } ctx.stroke(); ctx.restore(); } } }