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