blob: 3a0d71ac48d5458733ab3b71daa72ca101cc4aee [file] [log] [blame]
// Copyright 2014 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 "mojo/examples/keyboard/keyboard_view.h"
#include <algorithm>
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "mojo/examples/keyboard/keyboard_delegate.h"
#include "mojo/examples/keyboard/keys.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/button/label_button_border.h"
namespace mojo {
namespace examples {
namespace {
const int kHorizontalPadding = 6;
const int kVerticalPadding = 8;
base::string16 GetDisplayString(int key_code, int flags) {
return base::string16(1, ui::GetCharacterFromKeyCode(
static_cast<ui::KeyboardCode>(key_code), flags));
}
// Returns a font that fits in the space provided. |text| is used as a basis
// for determing the size.
gfx::FontList CalculateFont(int width, int height, const base::string16& text) {
gfx::FontList font;
gfx::FontList last_font;
while (gfx::Canvas::GetStringWidth(text, font) < width &&
font.GetHeight() < height) {
last_font = font;
font = font.DeriveWithSizeDelta(2);
}
return last_font;
}
// Returns the total number of keys in |rows|.
int NumKeys(const std::vector<const Row*>& rows) {
int result = 0;
for (size_t i = 0; i < rows.size(); ++i)
result += static_cast<int>(rows[i]->num_keys);
return result;
}
} // namespace
KeyboardView::KeyboardView(KeyboardDelegate* delegate)
: delegate_(delegate),
max_keys_in_row_(0),
keyboard_layout_(KEYBOARD_LAYOUT_ALPHA) {
set_background(views::Background::CreateSolidBackground(SK_ColorBLACK));
SetRows(GetQWERTYRows());
}
KeyboardView::~KeyboardView() {
}
void KeyboardView::Layout() {
if (width() == 0 || height() == 0 || rows_.empty() ||
last_layout_size_ == bounds().size())
return;
last_layout_size_ = bounds().size();
const int button_width =
(width() - (max_keys_in_row_ - 1) * kHorizontalPadding) /
max_keys_in_row_;
const int button_height =
(height() - (static_cast<int>(rows_.size() - 1) * kVerticalPadding)) /
static_cast<int>(rows_.size());
const int initial_x = (width() - button_width * max_keys_in_row_ -
kHorizontalPadding * (max_keys_in_row_ - 1)) / 2;
for (size_t i = 0; i < rows_.size(); ++i) {
LayoutRow(*(rows_[i]), static_cast<int>(i), initial_x, button_width,
button_height);
}
views::LabelButtonBorder border(views::Button::STYLE_TEXTBUTTON);
gfx::Insets insets(border.GetInsets());
gfx::FontList font = CalculateFont(button_width - insets.width(),
button_height - insets.height(),
base::ASCIIToUTF16("W"));
gfx::FontList special_font = CalculateFont(button_width - insets.width(),
button_height - insets.height(),
base::ASCIIToUTF16("?123"));
button_font_ = font;
ResetFonts(font, special_font);
}
void KeyboardView::SetLayout(KeyboardLayout keyboard_layout) {
if (keyboard_layout_ == keyboard_layout)
return;
keyboard_layout_ = keyboard_layout;
last_layout_size_ = gfx::Size();
if (keyboard_layout_ == KEYBOARD_LAYOUT_NUMERIC)
SetRows(GetNumericRows());
else
SetRows(GetQWERTYRows());
Layout();
SchedulePaint();
}
void KeyboardView::LayoutRow(const Row& row,
int row_index,
int initial_x,
int button_width,
int button_height) {
int x = initial_x + row.padding * (button_width + kHorizontalPadding);
const int y = row_index * (button_height + kVerticalPadding);
for (size_t i = 0; i < row.num_keys; ++i) {
views::View* button = GetButton(row_index, static_cast<int>(i));
int actual_width = button_width;
if (row.keys[i].size > 1) {
actual_width = (button_width + kHorizontalPadding) *
row.keys[i].size - kHorizontalPadding;
}
button->SetBounds(x, y, actual_width, button_height);
x += actual_width + kHorizontalPadding;
}
}
void KeyboardView::SetRows(const std::vector<const Row*>& rows) {
const int num_keys = NumKeys(rows);
while (child_count() > num_keys)
delete child_at(child_count() - 1);
for (int i = child_count(); i < num_keys; ++i)
AddChildView(CreateButton());
last_layout_size_ = gfx::Size();
rows_ = rows;
max_keys_in_row_ = 0;
for (size_t i = 0; i < rows_.size(); ++i) {
max_keys_in_row_ = std::max(max_keys_in_row_,
static_cast<int>(rows_[i]->num_keys));
ConfigureButtonsInRow(static_cast<int>(i), *rows_[i]);
}
}
void KeyboardView::ConfigureButtonsInRow(int row_index, const Row& row) {
for (size_t i = 0; i < row.num_keys; ++i) {
views::LabelButton* button = GetButton(row_index, static_cast<int>(i));
const Key& key(row.keys[i]);
switch (key.display_code) {
case SPECIAL_KEY_SHIFT:
// TODO: need image.
button->SetText(base::string16());
break;
case SPECIAL_KEY_NUMERIC:
button->SetText(base::ASCIIToUTF16("?123"));
break;
case SPECIAL_KEY_ALPHA:
button->SetText(base::ASCIIToUTF16("ABC"));
break;
default:
button->SetText(GetDisplayString(key.display_code,
key.event_flags | event_flags()));
break;
}
button->SetState(views::Button::STATE_NORMAL);
}
}
views::View* KeyboardView::CreateButton() {
views::LabelButton* button = new views::LabelButton(this, base::string16());
button->SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE);
button->SetHorizontalAlignment(gfx::ALIGN_CENTER);
button->set_background(views::Background::CreateSolidBackground(78, 78, 78));
button->SetFontList(button_font_);
// button->SetHaloColor(SK_ColorBLACK);
// Turn off animations as we reuse buttons in different layouts. If we didn't
// do this and you click a button to change the layout then the button you
// clicked on would animate the transition even though it may now represent a
// different key.
button->SetAnimationDuration(0);
return button;
}
views::LabelButton* KeyboardView::GetButton(int row, int column) {
int offset = column;
for (int i = 0; i < row; ++i)
offset += static_cast<int>(rows_[i]->num_keys);
return static_cast<views::LabelButton*>(child_at(offset));
}
const Key& KeyboardView::GetKeyForButton(views::Button* button) const {
int index = GetIndexOf(button);
DCHECK_NE(-1, index);
int row = 0;
while (index >= static_cast<int>(rows_[row]->num_keys)) {
index -= static_cast<int>(rows_[row]->num_keys);
row++;
}
return rows_[row]->keys[index];
}
void KeyboardView::ResetFonts(const gfx::FontList& button_font,
const gfx::FontList& special_font) {
for (size_t i = 0; i < rows_.size(); ++i) {
for (size_t j = 0; j < rows_[i]->num_keys; ++j) {
views::LabelButton* button = GetButton(static_cast<int>(i),
static_cast<int>(j));
const Key& key(GetKeyForButton(button));
switch (key.display_code) {
case SPECIAL_KEY_ALPHA:
case SPECIAL_KEY_NUMERIC:
button->SetFontList(special_font);
break;
default:
button->SetFontList(button_font);
break;
}
}
}
}
void KeyboardView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
const Key& key(GetKeyForButton(sender));
switch (key.display_code) {
case SPECIAL_KEY_SHIFT:
SetLayout((keyboard_layout_ == KEYBOARD_LAYOUT_SHIFT) ?
KEYBOARD_LAYOUT_ALPHA : KEYBOARD_LAYOUT_SHIFT);
return;
case SPECIAL_KEY_ALPHA:
SetLayout(KEYBOARD_LAYOUT_ALPHA);
return;
case SPECIAL_KEY_NUMERIC:
SetLayout(KEYBOARD_LAYOUT_NUMERIC);
return;
default:
break;
}
// Windows isn't happy if we pass in the flags used to get the display string.
#if defined(OS_WIN)
int key_event_flags = 0;
#else
int key_event_flags = key.event_flags;
#endif
delegate_->OnKeyPressed(key.keyboard_code(), key_event_flags | event_flags());
}
} // namespace examples
} // namespace mojo