2020-01-18 09:38:21 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 09:38:21 +01:00
|
|
|
*/
|
|
|
|
|
|
2020-02-16 09:17:49 +01:00
|
|
|
#include <LibCore/Timer.h>
|
2020-02-14 21:41:10 +01:00
|
|
|
#include <LibGUI/Painter.h>
|
2021-04-13 16:18:20 +02:00
|
|
|
#include <LibGUI/Scrollbar.h>
|
2020-02-14 21:41:10 +01:00
|
|
|
#include <LibGfx/CharacterBitmap.h>
|
2020-02-06 12:04:00 +01:00
|
|
|
#include <LibGfx/Palette.h>
|
|
|
|
|
#include <LibGfx/StylePainter.h>
|
2019-02-09 11:19:38 +01:00
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
REGISTER_WIDGET(GUI, Scrollbar)
|
2021-01-02 16:30:13 -07:00
|
|
|
|
2020-02-02 15:07:41 +01:00
|
|
|
namespace GUI {
|
|
|
|
|
|
2019-02-10 08:23:03 +01:00
|
|
|
static const char* s_up_arrow_bitmap_data = {
|
2019-02-10 11:57:19 +01:00
|
|
|
" "
|
|
|
|
|
" # "
|
|
|
|
|
" ### "
|
|
|
|
|
" ##### "
|
|
|
|
|
" ####### "
|
|
|
|
|
" ### "
|
|
|
|
|
" ### "
|
|
|
|
|
" ### "
|
|
|
|
|
" "
|
2019-02-10 08:23:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char* s_down_arrow_bitmap_data = {
|
2019-02-10 11:57:19 +01:00
|
|
|
" "
|
|
|
|
|
" ### "
|
|
|
|
|
" ### "
|
|
|
|
|
" ### "
|
|
|
|
|
" ####### "
|
|
|
|
|
" ##### "
|
|
|
|
|
" ### "
|
|
|
|
|
" # "
|
|
|
|
|
" "
|
2019-02-10 08:23:03 +01:00
|
|
|
};
|
|
|
|
|
|
2019-02-10 12:26:58 +01:00
|
|
|
static const char* s_left_arrow_bitmap_data = {
|
|
|
|
|
" "
|
|
|
|
|
" # "
|
|
|
|
|
" ## "
|
|
|
|
|
" ###### "
|
|
|
|
|
" ####### "
|
|
|
|
|
" ###### "
|
|
|
|
|
" ## "
|
|
|
|
|
" # "
|
|
|
|
|
" "
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char* s_right_arrow_bitmap_data = {
|
|
|
|
|
" "
|
|
|
|
|
" # "
|
|
|
|
|
" ## "
|
|
|
|
|
" ###### "
|
|
|
|
|
" ####### "
|
|
|
|
|
" ###### "
|
|
|
|
|
" ## "
|
|
|
|
|
" # "
|
|
|
|
|
" "
|
|
|
|
|
};
|
|
|
|
|
|
2020-02-06 11:56:38 +01:00
|
|
|
static Gfx::CharacterBitmap* s_up_arrow_bitmap;
|
|
|
|
|
static Gfx::CharacterBitmap* s_down_arrow_bitmap;
|
|
|
|
|
static Gfx::CharacterBitmap* s_left_arrow_bitmap;
|
|
|
|
|
static Gfx::CharacterBitmap* s_right_arrow_bitmap;
|
2019-02-10 08:23:03 +01:00
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
Scrollbar::Scrollbar(Orientation orientation)
|
2020-12-30 15:20:02 +01:00
|
|
|
: AbstractSlider(orientation)
|
2019-02-09 11:19:38 +01:00
|
|
|
{
|
2020-02-23 10:31:26 +01:00
|
|
|
m_automatic_scrolling_timer = add<Core::Timer>();
|
2019-02-10 08:23:03 +01:00
|
|
|
if (!s_up_arrow_bitmap)
|
2020-02-06 11:56:38 +01:00
|
|
|
s_up_arrow_bitmap = &Gfx::CharacterBitmap::create_from_ascii(s_up_arrow_bitmap_data, 9, 9).leak_ref();
|
2019-02-10 08:23:03 +01:00
|
|
|
if (!s_down_arrow_bitmap)
|
2020-02-06 11:56:38 +01:00
|
|
|
s_down_arrow_bitmap = &Gfx::CharacterBitmap::create_from_ascii(s_down_arrow_bitmap_data, 9, 9).leak_ref();
|
2019-02-10 12:26:58 +01:00
|
|
|
if (!s_left_arrow_bitmap)
|
2020-02-06 11:56:38 +01:00
|
|
|
s_left_arrow_bitmap = &Gfx::CharacterBitmap::create_from_ascii(s_left_arrow_bitmap_data, 9, 9).leak_ref();
|
2019-02-10 12:26:58 +01:00
|
|
|
if (!s_right_arrow_bitmap)
|
2020-02-06 11:56:38 +01:00
|
|
|
s_right_arrow_bitmap = &Gfx::CharacterBitmap::create_from_ascii(s_right_arrow_bitmap_data, 9, 9).leak_ref();
|
2019-02-10 11:57:19 +01:00
|
|
|
|
2020-12-30 15:20:02 +01:00
|
|
|
if (orientation == Orientation::Vertical) {
|
2020-12-30 01:23:32 +01:00
|
|
|
set_fixed_width(16);
|
2020-12-30 01:55:25 +01:00
|
|
|
} else {
|
|
|
|
|
set_fixed_height(16);
|
2019-02-10 11:57:19 +01:00
|
|
|
}
|
2019-06-07 10:43:10 +02:00
|
|
|
|
2019-09-20 15:19:46 +02:00
|
|
|
m_automatic_scrolling_timer->set_interval(100);
|
|
|
|
|
m_automatic_scrolling_timer->on_timeout = [this] {
|
2019-06-07 10:43:10 +02:00
|
|
|
on_automatic_scrolling_timer_fired();
|
|
|
|
|
};
|
2019-02-09 11:19:38 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
Scrollbar::~Scrollbar()
|
2019-02-09 11:19:38 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
Gfx::IntRect Scrollbar::decrement_button_rect() const
|
2019-02-09 11:19:38 +01:00
|
|
|
{
|
2019-04-11 13:16:43 +02:00
|
|
|
return { 0, 0, button_width(), button_height() };
|
2019-02-09 11:19:38 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
Gfx::IntRect Scrollbar::increment_button_rect() const
|
2019-02-09 11:19:38 +01:00
|
|
|
{
|
2019-02-10 12:26:58 +01:00
|
|
|
if (orientation() == Orientation::Vertical)
|
2019-04-11 13:16:43 +02:00
|
|
|
return { 0, height() - button_height(), button_width(), button_height() };
|
2019-02-10 12:26:58 +01:00
|
|
|
else
|
2019-04-11 13:16:43 +02:00
|
|
|
return { width() - button_width(), 0, button_width(), button_height() };
|
2019-02-09 11:19:38 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
int Scrollbar::scrubbable_range_in_pixels() const
|
2019-02-10 06:51:01 +01:00
|
|
|
{
|
2019-02-10 12:26:58 +01:00
|
|
|
if (orientation() == Orientation::Vertical)
|
2020-08-11 21:32:40 -04:00
|
|
|
return height() - button_height() * 2 - visible_scrubber_size();
|
2019-02-10 12:26:58 +01:00
|
|
|
else
|
2020-08-11 21:32:40 -04:00
|
|
|
return width() - button_width() * 2 - visible_scrubber_size();
|
2019-02-10 06:51:01 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
bool Scrollbar::has_scrubber() const
|
2019-02-10 07:11:01 +01:00
|
|
|
{
|
2020-12-30 15:20:02 +01:00
|
|
|
return max() != min();
|
2019-02-10 07:11:01 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
int Scrollbar::unclamped_scrubber_size() const
|
2019-03-29 02:51:19 +01:00
|
|
|
{
|
2019-07-01 02:46:36 -05:00
|
|
|
int pixel_range = length(orientation()) - button_size() * 2;
|
2020-12-30 15:20:02 +01:00
|
|
|
int value_range = max() - min();
|
2020-09-18 09:49:51 +02:00
|
|
|
|
2020-07-08 22:12:24 -06:00
|
|
|
int scrubber_size = 0;
|
|
|
|
|
if (value_range > 0) {
|
|
|
|
|
// Scrubber size should be proportional to the visible portion
|
|
|
|
|
// (page) in relation to the content (value range + page)
|
2020-12-30 15:20:02 +01:00
|
|
|
scrubber_size = (page_step() * pixel_range) / (value_range + page_step());
|
2020-07-08 22:12:24 -06:00
|
|
|
}
|
2020-08-11 21:35:15 -04:00
|
|
|
return scrubber_size;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
int Scrollbar::visible_scrubber_size() const
|
2020-08-11 21:35:15 -04:00
|
|
|
{
|
|
|
|
|
return ::max(unclamped_scrubber_size(), button_size());
|
2019-03-29 02:51:19 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
Gfx::IntRect Scrollbar::scrubber_rect() const
|
2019-02-09 11:19:38 +01:00
|
|
|
{
|
2020-08-11 21:32:40 -04:00
|
|
|
if (!has_scrubber() || length(orientation()) <= (button_size() * 2) + visible_scrubber_size())
|
2019-06-07 11:46:02 +02:00
|
|
|
return {};
|
2019-02-10 12:26:58 +01:00
|
|
|
float x_or_y;
|
2020-12-30 15:20:02 +01:00
|
|
|
if (value() == min())
|
2019-03-27 20:48:23 +01:00
|
|
|
x_or_y = button_size();
|
2020-12-30 15:20:02 +01:00
|
|
|
else if (value() == max())
|
2020-08-11 21:32:40 -04:00
|
|
|
x_or_y = (length(orientation()) - button_size() - visible_scrubber_size()) + 1;
|
2019-02-10 12:26:58 +01:00
|
|
|
else {
|
2020-12-30 15:20:02 +01:00
|
|
|
float range_size = max() - min();
|
2019-02-10 12:26:58 +01:00
|
|
|
float available = scrubbable_range_in_pixels();
|
|
|
|
|
float step = available / range_size;
|
2020-12-30 15:20:02 +01:00
|
|
|
x_or_y = (button_size() + (step * value()));
|
2019-02-10 12:26:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (orientation() == Orientation::Vertical)
|
2020-08-11 21:32:40 -04:00
|
|
|
return { 0, (int)x_or_y, button_width(), visible_scrubber_size() };
|
2019-02-10 12:26:58 +01:00
|
|
|
else
|
2020-08-11 21:32:40 -04:00
|
|
|
return { (int)x_or_y, 0, visible_scrubber_size(), button_height() };
|
2019-02-09 11:19:38 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::paint_event(PaintEvent& event)
|
2019-02-09 11:19:38 +01:00
|
|
|
{
|
2020-02-02 15:07:41 +01:00
|
|
|
Painter painter(*this);
|
2019-03-29 15:01:54 +01:00
|
|
|
painter.add_clip_rect(event.rect());
|
2019-02-09 11:19:38 +01:00
|
|
|
|
LibGUI: Only paint ScrollBar hover states if a component is pressed
While left-mouse is pressed on any component (arrows, gutter, scrubber),
don't draw hover states for components other than the pressed component.
For example, while clicking the arrow-down button and then dragging
around, the arrow-up button and the scrubber now aren't highlighted.
This also means that during a gutter drag session, the scrubber
isn't highlighted while it's under the mouse cursor. That makes
sense, since we get the gutter drag behavior, not the scrubber
drag behavior, in this case.
The highlight is supposed to indicate "clickability", but if the
mouse is already down, they can't be clicked.
Now that I check for it, this seems to match the scrollbar behavior
on Windows.
2020-08-25 13:29:52 -04:00
|
|
|
Component hovered_component_for_painting = m_hovered_component;
|
2020-09-09 14:57:12 -04:00
|
|
|
if (!has_scrubber() || (m_pressed_component != Component::None && m_hovered_component != m_pressed_component))
|
LibGUI: Only paint ScrollBar hover states if a component is pressed
While left-mouse is pressed on any component (arrows, gutter, scrubber),
don't draw hover states for components other than the pressed component.
For example, while clicking the arrow-down button and then dragging
around, the arrow-up button and the scrubber now aren't highlighted.
This also means that during a gutter drag session, the scrubber
isn't highlighted while it's under the mouse cursor. That makes
sense, since we get the gutter drag behavior, not the scrubber
drag behavior, in this case.
The highlight is supposed to indicate "clickability", but if the
mouse is already down, they can't be clicked.
Now that I check for it, this seems to match the scrollbar behavior
on Windows.
2020-08-25 13:29:52 -04:00
|
|
|
hovered_component_for_painting = Component::None;
|
|
|
|
|
|
2020-05-10 01:00:21 +02:00
|
|
|
painter.fill_rect_with_dither_pattern(rect(), palette().button().lightened(1.3f), palette().button());
|
2019-02-09 11:19:38 +01:00
|
|
|
|
2021-04-10 22:06:19 +02:00
|
|
|
bool decrement_pressed = (m_pressed_component == Component::DecrementButton) && (m_pressed_component == m_hovered_component);
|
|
|
|
|
bool increment_pressed = (m_pressed_component == Component::IncrementButton) && (m_pressed_component == m_hovered_component);
|
2020-02-16 08:56:13 +01:00
|
|
|
|
LibGUI: Only paint ScrollBar hover states if a component is pressed
While left-mouse is pressed on any component (arrows, gutter, scrubber),
don't draw hover states for components other than the pressed component.
For example, while clicking the arrow-down button and then dragging
around, the arrow-up button and the scrubber now aren't highlighted.
This also means that during a gutter drag session, the scrubber
isn't highlighted while it's under the mouse cursor. That makes
sense, since we get the gutter drag behavior, not the scrubber
drag behavior, in this case.
The highlight is supposed to indicate "clickability", but if the
mouse is already down, they can't be clicked.
Now that I check for it, this seems to match the scrollbar behavior
on Windows.
2020-08-25 13:29:52 -04:00
|
|
|
Gfx::StylePainter::paint_button(painter, decrement_button_rect(), palette(), Gfx::ButtonStyle::Normal, decrement_pressed, hovered_component_for_painting == Component::DecrementButton);
|
|
|
|
|
Gfx::StylePainter::paint_button(painter, increment_button_rect(), palette(), Gfx::ButtonStyle::Normal, increment_pressed, hovered_component_for_painting == Component::IncrementButton);
|
2019-07-01 02:46:36 -05:00
|
|
|
|
|
|
|
|
if (length(orientation()) > default_button_size()) {
|
2020-02-16 08:56:13 +01:00
|
|
|
auto decrement_location = decrement_button_rect().location().translated(3, 3);
|
|
|
|
|
if (decrement_pressed)
|
2021-04-12 11:47:09 -07:00
|
|
|
decrement_location.translate_by(1, 1);
|
2020-12-30 16:08:04 +01:00
|
|
|
if (!has_scrubber() || !is_enabled())
|
|
|
|
|
painter.draw_bitmap(decrement_location.translated(1, 1), orientation() == Orientation::Vertical ? *s_up_arrow_bitmap : *s_left_arrow_bitmap, palette().threed_highlight());
|
|
|
|
|
painter.draw_bitmap(decrement_location, orientation() == Orientation::Vertical ? *s_up_arrow_bitmap : *s_left_arrow_bitmap, (has_scrubber() && is_enabled()) ? palette().button_text() : palette().threed_shadow1());
|
2020-02-16 08:56:13 +01:00
|
|
|
|
|
|
|
|
auto increment_location = increment_button_rect().location().translated(3, 3);
|
|
|
|
|
if (increment_pressed)
|
2021-04-12 11:47:09 -07:00
|
|
|
increment_location.translate_by(1, 1);
|
2020-12-30 16:08:04 +01:00
|
|
|
if (!has_scrubber() || !is_enabled())
|
|
|
|
|
painter.draw_bitmap(increment_location.translated(1, 1), orientation() == Orientation::Vertical ? *s_down_arrow_bitmap : *s_right_arrow_bitmap, palette().threed_highlight());
|
|
|
|
|
painter.draw_bitmap(increment_location, orientation() == Orientation::Vertical ? *s_down_arrow_bitmap : *s_right_arrow_bitmap, (has_scrubber() && is_enabled()) ? palette().button_text() : palette().threed_shadow1());
|
2019-07-01 02:46:36 -05:00
|
|
|
}
|
2019-02-09 11:19:38 +01:00
|
|
|
|
2019-02-10 07:11:01 +01:00
|
|
|
if (has_scrubber())
|
LibGUI: Only paint ScrollBar hover states if a component is pressed
While left-mouse is pressed on any component (arrows, gutter, scrubber),
don't draw hover states for components other than the pressed component.
For example, while clicking the arrow-down button and then dragging
around, the arrow-up button and the scrubber now aren't highlighted.
This also means that during a gutter drag session, the scrubber
isn't highlighted while it's under the mouse cursor. That makes
sense, since we get the gutter drag behavior, not the scrubber
drag behavior, in this case.
The highlight is supposed to indicate "clickability", but if the
mouse is already down, they can't be clicked.
Now that I check for it, this seems to match the scrollbar behavior
on Windows.
2020-08-25 13:29:52 -04:00
|
|
|
Gfx::StylePainter::paint_button(painter, scrubber_rect(), palette(), Gfx::ButtonStyle::Normal, false, hovered_component_for_painting == Component::Scrubber || m_pressed_component == Component::Scrubber);
|
2019-02-09 11:19:38 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::on_automatic_scrolling_timer_fired()
|
2019-06-07 10:43:10 +02:00
|
|
|
{
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_pressed_component == Component::DecrementButton && component_at_position(m_last_mouse_position) == Component::DecrementButton) {
|
2020-12-30 15:20:02 +01:00
|
|
|
set_value(value() - step());
|
2019-06-07 10:43:10 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_pressed_component == Component::IncrementButton && component_at_position(m_last_mouse_position) == Component::IncrementButton) {
|
2020-12-30 15:20:02 +01:00
|
|
|
set_value(value() + step());
|
2019-06-07 10:43:10 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_pressed_component == Component::Gutter && component_at_position(m_last_mouse_position) == Component::Gutter) {
|
2020-08-25 11:14:45 -04:00
|
|
|
scroll_by_page(m_last_mouse_position);
|
2020-08-25 14:00:38 -04:00
|
|
|
m_hovered_component = component_at_position(m_last_mouse_position);
|
2020-08-25 11:14:45 -04:00
|
|
|
return;
|
|
|
|
|
}
|
2019-06-07 10:43:10 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::mousedown_event(MouseEvent& event)
|
2019-02-09 11:19:38 +01:00
|
|
|
{
|
2020-02-02 15:07:41 +01:00
|
|
|
if (event.button() != MouseButton::Left)
|
2019-02-09 11:19:38 +01:00
|
|
|
return;
|
2020-02-16 08:56:13 +01:00
|
|
|
if (!has_scrubber())
|
|
|
|
|
return;
|
|
|
|
|
|
2020-08-25 11:04:53 -04:00
|
|
|
m_last_mouse_position = event.position();
|
2020-08-25 12:54:40 -04:00
|
|
|
m_pressed_component = component_at_position(m_last_mouse_position);
|
2020-08-25 10:31:20 -04:00
|
|
|
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_pressed_component == Component::DecrementButton) {
|
|
|
|
|
set_automatic_scrolling_active(true, Component::DecrementButton);
|
2020-02-16 08:56:13 +01:00
|
|
|
update();
|
2019-02-09 11:19:38 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_pressed_component == Component::IncrementButton) {
|
|
|
|
|
set_automatic_scrolling_active(true, Component::IncrementButton);
|
2020-02-16 08:56:13 +01:00
|
|
|
update();
|
2019-02-09 11:19:38 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 08:16:51 -04:00
|
|
|
|
2020-08-25 10:31:20 -04:00
|
|
|
if (event.shift()) {
|
2020-08-25 08:16:51 -04:00
|
|
|
scroll_to_position(event.position());
|
2020-08-25 12:54:40 -04:00
|
|
|
m_pressed_component = component_at_position(event.position());
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY(m_pressed_component == Component::Scrubber);
|
2020-08-25 10:31:20 -04:00
|
|
|
}
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_pressed_component == Component::Scrubber) {
|
2019-02-10 06:51:01 +01:00
|
|
|
m_scrub_start_value = value();
|
|
|
|
|
m_scrub_origin = event.position();
|
2019-02-10 07:11:01 +01:00
|
|
|
update();
|
2019-02-10 06:51:01 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY(!event.shift());
|
2019-06-07 10:56:30 +02:00
|
|
|
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY(m_pressed_component == Component::Gutter);
|
2020-08-25 12:54:40 -04:00
|
|
|
set_automatic_scrolling_active(true, Component::Gutter);
|
2020-08-25 11:14:45 -04:00
|
|
|
update();
|
2019-02-10 06:51:01 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::mouseup_event(MouseEvent& event)
|
2019-02-10 06:51:01 +01:00
|
|
|
{
|
2020-02-02 15:07:41 +01:00
|
|
|
if (event.button() != MouseButton::Left)
|
2019-02-10 06:51:01 +01:00
|
|
|
return;
|
2020-08-25 12:54:40 -04:00
|
|
|
set_automatic_scrolling_active(false, Component::None);
|
2019-02-10 07:11:01 +01:00
|
|
|
update();
|
2019-02-10 06:51:01 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::mousewheel_event(MouseEvent& event)
|
2019-08-20 20:10:02 +02:00
|
|
|
{
|
|
|
|
|
if (!is_scrollable())
|
|
|
|
|
return;
|
2020-12-30 15:20:02 +01:00
|
|
|
set_value(value() + event.wheel_delta() * step());
|
2020-02-02 15:07:41 +01:00
|
|
|
Widget::mousewheel_event(event);
|
2019-08-20 20:10:02 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::set_automatic_scrolling_active(bool active, Component pressed_component)
|
2019-06-07 10:43:10 +02:00
|
|
|
{
|
2020-08-25 12:54:40 -04:00
|
|
|
m_pressed_component = pressed_component;
|
|
|
|
|
if (m_pressed_component == Component::Gutter)
|
2020-08-25 11:14:45 -04:00
|
|
|
m_automatic_scrolling_timer->set_interval(200);
|
|
|
|
|
else
|
|
|
|
|
m_automatic_scrolling_timer->set_interval(100);
|
2020-08-25 09:51:32 -04:00
|
|
|
|
2019-06-07 10:43:10 +02:00
|
|
|
if (active) {
|
|
|
|
|
on_automatic_scrolling_timer_fired();
|
2019-09-20 15:19:46 +02:00
|
|
|
m_automatic_scrolling_timer->start();
|
2019-06-07 10:43:10 +02:00
|
|
|
} else {
|
2019-09-20 15:19:46 +02:00
|
|
|
m_automatic_scrolling_timer->stop();
|
2019-06-07 10:43:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::scroll_by_page(const Gfx::IntPoint& click_position)
|
2020-08-11 21:35:15 -04:00
|
|
|
{
|
2020-12-30 15:20:02 +01:00
|
|
|
float range_size = max() - min();
|
2020-08-11 21:35:15 -04:00
|
|
|
float available = scrubbable_range_in_pixels();
|
|
|
|
|
float rel_scrubber_size = unclamped_scrubber_size() / available;
|
|
|
|
|
float page_increment = range_size * rel_scrubber_size;
|
|
|
|
|
|
|
|
|
|
if (click_position.primary_offset_for_orientation(orientation()) < scrubber_rect().primary_offset_for_orientation(orientation()))
|
|
|
|
|
set_value(value() - page_increment);
|
|
|
|
|
else
|
|
|
|
|
set_value(value() + page_increment);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::scroll_to_position(const Gfx::IntPoint& click_position)
|
2020-08-11 20:56:52 -04:00
|
|
|
{
|
2020-12-30 15:20:02 +01:00
|
|
|
float range_size = max() - min();
|
2020-08-11 20:56:52 -04:00
|
|
|
float available = scrubbable_range_in_pixels();
|
|
|
|
|
|
2020-08-11 21:33:03 -04:00
|
|
|
float x_or_y = ::max(0, click_position.primary_offset_for_orientation(orientation()) - button_width() - button_width() / 2);
|
|
|
|
|
float rel_x_or_y = x_or_y / available;
|
2020-12-30 15:20:02 +01:00
|
|
|
set_value(min() + rel_x_or_y * range_size);
|
2020-08-11 20:56:52 -04:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
Scrollbar::Component Scrollbar::component_at_position(const Gfx::IntPoint& position)
|
2020-08-25 10:31:20 -04:00
|
|
|
{
|
|
|
|
|
if (scrubber_rect().contains(position))
|
|
|
|
|
return Component::Scrubber;
|
|
|
|
|
if (decrement_button_rect().contains(position))
|
|
|
|
|
return Component::DecrementButton;
|
|
|
|
|
if (increment_button_rect().contains(position))
|
|
|
|
|
return Component::IncrementButton;
|
|
|
|
|
if (rect().contains(position))
|
|
|
|
|
return Component::Gutter;
|
2020-08-25 12:54:40 -04:00
|
|
|
return Component::None;
|
2020-08-25 10:31:20 -04:00
|
|
|
}
|
|
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::mousemove_event(MouseEvent& event)
|
2019-02-10 06:51:01 +01:00
|
|
|
{
|
2020-08-25 11:04:53 -04:00
|
|
|
m_last_mouse_position = event.position();
|
|
|
|
|
|
2019-04-06 13:55:56 +02:00
|
|
|
auto old_hovered_component = m_hovered_component;
|
2020-08-25 11:04:53 -04:00
|
|
|
m_hovered_component = component_at_position(m_last_mouse_position);
|
2019-06-07 10:43:10 +02:00
|
|
|
if (old_hovered_component != m_hovered_component) {
|
2019-04-06 13:55:56 +02:00
|
|
|
update();
|
2019-06-07 10:43:10 +02:00
|
|
|
}
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_pressed_component != Component::Scrubber)
|
2019-02-10 06:51:01 +01:00
|
|
|
return;
|
2019-02-10 12:26:58 +01:00
|
|
|
float delta = orientation() == Orientation::Vertical ? (event.y() - m_scrub_origin.y()) : (event.x() - m_scrub_origin.x());
|
2019-02-10 06:51:01 +01:00
|
|
|
float scrubbable_range = scrubbable_range_in_pixels();
|
2020-12-30 15:20:02 +01:00
|
|
|
float value_steps_per_scrubbed_pixel = (max() - min()) / scrubbable_range;
|
2019-02-10 06:51:01 +01:00
|
|
|
float new_value = m_scrub_start_value + (value_steps_per_scrubbed_pixel * delta);
|
|
|
|
|
set_value(new_value);
|
2019-02-09 11:19:38 +01:00
|
|
|
}
|
2019-04-06 13:55:56 +02:00
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::leave_event(Core::Event&)
|
2019-04-06 13:55:56 +02:00
|
|
|
{
|
2020-08-25 12:54:40 -04:00
|
|
|
if (m_hovered_component != Component::None) {
|
|
|
|
|
m_hovered_component = Component::None;
|
2019-04-10 01:50:10 +02:00
|
|
|
update();
|
|
|
|
|
}
|
2019-04-06 13:55:56 +02:00
|
|
|
}
|
2019-05-25 13:40:57 +02:00
|
|
|
|
2021-04-13 16:18:20 +02:00
|
|
|
void Scrollbar::change_event(Event& event)
|
2019-05-25 13:40:57 +02:00
|
|
|
{
|
2020-02-02 15:07:41 +01:00
|
|
|
if (event.type() == Event::Type::EnabledChange) {
|
2019-05-25 13:40:57 +02:00
|
|
|
if (!is_enabled())
|
2020-08-25 12:54:40 -04:00
|
|
|
set_automatic_scrolling_active(false, Component::None);
|
2019-05-25 13:40:57 +02:00
|
|
|
}
|
2020-02-02 15:07:41 +01:00
|
|
|
return Widget::change_event(event);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-25 13:40:57 +02:00
|
|
|
}
|