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 }