The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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"
22#include "overlay.hpp"
23#include "filesystem.hpp"
24
26
27namespace editor {
28
29template<class Item>
31{
33 for (gui::widget& b : buttons_) {
34 h.push_back(&b);
35 }
36 return h;
37}
38
39template<class Item>
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 }
61 items.emplace_back("label", groupname, "icon", img);
62 }
63}
64
65template<class Item>
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}
82template<class Item>
85 return (items_start_ != 0);
87
88template<class Item>
90{
91 return (items_start_ + buttons_.size() < num_items());
92}
93
94template<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
106template<class Item>
107void 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);
120 }
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}
131template<class Item>
132void editor_palette<Item>::set_group(std::size_t index)
133{
134 assert(groups_.size() > index);
135 set_group(groups_[index].id);
136}
137
138template<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
151template<class Item>
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 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
188template<class Item>
189void 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
198template<class Item>
199void 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
208template<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
217template<class Item>
219{
220 return group_map_[active_group_].size();
221}
222
223
224template<class Item>
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
241template<class Item>
243{
244 return selected_fg_item_ == id;
245}
246
247template<class Item>
249{
250 return selected_bg_item_ == id;
251}
252
253template<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
328template<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
341template class editor_palette<overlay>;
342
343} // end namespace editor
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)
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.
void adjust_size(const rect &target) override
Update the size of this widget.
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
#define ERR_ED
std::vector< events::sdl_handler * > sdl_handler_vector
Definition events.hpp:219
Declarations for File-IO.
static std::string _(const char *str)
Definition gettext.hpp:100
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
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
Definition markup.hpp:110
int w
Definition pathfind.cpp:188
Stores the info about the groups in a nice format.
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
#define i
#define h
#define b