The Battle for Wesnoth  1.15.12+dev
scrollbar.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 by David White <dave@whitevine.net>
3  2004 - 2015 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
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 
18 #include "widgets/scrollbar.hpp"
19 #include "picture.hpp"
20 #include "sdl/rect.hpp"
21 #include "sdl/utils.hpp"
22 #include "video.hpp"
23 
24 #include <iostream>
25 
26 namespace {
27 
28 const std::string scrollbar_top = "buttons/scrollbars/scrolltop.png";
29 const std::string scrollbar_bottom = "buttons/scrollbars/scrollbottom.png";
30 const std::string scrollbar_mid = "buttons/scrollbars/scrollmid.png";
31 
32 const std::string scrollbar_top_hl = "buttons/scrollbars/scrolltop-active.png";
33 const std::string scrollbar_bottom_hl = "buttons/scrollbars/scrollbottom-active.png";
34 const std::string scrollbar_mid_hl = "buttons/scrollbars/scrollmid-active.png";
35 
36 const std::string scrollbar_top_pressed = "buttons/scrollbars/scrolltop-pressed.png";
37 const std::string scrollbar_bottom_pressed = "buttons/scrollbars/scrollbottom-pressed.png";
38 const std::string scrollbar_mid_pressed = "buttons/scrollbars/scrollmid-pressed.png";
39 
40 }
41 
42 namespace gui {
43 
45  : widget(video)
46  , mid_scaled_(nullptr)
47  , groove_scaled_(nullptr)
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  static const surface img(image::get_image(scrollbar_mid));
57 
58  if (img != nullptr) {
59  set_width(img->w);
60  // this is a bit rough maybe
61  minimum_grip_height_ = 2 * img->h;
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  set_dirty();
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  set_dirty(true);
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  set_dirty(true);
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  surface mid_img;
159  surface bottom_img;
160  surface top_img;
161 
162  switch (state_) {
163 
164  case NORMAL:
165  top_img = image::get_image(scrollbar_top);
166  mid_img = image::get_image(scrollbar_mid);
167  bottom_img = image::get_image(scrollbar_bottom);
168  break;
169 
170  case ACTIVE:
171  top_img = image::get_image(scrollbar_top_hl);
172  mid_img = image::get_image(scrollbar_mid_hl);
173  bottom_img = image::get_image(scrollbar_bottom_hl);
174  break;
175 
176  case DRAGGED:
177  top_img = image::get_image(scrollbar_top_pressed);
178  mid_img = image::get_image(scrollbar_mid_pressed);
179  bottom_img = image::get_image(scrollbar_bottom_pressed);
180  break;
181 
182  case UNINIT:
183  default:
184  break;
185  }
186 
187  SDL_Rect grip = grip_area();
188  int mid_height = grip.h - top_img->h - bottom_img->h;
189  if (mid_height <= 0) {
190  // For now, minimum size of the middle piece is 1.
191  // This should never really be encountered, and if it is,
192  // it's a symptom of a larger problem, I think.
193  mid_height = 1;
194  }
195 
196  if(!mid_scaled_ || mid_scaled_->h != mid_height) {
197  mid_scaled_ = scale_surface(mid_img, mid_img->w, mid_height);
198  }
199 
200  SDL_Rect groove = location();
201 
202  if (!mid_scaled_) {
203  std::cerr << "Failure during scrollbar image scale.\n";
204  return;
205  }
206 
207  if (grip.h > groove.h) {
208  std::cerr << "abort draw scrollbar: grip too large\n";
209  return;
210  }
211 
212  // Draw scrollbar "groove"
213  const color_t c{0, 0, 0, uint8_t(255 * 0.35)};
214 
215  sdl::fill_rectangle(groove, c);
216 
217  // Draw scrollbar "grip"
218  video().blit_surface(grip.x, grip.y, top_img);
219  video().blit_surface(grip.x, grip.y + top_img->h, mid_scaled_);
220  video().blit_surface(grip.x, grip.y + top_img->h + mid_height, bottom_img);
221 }
222 
223 void scrollbar::handle_event(const SDL_Event& event)
224 {
226 
227  if (mouse_locked() || hidden())
228  return;
229 
230  STATE new_state = state_;
231  const SDL_Rect& grip = grip_area();
232  const SDL_Rect& groove = location();
233 
234 
235  switch (event.type) {
236  case SDL_MOUSEBUTTONUP:
237  {
238  const SDL_MouseButtonEvent& e = event.button;
239  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
240  new_state = on_grip ? ACTIVE : NORMAL;
241  break;
242  }
243  case SDL_MOUSEBUTTONDOWN:
244  {
245  const SDL_MouseButtonEvent& e = event.button;
246  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
247  bool on_groove = sdl::point_in_rect(e.x, e.y, groove);
248  if (on_grip && e.button == SDL_BUTTON_LEFT) {
249  mousey_on_grip_ = e.y - grip.y;
250  new_state = DRAGGED;
251  } else if (on_groove && e.button == SDL_BUTTON_LEFT && groove.h != grip.h) {
252  if (e.y < grip.y)
253  move_position(-static_cast<int>(grip_height_));
254  else
256  } else if (on_groove && e.button == SDL_BUTTON_MIDDLE && groove.h != grip.h) {
257  int y_dep = e.y - grip.y - grip.h/2;
258  int dep = y_dep * int(full_height_ - grip_height_)/ (groove.h - grip.h);
259  move_position(dep);
260  }
261  break;
262  }
263  case SDL_MOUSEMOTION:
264  {
265  const SDL_MouseMotionEvent& e = event.motion;
266  if (state_ == NORMAL || state_ == ACTIVE) {
267  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
268  new_state = on_grip ? ACTIVE : NORMAL;
269  } else if (state_ == DRAGGED && groove.h != grip.h) {
270  int y_dep = e.y - grip.y - mousey_on_grip_;
271  int dep = y_dep * static_cast<int>(full_height_ - grip_height_) / (groove.h - grip.h);
272  move_position(dep);
273  }
274  break;
275  }
276  case SDL_MOUSEWHEEL:
277  {
278  const SDL_MouseWheelEvent& e = event.wheel;
279  int x, y;
280  SDL_GetMouseState(&x, &y);
281  bool on_groove = sdl::point_in_rect(x, y, groove);
282  if (on_groove && e.y < 0) {
284  } else if (on_groove && e.y > 0) {
286  }
287  break;
288  }
289  default:
290  break;
291  }
292 
293 
294  if (new_state != state_) {
295  set_dirty();
296  mid_scaled_ = nullptr;
297  state_ = new_state;
298  }
299 }
300 
301 } // end namespace gui
surface get_image(const image::locator &i_locator, TYPE type)
Caches and returns an image.
Definition: picture.cpp:815
bool hidden() const
Definition: widget.cpp:188
unsigned get_position() const
Determine where the scrollbar is.
Definition: scrollbar.cpp:65
virtual void handle_event(const SDL_Event &event)
Definition: scrollbar.cpp:223
Definition: video.hpp:31
int minimum_grip_height_
Definition: scrollbar.hpp:82
unsigned int full_height_
Definition: scrollbar.hpp:84
unsigned get_max_position() const
Definition: scrollbar.cpp:70
General purpose widgets.
void set_width(int w)
Definition: widget.cpp:109
SDL_Rect grip_area() const
Definition: scrollbar.cpp:144
#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:196
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:162
void scroll_down()
Scrolls down one step.
Definition: scrollbar.cpp:134
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:75
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:22
unsigned int grip_height_
Definition: scrollbar.hpp:84
void adjust_position(unsigned pos)
Ensure the viewport contains the position.
Definition: scrollbar.cpp:85
virtual void draw_contents()
Definition: scrollbar.cpp:156
void scroll_up()
Scrolls up one step.
Definition: scrollbar.cpp:139
bool mouse_locked() const
Definition: widget.cpp:62
CVideo & video() const
Definition: widget.hpp:83
Contains the SDL_Rect helper code.
void set_shown_size(unsigned h)
Set the relative size of the grip.
Definition: scrollbar.cpp:102
surface mid_scaled_
Definition: scrollbar.hpp:77
void fill_rectangle(const SDL_Rect &rect, const color_t &color)
Draws a filled rectangle.
Definition: rect.cpp:65
scrollbar(CVideo &video)
Create a scrollbar.
Definition: scrollbar.cpp:44
void set_full_size(unsigned h)
Set the relative size of the scrollbar.
Definition: scrollbar.cpp:116
void set_scroll_rate(unsigned r)
Set scroll rate.
Definition: scrollbar.cpp:129
#define e
unsigned int grip_position_
Definition: scrollbar.hpp:84
mock_char c
void move_position(int dep)
Move the scrollbar.
Definition: scrollbar.cpp:93