Files
quickdots/shell.qml
2026-04-24 02:14:35 +00:00

248 lines
9.0 KiB
QML

//@ pragma UseQApplication
import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import "bar"
import "launcher"
import "widgets/bgDate"
import "config" as Cfg
ShellRoot {
id: root
readonly property bool isTop: Cfg.Config.barPosition === "top"
property color frameBackground: Cfg.Config.theme.bgFrame
property color frameBorderColor: Cfg.Config.theme.borderPopup
// ── Spacing Gap: Top / Bottom ────────────────────────────────────────
Variants {
model: Quickshell.screens
PanelWindow {
required property var modelData
screen: modelData
anchors {
top: !root.isTop
bottom: root.isTop
left: true
right: true
}
implicitHeight: Cfg.Config.margin
color: "transparent"
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.exclusiveZone: Cfg.Config.margin
mask: Region {}
}
}
// ── Spacing Gap: Left ────────────────────────────────────────────────
Variants {
model: Quickshell.screens
PanelWindow {
required property var modelData
screen: modelData
anchors { left: true; top: true; bottom: true }
implicitWidth: Cfg.Config.margin
color: "transparent"
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.exclusiveZone: Cfg.Config.margin
mask: Region {}
}
}
// ── Spacing Gap: Right ───────────────────────────────────────────────
Variants {
model: Quickshell.screens
PanelWindow {
required property var modelData
screen: modelData
anchors { right: true; top: true; bottom: true }
implicitWidth: Cfg.Config.margin
color: "transparent"
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.exclusiveZone: Cfg.Config.margin
mask: Region {}
}
}
// ── Wallpaper ────────────────────────────────────────────────────────
Variants {
model: Quickshell.screens
PanelWindow {
required property var modelData
screen: modelData
color: "transparent"
anchors { top: true; bottom: true; left: true; right: true }
WlrLayershell.layer: WlrLayer.Background
WlrLayershell.namespace: "main-shell-wallpaper"
WlrLayershell.exclusiveZone: -1
mask: Region {}
Image {
anchors.fill: parent
source: {
var p = Cfg.Config.theme.wallpaper
return p.startsWith("file://") ? p : "file://" + p
}
fillMode: Image.PreserveAspectCrop
asynchronous: true
}
}
}
// ── Center Clock (bgDate Widget) ─────────────────────────────────────
Variants {
model: Cfg.Config.enableBgDate ? Quickshell.screens : []
BgDate {
modelData: modelData
}
}
// ── Frame ────────────────────────────────────────────────────────────
Variants {
model: Quickshell.screens
PanelWindow {
required property var modelData
screen: modelData
color: "transparent"
anchors { top: true; bottom: true; left: true; right: true }
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "main-shell-frame"
WlrLayershell.exclusiveZone: -1
mask: Region {}
Canvas {
id: frameCanvas
anchors.fill: parent
// Animation Properties
opacity: 0
scale: 0.98
Component.onCompleted: startupAnimation.start()
ParallelAnimation {
id: startupAnimation
NumberAnimation {
target: frameCanvas; property: "opacity"
from: 0; to: 1; duration: 500
easing.type: Easing.OutCubic
}
NumberAnimation {
target: frameCanvas; property: "scale"
from: 0.98; to: 1.0; duration: 600
easing.type: Easing.OutBack
}
}
onPaint: {
var ctx = getContext("2d")
ctx.clearRect(0, 0, width, height)
var mT = root.isTop ? Cfg.Config.barHeight : Cfg.Config.margin
var mB = root.isTop ? Cfg.Config.margin : Cfg.Config.barHeight
var r = Cfg.Config.radius
var cw = width - Cfg.Config.margin * 2
var ch = height - mT - mB
var x = Cfg.Config.margin
var y = mT
ctx.fillStyle = root.frameBackground
ctx.fillRect(0, 0, width, mT)
ctx.fillRect(0, height - mB, width, mB)
ctx.fillRect(0, mT, x, ch)
ctx.fillRect(x + cw, mT, width - (x + cw), ch)
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(x + r, y)
ctx.arc(x + r, y + r, r, -Math.PI / 2, Math.PI, true)
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(x + cw, y)
ctx.lineTo(x + cw - r, y)
ctx.arc(x + cw - r, y + r, r, -Math.PI / 2, 0, false)
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(x + cw, y + ch)
ctx.lineTo(x + cw, y + ch - r)
ctx.arc(x + cw - r, y + ch - r, r, 0, Math.PI / 2, false)
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(x, y + ch)
ctx.lineTo(x + r, y + ch)
ctx.arc(x + r, y + ch - r, r, Math.PI / 2, Math.PI, false)
ctx.closePath()
ctx.fill()
ctx.lineWidth = Cfg.Config.frameBorderWidth
ctx.strokeStyle = root.frameBorderColor
ctx.beginPath()
ctx.moveTo(x + r, y)
ctx.lineTo(x + cw - r, y)
ctx.arcTo(x + cw, y, x + cw, y + r, r)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(x + cw, y + r)
ctx.lineTo(x + cw, y + ch - r)
ctx.arcTo(x + cw, y + ch, x + cw - r, y + ch, r)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(x + cw - r, y + ch)
ctx.lineTo(x + r, y + ch)
ctx.arcTo(x, y + ch, x, y + ch - r, r)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(x, y + ch - r)
ctx.lineTo(x, y + r)
ctx.arcTo(x, y, x + r, y, r)
ctx.stroke()
}
onWidthChanged: requestPaint()
onHeightChanged: requestPaint()
Connections {
target: root
function onFrameBackgroundChanged() { frameCanvas.requestPaint() }
function onFrameBorderColorChanged() { frameCanvas.requestPaint() }
}
}
}
}
// ── Launcher ─────────────────────────────────────────────────────────
Variants {
model: Quickshell.screens
Launcher {
required property var modelData
screen: modelData
isTop: root.isTop
Component.onCompleted: modelData.launcher = this
}
}
// ── Bar ──────────────────────────────────────────────────────────────
Variants {
model: Quickshell.screens
Bar {
screen: modelData
isTop: root.isTop
}
}
}