1 module pixelperfectengine.concrete.elements.scrollbar;
2
3 public import pixelperfectengine.concrete.elements.base;
4 import std.math : isNaN, nearbyint;
5 import pixelperfectengine.system.timer;
6
7 abstract class ScrollBar : WindowElement{
8 protected static enum PLUS_PRESSED = 1<<9;
9 protected static enum MINUS_PRESSED = 1<<10;
10 protected static enum SCROLLMATIC = 1<<11;
11 protected int _value, _maxValue, _barLength;
12 public int scrollSpeed = 1; ///Sets the scrollspeed for the given instance, can be useful for large number of items.
13 //protected double largeVal; ///Set to double.nan if value is less than travellength, or the ratio between
14 protected double valRatio; ///Ratio between the travel length and the maximum value
15 protected double barLength0;
16 public void delegate(Event ev) onScrolling; ///Called shen the scrollbar's value is changed
17
18 /**
19 * Returns the slider position.
20 */
21 public @property int value() @nogc @safe pure nothrow const {
22 return _value;
23 }
24 /**
25 * Sets the slider position, and redraws the slider.
26 * Returns the new slider position.
27 */
28 public @property int value(int val) {
29 if (val < 0) _value = 0;
30 else if (val < _maxValue) _value = val;
31 else _value = _maxValue;
32 draw();
33 if (onScrolling !is null)
34 onScrolling(new Event(this, EventType.MouseScroll, SourceType.WindowElement));
35 return _value;
36 }
37 /**
38 * Returns the maximum value of the slider.
39 */
40 public @property int maxValue() @nogc @safe pure nothrow const {
41 return _maxValue;
42 }
43 /**
44 * Sets the new maximum value and bar lenght of the slider.
45 * Position is kept or lowered if maximum is reached.
46 */
47 public @property int maxValue(int val) {
48 assert(val >= 0, "Value must be positive!");
49 const int iconSize = position.width < position.height ? position.width : position.height;
50 const int length = position.width > position.height ? position.width : position.height;
51 _maxValue = val;
52 if (_value > _maxValue) _value = _maxValue;
53 barLength0 = (length - iconSize * 2) / cast(double)(val + 1);
54 _barLength = barLength0 < 1.0 ? 1 : cast(int)nearbyint(barLength0);
55 //largeVal = barLength0 < 1.0 ? 1.0 / barLength0 : double.nan;
56 valRatio = 1.0 / barLength0;
57 return _maxValue;
58 }
59 /**
60 * Returns the length of the scrollbar in pixels.
61 *
62 * Automatically calculated every time maxValue is changed.
63 */
64 public @property int barLength() @nogc @safe pure nothrow const {
65 return _barLength;
66 }
67 protected void timerEvent(Duration jitter) nothrow {
68 try {
69 if (flags & PLUS_PRESSED) {
70 value = value + 1;
71 draw;
72 flags |= SCROLLMATIC;
73 registerTimer();
74 } else if (flags & MINUS_PRESSED) {
75 value = value - 1;
76 draw;
77 flags |= SCROLLMATIC;
78 registerTimer();
79 }
80 } catch (Exception e) {
81
82 }
83 }
84 protected void registerTimer() nothrow {
85 timer.register(&timerEvent, msecs(flags & SCROLLMATIC ? 50 : 1000));
86 }
87 }
88 /**
89 * Vertical scroll bar.
90 */
91 public class VertScrollBar : ScrollBar {
92 //public int[] brush;
93
94 //private int value, maxValue, barLength;
95
96 public this(int maxValue, string source, Box position){
97 this.position = position;
98 this.source = source;
99 this.maxValue = maxValue;
100 }
101 public override void draw(){
102 if (parent is null) return;
103 StyleSheet ss = getStyleSheet();
104 //draw background
105 parent.drawFilledBox(position, ss.getColor("SliderBackground"));
106 //draw slider
107 //const int travelLength = position.height - (position.width * 2) - _barLength;
108 Box slider;
109 //const int value0 = valRatio < 1.0 ? value : cast(int)(value / valRatio);
110 slider.left = position.left;
111 slider.right = position.right;
112 slider.top = position.top + position.width + cast(int)nearbyint(barLength0 * _value);
113 slider.bottom = slider.top + _barLength;
114 parent.drawFilledBox(slider, ss.getColor("SliderColor"));
115 //draw buttons
116 parent.bitBLT(position.cornerUL, flags & MINUS_PRESSED ? ss.getImage("upArrowB") : ss.getImage("upArrowA"));
117 Point lower = position.cornerLL;
118 lower.y -= position.width;
119 parent.bitBLT(lower, flags & PLUS_PRESSED ? ss.getImage("downArrowB") : ss.getImage("downArrowA"));
120 if (state == ElementState.Disabled) {
121 parent.bitBLTPattern(position, ss.getImage("ElementDisabledPtrn"));
122 }
123 /+if (isFocused) {
124 parent.drawBoxPattern(position, ss.pattern["blackDottedLine"]);
125 }+/
126 if (onDraw !is null) {
127 onDraw();
128 }
129 }
130 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) {
131 if (state != ElementState.Enabled) return;
132 mce.x -= position.left;
133 mce.y -= position.top;
134 if (mce.button == MouseButton.Left) {
135 if (mce.y < position.width) {
136 if (!(flags & MINUS_PRESSED) && mce.state == ButtonState.Pressed) {
137 value = _value - 1;
138 flags |= MINUS_PRESSED;
139 registerTimer();
140 } else if (flags & MINUS_PRESSED && mce.state == ButtonState.Released) {
141 flags &= ~(MINUS_PRESSED | SCROLLMATIC);
142 }
143 } else if (mce.y >= position.height - position.width) {
144 if (!(flags & PLUS_PRESSED) && mce.state == ButtonState.Pressed) {
145 value = _value + 1;
146 flags |= PLUS_PRESSED;
147 registerTimer();
148 } else if (flags & PLUS_PRESSED && mce.state == ButtonState.Released) {
149 flags &= ~(PLUS_PRESSED | SCROLLMATIC);
150 }
151 } else {
152 import std.math : nearbyint;
153 const double newVal = mce.y - position.width - (_barLength / 2.0);
154 if (newVal >= 0) {
155 //const int travelLength = position.height - (position.width * 2) - _barLength;
156 //const double valRatio = isNaN(largeVal) ? 1.0 : largeVal;
157 //value = cast(int)nearbyint((travelLength / newVal) * valRatio);
158 value = cast(int)nearbyint((newVal) * valRatio);
159 }
160 }
161 }
162 super.passMCE(mec, mce);
163 }
164 public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) {
165 if (state != ElementState.Enabled) return;
166 if (mme.buttonState == MouseButtonFlags.Left && mme.y > position.height && mme.y < position.width - position.height) {
167 import std.math : nearbyint;
168 const double newVal = mme.y - position.width - (_barLength / 2.0);
169 if (newVal >= 0)
170 value = cast(int)nearbyint((newVal) * valRatio);
171 //value = _value = mme.relY;
172 //draw();
173 }
174 super.passMME(mec, mme);
175 }
176 public override void passMWE(MouseEventCommons mec, MouseWheelEvent mwe) {
177 if (state != ElementState.Enabled) return;
178 value = _value - mwe.y * scrollSpeed;
179 super.passMWE(mec, mwe);
180 }
181
182 }
183 /**
184 * Horizontal scrollbar.
185 */
186 public class HorizScrollBar : ScrollBar {
187 public this(int maxValue, string source, Box position){
188 this.position = position;
189 this.source = source;
190 this.maxValue = maxValue;
191 }
192 public override void draw(){
193 if (parent is null) return;
194 StyleSheet ss = getStyleSheet();
195 //draw background
196 parent.drawFilledBox(position, ss.getColor("SliderBackground"));
197 //draw slider
198 //const int travelLength = position.width - position.height * 2;
199 const int value0 = valRatio < 1.0 ? value : cast(int)(value / valRatio);
200 Box slider;
201 slider.top = position.top;
202 slider.bottom = position.bottom;
203 slider.left = position.left + position.height + cast(int)nearbyint(barLength0 * value0);
204 slider.right = slider.left + _barLength;
205 parent.drawFilledBox(slider, ss.getColor("SliderColor"));
206
207 //draw buttons
208 parent.bitBLT(position.cornerUL, flags & MINUS_PRESSED ? ss.getImage("leftArrowB") : ss.getImage("leftArrowA"));
209 Point lower = position.cornerUR;
210 lower.x -= position.height;
211 parent.bitBLT(lower, flags & PLUS_PRESSED ? ss.getImage("rightArrowB") : ss.getImage("rightArrowA"));
212 /+if (isFocused) {
213 parent.drawBoxPattern(position, ss.pattern["blackDottedLine"]);
214 }+/
215 if (state == ElementState.Disabled) {
216 parent.bitBLTPattern(position, ss.getImage("ElementDisabledPtrn"));
217 }
218 }
219 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) {
220 if (state != ElementState.Enabled) return;
221 mce.x -= position.left;
222 mce.y -= position.top;
223 if (mce.button == MouseButton.Left) {
224 if (mce.x < position.height) {
225 if (!(flags & MINUS_PRESSED) && mce.state == ButtonState.Pressed) {
226 value = _value - 1;
227 flags |= MINUS_PRESSED;
228 registerTimer();
229 } else if (flags & MINUS_PRESSED && mce.state == ButtonState.Released) {
230 flags &= ~MINUS_PRESSED;
231 }
232 } else if (mce.x >= position.width - position.height) {
233 if (!(flags & PLUS_PRESSED) && mce.state == ButtonState.Pressed) {
234 value = _value + 1;
235 flags |= PLUS_PRESSED;
236 registerTimer();
237 } else if (flags & PLUS_PRESSED && mce.state == ButtonState.Released) {
238 flags &= ~PLUS_PRESSED;
239 }
240 } else {
241 import std.math : nearbyint;
242 const double newVal = mce.x - position.height - (_barLength / 2.0);
243 if (newVal >= 0) {
244 //const int travelLength = position.width - position.height * 2 - _barLength;
245 //const double valRatio = isNaN(largeVal) ? 1.0 : largeVal;
246 //value = cast(int)nearbyint((travelLength / newVal) * valRatio);
247 value = cast(int)nearbyint((newVal) * valRatio);
248 }
249 }
250 flags &= ~SCROLLMATIC;
251
252 }
253 super.passMCE(mec, mce);
254 }
255 public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) {
256 if (state != ElementState.Enabled) return;
257 if (mme.buttonState == MouseButtonFlags.Left && mme.x > position.width && mme.x < position.height) {
258 /* value = _value + mme.relX;
259 draw(); */
260 import std.math : nearbyint;
261 const double newVal = mme.x - position.height - (_barLength / 2.0);
262 if (newVal >= 0)
263 value = cast(int)nearbyint((newVal) * valRatio);
264 }
265 super.passMME(mec, mme);
266 }
267 public override void passMWE(MouseEventCommons mec, MouseWheelEvent mwe) {
268 if (state != ElementState.Enabled) return;
269 value = _value + mwe.x * scrollSpeed;
270 super.passMWE(mec, mwe);
271 }
272 }