The Battle for Wesnoth  1.15.0-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 http://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 /** @file */
17 
18 #define GETTEXT_DOMAIN "wesnoth-lib"
19 
20 #include "widgets/scrollbar.hpp"
21 #include "image.hpp"
22 #include "sdl/rect.hpp"
23 #include "video.hpp"
24 
25 #include <iostream>
26 
27 namespace {
28  const std::string scrollbar_top = "buttons/scrollbars_large/scrolltop.png";
29  const std::string scrollbar_bottom = "buttons/scrollbars_large/scrollbottom.png";
30  const std::string scrollbar_mid = "buttons/scrollbars_large/scrollmid.png";
31 
32  const std::string scrollbar_top_hl = "buttons/scrollbars_large/scrolltop-active.png";
33  const std::string scrollbar_bottom_hl = "buttons/scrollbars_large/scrollbottom-active.png";
34  const std::string scrollbar_mid_hl = "buttons/scrollbars_large/scrollmid-active.png";
35 
36  const std::string scrollbar_top_pressed = "buttons/scrollbars_large/scrolltop-pressed.png";
37  const std::string scrollbar_bottom_pressed = "buttons/scrollbars_large/scrollbottom-pressed.png";
38  const std::string scrollbar_mid_pressed = "buttons/scrollbars_large/scrollmid-pressed.png";
39 
40  const std::string groove_top = "buttons/scrollbars_large/scrollgroove-top.png";
41  const std::string groove_mid = "buttons/scrollbars_large/scrollgroove-mid.png";
42  const std::string groove_bottom = "buttons/scrollbars_large/scrollgroove-bottom.png";
43 
44 }
45 
46 namespace gui {
47 
49  : widget(video)
50  , mid_scaled_(nullptr)
51  , groove_scaled_(nullptr)
52  , uparrow_(video, "", button::TYPE_TURBO, "button_square/button_square_25"
53  , gui::button::DEFAULT_SPACE, true,"icons/arrows/arrows_ornate_up_25")
54  , downarrow_(video, "", button::TYPE_TURBO, "button_square/button_square_25"
55  , gui::button::DEFAULT_SPACE, true,"icons/arrows/arrows_ornate_down_25")
56  , state_(NORMAL)
57  , minimum_grip_height_(0)
58  , mousey_on_grip_(0)
59  , grip_position_(0)
60  , grip_height_(0)
61  , full_height_(0)
62  , scroll_rate_(1)
63 {
64  uparrow_.enable(false);
65  downarrow_.enable(false);
66 
67  static const surface img(image::get_image(scrollbar_mid));
68 
69  if (img != nullptr) {
70  set_width(img->w);
71  // this is a bit rough maybe
72  minimum_grip_height_ = 2 * img->h;
73  }
74 }
75 
77 {
79  h.push_back(&uparrow_);
80  h.push_back(&downarrow_);
81  return h;
82 }
83 
84 void scrollbar::update_location(const SDL_Rect& rect)
85 {
86  int uh = uparrow_.height(), dh = downarrow_.height();
87  uparrow_.set_location(rect.x, rect.y);
88  downarrow_.set_location(rect.x, rect.y + rect.h - dh);
89  SDL_Rect r = rect;
90  r.y += uh;
91  r.h -= uh + dh;
92 
94  //TODO comment or remove
95  //bg_register(r);
96 }
97 
98 void scrollbar::hide(bool value)
99 {
100  widget::hide(value);
101  uparrow_.hide(value);
102  downarrow_.hide(value);
103 }
104 
105 unsigned scrollbar::get_position() const
106 {
107  return grip_position_;
108 }
109 
111 {
112  return full_height_ - grip_height_;
113 }
114 
115 void scrollbar::set_position(unsigned pos)
116 {
117  if (pos > full_height_ - grip_height_)
118  pos = full_height_ - grip_height_;
119  if (pos == grip_position_)
120  return;
121  grip_position_ = pos;
123  downarrow_.enable(grip_position_ < full_height_ - grip_height_);
124  set_dirty();
125 }
126 
127 void scrollbar::adjust_position(unsigned pos)
128 {
129  if (pos < grip_position_)
130  set_position(pos);
131  else if (pos >= grip_position_ + grip_height_)
132  set_position(pos - (grip_height_ - 1));
133 }
134 
136 {
137  int pos = grip_position_ + dep;
138  if (pos > 0)
139  set_position(pos);
140  else
141  set_position(0);
142 }
143 
145 {
146  if (h > full_height_)
147  h = full_height_;
148  if (h == grip_height_)
149  return;
150  bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
151  grip_height_ = h;
152  if (at_bottom)
155  set_dirty(true);
156 }
157 
159 {
160  if (h == full_height_)
161  return;
162  bool at_bottom = get_position() == get_max_position() && get_max_position() > 0;
163  full_height_ = h;
164  if (at_bottom)
169  set_dirty(true);
170 }
171 
173 {
174  scroll_rate_ = r;
175 }
176 
178 {
179  int uh = uparrow_.height();
180  int dh = downarrow_.height();
181  if(uh + dh >= height) {
182  return false;
183  } else {
184  return true;
185  }
186 }
187 
189 {
191 }
192 
194 {
196 }
197 
199 {
200  if (uparrow_.pressed())
201  scroll_up();
202 
203  if (downarrow_.pressed())
204  scroll_down();
205 }
206 
207 SDL_Rect scrollbar::groove_area() const
208 {
209  SDL_Rect loc = location();
210  int uh = uparrow_.height();
211  int dh = downarrow_.height();
212  if(uh + dh >= loc.h) {
213  loc.h = 0;
214  } else {
215  loc.y += uh;
216  loc.h -= uh + dh;
217  }
218  return loc;
219 }
220 
221 SDL_Rect scrollbar::grip_area() const
222 {
223  const SDL_Rect& loc = groove_area();
224  if (full_height_ == grip_height_)
225  return loc;
226  int h = static_cast<int>(loc.h) * grip_height_ / full_height_;
227  if (h < minimum_grip_height_)
229  int y = loc.y + (static_cast<int>(loc.h) - h) * grip_position_ / (full_height_ - grip_height_);
230  return {loc.x, y, loc.w, h};
231 }
232 
234 {
235  surface mid_img;
236  surface bottom_img;
237  surface top_img;
238 
239  switch (state_) {
240 
241  case NORMAL:
242  top_img.assign(image::get_image(scrollbar_top));
243  mid_img.assign(image::get_image(scrollbar_mid));
244  bottom_img.assign(image::get_image(scrollbar_bottom));
245  break;
246 
247  case ACTIVE:
248  top_img.assign(image::get_image(scrollbar_top_hl));
249  mid_img.assign(image::get_image(scrollbar_mid_hl));
250  bottom_img.assign(image::get_image(scrollbar_bottom_hl));
251  break;
252 
253  case DRAGGED:
254  top_img.assign(image::get_image(scrollbar_top_pressed));
255  mid_img.assign(image::get_image(scrollbar_mid_pressed));
256  bottom_img.assign(image::get_image(scrollbar_bottom_pressed));
257  break;
258 
259  case UNINIT:
260  default:
261  break;
262  }
263 
264  const surface top_grv(image::get_image(groove_top));
265  const surface mid_grv(image::get_image(groove_mid));
266  const surface bottom_grv(image::get_image(groove_bottom));
267 
268  if (mid_img == nullptr || bottom_img == nullptr || top_img == nullptr
269  || top_grv == nullptr || bottom_grv == nullptr || mid_grv == nullptr) {
270  std::cerr << "Failure to load scrollbar image.\n";
271  return;
272  }
273 
274  SDL_Rect grip = grip_area();
275  int mid_height = grip.h - top_img->h - bottom_img->h;
276  if (mid_height <= 0) {
277  // For now, minimum size of the middle piece is 1.
278  // This should never really be encountered, and if it is,
279  // it's a symptom of a larger problem, I think.
280  mid_height = 1;
281  }
282 
283  if(mid_scaled_.null() || mid_scaled_->h != mid_height) {
284  mid_scaled_.assign(scale_surface(mid_img, mid_img->w, mid_height));
285  }
286 
287  SDL_Rect groove = groove_area();
288  int groove_height = groove.h - top_grv->h - bottom_grv->h;
289  if (groove_height <= 0) {
290  groove_height = 1;
291  }
292 
293  if (groove_scaled_.null() || groove_scaled_->h != groove_height) {
294  groove_scaled_.assign(scale_surface(mid_grv, mid_grv->w, groove_height));
295  }
296 
297  if (mid_scaled_.null() || groove_scaled_.null()) {
298  std::cerr << "Failure during scrollbar image scale.\n";
299  return;
300  }
301 
302  if (grip.h > groove.h) {
303  std::cerr << "abort draw scrollbar: grip too large\n";
304  return;
305  }
306 
307  // Draw scrollbar "groove"
308  video().blit_surface(groove.x, groove.y, top_grv);
309  video().blit_surface(groove.x, groove.y + top_grv->h, groove_scaled_);
310  video().blit_surface(groove.x, groove.y + top_grv->h + groove_height, bottom_grv);
311 
312  // Draw scrollbar "grip"
313  video().blit_surface(grip.x, grip.y, top_img);
314  video().blit_surface(grip.x, grip.y + top_img->h, mid_scaled_);
315  video().blit_surface(grip.x, grip.y + top_img->h + mid_height, bottom_img);
316 }
317 
318 void scrollbar::handle_event(const SDL_Event& event)
319 {
321 
322  if (mouse_locked() || hidden())
323  return;
324 
325  STATE new_state = state_;
326  const SDL_Rect& grip = grip_area();
327  const SDL_Rect& groove = groove_area();
328 
329 
330  switch (event.type) {
331  case SDL_MOUSEBUTTONUP:
332  {
333  const SDL_MouseButtonEvent& e = event.button;
334  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
335  new_state = on_grip ? ACTIVE : NORMAL;
336  break;
337  }
338  case SDL_MOUSEBUTTONDOWN:
339  {
340  const SDL_MouseButtonEvent& e = event.button;
341  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
342  bool on_groove = sdl::point_in_rect(e.x, e.y, groove);
343  if (on_grip && e.button == SDL_BUTTON_LEFT) {
344  mousey_on_grip_ = e.y - grip.y;
345  new_state = DRAGGED;
346  } else if (on_groove && e.button == SDL_BUTTON_LEFT && groove.h != grip.h) {
347  if (e.y < grip.y)
348  move_position(-static_cast<int>(grip_height_));
349  else
351  } else if (on_groove && e.button == SDL_BUTTON_MIDDLE && groove.h != grip.h) {
352  int y_dep = e.y - grip.y - grip.h/2;
353  int dep = y_dep * int(full_height_ - grip_height_)/ (groove.h - grip.h);
354  move_position(dep);
355  }
356  break;
357  }
358  case SDL_MOUSEMOTION:
359  {
360  const SDL_MouseMotionEvent& e = event.motion;
361  if (state_ == NORMAL || state_ == ACTIVE) {
362  bool on_grip = sdl::point_in_rect(e.x, e.y, grip);
363  new_state = on_grip ? ACTIVE : NORMAL;
364  } else if (state_ == DRAGGED && groove.h != grip.h) {
365  int y_dep = e.y - grip.y - mousey_on_grip_;
366  int dep = y_dep * static_cast<int>(full_height_ - grip_height_) / (groove.h - grip.h);
367  move_position(dep);
368  }
369  break;
370  }
371  case SDL_MOUSEWHEEL:
372  {
373  const SDL_MouseWheelEvent& e = event.wheel;
374  int x, y;
375  SDL_GetMouseState(&x, &y);
376  bool on_groove = sdl::point_in_rect(x, y, groove);
377  if (on_groove && e.y < 0) {
379  } else if (on_groove && e.y > 0) {
381  }
382  break;
383  }
384  default:
385  break;
386  }
387 
388 
389  if (new_state != state_) {
390  set_dirty();
391  mid_scaled_.assign(nullptr);
392  state_ = new_state;
393  }
394 }
395 
396 } // end namespace gui
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:1038
std::vector< char_t > string
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:177
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:273
bool hidden() const
Definition: widget.cpp:196
unsigned get_position() const
Determine where the scrollbar is.
Definition: scrollbar.cpp:105
virtual void handle_event(const SDL_Event &event)
Definition: scrollbar.cpp:318
Definition: video.hpp:35
virtual void enable(bool new_val=true)
Definition: button.cpp:291
button downarrow_
Definition: scrollbar.hpp:91
surface groove_scaled_
Definition: scrollbar.hpp:89
int minimum_grip_height_
Definition: scrollbar.hpp:96
unsigned int full_height_
Definition: scrollbar.hpp:98
unsigned get_max_position() const
Definition: scrollbar.cpp:110
General purpose widgets.
void set_width(int w)
Definition: widget.cpp:117
SDL_Rect grip_area() const
Definition: scrollbar.cpp:221
virtual void process_event()
Definition: scrollbar.cpp:198
#define h
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:112
button uparrow_
Definition: scrollbar.hpp:91
-file util.hpp
bool null() const
Definition: surface.hpp:79
void scroll_down()
Scrolls down one step.
Definition: scrollbar.cpp:188
void set_dirty(bool dirty=true)
Definition: widget.cpp:215
virtual void handle_event(const SDL_Event &)
Definition: widget.cpp:338
virtual void update_location(const SDL_Rect &rect)
Definition: scrollbar.cpp:84
virtual void hide(bool value=true)
Definition: widget.cpp:160
const SDL_Rect & location() const
Definition: widget.cpp:142
virtual void hide(bool value=true)
Definition: scrollbar.cpp:98
bool pressed()
Definition: button.cpp:582
virtual void set_location(const SDL_Rect &rect)
Definition: widget.cpp:83
void set_position(unsigned pos)
Manually update the scrollbar.
Definition: scrollbar.cpp:115
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:98
void adjust_position(unsigned pos)
Ensure the viewport contains the position.
Definition: scrollbar.cpp:127
SDL_Rect groove_area() const
Definition: scrollbar.cpp:207
virtual void draw_contents()
Definition: scrollbar.cpp:233
int height() const
Definition: widget.cpp:137
virtual sdl_handler_vector handler_members()
Definition: scrollbar.cpp:76
void scroll_up()
Scrolls up one step.
Definition: scrollbar.cpp:193
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.
void set_shown_size(unsigned h)
Set the relative size of the grip.
Definition: scrollbar.cpp:144
surface mid_scaled_
Definition: scrollbar.hpp:89
bool is_valid_height(int height) const
Return true if the scrollbar has a valid size.
Definition: scrollbar.cpp:177
scrollbar(CVideo &video)
Create a scrollbar.
Definition: scrollbar.cpp:48
void set_full_size(unsigned h)
Set the relative size of the scrollbar.
Definition: scrollbar.cpp:158
void set_scroll_rate(unsigned r)
Set scroll rate.
Definition: scrollbar.cpp:172
#define e
unsigned int grip_position_
Definition: scrollbar.hpp:98
virtual void update_location(const SDL_Rect &rect)
Definition: widget.cpp:97
void move_position(int dep)
Move the scrollbar.
Definition: scrollbar.cpp:135