mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
0dfc1d00f9
IME for ibus may send composition after filtering `GDK_KEY_PRESS` event asynchronously. In that case, IME or ibus usually synthesize `GDK_KEY_PRESS` again for letting the application know what's being handled. However, according to the bug report, IME may send composition without synthesizing the `GDK_KEY_PRESS` event. Without this patch, `IMContextWrapper` dispatches only `eContentCommandInsertText` event. Then, it'll cause only a set of `beforeinput` and `input` events. Therefore, web apps may fail to do something if they listen only composition and keyboard events only in Gecko. For avoiding Gecko only failure in this case, we should make `IMContentWrapper` handle the composition with `GDK_KEY_PRESS` event in the queue which it has not handled yet. Then, web apps can work with `keydown` events whose `key` is `"Process"`. Differential Revision: https://phabricator.services.mozilla.com/D170031
689 lines
27 KiB
C++
689 lines
27 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
|
*/
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef IMContextWrapper_h_
|
|
#define IMContextWrapper_h_
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "nsString.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsTArray.h"
|
|
#include "nsIWidget.h"
|
|
#include "mozilla/CheckedInt.h"
|
|
#include "mozilla/ContentData.h"
|
|
#include "mozilla/EventForwards.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/TextEventDispatcherListener.h"
|
|
#include "mozilla/WritingModes.h"
|
|
#include "mozilla/GUniquePtr.h"
|
|
#include "mozilla/widget/IMEData.h"
|
|
|
|
class nsWindow;
|
|
|
|
namespace mozilla {
|
|
namespace widget {
|
|
|
|
/**
|
|
* KeyHandlingState is result of IMContextWrapper::OnKeyEvent().
|
|
*/
|
|
enum class KeyHandlingState {
|
|
// The native key event has not been handled by IMContextWrapper.
|
|
eNotHandled,
|
|
// The native key event was handled by IMContextWrapper.
|
|
eHandled,
|
|
// The native key event has not been handled by IMContextWrapper,
|
|
// but eKeyDown or eKeyUp event has been dispatched.
|
|
eNotHandledButEventDispatched,
|
|
// The native key event has not been handled by IMContextWrapper,
|
|
// but eKeyDown or eKeyUp event has been dispatched and consumed.
|
|
eNotHandledButEventConsumed,
|
|
};
|
|
|
|
class IMContextWrapper final : public TextEventDispatcherListener {
|
|
public:
|
|
// TextEventDispatcherListener implementation
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
|
|
const IMENotification& aNotification) override;
|
|
NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override;
|
|
NS_IMETHOD_(void)
|
|
OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;
|
|
NS_IMETHOD_(void)
|
|
WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher,
|
|
WidgetKeyboardEvent& aKeyboardEvent,
|
|
uint32_t aIndexOfKeypress, void* aData) override;
|
|
|
|
public:
|
|
// aOwnerWindow is a pointer of the owner window. When aOwnerWindow is
|
|
// destroyed, the related IME contexts are released (i.e., IME cannot be
|
|
// used with the instance after that).
|
|
explicit IMContextWrapper(nsWindow* aOwnerWindow);
|
|
|
|
// Called when the process is being shut down.
|
|
static void Shutdown();
|
|
|
|
// "Enabled" means the users can use all IMEs.
|
|
// I.e., the focus is in the normal editors.
|
|
bool IsEnabled() const;
|
|
|
|
// OnFocusWindow is a notification that aWindow is going to be focused.
|
|
void OnFocusWindow(nsWindow* aWindow);
|
|
// OnBlurWindow is a notification that aWindow is going to be unfocused.
|
|
void OnBlurWindow(nsWindow* aWindow);
|
|
// OnDestroyWindow is a notification that aWindow is going to be destroyed.
|
|
void OnDestroyWindow(nsWindow* aWindow);
|
|
// OnFocusChangeInGecko is a notification that an editor gets focus.
|
|
void OnFocusChangeInGecko(bool aFocus);
|
|
// OnSelectionChange is a notification that selection (caret) is changed
|
|
// in the focused editor.
|
|
void OnSelectionChange(nsWindow* aCaller,
|
|
const IMENotification& aIMENotification);
|
|
// OnThemeChanged is called when desktop theme is changed.
|
|
static void OnThemeChanged();
|
|
|
|
/**
|
|
* OnKeyEvent() is called when aWindow gets a native key press event or a
|
|
* native key release event. If this returns true, the key event was
|
|
* filtered by IME. Otherwise, this returns false.
|
|
* NOTE: When the native key press event starts composition, this returns
|
|
* true but dispatches an eKeyDown event or eKeyUp event before
|
|
* dispatching composition events or content command event.
|
|
*
|
|
* @param aWindow A window on which user operate the
|
|
* key.
|
|
* @param aEvent A native key press or release
|
|
* event.
|
|
* @param aKeyboardEventWasDispatched true if eKeyDown or eKeyUp event
|
|
* for aEvent has already been
|
|
* dispatched. In this case,
|
|
* this class doesn't dispatch
|
|
* keyboard event anymore.
|
|
*/
|
|
KeyHandlingState OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent,
|
|
bool aKeyboardEventWasDispatched = false);
|
|
|
|
// IME related nsIWidget methods.
|
|
nsresult EndIMEComposition(nsWindow* aCaller);
|
|
void SetInputContext(nsWindow* aCaller, const InputContext* aContext,
|
|
const InputContextAction* aAction);
|
|
InputContext GetInputContext();
|
|
void OnUpdateComposition();
|
|
void OnLayoutChange();
|
|
|
|
TextEventDispatcher* GetTextEventDispatcher();
|
|
|
|
// TODO: Typically, new IM comes every several years. And now, our code
|
|
// becomes really IM behavior dependent. So, perhaps, we need prefs
|
|
// to control related flags for IM developers.
|
|
enum class IMContextID : uint8_t {
|
|
Fcitx, // 4.x or earlier
|
|
Fcitx5,
|
|
IBus,
|
|
IIIMF,
|
|
Scim,
|
|
Uim,
|
|
Wayland,
|
|
Unknown,
|
|
};
|
|
|
|
friend std::ostream& operator<<(std::ostream& aStream,
|
|
const IMContextID& aIMContextID) {
|
|
switch (aIMContextID) {
|
|
case IMContextID::Fcitx:
|
|
return aStream << "Fcitx";
|
|
case IMContextID::Fcitx5:
|
|
return aStream << "Fcitx5";
|
|
case IMContextID::IBus:
|
|
return aStream << "IBus";
|
|
case IMContextID::IIIMF:
|
|
return aStream << "IIIMF";
|
|
case IMContextID::Scim:
|
|
return aStream << "Scim";
|
|
case IMContextID::Uim:
|
|
return aStream << "Uim";
|
|
case IMContextID::Wayland:
|
|
return aStream << "Wayland";
|
|
case IMContextID::Unknown:
|
|
return aStream << "Unknown";
|
|
}
|
|
MOZ_ASSERT_UNREACHABLE("Add new case for the new IM support");
|
|
return aStream << "Unknown";
|
|
}
|
|
|
|
/**
|
|
* GetIMName() returns IM name associated with mContext. If the context is
|
|
* xim, this look for actual engine from XMODIFIERS environment variable.
|
|
*/
|
|
nsDependentCSubstring GetIMName() const;
|
|
|
|
/**
|
|
* GetWaitingSynthesizedKeyPressHardwareKeyCode() returns hardware_keycode
|
|
* value of last handled GDK_KEY_PRESS event which is probable handled by
|
|
* IME asynchronously and we have not received synthesized GDK_KEY_PRESS
|
|
* event yet.
|
|
*/
|
|
static guint16 GetWaitingSynthesizedKeyPressHardwareKeyCode() {
|
|
return sWaitingSynthesizedKeyPressHardwareKeyCode;
|
|
}
|
|
|
|
protected:
|
|
~IMContextWrapper();
|
|
|
|
/**
|
|
* SetInputPurposeAndInputHints() sets input-purpose and input-hints of
|
|
* current IM context to the values computed with mInputContext.
|
|
*/
|
|
void SetInputPurposeAndInputHints();
|
|
|
|
// Owner of an instance of this class. This should be top level window.
|
|
// The owner window must release the contexts when it's destroyed because
|
|
// the IME contexts need the native window. If OnDestroyWindow() is called
|
|
// with the owner window, it'll release IME contexts. Otherwise, it'll
|
|
// just clean up any existing composition if it's related to the destroying
|
|
// child window.
|
|
nsWindow* mOwnerWindow;
|
|
|
|
// A last focused window in this class's context.
|
|
nsWindow* mLastFocusedWindow;
|
|
|
|
// Actual context. This is used for handling the user's input.
|
|
GtkIMContext* mContext;
|
|
|
|
// mSimpleContext is used for the password field and
|
|
// the |ime-mode: disabled;| editors if sUseSimpleContext is true.
|
|
// These editors disable IME. But dead keys should work. Fortunately,
|
|
// the simple IM context of GTK2 support only them.
|
|
GtkIMContext* mSimpleContext;
|
|
|
|
// mDummyContext is a dummy context and will be used in Focus()
|
|
// when the state of mEnabled means disabled. This context's IME state is
|
|
// always "closed", so it closes IME forcedly.
|
|
GtkIMContext* mDummyContext;
|
|
|
|
// mComposingContext is not nullptr while one of mContext, mSimpleContext
|
|
// and mDummyContext has composition.
|
|
// XXX: We don't assume that two or more context have composition same time.
|
|
GtkIMContext* mComposingContext;
|
|
|
|
// IME enabled state and other things defined in InputContext.
|
|
// Use following helper methods if you don't need the detail of the status.
|
|
InputContext mInputContext;
|
|
|
|
// mCompositionStart is the start offset of the composition string in the
|
|
// current content. When <textarea> or <input> have focus, it means offset
|
|
// from the first character of them. When a HTML editor has focus, it
|
|
// means offset from the first character of the root element of the editor.
|
|
uint32_t mCompositionStart;
|
|
|
|
// mDispatchedCompositionString is the latest composition string which
|
|
// was dispatched by compositionupdate event.
|
|
nsString mDispatchedCompositionString;
|
|
|
|
// mSelectedStringRemovedByComposition is the selected string which was
|
|
// removed by first compositionchange event.
|
|
nsString mSelectedStringRemovedByComposition;
|
|
|
|
// OnKeyEvent() temporarily sets mProcessingKeyEvent to the given native
|
|
// event.
|
|
GdkEventKey* mProcessingKeyEvent;
|
|
|
|
/**
|
|
* GdkEventKeyQueue stores *copy* of GdkEventKey instances. However, this
|
|
* must be safe to our usecase since it has |time| and the value should not
|
|
* be same as older event.
|
|
*/
|
|
class GdkEventKeyQueue final {
|
|
public:
|
|
~GdkEventKeyQueue() { Clear(); }
|
|
|
|
void Clear() { mEvents.Clear(); }
|
|
|
|
/**
|
|
* PutEvent() puts new event into the queue.
|
|
*/
|
|
void PutEvent(const GdkEventKey* aEvent) {
|
|
GdkEventKey* newEvent = reinterpret_cast<GdkEventKey*>(
|
|
gdk_event_copy(reinterpret_cast<const GdkEvent*>(aEvent)));
|
|
newEvent->state &= GDK_MODIFIER_MASK;
|
|
mEvents.AppendElement(newEvent);
|
|
}
|
|
|
|
/**
|
|
* RemoveEvent() removes oldest same event and its preceding events
|
|
* from the queue.
|
|
*/
|
|
void RemoveEvent(const GdkEventKey* aEvent) {
|
|
size_t index = IndexOf(aEvent);
|
|
if (NS_WARN_IF(index == GdkEventKeyQueue::NoIndex())) {
|
|
return;
|
|
}
|
|
mEvents.RemoveElementAt(index);
|
|
}
|
|
|
|
/**
|
|
* Return corresponding GDK_KEY_PRESS event for aEvent. aEvent must be a
|
|
* GDK_KEY_RELEASE event.
|
|
*/
|
|
const GdkEventKey* GetCorrespondingKeyPressEvent(
|
|
const GdkEventKey* aEvent) const {
|
|
MOZ_ASSERT(aEvent->type == GDK_KEY_RELEASE);
|
|
for (const GUniquePtr<GdkEventKey>& pendingKeyEvent : mEvents) {
|
|
if (pendingKeyEvent->type == GDK_KEY_PRESS &&
|
|
aEvent->hardware_keycode == pendingKeyEvent->hardware_keycode) {
|
|
return pendingKeyEvent.get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* FirstEvent() returns oldest event in the queue.
|
|
*/
|
|
GdkEventKey* GetFirstEvent() const {
|
|
if (mEvents.IsEmpty()) {
|
|
return nullptr;
|
|
}
|
|
return mEvents[0].get();
|
|
}
|
|
|
|
bool IsEmpty() const { return mEvents.IsEmpty(); }
|
|
|
|
static size_t NoIndex() { return nsTArray<GdkEventKey*>::NoIndex; }
|
|
size_t Length() const { return mEvents.Length(); }
|
|
size_t IndexOf(const GdkEventKey* aEvent) const {
|
|
static_assert(!(GDK_MODIFIER_MASK & (1 << 24)),
|
|
"We assumes 25th bit is used by some IM, but used by GDK");
|
|
static_assert(!(GDK_MODIFIER_MASK & (1 << 25)),
|
|
"We assumes 26th bit is used by some IM, but used by GDK");
|
|
for (size_t i = 0; i < mEvents.Length(); i++) {
|
|
GdkEventKey* event = mEvents[i].get();
|
|
// It must be enough to compare only type, time, keyval and
|
|
// part of state. Note that we cannot compaire two events
|
|
// simply since IME may have changed unused bits of state.
|
|
if (event->time == aEvent->time) {
|
|
if (NS_WARN_IF(event->type != aEvent->type) ||
|
|
NS_WARN_IF(event->keyval != aEvent->keyval) ||
|
|
NS_WARN_IF(event->state != (aEvent->state & GDK_MODIFIER_MASK))) {
|
|
continue;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
return GdkEventKeyQueue::NoIndex();
|
|
}
|
|
|
|
private:
|
|
nsTArray<GUniquePtr<GdkEventKey>> mEvents;
|
|
};
|
|
// OnKeyEvent() append mPostingKeyEvents when it believes that a key event
|
|
// is posted to other IME process.
|
|
GdkEventKeyQueue mPostingKeyEvents;
|
|
|
|
static guint16 sWaitingSynthesizedKeyPressHardwareKeyCode;
|
|
|
|
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 : uint8_t {
|
|
eCompositionState_NotComposing,
|
|
eCompositionState_CompositionStartDispatched,
|
|
eCompositionState_CompositionChangeEventDispatched
|
|
};
|
|
eCompositionState mCompositionState;
|
|
|
|
bool IsComposing() const {
|
|
return (mCompositionState != eCompositionState_NotComposing);
|
|
}
|
|
|
|
bool IsComposingOn(GtkIMContext* aContext) const {
|
|
return IsComposing() && mComposingContext == aContext;
|
|
}
|
|
|
|
bool IsComposingOnCurrentContext() const {
|
|
return IsComposingOn(GetCurrentContext());
|
|
}
|
|
|
|
bool EditorHasCompositionString() {
|
|
return (mCompositionState ==
|
|
eCompositionState_CompositionChangeEventDispatched);
|
|
}
|
|
|
|
/**
|
|
* Checks if aContext is valid context for handling composition.
|
|
*
|
|
* @param aContext An IM context which is specified by native
|
|
* composition events.
|
|
* @return true if the context is valid context for
|
|
* handling composition. Otherwise, false.
|
|
*/
|
|
bool IsValidContext(GtkIMContext* aContext) const;
|
|
|
|
const char* GetCompositionStateName() {
|
|
switch (mCompositionState) {
|
|
case eCompositionState_NotComposing:
|
|
return "NotComposing";
|
|
case eCompositionState_CompositionStartDispatched:
|
|
return "CompositionStartDispatched";
|
|
case eCompositionState_CompositionChangeEventDispatched:
|
|
return "CompositionChangeEventDispatched";
|
|
default:
|
|
return "InvaildState";
|
|
}
|
|
}
|
|
|
|
// mIMContextID indicates the ID of mContext. This is actually indicates
|
|
// IM which user selected.
|
|
IMContextID mIMContextID;
|
|
|
|
// If mContentSelection is Nothing, it means that
|
|
// EnsureToCacheContentSelection failed to get selection or just not caching
|
|
// the selection.
|
|
Maybe<ContentSelection> mContentSelection;
|
|
|
|
/**
|
|
* Return true if mContentSelection is set to some. Otherwise, false.
|
|
*/
|
|
bool EnsureToCacheContentSelection(nsAString* aSelectedString = nullptr);
|
|
|
|
enum class IMEFocusState : uint8_t {
|
|
// IME has focus
|
|
Focused,
|
|
// IME was blurred
|
|
Blurred,
|
|
// IME was blurred without a focus change
|
|
BlurredWithoutFocusChange,
|
|
};
|
|
friend std::ostream& operator<<(std::ostream& aStream, IMEFocusState aState) {
|
|
switch (aState) {
|
|
case IMEFocusState::Focused:
|
|
return aStream << "IMEFocusState::Focused";
|
|
case IMEFocusState::Blurred:
|
|
return aStream << "IMEFocusState::Blurred";
|
|
case IMEFocusState::BlurredWithoutFocusChange:
|
|
return aStream << "IMEFocusState::BlurredWithoutFocusChange";
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Invalid value");
|
|
return aStream << "<illegal value>";
|
|
}
|
|
}
|
|
IMEFocusState mIMEFocusState = IMEFocusState::Blurred;
|
|
|
|
// mFallbackToKeyEvent is set to false when this class starts to handle
|
|
// a native key event (at that time, mProcessingKeyEvent is set to the
|
|
// native event). If active IME just commits composition with a character
|
|
// which is produced by the key with current keyboard layout, this is set
|
|
// to true.
|
|
bool mFallbackToKeyEvent;
|
|
// mKeyboardEventWasDispatched is used by OnKeyEvent() and
|
|
// MaybeDispatchKeyEventAsProcessedByIME().
|
|
// MaybeDispatchKeyEventAsProcessedByIME() dispatches an eKeyDown or
|
|
// eKeyUp event event if the composition is caused by a native
|
|
// key press event. If this is true, a keyboard event has been dispatched
|
|
// for the native event. If so, MaybeDispatchKeyEventAsProcessedByIME()
|
|
// won't dispatch keyboard event anymore.
|
|
bool mKeyboardEventWasDispatched;
|
|
// Whether the keyboard event which as dispatched at setting
|
|
// mKeyboardEventWasDispatched to true was consumed or not.
|
|
bool mKeyboardEventWasConsumed;
|
|
// mIsDeletingSurrounding is true while OnDeleteSurroundingNative() is
|
|
// trying to delete the surrounding text.
|
|
bool mIsDeletingSurrounding;
|
|
// mLayoutChanged is true after OnLayoutChange() is called. This is reset
|
|
// when eCompositionChange is being dispatched.
|
|
bool mLayoutChanged;
|
|
// mSetCursorPositionOnKeyEvent true when caret rect or position is updated
|
|
// with no composition. If true, we update candidate window position
|
|
// before key down
|
|
bool mSetCursorPositionOnKeyEvent;
|
|
// mPendingResettingIMContext becomes true if selection change notification
|
|
// is received during composition but the selection change occurred before
|
|
// starting the composition. In such case, we cannot notify IME of
|
|
// selection change during composition because we don't want to commit
|
|
// the composition in such case. However, we should notify IME of the
|
|
// selection change after the composition is committed.
|
|
bool mPendingResettingIMContext;
|
|
// mRetrieveSurroundingSignalReceived is true after "retrieve_surrounding"
|
|
// signal is received until selection is changed in Gecko.
|
|
bool mRetrieveSurroundingSignalReceived;
|
|
// mMaybeInDeadKeySequence is set to true when we detect a dead key press
|
|
// and set to false when we're sure dead key sequence has been finished.
|
|
// Note that we cannot detect which key event causes ending a dead key
|
|
// sequence. For example, when you press dead key grave with ibus Spanish
|
|
// keyboard layout, it just consumes the key event when we call
|
|
// gtk_im_context_filter_keypress(). Then, pressing "Escape" key cancels
|
|
// the dead key sequence but we don't receive any signal and it's consumed
|
|
// by gtk_im_context_filter_keypress() normally. On the other hand, when
|
|
// pressing "Shift" key causes exactly same behavior but dead key sequence
|
|
// isn't finished yet.
|
|
bool mMaybeInDeadKeySequence;
|
|
// mIsIMInAsyncKeyHandlingMode is set to true if we know that IM handles
|
|
// key events asynchronously. I.e., filtered key event may come again
|
|
// later.
|
|
bool mIsIMInAsyncKeyHandlingMode;
|
|
// mIsKeySnooped is set to true if IM uses key snooper to listen key events.
|
|
// In such case, we won't receive key events if IME consumes the event.
|
|
bool mIsKeySnooped;
|
|
// mSetInputPurposeAndInputHints is set if `SetInputContext` wants `Focus`
|
|
// to set input-purpose and input-hints.
|
|
bool mSetInputPurposeAndInputHints;
|
|
|
|
// sLastFocusedContext is a pointer to the last focused instance of this
|
|
// class. When a instance is destroyed and sLastFocusedContext refers it,
|
|
// this is cleared. So, this refers valid pointer always.
|
|
static IMContextWrapper* sLastFocusedContext;
|
|
|
|
// sUseSimpleContext indeicates if password editors and editors with
|
|
// |ime-mode: disabled;| should use GtkIMContextSimple.
|
|
// If true, they use GtkIMContextSimple. Otherwise, not.
|
|
static bool sUseSimpleContext;
|
|
|
|
// Callback methods for native IME events. These methods should call
|
|
// the related instance methods simply.
|
|
static gboolean OnRetrieveSurroundingCallback(GtkIMContext* aContext,
|
|
IMContextWrapper* aModule);
|
|
static gboolean OnDeleteSurroundingCallback(GtkIMContext* aContext,
|
|
gint aOffset, gint aNChars,
|
|
IMContextWrapper* aModule);
|
|
static void OnCommitCompositionCallback(GtkIMContext* aContext,
|
|
const gchar* aString,
|
|
IMContextWrapper* aModule);
|
|
static void OnChangeCompositionCallback(GtkIMContext* aContext,
|
|
IMContextWrapper* aModule);
|
|
static void OnStartCompositionCallback(GtkIMContext* aContext,
|
|
IMContextWrapper* aModule);
|
|
static void OnEndCompositionCallback(GtkIMContext* aContext,
|
|
IMContextWrapper* aModule);
|
|
|
|
// The instance methods for the native IME events.
|
|
gboolean OnRetrieveSurroundingNative(GtkIMContext* aContext);
|
|
gboolean OnDeleteSurroundingNative(GtkIMContext* aContext, gint aOffset,
|
|
gint aNChars);
|
|
void OnCommitCompositionNative(GtkIMContext* aContext, const gchar* aString);
|
|
void OnChangeCompositionNative(GtkIMContext* aContext);
|
|
void OnStartCompositionNative(GtkIMContext* aContext);
|
|
void OnEndCompositionNative(GtkIMContext* aContext);
|
|
|
|
/**
|
|
* GetCurrentContext() returns current IM context which is chosen with the
|
|
* enabled state.
|
|
* WARNING:
|
|
* When this class receives some signals for a composition after focus
|
|
* is moved in Gecko, the result of this may be different from given
|
|
* context by the signals.
|
|
*/
|
|
GtkIMContext* GetCurrentContext() const;
|
|
|
|
/**
|
|
* GetActiveContext() returns a composing context or current context.
|
|
*/
|
|
GtkIMContext* GetActiveContext() const {
|
|
return mComposingContext ? mComposingContext : GetCurrentContext();
|
|
}
|
|
|
|
// If the owner window and IM context have been destroyed, returns TRUE.
|
|
bool IsDestroyed() { return !mOwnerWindow; }
|
|
|
|
void NotifyIMEOfFocusChange(IMEFocusState aIMEFocusState);
|
|
|
|
// Initializes the instance.
|
|
void Init();
|
|
|
|
/**
|
|
* Reset the active context, i.e., if there is mComposingContext, reset it.
|
|
* Otherwise, reset current context. Note that all native composition
|
|
* events during calling this will be ignored.
|
|
*/
|
|
void ResetIME();
|
|
|
|
// Gets the current composition string by the native APIs.
|
|
void GetCompositionString(GtkIMContext* aContext,
|
|
nsAString& aCompositionString);
|
|
|
|
/**
|
|
* Generates our text range array from current composition string.
|
|
*
|
|
* @param aContext A GtkIMContext which is being handled.
|
|
* @param aCompositionString The data to be dispatched with
|
|
* compositionchange event.
|
|
*/
|
|
already_AddRefed<TextRangeArray> CreateTextRangeArray(
|
|
GtkIMContext* aContext, const nsAString& aCompositionString);
|
|
|
|
/**
|
|
* SetTextRange() initializes aTextRange with aPangoAttrIter.
|
|
*
|
|
* @param aPangoAttrIter An iter which represents a clause of the
|
|
* composition string.
|
|
* @param aUTF8CompositionString The whole composition string (UTF-8).
|
|
* @param aUTF16CaretOffset The caret offset in the composition
|
|
* string encoded as UTF-16.
|
|
* @param aTextRange The result.
|
|
* @return true if this initializes aTextRange.
|
|
* Otherwise, false.
|
|
*/
|
|
bool SetTextRange(PangoAttrIterator* aPangoAttrIter,
|
|
const gchar* aUTF8CompositionString,
|
|
uint32_t aUTF16CaretOffset, TextRange& aTextRange) const;
|
|
|
|
/**
|
|
* ToNscolor() converts the PangoColor in aPangoAttrColor to nscolor.
|
|
*/
|
|
static nscolor ToNscolor(PangoAttrColor* aPangoAttrColor);
|
|
|
|
/**
|
|
* Move the candidate window with "fake" cursor position.
|
|
*
|
|
* @param aContext A GtkIMContext which is being handled.
|
|
*/
|
|
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);
|
|
|
|
/**
|
|
* Delete text portion
|
|
*
|
|
* @param aContext A GtkIMContext which is being handled.
|
|
* @param aOffset Start offset of the range to delete.
|
|
* @param aNChars Count of characters to delete. It depends
|
|
* on |g_utf8_strlen()| what is one character.
|
|
*/
|
|
nsresult DeleteText(GtkIMContext* aContext, int32_t aOffset,
|
|
uint32_t aNChars);
|
|
|
|
// Called before destroying the context to work around some platform bugs.
|
|
void PrepareToDestroyContext(GtkIMContext* aContext);
|
|
|
|
/**
|
|
* WARNING:
|
|
* Following methods dispatch gecko events. Then, the focused widget
|
|
* can be destroyed, and also it can be stolen focus. If they returns
|
|
* FALSE, callers cannot continue the composition.
|
|
* - MaybeDispatchKeyEventAsProcessedByIME
|
|
* - DispatchCompositionStart
|
|
* - DispatchCompositionChangeEvent
|
|
* - DispatchCompositionCommitEvent
|
|
*/
|
|
|
|
/**
|
|
* Dispatch an eKeyDown or eKeyUp event whose mKeyCode value is
|
|
* NS_VK_PROCESSKEY and mKeyNameIndex is KEY_NAME_INDEX_Process if
|
|
* we're not in a dead key sequence, mProcessingKeyEvent is nullptr
|
|
* but mPostingKeyEvents is not empty or mProcessingKeyEvent is not
|
|
* nullptr and mKeyboardEventWasDispatched is still false. If this
|
|
* dispatches a keyboard event, this sets mKeyboardEventWasDispatched
|
|
* to true.
|
|
*
|
|
* @param aFollowingEvent The following event message.
|
|
* @return If the caller can continue to handle
|
|
* composition, returns true. Otherwise,
|
|
* false. For example, if focus is moved
|
|
* by dispatched keyboard event, returns
|
|
* false.
|
|
*/
|
|
bool MaybeDispatchKeyEventAsProcessedByIME(EventMessage aFollowingEvent);
|
|
|
|
/**
|
|
* Dispatches a composition start event.
|
|
*
|
|
* @param aContext A GtkIMContext which is being handled.
|
|
* @return true if the focused widget is neither
|
|
* destroyed nor changed. Otherwise, false.
|
|
*/
|
|
bool DispatchCompositionStart(GtkIMContext* aContext);
|
|
|
|
/**
|
|
* Dispatches a compositionchange event.
|
|
*
|
|
* @param aContext A GtkIMContext which is being handled.
|
|
* @param aCompositionString New composition string.
|
|
* @return true if the focused widget is neither
|
|
* destroyed nor changed. Otherwise, false.
|
|
*/
|
|
bool DispatchCompositionChangeEvent(GtkIMContext* aContext,
|
|
const nsAString& aCompositionString);
|
|
|
|
/**
|
|
* Dispatches a compositioncommit event or compositioncommitasis event.
|
|
*
|
|
* @param aContext A GtkIMContext which is being handled.
|
|
* @param aCommitString If this is nullptr, the composition will
|
|
* be committed with last dispatched data.
|
|
* Otherwise, the composition will be
|
|
* committed with this value.
|
|
* @return true if the focused widget is neither
|
|
* destroyed nor changed. Otherwise, false.
|
|
*/
|
|
bool DispatchCompositionCommitEvent(GtkIMContext* aContext,
|
|
const nsAString* aCommitString = nullptr);
|
|
};
|
|
|
|
} // namespace widget
|
|
} // namespace mozilla
|
|
|
|
#endif // #ifndef IMContextWrapper_h_
|