updated style
This commit is contained in:
@@ -31,6 +31,29 @@ Singleton {
|
||||
];
|
||||
}
|
||||
|
||||
// Vertical counterpart: the *horizontal* edges slant instead, by a fraction
|
||||
// of width. A tall rail slab has to lean this way — leaning its vertical
|
||||
// edges by a fraction of its height would shear it clean off the screen.
|
||||
function parallelogramV(w: real, h: real, lean: real): var {
|
||||
const off = w * lean;
|
||||
return [
|
||||
Qt.point(0, off),
|
||||
Qt.point(w, 0),
|
||||
Qt.point(w, h - off),
|
||||
Qt.point(0, h)
|
||||
];
|
||||
}
|
||||
|
||||
function parallelogramVL(w: real, h: real, lean: real): var {
|
||||
const off = w * lean;
|
||||
return [
|
||||
Qt.point(0, 0),
|
||||
Qt.point(w, off),
|
||||
Qt.point(w, h),
|
||||
Qt.point(0, h - off)
|
||||
];
|
||||
}
|
||||
|
||||
function rect(w: real, h: real): var {
|
||||
return [Qt.point(0, 0), Qt.point(w, 0), Qt.point(w, h), Qt.point(0, h)];
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ Item {
|
||||
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.
|
||||
@@ -37,10 +42,24 @@ Item {
|
||||
// Inner glow along the top edge, for panels that should feel backlit.
|
||||
property bool sheen: false
|
||||
|
||||
readonly property real leanInset: height * Math.abs(lean)
|
||||
readonly property var polygon: leanLeft
|
||||
? Geom.parallelogramL(width, height, lean)
|
||||
: Geom.parallelogram(width, height, lean)
|
||||
// The screen-print stack. A pure-black copy of the silhouette sits behind
|
||||
// the fill, offset down-and-right, and the fill carries a black keyline
|
||||
// outside its coloured border. Together these are what stop the panel from
|
||||
// reading as a flat clip-path.
|
||||
property bool drop: true
|
||||
property real dropOffset: Theme.dropOffset
|
||||
property color dropColor: Theme.ink
|
||||
property bool keyline: true
|
||||
property real keylineWidth: Theme.keylineWidth
|
||||
|
||||
readonly property real leanInset:
|
||||
(vertical ? width : height) * Math.abs(lean)
|
||||
|
||||
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
|
||||
@@ -52,14 +71,57 @@ Item {
|
||||
|
||||
readonly property real contentPad: padding + leanInset
|
||||
|
||||
// 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
|
||||
|
||||
PathPolyline {
|
||||
path: Geom.chamfer(root.polygon, root.chop, root.chopCorners)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
PathPolyline {
|
||||
path: Geom.chamfer(root.polygon, root.chop, root.chopCorners)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -141,13 +203,25 @@ Item {
|
||||
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;
|
||||
const w = root.slashWidth;
|
||||
return root.leanLeft
|
||||
? Geom.closed([Qt.point(0, 0), Qt.point(w, 0),
|
||||
Qt.point(off + w, root.height), Qt.point(off, root.height)])
|
||||
: Geom.closed([Qt.point(off, 0), Qt.point(off + w, 0),
|
||||
Qt.point(w, root.height), Qt.point(0, root.height)]);
|
||||
? 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)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,9 +231,9 @@ Item {
|
||||
Item {
|
||||
id: contentHolder
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: root.contentPad
|
||||
anchors.rightMargin: root.contentPad
|
||||
anchors.topMargin: root.padding
|
||||
anchors.bottomMargin: root.padding
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,25 @@ PopupWindow {
|
||||
|
||||
signal dismissedFully()
|
||||
|
||||
// Panels hanging off the vertical rail open sideways, so the entry slide has
|
||||
// to follow whichever axis the popout actually travels along, and start on
|
||||
// the side nearest its trigger.
|
||||
readonly property bool horizontal:
|
||||
fromEdge === Edges.Left || fromEdge === Edges.Right
|
||||
readonly property string slideProperty: horizontal ? "x" : "y"
|
||||
readonly property real slideOrigin: bleed + (fromEdge === Edges.Left
|
||||
|| fromEdge === Edges.Top ? 14 : -14)
|
||||
|
||||
anchor.item: anchorItem
|
||||
anchor.edges: fromEdge
|
||||
anchor.gravity: fromEdge
|
||||
anchor.adjustment: PopupAdjustment.SlideX
|
||||
anchor.adjustment: horizontal
|
||||
? PopupAdjustment.SlideY
|
||||
: PopupAdjustment.SlideX
|
||||
anchor.margins.top: Theme.popoutGap
|
||||
anchor.margins.bottom: Theme.popoutGap
|
||||
anchor.margins.left: Theme.popoutGap
|
||||
anchor.margins.right: Theme.popoutGap
|
||||
|
||||
implicitWidth: contentWidth + bleed * 2
|
||||
implicitHeight: contentHeight + bleed * 2
|
||||
@@ -83,7 +96,14 @@ PopupWindow {
|
||||
height: root.contentHeight
|
||||
|
||||
opacity: 0
|
||||
transformOrigin: Item.Top
|
||||
transformOrigin: {
|
||||
switch (root.fromEdge) {
|
||||
case Edges.Left: return Item.Right;
|
||||
case Edges.Right: return Item.Left;
|
||||
case Edges.Top: return Item.Bottom;
|
||||
default: return Item.Top;
|
||||
}
|
||||
}
|
||||
scale: 0.94
|
||||
|
||||
// The handler has to live on the content item, not the window: a
|
||||
@@ -105,8 +125,8 @@ PopupWindow {
|
||||
to: 1.0; duration: Theme.durSlow; easing.type: Easing.OutBack
|
||||
}
|
||||
NumberAnimation {
|
||||
target: contentHolder; property: "y"
|
||||
from: root.bleed - 14; to: root.bleed
|
||||
target: contentHolder; property: root.slideProperty
|
||||
from: root.slideOrigin; to: root.bleed
|
||||
duration: Theme.durSlow; easing.type: Easing.OutBack
|
||||
}
|
||||
}
|
||||
@@ -122,12 +142,14 @@ PopupWindow {
|
||||
to: 0.96; duration: Theme.durFast; easing.type: Easing.InQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
target: contentHolder; property: "y"
|
||||
to: root.bleed - 8; duration: Theme.durFast; easing.type: Easing.InQuad
|
||||
target: contentHolder; property: root.slideProperty
|
||||
to: root.bleed + (root.slideOrigin - root.bleed) * 0.6
|
||||
duration: Theme.durFast; easing.type: Easing.InQuad
|
||||
}
|
||||
onFinished: {
|
||||
root.shown = false;
|
||||
root.pinned = false;
|
||||
contentHolder.x = root.bleed;
|
||||
contentHolder.y = root.bleed;
|
||||
root.dismissedFully();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user