The Battle for Wesnoth  1.19.0-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  if (event.button == SDL_BUTTON_RIGHT) {
311  if (state_ == ACTIVE)
315  }
316 
317  if (event.button == SDL_BUTTON_LEFT) {
318  if (state_ == ACTIVE)
322  }
323 
324 }
325 
327  state_ = NORMAL;
328  draw_contents();
329 }
330 
331 void tristate_button::mouse_up(const SDL_MouseButtonEvent& event) {
332 
333  if (!(hit(event.x, event.y)))
334  return;
335 
336  // the user has stopped pressing the mouse left button while on the widget
337  if (event.button == SDL_BUTTON_LEFT) {
338 
339  if (state_ == TOUCHED_LEFT) {
342  //TODO
343  // palette_->draw(true);
344  pressed_ = true;
345  }
346  if (state_ == TOUCHED_BOTH_RIGHT) {
349  // palette_->select_bg_item(item_id_);
350  // palette_->draw(true);
351  pressed_ = true;
352  }
353  }
354 
355  if (event.button == SDL_BUTTON_RIGHT) {
356 
357  pressed_ = true;
359 
360  if (state_ == TOUCHED_RIGHT) {
362  // palette_->select_bg_item(item_id_);
363  // palette_->draw(true);
364  // pressed_ = true;
365  }
366  if (state_ == TOUCHED_BOTH_LEFT) {
368  // palette_->select_fg_item(item_id_);
369  // palette_->select_bg_item(item_id_);
370  // palette_->draw(true);
371  // pressed_ = true;
372  }
373  }
374 
375  if (pressed_)
377 }
378 
379 void tristate_button::handle_event(const SDL_Event& event) {
380 
382 
383  if (hidden() || !enabled())
384  return;
385 
386  STATE start_state = state_;
387 
388  if (!mouse_locked()) {
389  switch (event.type) {
390  case SDL_MOUSEBUTTONDOWN:
391  mouse_down(event.button);
392  break;
393  case SDL_MOUSEBUTTONUP:
394  mouse_up(event.button);
395  break;
396  case SDL_MOUSEMOTION:
397  mouse_motion(event.motion);
398  break;
399  default:
400  return;
401  }
402  }
403 
404  if (start_state != state_)
405  set_dirty(true);
406 }
407 
409  const bool res = pressed_;
410  pressed_ = false;
411  return res;
412 }
413 
414 }
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.
Definition: color_range.cpp:84
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:959
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1064
Contains the SDL_Rect helper code.
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition: rect.cpp:52
static lg::log_domain log_display("display")