The Battle for Wesnoth  1.19.0-dev
scrollbar.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 - 2024
3  by Guillaume Melquiond <guillaume.melquiond@gmail.com>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 #define GETTEXT_DOMAIN "wesnoth-lib"
18 
19 #include "draw.hpp"
20 #include "log.hpp"
21 #include "widgets/scrollbar.hpp"
22 #include "picture.hpp"
23 #include "sdl/input.hpp" // get_mouse_state
24 #include "sdl/rect.hpp"
25 #include "sdl/texture.hpp"
26 #include "sdl/utils.hpp"
27 
28 namespace {
29 
30 const std::string scrollbar_top = "buttons/scrollbars/scrolltop.png";
31 const std::string scrollbar_bottom = "buttons/scrollbars/scrollbottom.png";
32 const std::string scrollbar_mid = "buttons/scrollbars/scrollmid.png";
33 
34 const std::string scrollbar_top_hl = "buttons/scrollbars/scrolltop-active.png";
35 const std::string scrollbar_bottom_hl = "buttons/scrollbars/scrollbottom-active.png";
36 const std::string scrollbar_mid_hl = "buttons/scrollbars/scrollmid-active.png";
37 
38 const std::string scrollbar_top_pressed = "buttons/scrollbars/scrolltop-pressed.png";
39 const std::string scrollbar_bottom_pressed = "buttons/scrollbars/scrollbottom-pressed.png";
40 const std::string scrollbar_mid_pressed = "buttons/scrollbars/scrollmid-pressed.png";
41 
42 }
43 
44 namespace gui {
45 
47  : widget()
48  , state_(NORMAL)
49  , minimum_grip_height_(0)
50  , mousey_on_grip_(0)
51  , grip_position_(0)
52  , grip_height_(0)
53  , full_height_(0)
54  , scroll_rate_(1)
55 {
56  const point img_size(image::get_size(scrollbar_mid));
57 
58  if (img_size.x && img_size.y) {
59  set_width(img_size.x);
60  // this is a bit rough maybe
61  minimum_grip_height_ = 2 * img_size.y;
62  }
63 }
64 
65 unsigned scrollbar::get_position() const
66 {
67  return grip_position_;
68 }
69 
71 {
72  return full_height_ - grip_height_;
73 }
74 
75 void scrollbar::set_position(unsigned pos)
76 {
77  if (pos > full_height_ - grip_height_)
78  pos = full_height_ - grip_height_;
79  if (pos == grip_position_)
80  return;
81  grip_position_ = pos;
82  queue_redraw();
83 }
84 
85 void scrollbar::adjust_position(unsigned pos)
86 {
87  if (pos < grip_position_)
88  set_position(pos);
89  else if (pos >= grip_position_ + grip_height_)
90  set_position(pos - (grip_height_ - 1));
91 }
92 
94 {
95  int pos = grip_position_ + dep;
96  if (pos > 0)
97  set_position(pos);
98  else
99  set_position(0);
100 }
101 
103 {
104  if (h > full_height_)
105  h = full_height_;
106  if (h == grip_height_)
107  return;
108  bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
109  grip_height_ = h;
110  if (at_bottom)
113  queue_redraw();
114 }
115 
117 {
118  if (h == full_height_)
119  return;
120  bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
121  full_height_ = h;
122  if (at_bottom)
126  queue_redraw();
127 }
128 
130 {
131  scroll_rate_ = r;
132 }
133 
135 {
137 }
138 
140 {
142 }
143 
144 SDL_Rect scrollbar::grip_area() const
145 {
146  const SDL_Rect& loc = location();
147  if (full_height_ == grip_height_)
148  return loc;
149  int h = static_cast<int>(loc.h) * grip_height_ / full_height_;
150  if (h < minimum_grip_height_)
152  int y = loc.y + (static_cast<int>(loc.h) - h) * grip_position_ / (full_height_ - grip_height_);
153  return {loc.x, y, loc.w, h};
154 }
155 
157 {
158  texture mid_img;
159  texture bot_img;
160  texture top_img;
161 
162  switch (state_) {
163 
164  case NORMAL:
165  top_img = image::get_texture(scrollbar_top);
166  mid_img = image::get_texture(scrollbar_mid);
167  bot_img = image::get_texture(scrollbar_bottom);
168  break;
169 
170  case ACTIVE:
171  top_img = image::get_texture(scrollbar_top_hl);
172  mid_img = image::get_texture(scrollbar_mid_hl);
173  bot_img = image::get_texture(scrollbar_bottom_hl);
174  break;
175 
176  case DRAGGED:
177  top_img = image::get_texture(scrollbar_top_pressed);
178  mid_img = image::get_texture(scrollbar_mid_pressed);
179  bot_img = image::get_texture(scrollbar_bottom_pressed);
180  break;
181 
182  case UNINIT:
183  default:
184  break;
185  }
186 
187  SDL_Rect grip = grip_area();
188 
189  int mid_height = grip.h - top_img.h() - bot_img.h();
190  if (mid_height <= 0) {
191  // For now, minimum size of the middle piece is 1.
192  // This should never really be encountered, and if it is,
193  // it's a symptom of a larger problem, I think.
194  mid_height = 1;
195  }
196 
197  SDL_Rect groove = location();
198 
199  if (grip.h > groove.h) {
200  PLAIN_LOG << "abort draw scrollbar: grip too large";
201  return;
202  }
203 
204  // Draw scrollbar "groove"
205  const color_t c{0, 0, 0, uint8_t(255 * 0.35)};
206  draw::fill(groove, c);
207 
208  // Draw scrollbar "grip"
209  SDL_Rect dest{grip.x, grip.y, top_img.w(), top_img.h()};
210  draw::blit(top_img, dest);
211 
212  dest = {dest.x, dest.y + top_img.h(), mid_img.w(), mid_height};
213  draw::blit(mid_img, dest);
214 
215  dest = {dest.x, dest.y + mid_height, bot_img.w(), bot_img.h()};
216  draw::blit(bot_img, dest);
217 }
218 
219 void scrollbar::handle_event(const SDL_Event& event)
220 {
222 
223  if (mouse_locked() || hidden())
224  return;
225 
226  STATE new_state = state_;
227  const rect grip = grip_area();
228  const rect& groove = location();
229 
230 
231  switch (event.type) {
232  case SDL_MOUSEBUTTONUP:
233  {
234  const SDL_MouseButtonEvent& e = event.button;
235  bool on_grip = grip.contains(e.x, e.y);
236  new_state = on_grip ? ACTIVE : NORMAL;
237  break;
238  }
239  case SDL_MOUSEBUTTONDOWN:
240  {
241  const SDL_MouseButtonEvent& e = event.button;
242  bool on_grip = grip.contains(e.x, e.y);
243  bool on_groove = groove.contains(e.x, e.y);
244  if (on_grip && e.button == SDL_BUTTON_LEFT) {
245  mousey_on_grip_ = e.y - grip.y;
246  new_state = DRAGGED;
247  } else if (on_groove && e.button == SDL_BUTTON_LEFT && groove.h != grip.h) {
248  if (e.y < grip.y)
249  move_position(-static_cast<int>(grip_height_));
250  else
252  } else if (on_groove && e.button == SDL_BUTTON_MIDDLE && groove.h != grip.h) {
253  int y_dep = e.y - grip.y - grip.h/2;
254  int dep = y_dep * int(full_height_ - grip_height_)/ (groove.h - grip.h);
255  move_position(dep);
256  }
257  break;
258  }
259  case SDL_MOUSEMOTION:
260  {
261  const SDL_MouseMotionEvent& e = event.motion;
262  if (state_ == NORMAL || state_ == ACTIVE) {
263  bool on_grip = grip.contains(e.x, e.y);
264  new_state = on_grip ? ACTIVE : NORMAL;
265  } else if (state_ == DRAGGED && groove.h != grip.h) {
266  int y_dep = e.y - grip.y - mousey_on_grip_;
267  int dep = y_dep * static_cast<int>(full_height_ - grip_height_) / (groove.h - grip.h);
268  move_position(dep);
269  }
270  break;
271  }
272  case SDL_MOUSEWHEEL:
273  {
274  const SDL_MouseWheelEvent& e = event.wheel;
275  int x, y;
276  sdl::get_mouse_state(&x, &y);
277  bool on_groove = groove.contains(x, y);
278  if (on_groove && e.y < 0) {
280  } else if (on_groove && e.y > 0) {
282  }
283  break;
284  }
285  default:
286  break;
287  }
288 
289 
290  if (new_state != state_) {
291  state_ = new_state;
292  queue_redraw();
293  }
294 }
295 
296 } // end namespace gui
SDL_Rect grip_area() const
Definition: scrollbar.cpp:144
scrollbar()
Create a scrollbar.
Definition: scrollbar.cpp:46
void set_shown_size(unsigned h)
Set the relative size of the grip.
Definition: scrollbar.cpp:102
virtual void draw_contents()
Definition: scrollbar.cpp:156
void set_scroll_rate(unsigned r)
Set scroll rate.
Definition: scrollbar.cpp:129
unsigned int grip_position_
Definition: scrollbar.hpp:83
void scroll_up()
Scrolls up one step.
Definition: scrollbar.cpp:139
void move_position(int dep)
Move the scrollbar.
Definition: scrollbar.cpp:93
int minimum_grip_height_
Definition: scrollbar.hpp:81
void set_position(unsigned pos)
Manually update the scrollbar.
Definition: scrollbar.cpp:75
void scroll_down()
Scrolls down one step.
Definition: scrollbar.cpp:134
void set_full_size(unsigned h)
Set the relative size of the scrollbar.
Definition: scrollbar.cpp:116
unsigned get_position() const
Determine where the scrollbar is.
Definition: scrollbar.cpp:65
unsigned int full_height_
Definition: scrollbar.hpp:83
unsigned int grip_height_
Definition: scrollbar.hpp:83
unsigned get_max_position() const
Definition: scrollbar.cpp:70
void adjust_position(unsigned pos)
Ensure the viewport contains the position.
Definition: scrollbar.cpp:85
virtual void handle_event(const SDL_Event &event)
Definition: scrollbar.cpp:219
void set_width(int w)
Definition: widget.cpp:98
virtual void handle_event(const SDL_Event &) override
Definition: widget.hpp:90
const rect & location() const
Definition: widget.cpp:123
void queue_redraw()
Indicate that the widget should be redrawn.
Definition: widget.cpp:215
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
Drawing functions, for drawing things on the screen.
Contains functions for cleanly handling SDL input.
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:295
@ NORMAL
Definition: cursor.hpp:28
void fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
Definition: draw.cpp:50
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
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:960
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition: picture.cpp:814
uint32_t get_mouse_state(int *x, int *y)
A wrapper for SDL_GetMouseState that gives coordinates in draw space.
Definition: input.cpp:27
Contains the SDL_Rect helper code.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition: rect.cpp:52
mock_char c
#define e
#define h