OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/controls/textfield/textfield.h" | 5 #include "ui/views/controls/textfield/textfield.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/strings/string_util.h" | 10 #include "base/i18n/case_conversion.h" |
11 #include "base/strings/utf_string_conversions.h" | 11 #include "grit/ui_strings.h" |
12 #include "ui/base/accessibility/accessible_view_state.h" | 12 #include "ui/base/accessibility/accessible_view_state.h" |
13 #include "ui/base/ime/text_input_type.h" | 13 #include "ui/base/dragdrop/drag_drop_types.h" |
| 14 #include "ui/base/dragdrop/drag_utils.h" |
14 #include "ui/base/resource/resource_bundle.h" | 15 #include "ui/base/resource/resource_bundle.h" |
15 #include "ui/base/ui_base_switches.h" | 16 #include "ui/base/ui_base_switches_util.h" |
16 #include "ui/events/event.h" | 17 #include "ui/events/event.h" |
17 #include "ui/events/keycodes/keyboard_codes.h" | 18 #include "ui/events/keycodes/keyboard_codes.h" |
| 19 #include "ui/gfx/canvas.h" |
18 #include "ui/gfx/insets.h" | 20 #include "ui/gfx/insets.h" |
19 #include "ui/gfx/range/range.h" | |
20 #include "ui/gfx/selection_model.h" | |
21 #include "ui/native_theme/native_theme.h" | 21 #include "ui/native_theme/native_theme.h" |
| 22 #include "ui/views/background.h" |
| 23 #include "ui/views/controls/focusable_border.h" |
| 24 #include "ui/views/controls/menu/menu_item_view.h" |
| 25 #include "ui/views/controls/menu/menu_model_adapter.h" |
| 26 #include "ui/views/controls/menu/menu_runner.h" |
22 #include "ui/views/controls/native/native_view_host.h" | 27 #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/textfield_controller.h" | 28 #include "ui/views/controls/textfield/textfield_controller.h" |
| 29 #include "ui/views/drag_utils.h" |
| 30 #include "ui/views/ime/input_method.h" |
| 31 #include "ui/views/metrics.h" |
25 #include "ui/views/painter.h" | 32 #include "ui/views/painter.h" |
26 #include "ui/views/views_delegate.h" | 33 #include "ui/views/views_delegate.h" |
27 #include "ui/views/widget/widget.h" | 34 #include "ui/views/widget/widget.h" |
28 | 35 |
| 36 #if defined(USE_AURA) |
| 37 #include "ui/base/cursor/cursor.h" |
| 38 #endif |
| 39 |
| 40 #if defined(OS_WIN) && defined(USE_AURA) |
| 41 #include "base/win/win_util.h" |
| 42 #endif |
| 43 |
29 namespace { | 44 namespace { |
30 | 45 |
31 // Default placeholder text color. | 46 // Default placeholder text color. |
32 const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; | 47 const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; |
33 | 48 |
34 gfx::FontList GetDefaultFontList() { | 49 void ConvertRectToScreen(const views::View* src, gfx::Rect* r) { |
35 return ResourceBundle::GetSharedInstance().GetFontList( | 50 DCHECK(src); |
36 ResourceBundle::BaseFont); | 51 |
| 52 gfx::Point new_origin = r->origin(); |
| 53 views::View::ConvertPointToScreen(src, &new_origin); |
| 54 r->set_origin(new_origin); |
37 } | 55 } |
38 | 56 |
39 } // namespace | 57 } // namespace |
40 | 58 |
41 namespace views { | 59 namespace views { |
42 | 60 |
43 // static | 61 // static |
44 const char Textfield::kViewClassName[] = "Textfield"; | 62 const char Textfield::kViewClassName[] = "Textfield"; |
45 | 63 |
46 // static | 64 // static |
47 size_t Textfield::GetCaretBlinkMs() { | 65 size_t Textfield::GetCaretBlinkMs() { |
48 static const size_t default_value = 500; | 66 static const size_t default_value = 500; |
49 #if defined(OS_WIN) | 67 #if defined(OS_WIN) |
50 static const size_t system_value = ::GetCaretBlinkTime(); | 68 static const size_t system_value = ::GetCaretBlinkTime(); |
51 if (system_value != 0) | 69 if (system_value != 0) |
52 return (system_value == INFINITE) ? 0 : system_value; | 70 return (system_value == INFINITE) ? 0 : system_value; |
53 #endif | 71 #endif |
54 return default_value; | 72 return default_value; |
55 } | 73 } |
56 | 74 |
57 Textfield::Textfield() | 75 Textfield::Textfield() |
58 : textfield_view_(NULL), | 76 : model_(new TextfieldViewsModel(this)), |
59 controller_(NULL), | 77 controller_(NULL), |
60 style_(STYLE_DEFAULT), | 78 style_(STYLE_DEFAULT), |
61 font_list_(GetDefaultFontList()), | |
62 read_only_(false), | 79 read_only_(false), |
63 default_width_in_chars_(0), | 80 default_width_in_chars_(0), |
| 81 text_border_(new FocusableBorder()), |
64 draw_border_(true), | 82 draw_border_(true), |
65 text_color_(SK_ColorBLACK), | 83 text_color_(SK_ColorBLACK), |
66 use_default_text_color_(true), | 84 use_default_text_color_(true), |
67 background_color_(SK_ColorWHITE), | 85 background_color_(SK_ColorWHITE), |
68 use_default_background_color_(true), | 86 use_default_background_color_(true), |
69 horizontal_margins_were_set_(false), | 87 horizontal_margins_were_set_(false), |
70 vertical_margins_were_set_(false), | |
71 placeholder_text_color_(kDefaultPlaceholderTextColor), | 88 placeholder_text_color_(kDefaultPlaceholderTextColor), |
72 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), | 89 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), |
73 weak_ptr_factory_(this) { | 90 skip_input_method_cancel_composition_(false), |
| 91 is_cursor_visible_(false), |
| 92 is_drop_cursor_visible_(false), |
| 93 initiating_drag_(false), |
| 94 aggregated_clicks_(0), |
| 95 weak_ptr_factory_(this), |
| 96 cursor_weak_ptr_factory_(this) { |
| 97 set_context_menu_controller(this); |
| 98 set_drag_controller(this); |
| 99 set_border(text_border_); |
74 SetFocusable(true); | 100 SetFocusable(true); |
75 | 101 |
76 if (ViewsDelegate::views_delegate) { | 102 if (ViewsDelegate::views_delegate) { |
77 obscured_reveal_duration_ = ViewsDelegate::views_delegate-> | 103 obscured_reveal_duration_ = ViewsDelegate::views_delegate-> |
78 GetDefaultTextfieldObscuredRevealDuration(); | 104 GetDefaultTextfieldObscuredRevealDuration(); |
79 } | 105 } |
80 | 106 |
81 if (NativeViewHost::kRenderNativeControlFocus) | 107 if (NativeViewHost::kRenderNativeControlFocus) |
82 focus_painter_ = Painter::CreateDashedFocusPainter(); | 108 focus_painter_ = Painter::CreateDashedFocusPainter(); |
83 } | 109 } |
84 | 110 |
85 Textfield::Textfield(StyleFlags style) | 111 Textfield::Textfield(StyleFlags style) |
86 : textfield_view_(NULL), | 112 : model_(new TextfieldViewsModel(this)), |
87 controller_(NULL), | 113 controller_(NULL), |
88 style_(style), | 114 style_(style), |
89 font_list_(GetDefaultFontList()), | |
90 read_only_(false), | 115 read_only_(false), |
91 default_width_in_chars_(0), | 116 default_width_in_chars_(0), |
| 117 text_border_(new FocusableBorder()), |
92 draw_border_(true), | 118 draw_border_(true), |
93 text_color_(SK_ColorBLACK), | 119 text_color_(SK_ColorBLACK), |
94 use_default_text_color_(true), | 120 use_default_text_color_(true), |
95 background_color_(SK_ColorWHITE), | 121 background_color_(SK_ColorWHITE), |
96 use_default_background_color_(true), | 122 use_default_background_color_(true), |
97 horizontal_margins_were_set_(false), | 123 horizontal_margins_were_set_(false), |
98 vertical_margins_were_set_(false), | |
99 placeholder_text_color_(kDefaultPlaceholderTextColor), | 124 placeholder_text_color_(kDefaultPlaceholderTextColor), |
100 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), | 125 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), |
101 weak_ptr_factory_(this) { | 126 skip_input_method_cancel_composition_(false), |
| 127 is_cursor_visible_(false), |
| 128 is_drop_cursor_visible_(false), |
| 129 initiating_drag_(false), |
| 130 aggregated_clicks_(0), |
| 131 weak_ptr_factory_(this), |
| 132 cursor_weak_ptr_factory_(this) { |
| 133 set_context_menu_controller(this); |
| 134 set_drag_controller(this); |
| 135 set_border(text_border_); |
102 SetFocusable(true); | 136 SetFocusable(true); |
| 137 |
103 if (IsObscured()) | 138 if (IsObscured()) |
104 SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); | 139 SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); |
105 | 140 |
106 if (ViewsDelegate::views_delegate) { | 141 if (ViewsDelegate::views_delegate) { |
107 obscured_reveal_duration_ = ViewsDelegate::views_delegate-> | 142 obscured_reveal_duration_ = ViewsDelegate::views_delegate-> |
108 GetDefaultTextfieldObscuredRevealDuration(); | 143 GetDefaultTextfieldObscuredRevealDuration(); |
109 } | 144 } |
110 | 145 |
111 if (NativeViewHost::kRenderNativeControlFocus) | 146 if (NativeViewHost::kRenderNativeControlFocus) |
112 focus_painter_ = Painter::CreateDashedFocusPainter(); | 147 focus_painter_ = Painter::CreateDashedFocusPainter(); |
113 } | 148 } |
114 | 149 |
115 Textfield::~Textfield() { | 150 Textfield::~Textfield() { |
116 } | 151 } |
117 | 152 |
118 void Textfield::SetController(TextfieldController* controller) { | 153 void Textfield::SetController(TextfieldController* controller) { |
119 controller_ = controller; | 154 controller_ = controller; |
120 } | 155 } |
121 | 156 |
122 TextfieldController* Textfield::GetController() const { | 157 TextfieldController* Textfield::GetController() const { |
123 return controller_; | 158 return controller_; |
124 } | 159 } |
125 | 160 |
126 void Textfield::SetReadOnly(bool read_only) { | 161 void Textfield::SetReadOnly(bool read_only) { |
127 // Update read-only without changing the focusable state (or active, etc.). | 162 // Update read-only without changing the focusable state (or active, etc.). |
128 read_only_ = read_only; | 163 read_only_ = read_only; |
129 if (textfield_view_) { | 164 if (GetInputMethod()) |
130 textfield_view_->UpdateReadOnly(); | 165 GetInputMethod()->OnTextInputTypeChanged(this); |
131 textfield_view_->UpdateTextColor(); | 166 SetColor(GetTextColor()); |
132 textfield_view_->UpdateBackgroundColor(); | 167 UpdateBackgroundColor(); |
133 } | |
134 } | 168 } |
135 | 169 |
136 bool Textfield::IsObscured() const { | 170 bool Textfield::IsObscured() const { |
137 return style_ & STYLE_OBSCURED; | 171 return style_ & STYLE_OBSCURED; |
138 } | 172 } |
139 | 173 |
140 void Textfield::SetObscured(bool obscured) { | 174 void Textfield::SetObscured(bool obscured) { |
141 if (obscured) { | 175 if (obscured) { |
142 style_ = static_cast<StyleFlags>(style_ | STYLE_OBSCURED); | 176 style_ = static_cast<StyleFlags>(style_ | STYLE_OBSCURED); |
143 SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); | 177 SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); |
144 } else { | 178 } else { |
145 style_ = static_cast<StyleFlags>(style_ & ~STYLE_OBSCURED); | 179 style_ = static_cast<StyleFlags>(style_ & ~STYLE_OBSCURED); |
146 SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); | 180 SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); |
147 } | 181 } |
148 if (textfield_view_) | 182 GetRenderText()->SetObscured(obscured); |
149 textfield_view_->UpdateIsObscured(); | 183 OnCaretBoundsChanged(); |
150 } | 184 if (GetInputMethod()) |
151 | 185 GetInputMethod()->OnTextInputTypeChanged(this); |
152 ui::TextInputType Textfield::GetTextInputType() const { | 186 SchedulePaint(); |
153 if (read_only() || !enabled()) | |
154 return ui::TEXT_INPUT_TYPE_NONE; | |
155 return text_input_type_; | |
156 } | 187 } |
157 | 188 |
158 void Textfield::SetTextInputType(ui::TextInputType type) { | 189 void Textfield::SetTextInputType(ui::TextInputType type) { |
159 text_input_type_ = type; | 190 text_input_type_ = type; |
160 bool should_be_obscured = type == ui::TEXT_INPUT_TYPE_PASSWORD; | 191 bool should_be_obscured = type == ui::TEXT_INPUT_TYPE_PASSWORD; |
161 if (IsObscured() != should_be_obscured) | 192 if (IsObscured() != should_be_obscured) |
162 SetObscured(should_be_obscured); | 193 SetObscured(should_be_obscured); |
163 } | 194 } |
164 | 195 |
| 196 const base::string16& Textfield::GetText() const { |
| 197 return model_->GetText(); |
| 198 } |
| 199 |
165 void Textfield::SetText(const base::string16& text) { | 200 void Textfield::SetText(const base::string16& text) { |
166 text_ = text; | 201 model_->SetText(GetTextForDisplay(text)); |
167 if (textfield_view_) | 202 OnCaretBoundsChanged(); |
168 textfield_view_->UpdateText(); | 203 SchedulePaint(); |
| 204 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); |
169 } | 205 } |
170 | 206 |
171 void Textfield::AppendText(const base::string16& text) { | 207 void Textfield::AppendText(const base::string16& text) { |
172 text_ += text; | 208 if (text.empty()) |
173 if (textfield_view_) | 209 return; |
174 textfield_view_->AppendText(text); | 210 model_->Append(GetTextForDisplay(text)); |
| 211 OnCaretBoundsChanged(); |
| 212 SchedulePaint(); |
175 } | 213 } |
176 | 214 |
177 void Textfield::InsertOrReplaceText(const base::string16& text) { | 215 void Textfield::InsertOrReplaceText(const base::string16& text) { |
178 if (textfield_view_) { | 216 if (text.empty()) |
179 textfield_view_->InsertOrReplaceText(text); | 217 return; |
180 text_ = textfield_view_->GetText(); | 218 model_->InsertText(text); |
181 } | 219 OnCaretBoundsChanged(); |
| 220 SchedulePaint(); |
182 } | 221 } |
183 | 222 |
184 base::i18n::TextDirection Textfield::GetTextDirection() const { | 223 base::i18n::TextDirection Textfield::GetTextDirection() const { |
185 return textfield_view_ ? | 224 return GetRenderText()->GetTextDirection(); |
186 textfield_view_->GetTextDirection() : base::i18n::UNKNOWN_DIRECTION; | |
187 } | 225 } |
188 | 226 |
189 void Textfield::SelectAll(bool reversed) { | 227 void Textfield::SelectAll(bool reversed) { |
190 if (textfield_view_) | 228 model_->SelectAll(reversed); |
191 textfield_view_->SelectAll(reversed); | 229 OnCaretBoundsChanged(); |
| 230 SchedulePaint(); |
192 } | 231 } |
193 | 232 |
194 base::string16 Textfield::GetSelectedText() const { | 233 base::string16 Textfield::GetSelectedText() const { |
195 return textfield_view_ ? textfield_view_->GetSelectedText() : | 234 return model_->GetSelectedText(); |
196 base::string16(); | |
197 } | 235 } |
198 | 236 |
199 void Textfield::ClearSelection() const { | 237 void Textfield::ClearSelection() { |
200 if (textfield_view_) | 238 model_->ClearSelection(); |
201 textfield_view_->ClearSelection(); | 239 OnCaretBoundsChanged(); |
| 240 SchedulePaint(); |
202 } | 241 } |
203 | 242 |
204 bool Textfield::HasSelection() const { | 243 bool Textfield::HasSelection() const { |
205 return textfield_view_ && !textfield_view_->GetSelectedRange().is_empty(); | 244 return !GetSelectedRange().is_empty(); |
206 } | 245 } |
207 | 246 |
208 SkColor Textfield::GetTextColor() const { | 247 SkColor Textfield::GetTextColor() const { |
209 if (!use_default_text_color_) | 248 if (!use_default_text_color_) |
210 return text_color_; | 249 return text_color_; |
211 | 250 |
212 return GetNativeTheme()->GetSystemColor(read_only() ? | 251 return GetNativeTheme()->GetSystemColor(read_only() ? |
213 ui::NativeTheme::kColorId_TextfieldReadOnlyColor : | 252 ui::NativeTheme::kColorId_TextfieldReadOnlyColor : |
214 ui::NativeTheme::kColorId_TextfieldDefaultColor); | 253 ui::NativeTheme::kColorId_TextfieldDefaultColor); |
215 } | 254 } |
216 | 255 |
217 void Textfield::SetTextColor(SkColor color) { | 256 void Textfield::SetTextColor(SkColor color) { |
218 text_color_ = color; | 257 text_color_ = color; |
219 use_default_text_color_ = false; | 258 use_default_text_color_ = false; |
220 if (textfield_view_) | 259 SetColor(color); |
221 textfield_view_->UpdateTextColor(); | |
222 } | 260 } |
223 | 261 |
224 void Textfield::UseDefaultTextColor() { | 262 void Textfield::UseDefaultTextColor() { |
225 use_default_text_color_ = true; | 263 use_default_text_color_ = true; |
226 if (textfield_view_) | 264 SetColor(GetTextColor()); |
227 textfield_view_->UpdateTextColor(); | |
228 } | 265 } |
229 | 266 |
230 SkColor Textfield::GetBackgroundColor() const { | 267 SkColor Textfield::GetBackgroundColor() const { |
231 if (!use_default_background_color_) | 268 if (!use_default_background_color_) |
232 return background_color_; | 269 return background_color_; |
233 | 270 |
234 return GetNativeTheme()->GetSystemColor(read_only() ? | 271 return GetNativeTheme()->GetSystemColor(read_only() ? |
235 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground : | 272 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground : |
236 ui::NativeTheme::kColorId_TextfieldDefaultBackground); | 273 ui::NativeTheme::kColorId_TextfieldDefaultBackground); |
237 } | 274 } |
238 | 275 |
239 void Textfield::SetBackgroundColor(SkColor color) { | 276 void Textfield::SetBackgroundColor(SkColor color) { |
240 background_color_ = color; | 277 background_color_ = color; |
241 use_default_background_color_ = false; | 278 use_default_background_color_ = false; |
242 if (textfield_view_) | 279 UpdateBackgroundColor(); |
243 textfield_view_->UpdateBackgroundColor(); | |
244 } | 280 } |
245 | 281 |
246 void Textfield::UseDefaultBackgroundColor() { | 282 void Textfield::UseDefaultBackgroundColor() { |
247 use_default_background_color_ = true; | 283 use_default_background_color_ = true; |
248 if (textfield_view_) | 284 UpdateBackgroundColor(); |
249 textfield_view_->UpdateBackgroundColor(); | |
250 } | 285 } |
251 | 286 |
252 bool Textfield::GetCursorEnabled() const { | 287 bool Textfield::GetCursorEnabled() const { |
253 return textfield_view_ && textfield_view_->GetCursorEnabled(); | 288 return GetRenderText()->cursor_enabled(); |
254 } | 289 } |
255 | 290 |
256 void Textfield::SetCursorEnabled(bool enabled) { | 291 void Textfield::SetCursorEnabled(bool enabled) { |
257 if (textfield_view_) | 292 GetRenderText()->SetCursorEnabled(enabled); |
258 textfield_view_->SetCursorEnabled(enabled); | 293 } |
| 294 |
| 295 const gfx::FontList& Textfield::GetFontList() const { |
| 296 return GetRenderText()->font_list(); |
259 } | 297 } |
260 | 298 |
261 void Textfield::SetFontList(const gfx::FontList& font_list) { | 299 void Textfield::SetFontList(const gfx::FontList& font_list) { |
262 font_list_ = font_list; | 300 GetRenderText()->SetFontList(font_list); |
263 if (textfield_view_) | 301 OnCaretBoundsChanged(); |
264 textfield_view_->UpdateFont(); | |
265 PreferredSizeChanged(); | 302 PreferredSizeChanged(); |
266 } | 303 } |
267 | 304 |
268 const gfx::Font& Textfield::GetPrimaryFont() const { | |
269 return font_list_.GetPrimaryFont(); | |
270 } | |
271 | |
272 void Textfield::SetFont(const gfx::Font& font) { | |
273 SetFontList(gfx::FontList(font)); | |
274 } | |
275 | |
276 void Textfield::SetHorizontalMargins(int left, int right) { | 305 void Textfield::SetHorizontalMargins(int left, int right) { |
277 if (horizontal_margins_were_set_ && | 306 if (horizontal_margins_were_set_ && |
278 left == margins_.left() && right == margins_.right()) { | 307 left == margins_.left() && right == margins_.right()) { |
279 return; | 308 return; |
280 } | 309 } |
281 margins_.Set(margins_.top(), left, margins_.bottom(), right); | 310 margins_.Set(margins_.top(), left, margins_.bottom(), right); |
282 horizontal_margins_were_set_ = true; | 311 horizontal_margins_were_set_ = true; |
283 if (textfield_view_) | 312 UpdateHorizontalMargins(); |
284 textfield_view_->UpdateHorizontalMargins(); | |
285 PreferredSizeChanged(); | 313 PreferredSizeChanged(); |
286 } | 314 } |
287 | 315 |
288 void Textfield::SetVerticalMargins(int top, int bottom) { | |
289 if (vertical_margins_were_set_ && | |
290 top == margins_.top() && bottom == margins_.bottom()) { | |
291 return; | |
292 } | |
293 margins_.Set(top, margins_.left(), bottom, margins_.right()); | |
294 vertical_margins_were_set_ = true; | |
295 if (textfield_view_) | |
296 textfield_view_->UpdateVerticalMargins(); | |
297 PreferredSizeChanged(); | |
298 } | |
299 | |
300 void Textfield::RemoveBorder() { | 316 void Textfield::RemoveBorder() { |
301 if (!draw_border_) | 317 if (!draw_border_) |
302 return; | 318 return; |
303 | 319 |
304 draw_border_ = false; | 320 draw_border_ = false; |
305 if (textfield_view_) | 321 // By default, if a caller calls Textfield::RemoveBorder() and does not set |
306 textfield_view_->UpdateBorder(); | 322 // any explicit margins, they should get zero margins. But also call |
| 323 // UpdateHorizontalMargins() so we respect any explicitly-set margins. |
| 324 // |
| 325 // NOTE: If someday Textfield supports toggling |draw_border_| back on, we'll |
| 326 // need to update this conditional to set the insets to their default values. |
| 327 text_border_->SetInsets(0, 0, 0, 0); |
| 328 UpdateHorizontalMargins(); |
307 } | 329 } |
308 | 330 |
309 base::string16 Textfield::GetPlaceholderText() const { | 331 base::string16 Textfield::GetPlaceholderText() const { |
310 return placeholder_text_; | 332 return placeholder_text_; |
311 } | 333 } |
312 | 334 |
313 bool Textfield::GetHorizontalMargins(int* left, int* right) { | 335 bool Textfield::GetHorizontalMargins(int* left, int* right) { |
314 if (!horizontal_margins_were_set_) | 336 if (!horizontal_margins_were_set_) |
315 return false; | 337 return false; |
316 | 338 |
317 *left = margins_.left(); | 339 *left = margins_.left(); |
318 *right = margins_.right(); | 340 *right = margins_.right(); |
319 return true; | 341 return true; |
320 } | 342 } |
321 | 343 |
322 bool Textfield::GetVerticalMargins(int* top, int* bottom) { | |
323 if (!vertical_margins_were_set_) | |
324 return false; | |
325 | |
326 *top = margins_.top(); | |
327 *bottom = margins_.bottom(); | |
328 return true; | |
329 } | |
330 | |
331 void Textfield::UpdateAllProperties() { | |
332 if (textfield_view_) { | |
333 textfield_view_->UpdateText(); | |
334 textfield_view_->UpdateTextColor(); | |
335 textfield_view_->UpdateBackgroundColor(); | |
336 textfield_view_->UpdateReadOnly(); | |
337 textfield_view_->UpdateFont(); | |
338 textfield_view_->UpdateEnabled(); | |
339 textfield_view_->UpdateBorder(); | |
340 textfield_view_->UpdateIsObscured(); | |
341 textfield_view_->UpdateHorizontalMargins(); | |
342 textfield_view_->UpdateVerticalMargins(); | |
343 } | |
344 } | |
345 | |
346 void Textfield::SyncText() { | |
347 if (textfield_view_) { | |
348 base::string16 new_text = textfield_view_->GetText(); | |
349 if (new_text != text_) { | |
350 text_ = new_text; | |
351 if (controller_) | |
352 controller_->ContentsChanged(this, text_); | |
353 } | |
354 } | |
355 } | |
356 | |
357 bool Textfield::IsIMEComposing() const { | 344 bool Textfield::IsIMEComposing() const { |
358 return textfield_view_ && textfield_view_->IsIMEComposing(); | 345 return model_->HasCompositionText(); |
359 } | 346 } |
360 | 347 |
361 const gfx::Range& Textfield::GetSelectedRange() const { | 348 const gfx::Range& Textfield::GetSelectedRange() const { |
362 return textfield_view_->GetSelectedRange(); | 349 return GetRenderText()->selection(); |
363 } | 350 } |
364 | 351 |
365 void Textfield::SelectRange(const gfx::Range& range) { | 352 void Textfield::SelectRange(const gfx::Range& range) { |
366 textfield_view_->SelectRange(range); | 353 model_->SelectRange(range); |
| 354 OnCaretBoundsChanged(); |
| 355 SchedulePaint(); |
| 356 NotifyAccessibilityEvent( |
| 357 ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); |
367 } | 358 } |
368 | 359 |
369 const gfx::SelectionModel& Textfield::GetSelectionModel() const { | 360 const gfx::SelectionModel& Textfield::GetSelectionModel() const { |
370 return textfield_view_->GetSelectionModel(); | 361 return GetRenderText()->selection_model(); |
371 } | 362 } |
372 | 363 |
373 void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) { | 364 void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) { |
374 textfield_view_->SelectSelectionModel(sel); | 365 model_->SelectSelectionModel(sel); |
| 366 OnCaretBoundsChanged(); |
| 367 SchedulePaint(); |
375 } | 368 } |
376 | 369 |
377 size_t Textfield::GetCursorPosition() const { | 370 size_t Textfield::GetCursorPosition() const { |
378 return textfield_view_->GetCursorPosition(); | 371 return model_->GetCursorPosition(); |
379 } | 372 } |
380 | 373 |
381 void Textfield::SetColor(SkColor value) { | 374 void Textfield::SetColor(SkColor value) { |
382 return textfield_view_->SetColor(value); | 375 GetRenderText()->SetColor(value); |
| 376 SchedulePaint(); |
383 } | 377 } |
384 | 378 |
385 void Textfield::ApplyColor(SkColor value, const gfx::Range& range) { | 379 void Textfield::ApplyColor(SkColor value, const gfx::Range& range) { |
386 return textfield_view_->ApplyColor(value, range); | 380 GetRenderText()->ApplyColor(value, range); |
| 381 SchedulePaint(); |
387 } | 382 } |
388 | 383 |
389 void Textfield::SetStyle(gfx::TextStyle style, bool value) { | 384 void Textfield::SetStyle(gfx::TextStyle style, bool value) { |
390 return textfield_view_->SetStyle(style, value); | 385 GetRenderText()->SetStyle(style, value); |
| 386 SchedulePaint(); |
391 } | 387 } |
392 | 388 |
393 void Textfield::ApplyStyle(gfx::TextStyle style, | 389 void Textfield::ApplyStyle(gfx::TextStyle style, |
394 bool value, | 390 bool value, |
395 const gfx::Range& range) { | 391 const gfx::Range& range) { |
396 return textfield_view_->ApplyStyle(style, value, range); | 392 GetRenderText()->ApplyStyle(style, value, range); |
| 393 SchedulePaint(); |
397 } | 394 } |
398 | 395 |
399 void Textfield::ClearEditHistory() { | 396 void Textfield::ClearEditHistory() { |
400 textfield_view_->ClearEditHistory(); | 397 model_->ClearEditHistory(); |
401 } | 398 } |
402 | 399 |
403 void Textfield::SetAccessibleName(const base::string16& name) { | 400 void Textfield::SetAccessibleName(const base::string16& name) { |
404 accessible_name_ = name; | 401 accessible_name_ = name; |
405 } | 402 } |
406 | 403 |
407 void Textfield::ExecuteCommand(int command_id) { | 404 void Textfield::ExecuteCommand(int command_id) { |
408 textfield_view_->ExecuteCommand(command_id, ui::EF_NONE); | 405 ExecuteCommand(command_id, ui::EF_NONE); |
409 } | 406 } |
410 | 407 |
411 void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) { | 408 void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) { |
412 focus_painter_ = focus_painter.Pass(); | 409 focus_painter_ = focus_painter.Pass(); |
413 } | 410 } |
414 | 411 |
415 bool Textfield::HasTextBeingDragged() { | 412 bool Textfield::HasTextBeingDragged() { |
416 return textfield_view_->HasTextBeingDragged(); | 413 return initiating_drag_; |
417 } | 414 } |
418 | 415 |
419 //////////////////////////////////////////////////////////////////////////////// | 416 //////////////////////////////////////////////////////////////////////////////// |
420 // Textfield, View overrides: | 417 // Textfield, View overrides: |
421 | 418 |
422 void Textfield::Layout() { | |
423 if (textfield_view_) { | |
424 textfield_view_->SetBoundsRect(GetContentsBounds()); | |
425 textfield_view_->Layout(); | |
426 } | |
427 } | |
428 | |
429 int Textfield::GetBaseline() const { | 419 int Textfield::GetBaseline() const { |
430 gfx::Insets insets = GetTextInsets(); | 420 return GetInsets().top() + GetRenderText()->GetBaseline(); |
431 const int baseline = textfield_view_ ? | |
432 textfield_view_->GetTextfieldBaseline() : font_list_.GetBaseline(); | |
433 return insets.top() + baseline; | |
434 } | 421 } |
435 | 422 |
436 gfx::Size Textfield::GetPreferredSize() { | 423 gfx::Size Textfield::GetPreferredSize() { |
437 gfx::Insets insets = GetTextInsets(); | 424 const gfx::Insets& insets = GetInsets(); |
438 | 425 return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_) + |
439 const int font_height = textfield_view_ ? textfield_view_->GetFontHeight() : | 426 insets.width(), GetFontList().GetHeight() + insets.height()); |
440 font_list_.GetHeight(); | |
441 return gfx::Size( | |
442 GetPrimaryFont().GetExpectedTextWidth(default_width_in_chars_) | |
443 + insets.width(), | |
444 font_height + insets.height()); | |
445 } | 427 } |
446 | 428 |
447 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { | 429 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { |
448 SelectAll(false); | 430 SelectAll(false); |
449 } | 431 } |
450 | 432 |
451 bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { | 433 bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { |
452 // Skip any accelerator handling of backspace; textfields handle this key. | 434 // Skip any accelerator handling of backspace; textfields handle this key. |
453 // Also skip processing of [Alt]+<num-pad digit> Unicode alt key codes. | 435 // Also skip processing of [Alt]+<num-pad digit> Unicode alt key codes. |
454 return e.key_code() == ui::VKEY_BACK || e.IsUnicodeKeyCode(); | 436 return e.key_code() == ui::VKEY_BACK || e.IsUnicodeKeyCode(); |
455 } | 437 } |
456 | 438 |
457 void Textfield::OnPaint(gfx::Canvas* canvas) { | 439 void Textfield::OnPaint(gfx::Canvas* canvas) { |
458 View::OnPaint(canvas); | 440 OnPaintBackground(canvas); |
| 441 PaintTextAndCursor(canvas); |
| 442 if (draw_border_) |
| 443 OnPaintBorder(canvas); |
459 if (NativeViewHost::kRenderNativeControlFocus) | 444 if (NativeViewHost::kRenderNativeControlFocus) |
460 Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); | 445 Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); |
461 } | 446 } |
462 | 447 |
463 bool Textfield::OnKeyPressed(const ui::KeyEvent& e) { | 448 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { |
464 return textfield_view_ && textfield_view_->HandleKeyPressed(e); | 449 bool handled = controller_ && controller_->HandleKeyEvent(this, event); |
465 } | 450 touch_selection_controller_.reset(); |
466 | 451 if (handled) |
467 bool Textfield::OnKeyReleased(const ui::KeyEvent& e) { | 452 return true; |
468 return textfield_view_ && textfield_view_->HandleKeyReleased(e); | 453 |
469 } | 454 // TODO(oshima): Refactor and consolidate with ExecuteCommand. |
470 | 455 if (event.type() == ui::ET_KEY_PRESSED) { |
471 bool Textfield::OnMouseDragged(const ui::MouseEvent& e) { | 456 ui::KeyboardCode key_code = event.key_code(); |
472 if (!e.IsOnlyRightMouseButton()) | 457 if (key_code == ui::VKEY_TAB || event.IsUnicodeKeyCode()) |
473 return View::OnMouseDragged(e); | 458 return false; |
| 459 |
| 460 gfx::RenderText* render_text = GetRenderText(); |
| 461 const bool editable = !read_only(); |
| 462 const bool readable = !IsObscured(); |
| 463 const bool shift = event.IsShiftDown(); |
| 464 const bool control = event.IsControlDown(); |
| 465 const bool alt = event.IsAltDown() || event.IsAltGrDown(); |
| 466 bool text_changed = false; |
| 467 bool cursor_changed = false; |
| 468 |
| 469 OnBeforeUserAction(); |
| 470 switch (key_code) { |
| 471 case ui::VKEY_Z: |
| 472 if (control && !shift && !alt && editable) |
| 473 cursor_changed = text_changed = model_->Undo(); |
| 474 else if (control && shift && !alt && editable) |
| 475 cursor_changed = text_changed = model_->Redo(); |
| 476 break; |
| 477 case ui::VKEY_Y: |
| 478 if (control && !alt && editable) |
| 479 cursor_changed = text_changed = model_->Redo(); |
| 480 break; |
| 481 case ui::VKEY_A: |
| 482 if (control && !alt) { |
| 483 model_->SelectAll(false); |
| 484 cursor_changed = true; |
| 485 } |
| 486 break; |
| 487 case ui::VKEY_X: |
| 488 if (control && !alt && editable && readable) |
| 489 cursor_changed = text_changed = Cut(); |
| 490 break; |
| 491 case ui::VKEY_C: |
| 492 if (control && !alt && readable) |
| 493 Copy(); |
| 494 break; |
| 495 case ui::VKEY_V: |
| 496 if (control && !alt && editable) |
| 497 cursor_changed = text_changed = Paste(); |
| 498 break; |
| 499 case ui::VKEY_RIGHT: |
| 500 case ui::VKEY_LEFT: { |
| 501 // We should ignore the alt-left/right keys because alt key doesn't make |
| 502 // any special effects for them and they can be shortcut keys such like |
| 503 // forward/back of the browser history. |
| 504 if (alt) |
| 505 break; |
| 506 const gfx::Range selection_range = render_text->selection(); |
| 507 model_->MoveCursor( |
| 508 control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, |
| 509 (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT, |
| 510 shift); |
| 511 cursor_changed = render_text->selection() != selection_range; |
| 512 break; |
| 513 } |
| 514 case ui::VKEY_END: |
| 515 case ui::VKEY_HOME: |
| 516 if ((key_code == ui::VKEY_HOME) == |
| 517 (render_text->GetTextDirection() == base::i18n::RIGHT_TO_LEFT)) |
| 518 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift); |
| 519 else |
| 520 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift); |
| 521 cursor_changed = true; |
| 522 break; |
| 523 case ui::VKEY_BACK: |
| 524 case ui::VKEY_DELETE: |
| 525 if (!editable) |
| 526 break; |
| 527 if (!model_->HasSelection()) { |
| 528 gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ? |
| 529 gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT; |
| 530 if (shift && control) { |
| 531 // If both shift and control are pressed, then erase up to the |
| 532 // beginning/end of the buffer in ChromeOS. In windows, do nothing. |
| 533 #if defined(OS_WIN) |
| 534 break; |
| 535 #else |
| 536 model_->MoveCursor(gfx::LINE_BREAK, direction, true); |
| 537 #endif |
| 538 } else if (control) { |
| 539 // If only control is pressed, then erase the previous/next word. |
| 540 model_->MoveCursor(gfx::WORD_BREAK, direction, true); |
| 541 } |
| 542 } |
| 543 if (key_code == ui::VKEY_BACK) |
| 544 model_->Backspace(); |
| 545 else if (shift && model_->HasSelection() && readable) |
| 546 Cut(); |
| 547 else |
| 548 model_->Delete(); |
| 549 |
| 550 // Consume backspace and delete keys even if the edit did nothing. This |
| 551 // prevents potential unintended side-effects of further event handling. |
| 552 text_changed = true; |
| 553 break; |
| 554 case ui::VKEY_INSERT: |
| 555 if (control && !shift && readable) |
| 556 Copy(); |
| 557 else if (shift && !control && editable) |
| 558 cursor_changed = text_changed = Paste(); |
| 559 break; |
| 560 default: |
| 561 break; |
| 562 } |
| 563 |
| 564 // We must have input method in order to support text input. |
| 565 DCHECK(GetInputMethod()); |
| 566 UpdateAfterChange(text_changed, cursor_changed); |
| 567 OnAfterUserAction(); |
| 568 return (text_changed || cursor_changed); |
| 569 } |
| 570 return false; |
| 571 } |
| 572 |
| 573 bool Textfield::OnMousePressed(const ui::MouseEvent& event) { |
| 574 OnBeforeUserAction(); |
| 575 TrackMouseClicks(event); |
| 576 |
| 577 if (!controller_ || !controller_->HandleMouseEvent(this, event)) { |
| 578 if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) |
| 579 RequestFocus(); |
| 580 |
| 581 if (event.IsOnlyLeftMouseButton()) { |
| 582 initiating_drag_ = false; |
| 583 bool can_drag = true; |
| 584 |
| 585 switch (aggregated_clicks_) { |
| 586 case 0: |
| 587 if (can_drag && |
| 588 GetRenderText()->IsPointInSelection(event.location())) { |
| 589 initiating_drag_ = true; |
| 590 } else { |
| 591 MoveCursorTo(event.location(), event.IsShiftDown()); |
| 592 } |
| 593 break; |
| 594 case 1: |
| 595 MoveCursorTo(event.location(), false); |
| 596 model_->SelectWord(); |
| 597 double_click_word_ = GetRenderText()->selection(); |
| 598 OnCaretBoundsChanged(); |
| 599 break; |
| 600 case 2: |
| 601 model_->SelectAll(false); |
| 602 OnCaretBoundsChanged(); |
| 603 break; |
| 604 default: |
| 605 NOTREACHED(); |
| 606 } |
| 607 } |
| 608 SchedulePaint(); |
| 609 } |
| 610 |
| 611 OnAfterUserAction(); |
| 612 touch_selection_controller_.reset(); |
474 return true; | 613 return true; |
475 } | 614 } |
476 | 615 |
| 616 bool Textfield::OnMouseDragged(const ui::MouseEvent& event) { |
| 617 // Don't adjust the cursor on a potential drag and drop, or if the mouse |
| 618 // movement from the last mouse click does not exceed the drag threshold. |
| 619 if (initiating_drag_ || !event.IsOnlyLeftMouseButton() || |
| 620 !ExceededDragThreshold(event.location() - last_click_location_)) { |
| 621 return true; |
| 622 } |
| 623 |
| 624 if (!event.IsOnlyRightMouseButton()) { |
| 625 OnBeforeUserAction(); |
| 626 MoveCursorTo(event.location(), true); |
| 627 if (aggregated_clicks_ == 1) { |
| 628 model_->SelectWord(); |
| 629 // Expand the selection so the initially selected word remains selected. |
| 630 gfx::Range selection = GetRenderText()->selection(); |
| 631 const size_t min = std::min(selection.GetMin(), |
| 632 double_click_word_.GetMin()); |
| 633 const size_t max = std::max(selection.GetMax(), |
| 634 double_click_word_.GetMax()); |
| 635 const bool reversed = selection.is_reversed(); |
| 636 selection.set_start(reversed ? max : min); |
| 637 selection.set_end(reversed ? min : max); |
| 638 model_->SelectRange(selection); |
| 639 } |
| 640 SchedulePaint(); |
| 641 OnAfterUserAction(); |
| 642 } |
| 643 return true; |
| 644 } |
| 645 |
| 646 void Textfield::OnMouseReleased(const ui::MouseEvent& event) { |
| 647 OnBeforeUserAction(); |
| 648 // Cancel suspected drag initiations, the user was clicking in the selection. |
| 649 if (initiating_drag_ && MoveCursorTo(event.location(), false)) |
| 650 SchedulePaint(); |
| 651 initiating_drag_ = false; |
| 652 OnAfterUserAction(); |
| 653 } |
| 654 |
477 void Textfield::OnFocus() { | 655 void Textfield::OnFocus() { |
478 if (textfield_view_) | 656 GetRenderText()->set_focused(true); |
479 textfield_view_->HandleFocus(); | 657 is_cursor_visible_ = true; |
| 658 SchedulePaint(); |
| 659 GetInputMethod()->OnFocus(); |
| 660 OnCaretBoundsChanged(); |
| 661 |
| 662 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); |
| 663 if (caret_blink_ms != 0) { |
| 664 base::MessageLoop::current()->PostDelayedTask( |
| 665 FROM_HERE, |
| 666 base::Bind(&Textfield::UpdateCursor, |
| 667 cursor_weak_ptr_factory_.GetWeakPtr()), |
| 668 base::TimeDelta::FromMilliseconds(caret_blink_ms)); |
| 669 } |
| 670 |
480 View::OnFocus(); | 671 View::OnFocus(); |
481 SchedulePaint(); | 672 SchedulePaint(); |
482 } | 673 } |
483 | 674 |
484 void Textfield::OnBlur() { | 675 void Textfield::OnBlur() { |
485 if (textfield_view_) | 676 GetRenderText()->set_focused(false); |
486 textfield_view_->HandleBlur(); | 677 GetInputMethod()->OnBlur(); |
| 678 // Stop blinking cursor. |
| 679 cursor_weak_ptr_factory_.InvalidateWeakPtrs(); |
| 680 if (is_cursor_visible_) { |
| 681 is_cursor_visible_ = false; |
| 682 RepaintCursor(); |
| 683 } |
| 684 |
| 685 touch_selection_controller_.reset(); |
487 | 686 |
488 // Border typically draws focus indicator. | 687 // Border typically draws focus indicator. |
489 SchedulePaint(); | 688 SchedulePaint(); |
490 } | 689 } |
491 | 690 |
492 void Textfield::GetAccessibleState(ui::AccessibleViewState* state) { | 691 void Textfield::GetAccessibleState(ui::AccessibleViewState* state) { |
493 state->role = ui::AccessibilityTypes::ROLE_TEXT; | 692 state->role = ui::AccessibilityTypes::ROLE_TEXT; |
494 state->name = accessible_name_; | 693 state->name = accessible_name_; |
495 if (read_only()) | 694 if (read_only()) |
496 state->state |= ui::AccessibilityTypes::STATE_READONLY; | 695 state->state |= ui::AccessibilityTypes::STATE_READONLY; |
497 if (IsObscured()) | 696 if (IsObscured()) |
498 state->state |= ui::AccessibilityTypes::STATE_PROTECTED; | 697 state->state |= ui::AccessibilityTypes::STATE_PROTECTED; |
499 state->value = text_; | 698 state->value = model_->GetText(); |
500 | 699 |
501 const gfx::Range range = textfield_view_->GetSelectedRange(); | 700 const gfx::Range range = GetSelectedRange(); |
502 state->selection_start = range.start(); | 701 state->selection_start = range.start(); |
503 state->selection_end = range.end(); | 702 state->selection_end = range.end(); |
504 | 703 |
505 if (!read_only()) { | 704 if (!read_only()) { |
506 state->set_value_callback = | 705 state->set_value_callback = |
507 base::Bind(&Textfield::AccessibilitySetValue, | 706 base::Bind(&Textfield::AccessibilitySetValue, |
508 weak_ptr_factory_.GetWeakPtr()); | 707 weak_ptr_factory_.GetWeakPtr()); |
509 } | 708 } |
510 } | 709 } |
511 | 710 |
512 ui::TextInputClient* Textfield::GetTextInputClient() { | 711 ui::TextInputClient* Textfield::GetTextInputClient() { |
513 return textfield_view_ ? textfield_view_->GetTextInputClient() : NULL; | 712 return read_only_ ? NULL : this; |
514 } | 713 } |
515 | 714 |
516 gfx::Point Textfield::GetKeyboardContextMenuLocation() { | 715 gfx::Point Textfield::GetKeyboardContextMenuLocation() { |
517 return textfield_view_ ? textfield_view_->GetContextMenuLocation() : | 716 return GetCaretBounds().bottom_right(); |
518 View::GetKeyboardContextMenuLocation(); | 717 } |
| 718 |
| 719 void Textfield::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| 720 UpdateColorsFromTheme(theme); |
519 } | 721 } |
520 | 722 |
521 void Textfield::OnEnabledChanged() { | 723 void Textfield::OnEnabledChanged() { |
522 View::OnEnabledChanged(); | 724 View::OnEnabledChanged(); |
523 if (textfield_view_) | 725 if (GetInputMethod()) |
524 textfield_view_->UpdateEnabled(); | 726 GetInputMethod()->OnTextInputTypeChanged(this); |
525 } | 727 SchedulePaint(); |
526 | |
527 void Textfield::ViewHierarchyChanged( | |
528 const ViewHierarchyChangedDetails& details) { | |
529 if (details.is_add && !textfield_view_ && GetWidget()) { | |
530 // The textfield view's lifetime is managed by the view hierarchy. | |
531 textfield_view_ = new NativeTextfieldViews(this); | |
532 AddChildViewAt(textfield_view_, 0); | |
533 Layout(); | |
534 UpdateAllProperties(); | |
535 } | |
536 } | 728 } |
537 | 729 |
538 const char* Textfield::GetClassName() const { | 730 const char* Textfield::GetClassName() const { |
539 return kViewClassName; | 731 return kViewClassName; |
540 } | 732 } |
541 | 733 |
| 734 gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) { |
| 735 bool in_selection = GetRenderText()->IsPointInSelection(event.location()); |
| 736 bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED; |
| 737 bool text_cursor = !initiating_drag_ && (drag_event || !in_selection); |
| 738 #if defined(USE_AURA) |
| 739 return text_cursor ? ui::kCursorIBeam : ui::kCursorNull; |
| 740 #elif defined(OS_WIN) |
| 741 static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM); |
| 742 static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); |
| 743 return text_cursor ? ibeam : arrow; |
| 744 #endif |
| 745 } |
| 746 |
| 747 void Textfield::OnGestureEvent(ui::GestureEvent* event) { |
| 748 switch (event->type()) { |
| 749 case ui::ET_GESTURE_TAP_DOWN: |
| 750 OnBeforeUserAction(); |
| 751 RequestFocus(); |
| 752 // We don't deselect if the point is in the selection |
| 753 // because TAP_DOWN may turn into a LONG_PRESS. |
| 754 if (!GetRenderText()->IsPointInSelection(event->location()) && |
| 755 MoveCursorTo(event->location(), false)) |
| 756 SchedulePaint(); |
| 757 OnAfterUserAction(); |
| 758 event->SetHandled(); |
| 759 break; |
| 760 case ui::ET_GESTURE_SCROLL_UPDATE: |
| 761 OnBeforeUserAction(); |
| 762 if (MoveCursorTo(event->location(), true)) |
| 763 SchedulePaint(); |
| 764 OnAfterUserAction(); |
| 765 event->SetHandled(); |
| 766 break; |
| 767 case ui::ET_GESTURE_SCROLL_END: |
| 768 case ui::ET_SCROLL_FLING_START: |
| 769 CreateTouchSelectionControllerAndNotifyIt(); |
| 770 event->SetHandled(); |
| 771 break; |
| 772 case ui::ET_GESTURE_TAP: |
| 773 if (event->details().tap_count() == 1) { |
| 774 CreateTouchSelectionControllerAndNotifyIt(); |
| 775 } else { |
| 776 OnBeforeUserAction(); |
| 777 SelectAll(false); |
| 778 OnAfterUserAction(); |
| 779 event->SetHandled(); |
| 780 } |
| 781 #if defined(OS_WIN) && defined(USE_AURA) |
| 782 if (!read_only()) |
| 783 base::win::DisplayVirtualKeyboard(); |
| 784 #endif |
| 785 break; |
| 786 case ui::ET_GESTURE_LONG_PRESS: |
| 787 // If long press happens outside selection, select word and show context |
| 788 // menu (If touch selection is enabled, context menu is shown by the |
| 789 // |touch_selection_controller_|, hence we mark the event handled. |
| 790 // Otherwise, the regular context menu will be shown by views). |
| 791 // If long press happens in selected text and touch drag drop is enabled, |
| 792 // we will turn off touch selection (if one exists) and let views do drag |
| 793 // drop. |
| 794 if (!GetRenderText()->IsPointInSelection(event->location())) { |
| 795 OnBeforeUserAction(); |
| 796 model_->SelectWord(); |
| 797 touch_selection_controller_.reset( |
| 798 ui::TouchSelectionController::create(this)); |
| 799 OnCaretBoundsChanged(); |
| 800 SchedulePaint(); |
| 801 OnAfterUserAction(); |
| 802 if (touch_selection_controller_) |
| 803 event->SetHandled(); |
| 804 } else if (switches::IsTouchDragDropEnabled()) { |
| 805 initiating_drag_ = true; |
| 806 touch_selection_controller_.reset(); |
| 807 } else { |
| 808 if (!touch_selection_controller_) |
| 809 CreateTouchSelectionControllerAndNotifyIt(); |
| 810 if (touch_selection_controller_) |
| 811 event->SetHandled(); |
| 812 } |
| 813 return; |
| 814 case ui::ET_GESTURE_LONG_TAP: |
| 815 if (!touch_selection_controller_) |
| 816 CreateTouchSelectionControllerAndNotifyIt(); |
| 817 |
| 818 // If touch selection is enabled, the context menu on long tap will be |
| 819 // shown by the |touch_selection_controller_|, hence we mark the event |
| 820 // handled so views does not try to show context menu on it. |
| 821 if (touch_selection_controller_) |
| 822 event->SetHandled(); |
| 823 break; |
| 824 default: |
| 825 return; |
| 826 } |
| 827 } |
| 828 |
| 829 bool Textfield::GetDropFormats( |
| 830 int* formats, |
| 831 std::set<OSExchangeData::CustomFormat>* custom_formats) { |
| 832 if (!enabled() || read_only()) |
| 833 return false; |
| 834 // TODO(msw): Can we support URL, FILENAME, etc.? |
| 835 *formats = ui::OSExchangeData::STRING; |
| 836 if (controller_) |
| 837 controller_->AppendDropFormats(formats, custom_formats); |
| 838 return true; |
| 839 } |
| 840 |
| 841 bool Textfield::CanDrop(const OSExchangeData& data) { |
| 842 int formats; |
| 843 std::set<OSExchangeData::CustomFormat> custom_formats; |
| 844 GetDropFormats(&formats, &custom_formats); |
| 845 return enabled() && !read_only() && |
| 846 data.HasAnyFormat(formats, custom_formats); |
| 847 } |
| 848 |
| 849 int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) { |
| 850 DCHECK(CanDrop(event.data())); |
| 851 gfx::RenderText* render_text = GetRenderText(); |
| 852 const gfx::Range& selection = render_text->selection(); |
| 853 drop_cursor_position_ = render_text->FindCursorPosition(event.location()); |
| 854 bool in_selection = !selection.is_empty() && |
| 855 selection.Contains(gfx::Range(drop_cursor_position_.caret_pos())); |
| 856 is_drop_cursor_visible_ = !in_selection; |
| 857 // TODO(msw): Pan over text when the user drags to the visible text edge. |
| 858 OnCaretBoundsChanged(); |
| 859 SchedulePaint(); |
| 860 |
| 861 if (initiating_drag_) { |
| 862 if (in_selection) |
| 863 return ui::DragDropTypes::DRAG_NONE; |
| 864 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : |
| 865 ui::DragDropTypes::DRAG_MOVE; |
| 866 } |
| 867 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; |
| 868 } |
| 869 |
| 870 void Textfield::OnDragExited() { |
| 871 is_drop_cursor_visible_ = false; |
| 872 SchedulePaint(); |
| 873 } |
| 874 |
| 875 int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) { |
| 876 DCHECK(CanDrop(event.data())); |
| 877 is_drop_cursor_visible_ = false; |
| 878 |
| 879 if (controller_) { |
| 880 int drag_operation = controller_->OnDrop(event.data()); |
| 881 if (drag_operation != ui::DragDropTypes::DRAG_NONE) |
| 882 return drag_operation; |
| 883 } |
| 884 |
| 885 gfx::RenderText* render_text = GetRenderText(); |
| 886 DCHECK(!initiating_drag_ || |
| 887 !render_text->IsPointInSelection(event.location())); |
| 888 OnBeforeUserAction(); |
| 889 skip_input_method_cancel_composition_ = true; |
| 890 |
| 891 gfx::SelectionModel drop_destination_model = |
| 892 render_text->FindCursorPosition(event.location()); |
| 893 base::string16 text; |
| 894 event.data().GetString(&text); |
| 895 text = GetTextForDisplay(text); |
| 896 |
| 897 // Delete the current selection for a drag and drop within this view. |
| 898 const bool move = initiating_drag_ && !event.IsControlDown() && |
| 899 event.source_operations() & ui::DragDropTypes::DRAG_MOVE; |
| 900 if (move) { |
| 901 // Adjust the drop destination if it is on or after the current selection. |
| 902 size_t pos = drop_destination_model.caret_pos(); |
| 903 pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length(); |
| 904 model_->DeleteSelectionAndInsertTextAt(text, pos); |
| 905 } else { |
| 906 model_->MoveCursorTo(drop_destination_model); |
| 907 // Drop always inserts text even if the textfield is not in insert mode. |
| 908 model_->InsertText(text); |
| 909 } |
| 910 skip_input_method_cancel_composition_ = false; |
| 911 UpdateAfterChange(true, true); |
| 912 OnAfterUserAction(); |
| 913 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; |
| 914 } |
| 915 |
| 916 void Textfield::OnDragDone() { |
| 917 initiating_drag_ = false; |
| 918 is_drop_cursor_visible_ = false; |
| 919 } |
| 920 |
| 921 void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| 922 GetRenderText()->SetDisplayRect(GetContentsBounds()); |
| 923 OnCaretBoundsChanged(); |
| 924 } |
| 925 |
| 926 //////////////////////////////////////////////////////////////////////////////// |
| 927 // Textfield, TextfieldViewsModel::Delegate overrides: |
| 928 |
| 929 void Textfield::OnCompositionTextConfirmedOrCleared() { |
| 930 if (!skip_input_method_cancel_composition_) |
| 931 GetInputMethod()->CancelComposition(this); |
| 932 } |
| 933 |
| 934 //////////////////////////////////////////////////////////////////////////////// |
| 935 // Textfield, ContextMenuController overrides: |
| 936 |
| 937 void Textfield::ShowContextMenuForView( |
| 938 View* source, |
| 939 const gfx::Point& point, |
| 940 ui::MenuSourceType source_type) { |
| 941 UpdateContextMenu(); |
| 942 if (context_menu_runner_->RunMenuAt(GetWidget(), NULL, |
| 943 gfx::Rect(point, gfx::Size()), views::MenuItemView::TOPLEFT, |
| 944 source_type, |
| 945 MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) == |
| 946 MenuRunner::MENU_DELETED) |
| 947 return; |
| 948 } |
| 949 |
| 950 //////////////////////////////////////////////////////////////////////////////// |
| 951 // Textfield, views::DragController overrides: |
| 952 |
| 953 void Textfield::WriteDragDataForView(views::View* sender, |
| 954 const gfx::Point& press_pt, |
| 955 OSExchangeData* data) { |
| 956 DCHECK_NE(ui::DragDropTypes::DRAG_NONE, |
| 957 GetDragOperationsForView(sender, press_pt)); |
| 958 data->SetString(model_->GetSelectedText()); |
| 959 scoped_ptr<gfx::Canvas> canvas( |
| 960 views::GetCanvasForDragImage(GetWidget(), size())); |
| 961 GetRenderText()->DrawSelectedTextForDrag(canvas.get()); |
| 962 drag_utils::SetDragImageOnDataObject(*canvas, size(), |
| 963 press_pt.OffsetFromOrigin(), |
| 964 data); |
| 965 if (controller_) |
| 966 controller_->OnWriteDragData(data); |
| 967 } |
| 968 |
| 969 int Textfield::GetDragOperationsForView(views::View* sender, |
| 970 const gfx::Point& p) { |
| 971 int drag_operations = ui::DragDropTypes::DRAG_COPY; |
| 972 if (!enabled() || IsObscured() || !GetRenderText()->IsPointInSelection(p)) |
| 973 drag_operations = ui::DragDropTypes::DRAG_NONE; |
| 974 else if (sender == this && !read_only()) |
| 975 drag_operations = |
| 976 ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; |
| 977 if (controller_) |
| 978 controller_->OnGetDragOperationsForTextfield(&drag_operations); |
| 979 return drag_operations; |
| 980 } |
| 981 |
| 982 bool Textfield::CanStartDragForView(View* sender, |
| 983 const gfx::Point& press_pt, |
| 984 const gfx::Point& p) { |
| 985 return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt); |
| 986 } |
| 987 |
| 988 //////////////////////////////////////////////////////////////////////////////// |
| 989 // Textfield, ui::TouchEditable overrides: |
| 990 |
| 991 void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) { |
| 992 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| 993 return; |
| 994 |
| 995 gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start); |
| 996 gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end); |
| 997 gfx::SelectionModel selection( |
| 998 gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()), |
| 999 end_caret.caret_affinity()); |
| 1000 |
| 1001 OnBeforeUserAction(); |
| 1002 model_->SelectSelectionModel(selection); |
| 1003 OnCaretBoundsChanged(); |
| 1004 SchedulePaint(); |
| 1005 OnAfterUserAction(); |
| 1006 } |
| 1007 |
| 1008 void Textfield::MoveCaretTo(const gfx::Point& point) { |
| 1009 SelectRect(point, point); |
| 1010 } |
| 1011 |
| 1012 void Textfield::GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) { |
| 1013 gfx::RenderText* render_text = GetRenderText(); |
| 1014 const gfx::SelectionModel& sel = render_text->selection_model(); |
| 1015 gfx::SelectionModel start_sel = |
| 1016 render_text->GetSelectionModelForSelectionStart(); |
| 1017 *p1 = render_text->GetCursorBounds(start_sel, true); |
| 1018 *p2 = render_text->GetCursorBounds(sel, true); |
| 1019 } |
| 1020 |
| 1021 gfx::Rect Textfield::GetBounds() { |
| 1022 return bounds(); |
| 1023 } |
| 1024 |
| 1025 gfx::NativeView Textfield::GetNativeView() const { |
| 1026 return GetWidget()->GetNativeView(); |
| 1027 } |
| 1028 |
| 1029 void Textfield::ConvertPointToScreen(gfx::Point* point) { |
| 1030 View::ConvertPointToScreen(this, point); |
| 1031 } |
| 1032 |
| 1033 void Textfield::ConvertPointFromScreen(gfx::Point* point) { |
| 1034 View::ConvertPointFromScreen(this, point); |
| 1035 } |
| 1036 |
| 1037 bool Textfield::DrawsHandles() { |
| 1038 return false; |
| 1039 } |
| 1040 |
| 1041 void Textfield::OpenContextMenu(const gfx::Point& anchor) { |
| 1042 touch_selection_controller_.reset(); |
| 1043 ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU); |
| 1044 } |
| 1045 |
| 1046 //////////////////////////////////////////////////////////////////////////////// |
| 1047 // Textfield, ui::SimpleMenuModel::Delegate overrides: |
| 1048 |
| 1049 bool Textfield::IsCommandIdChecked(int command_id) const { |
| 1050 return true; |
| 1051 } |
| 1052 |
| 1053 bool Textfield::IsCommandIdEnabled(int command_id) const { |
| 1054 if (controller_ && controller_->HandlesCommand(command_id)) |
| 1055 return controller_->IsCommandIdEnabled(command_id); |
| 1056 |
| 1057 bool editable = !read_only(); |
| 1058 base::string16 result; |
| 1059 switch (command_id) { |
| 1060 case IDS_APP_UNDO: |
| 1061 return editable && model_->CanUndo(); |
| 1062 case IDS_APP_CUT: |
| 1063 return editable && model_->HasSelection() && !IsObscured(); |
| 1064 case IDS_APP_COPY: |
| 1065 return model_->HasSelection() && !IsObscured(); |
| 1066 case IDS_APP_PASTE: |
| 1067 ui::Clipboard::GetForCurrentThread()->ReadText( |
| 1068 ui::CLIPBOARD_TYPE_COPY_PASTE, &result); |
| 1069 return editable && !result.empty(); |
| 1070 case IDS_APP_DELETE: |
| 1071 return editable && model_->HasSelection(); |
| 1072 case IDS_APP_SELECT_ALL: |
| 1073 return !model_->GetText().empty(); |
| 1074 default: |
| 1075 return controller_->IsCommandIdEnabled(command_id); |
| 1076 } |
| 1077 } |
| 1078 |
| 1079 bool Textfield::GetAcceleratorForCommandId(int command_id, |
| 1080 ui::Accelerator* accelerator) { |
| 1081 return false; |
| 1082 } |
| 1083 |
| 1084 bool Textfield::IsItemForCommandIdDynamic(int command_id) const { |
| 1085 return controller_ && controller_->IsItemForCommandIdDynamic(command_id); |
| 1086 } |
| 1087 |
| 1088 base::string16 Textfield::GetLabelForCommandId(int command_id) const { |
| 1089 return controller_ ? controller_->GetLabelForCommandId(command_id) : |
| 1090 base::string16(); |
| 1091 } |
| 1092 |
| 1093 void Textfield::ExecuteCommand(int command_id, int event_flags) { |
| 1094 touch_selection_controller_.reset(); |
| 1095 if (!IsCommandIdEnabled(command_id)) |
| 1096 return; |
| 1097 |
| 1098 if (controller_ && controller_->HandlesCommand(command_id)) { |
| 1099 controller_->ExecuteCommand(command_id, 0); |
| 1100 } else { |
| 1101 bool text_changed = false; |
| 1102 switch (command_id) { |
| 1103 case IDS_APP_UNDO: |
| 1104 OnBeforeUserAction(); |
| 1105 text_changed = model_->Undo(); |
| 1106 UpdateAfterChange(text_changed, text_changed); |
| 1107 OnAfterUserAction(); |
| 1108 break; |
| 1109 case IDS_APP_CUT: |
| 1110 OnBeforeUserAction(); |
| 1111 text_changed = Cut(); |
| 1112 UpdateAfterChange(text_changed, text_changed); |
| 1113 OnAfterUserAction(); |
| 1114 break; |
| 1115 case IDS_APP_COPY: |
| 1116 OnBeforeUserAction(); |
| 1117 Copy(); |
| 1118 OnAfterUserAction(); |
| 1119 break; |
| 1120 case IDS_APP_PASTE: |
| 1121 OnBeforeUserAction(); |
| 1122 text_changed = Paste(); |
| 1123 UpdateAfterChange(text_changed, text_changed); |
| 1124 OnAfterUserAction(); |
| 1125 break; |
| 1126 case IDS_APP_DELETE: |
| 1127 OnBeforeUserAction(); |
| 1128 text_changed = model_->Delete(); |
| 1129 UpdateAfterChange(text_changed, text_changed); |
| 1130 OnAfterUserAction(); |
| 1131 break; |
| 1132 case IDS_APP_SELECT_ALL: |
| 1133 OnBeforeUserAction(); |
| 1134 SelectAll(false); |
| 1135 UpdateAfterChange(false, true); |
| 1136 OnAfterUserAction(); |
| 1137 break; |
| 1138 default: |
| 1139 controller_->ExecuteCommand(command_id, 0); |
| 1140 break; |
| 1141 } |
| 1142 } |
| 1143 } |
| 1144 |
| 1145 //////////////////////////////////////////////////////////////////////////////// |
| 1146 // Textfield, ui::TextInputClient overrides: |
| 1147 |
| 1148 void Textfield::SetCompositionText(const ui::CompositionText& composition) { |
| 1149 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| 1150 return; |
| 1151 |
| 1152 OnBeforeUserAction(); |
| 1153 skip_input_method_cancel_composition_ = true; |
| 1154 model_->SetCompositionText(composition); |
| 1155 skip_input_method_cancel_composition_ = false; |
| 1156 UpdateAfterChange(true, true); |
| 1157 OnAfterUserAction(); |
| 1158 } |
| 1159 |
| 1160 void Textfield::ConfirmCompositionText() { |
| 1161 if (!model_->HasCompositionText()) |
| 1162 return; |
| 1163 |
| 1164 OnBeforeUserAction(); |
| 1165 skip_input_method_cancel_composition_ = true; |
| 1166 model_->ConfirmCompositionText(); |
| 1167 skip_input_method_cancel_composition_ = false; |
| 1168 UpdateAfterChange(true, true); |
| 1169 OnAfterUserAction(); |
| 1170 } |
| 1171 |
| 1172 void Textfield::ClearCompositionText() { |
| 1173 if (!model_->HasCompositionText()) |
| 1174 return; |
| 1175 |
| 1176 OnBeforeUserAction(); |
| 1177 skip_input_method_cancel_composition_ = true; |
| 1178 model_->CancelCompositionText(); |
| 1179 skip_input_method_cancel_composition_ = false; |
| 1180 UpdateAfterChange(true, true); |
| 1181 OnAfterUserAction(); |
| 1182 } |
| 1183 |
| 1184 void Textfield::InsertText(const base::string16& text) { |
| 1185 // TODO(suzhe): Filter invalid characters. |
| 1186 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) |
| 1187 return; |
| 1188 |
| 1189 OnBeforeUserAction(); |
| 1190 skip_input_method_cancel_composition_ = true; |
| 1191 if (GetRenderText()->insert_mode()) |
| 1192 model_->InsertText(GetTextForDisplay(text)); |
| 1193 else |
| 1194 model_->ReplaceText(GetTextForDisplay(text)); |
| 1195 skip_input_method_cancel_composition_ = false; |
| 1196 UpdateAfterChange(true, true); |
| 1197 OnAfterUserAction(); |
| 1198 } |
| 1199 |
| 1200 void Textfield::InsertChar(base::char16 ch, int flags) { |
| 1201 // Filter out all control characters, including tab and new line characters, |
| 1202 // and all characters with Alt modifier. But allow characters with the AltGr |
| 1203 // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a |
| 1204 // different flag that we don't care about. |
| 1205 const bool should_insert_char = ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && |
| 1206 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; |
| 1207 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char) |
| 1208 return; |
| 1209 |
| 1210 OnBeforeUserAction(); |
| 1211 skip_input_method_cancel_composition_ = true; |
| 1212 if (GetRenderText()->insert_mode()) |
| 1213 model_->InsertChar(ch); |
| 1214 else |
| 1215 model_->ReplaceChar(ch); |
| 1216 skip_input_method_cancel_composition_ = false; |
| 1217 |
| 1218 model_->SetText(GetTextForDisplay(model_->GetText())); |
| 1219 |
| 1220 UpdateAfterChange(true, true); |
| 1221 OnAfterUserAction(); |
| 1222 |
| 1223 if (IsObscured() && obscured_reveal_duration_ != base::TimeDelta()) { |
| 1224 const size_t change_offset = model_->GetCursorPosition(); |
| 1225 DCHECK_GT(change_offset, 0u); |
| 1226 RevealObscuredChar(change_offset - 1, obscured_reveal_duration_); |
| 1227 } |
| 1228 } |
| 1229 |
| 1230 gfx::NativeWindow Textfield::GetAttachedWindow() const { |
| 1231 // Imagine the following hierarchy. |
| 1232 // [NativeWidget A] - FocusManager |
| 1233 // [View] |
| 1234 // [NativeWidget B] |
| 1235 // [View] |
| 1236 // [View X] |
| 1237 // An important thing is that [NativeWidget A] owns Win32 input focus even |
| 1238 // when [View X] is logically focused by FocusManager. As a result, an Win32 |
| 1239 // IME may want to interact with the native view of [NativeWidget A] rather |
| 1240 // than that of [NativeWidget B]. This is why we need to call |
| 1241 // GetTopLevelWidget() here. |
| 1242 return GetWidget()->GetTopLevelWidget()->GetNativeView(); |
| 1243 } |
| 1244 |
| 1245 ui::TextInputType Textfield::GetTextInputType() const { |
| 1246 if (read_only() || !enabled()) |
| 1247 return ui::TEXT_INPUT_TYPE_NONE; |
| 1248 return text_input_type_; |
| 1249 } |
| 1250 |
| 1251 ui::TextInputMode Textfield::GetTextInputMode() const { |
| 1252 return ui::TEXT_INPUT_MODE_DEFAULT; |
| 1253 } |
| 1254 |
| 1255 bool Textfield::CanComposeInline() const { |
| 1256 return true; |
| 1257 } |
| 1258 |
| 1259 gfx::Rect Textfield::GetCaretBounds() const { |
| 1260 gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds(); |
| 1261 ConvertRectToScreen(this, &rect); |
| 1262 return rect; |
| 1263 } |
| 1264 |
| 1265 bool Textfield::GetCompositionCharacterBounds(uint32 index, |
| 1266 gfx::Rect* rect) const { |
| 1267 DCHECK(rect); |
| 1268 if (!HasCompositionText()) |
| 1269 return false; |
| 1270 gfx::RenderText* render_text = GetRenderText(); |
| 1271 const gfx::Range& composition_range = render_text->GetCompositionRange(); |
| 1272 DCHECK(!composition_range.is_empty()); |
| 1273 |
| 1274 size_t text_index = composition_range.start() + index; |
| 1275 if (composition_range.end() <= text_index) |
| 1276 return false; |
| 1277 if (!render_text->IsCursorablePosition(text_index)) { |
| 1278 text_index = render_text->IndexOfAdjacentGrapheme( |
| 1279 text_index, gfx::CURSOR_BACKWARD); |
| 1280 } |
| 1281 if (text_index < composition_range.start()) |
| 1282 return false; |
| 1283 const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD); |
| 1284 *rect = render_text->GetCursorBounds(caret, false); |
| 1285 ConvertRectToScreen(this, rect); |
| 1286 return true; |
| 1287 } |
| 1288 |
| 1289 bool Textfield::HasCompositionText() const { |
| 1290 return model_->HasCompositionText(); |
| 1291 } |
| 1292 |
| 1293 bool Textfield::GetTextRange(gfx::Range* range) const { |
| 1294 if (!ImeEditingAllowed()) |
| 1295 return false; |
| 1296 |
| 1297 model_->GetTextRange(range); |
| 1298 return true; |
| 1299 } |
| 1300 |
| 1301 bool Textfield::GetCompositionTextRange(gfx::Range* range) const { |
| 1302 if (!ImeEditingAllowed()) |
| 1303 return false; |
| 1304 |
| 1305 model_->GetCompositionTextRange(range); |
| 1306 return true; |
| 1307 } |
| 1308 |
| 1309 bool Textfield::GetSelectionRange(gfx::Range* range) const { |
| 1310 if (!ImeEditingAllowed()) |
| 1311 return false; |
| 1312 *range = GetRenderText()->selection(); |
| 1313 return true; |
| 1314 } |
| 1315 |
| 1316 bool Textfield::SetSelectionRange(const gfx::Range& range) { |
| 1317 if (!ImeEditingAllowed() || !range.IsValid()) |
| 1318 return false; |
| 1319 |
| 1320 OnBeforeUserAction(); |
| 1321 SelectRange(range); |
| 1322 OnAfterUserAction(); |
| 1323 return true; |
| 1324 } |
| 1325 |
| 1326 bool Textfield::DeleteRange(const gfx::Range& range) { |
| 1327 if (!ImeEditingAllowed() || range.is_empty()) |
| 1328 return false; |
| 1329 |
| 1330 OnBeforeUserAction(); |
| 1331 model_->SelectRange(range); |
| 1332 if (model_->HasSelection()) { |
| 1333 model_->DeleteSelection(); |
| 1334 UpdateAfterChange(true, true); |
| 1335 } |
| 1336 OnAfterUserAction(); |
| 1337 return true; |
| 1338 } |
| 1339 |
| 1340 bool Textfield::GetTextFromRange(const gfx::Range& range, |
| 1341 base::string16* text) const { |
| 1342 if (!ImeEditingAllowed() || !range.IsValid()) |
| 1343 return false; |
| 1344 |
| 1345 gfx::Range text_range; |
| 1346 if (!GetTextRange(&text_range) || !text_range.Contains(range)) |
| 1347 return false; |
| 1348 |
| 1349 *text = model_->GetTextFromRange(range); |
| 1350 return true; |
| 1351 } |
| 1352 |
| 1353 void Textfield::OnInputMethodChanged() {} |
| 1354 |
| 1355 bool Textfield::ChangeTextDirectionAndLayoutAlignment( |
| 1356 base::i18n::TextDirection direction) { |
| 1357 // Restore text directionality mode when the indicated direction matches the |
| 1358 // current forced mode; otherwise, force the mode indicated. This helps users |
| 1359 // manage BiDi text layout without getting stuck in forced LTR or RTL modes. |
| 1360 const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ? |
| 1361 gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR; |
| 1362 if (mode == GetRenderText()->directionality_mode()) |
| 1363 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); |
| 1364 else |
| 1365 GetRenderText()->SetDirectionalityMode(mode); |
| 1366 SchedulePaint(); |
| 1367 return true; |
| 1368 } |
| 1369 |
| 1370 void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) { |
| 1371 gfx::Range range = GetRenderText()->selection(); |
| 1372 DCHECK_GE(range.start(), before); |
| 1373 |
| 1374 range.set_start(range.start() - before); |
| 1375 range.set_end(range.end() + after); |
| 1376 gfx::Range text_range; |
| 1377 if (GetTextRange(&text_range) && text_range.Contains(range)) |
| 1378 DeleteRange(range); |
| 1379 } |
| 1380 |
| 1381 void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {} |
| 1382 |
| 1383 void Textfield::OnCandidateWindowShown() {} |
| 1384 |
| 1385 void Textfield::OnCandidateWindowUpdated() {} |
| 1386 |
| 1387 void Textfield::OnCandidateWindowHidden() {} |
| 1388 |
| 1389 //////////////////////////////////////////////////////////////////////////////// |
| 1390 // Textfield, protected: |
| 1391 |
| 1392 gfx::RenderText* Textfield::GetRenderText() const { |
| 1393 return model_->render_text(); |
| 1394 } |
| 1395 |
542 //////////////////////////////////////////////////////////////////////////////// | 1396 //////////////////////////////////////////////////////////////////////////////// |
543 // Textfield, private: | 1397 // Textfield, private: |
544 | 1398 |
545 gfx::Insets Textfield::GetTextInsets() const { | 1399 base::string16 Textfield::GetTextForDisplay(const base::string16& text) { |
546 gfx::Insets insets = GetInsets(); | 1400 return style_ & Textfield::STYLE_LOWERCASE ? base::i18n::ToLower(text) : text; |
547 if (draw_border_ && textfield_view_) | |
548 insets += textfield_view_->GetInsets(); | |
549 return insets; | |
550 } | 1401 } |
551 | 1402 |
552 void Textfield::AccessibilitySetValue(const base::string16& new_value) { | 1403 void Textfield::AccessibilitySetValue(const base::string16& new_value) { |
553 if (!read_only()) { | 1404 if (!read_only()) { |
554 SetText(new_value); | 1405 SetText(new_value); |
555 ClearSelection(); | 1406 ClearSelection(); |
556 } | 1407 } |
557 } | 1408 } |
558 | 1409 |
| 1410 void Textfield::UpdateHorizontalMargins() { |
| 1411 int left, right; |
| 1412 if (!GetHorizontalMargins(&left, &right)) |
| 1413 return; |
| 1414 gfx::Insets inset = GetInsets(); |
| 1415 text_border_->SetInsets(inset.top(), left, inset.bottom(), right); |
| 1416 OnBoundsChanged(GetBounds()); |
| 1417 } |
| 1418 |
| 1419 void Textfield::UpdateBackgroundColor() { |
| 1420 const SkColor color = GetBackgroundColor(); |
| 1421 set_background(Background::CreateSolidBackground(color)); |
| 1422 GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF); |
| 1423 SchedulePaint(); |
| 1424 } |
| 1425 |
| 1426 void Textfield::UpdateColorsFromTheme(const ui::NativeTheme* theme) { |
| 1427 gfx::RenderText* render_text = GetRenderText(); |
| 1428 render_text->SetColor(GetTextColor()); |
| 1429 UpdateBackgroundColor(); |
| 1430 render_text->set_cursor_color(GetTextColor()); |
| 1431 render_text->set_selection_color(theme->GetSystemColor( |
| 1432 ui::NativeTheme::kColorId_TextfieldSelectionColor)); |
| 1433 render_text->set_selection_background_focused_color(theme->GetSystemColor( |
| 1434 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)); |
| 1435 } |
| 1436 |
| 1437 void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { |
| 1438 if (text_changed) { |
| 1439 if (controller_) |
| 1440 controller_->ContentsChanged(this, model_->GetText()); |
| 1441 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); |
| 1442 } |
| 1443 if (cursor_changed) { |
| 1444 is_cursor_visible_ = true; |
| 1445 RepaintCursor(); |
| 1446 if (!text_changed) { |
| 1447 // TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire |
| 1448 // this if only the selection changed. |
| 1449 NotifyAccessibilityEvent( |
| 1450 ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); |
| 1451 } |
| 1452 } |
| 1453 if (text_changed || cursor_changed) { |
| 1454 OnCaretBoundsChanged(); |
| 1455 SchedulePaint(); |
| 1456 } |
| 1457 } |
| 1458 |
| 1459 void Textfield::UpdateCursor() { |
| 1460 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); |
| 1461 is_cursor_visible_ = !is_cursor_visible_ || (caret_blink_ms == 0); |
| 1462 RepaintCursor(); |
| 1463 if (caret_blink_ms != 0) { |
| 1464 base::MessageLoop::current()->PostDelayedTask( |
| 1465 FROM_HERE, |
| 1466 base::Bind(&Textfield::UpdateCursor, |
| 1467 cursor_weak_ptr_factory_.GetWeakPtr()), |
| 1468 base::TimeDelta::FromMilliseconds(caret_blink_ms)); |
| 1469 } |
| 1470 } |
| 1471 |
| 1472 void Textfield::RepaintCursor() { |
| 1473 gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds()); |
| 1474 r.Inset(-1, -1, -1, -1); |
| 1475 SchedulePaintInRect(r); |
| 1476 } |
| 1477 |
| 1478 void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) { |
| 1479 TRACE_EVENT0("views", "Textfield::PaintTextAndCursor"); |
| 1480 canvas->Save(); |
| 1481 gfx::RenderText* render_text = GetRenderText(); |
| 1482 render_text->set_cursor_visible(!is_drop_cursor_visible_ && |
| 1483 is_cursor_visible_ && !model_->HasSelection()); |
| 1484 // Draw the text, cursor, and selection. |
| 1485 render_text->Draw(canvas); |
| 1486 |
| 1487 // Draw the detached drop cursor that marks where the text will be dropped. |
| 1488 if (is_drop_cursor_visible_) |
| 1489 render_text->DrawCursor(canvas, drop_cursor_position_); |
| 1490 |
| 1491 // Draw placeholder text if needed. |
| 1492 if (model_->GetText().empty() && !GetPlaceholderText().empty()) { |
| 1493 canvas->DrawStringRect(GetPlaceholderText(), GetFontList(), |
| 1494 placeholder_text_color(), render_text->display_rect()); |
| 1495 } |
| 1496 canvas->Restore(); |
| 1497 } |
| 1498 |
| 1499 bool Textfield::MoveCursorTo(const gfx::Point& point, bool select) { |
| 1500 if (!model_->MoveCursorTo(point, select)) |
| 1501 return false; |
| 1502 OnCaretBoundsChanged(); |
| 1503 return true; |
| 1504 } |
| 1505 |
| 1506 void Textfield::OnCaretBoundsChanged() { |
| 1507 if (GetInputMethod()) |
| 1508 GetInputMethod()->OnCaretBoundsChanged(this); |
| 1509 if (touch_selection_controller_) |
| 1510 touch_selection_controller_->SelectionChanged(); |
| 1511 } |
| 1512 |
| 1513 void Textfield::OnBeforeUserAction() { |
| 1514 if (controller_) |
| 1515 controller_->OnBeforeUserAction(this); |
| 1516 } |
| 1517 |
| 1518 void Textfield::OnAfterUserAction() { |
| 1519 if (controller_) |
| 1520 controller_->OnAfterUserAction(this); |
| 1521 } |
| 1522 |
| 1523 bool Textfield::Cut() { |
| 1524 if (!read_only() && !IsObscured() && model_->Cut()) { |
| 1525 if (controller_) |
| 1526 controller_->OnAfterCutOrCopy(); |
| 1527 return true; |
| 1528 } |
| 1529 return false; |
| 1530 } |
| 1531 |
| 1532 bool Textfield::Copy() { |
| 1533 if (!IsObscured() && model_->Copy()) { |
| 1534 if (controller_) |
| 1535 controller_->OnAfterCutOrCopy(); |
| 1536 return true; |
| 1537 } |
| 1538 return false; |
| 1539 } |
| 1540 |
| 1541 bool Textfield::Paste() { |
| 1542 if (read_only()) |
| 1543 return false; |
| 1544 |
| 1545 const base::string16 original_text = model_->GetText(); |
| 1546 if (model_->Paste()) { |
| 1547 // As Paste is handled in model_->Paste(), the RenderText may contain |
| 1548 // upper case characters. This is not consistent with other places |
| 1549 // which keeps RenderText only containing lower case characters. |
| 1550 base::string16 new_text = GetTextForDisplay(model_->GetText()); |
| 1551 model_->SetText(new_text); |
| 1552 if (controller_) |
| 1553 controller_->OnAfterPaste(); |
| 1554 return true; |
| 1555 } |
| 1556 return false; |
| 1557 } |
| 1558 |
| 1559 void Textfield::UpdateContextMenu() { |
| 1560 if (!context_menu_contents_.get()) { |
| 1561 context_menu_contents_.reset(new ui::SimpleMenuModel(this)); |
| 1562 context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO); |
| 1563 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); |
| 1564 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); |
| 1565 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
| 1566 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); |
| 1567 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); |
| 1568 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); |
| 1569 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, |
| 1570 IDS_APP_SELECT_ALL); |
| 1571 if (controller_) |
| 1572 controller_->UpdateContextMenu(context_menu_contents_.get()); |
| 1573 context_menu_delegate_.reset( |
| 1574 new views::MenuModelAdapter(context_menu_contents_.get())); |
| 1575 context_menu_runner_.reset( |
| 1576 new MenuRunner(new views::MenuItemView(context_menu_delegate_.get()))); |
| 1577 } |
| 1578 |
| 1579 context_menu_delegate_->BuildMenu(context_menu_runner_->GetMenu()); |
| 1580 } |
| 1581 |
| 1582 void Textfield::TrackMouseClicks(const ui::MouseEvent& event) { |
| 1583 if (event.IsOnlyLeftMouseButton()) { |
| 1584 base::TimeDelta time_delta = event.time_stamp() - last_click_time_; |
| 1585 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && |
| 1586 !ExceededDragThreshold(event.location() - last_click_location_)) { |
| 1587 // Upon clicking after a triple click, the count should go back to double |
| 1588 // click and alternate between double and triple. This assignment maps |
| 1589 // 0 to 1, 1 to 2, 2 to 1. |
| 1590 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1; |
| 1591 } else { |
| 1592 aggregated_clicks_ = 0; |
| 1593 } |
| 1594 last_click_time_ = event.time_stamp(); |
| 1595 last_click_location_ = event.location(); |
| 1596 } |
| 1597 } |
| 1598 |
| 1599 bool Textfield::ImeEditingAllowed() const { |
| 1600 // Disallow input method editing of password fields. |
| 1601 ui::TextInputType t = GetTextInputType(); |
| 1602 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD); |
| 1603 } |
| 1604 |
| 1605 void Textfield::RevealObscuredChar(int index, const base::TimeDelta& duration) { |
| 1606 GetRenderText()->SetObscuredRevealIndex(index); |
| 1607 SchedulePaint(); |
| 1608 |
| 1609 if (index != -1) { |
| 1610 obscured_reveal_timer_.Start(FROM_HERE, duration, |
| 1611 base::Bind(&Textfield::RevealObscuredChar, base::Unretained(this), |
| 1612 -1, base::TimeDelta())); |
| 1613 } |
| 1614 } |
| 1615 |
| 1616 void Textfield::CreateTouchSelectionControllerAndNotifyIt() { |
| 1617 if (!touch_selection_controller_) { |
| 1618 touch_selection_controller_.reset( |
| 1619 ui::TouchSelectionController::create(this)); |
| 1620 } |
| 1621 if (touch_selection_controller_) |
| 1622 touch_selection_controller_->SelectionChanged(); |
| 1623 } |
| 1624 |
559 } // namespace views | 1625 } // namespace views |
OLD | NEW |