Skip to content

Commit d2bb5be

Browse files
committed
Add BatteryParticles component to org.asteroid.controls for battery meter animations
- Introduced BatteryParticles, a reusable QML component in org.asteroid.controls, for animated particles in battery meter visualizations. - Supported multiple designs (diamonds, bubbles, logos, flashes) with dynamic size and opacity properties per design. - Implemented position, scale, and opacity animations, with self-destruction after a configurable lifetime. - Added QML documentation and example, styled like LabeledSwitch and OptionCycler, for system-wide use. - Registered component in org.asteroid.controls qmldir for module-based access.
1 parent 3ca38f3 commit d2bb5be

File tree

3 files changed

+283
-0
lines changed

3 files changed

+283
-0
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
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 BatteryParticles
37+
\inqmlmodule org.asteroid.controls
38+
\brief A particle component for battery meter animations with various designs.
39+
40+
This component renders a single particle for battery meter animations, supporting
41+
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.
44+
45+
Example usage in a battery meter:
46+
\qml
47+
import QtQuick 2.9
48+
import org.asteroid.controls 1.0
49+
50+
Item {
51+
id: batteryFill
52+
width: 100
53+
height: 20
54+
55+
function createParticle() {
56+
var component = Qt.createComponent("org.asteroid.controls.BatteryParticles");
57+
if (component.status === Component.Ready) {
58+
var particle = component.createObject(batteryFill, {
59+
"x": 10,
60+
"y": 5,
61+
"targetX": 50,
62+
"maxSize": 8,
63+
"lifetime": 1200,
64+
"isCharging": true,
65+
"design": "diamonds"
66+
});
67+
}
68+
}
69+
70+
Timer {
71+
interval: 200
72+
running: true
73+
repeat: true
74+
onTriggered: createParticle()
75+
}
76+
}
77+
\endqml
78+
*/
79+
Item {
80+
id: particleRoot
81+
width: maxSize
82+
height: maxSize
83+
84+
/*!
85+
\qmlproperty real BatteryParticles::maxSize
86+
The maximum size of the particle.
87+
*/
88+
property real maxSize: 10
89+
90+
/*!
91+
\qmlproperty real BatteryParticles::targetX
92+
The target X position for the particle's movement.
93+
*/
94+
property real targetX: 0
95+
96+
/*!
97+
\qmlproperty int BatteryParticles::lifetime
98+
The duration (in milliseconds) before the particle self-destructs.
99+
*/
100+
property int lifetime: 1200
101+
102+
/*!
103+
\qmlproperty bool BatteryParticles::isCharging
104+
Whether the battery is charging, affecting particle behavior.
105+
*/
106+
property bool isCharging: false
107+
108+
/*!
109+
\qmlproperty string BatteryParticles::design
110+
The particle design: "diamonds", "bubbles", "logos", or "flashes".
111+
*/
112+
property string design: "diamonds"
113+
114+
// Define design-specific properties
115+
property var designProperties: {
116+
"diamonds": { initialSize: 0.4, maxSize: 1.0, initialOpacity: 0, maxOpacity: 0.4 },
117+
"bubbles": { initialSize: 0.4, maxSize: 1.0, initialOpacity: 0, maxOpacity: 0.4 },
118+
"logos": { initialSize: 0.5, maxSize: 1.2, initialOpacity: 0, maxOpacity: 0.6 },
119+
"flashes": { initialSize: 0.6, maxSize: 1.4, initialOpacity: 0, maxOpacity: 0.7 }
120+
}
121+
122+
// Get design properties with fallback
123+
function getDesignProp(propName) {
124+
return (designProperties[design] && designProperties[design][propName])
125+
? designProperties[design][propName]
126+
: designProperties["diamonds"][propName];
127+
}
128+
129+
// Destroy timer to handle particle cleanup
130+
Timer {
131+
id: destroyTimer
132+
interval: lifetime
133+
running: true
134+
repeat: false
135+
onTriggered: particleRoot.destroy()
136+
}
137+
138+
// Diamond design
139+
Rectangle {
140+
id: diamond
141+
width: particleRoot.width * particleSize
142+
height: particleRoot.width * particleSize
143+
color: "#FFFFFF"
144+
anchors.centerIn: parent
145+
rotation: 45
146+
opacity: particleOpacity
147+
visible: particleRoot.design === "diamonds"
148+
149+
property real particleSize: getDesignProp("initialSize")
150+
property real particleOpacity: getDesignProp("initialOpacity")
151+
}
152+
153+
// Bubble design
154+
Rectangle {
155+
id: bubble
156+
width: particleRoot.width * particleSize
157+
height: particleRoot.width * particleSize
158+
radius: width / 2
159+
color: "#FFFFFF"
160+
anchors.centerIn: parent
161+
opacity: particleOpacity
162+
visible: particleRoot.design === "bubbles"
163+
164+
property real particleSize: getDesignProp("initialSize")
165+
property real particleOpacity: getDesignProp("initialOpacity")
166+
}
167+
168+
// Logo design
169+
Icon {
170+
id: logo
171+
width: particleRoot.width * particleSize
172+
height: particleRoot.width * particleSize
173+
name: "logo-asteroidos"
174+
anchors.centerIn: parent
175+
opacity: particleOpacity
176+
visible: particleRoot.design === "logos"
177+
178+
property real particleSize: getDesignProp("initialSize")
179+
property real particleOpacity: getDesignProp("initialOpacity")
180+
}
181+
182+
// Flash design
183+
Icon {
184+
id: flash
185+
width: particleRoot.width * particleSize
186+
height: particleRoot.width * particleSize
187+
name: "ios-flash"
188+
anchors.centerIn: parent
189+
opacity: particleOpacity
190+
visible: particleRoot.design === "flashes"
191+
192+
property real particleSize: getDesignProp("initialSize")
193+
property real particleOpacity: getDesignProp("initialOpacity")
194+
}
195+
196+
ParallelAnimation {
197+
id: particleAnimation
198+
running: true
199+
200+
// Position animation
201+
NumberAnimation {
202+
target: particleRoot
203+
property: "x"
204+
to: targetX
205+
duration: lifetime
206+
easing.type: Easing.InOutSine
207+
}
208+
209+
// Size animation - dynamically determine target based on current design
210+
SequentialAnimation {
211+
NumberAnimation {
212+
target: {
213+
switch(particleRoot.design) {
214+
case "diamonds": return diamond;
215+
case "bubbles": return bubble;
216+
case "logos": return logo;
217+
case "flashes": return flash;
218+
default: return diamond;
219+
}
220+
}
221+
property: "particleSize"
222+
from: getDesignProp("initialSize")
223+
to: getDesignProp("maxSize")
224+
duration: lifetime / 2
225+
easing.type: Easing.OutQuad
226+
}
227+
NumberAnimation {
228+
target: {
229+
switch(particleRoot.design) {
230+
case "diamonds": return diamond;
231+
case "bubbles": return bubble;
232+
case "logos": return logo;
233+
case "flashes": return flash;
234+
default: return diamond;
235+
}
236+
}
237+
property: "particleSize"
238+
from: getDesignProp("maxSize")
239+
to: getDesignProp("initialSize")
240+
duration: lifetime / 2
241+
easing.type: Easing.InQuad
242+
}
243+
}
244+
245+
// Opacity animation - dynamically determine target based on current design
246+
SequentialAnimation {
247+
NumberAnimation {
248+
target: {
249+
switch(particleRoot.design) {
250+
case "diamonds": return diamond;
251+
case "bubbles": return bubble;
252+
case "logos": return logo;
253+
case "flashes": return flash;
254+
default: return diamond;
255+
}
256+
}
257+
property: "particleOpacity"
258+
from: getDesignProp("initialOpacity")
259+
to: getDesignProp("maxOpacity")
260+
duration: lifetime / 2
261+
easing.type: Easing.OutQuad
262+
}
263+
NumberAnimation {
264+
target: {
265+
switch(particleRoot.design) {
266+
case "diamonds": return diamond;
267+
case "bubbles": return bubble;
268+
case "logos": return logo;
269+
case "flashes": return flash;
270+
default: return diamond;
271+
}
272+
}
273+
property: "particleOpacity"
274+
from: getDesignProp("maxOpacity")
275+
to: getDesignProp("initialOpacity")
276+
duration: lifetime / 2
277+
easing.type: Easing.InQuad
278+
}
279+
}
280+
}
281+
}

src/controls/qmldir

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +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
78
CircularSpinner 1.0 qrc:///org/asteroid/controls/qml/CircularSpinner.qml
89
singleton Dims 1.0 qrc:///org/asteroid/controls/qml/Dims.qml
910
HandWritingKeyboard 1.0 qrc:///org/asteroid/controls/qml/HandWritingKeyboard.qml

src/controls/resources.qrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<RCC>
22
<qresource prefix="/org/asteroid/controls/">
33
<file>qml/Application.qml</file>
4+
<file>qml/BatteryParticles.qml</file>
45
<file>qml/BorderGestureArea.qml</file>
56
<file>qml/CircularSpinner.qml</file>
67
<file>qml/Dims.qml</file>

0 commit comments

Comments
 (0)