1 #include "components/ComponentGrid.h"
2 
3 #include "Settings.h"
4 
5 using namespace GridFlags;
6 
ComponentGrid(Window * window,const Vector2i & gridDimensions)7 ComponentGrid::ComponentGrid(Window* window, const Vector2i& gridDimensions) : GuiComponent(window),
8 	mGridSize(gridDimensions), mCursor(0, 0)
9 {
10 	assert(gridDimensions.x() > 0 && gridDimensions.y() > 0);
11 
12 	mCells.reserve(gridDimensions.x() * gridDimensions.y());
13 
14 	mColWidths = new float[gridDimensions.x()];
15 	mRowHeights = new float[gridDimensions.y()];
16 	for(int x = 0; x < gridDimensions.x(); x++)
17 		mColWidths[x] = 0;
18 	for(int y = 0; y < gridDimensions.y(); y++)
19 		mRowHeights[y] = 0;
20 }
21 
~ComponentGrid()22 ComponentGrid::~ComponentGrid()
23 {
24 	delete[] mRowHeights;
25 	delete[] mColWidths;
26 }
27 
getColWidth(int col)28 float ComponentGrid::getColWidth(int col)
29 {
30 	if(mColWidths[col] != 0)
31 		return mColWidths[col] * mSize.x();
32 
33 	// calculate automatic width
34 	float freeWidthPerc = 1;
35 	int between = 0;
36 	for(int x = 0; x < mGridSize.x(); x++)
37 	{
38 		freeWidthPerc -= mColWidths[x]; // if it's 0 it won't do anything
39 		if(mColWidths[x] == 0)
40 			between++;
41 	}
42 
43 	return (freeWidthPerc * mSize.x()) / between;
44 }
45 
getRowHeight(int row)46 float ComponentGrid::getRowHeight(int row)
47 {
48 	if(mRowHeights[row] != 0)
49 		return mRowHeights[row] * mSize.y();
50 
51 	// calculate automatic height
52 	float freeHeightPerc = 1;
53 	int between = 0;
54 	for(int y = 0; y < mGridSize.y(); y++)
55 	{
56 		freeHeightPerc -= mRowHeights[y]; // if it's 0 it won't do anything
57 		if(mRowHeights[y] == 0)
58 			between++;
59 	}
60 
61 	return (freeHeightPerc * mSize.y()) / between;
62 }
63 
setColWidthPerc(int col,float width,bool update)64 void ComponentGrid::setColWidthPerc(int col, float width, bool update)
65 {
66 	assert(width >= 0 && width <= 1);
67 	assert(col >= 0 && col < mGridSize.x());
68 	mColWidths[col] = width;
69 
70 	if(update)
71 		onSizeChanged();
72 }
73 
setRowHeightPerc(int row,float height,bool update)74 void ComponentGrid::setRowHeightPerc(int row, float height, bool update)
75 {
76 	assert(height >= 0 && height <= 1);
77 	assert(row >= 0 && row < mGridSize.y());
78 	mRowHeights[row] = height;
79 
80 	if(update)
81 		onSizeChanged();
82 }
83 
setEntry(const std::shared_ptr<GuiComponent> & comp,const Vector2i & pos,bool canFocus,bool resize,const Vector2i & size,unsigned int border,GridFlags::UpdateType updateType)84 void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp, const Vector2i& pos, bool canFocus, bool resize, const Vector2i& size,
85 	unsigned int border, GridFlags::UpdateType updateType)
86 {
87 	assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y());
88 	assert(comp != nullptr);
89 	assert(comp->getParent() == NULL);
90 
91 	GridEntry entry(pos, size, comp, canFocus, resize, updateType, border);
92 	mCells.push_back(entry);
93 
94 	addChild(comp.get());
95 
96 	if(!cursorValid() && canFocus)
97 	{
98 		auto origCursor = mCursor;
99 		mCursor = pos;
100 		onCursorMoved(origCursor, mCursor);
101 	}
102 
103 	updateCellComponent(mCells.back());
104 	updateSeparators();
105 }
106 
removeEntry(const std::shared_ptr<GuiComponent> & comp)107 bool ComponentGrid::removeEntry(const std::shared_ptr<GuiComponent>& comp)
108 {
109 	for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
110 	{
111 		if(it->component == comp)
112 		{
113 			removeChild(comp.get());
114 			mCells.erase(it);
115 			return true;
116 		}
117 	}
118 
119 	return false;
120 }
121 
updateCellComponent(const GridEntry & cell)122 void ComponentGrid::updateCellComponent(const GridEntry& cell)
123 {
124 	// size
125 	Vector2f size(0, 0);
126 	for(int x = cell.pos.x(); x < cell.pos.x() + cell.dim.x(); x++)
127 		size[0] += getColWidth(x);
128 	for(int y = cell.pos.y(); y < cell.pos.y() + cell.dim.y(); y++)
129 		size[1] += getRowHeight(y);
130 
131 	if(cell.resize)
132 		cell.component->setSize(size);
133 
134 	// position
135 	// find top left corner
136 	Vector3f pos(0, 0, 0);
137 	for(int x = 0; x < cell.pos.x(); x++)
138 		pos[0] += getColWidth(x);
139 	for(int y = 0; y < cell.pos.y(); y++)
140 		pos[1] += getRowHeight(y);
141 
142 	// center component
143 	pos[0] = pos.x() + (size.x() - cell.component->getSize().x()) / 2;
144 	pos[1] = pos.y() + (size.y() - cell.component->getSize().y()) / 2;
145 
146 	cell.component->setPosition(pos);
147 }
148 
updateSeparators()149 void ComponentGrid::updateSeparators()
150 {
151 	mLines.clear();
152 
153 	const unsigned int color = Renderer::convertColor(0xC6C7C6FF);
154 	bool drawAll = Settings::getInstance()->getBool("DebugGrid");
155 
156 	Vector2f pos;
157 	Vector2f size;
158 	for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
159 	{
160 		if(!it->border && !drawAll)
161 			continue;
162 
163 		// find component position + size
164 		pos = Vector2f(0, 0);
165 		size = Vector2f(0, 0);
166 		for(int x = 0; x < it->pos.x(); x++)
167 			pos[0] += getColWidth(x);
168 		for(int y = 0; y < it->pos.y(); y++)
169 			pos[1] += getRowHeight(y);
170 		for(int x = it->pos.x(); x < it->pos.x() + it->dim.x(); x++)
171 			size[0] += getColWidth(x);
172 		for(int y = it->pos.y(); y < it->pos.y() + it->dim.y(); y++)
173 			size[1] += getRowHeight(y);
174 
175 		if(it->border & BORDER_TOP || drawAll)
176 		{
177 			mLines.push_back( { { pos.x(),               pos.y()               }, { 0.0f, 0.0f }, color } );
178 			mLines.push_back( { { pos.x() + size.x(),    pos.y()               }, { 0.0f, 0.0f }, color } );
179 		}
180 		if(it->border & BORDER_BOTTOM || drawAll)
181 		{
182 			mLines.push_back( { { pos.x(),               pos.y() + size.y()    }, { 0.0f, 0.0f }, color } );
183 			mLines.push_back( { { pos.x() + size.x(),    mLines.back().pos.y() }, { 0.0f, 0.0f }, color } );
184 		}
185 		if(it->border & BORDER_LEFT || drawAll)
186 		{
187 			mLines.push_back( { { pos.x(),               pos.y()               }, { 0.0f, 0.0f }, color } );
188 			mLines.push_back( { { pos.x(),               pos.y() + size.y()    }, { 0.0f, 0.0f }, color } );
189 		}
190 		if(it->border & BORDER_RIGHT || drawAll)
191 		{
192 			mLines.push_back( { { pos.x() + size.x(),    pos.y()               }, { 0.0f, 0.0f }, color } );
193 			mLines.push_back( { { mLines.back().pos.x(), pos.y() + size.y()    }, { 0.0f, 0.0f }, color } );
194 		}
195 	}
196 }
197 
onSizeChanged()198 void ComponentGrid::onSizeChanged()
199 {
200 	for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
201 		updateCellComponent(*it);
202 
203 	updateSeparators();
204 }
205 
getCellAt(int x,int y) const206 const ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) const
207 {
208 	assert(x >= 0 && x < mGridSize.x() && y >= 0 && y < mGridSize.y());
209 
210 	for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
211 	{
212 		int xmin = it->pos.x();
213 		int xmax = xmin + it->dim.x();
214 		int ymin = it->pos.y();
215 		int ymax = ymin + it->dim.y();
216 
217 		if(x >= xmin && y >= ymin && x < xmax && y < ymax)
218 			return &(*it);
219 	}
220 
221 	return NULL;
222 }
223 
input(InputConfig * config,Input input)224 bool ComponentGrid::input(InputConfig* config, Input input)
225 {
226 	const GridEntry* cursorEntry = getCellAt(mCursor);
227 	if(cursorEntry && cursorEntry->component->input(config, input))
228 		return true;
229 
230 	if(!input.value)
231 		return false;
232 
233 	if(config->isMappedLike("down", input))
234 	{
235 		return moveCursor(Vector2i(0, 1));
236 	}
237 	if(config->isMappedLike("up", input))
238 	{
239 		return moveCursor(Vector2i(0, -1));
240 	}
241 	if(config->isMappedLike("left", input))
242 	{
243 		return moveCursor(Vector2i(-1, 0));
244 	}
245 	if(config->isMappedLike("right", input))
246 	{
247 		return moveCursor(Vector2i(1, 0));
248 	}
249 
250 	return false;
251 }
252 
resetCursor()253 void ComponentGrid::resetCursor()
254 {
255 	if(!mCells.size())
256 		return;
257 
258 	for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
259 	{
260 		if(it->canFocus)
261 		{
262 			Vector2i origCursor = mCursor;
263 			mCursor = it->pos;
264 			onCursorMoved(origCursor, mCursor);
265 			break;
266 		}
267 	}
268 }
269 
moveCursor(Vector2i dir)270 bool ComponentGrid::moveCursor(Vector2i dir)
271 {
272 	assert(dir.x() || dir.y());
273 
274 	const Vector2i origCursor = mCursor;
275 
276 	const GridEntry* currentCursorEntry = getCellAt(mCursor);
277 
278 	Vector2i searchAxis(dir.x() == 0, dir.y() == 0);
279 
280 	while(mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y())
281 	{
282 		mCursor = mCursor + dir;
283 
284 		Vector2i curDirPos = mCursor;
285 
286 		const GridEntry* cursorEntry;
287 		//spread out on search axis+
288 		while(mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()
289 			&& mCursor.x() >= 0 && mCursor.y() >= 0)
290 		{
291 			cursorEntry = getCellAt(mCursor);
292 			if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry)
293 			{
294 				onCursorMoved(origCursor, mCursor);
295 				return true;
296 			}
297 
298 			mCursor += searchAxis;
299 		}
300 
301 		//now again on search axis-
302 		mCursor = curDirPos;
303 		while(mCursor.x() >= 0 && mCursor.y() >= 0
304 			&& mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y())
305 		{
306 			cursorEntry = getCellAt(mCursor);
307 			if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry)
308 			{
309 				onCursorMoved(origCursor, mCursor);
310 				return true;
311 			}
312 
313 			mCursor -= searchAxis;
314 		}
315 
316 		mCursor = curDirPos;
317 	}
318 
319 	//failed to find another focusable element in this direction
320 	mCursor = origCursor;
321 	return false;
322 }
323 
onFocusLost()324 void ComponentGrid::onFocusLost()
325 {
326 	const GridEntry* cursorEntry = getCellAt(mCursor);
327 	if(cursorEntry)
328 		cursorEntry->component->onFocusLost();
329 }
330 
onFocusGained()331 void ComponentGrid::onFocusGained()
332 {
333 	const GridEntry* cursorEntry = getCellAt(mCursor);
334 	if(cursorEntry)
335 		cursorEntry->component->onFocusGained();
336 }
337 
cursorValid()338 bool ComponentGrid::cursorValid()
339 {
340 	const GridEntry* e = getCellAt(mCursor);
341 	return (e != NULL && e->canFocus);
342 }
343 
update(int deltaTime)344 void ComponentGrid::update(int deltaTime)
345 {
346 	// update ALL THE THINGS
347 	const GridEntry* cursorEntry = getCellAt(mCursor);
348 	for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
349 	{
350 		if(it->updateType == UPDATE_ALWAYS || (it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it)))
351 			it->component->update(deltaTime);
352 	}
353 }
354 
render(const Transform4x4f & parentTrans)355 void ComponentGrid::render(const Transform4x4f& parentTrans)
356 {
357 	Transform4x4f trans = parentTrans * getTransform();
358 
359 	renderChildren(trans);
360 
361 	// draw cell separators
362 	if(mLines.size())
363 	{
364 		Renderer::setMatrix(trans);
365 		Renderer::bindTexture(0);
366 		Renderer::drawLines(&mLines[0], mLines.size());
367 	}
368 }
369 
textInput(const char * text)370 void ComponentGrid::textInput(const char* text)
371 {
372 	const GridEntry* selectedEntry = getCellAt(mCursor);
373 	if(selectedEntry != NULL && selectedEntry->canFocus)
374 		selectedEntry->component->textInput(text);
375 }
376 
onCursorMoved(Vector2i from,Vector2i to)377 void ComponentGrid::onCursorMoved(Vector2i from, Vector2i to)
378 {
379 	const GridEntry* cell = getCellAt(from);
380 	if(cell)
381 		cell->component->onFocusLost();
382 
383 	cell = getCellAt(to);
384 	if(cell)
385 		cell->component->onFocusGained();
386 
387 	updateHelpPrompts();
388 }
389 
setCursorTo(const std::shared_ptr<GuiComponent> & comp)390 void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& comp)
391 {
392 	for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
393 	{
394 		if(it->component == comp)
395 		{
396 			Vector2i oldCursor = mCursor;
397 			mCursor = it->pos;
398 			onCursorMoved(oldCursor, mCursor);
399 			return;
400 		}
401 	}
402 
403 	// component not found!!
404 	assert(false);
405 }
406 
getHelpPrompts()407 std::vector<HelpPrompt> ComponentGrid::getHelpPrompts()
408 {
409 	std::vector<HelpPrompt> prompts;
410 	const GridEntry* e = getCellAt(mCursor);
411 	if(e)
412 		prompts = e->component->getHelpPrompts();
413 
414 	bool canScrollVert = mGridSize.y() > 1;
415 	bool canScrollHoriz = mGridSize.x() > 1;
416 	for(auto it = prompts.cbegin(); it != prompts.cend(); it++)
417 	{
418 		if(it->first == "up/down/left/right")
419 		{
420 			canScrollHoriz = false;
421 			canScrollVert = false;
422 			break;
423 		}else if(it->first == "up/down")
424 		{
425 			canScrollVert = false;
426 		}else if(it->first == "left/right")
427 		{
428 			canScrollHoriz = false;
429 		}
430 	}
431 
432 	if(canScrollHoriz && canScrollVert)
433 		prompts.push_back(HelpPrompt("up/down/left/right", "choose"));
434 	else if(canScrollHoriz)
435 		prompts.push_back(HelpPrompt("left/right", "choose"));
436 	else if(canScrollVert)
437 		prompts.push_back(HelpPrompt("up/down", "choose"));
438 
439 	return prompts;
440 }
441