blob: 4dfeecb72bf76cff088c5627fb8a19bbbfd415cf [file] [log] [blame]
[email protected]1dfdc0b2014-06-25 19:28:311// 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 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
95void 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
109void 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
127void 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
146void 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
170views::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
185views::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
192const 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
203void 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
223void 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