Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1032)

Side by Side Diff: ppapi/examples/ime/ime.cc

Issue 7621010: Never submit: tentative Pepper IME. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: snapshot Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6 #include <cstdlib>
7 #include <sstream>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "ppapi/c/dev/ppb_console_dev.h"
13 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
14 #include "ppapi/cpp/completion_callback.h"
15 #include "ppapi/cpp/dev/font_dev.h"
16 #include "ppapi/cpp/dev/ime_control_dev.h"
17 #include "ppapi/cpp/graphics_2d.h"
18 #include "ppapi/cpp/image_data.h"
19 #include "ppapi/cpp/input_event.h"
20 #include "ppapi/cpp/instance.h"
21 #include "ppapi/cpp/module.h"
22 #include "ppapi/cpp/rect.h"
23 #include "ppapi/cpp/size.h"
24
25 namespace {
26
27 // Note: ARGB
28 static const uint32_t EDITBOX_BG_COLOR = 0xffffffff;
29 static const uint32_t EDITBOX_TEXT_COLOR = 0xff000000;
30 static const uint32_t EDITBOX_CARET_COLOR = 0xff000000;
31 static const uint32_t EDITBOX_PREEDIT_TEXT_COLOR = 0xffff0000;
32 static const uint32_t EDITBOX_UNDERLINE_COLOR_MAIN = 0xff00ff00;
33 static const uint32_t EDITBOX_UNDERLINE_COLOR_SUB = 0xff0000ff;
34
35 void FillRect(pp::ImageData* image,
36 int left, int top, int width, int height,
37 uint32_t color) {
38 for (int y = std::max(0, top);
39 y < std::min(image->size().height() - 1, top + height);
40 ++y) {
41 for (int x = std::max(0, left);
42 x < std::min(image->size().width() - 1, left + width);
43 ++x)
44 *image->GetAddr32(pp::Point(x, y)) = color;
45 }
46 }
47
48 void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
49 FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
50 }
51
52 // TODO(kinaba): We might use ICU macro here.
53 size_t utf8_prev(const std::string& str, size_t i) {
54 if (i > 0) {
55 do
56 --i;
57 while ((str[i] & 0xC0) == 0x80 && i > 0);
58 }
59 return i;
60 }
61
62 // TODO(kinaba): We might use ICU macro here.
63 size_t utf8_next(const std::string& str, size_t i) {
64 if (i < str.size()) {
65 do
66 ++i;
67 while ((str[i] & 0xC0) == 0x80 && i < str.size());
68 }
69 return i;
70 }
71
72 } // namespace
73
74 class MyInstance : public pp::Instance {
75 public:
76 explicit MyInstance(PP_Instance instance)
77 : pp::Instance(instance),
78 imecontrol_(this),
79 ime_unaware_mode_(false) {
80 }
81
82 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
83 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
84 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
85
86 edit_.push_back(MyEditBox(this, 10, 10, 300, 20, false));
87 edit_.back().set_text("Hello");
88 edit_.push_back(MyEditBox(this, 30, 100, 300, 20, true));
89 edit_.back().set_text("World");
90
91 for (uint32_t i=0; i<argc; ++i)
92 if (argn[i] == std::string("imeunaware")) {
93 ime_unaware_mode_ = true;
94 return true;
95 } else if (argn[i] == std::string("noime")) {
96 ime_unaware_mode_ = true;
97 imecontrol_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
98 return true;
99 }
100
101 for (uint32_t i=0; i<argc; ++i)
102 if (argn[i] == std::string("offthespot")) {
103 return true;
104 }
105 RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
106 return true;
107 }
108
109 // For debugging purpose.
110 void Log(const pp::Var& value) {
111 const PPB_Console_Dev* console = reinterpret_cast<const PPB_Console_Dev*>(
112 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_DEV_INTERFACE));
113 if (!console)
114 return;
115 console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
116 }
117
118 void focusIn(const pp::Rect& textarea,
119 const pp::Rect& textfield,
120 bool onthespot) {
kochi 2011/09/09 07:58:59 onthespot param not used.
121 if (ime_unaware_mode_)
122 return;
123 imecontrol_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT);
124 imecontrol_.UpdateCaretPosition(textarea, textfield);
125 }
126
127 void focusOut() {
128 if (ime_unaware_mode_)
129 return;
130 imecontrol_.CancelCompositionText();
131 imecontrol_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
132 }
133
134 protected:
135 // Input events
136 virtual bool HandleInputEvent(const pp::InputEvent& event) {
137 bool ret = false;
138 switch (event.GetType()) {
139 case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
140 const pp::MouseInputEvent mouseEvent(event);
141 ret = onMouseDown(mouseEvent);
142 break;
143 }
144 case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
145 const pp::MouseInputEvent mouseEvent(event);
146 ret = onMouseMove(mouseEvent);
147 break;
148 }
149 case PP_INPUTEVENT_TYPE_KEYDOWN: {
150 Log("Keydown");
151 const pp::KeyboardInputEvent keyEvent(event);
152 ret = onKeyDown(keyEvent);
153 break;
154 }
155 case PP_INPUTEVENT_TYPE_CHAR: {
156 const pp::KeyboardInputEvent keyEvent(event);
157 Log("Char ["+keyEvent.GetCharacterText().AsString()+"]");
158 ret = onChar(keyEvent);
159 break;
160 }
161 case PP_INPUTEVENT_TYPE_COMPOSITION_START: {
162 const pp::CompositionInputEvent compositionEvent(event);
163 Log("CompositionStart ["+compositionEvent.GetText().AsString()+"]");
164 ret = true;
165 break;
166 }
167 case PP_INPUTEVENT_TYPE_COMPOSITION_UPDATE: {
168 const pp::CompositionInputEvent compositionEvent(event);
169 Log("CompositionUpdate ["+compositionEvent.GetText().AsString()+"]");
170 ret = onCompositionUpdate(compositionEvent);
171 break;
172 }
173 case PP_INPUTEVENT_TYPE_COMPOSITION_END: {
174 const pp::CompositionInputEvent compositionEvent(event);
175 Log("CompositionEnd ["+compositionEvent.GetText().AsString()+"]");
176 ret = onCompositionEnd(compositionEvent);
177 break;
178 }
179 case PP_INPUTEVENT_TYPE_IME_TEXT: {
180 const pp::CompositionInputEvent compositionEvent(event);
181 Log("ImeText ["+compositionEvent.GetText().AsString()+"]");
182 ret = onImeText(compositionEvent);
183 break;
184 }
185 default:
186 break;
187 }
188 if (ret && event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE)
189 Paint();
190 return ret;
191 }
192
193 bool onCompositionUpdate(const pp::CompositionInputEvent& ev) {
194 for (std::vector<MyEditBox>::iterator it = edit_.begin();
195 it != edit_.end();
196 ++it) {
197 if (it->focused()) {
198 it->setComposition(ev.GetText().AsString(),
199 ev.GetSegments(),
200 ev.GetTargetSegment());
201 return true;
202 }
203 }
204 return false;
205 }
206
207 bool onCompositionEnd(const pp::CompositionInputEvent& ev) {
208 for (std::vector<MyEditBox>::iterator it = edit_.begin();
209 it != edit_.end();
210 ++it) {
211 if (it->focused()) {
212 it->setComposition("", std::vector<uint32_t>(), 0);
213 return true;
214 }
215 }
216 return false;
217 }
218
219 bool onMouseDown(const pp::MouseInputEvent& ev) {
220 bool anyoneFocused = false;
221 for (std::vector<MyEditBox>::iterator it = edit_.begin();
222 it != edit_.end();
223 ++it) {
224 if (it->refocusByMouseClick(ev.GetPosition().x(),
225 ev.GetPosition().y())) {
226 anyoneFocused = true;
227 }
228 }
229 if (!anyoneFocused)
230 focusOut();
231 return true;
232 }
233
234 bool onMouseMove(const pp::MouseInputEvent& ev) {
235 const PPB_CursorControl_Dev* cursor_control =
236 reinterpret_cast<const PPB_CursorControl_Dev*>(
237 pp::Module::Get()->GetBrowserInterface(
238 PPB_CURSOR_CONTROL_DEV_INTERFACE));
239 if (!cursor_control)
240 return false;
241
242 for (std::vector<MyEditBox>::iterator it = edit_.begin();
243 it != edit_.end();
244 ++it) {
245 if (it->contains(ev.GetPosition().x(),
246 ev.GetPosition().y())) {
247 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
248 0, NULL);
249 return true;
250 }
251 }
252 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
253 0, NULL);
254 return true;
255 }
256
257 bool onKeyDown(const pp::KeyboardInputEvent& ev) {
258 for (std::vector<MyEditBox>::iterator it = edit_.begin();
259 it != edit_.end();
260 ++it) {
261 if (it->focused()) {
262 switch (ev.GetKeyCode()) {
263 case 0x25: // VK_LEFT (TODO(kinaba): find an appropriate header)
264 it->keyLeft();
265 break;
266 case 0x27: // VK_RIGHT
267 it->keyRight();
268 break;
269 case 0x2E: // VK_DELETE
270 it->keyDelete();
271 break;
272 case 0x08: // VK_BACK
273 it->keyBackspace();
274 break;
275 }
276 return true;
277 }
278 }
279 return false;
280 }
281
282 bool onChar(const pp::KeyboardInputEvent& ev) {
283 for (std::vector<MyEditBox>::iterator it = edit_.begin();
284 it != edit_.end();
285 ++it) {
286 if (it->focused()) {
287 it->insert_text(ev.GetCharacterText().AsString());
288 return true;
289 }
290 }
291 return false;
292 }
293
294 bool onImeText(const pp::CompositionInputEvent& ev) {
295 for (std::vector<MyEditBox>::iterator it = edit_.begin();
296 it != edit_.end();
297 ++it) {
298 if (it->focused()) {
299 it->insert_text(ev.GetText().AsString());
300 return true;
301 }
302 }
303 return false;
304 }
305
306 // Painting
307 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
308 if (position.size() == last_size_)
309 return;
310 last_size_ = position.size();
311 Paint();
312 }
313
314 void Paint() {
315 pp::Rect clip(0, 0, last_size_.height(), last_size_.width());
316 PaintClip(clip);
317 }
318
319 void PaintClip(const pp::Rect& clip) {
320 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_,
321 true);
322 pp::Graphics2D device(this, last_size_, false);
323 BindGraphics(device);
324
325 for (std::vector<MyEditBox>::iterator it = edit_.begin();
326 it != edit_.end();
327 ++it) {
328 it->PaintOn(&image, clip);
329 }
330
331 device.PaintImageData(image, pp::Point(0, 0));
332 device.Flush(pp::CompletionCallback(&OnFlush, this));
333 }
334
335 static void OnFlush(void* user_data, int32_t result) {
336 }
337
338 private:
339 // Testing purpose EditBox.
340 class MyEditBox {
341 public:
342 MyEditBox(MyInstance* instance, int x, int y, int width, int height,
343 bool onthespot)
344 : instance_(instance),
345 area_(x, y, width, height),
346 font_size_(height-2),
347 cursor_pos_(std::string::npos),
348 onthespot_(onthespot) {
349 }
350
351 // Paint editbox image on the specified ImageData.
352 void PaintOn(pp::ImageData* image, pp::Rect clip) {
353 clip = clip.Intersect(area_);
354
355 pp::FontDescription_Dev desc;
356 desc.set_family(PP_FONTFAMILY_SANSSERIF);
357 desc.set_size(font_size_);
358 pp::Font_Dev font(instance_, desc);
359
360 FillRect(image, clip, EDITBOX_BG_COLOR);
361
362 if (cursor_pos_ != std::string::npos) {
363 int offset = area_.x();
364 // before cursor
365 {
366 std::string str = utf8_text_.substr(0, cursor_pos_);
367 font.DrawTextAt(
368 image,
369 pp::TextRun_Dev(str.c_str(), false, false),
370 pp::Point(offset, area_.y()+font_size_),
371 EDITBOX_TEXT_COLOR,
372 clip,
373 false);
374 offset += font.MeasureText(pp::TextRun_Dev(str.c_str()));
375 }
376 // composition
377 {
378 const std::string& str = composition_;
379 font.DrawTextAt(
380 image,
381 pp::TextRun_Dev(str.c_str(), false, false),
382 pp::Point(offset, area_.y()+font_size_),
383 EDITBOX_PREEDIT_TEXT_COLOR,
384 clip,
385 false);
386 for (size_t i=0; i<segments_.size(); ++i) {
387 size_t l = segments_[i].first;
388 size_t r = segments_[i].second;
389 if (l != r) {
390 int lx = font.MeasureText(pp::TextRun_Dev(str.substr(0,l).c_str()) );
391 int rx = font.MeasureText(pp::TextRun_Dev(str.substr(0,r).c_str()) );
kochi 2011/09/09 07:58:59 80 column
392 FillRect(image,
393 offset+lx+2, area_.y()+font_size_+1, (rx-lx)-4, 2,
394 i==targetSegment_ ?
395 EDITBOX_UNDERLINE_COLOR_MAIN :
396 EDITBOX_UNDERLINE_COLOR_SUB );
397 }
398 }
399 offset += font.MeasureText(pp::TextRun_Dev(str.c_str()));
400 }
401 // caret
402 FillRect(image, pp::Rect(offset, area_.y(), 2,
403 area_.height()), EDITBOX_CARET_COLOR);
404 // after cursor
405 {
406 std::string str = utf8_text_.substr(cursor_pos_);
407 font.DrawTextAt(
408 image,
409 pp::TextRun_Dev(str.c_str(), false, false),
410 pp::Point(offset, area_.y()+font_size_),
411 EDITBOX_TEXT_COLOR,
412 clip,
413 false);
414 offset += font.MeasureText(pp::TextRun_Dev(str.c_str()));
415 }
416 } else {
417 font.DrawTextAt(image,
418 pp::TextRun_Dev(utf8_text_.c_str(), false, false),
419 pp::Point(area_.x(), area_.y()+font_size_), EDITBOX_TEXT_COLOR,
420 clip, false);
421 }
422 }
423
424 void setComposition(const std::string& text,
425 const std::vector<uint32_t>& segments,
426 uint32_t target_segment) {
427 composition_ = text;
428
429 segments_.clear();
430 for (size_t i=0; i+1<segments.size(); ++i)
431 segments_.push_back(std::make_pair(segments[i], segments[i+1]));
432 targetSegment_ = target_segment;
433 cursorPosChanged();
434 }
435
436 // Am I focused?
437 bool focused() const {
438 // "<=" is not a typo of "<". we can focus on the tail of the text.
439 return (cursor_pos_ != std::string::npos &&
440 cursor_pos_ <= utf8_text_.size());
441 }
442
443 // Does the coordinate (x,y) is contained inside the edit box?
444 bool contains(int x, int y) {
445 return area_.Contains(x, y);
446 }
447
448 // reset the content text
449 void set_text(const std::string& text) {
450 utf8_text_ = text;
451 if (focused()) {
452 cursor_pos_ = text.size();
453 cursorPosChanged();
454 }
455 }
456
457 // insert the text at the cursor position
458 void insert_text(const std::string& text) {
459 if (!focused())
460 return;
461 utf8_text_.insert(cursor_pos_, text);
462 if (focused()) {
463 cursor_pos_ += text.size();
464 cursorPosChanged();
465 }
466 }
467
468 // event handling
469 bool refocusByMouseClick(int x, int y) {
470 if (!contains(x, y)) {
471 // unfocus
472 cursor_pos_ = std::string::npos;
473 return false;
474 }
475
476 // focus
477 pp::FontDescription_Dev desc;
478 desc.set_family(PP_FONTFAMILY_SANSSERIF);
479 desc.set_size(font_size_);
480 pp::Font_Dev font(instance_, desc);
481
482 size_t i = font.CharacterOffsetForPixel(
483 pp::TextRun_Dev(utf8_text_.c_str()), x-area_.x());
484
485 // TODO(kinaba) there must be a function to do this....
486 cursor_pos_ = 0;
487 while (i-- > 0)
488 cursor_pos_ = utf8_next(utf8_text_, cursor_pos_);
489 cursorPosChanged();
490 return true;
491 }
492
493 void keyLeft() {
494 if (!focused())
495 return;
496 cursor_pos_ = utf8_prev(utf8_text_, cursor_pos_);
497 cursorPosChanged();
498 }
499
500 void keyRight() {
501 if (!focused())
502 return;
503 cursor_pos_ = utf8_next(utf8_text_, cursor_pos_);
504 cursorPosChanged();
505 }
506
507 void keyDelete() {
508 if (!focused())
509 return;
510 size_t i = utf8_next(utf8_text_, cursor_pos_);
511 utf8_text_.erase(cursor_pos_, i-cursor_pos_);
512 cursorPosChanged();
513 }
514
515 void keyBackspace() {
516 if (!focused() || cursor_pos_ == 0)
517 return;
518 keyLeft();
519 keyDelete();
520 }
521
522 private:
523 void Log(const pp::Var& value) {
524 instance_->Log(value);
525 }
526
527 // Notify the plugin instance that the cursor position has changed.
528 void cursorPosChanged() {
529 if (focused()) {
530 pp::FontDescription_Dev desc;
531 desc.set_family(PP_FONTFAMILY_SANSSERIF);
532 desc.set_size(font_size_);
533 pp::Font_Dev font(instance_, desc);
534
535 int px = font.MeasureText(
536 pp::TextRun_Dev(utf8_text_.substr(0, cursor_pos_).c_str()));
537 pp::Rect textarea(area_.x()+px, area_.y(), 2, area_.height()+2);
538 instance_->focusIn(textarea, area_, onthespot_);
539 }
540 }
541
542 MyInstance* instance_;
543 pp::Rect area_;
544 int font_size_;
545 std::string utf8_text_;
546 size_t cursor_pos_;
547 bool onthespot_;
548 std::string composition_;
549 std::vector< std::pair<uint32_t,uint32_t> > segments_;
550 size_t targetSegment_;
551 };
552
553 // IME Control interface.
554 pp::IMEControl_Dev imecontrol_;
555 bool ime_unaware_mode_;
556 // Remembers the size of this instance.
557 pp::Size last_size_;
558 // Holds instances of edit box.
559 std::vector<MyEditBox> edit_;
560 };
561
562 // boilerplate code
563 class MyModule : public pp::Module {
564 virtual pp::Instance* CreateInstance(PP_Instance instance) {
565 return new MyInstance(instance);
566 }
567 };
568
569 namespace pp {
570 Module* CreateModule() {
571 return new MyModule();
572 }
573
574 } // namespace pp
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698