The Battle for Wesnoth  1.15.0-dev
tristate_button.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2018 by Fabian Mueller <fabianmueller5@gmx.de>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "game_config.hpp"
20 #include "picture.hpp"
21 #include "log.hpp"
22 #include "sdl/rect.hpp"
23 #include "sound.hpp"
24 #include "video.hpp"
25 
26 static lg::log_domain log_display("display");
27 #define ERR_DP LOG_STREAM(err, log_display)
28 
29 namespace gui {
30 
33  std::string button_image_name,
34  const bool auto_join) :
35  widget(video, auto_join),
36  baseImage_(nullptr), touchedBaseImage_(nullptr), activeBaseImage_(nullptr),
37  itemImage_(nullptr),
38  pressedDownImage_(nullptr), pressedUpImage_(nullptr), pressedBothImage_(nullptr),
39  pressedBothActiveImage_(nullptr), pressedDownActiveImage_(nullptr), pressedUpActiveImage_(nullptr),
40  touchedDownImage_(nullptr), touchedUpImage_(nullptr), touchedBothImage_(nullptr),
41  textRect_(),
42  state_(NORMAL), pressed_(false),
43  base_height_(0), base_width_(0),
44  palette_(palette), item_id_()
45 {
46 
47  if (button_image_name.empty()) {
48  button_image_name = "buttons/button_selectable/button_selectable_38_";
49  }
50 
52  image::get_image(button_image_name + "base.png"));
54  image::get_image(button_image_name + "base-active.png"));
56  image::get_image(button_image_name + "base-touched.png"));
57 
59  image::get_image(button_image_name + "border-touched-both.png"));
61  image::get_image(button_image_name + "border-touched-up.png"));
63  image::get_image(button_image_name + "border-touched-down.png"));
64 
66  image::get_image(button_image_name + "border-pressed-up.png"));
68  image::get_image(button_image_name + "border-pressed-down.png"));
70  image::get_image(button_image_name + "border-pressed-both.png"));
71 
73  image::get_image(button_image_name + "border-active-pressed-up.png"));
75  image::get_image(button_image_name + "border-active-pressed-down.png"));
77  image::get_image(button_image_name + "border-active-pressed-both.png"));
78 
79  //TODO
80 // if (button_image.null()) {
81 // ERR_DP<< "error initializing button!" << std::endl;
82 // throw error();
83 // }
84 
87 
88 }
89 
91 
93 
94  STATE new_state = state_;
95 
96  switch (new_pressed_state) {
97  case LEFT:
99  break;
100  case RIGHT:
102  break;
103  case BOTH:
105  break;
106  case NONE:
107  new_state = NORMAL;
108  }
109 
110  if (state_ != new_state) {
111  state_ = new_state;
112  set_dirty();
113  }
114 }
115 
116 //TODO
117 //void tristate_button::set_active(bool active) {
118 // if ((state_ == NORMAL) && active) {
119 // state_ = ACTIVE;
120 // set_dirty();
121 // } else if ((state_ == ACTIVE) && !active) {
122 // state_ = NORMAL;
123 // set_dirty();
124 // }
125 //}
126 
128 
129  switch (state_) {
130  case PRESSED_LEFT:
131  case PRESSED_ACTIVE_LEFT:
132  case TOUCHED_BOTH_LEFT:
133  return LEFT;
134  case PRESSED_RIGHT:
136  case TOUCHED_BOTH_RIGHT:
137  return RIGHT;
138  case PRESSED_BOTH:
139  case PRESSED_ACTIVE_BOTH:
140  return BOTH;
141  default:
142  return NONE;
143  }
144 }
145 
146 void tristate_button::enable(bool new_val) {
147  if (new_val != enabled()) {
148  pressed_ = false;
149  // check buttons should keep their state
150  state_ = NORMAL;
151 
152  widget::enable(new_val);
153  }
154 }
155 
157 
158  surface image(nullptr);
159 
160  surface overlay(nullptr);
161  surface base = baseImage_;
162 
163  switch (state_) {
164 
165  case UNINIT:
166  return;
167 
168  case NORMAL:
169  break;
170 
171  case TOUCHED_LEFT:
172  overlay = touchedUpImage_;
173  base = touchedBaseImage_;
174  break;
175  case TOUCHED_RIGHT:
176  overlay = touchedDownImage_;
177  base = touchedBaseImage_;
178  break;
179  case TOUCHED_BOTH_LEFT:
180  case TOUCHED_BOTH_RIGHT:
181  overlay = touchedBothImage_;
182  base = touchedBaseImage_;
183  break;
184  case ACTIVE:
185  // overlay = activeImage_;
186  base = activeBaseImage_;
187  break;
188  case PRESSED_LEFT:
189  base = activeBaseImage_;
190  overlay = pressedUpImage_;
191  break;
192  case PRESSED_RIGHT:
193  base = activeBaseImage_;
194  overlay = pressedDownImage_;
195  break;
196  case PRESSED_BOTH:
197  base = activeBaseImage_;
198  overlay = pressedBothImage_;
199  break;
200  case PRESSED_ACTIVE_LEFT:
201  overlay = pressedUpActiveImage_;
202  base = activeBaseImage_;
203  break;
204  case PRESSED_ACTIVE_BOTH:
205  overlay = pressedBothActiveImage_;
206  base = activeBaseImage_;
207  break;
209  overlay = pressedDownActiveImage_;
210  base = activeBaseImage_;
211  break;
212  }
213 
214  image = base;
215 
216  const SDL_Rect& loc = location();
217 
218  surface scalled_item;
219  scalled_item.assign(scale_surface(itemImage_,
220  36, 36));
221 
222  surface nitem = make_neutral_surface(scalled_item);
223  surface nbase = make_neutral_surface(base);
224 
225  //TODO avoid magic numbers
226  SDL_Rect r {1, 1, 0, 0};
227  sdl_blit(nitem, nullptr, nbase, &r);
228 
229  if (!overlay.null()) {
230  surface noverlay = make_neutral_surface(overlay);
231  sdl_blit(noverlay, nullptr, nbase, nullptr);
232  }
233 
234  bg_restore();
235 
236  image = nbase;
237  video().blit_surface(loc.x, loc.y, image);
238 }
239 
240 //TODO move to widget
241 bool tristate_button::hit(int x, int y) const {
242  return sdl::point_in_rect(x, y, location());
243 }
244 
245 void tristate_button::mouse_motion(const SDL_MouseMotionEvent& event) {
246 
247  if (hit(event.x, event.y))
248  { // the cursor is over the widget
249 
250  switch (state_) {
251 
252  case UNINIT:
253  return;
254 
255  case NORMAL:
256  state_ = ACTIVE;
257  break;
258  case PRESSED_LEFT:
260  break;
261  case PRESSED_RIGHT:
263  break;
264  case PRESSED_BOTH:
266  break;
267  default:
268  //assert(false);
269  break;
270  }
271 
272  } else { // the cursor is not over the widget
273 
274  switch (state_) {
275 
276  case ACTIVE:
277  state_ = NORMAL;
278  break;
279  case TOUCHED_LEFT:
280  case TOUCHED_RIGHT:
281  state_ = ACTIVE;
282  break;
284  case TOUCHED_BOTH_RIGHT:
286  break;
287  case PRESSED_ACTIVE_BOTH:
289  break;
290  case PRESSED_ACTIVE_LEFT:
291  case TOUCHED_BOTH_LEFT:
293  break;
294  default:
295  break;
296  }
297  }
298 }
299 
300 void tristate_button::mouse_down(const SDL_MouseButtonEvent& event) {
301 
302  if (!hit(event.x, event.y))
303  return;
304 
305  if (event.button == SDL_BUTTON_RIGHT) {
306  if (state_ == ACTIVE)
310  }
311 
312  if (event.button == SDL_BUTTON_LEFT) {
313  if (state_ == ACTIVE)
317  }
318 
319 }
320 
322  state_ = NORMAL;
323  draw_contents();
324 }
325 
326 void tristate_button::mouse_up(const SDL_MouseButtonEvent& event) {
327 
328  if (!(hit(event.x, event.y)))
329  return;
330 
331  // the user has stopped pressing the mouse left button while on the widget
332  if (event.button == SDL_BUTTON_LEFT) {
333 
334  if (state_ == TOUCHED_LEFT) {
337  //TODO
338  // palette_->draw(true);
339  pressed_ = true;
340  }
341  if (state_ == TOUCHED_BOTH_RIGHT) {
344  // palette_->select_bg_item(item_id_);
345  // palette_->draw(true);
346  pressed_ = true;
347  }
348  }
349 
350  if (event.button == SDL_BUTTON_RIGHT) {
351 
352  pressed_ = true;
354 
355  if (state_ == TOUCHED_RIGHT) {
357  // palette_->select_bg_item(item_id_);
358  // palette_->draw(true);
359  // pressed_ = true;
360  }
361  if (state_ == TOUCHED_BOTH_LEFT) {
363  // palette_->select_fg_item(item_id_);
364  // palette_->select_bg_item(item_id_);
365  // palette_->draw(true);
366  // pressed_ = true;
367  }
368  }
369 
370  if (pressed_)
372 }
373 
374 void tristate_button::handle_event(const SDL_Event& event) {
375 
377 
378  if (hidden() || !enabled())
379  return;
380 
381  STATE start_state = state_;
382 
383  if (!mouse_locked()) {
384  switch (event.type) {
385  case SDL_MOUSEBUTTONDOWN:
386  mouse_down(event.button);
387  break;
388  case SDL_MOUSEBUTTONUP:
389  mouse_up(event.button);
390  break;
391  case SDL_MOUSEMOTION:
392  mouse_motion(event.motion);
393  break;
394  default:
395  return;
396  }
397  }
398 
399  if (start_state != state_)
400  set_dirty(true);
401 }
402 
404  const bool res = pressed_;
405  pressed_ = false;
406  return res;
407 }
408 
409 }
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: picture.cpp:1022
virtual void mouse_up(const SDL_MouseButtonEvent &event)
bool enabled() const
Definition: widget.cpp:210
virtual void mouse_down(const SDL_MouseButtonEvent &event)
virtual void enable(bool new_val=true)
Definition: widget.cpp:202
bool hidden() const
Definition: widget.cpp:196
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:83
virtual ~tristate_button()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: video.hpp:31
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:274
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
-file util.hpp
editor::tristate_palette * palette_
bool null() const
Definition: surface.hpp:79
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:215
virtual void handle_event(const SDL_Event &)
Definition: widget.cpp:338
const SDL_Rect & location() const
Definition: widget.cpp:142
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:22
virtual void mouse_motion(const SDL_MouseMotionEvent &event)
virtual void draw_contents() override
void bg_restore() const
Definition: widget.cpp:249
bool mouse_locked() const
Definition: widget.cpp:70
CVideo & video() const
Definition: widget.hpp:84
void assign(SDL_Surface *surf)
Definition: surface.hpp:46
Contains the SDL_Rect helper code.
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:72
this module manages the cache of images.
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1011
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:33
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