Merge lp:~unity-team/nux/nux.text-entry-im-auto-test into lp:nux/2.0
- nux.text-entry-im-auto-test
- Merge into 2.0
Proposed by
Brandon Schaefer
Status: | Merged |
---|---|
Approved by: | Jay Taoko |
Approved revision: | 589 |
Merged at revision: | 582 |
Proposed branch: | lp:~unity-team/nux/nux.text-entry-im-auto-test |
Merge into: | lp:nux/2.0 |
Diff against target: |
4126 lines (+706/-2677) 14 files modified
Nux/InputMethodIBus.cpp (+53/-64) Nux/InputMethodIBus.h (+15/-16) Nux/Makefile.am (+5/-1) Nux/TextEntry.cpp (+106/-58) Nux/TextEntry.h (+36/-20) Nux/TextEntryIM.cpp (+0/-2126) Nux/TextEntryIM.h (+0/-383) configure.ac (+11/-1) examples/Makefile.am (+3/-1) tests/Makefile.am (+15/-4) tests/Readme.txt (+3/-0) tests/nux_automated_test_framework.cpp (+58/-3) tests/nux_automated_test_framework.h (+10/-0) tests/xtest-text-entry.cpp (+391/-0) |
To merge this branch: | bzr merge lp:~unity-team/nux/nux.text-entry-im-auto-test |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jay Taoko (community) | Approve | ||
Review via email: mp+94304@code.launchpad.net |
Commit message
Description of the change
TextEntry with InputMethod support.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Nux/InputMethodIBus.cpp' |
2 | --- Nux/InputMethodIBus.cpp 2012-02-12 01:55:02 +0000 |
3 | +++ Nux/InputMethodIBus.cpp 2012-02-23 05:24:18 +0000 |
4 | @@ -6,7 +6,7 @@ |
5 | { |
6 | IBusBus* IBusIMEContext::bus_ = NULL; |
7 | |
8 | - IBusIMEContext::IBusIMEContext(TextEntryIM* text_entry) |
9 | + IBusIMEContext::IBusIMEContext(TextEntry* text_entry) |
10 | : text_entry_(text_entry), |
11 | context_(NULL), |
12 | is_focused_(false) |
13 | @@ -68,14 +68,16 @@ |
14 | ibus_input_context_reset(context_); |
15 | } |
16 | |
17 | - // FIXME Need to get the key_code form nux NOT just the keysym |
18 | bool IBusIMEContext::FilterKeyEvent(const KeyEvent& event) |
19 | { |
20 | - guint keyval = event.key_code(); // todo(jaytaoko): ui::GdkKeyCodeForWindowsKeyCode(event.key_code(), event.IsShiftDown() ^ event.IsCapsLockDown()); |
21 | + guint keyval = event.key_sym(); // todo(jaytaoko): ui::GdkKeyCodeForWindowsKeyCode(event.key_code(), event.IsShiftDown() ^ event.IsCapsLockDown()); |
22 | |
23 | if (context_) { |
24 | guint modifiers = 0; |
25 | |
26 | + if (event.flags() & IBUS_IGNORED_MASK) |
27 | + return false; |
28 | + |
29 | if (event.type() == EVENT_KEY_UP) |
30 | modifiers |= IBUS_RELEASE_MASK; |
31 | |
32 | @@ -88,19 +90,14 @@ |
33 | if (event.IsCapsLockDown()) |
34 | modifiers |= IBUS_LOCK_MASK; |
35 | |
36 | - // FIXME Not the best way to get the x11_key_code. Should be able to get it from TextEntry::ProcessKeyEvent |
37 | - // The x11_keycode is needed for the ibus-hangul engine! |
38 | - nux::Event cur_event = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent(); |
39 | - |
40 | ibus_input_context_process_key_event_async(context_, |
41 | - keyval, cur_event.x11_keycode - 8, modifiers, |
42 | + keyval, event.key_code() - 8, modifiers, |
43 | -1, |
44 | NULL, |
45 | reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone), |
46 | new ProcessKeyEventData(this, event)); |
47 | return true; |
48 | } |
49 | - |
50 | return false; |
51 | } |
52 | |
53 | @@ -113,11 +110,14 @@ |
54 | nuxAssert(bus_ != NULL); |
55 | nuxAssert(ibus_bus_is_connected(bus_)); |
56 | |
57 | - context_ = ibus_bus_create_input_context(bus_, "nux"); |
58 | + if (!(context_ = ibus_bus_create_input_context(bus_, "nux"))) |
59 | + { |
60 | + nuxDebugMsg("[IBusIMEContext::IBusIMEContext] Cannot create InputContext"); |
61 | + return; |
62 | + } |
63 | |
64 | // connect input context signals |
65 | g_signal_connect(context_, "commit-text", G_CALLBACK(OnCommitText_), this); |
66 | - g_signal_connect(context_, "forward-key-event", G_CALLBACK(OnForwardKeyEvent_), this); |
67 | g_signal_connect(context_, "update-preedit-text", G_CALLBACK(OnUpdatePreeditText_), this); |
68 | g_signal_connect(context_, "show-preedit-text", G_CALLBACK(OnShowPreeditText_), this); |
69 | g_signal_connect(context_, "hide-preedit-text", G_CALLBACK(OnHidePreeditText_), this); |
70 | @@ -138,7 +138,7 @@ |
71 | |
72 | void IBusIMEContext::DestroyContext() |
73 | { |
74 | - nuxDebugMsg("***IBusIMEContext::DestroyContext***"); |
75 | + //nuxDebugMsg("***IBusIMEContext::DestroyContext***"); |
76 | if (!context_) |
77 | return; |
78 | |
79 | @@ -147,25 +147,27 @@ |
80 | nuxAssert(!context_); |
81 | } |
82 | |
83 | - // FIXME Also need to figure out how to get the pop up window |
84 | - // for a possible ibus-engine to always be on top of the active |
85 | - // window |
86 | void IBusIMEContext::UpdateCursorLocation() |
87 | { |
88 | nux::Rect strong, weak; |
89 | text_entry_->GetCursorRects(&strong, &weak); |
90 | - nux::Geometry geo = text_entry_->GetGeometry(); |
91 | + |
92 | + // Get the position of the TextEntry in the Window. |
93 | + nux::Geometry geo = text_entry_->GetAbsoluteGeometry(); |
94 | + |
95 | + // Get the Geometry of the window on the display. |
96 | + nux::Geometry window_geo = nux::GetGraphicsDisplay()->GetWindowGeometry(); |
97 | |
98 | ibus_input_context_set_cursor_location(context_, |
99 | - strong.x + geo.x, |
100 | - strong.y + geo.y, |
101 | - strong.width, |
102 | - strong.height); |
103 | + geo.x + window_geo.x + strong.x, |
104 | + geo.y + window_geo.y, |
105 | + 0, |
106 | + geo.height); |
107 | } |
108 | |
109 | void IBusIMEContext::OnConnected(IBusBus *bus) |
110 | { |
111 | - nuxDebugMsg("***IBusIMEContext::OnConnected***"); |
112 | + //nuxDebugMsg("***IBusIMEContext::OnConnected***"); |
113 | |
114 | nuxAssert(bus_ == bus); |
115 | nuxAssert(ibus_bus_is_connected(bus_)); |
116 | @@ -177,13 +179,15 @@ |
117 | |
118 | void IBusIMEContext::OnDisconnected(IBusBus *bus) |
119 | { |
120 | - nuxDebugMsg("***IBusIMEContext::OnDisonnected***"); |
121 | + //nuxDebugMsg("***IBusIMEContext::OnDisonnected***"); |
122 | + |
123 | + if (context_) |
124 | + DestroyContext(); |
125 | } |
126 | |
127 | void IBusIMEContext::OnCommitText(IBusInputContext *context, IBusText* text) |
128 | { |
129 | - nuxDebugMsg("***IBusIMEContext::OnCommitText***"); |
130 | - printf ("OnCommit %s\n", text->text); |
131 | + //nuxDebugMsg("***IBusIMEContext::OnCommitText::%s***", text->text); |
132 | nuxAssert(context_ == context); |
133 | |
134 | text_entry_->DeleteSelection(); |
135 | @@ -201,39 +205,15 @@ |
136 | } |
137 | } |
138 | |
139 | - void IBusIMEContext::OnForwardKeyEvent(IBusInputContext *context, guint keyval, guint keycode, guint state) |
140 | - { |
141 | - nuxDebugMsg("***IBusIMEContext::OnForwardKeyEvent***"); |
142 | - nuxAssert(context_ == context); |
143 | - |
144 | - int flags = 0; |
145 | - |
146 | - if (state & IBUS_LOCK_MASK) |
147 | - flags |= KEY_MODIFIER_CAPS_LOCK; |
148 | - if (state & IBUS_CONTROL_MASK) |
149 | - flags |= KEY_MODIFIER_CTRL; |
150 | - if (state & IBUS_SHIFT_MASK) |
151 | - flags |= KEY_MODIFIER_SHIFT; |
152 | - if (state & IBUS_MOD1_MASK) |
153 | - flags |= KEY_MODIFIER_ALT; |
154 | - |
155 | - int mouse_state = 0; |
156 | - if (state & IBUS_BUTTON1_MASK) |
157 | - mouse_state |= MOUSE_BUTTON1; |
158 | - if (state & IBUS_BUTTON2_MASK) |
159 | - mouse_state |= MOUSE_BUTTON2; |
160 | - if (state & IBUS_BUTTON3_MASK) |
161 | - mouse_state |= MOUSE_BUTTON3; |
162 | - |
163 | - //ForwardKeyEvent(KeyEvent(state & IBUS_RELEASE_MASK ? EVENT_KEY_DOWN : EVENT_KEY_UP, keyval /* todo(jaytaoko): ui::WindowsKeyCodeForGdkKeyCode(keyval)*/, mouse_state, flags)); |
164 | - } |
165 | - |
166 | void IBusIMEContext::OnUpdatePreeditText(IBusInputContext* context, IBusText* text, guint cursor_pos, gboolean visible) |
167 | { |
168 | - nuxDebugMsg("***IBusIMEContext::OnUpdatePreeditText***"); |
169 | + //nuxDebugMsg("***IBusIMEContext::OnUpdatePreeditText***"); |
170 | nuxAssert(context_ == context); |
171 | nuxAssert(IBUS_IS_TEXT(text)); |
172 | |
173 | + if (text_entry_->preedit_.empty()) |
174 | + UpdateCursorLocation(); |
175 | + |
176 | if (visible) |
177 | { |
178 | IBusAttrList* attrs = text->attrs; |
179 | @@ -279,50 +259,54 @@ |
180 | text_entry_->preedit_ = preedit; |
181 | text_entry_->preedit_cursor_ = preedit.length(); |
182 | text_entry_->QueueRefresh (true, true); |
183 | - text_entry_->sigTextChanged.emit(text_entry_); |
184 | - UpdateCursorLocation(); |
185 | + text_entry_->text_changed.emit(text_entry_); |
186 | } |
187 | } |
188 | + else |
189 | + { |
190 | + OnHidePreeditText(context_); |
191 | + } |
192 | } |
193 | |
194 | void IBusIMEContext::OnShowPreeditText(IBusInputContext *context) |
195 | { |
196 | - nuxDebugMsg("***IBusIMEContext::OnShowPreeditText***"); |
197 | + //nuxDebugMsg("***IBusIMEContext::OnShowPreeditText***"); |
198 | nuxAssert(context_ == context); |
199 | } |
200 | |
201 | void IBusIMEContext::OnHidePreeditText(IBusInputContext *context) |
202 | { |
203 | - nuxDebugMsg("***IBusIMEContext::OnHidePreeditText***"); |
204 | + //nuxDebugMsg("***IBusIMEContext::OnHidePreeditText***"); |
205 | nuxAssert(context_ == context); |
206 | |
207 | text_entry_->ResetPreedit(); |
208 | text_entry_->QueueRefresh (true, true); |
209 | - text_entry_->sigTextChanged.emit(text_entry_); |
210 | + text_entry_->text_changed.emit(text_entry_); |
211 | } |
212 | |
213 | void IBusIMEContext::OnEnable(IBusInputContext *context) |
214 | { |
215 | - nuxDebugMsg("***IBusIMEContext::OnEnable***"); |
216 | + //nuxDebugMsg("***IBusIMEContext::OnEnable***"); |
217 | nuxAssert(context_ == context); |
218 | |
219 | text_entry_->ime_active_ = true; |
220 | + UpdateCursorLocation(); |
221 | } |
222 | |
223 | void IBusIMEContext::OnDisable(IBusInputContext *context) |
224 | { |
225 | - nuxDebugMsg("***IBusIMEContext::OnDisable***"); |
226 | + //nuxDebugMsg("***IBusIMEContext::OnDisable***"); |
227 | nuxAssert(context_ == context); |
228 | |
229 | text_entry_->ime_active_ = false; |
230 | text_entry_->ResetPreedit(); |
231 | text_entry_->QueueRefresh (true, true); |
232 | - text_entry_->sigTextChanged.emit(text_entry_); |
233 | + text_entry_->text_changed.emit(text_entry_); |
234 | } |
235 | |
236 | void IBusIMEContext::OnDestroy(IBusInputContext *context) |
237 | { |
238 | - nuxDebugMsg("***IBusIMEContext::OnDestroy***"); |
239 | + //nuxDebugMsg("***IBusIMEContext::OnDestroy***"); |
240 | nuxAssert(context_ == context); |
241 | |
242 | g_object_unref(context_); |
243 | @@ -331,7 +315,7 @@ |
244 | |
245 | void IBusIMEContext::ProcessKeyEventDone(IBusInputContext *context, GAsyncResult* res, ProcessKeyEventData *data) |
246 | { |
247 | - nuxDebugMsg("***IBusIMEContext::ProcessKeyEventDone***"); |
248 | + //nuxDebugMsg("***IBusIMEContext::ProcessKeyEventDone***"); |
249 | nuxAssert(data->context->context_ == context); |
250 | |
251 | GError *error = NULL; |
252 | @@ -346,9 +330,14 @@ |
253 | g_error_free (error); |
254 | } |
255 | |
256 | - // FIXME Need to forward this event somewhere.. |
257 | if (processed == FALSE) |
258 | - printf ("Processed %i\n", processed); |
259 | + { |
260 | + data->context->text_entry_->ProcessKeyEvent(data->event.type(), |
261 | + data->event.key_sym(), |
262 | + data->event.flags() | IBUS_IGNORED_MASK, |
263 | + data->event.character().c_str(), |
264 | + 0); |
265 | + } |
266 | |
267 | delete data; |
268 | } |
269 | |
270 | === modified file 'Nux/InputMethodIBus.h' |
271 | --- Nux/InputMethodIBus.h 2012-02-12 00:39:29 +0000 |
272 | +++ Nux/InputMethodIBus.h 2012-02-23 05:24:18 +0000 |
273 | @@ -1,14 +1,14 @@ |
274 | -#ifndef INPUTMETHODIBUS_H |
275 | +#ifndef INPUTMETHODIBUS_H |
276 | #define INPUTMETHODIBUS_H |
277 | |
278 | -#include <Nux/TextEntryIM.h> |
279 | +#include <Nux/TextEntry.h> |
280 | #include <ibus.h> |
281 | |
282 | namespace nux |
283 | { |
284 | |
285 | class IBusIMEContext; |
286 | - class TextEntryIM; |
287 | + class TextEntry; |
288 | |
289 | // FIXME This class should be reworked to replace the mouse_state |
290 | // with the hardware key_code. |
291 | @@ -17,19 +17,21 @@ |
292 | public: |
293 | |
294 | KeyEvent(NuxEventType type, |
295 | - unsigned int key_code, |
296 | - unsigned int mouse_state, unsigned int event_flags) |
297 | + unsigned int key_sym, |
298 | + unsigned int key_code, unsigned int event_flags, const char* character) |
299 | : type_(type) |
300 | + , key_sym_(key_sym) |
301 | , key_code_(key_code) |
302 | , key_modifiers_(event_flags) |
303 | - , mouse_state_(mouse_state) |
304 | + , character_(character) |
305 | { |
306 | } |
307 | |
308 | NuxEventType type() const {return type_;} |
309 | + unsigned int key_sym() const {return key_sym_;} |
310 | unsigned int key_code() const {return key_code_;} |
311 | unsigned int flags() const {return key_modifiers_;} |
312 | - unsigned int MouseState() const {return mouse_state_;} |
313 | + std::string character() const {return character_;} |
314 | |
315 | bool IsShiftDown() const { return (key_modifiers_ & KEY_MODIFIER_SHIFT) != 0; } |
316 | bool IsControlDown() const { return (key_modifiers_ & KEY_MODIFIER_CTRL) != 0; } |
317 | @@ -38,9 +40,10 @@ |
318 | |
319 | private: |
320 | EventType type_; |
321 | + unsigned int key_sym_; |
322 | unsigned int key_code_; |
323 | unsigned int key_modifiers_; |
324 | - unsigned int mouse_state_; |
325 | + std::string character_; |
326 | |
327 | KeyEvent(const KeyEvent&); |
328 | void operator = (const KeyEvent&); |
329 | @@ -53,7 +56,7 @@ |
330 | ProcessKeyEventData(IBusIMEContext* context, |
331 | const KeyEvent& event) |
332 | : context(context) |
333 | - , event(event.type(), event.key_code(), event.MouseState(), event.flags()) |
334 | + , event(event.type(), event.key_sym(), event.key_code(), event.flags(), event.character().c_str()) |
335 | { |
336 | |
337 | } |
338 | @@ -65,7 +68,7 @@ |
339 | class IBusIMEContext |
340 | { |
341 | public: |
342 | - explicit IBusIMEContext(TextEntryIM* text_entry); |
343 | + explicit IBusIMEContext(TextEntry* text_entry); |
344 | virtual ~IBusIMEContext(); |
345 | |
346 | // views::IMEContext implementations: |
347 | @@ -87,7 +90,7 @@ |
348 | void OnConnected(IBusBus *bus); |
349 | |
350 | //CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDisconnected, IBusBus*); |
351 | - static void OnDisconnected_(IBusBus* bus, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnConnected(bus);} |
352 | + static void OnDisconnected_(IBusBus* bus, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnDisconnected(bus);} |
353 | void OnDisconnected(IBusBus *bus); |
354 | |
355 | // // Event handlers for IBusIMEContext: |
356 | @@ -95,10 +98,6 @@ |
357 | static void OnCommitText_(IBusInputContext* context, IBusText* text, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnCommitText(context, text);} |
358 | void OnCommitText(IBusInputContext *context, IBusText* text); |
359 | |
360 | - //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnForwardKeyEvent, IBusInputContext*, guint, guint, guint); |
361 | - static void OnForwardKeyEvent_(IBusInputContext* context, guint keyval, guint keycode, guint state, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnForwardKeyEvent(context, keyval, keycode, state);} |
362 | - void OnForwardKeyEvent(IBusInputContext *context, guint keyval, guint keycode, guint state); |
363 | - |
364 | //CHROMEG_CALLBACK_3(IBusIMEContext, void, OnUpdatePreeditText, IBusInputContext*, IBusText*, guint, gboolean); |
365 | static void OnUpdatePreeditText_(IBusInputContext* context, IBusText* text, guint cursor_pos, gboolean visible, void* data) {reinterpret_cast<IBusIMEContext*>(data)->OnUpdatePreeditText(context, text, cursor_pos, visible);} |
366 | void OnUpdatePreeditText(IBusInputContext *context, IBusText* text, guint cursor_pos, gboolean visible); |
367 | @@ -127,7 +126,7 @@ |
368 | GAsyncResult* res, |
369 | ProcessKeyEventData* data); |
370 | |
371 | - TextEntryIM* text_entry_; |
372 | + TextEntry* text_entry_; |
373 | IBusInputContext* context_; |
374 | bool is_focused_; |
375 | |
376 | |
377 | === modified file 'Nux/Makefile.am' |
378 | --- Nux/Makefile.am 2012-02-13 15:22:30 +0000 |
379 | +++ Nux/Makefile.am 2012-02-23 05:24:18 +0000 |
380 | @@ -15,13 +15,15 @@ |
381 | -DG_LOG_DOMAIN=\"Nux\" \ |
382 | $(GCC_FLAGS) \ |
383 | $(NUX_CFLAGS) \ |
384 | + $(IBUS_CFLAGS) \ |
385 | $(MAINTAINER_CFLAGS) |
386 | |
387 | libnux_@NUX_API_VERSION@_la_LIBADD = \ |
388 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
389 | $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \ |
390 | $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \ |
391 | - $(NUX_LIBS) |
392 | + $(NUX_LIBS) \ |
393 | + $(IBUS_LIBS) |
394 | |
395 | libnux_@NUX_API_VERSION@_la_LDFLAGS = \ |
396 | $(NUX_LT_LDFLAGS) |
397 | @@ -93,6 +95,7 @@ |
398 | $(srcdir)/SystemThread.cpp \ |
399 | $(srcdir)/TabView.cpp \ |
400 | $(srcdir)/TextEntry.cpp \ |
401 | + $(srcdir)/InputMethodIBus.cpp \ |
402 | $(srcdir)/TextLoader.cpp \ |
403 | $(srcdir)/TextureArea.cpp \ |
404 | $(srcdir)/Timeline.cpp \ |
405 | @@ -178,6 +181,7 @@ |
406 | $(srcdir)/SystemThread.h \ |
407 | $(srcdir)/TabView.h \ |
408 | $(srcdir)/TextEntry.h \ |
409 | + $(srcdir)/InputMethodIBus.h \ |
410 | $(srcdir)/TextLoader.h \ |
411 | $(srcdir)/TextureArea.h \ |
412 | $(srcdir)/Timeline.h \ |
413 | |
414 | === modified file 'Nux/TextEntry.cpp' |
415 | --- Nux/TextEntry.cpp 2012-02-18 21:32:06 +0000 |
416 | +++ Nux/TextEntry.cpp 2012-02-23 05:24:18 +0000 |
417 | @@ -76,8 +76,8 @@ |
418 | return result; |
419 | } |
420 | |
421 | -// Calculate pixel size based on the Windows DPI of 96 for compatibility |
422 | -// reasons. |
423 | + // Calculate pixel size based on the Windows DPI of 96 for compatibility |
424 | + // reasons. |
425 | CairoFont::CairoFont(const std::string &family, |
426 | /*PangoFontDescription *font,*/ |
427 | double pt_size, |
428 | @@ -112,10 +112,10 @@ |
429 | TextEntry::TextEntry(const char* text, NUX_FILE_LINE_DECL) |
430 | : View(NUX_FILE_LINE_PARAM) |
431 | , _size_match_text(true) |
432 | - , _texture2D(NULL) |
433 | - , canvas_(NULL) |
434 | - , cached_layout_(NULL) |
435 | - , preedit_attrs_(NULL) |
436 | + , _texture2D(nullptr) |
437 | + , canvas_(nullptr) |
438 | + , cached_layout_(nullptr) |
439 | + , preedit_attrs_(nullptr) |
440 | , completion_color_(color::Gray) |
441 | , last_dblclick_time_(0) |
442 | , cursor_(0) |
443 | @@ -152,7 +152,9 @@ |
444 | , align_(CairoGraphics::ALIGN_LEFT) |
445 | #if defined(NUX_OS_LINUX) |
446 | , caret_cursor_(None) |
447 | -#endif |
448 | + , ime_(new IBusIMEContext(this)) |
449 | +#endif |
450 | + , ime_active_(false) |
451 | , text_input_mode_(false) |
452 | , key_nav_mode_(false) |
453 | { |
454 | @@ -182,8 +184,6 @@ |
455 | |
456 | TextEntry::~TextEntry() |
457 | { |
458 | - ResetLayout(); |
459 | - |
460 | if (cursor_blink_timer_) |
461 | g_source_remove(cursor_blink_timer_); |
462 | |
463 | @@ -191,8 +191,10 @@ |
464 | if (_texture2D) |
465 | _texture2D->UnReference(); |
466 | |
467 | - if (canvas_) |
468 | - delete canvas_; |
469 | +#if defined(NUX_OS_LINUX) |
470 | + if (ime_) |
471 | + delete ime_; |
472 | +#endif |
473 | } |
474 | |
475 | void TextEntry::PreLayoutManagement() |
476 | @@ -242,7 +244,7 @@ |
477 | { |
478 | SelectLine(); |
479 | } |
480 | - else if (event_type == NUX_MOUSE_DOUBLECLICK) |
481 | + else if (event_type == NUX_MOUSE_DOUBLECLICK && !ime_active_) |
482 | { |
483 | SelectWord(); |
484 | last_dblclick_time_ = current_time; |
485 | @@ -281,26 +283,25 @@ |
486 | const char* character , /*character*/ |
487 | unsigned short keyCount /*key repeat count*/) |
488 | { |
489 | + bool retval = FALSE; |
490 | + |
491 | if (event_type == NUX_KEYDOWN) |
492 | text_input_mode_ = true; |
493 | |
494 | -// GdkEventKey *gdk_event = static_cast<GdkEventKey *>(event.GetOriginalEvent()); |
495 | -// ASSERT(gdk_event); |
496 | -// |
497 | -// Event::Type type = event.GetType(); |
498 | - // Cause the cursor to stop blinking for a while. |
499 | cursor_blink_status_ = 4; |
500 | |
501 | -// if (!readonly_ /*&& im_context_*/ && type != Event::EVENT_KEY_PRESS && 0/*&& gtk_im_context_filter_keypress(im_context_, gdk_event)*/) |
502 | -// { |
503 | -// need_im_reset_ = true; |
504 | -// QueueRefresh(false, true); |
505 | -// return EVENT_RESULT_HANDLED; |
506 | -// } |
507 | + // FIXME Have to get the current event fot he x11_keycode for ibus-hangul/korean input |
508 | + nux::Event cur_event = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent(); |
509 | + KeyEvent event((NuxEventType)event_type, keysym,cur_event.x11_keycode, state, character); |
510 | + |
511 | +#if defined(NUX_OS_LINUX) |
512 | + retval = ime_->FilterKeyEvent(event); |
513 | +#endif |
514 | |
515 | if (event_type == NUX_KEYUP) |
516 | return; |
517 | - |
518 | + |
519 | + |
520 | // we need to ignore some characters |
521 | if (keysym == NUX_VK_TAB) |
522 | return; |
523 | @@ -316,8 +317,7 @@ |
524 | bool ctrl = (state & NUX_STATE_CTRL); |
525 | |
526 | // DLOG("TextEntry::key_down(%d, shift:%d ctrl:%d)", keyval, shift, ctrl); |
527 | - |
528 | - if (event_type == NUX_KEYDOWN) |
529 | + if (event_type == NUX_KEYDOWN && !retval) |
530 | { |
531 | if (keyval == NUX_VK_LEFT) |
532 | { |
533 | @@ -428,7 +428,7 @@ |
534 | // } |
535 | } |
536 | |
537 | - if (character != 0 && (strlen(character) != 0)) |
538 | + if (!retval && character != 0 && (strlen(character) != 0)) |
539 | { |
540 | EnterText(character); |
541 | } |
542 | @@ -456,7 +456,7 @@ |
543 | { |
544 | ProcessMouseEvent(NUX_MOUSE_MOVE, x, y, dx, dy, button_flags, key_flags); |
545 | } |
546 | - |
547 | + |
548 | void TextEntry::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) |
549 | { |
550 | #if defined(NUX_OS_LINUX) |
551 | @@ -464,7 +464,7 @@ |
552 | { |
553 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); |
554 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); |
555 | - |
556 | + |
557 | if (display && window) |
558 | { |
559 | caret_cursor_ = XCreateFontCursor(display, XC_xterm); |
560 | @@ -473,7 +473,7 @@ |
561 | } |
562 | #endif |
563 | } |
564 | - |
565 | + |
566 | void TextEntry::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) |
567 | { |
568 | #if defined(NUX_OS_LINUX) |
569 | @@ -481,7 +481,7 @@ |
570 | { |
571 | Display* display = nux::GetGraphicsDisplay()->GetX11Display(); |
572 | nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); |
573 | - |
574 | + |
575 | if (display && window) |
576 | { |
577 | XUndefineCursor(display, window->GetInputWindowId()); |
578 | @@ -559,9 +559,9 @@ |
579 | // intentionally left empty |
580 | } |
581 | |
582 | - void TextEntry::SetText(const char *text) |
583 | + void TextEntry::SetText(const char* text) |
584 | { |
585 | - const char *end = NULL; |
586 | + const char* end = NULL; |
587 | g_utf8_validate(text, -1, &end); |
588 | |
589 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); |
590 | @@ -574,7 +574,7 @@ |
591 | need_im_reset_ = true; |
592 | //ResetImContext(); |
593 | QueueRefresh(true, true); |
594 | - sigTextChanged.emit(this); |
595 | + text_changed.emit(this); |
596 | } |
597 | |
598 | std::string const& TextEntry::GetText() const |
599 | @@ -582,9 +582,28 @@ |
600 | return text_; |
601 | } |
602 | |
603 | - void TextEntry::SetCompletion(const char *text) |
604 | - { |
605 | - const char *end = NULL; |
606 | + std::string TextEntry::GetTextSelection() const |
607 | + { |
608 | + if (text_.size() == 0) |
609 | + { |
610 | + return std::string(""); |
611 | + } |
612 | + |
613 | + int selection_start = 0; |
614 | + int selection_end = 0; |
615 | + if (GetSelectionBounds(&selection_start, &selection_end)) |
616 | + { |
617 | + return text_.substr(selection_start, selection_end); |
618 | + } |
619 | + else |
620 | + { |
621 | + return std::string(""); |
622 | + } |
623 | + } |
624 | + |
625 | + void TextEntry::SetCompletion(const char* text) |
626 | + { |
627 | + const char* end = NULL; |
628 | g_utf8_validate(text, -1, &end); |
629 | std::string txt((text && *text && end > text) ? std::string(text, end) : ""); |
630 | if (txt == completion_) |
631 | @@ -628,7 +647,7 @@ |
632 | void TextEntry::MainDraw() |
633 | { |
634 | |
635 | - CairoGraphics *edit_canvas = EnsureCanvas(); |
636 | + CairoGraphics* edit_canvas = EnsureCanvas(); |
637 | |
638 | if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty()) |
639 | { |
640 | @@ -677,6 +696,9 @@ |
641 | if (!readonly_ /*&& im_context_*/) |
642 | { |
643 | need_im_reset_ = true; |
644 | +#if defined(NUX_OS_LINUX) |
645 | + ime_->Focus(); |
646 | +#endif |
647 | //gtk_im_context_focus_in(im_context_); |
648 | //UpdateIMCursorLocation(); |
649 | } |
650 | @@ -696,6 +718,9 @@ |
651 | if (!readonly_ /*&& im_context_*/) |
652 | { |
653 | need_im_reset_ = true; |
654 | +#if defined(NUX_OS_LINUX) |
655 | + ime_->Blur(); |
656 | +#endif |
657 | //gtk_im_context_focus_out(im_context_); |
658 | } |
659 | cursor_visible_ = false; // hide cursor when losing focus |
660 | @@ -733,7 +758,7 @@ |
661 | int display_width = GetBaseWidth() - kInnerBorderX * 2; |
662 | int display_height = GetBaseHeight() - kInnerBorderY * 2; |
663 | |
664 | - PangoLayout *layout = EnsureLayout(); |
665 | + PangoLayout* layout = EnsureLayout(); |
666 | int text_width, text_height; |
667 | pango_layout_get_pixel_size(layout, &text_width, &text_height); |
668 | |
669 | @@ -1199,7 +1224,7 @@ |
670 | |
671 | int pre_completion_length = tmp_string.length(); |
672 | |
673 | - if (!completion_.empty() && !wrap_) |
674 | + if (!completion_.empty() && !wrap_ && preedit_.empty()) |
675 | { |
676 | tmp_string = text_ + completion_; |
677 | } |
678 | @@ -1225,7 +1250,7 @@ |
679 | } |
680 | if (!completion_.empty() && !wrap_) |
681 | { |
682 | - attr = pango_attr_foreground_new(65535 * completion_color_.red, |
683 | + attr = pango_attr_foreground_new(65535 * completion_color_.red, |
684 | 65535 * completion_color_.green, |
685 | 65535 * completion_color_.blue); |
686 | attr->start_index = static_cast<guint>(pre_completion_length); |
687 | @@ -1455,7 +1480,7 @@ |
688 | } |
689 | |
690 | ResetLayout(); |
691 | - sigTextChanged.emit(this); |
692 | + text_changed.emit(this); |
693 | } |
694 | |
695 | void TextEntry::DeleteText(int start, int end) |
696 | @@ -1486,7 +1511,7 @@ |
697 | selection_bound_ -= (end - start); |
698 | |
699 | ResetLayout(); |
700 | - sigTextChanged.emit(this); |
701 | + text_changed.emit(this); |
702 | } |
703 | |
704 | void TextEntry::SelectWord() |
705 | @@ -1532,6 +1557,11 @@ |
706 | QueueRefresh(true, true); |
707 | } |
708 | |
709 | + bool TextEntry::im_active() |
710 | + { |
711 | + return ime_active_; |
712 | + } |
713 | + |
714 | void TextEntry::DeleteSelection() |
715 | { |
716 | int start, end; |
717 | @@ -1668,8 +1698,8 @@ |
718 | continue; |
719 | if (end_index < line->start_index) |
720 | break; |
721 | - draw_start = Max<int>(start_index, line->start_index); |
722 | - draw_end = Min<int>(end_index, line->start_index + line->length); |
723 | + draw_start = std::max<int>(start_index, line->start_index); |
724 | + draw_end = std::min<int>(end_index, line->start_index + line->length); |
725 | pango_layout_line_get_x_ranges(line, draw_start, draw_end, |
726 | &ranges, &n_ranges); |
727 | pango_layout_line_get_pixel_extents(line, NULL, &line_extents); |
728 | @@ -1739,8 +1769,8 @@ |
729 | nuxAssert(count); |
730 | nuxAssert(preedit_.length() == 0); |
731 | |
732 | - PangoLayout *layout = EnsureLayout(); |
733 | - const char *text = pango_layout_get_text(layout); |
734 | + PangoLayout* layout = EnsureLayout(); |
735 | + const char* text = pango_layout_get_text(layout); |
736 | int index = TextIndexToLayoutIndex(current_index, false); |
737 | int new_index = 0; |
738 | int new_trailing = 0; |
739 | @@ -1780,11 +1810,11 @@ |
740 | |
741 | // The cursor movement direction shall be determined by the direction of |
742 | // current text line. |
743 | - PangoLayout *layout = EnsureLayout(); |
744 | + PangoLayout* layout = EnsureLayout(); |
745 | int n_log_attrs; |
746 | - PangoLogAttr *log_attrs; |
747 | + PangoLogAttr* log_attrs; |
748 | pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs); |
749 | - const char *text = pango_layout_get_text(layout); |
750 | + const char* text = pango_layout_get_text(layout); |
751 | int index = TextIndexToLayoutIndex(current_index, false); |
752 | int line_index; |
753 | pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL); |
754 | @@ -1797,12 +1827,12 @@ |
755 | } |
756 | |
757 | #if PANGO_VERSION_CHECK(1,16,0) |
758 | - PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); |
759 | + PangoLayoutLine* line = pango_layout_get_line_readonly(layout, line_index); |
760 | #else |
761 | - PangoLayoutLine *line = pango_layout_get_line(layout, line_index); |
762 | + PangoLayoutLine* line = pango_layout_get_line(layout, line_index); |
763 | #endif |
764 | bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL); |
765 | - const char *ptr = text + index; |
766 | + const char* ptr = text + index; |
767 | int offset = static_cast<int>(g_utf8_pointer_to_offset(text, ptr)); |
768 | while (count != 0) |
769 | { |
770 | @@ -2055,12 +2085,17 @@ |
771 | return Clamp(index, 0, static_cast<int>(text_.length())); |
772 | } |
773 | |
774 | - bool TextEntry::GetSelectionBounds(int *start, int *end) |
775 | + bool TextEntry::GetSelectionBounds(int* start, int* end) const |
776 | { |
777 | if (start) |
778 | - *start = Min<int>(selection_bound_, cursor_); |
779 | + { |
780 | + *start = std::min<int>(selection_bound_, cursor_); |
781 | + } |
782 | + |
783 | if (end) |
784 | - *end = Max<int>(selection_bound_, cursor_); |
785 | + { |
786 | + *end = std::max<int>(selection_bound_, cursor_); |
787 | + } |
788 | |
789 | return (selection_bound_ != cursor_); |
790 | } |
791 | @@ -2108,7 +2143,7 @@ |
792 | unsigned int key_sym, |
793 | const char* character) |
794 | { |
795 | - if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false)) |
796 | + if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false) && (ime_active_ == false)) |
797 | { |
798 | if (key_sym == NUX_VK_ENTER || |
799 | key_sym == NUX_KP_ENTER || |
800 | @@ -2124,7 +2159,7 @@ |
801 | } |
802 | } |
803 | |
804 | - if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true)) |
805 | + if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true) && (ime_active_ == false)) |
806 | { |
807 | // Enable to exit the TextEntry when in write mode(hack for unity dash) |
808 | if (key_sym == NUX_VK_UP || |
809 | @@ -2142,4 +2177,17 @@ |
810 | |
811 | return true; |
812 | } |
813 | + |
814 | + /// Public API |
815 | + |
816 | + void TextEntry::MoveCursorToLineStart() |
817 | + { |
818 | + MoveCursor(DISPLAY_LINE_ENDS, -1, 0); |
819 | + } |
820 | + |
821 | + void TextEntry::MoveCursorToLineEnd() |
822 | + { |
823 | + MoveCursor(DISPLAY_LINE_ENDS, 1, 0); |
824 | + } |
825 | + |
826 | } |
827 | |
828 | === modified file 'Nux/TextEntry.h' |
829 | --- Nux/TextEntry.h 2012-02-12 02:24:29 +0000 |
830 | +++ Nux/TextEntry.h 2012-02-23 05:24:18 +0000 |
831 | @@ -25,10 +25,14 @@ |
832 | #include "pango/pango.h" |
833 | #include "pango/pangocairo.h" |
834 | #include "NuxImage/CairoGraphics.h" |
835 | +#if defined(NUX_OS_LINUX) |
836 | +#include "InputMethodIBus.h" |
837 | +#endif |
838 | |
839 | namespace nux |
840 | { |
841 | class CairoGraphics; |
842 | + class IBusIMEContext; |
843 | |
844 | class CairoFont |
845 | { |
846 | @@ -81,9 +85,9 @@ |
847 | ~TextEntry(); |
848 | |
849 | Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type); |
850 | - virtual void Draw(GraphicsEngine &graphics_engine, bool force_draw); |
851 | - virtual void DrawContent(GraphicsEngine &graphics_engine, bool force_draw); |
852 | - virtual void PostDraw(GraphicsEngine &graphics_engine, bool force_draw); |
853 | + virtual void Draw(GraphicsEngine& graphics_engine, bool force_draw); |
854 | + virtual void DrawContent(GraphicsEngine& graphics_engine, bool force_draw); |
855 | + virtual void PostDraw(GraphicsEngine& graphics_engine, bool force_draw); |
856 | |
857 | void PreLayoutManagement(); |
858 | long PostLayoutManagement(long layoutResult); |
859 | @@ -125,24 +129,25 @@ |
860 | /*! |
861 | This signal is emitted when the text has changed. |
862 | */ |
863 | - sigc::signal <void, TextEntry*> sigTextChanged; |
864 | + sigc::signal <void, TextEntry*> text_changed; |
865 | sigc::signal <void> activated; |
866 | sigc::signal <void, int> cursor_moved; |
867 | |
868 | - void SetText(const char *text); |
869 | + void SetText(const char* text); |
870 | std::string const& GetText() const; |
871 | + std::string GetTextSelection() const; |
872 | |
873 | - void SetCompletion(const char *text); // Should use std::string, does not for consistancy |
874 | + void SetCompletion(const char* text); // Should use std::string, does not for consistancy |
875 | std::string const& GetCompletion() const; |
876 | |
877 | - void SetCompletionColor(const Color &color); |
878 | + void SetCompletionColor(const Color& color); |
879 | Color const& GetCompletionColor() const; |
880 | |
881 | - void SetTextColor(const Color &color); |
882 | + void SetTextColor(const Color& color); |
883 | Color const& GetTextColor() const; |
884 | - void SetFontFamily(const char *font); |
885 | + void SetFontFamily(const char* font); |
886 | void SetFontSize(double font_size); |
887 | - void SetFontOptions(const cairo_font_options_t *options); |
888 | + void SetFontOptions(const cairo_font_options_t* options); |
889 | |
890 | /** Select text between start and end. */ |
891 | void Select(int start, int end); |
892 | @@ -152,6 +157,11 @@ |
893 | CairoGraphics::Alignment GetAlign() const; |
894 | void SetAlign(CairoGraphics::Alignment align); |
895 | |
896 | + bool im_active(); |
897 | + |
898 | + void MoveCursorToLineStart(); |
899 | + void MoveCursorToLineEnd(); |
900 | + |
901 | protected: |
902 | bool _block_focus; // used to selectively ignore focus keyevents |
903 | |
904 | @@ -195,17 +205,17 @@ |
905 | void ResetPreedit(); |
906 | /** Send out a request to blink the cursor if necessary */ |
907 | void QueueCursorBlink(); |
908 | - static bool CursorBlinkCallback(TextEntry *data); |
909 | + static bool CursorBlinkCallback(TextEntry* data); |
910 | |
911 | void ShowCursor(); |
912 | void HideCursor(); |
913 | |
914 | /** Draw the Cursor to the canvas */ |
915 | - void DrawCursor(CairoGraphics *canvas); |
916 | + void DrawCursor(CairoGraphics* canvas); |
917 | /** Draw the text to the canvas */ |
918 | - void DrawText(CairoGraphics *canvas); |
919 | + void DrawText(CairoGraphics* canvas); |
920 | |
921 | - void GetCursorRects(Rect *strong, Rect *weak); |
922 | + void GetCursorRects(Rect* strong, Rect* weak); |
923 | |
924 | void UpdateCursorRegion(); |
925 | |
926 | @@ -230,7 +240,7 @@ |
927 | * coordinate in the layout */ |
928 | int XYToTextIndex(int x, int y); |
929 | /** Get the offset range that is currently selected,in number of characters.*/ |
930 | - bool GetSelectionBounds(int *start, int *end); |
931 | + bool GetSelectionBounds(int* start, int* end) const; |
932 | /** Set the offest range that should be selected, in number of characters. */ |
933 | void SetSelectionBounds(int selection_bound, int cursor); |
934 | |
935 | @@ -247,7 +257,7 @@ |
936 | int GetPrevCharLength(int index); |
937 | |
938 | /** Insert text at current caret position */ |
939 | - void EnterText(const char *str); |
940 | + void EnterText(const char* str); |
941 | /** Delete text in a specified range, in number of characters. */ |
942 | void DeleteText(int start, int end); |
943 | |
944 | @@ -279,8 +289,8 @@ |
945 | /** |
946 | * Gets the cursor location in pango layout. The unit is pixel. |
947 | */ |
948 | - void GetCursorLocationInLayout(int *strong_x, int *strong_y, int *strong_height, |
949 | - int *weak_x, int *weak_y, int *weak_height); |
950 | + void GetCursorLocationInLayout(int* strong_x, int* strong_y, int* strong_height, |
951 | + int* weak_x, int* weak_y, int* weak_height); |
952 | |
953 | /** The CairoCanvas which hold cairo_t inside */ |
954 | CairoGraphics* canvas_; |
955 | @@ -293,7 +303,7 @@ |
956 | /** The preedit text of the edit control */ |
957 | std::string preedit_; |
958 | /** Attribute list of the preedit text */ |
959 | - PangoAttrList *preedit_attrs_; |
960 | + PangoAttrList* preedit_attrs_; |
961 | /** |
962 | * The character that should be displayed in invisible mode. |
963 | * If this is empty, then the edit control is visible |
964 | @@ -387,7 +397,7 @@ |
965 | /** The font size of the text */ |
966 | double font_size_; |
967 | |
968 | - cairo_font_options_t *font_options_; |
969 | + cairo_font_options_t* font_options_; |
970 | double font_dpi_; |
971 | |
972 | /** The text color of the edit control */ |
973 | @@ -404,6 +414,12 @@ |
974 | std::list<Rect> last_cursor_region_; |
975 | std::list<Rect> cursor_region_; |
976 | |
977 | +#if defined(NUX_OS_LINUX) |
978 | + IBusIMEContext* ime_; |
979 | + friend class IBusIMEContext; |
980 | +#endif |
981 | + bool ime_active_; |
982 | + |
983 | protected: |
984 | bool text_input_mode_; |
985 | bool key_nav_mode_; |
986 | |
987 | === removed file 'Nux/TextEntryIM.cpp' |
988 | --- Nux/TextEntryIM.cpp 2012-02-12 01:55:02 +0000 |
989 | +++ Nux/TextEntryIM.cpp 1970-01-01 00:00:00 +0000 |
990 | @@ -1,2126 +0,0 @@ |
991 | -/* |
992 | - Copyright 2008 Google Inc. |
993 | - |
994 | - Licensed under the Apache License, Version 2.0(the "License"); |
995 | - you may not use this file except in compliance with the License. |
996 | - You may obtain a copy of the License at |
997 | - |
998 | - http://www.apache.org/licenses/LICENSE-2.0 |
999 | - |
1000 | - Unless required by applicable law or agreed to in writing, software |
1001 | - distributed under the License is distributed on an "AS IS" BASIS, |
1002 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1003 | - See the License for the specific language governing permissions and |
1004 | - limitations under the License. |
1005 | -*/ |
1006 | - |
1007 | - |
1008 | -#include "Nux.h" |
1009 | -#include "Layout.h" |
1010 | -#include "HLayout.h" |
1011 | -#include "VLayout.h" |
1012 | -#include "Validator.h" |
1013 | - |
1014 | -#include "cairo/cairo.h" |
1015 | -#include "pango/pango.h" |
1016 | -#include "pango/pangocairo.h" |
1017 | -#include "NuxImage/CairoGraphics.h" |
1018 | - |
1019 | -#include "TextEntryIM.h" |
1020 | -#include "TextEntry.h" |
1021 | - |
1022 | -#if defined(NUX_OS_LINUX) |
1023 | -#include <X11/cursorfont.h> |
1024 | -#endif |
1025 | - |
1026 | -namespace nux |
1027 | -{ |
1028 | - static const int kInnerBorderX = 2; |
1029 | - static const int kInnerBorderY = 0; //1; |
1030 | - static const int kCursorBlinkTimeout = 400; |
1031 | - static const double kStrongCursorLineWidth = 1; |
1032 | - static const double kStrongCursorBarWidth = 1; |
1033 | - static const double kWeakCursorLineWidth = 3; |
1034 | - static const double kWeakCursorBarWidth = 3; |
1035 | - static const Color kStrongCursorColor(0.9f, 0.9f, 0.9f, 1.0f); |
1036 | - static const Color kWeakCursorColor(1.0f, 1.0f, 1.0f, 0.5f); |
1037 | - static const Color kDefaultTextColor(0, 0, 0, 1.0f); |
1038 | - static const Color kDefaultBackgroundColor(1, 1, 1, 1.0f); |
1039 | - static const Color kDefaultSelectionBackgroundColor(0.5, 0.5, 0.5, 1.0f); |
1040 | - static const Color kDefaultSelectionTextColor(1, 1, 1, 1.0f); |
1041 | - static const unsigned long long kTripleClickTimeout = 500; |
1042 | - static const std::string kDefaultFontName = "Ubuntu"; |
1043 | - |
1044 | - static unsigned long long GetCurrentTime() |
1045 | - { |
1046 | - GTimeVal tv; |
1047 | - g_get_current_time(&tv); |
1048 | - return static_cast<unsigned long long>(tv.tv_sec) * 1000 + tv.tv_usec / 1000; |
1049 | - } |
1050 | - |
1051 | - static std::string CleanupLineBreaks(const char *source) |
1052 | - { |
1053 | - nuxAssert(source); |
1054 | - std::string result; |
1055 | - while (*source) { |
1056 | - if (*source == '\r') { |
1057 | - result += ' '; |
1058 | - if (source[1] == '\n') |
1059 | - source++; |
1060 | - } else if (*source == '\n') { |
1061 | - result += ' '; |
1062 | - } else { |
1063 | - result += *source; |
1064 | - } |
1065 | - source++; |
1066 | - } |
1067 | - return result; |
1068 | - } |
1069 | - |
1070 | - NUX_IMPLEMENT_OBJECT_TYPE(TextEntryIM); |
1071 | - |
1072 | - TextEntryIM::TextEntryIM(const char* text, NUX_FILE_LINE_DECL) |
1073 | - : View(NUX_FILE_LINE_PARAM) |
1074 | - , _size_match_text(true) |
1075 | - , _texture2D(nullptr) |
1076 | - , canvas_(nullptr) |
1077 | - , cached_layout_(nullptr) |
1078 | - , preedit_attrs_(nullptr) |
1079 | - , completion_color_(color::Gray) |
1080 | - , last_dblclick_time_(0) |
1081 | - , cursor_(0) |
1082 | - , preedit_cursor_(0) |
1083 | - , selection_bound_(0) |
1084 | - , scroll_offset_x_(0) |
1085 | - , scroll_offset_y_(0) |
1086 | - , cursor_blink_timer_(0) |
1087 | - , cursor_blink_status_(0) |
1088 | - , visible_(true) |
1089 | - , focused_(false) |
1090 | - , need_im_reset_(false) |
1091 | - , overwrite_(false) |
1092 | - , select_words_(false) |
1093 | - , select_lines_(false) |
1094 | - , button_(false) |
1095 | - , bold_(false) |
1096 | - , underline_(false) |
1097 | - , strikeout_(false) |
1098 | - , italic_(false) |
1099 | - , multiline_(false) |
1100 | - , wrap_(false) |
1101 | - , cursor_visible_(false) |
1102 | - , readonly_(false) |
1103 | - , content_modified_(false) |
1104 | - , selection_changed_(false) |
1105 | - , cursor_moved_(false) |
1106 | - , update_canvas_(true) |
1107 | - , font_family_("Ubuntu") |
1108 | - , font_size_(12) |
1109 | - , font_options_(cairo_font_options_create()) |
1110 | - , font_dpi_(96.0) |
1111 | - , _text_color(color::White) |
1112 | - , align_(CairoGraphics::ALIGN_LEFT) |
1113 | -#if defined(NUX_OS_LINUX) |
1114 | - , caret_cursor_(None) |
1115 | -#endif |
1116 | - , ime_(new IBusIMEContext(this)) |
1117 | - , ime_active_(false) |
1118 | - , text_input_mode_(false) |
1119 | - , key_nav_mode_(false) |
1120 | - { |
1121 | - cairo_font_options_set_antialias(font_options_, CAIRO_ANTIALIAS_SUBPIXEL); |
1122 | - cairo_font_options_set_hint_style(font_options_, CAIRO_HINT_STYLE_FULL); |
1123 | - cairo_font_options_set_hint_metrics(font_options_, CAIRO_HINT_METRICS_ON); |
1124 | - cairo_font_options_set_subpixel_order(font_options_, CAIRO_SUBPIXEL_ORDER_RGB); |
1125 | - |
1126 | - mouse_down.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDown)); |
1127 | - mouse_drag.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDrag)); |
1128 | - mouse_up.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseUp)); |
1129 | - mouse_double_click.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseDoubleClick)); |
1130 | - mouse_enter.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseEnter)); |
1131 | - mouse_leave.connect(sigc::mem_fun(this, &TextEntryIM::RecvMouseLeave)); |
1132 | - |
1133 | - key_down.connect(sigc::mem_fun(this, &TextEntryIM::RecvKeyEvent)); |
1134 | - |
1135 | - begin_key_focus.connect(sigc::mem_fun(this, &TextEntryIM::RecvStartKeyFocus)); |
1136 | - end_key_focus.connect(sigc::mem_fun(this, &TextEntryIM::RecvEndKeyFocus)); |
1137 | - |
1138 | - SetMinimumSize(DEFAULT_WIDGET_WIDTH, PRACTICAL_WIDGET_HEIGHT); |
1139 | - SetText(text); |
1140 | - |
1141 | - SetAcceptKeyboardEvent(true); |
1142 | - EnableDoubleClick(true); |
1143 | - } |
1144 | - |
1145 | - TextEntryIM::~TextEntryIM() |
1146 | - { |
1147 | - if (cursor_blink_timer_) |
1148 | - g_source_remove(cursor_blink_timer_); |
1149 | - |
1150 | - cairo_font_options_destroy(font_options_); |
1151 | - if (_texture2D) |
1152 | - _texture2D->UnReference(); |
1153 | - } |
1154 | - |
1155 | - void TextEntryIM::PreLayoutManagement() |
1156 | - { |
1157 | - View::PreLayoutManagement(); |
1158 | - } |
1159 | - |
1160 | - long TextEntryIM::PostLayoutManagement(long layoutResult) |
1161 | - { |
1162 | - long result = View::PostLayoutManagement(layoutResult); |
1163 | - MainDraw(); |
1164 | - return result; |
1165 | - } |
1166 | - |
1167 | - void TextEntryIM::GeometryChanged() |
1168 | - { |
1169 | - |
1170 | - update_canvas_ = true; |
1171 | - View::GeometryChanged(); |
1172 | - |
1173 | - } |
1174 | - |
1175 | - Area* TextEntryIM::FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type) |
1176 | - { |
1177 | - Area* area = View::FindAreaUnderMouse(mouse_position, event_type); |
1178 | - |
1179 | - return area; |
1180 | - } |
1181 | - |
1182 | - void TextEntryIM::ProcessMouseEvent(int event_type, int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) |
1183 | - { |
1184 | - if (GetEventButton(button_flags) != 1 && event_type != NUX_MOUSE_MOVE) |
1185 | - return; |
1186 | - |
1187 | - //ResetImContext(); |
1188 | - //Event::Type type = event.GetType(); |
1189 | - |
1190 | - int X = static_cast<int>(x /*round(event.GetX())*/) - kInnerBorderX - scroll_offset_x_; |
1191 | - int Y = static_cast<int>(y /*round(event.GetY())*/) - kInnerBorderY - scroll_offset_y_; |
1192 | - int index = XYToTextIndex(X, Y); |
1193 | - int sel_start, sel_end; |
1194 | - GetSelectionBounds(&sel_start, &sel_end); |
1195 | - |
1196 | - unsigned long long current_time = GetCurrentTime(); |
1197 | - |
1198 | - if ((event_type == NUX_MOUSE_PRESSED) && (current_time - last_dblclick_time_ <= kTripleClickTimeout)) |
1199 | - { |
1200 | - SelectLine(); |
1201 | - } |
1202 | - else if (event_type == NUX_MOUSE_DOUBLECLICK) |
1203 | - { |
1204 | - SelectWord(); |
1205 | - last_dblclick_time_ = current_time; |
1206 | - } |
1207 | - else if (event_type == NUX_MOUSE_PRESSED) |
1208 | - { |
1209 | - if (key_flags & NUX_STATE_SHIFT) |
1210 | - { |
1211 | - // If current click position is inside the selection range, then just |
1212 | - // cancel the selection. |
1213 | - if (index > sel_start && index < sel_end) |
1214 | - SetCursor(index); |
1215 | - else if (index <= sel_start) |
1216 | - SetSelectionBounds(sel_end, index); |
1217 | - else if (index >= sel_end) |
1218 | - SetSelectionBounds(sel_start, index); |
1219 | - } |
1220 | - else |
1221 | - { |
1222 | - SetCursor(index); |
1223 | - } |
1224 | - } |
1225 | - else if (event_type == NUX_MOUSE_MOVE) |
1226 | - { |
1227 | - SetSelectionBounds(selection_bound_, index); |
1228 | - } |
1229 | - |
1230 | - QueueRefresh(false, true); |
1231 | - //return EVENT_RESULT_HANDLED; |
1232 | - } |
1233 | - |
1234 | - void TextEntryIM::ProcessKeyEvent( |
1235 | - unsigned long event_type , /*event type*/ |
1236 | - unsigned long keysym , /*event keysym*/ |
1237 | - unsigned long state , /*event state*/ |
1238 | - const char* character , /*character*/ |
1239 | - unsigned short keyCount /*key repeat count*/) |
1240 | - { |
1241 | - |
1242 | - KeyEvent event((NuxEventType)event_type, keysym, 0, state); |
1243 | - ime_->FilterKeyEvent(event); |
1244 | - |
1245 | - if (event_type == NUX_KEYDOWN) |
1246 | - text_input_mode_ = true; |
1247 | - |
1248 | -// GdkEventKey *gdk_event = static_cast<GdkEventKey *>(event.GetOriginalEvent()); |
1249 | -// ASSERT(gdk_event); |
1250 | -// |
1251 | -// Event::Type type = event.GetType(); |
1252 | - // Cause the cursor to stop blinking for a while. |
1253 | - cursor_blink_status_ = 4; |
1254 | - |
1255 | -// if (!readonly_ /*&& im_context_*/ && type != Event::EVENT_KEY_PRESS && 0/*&& gtk_im_context_filter_keypress(im_context_, gdk_event)*/) |
1256 | -// { |
1257 | -// need_im_reset_ = true; |
1258 | -// QueueRefresh(false, true); |
1259 | -// return EVENT_RESULT_HANDLED; |
1260 | -// } |
1261 | - |
1262 | - if (event_type == NUX_KEYUP) |
1263 | - return; |
1264 | - |
1265 | - // we need to ignore some characters |
1266 | - if (keysym == NUX_VK_TAB) |
1267 | - return; |
1268 | - |
1269 | - if (keysym == NUX_VK_ENTER || keysym == NUX_KP_ENTER) |
1270 | - { |
1271 | - activated.emit(); |
1272 | - return; |
1273 | - } |
1274 | - |
1275 | - unsigned int keyval = keysym; |
1276 | - bool shift = (state & NUX_STATE_SHIFT); |
1277 | - bool ctrl = (state & NUX_STATE_CTRL); |
1278 | - |
1279 | - // DLOG("TextEntryIM::key_down(%d, shift:%d ctrl:%d)", keyval, shift, ctrl); |
1280 | - |
1281 | - if (event_type == NUX_KEYDOWN) |
1282 | - { |
1283 | - if (keyval == NUX_VK_LEFT) |
1284 | - { |
1285 | - if (!ctrl) |
1286 | - MoveCursor(VISUALLY, -1, shift); |
1287 | - else |
1288 | - MoveCursor(WORDS, -1, shift); |
1289 | - } |
1290 | - else if (keyval == NUX_VK_RIGHT) |
1291 | - { |
1292 | - if (!ctrl) |
1293 | - MoveCursor(VISUALLY, 1, shift); |
1294 | - else |
1295 | - MoveCursor(WORDS, 1, shift); |
1296 | - } |
1297 | - else if (keyval == NUX_VK_UP) |
1298 | - { |
1299 | - MoveCursor(DISPLAY_LINES, -1, shift); |
1300 | - } |
1301 | - else if (keyval == NUX_VK_DOWN) |
1302 | - { |
1303 | - MoveCursor(DISPLAY_LINES, 1, shift); |
1304 | - } |
1305 | - else if (keyval == NUX_VK_HOME) |
1306 | - { |
1307 | - if (!ctrl) |
1308 | - MoveCursor(DISPLAY_LINE_ENDS, -1, shift); |
1309 | - else |
1310 | - MoveCursor(BUFFER, -1, shift); |
1311 | - } |
1312 | - else if (keyval == NUX_VK_END) |
1313 | - { |
1314 | - if (!ctrl) |
1315 | - MoveCursor(DISPLAY_LINE_ENDS, 1, shift); |
1316 | - else |
1317 | - MoveCursor(BUFFER, 1, shift); |
1318 | - } |
1319 | - else if (keyval == NUX_VK_PAGE_UP) |
1320 | - { |
1321 | - if (!ctrl) |
1322 | - MoveCursor(PAGES, -1, shift); |
1323 | - else |
1324 | - MoveCursor(BUFFER, -1, shift); |
1325 | - } |
1326 | - else if (keyval == NUX_VK_PAGE_DOWN) |
1327 | - { |
1328 | - if (!ctrl) |
1329 | - MoveCursor(PAGES, 1, shift); |
1330 | - else |
1331 | - MoveCursor(BUFFER, 1, shift); |
1332 | - } |
1333 | - else if (((keyval == NUX_VK_x) && ctrl && !shift) || ((keyval == NUX_VK_DELETE) && shift && !ctrl)) |
1334 | - { |
1335 | - CutClipboard(); |
1336 | - } |
1337 | - else if (((keyval == NUX_VK_c) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && ctrl && (!shift))) |
1338 | - { |
1339 | - CopyClipboard(); |
1340 | - } |
1341 | - else if (((keyval == NUX_VK_v) && ctrl && (!shift)) || ((keyval == NUX_VK_INSERT) && shift && (!ctrl))) |
1342 | - { |
1343 | - PasteClipboard(); |
1344 | - } |
1345 | - else if ((keyval == NUX_VK_a) && ctrl) |
1346 | - { |
1347 | - // Select all |
1348 | - int text_length = static_cast<int>(text_.length()); |
1349 | - SetSelectionBounds(0, text_length); |
1350 | - QueueRefresh(false, true); |
1351 | - return; |
1352 | - } |
1353 | - else if (keyval == NUX_VK_BACKSPACE) |
1354 | - { |
1355 | - if (!ctrl) |
1356 | - BackSpace(VISUALLY); |
1357 | - else |
1358 | - BackSpace(WORDS); |
1359 | - } |
1360 | - else if ((keyval == NUX_VK_DELETE) && (!shift)) |
1361 | - { |
1362 | - if (!ctrl) |
1363 | - Delete(VISUALLY); |
1364 | - else |
1365 | - Delete(WORDS); |
1366 | - } |
1367 | - else if ((keyval == NUX_VK_INSERT) && (!shift) && (!ctrl)) |
1368 | - { |
1369 | - ToggleOverwrite(); |
1370 | - } |
1371 | -// else |
1372 | -// { |
1373 | -// return EVENT_RESULT_UNHANDLED; |
1374 | -// } |
1375 | - } |
1376 | - else |
1377 | - { // EVENT_KEY_PRESS |
1378 | -// if (keyval == GDK_Return || keyval == GDK_KP_Enter) |
1379 | -// { |
1380 | -// // If multiline_ is unset, just ignore new_line. |
1381 | -// if (multiline_) |
1382 | -// EnterText("\n"); |
1383 | -// else |
1384 | -// return; |
1385 | -// } |
1386 | -// else |
1387 | -// { |
1388 | -// return; |
1389 | -// } |
1390 | - } |
1391 | - |
1392 | - if (character != 0 && (strlen(character) != 0) && !ime_active_) |
1393 | - { |
1394 | - EnterText(character); |
1395 | - } |
1396 | - |
1397 | - QueueRefresh(false, true); |
1398 | - return; |
1399 | - } |
1400 | - |
1401 | - void TextEntryIM::RecvMouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags) |
1402 | - { |
1403 | - ProcessMouseEvent(NUX_MOUSE_DOUBLECLICK, x, y, 0, 0, button_flags, key_flags); |
1404 | - } |
1405 | - |
1406 | - void TextEntryIM::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) |
1407 | - { |
1408 | - ProcessMouseEvent(NUX_MOUSE_RELEASED, x, y, 0, 0, button_flags, key_flags); |
1409 | - } |
1410 | - |
1411 | - void TextEntryIM::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) |
1412 | - { |
1413 | - ProcessMouseEvent(NUX_MOUSE_PRESSED, x, y, 0, 0, button_flags, key_flags); |
1414 | - } |
1415 | - |
1416 | - void TextEntryIM::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) |
1417 | - { |
1418 | - ProcessMouseEvent(NUX_MOUSE_MOVE, x, y, dx, dy, button_flags, key_flags); |
1419 | - } |
1420 | - |
1421 | - void TextEntryIM::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) |
1422 | - { |
1423 | -#if defined(NUX_OS_LINUX) |
1424 | - if (caret_cursor_ == None) |
1425 | - { |
1426 | - Display* display = nux::GetGraphicsDisplay()->GetX11Display(); |
1427 | - nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); |
1428 | - |
1429 | - if (display && window) |
1430 | - { |
1431 | - caret_cursor_ = XCreateFontCursor(display, XC_xterm); |
1432 | - XDefineCursor(display, window->GetInputWindowId(), caret_cursor_); |
1433 | - } |
1434 | - } |
1435 | -#endif |
1436 | - } |
1437 | - |
1438 | - void TextEntryIM::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) |
1439 | - { |
1440 | -#if defined(NUX_OS_LINUX) |
1441 | - if (caret_cursor_ != None) |
1442 | - { |
1443 | - Display* display = nux::GetGraphicsDisplay()->GetX11Display(); |
1444 | - nux::BaseWindow* window = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()); |
1445 | - |
1446 | - if (display && window) |
1447 | - { |
1448 | - XUndefineCursor(display, window->GetInputWindowId()); |
1449 | - XFreeCursor(display, caret_cursor_); |
1450 | - caret_cursor_ = None; |
1451 | - } |
1452 | - } |
1453 | -#endif |
1454 | - } |
1455 | - |
1456 | - void TextEntryIM::RecvKeyEvent( |
1457 | - unsigned long eventType , /*event type*/ |
1458 | - unsigned long keysym , /*event keysym*/ |
1459 | - unsigned long state , /*event state*/ |
1460 | - const char* character , /*character*/ |
1461 | - unsigned short keyCount /*key repeat count*/) |
1462 | - { |
1463 | - ProcessKeyEvent(eventType, keysym, state, character, keyCount); |
1464 | - } |
1465 | - |
1466 | - void TextEntryIM::RecvStartKeyFocus() |
1467 | - { |
1468 | - key_nav_mode_ = true; |
1469 | - text_input_mode_ = false; |
1470 | - |
1471 | - FocusInx(); |
1472 | - } |
1473 | - |
1474 | - void TextEntryIM::RecvEndKeyFocus() |
1475 | - { |
1476 | - key_nav_mode_ = false; |
1477 | - text_input_mode_ = false; |
1478 | - |
1479 | - FocusOutx(); |
1480 | - } |
1481 | - |
1482 | - void TextEntryIM::Draw(GraphicsEngine& gfxContext, bool forceDraw) |
1483 | - { |
1484 | - MainDraw(); |
1485 | - Geometry base = GetGeometry(); |
1486 | - |
1487 | - gfxContext.PushClippingRectangle(base); |
1488 | - |
1489 | - nux::GetPainter().PaintBackground(gfxContext, base); |
1490 | - |
1491 | - Color col = color::Black; |
1492 | - col.alpha = 0; |
1493 | - gfxContext.QRP_Color(base.x, |
1494 | - base.y, |
1495 | - base.width, |
1496 | - base.height, |
1497 | - col); |
1498 | - |
1499 | - TexCoordXForm texxform; |
1500 | - texxform.SetWrap(TEXWRAP_REPEAT, TEXWRAP_REPEAT); |
1501 | - texxform.SetTexCoordType(TexCoordXForm::OFFSET_COORD); |
1502 | - gfxContext.QRP_1Tex(base.x, |
1503 | - base.y, |
1504 | - base.width, |
1505 | - base.height, |
1506 | - _texture2D->GetDeviceTexture(), |
1507 | - texxform, |
1508 | - _text_color); |
1509 | - |
1510 | - gfxContext.PopClippingRectangle(); |
1511 | - } |
1512 | - |
1513 | - void TextEntryIM::DrawContent(GraphicsEngine& gfxContext, bool forceDraw) |
1514 | - { |
1515 | - //MainDraw(); |
1516 | - } |
1517 | - |
1518 | - void TextEntryIM::PostDraw(GraphicsEngine& gfxContext, bool forceDraw) |
1519 | - { |
1520 | - // intentionally left empty |
1521 | - } |
1522 | - |
1523 | - void TextEntryIM::SetText(const char *text) |
1524 | - { |
1525 | - const char *end = NULL; |
1526 | - g_utf8_validate(text, -1, &end); |
1527 | - |
1528 | - std::string txt((text && *text && end > text) ? std::string(text, end) : ""); |
1529 | - if (txt == text_) |
1530 | - return; // prevent some redraws |
1531 | - |
1532 | - text_ = multiline_ ? txt : CleanupLineBreaks(txt.c_str()); |
1533 | - cursor_ = 0; |
1534 | - selection_bound_ = 0; |
1535 | - need_im_reset_ = true; |
1536 | - //ResetImContext(); |
1537 | - QueueRefresh(true, true); |
1538 | - sigTextChanged.emit(this); |
1539 | - } |
1540 | - |
1541 | - std::string const& TextEntryIM::GetText() const |
1542 | - { |
1543 | - return text_; |
1544 | - } |
1545 | - |
1546 | - void TextEntryIM::SetCompletion(const char *text) |
1547 | - { |
1548 | - const char *end = NULL; |
1549 | - g_utf8_validate(text, -1, &end); |
1550 | - std::string txt((text && *text && end > text) ? std::string(text, end) : ""); |
1551 | - if (txt == completion_) |
1552 | - return; |
1553 | - |
1554 | - completion_ = txt; |
1555 | - QueueRefresh(true, true); |
1556 | - } |
1557 | - |
1558 | - std::string const& TextEntryIM::GetCompletion() const |
1559 | - { |
1560 | - return completion_; |
1561 | - } |
1562 | - |
1563 | - void TextEntryIM::SetCompletionColor(const Color &color) |
1564 | - { |
1565 | - completion_color_ = color; |
1566 | - QueueRefresh(true, true); |
1567 | - } |
1568 | - |
1569 | - Color const& TextEntryIM::GetCompletionColor() const |
1570 | - { |
1571 | - return completion_color_; |
1572 | - } |
1573 | - |
1574 | - void TextEntryIM::SetTextColor(const Color &text_color) |
1575 | - { |
1576 | - if (_text_color != text_color) |
1577 | - { |
1578 | - _text_color = text_color; |
1579 | - QueueRefresh(true, true); |
1580 | - } |
1581 | - } |
1582 | - |
1583 | - Color const& TextEntryIM::GetTextColor() const |
1584 | - { |
1585 | - return _text_color; |
1586 | - } |
1587 | - |
1588 | - |
1589 | - void TextEntryIM::MainDraw() |
1590 | - { |
1591 | - |
1592 | - CairoGraphics *edit_canvas = EnsureCanvas(); |
1593 | - |
1594 | - if (update_canvas_ || !last_selection_region_.empty() || !selection_region_.empty()) |
1595 | - { |
1596 | - edit_canvas->PushState(); |
1597 | - DrawText(edit_canvas); |
1598 | - edit_canvas->PopState(); |
1599 | - } |
1600 | - |
1601 | -// if (background_) |
1602 | -// background_->Draw(canvas, 0, 0, GetBaseWidth, GetBaseHeight); |
1603 | - |
1604 | - CairoGraphics* final_canvas = new CairoGraphics(CAIRO_FORMAT_ARGB32, edit_canvas->GetWidth(), edit_canvas->GetHeight()); |
1605 | - |
1606 | - final_canvas->PushState(); |
1607 | - final_canvas->IntersectRectClipRegion(kInnerBorderX, |
1608 | - kInnerBorderY, |
1609 | - GetBaseWidth() - kInnerBorderX, |
1610 | - GetBaseHeight() - kInnerBorderY); |
1611 | - final_canvas->DrawCanvas(0, 0, edit_canvas); |
1612 | - final_canvas->PopState(); |
1613 | - DrawCursor(final_canvas); |
1614 | - |
1615 | - update_canvas_ = false; |
1616 | - last_selection_region_ = selection_region_; |
1617 | - last_cursor_region_ = cursor_region_; |
1618 | - |
1619 | - NBitmapData* bitmap = final_canvas->GetBitmap(); |
1620 | - delete final_canvas; |
1621 | - |
1622 | - if (!_texture2D || _texture2D->GetWidth() != bitmap->GetWidth() || _texture2D->GetHeight() != bitmap->GetHeight()) |
1623 | - { |
1624 | - if (_texture2D) |
1625 | - _texture2D->UnReference(); |
1626 | - _texture2D = GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture(); |
1627 | - } |
1628 | - |
1629 | - _texture2D->Update(bitmap); |
1630 | - delete bitmap; |
1631 | - } |
1632 | - |
1633 | - void TextEntryIM::FocusInx() |
1634 | - { |
1635 | - if (!focused_) |
1636 | - { |
1637 | - focused_ = true; |
1638 | - if (!readonly_ /*&& im_context_*/) |
1639 | - { |
1640 | - need_im_reset_ = true; |
1641 | - ime_->Focus(); |
1642 | - //gtk_im_context_focus_in(im_context_); |
1643 | - //UpdateIMCursorLocation(); |
1644 | - } |
1645 | - cursor_visible_ = true; // show cursor when getting focus |
1646 | - selection_changed_ = true; |
1647 | - cursor_moved_ = true; |
1648 | - // Don't adjust scroll. |
1649 | - QueueRefresh(true, false); |
1650 | - } |
1651 | - } |
1652 | - |
1653 | - void TextEntryIM::FocusOutx() |
1654 | - { |
1655 | - if (focused_) |
1656 | - { |
1657 | - focused_ = false; |
1658 | - if (!readonly_ /*&& im_context_*/) |
1659 | - { |
1660 | - need_im_reset_ = true; |
1661 | - ime_->Blur(); |
1662 | - //gtk_im_context_focus_out(im_context_); |
1663 | - } |
1664 | - cursor_visible_ = false; // hide cursor when losing focus |
1665 | - selection_changed_ = true; |
1666 | - cursor_moved_ = true; |
1667 | - // Don't adjust scroll. |
1668 | - QueueRefresh(true, false); |
1669 | - } |
1670 | - } |
1671 | - |
1672 | - CairoGraphics* TextEntryIM::EnsureCanvas() |
1673 | - { |
1674 | - if (canvas_) |
1675 | - { |
1676 | - if ((GetBaseWidth() == canvas_->GetWidth()) && (GetBaseHeight() == canvas_->GetHeight())) |
1677 | - { |
1678 | - return canvas_; |
1679 | - } |
1680 | - else |
1681 | - { |
1682 | - nuxDebugMsg("[TextEntryIM::EnsureCanvas] Recreate canvas"); |
1683 | - delete canvas_; |
1684 | - canvas_ = NULL; |
1685 | - } |
1686 | - } |
1687 | - canvas_ = new CairoGraphics(CAIRO_FORMAT_ARGB32, GetBaseWidth(), GetBaseHeight()); |
1688 | - nuxAssert(canvas_); |
1689 | - return canvas_; |
1690 | - } |
1691 | - |
1692 | - void TextEntryIM::AdjustScroll() |
1693 | - { |
1694 | - int old_offset_x = scroll_offset_x_; |
1695 | - int old_offset_y = scroll_offset_y_; |
1696 | - int display_width = GetBaseWidth() - kInnerBorderX * 2; |
1697 | - int display_height = GetBaseHeight() - kInnerBorderY * 2; |
1698 | - |
1699 | - PangoLayout *layout = EnsureLayout(); |
1700 | - int text_width, text_height; |
1701 | - pango_layout_get_pixel_size(layout, &text_width, &text_height); |
1702 | - |
1703 | - int strong_x, strong_y, strong_height; |
1704 | - int weak_x, weak_y, weak_height; |
1705 | - GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, |
1706 | - &weak_x, &weak_y, &weak_height); |
1707 | - |
1708 | - if (!wrap_ && display_width > text_width) |
1709 | - { |
1710 | - PangoAlignment align = pango_layout_get_alignment(layout); |
1711 | - if (align == PANGO_ALIGN_RIGHT) |
1712 | - scroll_offset_x_ = display_width - text_width; |
1713 | - else if (align == PANGO_ALIGN_CENTER) |
1714 | - scroll_offset_x_ = (display_width - text_width) / 2; |
1715 | - else |
1716 | - scroll_offset_x_ = 0; |
1717 | - } |
1718 | - else |
1719 | - { |
1720 | - if (scroll_offset_x_ + strong_x < 0) |
1721 | - scroll_offset_x_ = -strong_x; |
1722 | - else if (scroll_offset_x_ + strong_x > display_width) |
1723 | - scroll_offset_x_ = display_width - strong_x; |
1724 | - |
1725 | - if (std::abs(weak_x - strong_x) < display_width) |
1726 | - { |
1727 | - if (scroll_offset_x_ + weak_x < 0) |
1728 | - scroll_offset_x_ = - weak_x; |
1729 | - else if (scroll_offset_x_ + weak_x > display_width) |
1730 | - scroll_offset_x_ = display_width - weak_x; |
1731 | - } |
1732 | - } |
1733 | - |
1734 | - if (display_height > text_height) |
1735 | - { |
1736 | - scroll_offset_y_ = 0; |
1737 | - } |
1738 | - else |
1739 | - { |
1740 | - if (scroll_offset_y_ + strong_y + strong_height > display_height) |
1741 | - scroll_offset_y_ = display_height - strong_y - strong_height; |
1742 | - if (scroll_offset_y_ + strong_y < 0) |
1743 | - scroll_offset_y_ = -strong_y; |
1744 | - } |
1745 | - |
1746 | - if (old_offset_x != scroll_offset_x_ || old_offset_y != scroll_offset_y_) |
1747 | - content_modified_ = true; |
1748 | - } |
1749 | - |
1750 | - void TextEntryIM::QueueRefresh(bool relayout, bool adjust_scroll) |
1751 | - { |
1752 | - if (relayout) |
1753 | - ResetLayout(); |
1754 | - |
1755 | - if (adjust_scroll) |
1756 | - AdjustScroll(); |
1757 | - |
1758 | - QueueTextDraw(); |
1759 | - QueueCursorBlink(); |
1760 | - } |
1761 | - |
1762 | - void TextEntryIM::ResetImContext() |
1763 | - { |
1764 | - if (need_im_reset_) |
1765 | - { |
1766 | - need_im_reset_ = false; |
1767 | -// if (im_context_) |
1768 | -// gtk_im_context_reset(im_context_); |
1769 | - ResetPreedit(); |
1770 | - } |
1771 | - } |
1772 | - |
1773 | - void TextEntryIM::ResetPreedit() { |
1774 | - // Reset layout if there were some content in preedit string |
1775 | - if (preedit_.length()) |
1776 | - ResetLayout(); |
1777 | - |
1778 | - preedit_.clear(); |
1779 | - preedit_cursor_ = 0; |
1780 | - if (preedit_attrs_) { |
1781 | - pango_attr_list_unref(preedit_attrs_); |
1782 | - preedit_attrs_ = NULL; |
1783 | - } |
1784 | - } |
1785 | - |
1786 | - void TextEntryIM::DrawText(CairoGraphics *canvas) |
1787 | - { |
1788 | - PangoLayout *layout = EnsureLayout(); |
1789 | - |
1790 | - bool redraw_text = false; |
1791 | - if (update_canvas_) |
1792 | - { |
1793 | - canvas->ClearCanvas(); |
1794 | - canvas->PushState(); |
1795 | - redraw_text = true; |
1796 | - } |
1797 | - else if (!last_selection_region_.empty()) |
1798 | - { |
1799 | - //last_selection_region_.Integerize(); |
1800 | - canvas->PushState(); |
1801 | - canvas->IntersectGeneralClipRegion(last_selection_region_); |
1802 | - canvas->ClearRect(0, 0, GetBaseWidth(), GetBaseHeight()); |
1803 | - redraw_text = true; |
1804 | - } |
1805 | - |
1806 | - if (redraw_text) |
1807 | - { |
1808 | - cairo_set_source_rgb(canvas->GetInternalContext(), |
1809 | - _text_color.red, |
1810 | - _text_color.green, |
1811 | - _text_color.blue); |
1812 | - |
1813 | - cairo_move_to(canvas->GetInternalContext(), |
1814 | - scroll_offset_x_ + kInnerBorderX, |
1815 | - scroll_offset_y_ + kInnerBorderY); |
1816 | - |
1817 | - pango_cairo_show_layout(canvas->GetInternalContext(), layout); |
1818 | - |
1819 | - canvas->PopState(); |
1820 | - } |
1821 | - |
1822 | - // Draw selection background. |
1823 | - // Selection in a single line may be not continual, so we use pango to |
1824 | - // get the x-ranges of each selection range in one line, and draw them |
1825 | - // separately. |
1826 | - if (!selection_region_.empty()) |
1827 | - { |
1828 | - canvas->PushState(); |
1829 | - //selection_region_.Integerize(); |
1830 | - canvas->IntersectGeneralClipRegion(selection_region_); |
1831 | - |
1832 | - Color selection_color = GetSelectionBackgroundColor(); |
1833 | - Color text_color = GetSelectionTextColor(); |
1834 | - |
1835 | - cairo_set_source_rgb(canvas->GetInternalContext(), |
1836 | - selection_color.red, |
1837 | - selection_color.green, |
1838 | - selection_color.blue); |
1839 | - cairo_paint(canvas->GetInternalContext()); |
1840 | - |
1841 | - cairo_move_to(canvas->GetInternalContext(), |
1842 | - scroll_offset_x_ + kInnerBorderX, |
1843 | - scroll_offset_y_ + kInnerBorderY); |
1844 | - cairo_set_source_rgb(canvas->GetInternalContext(), |
1845 | - text_color.red, |
1846 | - text_color.green, |
1847 | - text_color.blue); |
1848 | - pango_cairo_show_layout(canvas->GetInternalContext(), layout); |
1849 | - canvas->PopState(); |
1850 | - } |
1851 | - } |
1852 | - |
1853 | - bool TextEntryIM::CursorBlinkCallback(TextEntryIM *self) |
1854 | - { |
1855 | - if (self->cursor_blink_status_) |
1856 | - self->ShowCursor(); |
1857 | - else |
1858 | - self->HideCursor(); |
1859 | - |
1860 | - if (--self->cursor_blink_status_ < 0) |
1861 | - self->cursor_blink_status_ = 2; |
1862 | - |
1863 | - return true; |
1864 | - } |
1865 | - |
1866 | - void TextEntryIM::QueueCursorBlink() |
1867 | - { |
1868 | - if (!cursor_blink_timer_) |
1869 | - cursor_blink_timer_ = g_timeout_add(kCursorBlinkTimeout, |
1870 | - (GSourceFunc)&CursorBlinkCallback, |
1871 | - this); |
1872 | - } |
1873 | - |
1874 | - void TextEntryIM::ShowCursor() |
1875 | - { |
1876 | - if (!cursor_visible_) |
1877 | - { |
1878 | - cursor_visible_ = true; |
1879 | - if (focused_ && !readonly_) |
1880 | - { |
1881 | - cursor_moved_ = true; |
1882 | - QueueRefresh(false, false); |
1883 | - } |
1884 | - } |
1885 | - } |
1886 | - |
1887 | - void TextEntryIM::HideCursor() |
1888 | - { |
1889 | - if (cursor_visible_) |
1890 | - { |
1891 | - cursor_visible_ = false; |
1892 | - if (focused_ && !readonly_) |
1893 | - { |
1894 | - cursor_moved_ = true; |
1895 | - QueueRefresh(false, false); |
1896 | - } |
1897 | - } |
1898 | - } |
1899 | - |
1900 | - void TextEntryIM::GetCursorRects(Rect *strong, Rect *weak) |
1901 | - { |
1902 | - int strong_x, strong_y, strong_height; |
1903 | - int weak_x, weak_y, weak_height; |
1904 | - GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, |
1905 | - &weak_x, &weak_y, &weak_height); |
1906 | - |
1907 | - strong->x = |
1908 | - strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth; |
1909 | - strong->width = kStrongCursorBarWidth * 2; |
1910 | - strong->y = strong_y + kInnerBorderY + scroll_offset_y_; |
1911 | - strong->height = strong_height; |
1912 | - |
1913 | - if (weak_x != strong_x) |
1914 | - { |
1915 | - weak->x = weak_x+ kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth; |
1916 | - weak->width = kWeakCursorBarWidth * 2; |
1917 | - weak->y = weak_y+ kInnerBorderY + scroll_offset_y_; |
1918 | - weak->height = weak_height; |
1919 | - } |
1920 | - else |
1921 | - { |
1922 | - *weak = *strong; |
1923 | - } |
1924 | - } |
1925 | - |
1926 | - void TextEntryIM::UpdateCursorRegion() |
1927 | - { |
1928 | - cursor_region_.clear(); |
1929 | - |
1930 | - Rect strong, weak; |
1931 | - GetCursorRects(&strong, &weak); |
1932 | - |
1933 | - cursor_region_.push_back(strong); |
1934 | - cursor_region_.push_back(weak); |
1935 | - } |
1936 | - |
1937 | - |
1938 | - void TextEntryIM::DrawCursor(CairoGraphics *canvas) |
1939 | - { |
1940 | - if (!cursor_visible_) |
1941 | - return; |
1942 | - |
1943 | - int strong_x, strong_y, strong_height; |
1944 | - int weak_x, weak_y, weak_height; |
1945 | - GetCursorLocationInLayout(&strong_x, &strong_y, &strong_height, |
1946 | - &weak_x, &weak_y, &weak_height); |
1947 | - |
1948 | - // Draw strong cursor. |
1949 | - // 0.5 is for cairo drawing between the grid |
1950 | - canvas->DrawLine(strong_x + kInnerBorderX + scroll_offset_x_ + 0.5, |
1951 | - strong_y + kInnerBorderY + scroll_offset_y_, |
1952 | - strong_x + kInnerBorderX + scroll_offset_x_ + 0.5, |
1953 | - strong_y + strong_height + kInnerBorderY + scroll_offset_y_, |
1954 | - kStrongCursorLineWidth, kStrongCursorColor); |
1955 | - // Draw a small arror towards weak cursor |
1956 | - if (strong_x > weak_x) |
1957 | - { |
1958 | - canvas->DrawLine( |
1959 | - strong_x + kInnerBorderX + scroll_offset_x_ - kStrongCursorBarWidth, |
1960 | - strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, |
1961 | - strong_x + kInnerBorderX + scroll_offset_x_, |
1962 | - strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, |
1963 | - kStrongCursorLineWidth, kStrongCursorColor); |
1964 | - } |
1965 | - else if (strong_x < weak_x) |
1966 | - { |
1967 | - canvas->DrawLine( |
1968 | - strong_x + kInnerBorderX + scroll_offset_x_, |
1969 | - strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, |
1970 | - strong_x + kInnerBorderX + scroll_offset_x_ + kStrongCursorBarWidth, |
1971 | - strong_y + kInnerBorderY + scroll_offset_y_ + kStrongCursorLineWidth, |
1972 | - kStrongCursorLineWidth, kStrongCursorColor); |
1973 | - } |
1974 | - |
1975 | - if (strong_x != weak_x ) |
1976 | - { |
1977 | - // Draw weak cursor. |
1978 | - canvas->DrawLine(weak_x + kInnerBorderX + scroll_offset_x_, |
1979 | - weak_y + kInnerBorderY + scroll_offset_y_, |
1980 | - weak_x + kInnerBorderX + scroll_offset_x_, |
1981 | - weak_y + weak_height + kInnerBorderY + scroll_offset_y_, |
1982 | - kWeakCursorLineWidth, kWeakCursorColor); |
1983 | - // Draw a small arror towards strong cursor |
1984 | - if (weak_x > strong_x) |
1985 | - { |
1986 | - canvas->DrawLine( |
1987 | - weak_x + kInnerBorderX + scroll_offset_x_ - kWeakCursorBarWidth, |
1988 | - weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, |
1989 | - weak_x + kInnerBorderX + scroll_offset_x_, |
1990 | - weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, |
1991 | - kWeakCursorLineWidth, kWeakCursorColor); |
1992 | - } |
1993 | - else |
1994 | - { |
1995 | - canvas->DrawLine( |
1996 | - weak_x + kInnerBorderX + scroll_offset_x_, |
1997 | - weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, |
1998 | - weak_x + kInnerBorderX + scroll_offset_x_ + kWeakCursorBarWidth, |
1999 | - weak_y + kInnerBorderY + scroll_offset_y_ + kWeakCursorLineWidth, |
2000 | - kWeakCursorLineWidth, kWeakCursorColor); |
2001 | - } |
2002 | - } |
2003 | - } |
2004 | - |
2005 | - Color TextEntryIM::GetSelectionBackgroundColor() |
2006 | - { |
2007 | - return kDefaultSelectionBackgroundColor; |
2008 | - } |
2009 | - |
2010 | - Color TextEntryIM::GetSelectionTextColor() |
2011 | - { |
2012 | - return kDefaultSelectionTextColor; |
2013 | - } |
2014 | - |
2015 | - void TextEntryIM::GetCursorLocationInLayout(int *strong_x, int *strong_y, |
2016 | - int *strong_height, |
2017 | - int *weak_x, int *weak_y, |
2018 | - int *weak_height) |
2019 | - { |
2020 | - PangoLayout *layout = EnsureLayout(); |
2021 | - int cursor_index = TextIndexToLayoutIndex(cursor_, true); |
2022 | - |
2023 | - PangoRectangle strong, weak; |
2024 | - pango_layout_get_cursor_pos(layout, cursor_index, &strong, &weak); |
2025 | - |
2026 | - if (strong_x) |
2027 | - *strong_x = PANGO_PIXELS(strong.x); |
2028 | - if (strong_y) |
2029 | - *strong_y = PANGO_PIXELS(strong.y); |
2030 | - if (strong_height) |
2031 | - *strong_height = PANGO_PIXELS(strong.height); |
2032 | - if (weak_x) |
2033 | - *weak_x = PANGO_PIXELS(weak.x); |
2034 | - if (weak_y) |
2035 | - *weak_y = PANGO_PIXELS(weak.y); |
2036 | - if (weak_height) |
2037 | - *weak_height = PANGO_PIXELS(weak.height); |
2038 | - } |
2039 | - |
2040 | - PangoLayout* TextEntryIM::EnsureLayout() |
2041 | - { |
2042 | - if (!cached_layout_) |
2043 | - { |
2044 | - cached_layout_ = CreateLayout(); |
2045 | - } |
2046 | - return cached_layout_; |
2047 | - } |
2048 | - |
2049 | - void TextEntryIM::QueueTextDraw() |
2050 | - { |
2051 | - if (content_modified_) |
2052 | - { |
2053 | - UpdateSelectionRegion(); |
2054 | - UpdateCursorRegion(); |
2055 | - QueueDraw(); //owner->QueueDraw(); |
2056 | - content_modified_ = false; |
2057 | - update_canvas_ = true; |
2058 | - } |
2059 | - else |
2060 | - { |
2061 | - if (selection_changed_) |
2062 | - { |
2063 | - UpdateSelectionRegion(); |
2064 | - if (!last_selection_region_.empty()) |
2065 | - QueueDraw(); //owner_->QueueDrawRegion(last_selection_region_); |
2066 | - if (!selection_region_.empty()) |
2067 | - QueueDraw(); //owner_->QueueDrawRegion(selection_region_); |
2068 | - selection_changed_ = false; |
2069 | - } |
2070 | - if (cursor_moved_) |
2071 | - { |
2072 | - UpdateCursorRegion(); |
2073 | - if (!last_cursor_region_.empty()) |
2074 | - QueueDraw(); //owner_->QueueDrawRegion(last_cursor_region_); |
2075 | - if (!cursor_region_.empty()) |
2076 | - QueueDraw(); //owner_->QueueDrawRegion(cursor_region_); |
2077 | - cursor_moved_ = false; |
2078 | - } |
2079 | - } |
2080 | - } |
2081 | - |
2082 | - void TextEntryIM::ResetLayout() |
2083 | - { |
2084 | - if (cached_layout_) |
2085 | - { |
2086 | - g_object_unref(cached_layout_); |
2087 | - cached_layout_ = NULL; |
2088 | - content_modified_ = true; |
2089 | - } |
2090 | - } |
2091 | - |
2092 | - PangoLayout* TextEntryIM::CreateLayout() |
2093 | - { |
2094 | - // Creates the pango layout with a temporary canvas that is not zoomed. |
2095 | - CairoGraphics *canvas = new CairoGraphics(CAIRO_FORMAT_ARGB32, 1, 1); |
2096 | - PangoLayout *layout = pango_cairo_create_layout(canvas->GetInternalContext()); |
2097 | - delete canvas; |
2098 | - PangoAttrList *tmp_attrs = pango_attr_list_new(); |
2099 | - std::string tmp_string; |
2100 | - |
2101 | - /* Set necessary parameters */ |
2102 | - pango_cairo_context_set_font_options(pango_layout_get_context(layout), |
2103 | - font_options_); |
2104 | - pango_cairo_context_set_resolution(pango_layout_get_context(layout), |
2105 | - font_dpi_); |
2106 | - |
2107 | - if (wrap_) |
2108 | - { |
2109 | - pango_layout_set_width(layout, (GetBaseWidth() - kInnerBorderX * 2) * PANGO_SCALE); |
2110 | - pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); |
2111 | - } |
2112 | - else |
2113 | - { |
2114 | - pango_layout_set_width(layout, -1); |
2115 | - } |
2116 | - |
2117 | - pango_layout_set_single_paragraph_mode(layout, !multiline_); |
2118 | - |
2119 | - if (preedit_.length()) |
2120 | - { |
2121 | - size_t cursor_index = static_cast<size_t>(cursor_); |
2122 | - size_t text_length = text_.length(); |
2123 | - size_t preedit_length = preedit_.length(); |
2124 | - if (visible_) |
2125 | - { |
2126 | - tmp_string = text_; |
2127 | - tmp_string.insert(cursor_index, preedit_); |
2128 | - } |
2129 | - else |
2130 | - { |
2131 | - size_t nchars = g_utf8_strlen(text_.c_str(), text_length); |
2132 | - size_t preedit_nchars = g_utf8_strlen(preedit_.c_str(), preedit_length); |
2133 | - nchars += preedit_nchars; |
2134 | - tmp_string.reserve(password_char_.length() * nchars); |
2135 | - for (size_t i = 0; i < nchars; ++i) |
2136 | - tmp_string.append(password_char_); |
2137 | - size_t cursor_offset = |
2138 | - g_utf8_pointer_to_offset(text_.c_str(), text_.c_str() + cursor_index); |
2139 | - /* Fix cursor index and preedit_length */ |
2140 | - cursor_index = cursor_offset * password_char_.length(); |
2141 | - preedit_length = preedit_nchars * password_char_.length(); |
2142 | - } |
2143 | - if (preedit_attrs_) |
2144 | - pango_attr_list_splice(tmp_attrs, preedit_attrs_, |
2145 | - static_cast<int>(cursor_index), |
2146 | - static_cast<int>(preedit_length)); |
2147 | - } |
2148 | - else |
2149 | - { |
2150 | - if (visible_) |
2151 | - { |
2152 | - tmp_string = text_; |
2153 | - } |
2154 | - else |
2155 | - { |
2156 | - size_t nchars = g_utf8_strlen(text_.c_str(), text_.length()); |
2157 | - tmp_string.reserve(password_char_.length() * nchars); |
2158 | - for (size_t i = 0; i < nchars; ++i) |
2159 | - tmp_string.append(password_char_); |
2160 | - } |
2161 | - } |
2162 | - |
2163 | - int pre_completion_length = tmp_string.length(); |
2164 | - |
2165 | - if (!completion_.empty() && !wrap_) |
2166 | - { |
2167 | - tmp_string = text_ + completion_; |
2168 | - } |
2169 | - |
2170 | - pango_layout_set_text(layout, tmp_string.c_str(), |
2171 | - static_cast<int>(tmp_string.length())); |
2172 | - |
2173 | - /* Set necessary attributes */ |
2174 | - PangoAttribute *attr; |
2175 | - if (underline_) |
2176 | - { |
2177 | - attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); |
2178 | - attr->start_index = 0; |
2179 | - attr->end_index = static_cast<guint>(pre_completion_length); |
2180 | - pango_attr_list_insert(tmp_attrs, attr); |
2181 | - } |
2182 | - if (strikeout_) |
2183 | - { |
2184 | - attr = pango_attr_strikethrough_new(TRUE); |
2185 | - attr->start_index = 0; |
2186 | - attr->end_index = static_cast<guint>(pre_completion_length); |
2187 | - pango_attr_list_insert(tmp_attrs, attr); |
2188 | - } |
2189 | - if (!completion_.empty() && !wrap_) |
2190 | - { |
2191 | - attr = pango_attr_foreground_new(65535 * completion_color_.red, |
2192 | - 65535 * completion_color_.green, |
2193 | - 65535 * completion_color_.blue); |
2194 | - attr->start_index = static_cast<guint>(pre_completion_length); |
2195 | - attr->end_index = static_cast<guint>(tmp_string.length()); |
2196 | - pango_attr_list_insert(tmp_attrs, attr); |
2197 | - } |
2198 | - /* Set font desc */ |
2199 | - { |
2200 | - /* safe to down_cast here, because we know the actual implementation. */ |
2201 | - CairoFont *font = new CairoFont( |
2202 | - font_family_.empty() ? kDefaultFontName : font_family_.c_str(), |
2203 | - font_size_, |
2204 | - italic_ ? CairoFont::STYLE_ITALIC : CairoFont::STYLE_NORMAL, |
2205 | - bold_ ? CairoFont::WEIGHT_BOLD : CairoFont::WEIGHT_NORMAL); |
2206 | - nuxAssert(font); |
2207 | - attr = pango_attr_font_desc_new(font->GetFontDescription()); |
2208 | - attr->start_index = 0; |
2209 | - attr->end_index = static_cast<unsigned int>(tmp_string.length()); |
2210 | - pango_attr_list_insert(tmp_attrs, attr); |
2211 | - pango_layout_set_font_description(layout, font->GetFontDescription()); |
2212 | - font->Destroy(); |
2213 | - } |
2214 | - pango_layout_set_attributes(layout, tmp_attrs); |
2215 | - pango_attr_list_unref(tmp_attrs); |
2216 | - |
2217 | - /* Set alignment according to text direction. Only set layout's alignment |
2218 | - * when it's not wrapped and in single line mode. |
2219 | - */ |
2220 | - if (!wrap_ && pango_layout_get_line_count(layout) <= 1 && |
2221 | - align_ != CairoGraphics::ALIGN_CENTER) |
2222 | - { |
2223 | - PangoDirection dir; |
2224 | - if (visible_) |
2225 | - dir = pango_find_base_dir(tmp_string.c_str(), |
2226 | - static_cast<int>(tmp_string.length())); |
2227 | - else |
2228 | - dir = PANGO_DIRECTION_NEUTRAL; |
2229 | - |
2230 | - if (dir == PANGO_DIRECTION_NEUTRAL) |
2231 | - { |
2232 | -// GtkWidget *widget = GetWidgetAndCursorLocation(NULL); |
2233 | -// if (widget && gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) |
2234 | -// dir = PANGO_DIRECTION_RTL; |
2235 | -// else |
2236 | -// dir = PANGO_DIRECTION_LTR; |
2237 | - |
2238 | - dir = PANGO_DIRECTION_LTR; |
2239 | - } |
2240 | - |
2241 | - // If wordWrap is false then "justify" alignment has no effect. |
2242 | - PangoAlignment pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ? |
2243 | - PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT); |
2244 | - |
2245 | - // Invert the alignment if text direction is right to left. |
2246 | - if (dir == PANGO_DIRECTION_RTL) |
2247 | - { |
2248 | - pango_align = (align_ == CairoGraphics::ALIGN_RIGHT ? |
2249 | - PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); |
2250 | - } |
2251 | - |
2252 | - pango_layout_set_alignment(layout, pango_align); |
2253 | - pango_layout_set_justify(layout, FALSE); |
2254 | - } |
2255 | - else if (align_ == CairoGraphics::ALIGN_JUSTIFY) |
2256 | - { |
2257 | - pango_layout_set_justify(layout, TRUE); |
2258 | - pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); |
2259 | - } |
2260 | - else if (align_ == CairoGraphics::ALIGN_RIGHT) |
2261 | - { |
2262 | - pango_layout_set_justify(layout, FALSE); |
2263 | - pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); |
2264 | - } |
2265 | - else if (align_ == CairoGraphics::ALIGN_CENTER) |
2266 | - { |
2267 | - pango_layout_set_justify(layout, FALSE); |
2268 | - pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); |
2269 | - } |
2270 | - else |
2271 | - { |
2272 | - pango_layout_set_justify(layout, FALSE); |
2273 | - pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); |
2274 | - } |
2275 | - |
2276 | - { |
2277 | - PangoContext *context; |
2278 | - PangoFontMetrics *metrics; |
2279 | - int ascent, descent; |
2280 | - |
2281 | - context = pango_layout_get_context(layout); |
2282 | - metrics = pango_context_get_metrics(context, |
2283 | - pango_layout_get_font_description(layout), |
2284 | - pango_context_get_language(context)); |
2285 | - |
2286 | - ascent = pango_font_metrics_get_ascent(metrics); |
2287 | - descent = pango_font_metrics_get_descent(metrics); |
2288 | - |
2289 | - int full_height = PANGO_PIXELS(ascent + descent) + (kInnerBorderY * 2); |
2290 | - SetMinimumHeight(full_height); |
2291 | - |
2292 | - pango_font_metrics_unref(metrics); |
2293 | - } |
2294 | - |
2295 | - return layout; |
2296 | - } |
2297 | - |
2298 | - int TextEntryIM::TextIndexToLayoutIndex(int text_index, bool consider_preedit_cursor) |
2299 | - { |
2300 | - if (visible_) |
2301 | - { |
2302 | - if (text_index < cursor_) |
2303 | - return text_index; |
2304 | - |
2305 | - if (text_index == cursor_ && consider_preedit_cursor) |
2306 | - return text_index + preedit_cursor_; |
2307 | - |
2308 | - return text_index + static_cast<int>(preedit_.length()); |
2309 | - } |
2310 | - |
2311 | - const char *text = text_.c_str(); |
2312 | - int offset = static_cast<int>( |
2313 | - g_utf8_pointer_to_offset(text, text + text_index)); |
2314 | - int preedit_offset = 0; |
2315 | - int preedit_chars = 0; |
2316 | - if (preedit_.length()) |
2317 | - { |
2318 | - const char *preedit_text = preedit_.c_str(); |
2319 | - preedit_offset = static_cast<int>(g_utf8_pointer_to_offset( |
2320 | - preedit_text, preedit_text + preedit_cursor_)); |
2321 | - preedit_chars = static_cast<int>(g_utf8_strlen( |
2322 | - preedit_text, preedit_.length())); |
2323 | - } |
2324 | - |
2325 | - int password_char_length = static_cast<int>(password_char_.length()); |
2326 | - |
2327 | - if (text_index < cursor_) |
2328 | - return offset * password_char_length; |
2329 | - |
2330 | - if (text_index == cursor_ && consider_preedit_cursor) |
2331 | - return (offset + preedit_offset) * password_char_length; |
2332 | - |
2333 | - return (offset + preedit_chars) * password_char_length; |
2334 | - } |
2335 | - |
2336 | - |
2337 | - int TextEntryIM::LayoutIndexToTextIndex(int layout_index) |
2338 | - { |
2339 | - if (visible_) |
2340 | - { |
2341 | - if (layout_index < cursor_) |
2342 | - return layout_index; |
2343 | - |
2344 | - int preedit_length = static_cast<int>(preedit_.length()); |
2345 | - if (layout_index >= cursor_ + preedit_length) |
2346 | - return layout_index - preedit_length; |
2347 | - |
2348 | - return cursor_; |
2349 | - } |
2350 | - |
2351 | - int password_char_length = static_cast<int>(password_char_.length()); |
2352 | - nuxAssert(layout_index % password_char_length == 0); |
2353 | - |
2354 | - int offset = layout_index / password_char_length; |
2355 | - |
2356 | - const char *text = text_.c_str(); |
2357 | - int cursor_offset = static_cast<int>( |
2358 | - g_utf8_pointer_to_offset(text, text + cursor_)); |
2359 | - int preedit_chars = static_cast<int>( |
2360 | - g_utf8_strlen(preedit_.c_str(), preedit_.length())); |
2361 | - |
2362 | - if (offset < cursor_offset) |
2363 | - return static_cast<int>(g_utf8_offset_to_pointer(text, offset) - text); |
2364 | - |
2365 | - if (offset >= cursor_offset + preedit_chars) |
2366 | - return static_cast<int>( |
2367 | - g_utf8_offset_to_pointer(text, offset - preedit_chars) - text); |
2368 | - |
2369 | - return cursor_; |
2370 | - } |
2371 | - |
2372 | - int TextEntryIM::GetCharLength(int index) |
2373 | - { |
2374 | - const char *text = text_.c_str(); |
2375 | - const char *ptr = text + index; |
2376 | - const char *end = text + text_.length(); |
2377 | - const char *next = g_utf8_find_next_char(ptr, end); |
2378 | - return static_cast<int>(next ? static_cast<int>(next - ptr) : end - ptr); |
2379 | - } |
2380 | - |
2381 | - int TextEntryIM::GetPrevCharLength(int index) |
2382 | - { |
2383 | - const char *text = text_.c_str(); |
2384 | - const char *ptr = text + index; |
2385 | - const char *prev = g_utf8_find_prev_char(text, ptr); |
2386 | - return static_cast<int>(prev ? static_cast<int>(ptr - prev) : ptr - text); |
2387 | - } |
2388 | - |
2389 | - void TextEntryIM::EnterText(const char *str) |
2390 | - { |
2391 | - if (readonly_ || !str || !*str) return; |
2392 | - |
2393 | - if (GetSelectionBounds(NULL, NULL)) |
2394 | - { |
2395 | - DeleteSelection(); |
2396 | - } |
2397 | - else if (overwrite_ && cursor_ != static_cast<int>(text_.length())) |
2398 | - { |
2399 | - DeleteText(cursor_, cursor_ + GetCharLength(cursor_)); |
2400 | - } |
2401 | - |
2402 | - std::string tmp_text; |
2403 | - if (!multiline_) |
2404 | - { |
2405 | - tmp_text = CleanupLineBreaks(str); |
2406 | - str = tmp_text.c_str(); |
2407 | - } |
2408 | - |
2409 | - const char *end = NULL; |
2410 | - g_utf8_validate(str, -1, &end); |
2411 | - if (end > str) |
2412 | - { |
2413 | - size_t len = end - str; |
2414 | - |
2415 | - text_.insert(cursor_, str, len); |
2416 | - cursor_ += static_cast<int>(len); |
2417 | - selection_bound_ += static_cast<int>(len); |
2418 | - } |
2419 | - |
2420 | - ResetLayout(); |
2421 | - sigTextChanged.emit(this); |
2422 | - } |
2423 | - |
2424 | - void TextEntryIM::DeleteText(int start, int end) |
2425 | - { |
2426 | - if (readonly_) return; |
2427 | - |
2428 | - int text_length = static_cast<int>(text_.length()); |
2429 | - if (start < 0) |
2430 | - start = 0; |
2431 | - else if (start > text_length) |
2432 | - start = text_length; |
2433 | - |
2434 | - if (end < 0) |
2435 | - end = 0; |
2436 | - else if (end > text_length) |
2437 | - end = text_length; |
2438 | - |
2439 | - if (start > end) |
2440 | - std::swap(start, end); |
2441 | - else if (start == end) |
2442 | - return; |
2443 | - |
2444 | - text_.erase(start, end - start); |
2445 | - |
2446 | - if (cursor_ >= end) |
2447 | - cursor_ -= (end - start); |
2448 | - if (selection_bound_ >= end) |
2449 | - selection_bound_ -= (end - start); |
2450 | - |
2451 | - ResetLayout(); |
2452 | - sigTextChanged.emit(this); |
2453 | - } |
2454 | - |
2455 | - void TextEntryIM::SelectWord() |
2456 | - { |
2457 | - int selection_bound = MoveWords(cursor_, -1); |
2458 | - int cursor = MoveWords(selection_bound, 1); |
2459 | - SetSelectionBounds(selection_bound, cursor); |
2460 | - } |
2461 | - |
2462 | - void TextEntryIM::SelectLine() |
2463 | - { |
2464 | - int selection_bound = MoveLineEnds(cursor_, -1); |
2465 | - int cursor = MoveLineEnds(selection_bound, 1); |
2466 | - SetSelectionBounds(selection_bound, cursor); |
2467 | - } |
2468 | - |
2469 | - void TextEntryIM::Select(int start, int end) { |
2470 | - int text_length = static_cast<int>(text_.length()); |
2471 | - if (start == -1) |
2472 | - start = text_length; |
2473 | - if (end == -1) |
2474 | - end = text_length; |
2475 | - |
2476 | - start = Clamp(start, 0, text_length); |
2477 | - end = Clamp(end, 0, text_length); |
2478 | - SetSelectionBounds(start, end); |
2479 | - QueueRefresh(false, true); |
2480 | - } |
2481 | - |
2482 | - void TextEntryIM::SelectAll() { |
2483 | - SetSelectionBounds(0, static_cast<int>(text_.length())); |
2484 | - QueueRefresh(false, true); |
2485 | - } |
2486 | - |
2487 | - CairoGraphics::Alignment TextEntryIM::GetAlign() const |
2488 | - { |
2489 | - return align_; |
2490 | - } |
2491 | - |
2492 | - void TextEntryIM::SetAlign(CairoGraphics::Alignment align) |
2493 | - { |
2494 | - align_ = align; |
2495 | - QueueRefresh(true, true); |
2496 | - } |
2497 | - |
2498 | - bool TextEntryIM::im_active() |
2499 | - { |
2500 | - return ime_active_; |
2501 | - } |
2502 | - |
2503 | - void TextEntryIM::DeleteSelection() |
2504 | - { |
2505 | - int start, end; |
2506 | - if (GetSelectionBounds(&start, &end)) |
2507 | - DeleteText(start, end); |
2508 | - } |
2509 | - |
2510 | - void TextEntryIM::CopyClipboard() |
2511 | - { |
2512 | -// int start, end; |
2513 | -// if (GetSelectionBounds(&start, &end)) |
2514 | -// { |
2515 | -// GtkWidget *widget = GetWidgetAndCursorLocation(NULL); |
2516 | -// if (widget) |
2517 | -// { |
2518 | -// if (visible_) |
2519 | -// { |
2520 | -// gtk_clipboard_set_text( |
2521 | -// gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), |
2522 | -// text_.c_str() + start, end - start); |
2523 | -// } |
2524 | -// else |
2525 | -// { |
2526 | -// // Don't copy real content if it's in invisible. |
2527 | -// std::string content; |
2528 | -// int nchars = static_cast<int>( |
2529 | -// g_utf8_strlen(text_.c_str() + start, end - start)); |
2530 | -// for (int i = 0; i < nchars; ++i) |
2531 | -// content.append(password_char_); |
2532 | -// gtk_clipboard_set_text( |
2533 | -// gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), |
2534 | -// content.c_str(), static_cast<int>(content.length())); |
2535 | -// } |
2536 | -// } |
2537 | -// } |
2538 | - } |
2539 | - |
2540 | - void TextEntryIM::CutClipboard() |
2541 | - { |
2542 | - CopyClipboard(); |
2543 | - DeleteSelection(); |
2544 | - } |
2545 | - |
2546 | - void TextEntryIM::PasteClipboard() |
2547 | - { |
2548 | -// GtkWidget *widget = GetWidgetAndCursorLocation(NULL); |
2549 | -// if (widget) |
2550 | -// { |
2551 | -// gtk_clipboard_request_text( |
2552 | -// gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD), |
2553 | -// PasteCallback, this); |
2554 | -// } |
2555 | - } |
2556 | - |
2557 | - void TextEntryIM::BackSpace(MovementStep step) |
2558 | - { |
2559 | - if (GetSelectionBounds(NULL, NULL)) |
2560 | - { |
2561 | - DeleteSelection(); |
2562 | - } |
2563 | - else |
2564 | - { |
2565 | - if (cursor_ == 0) |
2566 | - return; |
2567 | - if (step == VISUALLY) |
2568 | - { |
2569 | - DeleteText(cursor_ - GetPrevCharLength(cursor_), cursor_); |
2570 | - } |
2571 | - else if (step == WORDS) |
2572 | - { |
2573 | - int new_cursor; |
2574 | - new_cursor = MoveWords(cursor_, -1); |
2575 | - DeleteText(new_cursor, cursor_); |
2576 | - } |
2577 | - } |
2578 | - } |
2579 | - |
2580 | - void TextEntryIM::Delete(MovementStep step) |
2581 | - { |
2582 | - if (GetSelectionBounds(NULL, NULL)) |
2583 | - { |
2584 | - DeleteSelection(); |
2585 | - } |
2586 | - else |
2587 | - { |
2588 | - if (cursor_ == static_cast<int>(text_.length())) |
2589 | - return; |
2590 | - if (step == VISUALLY) |
2591 | - { |
2592 | - DeleteText(cursor_, cursor_ + GetCharLength(cursor_)); |
2593 | - } |
2594 | - else if (step == WORDS) |
2595 | - { |
2596 | - int new_cursor; |
2597 | - new_cursor = MoveWords(cursor_, 1); |
2598 | - DeleteText(cursor_, new_cursor); |
2599 | - } |
2600 | - } |
2601 | - } |
2602 | - |
2603 | - void TextEntryIM::ToggleOverwrite() |
2604 | - { |
2605 | - overwrite_ = !overwrite_; |
2606 | - } |
2607 | - |
2608 | - void TextEntryIM::UpdateSelectionRegion() |
2609 | - { |
2610 | - selection_region_.clear(); |
2611 | - |
2612 | - // Selection in a single line may be not continual, so we use pango to |
2613 | - // get the x-ranges of each selection range in one line, and draw them |
2614 | - // separately. |
2615 | - int start_index, end_index; |
2616 | - if (GetSelectionBounds(&start_index, &end_index)) |
2617 | - { |
2618 | - PangoLayout *layout = EnsureLayout(); |
2619 | - PangoRectangle line_extents, pos; |
2620 | - int draw_start, draw_end; |
2621 | - int *ranges; |
2622 | - int n_ranges; |
2623 | - int n_lines = pango_layout_get_line_count(layout); |
2624 | - |
2625 | - start_index = TextIndexToLayoutIndex(start_index, false); |
2626 | - end_index = TextIndexToLayoutIndex(end_index, false); |
2627 | - |
2628 | - for (int line_index = 0; line_index < n_lines; ++line_index) |
2629 | - { |
2630 | -#if PANGO_VERSION_CHECK(1,16,0) |
2631 | - PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); |
2632 | -#else |
2633 | - PangoLayoutLine *line = pango_layout_get_line(layout, line_index); |
2634 | -#endif |
2635 | - if (line->start_index + line->length < start_index) |
2636 | - continue; |
2637 | - if (end_index < line->start_index) |
2638 | - break; |
2639 | - draw_start = Max<int>(start_index, line->start_index); |
2640 | - draw_end = Min<int>(end_index, line->start_index + line->length); |
2641 | - pango_layout_line_get_x_ranges(line, draw_start, draw_end, |
2642 | - &ranges, &n_ranges); |
2643 | - pango_layout_line_get_pixel_extents(line, NULL, &line_extents); |
2644 | - pango_layout_index_to_pos(layout, line->start_index, &pos); |
2645 | - for (int i = 0; i < n_ranges; ++i) |
2646 | - { |
2647 | - selection_region_.push_back(Rect( |
2648 | - kInnerBorderX + scroll_offset_x_ + PANGO_PIXELS(ranges[i * 2]), |
2649 | - kInnerBorderY + scroll_offset_y_ + PANGO_PIXELS(pos.y), |
2650 | - PANGO_PIXELS(ranges[i * 2 + 1] - ranges[i * 2]), |
2651 | - line_extents.height)); |
2652 | - } |
2653 | - g_free(ranges); |
2654 | - } |
2655 | - } |
2656 | - } |
2657 | - |
2658 | - void TextEntryIM::MoveCursor(MovementStep step, int count, bool extend_selection) |
2659 | - { |
2660 | - if (ime_active_) |
2661 | - return; |
2662 | - |
2663 | - ResetImContext(); |
2664 | - int new_cursor = 0; |
2665 | - // Clear selection first if not extend it. |
2666 | - if (!extend_selection) |
2667 | - { |
2668 | - selection_changed_ = true; |
2669 | - cursor_moved_ = true; |
2670 | - selection_bound_ = cursor_; |
2671 | - cursor_moved.emit(cursor_); |
2672 | - } |
2673 | - |
2674 | - // Calculate the new offset after motion. |
2675 | - switch(step) |
2676 | - { |
2677 | - case VISUALLY: |
2678 | - new_cursor = MoveVisually(cursor_, count); |
2679 | - break; |
2680 | - case WORDS: |
2681 | - new_cursor = MoveWords(cursor_, count); |
2682 | - break; |
2683 | - case DISPLAY_LINES: |
2684 | - new_cursor = MoveDisplayLines(cursor_, count); |
2685 | - break; |
2686 | - case DISPLAY_LINE_ENDS: |
2687 | - new_cursor = MoveLineEnds(cursor_, count); |
2688 | - break; |
2689 | - case PAGES: |
2690 | - new_cursor = MovePages(cursor_, count); |
2691 | - break; |
2692 | - case BUFFER: |
2693 | - nuxAssert(count == -1 || count == 1); |
2694 | - new_cursor = static_cast<int>(count == -1 ? 0 : text_.length()); |
2695 | - break; |
2696 | - } |
2697 | - |
2698 | - if (extend_selection) |
2699 | - SetSelectionBounds(selection_bound_, new_cursor); |
2700 | - else |
2701 | - SetCursor(new_cursor); |
2702 | - |
2703 | - QueueRefresh(true, true); |
2704 | - } |
2705 | - |
2706 | - int TextEntryIM::MoveVisually(int current_index, int count) |
2707 | - { |
2708 | - nuxAssert(current_index >= 0 && |
2709 | - current_index <= static_cast<int>(text_.length())); |
2710 | - nuxAssert(count); |
2711 | - nuxAssert(preedit_.length() == 0); |
2712 | - |
2713 | - PangoLayout *layout = EnsureLayout(); |
2714 | - const char *text = pango_layout_get_text(layout); |
2715 | - int index = TextIndexToLayoutIndex(current_index, false); |
2716 | - int new_index = 0; |
2717 | - int new_trailing = 0; |
2718 | - while (count != 0) |
2719 | - { |
2720 | - if (count > 0) |
2721 | - { |
2722 | - --count; |
2723 | - pango_layout_move_cursor_visually(layout, true, index, 0, 1, |
2724 | - &new_index, &new_trailing); |
2725 | - } |
2726 | - else if (count < 0) |
2727 | - { |
2728 | - ++count; |
2729 | - pango_layout_move_cursor_visually(layout, true, index, 0, -1, |
2730 | - &new_index, &new_trailing); |
2731 | - } |
2732 | - index = new_index; |
2733 | - if (index < 0 || index == G_MAXINT) |
2734 | - return current_index; |
2735 | - index = static_cast<int>(g_utf8_offset_to_pointer(text + index, new_trailing) - text); |
2736 | - } |
2737 | - return LayoutIndexToTextIndex(index); |
2738 | - } |
2739 | - |
2740 | - int TextEntryIM::MoveWords(int current_index, int count) |
2741 | - { |
2742 | - nuxAssert(current_index >= 0 && |
2743 | - current_index <= static_cast<int>(text_.length())); |
2744 | - nuxAssert(count); |
2745 | - nuxAssert(preedit_.length() == 0); |
2746 | - |
2747 | - if (!visible_) |
2748 | - { |
2749 | - return static_cast<int>(count > 0 ? text_.length() : 0); |
2750 | - } |
2751 | - |
2752 | - // The cursor movement direction shall be determined by the direction of |
2753 | - // current text line. |
2754 | - PangoLayout *layout = EnsureLayout(); |
2755 | - int n_log_attrs; |
2756 | - PangoLogAttr *log_attrs; |
2757 | - pango_layout_get_log_attrs(layout, &log_attrs, &n_log_attrs); |
2758 | - const char *text = pango_layout_get_text(layout); |
2759 | - int index = TextIndexToLayoutIndex(current_index, false); |
2760 | - int line_index; |
2761 | - pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL); |
2762 | - |
2763 | - // Weird bug: line_index here may be >= than line count? |
2764 | - int line_count = pango_layout_get_line_count(layout); |
2765 | - if (line_index >= line_count) |
2766 | - { |
2767 | - line_index = line_count - 1; |
2768 | - } |
2769 | - |
2770 | -#if PANGO_VERSION_CHECK(1,16,0) |
2771 | - PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); |
2772 | -#else |
2773 | - PangoLayoutLine *line = pango_layout_get_line(layout, line_index); |
2774 | -#endif |
2775 | - bool rtl = (line->resolved_dir == PANGO_DIRECTION_RTL); |
2776 | - const char *ptr = text + index; |
2777 | - int offset = static_cast<int>(g_utf8_pointer_to_offset(text, ptr)); |
2778 | - while (count != 0) |
2779 | - { |
2780 | - if (((rtl && count < 0) || (!rtl && count > 0)) && *ptr) |
2781 | - { |
2782 | - if (log_attrs[offset].is_white) |
2783 | - { |
2784 | - while (ptr && *ptr && log_attrs[offset].is_white) |
2785 | - { |
2786 | - ptr = g_utf8_find_next_char(ptr, NULL); |
2787 | - ++offset; |
2788 | - } |
2789 | - } |
2790 | - else |
2791 | - { |
2792 | - if (ptr && *ptr) |
2793 | - { |
2794 | - ptr = g_utf8_find_next_char(ptr, NULL); |
2795 | - ++offset; |
2796 | - } |
2797 | - } |
2798 | - while (ptr && *ptr) |
2799 | - { |
2800 | - ptr = g_utf8_find_next_char(ptr, NULL); |
2801 | - ++offset; |
2802 | - if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end) |
2803 | - break; |
2804 | - } |
2805 | - if (!ptr) |
2806 | - { |
2807 | - ptr = text; |
2808 | - while (*ptr) ++ptr; |
2809 | - } |
2810 | - } |
2811 | - else if (((rtl && count > 0) || (!rtl && count < 0)) && (ptr > text)) |
2812 | - { |
2813 | - if (offset > 0 && log_attrs[offset - 1].is_white) |
2814 | - { |
2815 | - while (ptr && offset > 0 && log_attrs[offset - 1].is_white) |
2816 | - { |
2817 | - ptr = g_utf8_find_prev_char(text, ptr); |
2818 | - --offset; |
2819 | - } |
2820 | - } |
2821 | - else |
2822 | - { |
2823 | - if (ptr) |
2824 | - { |
2825 | - ptr = g_utf8_find_prev_char(text, ptr); |
2826 | - --offset; |
2827 | - } |
2828 | - } |
2829 | - while (ptr /*&& *ptr*/) //fix: when at the end of the string, allow ctrl+left arrow to move backward to the start/end of the previous word. |
2830 | - { |
2831 | - ptr = g_utf8_find_prev_char(text, ptr); |
2832 | - --offset; |
2833 | - if (log_attrs[offset].is_word_start || log_attrs[offset].is_word_end) |
2834 | - break; |
2835 | - } |
2836 | - if (!ptr) |
2837 | - ptr = text; |
2838 | - } |
2839 | - else |
2840 | - { |
2841 | - break; |
2842 | - } |
2843 | - if (count > 0) |
2844 | - --count; |
2845 | - else |
2846 | - ++count; |
2847 | - } |
2848 | - return LayoutIndexToTextIndex(static_cast<int>(ptr - text)); |
2849 | - } |
2850 | - |
2851 | - int TextEntryIM::MoveDisplayLines(int current_index, int count) |
2852 | - { |
2853 | - nuxAssert(current_index >= 0 && |
2854 | - current_index <= static_cast<int>(text_.length())); |
2855 | - nuxAssert(count); |
2856 | - nuxAssert(preedit_.length() == 0); |
2857 | - |
2858 | - PangoLayout *layout = EnsureLayout(); |
2859 | - const char *text = pango_layout_get_text(layout); |
2860 | - int index = TextIndexToLayoutIndex(current_index, false); |
2861 | - int n_lines = pango_layout_get_line_count(layout); |
2862 | - int line_index = 0; |
2863 | - int x_off = 0; |
2864 | - PangoRectangle rect; |
2865 | - |
2866 | - // Find the current cursor X position in layout |
2867 | - pango_layout_index_to_line_x(layout, index, FALSE, &line_index, &x_off); |
2868 | - |
2869 | - // Weird bug: line_index here may be >= than line count? |
2870 | - if (line_index >= n_lines) |
2871 | - { |
2872 | - line_index = n_lines - 1; |
2873 | - } |
2874 | - |
2875 | - pango_layout_get_cursor_pos(layout, index, &rect, NULL); |
2876 | - x_off = rect.x; |
2877 | - |
2878 | - line_index += count; |
2879 | - |
2880 | - if (line_index < 0) |
2881 | - { |
2882 | - return 0; |
2883 | - } |
2884 | - else if (line_index >= n_lines) |
2885 | - { |
2886 | - return static_cast<int>(text_.length()); |
2887 | - } |
2888 | - |
2889 | - int trailing; |
2890 | -#if PANGO_VERSION_CHECK(1,16,0) |
2891 | - PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); |
2892 | -#else |
2893 | - PangoLayoutLine *line = pango_layout_get_line(layout, line_index); |
2894 | -#endif |
2895 | - // Find out the cursor x offset related to the new line position. |
2896 | - if (line->resolved_dir == PANGO_DIRECTION_RTL) |
2897 | - { |
2898 | - pango_layout_get_cursor_pos(layout, line->start_index + line->length, |
2899 | - &rect, NULL); |
2900 | - } |
2901 | - else |
2902 | - { |
2903 | - pango_layout_get_cursor_pos(layout, line->start_index, &rect, NULL); |
2904 | - } |
2905 | - |
2906 | - // rect.x is the left edge position of the line in the layout |
2907 | - x_off -= rect.x; |
2908 | - if (x_off < 0) x_off = 0; |
2909 | - pango_layout_line_x_to_index(line, x_off, &index, &trailing); |
2910 | - |
2911 | - index = static_cast<int>(g_utf8_offset_to_pointer(text + index, trailing) - text); |
2912 | - return LayoutIndexToTextIndex(index); |
2913 | - } |
2914 | - |
2915 | - int TextEntryIM::MovePages(int current_index, int count) |
2916 | - { |
2917 | - nuxAssert(current_index >= 0 && |
2918 | - current_index <= static_cast<int>(text_.length())); |
2919 | - nuxAssert(count); |
2920 | - nuxAssert(preedit_.length() == 0); |
2921 | - |
2922 | - // Transfer pages to display lines. |
2923 | - PangoLayout *layout = EnsureLayout(); |
2924 | - int layout_height; |
2925 | - pango_layout_get_pixel_size(layout, NULL, &layout_height); |
2926 | - int n_lines = pango_layout_get_line_count(layout); |
2927 | - int line_height = layout_height / n_lines; |
2928 | - int page_lines = (GetBaseHeight() - kInnerBorderY * 2) / line_height; |
2929 | - return MoveDisplayLines(current_index, count * page_lines); |
2930 | - } |
2931 | - |
2932 | - int TextEntryIM::MoveLineEnds(int current_index, int count) |
2933 | - { |
2934 | - nuxAssert(current_index >= 0 && |
2935 | - current_index <= static_cast<int>(text_.length())); |
2936 | - nuxAssert(count); |
2937 | - nuxAssert(preedit_.length() == 0); |
2938 | - |
2939 | - PangoLayout *layout = EnsureLayout(); |
2940 | - int index = TextIndexToLayoutIndex(current_index, false); |
2941 | - int line_index = 0; |
2942 | - |
2943 | - // Find current line |
2944 | - pango_layout_index_to_line_x(layout, index, FALSE, &line_index, NULL); |
2945 | - |
2946 | - // Weird bug: line_index here may be >= than line count? |
2947 | - int line_count = pango_layout_get_line_count(layout); |
2948 | - if (line_index >= line_count) |
2949 | - { |
2950 | - line_index = line_count - 1; |
2951 | - } |
2952 | - |
2953 | -// #if PANGO_VERSION_CHECK(1,16,0) |
2954 | -// PangoLayoutLine *line = pango_layout_get_line_readonly(layout, line_index); |
2955 | -// #else |
2956 | - PangoLayoutLine *line = pango_layout_get_line(layout, line_index); |
2957 | -// #endif |
2958 | - |
2959 | - if (line->length == 0) |
2960 | - return current_index; |
2961 | - |
2962 | - if ((line->resolved_dir == PANGO_DIRECTION_RTL && count < 0) || |
2963 | - (line->resolved_dir != PANGO_DIRECTION_RTL && count > 0)) |
2964 | - { |
2965 | - index = line->start_index + line->length; |
2966 | - } |
2967 | - else |
2968 | - { |
2969 | - index = line->start_index; |
2970 | - } |
2971 | - return LayoutIndexToTextIndex(index); |
2972 | - } |
2973 | - |
2974 | - void TextEntryIM::SetCursor(int cursor) |
2975 | - { |
2976 | - if (cursor != cursor_) |
2977 | - { |
2978 | - ResetImContext(); |
2979 | - // If there was a selection range, then the selection range will be cleared. |
2980 | - // Then content_modified_ shall be set to true to force redrawing the text. |
2981 | - if (cursor_ != selection_bound_) |
2982 | - selection_changed_ = true; |
2983 | - cursor_ = cursor; |
2984 | - selection_bound_ = cursor; |
2985 | - cursor_moved_ = true; |
2986 | - |
2987 | - cursor_moved.emit(cursor); |
2988 | - } |
2989 | - } |
2990 | - |
2991 | - int TextEntryIM::XYToTextIndex(int x, int y) |
2992 | - { |
2993 | - int width, height; |
2994 | - PangoLayout *layout = EnsureLayout(); |
2995 | - const char *text = pango_layout_get_text(layout); |
2996 | - pango_layout_get_pixel_size(layout, &width, &height); |
2997 | - |
2998 | - if (y < 0) |
2999 | - { |
3000 | - return 0; |
3001 | - } |
3002 | - else if (y >= height) |
3003 | - { |
3004 | - return static_cast<int>(text_.length()); |
3005 | - } |
3006 | - |
3007 | - int trailing; |
3008 | - int index; |
3009 | - pango_layout_xy_to_index(layout, x * PANGO_SCALE, y * PANGO_SCALE, |
3010 | - &index, &trailing); |
3011 | - index = static_cast<int>( |
3012 | - g_utf8_offset_to_pointer(text + index, trailing) - text); |
3013 | - |
3014 | - index = LayoutIndexToTextIndex(index); |
3015 | - |
3016 | - // Adjust the offset if preedit is not empty and if the offset is after |
3017 | - // current cursor. |
3018 | - int preedit_length = static_cast<int>(preedit_.length()); |
3019 | - if (preedit_length && index > cursor_) |
3020 | - { |
3021 | - if (index >= cursor_ + preedit_length) |
3022 | - index -= preedit_length; |
3023 | - else |
3024 | - index = cursor_; |
3025 | - } |
3026 | - return Clamp(index, 0, static_cast<int>(text_.length())); |
3027 | - } |
3028 | - |
3029 | - bool TextEntryIM::GetSelectionBounds(int *start, int *end) |
3030 | - { |
3031 | - if (start) |
3032 | - *start = Min<int>(selection_bound_, cursor_); |
3033 | - if (end) |
3034 | - *end = Max<int>(selection_bound_, cursor_); |
3035 | - |
3036 | - return (selection_bound_ != cursor_); |
3037 | - } |
3038 | - |
3039 | - void TextEntryIM::SetSelectionBounds(int selection_bound, int cursor) |
3040 | - { |
3041 | - if (selection_bound_ != selection_bound || cursor_ != cursor) |
3042 | - { |
3043 | - selection_changed_ = true; |
3044 | - selection_bound_ = selection_bound; |
3045 | - if (cursor_ != cursor) |
3046 | - { |
3047 | - cursor_ = cursor; |
3048 | - cursor_moved_ = true; |
3049 | - cursor_moved.emit(cursor); |
3050 | - } |
3051 | - |
3052 | - //ResetImContext(); |
3053 | - } |
3054 | - } |
3055 | - |
3056 | - void TextEntryIM::SetFontFamily(const char *font) |
3057 | - { |
3058 | - font_family_ = font; |
3059 | - QueueRefresh(true, true); |
3060 | - } |
3061 | - |
3062 | - void TextEntryIM::SetFontSize(double font_size) |
3063 | - { |
3064 | - font_size_ = font_size; |
3065 | - QueueRefresh(true, true); |
3066 | - } |
3067 | - |
3068 | - void TextEntryIM::SetFontOptions(const cairo_font_options_t *options) |
3069 | - { |
3070 | - g_return_if_fail(options); |
3071 | - |
3072 | - cairo_font_options_destroy(font_options_); |
3073 | - font_options_ = cairo_font_options_copy(options); |
3074 | - |
3075 | - QueueRefresh(true, true); |
3076 | - } |
3077 | - |
3078 | - bool TextEntryIM::InspectKeyEvent(unsigned int eventType, |
3079 | - unsigned int key_sym, |
3080 | - const char* character) |
3081 | - { |
3082 | - if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == false) && (ime_active_ == false)) |
3083 | - { |
3084 | - if (key_sym == NUX_VK_ENTER || |
3085 | - key_sym == NUX_KP_ENTER || |
3086 | - key_sym == NUX_VK_UP || |
3087 | - key_sym == NUX_VK_DOWN || |
3088 | - key_sym == NUX_VK_LEFT || |
3089 | - key_sym == NUX_VK_RIGHT || |
3090 | - key_sym == NUX_VK_LEFT_TAB || |
3091 | - key_sym == NUX_VK_TAB || |
3092 | - key_sym == NUX_VK_ESCAPE) |
3093 | - { |
3094 | - return false; |
3095 | - } |
3096 | - } |
3097 | - |
3098 | - if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == true) && (text_input_mode_ == true) && (ime_active_ == false)) |
3099 | - { |
3100 | - // Enable to exit the TextEntryIM when in write mode(hack for unity dash) |
3101 | - if (key_sym == NUX_VK_UP || |
3102 | - key_sym == NUX_VK_DOWN || |
3103 | - key_sym == NUX_VK_ESCAPE) |
3104 | - { |
3105 | - return false; |
3106 | - } |
3107 | - } |
3108 | - |
3109 | - if ((eventType == NUX_KEYDOWN) && (key_nav_mode_ == false) && (text_input_mode_ == false)) |
3110 | - { |
3111 | - return false; |
3112 | - } |
3113 | - |
3114 | - return true; |
3115 | - } |
3116 | -} |
3117 | |
3118 | === removed file 'Nux/TextEntryIM.h' |
3119 | --- Nux/TextEntryIM.h 2012-02-12 01:55:02 +0000 |
3120 | +++ Nux/TextEntryIM.h 1970-01-01 00:00:00 +0000 |
3121 | @@ -1,383 +0,0 @@ |
3122 | -#ifndef TEXTENTRYIM_H |
3123 | -#define TEXTENTRYIM_H |
3124 | - |
3125 | - |
3126 | - |
3127 | -// Heavily inspired from google gadget code |
3128 | -/* |
3129 | - Copyright 2008 Google Inc. |
3130 | - |
3131 | - Licensed under the Apache License, Version 2.0(the "License"); |
3132 | - you may not use this file except in compliance with the License. |
3133 | - You may obtain a copy of the License at |
3134 | - |
3135 | - http://www.apache.org/licenses/LICENSE-2.0 |
3136 | - |
3137 | - Unless required by applicable law or agreed to in writing, software |
3138 | - distributed under the License is distributed on an "AS IS" BASIS, |
3139 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
3140 | - See the License for the specific language governing permissions and |
3141 | - limitations under the License. |
3142 | -*/ |
3143 | - |
3144 | - |
3145 | -#include "cairo/cairo.h" |
3146 | -#include "pango/pango.h" |
3147 | -#include "pango/pangocairo.h" |
3148 | -#include "NuxImage/CairoGraphics.h" |
3149 | -#include "InputMethodIBus.h" |
3150 | - |
3151 | -namespace nux |
3152 | -{ |
3153 | - class CairoGraphics; |
3154 | - class IBusIMEContext; |
3155 | - |
3156 | - class TextEntryIM: public View |
3157 | - { |
3158 | - NUX_DECLARE_OBJECT_TYPE(TextEntryIM, View); |
3159 | - public: |
3160 | - TextEntryIM(const char* text, NUX_FILE_LINE_PROTO); |
3161 | - ~TextEntryIM(); |
3162 | - |
3163 | - Area* FindAreaUnderMouse(const Point& mouse_position, NuxEventType event_type); |
3164 | - virtual void Draw(GraphicsEngine &graphics_engine, bool force_draw); |
3165 | - virtual void DrawContent(GraphicsEngine &graphics_engine, bool force_draw); |
3166 | - virtual void PostDraw(GraphicsEngine &graphics_engine, bool force_draw); |
3167 | - |
3168 | - void PreLayoutManagement(); |
3169 | - long PostLayoutManagement(long layoutResult); |
3170 | - |
3171 | - // Receivers |
3172 | - |
3173 | - void RecvMouseDoubleClick(int x, int y, unsigned long button_flags, unsigned long key_flags); |
3174 | - void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); |
3175 | - void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); |
3176 | - void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); |
3177 | - void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags); |
3178 | - void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags); |
3179 | - void RecvKeyEvent( |
3180 | - unsigned long eventType , /*event type*/ |
3181 | - unsigned long keysym , /*event keysym*/ |
3182 | - unsigned long state , /*event state*/ |
3183 | - const char* character , /*character*/ |
3184 | - unsigned short keyCount /*key repeat count*/); |
3185 | - |
3186 | - void RecvStartKeyFocus(); |
3187 | - void RecvEndKeyFocus(); |
3188 | - |
3189 | - bool _size_match_text; |
3190 | - BaseTexture *_texture2D; |
3191 | - |
3192 | - void MainDraw(); |
3193 | - void ProcessMouseEvent(int event_type, int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); |
3194 | - void ProcessKeyEvent ( |
3195 | - unsigned long eventType , /*event type*/ |
3196 | - unsigned long keysym , /*event keysym*/ |
3197 | - unsigned long state , /*event state*/ |
3198 | - const char* character , /*character*/ |
3199 | - unsigned short keyCount /*key repeat count*/); |
3200 | - |
3201 | - void FocusInx(); |
3202 | - void FocusOutx(); |
3203 | - |
3204 | - //! Text changed signal |
3205 | - /*! |
3206 | - This signal is emitted when the text has changed. |
3207 | - */ |
3208 | - sigc::signal <void, TextEntryIM*> sigTextChanged; |
3209 | - sigc::signal <void> activated; |
3210 | - sigc::signal <void, int> cursor_moved; |
3211 | - |
3212 | - void SetText(const char *text); |
3213 | - std::string const& GetText() const; |
3214 | - |
3215 | - void SetCompletion(const char *text); // Should use std::string, does not for consistancy |
3216 | - std::string const& GetCompletion() const; |
3217 | - |
3218 | - void SetCompletionColor(const Color &color); |
3219 | - Color const& GetCompletionColor() const; |
3220 | - |
3221 | - void SetTextColor(const Color &color); |
3222 | - Color const& GetTextColor() const; |
3223 | - void SetFontFamily(const char *font); |
3224 | - void SetFontSize(double font_size); |
3225 | - void SetFontOptions(const cairo_font_options_t *options); |
3226 | - |
3227 | - /** Select text between start and end. */ |
3228 | - void Select(int start, int end); |
3229 | - /** Select all text */ |
3230 | - void SelectAll(); |
3231 | - |
3232 | - CairoGraphics::Alignment GetAlign() const; |
3233 | - void SetAlign(CairoGraphics::Alignment align); |
3234 | - |
3235 | - bool im_active(); |
3236 | - |
3237 | - protected: |
3238 | - bool _block_focus; // used to selectively ignore focus keyevents |
3239 | - |
3240 | - virtual void GeometryChanged(); |
3241 | - |
3242 | - /** |
3243 | - * Enum used to specify different motion types. |
3244 | - */ |
3245 | - enum MovementStep { |
3246 | - VISUALLY, |
3247 | - WORDS, |
3248 | - DISPLAY_LINES, |
3249 | - DISPLAY_LINE_ENDS, |
3250 | - PAGES, |
3251 | - BUFFER |
3252 | - }; |
3253 | - |
3254 | - void QueueTextDraw(); |
3255 | - /** Remove the cached layout. */ |
3256 | - void ResetLayout(); |
3257 | - /** |
3258 | - * Create pango layout on-demand. If the layout is not changed, return the |
3259 | - * cached one. |
3260 | - */ |
3261 | - PangoLayout* EnsureLayout(); |
3262 | - /** Create a new layout containning current edit content */ |
3263 | - PangoLayout* CreateLayout(); |
3264 | - /** Create cairo canvas on-demand. */ |
3265 | - CairoGraphics* EnsureCanvas(); |
3266 | - /** Adjust the scroll information */ |
3267 | - void AdjustScroll(); |
3268 | - /** |
3269 | - * Send out a request to refresh all informations of the edit control |
3270 | - * and queue a draw request. |
3271 | - * If @c relayout is true then the layout will be regenerated. |
3272 | - * */ |
3273 | - void QueueRefresh(bool relayout, bool adjust_scroll); |
3274 | - /** Reset the input method context */ |
3275 | - void ResetImContext(); |
3276 | - /** Reset preedit text */ |
3277 | - void ResetPreedit(); |
3278 | - /** Send out a request to blink the cursor if necessary */ |
3279 | - void QueueCursorBlink(); |
3280 | - static bool CursorBlinkCallback(TextEntryIM *data); |
3281 | - |
3282 | - void ShowCursor(); |
3283 | - void HideCursor(); |
3284 | - |
3285 | - /** Draw the Cursor to the canvas */ |
3286 | - void DrawCursor(CairoGraphics *canvas); |
3287 | - /** Draw the text to the canvas */ |
3288 | - void DrawText(CairoGraphics *canvas); |
3289 | - |
3290 | - void GetCursorRects(Rect *strong, Rect *weak); |
3291 | - |
3292 | - void UpdateCursorRegion(); |
3293 | - |
3294 | - void UpdateSelectionRegion(); |
3295 | - |
3296 | - /** Move cursor */ |
3297 | - void MoveCursor(MovementStep step, int count, bool extend_selection); |
3298 | - /** Move cursor visually, meaning left or right */ |
3299 | - int MoveVisually(int current_pos, int count); |
3300 | - /** Move cursor in words */ |
3301 | - int MoveWords(int current_pos, int count); |
3302 | - /** Move cursor in display lines */ |
3303 | - int MoveDisplayLines(int current_pos, int count); |
3304 | - /** Move cursor in pages */ |
3305 | - int MovePages(int current_pos, int count); |
3306 | - /** Move cursor to the beginning or end of a display line */ |
3307 | - int MoveLineEnds(int current_pos, int count); |
3308 | - |
3309 | - /** Set the current cursor offset, in number of characters. */ |
3310 | - void SetCursor(int cursor); |
3311 | - /** Get the most reasonable character offset according to the pixel |
3312 | - * coordinate in the layout */ |
3313 | - int XYToTextIndex(int x, int y); |
3314 | - /** Get the offset range that is currently selected,in number of characters.*/ |
3315 | - bool GetSelectionBounds(int *start, int *end); |
3316 | - /** Set the offest range that should be selected, in number of characters. */ |
3317 | - void SetSelectionBounds(int selection_bound, int cursor); |
3318 | - |
3319 | - /** Convert index in text_ into index in layout text. */ |
3320 | - int TextIndexToLayoutIndex(int text_index, bool consider_preedit_cursor); |
3321 | - |
3322 | - /** Convert index in layout text into index in text_. */ |
3323 | - int LayoutIndexToTextIndex(int layout_index); |
3324 | - |
3325 | - /** Get char length at index, in number of bytes. */ |
3326 | - int GetCharLength(int index); |
3327 | - |
3328 | - /** Get previous char length before index, in number of bytes. */ |
3329 | - int GetPrevCharLength(int index); |
3330 | - |
3331 | - /** Insert text at current caret position */ |
3332 | - void EnterText(const char *str); |
3333 | - /** Delete text in a specified range, in number of characters. */ |
3334 | - void DeleteText(int start, int end); |
3335 | - |
3336 | - /** Select the current word under cursor */ |
3337 | - void SelectWord(); |
3338 | - /** Select the current display line under cursor */ |
3339 | - void SelectLine(); |
3340 | - /** Delete the text that is currently selected */ |
3341 | - void DeleteSelection(); |
3342 | - |
3343 | - /** Cut the current selected text to the clipboard */ |
3344 | - void CutClipboard(); |
3345 | - /** Copy the current selected text to the clipboard */ |
3346 | - void CopyClipboard(); |
3347 | - /** Paste the text in the clipboard to current offset */ |
3348 | - void PasteClipboard(); |
3349 | - /** Delete a character before the offset of the cursor */ |
3350 | - void BackSpace(MovementStep step); |
3351 | - /** Delete a character at the offset of the cursor */ |
3352 | - void Delete(MovementStep step); |
3353 | - /** Switch between the overwrite mode and the insert mode*/ |
3354 | - void ToggleOverwrite(); |
3355 | - |
3356 | - /** Gets the color of selection background. */ |
3357 | - Color GetSelectionBackgroundColor(); |
3358 | - /** Gets the color of selection text. */ |
3359 | - Color GetSelectionTextColor(); |
3360 | - |
3361 | - /** |
3362 | - * Gets the cursor location in pango layout. The unit is pixel. |
3363 | - */ |
3364 | - void GetCursorLocationInLayout(int *strong_x, int *strong_y, int *strong_height, |
3365 | - int *weak_x, int *weak_y, int *weak_height); |
3366 | - |
3367 | - /** The CairoCanvas which hold cairo_t inside */ |
3368 | - CairoGraphics* canvas_; |
3369 | - |
3370 | - /** The cached Pango Layout */ |
3371 | - PangoLayout* cached_layout_; |
3372 | - |
3373 | - /** The text content of the edit control */ |
3374 | - std::string text_; |
3375 | - /** The preedit text of the edit control */ |
3376 | - std::string preedit_; |
3377 | - /** Attribute list of the preedit text */ |
3378 | - PangoAttrList *preedit_attrs_; |
3379 | - /** |
3380 | - * The character that should be displayed in invisible mode. |
3381 | - * If this is empty, then the edit control is visible |
3382 | - */ |
3383 | - std::string password_char_; |
3384 | - |
3385 | - /** The completion string */ |
3386 | - std::string completion_; |
3387 | - |
3388 | - /** The completion colour */ |
3389 | - Color completion_color_; |
3390 | - |
3391 | - /** Last time of mouse double click event. */ |
3392 | - unsigned long long last_dblclick_time_; |
3393 | - |
3394 | - /** The current cursor position in number of bytes. */ |
3395 | - int cursor_; |
3396 | - /** |
3397 | - * The preedit cursor position within the preedit string, |
3398 | - * in number of bytes. |
3399 | - */ |
3400 | - int preedit_cursor_; |
3401 | - /** |
3402 | - * The current selection bound in number of bytes, |
3403 | - * range between cursor_ and selection_bound_ are selected. |
3404 | - */ |
3405 | - int selection_bound_; |
3406 | - |
3407 | - /** X offset of current scroll, in pixels */ |
3408 | - int scroll_offset_x_; |
3409 | - /** Y offset of current scroll, in pixels */ |
3410 | - int scroll_offset_y_; |
3411 | - /** Timer id of cursor blink callback */ |
3412 | - int cursor_blink_timer_; |
3413 | - /** |
3414 | - * Indicates the status of cursor blinking, |
3415 | - * 0 means hide cursor |
3416 | - * otherwise means show cursor. |
3417 | - * The maximum value would be 2, and decrased by one in each cursor blink |
3418 | - * callback, then there would be 2/3 visible time and 1/3 invisible time. |
3419 | - */ |
3420 | - int cursor_blink_status_; |
3421 | - |
3422 | - /** Whether the text is visible, decided by password_char_ */ |
3423 | - bool visible_; |
3424 | - /** Whether the edit control is focused */ |
3425 | - bool focused_; |
3426 | - /** Whether the input method should be reset */ |
3427 | - bool need_im_reset_; |
3428 | - /** Whether the keyboard in overwrite mode */ |
3429 | - bool overwrite_; |
3430 | - /** Whether the button click should select words */ |
3431 | - bool select_words_; |
3432 | - /** Whether the button click should select lines */ |
3433 | - bool select_lines_; |
3434 | - /** Whether the left button is pressed */ |
3435 | - bool button_; |
3436 | - /** Whether the text should be bold */ |
3437 | - bool bold_; |
3438 | - /** Whether the text should be underlined */ |
3439 | - bool underline_; |
3440 | - /** Whether the text should be struck-out */ |
3441 | - bool strikeout_; |
3442 | - /** Whether the text should be italic */ |
3443 | - bool italic_; |
3444 | - /** Whether the text could be shown in multilines */ |
3445 | - bool multiline_; |
3446 | - /** Whether the text should be wrapped */ |
3447 | - bool wrap_; |
3448 | - /** whether the cursor should be displayed */ |
3449 | - bool cursor_visible_; |
3450 | - /** whether the edit control is readonly */ |
3451 | - bool readonly_; |
3452 | - /** |
3453 | - * Indicates if the content of the edit control has been modified |
3454 | - * since last draw |
3455 | - */ |
3456 | - bool content_modified_; |
3457 | - |
3458 | - /** Indicates if the selection region has been changed since last draw. */ |
3459 | - bool selection_changed_; |
3460 | - |
3461 | - /** Indicates if the cursor position has been moved since last draw. */ |
3462 | - bool cursor_moved_; |
3463 | - |
3464 | - /** Indicates if the canvas cache needs updating. */ |
3465 | - bool update_canvas_; |
3466 | - |
3467 | - /** The font family of the text */ |
3468 | - std::string font_family_; |
3469 | - /** The font size of the text */ |
3470 | - double font_size_; |
3471 | - |
3472 | - cairo_font_options_t *font_options_; |
3473 | - double font_dpi_; |
3474 | - |
3475 | - /** The text color of the edit control */ |
3476 | - Color _text_color; |
3477 | - |
3478 | - CairoGraphics::Alignment align_; |
3479 | - |
3480 | -#if defined(NUX_OS_LINUX) |
3481 | - Cursor caret_cursor_; |
3482 | -#endif |
3483 | - |
3484 | - std::list<Rect> last_selection_region_; |
3485 | - std::list<Rect> selection_region_; |
3486 | - std::list<Rect> last_cursor_region_; |
3487 | - std::list<Rect> cursor_region_; |
3488 | - |
3489 | - IBusIMEContext* ime_; |
3490 | - bool ime_active_; |
3491 | - |
3492 | - friend class IBusIMEContext; |
3493 | - |
3494 | - protected: |
3495 | - bool text_input_mode_; |
3496 | - bool key_nav_mode_; |
3497 | - |
3498 | - virtual bool InspectKeyEvent(unsigned int eventType, |
3499 | - unsigned int keysym, |
3500 | - const char* character); |
3501 | -}; |
3502 | -} |
3503 | - |
3504 | -#endif // TEXTENTRY_H |
3505 | |
3506 | === modified file 'configure.ac' |
3507 | --- configure.ac 2012-02-21 06:39:45 +0000 |
3508 | +++ configure.ac 2012-02-23 05:24:18 +0000 |
3509 | @@ -22,7 +22,7 @@ |
3510 | # The number format is : year/month/day |
3511 | # e.g.: december 5th, 2011 is: 20111205 |
3512 | # To make more than one API change in a day, add a number to the date. Like 20111205.xx |
3513 | -m4_define([nux_abi_version], [20120221.01]) |
3514 | +m4_define([nux_abi_version], [20120223.01]) |
3515 | |
3516 | m4_define([nux_version], |
3517 | [nux_major_version.nux_minor_version.nux_micro_version]) |
3518 | @@ -186,6 +186,16 @@ |
3519 | AC_SUBST(UNITY_SUPPORT_TEST_CFLAGS) |
3520 | AC_SUBST(UNITY_SUPPORT_TEST_LIBS) |
3521 | |
3522 | +PKG_CHECK_MODULES(IBUS, |
3523 | + glib-2.0 >= 2.25.14 |
3524 | + gio-2.0 |
3525 | + ibus-1.0 |
3526 | + gobject-2.0 |
3527 | + ) |
3528 | + |
3529 | +AC_SUBST(IBUS_CFLAGS) |
3530 | +AC_SUBST(IBUS_LIBS) |
3531 | + |
3532 | dnl ************************************ |
3533 | dnl Enable/disable tests |
3534 | dnl ************************************ |
3535 | |
3536 | === modified file 'examples/Makefile.am' |
3537 | --- examples/Makefile.am 2012-02-12 22:25:19 +0000 |
3538 | +++ examples/Makefile.am 2012-02-23 05:24:18 +0000 |
3539 | @@ -46,6 +46,7 @@ |
3540 | $(NUX_CORE_CFLAGS) \ |
3541 | $(NUX_EXAMPLES_CFLAGS) \ |
3542 | $(NUX_CFLAGS) \ |
3543 | + $(IBUS_CFLAGS) \ |
3544 | $(MAINTAINER_CFLAGS) |
3545 | |
3546 | ALL_LIBS = \ |
3547 | @@ -54,7 +55,8 @@ |
3548 | $(top_builddir)/NuxGraphics/libnux-graphics-@NUX_API_VERSION@.la \ |
3549 | $(top_builddir)/Nux/libnux-@NUX_API_VERSION@.la \ |
3550 | $(NUX_EXAMPLES_LIBS) \ |
3551 | - $(NUX_LIBS) |
3552 | + $(NUX_LIBS) \ |
3553 | + $(IBUS_LIBS) |
3554 | |
3555 | # This is the individual executable build. For every $exe in noinst_PROGRAMS |
3556 | # you need a $exe_SOURCES and $exe_LDADD so it builds |
3557 | |
3558 | === modified file 'tests/Makefile.am' |
3559 | --- tests/Makefile.am 2012-02-19 07:04:11 +0000 |
3560 | +++ tests/Makefile.am 2012-02-23 05:24:18 +0000 |
3561 | @@ -13,8 +13,9 @@ |
3562 | xtest-vlayout-key-navigation \ |
3563 | xtest-scrollbar \ |
3564 | xtest-focus-on-mouse-down \ |
3565 | - xtest-focus-on-mouse-enter \ |
3566 | - xtest-keynav-directions |
3567 | + xtest-keynav-directions \ |
3568 | + xtest-text-entry \ |
3569 | + xtest-focus-on-mouse-enter |
3570 | |
3571 | # Please keep alphabetical |
3572 | test_nux_SOURCES = \ |
3573 | @@ -100,7 +101,8 @@ |
3574 | $(NUX_CORE_CFLAGS) \ |
3575 | $(NUX_EXAMPLES_CFLAGS) \ |
3576 | $(NUX_CFLAGS) \ |
3577 | - $(MAINTAINER_CFLAGS) |
3578 | + $(MAINTAINER_CFLAGS) \ |
3579 | + $(IBUS_CFLAGS) |
3580 | |
3581 | gtest_nux_LDADD = \ |
3582 | $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
3583 | @@ -124,7 +126,8 @@ |
3584 | $(NUX_CORE_CFLAGS) \ |
3585 | $(NUX_EXAMPLES_CFLAGS) \ |
3586 | $(NUX_CFLAGS) \ |
3587 | - $(MAINTAINER_CFLAGS) |
3588 | + $(MAINTAINER_CFLAGS) \ |
3589 | + $(IBUS_CFLAGS) |
3590 | |
3591 | TestLibs = $(top_builddir)/NuxCore/libnux-core-@NUX_API_VERSION@.la \ |
3592 | $(top_builddir)/NuxImage/libnux-image-@NUX_API_VERSION@.la \ |
3593 | @@ -227,6 +230,14 @@ |
3594 | xtest_keynav_directions_LDADD = $(TestLibs) |
3595 | xtest_keynav_directions_LDFLAGS = -lpthread -lXtst |
3596 | |
3597 | +xtest_text_entry_SOURCES = xtest-text-entry.cpp \ |
3598 | + nux_automated_test_framework.cpp \ |
3599 | + nux_automated_test_framework.h |
3600 | + |
3601 | +xtest_text_entry_CPPFLAGS = $(TestFlags) |
3602 | +xtest_text_entry_LDADD = $(TestLibs) |
3603 | +xtest_text_entry_LDFLAGS = -lpthread -lXtst |
3604 | + |
3605 | |
3606 | #run make test as part of make check |
3607 | check-local: test gtest test-apps |
3608 | |
3609 | === modified file 'tests/Readme.txt' |
3610 | --- tests/Readme.txt 2012-02-17 13:43:55 +0000 |
3611 | +++ tests/Readme.txt 2012-02-23 05:24:18 +0000 |
3612 | @@ -53,3 +53,6 @@ |
3613 | |
3614 | xtest-keynav-direction |
3615 | Test key navigation. Especially test the direction a key nav is coming from when the view is getting the focus. |
3616 | + |
3617 | +xtest-text-entry |
3618 | + Simulate various operations on the text entry |
3619 | |
3620 | === modified file 'tests/nux_automated_test_framework.cpp' |
3621 | --- tests/nux_automated_test_framework.cpp 2011-12-06 22:44:57 +0000 |
3622 | +++ tests/nux_automated_test_framework.cpp 2012-02-23 05:24:18 +0000 |
3623 | @@ -259,7 +259,16 @@ |
3624 | } |
3625 | |
3626 | std::string s(1, c); |
3627 | - SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); |
3628 | + |
3629 | + if (c == ' ') |
3630 | + { |
3631 | + SendFakeKeyEvent(XK_space, modifier); |
3632 | + } |
3633 | + else |
3634 | + { |
3635 | + SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); |
3636 | + } |
3637 | + |
3638 | nux::SleepForMilliseconds(300); |
3639 | } |
3640 | |
3641 | @@ -282,7 +291,15 @@ |
3642 | } |
3643 | |
3644 | std::string s(1, c); |
3645 | - SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); |
3646 | + |
3647 | + if (c == ' ') |
3648 | + { |
3649 | + SendFakeKeyEvent(XK_space, modifier); |
3650 | + } |
3651 | + else |
3652 | + { |
3653 | + SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); |
3654 | + } |
3655 | nux::SleepForMilliseconds(300); |
3656 | } |
3657 | } |
3658 | @@ -367,6 +384,44 @@ |
3659 | SendFakeKeyEvent(XK_Return, 0); |
3660 | } |
3661 | |
3662 | +void NuxAutomatedTestFramework::ViewSendRight() |
3663 | +{ |
3664 | + SendFakeKeyEvent(XK_Right, 0); |
3665 | +} |
3666 | + |
3667 | +void NuxAutomatedTestFramework::ViewSendLeft() |
3668 | +{ |
3669 | + SendFakeKeyEvent(XK_Left, 0); |
3670 | +} |
3671 | + |
3672 | +void NuxAutomatedTestFramework::ViewSendUp() |
3673 | +{ |
3674 | + SendFakeKeyEvent(XK_Up, 0); |
3675 | +} |
3676 | + |
3677 | +void NuxAutomatedTestFramework::ViewSendDown() |
3678 | +{ |
3679 | + SendFakeKeyEvent(XK_Down, 0); |
3680 | +} |
3681 | + |
3682 | +void NuxAutomatedTestFramework::ViewSendIBusToggle() |
3683 | +{ |
3684 | + KeyCode modcode0 = 0; |
3685 | + KeyCode modcode1 = 0; |
3686 | + |
3687 | + modcode0 = XKeysymToKeycode(display_, XK_Control_L); |
3688 | + XTestFakeKeyEvent(display_, modcode0, True, 0); |
3689 | + |
3690 | + modcode1 = XKeysymToKeycode(display_, XK_space); |
3691 | + XTestFakeKeyEvent(display_, modcode1, True, 0); |
3692 | + |
3693 | + // release |
3694 | + /* Generate modkey release */ |
3695 | + XTestFakeKeyEvent(display_, modcode1, False, 0); |
3696 | + XTestFakeKeyEvent(display_, modcode0, False, 0); |
3697 | + |
3698 | +} |
3699 | + |
3700 | void NuxAutomatedTestFramework::PutMouseAt(int x, int y) |
3701 | { |
3702 | XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), x, y, CurrentTime); |
3703 | @@ -458,4 +513,4 @@ |
3704 | { |
3705 | nuxAlertMsg("%s: %s", msg, "Failed"); |
3706 | } |
3707 | -} |
3708 | \ No newline at end of file |
3709 | +} |
3710 | |
3711 | === modified file 'tests/nux_automated_test_framework.h' |
3712 | --- tests/nux_automated_test_framework.h 2011-11-23 04:42:44 +0000 |
3713 | +++ tests/nux_automated_test_framework.h 2012-02-23 05:24:18 +0000 |
3714 | @@ -80,6 +80,16 @@ |
3715 | void ViewSendTab(); |
3716 | //! Simulate Return key. |
3717 | void ViewSendReturn(); |
3718 | + //! Simulate Left arrow key. |
3719 | + void ViewSendLeft(); |
3720 | + //! Simulate Right arrow key. |
3721 | + void ViewSendRight(); |
3722 | + //! Simulate Up arrow key. |
3723 | + void ViewSendUp(); |
3724 | + //! Simulate Down arrow key. |
3725 | + void ViewSendDown(); |
3726 | + |
3727 | + void ViewSendIBusToggle(); |
3728 | |
3729 | //! Put the mouse pointer anywhere on the display. |
3730 | void PutMouseAt(int x, int y); |
3731 | |
3732 | === added file 'tests/xtest-text-entry.cpp' |
3733 | --- tests/xtest-text-entry.cpp 1970-01-01 00:00:00 +0000 |
3734 | +++ tests/xtest-text-entry.cpp 2012-02-23 05:24:18 +0000 |
3735 | @@ -0,0 +1,391 @@ |
3736 | +/* |
3737 | + * Copyright 2010 Inalogic Inc. |
3738 | + * |
3739 | + * This program is free software: you can redistribute it and/or modify it |
3740 | + * under the terms of the GNU General Public License version 3, as published |
3741 | + * by the Free Software Foundation. |
3742 | + * |
3743 | + * This program is distributed in the hope that it will be useful, but |
3744 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3745 | + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
3746 | + * PURPOSE. See the GNU General Public License for more details. |
3747 | + * |
3748 | + * You should have received a copy of the GNU General Public License |
3749 | + * version 3 along with this program. If not, see |
3750 | + * <http://www.gnu.org/licenses/> |
3751 | + * |
3752 | + * Authored by: Jay Taoko <jaytaoko@inalogic.com> |
3753 | + * |
3754 | + */ |
3755 | + |
3756 | +#include "Nux/Nux.h" |
3757 | +#include "Nux/WindowThread.h" |
3758 | +#include "Nux/VLayout.h" |
3759 | +#include "Nux/TextEntry.h" |
3760 | +#include "Nux/ProgramFramework/ProgramTemplate.h" |
3761 | +#include "Nux/ProgramFramework/TestView.h" |
3762 | +#include <X11/extensions/XTest.h> |
3763 | +#include <X11/keysym.h> |
3764 | +#include "nux_automated_test_framework.h" |
3765 | + |
3766 | +class TextTextEntry: public ProgramTemplate |
3767 | +{ |
3768 | +public: |
3769 | + TextTextEntry(const char* program_name, int window_width, int window_height, int program_life_span); |
3770 | + ~TextTextEntry(); |
3771 | + |
3772 | + virtual void UserInterfaceSetup(); |
3773 | + |
3774 | + void TextEntryClick(nux::TextEntry* text_entry); |
3775 | + void ResetEvents(); |
3776 | + nux::TextEntry* text_entry_; |
3777 | + |
3778 | + bool clicked_; |
3779 | +}; |
3780 | + |
3781 | +TextTextEntry::TextTextEntry(const char* program_name, |
3782 | + int window_width, |
3783 | + int window_height, |
3784 | + int program_life_span) |
3785 | + : ProgramTemplate(program_name, window_width, window_height, program_life_span) |
3786 | +{ |
3787 | + ResetEvents(); |
3788 | + text_entry_ = NULL; |
3789 | +} |
3790 | + |
3791 | +TextTextEntry::~TextTextEntry() |
3792 | +{ |
3793 | + |
3794 | +} |
3795 | + |
3796 | +void TextTextEntry::ResetEvents() |
3797 | +{ |
3798 | + clicked_ = false; |
3799 | +} |
3800 | + |
3801 | +void TextTextEntry::TextEntryClick(nux::TextEntry* text_entry) |
3802 | +{ |
3803 | + if (text_entry_ == text_entry) |
3804 | + { |
3805 | + clicked_ = true; |
3806 | + } |
3807 | +} |
3808 | + |
3809 | +void TextTextEntry::UserInterfaceSetup() |
3810 | +{ |
3811 | + nux::VLayout* main_layout = new nux::VLayout(NUX_TRACKER_LOCATION); |
3812 | + text_entry_ = new nux::TextEntry("", NUX_TRACKER_LOCATION); |
3813 | + text_entry_->SetFontSize(76); |
3814 | + |
3815 | + main_layout->AddView(text_entry_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); |
3816 | + main_layout->SetSpaceBetweenChildren(10); |
3817 | + main_layout->SetContentDistribution(nux::MAJOR_POSITION_CENTER); |
3818 | + |
3819 | + static_cast<nux::WindowThread*>(window_thread_)->SetLayout(main_layout); |
3820 | + |
3821 | + nux::ColorLayer background(nux::Color(0xFF4D4D4D)); |
3822 | + static_cast<nux::WindowThread*>(window_thread_)->SetWindowBackgroundPaintLayer(&background); |
3823 | +} |
3824 | + |
3825 | +bool SetEngineActive (IBusBus* bus_, std::string engine) |
3826 | +{ |
3827 | + GList* engines = ibus_bus_list_active_engines(bus_); |
3828 | + GList* start = engines; |
3829 | + |
3830 | + bool found = false; |
3831 | + gboolean global_flag = ibus_bus_get_use_global_engine(bus_); |
3832 | + |
3833 | + // Iterates through the active engines |
3834 | + do |
3835 | + { |
3836 | + IBusEngineDesc *engine_desc = IBUS_ENGINE_DESC (engines->data); |
3837 | + |
3838 | + // Found Engine, make it active! |
3839 | + if (g_strcmp0(ibus_engine_desc_get_name(engine_desc), engine.c_str()) == 0) |
3840 | + { |
3841 | + found = true; |
3842 | + |
3843 | + // Set ibus to use global engines |
3844 | + if (!global_flag) |
3845 | + ibus_config_set_value (ibus_bus_get_config(bus_), "general", "use_global_engine", g_variant_new_boolean(true)); |
3846 | + |
3847 | + // Set and activate the engine |
3848 | + ibus_bus_set_global_engine(bus_,engine.c_str()); |
3849 | + } |
3850 | + } while ((engines = g_list_next(engines)) != NULL); |
3851 | + |
3852 | + // Restores the global setting back to what it was |
3853 | + ibus_config_set_value (ibus_bus_get_config(bus_), "general", "use_global_engine", g_variant_new_boolean(global_flag)); |
3854 | + |
3855 | + g_list_free(start); |
3856 | + return found; |
3857 | +} |
3858 | + |
3859 | +TextTextEntry* test_textentry = NULL; |
3860 | + |
3861 | +void TestingThread(nux::NThread* thread, void* user_data) |
3862 | +{ |
3863 | + while (test_textentry->ReadyToGo() == false) |
3864 | + { |
3865 | + nuxDebugMsg("Waiting to start"); |
3866 | + nux::SleepForMilliseconds(300); |
3867 | + } |
3868 | + |
3869 | + nux::SleepForMilliseconds(1300); |
3870 | + |
3871 | + nux::WindowThread* wnd_thread = static_cast<nux::WindowThread*>(user_data); |
3872 | + |
3873 | + NuxAutomatedTestFramework test(wnd_thread); |
3874 | + |
3875 | + test.Startup(); |
3876 | + |
3877 | + test.TestReportMsg(test_textentry->text_entry_, "TextEntry created"); |
3878 | + |
3879 | + test_textentry->ResetEvents(); |
3880 | + test.ViewSendMouseMotionToCenter(test_textentry->text_entry_); |
3881 | + |
3882 | + test.ViewSendMouseClick(0, 1); |
3883 | + |
3884 | + // Type "Nux" |
3885 | + // The cursor is at the end of the line |
3886 | + // Simulate CTRL+A to select the entire text |
3887 | + // Simulate DELETE key to delete the text |
3888 | + { |
3889 | + test.ViewSendString("Nux"); |
3890 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "Nux", "Typed \"Nux\""); |
3891 | + |
3892 | + test.ViewSendCtrlA(); |
3893 | + nux::SleepForMilliseconds(500); |
3894 | + test.TestReportMsg(test_textentry->text_entry_->GetTextSelection() == "Nux", "Selection is \"Nux\""); |
3895 | + |
3896 | + |
3897 | + test.ViewSendDelete(); |
3898 | + nux::SleepForMilliseconds(500); |
3899 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty"); |
3900 | + } |
3901 | + |
3902 | + // Type "0123456789" |
3903 | + // The cursor is at the end of the line |
3904 | + // Simulate BACKSPACE key until the text is enpty |
3905 | + { |
3906 | + test.ViewSendString("Ubuntu"); |
3907 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubuntu", "Typed \"Ubuntu\""); |
3908 | + |
3909 | + test.ViewSendBackspace(); |
3910 | + nux::SleepForMilliseconds(500); |
3911 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubunt", "Text is \"Ubunt\""); |
3912 | + |
3913 | + test.ViewSendBackspace(); |
3914 | + nux::SleepForMilliseconds(500); |
3915 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubun", "Text is \"Ubun\""); |
3916 | + |
3917 | + test.ViewSendBackspace(); |
3918 | + nux::SleepForMilliseconds(500); |
3919 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ubu", "Text is \"Ubu\""); |
3920 | + |
3921 | + test.ViewSendBackspace(); |
3922 | + nux::SleepForMilliseconds(500); |
3923 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "Ub", "Text is \"Ub\""); |
3924 | + |
3925 | + test.ViewSendBackspace(); |
3926 | + nux::SleepForMilliseconds(500); |
3927 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "U", "Text is \"U\""); |
3928 | + |
3929 | + test.ViewSendBackspace(); |
3930 | + nux::SleepForMilliseconds(500); |
3931 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty"); |
3932 | + } |
3933 | + |
3934 | + // Type "0123456789" |
3935 | + // Move cursor to start of line |
3936 | + // Simulate DELETE key until the text is 6789 |
3937 | + // Simulate CTRL+A to select the entire text |
3938 | + // Simulate DELETE key to delete the text |
3939 | + { |
3940 | + test.ViewSendString("0123456789"); |
3941 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "0123456789", "Typed \"0123456789\""); |
3942 | + |
3943 | + test_textentry->text_entry_->MoveCursorToLineStart(); |
3944 | + |
3945 | + test.ViewSendDelete(); |
3946 | + nux::SleepForMilliseconds(500); |
3947 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "123456789", "Text is \"123456789\""); |
3948 | + |
3949 | + test.ViewSendDelete(); |
3950 | + nux::SleepForMilliseconds(500); |
3951 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "23456789", "Text is \"23456789\""); |
3952 | + |
3953 | + test.ViewSendDelete(); |
3954 | + nux::SleepForMilliseconds(500); |
3955 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "3456789", "Text is \"3456789\""); |
3956 | + |
3957 | + test.ViewSendDelete(); |
3958 | + nux::SleepForMilliseconds(500); |
3959 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "456789", "Text is \"456789\""); |
3960 | + |
3961 | + test.ViewSendDelete(); |
3962 | + nux::SleepForMilliseconds(500); |
3963 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "56789", "Text is \"56789\""); |
3964 | + |
3965 | + test.ViewSendDelete(); |
3966 | + nux::SleepForMilliseconds(500); |
3967 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "6789", "Text \"6789\""); |
3968 | + |
3969 | + |
3970 | + test.ViewSendCtrlA(); |
3971 | + nux::SleepForMilliseconds(500); |
3972 | + test.TestReportMsg(test_textentry->text_entry_->GetTextSelection() == "6789", "Selection is \"6789\""); |
3973 | + |
3974 | + |
3975 | + test.ViewSendDelete(); |
3976 | + nux::SleepForMilliseconds(500); |
3977 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is empty"); |
3978 | + } |
3979 | + |
3980 | + // Toggle IBus |
3981 | + // Type "qwerty" |
3982 | + // Simulate key '1' to select the first IM option |
3983 | + { |
3984 | + // CTRL+Space to initiate iBus |
3985 | + //test.ViewSendIBusToggle(); |
3986 | + |
3987 | + IBusBus* bus_; |
3988 | + ibus_init(); |
3989 | + bus_ = ibus_bus_new(); |
3990 | + bool active = false; |
3991 | + |
3992 | + // Test for ibus-pinyin |
3993 | + if (SetEngineActive(bus_,"pinyin")) |
3994 | + { |
3995 | + // Type random stuff |
3996 | + { |
3997 | + test.ViewSendString("qwerty"); |
3998 | + nux::SleepForMilliseconds(500); |
3999 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit"); |
4000 | + |
4001 | + test.ViewSendChar('1'); |
4002 | + nux::SleepForMilliseconds(500); |
4003 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "请问儿童有", "TextEntry is \"请问儿童有\""); |
4004 | + |
4005 | + test.ViewSendCtrlA(); |
4006 | + nux::SleepForMilliseconds(500); |
4007 | + |
4008 | + test.ViewSendDelete(); |
4009 | + nux::SleepForMilliseconds(500); |
4010 | + } |
4011 | + |
4012 | + // Testing Cursor in the preedit window |
4013 | + { |
4014 | + test.ViewSendString("ming"); |
4015 | + nux::SleepForMilliseconds(500); |
4016 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit"); |
4017 | + |
4018 | + test.ViewSendLeft(); |
4019 | + nux::SleepForMilliseconds(500); |
4020 | + |
4021 | + test.ViewSendDelete(); |
4022 | + nux::SleepForMilliseconds(500); |
4023 | + |
4024 | + test.ViewSendChar('1'); |
4025 | + nux::SleepForMilliseconds(500); |
4026 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "民", "TextEntry is \"民\""); |
4027 | + |
4028 | + test.ViewSendCtrlA(); |
4029 | + nux::SleepForMilliseconds(500); |
4030 | + |
4031 | + test.ViewSendDelete(); |
4032 | + nux::SleepForMilliseconds(500); |
4033 | + } |
4034 | + |
4035 | + active = true; |
4036 | + } |
4037 | + else |
4038 | + { |
4039 | + test.TestReportMsg(false,"Pinyin is NOT an active input method" ); |
4040 | + } |
4041 | + |
4042 | + // Test for ibus-hangul |
4043 | + if (SetEngineActive(bus_,"hangul")) |
4044 | + { |
4045 | + // Test for the the space in ibus-hangul working correctlly |
4046 | + { |
4047 | + test.ViewSendString("asd abc "); |
4048 | + nux::SleepForMilliseconds(500); |
4049 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "ㅁㄴㅇ 뮻 ", "TextEntry is \"ㅁㄴㅇ 뮻 \""); |
4050 | + |
4051 | + test.ViewSendCtrlA(); |
4052 | + nux::SleepForMilliseconds(500); |
4053 | + |
4054 | + test.ViewSendDelete(); |
4055 | + nux::SleepForMilliseconds(500); |
4056 | + } |
4057 | + |
4058 | + active = true; |
4059 | + } |
4060 | + else |
4061 | + { |
4062 | + test.TestReportMsg(false,"Hangul is NOT an active input method" ); |
4063 | + } |
4064 | + |
4065 | + // Checking for ibus-anthy - Japanese |
4066 | + if (SetEngineActive(bus_,"anthy")) |
4067 | + { |
4068 | + { |
4069 | + test.ViewSendString("shisutemu "); |
4070 | + nux::SleepForMilliseconds(500); |
4071 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "", "TextEntry is only Preedit"); |
4072 | + |
4073 | + // Ctrl + J commits for ibus-anthy |
4074 | + test.ViewSendKeyCombo(XK_Control_L, 0, 0, 'j'); |
4075 | + nux::SleepForMilliseconds(500); |
4076 | + test.TestReportMsg(test_textentry->text_entry_->GetText() == "システム", "TextEntry is \"システム\""); |
4077 | + |
4078 | + test.ViewSendCtrlA(); |
4079 | + nux::SleepForMilliseconds(500); |
4080 | + |
4081 | + test.ViewSendDelete(); |
4082 | + nux::SleepForMilliseconds(500); |
4083 | + } |
4084 | + |
4085 | + active = true; |
4086 | + } |
4087 | + else |
4088 | + { |
4089 | + test.TestReportMsg(false,"Anthy is NOT an active input method" ); |
4090 | + } |
4091 | + |
4092 | + g_object_unref (bus_); |
4093 | + |
4094 | + // CTRL+Space to deactivate iBus |
4095 | + if (active) |
4096 | + test.ViewSendIBusToggle(); |
4097 | + } |
4098 | + |
4099 | + if (test.WhenDoneTerminateProgram()) |
4100 | + { |
4101 | + wnd_thread->ExitMainLoop(); |
4102 | + } |
4103 | + nuxDebugMsg("Exit testing thread"); |
4104 | +} |
4105 | + |
4106 | +int main(int argc, char** argv) |
4107 | +{ |
4108 | + int xstatus = XInitThreads(); |
4109 | + nuxAssertMsg(xstatus > 0, "XInitThreads has failed"); |
4110 | + |
4111 | + test_textentry = new TextTextEntry("Text Entry", 600, 200, 40000); |
4112 | + test_textentry->Startup(); |
4113 | + test_textentry->UserInterfaceSetup(); |
4114 | + |
4115 | + nux::SystemThread* test_thread = nux::CreateSystemThread(NULL, &TestingThread, test_textentry->GetWindowThread()); |
4116 | + |
4117 | + test_thread->Start(test_textentry); |
4118 | + |
4119 | + test_textentry->Run(); |
4120 | + |
4121 | + delete test_thread; |
4122 | + delete test_textentry; |
4123 | + |
4124 | + return 0; |
4125 | +} |
4126 | + |
Excellent work!