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 248290 1810110176bf29b6bae77cef1815dd9280d168fc
parent 248289 caa60ea601b2dca1b46dfb54ee2766f605d5ced8
child 248291 c6fb4870676c54778577e8ef58bcc0a6731d7b33
push idunknown
push userunknown
push dateunknown
reviewersm_kato
bugs1130937
milestone41.0a1
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);
 
     /**