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