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 }