The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
scrollbar.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2004 - 2025
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
28namespace {
29
30const std::string scrollbar_top = "buttons/scrollbars/scrolltop.png";
31const std::string scrollbar_bottom = "buttons/scrollbars/scrollbottom.png";
32const std::string scrollbar_mid = "buttons/scrollbars/scrollmid.png";
33
34const std::string scrollbar_top_hl = "buttons/scrollbars/scrolltop-active.png";
35const std::string scrollbar_bottom_hl = "buttons/scrollbars/scrollbottom-active.png";
36const std::string scrollbar_mid_hl = "buttons/scrollbars/scrollmid-active.png";
37
38const std::string scrollbar_top_pressed = "buttons/scrollbars/scrolltop-pressed.png";
39const std::string scrollbar_bottom_pressed = "buttons/scrollbars/scrollbottom-pressed.png";
40const std::string scrollbar_mid_pressed = "buttons/scrollbars/scrollmid-pressed.png";
41
42}
43
44namespace 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
66{
67 return grip_position_;
68}
69
71{
73}
74
75void scrollbar::set_position(unsigned pos)
76{
77 if (pos > full_height_ - grip_height_)
79 if (pos == grip_position_)
80 return;
81 grip_position_ = pos;
83}
84
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
138
143
145{
146 const rect& loc = location();
148 return loc;
149 int h = static_cast<int>(loc.h) * grip_height_ / full_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 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 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 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
219void 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_EVENT_MOUSE_BUTTON_UP:
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_EVENT_MOUSE_BUTTON_DOWN:
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_EVENT_MOUSE_MOTION:
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_EVENT_MOUSE_WHEEL:
273 {
274 const SDL_MouseWheelEvent& e = event.wheel;
275 bool on_groove = groove.contains(sdl::get_mouse_location());
276 if (on_groove && e.y < 0) {
278 } else if (on_groove && e.y > 0) {
280 }
281 break;
282 }
283 default:
284 break;
285 }
286
287
288 if (new_state != state_) {
289 state_ = new_state;
290 queue_redraw();
291 }
292}
293
294} // end namespace gui
map_location loc
Definition move.cpp:172
scrollbar()
Create a scrollbar.
Definition scrollbar.cpp:46
void set_shown_size(unsigned h)
Set the relative size of the grip.
virtual void draw_contents()
void set_scroll_rate(unsigned r)
Set scroll rate.
unsigned int grip_position_
Definition scrollbar.hpp:83
rect grip_area() const
void scroll_up()
Scrolls up one step.
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.
void set_full_size(unsigned h)
Set the relative size of the scrollbar.
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)
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:103
int h() const
The draw-space height of the texture, in pixels.
Definition texture.hpp:112
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:296
void fill()
Definition draw.cpp:115
void blit(const texture &tex, const ::rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition draw.cpp:409
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:955
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition picture.cpp:815
point get_mouse_location()
Returns the current mouse location in draw space.
Definition input.cpp:49
Contains the SDL_Rect helper code.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition color.hpp:51
Holds a 2D point.
Definition point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition rect.cpp:54
#define e
#define h
#define c