1 /**
2 Grimoire
3 Copyright (c) 2017 Enalye
4 
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising
7 from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute
11 it freely, subject to the following restrictions:
12 
13 	1. The origin of this software must not be misrepresented;
14 	   you must not claim that you wrote the original software.
15 	   If you use this software in a product, an acknowledgment
16 	   in the product documentation would be appreciated but
17 	   is not required.
18 
19 	2. Altered source versions must be plainly marked as such,
20 	   and must not be misrepresented as being the original software.
21 
22 	3. This notice may not be removed or altered from any source distribution.
23 */
24 
25 module ui.list.vlist;
26 
27 import std.conv: to;
28 
29 import core.all;
30 import render.all;
31 import common.all;
32 
33 import ui.all;
34 
35 private class ListContainer: WidgetGroup {
36 	public {
37 		View view;
38 		VLayout layout;
39 	}
40 
41 	this(Vec2f size) {
42 		createGui(size);
43 	}
44 
45 	override void onEvent(Event event) {
46 		pushView(view, false);
47 		super.onEvent(event);
48 		popView();
49 	}
50 
51 	override void update(float deltaTime) {
52 		pushView(view, false);
53 		super.update(deltaTime);
54 		popView();
55 	}
56 
57 	override void draw() {
58 		pushView(view, true);
59 		super.draw();
60 		popView();
61 		view.draw(_position);
62 	}
63 
64 	protected void createGui(Vec2f newSize) {
65 		_isFrame = true;
66 		isLocked = true;
67 		layout = new VLayout;
68 		view = new View(to!Vec2u(newSize));
69 		view.position = Vec2f.zero;
70 		size(newSize);
71 		addChild(layout);
72 	}
73 }
74 
75 class VList: WidgetGroup {
76 	protected {
77 		ListContainer _container;
78 		Slider _slider;
79 		Vec2f _lastMousePos = Vec2f.zero;
80 		float _layoutLength = 25f;
81 		uint _nbElements = 0u;
82 		uint _idElementSelected = 0u;
83 	}
84 
85 	@property {
86 		uint selected() const { return _idElementSelected; }
87 		uint selected(uint id) {
88 			if(id > _nbElements)
89 				throw new Exception("VList: index out of bounds");
90 			_idElementSelected = id;
91 			return _idElementSelected;
92 		}
93 
94 		float layoutLength() const { return _layoutLength; }
95 		float layoutLength(float length) {
96 			_layoutLength = length;
97 			_container.layout.size = Vec2f(_size.x, _layoutLength * _nbElements);
98 			return _layoutLength;
99 		}
100 	}
101 
102 	this(Vec2f newSize) {
103 		isLocked = true;
104 		_slider = new VScrollbar;
105 		_container = new ListContainer(newSize);
106 
107 		super.addChild(_slider);
108 		super.addChild(_container);
109 
110 		size(newSize);
111 		position(Vec2f.zero);
112 	}
113 
114 	override void onEvent(Event event) {
115 		super.onEvent(event);
116 		if(event.type == EventType.MouseDown || event.type == EventType.MouseUp || event.type == EventType.MouseUpdate) {
117 			if(_slider.isInside(event.position))
118 				_slider.onEvent(event);
119 			else if(event.type == EventType.MouseDown) {
120 
121 				auto widgets = _container.layout.children;
122 				foreach(uint id, ref Widget widget; _container.layout.children) {
123 					widget.isValidated = false;
124 					if(widget.isHovered)
125 						_idElementSelected = id;
126 				}
127 				if(_idElementSelected < widgets.length)
128 					widgets[_idElementSelected].isValidated = true;
129 			}
130 		}
131 
132 		if(!isOnInteractableWidget(_lastMousePos) && event.type == EventType.MouseWheel)
133 			_slider.onEvent(event);
134 	}
135 
136     override void onPosition() {
137         auto offset = _size * (.5f - _anchor);
138         _slider.position = _position - Vec2f((_size.x - _slider.size.x) / 2f, 0f) + offset;
139         _container.position = _position + Vec2f(_slider.size.x / 2f, 0f) + offset;
140     }
141 
142     override void onSize() {
143         _slider.size = Vec2f(10f, _size.y);
144         _container.layout.size = Vec2f(_size.x, _layoutLength * _nbElements);
145         _container.size = Vec2f(_size.x - _slider.size.x, _size.y);
146         _container.view.renderSize = _container.size.to!Vec2u;
147         onPosition();
148     }
149 
150 	override void update(float deltaTime) {
151 		super.update(deltaTime);
152 		float min = _container.view.size.y / 2f;
153 		float max = _container.layout.size.y - _container.view.size.y / 2f;
154 		float exceedingHeight = _container.layout.size.y - _container.view.size.y;
155 
156 		if(exceedingHeight < 0f) {
157 			_slider.max = 0;
158 			_slider.step = 0;
159 		}
160 		else {
161 			_slider.max = exceedingHeight / _layoutLength;
162 			_slider.step = to!uint(_slider.max);
163 		}
164 		_container.view.position = Vec2f(0f, lerp(min, max, _slider.offset));
165 	}
166 
167 	override void addChild(Widget widget) {
168 		widget.isValidated = (_nbElements == 0u);
169 
170 		_nbElements ++;
171 		_container.layout.size = Vec2f(size.x, _layoutLength * _nbElements);
172 		_container.layout.position = Vec2f(0f, _container.layout.size.y / 2f);
173 		_container.layout.addChild(widget);
174 	}
175 
176 	override void removeChildren() {
177 		_nbElements = 0u;
178 		_idElementSelected = 0u;
179 		_container.layout.size = Vec2f(size.x, 0f);
180 		_container.layout.position = Vec2f.zero;
181 		_container.layout.removeChildren();
182 	}
183 
184 	override void removeChild(uint id) {
185 		_container.layout.removeChild(id);
186 		_nbElements = _container.layout.getChildrenCount();
187 		_idElementSelected = 0u;
188 		_container.layout.size = Vec2f(size.x, _layoutLength * _nbElements);
189 		_container.layout.position = Vec2f(0f, _container.layout.size.y / 2f);
190 	}
191 
192 	override int getChildrenCount() {
193 		return _container.layout.getChildrenCount();	
194 	}
195 
196 	Widget[] getList() {
197 		return _container.layout.children;
198 	}
199 }
200