quickshell: screen frame with rounded cutout; flush-right dropdowns merge into it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0878cba10d
commit
a5feef766d
1 changed files with 132 additions and 7 deletions
|
|
@ -91,6 +91,8 @@ in
|
|||
readonly property string fontFamily: "${monoFont}"
|
||||
// Matches hyprland general.border_size (col.inactive_border = base03)
|
||||
readonly property int borderWidth: 2
|
||||
// Screen frame band; sits inside hyprland's gaps_out (12)
|
||||
readonly property int frameWidth: 6
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
|
@ -457,6 +459,75 @@ in
|
|||
color: Theme.barBg
|
||||
}
|
||||
|
||||
// ── Screen frame: a bar-coloured band around the monitor.
|
||||
// Fill is one donut path (screen rect minus rounded inner
|
||||
// cutout); the inner border is one continuous open path from
|
||||
// the top-left corner the long way around to the bottom-right
|
||||
// corner. The right column's border is separate so it can
|
||||
// open up where a flush-right dropdown merges into it.
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
fillColor: Theme.barBg
|
||||
strokeWidth: -1
|
||||
fillRule: ShapePath.OddEvenFill
|
||||
startX: 0; startY: 30
|
||||
PathLine { x: bar.width; y: 30 }
|
||||
PathLine { x: bar.width; y: bar.height }
|
||||
PathLine { x: 0; y: bar.height }
|
||||
PathLine { x: 0; y: 30 }
|
||||
PathMove { x: Theme.frameWidth + 8; y: 30 }
|
||||
PathLine { x: bar.width - Theme.frameWidth - 8; y: 30 }
|
||||
PathArc { x: bar.width - Theme.frameWidth; y: 38; radiusX: 8; radiusY: 8; direction: PathArc.Clockwise }
|
||||
PathLine { x: bar.width - Theme.frameWidth; y: bar.height - Theme.frameWidth - 8 }
|
||||
PathArc { x: bar.width - Theme.frameWidth - 8; y: bar.height - Theme.frameWidth; radiusX: 8; radiusY: 8; direction: PathArc.Clockwise }
|
||||
PathLine { x: Theme.frameWidth + 8; y: bar.height - Theme.frameWidth }
|
||||
PathArc { x: Theme.frameWidth; y: bar.height - Theme.frameWidth - 8; radiusX: 8; radiusY: 8; direction: PathArc.Clockwise }
|
||||
PathLine { x: Theme.frameWidth; y: 38 }
|
||||
PathArc { x: Theme.frameWidth + 8; y: 30; radiusX: 8; radiusY: 8; direction: PathArc.Clockwise }
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
strokeColor: Theme.base03
|
||||
strokeWidth: Theme.borderWidth
|
||||
capStyle: ShapePath.FlatCap
|
||||
startX: Theme.frameWidth + 8; startY: 30
|
||||
PathArc { x: Theme.frameWidth; y: 38; radiusX: 8; radiusY: 8; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: Theme.frameWidth; y: bar.height - Theme.frameWidth - 8 }
|
||||
PathArc { x: Theme.frameWidth + 8; y: bar.height - Theme.frameWidth; radiusX: 8; radiusY: 8; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: bar.width - Theme.frameWidth - 8; y: bar.height - Theme.frameWidth }
|
||||
PathArc { x: bar.width - Theme.frameWidth; y: bar.height - Theme.frameWidth - 8; radiusX: 8; radiusY: 8; direction: PathArc.Counterclockwise }
|
||||
}
|
||||
}
|
||||
|
||||
// Frame right-column inner border — starts below a flush-right
|
||||
// dropdown's bottom curve and follows the morph.
|
||||
Rectangle {
|
||||
x: bar.width - Theme.frameWidth - Theme.borderWidth / 2
|
||||
y: chrome.visible && chrome.flushRight ? 30 + chrome.height + 8 : 38
|
||||
width: Theme.borderWidth
|
||||
height: Math.max(0, bar.height - Theme.frameWidth - 8 - y)
|
||||
color: Theme.base03
|
||||
}
|
||||
|
||||
// Frame top-right inner corner — hidden while a flush-right
|
||||
// dropdown is merged into the column there.
|
||||
Shape {
|
||||
visible: !(chrome.visible && chrome.flushRight)
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
strokeColor: Theme.base03
|
||||
strokeWidth: Theme.borderWidth
|
||||
capStyle: ShapePath.FlatCap
|
||||
startX: bar.width - Theme.frameWidth - 8; startY: 30
|
||||
PathArc { x: bar.width - Theme.frameWidth; y: 38; radiusX: 8; radiusY: 8; direction: PathArc.Clockwise }
|
||||
}
|
||||
}
|
||||
|
||||
// The "gap source" for the bar border — the morphing chrome
|
||||
// panel takes priority, then the toast. Tracking the animated
|
||||
// chrome means the border gap follows the morph.
|
||||
|
|
@ -475,8 +546,9 @@ in
|
|||
// y=30 so it runs into the panel's edge-centered border stroke.
|
||||
Rectangle {
|
||||
id: barBorderLeft
|
||||
x: 0; y: 30 - Theme.borderWidth / 2
|
||||
width: bar.hasGap ? bar.gapLeft : bar.width
|
||||
x: Theme.frameWidth + 8
|
||||
y: 30 - Theme.borderWidth / 2
|
||||
width: Math.max(0, (bar.hasGap ? bar.gapLeft : bar.width - Theme.frameWidth - 8) - x)
|
||||
height: Theme.borderWidth
|
||||
color: Theme.base03
|
||||
}
|
||||
|
|
@ -487,7 +559,7 @@ in
|
|||
visible: bar.hasGap
|
||||
x: bar.gapRight
|
||||
y: 30 - Theme.borderWidth / 2
|
||||
width: bar.width - x
|
||||
width: Math.max(0, bar.width - Theme.frameWidth - 8 - x)
|
||||
height: Theme.borderWidth
|
||||
color: Theme.base03
|
||||
}
|
||||
|
|
@ -998,7 +1070,9 @@ in
|
|||
property real fullWidth: 200
|
||||
property real fullHeight: 200
|
||||
property int autoCloseMs: 1500
|
||||
property bool alignRight: false // legacy; all dropdowns now center on their widget
|
||||
// Flush-right dropdowns merge into the screen frame's
|
||||
// right column instead of centering on their widget.
|
||||
property bool alignRight: false
|
||||
property real dropdownHeight: open ? fullHeight : 0
|
||||
default property alias content: dropdownContent.data
|
||||
|
||||
|
|
@ -1024,9 +1098,11 @@ in
|
|||
_autoClose.restart();
|
||||
}
|
||||
|
||||
x: Math.round(Math.min(bar.width - width, Math.max(0, dropdownX - width / 2)))
|
||||
x: alignRight
|
||||
? bar.width - Theme.frameWidth - width
|
||||
: Math.round(Math.min(bar.width - Theme.frameWidth - width, Math.max(Theme.frameWidth, dropdownX - width / 2)))
|
||||
y: 30
|
||||
width: fullWidth + 16
|
||||
width: fullWidth + (alignRight ? 8 : 16)
|
||||
height: fullHeight + 4
|
||||
visible: false
|
||||
|
||||
|
|
@ -1065,7 +1141,8 @@ in
|
|||
// reveals/hides with the morph; fades on open/close.
|
||||
Item {
|
||||
id: _dropdownRect
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.top: parent.top
|
||||
width: dropdown.fullWidth
|
||||
height: Math.min(dropdown.fullHeight, chrome.height)
|
||||
|
|
@ -1124,6 +1201,41 @@ in
|
|||
}
|
||||
}
|
||||
|
||||
// Flush-right variant: no right ear; the bottom-right curve
|
||||
// merges the panel into the screen frame's right column.
|
||||
component PanelShapeFlush: Shape {
|
||||
id: pshapeF
|
||||
readonly property real ear: 8
|
||||
readonly property real rr: Math.min(8, Math.max(1, height / 2))
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
fillColor: Theme.barBg
|
||||
strokeWidth: -1
|
||||
startX: 0; startY: 0
|
||||
PathArc { x: pshapeF.ear; y: Math.min(pshapeF.ear, pshapeF.height); radiusX: pshapeF.ear; radiusY: pshapeF.ear; direction: PathArc.Clockwise }
|
||||
PathLine { x: pshapeF.ear; y: pshapeF.height - pshapeF.rr }
|
||||
PathArc { x: pshapeF.ear + pshapeF.rr; y: pshapeF.height; radiusX: pshapeF.rr; radiusY: pshapeF.rr; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: pshapeF.width - pshapeF.ear; y: pshapeF.height }
|
||||
PathArc { x: pshapeF.width; y: pshapeF.height + pshapeF.ear; radiusX: pshapeF.ear; radiusY: pshapeF.ear; direction: PathArc.Clockwise }
|
||||
PathLine { x: pshapeF.width; y: 0 }
|
||||
PathLine { x: 0; y: 0 }
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
strokeColor: Theme.base03
|
||||
strokeWidth: Theme.borderWidth
|
||||
capStyle: ShapePath.FlatCap
|
||||
startX: 0; startY: 0
|
||||
PathArc { x: pshapeF.ear; y: Math.min(pshapeF.ear, pshapeF.height); radiusX: pshapeF.ear; radiusY: pshapeF.ear; direction: PathArc.Clockwise }
|
||||
PathLine { x: pshapeF.ear; y: pshapeF.height - pshapeF.rr }
|
||||
PathArc { x: pshapeF.ear + pshapeF.rr; y: pshapeF.height; radiusX: pshapeF.rr; radiusY: pshapeF.rr; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: pshapeF.width - pshapeF.ear; y: pshapeF.height }
|
||||
PathArc { x: pshapeF.width; y: pshapeF.height + pshapeF.ear; radiusX: pshapeF.ear; radiusY: pshapeF.ear; direction: PathArc.Clockwise }
|
||||
}
|
||||
}
|
||||
|
||||
// The shared morphing panel: follows the active dropdown's
|
||||
// geometry with animation (the caelestia-style morph), snaps
|
||||
// instantly when opening from closed.
|
||||
|
|
@ -1132,6 +1244,7 @@ in
|
|||
property real tX: 0
|
||||
property real tW: 200
|
||||
property real tH: 0
|
||||
property bool flushRight: false
|
||||
property real openH: bar.activeDropdown ? tH : 0
|
||||
|
||||
x: tX
|
||||
|
|
@ -1158,6 +1271,12 @@ in
|
|||
when: bar.activeDropdown !== null
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
Binding {
|
||||
target: chrome; property: "flushRight"
|
||||
value: bar.activeDropdown ? bar.activeDropdown.alignRight : false
|
||||
when: bar.activeDropdown !== null
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
|
||||
Behavior on tX {
|
||||
enabled: chrome.height > 0.5
|
||||
|
|
@ -1172,6 +1291,12 @@ in
|
|||
}
|
||||
|
||||
PanelShape {
|
||||
visible: !chrome.flushRight
|
||||
width: chrome.width
|
||||
height: chrome.height
|
||||
}
|
||||
PanelShapeFlush {
|
||||
visible: chrome.flushRight
|
||||
width: chrome.width
|
||||
height: chrome.height
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue