181 lines
4.1 KiB
QML
181 lines
4.1 KiB
QML
import QtQuick
|
|
import QtQuick.Effects
|
|
import qs.Config
|
|
|
|
Elevation {
|
|
id: root
|
|
|
|
required property int currentIndex
|
|
property bool expanded
|
|
required property int from
|
|
property color insideTextColor: DynamicColors.palette.m3onPrimary
|
|
property int itemHeight
|
|
property int listHeight: 200
|
|
property color outsideTextColor: DynamicColors.palette.m3onSurfaceVariant
|
|
readonly property var spinnerModel: root.range(root.from, root.to)
|
|
required property int to
|
|
property Item triggerItem
|
|
|
|
signal itemSelected(item: int)
|
|
|
|
function range(first, last) {
|
|
let out = [];
|
|
for (let i = first; i <= last; ++i)
|
|
out.push(i);
|
|
return out;
|
|
}
|
|
|
|
implicitHeight: root.expanded ? view.implicitHeight : 0
|
|
level: root.expanded ? 2 : 0
|
|
radius: itemHeight / 2
|
|
visible: implicitHeight > 0
|
|
z: root.expanded ? 100 : 0
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {
|
|
}
|
|
}
|
|
|
|
onExpandedChanged: {
|
|
if (!root.expanded)
|
|
root.itemSelected(view.currentIndex + 1);
|
|
}
|
|
|
|
Component {
|
|
id: spinnerDelegate
|
|
|
|
Item {
|
|
id: wrapper
|
|
|
|
readonly property color delegateTextColor: wrapper.PathView.view ? wrapper.PathView.view.delegateTextColor : "white"
|
|
required property var modelData
|
|
|
|
height: root.itemHeight
|
|
opacity: wrapper.PathView.itemOpacity
|
|
visible: wrapper.PathView.onPath
|
|
width: wrapper.PathView.view ? wrapper.PathView.view.width : 0
|
|
z: wrapper.PathView.isCurrentItem ? 100 : Math.round(wrapper.PathView.itemScale * 100)
|
|
|
|
CustomText {
|
|
anchors.centerIn: parent
|
|
color: wrapper.delegateTextColor
|
|
font.pointSize: Appearance.font.size.large
|
|
text: wrapper.modelData
|
|
}
|
|
}
|
|
}
|
|
|
|
CustomClippingRect {
|
|
anchors.fill: parent
|
|
color: DynamicColors.palette.m3surfaceContainer
|
|
radius: parent.radius
|
|
z: root.z
|
|
|
|
// Main visible spinner: normal/outside text color
|
|
PathView {
|
|
id: view
|
|
|
|
property color delegateTextColor: root.outsideTextColor
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
clip: true
|
|
currentIndex: root.currentIndex - 1
|
|
delegate: spinnerDelegate
|
|
dragMargin: width
|
|
highlightRangeMode: PathView.StrictlyEnforceRange
|
|
implicitHeight: root.listHeight
|
|
model: root.spinnerModel
|
|
pathItemCount: 7
|
|
preferredHighlightBegin: 0.5
|
|
preferredHighlightEnd: 0.5
|
|
snapMode: PathView.SnapToItem
|
|
|
|
path: PathMenu {
|
|
viewHeight: view.height
|
|
viewWidth: view.width
|
|
}
|
|
}
|
|
|
|
// The selection rectangle itself
|
|
CustomRect {
|
|
id: selectionRect
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
color: DynamicColors.palette.m3primary
|
|
height: root.itemHeight
|
|
radius: root.itemHeight / 2
|
|
width: parent.width
|
|
z: 2
|
|
}
|
|
|
|
// Hidden source: same PathView, but with the "inside selection" text color
|
|
Item {
|
|
id: selectedTextSource
|
|
|
|
anchors.fill: parent
|
|
layer.enabled: true
|
|
visible: false
|
|
|
|
PathView {
|
|
id: selectedTextView
|
|
|
|
property color delegateTextColor: root.insideTextColor
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
clip: true
|
|
currentIndex: view.currentIndex
|
|
delegate: spinnerDelegate
|
|
dragMargin: view.dragMargin
|
|
highlightRangeMode: view.highlightRangeMode
|
|
implicitHeight: root.listHeight
|
|
interactive: false
|
|
model: view.model
|
|
|
|
// Keep this PathView visually locked to the real one
|
|
offset: view.offset
|
|
pathItemCount: view.pathItemCount
|
|
preferredHighlightBegin: view.preferredHighlightBegin
|
|
preferredHighlightEnd: view.preferredHighlightEnd
|
|
snapMode: view.snapMode
|
|
|
|
path: PathMenu {
|
|
viewHeight: selectedTextView.height
|
|
viewWidth: selectedTextView.width
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mask matching the selection rectangle
|
|
Item {
|
|
id: selectionMask
|
|
|
|
anchors.fill: parent
|
|
layer.enabled: true
|
|
visible: false
|
|
|
|
CustomRect {
|
|
color: "white"
|
|
height: selectionRect.height
|
|
radius: selectionRect.radius
|
|
width: selectionRect.width
|
|
x: selectionRect.x
|
|
y: selectionRect.y
|
|
}
|
|
}
|
|
|
|
// Only show the "inside selection" text where the mask exists
|
|
MultiEffect {
|
|
anchors.fill: selectedTextSource
|
|
maskEnabled: true
|
|
maskInverted: false
|
|
maskSource: selectionMask
|
|
source: selectedTextSource
|
|
z: 3
|
|
}
|
|
}
|
|
}
|