Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(342)

Side by Side Diff: ui/views/controls/textfield/native_textfield_views.cc

Issue 135863002: Reland Merge NativeTextfieldViews into views::Textfield. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Handle Ctrl-Shift-Delete and Backspace on Linux, like on ChromeOS. Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « ui/views/controls/textfield/native_textfield_views.h ('k') | ui/views/controls/textfield/native_textfield_views_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698