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