[email protected] | 1dfdc0b | 2014-06-25 19:28:31 | [diff] [blame^] | 1 | // Copyright 2014 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 "mojo/examples/keyboard/keyboard_view.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | |
| 9 | #include "base/strings/string16.h" |
| 10 | #include "base/strings/utf_string_conversions.h" |
| 11 | #include "mojo/examples/keyboard/keyboard_delegate.h" |
| 12 | #include "mojo/examples/keyboard/keys.h" |
| 13 | #include "ui/events/keycodes/keyboard_code_conversion.h" |
| 14 | #include "ui/events/keycodes/keyboard_codes.h" |
| 15 | #include "ui/gfx/canvas.h" |
| 16 | #include "ui/views/background.h" |
| 17 | #include "ui/views/controls/button/label_button.h" |
| 18 | #include "ui/views/controls/button/label_button_border.h" |
| 19 | |
| 20 | namespace mojo { |
| 21 | namespace examples { |
| 22 | |
| 23 | namespace { |
| 24 | |
| 25 | const int kHorizontalPadding = 6; |
| 26 | const int kVerticalPadding = 8; |
| 27 | |
| 28 | base::string16 GetDisplayString(int key_code, int flags) { |
| 29 | return base::string16(1, ui::GetCharacterFromKeyCode( |
| 30 | static_cast<ui::KeyboardCode>(key_code), flags)); |
| 31 | } |
| 32 | |
| 33 | // Returns a font that fits in the space provided. |text| is used as a basis |
| 34 | // for determing the size. |
| 35 | gfx::FontList CalculateFont(int width, int height, const base::string16& text) { |
| 36 | gfx::FontList font; |
| 37 | gfx::FontList last_font; |
| 38 | while (gfx::Canvas::GetStringWidth(text, font) < width && |
| 39 | font.GetHeight() < height) { |
| 40 | last_font = font; |
| 41 | font = font.DeriveWithSizeDelta(2); |
| 42 | } |
| 43 | return last_font; |
| 44 | } |
| 45 | |
| 46 | // Returns the total number of keys in |rows|. |
| 47 | int NumKeys(const std::vector<const Row*>& rows) { |
| 48 | int result = 0; |
| 49 | for (size_t i = 0; i < rows.size(); ++i) |
| 50 | result += static_cast<int>(rows[i]->num_keys); |
| 51 | return result; |
| 52 | } |
| 53 | |
| 54 | } // namespace |
| 55 | |
| 56 | KeyboardView::KeyboardView(KeyboardDelegate* delegate) |
| 57 | : delegate_(delegate), |
| 58 | max_keys_in_row_(0), |
| 59 | keyboard_layout_(KEYBOARD_LAYOUT_ALPHA) { |
| 60 | set_background(views::Background::CreateSolidBackground(SK_ColorBLACK)); |
| 61 | SetRows(GetQWERTYRows()); |
| 62 | } |
| 63 | |
| 64 | KeyboardView::~KeyboardView() { |
| 65 | } |
| 66 | |
| 67 | void KeyboardView::Layout() { |
| 68 | if (width() == 0 || height() == 0 || rows_.empty() || |
| 69 | last_layout_size_ == bounds().size()) |
| 70 | return; |
| 71 | |
| 72 | last_layout_size_ = bounds().size(); |
| 73 | |
| 74 | const int button_width = |
| 75 | (width() - (max_keys_in_row_ - 1) * kHorizontalPadding) / |
| 76 | max_keys_in_row_; |
| 77 | const int button_height = |
| 78 | (height() - (static_cast<int>(rows_.size() - 1) * kVerticalPadding)) / |
| 79 | static_cast<int>(rows_.size()); |
| 80 | for (size_t i = 0; i < rows_.size(); ++i) |
| 81 | LayoutRow(*(rows_[i]), static_cast<int>(i), button_width, button_height); |
| 82 | |
| 83 | views::LabelButtonBorder border(views::Button::STYLE_TEXTBUTTON); |
| 84 | gfx::Insets insets(border.GetInsets()); |
| 85 | gfx::FontList font = CalculateFont(button_width - insets.width(), |
| 86 | button_height - insets.height(), |
| 87 | base::ASCIIToUTF16("W")); |
| 88 | gfx::FontList special_font = CalculateFont(button_width - insets.width(), |
| 89 | button_height - insets.height(), |
| 90 | base::ASCIIToUTF16("?123")); |
| 91 | button_font_ = font; |
| 92 | ResetFonts(font, special_font); |
| 93 | } |
| 94 | |
| 95 | void KeyboardView::SetLayout(KeyboardLayout keyboard_layout) { |
| 96 | if (keyboard_layout_ == keyboard_layout) |
| 97 | return; |
| 98 | |
| 99 | keyboard_layout_ = keyboard_layout; |
| 100 | last_layout_size_ = gfx::Size(); |
| 101 | if (keyboard_layout_ == KEYBOARD_LAYOUT_NUMERIC) |
| 102 | SetRows(GetNumericRows()); |
| 103 | else |
| 104 | SetRows(GetQWERTYRows()); |
| 105 | Layout(); |
| 106 | SchedulePaint(); |
| 107 | } |
| 108 | |
| 109 | void KeyboardView::LayoutRow(const Row& row, |
| 110 | int row_index, |
| 111 | int button_width, |
| 112 | int button_height) { |
| 113 | int x = row.padding * (button_width + kHorizontalPadding); |
| 114 | const int y = row_index * (button_height + kVerticalPadding); |
| 115 | for (size_t i = 0; i < row.num_keys; ++i) { |
| 116 | views::View* button = GetButton(row_index, static_cast<int>(i)); |
| 117 | int actual_width = button_width; |
| 118 | if (row.keys[i].size > 1) { |
| 119 | actual_width = (button_width + kHorizontalPadding) * |
| 120 | row.keys[i].size - kHorizontalPadding; |
| 121 | } |
| 122 | button->SetBounds(x, y, actual_width, button_height); |
| 123 | x += actual_width + kHorizontalPadding; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | void KeyboardView::SetRows(const std::vector<const Row*>& rows) { |
| 128 | const int num_keys = NumKeys(rows); |
| 129 | while (child_count() > num_keys) |
| 130 | delete child_at(child_count() - 1); |
| 131 | for (int i = child_count(); i < num_keys; ++i) |
| 132 | AddChildView(CreateButton()); |
| 133 | |
| 134 | last_layout_size_ = gfx::Size(); |
| 135 | |
| 136 | rows_ = rows; |
| 137 | |
| 138 | max_keys_in_row_ = 0; |
| 139 | for (size_t i = 0; i < rows_.size(); ++i) { |
| 140 | max_keys_in_row_ = std::max(max_keys_in_row_, |
| 141 | static_cast<int>(rows_[i]->num_keys)); |
| 142 | ConfigureButtonsInRow(static_cast<int>(i), *rows_[i]); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | void KeyboardView::ConfigureButtonsInRow(int row_index, const Row& row) { |
| 147 | for (size_t i = 0; i < row.num_keys; ++i) { |
| 148 | views::LabelButton* button = GetButton(row_index, static_cast<int>(i)); |
| 149 | const Key& key(row.keys[i]); |
| 150 | switch (key.display_code) { |
| 151 | case SPECIAL_KEY_SHIFT: |
| 152 | // TODO: need image. |
| 153 | button->SetText(base::string16()); |
| 154 | break; |
| 155 | case SPECIAL_KEY_NUMERIC: |
| 156 | button->SetText(base::ASCIIToUTF16("?123")); |
| 157 | break; |
| 158 | case SPECIAL_KEY_ALPHA: |
| 159 | button->SetText(base::ASCIIToUTF16("ABC")); |
| 160 | break; |
| 161 | default: |
| 162 | button->SetText(GetDisplayString(key.display_code, |
| 163 | key.event_flags | event_flags())); |
| 164 | break; |
| 165 | } |
| 166 | button->SetState(views::Button::STATE_NORMAL); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | views::View* KeyboardView::CreateButton() { |
| 171 | views::LabelButton* button = new views::LabelButton(this, base::string16()); |
| 172 | button->SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE); |
| 173 | button->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| 174 | button->set_background(views::Background::CreateSolidBackground(78, 78, 78)); |
| 175 | button->SetFontList(button_font_); |
| 176 | // button->SetHaloColor(SK_ColorBLACK); |
| 177 | // Turn off animations as we reuse buttons in different layouts. If we didn't |
| 178 | // do this and you click a button to change the layout then the button you |
| 179 | // clicked on would animate the transition even though it may now represent a |
| 180 | // different key. |
| 181 | button->SetAnimationDuration(0); |
| 182 | return button; |
| 183 | } |
| 184 | |
| 185 | views::LabelButton* KeyboardView::GetButton(int row, int column) { |
| 186 | int offset = column; |
| 187 | for (int i = 0; i < row; ++i) |
| 188 | offset += static_cast<int>(rows_[i]->num_keys); |
| 189 | return static_cast<views::LabelButton*>(child_at(offset)); |
| 190 | } |
| 191 | |
| 192 | const Key& KeyboardView::GetKeyForButton(views::Button* button) const { |
| 193 | int index = GetIndexOf(button); |
| 194 | DCHECK_NE(-1, index); |
| 195 | int row = 0; |
| 196 | while (index >= static_cast<int>(rows_[row]->num_keys)) { |
| 197 | index -= static_cast<int>(rows_[row]->num_keys); |
| 198 | row++; |
| 199 | } |
| 200 | return rows_[row]->keys[index]; |
| 201 | } |
| 202 | |
| 203 | void KeyboardView::ResetFonts(const gfx::FontList& button_font, |
| 204 | const gfx::FontList& special_font) { |
| 205 | for (size_t i = 0; i < rows_.size(); ++i) { |
| 206 | for (size_t j = 0; j < rows_[i]->num_keys; ++j) { |
| 207 | views::LabelButton* button = GetButton(static_cast<int>(i), |
| 208 | static_cast<int>(j)); |
| 209 | const Key& key(GetKeyForButton(button)); |
| 210 | switch (key.display_code) { |
| 211 | case SPECIAL_KEY_ALPHA: |
| 212 | case SPECIAL_KEY_NUMERIC: |
| 213 | button->SetFontList(special_font); |
| 214 | break; |
| 215 | default: |
| 216 | button->SetFontList(button_font); |
| 217 | break; |
| 218 | } |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | void KeyboardView::ButtonPressed(views::Button* sender, |
| 224 | const ui::Event& event) { |
| 225 | const Key& key(GetKeyForButton(sender)); |
| 226 | switch (key.display_code) { |
| 227 | case SPECIAL_KEY_SHIFT: |
| 228 | SetLayout((keyboard_layout_ == KEYBOARD_LAYOUT_SHIFT) ? |
| 229 | KEYBOARD_LAYOUT_ALPHA : KEYBOARD_LAYOUT_SHIFT); |
| 230 | return; |
| 231 | case SPECIAL_KEY_ALPHA: |
| 232 | SetLayout(KEYBOARD_LAYOUT_ALPHA); |
| 233 | return; |
| 234 | case SPECIAL_KEY_NUMERIC: |
| 235 | SetLayout(KEYBOARD_LAYOUT_NUMERIC); |
| 236 | return; |
| 237 | default: |
| 238 | break; |
| 239 | } |
| 240 | |
| 241 | // Windows isn't happy if we pass in the flags used to get the display string. |
| 242 | #if defined(OS_WIN) |
| 243 | int key_event_flags = 0; |
| 244 | #else |
| 245 | int key_event_flags = key.event_flags; |
| 246 | #endif |
| 247 | delegate_->OnKeyPressed(key.keyboard_code(), key_event_flags | event_flags()); |
| 248 | } |
| 249 | |
| 250 | } // namespace examples |
| 251 | } // namespace mojo |