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

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

Powered by Google App Engine
This is Rietveld 408576698