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.gridlist;
26 
27 import std.conv: to;
28 
29 import core.all;
30 import render.all;
31 import common.all;
32 
33 import ui.widget;
34 import ui.layout;
35 import ui.slider;
36 
37 private class GridContainer: WidgetGroup {
38 	public {
39 		View view;
40 		GridLayout layout;
41 	}
42 
43 	this(Vec2f size) {
44 		createGui(size);
45 	}
46 
47 	override void onEvent(Event event) {
48 		pushView(view, false);
49 		super.onEvent(event);
50 		popView();
51 	}
52 
53 	override void update(float deltaTime) {
54 		pushView(view, false);
55 		super.update(deltaTime);
56 		popView();
57 	}
58 
59 	override void draw() {
60 		pushView(view, true);
61 		super.draw();
62 		popView();
63 		view.draw(_position);
64 	}
65 
66 	protected void createGui(Vec2f newSize) {
67 		_isFrame = true;
68 		isLocked = true;
69 		layout = new GridLayout;
70 		view = new View(to!Vec2u(newSize));
71 		view.position = Vec2f.zero;
72 		size(newSize);
73 		addChild(layout);
74 	}
75 }
76 
77 class GridList: WidgetGroup {
78 	protected {
79 		GridContainer _container;
80 		Slider _slider;
81 		Vec2f _lastMousePos = Vec2f.zero;
82 		float _layoutLength = 74f;
83 		uint _nbElements = 0u;
84 		uint _idElementSelected = 0u;
85 		uint _nbElementsPerLine = 4u;
86 	}
87 
88 	@property {
89 		uint selected() const { return _idElementSelected; }
90 		uint selected(uint id) {
91 			if(id > _nbElements)
92 				throw new Exception("GridList: index out of bounds");
93 			_idElementSelected = id;
94 			return _idElementSelected;
95 		}
96 	}
97 
98 	this(Vec2f size) {
99 		createGui(size);
100 	}
101 
102 	override void onEvent(Event event) {
103 		super.onEvent(event);
104 		if(event.type == EventType.MouseDown || event.type == EventType.MouseUp || event.type == EventType.MouseUpdate) {
105 			if(_slider.isInside(event.position))
106 				_slider.onEvent(event);
107 			else if(event.type == EventType.MouseDown) {
108 
109 				auto widgets = _container.layout.children;
110 				foreach(uint id, ref Widget widget; _container.layout.children) {
111 					widget.isValidated = false;
112 					if(widget.isHovered)
113 						_idElementSelected = id;
114 				}
115 				if(_idElementSelected < widgets.length)
116 					widgets[_idElementSelected].isValidated = true;
117 			}
118 		}
119 
120 		if(!isOnInteractableWidget(_lastMousePos) && event.type == EventType.MouseWheel)
121 			_slider.onEvent(event);
122 	}
123 
124 		override void onPosition() {
125 			_slider.position = _position - Vec2f((_size.x - _slider.size.x) / 2f, 0f);
126 			_container.position = _position + Vec2f(_slider.size.x / 2f, 0f);
127 		}
128 
129 		override void onSize() {
130 			_slider.size = Vec2f(10f, _size.y);
131 			_container.layout.capacity = Vec2u(_nbElementsPerLine, 0u);
132 			_container.layout.size = Vec2f(_size.x, _layoutLength * (_nbElements / _nbElementsPerLine));
133 			_container.size = Vec2f(_size.x - _slider.size.x, _size.y);
134 			_container.view.renderSize = _container.size.to!Vec2u;
135 			onPosition();
136 		}
137 
138 	override void update(float deltaTime) {
139 		super.update(deltaTime);
140 		float min = _container.view.size.y / 2f;
141 		float max = _container.layout.size.y - _container.view.size.y / 2f;
142 		float exceedingHeight = _container.layout.size.y - _container.view.size.y;
143 
144 		if(exceedingHeight < 0f) {
145 			_slider.max = 0;
146 			_slider.step = 0;
147 		}
148 		else {
149 			_slider.max = exceedingHeight / _layoutLength;
150 			_slider.step = to!uint(_slider.max);
151 		}
152 		_container.view.position = Vec2f(0f, lerp(min, max, _slider.offset));
153 	}
154 
155 	override void addChild(Widget widget) {
156 		widget.isValidated = (_nbElements == 0u);
157 
158 		_nbElements ++;
159 		_container.layout.size = Vec2f(_size.x, _layoutLength * (_nbElements / _nbElementsPerLine));
160 		_container.layout.position = Vec2f(0f, _container.layout.size.y / 2f);
161 		_container.layout.addChild(widget);
162 	}
163 
164 	override void removeChildren() {
165 		_nbElements = 0u;
166 		_idElementSelected = 0u;
167 		_container.layout.size = Vec2f(size.x, 0f);
168 		_container.layout.position = Vec2f.zero;
169 		_container.layout.removeChildren();
170 	}
171 
172 	override void removeChild(uint id) {
173 		_container.layout.removeChild(id);
174 		_nbElements = _container.layout.getChildrenCount();
175 		_idElementSelected = 0u;
176 		_container.layout.size = Vec2f(_size.x, _layoutLength * (_nbElements / _nbElementsPerLine));
177 		_container.layout.position = Vec2f(0f, _container.layout.size.y / 2f);
178 	}
179 
180 	override int getChildrenCount() {
181 		return _container.layout.getChildrenCount();
182 	}
183 
184 	Widget[] getList() {
185 		return _container.layout.children;
186 	}
187 
188 	protected void createGui(Vec2f newSize) {
189 		isLocked = true;
190 		_slider = new VScrollbar;
191 		_container = new GridContainer(newSize);
192 
193 		super.addChild(_slider);
194 		super.addChild(_container);
195 
196 		size(newSize);
197 		position(Vec2f.zero);
198 	}
199 }
200