• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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