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 StyleSheet ss = getStyleSheet(); 103 //draw background 104 parent.drawFilledBox(position, ss.getColor("SliderBackground")); 105 //draw slider 106 //const int travelLength = position.height - (position.width * 2) - _barLength; 107 Box slider; 108 //const int value0 = valRatio < 1.0 ? value : cast(int)(value / valRatio); 109 slider.left = position.left; 110 slider.right = position.right; 111 slider.top = position.top + position.width + cast(int)nearbyint(barLength0 * _value); 112 slider.bottom = slider.top + _barLength; 113 parent.drawFilledBox(slider, ss.getColor("SliderColor")); 114 //draw buttons 115 parent.bitBLT(position.cornerUL, flags & MINUS_PRESSED ? ss.getImage("upArrowB") : ss.getImage("upArrowA")); 116 Point lower = position.cornerLL; 117 lower.y -= position.width; 118 parent.bitBLT(lower, flags & PLUS_PRESSED ? ss.getImage("downArrowB") : ss.getImage("downArrowA")); 119 if (state == ElementState.Disabled) { 120 parent.bitBLTPattern(position, ss.getImage("ElementDisabledPtrn")); 121 } 122 /+if (isFocused) { 123 parent.drawBoxPattern(position, ss.pattern["blackDottedLine"]); 124 }+/ 125 if (onDraw !is null) { 126 onDraw(); 127 } 128 } 129 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) { 130 if (state != ElementState.Enabled) return; 131 mce.x -= position.left; 132 mce.y -= position.top; 133 if (mce.button == MouseButton.Left) { 134 if (mce.y < position.width) { 135 if (!(flags & MINUS_PRESSED) && mce.state == ButtonState.Pressed) { 136 value = _value - 1; 137 flags |= MINUS_PRESSED; 138 registerTimer(); 139 } else if (flags & MINUS_PRESSED && mce.state == ButtonState.Released) { 140 flags &= ~(MINUS_PRESSED | SCROLLMATIC); 141 } 142 } else if (mce.y >= position.height - position.width) { 143 if (!(flags & PLUS_PRESSED) && mce.state == ButtonState.Pressed) { 144 value = _value + 1; 145 flags |= PLUS_PRESSED; 146 registerTimer(); 147 } else if (flags & PLUS_PRESSED && mce.state == ButtonState.Released) { 148 flags &= ~(PLUS_PRESSED | SCROLLMATIC); 149 } 150 } else { 151 import std.math : nearbyint; 152 const double newVal = mce.y - position.width - (_barLength / 2.0); 153 if (newVal >= 0) { 154 //const int travelLength = position.height - (position.width * 2) - _barLength; 155 //const double valRatio = isNaN(largeVal) ? 1.0 : largeVal; 156 //value = cast(int)nearbyint((travelLength / newVal) * valRatio); 157 value = cast(int)nearbyint((newVal) * valRatio); 158 } 159 } 160 } 161 super.passMCE(mec, mce); 162 } 163 public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) { 164 if (state != ElementState.Enabled) return; 165 if (mme.buttonState == MouseButtonFlags.Left && mme.y > position.height && mme.y < position.width - position.height) { 166 value = _value = mme.relY; 167 draw(); 168 } 169 super.passMME(mec, mme); 170 } 171 public override void passMWE(MouseEventCommons mec, MouseWheelEvent mwe) { 172 if (state != ElementState.Enabled) return; 173 value = _value - mwe.y * scrollSpeed; 174 super.passMWE(mec, mwe); 175 } 176 177 } 178 /** 179 * Horizontal scrollbar. 180 */ 181 public class HorizScrollBar : ScrollBar { 182 public this(int maxValue, string source, Box position){ 183 this.position = position; 184 this.source = source; 185 this.maxValue = maxValue; 186 } 187 public override void draw(){ 188 StyleSheet ss = getStyleSheet(); 189 //draw background 190 parent.drawFilledBox(position, ss.getColor("SliderBackground")); 191 //draw slider 192 //const int travelLength = position.width - position.height * 2; 193 const int value0 = valRatio < 1.0 ? value : cast(int)(value / valRatio); 194 Box slider; 195 slider.top = position.top; 196 slider.bottom = position.bottom; 197 slider.left = position.left + position.height + cast(int)nearbyint(barLength0 * value0); 198 slider.right = slider.left + _barLength; 199 parent.drawFilledBox(slider, ss.getColor("SliderColor")); 200 201 //draw buttons 202 parent.bitBLT(position.cornerUL, flags & MINUS_PRESSED ? ss.getImage("leftArrowB") : ss.getImage("leftArrowA")); 203 Point lower = position.cornerUR; 204 lower.x -= position.height; 205 parent.bitBLT(lower, flags & PLUS_PRESSED ? ss.getImage("rightArrowB") : ss.getImage("rightArrowA")); 206 /+if (isFocused) { 207 parent.drawBoxPattern(position, ss.pattern["blackDottedLine"]); 208 }+/ 209 if (state == ElementState.Disabled) { 210 parent.bitBLTPattern(position, ss.getImage("ElementDisabledPtrn")); 211 } 212 } 213 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) { 214 if (state != ElementState.Enabled) return; 215 mce.x -= position.left; 216 mce.y -= position.top; 217 if (mce.button == MouseButton.Left) { 218 if (mce.x < position.height) { 219 if (!(flags & MINUS_PRESSED) && mce.state == ButtonState.Pressed) { 220 value = _value - 1; 221 flags |= MINUS_PRESSED; 222 registerTimer(); 223 } else if (flags & MINUS_PRESSED && mce.state == ButtonState.Released) { 224 flags &= ~MINUS_PRESSED; 225 } 226 } else if (mce.x >= position.width - position.height) { 227 if (!(flags & PLUS_PRESSED) && mce.state == ButtonState.Pressed) { 228 value = _value + 1; 229 flags |= PLUS_PRESSED; 230 registerTimer(); 231 } else if (flags & PLUS_PRESSED && mce.state == ButtonState.Released) { 232 flags &= ~PLUS_PRESSED; 233 } 234 } else { 235 import std.math : nearbyint; 236 const double newVal = mce.x - position.height - (_barLength / 2.0); 237 if (newVal >= 0) { 238 //const int travelLength = position.width - position.height * 2 - _barLength; 239 //const double valRatio = isNaN(largeVal) ? 1.0 : largeVal; 240 //value = cast(int)nearbyint((travelLength / newVal) * valRatio); 241 value = cast(int)nearbyint((newVal) * valRatio); 242 } 243 } 244 flags &= ~SCROLLMATIC; 245 246 } 247 super.passMCE(mec, mce); 248 } 249 public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) { 250 if (state != ElementState.Enabled) return; 251 if (mme.buttonState == MouseButtonFlags.Left && mme.x > position.width && mme.x < position.height) { 252 value = _value + mme.relX; 253 draw(); 254 } 255 super.passMME(mec, mme); 256 } 257 public override void passMWE(MouseEventCommons mec, MouseWheelEvent mwe) { 258 if (state != ElementState.Enabled) return; 259 value = _value + mwe.x * scrollSpeed; 260 super.passMWE(mec, mwe); 261 } 262 }