The Battle for Wesnoth  1.19.13+dev
editor_palettes.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
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 #define GETTEXT_DOMAIN "wesnoth-editor"
17 
19 
20 #include "gettext.hpp"
21 #include "serialization/markup.hpp"
22 #include "overlay.hpp"
23 #include "filesystem.hpp"
24 
26 
27 namespace editor {
28 
29 template<class Item>
31 {
33  for (gui::widget& b : buttons_) {
34  h.push_back(&b);
35  }
36  return h;
37 }
38 
39 template<class Item>
40 void editor_palette<Item>::expand_palette_groups_menu(std::vector<config>& items)
41 {
42  const std::vector<item_group>& item_groups = get_groups();
43 
44  for (std::size_t mci = 0; mci < item_groups.size(); ++mci) {
45  std::string groupname = item_groups[mci].name;
46  if (groupname.empty()) {
47  groupname = _("(Unknown Group)");
48  }
49  std::string img = item_groups[mci].icon + "_30";
50  if (mci == active_group_index()) {
51  std::string pressed_img = img + "-pressed.png";
52  if(filesystem::get_binary_file_location("images", pressed_img).has_value()) {
53  img = pressed_img;
54  } else {
55  img += ".png~CS(70,70,0)";
56  }
57  } else {
58  img += ".png";
59  }
60 
61  items.emplace_back("label", groupname, "icon", img);
62  }
63 }
64 
65 template<class Item>
67 {
68  bool scrolled = false;
69  if(can_scroll_up()) {
70  // This should only be reachable with items_start_ being a multiple of columns_, but guard against underflow anyway.
71  if(items_start_ < columns_) {
72  items_start_ = 0;
73  } else {
74  items_start_ -= columns_;
75  }
76  scrolled = true;
77  set_dirty(true);
78  }
79  return scrolled;
80 }
81 
82 template<class Item>
84 {
85  return (items_start_ != 0);
86 }
87 
88 template<class Item>
90 {
91  return (items_start_ + buttons_.size() < num_items());
92 }
93 
94 template<class Item>
96 {
97  bool scrolled = false;
98  if(can_scroll_down()) {
99  items_start_ += columns_;
100  scrolled = true;
101  set_dirty(true);
102  }
103  return scrolled;
104 }
105 
106 template<class Item>
107 void editor_palette<Item>::set_group(const std::string& id)
108 {
109  assert(!id.empty());
110 
111  bool found = false;
112  for (const item_group& group : groups_) {
113  if (group.id == id) {
114  found = true;
115  std::shared_ptr<gui::button> palette_menu_button = gui_.find_menu_button("menu-editor-terrain");
116  if (palette_menu_button) {
117  palette_menu_button->set_tooltip_string(group.name);
118  palette_menu_button->set_overlay(group.icon);
119  }
120  }
121  }
122  assert(found);
123 
124  active_group_ = id;
125 
126  if(active_group().empty()) {
127  ERR_ED << "No items found in group with the id: '" << id << "'.";
128  }
129 }
130 
131 template<class Item>
133 {
134  assert(groups_.size() > index);
135  set_group(groups_[index].id);
136 }
137 
138 template<class Item>
140 {
141  assert(!active_group_.empty());
142 
143  for (std::size_t i = 0 ; i < groups_.size(); i++) {
144  if (groups_[i].id == active_group_)
145  return i;
146  }
147 
148  return static_cast<std::size_t>(-1);
149 }
150 
151 template<class Item>
152 void editor_palette<Item>::adjust_size(const SDL_Rect& target)
153 {
154  // The number of columns is passed to the constructor, and isn't changed afterwards. It's likely to be
155  // exactly 4, but will always be a small number which makes the next cast reasonable.
156  const int items_fitting = static_cast<int>((target.h / item_space_) * columns_);
157 
158  // This might be called while the palette is not visible onscreen.
159  // If that happens, no items will fit and we'll have a negative number here.
160  // Just skip it in that case.
161  //
162  // New items can be added via the add_item function, so this creates as
163  // many buttons as can fit, even if there aren't yet enough items to need
164  // that many buttons.
165  if(items_fitting > 0) {
166  const auto buttons_needed = static_cast<std::size_t>(items_fitting);
167  if(buttons_.size() != buttons_needed) {
168  buttons_.resize(buttons_needed, gui::tristate_button(this));
169  }
170  }
171 
172  // Update button locations and sizes. Needs to be done even if the number of buttons hasn't changed,
173  // because adjust_size() also handles moving left and right when the window's width is changed.
174  SDL_Rect dstrect;
175  dstrect.w = item_size_ + 2;
176  dstrect.h = item_size_ + 2;
177  for(std::size_t i = 0; i < buttons_.size(); ++i) {
178  dstrect.x = target.x + static_cast<int>(i % columns_) * item_space_;
179  dstrect.y = target.y + static_cast<int>(i / columns_) * item_space_;
180  buttons_[i].set_location(dstrect);
181  }
182 
183  set_location(target);
184  set_dirty(true);
185  gui_.set_help_string(get_help_string());
186 }
187 
188 template<class Item>
189 void editor_palette<Item>::select_fg_item(const std::string& item_id)
190 {
191  if (selected_fg_item_ != item_id) {
192  selected_fg_item_ = item_id;
193  set_dirty();
194  }
195  gui_.set_help_string(get_help_string());
196 }
197 
198 template<class Item>
199 void editor_palette<Item>::select_bg_item(const std::string& item_id)
200 {
201  if (selected_bg_item_ != item_id) {
202  selected_bg_item_ = item_id;
203  set_dirty();
204  }
205  gui_.set_help_string(get_help_string());
206 }
207 
208 template<class Item>
210 {
211  std::swap(selected_fg_item_, selected_bg_item_);
212  select_fg_item(selected_fg_item_);
213  select_bg_item(selected_bg_item_);
214  set_dirty();
215 }
216 
217 template<class Item>
219 {
220  return group_map_[active_group_].size();
221 }
222 
223 
224 template<class Item>
225 void editor_palette<Item>::hide(bool hidden)
226 {
227  widget::hide(hidden);
228 
229  if (!hidden) {
230  gui_.set_help_string(get_help_string());
231  } else {
232  gui_.clear_help_string();
233  }
234 
235  for (gui::widget& w : buttons_) {
236  w.hide(hidden);
237  }
238 }
239 
240 
241 template<class Item>
242 bool editor_palette<Item>::is_selected_fg_item(const std::string& id)
243 {
244  return selected_fg_item_ == id;
245 }
246 
247 template<class Item>
248 bool editor_palette<Item>::is_selected_bg_item(const std::string& id)
249 {
250  return selected_bg_item_ == id;
251 }
252 
253 template<class Item>
255 {
256  if (!dirty()) {
257  return;
258  }
259 
260  toolkit_.set_mouseover_overlay(gui_);
261 
262  std::shared_ptr<gui::button> palette_menu_button = gui_.find_menu_button("menu-editor-terrain");
263  if(palette_menu_button) {
264  t_string& name = groups_[active_group_index()].name;
265  std::string& icon = groups_[active_group_index()].icon;
266 
267  palette_menu_button->set_tooltip_string(name);
268  palette_menu_button->set_overlay(icon);
269  }
270 
271  // The hotkey system will automatically enable and disable the buttons when it runs, but it doesn't
272  // get triggered when handling mouse-wheel scrolling. Therefore duplicate that functionality here.
273  std::shared_ptr<gui::button> upscroll_button = gui_.find_action_button("upscroll-button-editor");
274  if(upscroll_button)
275  upscroll_button->enable(can_scroll_up());
276  std::shared_ptr<gui::button> downscroll_button = gui_.find_action_button("downscroll-button-editor");
277  if(downscroll_button)
278  downscroll_button->enable(can_scroll_down());
279 
280  for(std::size_t i = 0; i < buttons_.size(); ++i) {
281  const auto item_index = items_start_ + i;
282  gui::tristate_button& tile = buttons_[i];
283 
284  tile.hide(true);
285 
286  // If we've scrolled to the end of the list, or if there aren't many items, leave the button hidden
287  if(item_index >= num_items()) {
288  continue;
289  }
290 
291  const std::string item_id = active_group()[item_index];
292  typename item_map::iterator item = item_map_.find(item_id);
293 
294  texture item_base, item_overlay;
295  std::stringstream tooltip_text;
296  setup_item((*item).second, item_base, item_overlay, tooltip_text);
297  bool is_core = non_core_items_.find(get_id((*item).second)) == non_core_items_.end();
298  if (!is_core) {
299  tooltip_text << " "
300  << _("(non-core)") << "\n"
301  << _("Will not work in game without extra care.");
302  tile.set_tooltip_string(markup::span_color(font::BAD_COLOR, tooltip_text.str()));
303  } else {
304  tile.set_tooltip_string(tooltip_text.str());
305  }
306 
307  tile.set_item_image(item_base, item_overlay);
308  tile.set_item_id(item_id);
309 
310  if (is_selected_bg_item(get_id(item->second))
311  && is_selected_fg_item(get_id(item->second))) {
313  } else if (is_selected_bg_item(get_id(item->second))) {
315  } else if (is_selected_fg_item(get_id(item->second))) {
317  } else {
319  }
320 
321  tile.set_dirty(true);
322  tile.hide(false);
323  }
324 
325  set_dirty(false);
326 }
327 
328 template<class Item>
330 {
331  // This is unnecessary as every GUI1 widget is a TLD.
332  //for(std::size_t i = 0; i < buttons_.size(); ++i) {
333  // gui::tristate_button& tile = buttons_[i];
334  // tile.draw();
335  //}
336 }
337 
338 // Force compilation of the following template instantiations
340 template class editor_palette<const unit_type&>;
341 template class editor_palette<overlay>;
342 
343 } // end namespace editor
void adjust_size(const SDL_Rect &target) override
Update the size of this widget.
virtual bool scroll_up() override
Scroll the editor-palette up one step if possible.
virtual void select_bg_item(const std::string &item_id) override
virtual bool is_selected_bg_item(const std::string &id)
std::size_t num_items() override
Return the number of items in the currently-active group.
virtual bool is_selected_fg_item(const std::string &id)
std::size_t active_group_index()
virtual void draw_contents() override
Called by widget::draw()
void swap() override
For tools which support fg and bg items, exchange the two items.
virtual bool scroll_down() override
Scroll the editor-palette down one step if possible.
virtual bool can_scroll_up() override
virtual void select_fg_item(const std::string &item_id) override
Select a foreground item.
virtual void layout() override
Called by draw_manager to validate layout before drawing.
virtual bool can_scroll_down() override
void hide(bool hidden) override
void expand_palette_groups_menu(std::vector< config > &items) override
Menu expanding for palette group list.
void set_group(std::size_t index) override
virtual sdl_handler_vector handler_members() override
void set_pressed(PRESSED_STATE new_pressed_state)
void set_item_id(const std::string &id)
void set_item_image(const texture &base, const texture &over=texture())
void set_dirty(bool dirty=true)
Definition: widget.cpp:180
virtual void hide(bool value=true)
Definition: widget.cpp:141
void set_tooltip_string(const std::string &str)
Definition: widget.cpp:246
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:33
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1339
#define ERR_ED
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:184
Declarations for File-IO.
std::size_t i
Definition: function.cpp:1032
int w
static std::string _(const char *str)
Definition: gettext.hpp:97
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:199
Manage the empty-palette in the editor.
Definition: action.cpp:31
utils::optional< std::string > get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type, if it exists.
const color_t BAD_COLOR
void hide()
Hides the fps report popup.
Definition: fps_report.cpp:147
std::string img(const std::string &src, const std::string &align, bool floating)
Generates a Help markup tag corresponding to an image.
Definition: markup.cpp:31
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
Definition: markup.hpp:110
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Stores the info about the groups in a nice format.
#define h
#define b