The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
menu.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "widgets/menu.hpp"
18 
19 #include "game_config.hpp"
20 #include "font/sdl_ttf.hpp"
21 #include "font/standard_colors.hpp"
22 #include "language.hpp"
23 #include "image.hpp"
24 #include "font/marked-up_text.hpp"
25 #include "sdl/rect.hpp"
26 #include "sound.hpp"
27 #include "utils/general.hpp"
28 #include "video.hpp"
29 #include "wml_separators.hpp"
30 
31 #include <numeric>
32 
33 namespace gui {
34 
36  : alpha_sort_()
37  , numeric_sort_()
38  , id_sort_()
39  , xp_sort_()
40  , level_sort_()
41  , redirect_sort_()
42  , pos_sort_()
43  , xp_col_(-1)
44 {
45  set_id_sort(-1);
46 }
47 
49 {
50  alpha_sort_.insert(column);
51  return *this;
52 }
53 
55 {
56  numeric_sort_.insert(column);
57  return *this;
58 }
59 
61 {
62  xp_sort_.insert(column);
63  return *this;
64 }
65 
66 menu::basic_sorter& menu::basic_sorter::set_level_sort(int level_column, int xp_column)
67 {
68  level_sort_.insert(level_column);
69  xp_col_ = xp_column;
70  return *this;
71 }
72 
74 {
75  id_sort_.insert(column);
76  return *this;
77 }
78 
80 {
81  if(column != to) {
82  redirect_sort_.emplace(column, to);
83  }
84 
85  return *this;
86 }
87 
88 menu::basic_sorter& menu::basic_sorter::set_position_sort(int column, const std::vector<int>& pos)
89 {
90  pos_sort_[column] = pos;
91  return *this;
92 }
93 
95 {
96  const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
97  if(redirect != redirect_sort_.end()) {
98  return column_sortable(redirect->second);
99  }
100 
101  return alpha_sort_.count(column) == 1 || numeric_sort_.count(column) == 1 ||
102  pos_sort_.count(column) == 1 || id_sort_.count(column) == 1 ||
103  xp_sort_.count(column) == 1 || level_sort_.count(column) == 1;
104 }
105 
106 static std::pair<int, int> parse_fraction(const std::string& s)
107 {
108  std::vector<std::string> parts = utils::split(s, '/', 0);
109  parts.resize(2);
110  int num = lexical_cast_default<int>(parts[0], 0);
111  int denom = lexical_cast_default<int>(parts[1], 0);
112  return std::make_pair(num, denom);
113 }
114 
115 static int xp_to_advance(const std::string& s) {
116  std::pair<int,int> xp_frac = parse_fraction(s);
117 
118  //consider units without AMLA or advancement as having xp_max=1000000
119  if(xp_frac.second == 0)
120  xp_frac.second = 1000000;
121 
122  return xp_frac.second - xp_frac.first;
123 }
124 
125 bool menu::basic_sorter::less(int column, const item& row1, const item& row2) const
126 {
127  const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
128  if(redirect != redirect_sort_.end()) {
129  return less(redirect->second,row1,row2);
130  }
131 
132  if(id_sort_.count(column) == 1) {
133  return row1.id < row2.id;
134  }
135 
136  if(column < 0 || column >= int(row2.fields.size())) {
137  return false;
138  }
139 
140  if(column >= int(row1.fields.size())) {
141  return true;
142  }
143 
144  const std::string& item1 = font::del_tags(row1.fields[column]);
145  const std::string& item2 = font::del_tags(row2.fields[column]);
146 
147  if(alpha_sort_.count(column) == 1) {
148  std::string::const_iterator begin1 = item1.begin(), end1 = item1.end(),
149  begin2 = item2.begin(), end2 = item2.end();
150  while(begin1 != end1 && is_wml_separator(*begin1)) {
151  ++begin1;
152  }
153 
154  while(begin2 != end2 && is_wml_separator(*begin2)) {
155  ++begin2;
156  }
157 
158  return std::lexicographical_compare(begin1,end1,begin2,end2,chars_less_insensitive);
159  } else if(numeric_sort_.count(column) == 1) {
160  int val_1 = lexical_cast_default<int>(item1, 0);
161  int val_2 = lexical_cast_default<int>(item2, 0);
162 
163  return val_1 > val_2;
164  } else if(xp_sort_.count(column) == 1) {
165  return xp_to_advance(item1) < xp_to_advance(item2);
166  } else if(level_sort_.count(column) == 1) {
167  int level_1 = lexical_cast_default<int>(item1, 0);
168  int level_2 = lexical_cast_default<int>(item2, 0);
169  if (level_1 == level_2) {
170  //break tie using xp
171  const std::string& xp_item1 = font::del_tags(row1.fields[xp_col_]);
172  const std::string& xp_item2 = font::del_tags(row2.fields[xp_col_]);
173  return xp_to_advance(xp_item1) < xp_to_advance(xp_item2);
174  }
175  return level_1 > level_2;
176  }
177 
178  const std::map<int,std::vector<int> >::const_iterator itor = pos_sort_.find(column);
179  if(itor != pos_sort_.end()) {
180  const std::vector<int>& pos = itor->second;
181  if(row1.id >= pos.size()) {
182  return false;
183  }
184 
185  if(row2.id >= pos.size()) {
186  return true;
187  }
188 
189  return pos[row1.id] < pos[row2.id];
190  }
191 
192  return false;
193 }
194 
195 menu::menu(CVideo& video, const std::vector<std::string>& items,
196  bool click_selects, int max_height, int max_width,
197  const sorter* sorter_obj, style *menu_style, const bool auto_join)
198 : scrollarea(video, auto_join), silent_(false),
199  max_height_(max_height), max_width_(max_width),
200  max_items_(-1), item_height_(-1),
201  heading_height_(-1),
202  cur_help_(-1,-1), help_string_(-1),
203  selected_(0), click_selects_(click_selects), out_(false),
204  previous_button_(true), show_result_(false),
205  double_clicked_(false),
206  num_selects_(true),
208  last_was_doubleclick_(false), use_ellipsis_(false),
209  sorter_(sorter_obj), sortby_(-1), sortreversed_(false), highlight_heading_(-1)
210 {
211  style_ = (menu_style) ? menu_style : &default_style;
212  style_->init();
213  fill_items(items, true);
214 }
215 
217 {
218 }
219 
220 void menu::fill_items(const std::vector<std::string>& items, bool strip_spaces)
221 {
222  for(std::vector<std::string>::const_iterator itor = items.begin();
223  itor != items.end(); ++itor) {
224 
225  if(itor->empty() == false && (*itor)[0] == HEADING_PREFIX) {
226  heading_ = utils::quoted_split(itor->substr(1),COLUMN_SEPARATOR, !strip_spaces);
227  continue;
228  }
229 
230  const size_t id = items_.size();
231  item_pos_.push_back(id);
232  const item new_item(utils::quoted_split(*itor, COLUMN_SEPARATOR, !strip_spaces),id);
233  items_.push_back(new_item);
234 
235  //make sure there is always at least one item
236  if(items_.back().fields.empty()) {
237  items_.back().fields.push_back(" ");
238  }
239 
240  //if the first character in an item is an asterisk,
241  //it means this item should be selected by default
242  std::string& first_item = items_.back().fields.front();
243  if(first_item.empty() == false && first_item[0] == DEFAULT_ITEM) {
244  selected_ = id;
245  first_item.erase(first_item.begin());
246  }
247  }
248 
250 
251  if(sortby_ >= 0) {
252  do_sort();
253  }
254  update_size();
255 }
256 
257 namespace {
258 
259 class sort_func
260 {
261 public:
262  sort_func(const menu::sorter& pred, int column) : pred_(&pred), column_(column)
263  {}
264 
265  bool operator()(const menu::item& a, const menu::item& b) const
266  {
267  return pred_->less(column_,a,b);
268  }
269 
270 private:
271  const menu::sorter* pred_;
272  int column_;
273 };
274 
275 }
276 
278 {
279  if(sorter_ == nullptr || sorter_->column_sortable(sortby_) == false) {
280  return;
281  }
282 
283  const int selectid = selection();
284 
285  std::stable_sort(items_.begin(), items_.end(), sort_func(*sorter_, sortby_));
286  if (sortreversed_)
287  std::reverse(items_.begin(), items_.end());
288 
289  recalculate_pos();
290 
291  if(selectid >= 0 && selectid < int(item_pos_.size())) {
292  move_selection_to(selectid, true, NO_MOVE_VIEWPORT);
293  }
294 
295  set_dirty();
296 }
297 
299 {
300  size_t sz = items_.size();
301  item_pos_.resize(sz);
302  for(size_t i = 0; i != sz; ++i)
303  item_pos_[items_[i].id] = i;
304  assert_pos();
305 }
306 
308 {
309  size_t sz = items_.size();
310  assert(item_pos_.size() == sz);
311  for(size_t n = 0; n != sz; ++n) {
312  assert(item_pos_[n] < sz && n == items_[item_pos_[n]].id);
313  }
314 }
315 
317 {
318  for(std::vector<item>::iterator i = items_.begin(); i != items_.end(); ++i) {
319  i->help.clear();
320  for(std::vector<std::string>::iterator j = i->fields.begin(); j != i->fields.end(); ++j) {
321  if(std::find(j->begin(),j->end(),static_cast<char>(HELP_STRING_SEPARATOR)) == j->end()) {
322  i->help.emplace_back();
323  } else {
324  const std::vector<std::string>& items = utils::split(*j, HELP_STRING_SEPARATOR, 0);
325  if(items.size() >= 2) {
326  *j = items.front();
327  i->help.push_back(items.back());
328  } else {
329  i->help.emplace_back();
330  }
331  }
332  }
333  }
334 }
335 
337 {
338  set_full_size(items_.size());
340 }
341 
343 {
344  int h = heading_height();
345  for(size_t i = get_position(),
346  i_end = std::min(items_.size(), i + max_items_onscreen());
347  i < i_end; ++i)
348  h += get_item_rect(i).h;
349  h = std::max(h, height());
350  if (max_height_ > 0 && h > (max_height_)) {
351  h = max_height_;
352  }
353 
354  use_ellipsis_ = false;
355  const std::vector<int>& widths = column_widths();
356  int w = std::accumulate(widths.begin(), widths.end(), 0);
357  if (items_.size() > max_items_onscreen())
358  w += scrollbar_width();
359  w = std::max(w, width());
360  if (max_width_ > 0 && w > (max_width_)) {
361  use_ellipsis_ = true;
362  w = max_width_;
363  }
364 
366  set_measurements(w, h);
367 }
368 
369 int menu::selection() const
370 {
371  if (selected_ >= items_.size()) {
372  return -1;
373  }
374 
375  return items_[selected_].id;
376 }
377 
378 void menu::set_inner_location(const SDL_Rect& rect)
379 {
380  itemRects_.clear();
382  bg_register(rect);
383 }
384 
385 void menu::change_item(int pos1, int pos2,const std::string& str)
386 {
387  if(pos1 < 0 || pos1 >= int(item_pos_.size()) ||
388  pos2 < 0 || pos2 >= int(items_[item_pos_[pos1]].fields.size())) {
389  return;
390  }
391 
392  items_[item_pos_[pos1]].fields[pos2] = str;
393  set_dirty();
394 }
395 
397 {
398  size_t nb_items = items_.size();
399  if (index >= nb_items)
400  return;
401  --nb_items;
402 
403  clear_item(nb_items);
404 
405  // fix ordered positions of items
406  size_t pos = item_pos_[index];
407  item_pos_.erase(item_pos_.begin() + index);
408  items_.erase(items_.begin() + pos);
409  for(size_t i = 0; i != nb_items; ++i) {
410  size_t &n1 = item_pos_[i], &n2 = items_[i].id;
411  if (n1 > pos) --n1;
412  if (n2 > index) --n2;
413  }
414  assert_pos();
415 
416  if (selected_ >= nb_items)
417  selected_ = nb_items - 1;
418 
421  itemRects_.clear();
422  set_dirty();
423 }
424 
425 void menu::set_heading(const std::vector<std::string>& heading)
426 {
427  itemRects_.clear();
428  column_widths_.clear();
429 
430  heading_ = heading;
431  max_items_ = -1;
432 
433  set_dirty();
434 }
435 
436 void menu::set_items(const std::vector<std::string>& items, bool strip_spaces, bool keep_viewport)
437 {
438 
439  const bool scrolled_to_max = (has_scrollbar() && get_position() == get_max_position());
440  items_.clear();
441  item_pos_.clear();
442  itemRects_.clear();
443  column_widths_.clear();
444  //undrawn_items_.clear();
445  max_items_ = -1; // Force recalculation of the max items.
446  item_height_ = -1; // Force recalculation of the item height.
447 
448  if (!keep_viewport || selected_ >= items.size()) {
449  selected_ = 0;
450  }
451 
452  fill_items(items, strip_spaces);
453  if(!keep_viewport) {
454  set_position(0);
455  } else if(scrolled_to_max) {
457  }
458 
460 
461  if(!keep_viewport) {
463  }
464  set_dirty();
465 }
466 
467 void menu::set_max_height(const int new_max_height)
468 {
469  max_height_ = new_max_height;
470  itemRects_.clear();
471  max_items_ = -1;
472  update_size();
473 }
474 
475 void menu::set_max_width(const int new_max_width)
476 {
477  max_width_ = new_max_width;
478  itemRects_.clear();
479  column_widths_.clear();
480  update_size();
481 }
482 
484 {
485  if(max_items_ != -1) {
486  return size_t(max_items_);
487  }
488 
489  const size_t max_height = (max_height_ == -1 ? (video().get_height()*66)/100 : max_height_) - heading_height();
490 
491  std::vector<int> heights;
492  size_t n;
493  for(n = 0; n != items_.size(); ++n) {
494  heights.push_back(get_item_height(n));
495  }
496 
497  std::sort(heights.begin(),heights.end(),std::greater<int>());
498  size_t sum = 0;
499  for(n = 0; n != items_.size() && sum < max_height; ++n) {
500  sum += heights[n];
501  }
502 
503  if(sum > max_height && n > 1)
504  --n;
505 
506  return max_items_ = n;
507 }
508 
510 {
511  if(click_selects_)
512  return;
514 }
515 
516 void menu::set_selection_pos(size_t new_selected, bool silent, SELECTION_MOVE_VIEWPORT move_viewport)
517 {
518  if (new_selected >= items_.size())
519  return;
520 
521  bool changed = false;
522  if (new_selected != selected_) {
524  invalidate_row_pos(new_selected);
525  selected_ = new_selected;
526  changed = true;
527  }
528 
529  if(move_viewport == MOVE_VIEWPORT) {
531  if(!silent_ && !silent && changed) {
533  }
534  }
535 }
536 
537 void menu::move_selection_up(size_t dep)
538 {
539  set_selection_pos(selected_ > dep ? selected_ - dep : 0);
540 }
541 
543 {
544  size_t nb_items = items_.size();
545  set_selection_pos(selected_ + dep >= nb_items ? nb_items - 1 : selected_ + dep);
546 }
547 
548 // private function with control over sound and viewport
549 void menu::move_selection_to(size_t id, bool silent, SELECTION_MOVE_VIEWPORT move_viewport)
550 {
551  if(id < item_pos_.size()) {
552  set_selection_pos(item_pos_[id], silent, move_viewport);
553  }
554 }
555 
556 // public function
557 void menu::move_selection(size_t id)
558 {
559  if(id < item_pos_.size()) {
561  }
562 }
563 
564 // public function
566 {
567  if(id < item_pos_.size()) {
569  }
570 }
571 
573 {
574  set_selection_pos(0, true);
575 }
576 
577 void menu::key_press(SDL_Keycode key)
578 {
579  if (!click_selects_) {
580  switch(key) {
581  case SDLK_UP:
583  break;
584  case SDLK_DOWN:
586  break;
587  case SDLK_PAGEUP:
589  break;
590  case SDLK_PAGEDOWN:
592  break;
593  case SDLK_HOME:
595  break;
596  case SDLK_END:
597  set_selection_pos(items_.size() - 1);
598  break;
599  //case SDLK_RETURN:
600  // double_clicked_ = true;
601  // break;
602  default:
603  break;
604  }
605  }
606 
607  if (num_selects_ && key >= SDLK_1 && key <= SDLK_9)
608  set_selection_pos(key - SDLK_1);
609 }
610 
611 bool menu::requires_event_focus(const SDL_Event* event) const
612 {
613  if(!focus_ || height() == 0 || hidden()) {
614  return false;
615  }
616  if(event == nullptr) {
617  //when event is not specified, signal that focus may be desired later
618  return true;
619  }
620 
621  if(event->type == SDL_KEYDOWN) {
622  SDL_Keycode key = event->key.keysym.sym;
623  if (!click_selects_) {
624  switch(key) {
625  case SDLK_UP:
626  case SDLK_DOWN:
627  case SDLK_PAGEUP:
628  case SDLK_PAGEDOWN:
629  case SDLK_HOME:
630  case SDLK_END:
631  return true;
632  default:
633  break;
634  }
635  }
636  if (num_selects_ && key >= SDLK_1 && key <= SDLK_9) {
637  return true;
638  }
639  }
640  //mouse events are processed regardless of focus
641  return false;
642 }
643 
644 void menu::handle_event(const SDL_Event& event)
645 {
647  if (height()==0 || hidden())
648  return;
649 
650  if(event.type == SDL_KEYDOWN) {
651  // Only pass key events if we have the focus
652  if (focus(&event))
653  key_press(event.key.keysym.sym);
654  } else if(!mouse_locked() && ((event.type == SDL_MOUSEBUTTONDOWN &&
655  (event.button.button == SDL_BUTTON_LEFT || event.button.button == SDL_BUTTON_RIGHT)) ||
656  event.type == DOUBLE_CLICK_EVENT)) {
657 
658  int x = 0;
659  int y = 0;
660  if(event.type == SDL_MOUSEBUTTONDOWN) {
661  x = event.button.x;
662  y = event.button.y;
663  } else {
664  x = reinterpret_cast<size_t>(event.user.data1);
665  y = reinterpret_cast<size_t>(event.user.data2);
666  }
667 
668  const int item = hit(x,y);
669  if(item != -1) {
670  set_focus(true);
671  move_selection_to(item);
672 
673  if(click_selects_) {
674  show_result_ = true;
675  }
676 
677  if(event.type == DOUBLE_CLICK_EVENT) {
679  ignore_next_doubleclick_ = false;
680  } else {
681  double_clicked_ = true;
682  last_was_doubleclick_ = true;
683  if(!silent_) {
685  }
686  }
687  } else if (last_was_doubleclick_) {
688  // If we have a double click as the next event, it means
689  // this double click was generated from a click that
690  // already has helped in generating a double click.
691  SDL_Event ev;
692  SDL_PeepEvents(&ev, 1, SDL_PEEKEVENT, DOUBLE_CLICK_EVENT, DOUBLE_CLICK_EVENT);
693  if (ev.type == DOUBLE_CLICK_EVENT) {
695  }
696  last_was_doubleclick_ = false;
697  }
698  }
699 
700 
701  if(sorter_ != nullptr) {
702  const int heading = hit_heading(x,y);
703  if(heading >= 0 && sorter_->column_sortable(heading)) {
704  sort_by(heading);
705  }
706  }
707  } else if(!mouse_locked() && event.type == SDL_MOUSEMOTION) {
708  if(click_selects_) {
709  const int item = hit(event.motion.x,event.motion.y);
710  const bool out = (item == -1);
711  if (out_ != out) {
712  out_ = out;
714  }
715  if (item != -1) {
716  move_selection_to(item);
717  }
718  }
719 
720  const int heading_item = hit_heading(event.motion.x,event.motion.y);
721  if(heading_item != highlight_heading_) {
722  highlight_heading_ = heading_item;
724  }
725  }
726 }
727 
729 {
730  if(show_result_) {
731  show_result_ = false;
732  return selected_;
733  } else {
734  return -1;
735  }
736 }
737 
739 {
740  bool old = double_clicked_;
741  double_clicked_ = false;
742  return old;
743 }
744 
745 void menu::set_click_selects(bool value)
746 {
747  click_selects_ = value;
748 }
749 
751 {
752  num_selects_ = value;
753 }
754 
755 void menu::scroll(unsigned int)
756 {
757  itemRects_.clear();
758  set_dirty();
759 }
760 
762 {
763  if(sortby_ >= 0) {
764  //clear an existing sort
765  sort_by(-1);
766  }
767  sorter_ = s;
768  sortreversed_ = false;
769  sortby_ = -1;
770 }
771 
772 void menu::sort_by(int column)
773 {
774  const bool already_sorted = (column == sortby_);
775 
776  if(already_sorted) {
777  if(sortreversed_ == false) {
778  sortreversed_ = true;
779  } else {
780  sortreversed_ = false;
781  sortby_ = -1;
782  }
783  } else {
784  sortby_ = column;
785  sortreversed_ = false;
786  }
787 
788  do_sort();
789  itemRects_.clear();
790  set_dirty();
791 }
792 
793 SDL_Rect menu::style::item_size(const std::string& item) const {
794  SDL_Rect res {0,0,0,0};
795  std::vector<std::string> img_text_items = utils::split(item, IMG_TEXT_SEPARATOR, utils::REMOVE_EMPTY);
796  for (std::vector<std::string>::const_iterator it = img_text_items.begin();
797  it != img_text_items.end(); ++it) {
798  if (res.w > 0 || res.h > 0) {
799  // Not the first item, add the spacing.
800  res.w += 5;
801  }
802  const std::string str = *it;
803  if (!str.empty() && str[0] == IMAGE_PREFIX) {
804  const std::string image_name(str.begin()+1,str.end());
805  surface const img = get_item_image(image_name);
806  if(img != nullptr) {
807  res.w += img->w;
808  res.h = std::max<int>(img->h, res.h);
809  }
810  }
811  else {
812  const SDL_Rect area {0,0,10000,10000};
813  const SDL_Rect font_size =
814  font::draw_text(nullptr,area,get_font_size(),font::NORMAL_COLOR,str,0,0);
815  res.w += font_size.w;
816  res.h = std::max<int>(font_size.h, res.h);
817  }
818  }
819  return res;
820 }
821 
822 void menu::style::draw_row_bg(menu& menu_ref, const size_t /*row_index*/, const SDL_Rect& rect, ROW_TYPE type)
823 {
824  menu_ref.bg_restore(rect);
825 
826  int rgb = 0;
827  double alpha = 0.0;
828 
829  switch(type) {
830  case NORMAL_ROW:
831  rgb = normal_rgb_;
832  alpha = normal_alpha_;
833  break;
834  case SELECTED_ROW:
835  rgb = selected_rgb_;
836  alpha = selected_alpha_;
837  break;
838  case HEADING_ROW:
839  rgb = heading_rgb_;
840  alpha = heading_alpha_;
841  break;
842  }
843 
844  // FIXME: make this clearer
845  color_t c((rgb & 0xff0000) >> 16, (rgb & 0xff00) >> 8, rgb & 0xff);
846  c.a = 255 * alpha;
847 
848  sdl::fill_rectangle(rect, c);
849 }
850 
851 void menu::style::draw_row(menu& menu_ref, const size_t row_index, const SDL_Rect& rect, ROW_TYPE type)
852 {
853  if(rect.w == 0 || rect.h == 0) {
854  return;
855  }
856  draw_row_bg(menu_ref, row_index, rect, type);
857 
858  SDL_Rect minirect = rect;
859  if(type != HEADING_ROW) {
860  minirect.x += thickness_;
861  minirect.y += thickness_;
862  minirect.w -= 2*thickness_;
863  minirect.h -= 2*thickness_;
864  }
865  menu_ref.draw_row(row_index, minirect, type);
866 }
867 
868 
869 
870 void menu::column_widths_item(const std::vector<std::string>& row, std::vector<int>& widths) const
871 {
872  for(size_t col = 0; col != row.size(); ++col) {
873  const SDL_Rect res = style_->item_size(row[col]);
874  size_t text_trailing_space = (item_ends_with_image(row[col])) ? 0 : style_->get_cell_padding();
875 
876  if(col == widths.size()) {
877  widths.push_back(res.w + text_trailing_space);
878  } else if(static_cast<size_t>(res.w) > widths[col] - text_trailing_space) {
879  widths[col] = res.w + text_trailing_space;
880  }
881  }
882 }
883 
885 {
886  std::string::size_type pos = item.find_last_of(IMG_TEXT_SEPARATOR);
887  pos = (pos == std::string::npos) ? 0 : pos+1;
888  return(item.size() > pos && item.at(pos) == IMAGE_PREFIX);
889 }
890 
891 const std::vector<int>& menu::column_widths() const
892 {
893  if(column_widths_.empty()) {
895  for(size_t row = 0; row != items_.size(); ++row) {
897  }
898  }
899 
900  return column_widths_;
901 }
902 
904 {
905  SDL_Rect rect = get_item_rect(item);
906  if (rect.w == 0)
907  return;
908  bg_restore(rect);
909 }
910 
911 void menu::draw_row(const size_t row_index, const SDL_Rect& rect, ROW_TYPE type)
912 {
913  //called from style, draws one row's contents in a generic and adaptable way
914  const std::vector<std::string>& row = (type == HEADING_ROW) ? heading_ : items_[row_index].fields;
915  const SDL_Rect& area = video().screen_area();
916  const SDL_Rect& loc = inner_location();
917  const std::vector<int>& widths = column_widths();
918  bool lang_rtl = current_language_rtl();
919  int dir = (lang_rtl) ? -1 : 1;
920  SDL_Rect column = loc;
921 
922  int xpos = rect.x;
923  if(lang_rtl)
924  xpos += rect.w;
925  for(size_t i = 0; i != row.size(); ++i) {
926 
927  if(lang_rtl)
928  xpos -= widths[i];
929  if(type == HEADING_ROW) {
930  SDL_Rect draw_rect {
931  xpos,
932  rect.y,
933  widths[i],
934  rect.h
935  };
936 
937  if(highlight_heading_ == int(i)) {
938  sdl::fill_rectangle(draw_rect, {255,255,255,77});
939  } else if(sortby_ == int(i)) {
940  sdl::fill_rectangle(draw_rect, {255,255,255,26});
941  }
942  }
943 
944  const int last_x = xpos;
945  column.w = widths[i];
946  std::string str = row[i];
947  std::vector<std::string> img_text_items = utils::split(str, IMG_TEXT_SEPARATOR, utils::REMOVE_EMPTY);
948  for (std::vector<std::string>::const_iterator it = img_text_items.begin();
949  it != img_text_items.end(); ++it) {
950  str = *it;
951  if (!str.empty() && str[0] == IMAGE_PREFIX) {
952  const std::string image_name(str.begin()+1,str.end());
953  const surface img = style_->get_item_image(image_name);
954  const int remaining_width = max_width_ < 0 ? area.w :
955  std::min<int>(max_width_, ((lang_rtl)? xpos - rect.x : rect.x + rect.w - xpos));
956  if(img != nullptr && img->w <= remaining_width
957  && rect.y + img->h < area.h) {
958  const size_t y = rect.y + (rect.h - img->h)/2;
959  const size_t w = img->w + 5;
960  const size_t x = xpos + ((lang_rtl) ? widths[i] - w : 0);
961  video().blit_surface(x,y,img);
962  if(!lang_rtl)
963  xpos += w;
964  column.w -= w;
965  }
966  } else {
967  column.x = xpos;
968  const bool has_wrap = (str.find_first_of("\r\n") != std::string::npos);
969  //prevent ellipsis calculation if there is any line wrapping
970  std::string to_show = str;
971  if (use_ellipsis_ && !has_wrap)
972  {
973  int fs = style_->get_font_size();
974  int style = TTF_STYLE_NORMAL;
975  int w = rect.w - (xpos - rect.x) - 2 * style_->get_thickness();
976  std::string::const_iterator it2_beg = to_show.begin(), it2_end = to_show.end(),
977  it2 = font::parse_markup(it2_beg, it2_end, &fs, nullptr, &style);
978  if (it2 != it2_end) {
979  std::string tmp(it2, it2_end);
980  to_show.erase(it2 - it2_beg, it2_end - it2_beg);
981  to_show += font::make_text_ellipsis(tmp, fs, w, style);
982  }
983  }
984  const SDL_Rect& text_size = font::text_area(str,style_->get_font_size());
985  const size_t y = rect.y + (rect.h - text_size.h)/2;
986  const size_t padding = 2;
988  (type == HEADING_ROW ? xpos+padding : xpos), y);
989 
990  if(type == HEADING_ROW && sortby_ == int(i)) {
991  const surface sort_img = image::get_image(sortreversed_ ? "buttons/sliders/slider_arrow_blue.png" :
992  "buttons/sliders/slider_arrow_blue.png~ROTATE(180)");
993  if(sort_img != nullptr && sort_img->w <= widths[i] && sort_img->h <= rect.h) {
994  const size_t sort_x = xpos + widths[i] - sort_img->w - padding;
995  const size_t sort_y = rect.y + rect.h/2 - sort_img->h/2;
996  video().blit_surface(sort_x,sort_y,sort_img);
997  }
998  }
999 
1000  xpos += dir * (text_size.w + 5);
1001  }
1002  }
1003  if(lang_rtl)
1004  xpos = last_x;
1005  else
1006  xpos = last_x + widths[i];
1007  }
1008 }
1009 
1011 {
1012  SDL_Rect heading_rect = inner_location();
1013  heading_rect.h = heading_height();
1014  style_->draw_row(*this,0,heading_rect,HEADING_ROW);
1015 
1016  for(size_t i = 0; i != item_pos_.size(); ++i) {
1018  (!out_ && item_pos_[i] == selected_) ? SELECTED_ROW : NORMAL_ROW);
1019  }
1020 }
1021 
1023 {
1024  if(hidden()) {
1025  return;
1026  }
1027 
1028  if(!dirty()) {
1029 
1030  for(std::set<int>::const_iterator i = invalid_.begin(); i != invalid_.end(); ++i) {
1031  if(*i == -1) {
1032  SDL_Rect heading_rect = inner_location();
1033  heading_rect.h = heading_height();
1034  bg_restore(heading_rect);
1035  style_->draw_row(*this,0,heading_rect,HEADING_ROW);
1036  } else if(*i >= 0 && *i < int(item_pos_.size())) {
1037  const unsigned int pos = item_pos_[*i];
1038  const SDL_Rect& rect = get_item_rect(*i);
1039  bg_restore(rect);
1040  style_->draw_row(*this,pos,rect,
1041  (!out_ && pos == selected_) ? SELECTED_ROW : NORMAL_ROW);
1042  }
1043  }
1044 
1045  invalid_.clear();
1046  return;
1047  }
1048 
1049  invalid_.clear();
1050 
1051  bg_restore();
1052 
1053  clip_rect_setter clipping_rect =
1054  clip_rect_setter(video().getSurface(), clip_rect(), clip_rect() != nullptr);
1055 
1056  draw_contents();
1057 
1058  set_dirty(false);
1059 }
1060 
1061 int menu::hit(int x, int y) const
1062 {
1063  const SDL_Rect& loc = inner_location();
1064  if (x >= loc.x && x < loc.x + loc.w && y >= loc.y && y < loc.y + loc.h) {
1065  for(size_t i = 0; i != items_.size(); ++i) {
1066  const SDL_Rect& rect = get_item_rect(i);
1067  if (y >= rect.y && y < rect.y + rect.h)
1068  return i;
1069  }
1070  }
1071 
1072  return -1;
1073 }
1074 
1075 int menu::hit_column(int x) const
1076 {
1077  const std::vector<int>& widths = column_widths();
1078  int j = -1, j_end = widths.size();
1079  for(x -= location().x; x >= 0; x -= widths[j]) {
1080  if(++j == j_end) {
1081  return -1;
1082  }
1083  }
1084  return j;
1085 }
1086 
1087 std::pair<int,int> menu::hit_cell(int x, int y) const
1088 {
1089  const int row = hit(x, y);
1090  if(row < 0) {
1091  return std::pair<int,int>(-1, -1);
1092  }
1093 
1094  const int col = hit_column(x);
1095  if(col < 0) {
1096  return std::pair<int,int>(-1, -1);
1097  }
1098 
1099  return std::pair<int,int>(x,y);
1100 }
1101 
1102 int menu::hit_heading(int x, int y) const
1103 {
1104  const size_t height = heading_height();
1105  const SDL_Rect& loc = inner_location();
1106  if(y >= loc.y && static_cast<size_t>(y) < loc.y + height) {
1107  return hit_column(x);
1108  } else {
1109  return -1;
1110  }
1111 }
1112 
1113 SDL_Rect menu::get_item_rect(int item) const
1114 {
1115  return get_item_rect_internal(item_pos_[item]);
1116 }
1117 
1118 SDL_Rect menu::get_item_rect_internal(size_t item) const
1119 {
1120  unsigned int first_item_on_screen = get_position();
1121  if (item < first_item_on_screen ||
1122  item >= first_item_on_screen + max_items_onscreen()) {
1123  return sdl::empty_rect;
1124  }
1125 
1126  const std::map<int,SDL_Rect>::const_iterator i = itemRects_.find(item);
1127  if(i != itemRects_.end())
1128  return i->second;
1129 
1130  const SDL_Rect& loc = inner_location();
1131 
1132  int y = loc.y + heading_height();
1133  if (item != first_item_on_screen) {
1134  const SDL_Rect& prev = get_item_rect_internal(item-1);
1135  y = prev.y + prev.h;
1136  }
1137 
1138  SDL_Rect res = sdl::create_rect(loc.x, y, loc.w, get_item_height(item));
1139 
1140  const SDL_Rect& screen_area = video().screen_area();
1141 
1142  if(res.x > screen_area.w) {
1143  return sdl::empty_rect;
1144  } else if(res.x + res.w > screen_area.w) {
1145  res.w = screen_area.w - res.x;
1146  }
1147 
1148  if(res.y > screen_area.h) {
1149  return sdl::empty_rect;
1150  } else if(res.y + res.h > screen_area.h) {
1151  res.h = screen_area.h - res.y;
1152  }
1153 
1154  //only insert into the cache if the menu's co-ordinates have
1155  //been initialized
1156  if (loc.x > 0 && loc.y > 0)
1157  itemRects_.emplace(item, res);
1158 
1159  return res;
1160 }
1161 
1162 size_t menu::get_item_height_internal(const std::vector<std::string>& item) const
1163 {
1164  size_t res = 0;
1165  for(std::vector<std::string>::const_iterator i = item.begin(); i != item.end(); ++i) {
1166  SDL_Rect rect = style_->item_size(*i);
1167  res = std::max<int>(rect.h,res);
1168  }
1169 
1170  return res;
1171 }
1172 
1173 size_t menu::heading_height() const
1174 {
1175  if(heading_height_ == -1) {
1177  }
1178 
1179  return std::min<unsigned int>(heading_height_,max_height_);
1180 }
1181 
1182 size_t menu::get_item_height(int) const
1183 {
1184  if(item_height_ != -1)
1185  return size_t(item_height_);
1186 
1187  size_t max_height = 0;
1188  for(size_t n = 0; n != items_.size(); ++n) {
1189  max_height = std::max<int>(max_height,get_item_height_internal(items_[n].fields));
1190  }
1191 
1192  return item_height_ = max_height;
1193 }
1194 
1195 void menu::process_help_string(int mousex, int mousey)
1196 {
1197  if (hidden()) return;
1198 
1199  const std::pair<int,int> loc(hit(mousex,mousey), hit_column(mousex));
1200  if(loc == cur_help_) {
1201  return;
1202  } else if(loc.first == -1) {
1204  help_string_ = -1;
1205  } else {
1206  if(help_string_ != -1) {
1208  help_string_ = -1;
1209  }
1210  if(size_t(loc.first) < items_.size()) {
1211  const std::vector<std::string>& row = items_[item_pos_[loc.first]].help;
1212  if(size_t(loc.second) < row.size()) {
1213  const std::string& help = row[loc.second];
1214  if(help.empty() == false) {
1215  //std::cerr << "setting help string from menu to '" << help << "'\n";
1217  }
1218  }
1219  }
1220  }
1221 
1222  cur_help_ = loc;
1223 }
1224 
1225 void menu::invalidate_row(size_t id)
1226 {
1227  if(id >= items_.size()) {
1228  return;
1229  }
1230 
1231  invalid_.insert(int(id));
1232 }
1233 
1235 {
1236  if(pos >= items_.size()) {
1237  return;
1238  }
1239 
1240  invalidate_row(items_[pos].id);
1241 }
1242 
1244 {
1245  invalid_.insert(-1);
1246 }
1247 
1248 }
basic_sorter & set_alpha_sort(int column)
Definition: menu.cpp:48
size_t max_items_onscreen() const
Definition: menu.cpp:483
SDL_Rect text_area(const std::string &text, int size, int style)
Calculate the size of a text (in pixels) if it were to be drawn.
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:920
int hit_heading(int x, int y) const
Definition: menu.cpp:1102
void sort_by(int column)
Definition: menu.cpp:772
size_t get_font_size() const
Definition: menu_style.cpp:55
bool last_was_doubleclick_
Definition: menu.hpp:289
char const IMG_TEXT_SEPARATOR
int hit(int x, int y) const
Definition: menu.cpp:1061
virtual void draw_row_bg(menu &menu_ref, const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
Definition: menu.cpp:822
std::vector< char_t > string
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
void set_shown_size(unsigned h)
Definition: scrollarea.cpp:108
const std::vector< int > & column_widths() const
Definition: menu.cpp:891
static std::pair< int, int > parse_fraction(const std::string &s)
Definition: menu.cpp:106
void set_numeric_keypress_selection(bool value)
Definition: menu.cpp:750
void adjust_position(unsigned pos)
Definition: scrollarea.cpp:98
void invalidate_row(size_t id)
Definition: menu.cpp:1225
virtual void init()
Definition: menu.hpp:39
size_t selected_
Definition: menu.hpp:253
int max_height_
Definition: menu.hpp:233
std::string::const_iterator parse_markup(std::string::const_iterator i1, std::string::const_iterator i2, int *font_size, color_t *color, int *style)
Parses the markup-tags at the front of a string.
bool num_selects_
variable which determines whether a numeric keypress should select an item on the dialog ...
Definition: menu.hpp:284
#define a
Definition: video.hpp:31
int sortby_
Definition: menu.hpp:295
ROW_TYPE
Definition: menu.hpp:32
basic_sorter & set_position_sort(int column, const std::vector< int > &pos)
Definition: menu.cpp:88
std::vector< std::string > heading_
Definition: menu.hpp:242
CVideo & video() const
Definition: widget.hpp:84
General purpose widgets.
bool has_scrollbar() const
Definition: scrollarea.cpp:33
void do_sort()
Definition: menu.cpp:277
unsigned scrollbar_width() const
Definition: scrollarea.cpp:144
std::vector< std::string > quoted_split(const std::string &val, char c, int flags, char quote)
This function is identical to split(), except it does not split when it otherwise would if the previo...
basic_sorter & set_redirect_sort(int column, int to)
Definition: menu.cpp:79
void move_selection_up(size_t dep)
Definition: menu.cpp:537
void set_focus(bool focus)
Definition: widget.cpp:147
SELECTION_MOVE_VIEWPORT
Definition: menu.hpp:308
const std::string menu_select
bool show_result_
Definition: menu.hpp:259
size_t get_item_height(int item) const
Definition: menu.cpp:1182
virtual void erase_item(size_t index)
Definition: menu.cpp:396
bool item_ends_with_image(const std::string &item) const
Definition: menu.cpp:884
#define h
virtual bool column_sortable(int column) const =0
const std::vector< std::string > items
std::vector< int > column_widths_
Definition: menu.hpp:251
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Draws a surface directly onto the screen framebuffer.
Definition: video.cpp:162
CONSTEXPR const SDL_Rect empty_rect
Definition: rect.hpp:32
virtual void handle_event(const SDL_Event &event)
Definition: menu.cpp:644
surface get_item_image(const image::locator &i_locator) const
Definition: menu_style.cpp:65
void adjust_viewport_to_selection()
Definition: menu.cpp:509
-file util.hpp
static int xp_to_advance(const std::string &s)
Definition: menu.cpp:115
void set_sorter(sorter *s)
Definition: menu.cpp:761
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
int item_height_
Definition: menu.hpp:234
bool chars_less_insensitive(char a, char b)
Definition: general.hpp:22
char const IMAGE_PREFIX
size_t get_item_height_internal(const std::vector< std::string > &item) const
Definition: menu.cpp:1162
void update_size()
Definition: menu.cpp:342
virtual void draw_row(const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
Definition: menu.cpp:911
void set_click_selects(bool value)
Definition: menu.cpp:745
void set_measurements(int w, int h)
Definition: widget.cpp:127
bool ignore_next_doubleclick_
Definition: menu.hpp:288
virtual bool column_sortable(int column) const
Definition: menu.cpp:94
#define b
void reset_selection()
Definition: menu.cpp:572
bool sortreversed_
Definition: menu.hpp:296
bool hidden() const
Definition: widget.cpp:196
void set_full_size(unsigned h)
Definition: scrollarea.cpp:115
void set_dirty(bool dirty=true)
Definition: widget.cpp:215
char const DEFAULT_ITEM
void create_help_strings()
Definition: menu.cpp:316
void move_selection_keeping_viewport(size_t id)
Definition: menu.cpp:565
void draw_contents()
Definition: menu.cpp:1010
void set_position(unsigned pos)
Definition: scrollarea.cpp:93
bool requires_event_focus(const SDL_Event *event=nullptr) const
Definition: menu.cpp:611
char const HELP_STRING_SEPARATOR
SDL_Rect inner_location() const
Definition: scrollarea.cpp:136
const SDL_Rect * clip_rect() const
Definition: widget.cpp:102
bool current_language_rtl()
Definition: language.cpp:52
void fill_items(const std::vector< std::string > &items, bool strip_spaces)
Set new items to show.
Definition: menu.cpp:220
bool focus(const SDL_Event *event)
Definition: widget.cpp:155
bool out_
Definition: menu.hpp:255
void update_scrollbar_grip_height()
Definition: menu.cpp:336
std::pair< int, int > cur_help_
Definition: menu.hpp:248
basic_sorter & set_id_sort(int column)
Definition: menu.cpp:73
virtual bool less(int column, const item &row1, const item &row2) const
Definition: menu.cpp:125
bool dirty() const
Definition: widget.cpp:225
std::vector< std::string > fields
Definition: menu.hpp:114
int process()
Definition: menu.cpp:728
size_t get_thickness() const
Definition: menu_style.cpp:57
int max_width_
Definition: menu.hpp:233
void bg_register(const SDL_Rect &rect)
Definition: widget.cpp:107
uint8_t a
Alpha value.
Definition: color.hpp:186
void set_heading(const std::vector< std::string > &heading)
Definition: menu.cpp:425
static style & default_style
Definition: menu.hpp:101
void set_max_width(const int new_max_width)
Definition: menu.cpp:475
int highlight_heading_
Definition: menu.hpp:297
void set_inner_location(const SDL_Rect &rect)
Definition: menu.cpp:378
const color_t NORMAL_COLOR
virtual void set_items(const std::vector< std::string > &items, bool strip_spaces=true, bool keep_viewport=false)
Set new items to show and redraw/recalculate everything.
Definition: menu.cpp:436
void move_selection(size_t id)
Definition: menu.cpp:557
unsigned get_position() const
Definition: scrollarea.cpp:83
SDL_Rect get_item_rect_internal(size_t pos) const
Definition: menu.cpp:1118
void change_item(int pos1, int pos2, const std::string &str)
Definition: menu.cpp:385
void move_selection_down(size_t dep)
Definition: menu.cpp:542
#define DOUBLE_CLICK_EVENT
Definition: events.hpp:23
bool is_wml_separator(char c)
int help_string_
Definition: menu.hpp:249
bool previous_button_
Definition: menu.hpp:256
void assert_pos()
Definition: menu.cpp:307
size_t heading_height() const
Definition: menu.cpp:1173
std::vector< size_t > item_pos_
Definition: menu.hpp:240
void invalidate_heading()
Definition: menu.cpp:1243
static map_location::DIRECTION s
virtual void handle_event(const SDL_Event &event)
Definition: scrollarea.cpp:149
bool focus_
Definition: widget.hpp:97
size_t i
Definition: function.cpp:933
void scroll(unsigned int pos)
Definition: menu.cpp:755
bool use_ellipsis_
Definition: menu.hpp:292
unsigned get_max_position() const
Definition: scrollarea.cpp:88
int w
static int sort(lua_State *L)
Definition: ltablib.cpp:411
void set_selection_pos(size_t pos, bool silent=false, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT)
Definition: menu.cpp:516
bool double_clicked()
Definition: menu.cpp:738
std::set< int > invalid_
Definition: menu.hpp:314
basic_sorter & set_numeric_sort(int column)
Definition: menu.cpp:54
void column_widths_item(const std::vector< std::string > &row, std::vector< int > &widths) const
Definition: menu.cpp:870
void invalidate_row_pos(size_t pos)
Definition: menu.cpp:1234
size_t id
Definition: menu.hpp:116
std::pair< int, int > hit_cell(int x, int y) const
Definition: menu.cpp:1087
int set_help_string(const std::string &str)
Displays a help string with the given text.
Definition: video.cpp:483
std::string make_text_ellipsis(const std::string &text, int font_size, int max_width, int style)
If the text exceeds the specified max width, end it with an ellipsis (...)
Definition: sdl_ttf.cpp:443
void draw()
Definition: menu.cpp:1022
SDL_Rect get_item_rect(int item) const
Definition: menu.cpp:1113
const std::string button_press
void key_press(SDL_Keycode key)
Definition: menu.cpp:577
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an SDL_Rect with the given dimensions.
Definition: rect.hpp:40
int get_height(bool as_pixels=true) const
Returns the window renderer height in pixels or in screen coordinates.
Definition: video.cpp:321
char const HEADING_PREFIX
void move_selection_to(size_t id, bool silent=false, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT)
Definition: menu.cpp:549
Contains the SDL_Rect helper code.
void bg_restore() const
Definition: widget.cpp:249
char const COLUMN_SEPARATOR
basic_sorter & set_level_sort(int level_column, int xp_column)
Definition: menu.cpp:66
std::string del_tags(const std::string &text)
Copy string, but without tags at the beginning.
bool click_selects_
Definition: menu.hpp:254
~menu()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: menu.cpp:216
bool find(E event, F functor)
Tests whether an event handler is available.
map_location prev
Definition: astarsearch.cpp:65
std::vector< item > items_
Definition: menu.hpp:239
SDL_Rect draw_text(surface &dst, const SDL_Rect &area, int size, const color_t &color, const std::string &txt, int x, int y, bool use_tooltips, int style)
Function to draw text on a surface.
basic_sorter & set_xp_sort(int column)
Definition: menu.cpp:60
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1020
void fill_rectangle(const SDL_Rect &rect, const color_t &color)
Draws a filled rectangle.
Definition: rect.cpp:65
const sorter * sorter_
Definition: menu.hpp:294
bool mouse_locked() const
Definition: widget.cpp:70
int hit_column(int x) const
Definition: menu.cpp:1075
bool double_clicked_
Definition: menu.hpp:261
void process_help_string(int mousex, int mousey)
Definition: menu.cpp:1195
SDL_Rect screen_area(bool as_pixels=true) const
Returns the current window renderer area, either in pixels or screen coordinates. ...
Definition: video.cpp:295
void clear_item(int item)
Definition: menu.cpp:903
bool silent_
Definition: menu.hpp:215
static void reverse(lua_State *L, StkId from, StkId to)
Definition: lapi.cpp:193
Definition: help.cpp:56
int width() const
Definition: widget.cpp:132
const SDL_Rect & location() const
Definition: widget.cpp:142
int heading_height_
Definition: menu.hpp:243
menu(CVideo &video, const std::vector< std::string > &items, bool click_selects=false, int max_height=-1, int max_width=-1, const sorter *sorter_obj=nullptr, style *menu_style=nullptr, const bool auto_join=true)
Definition: menu.cpp:195
mock_char c
void recalculate_pos()
Definition: menu.cpp:298
const int font_size
static map_location::DIRECTION n
virtual void draw_row(menu &menu_ref, const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
Definition: menu.cpp:851
int max_items_
Definition: menu.hpp:234
void set_max_height(const int new_max_height)
Set a new max height for this menu.
Definition: menu.cpp:467
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
std::map< int, SDL_Rect > itemRects_
Definition: menu.hpp:269
size_t get_cell_padding() const
Definition: menu_style.cpp:56
const std::string & id() const
Definition: widget.cpp:230
int selection() const
Definition: menu.cpp:369
style * style_
Definition: menu.hpp:214
void clear_help_string(int handle)
Removes the help string with the given handle.
Definition: video.cpp:515
int height() const
Definition: widget.cpp:137
virtual SDL_Rect item_size(const std::string &item) const
Definition: menu.cpp:793