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