quickshell: geometric melt into the frame column — one shape, cubic-morphed right side
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
dcf31fbe63
commit
1a71f2c07b
1 changed files with 60 additions and 72 deletions
|
|
@ -514,26 +514,22 @@ in
|
|||
}
|
||||
}
|
||||
|
||||
// Frame right-column inner border — starts below a flush-right
|
||||
// dropdown's bottom curve and follows the morph. The short y
|
||||
// animation softens the dock/undock jump.
|
||||
// Frame right-column inner border — always full: when a panel
|
||||
// is merged, its own right-edge stroke sits exactly on this
|
||||
// line, so they coincide instead of needing a gap.
|
||||
Rectangle {
|
||||
x: bar.width - Theme.frameWidth - Theme.borderWidth / 2
|
||||
y: chrome.mergedRight ? 30 + chrome.height + 8 : 38
|
||||
y: 38
|
||||
width: Theme.borderWidth
|
||||
height: Math.max(0, bar.height - Theme.frameWidth - 8 - y)
|
||||
color: Theme.base03
|
||||
Behavior on y {
|
||||
NumberAnimation { duration: 90; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
// Frame top-right inner corner — fades out while a flush-right
|
||||
// dropdown is merged into the column there.
|
||||
// Frame top-right inner corner — fades in sync with the
|
||||
// panel's geometric melt into the column.
|
||||
Shape {
|
||||
opacity: chrome.mergedRight ? 0 : 1
|
||||
opacity: 1 - chrome.mergeP
|
||||
visible: opacity > 0.01
|
||||
Behavior on opacity { NumberAnimation { duration: 90 } }
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
|
|
@ -1209,15 +1205,29 @@ in
|
|||
}
|
||||
}
|
||||
|
||||
// Panel silhouette (caelestia-inspired): fill and border are
|
||||
// each ONE continuous path — concave ear, side, rounded bottom
|
||||
// corners, side, ear — so there are no seams or junctions at
|
||||
// all. The border path is open at the top where the panel
|
||||
// joins the bar; the bar's own border strips meet it there.
|
||||
// Panel silhouette: fill and border are each ONE continuous
|
||||
// path — concave ear, side, rounded bottom corners, side,
|
||||
// ear — so there are no seams or junctions at all. The border
|
||||
// path is open at the top where the panel joins the bar.
|
||||
//
|
||||
// `merge` (0..1) continuously morphs the RIGHT side between
|
||||
// floating (concave top ear, convex bottom corner at an 8px
|
||||
// inset) and merged-into-the-frame-column (edge flush, ear
|
||||
// collapsed, bottom corner flipped into a concave flare).
|
||||
// The right side is built from cubics (kappa 0.5523) because
|
||||
// they degrade gracefully through the zero-radius midpoint,
|
||||
// where arcs would degenerate.
|
||||
component PanelShape: Shape {
|
||||
id: pshape
|
||||
property real merge: 0
|
||||
readonly property real ear: 8
|
||||
readonly property real rr: Math.min(8, Math.max(1, height / 2))
|
||||
// Right-side morph geometry
|
||||
readonly property real xr: width - ear * (1 - merge) // right edge x
|
||||
readonly property real re: ear * (1 - merge) // top ear radius
|
||||
readonly property real rek: re * 0.5523
|
||||
readonly property real bey: height - ear + 2 * ear * merge // bottom feature endpoint y
|
||||
readonly property real bck: ear * 0.5523 * (1 - 2 * merge) // endpoint control offset; sign flips at the melt point
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
|
|
@ -1227,10 +1237,18 @@ in
|
|||
PathArc { x: pshape.ear; y: Math.min(pshape.ear, pshape.height); radiusX: pshape.ear; radiusY: pshape.ear; direction: PathArc.Clockwise }
|
||||
PathLine { x: pshape.ear; y: pshape.height - pshape.rr }
|
||||
PathArc { x: pshape.ear + pshape.rr; y: pshape.height; radiusX: pshape.rr; radiusY: pshape.rr; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: pshape.width - pshape.ear - pshape.rr; y: pshape.height }
|
||||
PathArc { x: pshape.width - pshape.ear; y: pshape.height - pshape.rr; radiusX: pshape.rr; radiusY: pshape.rr; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: pshape.width - pshape.ear; y: Math.min(pshape.ear, pshape.height) }
|
||||
PathArc { x: pshape.width; y: 0; radiusX: pshape.ear; radiusY: pshape.ear; direction: PathArc.Clockwise }
|
||||
PathLine { x: pshape.xr - pshape.ear; y: pshape.height }
|
||||
PathCubic {
|
||||
x: pshape.xr; y: pshape.bey
|
||||
control1X: pshape.xr - pshape.ear + 4.42; control1Y: pshape.height
|
||||
control2X: pshape.xr; control2Y: pshape.bey + pshape.bck
|
||||
}
|
||||
PathLine { x: pshape.xr; y: Math.min(pshape.re, pshape.height) }
|
||||
PathCubic {
|
||||
x: pshape.xr + pshape.re; y: 0
|
||||
control1X: pshape.xr; control1Y: Math.max(0, Math.min(pshape.re, pshape.height) - pshape.rek)
|
||||
control2X: pshape.xr + pshape.re - pshape.rek; control2Y: 0
|
||||
}
|
||||
PathLine { x: 0; y: 0 }
|
||||
}
|
||||
|
||||
|
|
@ -1243,45 +1261,18 @@ in
|
|||
PathArc { x: pshape.ear; y: Math.min(pshape.ear, pshape.height); radiusX: pshape.ear; radiusY: pshape.ear; direction: PathArc.Clockwise }
|
||||
PathLine { x: pshape.ear; y: pshape.height - pshape.rr }
|
||||
PathArc { x: pshape.ear + pshape.rr; y: pshape.height; radiusX: pshape.rr; radiusY: pshape.rr; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: pshape.width - pshape.ear - pshape.rr; y: pshape.height }
|
||||
PathArc { x: pshape.width - pshape.ear; y: pshape.height - pshape.rr; radiusX: pshape.rr; radiusY: pshape.rr; direction: PathArc.Counterclockwise }
|
||||
PathLine { x: pshape.width - pshape.ear; y: Math.min(pshape.ear, pshape.height) }
|
||||
PathArc { x: pshape.width; y: 0; radiusX: pshape.ear; radiusY: pshape.ear; direction: PathArc.Clockwise }
|
||||
}
|
||||
}
|
||||
|
||||
// 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 }
|
||||
PathLine { x: pshape.xr - pshape.ear; y: pshape.height }
|
||||
PathCubic {
|
||||
x: pshape.xr; y: pshape.bey
|
||||
control1X: pshape.xr - pshape.ear + 4.42; control1Y: pshape.height
|
||||
control2X: pshape.xr; control2Y: pshape.bey + pshape.bck
|
||||
}
|
||||
PathLine { x: pshape.xr; y: Math.min(pshape.re, pshape.height) }
|
||||
PathCubic {
|
||||
x: pshape.xr + pshape.re; y: 0
|
||||
control1X: pshape.xr; control1Y: Math.max(0, Math.min(pshape.re, pshape.height) - pshape.rek)
|
||||
control2X: pshape.xr + pshape.re - pshape.rek; control2Y: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1365,22 +1356,19 @@ in
|
|||
NumberAnimation { duration: 280; easing.type: Easing.OutExpo }
|
||||
}
|
||||
|
||||
// Crossfade between the floating and column-merged
|
||||
// silhouettes at dock/undock; the brief double-draw of
|
||||
// the (near-identical) bodies is imperceptible over blur.
|
||||
PanelShape {
|
||||
opacity: chrome.mergedRight ? 0 : 1
|
||||
visible: opacity > 0.01
|
||||
Behavior on opacity { NumberAnimation { duration: 90 } }
|
||||
width: chrome.width
|
||||
height: chrome.height
|
||||
// Continuous geometric melt instead of a shape swap: on
|
||||
// dock the ear collapses, the edge slides the last 8px
|
||||
// into the column and the bottom corner flips into the
|
||||
// flare — one shape the whole way.
|
||||
property real mergeP: mergedRight ? 1 : 0
|
||||
Behavior on mergeP {
|
||||
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
|
||||
}
|
||||
PanelShapeFlush {
|
||||
opacity: chrome.mergedRight ? 1 : 0
|
||||
visible: opacity > 0.01
|
||||
Behavior on opacity { NumberAnimation { duration: 90 } }
|
||||
|
||||
PanelShape {
|
||||
width: chrome.width
|
||||
height: chrome.height
|
||||
merge: chrome.mergeP
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue