1 // Written in the D programming language.
2
3 /**
4 This module contains progress bar controls implementation.
5
6 ProgressBarWidget - progeress bar control
7
8
9 Synopsis:
10
11 ----
12 import dlangui.widgets.progressbar;
13
14 auto pb = new ProgressBarWidget();
15 // set progress
16 pb.progress = 300; // 0 .. 1000
17 // set animation interval
18 pb.animationInterval = 50; // 50 milliseconds
19
20 // for indeterminate state: set progress to PROGRESS_INDETERMINATE (-1)
21 pb.progress = PROGRESS_INDETERMINATE;
22
23 ----
24
25 Copyright: Vadim Lopatin, 2016
26 License: Boost License 1.0
27 Authors: Vadim Lopatin, coolreader.org@gmail.com
28 */
29 module dlangui.widgets.progressbar;
30
31 import dlangui.widgets.widget;
32
33 enum PROGRESS_INDETERMINATE = -1;
34 enum PROGRESS_HIDDEN = -2;
35 enum PROGRESS_ANIMATION_OFF = 0;
36 enum PROGRESS_MAX = 1000;
37
38 /// Base for different progress bar controls
39 class AbstractProgressBar : Widget {
40 this(string ID = null, int progress = PROGRESS_INDETERMINATE) {
41 super(ID);
42 _progress = progress;
43 }
44
45 protected int _progress = PROGRESS_INDETERMINATE;
46
47 /// Set current progress value, 0 .. 1000; -1 == indeterminate, -2 == hidden
48 @property AbstractProgressBar progress(int progress) {
49 if (progress < -2)
50 progress = -2;
51 if (progress > 1000)
52 progress = 1000;
53 if (_progress != progress) {
54 _progress = progress;
55 invalidate();
56 }
57 requestLayout();
58 return this;
59 }
60 /// Get current progress value, 0 .. 1000; -1 == indeterminate
61 @property int progress() {
62 return _progress;
63 }
64 /// returns true if progress bar is in indeterminate state
65 @property bool indeterminate() { return _progress == PROGRESS_INDETERMINATE; }
66
67 protected int _animationInterval = 0; // no animation by default
68 /// get animation interval in milliseconds, if 0 - no animation
69 @property int animationInterval() { return _animationInterval; }
70 /// set animation interval in milliseconds, if 0 - no animation
71 @property AbstractProgressBar animationInterval(int animationIntervalMillis) {
72 if (animationIntervalMillis < 0)
73 animationIntervalMillis = 0;
74 if (animationIntervalMillis > 5000)
75 animationIntervalMillis = 5000;
76 if (_animationInterval != animationIntervalMillis) {
77 _animationInterval = animationIntervalMillis;
78 if (!animationIntervalMillis)
79 stopAnimation();
80 else
81 scheduleAnimation();
82 }
83 return this;
84 }
85
86 protected ulong _animationTimerId;
87 protected void scheduleAnimation() {
88 if (!visible || !_animationInterval) {
89 if (_animationTimerId)
90 stopAnimation();
91 return;
92 }
93 stopAnimation();
94 _animationTimerId = setTimer(_animationInterval);
95 invalidate();
96 }
97
98 protected void stopAnimation() {
99 if (_animationTimerId) {
100 cancelTimer(_animationTimerId);
101 _animationTimerId = 0;
102 }
103 _lastAnimationTs = 0;
104 }
105
106 protected int _animationSpeedPixelsPerSecond = 20;
107 protected long _animationPhase;
108 protected long _lastAnimationTs;
109 /// called on animation timer
110 protected void onAnimationTimer(long millisElapsed) {
111 _animationPhase += millisElapsed;
112 invalidate();
113 }
114
115 /// handle timer; return true to repeat timer event after next interval, false cancel timer
116 override bool onTimer(ulong id) {
117 if (id == _animationTimerId) {
118 if (!visible || _progress == PROGRESS_HIDDEN) {
119 stopAnimation();
120 return false;
121 }
122 long elapsed = 0;
123 long ts = currentTimeMillis;
124 if (_lastAnimationTs) {
125 elapsed = ts - _lastAnimationTs;
126 if (elapsed < 0)
127 elapsed = 0;
128 else if (elapsed > 5000)
129 elapsed = 5000;
130 }
131 _lastAnimationTs = ts;
132 onAnimationTimer(elapsed);
133 return _animationInterval != 0;
134 }
135 // return true to repeat after the same interval, false to stop timer
136 return super.onTimer(id);
137 }
138 }
139
140 /// Progress bar widget
141 class ProgressBarWidget : AbstractProgressBar {
142 this(string ID = null, int progress = PROGRESS_INDETERMINATE) {
143 super(ID, progress);
144 styleId = STYLE_PROGRESS_BAR;
145 }
146
147 /**
148 Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
149
150 */
151 override void measure(int parentWidth, int parentHeight) {
152 int h = 0;
153 int w = 0;
154 DrawableRef gaugeDrawable = style.customDrawable("progress_bar_gauge");
155 DrawableRef indeterminateDrawable = style.customDrawable("progress_bar_indeterminate");
156 if (!gaugeDrawable.isNull) {
157 if (h < gaugeDrawable.height)
158 h = gaugeDrawable.height;
159 }
160 if (!indeterminateDrawable.isNull) {
161 if (h < indeterminateDrawable.height)
162 h = indeterminateDrawable.height;
163 }
164 measuredContent(parentWidth, parentHeight, w, h);
165 }
166
167
168 /// Draw widget at its position to buffer
169 override void onDraw(DrawBuf buf) {
170 if (visibility != Visibility.Visible)
171 return;
172 super.onDraw(buf);
173 Rect rc = _pos;
174 applyMargins(rc);
175 applyPadding(rc);
176 DrawableRef animDrawable;
177 if (_progress >= 0) {
178 DrawableRef gaugeDrawable = style.customDrawable("progress_bar_gauge");
179 animDrawable = style.customDrawable("progress_bar_gauge_animation");
180 int x = rc.left + _progress * rc.width / PROGRESS_MAX;
181 if (!gaugeDrawable.isNull) {
182 gaugeDrawable.drawTo(buf, Rect(rc.left, rc.top, x, rc.bottom));
183 } else {
184 }
185 } else {
186 DrawableRef indeterminateDrawable = style.customDrawable("progress_bar_indeterminate");
187 if (!indeterminateDrawable.isNull) {
188 indeterminateDrawable.drawTo(buf, rc);
189 }
190 animDrawable = style.customDrawable("progress_bar_indeterminate_animation");
191 }
192 if (!animDrawable.isNull && _animationInterval) {
193 if (!_animationTimerId)
194 scheduleAnimation();
195 int w = animDrawable.width;
196 _animationPhase %= w * 1000;
197 animDrawable.drawTo(buf, rc, 0, cast(int)(_animationPhase * _animationSpeedPixelsPerSecond / 1000), 0);
198 //Log.d("progress animation draw ", _animationPhase, " rc=", rc);
199 }
200 }
201 }