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