The Battle for Wesnoth  1.17.10+dev
help_menu.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2022
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "help/help_menu.hpp"
17 
18 #include "game_config.hpp" // for menu_contract, menu_expand
19 #include "help/help_impl.hpp" // for section, topic, topic_list, etc
20 #include "sound.hpp" // for play_UI_sound
21 #include "wml_separators.hpp" // for IMG_TEXT_SEPARATOR, etc
22 #include "sdl/input.hpp" // for get_mouse_state
23 
24 #include <algorithm> // for find
25 #include <list> // for _List_const_iterator, etc
26 #include <utility> // for pair
27 #include <SDL2/SDL.h>
28 
29 namespace help {
30 
31 help_menu::help_menu(const section& toplevel, int max_height) :
32  gui::menu(empty_string_vector, true, max_height, -1, nullptr, &gui::menu::bluebg_style),
33  visible_items_(),
34  toplevel_(toplevel),
35  expanded_(),
36  chosen_topic_(nullptr),
37  selected_item_(&toplevel, "", 0)
38 {
39  silent_ = true; //silence the default menu sounds
42  if (!visible_items_.empty())
44 }
45 
46 bool help_menu::expanded(const section &sec)
47 {
48  return expanded_.find(&sec) != expanded_.end();
49 }
50 
51 void help_menu::expand(const section &sec)
52 {
53  if (sec.id != "toplevel" && expanded_.insert(&sec).second) {
55  }
56 }
57 
58 void help_menu::contract(const section &sec)
59 {
60  if (expanded_.erase(&sec)) {
62  }
63 }
64 
65 void help_menu::update_visible_items(const section &sec, unsigned level)
66 {
67  if (level == 0) {
68  // Clear if this is the top level, otherwise append items.
69  visible_items_.clear();
70  }
71  for (const auto &s : sec.sections) {
72  if (is_visible_id(s.id)) {
73  const std::string vis_string = get_string_to_show(s, level + 1);
74  visible_items_.emplace_back(&s, vis_string, level + 1);
75  if (expanded(s)) {
76  update_visible_items(s, level + 1);
77  }
78  }
79  }
80  for (const auto &t : sec.topics) {
81  if (is_visible_id(t.id)) {
82  const std::string vis_string = get_string_to_show(t, level + 1);
83  visible_items_.emplace_back(&t, vis_string, level + 1);
84  }
85  }
86 }
87 
88 std::string help_menu::indent_list(const std::string& icon, const unsigned level) {
89  std::stringstream to_show;
90  for (unsigned i = 1; i < level; ++i) {
91  to_show << " "; // Indent 4 spaces
92  }
93 
94  to_show << IMG_TEXT_SEPARATOR << IMAGE_PREFIX << icon;
95  return to_show.str();
96 }
97 
98 std::string help_menu::get_string_to_show(const section &sec, const unsigned level)
99 {
100  std::stringstream to_show;
101  to_show << indent_list(expanded(sec) ? open_section_img : closed_section_img, level)
102  << IMG_TEXT_SEPARATOR << sec.title;
103  return to_show.str();
104 }
105 
106 std::string help_menu::get_string_to_show(const topic &topic, const unsigned level)
107 {
108  std::stringstream to_show;
109  to_show << indent_list(topic_img, level)
110  << IMG_TEXT_SEPARATOR << topic.title;
111  return to_show.str();
112 }
113 
115 {
116  topic_list::const_iterator tit =
117  std::find(sec.topics.begin(), sec.topics.end(), t);
118  if (tit != sec.topics.end()) {
119  // topic starting with ".." are assumed as rooted in the parent section
120  // and so only expand the parent when selected
121  if (t.id.size()<2 || t.id[0] != '.' || t.id[1] != '.')
122  expand(sec);
123  return true;
124  }
125  for (const auto &s : sec.sections) {
126  if (select_topic_internal(t, s)) {
127  expand(sec);
128  return true;
129  }
130  }
131  return false;
132 }
133 
135 {
136  if (selected_item_ == t) {
137  // The requested topic is already selected.
138  return;
139  }
142  for (std::vector<visible_item>::const_iterator it = visible_items_.begin();
143  it != visible_items_.end(); ++it) {
144  if (*it == t) {
145  selected_item_ = *it;
146  break;
147  }
148  }
150  }
151 }
152 
154 {
155  int res = menu::process();
156  int mousex, mousey;
157  sdl::get_mouse_state(&mousex, &mousey);
158 
159  if (!visible_items_.empty()
160  && static_cast<std::size_t>(res) < visible_items_.size())
161  {
163  const section* sec = selected_item_.sec;
164  if (sec != nullptr) {
165  // Behavior of the UI, for section headings:
166  // * user single-clicks on the text part: show the ".." topic in the right-hand pane
167  // * user single-clicks on the icon (or to the left of it): expand or collapse the tree view
168  // * user double-clicks anywhere: expand or collapse the tree view
169  // * note: the first click of the double-click has the single-click effect too
170  int x = mousex - menu::location().x;
171 
172  const std::string icon_img = expanded(*sec) ? open_section_img : closed_section_img;
173  // the "thickness" is the width of the left border
174  int text_start = style_->item_size(indent_list(icon_img, selected_item_.level)).w - style_->get_thickness();
175 
176  // NOTE: if you want to forbid click to the left of the icon
177  // also check x >= text_start-image_width(icon_img)
178  if (menu::double_clicked() || x < text_start) {
179  // Open or close a section if we double-click on it
180  // or do simple click on the icon.
181  expanded(*sec) ? contract(*sec) : expand(*sec);
184  } else if (x >= text_start){
185  // click on title open the topic associated to this section
187  }
188  } else if (selected_item_.t != nullptr) {
189  // Choose a topic if it is clicked.
191  }
192  }
193  return res;
194 }
195 
197 {
198  const topic *ret = chosen_topic_;
199  chosen_topic_ = nullptr;
200  return ret;
201 }
202 
204 {
205  std::vector<std::string> menu_items;
206  for(std::vector<visible_item>::const_iterator items_it = visible_items_.begin(),
207  end = visible_items_.end(); items_it != end; ++items_it) {
208  std::string to_show = items_it->visible_string;
209  if (selected_item_ == *items_it)
210  to_show = std::string("*") + to_show;
211  menu_items.push_back(to_show);
212  }
213  set_items(menu_items, false, true);
214 }
215 
216 help_menu::visible_item::visible_item(const section *_sec, const std::string &vis_string, int lev) :
217  t(nullptr), sec(_sec), visible_string(vis_string), level(lev) {}
218 
219 help_menu::visible_item::visible_item(const topic *_t, const std::string &vis_string, int lev) :
220  t(_t), sec(nullptr), visible_string(vis_string), level(lev) {}
221 
223 {
224  return sec != nullptr && *sec == _sec;
225 }
226 
228 {
229  return t != nullptr && *t == _t;
230 }
231 
233 {
234  return t == vis_item.t && sec == vis_item.sec;
235 }
236 
237 } // end namespace help
void contract(const section &sec)
Contract (close) a section.
Definition: help_menu.cpp:58
std::string get_string_to_show(const section &sec, const unsigned level)
Return the string to use as the menu-string for sections at the specified level.
Definition: help_menu.cpp:98
char const IMG_TEXT_SEPARATOR
std::string id
Definition: help_impl.hpp:164
const std::string open_section_img
Definition: help_impl.cpp:84
const std::string topic_img
Definition: help_impl.cpp:82
visible_item selected_item_
Definition: help_menu.hpp:105
topic const * chosen_topic_
Definition: help_menu.hpp:104
const std::string closed_section_img
Definition: help_impl.cpp:83
A section contains topics and sections along with title and ID.
Definition: help_impl.hpp:146
const std::string menu_expand
General purpose widgets.
virtual SDL_Rect item_size(const std::string &item) const
Definition: menu.cpp:752
std::vector< std::string > empty_string_vector
Definition: help_impl.cpp:75
bool expanded(const section &sec)
Return true if the section is expanded.
Definition: help_menu.cpp:46
visible_item(const section *_sec, const std::string &visible_string, int level)
Definition: help_menu.cpp:216
bool is_visible_id(const std::string &id)
Definition: help_impl.cpp:1516
Contains functions for cleanly handling SDL input.
char const IMAGE_PREFIX
section_list sections
Definition: help_impl.hpp:166
std::string id
Definition: help_impl.hpp:137
void process(int mousex, int mousey)
Definition: tooltips.cpp:278
void display_visible_items()
Draw the currently visible items.
Definition: help_menu.cpp:203
std::string title
Definition: help_impl.hpp:164
const section & toplevel_
Definition: help_menu.hpp:102
std::string indent_list(const std::string &icon, const unsigned level)
Return the string to use as the prefix for the icon part of the menu-string at the specified level...
Definition: help_menu.cpp:88
help_menu(const section &toplevel, int max_height=-1)
Definition: help_menu.cpp:31
Information about an item that is visible in the menu.
Definition: help_menu.hpp:53
void expand(const section &sec)
Mark a section as expanded.
Definition: help_menu.cpp:51
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:391
std::set< const section * > expanded_
Definition: help_menu.hpp:103
std::size_t i
Definition: function.cpp:967
static map_location::DIRECTION s
std::vector< visible_item > visible_items_
Definition: help_menu.hpp:101
const std::string menu_contract
bool operator==(const visible_item &vis_item) const
Definition: help_menu.cpp:232
void select_topic(const topic &t)
Make the topic the currently selected one, and expand all sections that need to be expanded to show i...
Definition: help_menu.cpp:134
double t
Definition: astarsearch.cpp:65
const topic * find_topic(const section &sec, const std::string &id)
Search for the topic with the specified identifier in the section and its subsections.
Definition: help_impl.cpp:1213
uint32_t get_mouse_state(int *x, int *y)
A wrapper for SDL_GetMouseState that gives coordinates in draw space.
Definition: input.cpp:27
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1066
A topic contains a title, an id and some text.
Definition: help_impl.hpp:112
void update_visible_items(const section &top_level, unsigned starting_level=0)
Regenerate what items are visible by checking what sections are expanded.
Definition: help_menu.cpp:65
const topic * chosen_topic()
If a topic has been chosen, return that topic, otherwise nullptr.
Definition: help_menu.cpp:196
bool silent_
Definition: menu.hpp:210
Definition: help.cpp:57
std::string title
Definition: help_impl.hpp:137
bool select_topic_internal(const topic &t, const section &sec)
Internal recursive thingie.
Definition: help_menu.cpp:114
topic_list topics
Definition: help_impl.hpp:165
style * style_
Definition: menu.hpp:209
std::size_t get_thickness() const
Definition: menu_style.cpp:58
help::section default_toplevel
Definition: help_impl.cpp:67