refactor(quickshell): drop the Persona names from the primitives
P5Panel, P5Button, P5Slider and P5Tooltip become Panel, ActionButton, LevelSlider and Tooltip. ActionButton and LevelSlider avoid clashing with QtQuick.Controls, which the ask panel imports.
This commit is contained in:
1 parent
7b7cd3e575
commit
add12ccc76
20 files changed
+29
-29
No files matched your search
@@ -0,0 +1,303 @@
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import QtQuick.Effects
|
||||
import "root:/config"
|
||||
import "root:/components"
|
||||
|
||||
// The base surface for everything in this shell. Under the YoRHa theme it is a
|
||||
// rounded rectangle with a hairline border, an optional dot wash, an optional
|
||||
// accent bar down its leading edge, and optional corner ticks.
|
||||
//
|
||||
// The lean and chop knobs still exist and still work, so a caller can bring a
|
||||
// shape back on purpose, but Theme.skew and Theme.cut are both zero.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property color fill: Theme.surface
|
||||
property real fillOpacity: 0.94
|
||||
property color border: Theme.outline
|
||||
property real borderWidth: Theme.stroke
|
||||
|
||||
// Lean of the vertical edges, as a fraction of height. 0 = upright.
|
||||
property real lean: Theme.skew
|
||||
property bool leanLeft: false
|
||||
|
||||
// Tall panels (the right-hand rail) slant their horizontal edges instead,
|
||||
// by a fraction of width, and inset their content top/bottom rather than
|
||||
// left/right.
|
||||
property bool vertical: false
|
||||
|
||||
// Corner chop size, and which corners get chopped (indices into the
|
||||
// polygon, clockwise from the top-left). Default chops the two corners that
|
||||
// read as "cut" against the lean.
|
||||
property real chop: Theme.cut
|
||||
property var chopCorners: [1, 3]
|
||||
|
||||
// Corner radius. Used whenever the panel neither leans nor is chopped,
|
||||
// which under the YoRHa theme is always.
|
||||
property real radius: Theme.radiusM
|
||||
|
||||
// Accent bar down the leading edge.
|
||||
property bool slash: false
|
||||
property color slashColor: Theme.accent
|
||||
property real slashWidth: 4
|
||||
|
||||
// Dot-grid wash.
|
||||
property bool halftone: false
|
||||
property color halftoneColor: Theme.primary
|
||||
property real halftoneOpacity: 0.07
|
||||
|
||||
// Accepted and ignored. This used to paint a teal-to-transparent gradient
|
||||
// down the top of every panel in the shell — the single biggest source of
|
||||
// the murky blue cast. Fills are flat now. Kept as a no-op property so the
|
||||
// call sites that still set it don't have to be chased down.
|
||||
property bool sheen: false
|
||||
|
||||
// Optional print stack: an ink copy of the silhouette offset behind the
|
||||
// fill, and an ink keyline outside the border. Both off by default — on a
|
||||
// dark ground they only thicken the edge.
|
||||
property bool drop: false
|
||||
property real dropOffset: Theme.dropOffset
|
||||
property color dropColor: Theme.ink
|
||||
property bool keyline: false
|
||||
property real keylineWidth: Theme.keylineWidth
|
||||
|
||||
// Corner ticks: a short L at each corner, just outside the border.
|
||||
property bool ticks: false
|
||||
property color tickColor: Theme.outline
|
||||
property real tickLength: Theme.tick
|
||||
|
||||
readonly property real leanInset:
|
||||
(vertical ? width : height) * Math.abs(lean)
|
||||
|
||||
// The outline every layer below is drawn from.
|
||||
readonly property string outline: root.lean === 0 && root.chop <= 0
|
||||
? Geom.roundedRect(root.width, root.height, root.radius)
|
||||
: Geom.svg(Geom.chamfer(root.polygon, root.chop, root.chopCorners).slice(0, -1))
|
||||
|
||||
readonly property var polygon: vertical
|
||||
? (leanLeft ? Geom.parallelogramVL(width, height, lean)
|
||||
: Geom.parallelogramV(width, height, lean))
|
||||
: (leanLeft ? Geom.parallelogramL(width, height, lean)
|
||||
: Geom.parallelogram(width, height, lean))
|
||||
|
||||
// Child content lives inside `contentItem`, inset on both sides far enough
|
||||
// to clear the slanted edges. Panels do not auto-size: callers set implicit
|
||||
// sizes from their own layout (`row.implicitWidth + contentPad * 2`), which
|
||||
// keeps the shape maths out of every binding loop.
|
||||
default property alias content: contentHolder.data
|
||||
readonly property alias contentItem: contentHolder
|
||||
property real padding: Theme.padM
|
||||
|
||||
readonly property real contentPad: padding + leanInset
|
||||
|
||||
// The panel outline as a mask texture. Anything that is painted from the
|
||||
// raw polygon rather than from the chamfered one has to be clipped to this,
|
||||
// or it escapes at the chopped corners.
|
||||
Shape {
|
||||
id: silhouette
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
fillColor: "white"
|
||||
strokeColor: "transparent"
|
||||
joinStyle: ShapePath.MiterJoin
|
||||
|
||||
PathSvg {
|
||||
path: root.outline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Offset black copy of the silhouette, behind everything.
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: root.drop && root.dropOffset > 0
|
||||
sourceComponent: Shape {
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
asynchronous: false
|
||||
|
||||
transform: Translate {
|
||||
x: root.dropOffset * (root.leanLeft ? -1 : 1)
|
||||
y: root.dropOffset
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
fillColor: root.dropColor
|
||||
strokeColor: "transparent"
|
||||
joinStyle: ShapePath.MiterJoin
|
||||
|
||||
PathSvg {
|
||||
path: root.outline
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shape {
|
||||
id: body
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
asynchronous: false
|
||||
|
||||
// Fill, carrying the black keyline. The stroke is centred on the path,
|
||||
// so half of it sits outside the silhouette — which is exactly the
|
||||
// heavy outer contour the style wants.
|
||||
ShapePath {
|
||||
fillColor: Theme.alpha(root.fill, root.fillOpacity)
|
||||
strokeColor: root.keyline ? root.dropColor : "transparent"
|
||||
strokeWidth: root.keyline
|
||||
? root.borderWidth + root.keylineWidth * 2
|
||||
: 0
|
||||
joinStyle: ShapePath.MiterJoin
|
||||
capStyle: ShapePath.FlatCap
|
||||
|
||||
PathSvg {
|
||||
path: root.outline
|
||||
}
|
||||
}
|
||||
|
||||
// Coloured border, drawn on top of the keyline so it reads as an inlay.
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
strokeColor: root.borderWidth > 0 ? root.border : "transparent"
|
||||
strokeWidth: root.borderWidth
|
||||
joinStyle: ShapePath.MiterJoin
|
||||
capStyle: ShapePath.FlatCap
|
||||
|
||||
PathSvg {
|
||||
path: root.outline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Halftone wash, masked to the panel silhouette.
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: root.halftone
|
||||
asynchronous: true
|
||||
sourceComponent: Image {
|
||||
anchors.fill: parent
|
||||
source: Theme.texDots
|
||||
fillMode: Image.Tile
|
||||
opacity: root.halftoneOpacity
|
||||
smooth: false
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
colorization: 1.0
|
||||
colorizationColor: root.halftoneColor
|
||||
maskEnabled: true
|
||||
maskSource: silhouette
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Leading-edge slash.
|
||||
//
|
||||
// Its polygon is built from the panel's raw corners, so on a panel with a
|
||||
// chopped corner the slash used to run straight through the cut and out the
|
||||
// far side — an accent wedge sitting a few pixels outside the silhouette,
|
||||
// at an angle that matched nothing. It is masked to the outline now, so the
|
||||
// slash ends exactly where the chamfer does.
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: root.slash
|
||||
sourceComponent: Item {
|
||||
Shape {
|
||||
id: slashShape
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
fillColor: root.slashColor
|
||||
strokeColor: "transparent"
|
||||
|
||||
PathPolyline {
|
||||
path: {
|
||||
const t = root.slashWidth;
|
||||
|
||||
// On a rail slab the slash runs along the slanted
|
||||
// top edge instead of down a side.
|
||||
if (root.vertical) {
|
||||
const voff = root.width * root.lean;
|
||||
return root.leanLeft
|
||||
? Geom.closed([Qt.point(0, 0), Qt.point(root.width, voff),
|
||||
Qt.point(root.width, voff + t), Qt.point(0, t)])
|
||||
: Geom.closed([Qt.point(0, voff), Qt.point(root.width, 0),
|
||||
Qt.point(root.width, t), Qt.point(0, voff + t)]);
|
||||
}
|
||||
|
||||
const off = root.height * root.lean;
|
||||
return root.leanLeft
|
||||
? Geom.closed([Qt.point(0, 0), Qt.point(t, 0),
|
||||
Qt.point(off + t, root.height), Qt.point(off, root.height)])
|
||||
: Geom.closed([Qt.point(off, 0), Qt.point(off + t, 0),
|
||||
Qt.point(t, root.height), Qt.point(0, root.height)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: slashShape
|
||||
maskEnabled: true
|
||||
maskSource: silhouette
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Corner ticks. Two hairlines per corner, sitting 3px outside the panel so
|
||||
// they read as a frame around it rather than as part of its border.
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -3
|
||||
active: root.ticks
|
||||
sourceComponent: Item {
|
||||
Repeater {
|
||||
model: 4
|
||||
|
||||
Item {
|
||||
required property int index
|
||||
readonly property bool atRight: index === 1 || index === 2
|
||||
readonly property bool atBottom: index >= 2
|
||||
|
||||
x: atRight ? parent.width - width : 0
|
||||
y: atBottom ? parent.height - height : 0
|
||||
width: root.tickLength
|
||||
height: root.tickLength
|
||||
|
||||
Rectangle {
|
||||
y: parent.atBottom ? parent.height - 1 : 0
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: root.tickColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: parent.atRight ? parent.width - 1 : 0
|
||||
width: 1
|
||||
height: parent.height
|
||||
color: root.tickColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentHolder
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: root.vertical ? root.padding : root.contentPad
|
||||
anchors.rightMargin: root.vertical ? root.padding : root.contentPad
|
||||
anchors.topMargin: root.vertical ? root.contentPad : root.padding
|
||||
anchors.bottomMargin: root.vertical ? root.contentPad : root.padding
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user