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 }