1 /**
2 Copyright: Copyright (c) 2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module anchovy.gui.behaviors.scrollbarbehavior;
8 
9 import anchovy.core.math;
10 import anchovy.gui;
11 import anchovy.gui.interfaces.iwidgetbehavior;
12 
13 alias ScrollbarBehaviorVert = ScrollbarBehavior!true;
14 alias ScrollbarBehaviorHori = ScrollbarBehavior!false;
15 
16 class ScrollbarBehavior(bool vertical) : IWidgetBehavior
17 {
18 protected:
19 	Widget _slider;
20 	Widget _body;
21 	Widget _widget;
22 
23 public:
24 
25 	override void attachPropertiesTo(Widget widget)
26 	{
27 		widget["sliderSize"] = new ValueProperty(_widget, 0.5);
28 		widget["sliderPos"] = new ValueProperty(_widget, 0.0);
29 	}
30 
31 	override void attachTo(Widget widget)
32 	{
33 		_body = widget["subwidgets"].get!(Widget[string]).get("body", null);
34 		_slider = widget["subwidgets"].get!(Widget[string]).get("slider", null);
35 		_widget = widget;
36 
37 		if (_slider && _body)
38 		{
39 			_body.size.valueChanged.connect((FlexibleObject obj, Variant value){updateSize();});
40 
41 			_slider.addEventHandler(&handleSliderDrag);
42 			_slider.position.valueChanging.connect(&handleSliderMoving);
43 
44 			_widget.property("sliderPos").valueChanging.connect(&handleSliderPositionChanging);
45 			_widget.property("sliderSize").valueChanging.connect(&handleSliderSizeChanging);
46 			_widget.property("sliderSize").valueChanged.connect(&handleSliderSizeChanged);
47 
48 			updateSize();
49 		}
50 	}
51 
52 	void updateSize()
53 	{
54 		ivec2 minSize = _slider.getPropertyAs!("minSize", ivec2);
55 		ivec2 bodySize = _body.size.value.get!ivec2;
56 
57 		static if (vertical)
58 		{
59 			int size = cast(int)(bodySize.y * _widget["sliderSize"].get!double);
60 			_slider.setProperty!"size"(ivec2(bodySize.x, max(size, minSize.y)));
61 
62 			int position = cast(int)((clamp(bodySize.y - _slider["size"].get!ivec2.y, 0, int.max)) *
63 				_widget["sliderPos"].get!double);
64 			_slider.setProperty!"position"(ivec2(0, position));
65 		}
66 		else
67 		{
68 			int size = cast(int)(bodySize.x * _widget["sliderSize"].get!double);
69 			_slider.setProperty!"size"(ivec2(max(size, minSize.x), bodySize.y));
70 
71 			int position = cast(int)((clamp(bodySize.x - _slider["size"].get!ivec2.x, 0, int.max)) *
72 				_widget["sliderPos"].get!double);
73 			_slider.setProperty!"position"(ivec2(position, 0));
74 		}
75 	}
76 
77 	double clampToNormal(double num)
78 	{
79 		return num < 0 ? 0 : (num > 1.0 ? 1.0 : num);
80 	}
81 
82 	// clamps value to [0.0..1.0]
83 	void handleSliderPositionChanging(FlexibleObject widget, Variant* value)
84 	{
85 		double* val = (*value).peek!double;
86 		if (!val) *val = (*value).coerce!double;
87 
88 		if (val)
89 		{
90 			*val = clampToNormal(*val);
91 		}
92 
93 		*value = *val;
94 	}
95 
96 	void handleSliderSizeChanging(FlexibleObject widget, Variant* value)
97 	{
98 		handleSliderPositionChanging(widget, value);
99 	}
100 
101 	void handleSliderSizeChanged(FlexibleObject obj, Variant value)
102 	{
103 		updateSize();
104 	}
105 
106 	bool handleSliderDrag(Widget widget, DragEvent event)
107 	{
108 		_slider["position"] = _slider.getPropertyAs!("position", ivec2) + event.delta;
109 		return true;
110 	}
111 
112 	void handleSliderMoving(FlexibleObject slider, Variant* position)
113 	{
114 		int newPosition;
115 		int bodySize;
116 		int sliderSize;
117 
118 		static if (vertical)
119 		{
120 			int pos = position.get!ivec2.y;
121 			bodySize = _body.size.value.get!ivec2.y;
122 			sliderSize = _slider.size.value.get!ivec2.y;
123 			newPosition = pos < 0 ? 0 : ((pos + sliderSize) > bodySize ? bodySize - sliderSize : pos);
124 			(*position) = ivec2(0, newPosition);
125 		}
126 		else
127 		{
128 			int pos = position.get!ivec2.x;
129 			bodySize = _body.size.value.get!ivec2.x;
130 			sliderSize = _slider.size.value.get!ivec2.x;
131 			newPosition = pos < 0 ? 0 : ((pos + sliderSize) > bodySize ? bodySize - sliderSize : pos);
132 			(*position) = ivec2(newPosition, 0);
133 		}
134 
135 		double sliderPos = cast(double)newPosition / cast(double)(bodySize - sliderSize);
136 		_widget["sliderPos"] = sliderPos is double.nan ? 0 : sliderPos;
137 
138 		//writeln("slider moved ", sliderPos);
139 	}
140 
141 }