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