Commit 0597e2b8 authored by Dana Fried's avatar Dana Fried Committed by Commit Bot

Improvements to support scrollable tabstrip.

Creates a mode that allows scrolling without a scrollbar in either
direction as well as the ability to treat scroll-specific events (e.g.
mousewheel events) as horizontal scroll inputs.

Changes:
* Adds a third scrollbar state: visible but not enabled
* Allows enabled/disabled/invisible state for vertical scrollbar as well
as horizontal (previously only horizontal scrollbar could be hidden/
disabled).
* Adds the option to treat vertical scroll events as horizontal, so that
for example a mousewheel or vertical scroll gesture can allow a
horizontal-scroll-only pane to scroll.

Bug: 1122214
Change-Id: I7f01b26b33ede76e1903ee37a493a2818b7b1103
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2433254
Commit-Queue: Dana Fried <dfried@chromium.org>
Reviewed-by: default avatarRobert Liao <robliao@chromium.org>
Reviewed-by: default avatarCharlene Yan <cyan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813839}
parent 6e104a7d
......@@ -1163,7 +1163,8 @@ void AutofillPopupViewNativeViews::CreateChildViews() {
}
scroll_view_ = new views::ScrollView();
scroll_view_->SetHideHorizontalScrollBar(true);
scroll_view_->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
body_container_ = scroll_view_->SetContents(std::move(body_container));
scroll_view_->SetDrawOverflowIndicator(false);
scroll_view_->ClipHeightTo(0, body_container_->GetPreferredSize().height());
......
......@@ -160,7 +160,8 @@ std::unique_ptr<views::ScrollView> CreateCardList(
}
auto card_list_scroll_view = std::make_unique<views::ScrollView>();
card_list_scroll_view->SetHideHorizontalScrollBar(true);
card_list_scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
card_list_scroll_view->SetContents(std::move(card_list_view));
card_list_scroll_view->SetDrawOverflowIndicator(false);
constexpr int kCardListScrollViewHeight = 140;
......
......@@ -126,7 +126,8 @@ DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
screen_scroll_view->ClipHeightTo(
kGenericScreenStyle.item_size.height(),
kGenericScreenStyle.item_size.height() * 2);
screen_scroll_view->SetHideHorizontalScrollBar(true);
screen_scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
panes.push_back(
std::make_pair(screen_title_text, std::move(screen_scroll_view)));
......@@ -156,7 +157,8 @@ DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
window_scroll_view->ClipHeightTo(kWindowStyle.item_size.height(),
kWindowStyle.item_size.height() * 2);
window_scroll_view->SetHideHorizontalScrollBar(true);
window_scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
panes.push_back(
std::make_pair(window_title_text, std::move(window_scroll_view)));
......
......@@ -121,7 +121,8 @@ void ExtensionInstallBlockedDialogView::AddCustomMessageContents(
header_label->SizeToFit(content_width);
auto* scroll_view = AddChildView(std::make_unique<views::ScrollView>());
scroll_view->SetHideHorizontalScrollBar(true);
scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
scroll_view->SetContents(std::move(extension_info_container));
scroll_view->ClipHeightTo(
0, provider->GetDistanceMetric(
......
......@@ -520,7 +520,8 @@ void ExtensionInstallDialogView::CreateContents() {
}
scroll_view_ = new views::ScrollView();
scroll_view_->SetHideHorizontalScrollBar(true);
scroll_view_->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
scroll_view_->SetContents(std::move(extension_info_container));
scroll_view_->ClipHeightTo(
0, provider->GetDistanceMetric(
......
......@@ -132,7 +132,8 @@ void ExtensionsMenuView::Populate() {
auto scroll_view = std::make_unique<views::ScrollView>();
scroll_view->ClipHeightTo(0, kMaxExtensionButtonsHeightDp);
scroll_view->SetDrawOverflowIndicator(false);
scroll_view->SetHideHorizontalScrollBar(true);
scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
scroll_view->SetContents(std::move(extension_buttons));
AddChildView(std::move(scroll_view));
......
......@@ -17,6 +17,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/frame/browser_frame.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
......@@ -259,7 +260,13 @@ const char* BrowserRootView::GetClassName() const {
}
bool BrowserRootView::OnMouseWheel(const ui::MouseWheelEvent& event) {
if (browser_defaults::kScrollEventChangesTab) {
// TODO(dfried): See if it's possible to move this logic deeper into the view
// hierarchy - ideally to TabStripRegionView.
// Scroll-event-changes-tab is incompatible with scrolling tabstrip, so
// disable it if the latter feature is enabled.
if (browser_defaults::kScrollEventChangesTab &&
!base::FeatureList::IsEnabled(features::kScrollableTabStrip)) {
// Switch to the left/right tab if the wheel-scroll happens over the
// tabstrip, or the empty space beside the tabstrip.
views::View* hit_view = GetEventHandlerForPoint(event.location());
......
......@@ -31,7 +31,8 @@ TabStripRegionView::TabStripRegionView(std::unique_ptr<TabStrip> tab_strip) {
AddChildView(std::make_unique<views::ScrollView>(
views::ScrollView::ScrollWithLayers::kEnabled));
tab_strip_scroll_container->SetBackgroundColor(base::nullopt);
tab_strip_scroll_container->SetHideHorizontalScrollBar(true);
tab_strip_scroll_container->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
tab_strip_container_ = tab_strip_scroll_container;
tab_strip_scroll_container->SetContents(std::move(tab_strip));
} else {
......
......@@ -253,7 +253,8 @@ std::unique_ptr<views::View> PaymentRequestSheetController::CreateView() {
scroll_ = layout->AddView(DisplayDynamicBorderForHiddenContents()
? std::make_unique<BorderedScrollView>()
: std::make_unique<views::ScrollView>());
scroll_->SetHideHorizontalScrollBar(true);
scroll_->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
pane_ = scroll_->SetContents(std::make_unique<views::View>());
views::GridLayout* pane_layout =
pane_->SetLayoutManager(std::make_unique<views::GridLayout>());
......
......@@ -913,7 +913,8 @@ void ProfileMenuViewBase::Reset() {
// Create a scroll view to hold the components.
auto scroll_view = std::make_unique<views::ScrollView>();
scroll_view->SetHideHorizontalScrollBar(true);
scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
// TODO(https://crbug.com/871762): it's a workaround for the crash.
scroll_view->SetDrawOverflowIndicator(false);
scroll_view->ClipHeightTo(0, GetMaxHeight());
......
......@@ -460,7 +460,8 @@ void ParentPermissionDialogView::CreateContents() {
// Add section container to an enclosing scroll view.
auto scroll_view = std::make_unique<views::ScrollView>();
scroll_view->SetHideHorizontalScrollBar(true);
scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
scroll_view->SetContents(std::move(install_permissions_section_container));
scroll_view->ClipHeightTo(
0, provider->GetDistanceMetric(
......
......@@ -24,12 +24,28 @@
#include "ui/views/border.h"
#include "ui/views/controls/focus_ring.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 +319,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;
hide_horizontal_scrollbar_ = visible;
OnPropertyChanged(&hide_horizontal_scrollbar_, kPropertyEffectsPaint);
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;
}
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 +430,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 +626,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 +638,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 +661,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 +690,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,13 +710,19 @@ 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)
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)
if (!event->handled() && IsHorizontalScrollEnabled() &&
(scroll_event || (horiz_sb_->GetVisible() &&
horiz_sb_->bounds().Contains(event->location())))) {
horiz_sb_->OnGestureEvent(event);
}
}
......@@ -719,13 +803,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 +867,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 +916,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 +972,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 +1015,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 +1131,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 +1157,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 ----------------------------------------------
......
......@@ -50,6 +50,19 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
// Indicates whether or not scroll view is initialized with layer-scrolling.
enum class ScrollWithLayers { kDisabled, kEnabled };
// Controls how a scroll bar appears and functions.
enum class ScrollBarMode {
// The scrollbar is hidden, and the pane will not respond to e.g. mousewheel
// events even if the contents are larger than the viewport.
kDisabled,
// The scrollbar is hidden whether or not the contents are larger than the
// viewport, but the pane will respond to scroll events.
kHiddenButEnabled,
// The scrollbar will be visible if the contents are larger than the
// viewport and the pane will respond to scroll events.
kEnabled
};
ScrollView();
// Additional constructor for overriding scrolling as defined by
......@@ -110,8 +123,19 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
bool GetUseColorId() const { return !!background_color_id_; }
bool GetHideHorizontalScrollBar() const { return hide_horizontal_scrollbar_; }
void SetHideHorizontalScrollBar(bool visible);
ScrollBarMode GetHorizontalScrollBarMode() const {
return horizontal_scroll_bar_mode_;
}
ScrollBarMode GetVerticalScrollBarMode() const {
return vertical_scroll_bar_mode_;
}
bool GetTreatAllScrollEventsAsHorizontal() const {
return treat_all_scroll_events_as_horizontal_;
}
void SetHorizontalScrollBarMode(ScrollBarMode horizontal_scroll_bar_mode);
void SetVerticalScrollBarMode(ScrollBarMode vertical_scroll_bar_mode);
void SetTreatAllScrollEventsAsHorizontal(
bool treat_all_scroll_events_as_horizontal);
bool GetDrawOverflowIndicator() const { return draw_overflow_indicator_; }
void SetDrawOverflowIndicator(bool draw_overflow_indicator);
......@@ -165,6 +189,9 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
class Viewport;
bool IsHorizontalScrollEnabled() const;
bool IsVerticalScrollEnabled() const;
// Forces |contents_viewport_| to have a Layer (assuming it doesn't already).
void EnableViewportLayer();
......@@ -269,9 +296,14 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
base::Optional<ui::NativeTheme::ColorId> background_color_id_ =
ui::NativeTheme::kColorId_DialogBackground;
// If true, never show the horizontal scrollbar (even if the contents is wider
// than the viewport).
bool hide_horizontal_scrollbar_ = false;
// How to handle the case when the contents overflow the viewport.
ScrollBarMode horizontal_scroll_bar_mode_ = ScrollBarMode::kEnabled;
ScrollBarMode vertical_scroll_bar_mode_ = ScrollBarMode::kEnabled;
// Causes vertical scroll events (e.g. scrolling with the mousewheel) as
// horizontal events, to make scrolling in horizontal-only scroll situations
// easier for the user.
bool treat_all_scroll_events_as_horizontal_ = false;
// In Harmony, the indicator is a focus ring. Pre-Harmony, the indicator is a
// different border painter.
......@@ -298,7 +330,9 @@ VIEW_BUILDER_VIEW_TYPE_PROPERTY(View, Contents)
VIEW_BUILDER_VIEW_TYPE_PROPERTY(View, Header)
VIEW_BUILDER_PROPERTY(base::Optional<ui::NativeTheme::ColorId>,
BackgroundThemeColorId)
VIEW_BUILDER_PROPERTY(bool, HideHorizontalScrollBar)
VIEW_BUILDER_PROPERTY(ScrollView::ScrollBarMode, HorizontalScrollBarMode)
VIEW_BUILDER_PROPERTY(ScrollView::ScrollBarMode, VerticalScrollBarMode)
VIEW_BUILDER_PROPERTY(bool, TreatAllScrollEventsAsHorizontal)
VIEW_BUILDER_PROPERTY(bool, DrawOverflowIndicator)
VIEW_BUILDER_PROPERTY(base::Optional<SkColor>, BackgroundColor)
VIEW_BUILDER_VIEW_PROPERTY(ScrollBar, HorizontalScrollBar)
......
......@@ -65,7 +65,9 @@ class ScrollViewTestApi {
return gfx::Point() - gfx::ScrollOffsetToFlooredVector2d(CurrentOffset());
}
gfx::ScrollOffset CurrentOffset() { return scroll_view_->CurrentOffset(); }
gfx::ScrollOffset CurrentOffset() const {
return scroll_view_->CurrentOffset();
}
base::RetainingOneShotTimer* GetScrollBarHideTimer(
ScrollBarOrientation orientation) {
......@@ -183,6 +185,21 @@ class VerticalResizingView : public View {
DISALLOW_COPY_AND_ASSIGN(VerticalResizingView);
};
// Same as VerticalResizingView, but horizontal instead.
class HorizontalResizingView : public View {
public:
HorizontalResizingView() = default;
~HorizontalResizingView() override = default;
void Layout() override {
int height = 10000;
int width = parent()->width();
SetBounds(x(), y(), width, height);
}
private:
DISALLOW_COPY_AND_ASSIGN(HorizontalResizingView);
};
class TestScrollBarThumb : public BaseScrollBarThumb {
public:
using BaseScrollBarThumb::BaseScrollBarThumb;
......@@ -469,6 +486,53 @@ TEST_F(ScrollViewTest, VerticalScrollbarDoesNotAppearUnnecessarily) {
EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
// Same as above, but setting horizontal scroll bar to hidden.
TEST_F(ScrollViewTest, HorizontalScrollbarDoesNotAppearIfHidden) {
const gfx::Rect default_outer_bounds(0, 0, 100, 100);
scroll_view_->SetHorizontalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
scroll_view_->SetContents(std::make_unique<VerticalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
scroll_view_->Layout();
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
// Same as above, but setting vertical scrollbar instead.
TEST_F(ScrollViewTest, VerticalScrollbarDoesNotAppearIfHidden) {
const gfx::Rect default_outer_bounds(0, 0, 100, 100);
scroll_view_->SetVerticalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
scroll_view_->SetContents(std::make_unique<HorizontalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
scroll_view_->Layout();
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
// Same as above, but setting horizontal scroll bar to disabled.
TEST_F(ScrollViewTest, HorizontalScrollbarDoesNotAppearIfDisabled) {
const gfx::Rect default_outer_bounds(0, 0, 100, 100);
scroll_view_->SetHorizontalScrollBarMode(
ScrollView::ScrollBarMode::kDisabled);
scroll_view_->SetContents(std::make_unique<VerticalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
scroll_view_->Layout();
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
// Same as above, but setting vertical scrollbar instead.
TEST_F(ScrollViewTest, VerticallScrollbarDoesNotAppearIfDisabled) {
const gfx::Rect default_outer_bounds(0, 0, 100, 100);
scroll_view_->SetVerticalScrollBarMode(ScrollView::ScrollBarMode::kDisabled);
scroll_view_->SetContents(std::make_unique<HorizontalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
scroll_view_->Layout();
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
// Verifies the scrollbars are added as necessary.
// If on Mac, test the non-overlay scrollbars.
TEST_F(ScrollViewTest, ScrollBars) {
......@@ -742,6 +806,46 @@ TEST_F(ScrollViewTest, ScrollToPositionUpdatesScrollBar) {
EXPECT_GT(scroll_bar->GetPosition(), 0);
}
// Test that calling ScrollToPosition() also updates the position of the
// child view even when the horizontal scrollbar is hidden.
TEST_F(ScrollViewTest, ScrollToPositionUpdatesWithHiddenHorizontalScrollBar) {
scroll_view_->SetHorizontalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
ScrollViewTestApi test_api(scroll_view_.get());
View* contents = InstallContents();
contents->SetBounds(0, 0, 400, 50);
scroll_view_->Layout();
auto* scroll_bar = test_api.GetScrollBar(HORIZONTAL);
ASSERT_TRUE(scroll_bar);
EXPECT_FALSE(scroll_bar->GetVisible());
// We can't rely on the scrollbar, which may not be updated as it's not
// visible, but we can check the scroll offset itself.
EXPECT_EQ(0, test_api.CurrentOffset().x());
scroll_view_->ScrollToPosition(scroll_bar, 20);
EXPECT_GT(test_api.CurrentOffset().x(), 0);
}
// Test that calling ScrollToPosition() also updates the position of the
// child view even when the horizontal scrollbar is hidden.
TEST_F(ScrollViewTest, ScrollToPositionUpdatesWithHiddenVerticalScrollBar) {
scroll_view_->SetVerticalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
ScrollViewTestApi test_api(scroll_view_.get());
View* contents = InstallContents();
contents->SetBounds(0, 0, 50, 400);
scroll_view_->Layout();
auto* scroll_bar = test_api.GetScrollBar(VERTICAL);
ASSERT_TRUE(scroll_bar);
EXPECT_FALSE(scroll_bar->GetVisible());
// We can't rely on the scrollbar, which may not be updated as it's not
// visible, but we can check the scroll offset itself.
EXPECT_EQ(0, test_api.CurrentOffset().y());
scroll_view_->ScrollToPosition(scroll_bar, 20);
EXPECT_GT(test_api.CurrentOffset().y(), 0);
}
// Verifies ScrollRectToVisible() on the child works.
TEST_F(ScrollViewTest, ScrollRectToVisible) {
ScrollViewTestApi test_api(scroll_view_.get());
......@@ -769,6 +873,66 @@ TEST_F(ScrollViewTest, ScrollRectToVisible) {
EXPECT_EQ(415 - viewport_height, test_api.CurrentOffset().y());
}
// Verifies ScrollRectToVisible() scrolls the view horizontally even if the
// horizontal scrollbar is hidden (but not disabled).
TEST_F(ScrollViewTest, ScrollRectToVisibleWithHiddenHorizontalScrollbar) {
scroll_view_->SetHorizontalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
ScrollViewTestApi test_api(scroll_view_.get());
auto contents = std::make_unique<CustomView>();
contents->SetPreferredSize(gfx::Size(500, 1000));
auto* contents_ptr = scroll_view_->SetContents(std::move(contents));
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
scroll_view_->Layout();
EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString());
// Scroll to x=305 width=10, this should make the x position of the content
// at (305 + 10) - viewport_width (scroll region right aligned).
contents_ptr->ScrollRectToVisible(gfx::Rect(305, 0, 10, 10));
const int viewport_width = test_api.contents_viewport()->width();
// Expect there to be a vertical scrollbar, making the viewport shorter.
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(), viewport_width);
gfx::ScrollOffset offset = test_api.CurrentOffset();
EXPECT_EQ(315 - viewport_width, offset.x());
// Scroll to the current x-location and 10x10; should do nothing.
contents_ptr->ScrollRectToVisible(gfx::Rect(offset.x(), 0, 10, 10));
EXPECT_EQ(315 - viewport_width, test_api.CurrentOffset().x());
}
// Verifies ScrollRectToVisible() scrolls the view horizontally even if the
// horizontal scrollbar is hidden (but not disabled).
TEST_F(ScrollViewTest, ScrollRectToVisibleWithHiddenVerticalScrollbar) {
scroll_view_->SetVerticalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
ScrollViewTestApi test_api(scroll_view_.get());
auto contents = std::make_unique<CustomView>();
contents->SetPreferredSize(gfx::Size(1000, 500));
auto* contents_ptr = scroll_view_->SetContents(std::move(contents));
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
scroll_view_->Layout();
EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString());
// Scroll to y=305 height=10, this should make the y position of the content
// at (305 + 10) - viewport_height (scroll region bottom aligned).
contents_ptr->ScrollRectToVisible(gfx::Rect(0, 305, 10, 10));
const int viewport_height = test_api.contents_viewport()->height();
// Expect there to be a vertical scrollbar, making the viewport shorter.
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(), viewport_height);
gfx::ScrollOffset offset = test_api.CurrentOffset();
EXPECT_EQ(315 - viewport_height, offset.y());
// Scroll to the current x-location and 10x10; should do nothing.
contents_ptr->ScrollRectToVisible(gfx::Rect(0, offset.y(), 10, 10));
EXPECT_EQ(315 - viewport_height, test_api.CurrentOffset().y());
}
// Verifies that child scrolls into view when it's focused.
TEST_F(ScrollViewTest, ScrollChildToVisibleOnFocus) {
ScrollViewTestApi test_api(scroll_view_.get());
......@@ -1576,8 +1740,34 @@ TEST_F(ScrollViewTest, VerticalWithHeaderOverflowIndicators) {
EXPECT_FALSE(test_api.more_content_right()->GetVisible());
}
// Ensure ScrollView::Layout succeeds if a hidden scrollbar's overlap style
// Ensure ScrollView::Layout succeeds if a disabled scrollbar's overlap style
// does not match the other scrollbar.
TEST_F(ScrollViewTest, IgnoreOverlapWithDisabledHorizontalScroll) {
ScrollViewTestApi test_api(scroll_view_.get());
constexpr int kThickness = 1;
// Assume horizontal scroll bar is the default and is overlapping.
scroll_view_->SetHorizontalScrollBar(std::make_unique<TestScrollBar>(
/* horizontal */ true, /* overlaps_content */ true, kThickness));
// Assume vertical scroll bar is custom and it we want it to not overlap.
scroll_view_->SetVerticalScrollBar(std::make_unique<TestScrollBar>(
/* horizontal */ false, /* overlaps_content */ false, kThickness));
// Also, let's turn off horizontal scroll bar.
scroll_view_->SetHorizontalScrollBarMode(
ScrollView::ScrollBarMode::kDisabled);
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
scroll_view_->Layout();
gfx::Size expected_size = scroll_view_->size();
expected_size.Enlarge(-kThickness, 0);
EXPECT_EQ(expected_size, test_api.contents_viewport()->size());
}
// Ensure ScrollView::Layout succeeds if a hidden but enabled scrollbar's
// overlap style does not match the other scrollbar.
TEST_F(ScrollViewTest, IgnoreOverlapWithHiddenHorizontalScroll) {
ScrollViewTestApi test_api(scroll_view_.get());
......@@ -1590,7 +1780,8 @@ TEST_F(ScrollViewTest, IgnoreOverlapWithHiddenHorizontalScroll) {
/* horizontal */ false, /* overlaps_content */ false, kThickness));
// Also, let's turn off horizontal scroll bar.
scroll_view_->SetHideHorizontalScrollBar(true);
scroll_view_->SetHorizontalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
......@@ -1601,6 +1792,57 @@ TEST_F(ScrollViewTest, IgnoreOverlapWithHiddenHorizontalScroll) {
EXPECT_EQ(expected_size, test_api.contents_viewport()->size());
}
// Ensure ScrollView::Layout succeeds if a disabled scrollbar's overlap style
// does not match the other scrollbar.
TEST_F(ScrollViewTest, IgnoreOverlapWithDisabledVerticalScroll) {
ScrollViewTestApi test_api(scroll_view_.get());
constexpr int kThickness = 1;
// Assume horizontal scroll bar is custom and it we want it to not overlap.
scroll_view_->SetHorizontalScrollBar(std::make_unique<TestScrollBar>(
/* horizontal */ true, /* overlaps_content */ false, kThickness));
// Assume vertical scroll bar is the default and is overlapping.
scroll_view_->SetVerticalScrollBar(std::make_unique<TestScrollBar>(
/* horizontal */ false, /* overlaps_content */ true, kThickness));
// Also, let's turn off horizontal scroll bar.
scroll_view_->SetVerticalScrollBarMode(ScrollView::ScrollBarMode::kDisabled);
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
scroll_view_->Layout();
gfx::Size expected_size = scroll_view_->size();
expected_size.Enlarge(0, -kThickness);
EXPECT_EQ(expected_size, test_api.contents_viewport()->size());
}
// Ensure ScrollView::Layout succeeds if a hidden but enabled scrollbar's
// overlap style does not match the other scrollbar.
TEST_F(ScrollViewTest, IgnoreOverlapWithHiddenVerticalScroll) {
ScrollViewTestApi test_api(scroll_view_.get());
constexpr int kThickness = 1;
// Assume horizontal scroll bar is custom and it we want it to not overlap.
scroll_view_->SetHorizontalScrollBar(std::make_unique<TestScrollBar>(
/* horizontal */ true, /* overlaps_content */ false, kThickness));
// Assume vertical scroll bar is the default and is overlapping.
scroll_view_->SetVerticalScrollBar(std::make_unique<TestScrollBar>(
/* horizontal */ false, /* overlaps_content */ true, kThickness));
// Also, let's turn off horizontal scroll bar.
scroll_view_->SetVerticalScrollBarMode(
ScrollView::ScrollBarMode::kHiddenButEnabled);
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
scroll_view_->Layout();
gfx::Size expected_size = scroll_view_->size();
expected_size.Enlarge(0, -kThickness);
EXPECT_EQ(expected_size, test_api.contents_viewport()->size());
}
// Test scrolling behavior when clicking on the scroll track.
TEST_F(WidgetScrollViewTest, ScrollTrackScrolling) {
// Set up with a vertical scroller.
......@@ -1847,43 +2089,123 @@ TEST_P(WidgetScrollViewTestRTLAndLayers, ScrollOffsetUsingLayers) {
EXPECT_EQ(gfx::ScrollOffset(offset.x(), offset.y()), impl_offset);
}
// Tests to see the scroll events are handled correctly in composited and
// non-composited scrolling.
TEST_F(WidgetScrollViewTest, CompositedScrollEvents) {
// Set up with a vertical scroll bar.
ScrollView* scroll_view =
AddScrollViewWithContentSize(gfx::Size(10, kDefaultHeight * 5));
ScrollViewTestApi test_api(scroll_view);
namespace {
// Create a fake scroll event and send it to the scroll view.
ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), base::TimeTicks::Now(), 0,
0, -10, 0, -10, 3);
EXPECT_FALSE(scroll.handled());
EXPECT_FALSE(scroll.stopped_propagation());
scroll_view->OnScrollEvent(&scroll);
// Applies |scroll_event| to |scroll_view| and verifies that the event is
// applied correctly whether or not compositor scrolling is enabled.
static void ApplyScrollEvent(const ScrollViewTestApi& test_api,
ScrollView* scroll_view,
ui::ScrollEvent& scroll_event) {
EXPECT_FALSE(scroll_event.handled());
EXPECT_FALSE(scroll_event.stopped_propagation());
scroll_view->OnScrollEvent(&scroll_event);
// Check to see if the scroll event is handled by the scroll view.
if (base::FeatureList::IsEnabled(::features::kUiCompositorScrollWithLayers)) {
// If UiCompositorScrollWithLayers is enabled, the event is set handled
// and its propagation is stopped.
EXPECT_TRUE(scroll.handled());
EXPECT_TRUE(scroll.stopped_propagation());
EXPECT_TRUE(scroll_event.handled());
EXPECT_TRUE(scroll_event.stopped_propagation());
} else {
// If UiCompositorScrollWithLayers is disabled, the event isn't handled.
// This informs Widget::OnScrollEvent() to convert to a MouseWheel event
// and dispatch again. Simulate that.
EXPECT_FALSE(scroll.handled());
EXPECT_FALSE(scroll.stopped_propagation());
EXPECT_FALSE(scroll_event.handled());
EXPECT_FALSE(scroll_event.stopped_propagation());
EXPECT_EQ(gfx::ScrollOffset(), test_api.CurrentOffset());
ui::MouseWheelEvent wheel(scroll);
ui::MouseWheelEvent wheel(scroll_event);
scroll_view->OnMouseEvent(&wheel);
}
}
} // namespace
// Tests to see the scroll events are handled correctly in composited and
// non-composited scrolling.
TEST_F(WidgetScrollViewTest, CompositedScrollEvents) {
// Set up with a vertical scroll bar.
ScrollView* scroll_view =
AddScrollViewWithContentSize(gfx::Size(10, kDefaultHeight * 5));
ScrollViewTestApi test_api(scroll_view);
// Create a fake scroll event and send it to the scroll view.
ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), base::TimeTicks::Now(), 0,
0, -10, 0, -10, 3);
ApplyScrollEvent(test_api, scroll_view, scroll);
// Check if the scroll view has been offset.
EXPECT_EQ(gfx::ScrollOffset(0, 10), test_api.CurrentOffset());
}
// Tests to see that transposed (treat-as-horizontal) scroll events are handled
// correctly in composited and non-composited scrolling.
TEST_F(WidgetScrollViewTest, CompositedTransposedScrollEvents) {
// Set up with a vertical scroll bar.
ScrollView* scroll_view =
AddScrollViewWithContentSize(gfx::Size(kDefaultHeight * 5, 10));
scroll_view->SetTreatAllScrollEventsAsHorizontal(true);
ScrollViewTestApi test_api(scroll_view);
// Create a fake scroll event and send it to the scroll view.
// Note that this is still a VERTICAL scroll event, but we'll be looking for
// HORIZONTAL motion later because we're transposed.
ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), base::TimeTicks::Now(), 0,
0, -10, 0, -10, 3);
ApplyScrollEvent(test_api, scroll_view, scroll);
// Check if the scroll view has been offset.
EXPECT_EQ(gfx::ScrollOffset(10, 0), test_api.CurrentOffset());
}
// Tests to see that transposed (treat-as-horizontal) scroll events are handled
// correctly in composited and non-composited scrolling when the scroll offset
// is somewhat ambiguous. This is the case where the horizontal component is
// larger than the vertical.
TEST_F(WidgetScrollViewTest,
CompositedTransposedScrollEventsHorizontalComponentIsLarger) {
// Set up with a vertical scroll bar.
ScrollView* scroll_view =
AddScrollViewWithContentSize(gfx::Size(kDefaultHeight * 5, 10));
scroll_view->SetTreatAllScrollEventsAsHorizontal(true);
ScrollViewTestApi test_api(scroll_view);
// Create a fake scroll event and send it to the scroll view.
// This will be a horizontal scroll event but there will be a conflicting
// vertical element. We should still scroll horizontally, since the horizontal
// component is greater.
ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), base::TimeTicks::Now(), 0,
-10, 7, -10, 7, 3);
ApplyScrollEvent(test_api, scroll_view, scroll);
// Check if the scroll view has been offset.
EXPECT_EQ(gfx::ScrollOffset(10, 0), test_api.CurrentOffset());
}
// Tests to see that transposed (treat-as-horizontal) scroll events are handled
// correctly in composited and non-composited scrolling when the scroll offset
// is somewhat ambiguous. This is the case where the vertical component is
// larger than the horizontal.
TEST_F(WidgetScrollViewTest,
CompositedTransposedScrollEventsVerticalComponentIsLarger) {
// Set up with a vertical scroll bar.
ScrollView* scroll_view =
AddScrollViewWithContentSize(gfx::Size(kDefaultHeight * 5, 10));
scroll_view->SetTreatAllScrollEventsAsHorizontal(true);
ScrollViewTestApi test_api(scroll_view);
// Create a fake scroll event and send it to the scroll view.
// This will be a vertical scroll event but there will be a conflicting
// horizontal element. We should still scroll horizontally, since the vertical
// component is greater.
ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), base::TimeTicks::Now(), 0,
7, -10, 7, -10, 3);
ApplyScrollEvent(test_api, scroll_view, scroll);
// Check if the scroll view has been offset.
EXPECT_EQ(gfx::ScrollOffset(10, 0), test_api.CurrentOffset());
}
INSTANTIATE_TEST_SUITE_P(All,
WidgetScrollViewTestRTLAndLayers,
::testing::Values(UiConfig::kLtr,
......
......@@ -14,6 +14,7 @@
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/controls/scroll_view.h"
namespace views {
namespace metadata {
......@@ -354,6 +355,14 @@ DEFINE_ENUM_CONVERTERS(ui::MenuSeparatorType,
{ui::MenuSeparatorType::PADDED_SEPARATOR,
base::ASCIIToUTF16("PADDED_SEPARATOR")})
DEFINE_ENUM_CONVERTERS(views::ScrollView::ScrollBarMode,
{views::ScrollView::ScrollBarMode::kDisabled,
base::ASCIIToUTF16("kDisabled")},
{views::ScrollView::ScrollBarMode::kHiddenButEnabled,
base::ASCIIToUTF16("kHiddenButEnabled")},
{views::ScrollView::ScrollBarMode::kEnabled,
base::ASCIIToUTF16("kEnabled")})
#define OP(enum_name) \
{ ui::NativeTheme::enum_name, base::ASCIIToUTF16(#enum_name) }
DEFINE_ENUM_CONVERTERS(ui::NativeTheme::ColorId, NATIVE_THEME_COLOR_IDS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment