Skip to content

Commit 76d931e

Browse files
committed
Add ValueMeter.qml and rename BatteryParticles.qml to Particles.qml
- Introduced ValueMeter.qml, a reusable component in org.asteroid.controls for displaying values within a range, with properties for width, height, value bounds, current value, isIncreasing, animations, and particle design. - Generalized battery meter logic, supporting wave animation and particle effects via Particles.qml. - Added fillColor property to ValueMeter.qml, moving battery-specific color logic to callers. - Removed distracting color pulse animation from ValueMeter.qml for cleaner visuals. - Renamed BatteryParticles.qml to Particles.qml, generalizing nomenclature and changing isCharging to isIncreasing for consistency. - Updated ValueMeter.qml to reference Particles.qml, ensuring compatibility.
1 parent 7dfb05e commit 76d931e

File tree

4 files changed

+281
-21
lines changed

4 files changed

+281
-21
lines changed

src/controls/qml/BatteryParticles.qml renamed to src/controls/qml/Particles.qml

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* * Redistributions of source code must retain the above copyright
1111
* notice, this list of conditions and the following disclaimer.
1212
* * Redistributions in binary form must reproduce the above copyright
13-
* notice, this list of conditions and the following disclaimer in the
13+
notice, this list of conditions and the following disclaimer in the
1414
* documentation and/or other materials provided with the distribution.
1515
* * Neither the name of the author nor the
1616
* names of its contributors may be used to endorse or promote products
@@ -33,35 +33,35 @@ import QtGraphicalEffects 1.15
3333
import org.asteroid.controls 1.0
3434

3535
/*!
36-
\qmltype BatteryParticles
36+
\qmltype Particles
3737
\inqmlmodule org.asteroid.controls
38-
\brief A particle component for battery meter animations with various designs.
38+
\brief A particle component for meter animations with various designs.
3939
40-
This component renders a single particle for battery meter animations, supporting
40+
This component renders a single particle for meter or gauge animations, supporting
4141
different designs (diamonds, bubbles, logos, flashes). It moves to a target X position,
42-
scales, fades, and self-destructs after a specified lifetime. Used in battery meter
43-
visualizations to show charging or low-battery effects.
42+
scales, fades, and self-destructs after a specified lifetime. Used in visualizations
43+
like value meters to show increasing or decreasing states.
4444
45-
Example usage in a battery meter:
45+
Example usage in a value meter:
4646
\qml
4747
import QtQuick 2.9
4848
import org.asteroid.controls 1.0
4949
5050
Item {
51-
id: batteryFill
51+
id: meterFill
5252
width: 100
5353
height: 20
5454
5555
function createParticle() {
56-
var component = Qt.createComponent("org.asteroid.controls.BatteryParticles");
56+
var component = Qt.createComponent("qrc:///org/asteroid/controls/qml/Particles.qml");
5757
if (component.status === Component.Ready) {
58-
var particle = component.createObject(batteryFill, {
58+
var particle = component.createObject(meterFill, {
5959
"x": 10,
6060
"y": 5,
6161
"targetX": 50,
6262
"maxSize": 8,
6363
"lifetime": 1200,
64-
"isCharging": true,
64+
"isIncreasing": true,
6565
"design": "diamonds"
6666
});
6767
}
@@ -82,37 +82,37 @@ Item {
8282
height: maxSize
8383

8484
/*!
85-
\qmlproperty real BatteryParticles::maxSize
85+
\qmlproperty real Particles::maxSize
8686
The maximum size of the particle.
8787
*/
8888
property real maxSize: 10
8989

9090
/*!
91-
\qmlproperty real BatteryParticles::targetX
91+
\qmlproperty real Particles::targetX
9292
The target X position for the particle's movement.
9393
*/
9494
property real targetX: 0
9595

9696
/*!
97-
\qmlproperty int BatteryParticles::lifetime
97+
\qmlproperty int Particles::lifetime
9898
The duration (in milliseconds) before the particle self-destructs.
9999
*/
100100
property int lifetime: 1200
101101

102102
/*!
103-
\qmlproperty bool BatteryParticles::isCharging
104-
Whether the battery is charging, affecting particle behavior.
103+
\qmlproperty bool Particles::isIncreasing
104+
Whether the value is increasing, affecting particle behavior.
105105
*/
106-
property bool isCharging: false
106+
property bool isIncreasing: false
107107

108108
/*!
109-
\qmlproperty string BatteryParticles::design
109+
\qmlproperty string Particles::design
110110
The particle design: "diamonds", "bubbles", "logos", or "flashes".
111111
*/
112112
property string design: "diamonds"
113113

114114
/*!
115-
\qmlproperty rect BatteryParticles::clipBounds
115+
\qmlproperty rect Particles::clipBounds
116116
The bounding rectangle (x, y, width, height) for clipping. Particles are destroyed if they move outside this area.
117117
*/
118118
property rect clipBounds: Qt.rect(0, 0, 0, 0)

src/controls/qml/ValueMeter.qml

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/*
2+
* Copyright (C) 2025 Timo Könnecke <github.com/eLtMosen>
3+
*
4+
* All rights reserved.
5+
*
6+
* You may use this file under the terms of BSD license as follows:
7+
*
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are met:
10+
* * Redistributions of source code must retain the above copyright
11+
* notice, this list of conditions and the following disclaimer.
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
* * Neither the name of the author nor the
16+
* names of its contributors may be used to endorse or promote products
17+
* derived from this software without specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
23+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
import QtQuick 2.9
32+
import QtGraphicalEffects 1.15
33+
import org.asteroid.controls 1.0
34+
35+
/*!
36+
\qmltype ValueMeter
37+
\inqmlmodule org.asteroid.controls
38+
\brief A customizable meter component for displaying a value within a specified range.
39+
40+
This component displays a rounded rectangular meter with a fill that represents a value
41+
between a lower and upper bound. It supports animations (wave effect during active state,
42+
color pulsing at low values), colored fill based on value thresholds, and particle effects
43+
for visual feedback. Designed for system-wide use in AsteroidOS, it is ideal for battery
44+
levels, volume, or other ranged values.
45+
46+
Example usage:
47+
\qml
48+
import QtQuick 2.9
49+
import org.asteroid.controls 1.0
50+
51+
ValueMeter {
52+
width: Dims.l(28) * 1.8
53+
height: Dims.l(8)
54+
valueLowerBound: 0
55+
valueUpperBound: 100
56+
value: 75
57+
isActive: true
58+
enableAnimations: true
59+
enableColoredFill: false
60+
particleDesign: "diamonds"
61+
}
62+
\endqml
63+
*/
64+
Item {
65+
id: valueMeter
66+
67+
/*!
68+
\qmlproperty real ValueMeter::width
69+
The width of the meter.
70+
*/
71+
width: Dims.l(28) * 1.8
72+
73+
/*!
74+
\qmlproperty real ValueMeter::height
75+
The height of the meter.
76+
*/
77+
height: Dims.l(8)
78+
79+
/*!
80+
\qmlproperty real ValueMeter::valueLowerBound
81+
The lower bound of the value range.
82+
*/
83+
property real valueLowerBound: 0
84+
85+
/*!
86+
\qmlproperty real ValueMeter::valueUpperBound
87+
The upper bound of the value range.
88+
*/
89+
property real valueUpperBound: 100
90+
91+
/*!
92+
\qmlproperty real ValueMeter::value
93+
The current value to display, clamped between valueLowerBound and valueUpperBound.
94+
*/
95+
property real value: 0
96+
97+
/*!
98+
\qmlproperty bool ValueMeter::isIncreasing
99+
Whether the meter is in an increasing state (e.g., charging for battery), enabling wave animation.
100+
*/
101+
property bool isIncreasing: false
102+
103+
/*!
104+
\qmlproperty bool ValueMeter::enableAnimations
105+
Enables wave and particle animations when true.
106+
*/
107+
property bool enableAnimations: true
108+
109+
/*!
110+
\qmlproperty bool ValueMeter::enableColoredFill
111+
Enables colored fill based on value thresholds when true.
112+
*/
113+
property bool enableColoredFill: false
114+
115+
/*!
116+
\qmlproperty string ValueMeter::particleDesign
117+
The design type for particle effects. Options: "diamonds", "bubbles", "logos", "flashes".
118+
*/
119+
property string particleDesign: "diamonds"
120+
121+
/*!
122+
\qmlproperty color ValueMeter::fillColor
123+
The color of the meter's fill. Defaults to a semi-transparent white.
124+
*/
125+
property color fillColor: Qt.rgba(1, 1, 1, 0.3)
126+
127+
Rectangle {
128+
id: outline
129+
anchors.fill: parent
130+
color: Qt.rgba(1, 1, 1, 0.2)
131+
radius: height / 2
132+
}
133+
134+
Rectangle {
135+
id: fill
136+
height: parent.height
137+
width: {
138+
var range = valueUpperBound - valueLowerBound
139+
var normalizedValue = range > 0 ? (value - valueLowerBound) / range : 0
140+
var baseWidth = parent.width * Math.min(Math.max(normalizedValue, 0), 1)
141+
if (isIncreasing && enableAnimations && fill.isVisible) {
142+
var waveAmplitude = parent.width * 0.05
143+
return baseWidth + waveAmplitude * Math.sin(waveTime)
144+
}
145+
return baseWidth
146+
}
147+
color: fillColor
148+
anchors.left: parent.left
149+
opacity: 1.0
150+
clip: true
151+
152+
property real waveTime: 0
153+
property bool isVisible: valueMeter.visible && Qt.application.active
154+
155+
NumberAnimation on waveTime {
156+
id: waveAnimation
157+
running: isIncreasing && enableAnimations && fill.isVisible
158+
from: 0
159+
to: 2 * Math.PI
160+
duration: 1500
161+
loops: Animation.Infinite
162+
}
163+
164+
Item {
165+
id: particleContainer
166+
anchors.fill: parent
167+
visible: enableAnimations
168+
169+
property int particleCount: 5
170+
property int activeParticles: 0
171+
property int nextHorizontalBand: 0
172+
property int spawnInterval: 300
173+
174+
Component {
175+
id: cleanupTimerComponent
176+
Timer {
177+
id: cleanupTimer
178+
interval: 0
179+
running: true
180+
repeat: false
181+
onTriggered: {
182+
particleContainer.activeParticles--
183+
}
184+
}
185+
}
186+
187+
function createParticle() {
188+
if (!particleContainer.visible || !fill.isVisible || activeParticles >= 16) {
189+
return
190+
}
191+
var component = Qt.createComponent("qrc:///org/asteroid/controls/qml/Particles.qml")
192+
if (component.status === Component.Ready) {
193+
var speed = isIncreasing ? 60 : 20
194+
var pathLength = isIncreasing ? fill.width / 2 : fill.width
195+
var lifetime = isIncreasing ? 2500 : 8500
196+
particleContainer.spawnInterval = isIncreasing ? 200 : 750
197+
var maxSize = fill.height / 2
198+
var minSize = fill.height / 6
199+
var designType = particleDesign || "diamonds"
200+
var isLogoOrFlash = designType === "logos" || designType === "flashes"
201+
var sizeMultiplier = isLogoOrFlash ? 1.3 : 1.0
202+
var opacity = 0.6
203+
204+
var horizontalBand = particleContainer.nextHorizontalBand
205+
var startX = isIncreasing ?
206+
(horizontalBand === 0 ? Math.random() * (fill.width / 4) : (fill.width / 4) + Math.random() * (fill.width / 4)) :
207+
(horizontalBand === 0 ? fill.width / 2 + Math.random() * (fill.width / 4) : (3 * fill.width / 4) + Math.random() * (fill.width / 4))
208+
particleContainer.nextHorizontalBand = (horizontalBand + 1) % 2
209+
210+
var endX = isIncreasing ? startX + pathLength : startX - pathLength
211+
212+
var band = Math.floor(Math.random() * 3)
213+
var startY = (band * fill.height / 3) + (Math.random() * fill.height / 3)
214+
215+
var size = (minSize + Math.random() * (maxSize - minSize)) * sizeMultiplier
216+
217+
var particle = component.createObject(particleContainer, {
218+
"x": startX,
219+
"y": startY,
220+
"targetX": endX,
221+
"maxSize": size,
222+
"lifetime": lifetime,
223+
"isIncreasing": isIncreasing,
224+
"design": designType,
225+
"opacity": opacity,
226+
"clipBounds": Qt.rect(0, 0, fill.width, fill.height)
227+
})
228+
if (particle !== null) {
229+
activeParticles++
230+
var cleanupTimer = cleanupTimerComponent.createObject(particleContainer, {
231+
"interval": lifetime
232+
})
233+
}
234+
}
235+
}
236+
237+
Timer {
238+
id: particleTimer
239+
interval: particleContainer.spawnInterval
240+
running: fill.width > 0 && enableAnimations && particleContainer.visible && fill.isVisible
241+
repeat: true
242+
triggeredOnStart: true
243+
onTriggered: {
244+
particleContainer.createParticle()
245+
}
246+
}
247+
}
248+
}
249+
250+
layer.enabled: true
251+
layer.effect: OpacityMask {
252+
maskSource: Item {
253+
width: valueMeter.width
254+
height: valueMeter.height
255+
Rectangle { anchors.fill: parent; radius: outline.radius }
256+
}
257+
}
258+
}

src/controls/qmldir

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugin asteroidcontrolsplugin
44

55
Application 1.0 qrc:///org/asteroid/controls/qml/Application.qml
66
BorderGestureArea 1.0 qrc:///org/asteroid/controls/qml/BorderGestureArea.qml
7-
BatteryParticles 1.0 qrc:///org/asteroid/controls/qml/BatteryParticles.qml
7+
Particles 1.0 qrc:///org/asteroid/controls/qml/Particles.qml
88
CircularSpinner 1.0 qrc:///org/asteroid/controls/qml/CircularSpinner.qml
99
singleton Dims 1.0 qrc:///org/asteroid/controls/qml/Dims.qml
1010
HandWritingKeyboard 1.0 qrc:///org/asteroid/controls/qml/HandWritingKeyboard.qml
@@ -30,3 +30,4 @@ Switch 1.0 qrc:///org/asteroid/controls/qml/Switch.qml
3030
TextArea 1.0 qrc:///org/asteroid/controls/qml/TextArea.qml
3131
TextBase 1.0 qrc:///org/asteroid/controls/qml/TextBase.qml
3232
TextField 1.0 qrc:///org/asteroid/controls/qml/TextField.qml
33+
ValueMeter 1.0 qrc:///org/asteroid/controls/qml/ValueMeter.qml

src/controls/resources.qrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<RCC>
22
<qresource prefix="/org/asteroid/controls/">
33
<file>qml/Application.qml</file>
4-
<file>qml/BatteryParticles.qml</file>
4+
<file>qml/Particles.qml</file>
55
<file>qml/BorderGestureArea.qml</file>
66
<file>qml/CircularSpinner.qml</file>
77
<file>qml/Dims.qml</file>
@@ -28,5 +28,6 @@
2828
<file>qml/TextArea.qml</file>
2929
<file>qml/TextBase.qml</file>
3030
<file>qml/TextField.qml</file>
31+
<file>qml/ValueMeter.qml</file>
3132
</qresource>
3233
</RCC>

0 commit comments

Comments
 (0)