LibGUI: Add visual clue to Scrollbar gutter

This adds a visual clue to scrolling by clicking on the Scrollbar
gutter. This gives the user a hint that scrolling will continue in the
direction of the darkened gutter, until the mouse is released.
It is inspired by very similar behavior on old windows.
This commit is contained in:
FrHun 2021-11-02 12:01:18 +01:00 committed by Brian Gianforcaro
parent 52e01b46eb
commit 86363ffe6e
2 changed files with 44 additions and 3 deletions

View file

@ -174,6 +174,30 @@ void Scrollbar::paint_event(PaintEvent& event)
hovered_component_for_painting = Component::None;
painter.fill_rect_with_dither_pattern(rect(), palette().button().lightened(1.3f), palette().button());
if (gutter_click_state != GutterClickState::NotPressed && has_scrubber() && hovered_component_for_painting == Component::Gutter) {
VERIFY(!scrubber_rect().is_null());
Gfx::IntRect rect_to_fill = rect();
if (orientation() == Orientation::Vertical) {
if (gutter_click_state == GutterClickState::BeforeScrubber) {
rect_to_fill.set_top(decrement_button_rect().bottom());
rect_to_fill.set_bottom(scrubber_rect().top());
} else {
VERIFY(gutter_click_state == GutterClickState::AfterScrubber);
rect_to_fill.set_top(scrubber_rect().bottom());
rect_to_fill.set_bottom(increment_button_rect().top());
}
} else {
if (gutter_click_state == GutterClickState::BeforeScrubber) {
rect_to_fill.set_left(decrement_button_rect().right());
rect_to_fill.set_right(scrubber_rect().left());
} else {
VERIFY(gutter_click_state == GutterClickState::AfterScrubber);
rect_to_fill.set_left(scrubber_rect().right());
rect_to_fill.set_right(increment_button_rect().left());
}
}
painter.fill_rect_with_dither_pattern(rect_to_fill, palette().button(), palette().button().lightened(0.77f));
}
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);
@ -213,9 +237,15 @@ void Scrollbar::on_automatic_scrolling_timer_fired()
}
if (m_pressed_component == Component::Gutter && component_at_position(m_last_mouse_position) == Component::Gutter) {
scroll_by_page(m_last_mouse_position);
m_hovered_component = component_at_position(m_last_mouse_position);
if (m_hovered_component != component_at_position(m_last_mouse_position)) {
m_hovered_component = component_at_position(m_last_mouse_position);
if (m_hovered_component != Component::Gutter)
gutter_click_state = GutterClickState::NotPressed;
update();
}
return;
}
gutter_click_state = GutterClickState::NotPressed;
}
void Scrollbar::mousedown_event(MouseEvent& event)
@ -286,6 +316,7 @@ void Scrollbar::set_automatic_scrolling_active(bool active, Component pressed_co
m_automatic_scrolling_timer->start();
} else {
m_automatic_scrolling_timer->stop();
gutter_click_state = GutterClickState::NotPressed;
}
}
@ -296,10 +327,13 @@ void Scrollbar::scroll_by_page(const Gfx::IntPoint& click_position)
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()))
if (click_position.primary_offset_for_orientation(orientation()) < scrubber_rect().primary_offset_for_orientation(orientation())) {
gutter_click_state = GutterClickState::BeforeScrubber;
set_value(value() - page_increment);
else
} else {
gutter_click_state = GutterClickState::AfterScrubber;
set_value(value() + page_increment);
}
}
void Scrollbar::scroll_to_position(const Gfx::IntPoint& click_position)

View file

@ -41,6 +41,13 @@ protected:
virtual void change_event(Event&) override;
private:
enum class GutterClickState {
NotPressed,
BeforeScrubber,
AfterScrubber,
} gutter_click_state
= GutterClickState::NotPressed;
int default_button_size() const { return 16; }
int button_size() const { return length(orientation()) <= (default_button_size() * 2) ? length(orientation()) / 2 : default_button_size(); }
int button_width() const { return orientation() == Orientation::Vertical ? width() : button_size(); }