1 // Copyright 2013 Samsung Electronics. 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 "selection_controller_efl.h"
7 #include <Elementary.h>
9 #include "base/trace_event/trace_event.h"
10 #include "content/browser/renderer_host/render_view_host_impl.h"
11 #include "content/browser/renderer_host/render_widget_host_impl.h"
12 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
13 #include "content/browser/web_contents/web_contents_impl.h"
14 #include "content/browser/web_contents/web_contents_impl_efl.h"
15 #include "content/public/browser/context_menu_params.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "ui/base/clipboard/clipboard_helper_efl.h"
19 #include "ui/display/screen.h"
20 #include "ui/gfx/geometry/dip_util.h"
24 static const int menuHeight = 140;// The Height fo the context menu.
25 static const int menuPadding = 60;// This is padding for deciding when to modify context menu position.
26 static const int spacePadding = 0; // 24;// This is for making context menu closer to the handles.
28 bool IsRectEmpty(const gfx::Rect& rect) {
29 return rect == gfx::Rect();
32 gfx::Vector2dF ComputeLineOffsetFromBottom(const gfx::SelectionBound& bound) {
33 gfx::Vector2dF line_offset =
34 gfx::ScaleVector2d(bound.edge_start() - bound.edge_end(), 0.5f);
35 // An offset of 8 DIPs is sufficient for most line sizes. For small lines,
36 // using half the line height avoids synthesizing a point on a line above
37 // (or below) the intended line.
38 const gfx::Vector2dF kMaxLineOffset(8.f, 8.f);
39 line_offset.SetToMin(kMaxLineOffset);
40 line_offset.SetToMax(-kMaxLineOffset);
44 content::WebContents* SelectionControllerEfl::web_contents() const {
45 return rwhva_->offscreen_helper()->GetWebContents();
48 SelectionControllerEfl::SelectionControllerEfl(RenderWidgetHostViewAura* rwhva)
50 selection_data_(new SelectionBoxEfl(rwhva)),
52 new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_LEFT)),
54 new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_RIGHT)),
56 new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_INPUT)),
57 magnifier_(new SelectionMagnifierEfl(this)),
58 selection_mode_(None) {
59 evas_object_event_callback_add(rwhva_->offscreen_helper()->content_image(),
61 &EvasParentViewMoveCallback, this);
62 #if BUILDFLAG(IS_TIZEN)
63 vconf_notify_key_changed(VCONFKEY_LANGSET, PlatformLanguageChanged, this);
67 SelectionControllerEfl::~SelectionControllerEfl() {
68 if (ecore_events_filter_)
69 ecore_event_filter_del(ecore_events_filter_);
70 if (GetSelectionStatus())
71 ClearSelectionViaEWebView();
72 HideHandleAndContextMenu();
74 evas_object_event_callback_del(rwhva_->offscreen_helper()->content_image(),
76 &EvasParentViewMoveCallback);
79 void SelectionControllerEfl::SetSelectionStatus(bool enable) {
80 TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__, "enable", enable);
81 selection_data_->SetStatus(enable);
84 bool SelectionControllerEfl::GetSelectionStatus() const {
85 TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
86 "status", selection_data_->GetStatus());
87 return selection_data_->GetStatus();
90 #if BUILDFLAG(IS_TIZEN)
91 void SelectionControllerEfl::PlatformLanguageChanged(keynode_t* keynode, void* data) {
92 SelectionControllerEfl* selection_controller = static_cast<SelectionControllerEfl*>(data);
93 if (!selection_controller)
96 if (selection_controller->GetSelectionStatus())
97 selection_controller->ClearSelectionViaEWebView();
98 selection_controller->HideHandleAndContextMenu();
102 void SelectionControllerEfl::SetSelectionEditable(bool enable) {
103 TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__, "enable", enable);
104 selection_data_->SetEditable(enable);
107 bool SelectionControllerEfl::GetSelectionEditable() const {
108 TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
109 "editable", selection_data_->GetEditable());
110 return selection_data_->GetEditable();
113 bool SelectionControllerEfl::GetCaretSelectionStatus() const {
114 TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
115 "caret selection", selection_mode_ == Caret);
116 return selection_mode_ == Caret;
119 void SelectionControllerEfl::SetControlsTemporarilyHidden(bool value) {
120 TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
121 "controls are hidden:", value);
122 if (controls_temporarily_hidden_ == value)
124 // Make sure to show selection controls only when no finger
125 // is left touching the screen.
126 if (!value && rwhva_->event_handler()->pointer_state().GetPointerCount())
128 controls_temporarily_hidden_ = value;
131 CancelContextMenu(0);
133 ShowHandleAndContextMenuIfRequired(Reason::ScrollOrZoomGestureEnded);
137 void SelectionControllerEfl::SetIsAnchorFirst(bool value) {
138 selection_data_->SetIsAnchorFirst(value);
141 void SelectionControllerEfl::OnSelectionChanged(
142 const gfx::SelectionBound& start, const gfx::SelectionBound& end) {
144 if (start_selection_ == start && end_selection_ == end)
147 start_selection_ = start;
148 end_selection_ = end;
150 gfx::Rect truncated_start(start.edge_start_rounded(),
151 gfx::Size(0, start.GetHeight()));
152 gfx::Rect truncated_end(end.edge_start_rounded(),
153 gfx::Size(0, end.GetHeight()));
155 static float device_scale_factor =
156 display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
157 truncated_start = gfx::ToEnclosingRect(
158 gfx::ConvertRectToPixels(truncated_start, device_scale_factor));
159 truncated_end = gfx::ToEnclosingRect(
160 gfx::ConvertRectToPixels(truncated_end, device_scale_factor));
162 if (handle_being_dragged_ &&
163 dragging_handle_->Type() != SelectionHandleEfl::HANDLE_TYPE_INPUT) {
164 dragging_handle_->MoveObject(selection_data_->GetIsAnchorFirst()
165 ? truncated_end.bottom_right()
166 : truncated_start.bottom_left());
169 bool finger_down = handle_being_dragged_ || long_mouse_press_;
170 bool show = (selection_change_reason_ != Reason::Irrelevant) && !finger_down;
171 UpdateSelectionDataAndShow(
172 truncated_start, truncated_end, show);
173 selection_change_reason_ = Reason::Irrelevant;
175 // In case of the selected text contains only line break and no other
176 // characters, we should use caret selection mode.
177 if (GetSelectionEditable() && !handle_being_dragged_ &&
178 rwhva_->GetSelectedText() == (u"\n")) {
179 rwhva_->offscreen_helper()->MoveCaret(
180 selection_data_->GetLeftRect().origin());
181 is_caret_mode_forced_ = true;
185 void SelectionControllerEfl::OnTextInputStateChanged() {
186 // In order to confirm if a long press gesture is ongoing,
187 // we just needed to check "long_mouse_press_".
188 // However, when long press happens on a link or image,
189 // long_mouse_press_ is set to false in ::HandleLongPressEvent,
190 // although finger is still down.
191 // To compensate this, add an extra check asking from the engine
192 // if a finger is still touching down.
194 rwhva_->event_handler()->pointer_state().GetPointerCount() > 0;
196 // If on an editable field and in caret selection mode, only
197 // show the large handle if:
198 // 1) we are dragging the insertion handle around;
199 // 2) we are expecting an "update" from the engine, e.g.
200 // handling "tap" gesture.
201 bool finger_down = handle_being_dragged_ || long_mouse_press_ || is_touch_down;
203 if (GetSelectionEditable() && GetCaretSelectionStatus() && !finger_down &&
204 !controls_temporarily_hidden_ &&
205 selection_change_reason_ == Reason::Irrelevant) {
206 HideHandleAndContextMenu();
211 void SelectionControllerEfl::UpdateSelectionData(const std::u16string& text) {
212 selection_data_->UpdateSelectStringData(text);
215 bool SelectionControllerEfl::ClearSelectionViaEWebView() {
216 if (!GetSelectionStatus() || !web_contents()->GetRenderViewHost() ||
217 web_contents()->IsBeingDestroyed()) {
221 RenderWidgetHostDelegate* host_delegate =
222 RenderWidgetHostImpl::From(
223 web_contents()->GetRenderViewHost()->GetWidget())
226 host_delegate->ExecuteEditCommand("Unselect", absl::nullopt);
232 void SelectionControllerEfl::SetSelectionMode(enum SelectionMode mode) {
233 selection_mode_ = mode;
236 void SelectionControllerEfl::ToggleCaretAfterSelection() {
237 if (!GetCaretSelectionStatus() && GetSelectionEditable()) {
238 rwhva_->offscreen_helper()->MoveCaret(
239 selection_data_->GetRightRect().origin());
244 void SelectionControllerEfl::DetermineSelectionMode(
245 const gfx::Rect& left_rect,
246 const gfx::Rect& right_rect) {
248 if (left_rect == gfx::Rect() && right_rect == gfx::Rect())
249 SetSelectionMode(None);
250 else if (left_rect == right_rect && GetSelectionEditable())
251 SetSelectionMode(Caret);
253 SetSelectionMode(Range);
256 bool SelectionControllerEfl::UpdateSelectionDataAndShow(
257 const gfx::Rect& left_rect,
258 const gfx::Rect& right_rect,
261 DetermineSelectionMode(left_rect, right_rect);
263 TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
264 if ((selection_change_reason_ == Reason::Irrelevant) && !IsSelectionValid(left_rect, right_rect)) {
265 if (!GetCaretSelectionStatus())
267 selection_data_->UpdateRectData(left_rect, right_rect);
271 if (selection_on_empty_form_control_ &&
272 (!ClipboardHelperEfl::GetInstance()->CanPasteFromSystemClipboard() ||
273 selection_change_reason_ == Reason::Tap)) {
278 if (selection_data_->UpdateRectData(left_rect, right_rect))
279 ShowHandleAndContextMenuIfRequired();
284 void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired(
285 Reason explicit_reason) {
286 TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
288 Reason effective_reason = selection_change_reason_;
289 if (explicit_reason != Reason::Irrelevant)
290 effective_reason = explicit_reason;
292 // TODO(a1.gomes): Is not in selection mode, maybe we should not be this far
294 if (!selection_data_->GetStatus())
297 if (controls_temporarily_hidden_ || long_mouse_press_)
300 gfx::Rect left, right;
301 left = selection_data_->GetLeftRect();
302 right = selection_data_->GetRightRect();
304 if (left.x() == 0 && left.y() == 0 && right.x() == 0 && right.y() == 0) {
305 selection_data_->ClearRectData();
309 bool is_selection_range_visible =
310 start_selection_.visible() || end_selection_.visible();
312 // Is in edit field and no text is selected. show only single handle
313 if (selection_data_->IsInEditField() && left == right) {
314 if (!GetSelectionEditable())
317 CHECK(start_selection_ == end_selection_);
319 if (GetCaretSelectionStatus() && is_selection_range_visible) {
320 gfx::Rect left = selection_data_->GetLeftRect();
321 input_handle_->SetBasePosition(gfx::Point(left.x(), left.y()));
322 input_handle_->Move(left.bottom_right());
323 input_handle_->Show();
325 start_handle_->Hide();
328 bool show_context_menu =
329 IsAnyHandleVisible() &&
330 effective_reason != Reason::ScrollOrZoomGestureEnded &&
331 effective_reason != Reason::Tap &&
332 effective_reason != Reason::Irrelevant;
333 if (!handle_being_dragged_ && show_context_menu)
338 input_handle_->Hide();
340 SelectionHandleEfl* start_handle = start_handle_.get();
341 SelectionHandleEfl* end_handle = end_handle_.get();
342 if (handle_being_dragged_) {
343 bool is_anchor_first = selection_data_->GetIsAnchorFirst();
344 if (is_anchor_first) {
345 start_handle = stationary_handle_;
346 end_handle = dragging_handle_;
348 start_handle = dragging_handle_;
349 end_handle = stationary_handle_;
353 start_handle_->SetBasePosition(left.bottom_left());
354 start_handle->Move(left.bottom_left());
355 if (start_selection_.visible())
356 start_handle->Show();
358 start_handle->Hide();
360 end_handle_->SetBasePosition(right.bottom_right());
361 end_handle->Move(right.bottom_right());
362 if (end_selection_.visible())
367 // Do not show the context menu during selection extend and
368 // offscreen selection.
369 if (!handle_being_dragged_ && effective_reason != Reason::Irrelevant &&
370 IsAnyHandleVisible()) {
375 void SelectionControllerEfl::ShowContextMenu() {
376 #if !defined(USE_AURA)
377 content::ContextMenuParams convertedParams = *(selection_data_->GetContextMenuParams());
379 RenderWidgetHostViewEfl* rwhv =
380 static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
383 rwhv->EvasToBlinkCords(convertedParams.x, convertedParams.y, &blinkX, &blinkY);
384 convertedParams.x = blinkX;
385 convertedParams.y = blinkY;
387 // TODO(a1.gomes): In case of EWK apps, the call below end up calling
388 // EWebView::ShowContextMenu. We have to make sure parameters
390 WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
391 WebContentsViewEfl* wcve = static_cast<WebContentsViewEfl*>(wci->GetView());
392 wcve->ShowContextMenu(convertedParams);
397 void SelectionControllerEfl::CancelContextMenu(int request_id) {
398 #if !defined(USE_AURA)
399 WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
400 WebContentsViewEfl* wcve = static_cast<WebContentsViewEfl*>(wci->GetView());
401 wcve->CancelContextMenu(request_id);
405 void SelectionControllerEfl::HideHandles() {
409 void SelectionControllerEfl::HideHandleAndContextMenu() {
410 CancelContextMenu(0);
414 bool SelectionControllerEfl::IsAnyHandleVisible() const {
415 return (start_handle_->IsVisible() ||
416 end_handle_->IsVisible() ||
417 input_handle_->IsVisible());
420 void SelectionControllerEfl::Clear() {
421 start_handle_->Hide();
423 input_handle_->Hide();
426 Eina_Bool SelectionControllerEfl::EcoreEventFilterCallback(void* user_data,
430 if (type == ECORE_EVENT_MOUSE_MOVE) {
431 auto controller = static_cast<SelectionControllerEfl*>(user_data);
432 if (!controller || !controller->dragged_handle_)
433 return ECORE_CALLBACK_PASS_ON;
435 auto event = static_cast<Ecore_Event_Mouse_Move*>(event_info);
436 controller->dragged_handle_->UpdatePosition(gfx::Point(event->x, event->y));
437 return ECORE_CALLBACK_CANCEL;
440 return ECORE_CALLBACK_PASS_ON;
443 void SelectionControllerEfl::HandleDragBeginNotification(
444 SelectionHandleEfl* handle) {
445 selection_change_reason_ = Reason::HandleDragged;
446 dragged_handle_ = handle;
447 ecore_events_filter_ =
448 ecore_event_filter_add(nullptr, EcoreEventFilterCallback, nullptr, this);
450 if (!ecore_events_filter_) {
451 LOG(ERROR) << __PRETTY_FUNCTION__
452 << " : Unable to create ecore events filter";
455 // Hide context menu on mouse down
456 CancelContextMenu(0);
458 handle_being_dragged_ = true;
461 evas_object_geometry_get(rwhva_->offscreen_helper()->content_image(), &x, &y,
463 gfx::Point magnifier_point(
464 handle->GetBasePosition().x() + x,
465 handle->GetBasePosition().y() + y);
467 magnifier_->UpdateLocation(magnifier_point);
468 magnifier_->Move(magnifier_point);
471 if (handle == input_handle_.get())
474 gfx::Vector2dF base_offset, extent_offset;
476 // When moving the handle we want to move only the extent point.
477 // Before doing so, we must make sure that the base point is set correctly.
478 if (handle == start_handle_.get()) {
479 dragging_handle_ = start_handle_.get();
480 stationary_handle_ = end_handle_.get();
482 base_offset = ComputeLineOffsetFromBottom(end_selection_);
483 extent_offset = ComputeLineOffsetFromBottom(start_selection_);
485 dragging_handle_ = end_handle_.get();
486 stationary_handle_ = start_handle_.get();
488 base_offset = ComputeLineOffsetFromBottom(start_selection_);
489 extent_offset = ComputeLineOffsetFromBottom(end_selection_);
492 float device_scale_factor =
493 display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
494 gfx::PointF base = gfx::ScalePoint(
495 gfx::PointF(stationary_handle_->GetBasePosition()), 1 / device_scale_factor);
496 gfx::PointF extent = gfx::ScalePoint(
497 gfx::PointF(dragging_handle_->GetBasePosition()), 1 / device_scale_factor);
500 extent += extent_offset;
502 WebContentsImpl* wci = static_cast<WebContentsImpl*>(web_contents());
503 wci->SelectRange(gfx::ToFlooredPoint(base), gfx::ToFlooredPoint(extent));
506 void SelectionControllerEfl::HandleDragUpdateNotification(SelectionHandleEfl* handle) {
510 // FIXME : Check the text Direction later
511 gfx::Rect view_bounds = rwhva_->offscreen_helper()->GetViewBoundsInPix();
512 selection_change_reason_ = Reason::HandleDragged;
514 gfx::Point magnifier_point;
515 magnifier_point.set_x(handle->GetBasePosition().x() + view_bounds.x());
516 magnifier_point.set_y(handle->GetBasePosition().y() + view_bounds.y());
517 magnifier_->UpdateLocation(magnifier_point);
518 magnifier_->Move(magnifier_point);
520 switch (handle->Type()) {
521 case SelectionHandleEfl::HANDLE_TYPE_INPUT: {
522 rwhva_->offscreen_helper()->MoveCaret(handle->GetBasePosition());
525 case SelectionHandleEfl::HANDLE_TYPE_LEFT:
526 case SelectionHandleEfl::HANDLE_TYPE_RIGHT: {
527 float device_scale_factor =
528 display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
529 gfx::PointF extent = gfx::ScalePoint(
530 gfx::PointF(handle->GetBasePosition()), 1 / device_scale_factor);
532 bool is_anchor_first = selection_data_->GetIsAnchorFirst();
533 gfx::Vector2dF line_offset = is_anchor_first
534 ? ComputeLineOffsetFromBottom(start_selection_)
535 : ComputeLineOffsetFromBottom(end_selection_);
536 extent += line_offset;
538 WebContentsImpl* wci = static_cast<WebContentsImpl*>(web_contents());
539 wci->MoveRangeSelectionExtent(gfx::ToFlooredPoint(extent));
545 void SelectionControllerEfl::HandleDragEndNotification() {
546 dragged_handle_ = nullptr;
548 if (ecore_events_filter_)
549 ecore_event_filter_del(ecore_events_filter_);
550 ecore_events_filter_ = nullptr;
552 selection_change_reason_ = Reason::Irrelevant;
554 start_handle_->SetBasePosition(selection_data_->GetLeftRect().bottom_left());
555 end_handle_->SetBasePosition(selection_data_->GetRightRect().bottom_right());
556 handle_being_dragged_ = false;
557 ShowHandleAndContextMenuIfRequired(Reason::HandleReleased);
560 void SelectionControllerEfl::GetSelectionBounds(gfx::Rect* left,
563 *left = selection_data_->GetLeftRect();
565 *right = selection_data_->GetRightRect();
568 void SelectionControllerEfl::HandleGesture(blink::WebGestureEvent& event) {
569 blink::WebInputEvent::Type event_type = event.GetType();
570 #if !defined(EWK_BRINGUP) // FIXME: m67 bringup
571 if (event_type == blink::WebInputEvent::kGestureTap) {
572 HandlePostponedGesture(event.x, event.y, ui::ET_GESTURE_TAP);
573 } else if (event_type == blink::WebInputEvent::kGestureShowPress) {
574 HandlePostponedGesture(
575 event.x, event.y, ui::ET_GESTURE_SHOW_PRESS);
576 } else if (event_type == blink::WebInputEvent::kGestureLongPress) {
577 HandlePostponedGesture(
578 event.x, event.y, ui::ET_GESTURE_LONG_PRESS);
579 long_mouse_press_ = true;
582 if (event_type == blink::WebInputEvent::Type::kGestureScrollBegin ||
583 event_type == blink::WebInputEvent::Type::kGesturePinchBegin) {
584 SetControlsTemporarilyHidden(true);
585 } else if (event_type == blink::WebInputEvent::Type::kGestureScrollEnd ||
586 event_type == blink::WebInputEvent::Type::kGesturePinchEnd) {
587 SetControlsTemporarilyHidden(false);
591 void SelectionControllerEfl::HandlePostponedGesture(int x,
593 ui::EventType type) {
594 DVLOG(0) << "HandlePostponedGesture :: " << type;
595 #if !defined(USE_AURA)
596 RenderWidgetHostViewEfl* rwhv =
597 static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
599 gfx::Point point = gfx::Point(x, y);
601 point = rwhv->ConvertPointInViewPix(point);
604 case ui::ET_GESTURE_LONG_PRESS: {
605 ClearSelectionViaEWebView();
606 HideHandleAndContextMenu();
607 // Long press data will call to WebContentsViewDelegateEwk.
608 // It is called by the chain that handles FrameHostMsg_ContextMenu.
611 case ui::ET_GESTURE_TAP:
612 case ui::ET_GESTURE_SHOW_PRESS: {
613 // Do not do anything.
617 ClearSelectionViaEWebView();
622 bool SelectionControllerEfl::HandleLongPressEvent(
623 const gfx::Point& touch_point,
624 const content::ContextMenuParams& params) {
626 if (params.is_editable) {
627 // If one long press on an empty form, do not enter selection mode.
628 // Instead, context menu will be shown if needed for clipboard actions.
629 #if !defined(USE_AURA)
630 RenderWidgetHostViewEfl* rwhv =
631 static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
632 if (params.selection_text.empty() &&
633 !(rwhv && !rwhv->IsLastAvailableTextEmpty()) &&
634 !ClipboardHelperEfl::GetInstance()->CanPasteFromSystemClipboard()) {
635 long_mouse_press_ = false;
639 SetSelectionStatus(true);
640 SetSelectionEditable(true);
641 if (selection_mode_ == None)
642 SetSelectionMode(Caret);
643 HandleLongPressEventPrivate(touch_point);
645 } else if (params.link_url.is_empty() && params.src_url.is_empty() &&
646 params.is_text_node ||
647 !params.selection_text.empty()) {
648 // If user is long pressing on a content with
649 // -webkit-user-select: none, we should bail and not enter
650 // selection neither show magnigier class or context menu.
651 if (params.selection_text.empty()) {
652 long_mouse_press_ = false;
655 SetSelectionStatus(true);
656 SetSelectionEditable(false);
657 HandleLongPressEventPrivate(touch_point);
658 DVLOG(1) << __PRETTY_FUNCTION__ << ":: !link, !image, !media, text";
660 } else if (!params.src_url.is_empty() && params.has_image_contents) {
661 DVLOG(1) << __PRETTY_FUNCTION__ << ":: IMAGE";
662 long_mouse_press_ = false;
664 } else if (!params.link_url.is_empty()) {
665 DVLOG(1) << __PRETTY_FUNCTION__ << ":: LINK";
666 long_mouse_press_ = false;
669 // Default return is true, because if 'params' did not
670 // fall through any of the switch cases above, no further
671 // action should be taken by the callee.
675 void SelectionControllerEfl::HandleLongPressEventPrivate(
676 const gfx::Point& touch_point) {
679 gfx::Rect view_bounds = rwhva_->offscreen_helper()->GetViewBoundsInPix();
680 magnifier_->HandleLongPress(gfx::Point(touch_point.x() + view_bounds.x(),
681 touch_point.y() + view_bounds.y()));
684 void SelectionControllerEfl::HandleLongPressMoveEvent(const gfx::Point& touch_point) {
686 rwhva_->offscreen_helper()->SelectClosestWord(touch_point);
689 void SelectionControllerEfl::HandleLongPressEndEvent() {
690 long_mouse_press_ = false;
691 selection_change_reason_ = Reason::Irrelevant;
692 ShowHandleAndContextMenuIfRequired(Reason::LongPressEnded);
695 void SelectionControllerEfl::PostHandleTapGesture(bool is_content_editable) {
696 if (is_content_editable) {
697 DVLOG(1) << "PostHandleTapGesture :: Editable";
698 SetSelectionStatus(true);
699 SetSelectionEditable(true);
700 selection_change_reason_ = Reason::Tap;
702 SetSelectionEditable(false);
706 bool SelectionControllerEfl::IsSelectionValid(const gfx::Rect& left_rect,
707 const gfx::Rect& right_rect) {
708 TRACE_EVENT2("selection,efl", __PRETTY_FUNCTION__,
709 "left_rect", left_rect.ToString(),
710 "right_rect", right_rect.ToString());
711 // For all normal cases the widht will be 0 and we want to check empty which Implies
712 // x, y, h w all to be 0
713 if ((IsRectEmpty(left_rect) || IsRectEmpty(right_rect))) {
714 SetSelectionStatus(false);
718 // The most of sites return width of each rects as 0 when text is selected.
719 // However, some websites that have viewport attributes on meta tag v
720 // return width 0 right after they are loaded, even though text is not selected.
721 // Thus the width is not sufficient for checking selection condition.
722 // Further invesitigation showed left_rect and right_rect always have the same x,y values
723 // for such cases. So, the equality for x and y rather than width should be tested.
724 if (left_rect.x() == right_rect.x() && left_rect.y() == right_rect.y() &&
725 !selection_data_->IsInEditField() && !handle_being_dragged_ &&
726 selection_change_reason_ == Reason::Irrelevant) {
727 SetSelectionStatus(false);
734 void SelectionControllerEfl::ClearSelection() {
735 TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
737 CancelContextMenu(0);
738 SetSelectionStatus(false);
739 SetSelectionEditable(false);
740 SetSelectionMode(None);
743 void SelectionControllerEfl::OnParentParentViewMove() {
744 TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
745 start_handle_->Move(start_handle_->GetBasePosition());
746 end_handle_->Move(end_handle_->GetBasePosition());
749 gfx::Rect SelectionControllerEfl::GetLeftRect() {
750 return selection_data_->GetLeftRect();
753 gfx::Rect SelectionControllerEfl::GetRightRect() {
754 return selection_data_->GetRightRect();
757 void SelectionControllerEfl::ChangeContextMenuPosition(gfx::Point& position, int& drawDirection) {
758 drawDirection = DirectionNone; // Giving default Direction.
759 int handleHeight, webViewX, webViewY, webViewWidth, webViewHeight;
761 edje_object_part_geometry_get(input_handle_->evas_object(), "handle", 0, 0, 0, &handleHeight);
762 evas_object_geometry_get(rwhva_->offscreen_helper()->content_image(),
763 &webViewX, &webViewY, &webViewWidth, &webViewHeight);
764 gfx::Rect viewportRect = gfx::Rect(webViewX, webViewY, webViewWidth, webViewHeight);
765 #if !defined(USE_AURA)
766 RenderWidgetHostViewEfl* rwhv =
767 static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
768 if (rwhv && rwhv->IsIMEShow()) { // Get the Visible Rect .
769 imeRect = rwhv->GetIMERect();
770 if ((viewportRect.y() + viewportRect.height()) > imeRect.y())
771 viewportRect.set_height(imeRect.y() - viewportRect.y());
774 gfx::Rect leftHandleRect = selection_data_->GetLeftRect();
775 leftHandleRect.set_x(webViewX + leftHandleRect.x());
776 leftHandleRect.set_y(webViewY + leftHandleRect.y());
778 if (!GetCaretSelectionStatus()) {
779 gfx::Rect rightHandleRect = selection_data_->GetRightRect();
780 rightHandleRect.set_x(webViewX + rightHandleRect.x());
781 rightHandleRect.set_y(webViewY + rightHandleRect.y());
782 gfx::Rect oldLeftHandleRect = leftHandleRect;
783 gfx::Rect oldRightHandleRect = rightHandleRect;
784 gfx::Rect effectiveRect;
785 bool isEffecrtiveRectAvailable = false;
788 bool isRightHandle = false;
789 bool doConsiderRightHandle = false;
790 bool doNotConsiderMenuUpward = false;
792 //if (leftHandleRect.x() < 0)
793 // leftHandleRect.set_x(0);
794 //if (leftHandleRect.y() < 0)
795 // leftHandleRect.set_y(0);
797 // Giving first preference to left handle.
798 if (leftHandleRect.IsEmpty() && viewportRect.Contains(leftHandleRect.x(), leftHandleRect.y())) {
799 isTop = start_handle_->IsTop();
800 effectiveRect = leftHandleRect;
801 isEffecrtiveRectAvailable = true;
802 // Check whether Menu will overlap the right handle or not.
803 if (leftHandleRect != rightHandleRect) {
805 // If there is sufficient space above the handler that Menu can be placed. then shift context menu
806 // then no need to change effectiveRect from leftHandleRect to rightHandleRect.
807 bool directionUp = effectiveRect.y() - menuHeight > viewportRect.y() && (effectiveRect.y() - (viewportRect.y() + menuHeight) > menuPadding);
808 if (!directionUp && ((leftHandleRect.y() + leftHandleRect.height()) + menuHeight) > rightHandleRect.y()) {
809 doConsiderRightHandle = true;
810 doNotConsiderMenuUpward = true; // As if we draw the direction is UP it will overlap with left Handle.
815 doConsiderRightHandle = true;
818 if (doConsiderRightHandle && rightHandleRect.IsEmpty() && viewportRect.Contains(rightHandleRect.x(), rightHandleRect.y())) {
819 effectiveRect = rightHandleRect;
820 isEffecrtiveRectAvailable = true;
821 isTop = end_handle_->IsTop();
822 isRightHandle = true;
825 if (isEffecrtiveRectAvailable) {
826 //We will go for UpWard, DownWard, Left, Right directions.
827 if (effectiveRect.y() - menuHeight > viewportRect.y() && (effectiveRect.y() - (viewportRect.y() + menuHeight) > menuPadding) && !doNotConsiderMenuUpward) {
830 position.set_y(effectiveRect.y() - spacePadding - handleHeight);
832 position.set_y(effectiveRect.y() - spacePadding - handleHeight);
833 //position.set_y(position.y() - effectiveRect.height());
836 position.set_y(effectiveRect.y() - spacePadding);
838 drawDirection = DirectionUp;
839 } else if ((effectiveRect.y() + effectiveRect.height()) + menuHeight < (viewportRect.y() + viewportRect.height())) {
840 position.set_y((effectiveRect.y() + effectiveRect.height()) - spacePadding + handleHeight);
841 drawDirection = DirectionDown;
842 } else if (effectiveRect.x() < (viewportRect.x() + viewportRect.width()/2)) {
843 position.set_x((effectiveRect.x() + effectiveRect.width()) + spacePadding + handleHeight);
844 position.set_y(effectiveRect.CenterPoint().y());
845 drawDirection = DirectionLeft;
847 position.set_x(effectiveRect.x());
848 position.set_y(effectiveRect.CenterPoint().y());
849 drawDirection = DirectionRight;
852 if (oldLeftHandleRect.y() < viewportRect.y() && oldRightHandleRect.y() > (viewportRect.y() + viewportRect.height()) && viewportRect.height() <= webViewHeight) {
853 position.set_y(viewportRect.CenterPoint().y() - spacePadding);
855 position.set_y(position.y() + spacePadding + handleHeight);// This value is chosen based on the how much close the context menu arrow should come to word.
857 drawDirection = DirectionNone;
863 if (!selection_data_->IsInEditField())
866 position.set_y(leftHandleRect.y() - spacePadding);
868 if (input_handle_->IsTop()) {
869 position.set_y(position.y() - leftHandleRect.height() - handleHeight);
873 // The Width of the context menu is menuHeight so comapairing current position with the viewport point plus menuHeight.
874 // If position is less than this value then set new position for the contextmenu.
875 if ((position.y() <= (webViewY + menuHeight)) || ((position.y() - (webViewY + menuHeight)) <= menuPadding)) {
876 if (leftHandleRect.IsEmpty()) {
877 position.set_y(position.y() + spacePadding + handleHeight);// This value is chosen based on the how much close the context menu arrow should come to word.
878 drawDirection = DirectionDown;
882 position.set_y((leftHandleRect.y() + leftHandleRect.height()) - spacePadding + handleHeight);
883 drawDirection = DirectionDown;
890 gfx::Rect SelectionControllerEfl::GetVisibleViewportRect() const {
891 if (custom_visible_view_rect_.IsEmpty())
892 return rwhva_->offscreen_helper()->GetViewBoundsInPix();
894 return gfx::IntersectRects(rwhva_->offscreen_helper()->GetViewBoundsInPix(),
895 custom_visible_view_rect_);
898 bool SelectionControllerEfl::TextSelectionDown(int x, int y) {
900 * According to webkit-efl textSelectionDown is used on long press gesture, we already
901 * have implementation for handling this gesture in SelectionControllerEfl so we just
902 * fallback into it. Although I'm not totally sure that this is expected behaviour as there
903 * is no clear explanation what should this API do.
905 * Reference from webkit-efl:
906 * Source/WebKit2/UIProcess/API/efl/ewk_view.cpp line 614
908 if (!long_mouse_press_) {
909 long_mouse_press_ = true;
910 HandleLongPressEventPrivate(gfx::Point(x, y));
917 bool SelectionControllerEfl::TextSelectionUp(int /*x*/, int /*y*/) {
919 * According to webkit-efl textSelectionUp is used when MouseUp event occurs. We already
920 * have implementation for handling MouseUp after long press in SelectionMagnifierEfl so we just
921 * fallback into it. Although I'm not totally sure that this is expected behaviour as there
922 * is no clear explanation what should this API do.
924 * Reference from webkit-efl:
925 * Source/WebKit2/UIProcess/API/efl/tizen/TextSelection.cpp line 807
928 if (long_mouse_press_) {
929 magnifier_->OnAnimatorUp();