diff options
Diffstat (limited to 'chromium/ui/views/controls/scroll_view.cc')
-rw-r--r-- | chromium/ui/views/controls/scroll_view.cc | 192 |
1 files changed, 157 insertions, 35 deletions
diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index 81430515e58..1911f194535 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -23,13 +23,30 @@ #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/controls/focus_ring.h" +#include "ui/views/metadata/metadata_impl_macros.h" #include "ui/views/style/platform_style.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" namespace views { namespace { +// Returns the combined scroll amount given separate x and y offsets. This is +// used in the "treat all scroll events as horizontal" case when there is both +// an x and y offset and we do not want them to add in unintuitive ways. +// +// The current approach is to return whichever offset has the larger absolute +// value, which should at least handle the case in which the gesture is mostly +// vertical or horizontal. It does mean that for a gesture at 135° or 315° from +// the x axis there is a breakpoint where scroll direction reverses, but we do +// not typically expect users to try to scroll a horizontal-scroll-only view at +// this exact angle. +template <class T> +T CombineScrollOffsets(T x, T y) { + return std::abs(x) >= std::abs(y) ? x : y; +} + class ScrollCornerView : public View { public: ScrollCornerView() = default; @@ -303,11 +320,42 @@ gfx::Rect ScrollView::GetVisibleRect() const { contents_viewport_->height()); } -void ScrollView::SetHideHorizontalScrollBar(bool visible) { - if (hide_horizontal_scrollbar_ == visible) +void ScrollView::SetHorizontalScrollBarMode( + ScrollBarMode horizontal_scroll_bar_mode) { + if (horizontal_scroll_bar_mode_ == horizontal_scroll_bar_mode) + return; + horizontal_scroll_bar_mode_ = horizontal_scroll_bar_mode; + OnPropertyChanged(&horizontal_scroll_bar_mode_, kPropertyEffectsPaint); +} + +void ScrollView::SetVerticalScrollBarMode( + ScrollBarMode vertical_scroll_bar_mode) { + if (vertical_scroll_bar_mode_ == vertical_scroll_bar_mode) + return; + + // Enabling vertical scrolling is incompatible with all scrolling being + // interpreted as horizontal. + DCHECK(!treat_all_scroll_events_as_horizontal_ || + vertical_scroll_bar_mode == ScrollBarMode::kDisabled); + + vertical_scroll_bar_mode_ = vertical_scroll_bar_mode; + OnPropertyChanged(&vertical_scroll_bar_mode_, kPropertyEffectsPaint); +} + +void ScrollView::SetTreatAllScrollEventsAsHorizontal( + bool treat_all_scroll_events_as_horizontal) { + if (treat_all_scroll_events_as_horizontal_ == + treat_all_scroll_events_as_horizontal) { return; - hide_horizontal_scrollbar_ = visible; - OnPropertyChanged(&hide_horizontal_scrollbar_, kPropertyEffectsPaint); + } + treat_all_scroll_events_as_horizontal_ = + treat_all_scroll_events_as_horizontal; + OnPropertyChanged(&treat_all_scroll_events_as_horizontal_, + kPropertyEffectsNone); + + // Since this effectively disables vertical scrolling, don't show a + // vertical scrollbar. + SetVerticalScrollBarMode(ScrollBarMode::kDisabled); } void ScrollView::SetDrawOverflowIndicator(bool draw_overflow_indicator) { @@ -383,9 +431,10 @@ int ScrollView::GetHeightForWidth(int width) const { } void ScrollView::Layout() { - // When horizontal scrollbar is disabled, it should not matter - // if its OverlapsContent matches vertical bar's. - if (!hide_horizontal_scrollbar_) { + // When either scrollbar is disabled, it should not matter + // if its OverlapsContent matches other bar's. + if (horizontal_scroll_bar_mode_ == ScrollBarMode::kEnabled && + vertical_scroll_bar_mode_ == ScrollBarMode::kEnabled) { #if defined(OS_APPLE) // On Mac, scrollbars may update their style one at a time, so they may // temporarily be of different types. Refuse to lay out at this point. @@ -578,10 +627,10 @@ bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) { bool processed = false; // Give vertical scrollbar priority - if (vert_sb_->GetVisible()) + if (IsVerticalScrollEnabled()) processed = vert_sb_->OnKeyPressed(event); - if (!processed && horiz_sb_->GetVisible()) + if (!processed && IsHorizontalScrollEnabled()) processed = horiz_sb_->OnKeyPressed(event); return processed; @@ -590,12 +639,21 @@ bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) { bool ScrollView::OnMouseWheel(const ui::MouseWheelEvent& e) { bool processed = false; + const ui::MouseWheelEvent to_propagate = + treat_all_scroll_events_as_horizontal_ + ? ui::MouseWheelEvent( + e, CombineScrollOffsets(e.x_offset(), e.y_offset()), 0) + : e; + // TODO(https://crbug.com/615948): Use composited scrolling. - if (vert_sb_->GetVisible()) - processed = vert_sb_->OnMouseWheel(e); + if (IsVerticalScrollEnabled()) + processed = vert_sb_->OnMouseWheel(to_propagate); - if (horiz_sb_->GetVisible()) - processed = horiz_sb_->OnMouseWheel(e) || processed; + if (IsHorizontalScrollEnabled()) { + // When there is no vertical scrollbar, allow vertical scroll events to be + // interpreted as horizontal scroll events. + processed |= horiz_sb_->OnMouseWheel(to_propagate); + } return processed; } @@ -604,13 +662,28 @@ void ScrollView::OnScrollEvent(ui::ScrollEvent* event) { if (!contents_) return; + // Possibly force the scroll event to horizontal based on the configuration + // option. + ui::ScrollEvent e = + treat_all_scroll_events_as_horizontal_ + ? ui::ScrollEvent( + event->type(), event->location_f(), event->root_location_f(), + event->time_stamp(), event->flags(), + CombineScrollOffsets(event->x_offset(), event->y_offset()), + 0.0f, + CombineScrollOffsets(event->y_offset_ordinal(), + event->x_offset_ordinal()), + 0.0f, event->finger_count(), event->momentum_phase(), + event->scroll_event_phase()) + : *event; + ui::ScrollInputHandler* compositor_scroller = GetWidget()->GetCompositor()->scroll_input_handler(); if (compositor_scroller) { DCHECK(scroll_with_layers_enabled_); - if (compositor_scroller->OnScrollEvent(*event, contents_->layer())) { - event->SetHandled(); - event->StopPropagation(); + if (compositor_scroller->OnScrollEvent(e, contents_->layer())) { + e.SetHandled(); + e.StopPropagation(); } } @@ -618,9 +691,15 @@ void ScrollView::OnScrollEvent(ui::ScrollEvent* event) { // scrollbars that they may be about scroll, or that they may need to cancel // UI feedback once the scrolling direction is known. if (horiz_sb_) - horiz_sb_->ObserveScrollEvent(*event); + horiz_sb_->ObserveScrollEvent(e); if (vert_sb_) - vert_sb_->ObserveScrollEvent(*event); + vert_sb_->ObserveScrollEvent(e); + + // Need to copy state back to original event. + if (e.handled()) + event->SetHandled(); + if (e.stopped_propagation()) + event->StopPropagation(); } void ScrollView::OnGestureEvent(ui::GestureEvent* event) { @@ -632,14 +711,20 @@ void ScrollView::OnGestureEvent(ui::GestureEvent* event) { event->type() == ui::ET_GESTURE_SCROLL_END || event->type() == ui::ET_SCROLL_FLING_START; + // Note: we will not invert gesture events because it will be confusing to + // have a vertical finger gesture on a touchscreen cause the scroll pane to + // scroll horizontally. + // TODO(https://crbug.com/615948): Use composited scrolling. - if (vert_sb_->GetVisible()) { - if (vert_sb_->bounds().Contains(event->location()) || scroll_event) - vert_sb_->OnGestureEvent(event); + if (IsVerticalScrollEnabled() && + (scroll_event || (vert_sb_->GetVisible() && + vert_sb_->bounds().Contains(event->location())))) { + vert_sb_->OnGestureEvent(event); } - if (!event->handled() && horiz_sb_->GetVisible()) { - if (horiz_sb_->bounds().Contains(event->location()) || scroll_event) - horiz_sb_->OnGestureEvent(event); + if (!event->handled() && IsHorizontalScrollEnabled() && + (scroll_event || (horiz_sb_->GetVisible() && + horiz_sb_->bounds().Contains(event->location())))) { + horiz_sb_->OnGestureEvent(event); } } @@ -719,13 +804,13 @@ void ScrollView::ScrollToPosition(ScrollBar* source, int position) { return; gfx::ScrollOffset offset = CurrentOffset(); - if (source == horiz_sb_.get() && horiz_sb_->GetVisible()) { + if (source == horiz_sb_.get() && IsHorizontalScrollEnabled()) { position = AdjustPosition(offset.x(), position, contents_->width(), contents_viewport_->width()); if (offset.x() == position) return; offset.set_x(position); - } else if (source == vert_sb_.get() && vert_sb_->GetVisible()) { + } else if (source == vert_sb_.get() && IsVerticalScrollEnabled()) { position = AdjustPosition(offset.y(), position, contents_->height(), contents_viewport_->height()); if (offset.y() == position) @@ -783,8 +868,10 @@ void ScrollView::SetHeaderOrContents(View* parent, } void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) { - if (!contents_ || (!horiz_sb_->GetVisible() && !vert_sb_->GetVisible())) + if (!contents_ || + (!IsHorizontalScrollEnabled() && !IsVerticalScrollEnabled())) { return; + } // Figure out the maximums for this scroll view. const int contents_max_x = @@ -830,9 +917,19 @@ void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size, const gfx::Size& content_size, bool* horiz_is_shown, bool* vert_is_shown) const { - if (hide_horizontal_scrollbar_) { + const bool horizontal_enabled = + horizontal_scroll_bar_mode_ == ScrollBarMode::kEnabled; + const bool vertical_enabled = + vertical_scroll_bar_mode_ == ScrollBarMode::kEnabled; + if (!horizontal_enabled) { *horiz_is_shown = false; - *vert_is_shown = content_size.height() > vp_size.height(); + *vert_is_shown = + vertical_enabled && content_size.height() > vp_size.height(); + return; + } + if (!vertical_enabled) { + *vert_is_shown = false; + *horiz_is_shown = content_size.width() > vp_size.width(); return; } @@ -876,12 +973,12 @@ void ScrollView::UpdateScrollBarPositions() { return; const gfx::ScrollOffset offset = CurrentOffset(); - if (horiz_sb_->GetVisible()) { + if (IsHorizontalScrollEnabled()) { int vw = contents_viewport_->width(); int cw = contents_->width(); horiz_sb_->Update(vw, cw, offset.x()); } - if (vert_sb_->GetVisible()) { + if (IsVerticalScrollEnabled()) { int vh = contents_viewport_->height(); int ch = contents_->height(); vert_sb_->Update(vh, ch, offset.y()); @@ -919,6 +1016,28 @@ bool ScrollView::ScrollsWithLayers() const { return contents_viewport_->layer() != nullptr; } +bool ScrollView::IsHorizontalScrollEnabled() const { + switch (horizontal_scroll_bar_mode_) { + case ScrollBarMode::kDisabled: + return false; + case ScrollBarMode::kHiddenButEnabled: + return bool{horiz_sb_}; + case ScrollBarMode::kEnabled: + return horiz_sb_ && horiz_sb_->GetVisible(); + } +} + +bool ScrollView::IsVerticalScrollEnabled() const { + switch (vertical_scroll_bar_mode_) { + case ScrollBarMode::kDisabled: + return false; + case ScrollBarMode::kHiddenButEnabled: + return bool{vert_sb_}; + case ScrollBarMode::kEnabled: + return vert_sb_ && vert_sb_->GetVisible(); + } +} + void ScrollView::EnableViewportLayer() { if (DoesViewportOrScrollViewHaveLayer()) return; @@ -1013,20 +1132,21 @@ void ScrollView::PositionOverflowIndicators() { void ScrollView::UpdateOverflowIndicatorVisibility( const gfx::ScrollOffset& offset) { SetControlVisibility(more_content_top_.get(), - !draw_border_ && !header_ && vert_sb_->GetVisible() && + !draw_border_ && !header_ && IsVerticalScrollEnabled() && offset.y() > vert_sb_->GetMinPosition() && draw_overflow_indicator_); SetControlVisibility( more_content_bottom_.get(), !draw_border_ && vert_sb_->GetVisible() && !horiz_sb_->GetVisible() && offset.y() < vert_sb_->GetMaxPosition() && draw_overflow_indicator_); + SetControlVisibility(more_content_left_.get(), - !draw_border_ && horiz_sb_->GetVisible() && + !draw_border_ && IsHorizontalScrollEnabled() && offset.x() > horiz_sb_->GetMinPosition() && draw_overflow_indicator_); SetControlVisibility( more_content_right_.get(), - !draw_border_ && horiz_sb_->GetVisible() && !vert_sb_->GetVisible() && + !draw_border_ && IsHorizontalScrollEnabled() && !vert_sb_->GetVisible() && offset.x() < horiz_sb_->GetMaxPosition() && draw_overflow_indicator_); } @@ -1038,7 +1158,9 @@ ADD_PROPERTY_METADATA(base::Optional<ui::NativeTheme::ColorId>, BackgroundThemeColorId) ADD_PROPERTY_METADATA(bool, DrawOverflowIndicator) ADD_PROPERTY_METADATA(bool, HasFocusIndicator) -ADD_PROPERTY_METADATA(bool, HideHorizontalScrollBar) +ADD_PROPERTY_METADATA(ScrollView::ScrollBarMode, HorizontalScrollBarMode) +ADD_PROPERTY_METADATA(ScrollView::ScrollBarMode, VerticalScrollBarMode) +ADD_PROPERTY_METADATA(bool, TreatAllScrollEventsAsHorizontal) END_METADATA // VariableRowHeightScrollHelper ---------------------------------------------- |