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