Bug 1130937 part.2 nsGtkIMModule should set candidiate window position to bottom left of the target clause in vertical writing mode r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 11 Jun 2015 19:50:15 +0900
changeset 279061 1810110176bf29b6bae77cef1815dd9280d168fc
parent 279060 caa60ea601b2dca1b46dfb54ee2766f605d5ced8
child 279062 c6fb4870676c54778577e8ef58bcc0a6731d7b33
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1130937
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1130937 part.2 nsGtkIMModule should set candidiate window position to bottom left of the target clause in vertical writing mode r=m_kato
widget/TextEvents.h
widget/TextRange.h
widget/gtk/nsGtkIMModule.cpp
widget/gtk/nsGtkIMModule.h
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -405,16 +405,25 @@ public:
     return mRanges && mRanges->IsComposing();
   }
 
   uint32_t TargetClauseOffset() const
   {
     return mRanges ? mRanges->TargetClauseOffset() : 0;
   }
 
+  uint32_t TargetClauseLength() const
+  {
+    uint32_t length = UINT32_MAX;
+    if (mRanges) {
+      length = mRanges->TargetClauseLength();
+    }
+    return length == UINT32_MAX ? mData.Length() : length;
+  }
+
   uint32_t RangeCount() const
   {
     return mRanges ? mRanges->Length() : 0;
   }
 
   bool CausesDOMTextEvent() const
   {
     return message == NS_COMPOSITION_CHANGE ||
--- a/widget/TextRange.h
+++ b/widget/TextRange.h
@@ -190,39 +190,53 @@ struct TextRange
  * mozilla::TextRangeArray
  ******************************************************************************/
 class TextRangeArray final : public nsAutoTArray<TextRange, 10>
 {
   ~TextRangeArray() {}
 
   NS_INLINE_DECL_REFCOUNTING(TextRangeArray)
 
+  const TextRange* GetTargetClause() const
+  {
+    for (uint32_t i = 0; i < Length(); ++i) {
+      const TextRange& range = ElementAt(i);
+      if (range.mRangeType == NS_TEXTRANGE_SELECTEDRAWTEXT ||
+          range.mRangeType == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT) {
+        return &range;
+      }
+    }
+    return nullptr;
+  }
+
 public:
   bool IsComposing() const
   {
     for (uint32_t i = 0; i < Length(); ++i) {
       if (ElementAt(i).IsClause()) {
         return true;
       }
     }
     return false;
   }
 
-  // Returns target clase offset.  If there are selected clauses, this returns
+  // Returns target clause offset.  If there are selected clauses, this returns
   // the first selected clause offset.  Otherwise, 0.
   uint32_t TargetClauseOffset() const
   {
-    for (uint32_t i = 0; i < Length(); ++i) {
-      const TextRange& range = ElementAt(i);
-      if (range.mRangeType == NS_TEXTRANGE_SELECTEDRAWTEXT ||
-          range.mRangeType == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT) {
-        return range.mStartOffset;
-      }
-    }
-    return 0;
+    const TextRange* range = GetTargetClause();
+    return range ? range->mStartOffset : 0;
+  }
+
+  // Returns target clause length.  If there are selected clauses, this returns
+  // the first selected clause length.  Otherwise, UINT32_MAX.
+  uint32_t TargetClauseLength() const
+  {
+    const TextRange* range = GetTargetClause();
+    return range ? range->Length() : UINT32_MAX;
   }
 
   bool Equals(const TextRangeArray& aOther) const
   {
     size_t len = Length();
     if (len != aOther.Length()) {
       return false;
     }
--- a/widget/gtk/nsGtkIMModule.cpp
+++ b/widget/gtk/nsGtkIMModule.cpp
@@ -9,16 +9,17 @@
 
 #include "nsGtkIMModule.h"
 #include "nsWindow.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
+#include "WritingModes.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
 PRLogModuleInfo* gGtkIMLog = nullptr;
 
 static const char*
 GetBoolName(bool aBool)
@@ -106,17 +107,16 @@ nsGtkIMModule::nsGtkIMModule(nsWindow* a
     : mOwnerWindow(aOwnerWindow)
     , mLastFocusedWindow(nullptr)
     , mContext(nullptr)
     , mSimpleContext(nullptr)
     , mDummyContext(nullptr)
     , mComposingContext(nullptr)
     , mCompositionStart(UINT32_MAX)
     , mProcessingKeyEvent(nullptr)
-    , mCompositionTargetOffset(UINT32_MAX)
     , mCompositionState(eCompositionState_NotComposing)
     , mIsIMFocused(false)
     , mIsDeletingSurrounding(false)
 {
     if (!gGtkIMLog) {
         gGtkIMLog = PR_NewLogModule("nsGtkIMModuleWidgets");
     }
     static bool sFirstInstance = true;
@@ -530,17 +530,17 @@ nsGtkIMModule::EndIMEComposition(nsWindo
 
 void
 nsGtkIMModule::OnUpdateComposition()
 {
     if (MOZ_UNLIKELY(IsDestroyed())) {
         return;
     }
 
-    SetCursorPosition(GetActiveContext(), mCompositionTargetOffset);
+    SetCursorPosition(GetActiveContext());
 }
 
 void
 nsGtkIMModule::SetInputContext(nsWindow* aCaller,
                                const InputContext* aContext,
                                const InputContextAction* aAction)
 {
     if (MOZ_UNLIKELY(IsDestroyed())) {
@@ -814,17 +814,18 @@ nsGtkIMModule::OnStartCompositionNative(
         return;
     }
 
     mComposingContext = static_cast<GtkIMContext*>(g_object_ref(aContext));
 
     if (!DispatchCompositionStart(aContext)) {
         return;
     }
-    mCompositionTargetOffset = mCompositionStart;
+    mCompositionTargetRange.mOffset = mCompositionStart;
+    mCompositionTargetRange.mLength = 0;
 }
 
 /* static */
 void
 nsGtkIMModule::OnEndCompositionCallback(GtkIMContext *aContext,
                                         nsGtkIMModule* aModule)
 {
     aModule->OnEndCompositionNative(aContext);
@@ -1190,17 +1191,19 @@ nsGtkIMModule::DispatchCompositionChange
             ("    NOTE, the focused widget was destroyed/changed by "
              "compositionchange event"));
         return false;
     }
 
     // We cannot call SetCursorPosition for e10s-aware.
     // DispatchEvent is async on e10s, so composition rect isn't updated now
     // on tab parent.
-    mCompositionTargetOffset = targetOffset;
+    mCompositionTargetRange.mOffset = targetOffset;
+    mCompositionTargetRange.mLength =
+        compositionChangeEvent.mRanges->TargetClauseLength();
 
     return true;
 }
 
 bool
 nsGtkIMModule::DispatchCompositionCommitEvent(
                    GtkIMContext* aContext,
                    const nsAString* aCommitString)
@@ -1233,17 +1236,17 @@ nsGtkIMModule::DispatchCompositionCommit
     }
 
     nsRefPtr<nsWindow> lastFocusedWindow(mLastFocusedWindow);
 
     uint32_t message = aCommitString ? NS_COMPOSITION_COMMIT :
                                        NS_COMPOSITION_COMMIT_AS_IS;
     mCompositionState = eCompositionState_NotComposing;
     mCompositionStart = UINT32_MAX;
-    mCompositionTargetOffset = UINT32_MAX;
+    mCompositionTargetRange.Clear();
     mDispatchedCompositionString.Truncate();
 
     WidgetCompositionEvent compositionCommitEvent(true, message,
                                                   mLastFocusedWindow);
     InitEvent(compositionCommitEvent);
     if (message == NS_COMPOSITION_COMMIT) {
         compositionCommitEvent.mData = *aCommitString;
     }
@@ -1388,26 +1391,29 @@ nsGtkIMModule::CreateTextRangeArray(GtkI
     pango_attr_iterator_destroy(iter);
     pango_attr_list_unref(feedback_list);
     g_free(preedit_string);
 
     return textRangeArray.forget();
 }
 
 void
-nsGtkIMModule::SetCursorPosition(GtkIMContext* aContext,
-                                 uint32_t aTargetOffset)
+nsGtkIMModule::SetCursorPosition(GtkIMContext* aContext)
 {
     MOZ_LOG(gGtkIMLog, LogLevel::Info,
-        ("GtkIMModule(%p): SetCursorPosition, aContext=%p, aTargetOffset=%u",
-         this, aContext, aTargetOffset));
+        ("GtkIMModule(%p): SetCursorPosition, aContext=%p, "
+         "mCompositionTargetRange={ mOffset=%u, mLength=%u }"
+         "mSelection.mWritingMode=%s",
+         this, aContext, mCompositionTargetRange.mOffset,
+         mCompositionTargetRange.mLength,
+         GetWritingModeName(mSelection.mWritingMode).get()));
 
-    if (aTargetOffset == UINT32_MAX) {
+    if (!mCompositionTargetRange.IsValid()) {
         MOZ_LOG(gGtkIMLog, LogLevel::Info,
-            ("    FAILED, aTargetOffset is wrong offset"));
+            ("    FAILED, mCompositionTargetRange is invalid"));
         return;
     }
 
     if (!mLastFocusedWindow) {
         MOZ_LOG(gGtkIMLog, LogLevel::Info,
             ("    FAILED, there are no focused window"));
         return;
     }
@@ -1415,25 +1421,34 @@ nsGtkIMModule::SetCursorPosition(GtkIMCo
     if (MOZ_UNLIKELY(!aContext)) {
         MOZ_LOG(gGtkIMLog, LogLevel::Info,
             ("    FAILED, there are no context"));
         return;
     }
 
     WidgetQueryContentEvent charRect(true, NS_QUERY_TEXT_RECT,
                                      mLastFocusedWindow);
-    charRect.InitForQueryTextRect(aTargetOffset, 1);
+    if (mSelection.mWritingMode.IsVertical()) {
+        // For preventing the candidate window to overlap the target clause,
+        // we should set fake (typically, very tall) caret rect.
+        uint32_t length = mCompositionTargetRange.mLength ?
+            mCompositionTargetRange.mLength : 1;
+        charRect.InitForQueryTextRect(mCompositionTargetRange.mOffset, length);
+    } else {
+        charRect.InitForQueryTextRect(mCompositionTargetRange.mOffset, 1);
+    }
     InitEvent(charRect);
     nsEventStatus status;
     mLastFocusedWindow->DispatchEvent(&charRect, status);
     if (!charRect.mSucceeded) {
         MOZ_LOG(gGtkIMLog, LogLevel::Info,
             ("    FAILED, NS_QUERY_TEXT_RECT was failed"));
         return;
     }
+
     nsWindow* rootWindow =
         static_cast<nsWindow*>(mLastFocusedWindow->GetTopLevelWidget());
 
     // Get the position of the rootWindow in screen.
     gint rootX, rootY;
     gdk_window_get_origin(rootWindow->GetGdkWindow(), &rootX, &rootY);
 
     // Get the position of IM context owner window in screen.
--- a/widget/gtk/nsGtkIMModule.h
+++ b/widget/gtk/nsGtkIMModule.h
@@ -120,18 +120,37 @@ protected:
     // mSelectedString is the selected string which was removed by first
     // compositionchange event.
     nsString mSelectedString;
 
     // OnKeyEvent() temporarily sets mProcessingKeyEvent to the given native
     // event.
     GdkEventKey* mProcessingKeyEvent;
 
-    // current target offset of IME composition
-    uint32_t mCompositionTargetOffset;
+    struct Range
+    {
+        uint32_t mOffset;
+        uint32_t mLength;
+
+        Range()
+            : mOffset(UINT32_MAX)
+            , mLength(UINT32_MAX)
+        {
+        }
+
+        bool IsValid() const { return mOffset != UINT32_MAX; }
+        void Clear()
+        {
+            mOffset = UINT32_MAX;
+            mLength = UINT32_MAX;
+        }
+    };
+
+    // current target offset and length of IME composition
+    Range mCompositionTargetRange;
 
     // mCompositionState indicates current status of composition.
     enum eCompositionState {
         eCompositionState_NotComposing,
         eCompositionState_CompositionStartDispatched,
         eCompositionState_CompositionChangeEventDispatched
     };
     eCompositionState mCompositionState;
@@ -322,24 +341,21 @@ protected:
      *                              of current composition.  This should be
      *                              mDispatchedCompositionString.
      */
     already_AddRefed<mozilla::TextRangeArray>
         CreateTextRangeArray(GtkIMContext* aContext,
                              const nsAString& aLastDispatchedData);
 
     /**
-     * Sets the offset's cursor position to IME.
+     * Move the candidate window with "fake" cursor position.
      *
      * @param aContext              A GtkIMContext which is being handled.
-     * @param aTargetOffset         Offset of a character which is anchor of
-     *                              a candidate window.  This is offset in
-     *                              UTF-16 string.
      */
-    void SetCursorPosition(GtkIMContext* aContext, uint32_t aTargetOffset);
+    void SetCursorPosition(GtkIMContext* aContext);
 
     // Queries the current selection offset of the window.
     uint32_t GetSelectionOffset(nsWindow* aWindow);
 
     // Get current paragraph text content and cursor position
     nsresult GetCurrentParagraph(nsAString& aText, uint32_t& aCursorPos);
 
     /**