Commit 9ba2ae51 authored by Noah Rose Ledesma's avatar Noah Rose Ledesma Committed by Commit Bot

Allow views to override UI direction

This CL adds a method to the view class called IsRTL that replaces calls
to base::i18n::IsRTL() in the view impl. The result of View::IsRTL may
be overridden with View::SetIsRTL().

The purpose of this change is to allow views that should always be
presented in left-to-right do so with ease. For reference, see the next
CL in the relation chain.

Bug: 1106037
Change-Id: Ieee3b1667979f9f010ac971bcca3ea4d902f9d93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2300799
Commit-Queue: Noah Rose Ledesma <noahrose@google.com>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#789331}
parent 9edebb56
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/containers/adapters.h" #include "base/containers/adapters.h"
#include "base/feature_list.h" #include "base/feature_list.h"
#include "base/i18n/rtl.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/scoped_observer.h" #include "base/scoped_observer.h"
...@@ -341,7 +342,7 @@ void View::SetBoundsRect(const gfx::Rect& bounds) { ...@@ -341,7 +342,7 @@ void View::SetBoundsRect(const gfx::Rect& bounds) {
// In RTL mode, if our width has changed, our children's mirrored bounds // In RTL mode, if our width has changed, our children's mirrored bounds
// will have changed. Update the child's layer bounds, or if it is not a // will have changed. Update the child's layer bounds, or if it is not a
// layer, the bounds of any layers inside the child. // layer, the bounds of any layers inside the child.
if (base::i18n::IsRTL() && bounds_.width() != prev.width()) { if (GetMirrored() && bounds_.width() != prev.width()) {
for (View* child : children_) { for (View* child : children_) {
child->UpdateChildLayerBounds( child->UpdateChildLayerBounds(
LayerOffsetData(layer()->device_scale_factor(), LayerOffsetData(layer()->device_scale_factor(),
...@@ -586,7 +587,7 @@ gfx::Transform View::GetTransform() const { ...@@ -586,7 +587,7 @@ gfx::Transform View::GetTransform() const {
gfx::ScrollOffset scroll_offset = layer()->CurrentScrollOffset(); gfx::ScrollOffset scroll_offset = layer()->CurrentScrollOffset();
// Offsets for layer-based scrolling are never negative, but the horizontal // Offsets for layer-based scrolling are never negative, but the horizontal
// scroll direction is reversed in RTL via canvas flipping. // scroll direction is reversed in RTL via canvas flipping.
transform.Translate((base::i18n::IsRTL() ? 1 : -1) * scroll_offset.x(), transform.Translate((GetMirrored() ? 1 : -1) * scroll_offset.x(),
-scroll_offset.y()); -scroll_offset.y());
return transform; return transform;
} }
...@@ -734,7 +735,7 @@ int View::GetMirroredX() const { ...@@ -734,7 +735,7 @@ int View::GetMirroredX() const {
} }
int View::GetMirroredXForRect(const gfx::Rect& rect) const { int View::GetMirroredXForRect(const gfx::Rect& rect) const {
return base::i18n::IsRTL() ? (width() - rect.x() - rect.width()) : rect.x(); return GetMirrored() ? (width() - rect.x() - rect.width()) : rect.x();
} }
gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const { gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const {
...@@ -744,11 +745,11 @@ gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const { ...@@ -744,11 +745,11 @@ gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const {
} }
int View::GetMirroredXInView(int x) const { int View::GetMirroredXInView(int x) const {
return base::i18n::IsRTL() ? width() - x : x; return GetMirrored() ? width() - x : x;
} }
int View::GetMirroredXWithWidthInView(int x, int w) const { int View::GetMirroredXWithWidthInView(int x, int w) const {
return base::i18n::IsRTL() ? width() - x - w : x; return GetMirrored() ? width() - x - w : x;
} }
// Layout ---------------------------------------------------------------------- // Layout ----------------------------------------------------------------------
...@@ -1145,6 +1146,10 @@ void View::EnableCanvasFlippingForRTLUI(bool enable) { ...@@ -1145,6 +1146,10 @@ void View::EnableCanvasFlippingForRTLUI(bool enable) {
flip_canvas_on_paint_for_rtl_ui_ = enable; flip_canvas_on_paint_for_rtl_ui_ = enable;
} }
bool View::GetMirrored() const {
return is_mirrored_.value_or(base::i18n::IsRTL());
}
// Input ----------------------------------------------------------------------- // Input -----------------------------------------------------------------------
View* View::GetEventHandlerForPoint(const gfx::Point& point) { View* View::GetEventHandlerForPoint(const gfx::Point& point) {
...@@ -3006,6 +3011,7 @@ ADD_PROPERTY_METADATA(View, int, Group) ...@@ -3006,6 +3011,7 @@ ADD_PROPERTY_METADATA(View, int, Group)
ADD_PROPERTY_METADATA(View, int, ID) ADD_PROPERTY_METADATA(View, int, ID)
ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MaximumSize) ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MaximumSize)
ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MinimumSize) ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MinimumSize)
ADD_PROPERTY_METADATA(View, bool, Mirrored)
ADD_PROPERTY_METADATA(View, bool, Visible) ADD_PROPERTY_METADATA(View, bool, Visible)
END_METADATA() END_METADATA()
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPath.h"
#include "ui/accessibility/ax_enums.mojom-forward.h" #include "ui/accessibility/ax_enums.mojom-forward.h"
...@@ -907,7 +908,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ...@@ -907,7 +908,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// Enables or disables flipping of the gfx::Canvas during Paint(). Note that // Enables or disables flipping of the gfx::Canvas during Paint(). Note that
// if canvas flipping is enabled, the canvas will be flipped only if the UI // if canvas flipping is enabled, the canvas will be flipped only if the UI
// layout is right-to-left; that is, the canvas will be flipped only if // layout is right-to-left; that is, the canvas will be flipped only if
// base::i18n::IsRTL() returns true. // GetMirrored() is true.
// //
// Enabling canvas flipping is useful for leaf views that draw an image that // Enabling canvas flipping is useful for leaf views that draw an image that
// needs to be flipped horizontally when the UI layout is right-to-left // needs to be flipped horizontally when the UI layout is right-to-left
...@@ -916,6 +917,19 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ...@@ -916,6 +917,19 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// the UI directionality. // the UI directionality.
virtual void EnableCanvasFlippingForRTLUI(bool enable); virtual void EnableCanvasFlippingForRTLUI(bool enable);
// When set, this view will ignore base::l18n::IsRTL() and instead be drawn
// according to |is_mirrored|.
//
// This is useful for views that should be displayed the same regardless of UI
// direction. Unlike EnableCanvasFlippingForRTLUI this setting has an effect
// on the visual order of child views.
//
// This setting does not propagate to child views. So while the visual order
// of this view's children may change, the visual order of this view's
// grandchildren in relation to their parents are unchanged.
void SetMirrored(bool is_mirrored) { is_mirrored_ = is_mirrored; }
bool GetMirrored() const;
// Input --------------------------------------------------------------------- // Input ---------------------------------------------------------------------
// The points, rects, mouse locations, and touch locations in the following // The points, rects, mouse locations, and touch locations in the following
// functions are in the view's coordinates, except for a RootView. // functions are in the view's coordinates, except for a RootView.
...@@ -1976,6 +1990,13 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ...@@ -1976,6 +1990,13 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// right-to-left locales for this View. // right-to-left locales for this View.
bool flip_canvas_on_paint_for_rtl_ui_ = false; bool flip_canvas_on_paint_for_rtl_ui_ = false;
// Controls whether GetTransform(), the mirroring functions, and the like
// horizontally mirror. This controls how child views are physically
// positioned onscreen. The default behavior should be correct in most cases,
// but can be overridden if a particular view must always be laid out in some
// direction regardless of the application's default UI direction.
base::Optional<bool> is_mirrored_;
// Accelerated painting ------------------------------------------------------ // Accelerated painting ------------------------------------------------------
// Whether layer painting was explicitly set by a call to |SetPaintToLayer()|. // Whether layer painting was explicitly set by a call to |SetPaintToLayer()|.
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/native/native_view_host.h"
#include "ui/views/controls/scroll_view.h" #include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/metadata/metadata_types.h" #include "ui/views/metadata/metadata_types.h"
#include "ui/views/paint_info.h" #include "ui/views/paint_info.h"
#include "ui/views/test/view_metadata_test_utils.h" #include "ui/views/test/view_metadata_test_utils.h"
...@@ -3782,6 +3783,62 @@ TEST_F(ViewTest, AddExistingChild) { ...@@ -3782,6 +3783,62 @@ TEST_F(ViewTest, AddExistingChild) {
EXPECT_EQ(1, v1.GetIndexOf(&v3)); EXPECT_EQ(1, v1.GetIndexOf(&v3));
} }
TEST_F(ViewTest, UseMirroredLayoutDisableMirroring) {
base::i18n::SetICUDefaultLocale("ar");
ASSERT_TRUE(base::i18n::IsRTL());
View parent, child1, child2;
parent.SetLayoutManager(
std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));
child1.SetPreferredSize(gfx::Size(10, 10));
child2.SetPreferredSize(gfx::Size(10, 10));
parent.AddChildView(&child1);
parent.AddChildView(&child2);
parent.SizeToPreferredSize();
EXPECT_EQ(child1.GetNextFocusableView(), &child2);
EXPECT_GT(child1.GetMirroredX(), child2.GetMirroredX());
EXPECT_LT(child1.x(), child2.x());
EXPECT_NE(parent.GetMirroredXInView(5), 5);
parent.SetMirrored(false);
EXPECT_EQ(child1.GetNextFocusableView(), &child2);
EXPECT_GT(child2.GetMirroredX(), child1.GetMirroredX());
EXPECT_LT(child1.x(), child2.x());
EXPECT_EQ(parent.GetMirroredXInView(5), 5);
}
TEST_F(ViewTest, UseMirroredLayoutEnableMirroring) {
base::i18n::SetICUDefaultLocale("en");
ASSERT_FALSE(base::i18n::IsRTL());
View parent, child1, child2;
parent.SetLayoutManager(
std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));
child1.SetPreferredSize(gfx::Size(10, 10));
child2.SetPreferredSize(gfx::Size(10, 10));
parent.AddChildView(&child1);
parent.AddChildView(&child2);
parent.SizeToPreferredSize();
EXPECT_EQ(child1.GetNextFocusableView(), &child2);
EXPECT_LT(child1.GetMirroredX(), child2.GetMirroredX());
EXPECT_LT(child1.x(), child2.x());
EXPECT_NE(parent.GetMirroredXInView(5), 15);
parent.SetMirrored(true);
EXPECT_EQ(child1.GetNextFocusableView(), &child2);
EXPECT_LT(child2.GetMirroredX(), child1.GetMirroredX());
EXPECT_LT(child1.x(), child2.x());
EXPECT_EQ(parent.GetMirroredXInView(5), 15);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// FocusManager // FocusManager
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
......
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