The Battle for Wesnoth  1.17.17+dev
editor_palettes.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
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 "font/text_formatting.hpp"
22 #include "tooltips.hpp"
23 #include "overlay.hpp"
24 #include "filesystem.hpp"
25 #include "units/types.hpp"
26 
28 
29 namespace editor {
30 
31 template<class Item>
33 {
35  for (gui::widget& b : buttons_) {
36  h.push_back(&b);
37  }
38  return h;
39 }
40 
41 template<class Item>
43 {
44  auto pos = items.erase(items.begin() + i);
45 
46  std::vector<config> groups;
47  const std::vector<item_group>& item_groups = get_groups();
48 
49  for (std::size_t mci = 0; mci < item_groups.size(); ++mci) {
50  std::string groupname = item_groups[mci].name;
51  if (groupname.empty()) {
52  groupname = _("(Unknown Group)");
53  }
54  std::string img = item_groups[mci].icon + "_30";
55  if (mci == active_group_index()) {
56  std::string pressed_img = img + "-pressed.png";
57  if(!filesystem::get_binary_file_location("images", pressed_img).empty()) {
58  img = pressed_img;
59  } else {
60  img += ".png~CS(70,70,0)";
61  }
62  } else {
63  img += ".png";
64  }
65 
66  groups.emplace_back(
67  "label", groupname,
68  "icon", img
69  );
70  }
71 
72  items.insert(pos, groups.begin(), groups.end());
73 }
74 
75 template<class Item>
77 {
78  bool scrolled = false;
79  if(can_scroll_up()) {
80  // This should only be reachable with items_start_ being a multiple of columns_, but guard against underflow anyway.
81  if(items_start_ < columns_) {
82  items_start_ = 0;
83  } else {
84  items_start_ -= columns_;
85  }
86  scrolled = true;
87  set_dirty(true);
88  }
89  return scrolled;
90 }
91 
92 template<class Item>
94 {
95  return (items_start_ != 0);
96 }
97 
98 template<class Item>
100 {
101  return (items_start_ + buttons_.size() < num_items());
102 }
103 
104 template<class Item>
106 {
107  bool scrolled = false;
108  if(can_scroll_down()) {
109  items_start_ += columns_;
110  scrolled = true;
111  set_dirty(true);
112  }
113  return scrolled;
114 }
115 
116 template<class Item>
117 void editor_palette<Item>::set_group(const std::string& id)
118 {
119  assert(!id.empty());
120 
121  bool found = false;
122  for (const item_group& group : groups_) {
123  if (group.id == id) {
124  found = true;
125  std::shared_ptr<gui::button> palette_menu_button = gui_.find_menu_button("menu-editor-terrain");
126  if (palette_menu_button) {
127  //palette_menu_button->set_label(group.name);
128  palette_menu_button->set_tooltip_string(group.name);
129  palette_menu_button->set_overlay(group.icon);
130  }
131  }
132  }
133  assert(found);
134 
135  active_group_ = id;
136 
137  if(active_group().empty()) {
138  ERR_ED << "No items found in group with the id: '" << id << "'.";
139  }
140 }
141 
142 template<class Item>
144 {
145  assert(groups_.size() > index);
146  set_group(groups_[index].id);
147 }
148 
149 template<class Item>
151 {
152  assert(!active_group_.empty());
153 
154  for (std::size_t i = 0 ; i < groups_.size(); i++) {
155  if (groups_[i].id == active_group_)
156  return i;
157  }
158 
159  return static_cast<std::size_t>(-1);
160 }
161 
162 template<class Item>
163 void editor_palette<Item>::adjust_size(const SDL_Rect& target)
164 {
165  // The number of columns is passed to the constructor, and isn't changed afterwards. It's likely to be
166  // exactly 4, but will always be a small number which makes the next cast reasonable.
167  const int items_fitting = static_cast<int>((target.h / item_space_) * columns_);
168 
169  // This might be called while the palette is not visible onscreen.
170  // If that happens, no items will fit and we'll have a negative number here.
171  // Just skip it in that case.
172  //
173  // New items can be added via the add_item function, so this creates as
174  // many buttons as can fit, even if there aren't yet enough items to need
175  // that many buttons.
176  if(items_fitting > 0) {
177  const auto buttons_needed = static_cast<std::size_t>(items_fitting);
178  if(buttons_.size() != buttons_needed) {
179  buttons_.resize(buttons_needed, gui::tristate_button(this));
180  }
181  }
182 
183  // Update button locations and sizes. Needs to be done even if the number of buttons hasn't changed,
184  // because adjust_size() also handles moving left and right when the window's width is changed.
185  SDL_Rect dstrect;
186  dstrect.w = item_size_ + 2;
187  dstrect.h = item_size_ + 2;
188  for(std::size_t i = 0; i < buttons_.size(); ++i) {
189  dstrect.x = target.x + static_cast<int>(i % columns_) * item_space_;
190  dstrect.y = target.y + static_cast<int>(i / columns_) * item_space_;
191  buttons_[i].set_location(dstrect);
192  }
193 
194  set_location(target);
195  set_dirty(true);
196  gui_.set_help_string(get_help_string());
197 }
198 
199 template<class Item>
200 void editor_palette<Item>::select_fg_item(const std::string& item_id)
201 {
202  if (selected_fg_item_ != item_id) {
203  selected_fg_item_ = item_id;
204  set_dirty();
205  }
206  gui_.set_help_string(get_help_string());
207 }
208 
209 template<class Item>
210 void editor_palette<Item>::select_bg_item(const std::string& item_id)
211 {
212  if (selected_bg_item_ != item_id) {
213  selected_bg_item_ = item_id;
214  set_dirty();
215  }
216  gui_.set_help_string(get_help_string());
217 }
218 
219 template<class Item>
221 {
222  std::swap(selected_fg_item_, selected_bg_item_);
223  select_fg_item(selected_fg_item_);
224  select_bg_item(selected_bg_item_);
225  set_dirty();
226 }
227 
228 template<class Item>
230 {
231  return group_map_[active_group_].size();
232 }
233 
234 
235 template<class Item>
236 void editor_palette<Item>::hide(bool hidden)
237 {
238  widget::hide(hidden);
239 
240  if (!hidden) {
241  gui_.set_help_string(get_help_string());
242  } else {
243  gui_.clear_help_string();
244  }
245 
246  for (gui::widget& w : buttons_) {
247  w.hide(hidden);
248  }
249 }
250 
251 
252 template<class Item>
253 bool editor_palette<Item>::is_selected_fg_item(const std::string& id)
254 {
255  return selected_fg_item_ == id;
256 }
257 
258 template<class Item>
259 bool editor_palette<Item>::is_selected_bg_item(const std::string& id)
260 {
261  return selected_bg_item_ == id;
262 }
263 
264 template<class Item>
266 {
267  if (!dirty()) {
268  return;
269  }
270 
271  toolkit_.set_mouseover_overlay(gui_);
272 
273  std::shared_ptr<gui::button> palette_menu_button = gui_.find_menu_button("menu-editor-terrain");
274  if(palette_menu_button) {
275  t_string& name = groups_[active_group_index()].name;
276  std::string& icon = groups_[active_group_index()].icon;
277 
278  palette_menu_button->set_tooltip_string(name);
279  palette_menu_button->set_overlay(icon);
280  }
281 
282  // The hotkey system will automatically enable and disable the buttons when it runs, but it doesn't
283  // get triggered when handling mouse-wheel scrolling. Therefore duplicate that functionality here.
284  std::shared_ptr<gui::button> upscroll_button = gui_.find_action_button("upscroll-button-editor");
285  if(upscroll_button)
286  upscroll_button->enable(can_scroll_up());
287  std::shared_ptr<gui::button> downscroll_button = gui_.find_action_button("downscroll-button-editor");
288  if(downscroll_button)
289  downscroll_button->enable(can_scroll_down());
290 
291  for(std::size_t i = 0; i < buttons_.size(); ++i) {
292  const auto item_index = items_start_ + i;
293  gui::tristate_button& tile = buttons_[i];
294 
295  tile.hide(true);
296 
297  // If we've scrolled to the end of the list, or if there aren't many items, leave the button hidden
298  if(item_index >= num_items()) {
299  continue;
300  }
301 
302  const std::string item_id = active_group()[item_index];
303  //typedef std::map<std::string, Item> item_map_wurscht;
304  typename item_map::iterator item = item_map_.find(item_id);
305 
306  texture item_base, item_overlay;
307  std::stringstream tooltip_text;
308  setup_item((*item).second, item_base, item_overlay, tooltip_text);
309 
310  bool is_core = non_core_items_.find(get_id((*item).second)) == non_core_items_.end();
311  if (!is_core) {
312  tooltip_text << " "
314  << _("(non-core)") << "\n"
315  << _("Will not work in game without extra care.")
316  << "</span>";
317  }
318 
319  tile.set_tooltip_string(tooltip_text.str());
320  tile.set_item_image(item_base, item_overlay);
321  tile.set_item_id(item_id);
322 
323 // if (get_id((*item).second) == selected_bg_item_
324 // && get_id((*item).second) == selected_fg_item_) {
325 // tile.set_pressed(gui::tristate_button::BOTH);
326 // } else if (get_id((*item).second) == selected_bg_item_) {
327 // tile.set_pressed(gui::tristate_button::RIGHT);
328 // } else if (get_id((*item).second) == selected_fg_item_) {
329 // tile.set_pressed(gui::tristate_button::LEFT);
330 // } else {
331 // tile.set_pressed(gui::tristate_button::NONE);
332 // }
333 
334  if (is_selected_bg_item(get_id(item->second))
335  && is_selected_fg_item(get_id(item->second))) {
337  } else if (is_selected_bg_item(get_id(item->second))) {
339  } else if (is_selected_fg_item(get_id(item->second))) {
341  } else {
343  }
344 
345  tile.set_dirty(true);
346  tile.hide(false);
347  }
348 
349  set_dirty(false);
350 }
351 
352 template<class Item>
354 {
355  // This is unnecessary as every GUI1 widget is a TLD.
356  //for(std::size_t i = 0; i < buttons_.size(); ++i) {
357  // gui::tristate_button& tile = buttons_[i];
358  // tile.draw();
359  //}
360 }
361 
362 // Force compilation of the following template instantiations
364 template class editor_palette<const unit_type&>;
365 template class editor_palette<overlay>;
366 
367 } // 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()
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, int i) 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:1351
#define ERR_ED
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:190
Declarations for File-IO.
std::size_t i
Definition: function.cpp:968
int w
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
Manage the empty-palette in the editor.
Definition: action.cpp:31
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 or an empty string if the file isn't prese...
const color_t BAD_COLOR
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
static void setup_item(const std::string &item, window &window)
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:414
const std::vector< std::string > items
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Stores the info about the groups in a nice format.
#define h
#define b