Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / widget / TextEventDispatcher.h
blobe2f9a7fe639f38b9fd1855a82cbc66c215313353
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_textcompositionsynthesizer_h_
7 #define mozilla_textcompositionsynthesizer_h_
9 #include "nsAutoPtr.h"
10 #include "nsString.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/TextEventDispatcherListener.h"
14 #include "mozilla/TextRange.h"
16 class nsIWidget;
18 namespace mozilla {
19 namespace widget {
21 struct IMENotification;
23 /**
24 * TextEventDispatcher is a helper class for dispatching widget events defined
25 * in TextEvents.h. Currently, this is a helper for dispatching
26 * WidgetCompositionEvent and WidgetKeyboardEvent. This manages the behavior
27 * of them for conforming to DOM Level 3 Events.
28 * An instance of this class is created by nsIWidget instance and owned by it.
29 * This is typically created only by the top level widgets because only they
30 * handle IME.
33 class TextEventDispatcher final
35 ~TextEventDispatcher()
39 NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher)
41 public:
42 explicit TextEventDispatcher(nsIWidget* aWidget);
44 /**
45 * Initializes the instance for IME or automated test. Either IME or tests
46 * need to call one of them before starting composition. If they return
47 * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
48 * notifications from TextEventDispatcher for same purpose (for IME or tests).
49 * If this returns another error, the caller shouldn't keep starting
50 * composition.
52 * @param aListener Specify the listener to listen notifications and
53 * requests. This must not be null.
54 * NOTE: aListener is stored as weak reference in
55 * TextEventDispatcher. See mListener
56 * definition below.
58 nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
59 nsresult BeginInputTransactionForTests(
60 TextEventDispatcherListener* aListener);
62 /**
63 * OnDestroyWidget() is called when mWidget is being destroyed.
65 void OnDestroyWidget();
67 /**
68 * GetState() returns current state of this class.
70 * @return NS_OK: Fine to compose text.
71 * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
72 * BeginInputTransactionForTests()
73 * should be called.
74 * NS_ERROR_NOT_AVAILABLE: The widget isn't available for
75 * composition.
77 nsresult GetState() const;
79 /**
80 * IsComposing() returns true after calling StartComposition() and before
81 * calling CommitComposition().
83 bool IsComposing() const { return mIsComposing; }
85 /**
86 * IsDispatchingEvent() returns true while this instance dispatching an event.
88 bool IsDispatchingEvent() const { return mDispatchingEvent > 0; }
90 /**
91 * StartComposition() starts composition explicitly.
93 nsresult StartComposition(nsEventStatus& aStatus);
95 /**
96 * CommitComposition() commits composition.
98 * @param aCommitString If this is null, commits with the last composition
99 * string. Otherwise, commits the composition with
100 * this value.
102 nsresult CommitComposition(nsEventStatus& aStatus,
103 const nsAString* aCommitString = nullptr);
106 * SetPendingCompositionString() sets new composition string which will be
107 * dispatched with NS_COMPOSITION_CHANGE event by calling Flush().
109 * @param aString New composition string.
111 nsresult SetPendingCompositionString(const nsAString& aString)
113 return mPendingComposition.SetString(aString);
117 * AppendClauseToPendingComposition() appends a clause information to
118 * the pending composition string.
120 * @param aLength Length of the clause.
121 * @param aAttribute One of NS_TEXTRANGE_RAWINPUT,
122 * NS_TEXTRANGE_SELECTEDRAWTEXT,
123 * NS_TEXTRANGE_CONVERTEDTEXT or
124 * NS_TEXTRANGE_SELECTEDCONVERTEDTEXT.
126 nsresult AppendClauseToPendingComposition(uint32_t aLength,
127 uint32_t aAttribute)
129 return mPendingComposition.AppendClause(aLength, aAttribute);
133 * SetCaretInPendingComposition() sets caret position in the pending
134 * composition string and its length. This is optional. If IME doesn't
135 * want to show caret, it shouldn't need to call this.
137 * @param aOffset Offset of the caret in the pending composition
138 * string. This should not be larger than the length
139 * of the pending composition string.
140 * @param aLength Caret width. If this is 0, caret will be collapsed.
141 * Note that Goanna doesn't supported wide caret yet,
142 * therefore, this is ignored for now.
144 nsresult SetCaretInPendingComposition(uint32_t aOffset,
145 uint32_t aLength)
147 return mPendingComposition.SetCaret(aOffset, aLength);
151 * FlushPendingComposition() sends the pending composition string
152 * to the widget of the store DOM window. Before calling this, IME needs to
153 * set pending composition string with SetPendingCompositionString(),
154 * AppendClauseToPendingComposition() and/or
155 * SetCaretInPendingComposition().
157 nsresult FlushPendingComposition(nsEventStatus& aStatus)
159 return mPendingComposition.Flush(this, aStatus);
163 * ClearPendingComposition() makes this instance forget pending composition.
165 void ClearPendingComposition()
167 mPendingComposition.Clear();
171 * @see nsIWidget::NotifyIME()
173 nsresult NotifyIME(const IMENotification& aIMENotification);
176 * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
178 * @param aMessage Must be NS_KEY_DOWN or NS_KEY_UP.
179 * Use MaybeDispatchKeypressEvents() for dispatching
180 * NS_KEY_PRESS.
181 * @param aKeyboardEvent A keyboard event.
182 * @param aStatus If dispatching event should be marked as consumed,
183 * set nsEventStatus_eConsumeNoDefault. Otherwise,
184 * set nsEventStatus_eIgnore. After dispatching
185 * a event and it's consumed this returns
186 * nsEventStatus_eConsumeNoDefault.
187 * @return true if an event is dispatched. Otherwise, false.
189 bool DispatchKeyboardEvent(uint32_t aMessage,
190 const WidgetKeyboardEvent& aKeyboardEvent,
191 nsEventStatus& aStatus);
194 * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
195 * generated from aKeydownEvent.
197 * @param aKeyboardEvent A keyboard event.
198 * @param aStatus Sets the result when the caller dispatches
199 * aKeyboardEvent. Note that if the value is
200 * nsEventStatus_eConsumeNoDefault, this does NOT
201 * dispatch keypress events.
202 * When this method dispatches one or more keypress
203 * events and one of them is consumed, this returns
204 * nsEventStatus_eConsumeNoDefault.
205 * @return true if one or more events are dispatched.
206 * Otherwise, false.
208 bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
209 nsEventStatus& aStatus);
211 private:
212 // mWidget is owner of the instance. When this is created, this is set.
213 // And when mWidget is released, this is cleared by OnDestroyWidget().
214 // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
215 // return true).
216 nsIWidget* mWidget;
217 // mListener is a weak reference to TextEventDispatcherListener. That might
218 // be referred by JS. Therefore, the listener might be difficult to release
219 // itself if this is a strong reference. Additionally, it's difficult to
220 // check if a method to uninstall the listener is called by valid instance.
221 // So, using weak reference is the best way in this case.
222 nsWeakPtr mListener;
224 // mPendingComposition stores new composition string temporarily.
225 // These values will be used for dispatching NS_COMPOSITION_CHANGE event
226 // in Flush(). When Flush() is called, the members will be cleared
227 // automatically.
228 class PendingComposition
230 public:
231 PendingComposition();
232 nsresult SetString(const nsAString& aString);
233 nsresult AppendClause(uint32_t aLength, uint32_t aAttribute);
234 nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
235 nsresult Flush(TextEventDispatcher* aDispatcher, nsEventStatus& aStatus);
236 void Clear();
238 private:
239 nsAutoString mString;
240 nsRefPtr<TextRangeArray> mClauses;
241 TextRange mCaret;
243 void EnsureClauseArray();
245 PendingComposition mPendingComposition;
247 // While dispatching an event, this is incremented.
248 uint16_t mDispatchingEvent;
250 bool mForTests;
251 // See IsComposing().
252 bool mIsComposing;
254 // If this is true, keydown and keyup events are dispatched even when there
255 // is a composition.
256 static bool sDispatchKeyEventsDuringComposition;
258 nsresult BeginInputTransactionInternal(
259 TextEventDispatcherListener* aListener,
260 bool aForTests);
263 * InitEvent() initializes aEvent. This must be called before dispatching
264 * the event.
266 void InitEvent(WidgetGUIEvent& aEvent) const;
269 * DispatchEvent() dispatches aEvent on aWidget.
271 nsresult DispatchEvent(nsIWidget* aWidget,
272 WidgetGUIEvent& aEvent,
273 nsEventStatus& aStatus);
276 * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
277 * been started it yet.
279 * @param aStatus If it succeeded to start composition normally, this
280 * returns nsEventStatus_eIgnore. Otherwise, e.g.,
281 * the composition is canceled during dispatching
282 * compositionstart event, this returns
283 * nsEventStatus_eConsumeNoDefault. In this case,
284 * the caller shouldn't keep doing its job.
285 * @return Only when something unexpected occurs, this returns
286 * an error. Otherwise, returns NS_OK even if aStatus
287 * is nsEventStatus_eConsumeNoDefault.
289 nsresult StartCompositionAutomaticallyIfNecessary(nsEventStatus& aStatus);
292 * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
294 * @param aMessage Must be NS_KEY_DOWN, NS_KEY_UP or NS_KEY_PRESS.
295 * @param aKeyboardEvent A keyboard event. If aMessage is NS_KEY_PRESS and
296 * the event is for second or later character, its
297 * mKeyValue should be empty string.
298 * @param aStatus If dispatching event should be marked as consumed,
299 * set nsEventStatus_eConsumeNoDefault. Otherwise,
300 * set nsEventStatus_eIgnore. After dispatching
301 * a event and it's consumed this returns
302 * nsEventStatus_eConsumeNoDefault.
303 * @param aIndexOfKeypress This must be 0 if aMessage isn't NS_KEY_PRESS or
304 * aKeyboard.mKeyNameIndex isn't
305 * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e.,
306 * when an NS_KEY_PRESS event causes inputting
307 * text, this must be between 0 and
308 * mKeyValue.Length() - 1 since keypress events
309 * sending only one character per event.
310 * @return true if an event is dispatched. Otherwise, false.
312 bool DispatchKeyboardEventInternal(uint32_t aMessage,
313 const WidgetKeyboardEvent& aKeyboardEvent,
314 nsEventStatus& aStatus,
315 uint32_t aIndexOfKeypress = 0);
318 } // namespace widget
319 } // namespace mozilla
321 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_