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

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: . 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 } 409 }
410 410
411 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { 411 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const {
412 range->set_start(render_text_->GetSelectionStart()); 412 *range = render_text_->selection();
413 range->set_end(render_text_->GetCursorPosition());
414 } 413 }
415 414
416 void TextfieldViewsModel::SelectRange(const ui::Range& range) { 415 void TextfieldViewsModel::SelectRange(const ui::Range& range) {
417 if (HasCompositionText()) 416 if (HasCompositionText())
418 ConfirmCompositionText(); 417 ConfirmCompositionText();
419 render_text_->SelectRange(range); 418 render_text_->SelectRange(range);
420 } 419 }
421 420
422 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { 421 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const {
423 *sel = render_text_->selection_model(); 422 *sel = render_text_->selection_model();
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 498
500 bool TextfieldViewsModel::Cut() { 499 bool TextfieldViewsModel::Cut() {
501 if (!HasCompositionText() && HasSelection()) { 500 if (!HasCompositionText() && HasSelection()) {
502 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate 501 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate
503 ->GetClipboard()).WriteText(GetSelectedText()); 502 ->GetClipboard()).WriteText(GetSelectedText());
504 // A trick to let undo/redo handle cursor correctly. 503 // A trick to let undo/redo handle cursor correctly.
505 // 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
506 // than beginning, unlike Delete/Backspace. 505 // than beginning, unlike Delete/Backspace.
507 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, 506 // TODO(oshima): Change Delete/Backspace to use DeleteSelection,
508 // update DeleteEdit and remove this trick. 507 // update DeleteEdit and remove this trick.
509 render_text_->SelectRange(ui::Range(render_text_->GetCursorPosition(), 508 const ui::Range& selection = render_text_->selection();
510 render_text_->GetSelectionStart())); 509 render_text_->SelectRange(ui::Range(selection.end(), selection.start()));
511 DeleteSelection(); 510 DeleteSelection();
512 return true; 511 return true;
513 } 512 }
514 return false; 513 return false;
515 } 514 }
516 515
517 bool TextfieldViewsModel::Copy() { 516 bool TextfieldViewsModel::Copy() {
518 if (!HasCompositionText() && HasSelection()) { 517 if (!HasCompositionText() && HasSelection()) {
519 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate 518 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate
520 ->GetClipboard()).WriteText(GetSelectedText()); 519 ->GetClipboard()).WriteText(GetSelectedText());
521 return true; 520 return true;
522 } 521 }
523 return false; 522 return false;
524 } 523 }
525 524
526 bool TextfieldViewsModel::Paste() { 525 bool TextfieldViewsModel::Paste() {
527 string16 result; 526 string16 result;
528 views::ViewsDelegate::views_delegate->GetClipboard() 527 views::ViewsDelegate::views_delegate->GetClipboard()
529 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 528 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result);
530 if (!result.empty()) { 529 if (!result.empty()) {
531 InsertTextInternal(result, false); 530 InsertTextInternal(result, false);
532 return true; 531 return true;
533 } 532 }
534 return false; 533 return false;
535 } 534 }
536 535
537 bool TextfieldViewsModel::HasSelection() const { 536 bool TextfieldViewsModel::HasSelection() const {
538 return !render_text_->EmptySelection(); 537 return !render_text_->selection().is_empty();
539 } 538 }
540 539
541 void TextfieldViewsModel::DeleteSelection() { 540 void TextfieldViewsModel::DeleteSelection() {
542 DCHECK(!HasCompositionText()); 541 DCHECK(!HasCompositionText());
543 DCHECK(HasSelection()); 542 DCHECK(HasSelection());
544 ExecuteAndRecordDelete(render_text_->GetSelectionStart(), 543 ExecuteAndRecordDelete(render_text_->selection(), false);
545 render_text_->GetCursorPosition(), false);
546 } 544 }
547 545
548 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( 546 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt(
549 const string16& text, size_t position) { 547 const string16& text, size_t position) {
550 if (HasCompositionText()) 548 if (HasCompositionText())
551 CancelCompositionText(); 549 CancelCompositionText();
552 ExecuteAndRecordReplace(DO_NOT_MERGE, 550 ExecuteAndRecordReplace(DO_NOT_MERGE,
553 GetCursorPosition(), 551 GetCursorPosition(),
554 position + text.length(), 552 position + text.length(),
555 text, 553 text,
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
650 bool mergeable) { 648 bool mergeable) {
651 if (HasCompositionText()) { 649 if (HasCompositionText()) {
652 CancelCompositionText(); 650 CancelCompositionText();
653 } else if (!HasSelection()) { 651 } else if (!HasSelection()) {
654 size_t cursor = GetCursorPosition(); 652 size_t cursor = GetCursorPosition();
655 const gfx::SelectionModel& model = render_text_->selection_model(); 653 const gfx::SelectionModel& model = render_text_->selection_model();
656 // When there is no selection, the default is to replace the next grapheme 654 // 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. 655 // with |text|. So, need to find the index of next grapheme first.
658 size_t next = 656 size_t next =
659 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); 657 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD);
660 if (next == model.selection_end()) 658 if (next == model.caret_pos())
661 render_text_->MoveCursorTo(model); 659 render_text_->MoveCursorTo(model);
662 else 660 else
663 render_text_->SelectRange(ui::Range(next, model.selection_end())); 661 render_text_->SelectRange(ui::Range(next, model.caret_pos()));
664 } 662 }
665 // Edit history is recorded in InsertText. 663 // Edit history is recorded in InsertText.
666 InsertTextInternal(text, mergeable); 664 InsertTextInternal(text, mergeable);
667 } 665 }
668 666
669 void TextfieldViewsModel::ClearEditHistory() { 667 void TextfieldViewsModel::ClearEditHistory() {
670 STLDeleteContainerPointers(edit_history_.begin(), 668 STLDeleteContainerPointers(edit_history_.begin(),
671 edit_history_.end()); 669 edit_history_.end());
672 edit_history_.clear(); 670 edit_history_.clear();
673 current_edit_ = edit_history_.end(); 671 current_edit_ = edit_history_.end();
674 } 672 }
675 673
676 void TextfieldViewsModel::ClearRedoHistory() { 674 void TextfieldViewsModel::ClearRedoHistory() {
677 if (edit_history_.begin() == edit_history_.end()) 675 if (edit_history_.begin() == edit_history_.end())
678 return; 676 return;
679 if (current_edit_ == edit_history_.end()) { 677 if (current_edit_ == edit_history_.end()) {
680 ClearEditHistory(); 678 ClearEditHistory();
681 return; 679 return;
682 } 680 }
683 EditHistory::iterator delete_start = current_edit_; 681 EditHistory::iterator delete_start = current_edit_;
684 delete_start++; 682 delete_start++;
685 STLDeleteContainerPointers(delete_start, edit_history_.end()); 683 STLDeleteContainerPointers(delete_start, edit_history_.end());
686 edit_history_.erase(delete_start, edit_history_.end()); 684 edit_history_.erase(delete_start, edit_history_.end());
687 } 685 }
688 686
689 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, 687 void TextfieldViewsModel::ExecuteAndRecordDelete(ui::Range range,
690 size_t to,
691 bool mergeable) { 688 bool mergeable) {
692 size_t old_text_start = std::min(from, to); 689 size_t old_text_start = range.GetMin();
693 const string16 text = GetText().substr(old_text_start, 690 const string16 text = GetText().substr(old_text_start, range.length());
694 std::abs(static_cast<long>(from - to))); 691 bool backward = range.is_reversed();
695 bool backward = from > to;
696 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); 692 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward);
697 bool delete_edit = AddOrMergeEditHistory(edit); 693 bool delete_edit = AddOrMergeEditHistory(edit);
698 edit->Redo(this); 694 edit->Redo(this);
699 if (delete_edit) 695 if (delete_edit)
700 delete edit; 696 delete edit;
701 } 697 }
702 698
703 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( 699 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection(
704 MergeType merge_type, const string16& new_text) { 700 MergeType merge_type, const string16& new_text) {
705 size_t new_text_start = render_text_->MinOfSelection(); 701 size_t new_text_start = render_text_->selection().GetMin();
706 size_t new_cursor_pos = new_text_start + new_text.length(); 702 size_t new_cursor_pos = new_text_start + new_text.length();
707 ExecuteAndRecordReplace(merge_type, 703 ExecuteAndRecordReplace(merge_type,
708 GetCursorPosition(), 704 GetCursorPosition(),
709 new_cursor_pos, 705 new_cursor_pos,
710 new_text, 706 new_text,
711 new_text_start); 707 new_text_start);
712 } 708 }
713 709
714 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, 710 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type,
715 size_t old_cursor_pos, 711 size_t old_cursor_pos,
716 size_t new_cursor_pos, 712 size_t new_cursor_pos,
717 const string16& new_text, 713 const string16& new_text,
718 size_t new_text_start) { 714 size_t new_text_start) {
719 size_t old_text_start = render_text_->MinOfSelection(); 715 size_t old_text_start = render_text_->selection().GetMin();
720 bool backward = 716 bool backward = render_text_->selection().is_reversed();
721 render_text_->GetSelectionStart() > render_text_->GetCursorPosition();
722 Edit* edit = new ReplaceEdit(merge_type, 717 Edit* edit = new ReplaceEdit(merge_type,
723 GetSelectedText(), 718 GetSelectedText(),
724 old_cursor_pos, 719 old_cursor_pos,
725 old_text_start, 720 old_text_start,
726 backward, 721 backward,
727 new_cursor_pos, 722 new_cursor_pos,
728 new_text, 723 new_text,
729 new_text_start); 724 new_text_start);
730 bool delete_edit = AddOrMergeEditHistory(edit); 725 bool delete_edit = AddOrMergeEditHistory(edit);
731 edit->Redo(this); 726 edit->Redo(this);
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
774 if (delete_from != delete_to) 769 if (delete_from != delete_to)
775 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); 770 render_text_->SetText(text.erase(delete_from, delete_to - delete_from));
776 if (!new_text.empty()) 771 if (!new_text.empty())
777 render_text_->SetText(text.insert(new_text_insert_at, new_text)); 772 render_text_->SetText(text.insert(new_text_insert_at, new_text));
778 render_text_->SetCursorPosition(new_cursor_pos); 773 render_text_->SetCursorPosition(new_cursor_pos);
779 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). 774 // 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. 775 // This looks fine feature and we may want to do the same.
781 } 776 }
782 777
783 } // namespace views 778 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698