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 }