The Battle for Wesnoth  1.17.0-dev
scrollbar.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 - 2021
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 "widgets/scrollbar.hpp"
20 #include "picture.hpp"
21 #include "sdl/rect.hpp"
22 #include "sdl/utils.hpp"
23 #include "video.hpp"
24 
25 #include <iostream>
26 
27 namespace {
28 
29 const std::string scrollbar_top = "buttons/scrollbars/scrolltop.png";
30 const std::string scrollbar_bottom = "buttons/scrollbars/scrollbottom.png";
31 const std::string scrollbar_mid = "buttons/scrollbars/scrollmid.png";
32 
33 const std::string scrollbar_top_hl = "buttons/scrollbars/scrolltop-active.png";
34 const std::string scrollbar_bottom_hl = "buttons/scrollbars/scrollbottom-active.png";
35 const std::string scrollbar_mid_hl = "buttons/scrollbars/scrollmid-active.png";
36 
37 const std::string scrollbar_top_pressed = "buttons/scrollbars/scrolltop-pressed.png";
38 const std::string scrollbar_bottom_pressed = "buttons/scrollbars/scrollbottom-pressed.png";
39 const std::string scrollbar_mid_pressed = "buttons/scrollbars/scrollmid-pressed.png";
40 
41 }
42 
43 namespace gui {
44 
46  : widget(video)
47  , mid_scaled_(nullptr)
48  , groove_scaled_(nullptr)
49  , state_(NORMAL)
50  , minimum_grip_height_(0)
51  , mousey_on_grip_(0)
52  , grip_position_(0)
53  , grip_height_(0)
54  , full_height_(0)
55  , scroll_rate_(1)
56 {
57  static const surface img(image::get_image(scrollbar_mid));
58 
59  if (img != nullptr) {
60  set_width(img->w);
61  // this is a bit rough maybe
62  minimum_grip_height_ = 2 * img->h;
63  }
64 }
65 
66 unsigned scrollbar::get_position() const
67 {
68  return grip_position_;
69 }
70 
72 {
73  return full_height_ - grip_height_;
74 }
75 
76 void scrollbar::set_position(unsigned pos)
77 {
78  if (pos > full_height_ - grip_height_)
79  pos = full_height_ - grip_height_;
80  if (pos == grip_position_)
81  return;
82  grip_position_ = pos;
83  set_dirty();
84 }
85 
86 void scrollbar::adjust_position(unsigned pos)
87 {
88  if (pos < grip_position_)
89  set_position(pos);
90  else if (pos >= grip_position_ + grip_height_)
91  set_position(pos - (grip_height_ - 1));
92 }
93 
95 {
96  int pos = grip_position_ + dep;
97  if (pos > 0)
98  set_position(pos);
99  else
100  set_position(0);
101 }
102 
104 {
105  if (h > full_height_)
106  h = full_height_;
107  if (h == grip_height_)
108  return;
109  bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
110  grip_height_ = h;
111  if (at_bottom)
114  set_dirty(true);
115 }
116 
118 {
119  if (h == full_height_)
120  return;
121  bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
122  full_height_ = h;
123  if (at_bottom)
127  set_dirty(true);
128 }
129 
131 {
132  scroll_rate_ = r;
133 }
134 
136 {
138 }
139 
141 {
143 }
144 
145 SDL_Rect scrollbar::grip_area() const
146 {
147  const SDL_Rect& loc = location();
148  if (full_height_ == grip_height_)
149  return loc;
150  int h = static_cast<int>(loc.h) * grip_height_ / full_height_;
151  if (h < minimum_grip_height_)
153  int y = loc.y + (static_cast<int>(loc.h) - h) * grip_position_ / (full_height_ - grip_height_);
154  return {loc.x, y, loc.w, h};
155 }
156 
158 {
159  surface mid_img;
160  surface bottom_img;
161  surface top_img;
162 
163  switch (state_) {
164 
165  case NORMAL:
166  top_img = image::get_image(scrollbar_top);
167  mid_img = image::get_image(scrollbar_mid);
168  bottom_img = image::get_image(scrollbar_bottom);
169  break;
170 
171  case ACTIVE:
172  top_img = image::get_image(scrollbar_top_hl);
173  mid_img = image::get_image(scrollbar_mid_hl);
174  bottom_img = image::get_image(scrollbar_bottom_hl);
175  break;
176 
177  case DRAGGED:
178  top_img = image::get_image(scrollbar_top_pressed);
179  mid_img = image::get_image(scrollbar_mid_pressed);
180  bottom_img = image::get_image(scrollbar_bottom_pressed);
181  break;
182 
183  case UNINIT:
184  default:
185  break;
186  }
187 
188  SDL_Rect grip = grip_area();
189  int mid_height = grip.h - top_img->h - bottom_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  if(!mid_scaled_ || mid_scaled_->h != mid_height) {
198  mid_scaled_ = scale_surface(mid_img, mid_img->w, mid_height);
199  }
200 
201  SDL_Rect groove = location();
202 
203  if (!mid_scaled_) {
204  std::cerr << "Failure during scrollbar image scale.\n";
205  return;
206  }
207 
208  if (grip.h > groove.h) {
209  std::cerr << "abort draw scrollbar: grip too large\n";
210  return;
211  }
212 
213  // Draw scrollbar "groove"
214  const color_t c{0, 0, 0, uint8_t(255 * 0.35)};
215 
216  sdl::fill_rectangle(groove, c);
217 
218  // Draw scrollbar "grip"
219  video().blit_surface(grip.x, grip.y, top_img);
220  video().blit_surface(grip.x, grip.y + top_img->h, mid_scaled_);
221  video().blit_surface(grip.x, grip.y + top_img->h + mid_height, bottom_img);
222 }
223 
224 void scrollbar::handle_event(const SDL_Event& event)
225 {
227 
228  if (mouse_locked() || hidden())
229  return;
230 
231  STATE new_state = state_;
232  const SDL_Rect& grip = grip_area();
233  const SDL_Rect& groove = location();
234 
235 
236  switch (event.type) {
237  case SDL_MOUSEBUTTONUP:
238  {
239  const SDL_MouseButtonEvent& e = event.button;
240  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
241  new_state = on_grip ? ACTIVE : NORMAL;
242  break;
243  }
244  case SDL_MOUSEBUTTONDOWN:
245  {
246  const SDL_MouseButtonEvent& e = event.button;
247  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
248  bool on_groove = sdl::point_in_rect(e.x, e.y, groove);
249  if (on_grip && e.button == SDL_BUTTON_LEFT) {
250  mousey_on_grip_ = e.y - grip.y;
251  new_state = DRAGGED;
252  } else if (on_groove && e.button == SDL_BUTTON_LEFT && groove.h != grip.h) {
253  if (e.y < grip.y)
254  move_position(-static_cast<int>(grip_height_));
255  else
257  } else if (on_groove && e.button == SDL_BUTTON_MIDDLE && groove.h != grip.h) {
258  int y_dep = e.y - grip.y - grip.h/2;
259  int dep = y_dep * int(full_height_ - grip_height_)/ (groove.h - grip.h);
260  move_position(dep);
261  }
262  break;
263  }
264  case SDL_MOUSEMOTION:
265  {
266  const SDL_MouseMotionEvent& e = event.motion;
267  if (state_ == NORMAL || state_ == ACTIVE) {
268  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
269  new_state = on_grip ? ACTIVE : NORMAL;
270  } else if (state_ == DRAGGED && groove.h != grip.h) {
271  int y_dep = e.y - grip.y - mousey_on_grip_;
272  int dep = y_dep * static_cast<int>(full_height_ - grip_height_) / (groove.h - grip.h);
273  move_position(dep);
274  }
275  break;
276  }
277  case SDL_MOUSEWHEEL:
278  {
279  const SDL_MouseWheelEvent& e = event.wheel;
280  int x, y;
281  SDL_GetMouseState(&x, &y);
282  bool on_groove = sdl::point_in_rect(x, y, groove);
283  if (on_groove && e.y < 0) {
285  } else if (on_groove && e.y > 0) {
287  }
288  break;
289  }
290  default:
291  break;
292  }
293 
294 
295  if (new_state != state_) {
296  set_dirty();
297  mid_scaled_ = nullptr;
298  state_ = new_state;
299  }
300 }
301 
302 } // end namespace gui
surface get_image(const image::locator &i_locator, TYPE type)
Caches and returns an image.
Definition: picture.cpp:816
bool hidden() const
Definition: widget.cpp:188
unsigned get_position() const
Determine where the scrollbar is.
Definition: scrollbar.cpp:66
virtual void handle_event(const SDL_Event &event)
Definition: scrollbar.cpp:224
Definition: video.hpp:32
int minimum_grip_height_
Definition: scrollbar.hpp:83
unsigned int full_height_
Definition: scrollbar.hpp:85
unsigned get_max_position() const
Definition: scrollbar.cpp:71
General purpose widgets.
void set_width(int w)
Definition: widget.cpp:109
SDL_Rect grip_area() const
Definition: scrollbar.cpp:145
#define h
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
void scroll_down()
Scrolls down one step.
Definition: scrollbar.cpp:135
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
void set_position(unsigned pos)
Manually update the scrollbar.
Definition: scrollbar.cpp:76
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:23
unsigned int grip_height_
Definition: scrollbar.hpp:85
void adjust_position(unsigned pos)
Ensure the viewport contains the position.
Definition: scrollbar.cpp:86
virtual void draw_contents()
Definition: scrollbar.cpp:157
void scroll_up()
Scrolls up one step.
Definition: scrollbar.cpp:140
bool mouse_locked() const
Definition: widget.cpp:62
CVideo & video() const
Definition: widget.hpp:84
Contains the SDL_Rect helper code.
void set_shown_size(unsigned h)
Set the relative size of the grip.
Definition: scrollbar.cpp:103
surface mid_scaled_
Definition: scrollbar.hpp:78
void fill_rectangle(const SDL_Rect &rect, const color_t &color)
Draws a filled rectangle.
Definition: rect.cpp:66
scrollbar(CVideo &video)
Create a scrollbar.
Definition: scrollbar.cpp:45
void set_full_size(unsigned h)
Set the relative size of the scrollbar.
Definition: scrollbar.cpp:117
void set_scroll_rate(unsigned r)
Set scroll rate.
Definition: scrollbar.cpp:130
#define e
unsigned int grip_position_
Definition: scrollbar.hpp:85
mock_char c
void move_position(int dep)
Move the scrollbar.
Definition: scrollbar.cpp:94