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