blob: 04003ba57ff95c500459ecaf1018a347a8cec3de [file] [log] [blame]
// Copyright (c) 2012 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 "ui/views/controls/textfield/textfield.h"
#include <string>
#include "base/command_line.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_switches.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/insets.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/selection_model.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/controls/textfield/native_textfield_views.h"
#include "ui/views/controls/textfield/native_textfield_wrapper.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/painter.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/widget.h"
namespace {
// Default placeholder text color.
const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY;
gfx::FontList GetDefaultFontList() {
return ResourceBundle::GetSharedInstance().GetFontList(
ResourceBundle::BaseFont);
}
} // namespace
namespace views {
// static
const char Textfield::kViewClassName[] = "Textfield";
// static
size_t Textfield::GetCaretBlinkMs() {
static const size_t default_value = 500;
#if defined(OS_WIN)
static const size_t system_value = ::GetCaretBlinkTime();
if (system_value != 0)
return (system_value == INFINITE) ? 0 : system_value;
#endif
return default_value;
}
Textfield::Textfield()
: native_wrapper_(NULL),
controller_(NULL),
style_(STYLE_DEFAULT),
font_list_(GetDefaultFontList()),
read_only_(false),
default_width_in_chars_(0),
draw_border_(true),
text_color_(SK_ColorBLACK),
use_default_text_color_(true),
background_color_(SK_ColorWHITE),
use_default_background_color_(true),
horizontal_margins_were_set_(false),
vertical_margins_were_set_(false),
placeholder_text_color_(kDefaultPlaceholderTextColor),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
weak_ptr_factory_(this) {
set_focusable(true);
if (ViewsDelegate::views_delegate) {
obscured_reveal_duration_ = ViewsDelegate::views_delegate->
GetDefaultTextfieldObscuredRevealDuration();
}
if (NativeViewHost::kRenderNativeControlFocus)
focus_painter_ = Painter::CreateDashedFocusPainter();
}
Textfield::Textfield(StyleFlags style)
: native_wrapper_(NULL),
controller_(NULL),
style_(style),
font_list_(GetDefaultFontList()),
read_only_(false),
default_width_in_chars_(0),
draw_border_(true),
text_color_(SK_ColorBLACK),
use_default_text_color_(true),
background_color_(SK_ColorWHITE),
use_default_background_color_(true),
horizontal_margins_were_set_(false),
vertical_margins_were_set_(false),
placeholder_text_color_(kDefaultPlaceholderTextColor),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
weak_ptr_factory_(this) {
set_focusable(true);
if (IsObscured())
SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
if (ViewsDelegate::views_delegate) {
obscured_reveal_duration_ = ViewsDelegate::views_delegate->
GetDefaultTextfieldObscuredRevealDuration();
}
if (NativeViewHost::kRenderNativeControlFocus)
focus_painter_ = Painter::CreateDashedFocusPainter();
}
Textfield::~Textfield() {
}
void Textfield::SetController(TextfieldController* controller) {
controller_ = controller;
}
TextfieldController* Textfield::GetController() const {
return controller_;
}
void Textfield::SetReadOnly(bool read_only) {
// Update read-only without changing the focusable state (or active, etc.).
read_only_ = read_only;
if (native_wrapper_) {
native_wrapper_->UpdateReadOnly();
native_wrapper_->UpdateTextColor();
native_wrapper_->UpdateBackgroundColor();
}
}
bool Textfield::IsObscured() const {
return style_ & STYLE_OBSCURED;
}
void Textfield::SetObscured(bool obscured) {
if (obscured) {
style_ = static_cast<StyleFlags>(style_ | STYLE_OBSCURED);
SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
} else {
style_ = static_cast<StyleFlags>(style_ & ~STYLE_OBSCURED);
SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
}
if (native_wrapper_)
native_wrapper_->UpdateIsObscured();
}
ui::TextInputType Textfield::GetTextInputType() const {
if (read_only() || !enabled())
return ui::TEXT_INPUT_TYPE_NONE;
return text_input_type_;
}
void Textfield::SetTextInputType(ui::TextInputType type) {
text_input_type_ = type;
bool should_be_obscured = type == ui::TEXT_INPUT_TYPE_PASSWORD;
if (IsObscured() != should_be_obscured)
SetObscured(should_be_obscured);
}
void Textfield::SetText(const string16& text) {
text_ = text;
if (native_wrapper_)
native_wrapper_->UpdateText();
}
void Textfield::AppendText(const string16& text) {
text_ += text;
if (native_wrapper_)
native_wrapper_->AppendText(text);
}
void Textfield::InsertOrReplaceText(const string16& text) {
if (native_wrapper_) {
native_wrapper_->InsertOrReplaceText(text);
text_ = native_wrapper_->GetText();
}
}
base::i18n::TextDirection Textfield::GetTextDirection() const {
return native_wrapper_ ?
native_wrapper_->GetTextDirection() : base::i18n::UNKNOWN_DIRECTION;
}
void Textfield::SelectAll(bool reversed) {
if (native_wrapper_)
native_wrapper_->SelectAll(reversed);
}
string16 Textfield::GetSelectedText() const {
return native_wrapper_ ? native_wrapper_->GetSelectedText() : string16();
}
void Textfield::ClearSelection() const {
if (native_wrapper_)
native_wrapper_->ClearSelection();
}
bool Textfield::HasSelection() const {
return native_wrapper_ && !native_wrapper_->GetSelectedRange().is_empty();
}
SkColor Textfield::GetTextColor() const {
if (!use_default_text_color_)
return text_color_;
return GetNativeTheme()->GetSystemColor(read_only() ?
ui::NativeTheme::kColorId_TextfieldReadOnlyColor :
ui::NativeTheme::kColorId_TextfieldDefaultColor);
}
void Textfield::SetTextColor(SkColor color) {
text_color_ = color;
use_default_text_color_ = false;
if (native_wrapper_)
native_wrapper_->UpdateTextColor();
}
void Textfield::UseDefaultTextColor() {
use_default_text_color_ = true;
if (native_wrapper_)
native_wrapper_->UpdateTextColor();
}
SkColor Textfield::GetBackgroundColor() const {
if (!use_default_background_color_)
return background_color_;
return GetNativeTheme()->GetSystemColor(read_only() ?
ui::NativeTheme::kColorId_TextfieldReadOnlyBackground :
ui::NativeTheme::kColorId_TextfieldDefaultBackground);
}
void Textfield::SetBackgroundColor(SkColor color) {
background_color_ = color;
use_default_background_color_ = false;
if (native_wrapper_)
native_wrapper_->UpdateBackgroundColor();
}
void Textfield::UseDefaultBackgroundColor() {
use_default_background_color_ = true;
if (native_wrapper_)
native_wrapper_->UpdateBackgroundColor();
}
bool Textfield::GetCursorEnabled() const {
return native_wrapper_ && native_wrapper_->GetCursorEnabled();
}
void Textfield::SetCursorEnabled(bool enabled) {
if (native_wrapper_)
native_wrapper_->SetCursorEnabled(enabled);
}
void Textfield::SetFontList(const gfx::FontList& font_list) {
font_list_ = font_list;
if (native_wrapper_)
native_wrapper_->UpdateFont();
PreferredSizeChanged();
}
const gfx::Font& Textfield::GetPrimaryFont() const {
return font_list_.GetPrimaryFont();
}
void Textfield::SetFont(const gfx::Font& font) {
SetFontList(gfx::FontList(font));
}
void Textfield::SetHorizontalMargins(int left, int right) {
if (horizontal_margins_were_set_ &&
left == margins_.left() && right == margins_.right()) {
return;
}
margins_.Set(margins_.top(), left, margins_.bottom(), right);
horizontal_margins_were_set_ = true;
if (native_wrapper_)
native_wrapper_->UpdateHorizontalMargins();
PreferredSizeChanged();
}
void Textfield::SetVerticalMargins(int top, int bottom) {
if (vertical_margins_were_set_ &&
top == margins_.top() && bottom == margins_.bottom()) {
return;
}
margins_.Set(top, margins_.left(), bottom, margins_.right());
vertical_margins_were_set_ = true;
if (native_wrapper_)
native_wrapper_->UpdateVerticalMargins();
PreferredSizeChanged();
}
void Textfield::RemoveBorder() {
if (!draw_border_)
return;
draw_border_ = false;
if (native_wrapper_)
native_wrapper_->UpdateBorder();
}
base::string16 Textfield::GetPlaceholderText() const {
return placeholder_text_;
}
bool Textfield::GetHorizontalMargins(int* left, int* right) {
if (!horizontal_margins_were_set_)
return false;
*left = margins_.left();
*right = margins_.right();
return true;
}
bool Textfield::GetVerticalMargins(int* top, int* bottom) {
if (!vertical_margins_were_set_)
return false;
*top = margins_.top();
*bottom = margins_.bottom();
return true;
}
void Textfield::UpdateAllProperties() {
if (native_wrapper_) {
native_wrapper_->UpdateText();
native_wrapper_->UpdateTextColor();
native_wrapper_->UpdateBackgroundColor();
native_wrapper_->UpdateReadOnly();
native_wrapper_->UpdateFont();
native_wrapper_->UpdateEnabled();
native_wrapper_->UpdateBorder();
native_wrapper_->UpdateIsObscured();
native_wrapper_->UpdateHorizontalMargins();
native_wrapper_->UpdateVerticalMargins();
}
}
void Textfield::SyncText() {
if (native_wrapper_) {
string16 new_text = native_wrapper_->GetText();
if (new_text != text_) {
text_ = new_text;
if (controller_)
controller_->ContentsChanged(this, text_);
}
}
}
bool Textfield::IsIMEComposing() const {
return native_wrapper_ && native_wrapper_->IsIMEComposing();
}
gfx::Range Textfield::GetSelectedRange() const {
return native_wrapper_->GetSelectedRange();
}
void Textfield::SelectRange(const gfx::Range& range) {
native_wrapper_->SelectRange(range);
}
gfx::SelectionModel Textfield::GetSelectionModel() const {
return native_wrapper_->GetSelectionModel();
}
void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) {
native_wrapper_->SelectSelectionModel(sel);
}
size_t Textfield::GetCursorPosition() const {
return native_wrapper_->GetCursorPosition();
}
void Textfield::SetColor(SkColor value) {
return native_wrapper_->SetColor(value);
}
void Textfield::ApplyColor(SkColor value, const gfx::Range& range) {
return native_wrapper_->ApplyColor(value, range);
}
void Textfield::SetStyle(gfx::TextStyle style, bool value) {
return native_wrapper_->SetStyle(style, value);
}
void Textfield::ApplyStyle(gfx::TextStyle style,
bool value,
const gfx::Range& range) {
return native_wrapper_->ApplyStyle(style, value, range);
}
void Textfield::ClearEditHistory() {
native_wrapper_->ClearEditHistory();
}
void Textfield::SetAccessibleName(const string16& name) {
accessible_name_ = name;
}
void Textfield::ExecuteCommand(int command_id) {
native_wrapper_->ExecuteTextCommand(command_id);
}
void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
focus_painter_ = focus_painter.Pass();
}
bool Textfield::HasTextBeingDragged() {
return native_wrapper_->HasTextBeingDragged();
}
////////////////////////////////////////////////////////////////////////////////
// Textfield, View overrides:
void Textfield::Layout() {
if (native_wrapper_) {
native_wrapper_->GetView()->SetBoundsRect(GetContentsBounds());
native_wrapper_->GetView()->Layout();
}
}
int Textfield::GetBaseline() const {
gfx::Insets insets = GetTextInsets();
const int baseline = native_wrapper_ ?
native_wrapper_->GetTextfieldBaseline() : font_list_.GetBaseline();
return insets.top() + baseline;
}
gfx::Size Textfield::GetPreferredSize() {
gfx::Insets insets = GetTextInsets();
const int font_height = native_wrapper_ ? native_wrapper_->GetFontHeight() :
font_list_.GetHeight();
return gfx::Size(
GetPrimaryFont().GetExpectedTextWidth(default_width_in_chars_)
+ insets.width(),
font_height + insets.height());
}
void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
SelectAll(false);
}
bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
// Skip any accelerator handling of backspace; textfields handle this key.
// Also skip processing of [Alt]+<num-pad digit> Unicode alt key codes.
return e.key_code() == ui::VKEY_BACK || e.IsUnicodeKeyCode();
}
void Textfield::OnPaint(gfx::Canvas* canvas) {
View::OnPaint(canvas);
if (NativeViewHost::kRenderNativeControlFocus)
Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
}
bool Textfield::OnKeyPressed(const ui::KeyEvent& e) {
return native_wrapper_ && native_wrapper_->HandleKeyPressed(e);
}
bool Textfield::OnKeyReleased(const ui::KeyEvent& e) {
return native_wrapper_ && native_wrapper_->HandleKeyReleased(e);
}
bool Textfield::OnMouseDragged(const ui::MouseEvent& e) {
if (!e.IsOnlyRightMouseButton())
return View::OnMouseDragged(e);
return true;
}
void Textfield::OnFocus() {
if (native_wrapper_)
native_wrapper_->HandleFocus();
// Forward the focus to the wrapper if it exists.
if (!native_wrapper_ || !native_wrapper_->SetFocus()) {
// If there is no wrapper or the wrapper didn't take focus, call
// View::Focus to clear the native focus so that we still get
// keyboard messages.
View::OnFocus();
}
// Border typically draws focus indicator.
SchedulePaint();
}
void Textfield::OnBlur() {
if (native_wrapper_)
native_wrapper_->HandleBlur();
// Border typically draws focus indicator.
SchedulePaint();
}
void Textfield::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_TEXT;
state->name = accessible_name_;
if (read_only())
state->state |= ui::AccessibilityTypes::STATE_READONLY;
if (IsObscured())
state->state |= ui::AccessibilityTypes::STATE_PROTECTED;
state->value = text_;
const gfx::Range range = native_wrapper_->GetSelectedRange();
state->selection_start = range.start();
state->selection_end = range.end();
if (!read_only()) {
state->set_value_callback =
base::Bind(&Textfield::AccessibilitySetValue,
weak_ptr_factory_.GetWeakPtr());
}
}
ui::TextInputClient* Textfield::GetTextInputClient() {
return native_wrapper_ ? native_wrapper_->GetTextInputClient() : NULL;
}
gfx::Point Textfield::GetKeyboardContextMenuLocation() {
return native_wrapper_ ? native_wrapper_->GetContextMenuLocation() :
View::GetKeyboardContextMenuLocation();
}
void Textfield::OnEnabledChanged() {
View::OnEnabledChanged();
if (native_wrapper_)
native_wrapper_->UpdateEnabled();
}
void Textfield::ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) {
if (details.is_add && !native_wrapper_ && GetWidget()) {
// The native wrapper's lifetime will be managed by the view hierarchy after
// we call AddChildView.
native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this);
AddChildViewAt(native_wrapper_->GetView(), 0);
Layout();
UpdateAllProperties();
}
}
const char* Textfield::GetClassName() const {
return kViewClassName;
}
////////////////////////////////////////////////////////////////////////////////
// Textfield, private:
gfx::Insets Textfield::GetTextInsets() const {
gfx::Insets insets = GetInsets();
if (draw_border_ && native_wrapper_)
insets += native_wrapper_->CalculateInsets();
return insets;
}
void Textfield::AccessibilitySetValue(const string16& new_value) {
if (!read_only()) {
SetText(new_value);
ClearSelection();
}
}
////////////////////////////////////////////////////////////////////////////////
// NativeTextfieldWrapper, public:
// static
NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper(
Textfield* field) {
return new NativeTextfieldViews(field);
}
} // namespace views