The Battle for Wesnoth  1.19.5+dev
tristate_button.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2024
3  by Fabian Mueller <fabianmueller5@gmx.de>
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-lib"
17 
19 
20 #include "draw.hpp"
21 #include "game_config.hpp"
22 #include "picture.hpp"
23 #include "log.hpp"
24 #include "sdl/rect.hpp"
25 #include "sound.hpp"
26 
27 static lg::log_domain log_display("display");
28 #define ERR_DP LOG_STREAM(err, log_display)
29 
30 namespace gui {
31 
34  std::string button_image_name,
35  const bool auto_join)
36  : widget(auto_join)
37  , baseImage_()
38  , touchedBaseImage_()
39  , activeBaseImage_()
40  , itemBaseImage_()
41  , itemOverlayImage_()
42  , pressedDownImage_()
43  , pressedUpImage_()
44  , pressedBothImage_()
45  , pressedBothActiveImage_()
46  , pressedDownActiveImage_()
47  , pressedUpActiveImage_()
48  , touchedDownImage_()
49  , touchedUpImage_()
50  , touchedBothImage_()
51  , textRect_()
52  , state_(NORMAL)
53  , pressed_(false)
54  , base_height_(0)
55  , base_width_(0)
56  , palette_(palette)
57  , item_id_()
58 {
59  // TODO: highdpi - there seem to be higher-quality 74-px versions of these available, but as that's not actually an integer multiple of 38... this is not a straight swap
60 
61  if (button_image_name.empty()) {
62  button_image_name = "buttons/button_selectable/button_selectable_38_";
63  }
64 
65  baseImage_ =
66  image::get_texture(button_image_name + "base.png");
68  image::get_texture(button_image_name + "base-active.png");
70  image::get_texture(button_image_name + "base-touched.png");
71 
73  image::get_texture(button_image_name + "border-touched-both.png");
75  image::get_texture(button_image_name + "border-touched-up.png");
77  image::get_texture(button_image_name + "border-touched-down.png");
78 
80  image::get_texture(button_image_name + "border-pressed-up.png");
82  image::get_texture(button_image_name + "border-pressed-down.png");
84  image::get_texture(button_image_name + "border-pressed-both.png");
85 
87  image::get_texture(button_image_name + "border-active-pressed-up.png");
89  image::get_texture(button_image_name + "border-active-pressed-down.png");
91  image::get_texture(button_image_name + "border-active-pressed-both.png");
92 
93  //TODO
94 // if (button_image.null()) {
95 // ERR_DP<< "error initializing button!";
96 // throw error();
97 // }
98 
99  // TODO: highdpi - set this some better way
102 
103 }
104 
106 
108 
109  STATE new_state = state_;
110 
111  switch (new_pressed_state) {
112  case LEFT:
114  break;
115  case RIGHT:
117  break;
118  case BOTH:
120  break;
121  case NONE:
122  new_state = NORMAL;
123  }
124 
125  if (state_ != new_state) {
126  state_ = new_state;
127  set_dirty();
128  }
129 }
130 
131 //TODO
132 //void tristate_button::set_active(bool active) {
133 // if ((state_ == NORMAL) && active) {
134 // state_ = ACTIVE;
135 // set_dirty();
136 // } else if ((state_ == ACTIVE) && !active) {
137 // state_ = NORMAL;
138 // set_dirty();
139 // }
140 //}
141 
143 
144  switch (state_) {
145  case PRESSED_LEFT:
146  case PRESSED_ACTIVE_LEFT:
147  case TOUCHED_BOTH_LEFT:
148  return LEFT;
149  case PRESSED_RIGHT:
151  case TOUCHED_BOTH_RIGHT:
152  return RIGHT;
153  case PRESSED_BOTH:
154  case PRESSED_ACTIVE_BOTH:
155  return BOTH;
156  default:
157  return NONE;
158  }
159 }
160 
161 void tristate_button::enable(bool new_val) {
162  if (new_val != enabled()) {
163  pressed_ = false;
164  // check buttons should keep their state
165  state_ = NORMAL;
166 
167  widget::enable(new_val);
168  }
169 }
170 
172 {
174  texture base = baseImage_;
175 
176  switch (state_) {
177 
178  case UNINIT:
179  return;
180 
181  case NORMAL:
182  break;
183 
184  case TOUCHED_LEFT:
186  base = touchedBaseImage_;
187  break;
188  case TOUCHED_RIGHT:
190  base = touchedBaseImage_;
191  break;
192  case TOUCHED_BOTH_LEFT:
193  case TOUCHED_BOTH_RIGHT:
195  base = touchedBaseImage_;
196  break;
197  case ACTIVE:
198  // overlay = activeImage_;
199  base = activeBaseImage_;
200  break;
201  case PRESSED_LEFT:
202  base = activeBaseImage_;
204  break;
205  case PRESSED_RIGHT:
206  base = activeBaseImage_;
208  break;
209  case PRESSED_BOTH:
210  base = activeBaseImage_;
212  break;
213  case PRESSED_ACTIVE_LEFT:
215  base = activeBaseImage_;
216  break;
217  case PRESSED_ACTIVE_BOTH:
219  base = activeBaseImage_;
220  break;
223  base = activeBaseImage_;
224  break;
225  }
226 
227  // Draw the button base.
228  const SDL_Rect& loc = location();
229  draw::blit(base, loc);
230 
231  // Draw the item.
232  // TODO: don't hardcode an implicit reliance on 38 pixel buttons
233  SDL_Rect magic{loc.x + 1, loc.y + 1, 36, 36};
234  draw::blit(itemBaseImage_, magic);
235  if (itemOverlayImage_) {
237  }
238 
239  // Draw the button overlay, if any.
240  if (overlay) {
241  draw::blit(overlay, loc);
242  }
243 }
244 
245 //TODO move to widget
246 bool tristate_button::hit(int x, int y) const {
247  return location().contains(x, y);
248 }
249 
250 void tristate_button::mouse_motion(const SDL_MouseMotionEvent& event) {
251 
252  if (hit(event.x, event.y))
253  { // the cursor is over the widget
254 
255  switch (state_) {
256 
257  case UNINIT:
258  return;
259 
260  case NORMAL:
261  state_ = ACTIVE;
262  break;
263  case PRESSED_LEFT:
265  break;
266  case PRESSED_RIGHT:
268  break;
269  case PRESSED_BOTH:
271  break;
272  default:
273  //assert(false);
274  break;
275  }
276 
277  } else { // the cursor is not over the widget
278 
279  switch (state_) {
280 
281  case ACTIVE:
282  state_ = NORMAL;
283  break;
284  case TOUCHED_LEFT:
285  case TOUCHED_RIGHT:
286  state_ = ACTIVE;
287  break;
289  case TOUCHED_BOTH_RIGHT:
291  break;
292  case PRESSED_ACTIVE_BOTH:
294  break;
295  case PRESSED_ACTIVE_LEFT:
296  case TOUCHED_BOTH_LEFT:
298  break;
299  default:
300  break;
301  }
302  }
303 }
304 
305 void tristate_button::mouse_down(const SDL_MouseButtonEvent& event) {
306 
307  if (!hit(event.x, event.y))
308  return;
309 
310  //The widget is expected to be in one of the "active" states when the mouse cursor is hovering over it, but that currently doesn't happen if the widget is moved under the cursor by scrolling the palette.
311  if (event.button == SDL_BUTTON_RIGHT) {
312  if (state_ == ACTIVE || state_ == NORMAL)
316  }
317 
318  if (event.button == SDL_BUTTON_LEFT) {
319  if (state_ == ACTIVE || state_ == NORMAL)
323  }
324 
325 }
326 
328  state_ = NORMAL;
329  draw_contents();
330 }
331 
332 void tristate_button::mouse_up(const SDL_MouseButtonEvent& event) {
333 
334  if (!(hit(event.x, event.y)))
335  return;
336 
337  // the user has stopped pressing the mouse left button while on the widget
338  if (event.button == SDL_BUTTON_LEFT) {
339 
340  if (state_ == TOUCHED_LEFT) {
343  //TODO
344  // palette_->draw(true);
345  pressed_ = true;
346  }
347  if (state_ == TOUCHED_BOTH_RIGHT) {
350  // palette_->select_bg_item(item_id_);
351  // palette_->draw(true);
352  pressed_ = true;
353  }
354  }
355 
356  if (event.button == SDL_BUTTON_RIGHT) {
357 
358  pressed_ = true;
360 
361  if (state_ == TOUCHED_RIGHT) {
363  // palette_->select_bg_item(item_id_);
364  // palette_->draw(true);
365  // pressed_ = true;
366  }
367  if (state_ == TOUCHED_BOTH_LEFT) {
369  // palette_->select_fg_item(item_id_);
370  // palette_->select_bg_item(item_id_);
371  // palette_->draw(true);
372  // pressed_ = true;
373  }
374  }
375 
376  if (pressed_)
378 }
379 
380 void tristate_button::handle_event(const SDL_Event& event) {
381 
383 
384  if (hidden() || !enabled())
385  return;
386 
387  STATE start_state = state_;
388 
389  if (!mouse_locked()) {
390  switch (event.type) {
391  case SDL_MOUSEBUTTONDOWN:
392  mouse_down(event.button);
393  break;
394  case SDL_MOUSEBUTTONUP:
395  mouse_up(event.button);
396  break;
397  case SDL_MOUSEMOTION:
398  mouse_motion(event.motion);
399  break;
400  default:
401  return;
402  }
403  }
404 
405  if (start_state != state_)
406  set_dirty(true);
407 }
408 
410  const bool res = pressed_;
411  pressed_ = false;
412  return res;
413 }
414 
415 }
virtual void select_bg_item(const std::string &item_id)=0
virtual void select_fg_item(const std::string &item_id)=0
virtual void draw_contents() override
bool hit(int x, int y) const
void set_pressed(PRESSED_STATE new_pressed_state)
virtual ~tristate_button()
Default implementation, but defined out-of-line for efficiency reasons.
editor::tristate_palette * palette_
virtual void handle_event(const SDL_Event &event) override
virtual void mouse_up(const SDL_MouseButtonEvent &event)
PRESSED_STATE pressed_state() const
virtual void enable(bool new_val=true) override
virtual void mouse_down(const SDL_MouseButtonEvent &event)
virtual void mouse_motion(const SDL_MouseMotionEvent &event)
tristate_button(editor::tristate_palette *palette, std::string button_image="", const bool auto_join=true)
virtual void enable(bool new_val=true)
Definition: widget.cpp:167
void set_dirty(bool dirty=true)
Definition: widget.cpp:180
virtual void handle_event(const SDL_Event &) override
Definition: widget.hpp:90
const rect & location() const
Definition: widget.cpp:123
bool enabled() const
Definition: widget.cpp:175
bool hidden() const
Definition: widget.cpp:161
bool mouse_locked() const
Definition: widget.cpp:64
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:33
int w() const
The draw-space width of the texture, in pixels.
Definition: texture.hpp:105
int h() const
The draw-space height of the texture, in pixels.
Definition: texture.hpp:114
std::vector< color_t > palette(const color_range &cr)
Creates a reference color palette from a color range.
Drawing functions, for drawing things on the screen.
Standard logging facilities (interface).
@ NORMAL
Definition: cursor.hpp:28
void blit(const texture &tex, const SDL_Rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition: draw.cpp:310
const std::string checkbox_release
General purpose widgets.
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
Definition: picture.cpp:920
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1077
Contains the SDL_Rect helper code.
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition: rect.cpp:53
static lg::log_domain log_display("display")