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