nsnake
Classic snake game for the terminal
Menu.cpp
1 #include <Interface/Menu/Menu.hpp>
2 #include <Misc/Utils.hpp>
3 #include <Config/Globals.hpp>
4 #include <Flow/InputManager.hpp>
5 
6 Menu::Menu(int x, int y, int width, int height):
7  current(NULL),
8  currentIndex(0),
9  x(x),
10  y(y),
11  width(width),
12  height(height),
13  selected(false),
14  selectedItem(NULL)
15 { }
16 Menu::~Menu()
17 {
18  for (unsigned int i = 0; i < (this->item.size()); i++)
19  SAFE_DELETE(this->item[i]);
20 
21  this->item.clear();
22 }
23 void Menu::add(MenuItem* item)
24 {
25  this->item.push_back(item);
26 
27  // Means we're the first item being added!
28  if (this->item.size() == 1)
29  {
30  this->current = this->item.back();
31  this->currentIndex = this->item.size() - 1;
32  }
33 
34  // Means we're the first non-nil item being added!
35  unsigned int i = 0;
36  for (i = 0; i < (this->item.size()); i++)
37  if (this->item[i])
38  break;
39 
40  this->current = this->item[i];
41  this->currentIndex = i;
42 }
43 void Menu::addBlank()
44 {
45  this->item.push_back(NULL);
46 }
47 void Menu::removeByID(int id)
48 {
49  std::vector<MenuItem*>::iterator it = this->item.begin();
50 
51  while (it != this->item.end())
52  {
53  if (!(*it))
54  ++it;
55 
56  if ((*it)->id == id)
57  {
58  if (this->current == *it)
59  {
60  this->goNext();
61  this->currentIndex--;
62  }
63 
64  this->item.erase(it);
65  return;
66  }
67  else
68  ++it;
69  }
70 }
71 void Menu::removeByLabel(std::string label)
72 {
73  std::vector<MenuItem*>::iterator it = this->item.begin();
74 
75  while (it != this->item.end())
76  {
77  if (!(*it))
78  ++it;
79 
80  if ((*it)->label == label)
81  {
82  if (this->current == *it)
83  {
84  this->goNext();
85  this->currentIndex--;
86  }
87 
88  this->item.erase(it);
89  return;
90  }
91  else
92  ++it;
93  }
94 }
95 void Menu::draw(Window* window)
96 {
97  // If we have more Items than we can draw, we need to
98  // ask the user to scroll.
99 
100  // So these are the indexes of the items we'll actually
101  // show on the screen.
102  unsigned int draw_begin = 0;
103  unsigned int draw_end = this->item.size();
104 
105  if (this->height < (int)this->item.size())
106  {
107  if ((int)this->currentIndex <= this->height/2)
108  {
109  draw_end = this->height - 1;
110  }
111  else if ((int)this->currentIndex < ((int)this->item.size() - this->height/2) - 1)
112  {
113  draw_begin = this->currentIndex - this->height/2;
114  draw_end = this->currentIndex + this->height/2;
115  }
116  else
117  {
118  draw_begin = this->item.size() - this->height;
119  }
120  }
121 
122  // `i` is the index of the item to draw
123  for (unsigned int curitem = draw_begin, yoffset = 0;
124  curitem < draw_end;
125  curitem++, yoffset++)
126  {
127  // Menu is big and needs scrolling,
128  // we need to show (more) on the top
129  if ((curitem == draw_begin) && (curitem != 0))
130  {
131  window->print("(more)",
132  this->x + this->width/2 - 3,
133  this->y + yoffset,
134  Colors::pair(COLOR_WHITE, COLOR_DEFAULT));
135  continue;
136  }
137  // Menu is big and needs scrolling,
138  // we need to show (more) on the end
139  if ((curitem == draw_end - 1) && (curitem != this->item.size() - 1))
140  {
141  window->print("(more)",
142  this->x + this->width/2 - 3,
143  this->y + yoffset + 1,
144  Colors::pair(COLOR_WHITE, COLOR_DEFAULT));
145  continue;
146  }
147 
148  // <rant>
149  // THIS IS VERY UGLY
150  // HOW CAN I LAY DOWN A CLASS HIERARCHY
151  // AND OVERRIDE PARENT FUNCTIONS ON CHILD CLASSES
152  // IF WHEN I HAVE A PARENT POINTER I CANT LET THE
153  // COMPILER DECIDE WETHER TO CALL PARENT OR CHILD
154  // FUNCTIONS?
155  // </rant>
156 
157 // MenuItemCheckbox* pCheckbox = dynamic_cast<MenuItemCheckbox*>a
158 
159  // Blank Menu Item, will show horizontal line
160  if (! this->item[curitem])
161  {
162  for (int j = 0; j < (this->width); j++)
163  window->printChar(((Globals::Screen::fancy_borders) ?
164  ACS_HLINE :
165  '-'),
166  this->x + j,
167  this->y + yoffset,
168  Colors::pair(COLOR_WHITE, COLOR_DEFAULT));
169  }
170  else
171  {
172  this->item[curitem]->draw(window,
173  this->x,
174  this->y + yoffset,
175  this->width,
176 
177  // Highlighting current item if
178  // it's the current.
179  (this->item[curitem] == this->current));
180  }
181  }
182 }
184 {
185  if (InputManager::noKeyPressed())
186  return;
187 
188  if (InputManager::isPressed("down") || // user-defined
189  InputManager::isPressed(KEY_DOWN) ||
190  InputManager::isPressed('\t'))
191  this->goNext();
192 
193  else if (InputManager::isPressed("up") ||
194  InputManager::isPressed(KEY_UP) ||
195  InputManager::isPressed(KEY_BTAB))
196  this->goPrevious();
197 
198  else if (InputManager::isPressed(KEY_HOME) ||
199  InputManager::isPressed(KEY_PPAGE))
200  this->goFirst();
201 
202  else if (InputManager::isPressed(KEY_END) ||
203  InputManager::isPressed(KEY_NPAGE))
204  this->goLast();
205 
206  else if (InputManager::isPressed(KEY_ENTER) ||
207  InputManager::isPressed('\n'))
208  {
209  // Will only quit if the selected item is a simple
210  // item or label - more complex ones doesn't quit.
211  if (this->current->type == MenuItem::ITEM ||
212  this->current->type == MenuItem::LABEL)
213  {
214  this->selected = true;
215  this->selectedItem = this->current;
216  }
217  else
218  this->current->handleInput();
219  }
220  else
221  {
222  if (this->current)
223  this->current->handleInput();
224  }
225 }
227 {
228  // Empty element, piece of cake
229  if (this->item.size() == 0)
230  return;
231 
232  // Empty element, also piece of cake
233  if (this->item.size() == 1)
234  {
235  this->current = *(this->item.begin());
236  this->currentIndex = 0;
237  return;
238  }
239 
240  // Last element...
241  // Well, if the last element is nil then we have
242  // a problem.
243  if (this->current == this->item.back())
244  {
245  // Assuming we're not nil, things will go smooth
246  if (this->currentIndex == (this->item.size() - 1))
247  {
248  this->goFirst();
249  return;
250  }
251  }
252 
253  this->currentIndex++;
254  this->current = this->item[this->currentIndex];
255 
256  if (! this->current)
257  this->goNext();
258 }
260 {
261  if (this->item.size() == 0)
262  return;
263 
264  if (this->item.size() == 1)
265  {
266  this->current = this->item.front();
267  this->currentIndex = 0;
268  return;
269  }
270 
271  if (this->current == this->item.front())
272  {
273  if (this->currentIndex == 0)
274  {
275  this->goLast();
276  return;
277  }
278  }
279 
280  this->currentIndex--;
281  this->current = this->item[this->currentIndex];
282 
283  if (! this->current)
284  this->goPrevious();
285 }
286 void Menu::goFirst()
287 {
288  if (this->item.size() == 0)
289  return;
290 
291  if (this->item.size() == 1)
292  {
293  this->current = this->item.front();
294  this->currentIndex = 0;
295  return;
296  }
297 
298  this->current = this->item.front();
299  this->currentIndex = 0;
300 
301  if (! this->current)
302  this->goNext();
303 }
304 void Menu::goLast()
305 {
306  if (this->item.size() == 0)
307  return;
308 
309  if (this->item.size() == 1)
310  {
311  this->current = this->item.front();
312  this->currentIndex = 0;
313  return;
314  }
315 
316  this->current = this->item.back();
317  this->currentIndex = (this->item.size() - 1);
318 
319  if (! this->current)
320  this->goPrevious();
321 }
322 void Menu::goRandom()
323 {
324  if (this->item.size() == 0)
325  return;
326 
327  this->currentIndex = Utils::Random::between(0, this->item.size() - 1);
328  this->current = this->item[this->currentIndex];
329 }
331 {
332  // Will only quit if the user selected an item
333  // and the item selected is valid.
334  return (this->selected && this->selectedItem);
335 }
336 std::string Menu::currentLabel()
337 {
338  if (! this->current)
339  this->goNext();
340 
341  return (this->current->label);
342 }
344 {
345  if (! this->current)
346  this->goNext();
347 
348  return (this->current->id);
349 }
350 bool Menu::getBool(int id)
351 {
352  for (unsigned int i = 0; i < (this->item.size()); i++)
353  {
354  if (! this->item[i])
355  continue;
356 
357  if (this->item[i]->id == id)
358  {
359  // Either the type got messed up or we have
360  // two items with the same id.
361  if (this->item[i]->type != MenuItem::CHECKBOX)
362  return false;
363 
364  // This cast may be dangerous if the type was
365  // somehow changed.
366  MenuItemCheckbox* c = (MenuItemCheckbox*)this->item[i];
367  return c->isChecked();
368  }
369  }
370  return false;
371 }
372 int Menu::getInt(int id)
373 {
374  for (unsigned int i = 0; i < (this->item.size()); i++)
375  {
376  if (! this->item[i])
377  continue;
378 
379  if (this->item[i]->id == id)
380  {
381  // Either the type got messed up or we have
382  // two items with the same id.
383  if (this->item[i]->type != MenuItem::NUMBERBOX)
384  return -1;
385 
386  // This cast may be dangerous if the type was
387  // somehow changed.
388  MenuItemNumberbox* c = (MenuItemNumberbox*)this->item[i];
389  return c->current;
390  }
391  }
392  return -1;
393 }
394 std::string Menu::getString(int id)
395 {
396  for (unsigned int i = 0; i < (this->item.size()); i++)
397  {
398  if (! this->item[i])
399  continue;
400 
401  if (this->item[i]->id == id)
402  {
403  // Either the type got messed up or we have
404  // two items with the same id.
405  if (this->item[i]->type == MenuItem::TEXTBOX)
406  {
407  MenuItemTextbox* c = (MenuItemTextbox*)this->item[i];
408  return c->currentText;
409  }
410  else if (this->item[i]->type == MenuItem::TEXTLIST)
411  {
412  MenuItemTextlist* c = (MenuItemTextlist*)this->item[i];
413  return c->currentText();
414  }
415  else
416  return "";
417 
418  // This cast may be dangerous if the type was
419  // somehow changed.
420  }
421  }
422  return "";
423 }
425 {
426  this->selected = false;
427  this->selectedItem = NULL;
428 }
429 
430 
int id
User-defined id to identify this item.
Definition: MenuItem.hpp:57
void handleInput()
Makes the menu react to input, as seen on the global InputManager.
Definition: Menu.cpp:183
void reset()
Makes the menu able to be selected again.
Definition: Menu.cpp:424
MenuItem * selectedItem
Specifies which item the user pressed Enter on.
Definition: Menu.hpp:130
void goNext()
Makes the menu select the next item.
Definition: Menu.cpp:226
MenuItemType type
Specific type of this widget.
Definition: MenuItem.hpp:51
A segment of the terminal screen (2D char matrix).
Definition: Window.hpp:16
MenuItem * current
Current item selected.
Definition: Menu.hpp:115
std::string currentLabel()
Returns the label of the currently selected item.
Definition: Menu.cpp:336
std::string getString(int id)
Returns the string value of the item that has #id.
Definition: Menu.cpp:394
void removeByID(int id)
Removes the menu item with #id.
Definition: Menu.cpp:47
bool getBool(int id)
Returns the bool internal value of item that has #id.
Definition: Menu.cpp:350
int getInt(int id)
Returns the integer value of the item that has #id.
Definition: Menu.cpp:372
Menu(int x, int y, int width, int height)
Creates a menu at #x and #y with #width and #height.
Definition: Menu.cpp:6
void goPrevious()
Makes the menu select the previous item.
Definition: Menu.cpp:259
Place where you can input characters.
bool willQuit()
Tells if the user selected an item that quits the menu.
Definition: Menu.cpp:330
void draw(Window *window)
Draws the whole Menu on #window.
Definition: Menu.cpp:95
unsigned int currentIndex
Index of the currently selected item.
Definition: Menu.hpp:119
std::vector< MenuItem * > item
Container of all the options inside the menu.
Definition: Menu.hpp:109
bool selected
Tells if the user selected an item (pressed Enter).
Definition: Menu.hpp:127
A list of selectable text.
Simplest type of item possible, with a label and user-defined id.
Definition: MenuItem.hpp:11
virtual void draw(Window *window, int x, int y, int width, bool hilite=false)
Shows this item at #x, #y with #width.
Definition: MenuItem.cpp:12
int between(int min, int max)
Random number between min and max.
Definition: Utils.cpp:48
virtual void handleInput()
Makes the menu item react to input, as seen on the global InputManager.
Definition: MenuItem.cpp:19
std::string label
Text that will be shown on the screen.
Definition: MenuItem.hpp:54
void printChar(int c, int x, int y, ColorPair pair=0)
Shows #c at #x #y with color #pair.
Definition: Window.cpp:105
Allows to select a number, kinda like a slider.
A little box that can be checked or not.
void print(std::string str, int x, int y, ColorPair pair=0)
Shows text #str at #x #y on the window with color #pair.
Definition: Window.cpp:94
void removeByLabel(std::string label)
Removes the menu item with #label.
Definition: Menu.cpp:71
int currentID()
Returns the user-specified id of the selected item.
Definition: Menu.cpp:343