The Battle for Wesnoth  1.17.0-dev
tristate_button.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2021
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 "game_config.hpp"
21 #include "picture.hpp"
22 #include "log.hpp"
23 #include "sdl/rect.hpp"
24 #include "sound.hpp"
25 #include "video.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(video, auto_join),
37  baseImage_(nullptr), touchedBaseImage_(nullptr), activeBaseImage_(nullptr),
38  itemImage_(nullptr),
39  pressedDownImage_(nullptr), pressedUpImage_(nullptr), pressedBothImage_(nullptr),
40  pressedBothActiveImage_(nullptr), pressedDownActiveImage_(nullptr), pressedUpActiveImage_(nullptr),
41  touchedDownImage_(nullptr), touchedUpImage_(nullptr), touchedBothImage_(nullptr),
42  textRect_(),
43  state_(NORMAL), pressed_(false),
44  base_height_(0), base_width_(0),
45  palette_(palette), item_id_()
46 {
47 
48  if (button_image_name.empty()) {
49  button_image_name = "buttons/button_selectable/button_selectable_38_";
50  }
51 
52  baseImage_ =
53  image::get_image(button_image_name + "base.png");
55  image::get_image(button_image_name + "base-active.png");
57  image::get_image(button_image_name + "base-touched.png");
58 
60  image::get_image(button_image_name + "border-touched-both.png");
62  image::get_image(button_image_name + "border-touched-up.png");
64  image::get_image(button_image_name + "border-touched-down.png");
65 
67  image::get_image(button_image_name + "border-pressed-up.png");
69  image::get_image(button_image_name + "border-pressed-down.png");
71  image::get_image(button_image_name + "border-pressed-both.png");
72 
74  image::get_image(button_image_name + "border-active-pressed-up.png");
76  image::get_image(button_image_name + "border-active-pressed-down.png");
78  image::get_image(button_image_name + "border-active-pressed-both.png");
79 
80  //TODO
81 // if (button_image.null()) {
82 // ERR_DP<< "error initializing button!" << std::endl;
83 // throw error();
84 // }
85 
88 
89 }
90 
92 
94 
95  STATE new_state = state_;
96 
97  switch (new_pressed_state) {
98  case LEFT:
100  break;
101  case RIGHT:
103  break;
104  case BOTH:
106  break;
107  case NONE:
108  new_state = NORMAL;
109  }
110 
111  if (state_ != new_state) {
112  state_ = new_state;
113  set_dirty();
114  }
115 }
116 
117 //TODO
118 //void tristate_button::set_active(bool active) {
119 // if ((state_ == NORMAL) && active) {
120 // state_ = ACTIVE;
121 // set_dirty();
122 // } else if ((state_ == ACTIVE) && !active) {
123 // state_ = NORMAL;
124 // set_dirty();
125 // }
126 //}
127 
129 
130  switch (state_) {
131  case PRESSED_LEFT:
132  case PRESSED_ACTIVE_LEFT:
133  case TOUCHED_BOTH_LEFT:
134  return LEFT;
135  case PRESSED_RIGHT:
137  case TOUCHED_BOTH_RIGHT:
138  return RIGHT;
139  case PRESSED_BOTH:
140  case PRESSED_ACTIVE_BOTH:
141  return BOTH;
142  default:
143  return NONE;
144  }
145 }
146 
147 void tristate_button::enable(bool new_val) {
148  if (new_val != enabled()) {
149  pressed_ = false;
150  // check buttons should keep their state
151  state_ = NORMAL;
152 
153  widget::enable(new_val);
154  }
155 }
156 
158 
159  surface image(nullptr);
160 
161  surface overlay(nullptr);
162  surface base = baseImage_;
163 
164  switch (state_) {
165 
166  case UNINIT:
167  return;
168 
169  case NORMAL:
170  break;
171 
172  case TOUCHED_LEFT:
173  overlay = touchedUpImage_;
174  base = touchedBaseImage_;
175  break;
176  case TOUCHED_RIGHT:
177  overlay = touchedDownImage_;
178  base = touchedBaseImage_;
179  break;
180  case TOUCHED_BOTH_LEFT:
181  case TOUCHED_BOTH_RIGHT:
182  overlay = touchedBothImage_;
183  base = touchedBaseImage_;
184  break;
185  case ACTIVE:
186  // overlay = activeImage_;
187  base = activeBaseImage_;
188  break;
189  case PRESSED_LEFT:
190  base = activeBaseImage_;
191  overlay = pressedUpImage_;
192  break;
193  case PRESSED_RIGHT:
194  base = activeBaseImage_;
195  overlay = pressedDownImage_;
196  break;
197  case PRESSED_BOTH:
198  base = activeBaseImage_;
199  overlay = pressedBothImage_;
200  break;
201  case PRESSED_ACTIVE_LEFT:
202  overlay = pressedUpActiveImage_;
203  base = activeBaseImage_;
204  break;
205  case PRESSED_ACTIVE_BOTH:
206  overlay = pressedBothActiveImage_;
207  base = activeBaseImage_;
208  break;
210  overlay = pressedDownActiveImage_;
211  base = activeBaseImage_;
212  break;
213  }
214 
215  image = base;
216 
217  const SDL_Rect& loc = location();
218 
219  surface scaled_item = scale_surface(itemImage_, 36, 36);
220 
221  surface nbase = base.clone();
222 
223  //TODO avoid magic numbers
224  SDL_Rect r {1, 1, 0, 0};
225  sdl_blit(scaled_item, nullptr, nbase, &r);
226 
227  if (overlay) {
228  sdl_blit(overlay, nullptr, nbase, nullptr);
229  }
230 
231  bg_restore();
232 
233  image = nbase;
234  video().blit_surface(loc.x, loc.y, image);
235 }
236 
237 //TODO move to widget
238 bool tristate_button::hit(int x, int y) const {
239  return sdl::point_in_rect(x, y, location());
240 }
241 
242 void tristate_button::mouse_motion(const SDL_MouseMotionEvent& event) {
243 
244  if (hit(event.x, event.y))
245  { // the cursor is over the widget
246 
247  switch (state_) {
248 
249  case UNINIT:
250  return;
251 
252  case NORMAL:
253  state_ = ACTIVE;
254  break;
255  case PRESSED_LEFT:
257  break;
258  case PRESSED_RIGHT:
260  break;
261  case PRESSED_BOTH:
263  break;
264  default:
265  //assert(false);
266  break;
267  }
268 
269  } else { // the cursor is not over the widget
270 
271  switch (state_) {
272 
273  case ACTIVE:
274  state_ = NORMAL;
275  break;
276  case TOUCHED_LEFT:
277  case TOUCHED_RIGHT:
278  state_ = ACTIVE;
279  break;
281  case TOUCHED_BOTH_RIGHT:
283  break;
284  case PRESSED_ACTIVE_BOTH:
286  break;
287  case PRESSED_ACTIVE_LEFT:
288  case TOUCHED_BOTH_LEFT:
290  break;
291  default:
292  break;
293  }
294  }
295 }
296 
297 void tristate_button::mouse_down(const SDL_MouseButtonEvent& event) {
298 
299  if (!hit(event.x, event.y))
300  return;
301 
302  if (event.button == SDL_BUTTON_RIGHT) {
303  if (state_ == ACTIVE)
307  }
308 
309  if (event.button == SDL_BUTTON_LEFT) {
310  if (state_ == ACTIVE)
314  }
315 
316 }
317 
319  state_ = NORMAL;
320  draw_contents();
321 }
322 
323 void tristate_button::mouse_up(const SDL_MouseButtonEvent& event) {
324 
325  if (!(hit(event.x, event.y)))
326  return;
327 
328  // the user has stopped pressing the mouse left button while on the widget
329  if (event.button == SDL_BUTTON_LEFT) {
330 
331  if (state_ == TOUCHED_LEFT) {
334  //TODO
335  // palette_->draw(true);
336  pressed_ = true;
337  }
338  if (state_ == TOUCHED_BOTH_RIGHT) {
341  // palette_->select_bg_item(item_id_);
342  // palette_->draw(true);
343  pressed_ = true;
344  }
345  }
346 
347  if (event.button == SDL_BUTTON_RIGHT) {
348 
349  pressed_ = true;
351 
352  if (state_ == TOUCHED_RIGHT) {
354  // palette_->select_bg_item(item_id_);
355  // palette_->draw(true);
356  // pressed_ = true;
357  }
358  if (state_ == TOUCHED_BOTH_LEFT) {
360  // palette_->select_fg_item(item_id_);
361  // palette_->select_bg_item(item_id_);
362  // palette_->draw(true);
363  // pressed_ = true;
364  }
365  }
366 
367  if (pressed_)
369 }
370 
371 void tristate_button::handle_event(const SDL_Event& event) {
372 
374 
375  if (hidden() || !enabled())
376  return;
377 
378  STATE start_state = state_;
379 
380  if (!mouse_locked()) {
381  switch (event.type) {
382  case SDL_MOUSEBUTTONDOWN:
383  mouse_down(event.button);
384  break;
385  case SDL_MOUSEBUTTONUP:
386  mouse_up(event.button);
387  break;
388  case SDL_MOUSEMOTION:
389  mouse_motion(event.motion);
390  break;
391  default:
392  return;
393  }
394  }
395 
396  if (start_state != state_)
397  set_dirty(true);
398 }
399 
401  const bool res = pressed_;
402  pressed_ = false;
403  return res;
404 }
405 
406 }
surface get_image(const image::locator &i_locator, TYPE type)
Caches and returns an image.
Definition: picture.cpp:816
virtual void mouse_up(const SDL_MouseButtonEvent &event)
bool enabled() const
Definition: widget.cpp:202
virtual void mouse_down(const SDL_MouseButtonEvent &event)
virtual void enable(bool new_val=true)
Definition: widget.cpp:194
bool hidden() const
Definition: widget.cpp:188
virtual void select_bg_item(const std::string &item_id)=0
std::vector< color_t > palette(const color_range &cr)
Creates a reference color palette from a color range.
Definition: color_range.cpp:84
virtual ~tristate_button()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: video.hpp:32
General purpose widgets.
surface scale_surface(const surface &surf, int w, int h)
Scale a surface using alpha-weighted modified bilinear filtering Note: causes artifacts with alpha gr...
Definition: utils.cpp:197
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Draws a surface directly onto the screen framebuffer.
Definition: video.cpp:163
surface clone() const
Makes a copy of this surface.
Definition: surface.cpp:63
editor::tristate_palette * palette_
virtual void select_fg_item(const std::string &item_id)=0
void set_pressed(PRESSED_STATE new_pressed_state)
void set_dirty(bool dirty=true)
Definition: widget.cpp:207
virtual void handle_event(const SDL_Event &)
Definition: widget.cpp:330
const SDL_Rect & location() const
Definition: widget.cpp:134
virtual void handle_event(const SDL_Event &event) override
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:23
virtual void mouse_motion(const SDL_MouseMotionEvent &event)
virtual void draw_contents() override
void bg_restore() const
Definition: widget.cpp:241
bool mouse_locked() const
Definition: widget.cpp:62
CVideo & video() const
Definition: widget.hpp:84
Contains the SDL_Rect helper code.
Functions to load and save images from/to disk.
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1052
Standard logging facilities (interface).
PRESSED_STATE pressed_state() const
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:32
virtual void enable(bool new_val=true) override
static lg::log_domain log_display("display")
tristate_button(CVideo &video, editor::tristate_palette *palette, std::string button_image="", const bool auto_join=true)
bool hit(int x, int y) const
const std::string checkbox_release