1// Copyright (c) 2012 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 "ui/views/controls/textfield/textfield.h" 6 7#include <string> 8 9#include "base/command_line.h" 10#include "base/strings/string_util.h" 11#include "base/strings/utf_string_conversions.h" 12#include "ui/base/accessibility/accessible_view_state.h" 13#include "ui/base/events/event.h" 14#include "ui/base/ime/text_input_type.h" 15#include "ui/base/keycodes/keyboard_codes.h" 16#include "ui/base/range/range.h" 17#include "ui/base/resource/resource_bundle.h" 18#include "ui/base/ui_base_switches.h" 19#include "ui/base/ui_base_switches_util.h" 20#include "ui/gfx/insets.h" 21#include "ui/gfx/selection_model.h" 22#include "ui/native_theme/native_theme.h" 23#include "ui/views/controls/native/native_view_host.h" 24#include "ui/views/controls/textfield/native_textfield_views.h" 25#include "ui/views/controls/textfield/native_textfield_wrapper.h" 26#include "ui/views/controls/textfield/textfield_controller.h" 27#include "ui/views/views_delegate.h" 28#include "ui/views/widget/widget.h" 29 30#if defined(OS_WIN) 31#include "base/win/win_util.h" 32// TODO(beng): this should be removed when the OS_WIN hack from 33// ViewHierarchyChanged is removed. 34#include "ui/views/controls/textfield/native_textfield_win.h" 35#endif 36 37namespace { 38 39// Default placeholder text color. 40const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; 41 42gfx::FontList GetDefaultFontList() { 43 return ResourceBundle::GetSharedInstance().GetFontList( 44 ResourceBundle::BaseFont); 45} 46 47} // namespace 48 49namespace views { 50 51///////////////////////////////////////////////////////////////////////////// 52// Textfield 53 54// static 55const char Textfield::kViewClassName[] = "Textfield"; 56 57// static 58bool Textfield::IsViewsTextfieldEnabled() { 59#if defined(OS_WIN) && !defined(USE_AURA) 60 CommandLine* command_line = CommandLine::ForCurrentProcess(); 61 if (command_line->HasSwitch(switches::kDisableViewsTextfield)) 62 return false; 63 if (command_line->HasSwitch(switches::kEnableViewsTextfield)) 64 return true; 65 // The new dialog style cannot host native Windows textfield controls. 66 if (switches::IsNewDialogStyleEnabled()) 67 return true; 68 // Avoid native Windows Textfields if the RichEdit library is not available. 69 static const HMODULE loaded_msftedit_dll = LoadLibrary(L"msftedit.dll"); 70 if (!loaded_msftedit_dll) 71 return true; 72#endif 73 return true; 74} 75 76Textfield::Textfield() 77 : native_wrapper_(NULL), 78 controller_(NULL), 79 style_(STYLE_DEFAULT), 80 font_list_(GetDefaultFontList()), 81 read_only_(false), 82 default_width_in_chars_(0), 83 draw_border_(true), 84 text_color_(SK_ColorBLACK), 85 use_default_text_color_(true), 86 background_color_(SK_ColorWHITE), 87 use_default_background_color_(true), 88 horizontal_margins_were_set_(false), 89 vertical_margins_were_set_(false), 90 vertical_alignment_(gfx::ALIGN_VCENTER), 91 placeholder_text_color_(kDefaultPlaceholderTextColor), 92 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), 93 weak_ptr_factory_(this) { 94 set_focusable(true); 95 96 if (ViewsDelegate::views_delegate) { 97 obscured_reveal_duration_ = ViewsDelegate::views_delegate-> 98 GetDefaultTextfieldObscuredRevealDuration(); 99 } 100} 101 102Textfield::Textfield(StyleFlags style) 103 : native_wrapper_(NULL), 104 controller_(NULL), 105 style_(style), 106 font_list_(GetDefaultFontList()), 107 read_only_(false), 108 default_width_in_chars_(0), 109 draw_border_(true), 110 text_color_(SK_ColorBLACK), 111 use_default_text_color_(true), 112 background_color_(SK_ColorWHITE), 113 use_default_background_color_(true), 114 horizontal_margins_were_set_(false), 115 vertical_margins_were_set_(false), 116 vertical_alignment_(gfx::ALIGN_VCENTER), 117 placeholder_text_color_(kDefaultPlaceholderTextColor), 118 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), 119 weak_ptr_factory_(this) { 120 set_focusable(true); 121 if (IsObscured()) 122 SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); 123 124 if (ViewsDelegate::views_delegate) { 125 obscured_reveal_duration_ = ViewsDelegate::views_delegate-> 126 GetDefaultTextfieldObscuredRevealDuration(); 127 } 128} 129 130Textfield::~Textfield() { 131} 132 133void Textfield::SetController(TextfieldController* controller) { 134 controller_ = controller; 135} 136 137TextfieldController* Textfield::GetController() const { 138 return controller_; 139} 140 141void Textfield::SetReadOnly(bool read_only) { 142 // Update read-only without changing the focusable state (or active, etc.). 143 read_only_ = read_only; 144 if (native_wrapper_) { 145 native_wrapper_->UpdateReadOnly(); 146 native_wrapper_->UpdateTextColor(); 147 native_wrapper_->UpdateBackgroundColor(); 148 } 149} 150 151bool Textfield::IsObscured() const { 152 return style_ & STYLE_OBSCURED; 153} 154 155void Textfield::SetObscured(bool obscured) { 156 if (obscured) { 157 style_ = static_cast<StyleFlags>(style_ | STYLE_OBSCURED); 158 SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); 159 } else { 160 style_ = static_cast<StyleFlags>(style_ & ~STYLE_OBSCURED); 161 SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); 162 } 163 if (native_wrapper_) 164 native_wrapper_->UpdateIsObscured(); 165} 166 167ui::TextInputType Textfield::GetTextInputType() const { 168 if (read_only() || !enabled()) 169 return ui::TEXT_INPUT_TYPE_NONE; 170 return text_input_type_; 171} 172 173void Textfield::SetTextInputType(ui::TextInputType type) { 174 text_input_type_ = type; 175 bool should_be_obscured = type == ui::TEXT_INPUT_TYPE_PASSWORD; 176 if (IsObscured() != should_be_obscured) 177 SetObscured(should_be_obscured); 178} 179 180void Textfield::SetText(const string16& text) { 181 text_ = text; 182 if (native_wrapper_) 183 native_wrapper_->UpdateText(); 184} 185 186void Textfield::AppendText(const string16& text) { 187 text_ += text; 188 if (native_wrapper_) 189 native_wrapper_->AppendText(text); 190} 191 192void Textfield::InsertOrReplaceText(const string16& text) { 193 if (native_wrapper_) { 194 native_wrapper_->InsertOrReplaceText(text); 195 text_ = native_wrapper_->GetText(); 196 } 197} 198 199base::i18n::TextDirection Textfield::GetTextDirection() const { 200 return native_wrapper_ ? 201 native_wrapper_->GetTextDirection() : base::i18n::UNKNOWN_DIRECTION; 202} 203 204void Textfield::SelectAll(bool reversed) { 205 if (native_wrapper_) 206 native_wrapper_->SelectAll(reversed); 207} 208 209string16 Textfield::GetSelectedText() const { 210 return native_wrapper_ ? native_wrapper_->GetSelectedText() : string16(); 211} 212 213void Textfield::ClearSelection() const { 214 if (native_wrapper_) 215 native_wrapper_->ClearSelection(); 216} 217 218bool Textfield::HasSelection() const { 219 return native_wrapper_ && !native_wrapper_->GetSelectedRange().is_empty(); 220} 221 222SkColor Textfield::GetTextColor() const { 223 if (!use_default_text_color_) 224 return text_color_; 225 226 return GetNativeTheme()->GetSystemColor(read_only() ? 227 ui::NativeTheme::kColorId_TextfieldReadOnlyColor : 228 ui::NativeTheme::kColorId_TextfieldDefaultColor); 229} 230 231void Textfield::SetTextColor(SkColor color) { 232 text_color_ = color; 233 use_default_text_color_ = false; 234 if (native_wrapper_) 235 native_wrapper_->UpdateTextColor(); 236} 237 238void Textfield::UseDefaultTextColor() { 239 use_default_text_color_ = true; 240 if (native_wrapper_) 241 native_wrapper_->UpdateTextColor(); 242} 243 244SkColor Textfield::GetBackgroundColor() const { 245 if (!use_default_background_color_) 246 return background_color_; 247 248 return GetNativeTheme()->GetSystemColor(read_only() ? 249 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground : 250 ui::NativeTheme::kColorId_TextfieldDefaultBackground); 251} 252 253void Textfield::SetBackgroundColor(SkColor color) { 254 background_color_ = color; 255 use_default_background_color_ = false; 256 if (native_wrapper_) 257 native_wrapper_->UpdateBackgroundColor(); 258} 259 260void Textfield::UseDefaultBackgroundColor() { 261 use_default_background_color_ = true; 262 if (native_wrapper_) 263 native_wrapper_->UpdateBackgroundColor(); 264} 265 266bool Textfield::GetCursorEnabled() const { 267 return native_wrapper_ && native_wrapper_->GetCursorEnabled(); 268} 269 270void Textfield::SetCursorEnabled(bool enabled) { 271 if (native_wrapper_) 272 native_wrapper_->SetCursorEnabled(enabled); 273} 274 275void Textfield::SetFontList(const gfx::FontList& font_list) { 276 font_list_ = font_list; 277 if (native_wrapper_) 278 native_wrapper_->UpdateFont(); 279 PreferredSizeChanged(); 280} 281 282const gfx::Font& Textfield::GetPrimaryFont() const { 283 return font_list_.GetPrimaryFont(); 284} 285 286void Textfield::SetFont(const gfx::Font& font) { 287 SetFontList(gfx::FontList(font)); 288} 289 290void Textfield::SetHorizontalMargins(int left, int right) { 291 margins_.Set(margins_.top(), left, margins_.bottom(), right); 292 horizontal_margins_were_set_ = true; 293 if (native_wrapper_) 294 native_wrapper_->UpdateHorizontalMargins(); 295 PreferredSizeChanged(); 296} 297 298void Textfield::SetVerticalMargins(int top, int bottom) { 299 margins_.Set(top, margins_.left(), bottom, margins_.right()); 300 vertical_margins_were_set_ = true; 301 if (native_wrapper_) 302 native_wrapper_->UpdateVerticalMargins(); 303 PreferredSizeChanged(); 304} 305 306void Textfield::SetVerticalAlignment(gfx::VerticalAlignment alignment) { 307 vertical_alignment_ = alignment; 308 if (native_wrapper_) 309 native_wrapper_->UpdateVerticalAlignment(); 310} 311 312void Textfield::RemoveBorder() { 313 if (!draw_border_) 314 return; 315 316 draw_border_ = false; 317 if (native_wrapper_) 318 native_wrapper_->UpdateBorder(); 319} 320 321bool Textfield::GetHorizontalMargins(int* left, int* right) { 322 if (!horizontal_margins_were_set_) 323 return false; 324 325 *left = margins_.left(); 326 *right = margins_.right(); 327 return true; 328} 329 330bool Textfield::GetVerticalMargins(int* top, int* bottom) { 331 if (!vertical_margins_were_set_) 332 return false; 333 334 *top = margins_.top(); 335 *bottom = margins_.bottom(); 336 return true; 337} 338 339void Textfield::UpdateAllProperties() { 340 if (native_wrapper_) { 341 native_wrapper_->UpdateText(); 342 native_wrapper_->UpdateTextColor(); 343 native_wrapper_->UpdateBackgroundColor(); 344 native_wrapper_->UpdateReadOnly(); 345 native_wrapper_->UpdateFont(); 346 native_wrapper_->UpdateEnabled(); 347 native_wrapper_->UpdateBorder(); 348 native_wrapper_->UpdateIsObscured(); 349 native_wrapper_->UpdateHorizontalMargins(); 350 native_wrapper_->UpdateVerticalMargins(); 351 native_wrapper_->UpdateVerticalAlignment(); 352 } 353} 354 355void Textfield::SyncText() { 356 if (native_wrapper_) { 357 string16 new_text = native_wrapper_->GetText(); 358 if (new_text != text_) { 359 text_ = new_text; 360 if (controller_) 361 controller_->ContentsChanged(this, text_); 362 } 363 } 364} 365 366bool Textfield::IsIMEComposing() const { 367 return native_wrapper_ && native_wrapper_->IsIMEComposing(); 368} 369 370ui::Range Textfield::GetSelectedRange() const { 371 return native_wrapper_->GetSelectedRange(); 372} 373 374void Textfield::SelectRange(const ui::Range& range) { 375 native_wrapper_->SelectRange(range); 376} 377 378gfx::SelectionModel Textfield::GetSelectionModel() const { 379 return native_wrapper_->GetSelectionModel(); 380} 381 382void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) { 383 native_wrapper_->SelectSelectionModel(sel); 384} 385 386size_t Textfield::GetCursorPosition() const { 387 return native_wrapper_->GetCursorPosition(); 388} 389 390void Textfield::SetColor(SkColor value) { 391 return native_wrapper_->SetColor(value); 392} 393 394void Textfield::ApplyColor(SkColor value, const ui::Range& range) { 395 return native_wrapper_->ApplyColor(value, range); 396} 397 398void Textfield::SetStyle(gfx::TextStyle style, bool value) { 399 return native_wrapper_->SetStyle(style, value); 400} 401 402void Textfield::ApplyStyle(gfx::TextStyle style, 403 bool value, 404 const ui::Range& range) { 405 return native_wrapper_->ApplyStyle(style, value, range); 406} 407 408void Textfield::ClearEditHistory() { 409 native_wrapper_->ClearEditHistory(); 410} 411 412void Textfield::SetAccessibleName(const string16& name) { 413 accessible_name_ = name; 414} 415 416void Textfield::ExecuteCommand(int command_id) { 417 native_wrapper_->ExecuteTextCommand(command_id); 418} 419 420bool Textfield::HasTextBeingDragged() { 421 return native_wrapper_->HasTextBeingDragged(); 422} 423 424//////////////////////////////////////////////////////////////////////////////// 425// Textfield, View overrides: 426 427void Textfield::Layout() { 428 if (native_wrapper_) { 429 native_wrapper_->GetView()->SetBoundsRect(GetContentsBounds()); 430 native_wrapper_->GetView()->Layout(); 431 } 432} 433 434int Textfield::GetBaseline() const { 435 gfx::Insets insets = GetTextInsets(); 436 const int baseline = native_wrapper_ ? 437 native_wrapper_->GetTextfieldBaseline() : font_list_.GetBaseline(); 438 return insets.top() + baseline; 439} 440 441gfx::Size Textfield::GetPreferredSize() { 442 gfx::Insets insets = GetTextInsets(); 443 444 const int font_height = native_wrapper_ ? native_wrapper_->GetFontHeight() : 445 font_list_.GetHeight(); 446 return gfx::Size( 447 GetPrimaryFont().GetExpectedTextWidth(default_width_in_chars_) 448 + insets.width(), 449 font_height + insets.height()); 450} 451 452void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { 453 SelectAll(false); 454} 455 456bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 457 // Skip any accelerator handling of backspace; textfields handle this key. 458 // Also skip processing of [Alt]+<num-pad digit> Unicode alt key codes. 459 return e.key_code() == ui::VKEY_BACK || e.IsUnicodeKeyCode(); 460} 461 462void Textfield::OnPaintFocusBorder(gfx::Canvas* canvas) { 463 if (NativeViewHost::kRenderNativeControlFocus) 464 View::OnPaintFocusBorder(canvas); 465} 466 467bool Textfield::OnKeyPressed(const ui::KeyEvent& e) { 468 return native_wrapper_ && native_wrapper_->HandleKeyPressed(e); 469} 470 471bool Textfield::OnKeyReleased(const ui::KeyEvent& e) { 472 return native_wrapper_ && native_wrapper_->HandleKeyReleased(e); 473} 474 475bool Textfield::OnMouseDragged(const ui::MouseEvent& e) { 476 if (!e.IsOnlyRightMouseButton()) 477 return View::OnMouseDragged(e); 478 return true; 479} 480 481void Textfield::OnFocus() { 482 if (native_wrapper_) 483 native_wrapper_->HandleFocus(); 484 485 // Forward the focus to the wrapper if it exists. 486 if (!native_wrapper_ || !native_wrapper_->SetFocus()) { 487 // If there is no wrapper or the wrapper didn't take focus, call 488 // View::Focus to clear the native focus so that we still get 489 // keyboard messages. 490 View::OnFocus(); 491 } 492} 493 494void Textfield::OnBlur() { 495 if (native_wrapper_) 496 native_wrapper_->HandleBlur(); 497} 498 499void Textfield::GetAccessibleState(ui::AccessibleViewState* state) { 500 state->role = ui::AccessibilityTypes::ROLE_TEXT; 501 state->name = accessible_name_; 502 if (read_only()) 503 state->state |= ui::AccessibilityTypes::STATE_READONLY; 504 if (IsObscured()) 505 state->state |= ui::AccessibilityTypes::STATE_PROTECTED; 506 state->value = text_; 507 508 const ui::Range range = native_wrapper_->GetSelectedRange(); 509 state->selection_start = range.start(); 510 state->selection_end = range.end(); 511 512 if (!read_only()) { 513 state->set_value_callback = 514 base::Bind(&Textfield::AccessibilitySetValue, 515 weak_ptr_factory_.GetWeakPtr()); 516 } 517} 518 519ui::TextInputClient* Textfield::GetTextInputClient() { 520 return native_wrapper_ ? native_wrapper_->GetTextInputClient() : NULL; 521} 522 523void Textfield::OnEnabledChanged() { 524 View::OnEnabledChanged(); 525 if (native_wrapper_) 526 native_wrapper_->UpdateEnabled(); 527} 528 529void Textfield::ViewHierarchyChanged( 530 const ViewHierarchyChangedDetails& details) { 531 if (details.is_add && !native_wrapper_ && GetWidget()) { 532 // The native wrapper's lifetime will be managed by the view hierarchy after 533 // we call AddChildView. 534 native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this); 535 AddChildViewAt(native_wrapper_->GetView(), 0); 536 Layout(); 537 538 // TODO(beng): Move this initialization to NativeTextfieldWin once it 539 // subclasses NativeControlWin. 540 UpdateAllProperties(); 541 542#if defined(OS_WIN) && !defined(USE_AURA) 543 // TODO(beng): Remove this once NativeTextfieldWin subclasses 544 // NativeControlWin. This is currently called to perform post-AddChildView 545 // initialization for the wrapper. 546 // 547 // Remove the include for native_textfield_win.h above when you fix this. 548 if (!IsViewsTextfieldEnabled()) 549 static_cast<NativeTextfieldWin*>(native_wrapper_)->AttachHack(); 550#endif 551 } 552} 553 554const char* Textfield::GetClassName() const { 555 return kViewClassName; 556} 557 558//////////////////////////////////////////////////////////////////////////////// 559// Textfield, private: 560 561gfx::Insets Textfield::GetTextInsets() const { 562 gfx::Insets insets = GetInsets(); 563 if (draw_border_ && native_wrapper_) 564 insets += native_wrapper_->CalculateInsets(); 565 return insets; 566} 567 568void Textfield::AccessibilitySetValue(const string16& new_value) { 569 if (!read_only()) { 570 SetText(new_value); 571 ClearSelection(); 572 } 573} 574 575//////////////////////////////////////////////////////////////////////////////// 576// NativeTextfieldWrapper, public: 577 578// static 579NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper( 580 Textfield* field) { 581#if defined(OS_WIN) && !defined(USE_AURA) 582 if (!Textfield::IsViewsTextfieldEnabled()) 583 return new NativeTextfieldWin(field); 584#endif 585 return new NativeTextfieldViews(field); 586} 587 588} // namespace views 589