Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(167)

Unified Diff: ppapi/examples/ime/ime.cc

Issue 7621010: Never submit: tentative Pepper IME. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: snapshot Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ppapi/examples/ime/ime.cc
diff --git a/ppapi/examples/ime/ime.cc b/ppapi/examples/ime/ime.cc
new file mode 100644
index 0000000000000000000000000000000000000000..6fb243f44f15ca9e626f5ca736d26cd296fa85eb
--- /dev/null
+++ b/ppapi/examples/ime/ime.cc
@@ -0,0 +1,574 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cstdlib>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ppapi/c/dev/ppb_console_dev.h"
+#include "ppapi/c/dev/ppb_cursor_control_dev.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/dev/font_dev.h"
+#include "ppapi/cpp/dev/ime_control_dev.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/size.h"
+
+namespace {
+
+// Note: ARGB
+static const uint32_t EDITBOX_BG_COLOR = 0xffffffff;
+static const uint32_t EDITBOX_TEXT_COLOR = 0xff000000;
+static const uint32_t EDITBOX_CARET_COLOR = 0xff000000;
+static const uint32_t EDITBOX_PREEDIT_TEXT_COLOR = 0xffff0000;
+static const uint32_t EDITBOX_UNDERLINE_COLOR_MAIN = 0xff00ff00;
+static const uint32_t EDITBOX_UNDERLINE_COLOR_SUB = 0xff0000ff;
+
+void FillRect(pp::ImageData* image,
+ int left, int top, int width, int height,
+ uint32_t color) {
+ for (int y = std::max(0, top);
+ y < std::min(image->size().height() - 1, top + height);
+ ++y) {
+ for (int x = std::max(0, left);
+ x < std::min(image->size().width() - 1, left + width);
+ ++x)
+ *image->GetAddr32(pp::Point(x, y)) = color;
+ }
+}
+
+void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
+ FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
+}
+
+// TODO(kinaba): We might use ICU macro here.
+size_t utf8_prev(const std::string& str, size_t i) {
+ if (i > 0) {
+ do
+ --i;
+ while ((str[i] & 0xC0) == 0x80 && i > 0);
+ }
+ return i;
+}
+
+// TODO(kinaba): We might use ICU macro here.
+size_t utf8_next(const std::string& str, size_t i) {
+ if (i < str.size()) {
+ do
+ ++i;
+ while ((str[i] & 0xC0) == 0x80 && i < str.size());
+ }
+ return i;
+}
+
+} // namespace
+
+class MyInstance : public pp::Instance {
+ public:
+ explicit MyInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ imecontrol_(this),
+ ime_unaware_mode_(false) {
+ }
+
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
+ RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
+
+ edit_.push_back(MyEditBox(this, 10, 10, 300, 20, false));
+ edit_.back().set_text("Hello");
+ edit_.push_back(MyEditBox(this, 30, 100, 300, 20, true));
+ edit_.back().set_text("World");
+
+ for (uint32_t i=0; i<argc; ++i)
+ if (argn[i] == std::string("imeunaware")) {
+ ime_unaware_mode_ = true;
+ return true;
+ } else if (argn[i] == std::string("noime")) {
+ ime_unaware_mode_ = true;
+ imecontrol_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
+ return true;
+ }
+
+ for (uint32_t i=0; i<argc; ++i)
+ if (argn[i] == std::string("offthespot")) {
+ return true;
+ }
+ RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
+ return true;
+ }
+
+ // For debugging purpose.
+ void Log(const pp::Var& value) {
+ const PPB_Console_Dev* console = reinterpret_cast<const PPB_Console_Dev*>(
+ pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_DEV_INTERFACE));
+ if (!console)
+ return;
+ console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
+ }
+
+ void focusIn(const pp::Rect& textarea,
+ const pp::Rect& textfield,
+ bool onthespot) {
kochi 2011/09/09 07:58:59 onthespot param not used.
+ if (ime_unaware_mode_)
+ return;
+ imecontrol_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT);
+ imecontrol_.UpdateCaretPosition(textarea, textfield);
+ }
+
+ void focusOut() {
+ if (ime_unaware_mode_)
+ return;
+ imecontrol_.CancelCompositionText();
+ imecontrol_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
+ }
+
+ protected:
+ // Input events
+ virtual bool HandleInputEvent(const pp::InputEvent& event) {
+ bool ret = false;
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
+ const pp::MouseInputEvent mouseEvent(event);
+ ret = onMouseDown(mouseEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
+ const pp::MouseInputEvent mouseEvent(event);
+ ret = onMouseMove(mouseEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_KEYDOWN: {
+ Log("Keydown");
+ const pp::KeyboardInputEvent keyEvent(event);
+ ret = onKeyDown(keyEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_CHAR: {
+ const pp::KeyboardInputEvent keyEvent(event);
+ Log("Char ["+keyEvent.GetCharacterText().AsString()+"]");
+ ret = onChar(keyEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_COMPOSITION_START: {
+ const pp::CompositionInputEvent compositionEvent(event);
+ Log("CompositionStart ["+compositionEvent.GetText().AsString()+"]");
+ ret = true;
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_COMPOSITION_UPDATE: {
+ const pp::CompositionInputEvent compositionEvent(event);
+ Log("CompositionUpdate ["+compositionEvent.GetText().AsString()+"]");
+ ret = onCompositionUpdate(compositionEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_COMPOSITION_END: {
+ const pp::CompositionInputEvent compositionEvent(event);
+ Log("CompositionEnd ["+compositionEvent.GetText().AsString()+"]");
+ ret = onCompositionEnd(compositionEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_IME_TEXT: {
+ const pp::CompositionInputEvent compositionEvent(event);
+ Log("ImeText ["+compositionEvent.GetText().AsString()+"]");
+ ret = onImeText(compositionEvent);
+ break;
+ }
+ default:
+ break;
+ }
+ if (ret && event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE)
+ Paint();
+ return ret;
+ }
+
+ bool onCompositionUpdate(const pp::CompositionInputEvent& ev) {
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ if (it->focused()) {
+ it->setComposition(ev.GetText().AsString(),
+ ev.GetSegments(),
+ ev.GetTargetSegment());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool onCompositionEnd(const pp::CompositionInputEvent& ev) {
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ if (it->focused()) {
+ it->setComposition("", std::vector<uint32_t>(), 0);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool onMouseDown(const pp::MouseInputEvent& ev) {
+ bool anyoneFocused = false;
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ if (it->refocusByMouseClick(ev.GetPosition().x(),
+ ev.GetPosition().y())) {
+ anyoneFocused = true;
+ }
+ }
+ if (!anyoneFocused)
+ focusOut();
+ return true;
+ }
+
+ bool onMouseMove(const pp::MouseInputEvent& ev) {
+ const PPB_CursorControl_Dev* cursor_control =
+ reinterpret_cast<const PPB_CursorControl_Dev*>(
+ pp::Module::Get()->GetBrowserInterface(
+ PPB_CURSOR_CONTROL_DEV_INTERFACE));
+ if (!cursor_control)
+ return false;
+
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ if (it->contains(ev.GetPosition().x(),
+ ev.GetPosition().y())) {
+ cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
+ 0, NULL);
+ return true;
+ }
+ }
+ cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
+ 0, NULL);
+ return true;
+ }
+
+ bool onKeyDown(const pp::KeyboardInputEvent& ev) {
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ if (it->focused()) {
+ switch (ev.GetKeyCode()) {
+ case 0x25: // VK_LEFT (TODO(kinaba): find an appropriate header)
+ it->keyLeft();
+ break;
+ case 0x27: // VK_RIGHT
+ it->keyRight();
+ break;
+ case 0x2E: // VK_DELETE
+ it->keyDelete();
+ break;
+ case 0x08: // VK_BACK
+ it->keyBackspace();
+ break;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool onChar(const pp::KeyboardInputEvent& ev) {
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ if (it->focused()) {
+ it->insert_text(ev.GetCharacterText().AsString());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool onImeText(const pp::CompositionInputEvent& ev) {
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ if (it->focused()) {
+ it->insert_text(ev.GetText().AsString());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Painting
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
+ if (position.size() == last_size_)
+ return;
+ last_size_ = position.size();
+ Paint();
+ }
+
+ void Paint() {
+ pp::Rect clip(0, 0, last_size_.height(), last_size_.width());
+ PaintClip(clip);
+ }
+
+ void PaintClip(const pp::Rect& clip) {
+ pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_,
+ true);
+ pp::Graphics2D device(this, last_size_, false);
+ BindGraphics(device);
+
+ for (std::vector<MyEditBox>::iterator it = edit_.begin();
+ it != edit_.end();
+ ++it) {
+ it->PaintOn(&image, clip);
+ }
+
+ device.PaintImageData(image, pp::Point(0, 0));
+ device.Flush(pp::CompletionCallback(&OnFlush, this));
+ }
+
+ static void OnFlush(void* user_data, int32_t result) {
+ }
+
+ private:
+ // Testing purpose EditBox.
+ class MyEditBox {
+ public:
+ MyEditBox(MyInstance* instance, int x, int y, int width, int height,
+ bool onthespot)
+ : instance_(instance),
+ area_(x, y, width, height),
+ font_size_(height-2),
+ cursor_pos_(std::string::npos),
+ onthespot_(onthespot) {
+ }
+
+ // Paint editbox image on the specified ImageData.
+ void PaintOn(pp::ImageData* image, pp::Rect clip) {
+ clip = clip.Intersect(area_);
+
+ pp::FontDescription_Dev desc;
+ desc.set_family(PP_FONTFAMILY_SANSSERIF);
+ desc.set_size(font_size_);
+ pp::Font_Dev font(instance_, desc);
+
+ FillRect(image, clip, EDITBOX_BG_COLOR);
+
+ if (cursor_pos_ != std::string::npos) {
+ int offset = area_.x();
+ // before cursor
+ {
+ std::string str = utf8_text_.substr(0, cursor_pos_);
+ font.DrawTextAt(
+ image,
+ pp::TextRun_Dev(str.c_str(), false, false),
+ pp::Point(offset, area_.y()+font_size_),
+ EDITBOX_TEXT_COLOR,
+ clip,
+ false);
+ offset += font.MeasureText(pp::TextRun_Dev(str.c_str()));
+ }
+ // composition
+ {
+ const std::string& str = composition_;
+ font.DrawTextAt(
+ image,
+ pp::TextRun_Dev(str.c_str(), false, false),
+ pp::Point(offset, area_.y()+font_size_),
+ EDITBOX_PREEDIT_TEXT_COLOR,
+ clip,
+ false);
+ for (size_t i=0; i<segments_.size(); ++i) {
+ size_t l = segments_[i].first;
+ size_t r = segments_[i].second;
+ if (l != r) {
+ int lx = font.MeasureText(pp::TextRun_Dev(str.substr(0,l).c_str()));
+ int rx = font.MeasureText(pp::TextRun_Dev(str.substr(0,r).c_str()));
kochi 2011/09/09 07:58:59 80 column
+ FillRect(image,
+ offset+lx+2, area_.y()+font_size_+1, (rx-lx)-4, 2,
+ i==targetSegment_ ?
+ EDITBOX_UNDERLINE_COLOR_MAIN :
+ EDITBOX_UNDERLINE_COLOR_SUB );
+ }
+ }
+ offset += font.MeasureText(pp::TextRun_Dev(str.c_str()));
+ }
+ // caret
+ FillRect(image, pp::Rect(offset, area_.y(), 2,
+ area_.height()), EDITBOX_CARET_COLOR);
+ // after cursor
+ {
+ std::string str = utf8_text_.substr(cursor_pos_);
+ font.DrawTextAt(
+ image,
+ pp::TextRun_Dev(str.c_str(), false, false),
+ pp::Point(offset, area_.y()+font_size_),
+ EDITBOX_TEXT_COLOR,
+ clip,
+ false);
+ offset += font.MeasureText(pp::TextRun_Dev(str.c_str()));
+ }
+ } else {
+ font.DrawTextAt(image,
+ pp::TextRun_Dev(utf8_text_.c_str(), false, false),
+ pp::Point(area_.x(), area_.y()+font_size_), EDITBOX_TEXT_COLOR,
+ clip, false);
+ }
+ }
+
+ void setComposition(const std::string& text,
+ const std::vector<uint32_t>& segments,
+ uint32_t target_segment) {
+ composition_ = text;
+
+ segments_.clear();
+ for (size_t i=0; i+1<segments.size(); ++i)
+ segments_.push_back(std::make_pair(segments[i], segments[i+1]));
+ targetSegment_ = target_segment;
+ cursorPosChanged();
+ }
+
+ // Am I focused?
+ bool focused() const {
+ // "<=" is not a typo of "<". we can focus on the tail of the text.
+ return (cursor_pos_ != std::string::npos &&
+ cursor_pos_ <= utf8_text_.size());
+ }
+
+ // Does the coordinate (x,y) is contained inside the edit box?
+ bool contains(int x, int y) {
+ return area_.Contains(x, y);
+ }
+
+ // reset the content text
+ void set_text(const std::string& text) {
+ utf8_text_ = text;
+ if (focused()) {
+ cursor_pos_ = text.size();
+ cursorPosChanged();
+ }
+ }
+
+ // insert the text at the cursor position
+ void insert_text(const std::string& text) {
+ if (!focused())
+ return;
+ utf8_text_.insert(cursor_pos_, text);
+ if (focused()) {
+ cursor_pos_ += text.size();
+ cursorPosChanged();
+ }
+ }
+
+ // event handling
+ bool refocusByMouseClick(int x, int y) {
+ if (!contains(x, y)) {
+ // unfocus
+ cursor_pos_ = std::string::npos;
+ return false;
+ }
+
+ // focus
+ pp::FontDescription_Dev desc;
+ desc.set_family(PP_FONTFAMILY_SANSSERIF);
+ desc.set_size(font_size_);
+ pp::Font_Dev font(instance_, desc);
+
+ size_t i = font.CharacterOffsetForPixel(
+ pp::TextRun_Dev(utf8_text_.c_str()), x-area_.x());
+
+ // TODO(kinaba) there must be a function to do this....
+ cursor_pos_ = 0;
+ while (i-- > 0)
+ cursor_pos_ = utf8_next(utf8_text_, cursor_pos_);
+ cursorPosChanged();
+ return true;
+ }
+
+ void keyLeft() {
+ if (!focused())
+ return;
+ cursor_pos_ = utf8_prev(utf8_text_, cursor_pos_);
+ cursorPosChanged();
+ }
+
+ void keyRight() {
+ if (!focused())
+ return;
+ cursor_pos_ = utf8_next(utf8_text_, cursor_pos_);
+ cursorPosChanged();
+ }
+
+ void keyDelete() {
+ if (!focused())
+ return;
+ size_t i = utf8_next(utf8_text_, cursor_pos_);
+ utf8_text_.erase(cursor_pos_, i-cursor_pos_);
+ cursorPosChanged();
+ }
+
+ void keyBackspace() {
+ if (!focused() || cursor_pos_ == 0)
+ return;
+ keyLeft();
+ keyDelete();
+ }
+
+ private:
+ void Log(const pp::Var& value) {
+ instance_->Log(value);
+ }
+
+ // Notify the plugin instance that the cursor position has changed.
+ void cursorPosChanged() {
+ if (focused()) {
+ pp::FontDescription_Dev desc;
+ desc.set_family(PP_FONTFAMILY_SANSSERIF);
+ desc.set_size(font_size_);
+ pp::Font_Dev font(instance_, desc);
+
+ int px = font.MeasureText(
+ pp::TextRun_Dev(utf8_text_.substr(0, cursor_pos_).c_str()));
+ pp::Rect textarea(area_.x()+px, area_.y(), 2, area_.height()+2);
+ instance_->focusIn(textarea, area_, onthespot_);
+ }
+ }
+
+ MyInstance* instance_;
+ pp::Rect area_;
+ int font_size_;
+ std::string utf8_text_;
+ size_t cursor_pos_;
+ bool onthespot_;
+ std::string composition_;
+ std::vector< std::pair<uint32_t,uint32_t> > segments_;
+ size_t targetSegment_;
+ };
+
+ // IME Control interface.
+ pp::IMEControl_Dev imecontrol_;
+ bool ime_unaware_mode_;
+ // Remembers the size of this instance.
+ pp::Size last_size_;
+ // Holds instances of edit box.
+ std::vector<MyEditBox> edit_;
+};
+
+// boilerplate code
+class MyModule : public pp::Module {
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new MyInstance(instance);
+ }
+};
+
+namespace pp {
+Module* CreateModule() {
+ return new MyModule();
+}
+
+} // namespace pp

Powered by Google App Engine
This is Rietveld 408576698