1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/input/touch_selection_controller_client_aura.h"
7 #include "content/browser/renderer_host/render_widget_host_delegate.h"
8 #include "content/browser/renderer_host/render_widget_host_impl.h"
9 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
10 #include "content/common/view_messages.h"
11 #include "content/public/browser/render_view_host.h"
12 #include "ui/aura/client/cursor_client.h"
13 #include "ui/aura/client/screen_position_client.h"
14 #include "ui/aura/env.h"
15 #include "ui/aura/window.h"
16 #include "ui/base/clipboard/clipboard.h"
17 #include "ui/gfx/geometry/point_conversions.h"
18 #include "ui/gfx/geometry/size_conversions.h"
19 #include "ui/strings/grit/ui_strings.h"
20 #include "ui/touch_selection/touch_handle_drawable_aura.h"
21 #include "ui/touch_selection/touch_selection_menu_runner.h"
26 // Delay before showing the quick menu, in milliseconds.
27 const int kQuickMenuDelayInMs
= 100;
29 gfx::Rect
ConvertRectToScreen(aura::Window
* window
, const gfx::RectF
& rect
) {
30 gfx::Point origin
= gfx::ToRoundedPoint(rect
.origin());
31 gfx::Point bottom_right
= gfx::ToRoundedPoint(rect
.bottom_right());
33 aura::Window
* root_window
= window
->GetRootWindow();
35 aura::client::ScreenPositionClient
* screen_position_client
=
36 aura::client::GetScreenPositionClient(root_window
);
37 if (screen_position_client
) {
38 screen_position_client
->ConvertPointToScreen(window
, &origin
);
39 screen_position_client
->ConvertPointToScreen(window
, &bottom_right
);
42 return gfx::Rect(origin
.x(), origin
.y(), bottom_right
.x() - origin
.x(),
43 bottom_right
.y() - origin
.y());
48 // A pre-target event handler for aura::Env which deactivates touch selection on
49 // mouse and keyboard events.
50 class TouchSelectionControllerClientAura::EnvPreTargetHandler
51 : public ui::EventHandler
{
53 EnvPreTargetHandler(ui::TouchSelectionController
* selection_controller
,
54 aura::Window
* window
);
55 ~EnvPreTargetHandler() override
;
59 void OnKeyEvent(ui::KeyEvent
* event
) override
;
60 void OnMouseEvent(ui::MouseEvent
* event
) override
;
61 void OnScrollEvent(ui::ScrollEvent
* event
) override
;
63 ui::TouchSelectionController
* selection_controller_
;
64 aura::Window
* window_
;
66 DISALLOW_COPY_AND_ASSIGN(EnvPreTargetHandler
);
69 TouchSelectionControllerClientAura::EnvPreTargetHandler::EnvPreTargetHandler(
70 ui::TouchSelectionController
* selection_controller
,
72 : selection_controller_(selection_controller
), window_(window
) {
73 aura::Env::GetInstance()->AddPreTargetHandler(this);
76 TouchSelectionControllerClientAura::EnvPreTargetHandler::
77 ~EnvPreTargetHandler() {
78 aura::Env::GetInstance()->RemovePreTargetHandler(this);
81 void TouchSelectionControllerClientAura::EnvPreTargetHandler::OnKeyEvent(
82 ui::KeyEvent
* event
) {
83 DCHECK_NE(ui::TouchSelectionController::INACTIVE
,
84 selection_controller_
->active_status());
86 selection_controller_
->HideAndDisallowShowingAutomatically();
89 void TouchSelectionControllerClientAura::EnvPreTargetHandler::OnMouseEvent(
90 ui::MouseEvent
* event
) {
91 DCHECK_NE(ui::TouchSelectionController::INACTIVE
,
92 selection_controller_
->active_status());
94 // If mouse events are not enabled, this mouse event is synthesized from a
95 // touch event in which case we don't want to deactivate touch selection.
96 aura::client::CursorClient
* cursor_client
=
97 aura::client::GetCursorClient(window_
->GetRootWindow());
98 if (!cursor_client
|| cursor_client
->IsMouseEventsEnabled())
99 selection_controller_
->HideAndDisallowShowingAutomatically();
102 void TouchSelectionControllerClientAura::EnvPreTargetHandler::OnScrollEvent(
103 ui::ScrollEvent
* event
) {
104 DCHECK_NE(ui::TouchSelectionController::INACTIVE
,
105 selection_controller_
->active_status());
107 selection_controller_
->HideAndDisallowShowingAutomatically();
110 TouchSelectionControllerClientAura::TouchSelectionControllerClientAura(
111 RenderWidgetHostViewAura
* rwhva
)
115 base::TimeDelta::FromMilliseconds(kQuickMenuDelayInMs
),
116 base::Bind(&TouchSelectionControllerClientAura::ShowQuickMenu
,
117 base::Unretained(this)),
120 scroll_in_progress_(false),
121 handle_drag_in_progress_(false) {
125 TouchSelectionControllerClientAura::~TouchSelectionControllerClientAura() {
128 void TouchSelectionControllerClientAura::OnWindowMoved() {
132 void TouchSelectionControllerClientAura::OnTouchDown() {
137 void TouchSelectionControllerClientAura::OnTouchUp() {
142 void TouchSelectionControllerClientAura::OnScrollStarted() {
143 scroll_in_progress_
= true;
144 rwhva_
->selection_controller()->SetTemporarilyHidden(true);
148 void TouchSelectionControllerClientAura::OnScrollCompleted() {
149 scroll_in_progress_
= false;
150 rwhva_
->selection_controller()->SetTemporarilyHidden(false);
154 bool TouchSelectionControllerClientAura::IsQuickMenuAllowed() const {
155 return !touch_down_
&& !scroll_in_progress_
&& !handle_drag_in_progress_
;
158 void TouchSelectionControllerClientAura::ShowQuickMenu() {
159 if (!ui::TouchSelectionMenuRunner::GetInstance())
162 gfx::RectF rect
= rwhva_
->selection_controller()->GetRectBetweenBounds();
164 // Clip rect, which is in |rwhva_|'s window's coordinate space, to client
166 gfx::PointF origin
= rect
.origin();
167 gfx::PointF bottom_right
= rect
.bottom_right();
168 gfx::Rect client_bounds
= rwhva_
->GetNativeView()->bounds();
169 origin
.SetToMax(client_bounds
.origin());
170 bottom_right
.SetToMin(client_bounds
.bottom_right());
171 if (origin
.x() > bottom_right
.x() || origin
.y() > bottom_right
.y())
174 gfx::Vector2dF diagonal
= bottom_right
- origin
;
175 gfx::SizeF
size(diagonal
.x(), diagonal
.y());
176 gfx::RectF
anchor_rect(origin
, size
);
178 // Calculate maximum handle image size;
179 gfx::SizeF max_handle_size
=
180 rwhva_
->selection_controller()->GetStartHandleRect().size();
181 max_handle_size
.SetToMax(
182 rwhva_
->selection_controller()->GetEndHandleRect().size());
184 aura::Window
* parent
= rwhva_
->GetNativeView();
185 ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu(
186 this, ConvertRectToScreen(parent
, anchor_rect
),
187 gfx::ToRoundedSize(max_handle_size
), parent
->GetToplevelWindow());
190 void TouchSelectionControllerClientAura::UpdateQuickMenu() {
191 bool menu_is_showing
=
192 ui::TouchSelectionMenuRunner::GetInstance() &&
193 ui::TouchSelectionMenuRunner::GetInstance()->IsRunning();
194 bool menu_should_show
= rwhva_
->selection_controller()->active_status() !=
195 ui::TouchSelectionController::INACTIVE
&&
196 IsQuickMenuAllowed();
198 // Hide the quick menu if there is any. This should happen even if the menu
199 // should be shown again, in order to update its location or content.
201 ui::TouchSelectionMenuRunner::GetInstance()->CloseMenu();
203 quick_menu_timer_
.Stop();
205 // Start timer to show quick menu if necessary.
206 if (menu_should_show
) {
207 if (show_quick_menu_immediately_for_test_
)
210 quick_menu_timer_
.Reset();
214 bool TouchSelectionControllerClientAura::SupportsAnimation() const {
218 void TouchSelectionControllerClientAura::SetNeedsAnimate() {
222 void TouchSelectionControllerClientAura::MoveCaret(
223 const gfx::PointF
& position
) {
224 RenderWidgetHostImpl
* host
=
225 RenderWidgetHostImpl::From(rwhva_
->GetRenderWidgetHost());
226 host
->MoveCaret(gfx::ToRoundedPoint(position
));
229 void TouchSelectionControllerClientAura::MoveRangeSelectionExtent(
230 const gfx::PointF
& extent
) {
231 RenderWidgetHostDelegate
* host_delegate
=
232 RenderWidgetHostImpl::From(rwhva_
->GetRenderWidgetHost())->delegate();
234 host_delegate
->MoveRangeSelectionExtent(gfx::ToRoundedPoint(extent
));
237 void TouchSelectionControllerClientAura::SelectBetweenCoordinates(
238 const gfx::PointF
& base
,
239 const gfx::PointF
& extent
) {
240 RenderWidgetHostDelegate
* host_delegate
=
241 RenderWidgetHostImpl::From(rwhva_
->GetRenderWidgetHost())->delegate();
243 host_delegate
->SelectRange(gfx::ToRoundedPoint(base
),
244 gfx::ToRoundedPoint(extent
));
248 void TouchSelectionControllerClientAura::OnSelectionEvent(
249 ui::SelectionEventType event
) {
251 case ui::SELECTION_HANDLES_SHOWN
:
252 case ui::INSERTION_HANDLE_SHOWN
:
254 env_pre_target_handler_
.reset(new EnvPreTargetHandler(
255 rwhva_
->selection_controller(), rwhva_
->GetNativeView()));
257 case ui::SELECTION_HANDLES_CLEARED
:
258 case ui::INSERTION_HANDLE_CLEARED
:
259 env_pre_target_handler_
.reset();
262 case ui::SELECTION_HANDLE_DRAG_STARTED
:
263 case ui::INSERTION_HANDLE_DRAG_STARTED
:
264 handle_drag_in_progress_
= true;
267 case ui::SELECTION_HANDLE_DRAG_STOPPED
:
268 case ui::INSERTION_HANDLE_DRAG_STOPPED
:
269 handle_drag_in_progress_
= false;
272 case ui::SELECTION_HANDLES_MOVED
:
273 case ui::INSERTION_HANDLE_MOVED
:
276 case ui::INSERTION_HANDLE_TAPPED
:
277 case ui::SELECTION_ESTABLISHED
:
278 case ui::SELECTION_DISSOLVED
:
283 scoped_ptr
<ui::TouchHandleDrawable
>
284 TouchSelectionControllerClientAura::CreateDrawable() {
285 return scoped_ptr
<ui::TouchHandleDrawable
>(
286 new ui::TouchHandleDrawableAura(rwhva_
->GetNativeView()));
289 bool TouchSelectionControllerClientAura::IsCommandIdEnabled(
290 int command_id
) const {
291 bool editable
= rwhva_
->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE
;
292 bool readable
= rwhva_
->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD
;
293 gfx::Range selection_range
;
294 rwhva_
->GetSelectionRange(&selection_range
);
295 bool has_selection
= !selection_range
.is_empty();
296 switch (command_id
) {
298 return editable
&& readable
&& has_selection
;
300 return readable
&& has_selection
;
301 case IDS_APP_PASTE
: {
302 base::string16 result
;
303 ui::Clipboard::GetForCurrentThread()->ReadText(
304 ui::CLIPBOARD_TYPE_COPY_PASTE
, &result
);
305 return editable
&& !result
.empty();
312 void TouchSelectionControllerClientAura::ExecuteCommand(int command_id
,
314 rwhva_
->selection_controller()->HideAndDisallowShowingAutomatically();
315 RenderWidgetHostDelegate
* host_delegate
=
316 RenderWidgetHostImpl::From(rwhva_
->GetRenderWidgetHost())->delegate();
320 switch (command_id
) {
322 host_delegate
->Cut();
325 host_delegate
->Copy();
328 host_delegate
->Paste();
336 void TouchSelectionControllerClientAura::RunContextMenu() {
337 gfx::RectF anchor_rect
=
338 rwhva_
->selection_controller()->GetRectBetweenBounds();
339 gfx::PointF anchor_point
=
340 gfx::PointF(anchor_rect
.CenterPoint().x(), anchor_rect
.y());
341 RenderWidgetHostImpl
* host
=
342 RenderWidgetHostImpl::From(rwhva_
->GetRenderWidgetHost());
343 host
->Send(new ViewMsg_ShowContextMenu(host
->GetRoutingID(),
344 ui::MENU_SOURCE_TOUCH_EDIT_MENU
,
345 gfx::ToRoundedPoint(anchor_point
)));
347 // Hide selection handles after getting rect-between-bounds from touch
348 // selection controller; otherwise, rect would be empty and the above
349 // calculations would be invalid.
350 rwhva_
->selection_controller()->HideAndDisallowShowingAutomatically();
353 } // namespace content