| // Copyright 2015 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 "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/macros.h" |
| #include "mojo/tools/embed/data.h" |
| #include "services/keyboard_native/kActionIcon.h" |
| #include "services/keyboard_native/kDeleteIcon.h" |
| #include "services/keyboard_native/kLowerCaseIcon.h" |
| #include "services/keyboard_native/kUpperCaseIcon.h" |
| #include "services/keyboard_native/key_layout.h" |
| #include "skia/ext/refptr.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkImageDecoder.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| #include "third_party/skia/include/core/SkTypeface.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/skia_util.h" |
| |
| namespace keyboard { |
| |
| // An implementation of Key that draws itself as ASCII text. |
| class KeyLayout::TextKey : public Key { |
| public: |
| TextKey(const char* text, |
| base::Callback<void(const TextKey&)> touch_up_callback) |
| : text_(text), touch_up_callback_(touch_up_callback) {} |
| |
| ~TextKey() override {} |
| |
| void Draw(SkCanvas* canvas, |
| const SkPaint& paint, |
| const gfx::RectF& rect) override { |
| float text_baseline_offset = rect.height() / 5.0f; |
| canvas->drawText(text_, strlen(text_), rect.x() + (rect.width() / 2.0f), |
| rect.y() + rect.height() - text_baseline_offset, paint); |
| } |
| |
| const char* ToText() const override { return text_; } |
| |
| void OnTouchUp() override { touch_up_callback_.Run(*this); } |
| |
| private: |
| const char* text_; |
| base::Callback<void(const TextKey&)> touch_up_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TextKey); |
| }; |
| |
| // An implementation of Key that draws itself as an image. |
| class ImageKey : public KeyLayout::Key { |
| public: |
| ImageKey(const char* text, |
| base::Callback<void(const KeyLayout::TextKey&)> touch_up_callback, |
| const mojo::embed::Data& data) |
| : text_key_(text, touch_up_callback), bitmap_valid_(false), bitmap_() { |
| bool result = gfx::PNGCodec::Decode( |
| reinterpret_cast<const unsigned char*>(data.data), data.size, &bitmap_); |
| bitmap_valid_ = result && bitmap_.width() > 0 && bitmap_.height() > 0; |
| DCHECK(bitmap_valid_); |
| } |
| ~ImageKey() override {} |
| |
| // Key implementation. |
| void Draw(SkCanvas* canvas, |
| const SkPaint& paint, |
| const gfx::RectF& rect) override { |
| // If our bitmap is somehow invalid, default to drawing the text of the key. |
| if (!bitmap_valid_) { |
| text_key_.Draw(canvas, paint, rect); |
| return; |
| } |
| |
| float width_scale = rect.width() / bitmap_.width(); |
| float height_scale = rect.height() / bitmap_.height(); |
| float scale = width_scale > height_scale ? height_scale : width_scale; |
| float target_width = bitmap_.width() * scale; |
| float target_height = bitmap_.height() * scale; |
| float delta_width = rect.width() - target_width; |
| float target_x = rect.x() + (delta_width / 2.0f); |
| float delta_height = rect.height() - target_height; |
| float target_y = rect.y() + (delta_height / 2.0f); |
| canvas->drawBitmapRect( |
| bitmap_, |
| SkRect::MakeXYWH(target_x, target_y, target_width, target_height), |
| &paint); |
| } |
| const char* ToText() const override { return text_key_.ToText(); } |
| void OnTouchUp() override { text_key_.OnTouchUp(); } |
| |
| private: |
| KeyLayout::TextKey text_key_; |
| bool bitmap_valid_; |
| SkBitmap bitmap_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ImageKey); |
| }; |
| |
| KeyLayout::KeyLayout() |
| : on_text_callback_(), |
| layout_(&letters_layout_), |
| key_map_(&lower_case_key_map_), |
| weak_factory_(this) { |
| InitLayouts(); |
| InitKeyMaps(); |
| } |
| |
| KeyLayout::~KeyLayout() { |
| for (auto& row : lower_case_key_map_) { |
| for (auto& key : row) { |
| delete key; |
| } |
| } |
| for (auto& row : upper_case_key_map_) { |
| for (auto& key : row) { |
| delete key; |
| } |
| } |
| for (auto& row : symbols_key_map_) { |
| for (auto& key : row) { |
| delete key; |
| } |
| } |
| } |
| |
| void KeyLayout::SetTextCallback( |
| base::Callback<void(const std::string&)> on_text_callback) { |
| on_text_callback_ = on_text_callback; |
| } |
| |
| void KeyLayout::SetDeleteCallback(base::Callback<void()> on_delete_callback) { |
| on_delete_callback_ = on_delete_callback; |
| } |
| |
| void KeyLayout::SetKeyArea(const gfx::RectF& key_area) { |
| key_area_ = key_area; |
| } |
| |
| void KeyLayout::Draw(SkCanvas* canvas) { |
| float row_height = key_area_.height() / static_cast<float>(layout_->size()); |
| |
| skia::RefPtr<SkTypeface> typeface = |
| skia::AdoptRef(SkTypeface::CreateFromName("Arial", SkTypeface::kNormal)); |
| SkPaint text_paint; |
| text_paint.setTypeface(typeface.get()); |
| text_paint.setColor(SK_ColorBLACK); |
| text_paint.setTextSize(row_height / 2); |
| text_paint.setAntiAlias(true); |
| text_paint.setTextAlign(SkPaint::kCenter_Align); |
| |
| SkPaint background_paint; |
| background_paint.setColor(SK_ColorLTGRAY); |
| canvas->drawRect(RectFToSkRect(key_area_), background_paint); |
| |
| SkPaint paint; |
| for (size_t row_index = 0; row_index < layout_->size(); row_index++) { |
| float current_top = key_area_.y() + row_index * row_height; |
| float current_left = key_area_.x(); |
| for (size_t key_index = 0; key_index < (*layout_)[row_index].size(); |
| key_index++) { |
| float key_width = static_cast<float>(key_area_.width()) * |
| (*layout_)[row_index][key_index]; |
| |
| (*key_map_)[row_index][key_index]->Draw( |
| canvas, text_paint, |
| gfx::RectF(current_left, current_top, key_width, row_height)); |
| current_left += key_width; |
| } |
| } |
| } |
| |
| KeyLayout::Key* KeyLayout::GetKeyAtPoint(const gfx::PointF& point) { |
| if (!key_area_.Contains(point)) { |
| return nullptr; |
| } |
| |
| int row_index = |
| (point.y() - key_area_.y()) / (key_area_.height() / layout_->size()); |
| float width_percent = static_cast<float>(point.x() - key_area_.x()) / |
| static_cast<float>(key_area_.width()); |
| |
| int key_index = 0; |
| while (width_percent >= (*layout_)[row_index][key_index]) { |
| width_percent -= (*layout_)[row_index][key_index]; |
| key_index++; |
| } |
| return (*key_map_)[row_index][key_index]; |
| } |
| |
| void KeyLayout::OnTouchUp(const gfx::PointF& touch_up) { |
| Key* key = GetKeyAtPoint(touch_up); |
| if (key != nullptr) { |
| key->OnTouchUp(); |
| } |
| } |
| |
| void KeyLayout::InitLayouts() { |
| // Row layouts are specified by a vector of floats which indicate the percent |
| // width a given key takes up in that row. The floats of a given row *MUST* |
| // add up to 1. |
| std::vector<float> ten_key_row_layout = { |
| 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f}; |
| std::vector<float> nine_key_row_layout = { |
| 0.15f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.15f}; |
| std::vector<float> seven_key_row_layout = { |
| 0.15f, 0.1f, 0.1f, 0.3f, 0.1f, 0.1f, 0.15f}; |
| std::vector<float> five_key_row_layout = {0.15f, 0.1f, 0.5f, 0.1f, 0.15f}; |
| |
| letters_layout_.push_back(ten_key_row_layout); |
| letters_layout_.push_back(nine_key_row_layout); |
| letters_layout_.push_back(nine_key_row_layout); |
| letters_layout_.push_back(five_key_row_layout); |
| |
| symbols_layout_.push_back(ten_key_row_layout); |
| symbols_layout_.push_back(nine_key_row_layout); |
| symbols_layout_.push_back(nine_key_row_layout); |
| symbols_layout_.push_back(seven_key_row_layout); |
| } |
| |
| void KeyLayout::OnKeyDoNothing(const TextKey& key) { |
| // do nothing |
| } |
| |
| void KeyLayout::OnKeyEmitText(const TextKey& key) { |
| on_text_callback_.Run(std::string(key.ToText())); |
| } |
| |
| void KeyLayout::OnKeyDelete(const TextKey& key) { |
| on_delete_callback_.Run(); |
| } |
| |
| void KeyLayout::OnKeySwitchToUpperCase(const TextKey& key) { |
| layout_ = &letters_layout_; |
| key_map_ = &upper_case_key_map_; |
| } |
| |
| void KeyLayout::OnKeySwitchToLowerCase(const TextKey& key) { |
| layout_ = &letters_layout_; |
| key_map_ = &lower_case_key_map_; |
| } |
| |
| void KeyLayout::OnKeySwitchToSymbols(const TextKey& key) { |
| layout_ = &symbols_layout_; |
| key_map_ = &symbols_key_map_; |
| } |
| |
| void KeyLayout::InitKeyMaps() { |
| base::Callback<void(const TextKey&)> do_nothing_callback = |
| base::Bind(&KeyLayout::OnKeyDoNothing, weak_factory_.GetWeakPtr()); |
| base::Callback<void(const TextKey&)> emit_text_callback = |
| base::Bind(&KeyLayout::OnKeyEmitText, weak_factory_.GetWeakPtr()); |
| base::Callback<void(const TextKey&)> delete_callback = |
| base::Bind(&KeyLayout::OnKeyDelete, weak_factory_.GetWeakPtr()); |
| base::Callback<void(const TextKey&)> switch_to_upper_case_callback = |
| base::Bind(&KeyLayout::OnKeySwitchToUpperCase, |
| weak_factory_.GetWeakPtr()); |
| base::Callback<void(const TextKey&)> switch_to_lower_case_callback = |
| base::Bind(&KeyLayout::OnKeySwitchToLowerCase, |
| weak_factory_.GetWeakPtr()); |
| base::Callback<void(const TextKey&)> switch_to_symbols_callback = |
| base::Bind(&KeyLayout::OnKeySwitchToSymbols, weak_factory_.GetWeakPtr()); |
| |
| ImageKey* switch_to_upper_case_image_key = new ImageKey( |
| "/\\", switch_to_upper_case_callback, keyboard_native::kUpperCaseIcon); |
| ImageKey* switch_to_lower_case_image_key = new ImageKey( |
| "\\/", switch_to_lower_case_callback, keyboard_native::kLowerCaseIcon); |
| ImageKey* delete_image_key = |
| new ImageKey("<-", delete_callback, keyboard_native::kDeleteIcon); |
| ImageKey* action_image_key = |
| new ImageKey(":)", do_nothing_callback, keyboard_native::kActionIcon); |
| |
| std::vector<Key*> lower_case_key_map_row_one = { |
| new TextKey("q", emit_text_callback), |
| new TextKey("w", emit_text_callback), |
| new TextKey("e", emit_text_callback), |
| new TextKey("r", emit_text_callback), |
| new TextKey("t", emit_text_callback), |
| new TextKey("y", emit_text_callback), |
| new TextKey("u", emit_text_callback), |
| new TextKey("o", emit_text_callback), |
| new TextKey("i", emit_text_callback), |
| new TextKey("p", emit_text_callback)}; |
| |
| std::vector<Key*> lower_case_key_map_row_two = { |
| new TextKey("a", emit_text_callback), |
| new TextKey("s", emit_text_callback), |
| new TextKey("d", emit_text_callback), |
| new TextKey("f", emit_text_callback), |
| new TextKey("g", emit_text_callback), |
| new TextKey("h", emit_text_callback), |
| new TextKey("j", emit_text_callback), |
| new TextKey("k", emit_text_callback), |
| new TextKey("l", emit_text_callback)}; |
| |
| std::vector<Key*> lower_case_key_map_row_three = { |
| switch_to_upper_case_image_key, |
| new TextKey("z", emit_text_callback), |
| new TextKey("x", emit_text_callback), |
| new TextKey("c", emit_text_callback), |
| new TextKey("v", emit_text_callback), |
| new TextKey("b", emit_text_callback), |
| new TextKey("n", emit_text_callback), |
| new TextKey("m", emit_text_callback), |
| delete_image_key}; |
| |
| std::vector<Key*> lower_case_key_map_row_four = { |
| new TextKey("sym", switch_to_symbols_callback), |
| new TextKey(",", emit_text_callback), |
| new TextKey(" ", emit_text_callback), |
| new TextKey(".", emit_text_callback), |
| action_image_key}; |
| |
| lower_case_key_map_ = {lower_case_key_map_row_one, |
| lower_case_key_map_row_two, |
| lower_case_key_map_row_three, |
| lower_case_key_map_row_four}; |
| |
| std::vector<Key*> upper_case_key_map_row_one = { |
| new TextKey("Q", emit_text_callback), |
| new TextKey("W", emit_text_callback), |
| new TextKey("E", emit_text_callback), |
| new TextKey("R", emit_text_callback), |
| new TextKey("T", emit_text_callback), |
| new TextKey("Y", emit_text_callback), |
| new TextKey("U", emit_text_callback), |
| new TextKey("O", emit_text_callback), |
| new TextKey("I", emit_text_callback), |
| new TextKey("P", emit_text_callback)}; |
| |
| std::vector<Key*> upper_case_key_map_row_two = { |
| new TextKey("A", emit_text_callback), |
| new TextKey("S", emit_text_callback), |
| new TextKey("D", emit_text_callback), |
| new TextKey("F", emit_text_callback), |
| new TextKey("G", emit_text_callback), |
| new TextKey("H", emit_text_callback), |
| new TextKey("J", emit_text_callback), |
| new TextKey("K", emit_text_callback), |
| new TextKey("L", emit_text_callback)}; |
| |
| std::vector<Key*> upper_case_key_map_row_three = { |
| switch_to_lower_case_image_key, |
| new TextKey("Z", emit_text_callback), |
| new TextKey("X", emit_text_callback), |
| new TextKey("C", emit_text_callback), |
| new TextKey("V", emit_text_callback), |
| new TextKey("B", emit_text_callback), |
| new TextKey("N", emit_text_callback), |
| new TextKey("M", emit_text_callback), |
| delete_image_key}; |
| |
| std::vector<Key*> upper_case_key_map_row_four = { |
| new TextKey("SYM", switch_to_symbols_callback), |
| new TextKey(",", emit_text_callback), |
| new TextKey(" ", emit_text_callback), |
| new TextKey(".", emit_text_callback), |
| action_image_key}; |
| |
| upper_case_key_map_ = {upper_case_key_map_row_one, |
| upper_case_key_map_row_two, |
| upper_case_key_map_row_three, |
| upper_case_key_map_row_four}; |
| |
| std::vector<Key*> symbols_key_map_row_one = { |
| new TextKey("1", emit_text_callback), |
| new TextKey("2", emit_text_callback), |
| new TextKey("3", emit_text_callback), |
| new TextKey("4", emit_text_callback), |
| new TextKey("5", emit_text_callback), |
| new TextKey("6", emit_text_callback), |
| new TextKey("7", emit_text_callback), |
| new TextKey("8", emit_text_callback), |
| new TextKey("9", emit_text_callback), |
| new TextKey("0", emit_text_callback)}; |
| |
| std::vector<Key*> symbols_key_map_row_two = { |
| new TextKey("@", emit_text_callback), |
| new TextKey("#", emit_text_callback), |
| new TextKey("$", emit_text_callback), |
| new TextKey("%", emit_text_callback), |
| new TextKey("&", emit_text_callback), |
| new TextKey("-", emit_text_callback), |
| new TextKey("+", emit_text_callback), |
| new TextKey("(", emit_text_callback), |
| new TextKey(")", emit_text_callback)}; |
| |
| std::vector<Key*> symbols_key_map_row_three = { |
| new TextKey("=\\<", switch_to_symbols_callback), |
| new TextKey("*", emit_text_callback), |
| new TextKey("\"", emit_text_callback), |
| new TextKey("'", emit_text_callback), |
| new TextKey(":", emit_text_callback), |
| new TextKey(";", emit_text_callback), |
| new TextKey("!", emit_text_callback), |
| new TextKey("?", emit_text_callback), |
| delete_image_key}; |
| |
| std::vector<Key*> symbols_key_map_row_four = { |
| new TextKey("ABC", switch_to_lower_case_callback), |
| new TextKey(",", emit_text_callback), |
| new TextKey("_", emit_text_callback), |
| new TextKey(" ", emit_text_callback), |
| new TextKey("/", emit_text_callback), |
| new TextKey(".", emit_text_callback), |
| action_image_key}; |
| |
| symbols_key_map_ = {symbols_key_map_row_one, |
| symbols_key_map_row_two, |
| symbols_key_map_row_three, |
| symbols_key_map_row_four}; |
| } |
| } |
| // namespace keyboard |