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