Improve text selection handling
authorPiotr Tworek <p.tworek@samsung.com>
Thu, 19 Jun 2014 18:34:42 +0000 (11:34 -0700)
committerYoungsoo Choi <kenshin.choi@samsung.com>
Tue, 10 Jul 2018 06:57:09 +0000 (06:57 +0000)
This patch fixes several selection handle drawing issues caused by the
fact that SelectionBoxEfl was only fully updated from after mouse up event
handler was invoked. If a user manipulated the handles in a way that the
handles needed to be updated several times during one selection the end
results were not always matching the real state of the selection.

Long term, it'd be good if the selection state could be storred only in
one class. As it is right now the actual state of the selection resides
in the SelectionBoxEfl, while the visible state of the handles is in
SelectionHandleEfl. Those states are 'synchronized' in
SelectionControllerEfl. Such approach is very error prone as it's not
always obvious which state is valid at any given point in time.

Patch also hide context menu during magnifier is showing.

Issue-Id: CBBROWSER-156

Change-Id: I8c13e6c2734b294b5e912ce3a9fa78899da7e6de

Conflicts:
impl/browser/renderer_host/render_widget_host_view_efl.cc
impl/browser/renderer_host/render_widget_host_view_efl.h
impl/selection_controller_efl.cc
impl/selection_controller_efl.h

tizen_src/impl/browser/renderer_host/render_widget_host_view_efl.cc
tizen_src/impl/browser/renderer_host/render_widget_host_view_efl.h
tizen_src/impl/selection_box_efl.cc
tizen_src/impl/selection_box_efl.h
tizen_src/impl/selection_controller_efl.cc
tizen_src/impl/selection_controller_efl.h
tizen_src/impl/selection_handle_efl.cc
tizen_src/impl/selection_handle_efl.h
tizen_src/impl/selection_magnifier_efl.cc
tizen_src/impl/selection_magnifier_efl.h

index 051ac9ad316759277210ef3320b039eb201c3c78..b044fc8db846d2164ec6a9e361d9219babdea281 100644 (file)
@@ -503,6 +503,14 @@ void RenderWidgetHostViewEfl::TextInputTypeChanged(ui::TextInputType type, ui::T
     // Finally, the empty rect is not used.
     host_->ScrollFocusedEditableNodeIntoRect(gfx::Rect(0, 0, 0, 0));
   }
+
+  if (GetSelectionController()) {
+    GetSelectionController()->SetSelectionEditable(
+      type == ui::TEXT_INPUT_TYPE_TEXT ||
+      type == ui::TEXT_INPUT_TYPE_PASSWORD ||
+      type == ui::TEXT_INPUT_TYPE_TEXT_AREA ||
+      type == ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE);
+  }
 }
 
 void RenderWidgetHostViewEfl::TextInputStateChanged(
@@ -513,33 +521,18 @@ void RenderWidgetHostViewEfl::TextInputStateChanged(
 }
 
 void RenderWidgetHostViewEfl::ImeCancelComposition() {
-  LOG(INFO) << __PRETTY_FUNCTION__;
   if (im_context_)
     im_context_->CancelComposition();
 }
 
-void RenderWidgetHostViewEfl::OnTextInputInFormStateChanged(bool is_in_form_tag)
-{
+void RenderWidgetHostViewEfl::OnTextInputInFormStateChanged(bool is_in_form_tag) {
   if (im_context_)
     im_context_->SetIsInFormTag(is_in_form_tag);
 }
 
-bool RenderWidgetHostViewEfl::GetCompositionCharacterBounds(uint32 index, gfx::Rect* rect) const
-{
-  LOG(INFO) << __PRETTY_FUNCTION__;
-  DCHECK(rect);
-  if (index >= composition_character_bounds_.size())
-    return false;
-
-  *rect = composition_character_bounds_[index];
-  return true;
-}
-
 void RenderWidgetHostViewEfl::ImeCompositionRangeChanged(
-    const gfx::Range& range,
-    const std::vector<gfx::Rect>& character_bounds) {
-  LOG(INFO) << __PRETTY_FUNCTION__;
-  composition_character_bounds_ = character_bounds;
+  const gfx::Range& range,
+  const std::vector<gfx::Rect>& character_bounds) {
   SelectionControllerEfl* controller = web_view_->GetSelectionController();
   if (controller) {
     controller->SetCaretSelectionStatus(false);
@@ -569,7 +562,6 @@ void RenderWidgetHostViewEfl::SetTooltipText(const base::string16& text) {
 void RenderWidgetHostViewEfl::SelectionChanged(const base::string16& text,
   size_t offset,
   const gfx::Range& range) {
-  LOG(INFO) << __PRETTY_FUNCTION__;
   SelectionControllerEfl* controller = web_view_->GetSelectionController();
   if (controller)
     controller->UpdateSelectionData(text);
@@ -577,7 +569,6 @@ void RenderWidgetHostViewEfl::SelectionChanged(const base::string16& text,
 
 void RenderWidgetHostViewEfl::SelectionBoundsChanged(
   const ViewHostMsg_SelectionBounds_Params& params) {
-  LOG(INFO) << __PRETTY_FUNCTION__;
   ViewHostMsg_SelectionBounds_Params guest_params(params);
   guest_params.anchor_rect = ConvertRectToPixel(device_scale_factor_, params.anchor_rect);
   guest_params.focus_rect = ConvertRectToPixel(device_scale_factor_, params.focus_rect);
@@ -596,7 +587,8 @@ void RenderWidgetHostViewEfl::ScrollOffsetChanged() {
 
 void RenderWidgetHostViewEfl::SelectionRootBoundsChanged(const gfx::Rect& rect) {
   if (GetSelectionController())
-    GetSelectionController()->SetVisibilityBounds(rect);
+    GetSelectionController()->SetVisibilityBounds(
+      ConvertRectToPixel(device_scale_factor_, rect));
 }
 
 void RenderWidgetHostViewEfl::DidStopFlinging() {
@@ -1137,8 +1129,6 @@ void RenderWidgetHostViewEfl::HandleGesture(ui::GestureEvent* event) {
   gesture.globalX = root_point.x();
   gesture.globalY = root_point.y();
 
-  LOG(INFO) << "RenderWidgetHostViewEfl::HandleGesture : type = " << event->type();
-
   if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
     // Webkit does not stop a fling-scroll on tap-down. So explicitly send an
     // event to stop any in-progress flings.
index af6a939a6a454dc0c3a8150fce12192d49907e28..ea98c15b87da773827bcb09fcd8b03eeaf2ace99 100755 (executable)
@@ -57,7 +57,6 @@ class ReadbackYUVInterface;
 // RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
 class RenderWidgetHostViewEfl
   : public RenderWidgetHostViewBase,
-    public ui::TextInputClient,
     public base::SupportsWeakPtr<RenderWidgetHostViewEfl>,
     public IPC::Sender {
  public:
@@ -158,44 +157,11 @@ class RenderWidgetHostViewEfl
   virtual void RenderProcessGone(base::TerminationStatus, int) OVERRIDE;
   virtual bool OnMessageReceived(const IPC::Message&) OVERRIDE;
   virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo&, InputEventAckState) OVERRIDE;
+  virtual void DidStopFlinging() OVERRIDE;
 
-  virtual bool IsEditingCommandEnabled(int) OVERRIDE { return false; }
-  virtual void ExecuteEditingCommand(int) OVERRIDE {}
   // IPC::Sender implementation:
   virtual bool Send(IPC::Message*) OVERRIDE;
 
-  // Overridden from ui::TextInputClient implementation:
-  virtual void SetCompositionText(
-      const ui::CompositionText& composition) OVERRIDE {}
-  virtual void ConfirmCompositionText() OVERRIDE {}
-  virtual void ClearCompositionText() OVERRIDE {}
-  virtual void InsertText(const base::string16& text) OVERRIDE {}
-  virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {}
-  virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE { return gfx::NativeWindow(); }
-  virtual ui::TextInputType GetTextInputType() const OVERRIDE { return ui::TextInputType(); }
-  virtual ui::TextInputMode GetTextInputMode() const OVERRIDE { return ui::TextInputMode(); }
-  virtual bool CanComposeInline() const OVERRIDE { return false; }
-  virtual gfx::Rect GetCaretBounds() const OVERRIDE { return gfx::Rect(); }
-  virtual bool GetCompositionCharacterBounds(uint32 index,
-                                             gfx::Rect* rect) const OVERRIDE;
-  virtual bool HasCompositionText() const OVERRIDE { return false; }
-  virtual bool GetTextRange(gfx::Range* range) const OVERRIDE { return false; }
-  virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE { return false; }
-  virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE { return false; }
-  virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE { return false; }
-  virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; }
-  virtual bool GetTextFromRange(const gfx::Range& range,
-                                base::string16* text) const OVERRIDE { return false; }
-  virtual void OnInputMethodChanged() OVERRIDE {}
-  virtual bool ChangeTextDirectionAndLayoutAlignment(
-      base::i18n::TextDirection direction) OVERRIDE { return false; }
-  virtual void ExtendSelectionAndDelete(size_t before, size_t after) OVERRIDE {}
-  virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {}
-  virtual void OnCandidateWindowShown() OVERRIDE {}
-  virtual void OnCandidateWindowUpdated() OVERRIDE {}
-  virtual void OnCandidateWindowHidden() OVERRIDE {}
-  virtual void DidStopFlinging() OVERRIDE;
-
   void OnDidFirstVisuallyNonEmptyLayout();
   void OnSelectionTextStyleState(const SelectionStylePrams& params);
   void OnDidChangeMaxScrollOffset(int maxScrollX, int maxScrollY);
@@ -305,9 +271,6 @@ class RenderWidgetHostViewEfl
   typedef std::map<gfx::PluginWindowHandle, Ecore_X_Window> PluginWindowToWidgetMap;
   PluginWindowToWidgetMap plugin_window_to_widget_map_;
 
-  // The current composition character bounds.
-  std::vector<gfx::Rect> composition_character_bounds_;
-
   bool m_magnifier;
 
   // Whether we are currently loading.
index 505d98af1440cfd4d8fbc3364656274006cb781f..1354fe694640e6ab92df89c387bfda863d4d8728 100644 (file)
@@ -25,22 +25,11 @@ namespace content {
 SelectionBoxEfl::SelectionBoxEfl(EWebView* parent_view)
   : status_(false),
     editable_(false),
-    is_anchor_first_(true),
     is_caret_selection_(false),
     context_params_(new ContextMenuParams()),
     parent_view_(parent_view) {
 }
 
-void SelectionBoxEfl::UpdateHandleData() {
-  // Swap the handlers when these handles cross over
-  if (!is_anchor_first_) {
-    gfx::Rect swap_rect_;
-    swap_rect_ = left_rect_;
-    left_rect_ = right_rect_;
-    right_rect_ = swap_rect_;
-  }
-}
-
 void SelectionBoxEfl::UpdateSelectStringData(const base::string16& text) {
   context_params_->selection_text = text;
 }
@@ -51,13 +40,17 @@ void SelectionBoxEfl::ClearRectData() {
   context_params_->x = context_params_->y = 0;
 }
 
-void SelectionBoxEfl::UpdateRectData(const gfx::Rect& left_rect, const gfx::Rect& right_rect, bool is_anchor_first) {
+void SelectionBoxEfl::UpdateRectData(const gfx::Rect& left_rect, const gfx::Rect& right_rect) {
   TRACE_EVENT2("selection,efl", __PRETTY_FUNCTION__,
                "left_rect", left_rect.ToString(),
                "right_rect", right_rect.ToString());
-  is_anchor_first_ = is_anchor_first;
-  left_rect_ = left_rect;
-  right_rect_ = right_rect;
+  if (left_rect < right_rect) {
+    left_rect_ = left_rect;
+    right_rect_ = right_rect;
+  } else {
+    right_rect_ = left_rect;
+    left_rect_ = right_rect;
+  }
 
   // Display point of context Menu
   Evas_Coord x, y;
@@ -65,8 +58,6 @@ void SelectionBoxEfl::UpdateRectData(const gfx::Rect& left_rect, const gfx::Rect
   //context params suppose to be global - related to evas not the web view
   context_params_->x = left_rect_.x() + x;
   context_params_->y = left_rect_.y() + y;
-
-  UpdateHandleData();
 }
 
 bool SelectionBoxEfl::IsInEditField() const {
index df231fd2ef1f91d7996af347b15505825318c2e0..902602db3383e7323c292b57968ce3a724a667f3 100644 (file)
@@ -41,9 +41,8 @@ class SelectionBoxEfl {
   bool GetStatus() const { return status_; }
   void SetEditable(bool enable) { GetContextMenuParams()->is_editable = editable_ = enable; }
   bool GetEditable() const { return editable_; }
-  void UpdateHandleData();
   void UpdateSelectStringData(const base::string16& text);
-  void UpdateRectData(const gfx::Rect& left_rect, const gfx::Rect& right_rect, bool is_anchor_first);
+  void UpdateRectData(const gfx::Rect& left_rect, const gfx::Rect& right_rect);
   void ClearRectData();
   bool IsInEditField() const;
   void SetCaretSelectionStatus(const bool enable) { is_caret_selection_ = enable; }
@@ -51,7 +50,6 @@ class SelectionBoxEfl {
   gfx::Rect GetLeftRect() const { return left_rect_; }
   gfx::Rect GetRightRect() const { return right_rect_; }
   ContextMenuParams* GetContextMenuParams() const { return context_params_.get(); }
-  bool IsAnchorFirst() { return is_anchor_first_; }
 
  private:
   // Save the state of selection, if active or not
@@ -60,9 +58,6 @@ class SelectionBoxEfl {
   // Save if the selection is in one of the editable fields
   bool editable_;
 
-  // Is set if handlers crossover rects are interchanged
-  bool is_anchor_first_;
-
   // Caret is in a input field
   bool is_caret_selection_;
 
index 4a5858c5e799f5aaeb2661fd3ac79b59eab50f77..adc70cc276993c4b9f76b43463adc0364846dbcb 100644 (file)
@@ -42,11 +42,13 @@ SelectionControllerEfl::SelectionControllerEfl(EWebView* parent_view)
     :  parent_view_(parent_view),
        mouse_press_(false),
        scrolling_(false),
+       expecting_update_(false),
        long_mouse_press_(false),
        selection_data_(new SelectionBoxEfl(parent_view)),
-       start_handle_(new SelectionHandleEfl(this, SelectionHandleEfl::HANDLE_TYPE_LEFT, parent_view->evas_object())),
-       end_handle_(new SelectionHandleEfl(this, SelectionHandleEfl::HANDLE_TYPE_RIGHT, parent_view->evas_object())),
-       input_handle_(new SelectionHandleEfl(this, SelectionHandleEfl::HANDLE_TYPE_INPUT, parent_view->evas_object())),
+       start_handle_(new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_LEFT, parent_view->evas_object())),
+       end_handle_(new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_RIGHT, parent_view->evas_object())),
+       input_handle_(new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_INPUT, parent_view->evas_object())),
+
        magnifier_(new SelectionMagnifierEfl(this)) {
   evas_object_event_callback_add(parent_view_->evas_object(), EVAS_CALLBACK_MOVE, &EvasParentViewMoveCallback, this);
 
@@ -131,70 +133,74 @@ void SelectionControllerEfl::ClearSelectionViaEWebView() {
   parent_view_->ClearSelection();
 }
 
-void SelectionControllerEfl::UpdateSelectionDataAndShow(const gfx::Rect& left_rect, const gfx::Rect& right_rect, bool is_anchor_first) {
+void SelectionControllerEfl::UpdateSelectionDataAndShow(const gfx::Rect& left_rect, const gfx::Rect& right_rect, bool) {
   TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
-  selection_data_->UpdateRectData(left_rect, right_rect, is_anchor_first);
-  parent_view_->QuerySelectionStyle();
+  selection_data_->UpdateRectData(left_rect, right_rect);
 
   if (!IsSelectionValid(left_rect, right_rect)) {
     selection_data_->ClearRectData();
     Clear();
-    return;
+  } else {
+    if (selection_data_->GetEditable()) {
+      // In case we're selecting text in editable text field we've already sent swapped
+      // coordinates from OnMouseMove. No need to do it for the second time.
+      ShowHandleAndContextMenuIfRequired();
+    } else {
+      ShowHandleAndContextMenuIfRequired(left_rect < right_rect);
+    }
   }
-
-  // Do not show the context menu and handlers untill long mouse press is released
-  if (long_mouse_press_)
-    return;
-
-  // Do not show the context menu and handlers while page is scrolling
-  if (scrolling_)
-    return;
-
-  ShowHandleAndContextMenuIfRequired();
+  expecting_update_ = false;
 }
 
-void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired() {
+void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired(bool anchor_first) {
   TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
   if (!selection_data_->GetStatus())
     return;
 
-  Clear();
+  gfx::Rect left, right;
+  if (anchor_first) {
+    left = selection_data_->GetLeftRect();
+    right = selection_data_->GetRightRect();
+  } else {
+    right = selection_data_->GetLeftRect();
+    left = selection_data_->GetRightRect();
+  }
 
   // Is in edit field and no text is selected. show only single handle
-  if (selection_data_->IsInEditField() && GetCaretSelectionStatus()) {
+  if (selection_data_->IsInEditField() && left == right) {
     gfx::Rect left = selection_data_->GetLeftRect();
     input_handle_->SetBasePosition(gfx::Point(left.x(), left.y()));
-    input_handle_->SetCursorHandlerStatus(true);
-    input_handle_->Move(gfx::Point(left.x(), left.y() + left.height()));
+    input_handle_->Move(left.bottom_right());
     input_handle_->Show();
+    start_handle_->Hide();
+    end_handle_->Hide();
 
     if (!mouse_press_)
       parent_view_->ShowContextMenu(*(selection_data_->GetContextMenuParams()), MENU_TYPE_SELECTION);
     parent_view_->QuerySelectionStyle();
     return;
+  } else {
+    input_handle_->Hide();
   }
 
-  gfx::Rect left = selection_data_->GetLeftRect();
-  gfx::Rect right = selection_data_->GetRightRect();
-
   if (left.x() == 0 && left.y() == 0 && right.x() == 0 && right.y() == 0) {
     selection_data_->ClearRectData();
     return;
   }
   // The base position of start_handle should be set to the middle of the left rectangle.
   // Otherwise the start_handle may be shifted up when the right_handle is moving
-  start_handle_->SetBasePosition(gfx::Point(left.x(), left.y() + (left.height() / 2)));
-  start_handle_->Move(gfx::Point(left.x(), left.y() + left.height()));
-  if (left.x() >= visibility_rect_.x() && left.x() <= (visibility_rect_.x() + visibility_rect_.width()))
+  start_handle_->SetBasePosition(left.CenterPoint());
+  start_handle_->Move(left.bottom_left());
+  if (left.x() >= visibility_rect_.x() && left.x() <= visibility_rect_.right())
     start_handle_->Show();
   else
     start_handle_->Hide();
 
   // The base position of end_handle should be set to the middle of the right rectangle.
   // Otherwise the end_handle may be shifted up when the left_handle is moving
-  end_handle_->SetBasePosition(gfx::Point(right.x(), right.y() + (right.height() / 2)));
-  end_handle_->Move(gfx::Point(right.x() + right.width(), right.y() + right.height()));
-  if (right.x() >= visibility_rect_.x() && right.x() <= (visibility_rect_.x() + visibility_rect_.width()))
+  end_handle_->SetBasePosition(right.CenterPoint());
+  end_handle_->Move(right.bottom_right());
+  if (right.x() >= visibility_rect_.x() && right.x() <= visibility_rect_.right())
     end_handle_->Show();
   else
     end_handle_->Hide();
@@ -227,6 +233,12 @@ void SelectionControllerEfl::Clear() {
   input_handle_->Hide();
 }
 
+bool SelectionControllerEfl::IsShowingMagnifier() {
+  if(magnifier_->IsShowing())
+    return true;
+  return false;
+}
+
 void SelectionControllerEfl::OnMouseDown(const gfx::Point& touch_point) {
   // Hide context menu on mouse down
   parent_view_->CancelContextMenu(0);
@@ -240,14 +252,20 @@ void SelectionControllerEfl::OnMouseMove(const gfx::Point& touch_point, Selectio
   // FIXME : Check the text Direction later
   magnifier_->UpdateLocation(touch_point);
   magnifier_->Move(touch_point);
+  expecting_update_ = true;
   switch (handle) {
     case SelectionHandleEfl::HANDLE_TYPE_INPUT:
       parent_view_->MoveCaret(input_handle_->GetBasePosition());
       return;
     case SelectionHandleEfl::HANDLE_TYPE_LEFT:
-      parent_view_->SelectRange(end_handle_->GetBasePosition(),
-                                start_handle_->GetBasePosition());
-      return;
+      if (GetSelectionEditable()) {
+        // Form elements only support scrolling of extent/end caret. To
+        // move the start element we need to swap the coordinates provided
+        // to SelectRange function.
+        parent_view_->SelectRange(end_handle_->GetBasePosition(),
+                                  start_handle_->GetBasePosition());
+        return;
+      }
     case SelectionHandleEfl::HANDLE_TYPE_RIGHT:
       parent_view_->SelectRange(start_handle_->GetBasePosition(),
                                 end_handle_->GetBasePosition());
@@ -256,10 +274,11 @@ void SelectionControllerEfl::OnMouseMove(const gfx::Point& touch_point, Selectio
 }
 
 void SelectionControllerEfl::OnMouseUp(const gfx::Point& touch_point) {
-  selection_data_->UpdateHandleData();
   mouse_press_ = false;
   magnifier_->Hide();
-  parent_view_->ShowContextMenu(*(selection_data_->GetContextMenuParams()), MENU_TYPE_SELECTION);
+  start_handle_->SetBasePosition(selection_data_->GetLeftRect().bottom_left());
+  end_handle_->SetBasePosition(selection_data_->GetRightRect().bottom_right());
+  ShowHandleAndContextMenuIfRequired();
 }
 
 void SelectionControllerEfl::GetSelectionBounds(gfx::Rect* left, gfx::Rect* right) {
@@ -271,6 +290,7 @@ void SelectionControllerEfl::GetSelectionBounds(gfx::Rect* left, gfx::Rect* righ
 
 void SelectionControllerEfl::HandleLongPressEvent(const gfx::Point& touch_point) {
   long_mouse_press_ = true;
+  Clear();
   magnifier_->HandleLongPress(touch_point);
 }
 
@@ -312,8 +332,8 @@ bool SelectionControllerEfl::IsSelectionValid(const gfx::Rect& left_rect, const
   // Thus the width is not sufficient for checking selection condition.
   // Further invesitigation showed left_rect and right_rect always have the same x,y values
   // for such cases. So, the equality for x and y rather than width should be tested.
-  if (left_rect.x()==right_rect.x() && left_rect.y()==right_rect.y() &&
-      !selection_data_->IsInEditField() && !mouse_press_) {
+  if (left_rect.x() == right_rect.x() && left_rect.y() == right_rect.y() &&
+      !selection_data_->IsInEditField()  && !mouse_press_ && !expecting_update_) {
     SetSelectionStatus(false);
     return false;
   }
index 519946f1a9e2a2086c66cea56c9f99ede918d137..faab136e7e1c9fcfb00669622bf4cd01a0e66c51 100644 (file)
@@ -77,6 +77,7 @@ class SelectionControllerEfl {
   // To update the selection bounds
   void UpdateSelectionDataAndShow(const gfx::Rect& left_rect, const gfx::Rect& right_rect, bool is_anchor_first);
   void GetSelectionBounds(gfx::Rect* left, gfx::Rect* right);
+
   // Handles the mouse press,move and relase events on selection handles
   void OnMouseDown(const gfx::Point& touch_point);
   void OnMouseMove(const gfx::Point& touch_point, SelectionHandleEfl::HandleType);
@@ -103,8 +104,12 @@ class SelectionControllerEfl {
 
   void ChangeContextMenuPosition(gfx::Point& position, int& drawDirection);
 
+  bool GetLongPressed() { return long_mouse_press_; }
+
+  bool IsShowingMagnifier();
+
  private:
-  void ShowHandleAndContextMenuIfRequired();
+  void ShowHandleAndContextMenuIfRequired(bool anchor_first = true);
   void Clear();
   bool IsSelectionValid(const gfx::Rect& left_rect, const gfx::Rect& right_rect);
 
@@ -126,6 +131,10 @@ class SelectionControllerEfl {
   // Saves state so that context menu is not displayed during page scrolling
   bool scrolling_;
 
+  // True when new caret/selection position was sent to chromium,
+  // but no reply was received, yet
+  bool expecting_update_;
+
   // Saves state so that handlers and context menu is not shown when seletion change event occurs.
   bool long_mouse_press_;
 
@@ -144,7 +153,7 @@ class SelectionControllerEfl {
   // Points to show the contents magnified
   scoped_ptr<SelectionMagnifierEfl> magnifier_;
 
-  // Visibility rectangle
+  // Rectangle inside of which selection handles should be visible.
   gfx::Rect visibility_rect_;
 };
 
index e0666430fb693709b6a1960c85b46004edb7768c..ddc8d19cd1188b988b20c42aae20a77b215f9505 100644 (file)
@@ -27,6 +27,8 @@
 
 namespace content {
 
+// Size of the left/right margin which causes selection handles to be rotated
+const int kReverseMargin = 32;
 const char* kLeftHandlePath = "elm/entry/selection/block_handle_left";
 const char* kRightHandlePath = "elm/entry/selection/block_handle_right";
 const char* kInputHandlePath = "elm/entry/cursor_handle/default";
@@ -44,13 +46,12 @@ static const char* GetEdjeObjectGroupPath(SelectionHandleEfl::HandleType type) {
   }
 }
 
-SelectionHandleEfl::SelectionHandleEfl(SelectionControllerEfl* controller, HandleType type, Evas_Object* parent)
+SelectionHandleEfl::SelectionHandleEfl(SelectionControllerEfl& controller, HandleType type, Evas_Object* parent)
   : base_point_(0,0),
     current_touch_point_(0,0),
     diff_point_(0,0),
     controller_(controller),
-    is_cursor_handle_(false),
-    is_mouse_downed_(false),
+    pressed_(false),
     is_top_(false),
     handle_type_(type) {
 
@@ -85,7 +86,6 @@ SelectionHandleEfl::~SelectionHandleEfl() {
 }
 
 void SelectionHandleEfl::Show() {
-  // FIXME: Currently no viewport boudry checks present. Add later
   evas_object_show(handle_);
 }
 
@@ -98,157 +98,165 @@ bool SelectionHandleEfl::IsVisible() const {
 }
 
 void SelectionHandleEfl::Move(const gfx::Point& point) {
-  Evas_Coord x, y, deviceWidth, deviceHeight;
-  gfx::Rect selectionRect = GetSelectionRect();
-  gfx::Point movePoint = point;
-  const int reverseMargin = 32;
-  int handleHeight;
-
-  edje_object_part_geometry_get(handle_, "handle", 0, 0, 0, &handleHeight);
-  evas_object_geometry_get(controller_->GetParentView()->evas_object(), &x, &y, &deviceWidth, &deviceHeight);
-
-  if ((handle_type_ == HANDLE_TYPE_LEFT && (movePoint.x() <= reverseMargin))
-      || (handle_type_ == HANDLE_TYPE_RIGHT && (movePoint.x() >= deviceWidth - reverseMargin))) {
-    if ((movePoint.y() + handleHeight) > (y + deviceHeight)) {
-      movePoint.set_y(movePoint.y() - selectionRect.height());
-      if (!is_mouse_downed_) {
-        ChangeObjectDirection(handle_type_, DirectionTopReverse);
-        is_top_ = true;
-      }
-    } else {
-      if (!is_mouse_downed_) {
-        ChangeObjectDirection(handle_type_, DirectionBottomReverse);
-        is_top_ = false;
-      }
-    }
-  } else {
-    if ((movePoint.y() + handleHeight) > (y + deviceHeight)) {
-      // Check If handler is not overLapping Device Boundary.
-      if ((movePoint.y() - selectionRect.height() - handleHeight) >= y) {
-        movePoint.set_y(movePoint.y() - selectionRect.height());
-        if (!is_mouse_downed_) {
-          ChangeObjectDirection(handle_type_, DirectionTopNormal);
-          is_top_ = true;
-        }
-        MoveObject(movePoint);
-        return;
-      }
-    }
-
-    if (!is_mouse_downed_) {
-      ChangeObjectDirection(handle_type_, DirectionBottomNormal);
-      is_top_ = false;
-    }
-  }
-
-  MoveObject(movePoint);
+  ChangeObjectDirection(CalculateDirection(point));
+  MoveObject(point);
 }
 
 void SelectionHandleEfl::OnMouseDown(void* data, Evas*, Evas_Object*, void* event_info) {
   Evas_Event_Mouse_Down* event = static_cast<Evas_Event_Mouse_Down*>(event_info);
   SelectionHandleEfl* handle = static_cast<SelectionHandleEfl*>(data);
-  handle->SetIsMouseDowned(true);
+  handle->pressed_ = true;
   evas_object_event_callback_add(handle->handle_, EVAS_CALLBACK_MOUSE_MOVE, OnMouseMove, handle);
   //Save the diff_point between touch point and base point of the handle
   handle->diff_point_.SetPoint(event->canvas.x - handle->base_point_.x(), event->canvas.y - handle->base_point_.y());
-  handle->controller_->OnMouseDown(gfx::Point(event->canvas.x , event->canvas.y ));
+  handle->controller_.OnMouseDown(gfx::Point(event->canvas.x , event->canvas.y ));
 }
 
 void SelectionHandleEfl::OnMouseMove(void* data, Evas*, Evas_Object*, void* event_info) {
   Evas_Event_Mouse_Move* event = static_cast<Evas_Event_Mouse_Move*>(event_info);
   SelectionHandleEfl* handle = static_cast<SelectionHandleEfl*>(data);
   handle->current_touch_point_.SetPoint(event->cur.canvas.x, event->cur.canvas.y);
-   ecore_job_add(UpdateMouseMove, handle);
+  ecore_job_add(UpdateMouseMove, handle);
 }
 
 void SelectionHandleEfl::OnMouseUp(void* data, Evas*, Evas_Object*, void* event_info) {
   Evas_Event_Mouse_Up* event = static_cast<Evas_Event_Mouse_Up*>(event_info);
   SelectionHandleEfl* handle = static_cast<SelectionHandleEfl*>(data);
-  handle->SetIsMouseDowned(false);
+  handle->pressed_ = false;
   evas_object_event_callback_del(handle->handle_, EVAS_CALLBACK_MOUSE_MOVE, OnMouseMove);
 
   Evas_Coord x, y;
-  evas_object_geometry_get(handle->controller_->GetParentView()->evas_object(), &x, &y, 0, 0);
-  handle->controller_->OnMouseUp(gfx::Point(event->canvas.x - x, event->canvas.y - y));
+  evas_object_geometry_get(handle->controller_.GetParentView()->evas_object(), &x, &y, 0, 0);
+  handle->controller_.OnMouseUp(gfx::Point(event->canvas.x - x, event->canvas.y - y));
 }
 
 void SelectionHandleEfl::UpdateMouseMove(void* data) {
   SelectionHandleEfl* handle = static_cast<SelectionHandleEfl*>(data);
   Evas_Coord x, y;
-  evas_object_geometry_get(handle->controller_->GetParentView()->evas_object(), &x, &y, 0, 0);
-  if (!handle->is_cursor_handle_) {
+  evas_object_geometry_get(handle->controller_.GetParentView()->evas_object(), &x, &y, 0, 0);
+  if (handle->handle_type_ != HANDLE_TYPE_INPUT) {
     //New base point calculation should be based on new touch point and diff_point
     int delta_x = handle->current_touch_point_.x() - handle->diff_point_.x();
     int delta_y = handle->current_touch_point_.y() - handle->diff_point_.y();
     handle->base_point_.SetPoint(delta_x, delta_y);
-    handle->controller_->OnMouseMove(
+    handle->controller_.OnMouseMove(
         gfx::Point(handle->current_touch_point_.x(),
                    handle->current_touch_point_.y()), handle->handle_type_);
   } else {
     handle->base_point_.set_x(handle->current_touch_point_.x() - x);
-    handle->controller_->OnMouseMove(
+    handle->controller_.OnMouseMove(
         gfx::Point(handle->current_touch_point_.x() - x,
                    handle->current_touch_point_.y() - y), handle->handle_type_);
   }
 }
 
-void SelectionHandleEfl::ChangeObjectDirection(HandleType handleType, int direction) {
+SelectionHandleEfl::HandleDirection SelectionHandleEfl::CalculateDirection(
+    const gfx::Point& point) const {
+  bool reverse_horizontally = false,  reverse_vertically = false;
+  Evas_Coord x, y, deviceWidth, deviceHeight;
+  int handleHeight;
+
+  edje_object_part_geometry_get(handle_, "handle", 0, 0, 0, &handleHeight);
+  evas_object_geometry_get(controller_.GetParentView()->evas_object(),
+      &x, &y, &deviceWidth, &deviceHeight);
+  gfx::Rect reverse_direction_rect = gfx::Rect(x + kReverseMargin,
+      y, deviceWidth - 2 * kReverseMargin, deviceHeight - handleHeight);
+
+  if (!reverse_direction_rect.Contains(point)) {
+    if (point.x() <= reverse_direction_rect.x() ||
+        point.x() >= reverse_direction_rect.right()) {
+      reverse_vertically = true;
+    }
+    if (point.y() <= reverse_direction_rect.y() ||
+        point.y() >= reverse_direction_rect.bottom()) {
+      reverse_horizontally = true;
+    }
+  }
+
+  if (handle_type_ != HANDLE_TYPE_INPUT) {
+     if (reverse_vertically) {
+       if (reverse_horizontally) {
+         return DirectionTopReverse;
+       } else {
+         return DirectionBottomReverse;
+       }
+     } else {
+       if (reverse_horizontally) {
+         return DirectionTopNormal;
+       } else {
+         return DirectionBottomNormal;
+       }
+     }
+  } else {
+    // Input handle can only be rotated horizontally
+    if (reverse_horizontally) {
+      return DirectionTopNormal;
+    } else {
+      return DirectionBottomNormal;
+    }
+  }
+
+  NOTREACHED();
+}
+
+void SelectionHandleEfl::ChangeObjectDirection(HandleDirection direction) {
   TRACE_EVENT2("selection,efl", __PRETTY_FUNCTION__,
-               "handle type", handleType, "direction", direction);
+               "handle type", handle_type_, "direction", direction);
+
+  is_top_ = (direction == DirectionTopNormal || direction == DirectionTopReverse);
+
   switch (direction) {
     case DirectionBottomNormal:
-      if (is_cursor_handle_)
+      if (handle_type_ == HANDLE_TYPE_INPUT)
         edje_object_signal_emit(handle_, "edje,cursor,handle,show", "edje");
       else
         edje_object_signal_emit(handle_, "elm,state,bottom", "elm");
       break;
     case DirectionBottomReverse:
-      if (is_cursor_handle_)
+      if (handle_type_ == HANDLE_TYPE_INPUT)
         edje_object_signal_emit(handle_, "edje,cursor,handle,show", "edje");
       else
         edje_object_signal_emit(handle_, "elm,state,bottom,reversed", "elm");
       break;
     case DirectionTopNormal:
-      if (is_cursor_handle_)
+      if (handle_type_ == HANDLE_TYPE_INPUT)
         edje_object_signal_emit(handle_, "edje,cursor,handle,top", "edje");
       else
         edje_object_signal_emit(handle_, "elm,state,top", "elm");
       break;
     case DirectionTopReverse:
-      if (is_cursor_handle_)
+      if (handle_type_ == HANDLE_TYPE_INPUT)
         edje_object_signal_emit(handle_, "edje,cursor,handle,top", "edje");
       else
         edje_object_signal_emit(handle_, "elm,state,top,reversed", "elm");
       break;
     }
 
-  switch (handleType) {
+  switch (handle_type_) {
     case HANDLE_TYPE_LEFT:
-      evas_object_smart_callback_call(controller_->GetParentView()->evas_object(), "selection,handle,left,direction", &direction);
+      evas_object_smart_callback_call(controller_.GetParentView()->evas_object(),
+        "selection,handle,left,direction", &direction);
       break;
     case HANDLE_TYPE_RIGHT:
-      evas_object_smart_callback_call(controller_->GetParentView()->evas_object(), "selection,handle,right,direction", &direction);
+      evas_object_smart_callback_call(controller_.GetParentView()->evas_object(),
+        "selection,handle,right,direction", &direction);
       break;
     case HANDLE_TYPE_INPUT:
-      evas_object_smart_callback_call(controller_->GetParentView()->evas_object(), "selection,handle,large,direction", &direction);
+      evas_object_smart_callback_call(controller_.GetParentView()->evas_object(),
+        "selection,handle,large,direction", &direction);
       break;
   }
-  handle_type_ = handleType;
-}
-
-SelectionHandleEfl::HandleType SelectionHandleEfl::GetHandleType() const {
-  return handle_type_;
 }
 
-gfx::Rect SelectionHandleEfl::GetSelectionRect() {
+gfx::Rect SelectionHandleEfl::GetSelectionRect() const {
   gfx::Rect rect;
 
   switch(handle_type_) {
   case HANDLE_TYPE_RIGHT:
-    return controller_->GetRightRect();
+    return controller_.GetRightRect();
 
   case HANDLE_TYPE_LEFT:
-    return controller_->GetLeftRect();
+    return controller_.GetLeftRect();
 
   case HANDLE_TYPE_INPUT:
     // no rect for this type of handle
@@ -258,10 +266,10 @@ gfx::Rect SelectionHandleEfl::GetSelectionRect() {
   return gfx::Rect();
 }
 
-void SelectionHandleEfl::MoveObject(gfx::Point& point) {
+void SelectionHandleEfl::MoveObject(const gfx::Point& point) {
   Evas_Coord x, y;
-  evas_object_geometry_get(controller_->GetParentView()->evas_object(), &x, &y, 0, 0);
+  evas_object_geometry_get(controller_.GetParentView()->evas_object(), &x, &y, 0, 0);
   evas_object_move(handle_, point.x() + x, point.y() + y);
 }
 
-}
+} // namespace content
index bfca980235ec0815fd1757bc56c506a4c027c7d9..edd64fec9ca52f6dadd3fd5946bbfab9295d2caa 100644 (file)
@@ -55,7 +55,7 @@ class SelectionHandleEfl {
     DirectionNone,
   };
 
-  SelectionHandleEfl(SelectionControllerEfl* controller, HandleType type, Evas_Object* parent);
+  SelectionHandleEfl(SelectionControllerEfl& controller, HandleType type, Evas_Object* parent);
   ~SelectionHandleEfl();
   void Show();
   void Hide();
@@ -63,7 +63,6 @@ class SelectionHandleEfl {
   void Move(const gfx::Point& point);
   void SetBasePosition(const gfx::Point& point) { base_point_ = point; }
   gfx::Point GetBasePosition() const { return base_point_; }
-  void SetCursorHandlerStatus(bool enable) { is_cursor_handle_ = enable; }
   bool IsTop() { return is_top_; }
   Evas_Object* evas_object() const { return handle_; }
 
@@ -73,11 +72,10 @@ class SelectionHandleEfl {
   static void OnMouseUp(void* data, Evas*, Evas_Object*, void* event_info);
   static void UpdateMouseMove(void* data);
 
-  void ChangeObjectDirection(HandleType, int);
-  HandleType GetHandleType() const;
-  gfx::Rect GetSelectionRect();
-  void MoveObject(gfx::Point&);
-  void SetIsMouseDowned(bool enable) { is_mouse_downed_ = enable; };
+  HandleDirection CalculateDirection(const gfx::Point&) const;
+  void ChangeObjectDirection(HandleDirection direction);
+  gfx::Rect GetSelectionRect() const;
+  void MoveObject(const gfx::Point& point);
 
   // This point is one which will be used during extending selection
   // it is in web view coordinates
@@ -91,16 +89,13 @@ class SelectionHandleEfl {
   gfx::Point diff_point_;
 
   // Parent to send back mouse events
-  content::SelectionControllerEfl* controller_;
+  SelectionControllerEfl& controller_;
 
   // Handler object
   Evas_Object* handle_;
 
-  // Is set if the handle is of type input
-  bool is_cursor_handle_;
-
-  // Is mouse down
-  bool is_mouse_downed_;
+  // Is pressed
+  bool pressed_;
 
   // Direction of handle
   bool is_top_;
index 18662957b655345da7333f1efa85274daba969c1..490a5f327916b859721a491d7cf04052f43cce7f 100644 (file)
@@ -36,7 +36,8 @@ const float kZoomScale = 0.66;
 SelectionMagnifierEfl::SelectionMagnifierEfl(content::SelectionControllerEfl* controller)
   : controller_(controller),
     content_image_(0),
-    animator_(0) {
+    animator_(0),
+    shown_(false) {
   Evas_Object* top_widget = elm_object_top_widget_get(
       elm_object_parent_widget_get(controller->GetParentView()->evas_object()));
   if (!top_widget)
@@ -177,12 +178,14 @@ void SelectionMagnifierEfl::Move(const gfx::Point& location) {
 }
 
 void SelectionMagnifierEfl::Show() {
+  shown_ = true;
   evas_object_show(container_);
   controller_->GetParentView()->SmartCallback<EWebViewCallbacks::MagnifierShow>().call();
   controller_->GetParentView()->set_magnifier(true);
 }
 
 void SelectionMagnifierEfl::Hide() {
+  shown_ = false;
   evas_object_hide(content_image_);
   evas_object_hide(container_);
   controller_->GetParentView()->SmartCallback<EWebViewCallbacks::MagnifierHide>().call();
index 381442be359ddfe87367013b5f043daa510b36a1..7addb98630ce7251fc0a85c14d8acb439e0cabfa 100644 (file)
@@ -42,6 +42,7 @@ class SelectionMagnifierEfl {
   void Move(const gfx::Point& location);
   void Show();
   void Hide();
+  bool IsShowing() { return shown_; }
 
  private:
   static Eina_Bool MoveAnimatorCallback(void* data);
@@ -64,6 +65,9 @@ class SelectionMagnifierEfl {
 
   // Handle longmove
   Ecore_Animator* animator_;
+
+  // Is magnifier showing
+  bool shown_;
 };
 
 }