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

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

Issue 9390022: Simplify handling of BiDi cursor movement (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: make set_selection_start private, fix accidental inclusion of local hacks Created 8 years, 10 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/controls/textfield/textfield_views_model.h" 5 #include "ui/views/controls/textfield/textfield_views_model.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/i18n/break_iterator.h" 9 #include "base/i18n/break_iterator.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 return true; 338 return true;
339 } 339 }
340 if (HasSelection()) { 340 if (HasSelection()) {
341 DeleteSelection(); 341 DeleteSelection();
342 return true; 342 return true;
343 } 343 }
344 if (GetText().length() > GetCursorPosition()) { 344 if (GetText().length() > GetCursorPosition()) {
345 size_t cursor_position = GetCursorPosition(); 345 size_t cursor_position = GetCursorPosition();
346 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( 346 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme(
347 cursor_position, gfx::CURSOR_FORWARD); 347 cursor_position, gfx::CURSOR_FORWARD);
348 ExecuteAndRecordDelete(cursor_position, next_grapheme_index, true); 348 ExecuteAndRecordDelete(ui::Range(cursor_position, next_grapheme_index),
349 true);
349 return true; 350 return true;
350 } 351 }
351 return false; 352 return false;
352 } 353 }
353 354
354 bool TextfieldViewsModel::Backspace() { 355 bool TextfieldViewsModel::Backspace() {
355 if (HasCompositionText()) { 356 if (HasCompositionText()) {
356 // No undo/redo for composition text. 357 // No undo/redo for composition text.
357 CancelCompositionText(); 358 CancelCompositionText();
358 return true; 359 return true;
359 } 360 }
360 if (HasSelection()) { 361 if (HasSelection()) {
361 DeleteSelection(); 362 DeleteSelection();
362 return true; 363 return true;
363 } 364 }
364 if (GetCursorPosition() > 0) { 365 if (GetCursorPosition() > 0) {
365 size_t cursor_position = GetCursorPosition(); 366 size_t cursor_position = GetCursorPosition();
366 ExecuteAndRecordDelete(cursor_position, cursor_position - 1, true); 367 ExecuteAndRecordDelete(ui::Range(cursor_position, cursor_position - 1),
368 true);
367 return true; 369 return true;
368 } 370 }
369 return false; 371 return false;
370 } 372 }
371 373
372 size_t TextfieldViewsModel::GetCursorPosition() const { 374 size_t TextfieldViewsModel::GetCursorPosition() const {
373 return render_text_->GetCursorPosition(); 375 return render_text_->cursor_position();
374 } 376 }
375 377
376 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, 378 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type,
377 gfx::VisualCursorDirection direction, 379 gfx::VisualCursorDirection direction,
378 bool select) { 380 bool select) {
379 if (HasCompositionText()) 381 if (HasCompositionText())
380 ConfirmCompositionText(); 382 ConfirmCompositionText();
381 render_text_->MoveCursor(break_type, direction, select); 383 render_text_->MoveCursor(break_type, direction, select);
382 } 384 }
383 385
384 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) { 386 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& model) {
385 if (HasCompositionText()) { 387 if (HasCompositionText()) {
386 ConfirmCompositionText(); 388 ConfirmCompositionText();
387 // ConfirmCompositionText() updates cursor position. Need to reflect it in 389 // ConfirmCompositionText() updates cursor position. Need to reflect it in
388 // the SelectionModel parameter of MoveCursorTo(). 390 // the SelectionModel parameter of MoveCursorTo().
389 if (render_text_->GetSelectionStart() != selection.selection_end()) 391 ui::Range range(render_text_->selection().start(), model.caret_pos());
390 return render_text_->SelectRange(ui::Range( 392 if (!range.is_empty())
391 render_text_->GetSelectionStart(), selection.selection_end())); 393 return render_text_->SelectRange(range);
392 gfx::SelectionModel sel(selection.selection_end(), 394 return render_text_->MoveCursorTo(
393 selection.caret_pos(), 395 gfx::SelectionModel(model.caret_pos(), model.caret_affinity()));
394 selection.caret_placement());
395 return render_text_->MoveCursorTo(sel);
396 } 396 }
397 return render_text_->MoveCursorTo(selection); 397 return render_text_->MoveCursorTo(model);
398 } 398 }
399 399
400 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { 400 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) {
401 if (HasCompositionText()) 401 if (HasCompositionText())
402 ConfirmCompositionText(); 402 ConfirmCompositionText();
403 return render_text_->MoveCursorTo(point, select); 403 return render_text_->MoveCursorTo(point, select);
404 } 404 }
405 405
406 string16 TextfieldViewsModel::GetSelectedText() const { 406 string16 TextfieldViewsModel::GetSelectedText() const {
407 return GetText().substr(render_text_->MinOfSelection(), 407 return GetText().substr(render_text_->selection().GetMin(),
408 (render_text_->MaxOfSelection() - render_text_->MinOfSelection())); 408 render_text_->selection().length());
409 }
410
411 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const {
412 range->set_start(render_text_->GetSelectionStart());
413 range->set_end(render_text_->GetCursorPosition());
414 } 409 }
415 410
416 void TextfieldViewsModel::SelectRange(const ui::Range& range) { 411 void TextfieldViewsModel::SelectRange(const ui::Range& range) {
417 if (HasCompositionText()) 412 if (HasCompositionText())
418 ConfirmCompositionText(); 413 ConfirmCompositionText();
419 render_text_->SelectRange(range); 414 render_text_->SelectRange(range);
420 } 415 }
421 416
422 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { 417 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const {
423 *sel = render_text_->selection_model(); 418 *sel = render_text_->selection_model();
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 494
500 bool TextfieldViewsModel::Cut() { 495 bool TextfieldViewsModel::Cut() {
501 if (!HasCompositionText() && HasSelection()) { 496 if (!HasCompositionText() && HasSelection()) {
502 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate 497 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate
503 ->GetClipboard()).WriteText(GetSelectedText()); 498 ->GetClipboard()).WriteText(GetSelectedText());
504 // A trick to let undo/redo handle cursor correctly. 499 // A trick to let undo/redo handle cursor correctly.
505 // Undoing CUT moves the cursor to the end of the change rather 500 // Undoing CUT moves the cursor to the end of the change rather
506 // than beginning, unlike Delete/Backspace. 501 // than beginning, unlike Delete/Backspace.
507 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, 502 // TODO(oshima): Change Delete/Backspace to use DeleteSelection,
508 // update DeleteEdit and remove this trick. 503 // update DeleteEdit and remove this trick.
509 render_text_->SelectRange(ui::Range(render_text_->GetCursorPosition(), 504 const ui::Range& selection = render_text_->selection();
510 render_text_->GetSelectionStart())); 505 render_text_->SelectRange(ui::Range(selection.end(), selection.start()));
511 DeleteSelection(); 506 DeleteSelection();
512 return true; 507 return true;
513 } 508 }
514 return false; 509 return false;
515 } 510 }
516 511
517 bool TextfieldViewsModel::Copy() { 512 bool TextfieldViewsModel::Copy() {
518 if (!HasCompositionText() && HasSelection()) { 513 if (!HasCompositionText() && HasSelection()) {
519 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate 514 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate
520 ->GetClipboard()).WriteText(GetSelectedText()); 515 ->GetClipboard()).WriteText(GetSelectedText());
521 return true; 516 return true;
522 } 517 }
523 return false; 518 return false;
524 } 519 }
525 520
526 bool TextfieldViewsModel::Paste() { 521 bool TextfieldViewsModel::Paste() {
527 string16 result; 522 string16 result;
528 views::ViewsDelegate::views_delegate->GetClipboard() 523 views::ViewsDelegate::views_delegate->GetClipboard()
529 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 524 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result);
530 if (!result.empty()) { 525 if (!result.empty()) {
531 InsertTextInternal(result, false); 526 InsertTextInternal(result, false);
532 return true; 527 return true;
533 } 528 }
534 return false; 529 return false;
535 } 530 }
536 531
537 bool TextfieldViewsModel::HasSelection() const { 532 bool TextfieldViewsModel::HasSelection() const {
538 return !render_text_->EmptySelection(); 533 return !render_text_->selection().is_empty();
539 } 534 }
540 535
541 void TextfieldViewsModel::DeleteSelection() { 536 void TextfieldViewsModel::DeleteSelection() {
542 DCHECK(!HasCompositionText()); 537 DCHECK(!HasCompositionText());
543 DCHECK(HasSelection()); 538 DCHECK(HasSelection());
544 ExecuteAndRecordDelete(render_text_->GetSelectionStart(), 539 ExecuteAndRecordDelete(render_text_->selection(), false);
545 render_text_->GetCursorPosition(), false);
546 } 540 }
547 541
548 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( 542 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt(
549 const string16& text, size_t position) { 543 const string16& text, size_t position) {
550 if (HasCompositionText()) 544 if (HasCompositionText())
551 CancelCompositionText(); 545 CancelCompositionText();
552 ExecuteAndRecordReplace(DO_NOT_MERGE, 546 ExecuteAndRecordReplace(DO_NOT_MERGE,
553 GetCursorPosition(), 547 GetCursorPosition(),
554 position + text.length(), 548 position + text.length(),
555 text, 549 text,
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
650 bool mergeable) { 644 bool mergeable) {
651 if (HasCompositionText()) { 645 if (HasCompositionText()) {
652 CancelCompositionText(); 646 CancelCompositionText();
653 } else if (!HasSelection()) { 647 } else if (!HasSelection()) {
654 size_t cursor = GetCursorPosition(); 648 size_t cursor = GetCursorPosition();
655 const gfx::SelectionModel& model = render_text_->selection_model(); 649 const gfx::SelectionModel& model = render_text_->selection_model();
656 // When there is no selection, the default is to replace the next grapheme 650 // When there is no selection, the default is to replace the next grapheme
657 // with |text|. So, need to find the index of next grapheme first. 651 // with |text|. So, need to find the index of next grapheme first.
658 size_t next = 652 size_t next =
659 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); 653 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD);
660 if (next == model.selection_end()) 654 if (next == model.caret_pos())
661 render_text_->MoveCursorTo(model); 655 render_text_->MoveCursorTo(model);
662 else 656 else
663 render_text_->SelectRange(ui::Range(next, model.selection_end())); 657 render_text_->SelectRange(ui::Range(next, model.caret_pos()));
664 } 658 }
665 // Edit history is recorded in InsertText. 659 // Edit history is recorded in InsertText.
666 InsertTextInternal(text, mergeable); 660 InsertTextInternal(text, mergeable);
667 } 661 }
668 662
669 void TextfieldViewsModel::ClearEditHistory() { 663 void TextfieldViewsModel::ClearEditHistory() {
670 STLDeleteContainerPointers(edit_history_.begin(), 664 STLDeleteContainerPointers(edit_history_.begin(),
671 edit_history_.end()); 665 edit_history_.end());
672 edit_history_.clear(); 666 edit_history_.clear();
673 current_edit_ = edit_history_.end(); 667 current_edit_ = edit_history_.end();
674 } 668 }
675 669
676 void TextfieldViewsModel::ClearRedoHistory() { 670 void TextfieldViewsModel::ClearRedoHistory() {
677 if (edit_history_.begin() == edit_history_.end()) 671 if (edit_history_.begin() == edit_history_.end())
678 return; 672 return;
679 if (current_edit_ == edit_history_.end()) { 673 if (current_edit_ == edit_history_.end()) {
680 ClearEditHistory(); 674 ClearEditHistory();
681 return; 675 return;
682 } 676 }
683 EditHistory::iterator delete_start = current_edit_; 677 EditHistory::iterator delete_start = current_edit_;
684 delete_start++; 678 delete_start++;
685 STLDeleteContainerPointers(delete_start, edit_history_.end()); 679 STLDeleteContainerPointers(delete_start, edit_history_.end());
686 edit_history_.erase(delete_start, edit_history_.end()); 680 edit_history_.erase(delete_start, edit_history_.end());
687 } 681 }
688 682
689 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, 683 void TextfieldViewsModel::ExecuteAndRecordDelete(ui::Range range,
690 size_t to,
691 bool mergeable) { 684 bool mergeable) {
692 size_t old_text_start = std::min(from, to); 685 size_t old_text_start = range.GetMin();
693 const string16 text = GetText().substr(old_text_start, 686 const string16 text = GetText().substr(old_text_start, range.length());
694 std::abs(static_cast<long>(from - to))); 687 bool backward = range.is_reversed();
695 bool backward = from > to;
696 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); 688 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward);
697 bool delete_edit = AddOrMergeEditHistory(edit); 689 bool delete_edit = AddOrMergeEditHistory(edit);
698 edit->Redo(this); 690 edit->Redo(this);
699 if (delete_edit) 691 if (delete_edit)
700 delete edit; 692 delete edit;
701 } 693 }
702 694
703 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( 695 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection(
704 MergeType merge_type, const string16& new_text) { 696 MergeType merge_type, const string16& new_text) {
705 size_t new_text_start = render_text_->MinOfSelection(); 697 size_t new_text_start = render_text_->selection().GetMin();
706 size_t new_cursor_pos = new_text_start + new_text.length(); 698 size_t new_cursor_pos = new_text_start + new_text.length();
707 ExecuteAndRecordReplace(merge_type, 699 ExecuteAndRecordReplace(merge_type,
708 GetCursorPosition(), 700 GetCursorPosition(),
709 new_cursor_pos, 701 new_cursor_pos,
710 new_text, 702 new_text,
711 new_text_start); 703 new_text_start);
712 } 704 }
713 705
714 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, 706 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type,
715 size_t old_cursor_pos, 707 size_t old_cursor_pos,
716 size_t new_cursor_pos, 708 size_t new_cursor_pos,
717 const string16& new_text, 709 const string16& new_text,
718 size_t new_text_start) { 710 size_t new_text_start) {
719 size_t old_text_start = render_text_->MinOfSelection(); 711 size_t old_text_start = render_text_->selection().GetMin();
720 bool backward = 712 bool backward = render_text_->selection().is_reversed();
721 render_text_->GetSelectionStart() > render_text_->GetCursorPosition();
722 Edit* edit = new ReplaceEdit(merge_type, 713 Edit* edit = new ReplaceEdit(merge_type,
723 GetSelectedText(), 714 GetSelectedText(),
724 old_cursor_pos, 715 old_cursor_pos,
725 old_text_start, 716 old_text_start,
726 backward, 717 backward,
727 new_cursor_pos, 718 new_cursor_pos,
728 new_text, 719 new_text,
729 new_text_start); 720 new_text_start);
730 bool delete_edit = AddOrMergeEditHistory(edit); 721 bool delete_edit = AddOrMergeEditHistory(edit);
731 edit->Redo(this); 722 edit->Redo(this);
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
774 if (delete_from != delete_to) 765 if (delete_from != delete_to)
775 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); 766 render_text_->SetText(text.erase(delete_from, delete_to - delete_from));
776 if (!new_text.empty()) 767 if (!new_text.empty())
777 render_text_->SetText(text.insert(new_text_insert_at, new_text)); 768 render_text_->SetText(text.insert(new_text_insert_at, new_text));
778 render_text_->SetCursorPosition(new_cursor_pos); 769 render_text_->SetCursorPosition(new_cursor_pos);
779 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). 770 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't).
780 // This looks fine feature and we may want to do the same. 771 // This looks fine feature and we may want to do the same.
781 } 772 }
782 773
783 } // namespace views 774 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698