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 @@
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/feature_list.h"
#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "base/notreached.h"
#include "base/scoped_observer.h"
......@@ -341,7 +342,7 @@ void View::SetBoundsRect(const gfx::Rect& 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
// 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_) {
child->UpdateChildLayerBounds(
LayerOffsetData(layer()->device_scale_factor(),
......@@ -586,7 +587,7 @@ gfx::Transform View::GetTransform() const {
gfx::ScrollOffset scroll_offset = layer()->CurrentScrollOffset();
// Offsets for layer-based scrolling are never negative, but the horizontal
// 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());
return transform;
}
......@@ -734,7 +735,7 @@ int View::GetMirroredX() 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 {
......@@ -744,11 +745,11 @@ gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) 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 {
return base::i18n::IsRTL() ? width() - x - w : x;
return GetMirrored() ? width() - x - w : x;
}
// Layout ----------------------------------------------------------------------
......@@ -1145,6 +1146,10 @@ void View::EnableCanvasFlippingForRTLUI(bool enable) {
flip_canvas_on_paint_for_rtl_ui_ = enable;
}
bool View::GetMirrored() const {
return is_mirrored_.value_or(base::i18n::IsRTL());
}
// Input -----------------------------------------------------------------------
View* View::GetEventHandlerForPoint(const gfx::Point& point) {
......@@ -3006,6 +3011,7 @@ ADD_PROPERTY_METADATA(View, int, Group)
ADD_PROPERTY_METADATA(View, int, ID)
ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MaximumSize)
ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MinimumSize)
ADD_PROPERTY_METADATA(View, bool, Mirrored)
ADD_PROPERTY_METADATA(View, bool, Visible)
END_METADATA()
......
......@@ -23,6 +23,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "build/build_config.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
......@@ -907,7 +908,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// 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
// 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
// needs to be flipped horizontally when the UI layout is right-to-left
......@@ -916,6 +917,19 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// the UI directionality.
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 ---------------------------------------------------------------------
// The points, rects, mouse locations, and touch locations in the following
// functions are in the view's coordinates, except for a RootView.
......@@ -1976,6 +1990,13 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// right-to-left locales for this View.
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 ------------------------------------------------------
// Whether layer painting was explicitly set by a call to |SetPaintToLayer()|.
......
......@@ -51,6 +51,7 @@
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/controls/scroll_view.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/paint_info.h"
#include "ui/views/test/view_metadata_test_utils.h"
......@@ -3782,6 +3783,62 @@ TEST_F(ViewTest, AddExistingChild) {
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
////////////////////////////////////////////////////////////////////////////////
......
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