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 20namespace mojo { 21namespace examples { 22 23namespace { 24 25const int kHorizontalPadding = 6; 26const int kVerticalPadding = 8; 27 28base::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. 35gfx::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|. 47int 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 56KeyboardView::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 64KeyboardView::~KeyboardView() { 65} 66 67void 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 const int initial_x = (width() - button_width * max_keys_in_row_ - 81 kHorizontalPadding * (max_keys_in_row_ - 1)) / 2; 82 for (size_t i = 0; i < rows_.size(); ++i) { 83 LayoutRow(*(rows_[i]), static_cast<int>(i), initial_x, button_width, 84 button_height); 85 } 86 87 views::LabelButtonBorder border(views::Button::STYLE_TEXTBUTTON); 88 gfx::Insets insets(border.GetInsets()); 89 gfx::FontList font = CalculateFont(button_width - insets.width(), 90 button_height - insets.height(), 91 base::ASCIIToUTF16("W")); 92 gfx::FontList special_font = CalculateFont(button_width - insets.width(), 93 button_height - insets.height(), 94 base::ASCIIToUTF16("?123")); 95 button_font_ = font; 96 ResetFonts(font, special_font); 97} 98 99void KeyboardView::SetLayout(KeyboardLayout keyboard_layout) { 100 if (keyboard_layout_ == keyboard_layout) 101 return; 102 103 keyboard_layout_ = keyboard_layout; 104 last_layout_size_ = gfx::Size(); 105 if (keyboard_layout_ == KEYBOARD_LAYOUT_NUMERIC) 106 SetRows(GetNumericRows()); 107 else 108 SetRows(GetQWERTYRows()); 109 Layout(); 110 SchedulePaint(); 111} 112 113void KeyboardView::LayoutRow(const Row& row, 114 int row_index, 115 int initial_x, 116 int button_width, 117 int button_height) { 118 int x = initial_x + row.padding * (button_width + kHorizontalPadding); 119 const int y = row_index * (button_height + kVerticalPadding); 120 for (size_t i = 0; i < row.num_keys; ++i) { 121 views::View* button = GetButton(row_index, static_cast<int>(i)); 122 int actual_width = button_width; 123 if (row.keys[i].size > 1) { 124 actual_width = (button_width + kHorizontalPadding) * 125 row.keys[i].size - kHorizontalPadding; 126 } 127 button->SetBounds(x, y, actual_width, button_height); 128 x += actual_width + kHorizontalPadding; 129 } 130} 131 132void KeyboardView::SetRows(const std::vector<const Row*>& rows) { 133 const int num_keys = NumKeys(rows); 134 while (child_count() > num_keys) 135 delete child_at(child_count() - 1); 136 for (int i = child_count(); i < num_keys; ++i) 137 AddChildView(CreateButton()); 138 139 last_layout_size_ = gfx::Size(); 140 141 rows_ = rows; 142 143 max_keys_in_row_ = 0; 144 for (size_t i = 0; i < rows_.size(); ++i) { 145 max_keys_in_row_ = std::max(max_keys_in_row_, 146 static_cast<int>(rows_[i]->num_keys)); 147 ConfigureButtonsInRow(static_cast<int>(i), *rows_[i]); 148 } 149} 150 151void KeyboardView::ConfigureButtonsInRow(int row_index, const Row& row) { 152 for (size_t i = 0; i < row.num_keys; ++i) { 153 views::LabelButton* button = GetButton(row_index, static_cast<int>(i)); 154 const Key& key(row.keys[i]); 155 switch (key.display_code) { 156 case SPECIAL_KEY_SHIFT: 157 // TODO: need image. 158 button->SetText(base::string16()); 159 break; 160 case SPECIAL_KEY_NUMERIC: 161 button->SetText(base::ASCIIToUTF16("?123")); 162 break; 163 case SPECIAL_KEY_ALPHA: 164 button->SetText(base::ASCIIToUTF16("ABC")); 165 break; 166 default: 167 button->SetText(GetDisplayString(key.display_code, 168 key.event_flags | event_flags())); 169 break; 170 } 171 button->SetState(views::Button::STATE_NORMAL); 172 } 173} 174 175views::View* KeyboardView::CreateButton() { 176 views::LabelButton* button = new views::LabelButton(this, base::string16()); 177 button->SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE); 178 button->SetHorizontalAlignment(gfx::ALIGN_CENTER); 179 button->set_background(views::Background::CreateSolidBackground(78, 78, 78)); 180 button->SetFontList(button_font_); 181 // button->SetHaloColor(SK_ColorBLACK); 182 // Turn off animations as we reuse buttons in different layouts. If we didn't 183 // do this and you click a button to change the layout then the button you 184 // clicked on would animate the transition even though it may now represent a 185 // different key. 186 button->SetAnimationDuration(0); 187 return button; 188} 189 190views::LabelButton* KeyboardView::GetButton(int row, int column) { 191 int offset = column; 192 for (int i = 0; i < row; ++i) 193 offset += static_cast<int>(rows_[i]->num_keys); 194 return static_cast<views::LabelButton*>(child_at(offset)); 195} 196 197const Key& KeyboardView::GetKeyForButton(views::Button* button) const { 198 int index = GetIndexOf(button); 199 DCHECK_NE(-1, index); 200 int row = 0; 201 while (index >= static_cast<int>(rows_[row]->num_keys)) { 202 index -= static_cast<int>(rows_[row]->num_keys); 203 row++; 204 } 205 return rows_[row]->keys[index]; 206} 207 208void KeyboardView::ResetFonts(const gfx::FontList& button_font, 209 const gfx::FontList& special_font) { 210 for (size_t i = 0; i < rows_.size(); ++i) { 211 for (size_t j = 0; j < rows_[i]->num_keys; ++j) { 212 views::LabelButton* button = GetButton(static_cast<int>(i), 213 static_cast<int>(j)); 214 const Key& key(GetKeyForButton(button)); 215 switch (key.display_code) { 216 case SPECIAL_KEY_ALPHA: 217 case SPECIAL_KEY_NUMERIC: 218 button->SetFontList(special_font); 219 break; 220 default: 221 button->SetFontList(button_font); 222 break; 223 } 224 } 225 } 226} 227 228void KeyboardView::ButtonPressed(views::Button* sender, 229 const ui::Event& event) { 230 const Key& key(GetKeyForButton(sender)); 231 switch (key.display_code) { 232 case SPECIAL_KEY_SHIFT: 233 SetLayout((keyboard_layout_ == KEYBOARD_LAYOUT_SHIFT) ? 234 KEYBOARD_LAYOUT_ALPHA : KEYBOARD_LAYOUT_SHIFT); 235 return; 236 case SPECIAL_KEY_ALPHA: 237 SetLayout(KEYBOARD_LAYOUT_ALPHA); 238 return; 239 case SPECIAL_KEY_NUMERIC: 240 SetLayout(KEYBOARD_LAYOUT_NUMERIC); 241 return; 242 default: 243 break; 244 } 245 246 // Windows isn't happy if we pass in the flags used to get the display string. 247#if defined(OS_WIN) 248 int key_event_flags = 0; 249#else 250 int key_event_flags = key.event_flags; 251#endif 252 delegate_->OnKeyPressed(key.keyboard_code(), key_event_flags | event_flags()); 253} 254 255} // namespace examples 256} // namespace mojo 257