resources popout refresh + new components & reskinned old components
This commit is contained in:
@@ -17,8 +17,8 @@ BusyIndicator {
|
||||
}
|
||||
|
||||
property int animState
|
||||
property color bgColour: DynamicColors.palette.m3secondaryContainer
|
||||
property color fgColour: DynamicColors.palette.m3primary
|
||||
property color bgColor: DynamicColors.palette.m3secondaryContainer
|
||||
property color fgColor: DynamicColors.palette.m3primary
|
||||
property real implicitSize: Appearance.font.size.normal * 3
|
||||
property real internalStrokeWidth: strokeWidth
|
||||
readonly property alias progress: manager.progress
|
||||
@@ -31,8 +31,8 @@ BusyIndicator {
|
||||
|
||||
contentItem: CircularProgress {
|
||||
anchors.fill: parent
|
||||
bgColour: root.bgColour
|
||||
fgColour: root.fgColour
|
||||
bgColor: root.bgColor
|
||||
fgColor: root.fgColor
|
||||
padding: root.padding
|
||||
rotation: manager.rotation
|
||||
startAngle: manager.startFraction * 360
|
||||
@@ -73,7 +73,6 @@ BusyIndicator {
|
||||
|
||||
CircularIndicatorManager {
|
||||
id: manager
|
||||
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
|
||||
@@ -1,66 +1,116 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import ZShell.Components
|
||||
import qs.Config
|
||||
|
||||
Shape {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property real arcRadius: (size - padding - strokeWidth) / 2
|
||||
property color bgColour: DynamicColors.palette.m3secondaryContainer
|
||||
property color fgColour: DynamicColors.palette.m3primary
|
||||
readonly property real arcRadius: (size - padding - strokeWidth * (1 + waveAmplitude * 2)) / 2
|
||||
property color bgColor: DynamicColors.palette.m3secondaryContainer
|
||||
property real clampedVal: Math.max(1 / 360, Math.min(1, isNaN(value) ? 0 : value))
|
||||
readonly property real dotAngleRad: (startAngle + sweepAngle - gapAngle * (sweepAngle < 360 ? 0 : 1)) * Math.PI / 180
|
||||
property color fgColor: DynamicColors.palette.m3primary
|
||||
readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI)
|
||||
property alias hasEndIndicator: dot.active
|
||||
property real implicitSize
|
||||
property int padding: 0
|
||||
readonly property real size: Math.min(width, height)
|
||||
property int spacing: Appearance.spacing.small
|
||||
property int startAngle: -90
|
||||
property int strokeWidth: Appearance.padding.smaller
|
||||
readonly property real vValue: value || 1 / 360
|
||||
property int strokeWidth: Appearance.padding.small
|
||||
property int sweepAngle: 360
|
||||
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
|
||||
property real value
|
||||
property alias waveAmplitude: wave.amplitudeMultiplier
|
||||
property alias waveDuration: waveProgAnim.duration
|
||||
property alias waveFrequency: wave.frequency
|
||||
property bool wavePaused
|
||||
property bool wavy: false
|
||||
|
||||
asynchronous: true
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
implicitHeight: implicitSize
|
||||
implicitWidth: implicitSize
|
||||
|
||||
ShapePath {
|
||||
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
strokeColor: root.bgColour
|
||||
strokeWidth: root.strokeWidth
|
||||
Shape {
|
||||
asynchronous: true
|
||||
opacity: Math.min(1, remainingArc.sweepAngle)
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {
|
||||
duration: Appearance.anim.durations.large
|
||||
ShapePath {
|
||||
capStyle: ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
strokeColor: root.bgColor
|
||||
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
centerX: root.size / 2
|
||||
centerY: root.size / 2
|
||||
radiusX: root.arcRadius
|
||||
radiusY: root.arcRadius
|
||||
startAngle: root.startAngle + 360 * root.vValue + root.gapAngle
|
||||
sweepAngle: Math.max(-root.gapAngle, 360 * (1 - root.vValue) - root.gapAngle * 2)
|
||||
PathAngleArc {
|
||||
id: remainingArc
|
||||
|
||||
centerX: root.size / 2
|
||||
centerY: root.size / 2
|
||||
radiusX: root.arcRadius
|
||||
radiusY: root.arcRadius
|
||||
startAngle: root.startAngle + root.clampedVal * root.sweepAngle + root.gapAngle
|
||||
sweepAngle: Math.max(1 / 360, root.sweepAngle * (1 - root.clampedVal) - root.gapAngle * (root.sweepAngle < 360 ? 1 : 2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
strokeColor: root.fgColour
|
||||
strokeWidth: root.strokeWidth
|
||||
WavyLine {
|
||||
id: wave
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {
|
||||
duration: Appearance.anim.durations.large
|
||||
amplitudeMultiplier: root.wavy ? 0.5 : 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: -lineWidth * amplitudeMultiplier
|
||||
color: root.fgColor
|
||||
frequency: 8
|
||||
fullAngle: root.sweepAngle
|
||||
lineWidth: root.strokeWidth
|
||||
pathType: WavyLine.Arc
|
||||
radius: root.arcRadius
|
||||
startAngle: root.startAngle
|
||||
value: root.clampedVal
|
||||
|
||||
Behavior on amplitudeMultiplier {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
Anim on waveProgress {
|
||||
id: waveProgAnim
|
||||
|
||||
PathAngleArc {
|
||||
centerX: root.size / 2
|
||||
centerY: root.size / 2
|
||||
radiusX: root.arcRadius
|
||||
radiusY: root.arcRadius
|
||||
startAngle: root.startAngle
|
||||
sweepAngle: 360 * root.vValue
|
||||
duration: 2000
|
||||
easing.type: Easing.Linear
|
||||
from: 0
|
||||
loops: Animation.Infinite
|
||||
paused: root.wavePaused || wave.amplitudeMultiplier === 0
|
||||
running: true
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: dot
|
||||
|
||||
x: root.size / 2 + root.arcRadius * Math.cos(root.dotAngleRad) - width / 2
|
||||
y: root.size / 2 + root.arcRadius * Math.sin(root.dotAngleRad) - height / 2
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
color: root.fgColor
|
||||
implicitHeight: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
|
||||
implicitWidth: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
|
||||
opacity: Math.min(1, remainingArc.sweepAngle)
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Templates
|
||||
import ZShell
|
||||
import ZShell.Components
|
||||
import ZShell.Internal
|
||||
import qs.Config
|
||||
|
||||
ProgressBar {
|
||||
id: root
|
||||
|
||||
enum IndeterminateAnimState {
|
||||
Running,
|
||||
Completing,
|
||||
Stopped
|
||||
}
|
||||
|
||||
property color bgColor: DynamicColors.palette.m3secondaryContainer
|
||||
property color fgColor: DynamicColors.palette.m3primary
|
||||
property int indeterminateAnimState: CustomProgressBar.Stopped
|
||||
property real waveAmplitude: 0.5
|
||||
property int waveDuration: 1000
|
||||
property int waveFrequency: 6
|
||||
property bool wavePaused
|
||||
property bool wavy
|
||||
|
||||
function toBounds(startFrac: real, endFrac: real, gapSize: real): point {
|
||||
startFrac = ZUtils.clamp(startFrac, 0, 1);
|
||||
endFrac = ZUtils.clamp(endFrac, 0, 1);
|
||||
|
||||
// Ramp down gap size
|
||||
const GAP_RAMP_DOWN_THRESHOLD = 0.01;
|
||||
gapSize += height / 2;
|
||||
const startGapSize = (gapSize * ZUtils.clamp(startFrac, 0, GAP_RAMP_DOWN_THRESHOLD) / GAP_RAMP_DOWN_THRESHOLD);
|
||||
const endGapSize = (gapSize * (1 - ZUtils.clamp(endFrac, 1 - GAP_RAMP_DOWN_THRESHOLD, 1)) / GAP_RAMP_DOWN_THRESHOLD);
|
||||
const start = width * startFrac + startGapSize;
|
||||
const end = width * endFrac - endGapSize;
|
||||
|
||||
return start >= end ? Qt.point(0, 0) : Qt.point(start, end);
|
||||
}
|
||||
|
||||
function updateIAnimState(): void {
|
||||
if (indeterminate) {
|
||||
manager.completeEndProgress = 0;
|
||||
indeterminateAnimState = CustomProgressBar.Running;
|
||||
} else if (indeterminateAnimState === CustomProgressBar.Running) {
|
||||
indeterminateAnimState = CustomProgressBar.Completing;
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: 4
|
||||
implicitWidth: 200
|
||||
|
||||
contentItem: Loader {
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
sourceComponent: root.indeterminate || root.indeterminateAnimState !== CustomProgressBar.Stopped ? indeterminateComp : determinateComp
|
||||
}
|
||||
|
||||
Component.onCompleted: updateIAnimState()
|
||||
onIndeterminateChanged: updateIAnimState()
|
||||
|
||||
LinearIndicatorManager {
|
||||
id: manager
|
||||
|
||||
gap: Appearance.spacing.extraSmall
|
||||
|
||||
Anim on completeEndProgress {
|
||||
duration: manager.completeEndDuration
|
||||
running: root.indeterminateAnimState === CustomProgressBar.Completing
|
||||
to: 1
|
||||
|
||||
onFinished: {
|
||||
if (root.indeterminateAnimState === CustomProgressBar.Completing)
|
||||
root.indeterminateAnimState = CustomProgressBar.Stopped;
|
||||
}
|
||||
}
|
||||
Anim on progress {
|
||||
duration: manager.duration
|
||||
easing.type: Easing.Linear
|
||||
from: 0
|
||||
loops: Animation.Infinite
|
||||
running: root.indeterminateAnimState !== CustomProgressBar.Stopped
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: determinateComp
|
||||
|
||||
Item {
|
||||
Line {
|
||||
id: remaining
|
||||
|
||||
anchors.right: parent.right
|
||||
implicitWidth: parent.width - wave.implicitWidth - Appearance.spacing.extraSmall
|
||||
}
|
||||
|
||||
Line {
|
||||
property real implicitSize
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: (parent.height - implicitHeight) / 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.fgColor
|
||||
implicitHeight: implicitSize
|
||||
implicitWidth: implicitSize
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Behavior on implicitSize {
|
||||
Anim {
|
||||
type: Anim.FastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: implicitSize = Qt.binding(() => parent.width - wave.implicitWidth < parent.height ? parent.height : 4)
|
||||
}
|
||||
|
||||
Wave {
|
||||
id: wave
|
||||
|
||||
anchors.left: parent.left
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: indeterminateComp
|
||||
|
||||
Item {
|
||||
id: content
|
||||
|
||||
Line {
|
||||
bounds: {
|
||||
const i = manager.activeIndicators[0]; // qmllint disable unresolved-type
|
||||
return i ? root.toBounds(0, i.startFraction, i.gapSize / 2) : Qt.point(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Line {
|
||||
bounds: {
|
||||
const i = manager.activeIndicators[manager.activeIndicators.length - 1]; // qmllint disable unresolved-type
|
||||
return i ? root.toBounds(i.endFraction, 1, i.gapSize / 2) : Qt.point(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
model: Math.max(manager.activeIndicators.length, 1) - 1 // qmllint disable unresolved-type
|
||||
|
||||
delegate: Line {
|
||||
readonly property LinearIndicatorSegment cur: manager.activeIndicators[index] // qmllint disable unresolved-type
|
||||
required property int index
|
||||
readonly property LinearIndicatorSegment next: manager.activeIndicators[index + 1 % manager.activeIndicators.length] // qmllint disable unresolved-type
|
||||
|
||||
bounds: root.toBounds(cur.endFraction, next.startFraction, cur.gapSize / 2)
|
||||
}
|
||||
|
||||
onObjectAdded: (_, obj) => content.data.push(obj)
|
||||
onObjectRemoved: (_, obj) => {
|
||||
const idx = content.data.indexOf(obj);
|
||||
if (idx !== -1)
|
||||
content.data.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
model: manager.activeIndicators // qmllint disable unresolved-type
|
||||
|
||||
delegate: Wave {
|
||||
readonly property point bounds: root.toBounds(modelData.startFraction, modelData.endFraction, modelData.gapSize / 2)
|
||||
required property LinearIndicatorSegment modelData
|
||||
|
||||
color: root.fgColor
|
||||
implicitWidth: bounds.y - bounds.x
|
||||
x: bounds.x
|
||||
}
|
||||
|
||||
onObjectAdded: (_, obj) => content.data.push(obj)
|
||||
onObjectRemoved: (_, obj) => {
|
||||
const idx = content.data.indexOf(obj);
|
||||
if (idx !== -1)
|
||||
content.data.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Line: CustomRect {
|
||||
property point bounds
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.bgColor
|
||||
implicitHeight: parent.height
|
||||
implicitWidth: bounds.y - bounds.x
|
||||
radius: Appearance.rounding.full
|
||||
x: bounds.x
|
||||
}
|
||||
component Wave: WavyLine {
|
||||
id: wave
|
||||
|
||||
amplitudeMultiplier: root.wavy ? root.waveAmplitude : 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.fgColor
|
||||
frequency: root.waveFrequency
|
||||
fullLength: parent.width
|
||||
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
|
||||
lineWidth: parent.height
|
||||
startX: x
|
||||
|
||||
Behavior on amplitudeMultiplier {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
Anim on waveProgress {
|
||||
duration: root.waveDuration
|
||||
easing.type: Easing.Linear
|
||||
from: 0
|
||||
loops: Animation.Infinite
|
||||
paused: wave.amplitudeMultiplier === 0 || root.wavePaused
|
||||
running: true
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user