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 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 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 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 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 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: 92 virtual ~TextFieldStatusHandler() {} 93 virtual void FocusIn(const pp::Rect& caret) {} 94 virtual void FocusOut() {} 95 virtual void UpdateSelection(const std::string& text) {} 96 }; 97 98 class TextFieldStatusNotifyingHandler : public TextFieldStatusHandler { 99 public: 100 explicit TextFieldStatusNotifyingHandler(pp::Instance* instance) 101 : textinput_control_(instance) { 102 } 103 104 protected: 105 // Implement TextFieldStatusHandler. 106 virtual void FocusIn(const pp::Rect& caret) { 107 textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT); 108 textinput_control_.UpdateCaretPosition(caret); 109 } 110 virtual void FocusOut() { 111 textinput_control_.CancelCompositionText(); 112 textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE); 113 } 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: 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. 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. 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? 248 bool Focused() const { 249 return caret_pos_ != std::string::npos; 250 } 251 252 // Does the coordinate (x,y) is contained inside the edit box? 253 bool Contains(int x, int y) const { 254 return area_.Contains(x, y); 255 } 256 257 // Resets the content 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. 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. 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 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 300 void MouseUp(int x, int y) { 301 if (!Focused()) 302 return; 303 CaretPosChanged(); 304 } 305 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 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 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 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. 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 } 374 size_t SelectionLeft() const { 375 return std::min(caret_pos_, anchor_pos_); 376 } 377 size_t SelectionRight() const { 378 return std::max(caret_pos_, anchor_pos_); 379 } 380 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: 401 explicit MyInstance(PP_Instance instance) 402 : pp::Instance(instance), 403 status_handler_(new TextFieldStatusHandler), 404 dragging_(false) { 405 } 406 407 ~MyInstance() { 408 delete status_handler_; 409 } 410 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: 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 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: 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 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 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 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 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 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 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 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 677 void Paint() { 678 pp::Rect clip(0, 0, last_size_.width(), last_size_.height()); 679 PaintClip(clip); 680 } 681 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 697 static void OnFlush(void* user_data, int32_t result) {} 698 699 // Prints a debug message. 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 { 722 virtual pp::Instance* CreateInstance(PP_Instance instance) { 723 return new MyInstance(instance); 724 } 725 }; 726 727 namespace pp { 728 729 Module* CreateModule() { 730 return new MyModule(); 731 } 732 733 } // namespace pp 734