1 // Copyright (c) 2012 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 <string>
7 #include <utility>
8 #include <vector>
9
10 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
11 #include "ppapi/c/ppb_console.h"
12 #include "ppapi/cpp/completion_callback.h"
13 #include "ppapi/cpp/dev/font_dev.h"
14 #include "ppapi/cpp/graphics_2d.h"
15 #include "ppapi/cpp/image_data.h"
16 #include "ppapi/cpp/input_event.h"
17 #include "ppapi/cpp/instance.h"
18 #include "ppapi/cpp/module.h"
19 #include "ppapi/cpp/rect.h"
20 #include "ppapi/cpp/size.h"
21 #include "ppapi/cpp/text_input_controller.h"
22
23 namespace {
24
25 // Extracted from: ui/events/keycodes/keyboard_codes.h
26 enum {
27 VKEY_BACK = 0x08,
28 VKEY_SHIFT = 0x10,
29 VKEY_DELETE = 0x2E,
30 VKEY_LEFT = 0x25,
31 VKEY_UP = 0x26,
32 VKEY_RIGHT = 0x27,
33 VKEY_DOWN = 0x28,
34 };
35
36 const uint32_t kTextfieldBgColor = 0xffffffff;
37 const uint32_t kTextfieldTextColor = 0xff000000;
38 const uint32_t kTextfieldCaretColor = 0xff000000;
39 const uint32_t kTextfieldPreeditTextColor = 0xffff0000;
40 const uint32_t kTextfieldSelectionBackgroundColor = 0xffeecccc;
41 const uint32_t kTextfieldUnderlineColorMain = 0xffff0000;
42 const uint32_t kTextfieldUnderlineColorSub = 0xffddaaaa;
43
FillRect(pp::ImageData * image,int left,int top,int width,int height,uint32_t color)44 void FillRect(pp::ImageData* image,
45 int left, int top, int width, int height,
46 uint32_t color) {
47 for (int y = std::max(0, top);
48 y < std::min(image->size().height() - 1, top + height);
49 ++y) {
50 for (int x = std::max(0, left);
51 x < std::min(image->size().width() - 1, left + width);
52 ++x)
53 *image->GetAddr32(pp::Point(x, y)) = color;
54 }
55 }
56
FillRect(pp::ImageData * image,const pp::Rect & rect,uint32_t color)57 void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
58 FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
59 }
60
GetPrevCharOffsetUtf8(const std::string & str,size_t current_pos)61 size_t GetPrevCharOffsetUtf8(const std::string& str, size_t current_pos) {
62 size_t i = current_pos;
63 if (i > 0) {
64 do
65 --i;
66 while (i > 0 && (str[i] & 0xc0) == 0x80);
67 }
68 return i;
69 }
70
GetNextCharOffsetUtf8(const std::string & str,size_t current_pos)71 size_t GetNextCharOffsetUtf8(const std::string& str, size_t current_pos) {
72 size_t i = current_pos;
73 if (i < str.size()) {
74 do
75 ++i;
76 while (i < str.size() && (str[i] & 0xc0) == 0x80);
77 }
78 return i;
79 }
80
GetNthCharOffsetUtf8(const std::string & str,size_t n)81 size_t GetNthCharOffsetUtf8(const std::string& str, size_t n) {
82 size_t i = 0;
83 for (size_t step = 0; step < n; ++step)
84 i = GetNextCharOffsetUtf8(str, i);
85 return i;
86 }
87
88 } // namespace
89
90 class TextFieldStatusHandler {
91 public:
~TextFieldStatusHandler()92 virtual ~TextFieldStatusHandler() {}
FocusIn(const pp::Rect & caret)93 virtual void FocusIn(const pp::Rect& caret) {}
FocusOut()94 virtual void FocusOut() {}
UpdateSelection(const std::string & text)95 virtual void UpdateSelection(const std::string& text) {}
96 };
97
98 class TextFieldStatusNotifyingHandler : public TextFieldStatusHandler {
99 public:
TextFieldStatusNotifyingHandler(pp::Instance * instance)100 explicit TextFieldStatusNotifyingHandler(pp::Instance* instance)
101 : textinput_control_(instance) {
102 }
103
104 protected:
105 // Implement TextFieldStatusHandler.
FocusIn(const pp::Rect & caret)106 virtual void FocusIn(const pp::Rect& caret) {
107 textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT);
108 textinput_control_.UpdateCaretPosition(caret);
109 }
FocusOut()110 virtual void FocusOut() {
111 textinput_control_.CancelCompositionText();
112 textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
113 }
UpdateSelection(const std::string & text)114 virtual void UpdateSelection(const std::string& text) {
115 textinput_control_.UpdateSurroundingText(text, 0, text.size());
116 }
117
118 private:
119 pp::TextInputController textinput_control_;
120 };
121
122 // Hand-made text field for demonstrating text input API.
123 class MyTextField {
124 public:
MyTextField(pp::Instance * instance,TextFieldStatusHandler * handler,int x,int y,int width,int height)125 MyTextField(pp::Instance* instance, TextFieldStatusHandler* handler,
126 int x, int y, int width, int height)
127 : instance_(instance),
128 status_handler_(handler),
129 area_(x, y, width, height),
130 font_size_(height - 2),
131 caret_pos_(std::string::npos),
132 anchor_pos_(std::string::npos),
133 target_segment_(0) {
134 pp::FontDescription_Dev desc;
135 desc.set_family(PP_FONTFAMILY_SANSSERIF);
136 desc.set_size(font_size_);
137 font_ = pp::Font_Dev(instance_, desc);
138 }
139
140 // Paint on the specified ImageData.
PaintOn(pp::ImageData * image,pp::Rect clip)141 void PaintOn(pp::ImageData* image, pp::Rect clip) {
142 clip = clip.Intersect(area_);
143 FillRect(image, clip, kTextfieldBgColor);
144
145 if (caret_pos_ != std::string::npos) {
146 int offset = area_.x();
147 // selection (for the case without composition text)
148 if (composition_.empty() && HasSelection()) {
149 int left_x = font_.MeasureSimpleText(
150 utf8_text_.substr(0, SelectionLeft()));
151 int right_x = font_.MeasureSimpleText(
152 utf8_text_.substr(0, SelectionRight()));
153 FillRect(image, offset + left_x, area_.y(), right_x - left_x,
154 area_.height(), kTextfieldSelectionBackgroundColor);
155 }
156 // before caret
157 {
158 std::string str = utf8_text_.substr(0, caret_pos_);
159 font_.DrawTextAt(
160 image,
161 pp::TextRun_Dev(str.c_str(), false, false),
162 pp::Point(offset, area_.y() + font_size_),
163 kTextfieldTextColor,
164 clip,
165 false);
166 offset += font_.MeasureSimpleText(str);
167 }
168 // composition
169 {
170 const std::string& str = composition_;
171 // selection
172 if (composition_selection_.first != composition_selection_.second) {
173 int left_x = font_.MeasureSimpleText(
174 str.substr(0, composition_selection_.first));
175 int right_x = font_.MeasureSimpleText(
176 str.substr(0, composition_selection_.second));
177 FillRect(image, offset + left_x, area_.y(), right_x - left_x,
178 area_.height(), kTextfieldSelectionBackgroundColor);
179 }
180 // composition text
181 font_.DrawTextAt(
182 image,
183 pp::TextRun_Dev(str.c_str(), false, false),
184 pp::Point(offset, area_.y() + font_size_),
185 kTextfieldPreeditTextColor,
186 clip,
187 false);
188 for (size_t i = 0; i < segments_.size(); ++i) {
189 size_t l = segments_[i].first;
190 size_t r = segments_[i].second;
191 if (l != r) {
192 int lx = font_.MeasureSimpleText(str.substr(0, l));
193 int rx = font_.MeasureSimpleText(str.substr(0, r));
194 FillRect(image,
195 offset + lx + 2, area_.y() + font_size_ + 1,
196 rx - lx - 4, 2,
197 i == static_cast<size_t>(target_segment_) ?
198 kTextfieldUnderlineColorMain :
199 kTextfieldUnderlineColorSub);
200 }
201 }
202 // caret
203 int caretx = font_.MeasureSimpleText(
204 str.substr(0, composition_selection_.first));
205 FillRect(image,
206 pp::Rect(offset + caretx, area_.y(), 2, area_.height()),
207 kTextfieldCaretColor);
208 offset += font_.MeasureSimpleText(str);
209 }
210 // after caret
211 {
212 std::string str = utf8_text_.substr(caret_pos_);
213 font_.DrawTextAt(
214 image,
215 pp::TextRun_Dev(str.c_str(), false, false),
216 pp::Point(offset, area_.y() + font_size_),
217 kTextfieldTextColor,
218 clip,
219 false);
220 }
221 } else {
222 font_.DrawTextAt(
223 image,
224 pp::TextRun_Dev(utf8_text_.c_str(), false, false),
225 pp::Point(area_.x(), area_.y() + font_size_),
226 kTextfieldTextColor,
227 clip,
228 false);
229 }
230 }
231
232 // Update current composition text.
SetComposition(const std::string & text,const std::vector<std::pair<uint32_t,uint32_t>> & segments,int32_t target_segment,const std::pair<uint32_t,uint32_t> & selection)233 void SetComposition(
234 const std::string& text,
235 const std::vector< std::pair<uint32_t, uint32_t> >& segments,
236 int32_t target_segment,
237 const std::pair<uint32_t, uint32_t>& selection) {
238 if (HasSelection() && !text.empty())
239 InsertText(std::string());
240 composition_ = text;
241 segments_ = segments;
242 target_segment_ = target_segment;
243 composition_selection_ = selection;
244 CaretPosChanged();
245 }
246
247 // Is the text field focused?
Focused() const248 bool Focused() const {
249 return caret_pos_ != std::string::npos;
250 }
251
252 // Does the coordinate (x,y) is contained inside the edit box?
Contains(int x,int y) const253 bool Contains(int x, int y) const {
254 return area_.Contains(x, y);
255 }
256
257 // Resets the content text.
SetText(const std::string & text)258 void SetText(const std::string& text) {
259 utf8_text_ = text;
260 if (Focused()) {
261 caret_pos_ = anchor_pos_ = text.size();
262 CaretPosChanged();
263 }
264 }
265
266 // Inserts a text at the current caret position.
InsertText(const std::string & text)267 void InsertText(const std::string& text) {
268 if (!Focused())
269 return;
270 utf8_text_.replace(SelectionLeft(), SelectionRight() - SelectionLeft(),
271 text);
272 caret_pos_ = anchor_pos_ = SelectionLeft() + text.size();
273 CaretPosChanged();
274 }
275
276 // Handles mouse click event and changes the focus state.
RefocusByMouseClick(int x,int y)277 bool RefocusByMouseClick(int x, int y) {
278 if (!Contains(x, y)) {
279 // The text field is unfocused.
280 caret_pos_ = anchor_pos_ = std::string::npos;
281 return false;
282 }
283
284 // The text field is focused.
285 size_t n = font_.CharacterOffsetForPixel(
286 pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
287 caret_pos_ = anchor_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
288 CaretPosChanged();
289 return true;
290 }
291
MouseDrag(int x,int y)292 void MouseDrag(int x, int y) {
293 if (!Focused())
294 return;
295 size_t n = font_.CharacterOffsetForPixel(
296 pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
297 caret_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
298 }
299
MouseUp(int x,int y)300 void MouseUp(int x, int y) {
301 if (!Focused())
302 return;
303 CaretPosChanged();
304 }
305
KeyLeft(bool shift)306 void KeyLeft(bool shift) {
307 if (!Focused())
308 return;
309 // Move caret to the head of the selection or to the previous character.
310 if (!shift && HasSelection())
311 caret_pos_ = SelectionLeft();
312 else
313 caret_pos_ = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
314 // Move the anchor if the shift key is not pressed.
315 if (!shift)
316 anchor_pos_ = caret_pos_;
317 CaretPosChanged();
318 }
319
KeyRight(bool shift)320 void KeyRight(bool shift) {
321 if (!Focused())
322 return;
323 // Move caret to the end of the selection or to the next character.
324 if (!shift && HasSelection())
325 caret_pos_ = SelectionRight();
326 else
327 caret_pos_ = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
328 // Move the anchor if the shift key is not pressed.
329 if (!shift)
330 anchor_pos_ = caret_pos_;
331 CaretPosChanged();
332 }
333
KeyDelete()334 void KeyDelete() {
335 if (!Focused())
336 return;
337 if (HasSelection()) {
338 InsertText(std::string());
339 } else {
340 size_t i = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
341 utf8_text_.erase(caret_pos_, i - caret_pos_);
342 CaretPosChanged();
343 }
344 }
345
KeyBackspace()346 void KeyBackspace() {
347 if (!Focused())
348 return;
349 if (HasSelection()) {
350 InsertText(std::string());
351 } else if (caret_pos_ != 0) {
352 size_t i = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
353 utf8_text_.erase(i, caret_pos_ - i);
354 caret_pos_ = anchor_pos_ = i;
355 CaretPosChanged();
356 }
357 }
358
359 private:
360 // Notify the plugin instance that the caret position has changed.
CaretPosChanged()361 void CaretPosChanged() {
362 if (Focused()) {
363 std::string str = utf8_text_.substr(0, caret_pos_);
364 if (!composition_.empty())
365 str += composition_.substr(0, composition_selection_.first);
366 int px = font_.MeasureSimpleText(str);
367 pp::Rect caret(area_.x() + px, area_.y(), 0, area_.height() + 2);
368 status_handler_->FocusIn(caret);
369 status_handler_->UpdateSelection(
370 utf8_text_.substr(SelectionLeft(),
371 SelectionRight() - SelectionLeft()));
372 }
373 }
SelectionLeft() const374 size_t SelectionLeft() const {
375 return std::min(caret_pos_, anchor_pos_);
376 }
SelectionRight() const377 size_t SelectionRight() const {
378 return std::max(caret_pos_, anchor_pos_);
379 }
HasSelection() const380 bool HasSelection() const {
381 return caret_pos_ != anchor_pos_;
382 }
383
384 pp::Instance* instance_;
385 TextFieldStatusHandler* status_handler_;
386
387 pp::Rect area_;
388 int font_size_;
389 pp::Font_Dev font_;
390 std::string utf8_text_;
391 size_t caret_pos_;
392 size_t anchor_pos_;
393 std::string composition_;
394 std::vector< std::pair<uint32_t, uint32_t> > segments_;
395 std::pair<uint32_t, uint32_t> composition_selection_;
396 int target_segment_;
397 };
398
399 class MyInstance : public pp::Instance {
400 public:
MyInstance(PP_Instance instance)401 explicit MyInstance(PP_Instance instance)
402 : pp::Instance(instance),
403 status_handler_(new TextFieldStatusHandler),
404 dragging_(false) {
405 }
406
~MyInstance()407 ~MyInstance() {
408 delete status_handler_;
409 }
410
Init(uint32_t argc,const char * argn[],const char * argv[])411 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
412 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
413 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
414
415 for (uint32_t i = 0; i < argc; ++i) {
416 if (argn[i] == std::string("ime")) {
417 if (argv[i] == std::string("no")) {
418 // Example of NO-IME plugins (e.g., games).
419 //
420 // When a plugin never wants to accept text input, at initialization
421 // explicitly turn off the text input feature by calling:
422 pp::TextInputController(this).SetTextInputType(
423 PP_TEXTINPUT_TYPE_NONE);
424 } else if (argv[i] == std::string("unaware")) {
425 // Demonstrating the behavior of IME-unaware plugins.
426 // Never call any text input related APIs.
427 //
428 // In such a case, the plugin is assumed to always accept text input.
429 // For example, when the plugin is focused in touch devices a virtual
430 // keyboard may pop up, or in environment IME is used, users can type
431 // text via IME on the plugin. The characters are delivered to the
432 // plugin via PP_INPUTEVENT_TYPE_CHAR events.
433 } else if (argv[i] == std::string("caretmove")) {
434 // Demonstrating the behavior of plugins with limited IME support.
435 //
436 // It uses SetTextInputType() and UpdateCaretPosition() API to notify
437 // text input status to the browser, but unable to handle inline
438 // compositions. By using the notified information. the browser can,
439 // say, show virtual keyboards or IMEs only at appropriate timing
440 // that the plugin does need to accept text input.
441 delete status_handler_;
442 status_handler_ = new TextFieldStatusNotifyingHandler(this);
443 } else if (argv[i] == std::string("full")) {
444 // Demonstrating the behavior of plugins fully supporting IME.
445 //
446 // It notifies updates of caret positions to the browser,
447 // and handles all text input events by itself.
448 delete status_handler_;
449 status_handler_ = new TextFieldStatusNotifyingHandler(this);
450 RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
451 }
452 break;
453 }
454 }
455
456 textfield_.push_back(MyTextField(this, status_handler_,
457 10, 10, 300, 20));
458 textfield_.back().SetText("Hello");
459 textfield_.push_back(MyTextField(this, status_handler_,
460 30, 100, 300, 20));
461 textfield_.back().SetText("World");
462 return true;
463 }
464
465 protected:
HandleInputEvent(const pp::InputEvent & event)466 virtual bool HandleInputEvent(const pp::InputEvent& event) {
467 bool ret = false;
468 switch (event.GetType()) {
469 case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
470 const pp::MouseInputEvent mouseEvent(event);
471 ret = OnMouseDown(mouseEvent);
472 break;
473 }
474 case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
475 const pp::MouseInputEvent mouseEvent(event);
476 ret = OnMouseMove(mouseEvent);
477 break;
478 }
479 case PP_INPUTEVENT_TYPE_MOUSEUP: {
480 const pp::MouseInputEvent mouseEvent(event);
481 ret = OnMouseUp(mouseEvent);
482 break;
483 }
484 case PP_INPUTEVENT_TYPE_KEYDOWN: {
485 Log("Keydown");
486 const pp::KeyboardInputEvent keyEvent(event);
487 ret = OnKeyDown(keyEvent);
488 break;
489 }
490 case PP_INPUTEVENT_TYPE_CHAR: {
491 const pp::KeyboardInputEvent keyEvent(event);
492 Log("Char [" + keyEvent.GetCharacterText().AsString() + "]");
493 ret = OnChar(keyEvent);
494 break;
495 }
496 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: {
497 const pp::IMEInputEvent imeEvent(event);
498 Log("CompositionStart [" + imeEvent.GetText().AsString() + "]");
499 ret = true;
500 break;
501 }
502 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: {
503 const pp::IMEInputEvent imeEvent(event);
504 Log("CompositionUpdate [" + imeEvent.GetText().AsString() + "]");
505 ret = OnCompositionUpdate(imeEvent);
506 break;
507 }
508 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: {
509 const pp::IMEInputEvent imeEvent(event);
510 Log("CompositionEnd [" + imeEvent.GetText().AsString() + "]");
511 ret = OnCompositionEnd(imeEvent);
512 break;
513 }
514 case PP_INPUTEVENT_TYPE_IME_TEXT: {
515 const pp::IMEInputEvent imeEvent(event);
516 Log("ImeText [" + imeEvent.GetText().AsString() + "]");
517 ret = OnImeText(imeEvent);
518 break;
519 }
520 default:
521 break;
522 }
523 if (ret && (dragging_ || event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE))
524 Paint();
525 return ret;
526 }
527
DidChangeView(const pp::Rect & position,const pp::Rect & clip)528 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
529 if (position.size() == last_size_)
530 return;
531 last_size_ = position.size();
532 Paint();
533 }
534
535 private:
OnCompositionUpdate(const pp::IMEInputEvent & ev)536 bool OnCompositionUpdate(const pp::IMEInputEvent& ev) {
537 for (std::vector<MyTextField>::iterator it = textfield_.begin();
538 it != textfield_.end();
539 ++it) {
540 if (it->Focused()) {
541 std::vector< std::pair<uint32_t, uint32_t> > segs;
542 for (uint32_t i = 0; i < ev.GetSegmentNumber(); ++i)
543 segs.push_back(std::make_pair(ev.GetSegmentOffset(i),
544 ev.GetSegmentOffset(i + 1)));
545 uint32_t selection_start;
546 uint32_t selection_end;
547 ev.GetSelection(&selection_start, &selection_end);
548 it->SetComposition(ev.GetText().AsString(),
549 segs,
550 ev.GetTargetSegment(),
551 std::make_pair(selection_start, selection_end));
552 return true;
553 }
554 }
555 return false;
556 }
557
OnCompositionEnd(const pp::IMEInputEvent & ev)558 bool OnCompositionEnd(const pp::IMEInputEvent& ev) {
559 for (std::vector<MyTextField>::iterator it = textfield_.begin();
560 it != textfield_.end();
561 ++it) {
562 if (it->Focused()) {
563 it->SetComposition(std::string(),
564 std::vector<std::pair<uint32_t, uint32_t> >(),
565 0,
566 std::make_pair(0, 0));
567 return true;
568 }
569 }
570 return false;
571 }
572
OnMouseDown(const pp::MouseInputEvent & ev)573 bool OnMouseDown(const pp::MouseInputEvent& ev) {
574 dragging_ = true;
575
576 bool anyone_focused = false;
577 for (std::vector<MyTextField>::iterator it = textfield_.begin();
578 it != textfield_.end();
579 ++it) {
580 if (it->RefocusByMouseClick(ev.GetPosition().x(),
581 ev.GetPosition().y())) {
582 anyone_focused = true;
583 }
584 }
585 if (!anyone_focused)
586 status_handler_->FocusOut();
587 return true;
588 }
589
OnMouseMove(const pp::MouseInputEvent & ev)590 bool OnMouseMove(const pp::MouseInputEvent& ev) {
591 const PPB_CursorControl_Dev* cursor_control =
592 reinterpret_cast<const PPB_CursorControl_Dev*>(
593 pp::Module::Get()->GetBrowserInterface(
594 PPB_CURSOR_CONTROL_DEV_INTERFACE));
595 if (!cursor_control)
596 return false;
597
598 for (std::vector<MyTextField>::iterator it = textfield_.begin();
599 it != textfield_.end();
600 ++it) {
601 if (it->Contains(ev.GetPosition().x(),
602 ev.GetPosition().y())) {
603 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
604 0, NULL);
605 if (it->Focused() && dragging_)
606 it->MouseDrag(ev.GetPosition().x(), ev.GetPosition().y());
607 return true;
608 }
609 }
610 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
611 0, NULL);
612 return true;
613 }
614
OnMouseUp(const pp::MouseInputEvent & ev)615 bool OnMouseUp(const pp::MouseInputEvent& ev) {
616 dragging_ = false;
617 for (std::vector<MyTextField>::iterator it = textfield_.begin();
618 it != textfield_.end();
619 ++it)
620 if (it->Focused())
621 it->MouseUp(ev.GetPosition().x(), ev.GetPosition().y());
622 return false;
623 }
624
OnKeyDown(const pp::KeyboardInputEvent & ev)625 bool OnKeyDown(const pp::KeyboardInputEvent& ev) {
626 for (std::vector<MyTextField>::iterator it = textfield_.begin();
627 it != textfield_.end();
628 ++it) {
629 if (it->Focused()) {
630 bool shift = ev.GetModifiers() & PP_INPUTEVENT_MODIFIER_SHIFTKEY;
631 switch (ev.GetKeyCode()) {
632 case VKEY_LEFT:
633 it->KeyLeft(shift);
634 break;
635 case VKEY_RIGHT:
636 it->KeyRight(shift);
637 break;
638 case VKEY_DELETE:
639 it->KeyDelete();
640 break;
641 case VKEY_BACK:
642 it->KeyBackspace();
643 break;
644 }
645 return true;
646 }
647 }
648 return false;
649 }
650
OnChar(const pp::KeyboardInputEvent & ev)651 bool OnChar(const pp::KeyboardInputEvent& ev) {
652 for (std::vector<MyTextField>::iterator it = textfield_.begin();
653 it != textfield_.end();
654 ++it) {
655 if (it->Focused()) {
656 std::string str = ev.GetCharacterText().AsString();
657 if (str != "\r" && str != "\n")
658 it->InsertText(str);
659 return true;
660 }
661 }
662 return false;
663 }
664
OnImeText(const pp::IMEInputEvent ev)665 bool OnImeText(const pp::IMEInputEvent ev) {
666 for (std::vector<MyTextField>::iterator it = textfield_.begin();
667 it != textfield_.end();
668 ++it) {
669 if (it->Focused()) {
670 it->InsertText(ev.GetText().AsString());
671 return true;
672 }
673 }
674 return false;
675 }
676
Paint()677 void Paint() {
678 pp::Rect clip(0, 0, last_size_.width(), last_size_.height());
679 PaintClip(clip);
680 }
681
PaintClip(const pp::Rect & clip)682 void PaintClip(const pp::Rect& clip) {
683 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_, true);
684 pp::Graphics2D device(this, last_size_, false);
685 BindGraphics(device);
686
687 for (std::vector<MyTextField>::iterator it = textfield_.begin();
688 it != textfield_.end();
689 ++it) {
690 it->PaintOn(&image, clip);
691 }
692
693 device.PaintImageData(image, pp::Point(0, 0));
694 device.Flush(pp::CompletionCallback(&OnFlush, this));
695 }
696
OnFlush(void * user_data,int32_t result)697 static void OnFlush(void* user_data, int32_t result) {}
698
699 // Prints a debug message.
Log(const pp::Var & value)700 void Log(const pp::Var& value) {
701 const PPB_Console* console = reinterpret_cast<const PPB_Console*>(
702 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
703 if (!console)
704 return;
705 console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
706 }
707
708 // IME Control interface.
709 TextFieldStatusHandler* status_handler_;
710
711 // Remembers the size of this instance.
712 pp::Size last_size_;
713
714 // Holds instances of text fields.
715 std::vector<MyTextField> textfield_;
716
717 // Whether or not during a drag operation.
718 bool dragging_;
719 };
720
721 class MyModule : public pp::Module {
CreateInstance(PP_Instance instance)722 virtual pp::Instance* CreateInstance(PP_Instance instance) {
723 return new MyInstance(instance);
724 }
725 };
726
727 namespace pp {
728
CreateModule()729 Module* CreateModule() {
730 return new MyModule();
731 }
732
733 } // namespace pp
734