Adjust position of context menu according to visible status
authorYoungbok Yoon <youngbok.yoon@samsung.com>
Tue, 10 Jun 2014 21:09:28 +0000 (14:09 -0700)
committerYoungsoo Choi <kenshin.choi@samsung.com>
Tue, 10 Jul 2018 06:57:09 +0000 (06:57 +0000)
Issue: CBBROWSER-197, CBBROWSER-183, CBBROWSER-182
Change-Id: I4c969ad5f21541964324450e880195026cfbac2c

Conflicts:

impl/browser/renderer_host/im_context_efl.h
impl/eweb_view.h

tizen_src/impl/browser/renderer_host/im_context_efl.cc
tizen_src/impl/browser/renderer_host/im_context_efl.h
tizen_src/impl/browser/renderer_host/render_widget_host_view_efl.h
tizen_src/impl/context_menu_controller_efl.cc
tizen_src/impl/eweb_view.cc
tizen_src/impl/eweb_view.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

index 983bf98..598cded 100644 (file)
@@ -24,7 +24,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "browser/renderer_host/render_widget_host_view_efl.h"
-#include "ui/gfx/rect.h"
 
 #include <Ecore_Evas.h>
 #include <Ecore_IMF_Evas.h>
@@ -405,6 +404,8 @@ void IMContextEfl::OnInputPanelGeometryChanged() {
   Eina_Rectangle rect;
   ecore_imf_context_input_panel_geometry_get(context_, &rect.x, &rect.y, &rect.w, &rect.h);
   view_->eweb_view()->SmartCallback<EWebViewCallbacks::IMEInputMethodChanged>().call(&rect);
+
+  SetIMERect(gfx::Rect(rect.x, rect.y, rect.w, rect.h));
 }
 
 void IMContextEfl::OnCandidateInputPanelStateChanged(int state) {
@@ -432,4 +433,8 @@ void IMContextEfl::OnCandidateInputPanelLanguageChanged(Ecore_IMF_Context* /*con
   composition_.Clear();
 }
 
+bool IMContextEfl::IsShow() {
+  return (context_ && focused_ && ecore_imf_context_input_panel_state_get(context_) != ECORE_IMF_INPUT_PANEL_STATE_HIDE);
+}
+
 } // namespace content
index fad133f..adafe81 100644 (file)
@@ -23,6 +23,7 @@
 #include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ime/text_input_type.h"
 #include "ui/base/ime/composition_text.h"
+#include "ui/gfx/rect.h"
 
 #include <Evas.h>
 
@@ -58,6 +59,8 @@ class IMContextEfl {
   void CancelComposition();
   void ConfirmComposition();
   void SetIsInFormTag(bool is_in_form_tag);
+  bool IsShow();
+  gfx::Rect GetIMERect() const { return ime_rect_; }
 
  private:
   IMContextEfl(RenderWidgetHostViewEfl*, Ecore_IMF_Context*);
@@ -68,6 +71,8 @@ class IMContextEfl {
   void ShowPanel(ui::TextInputType, ui::TextInputMode, bool is_user_action = false);
   void HidePanel();
 
+  void SetIMERect(const gfx::Rect& rect) { ime_rect_ = rect; }
+
   // callbacks
   static void IMFCommitCallback(void* data, Ecore_IMF_Context*, void* event_info) { reinterpret_cast<IMContextEfl*>(data)->OnCommit(event_info); }
   static void IMFPreeditChangedCallback(void* data, Ecore_IMF_Context* context, void* event_info) { reinterpret_cast<IMContextEfl*>(data)->OnPreeditChanged(data, context, event_info); }
@@ -115,6 +120,8 @@ class IMContextEfl {
   bool is_in_form_tag_;
 
   ui::CompositionText composition_;
+
+  gfx::Rect ime_rect_;
 };
 
 } // namespace content
index a4d05ac..c41a3ef 100755 (executable)
@@ -22,6 +22,7 @@
 #include "content/browser/compositor/owned_mailbox.h"
 #include "ui/base/ime/text_input_client.h"
 #include "eweb_view.h"
+#include "browser/renderer_host/im_context_efl.h"
 #include <Evas.h>
 #include <Ecore_Evas.h>
 #include <Evas_GL.h>
@@ -208,6 +209,7 @@ class RenderWidgetHostViewEfl
   void set_eweb_view(EWebView*);
   EWebView* eweb_view() const { return web_view_; }
   RenderWidgetHostImpl* host() const { return host_; }
+  IMContextEfl* im_context() const { return im_context_; }
 
   float device_scale_factor() const { return device_scale_factor_; }
 
index c3c9ee2..af1c201 100644 (file)
@@ -43,6 +43,7 @@ ContextMenuControllerEfl::~ContextMenuControllerEfl() {
 }
 
 bool ContextMenuControllerEfl::PopulateAndShowContextMenu(const ContextMenuParams &params) {
+  LOG(INFO) << __PRETTY_FUNCTION__;
   EWebView* view = ToEWebView(evas_object_);
   if (!view)
     return false;
@@ -203,21 +204,69 @@ bool ContextMenuControllerEfl::ShowContextMenu() {
   if (!popup_)
     return false;
 
-  evas_object_move(popup_, params_.x, params_.y);
-#if defined(OS_TIZEN)
-  // In device context menu is supported as per the platform efl theme.
-  // So this supports horizontal popup and on this popup style is applied.
-  // But style is not there on desktop version so using default provided by efl
+  LOG(INFO) << __PRETTY_FUNCTION__ << "param.x = " << params_.x << " params.y = " << params_.y;
+
   if (type_ == MENU_TYPE_SELECTION) {
-    elm_ctxpopup_horizontal_set(popup_, EINA_TRUE);
-    elm_ctxpopup_direction_priority_set(popup_,
-                                        ELM_CTXPOPUP_DIRECTION_UP,
-                                        ELM_CTXPOPUP_DIRECTION_DOWN,
-                                        ELM_CTXPOPUP_DIRECTION_LEFT,
-                                        ELM_CTXPOPUP_DIRECTION_RIGHT);
+    int webViewX, webViewY, webViewWidth, webViewHeight;
+    Evas_Point point;
+
+    gfx::Point popupPosition = gfx::Point(params_.x, params_.y);
+
+    EWebView* view = ToEWebView(evas_object_);
+    if (!view)
+      return false;
+
+    evas_object_geometry_get(view->evas_object(), &webViewX, &webViewY, &webViewWidth, &webViewHeight);
+    popupPosition.set_x(popupPosition.x() + webViewX);
+    popupPosition.set_y(popupPosition.y() + webViewY);
+
+    point.x = popupPosition.x();
+    point.y = popupPosition.y();
+
+    evas_object_smart_callback_call(view->evas_object(), "contextmenu,willshow", &point);
+
+    bool allowed = true;
+    evas_object_smart_callback_call(view->evas_object(), "contextmenu,allowed", &allowed);
+    if (!allowed) {
+      evas_object_del(popup_);
+      return false;
+    }
+
     elm_object_style_set(popup_, "copypaste");
-  }
+    elm_ctxpopup_horizontal_set(popup_, EINA_TRUE);
+
+    int drawDirection;
+    SelectionControllerEfl* controller = view->GetSelectionController();
+
+    if (!controller) {
+      evas_object_del(popup_);
+      return false;
+    }
+
+    controller->ChangeContextMenuPosition(popupPosition, drawDirection);
+    switch(drawDirection) {
+      case 0:
+        elm_ctxpopup_direction_priority_set(popup_, ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_DOWN);
+        break;
+      case 1:
+        elm_ctxpopup_direction_priority_set(popup_, ELM_CTXPOPUP_DIRECTION_UP, ELM_CTXPOPUP_DIRECTION_UP, ELM_CTXPOPUP_DIRECTION_UP, ELM_CTXPOPUP_DIRECTION_UP);
+        break;
+      case 2:
+        elm_ctxpopup_direction_priority_set(popup_, ELM_CTXPOPUP_DIRECTION_LEFT, ELM_CTXPOPUP_DIRECTION_LEFT, ELM_CTXPOPUP_DIRECTION_LEFT, ELM_CTXPOPUP_DIRECTION_LEFT);
+        break;
+      case 3:
+        elm_ctxpopup_direction_priority_set(popup_, ELM_CTXPOPUP_DIRECTION_RIGHT, ELM_CTXPOPUP_DIRECTION_RIGHT, ELM_CTXPOPUP_DIRECTION_RIGHT, ELM_CTXPOPUP_DIRECTION_RIGHT);
+        break;
+      default:
+        elm_ctxpopup_direction_priority_set(popup_, ELM_CTXPOPUP_DIRECTION_UP, ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_LEFT, ELM_CTXPOPUP_DIRECTION_RIGHT);
+    };
+    evas_object_move(popup_, popupPosition.x(), popupPosition.y());
+#ifdef OS_TIZEN_MOBILE
+    elm_ctxpopup_auto_hide_disabled_set(popup_, EINA_TRUE);
 #endif
+  } else {
+    evas_object_move(popup_, params_.x, params_.y);
+  }
   evas_object_smart_callback_add(popup_, "dismissed", contextMenuCancelCallback, 0);
   evas_object_show(popup_);
   return true;
index 0c0ea4b..253c546 100644 (file)
@@ -1624,3 +1624,11 @@ bool EWebView::SetColorPickerColor(int r, int g, int b, int a) {
   web_contents_delegate()->web_contents()->DidChooseColorInColorChooser(SkColorSetARGB(a, r, g, b));
   return true;
 }
+
+bool EWebView::IsIMEShow() {
+  return rwhv()->im_context()->IsShow();
+}
+
+gfx::Rect EWebView::GetIMERect() {
+  return rwhv()->im_context()->GetIMERect();
+}
index 4545311..7eab524 100644 (file)
@@ -257,6 +257,9 @@ class EWebView
   scoped_refptr<EdgeEffect> edgeEffect() { return edge_effect_; }
 #endif
 
+  bool IsIMEShow();
+  gfx::Rect GetIMERect();
+
  private:
   EWebView(EWebContext*, Evas_Object* smart_object);
   ~EWebView();
index 5f0ec80..ce0acca 100644 (file)
 #include "selection_box_efl.h"
 #include "selection_magnifier_efl.h"
 
+#include <Edje.h>
+
 namespace content {
 
+const int menuHeight = 140;// The Height fo the context menu.
+const int menuPadding = 60;// This is padding for deciding when to modify context menu position.
+//const int spacePadding = 24;// This is for making context menu closer to the handles.
+const int spacePadding = 0;// This is for making context menu closer to the handles.
+const int textSelectionScrollSize = 50;// Scroll step when selection handler is moving out of viewport.
+static int s_magnifierMaxYOffsetDelta = 50;// Magnifier should not show the image below the viewport
+static int s_textSelectionMargin = 5;
+
 static bool IsRectEmpty(const gfx::Rect& rect) {
   if (rect.x() == 0 && rect.y() == 0 && rect.height() == 0 && rect.width() == 0) {
     LOG(INFO) << "SelectionControllerEfl:IsRectEmpty : true";
@@ -300,4 +310,134 @@ gfx::Rect SelectionControllerEfl::GetRightRect() {
   return selection_data_->GetRightRect();
 }
 
+void SelectionControllerEfl::ChangeContextMenuPosition(gfx::Point& position, int& drawDirection) {
+  drawDirection = DirectionNone; // Giving default Direction.
+  int handleHeight, webViewX, webViewY, webViewWidth, webViewHeight;
+  gfx::Rect imeRect;
+  edje_object_part_geometry_get(input_handle_->evas_object(), "handle", 0, 0, 0, &handleHeight);
+  evas_object_geometry_get(GetParentView()->evas_object(), &webViewX, &webViewY, &webViewWidth, &webViewHeight);
+  gfx::Rect viewportRect = gfx::Rect(webViewX, webViewY, webViewWidth, webViewHeight);
+
+  if (GetParentView()->IsIMEShow()) { // Get the Visible Rect .
+    imeRect = GetParentView()->GetIMERect();
+    if ((viewportRect.y() + viewportRect.height()) > imeRect.y())
+        viewportRect.set_height(imeRect.y() - viewportRect.y());
+  }
+
+  gfx::Rect leftHandleRect = selection_data_->GetLeftRect();
+  leftHandleRect.set_x(webViewX + leftHandleRect.x());
+  leftHandleRect.set_y(webViewY + leftHandleRect.y());
+
+  if (!GetCaretSelectionStatus()) {
+    gfx::Rect rightHandleRect = selection_data_->GetRightRect();
+    rightHandleRect.set_x(webViewX + rightHandleRect.x());
+    rightHandleRect.set_y(webViewY + rightHandleRect.y());
+    gfx::Rect oldLeftHandleRect = leftHandleRect;
+    gfx::Rect oldRightHandleRect = rightHandleRect;
+    gfx::Rect effectiveRect;
+    bool isEffecrtiveRectAvailable = false;
+
+    bool isTop = false;
+    bool isRightHandle = false;
+    bool doConsiderRightHandle = false;
+    bool doNotConsiderMenuUpward = false;
+
+    //if (leftHandleRect.x() < 0)
+    //    leftHandleRect.set_x(0);
+    //if (leftHandleRect.y() < 0)
+    //    leftHandleRect.set_y(0);
+
+    // Giving first preference to left handle.
+    if (leftHandleRect.IsEmpty() && viewportRect.Contains(leftHandleRect.x(), leftHandleRect.y())) {
+      isTop = start_handle_->IsTop();
+      effectiveRect = leftHandleRect;
+      isEffecrtiveRectAvailable = true;
+      // Check whether Menu will overlap the right handle or not.
+      if (leftHandleRect != rightHandleRect) {
+        if (!isTop) {
+          // If there is sufficient space above the handler that Menu can be placed. then shift context menu
+          // then no need to change effectiveRect from leftHandleRect to rightHandleRect.
+          bool directionUp = effectiveRect.y() - menuHeight > viewportRect.y() && (effectiveRect.y() - (viewportRect.y() + menuHeight) > menuPadding);
+          if (!directionUp && ((leftHandleRect.y() + leftHandleRect.height()) + menuHeight) > rightHandleRect.y()) {
+            doConsiderRightHandle = true;
+            doNotConsiderMenuUpward = true; // As if we draw the direction is UP it will overlap with left Handle.
+          }
+        }
+      }
+    } else {
+      doConsiderRightHandle = true;
+    }
+
+    if (doConsiderRightHandle && rightHandleRect.IsEmpty() && viewportRect.Contains(rightHandleRect.x(), rightHandleRect.y())) {
+      effectiveRect = rightHandleRect;
+      isEffecrtiveRectAvailable = true;
+      isTop = end_handle_->IsTop();
+      isRightHandle = true;
+    }
+
+    if (isEffecrtiveRectAvailable) {
+      //We will go for UpWard, DownWard, Left, Right directions.
+      if (effectiveRect.y() - menuHeight > viewportRect.y() && (effectiveRect.y() - (viewportRect.y() + menuHeight) > menuPadding) && !doNotConsiderMenuUpward) {
+        if (isTop) {
+          if (isRightHandle) {
+            position.set_y(effectiveRect.y() - spacePadding - handleHeight);
+          } else {
+            position.set_y(effectiveRect.y() - spacePadding - handleHeight);
+            //position.set_y(position.y() - effectiveRect.height());
+          }
+        } else {
+          position.set_y(effectiveRect.y() - spacePadding);
+        }
+        drawDirection = DirectionUp;
+      } else if ((effectiveRect.y() + effectiveRect.height()) + menuHeight < (viewportRect.y() + viewportRect.height())) {
+        position.set_y((effectiveRect.y() + effectiveRect.height()) - spacePadding + handleHeight);
+        drawDirection = DirectionDown;
+      } else if (effectiveRect.x() < (viewportRect.x() + viewportRect.width()/2)) {
+        position.set_x((effectiveRect.x() + effectiveRect.width()) + spacePadding + handleHeight);
+        position.set_y(effectiveRect.CenterPoint().y());
+        drawDirection = DirectionLeft;
+      } else {
+        position.set_x(effectiveRect.x());
+        position.set_y(effectiveRect.CenterPoint().y());
+        drawDirection = DirectionRight;
+      }
+    } else {
+      if (oldLeftHandleRect.y() < viewportRect.y() && oldRightHandleRect.y() > (viewportRect.y() + viewportRect.height()) && viewportRect.height() <= webViewHeight) {
+        position.set_y(viewportRect.CenterPoint().y() - spacePadding);
+      } else {
+        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.
+      }
+      drawDirection = DirectionNone;
+    }
+
+    return;
+  }
+
+  if (!selection_data_->IsInEditField())
+    return;
+
+  position.set_y(leftHandleRect.y() - spacePadding);
+
+  if (input_handle_->IsTop()) {
+    position.set_y(position.y() - leftHandleRect.height() - handleHeight);
+    return;
+  }
+
+  // The Width of the context menu is menuHeight so comapairing current position with the viewport point plus menuHeight.
+  // If position is less than this value then set new position for the contextmenu.
+  if ((position.y() <= (webViewY + menuHeight)) || ((position.y() - (webViewY + menuHeight)) <= menuPadding)) {
+    if (leftHandleRect.IsEmpty()) {
+      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.
+      drawDirection = DirectionDown;
+     return;
+    }
+
+    position.set_y((leftHandleRect.y() + leftHandleRect.height()) - spacePadding + handleHeight);
+    drawDirection = DirectionDown;
+    return;
+  }
+
+  return;
+}
+
 }
index a7adccb..e810acb 100644 (file)
@@ -41,6 +41,15 @@ namespace content {
 // Hnadlers are shown to extent selection, On handler move touch points are sent to engine
 // by SelectRange to extend the selection
 class SelectionControllerEfl {
+
+  enum ContextMenuDirection {
+    DirectionDown = 0,
+    DirectionUp,
+    DirectionLeft,
+    DirectionRight,
+    DirectionNone,
+  };
+
  public:
   explicit SelectionControllerEfl(EWebView* parent_view);
 
@@ -83,6 +92,8 @@ class SelectionControllerEfl {
   gfx::Rect GetLeftRect();
   gfx::Rect GetRightRect();
 
+  void ChangeContextMenuPosition(gfx::Point& position, int& drawDirection);
+
  private:
   void ShowHandleAndContextMenuIfRequired();
   void Clear();
index fa1fa36..44daf4f 100644 (file)
@@ -50,7 +50,8 @@ SelectionHandleEfl::SelectionHandleEfl(SelectionControllerEfl* controller, Handl
     diff_point_(0,0),
     controller_(controller),
     is_cursor_handle_(false),
-    is_mouse_downed_(false) {
+    is_mouse_downed_(false),
+    is_top_(false) {
 
   Evas* evas = evas_object_evas_get(parent);
   handle_ = edje_object_add(evas);
@@ -111,11 +112,15 @@ void SelectionHandleEfl::Move(const gfx::Point& point) {
       || (handleType == HANDLE_TYPE_RIGHT && (movePoint.x() >= deviceWidth - reverseMargin))) {
     if ((movePoint.y() + handleHeight) > (y + deviceHeight)) {
       movePoint.set_y(movePoint.y() - selectionRect.height());
-      if (!is_mouse_downed_)
+      if (!is_mouse_downed_) {
         ChangeObjectDirection(handleType, DirectionTopReverse);
+        is_top_ = true;
+      }
     } else {
-      if (!is_mouse_downed_)
+      if (!is_mouse_downed_) {
         ChangeObjectDirection(handleType, DirectionBottomReverse);
+        is_top_ = false;
+      }
     }
   } else {
     if ((movePoint.y() + handleHeight) > (y + deviceHeight)) {
@@ -127,6 +132,7 @@ void SelectionHandleEfl::Move(const gfx::Point& point) {
             ChangeObjectDirection(handleType, DirectionTopNormal);
           else
             ChangeObjectDirection(handleType, DirectionTopNormal);
+          is_top_ = true;
         }
         MoveObject(movePoint);
         return;
@@ -138,6 +144,7 @@ void SelectionHandleEfl::Move(const gfx::Point& point) {
         ChangeObjectDirection(handleType, DirectionBottomNormal);
       else
         ChangeObjectDirection(handleType, DirectionBottomNormal);
+      is_top_ = false;
     }
   }
 
index ae2efd1..d5923a4 100644 (file)
@@ -47,6 +47,14 @@ class SelectionHandleEfl {
     DirectionTopReverse,
   };
 
+  enum ContextMenuDirection {
+    DirectionDown,
+    DirectionUp,
+    DirectionLeft,
+    DirectionRight,
+    DirectionNone,
+  };
+
   SelectionHandleEfl(SelectionControllerEfl* controller, HandleType type, Evas_Object* parent);
   ~SelectionHandleEfl();
   void Show();
@@ -56,6 +64,8 @@ class SelectionHandleEfl {
   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_; }
 
  private:
   static void OnMouseDown(void* data, Evas*, Evas_Object*, void* event_info);
@@ -91,6 +101,9 @@ class SelectionHandleEfl {
 
   // Is mouse down
   bool is_mouse_downed_;
+
+  // Direction of handle
+  bool is_top_;
 };
 
 } // namespace