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

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
« no previous file with comments | « ppapi/cpp/input_event.cc ('k') | ppapi/examples/ime/ime.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 <string>
6 #include <utility>
7 #include <vector>
8
9 #include "base/scoped_ptr.h"
10 #include "ppapi/c/dev/ppb_console_dev.h"
11 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
12 #include "ppapi/cpp/completion_callback.h"
13 #include "ppapi/cpp/dev/font_dev.h"
14 #include "ppapi/cpp/dev/text_input_dev.h"
15 #include "ppapi/cpp/graphics_2d.h"
16 #include "ppapi/cpp/image_data.h"
17 #include "ppapi/cpp/input_event.h"
18 #include "ppapi/cpp/instance.h"
19 #include "ppapi/cpp/module.h"
20 #include "ppapi/cpp/rect.h"
21 #include "ppapi/cpp/size.h"
22 #include "ui/base/keycodes/keyboard_codes.h"
23
24 namespace {
25
26 static const uint32_t TEXTFIELD_BG_COLOR = 0xffffffff;
27 static const uint32_t TEXTFIELD_TEXT_COLOR = 0xff000000;
28 static const uint32_t TEXTFIELD_CARET_COLOR = 0xff000000;
29 static const uint32_t TEXTFIELD_PREEDIT_TEXT_COLOR = 0xffff0000;
30 static const uint32_t TEXTFIELD_UNDERLINE_COLOR_MAIN = 0xffff0000;
31 static const uint32_t TEXTFIELD_UNDERLINE_COLOR_SUB = 0xffddaaaa;
32
33 void FillRect(pp::ImageData* image,
34 int left, int top, int width, int height,
35 uint32_t color) {
36 for (int y = std::max(0, top);
37 y < std::min(image->size().height() - 1, top + height);
38 ++y) {
39 for (int x = std::max(0, left);
40 x < std::min(image->size().width() - 1, left + width);
41 ++x)
42 *image->GetAddr32(pp::Point(x, y)) = color;
43 }
44 }
45
46 void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
47 FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
48 }
49
50 size_t utf8_prev(const std::string& str, size_t i) {
51 if (i > 0) {
52 do
53 --i;
54 while ((str[i] & 0xC0) == 0x80 && i > 0);
55 }
56 return i;
57 }
58
59 size_t utf8_next(const std::string& str, size_t i, size_t n) {
60 for (size_t step = 0; step < n; ++step) {
61 if (i < str.size()) {
62 do
63 ++i;
64 while ((str[i] & 0xC0) == 0x80 && i < str.size());
65 }
66 }
67 return i;
68 }
69
70 } // namespace
71
72 class TextFieldStatusHandler {
73 public:
74 virtual ~TextFieldStatusHandler() {}
75 virtual void focusIn(const pp::Rect& caret, const pp::Rect& boundingBox) {}
76 virtual void focusOut() {}
77 };
78
79 class TextFieldStatusNotifyingHanlder : public TextFieldStatusHandler {
80 public:
81 explicit TextFieldStatusNotifyingHanlder(pp::Instance* instance)
82 : instance_(instance),
83 textinput_control_(instance) {}
84
85 protected:
86 virtual void focusIn(const pp::Rect& caret, const pp::Rect& boundingBox) {
87 textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT);
88 textinput_control_.UpdateCaretPosition(caret, boundingBox);
89 }
90 virtual void focusOut() {
91 textinput_control_.CancelCompositionText();
92 textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
93 }
94
95 private:
96 pp::Instance* instance_;
97 pp::TextInput_Dev textinput_control_;
98 };
99
100 // Hand-made text field for demonstrating text input API.
101 class MyTextField {
102 public:
103 MyTextField(pp::Instance* instance, TextFieldStatusHandler* handler,
104 int x, int y, int width, int height)
105 : instance_(instance),
106 status_handler_(handler),
107 area_(x, y, width, height),
108 font_size_(height-2),
109 caret_pos_(std::string::npos) {
110 pp::FontDescription_Dev desc;
111 desc.set_family(PP_FONTFAMILY_SANSSERIF);
112 desc.set_size(font_size_);
113 font_ = pp::Font_Dev(instance_, desc);
114 }
115
116 // Paint on the specified ImageData.
117 void PaintOn(pp::ImageData* image, pp::Rect clip) {
118 clip = clip.Intersect(area_);
119 FillRect(image, clip, TEXTFIELD_BG_COLOR);
120
121 if (caret_pos_ != std::string::npos) {
122 int offset = area_.x();
123 // before caret
124 {
125 std::string str = utf8_text_.substr(0, caret_pos_);
126 font_.DrawTextAt(
127 image,
128 pp::TextRun_Dev(str.c_str(), false, false),
129 pp::Point(offset, area_.y()+font_size_),
130 TEXTFIELD_TEXT_COLOR,
131 clip,
132 false);
133 offset += font_.MeasureSimpleText(str);
134 }
135 // composition
136 {
137 const std::string& str = composition_;
138 font_.DrawTextAt(
139 image,
140 pp::TextRun_Dev(str.c_str(), false, false),
141 pp::Point(offset, area_.y()+font_size_),
142 TEXTFIELD_PREEDIT_TEXT_COLOR,
143 clip,
144 false);
145 for (size_t i = 0; i < segments_.size(); ++i) {
146 size_t l = segments_[i].first;
147 size_t r = segments_[i].second;
148 if (l != r) {
149 int lx = font_.MeasureSimpleText(str.substr(0, l));
150 int rx = font_.MeasureSimpleText(str.substr(0, r));
151 FillRect(image,
152 offset+lx+2, area_.y()+font_size_+1, (rx-lx)-4, 2,
153 i == static_cast<size_t>(targetSegment_) ?
154 TEXTFIELD_UNDERLINE_COLOR_MAIN :
155 TEXTFIELD_UNDERLINE_COLOR_SUB);
156 }
157 }
158 // caret
159 int caretx = font_.MeasureSimpleText(str.substr(0, selection_.first));
160 FillRect(image,
161 pp::Rect(offset+caretx, area_.y(), 2, area_.height()),
162 TEXTFIELD_CARET_COLOR);
163 offset += font_.MeasureSimpleText(str);
164 }
165 // after caret
166 {
167 std::string str = utf8_text_.substr(caret_pos_);
168 font_.DrawTextAt(
169 image,
170 pp::TextRun_Dev(str.c_str(), false, false),
171 pp::Point(offset, area_.y()+font_size_),
172 TEXTFIELD_TEXT_COLOR,
173 clip,
174 false);
175 }
176 } else {
177 font_.DrawTextAt(
178 image,
179 pp::TextRun_Dev(utf8_text_.c_str(), false, false),
180 pp::Point(area_.x(), area_.y()+font_size_),
181 TEXTFIELD_TEXT_COLOR,
182 clip,
183 false);
184 }
185 }
186
187 // Update current composition text.
188 void setComposition(
189 const std::string& text,
190 const std::vector< std::pair<uint32_t, uint32_t> >& segments,
191 int32_t target_segment,
192 const std::pair<uint32_t, uint32_t>& selection) {
193 composition_ = text;
194 segments_ = segments;
195 targetSegment_ = target_segment;
196 selection_ = selection;
197 caretPosChanged();
198 }
199
200 // Am I focused?
201 bool focused() const {
202 return caret_pos_ != std::string::npos;
203 }
204
205 // Does the coordinate (x,y) is contained inside the edit box?
206 bool contains(int x, int y) const {
207 return area_.Contains(x, y);
208 }
209
210 // reset the content text
211 void set_text(const std::string& text) {
212 utf8_text_ = text;
213 if (focused()) {
214 caret_pos_ = text.size();
215 caretPosChanged();
216 }
217 }
218
219 // insert the text at the caret position
220 void insert_text(const std::string& text) {
221 if (!focused())
222 return;
223 utf8_text_.insert(caret_pos_, text);
224 if (focused()) {
225 caret_pos_ += text.size();
226 caretPosChanged();
227 }
228 }
229
230 // event handling
231 bool refocusByMouseClick(int x, int y) {
232 if (!contains(x, y)) {
233 // unfocus
234 caret_pos_ = std::string::npos;
235 return false;
236 }
237
238 // focus
239 size_t n = font_.CharacterOffsetForPixel(
240 pp::TextRun_Dev(utf8_text_.c_str()), x-area_.x());
241 caret_pos_ = utf8_next(utf8_text_, 0, n);
242 caretPosChanged();
243 return true;
244 }
245
246 void keyLeft() {
247 if (!focused())
248 return;
249 caret_pos_ = utf8_prev(utf8_text_, caret_pos_);
250 caretPosChanged();
251 }
252
253 void keyRight() {
254 if (!focused())
255 return;
256 caret_pos_ = utf8_next(utf8_text_, caret_pos_, 1);
257 caretPosChanged();
258 }
259
260 void keyDelete() {
261 if (!focused())
262 return;
263 size_t i = utf8_next(utf8_text_, caret_pos_, 1);
264 utf8_text_.erase(caret_pos_, i-caret_pos_);
265 caretPosChanged();
266 }
267
268 void keyBackspace() {
269 if (!focused() || caret_pos_ == 0)
270 return;
271 keyLeft();
272 keyDelete();
273 }
274
275 private:
276 // Notify the plugin instance that the caret position has changed.
277 void caretPosChanged() {
278 if (focused()) {
279 std::string str = utf8_text_.substr(0, caret_pos_);
280 if (!composition_.empty())
281 str += composition_.substr(0, selection_.first);
282 int px = font_.MeasureSimpleText(str);
283 pp::Rect textarea(area_.x()+px, area_.y(), 2, area_.height()+2);
284 status_handler_->focusIn(textarea, area_);
285 }
286 }
287
288 pp::Instance* instance_;
289 TextFieldStatusHandler* status_handler_;
290
291 pp::Rect area_;
292 int font_size_;
293 pp::Font_Dev font_;
294 std::string utf8_text_;
295 size_t caret_pos_;
296 std::string composition_;
297 std::vector< std::pair<uint32_t, uint32_t> > segments_;
298 std::pair<uint32_t, uint32_t> selection_;
299 int targetSegment_;
300 };
301
302 class MyInstance : public pp::Instance {
303 public:
304 explicit MyInstance(PP_Instance instance)
305 : pp::Instance(instance),
306 status_handler_(new TextFieldStatusHandler) {
307 }
308
309 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
310 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
311 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
312
313 for (uint32_t i = 0; i < argc; ++i) {
314 if (argn[i] == std::string("ime")) {
315 if (argv[i] == std::string("no")) {
316 // Example of NO-IME plugins (e.g., games).
317 // Explicitly turns off the text input feature, and
318 // Never call any text input related APIs.
319 pp::TextInput_Dev(this).SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
320 } else if (argv[i] == std::string("unaware")) {
321 // Demonstrating the behavior of IME-unaware plugins.
322 // Never call any text input related APIs.
323 } else if (argv[i] == std::string("caretmove")) {
324 // Demonstrating the behavior of plugins with limited IME support.
325 // It notifies updates of caret positions to the browser,
326 // but unable to handle inline compositions.
327 status_handler_.reset(new TextFieldStatusNotifyingHanlder(this));
328 } else if (argv[i] == std::string("full")) {
329 // Demonstrating the behavior of plugins fully supporting IME.
330 // It notifies updates of caret positions to the browser,
331 // but handles all text input events by itself.
332 status_handler_.reset(new TextFieldStatusNotifyingHanlder(this));
333 RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
334 }
335 break;
336 }
337 }
338
339 textfield_.push_back(MyTextField(this, status_handler_.get(),
340 10, 10, 300, 20));
341 textfield_.back().set_text("Hello");
342 textfield_.push_back(MyTextField(this, status_handler_.get(),
343 30, 100, 300, 20));
344 textfield_.back().set_text("World");
345 return true;
346 }
347
348 protected:
349 virtual bool HandleInputEvent(const pp::InputEvent& event) {
350 bool ret = false;
351 switch (event.GetType()) {
352 case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
353 const pp::MouseInputEvent mouseEvent(event);
354 ret = onMouseDown(mouseEvent);
355 break;
356 }
357 case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
358 const pp::MouseInputEvent mouseEvent(event);
359 ret = onMouseMove(mouseEvent);
360 break;
361 }
362 case PP_INPUTEVENT_TYPE_KEYDOWN: {
363 Log("Keydown");
364 const pp::KeyboardInputEvent keyEvent(event);
365 ret = onKeyDown(keyEvent);
366 break;
367 }
368 case PP_INPUTEVENT_TYPE_CHAR: {
369 const pp::KeyboardInputEvent keyEvent(event);
370 Log("Char ["+keyEvent.GetCharacterText().AsString()+"]");
371 ret = onChar(keyEvent);
372 break;
373 }
374 case PP_INPUTEVENT_TYPE_COMPOSITION_START: {
375 const pp::CompositionInputEvent compositionEvent(event);
376 Log("CompositionStart ["+compositionEvent.GetText().AsString()+"]");
377 ret = true;
378 break;
379 }
380 case PP_INPUTEVENT_TYPE_COMPOSITION_UPDATE: {
381 const pp::CompositionInputEvent compositionEvent(event);
382 Log("CompositionUpdate ["+compositionEvent.GetText().AsString()+"]");
383 ret = onCompositionUpdate(compositionEvent);
384 break;
385 }
386 case PP_INPUTEVENT_TYPE_COMPOSITION_END: {
387 const pp::CompositionInputEvent compositionEvent(event);
388 Log("CompositionEnd ["+compositionEvent.GetText().AsString()+"]");
389 ret = onCompositionEnd(compositionEvent);
390 break;
391 }
392 case PP_INPUTEVENT_TYPE_IME_TEXT: {
393 const pp::CompositionInputEvent compositionEvent(event);
394 Log("ImeText ["+compositionEvent.GetText().AsString()+"]");
395 ret = onImeText(compositionEvent);
396 break;
397 }
398 default:
399 break;
400 }
401 if (ret && event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE)
402 Paint();
403 return ret;
404 }
405
406 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
407 if (position.size() == last_size_)
408 return;
409 last_size_ = position.size();
410 Paint();
411 }
412
413 private:
414 bool onCompositionUpdate(const pp::CompositionInputEvent& ev) {
415 for (std::vector<MyTextField>::iterator it = textfield_.begin();
416 it != textfield_.end();
417 ++it) {
418 if (it->focused()) {
419 it->setComposition(ev.GetText().AsString(),
420 ev.GetSegments(),
421 ev.GetTargetSegment(),
422 ev.GetSelection());
423 return true;
424 }
425 }
426 return false;
427 }
428
429 bool onCompositionEnd(const pp::CompositionInputEvent& ev) {
430 for (std::vector<MyTextField>::iterator it = textfield_.begin();
431 it != textfield_.end();
432 ++it) {
433 if (it->focused()) {
434 it->setComposition("", std::vector< std::pair<uint32_t, uint32_t> >(),
435 0, std::make_pair(0, 0));
436 return true;
437 }
438 }
439 return false;
440 }
441
442 bool onMouseDown(const pp::MouseInputEvent& ev) {
443 bool anyoneFocused = false;
444 for (std::vector<MyTextField>::iterator it = textfield_.begin();
445 it != textfield_.end();
446 ++it) {
447 if (it->refocusByMouseClick(ev.GetPosition().x(),
448 ev.GetPosition().y())) {
449 anyoneFocused = true;
450 }
451 }
452 if (!anyoneFocused)
453 status_handler_->focusOut();
454 return true;
455 }
456
457 bool onMouseMove(const pp::MouseInputEvent& ev) {
458 const PPB_CursorControl_Dev* cursor_control =
459 reinterpret_cast<const PPB_CursorControl_Dev*>(
460 pp::Module::Get()->GetBrowserInterface(
461 PPB_CURSOR_CONTROL_DEV_INTERFACE));
462 if (!cursor_control)
463 return false;
464
465 for (std::vector<MyTextField>::iterator it = textfield_.begin();
466 it != textfield_.end();
467 ++it) {
468 if (it->contains(ev.GetPosition().x(),
469 ev.GetPosition().y())) {
470 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
471 0, NULL);
472 return true;
473 }
474 }
475 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
476 0, NULL);
477 return true;
478 }
479
480 bool onKeyDown(const pp::KeyboardInputEvent& ev) {
481 for (std::vector<MyTextField>::iterator it = textfield_.begin();
482 it != textfield_.end();
483 ++it) {
484 if (it->focused()) {
485 switch (ev.GetKeyCode()) {
486 case ui::VKEY_LEFT:
487 it->keyLeft();
488 break;
489 case ui::VKEY_RIGHT:
490 it->keyRight();
491 break;
492 case ui::VKEY_DELETE:
493 it->keyDelete();
494 break;
495 case ui::VKEY_BACK:
496 it->keyBackspace();
497 break;
498 }
499 return true;
500 }
501 }
502 return false;
503 }
504
505 bool onChar(const pp::KeyboardInputEvent& ev) {
506 for (std::vector<MyTextField>::iterator it = textfield_.begin();
507 it != textfield_.end();
508 ++it) {
509 if (it->focused()) {
510 it->insert_text(ev.GetCharacterText().AsString());
511 return true;
512 }
513 }
514 return false;
515 }
516
517 bool onImeText(const pp::CompositionInputEvent& ev) {
518 for (std::vector<MyTextField>::iterator it = textfield_.begin();
519 it != textfield_.end();
520 ++it) {
521 if (it->focused()) {
522 it->insert_text(ev.GetText().AsString());
523 return true;
524 }
525 }
526 return false;
527 }
528
529 void Paint() {
530 pp::Rect clip(0, 0, last_size_.height(), last_size_.width());
531 PaintClip(clip);
532 }
533
534 void PaintClip(const pp::Rect& clip) {
535 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_, true);
536 pp::Graphics2D device(this, last_size_, false);
537 BindGraphics(device);
538
539 for (std::vector<MyTextField>::iterator it = textfield_.begin();
540 it != textfield_.end();
541 ++it) {
542 it->PaintOn(&image, clip);
543 }
544
545 device.PaintImageData(image, pp::Point(0, 0));
546 device.Flush(pp::CompletionCallback(&OnFlush, this));
547 }
548
549 static void OnFlush(void* user_data, int32_t result) {}
550
551 // For debugging purpose.
552 void Log(const pp::Var& value) {
553 const PPB_Console_Dev* console = reinterpret_cast<const PPB_Console_Dev*>(
554 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_DEV_INTERFACE));
555 if (!console)
556 return;
557 console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
558 }
559
560 // IME Control interface.
561 scoped_ptr<TextFieldStatusHandler> status_handler_;
562
563 // Remembers the size of this instance.
564 pp::Size last_size_;
565
566 // Holds instances of text fields.
567 std::vector<MyTextField> textfield_;
568 };
569
570 class MyModule : public pp::Module {
571 virtual pp::Instance* CreateInstance(PP_Instance instance) {
572 return new MyInstance(instance);
573 }
574 };
575
576 namespace pp {
577
578 Module* CreateModule() {
579 return new MyModule();
580 }
581
582 } // namespace pp
OLDNEW
« no previous file with comments | « ppapi/cpp/input_event.cc ('k') | ppapi/examples/ime/ime.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698