From 218e811496df0cd6660d2f86e13ca04d10d5ef4f Mon Sep 17 00:00:00 2001 From: mfomitchev Date: Mon, 17 Nov 2014 16:13:43 -0800 Subject: This change implements support for directional selection handles, i.e. handles that look differently depending if they are located at the left or right edge of the selection, or at the cursor. For web contents, the type of the handle is determined based on the information passed up from Blink in ViewHostMsg_SelectionBounds_Params. For Aura Textfield, it is calculated directly. New image assets (submitted in https://codereview.chromium.org/694873002) are used. New struct ui::SelectionBound is introduced to encapsulate the information about a selection bound. This struct is purposefully made similar to cc::ViewportSelectionBound which is used by TouchSelectionController, and which we'll likely be using for RenderWidgetHostViewAura once we switch to the unified touch selection implementation between Android and Aura. Depends on: https://codereview.chromium.org/686513004/ https://codereview.chromium.org/694873002/ BUG=398053, 419898 Review URL: https://codereview.chromium.org/700563002 Cr-Commit-Position: refs/heads/master@{#304524} --- ui/base/touch/touch_editing_controller.cc | 39 +++++++++ ui/base/touch/touch_editing_controller.h | 47 ++++++++-- ui/base/touch/touch_editing_controller_unittest.cc | 99 ++++++++++++++++++++++ 3 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 ui/base/touch/touch_editing_controller_unittest.cc (limited to 'ui/base/touch') diff --git a/ui/base/touch/touch_editing_controller.cc b/ui/base/touch/touch_editing_controller.cc index 1b84ed9..f3c4c24 100644 --- a/ui/base/touch/touch_editing_controller.cc +++ b/ui/base/touch/touch_editing_controller.cc @@ -10,6 +10,45 @@ namespace { TouchSelectionControllerFactory* g_shared_instance = NULL; } // namespace +SelectionBound::SelectionBound() + : type(ui::SelectionBound::EMPTY) { +} + +SelectionBound::~SelectionBound() { +} + +int SelectionBound::GetHeight() const { + return edge_bottom.y() - edge_top.y(); +} + +bool operator==(const SelectionBound& lhs, const SelectionBound& rhs) { + return lhs.type == rhs.type && lhs.edge_top == rhs.edge_top && + lhs.edge_bottom == rhs.edge_bottom; +} + +bool operator!=(const SelectionBound& lhs, const SelectionBound& rhs) { + return !(lhs == rhs); +} + +gfx::Rect RectBetweenSelectionBounds(const SelectionBound& b1, + const SelectionBound& b2) { + int all_x[] ={ + b1.edge_top.x(), b2.edge_top.x(), b1.edge_bottom.x(), b2.edge_bottom.x() + }; + int all_y[] = { + b1.edge_top.y(), b2.edge_top.y(), b1.edge_bottom.y(), b2.edge_bottom.y() + }; + const int num_elements = arraysize(all_x); + COMPILE_ASSERT(arraysize(all_y) == num_elements, array_size_mismatch); + + int left = *std::min_element(all_x, all_x + num_elements); + int top = *std::min_element(all_y, all_y + num_elements); + int right = *std::max_element(all_x, all_x + num_elements); + int bottom = *std::max_element(all_y, all_y + num_elements); + + return gfx::Rect(left, top, right - left, bottom - top); +} + TouchSelectionController* TouchSelectionController::create( TouchEditable* client_view) { if (g_shared_instance) diff --git a/ui/base/touch/touch_editing_controller.h b/ui/base/touch/touch_editing_controller.h index 00d8d49..c8c531d 100644 --- a/ui/base/touch/touch_editing_controller.h +++ b/ui/base/touch/touch_editing_controller.h @@ -11,6 +11,36 @@ namespace ui { +// Bound of a selected region. +struct UI_BASE_EXPORT SelectionBound { + public: + enum Type { + LEFT, + RIGHT, + CENTER, + EMPTY, + LAST = EMPTY + }; + + SelectionBound(); + ~SelectionBound(); + + int GetHeight() const; + + Type type; + + gfx::Point edge_top; + gfx::Point edge_bottom; +}; + +UI_BASE_EXPORT bool operator==(const SelectionBound& lhs, + const SelectionBound& rhs); +UI_BASE_EXPORT bool operator!=(const SelectionBound& lhs, + const SelectionBound& rhs); + +UI_BASE_EXPORT gfx::Rect RectBetweenSelectionBounds(const SelectionBound& b1, + const SelectionBound& b2); + // An interface implemented by widget that has text that can be selected/edited // using touch. class UI_BASE_EXPORT TouchEditable : public ui::SimpleMenuModel::Delegate { @@ -26,17 +56,18 @@ class UI_BASE_EXPORT TouchEditable : public ui::SimpleMenuModel::Delegate { // Move the caret to |point|. |point| is in local coordinates. virtual void MoveCaretTo(const gfx::Point& point) = 0; - // Gets the end points of the current selection. The end points p1 and p2 must - // be the cursor rect for the start and end of selection (in local - // coordinates): + // Gets the end points of the current selection. The end points |anchor| and + // |focus| must be the cursor rect for the logical start and logical end of + // selection (in local coordinates): // ____________________________________ // | textfield with |selected text| | // ------------------------------------ - // ^p1 ^p2 + // ^anchor ^focus // - // p1 should be the logical start and p2 the logical end of selection. Hence, - // visually, p1 could be to the right of p2 in the figure above. - virtual void GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) = 0; + // Visually, anchor could be to the right of focus in the figure above - it + // depends on the selection direction. + virtual void GetSelectionEndPoints(ui::SelectionBound* anchor, + ui::SelectionBound* focus) = 0; // Gets the bounds of the client view in its local coordinates. virtual gfx::Rect GetBounds() = 0; @@ -94,6 +125,6 @@ class UI_BASE_EXPORT TouchSelectionControllerFactory { virtual ~TouchSelectionControllerFactory() {} }; -} // namespace views +} // namespace ui #endif // UI_BASE_TOUCH_TOUCH_EDITING_CONTROLLER_H_ diff --git a/ui/base/touch/touch_editing_controller_unittest.cc b/ui/base/touch/touch_editing_controller_unittest.cc new file mode 100644 index 0000000..d6b047e --- /dev/null +++ b/ui/base/touch/touch_editing_controller_unittest.cc @@ -0,0 +1,99 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/touch/touch_editing_controller.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace ui { + +namespace { + +} + +TEST(TouchEditingControllerTest, SelectionBound) { + SelectionBound b1, b2; + // Simple case of aligned vertical bounds of equal height + b1.edge_top = gfx::Point(0, 20); + b1.edge_bottom = gfx::Point(0, 25); + b2.edge_top = gfx::Point(110, 20); + b2.edge_bottom = gfx::Point(110, 25); + EXPECT_EQ(gfx::Rect(b1.edge_top, + gfx::Size(b2.edge_top.x() - b1.edge_top.x(), + b2.edge_bottom.y() - b2.edge_top.y())), + RectBetweenSelectionBounds(b1, b2)); + EXPECT_EQ(RectBetweenSelectionBounds(b1, b2), + RectBetweenSelectionBounds(b2, b1)); + + // Parallel vertical bounds of different heights + b1.edge_top = gfx::Point(10, 20); + b1.edge_bottom = gfx::Point(10, 25); + b2.edge_top = gfx::Point(110, 0); + b2.edge_bottom = gfx::Point(110, 35); + EXPECT_EQ(gfx::Rect(gfx::Point(b1.edge_top.x(), b2.edge_top.y()), + gfx::Size(b2.edge_top.x() - b1.edge_top.x(), + b2.edge_bottom.y() - b2.edge_top.y())), + RectBetweenSelectionBounds(b1, b2)); + EXPECT_EQ(RectBetweenSelectionBounds(b1, b2), + RectBetweenSelectionBounds(b2, b1)); + + b1.edge_top = gfx::Point(10, 20); + b1.edge_bottom = gfx::Point(10, 30); + b2.edge_top = gfx::Point(110, 25); + b2.edge_bottom = gfx::Point(110, 45); + EXPECT_EQ(gfx::Rect(b1.edge_top, + gfx::Size(b2.edge_top.x() - b1.edge_top.x(), + b2.edge_bottom.y() - b1.edge_top.y())), + RectBetweenSelectionBounds(b1, b2)); + EXPECT_EQ(RectBetweenSelectionBounds(b1, b2), + RectBetweenSelectionBounds(b2, b1)); + + b1.edge_top = gfx::Point(10, 20); + b1.edge_bottom = gfx::Point(10, 30); + b2.edge_top = gfx::Point(110, 40); + b2.edge_bottom = gfx::Point(110, 60); + EXPECT_EQ(gfx::Rect(b1.edge_top, + gfx::Size(b2.edge_top.x() - b1.edge_top.x(), + b2.edge_bottom.y() - b1.edge_top.y())), + RectBetweenSelectionBounds(b1, b2)); + EXPECT_EQ(RectBetweenSelectionBounds(b1, b2), + RectBetweenSelectionBounds(b2, b1)); + + // Overlapping vertical bounds + b1.edge_top = gfx::Point(10, 20); + b1.edge_bottom = gfx::Point(10, 30); + b2.edge_top = gfx::Point(10, 25); + b2.edge_bottom = gfx::Point(10, 40); + EXPECT_EQ(gfx::Rect(b1.edge_top, + gfx::Size(0, b2.edge_bottom.y() - b1.edge_top.y())), + RectBetweenSelectionBounds(b1, b2)); + EXPECT_EQ(RectBetweenSelectionBounds(b1, b2), + RectBetweenSelectionBounds(b2, b1)); + + // Non-vertical bounds: "\ \" + b1.edge_top = gfx::Point(10, 20); + b1.edge_bottom = gfx::Point(20, 30); + b2.edge_top = gfx::Point(110, 40); + b2.edge_bottom = gfx::Point(120, 60); + EXPECT_EQ(gfx::Rect(b1.edge_top, + gfx::Size(b2.edge_bottom.x() - b1.edge_top.x(), + b2.edge_bottom.y() - b1.edge_top.y())), + RectBetweenSelectionBounds(b1, b2)); + EXPECT_EQ(RectBetweenSelectionBounds(b1, b2), + RectBetweenSelectionBounds(b2, b1)); + + // Non-vertical bounds: "/ \" + b1.edge_top = gfx::Point(20, 30); + b1.edge_bottom = gfx::Point(0, 40); + b2.edge_top = gfx::Point(110, 30); + b2.edge_bottom = gfx::Point(120, 40); + EXPECT_EQ(gfx::Rect(gfx::Point(b1.edge_bottom.x(), b1.edge_top.y()), + gfx::Size(b2.edge_bottom.x() - b1.edge_bottom.x(), + b2.edge_bottom.y() - b2.edge_top.y())), + RectBetweenSelectionBounds(b1, b2)); + EXPECT_EQ(RectBetweenSelectionBounds(b1, b2), + RectBetweenSelectionBounds(b2, b1)); +} + +} // namespace ui -- cgit v1.1