hefty: bar: make popups work for vertical

This commit is contained in:
end-4
2026-03-05 16:20:06 +01:00
parent 7a74897b47
commit 6cd2d31c99
3 changed files with 202 additions and 47 deletions
@@ -28,12 +28,9 @@ Item {
Side { Side {
id: centerLeftSide id: centerLeftSide
anchors.right: !root.vertical ? centerSide.left : parent.right anchors.right: !root.vertical ? centerSide.left : parent.right
anchors.bottom: root.vertical ? parent.bottom : undefined anchors.bottom: !root.vertical ? parent.bottom : centerSide.top
HBarUserFallbackComponentRepeater { HBarUserFallbackComponentRepeater {
componentNames: [ componentNames: [...root.centerLeftWidgets, ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []),]
...root.centerLeftWidgets,
...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []),
]
} }
} }
@@ -42,23 +39,16 @@ Item {
anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined
anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined
HBarUserFallbackComponentRepeater { HBarUserFallbackComponentRepeater {
componentNames: [ componentNames: [...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), ...root.centerWidgets, ...(root.centerRightWidgets.length > 0 ? [invisibleItem] : []),]
...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []),
...root.centerWidgets,
...(root.centerRightWidgets.length > 0 ? [invisibleItem] : []),
]
} }
} }
Side { Side {
id: centerRightSide id: centerRightSide
anchors.left: !root.vertical ? centerSide.right : parent.left anchors.left: !root.vertical ? centerSide.right : parent.left
anchors.top: root.vertical ? parent.top : undefined anchors.top: root.vertical ? parent.top : centerSide.bottom
HBarUserFallbackComponentRepeater { HBarUserFallbackComponentRepeater {
componentNames: [ componentNames: [...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), ...root.centerRightWidgets,]
...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []),
...root.centerRightWidgets,
]
} }
} }
@@ -7,10 +7,13 @@ import qs.modules.common.widgets as W
import qs.modules.common.widgets.shapes as Shapes import qs.modules.common.widgets.shapes as Shapes
import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes
// TODO: generalize this shi for vertical // Notes
// vertical + atBottom = right side
// start radius = top or left, end radius = bottom or right
Shapes.ShapeCanvas { Shapes.ShapeCanvas {
id: bgShape id: bgShape
// Stuff fed from outside
required property bool vertical required property bool vertical
required property bool atBottom required property bool atBottom
required property bool showPopup required property bool showPopup
@@ -23,45 +26,98 @@ Shapes.ShapeCanvas {
property real popupHeight: popupContentHeight + popupPadding * 2 property real popupHeight: popupContentHeight + popupPadding * 2
required property real startRadius required property real startRadius
required property real endRadius required property real endRadius
property real baseMargin: (parent.height - containerShape.height) / 2 // TODO vertical property real baseMargin: {
readonly property real popupContentOffsetBase: -baseMargin + popupPadding if (!vertical)
readonly property real popupContentOffsetY: spacing + popupContentOffsetBase + (atBottom ? -(popupHeight + backgroundHeight + spacing * 2) : 0) return (parent.height - containerShape.height) / 2;
readonly property real popupContentOffsetX: popupXOffset + popupContentOffsetBase else
return (parent.width - containerShape.width) / 2;
}
property alias containerShape: containerShape property alias containerShape: containerShape
property alias popupShape: popupShape property alias popupShape: popupShape
// Popup constraints
// mapToGlobal is not reactive so we gotta hook manually // mapToGlobal is not reactive so we gotta hook manually
property real xInGlobal property real xInGlobal
property real yInGlobal
function updateXInGlobal() { function updateXInGlobal() {
xInGlobal = mapToGlobal(0, 0).x + xOffset; xInGlobal = mapToGlobal(0, 0).x + xOffset;
} }
Component.onCompleted: updateXInGlobal() function updateYInGlobal() {
onXChanged: updateXInGlobal() yInGlobal = mapToGlobal(0, 0).y + yOffset;
}
function updatePosInGlobal() {
updateXInGlobal()
updateYInGlobal()
}
Component.onCompleted: updatePosInGlobal()
onXChanged: updatePosInGlobal()
onYChanged: updatePosInGlobal()
readonly property real minPopupXOffset: -xInGlobal + baseMargin readonly property real minPopupXOffset: -xInGlobal + baseMargin
readonly property real minPopupYOffset: -yInGlobal + baseMargin
readonly property real maxPopupXOffset: { readonly property real maxPopupXOffset: {
const maxPopupX = QsWindow.window.screen.width - popupWidth - baseMargin; const maxPopupX = QsWindow.window.screen.width - popupWidth - baseMargin;
const maxOffset = maxPopupX - xInGlobal; const maxOffset = maxPopupX - xInGlobal;
return maxOffset; return maxOffset;
} }
readonly property real popupXOffset: Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset) readonly property real maxPopupYOffset: {
const maxPopupY = QsWindow.window.screen.height - popupHeight - baseMargin;
const maxOffset = maxPopupY - yInGlobal;
return maxOffset;
}
readonly property real popupXOffset: {
if (!vertical) return Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset);
else return atBottom ? -(popupShape.width + spacing) : (containerShape.width + spacing);
}
readonly property real popupYOffset: {
if (!vertical) return atBottom ? -(popupShape.height + spacing) : (containerShape.height + spacing);
else return Math.min(Math.max(-(popupHeight - containerShape.height) / 2, minPopupYOffset), maxPopupYOffset)
}
// Positioning
readonly property real popupContentOffsetBase: -baseMargin + popupPadding
readonly property real paddedContainerHeight: containerShape.height + baseMargin * 2
readonly property real paddedContainerWidth: containerShape.width + baseMargin * 2
readonly property real popupContentOffsetY: {
if (!vertical) return paddedContainerHeight + spacing + popupContentOffsetBase + (atBottom ? -(popupHeight + backgroundHeight + spacing * 2) : 0)
else return popupYOffset + popupContentOffsetBase;
}
readonly property real popupContentOffsetX: {
if (!vertical) return popupXOffset + popupContentOffsetBase;
else return paddedContainerWidth + spacing + popupContentOffsetBase + (atBottom ? -(popupWidth + backgroundWidth + spacing * 2) : 0);
}
anchors { anchors {
left: parent.left left: parent.left
leftMargin: -xOffset leftMargin: {
if (!vertical) return -xOffset;
if (!bgShape.atBottom || !bgShape.showPopup) return baseMargin;
return baseMargin - popupShape.width - bgShape.spacing;
}
top: parent.top top: parent.top
topMargin: { topMargin: {
if (!bgShape.atBottom || !bgShape.showPopup) if (vertical) return -yOffset;
return baseMargin; if (!bgShape.atBottom || !bgShape.showPopup) return baseMargin;
else return baseMargin - popupShape.height - bgShape.spacing;
return baseMargin - popupShape.height - bgShape.spacing;
} }
} }
width: bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth width: {
height: bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height if (!vertical) return bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth;
else return bgShape.showPopup ? (containerShape.width + popupShape.width + bgShape.spacing) : containerShape.width;
}
height: {
if (!vertical) return bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height;
else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight) : backgroundHeight;
}
color: bgShape.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1 color: bgShape.showPopup || progress < 1 ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1
xOffset: showPopup ? -popupXOffset : 0 xOffset: {
yOffset: bgShape.atBottom ? (height - containerShape.height) : 0 if (!vertical) return showPopup ? -popupXOffset : 0;
else return bgShape.atBottom ? (width - containerShape.width) : 0;
}
yOffset: {
if (!vertical) return bgShape.atBottom ? (height - containerShape.height) : 0;
else return showPopup ? -popupYOffset : 0;
}
animation: Anim {} animation: Anim {}
Behavior on width { Behavior on width {
@@ -71,10 +127,16 @@ Shapes.ShapeCanvas {
Anim {} Anim {}
} }
Behavior on anchors.topMargin { Behavior on anchors.topMargin {
Anim {} animation: !bgShape.vertical ? animComp.createObject(this) : undefined
}
Behavior on anchors.leftMargin {
animation: bgShape.vertical ? animComp.createObject(this) : undefined
} }
Behavior on xOffset { Behavior on xOffset {
Anim {} animation: !bgShape.vertical ? animComp.createObject(this) : undefined
}
Behavior on yOffset {
animation: bgShape.vertical ? animComp.createObject(this) : undefined
} }
polygonIsNormalized: false polygonIsNormalized: false
@@ -85,6 +147,7 @@ Shapes.ShapeCanvas {
height: bgShape.backgroundHeight height: bgShape.backgroundHeight
startRadius: bgShape.startRadius startRadius: bgShape.startRadius
endRadius: bgShape.endRadius endRadius: bgShape.endRadius
vertical: bgShape.vertical
} }
W.RectangularContainerShape { W.RectangularContainerShape {
id: popupShape id: popupShape
@@ -92,18 +155,73 @@ Shapes.ShapeCanvas {
height: bgShape.popupHeight height: bgShape.popupHeight
radius: C.Appearance.rounding.large radius: C.Appearance.rounding.large
xOffset: bgShape.popupXOffset xOffset: bgShape.popupXOffset
yOffset: bgShape.atBottom ? -(popupShape.height + bgShape.spacing) : (containerShape.height + bgShape.spacing) yOffset: bgShape.popupYOffset
} }
roundedPolygon: { roundedPolygon: {
if (!bgShape.showPopup) var points = [];
return containerShape.getFullShape(); if (!bgShape.showPopup) return containerShape.getFullShape();
// return popupShape.getFullShape(); // debug if (!bgShape.vertical) {
const points = [...(bgShape.atBottom ? containerShape.getFirstBottomPoints() : [...popupShape.getFirstBottomPoints(), popupShape.getBottomLeftPoint(), ...popupShape.leftPoints, popupShape.getTopLeftPoint(),]), containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), // Inline comment spam to mitigate qmlls' sabotaging of the (code) layout
// ...containerShape.leftPoints, points = [
containerShape.getTopLeftPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), ...(!bgShape.atBottom ? containerShape.topPoints : [popupShape.getBottomLeftPoint(), ...popupShape.leftPoints, popupShape.getTopLeftPoint(), ...popupShape.topPoints, popupShape.getTopRightPoint(), ...popupShape.rightPoints, popupShape.getBottomRightPoint(),]), containerShape.getTopRightPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), ...(bgShape.atBottom ? containerShape.getFirstBottomPoints() : [ //
// ...containerShape.rightPoints, ...popupShape.getFirstBottomPoints(), popupShape.getBottomLeftPoint(), //
containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), ...(bgShape.atBottom ? containerShape.getLastBottomPoints() : [popupShape.getTopRightPoint(), ...popupShape.rightPoints, popupShape.getBottomRightPoint(), ...popupShape.getLastBottomPoints(),]),]; ...popupShape.leftPoints, //
popupShape.getTopLeftPoint(), //
]), //
containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), //
containerShape.getTopLeftPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), //
...(!bgShape.atBottom ? containerShape.topPoints : [ //
popupShape.getBottomLeftPoint(), //
...popupShape.leftPoints, //
popupShape.getTopLeftPoint(), //
...popupShape.topPoints, //
popupShape.getTopRightPoint(), //
...popupShape.rightPoints, //
popupShape.getBottomRightPoint(), //
]), //
containerShape.getTopRightPoint(0, bgShape.spacing * (bgShape.atBottom ? -1 : 0), containerShape.radiusLimit), //
containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom ? 1 : 0), containerShape.radiusLimit), //
...(bgShape.atBottom ? containerShape.getLastBottomPoints() : [ //
popupShape.getTopRightPoint(), //
...popupShape.rightPoints, //
popupShape.getBottomRightPoint(), //
...popupShape.getLastBottomPoints(), //
]),
];
} else {
points = [ //
...containerShape.getFirstBottomPoints(), //
containerShape.getBottomLeftPoint(), //
...(!bgShape.atBottom ? containerShape.leftPoints : [ //
containerShape.getBottomLeftPoint(-bgShape.spacing, 0, containerShape.radiusLimit), //
popupShape.getBottomRightPoint(), //
...popupShape.bottomPoints, //
popupShape.getBottomLeftPoint(), //
...popupShape.leftPoints, //
popupShape.getTopLeftPoint(), //
...popupShape.topPoints, //
popupShape.getTopRightPoint(), //
containerShape.getTopLeftPoint(-bgShape.spacing, 0, containerShape.radiusLimit), //
]), //
containerShape.getTopLeftPoint(), //
...containerShape.topPoints, //
containerShape.getTopRightPoint(), //
...(bgShape.atBottom ? containerShape.rightPoints : [ //
containerShape.getTopRightPoint(bgShape.spacing, 0, containerShape.radiusLimit), //
popupShape.getTopLeftPoint(), //
...popupShape.topPoints, //
popupShape.getTopRightPoint(), //
...popupShape.rightPoints, //
popupShape.getBottomRightPoint(), //
...popupShape.bottomPoints, //
popupShape.getBottomLeftPoint(), //
containerShape.getBottomRightPoint(bgShape.spacing, 0, containerShape.radiusLimit), //
]), //
containerShape.getBottomRightPoint(), //
...containerShape.getLastBottomPoints(), //
];
}
return MaterialShapes.customPolygon(points); return MaterialShapes.customPolygon(points);
} }
@@ -111,4 +229,9 @@ Shapes.ShapeCanvas {
spring: 3.5 spring: 3.5
damping: 0.3 damping: 0.3
} }
Component {
id: animComp
Anim {}
}
} }
@@ -15,6 +15,11 @@ HBarWidgetContainer {
readonly property bool vertical: C.Config.options.bar.vertical readonly property bool vertical: C.Config.options.bar.vertical
readonly property bool atBottom: C.Config.options.bar.bottom readonly property bool atBottom: C.Config.options.bar.bottom
readonly property string timeFormatString: C.Config.options.time.format
readonly property bool is12h: timeFormatString.startsWith("h:")
readonly property bool hasAmPm: timeFormatString.toLowerCase().includes("ap") || timeFormatString.toLowerCase().endsWith("a")
readonly property bool capitalizedAmPm: timeFormatString.includes("AP") || timeFormatString.endsWith("A")
// Interactions // Interactions
property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems") property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems")
onShowPopupChanged: { onShowPopupChanged: {
@@ -83,14 +88,14 @@ HBarWidgetContainer {
text: S.DateTime.time text: S.DateTime.time
} }
W.StyledText { W.VisuallyCenteredStyledText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
font.pixelSize: C.Appearance.font.pixelSize.small font.pixelSize: C.Appearance.font.pixelSize.small
color: C.Appearance.colors.colOnLayer1 color: C.Appearance.colors.colOnLayer1
text: "•" text: "•"
} }
W.StyledText { W.VisuallyCenteredStyledText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
font.pixelSize: C.Appearance.font.pixelSize.small font.pixelSize: C.Appearance.font.pixelSize.small
color: C.Appearance.colors.colOnLayer1 color: C.Appearance.colors.colOnLayer1
@@ -104,15 +109,52 @@ HBarWidgetContainer {
W.FadeLoader { W.FadeLoader {
id: verticalContent id: verticalContent
anchors.fill: parent anchors.fill: parent
shown: contentRoot.vertical
sourceComponent: Item {
anchors.fill: parent
implicitWidth: contentLayoutVertical.implicitWidth
implicitHeight: contentLayoutVertical.implicitHeight
ColumnLayout {
id: contentLayoutVertical
anchors.fill: parent
spacing: -6
W.StyledText {
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: {
var hrs = S.DateTime.clock.hours;
if (root.is12h) hrs %= 12;
return hrs.toString().padStart(2, '0')
}
}
W.StyledText {
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.large
color: C.Appearance.colors.colOnLayer1
text: S.DateTime.clock.minutes.toString().padStart(2, '0')
}
W.StyledText {
visible: root.hasAmPm
Layout.alignment: Qt.AlignHCenter
font.pixelSize: C.Appearance.font.pixelSize.smaller
color: C.Appearance.colors.colOnLayer1
text: Qt.locale().toString(S.DateTime.clock.date, root.capitalizedAmPm ? "AP" : "ap")
}
}
}
} }
// Popup content // Popup content
W.ChoreographerGrid { W.ChoreographerGrid {
id: popupContent id: popupContent
anchors { anchors {
top: horizontalContent.bottom top: root.vertical ? verticalContent.top : horizontalContent.top
topMargin: bgShape.popupContentOffsetY topMargin: bgShape.popupContentOffsetY
left: horizontalContent.left left: root.vertical ? verticalContent.left : horizontalContent.left
leftMargin: bgShape.popupContentOffsetX leftMargin: bgShape.popupContentOffsetX
} }