Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/gfx/render_text.h" | 5 #include "ui/gfx/render_text.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
| 10 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 331 // range ends beyond text_.length, it must be the last one. | 331 // range ends beyond text_.length, it must be the last one. |
| 332 style_ranges_.back().range.set_end(text_.length()); | 332 style_ranges_.back().range.set_end(text_.length()); |
| 333 } | 333 } |
| 334 #ifndef NDEBUG | 334 #ifndef NDEBUG |
| 335 CheckStyleRanges(style_ranges_, text_.length()); | 335 CheckStyleRanges(style_ranges_, text_.length()); |
| 336 #endif | 336 #endif |
| 337 cached_bounds_and_offset_valid_ = false; | 337 cached_bounds_and_offset_valid_ = false; |
| 338 | 338 |
| 339 // Reset selection model. SetText should always followed by SetSelectionModel | 339 // Reset selection model. SetText should always followed by SetSelectionModel |
| 340 // or SetCursorPosition in upper layer. | 340 // or SetCursorPosition in upper layer. |
| 341 SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING)); | 341 SetSelectionModel(SelectionModel()); |
| 342 | 342 |
| 343 UpdateLayout(); | 343 UpdateLayout(); |
| 344 } | 344 } |
| 345 | 345 |
| 346 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) { | 346 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) { |
| 347 if (horizontal_alignment_ != alignment) { | 347 if (horizontal_alignment_ != alignment) { |
| 348 horizontal_alignment_ = alignment; | 348 horizontal_alignment_ = alignment; |
| 349 display_offset_ = Point(); | 349 display_offset_ = Point(); |
| 350 cached_bounds_and_offset_valid_ = false; | 350 cached_bounds_and_offset_valid_ = false; |
| 351 } | 351 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 UpdateLayout(); | 384 UpdateLayout(); |
| 385 } | 385 } |
| 386 } | 386 } |
| 387 | 387 |
| 388 void RenderText::SetDisplayRect(const Rect& r) { | 388 void RenderText::SetDisplayRect(const Rect& r) { |
| 389 display_rect_ = r; | 389 display_rect_ = r; |
| 390 cached_bounds_and_offset_valid_ = false; | 390 cached_bounds_and_offset_valid_ = false; |
| 391 UpdateLayout(); | 391 UpdateLayout(); |
| 392 } | 392 } |
| 393 | 393 |
| 394 size_t RenderText::GetCursorPosition() const { | |
| 395 return selection_model_.selection_end(); | |
| 396 } | |
| 397 | |
| 398 void RenderText::SetCursorPosition(size_t position) { | 394 void RenderText::SetCursorPosition(size_t position) { |
| 399 MoveCursorTo(position, false); | 395 MoveCursorTo(position, false); |
| 400 } | 396 } |
| 401 | 397 |
| 402 void RenderText::MoveCursor(BreakType break_type, | 398 void RenderText::MoveCursor(BreakType break_type, |
| 403 VisualCursorDirection direction, | 399 VisualCursorDirection direction, |
| 404 bool select) { | 400 bool select) { |
| 405 SelectionModel position(selection_model()); | 401 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); |
| 406 position.set_selection_start(GetCursorPosition()); | |
| 407 // Cancelling a selection moves to the edge of the selection. | 402 // Cancelling a selection moves to the edge of the selection. |
| 408 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 403 if (break_type != LINE_BREAK && !selection().is_empty() && !select) { |
| 409 SelectionModel selection_start = GetSelectionModelForSelectionStart(); | 404 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
| 410 int start_x = GetCursorBounds(selection_start, true).x(); | 405 int start_x = GetCursorBounds(selection_start, true).x(); |
| 411 int cursor_x = GetCursorBounds(position, true).x(); | 406 int cursor_x = GetCursorBounds(position, true).x(); |
| 412 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) | 407 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) |
| 413 // or right (when |direction| is CURSOR_RIGHT) of the selection end. | 408 // or right (when |direction| is CURSOR_RIGHT) of the selection end. |
| 414 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) | 409 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) |
| 415 position = selection_start; | 410 position = selection_start; |
| 416 // For word breaks, use the nearest word boundary in the appropriate | 411 // For word breaks, use the nearest word boundary in the appropriate |
| 417 // |direction|. | 412 // |direction|. |
| 418 if (break_type == WORD_BREAK) | 413 if (break_type == WORD_BREAK) |
| 419 position = GetAdjacentSelectionModel(position, break_type, direction); | 414 position = GetAdjacentSelectionModel(position, break_type, direction); |
| 420 } else { | 415 } else { |
| 421 position = GetAdjacentSelectionModel(position, break_type, direction); | 416 position = GetAdjacentSelectionModel(position, break_type, direction); |
| 422 } | 417 } |
| 423 if (select) | 418 if (select) |
| 424 position.set_selection_start(GetSelectionStart()); | 419 position.set_selection_start(selection().start()); |
| 425 MoveCursorTo(position); | 420 MoveCursorTo(position); |
| 426 } | 421 } |
| 427 | 422 |
| 428 bool RenderText::MoveCursorTo(const SelectionModel& model) { | 423 bool RenderText::MoveCursorTo(const SelectionModel& model) { |
| 429 SelectionModel sel(model); | 424 // Enforce valid selection model components. |
| 430 size_t text_length = text().length(); | 425 size_t text_length = text().length(); |
| 431 // Enforce valid selection model components. | 426 LogicalCursorDirection affinity = model.caret_affinity(); |
| 432 if (sel.selection_start() > text_length) | 427 if (model.caret_pos() > text_length) { |
|
msw
2012/03/08 18:07:19
When is this true? What tries to move the cursor t
benrg
2012/03/08 21:22:03
I think that nobody should, but I'm nervous about
msw
2012/03/08 21:33:57
Let's just remove this; in the worst case, a calle
benrg
2012/03/08 22:06:31
Done.
| |
| 433 sel.set_selection_start(text_length); | 428 // Make the cursor appear at the edge of the text, not next to the final |
| 434 if (sel.selection_end() > text_length) | 429 // grapheme. |
| 435 sel.set_selection_end(text_length); | 430 affinity = CURSOR_FORWARD; |
| 436 // The current model only supports caret positions at valid character indices. | |
| 437 if (text_length == 0) { | |
| 438 sel.set_caret_pos(0); | |
| 439 sel.set_caret_placement(SelectionModel::LEADING); | |
| 440 } else if (sel.caret_pos() >= text_length) { | |
| 441 SelectionModel end_selection = | |
| 442 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd()); | |
| 443 sel.set_caret_pos(end_selection.caret_pos()); | |
| 444 sel.set_caret_placement(end_selection.caret_placement()); | |
| 445 } | 431 } |
| 446 | 432 |
| 447 if (!IsCursorablePosition(sel.selection_start()) || | 433 ui::Range range(std::min(model.selection().start(), text_length), |
| 448 !IsCursorablePosition(sel.selection_end()) || | 434 std::min(model.caret_pos(), text_length)); |
| 449 !IsCursorablePosition(sel.caret_pos())) | 435 // The current model only supports caret positions at valid character indices. |
| 436 if (!IsCursorablePosition(range.start()) || | |
| 437 !IsCursorablePosition(range.end())) | |
| 450 return false; | 438 return false; |
| 451 | 439 SelectionModel sel(range, affinity); |
| 452 bool changed = !sel.Equals(selection_model_); | 440 bool changed = sel != selection_model_; |
| 453 SetSelectionModel(sel); | 441 SetSelectionModel(sel); |
| 454 return changed; | 442 return changed; |
| 455 } | 443 } |
| 456 | 444 |
| 457 bool RenderText::MoveCursorTo(const Point& point, bool select) { | 445 bool RenderText::MoveCursorTo(const Point& point, bool select) { |
| 458 SelectionModel selection = FindCursorPosition(point); | 446 SelectionModel position = FindCursorPosition(point); |
| 459 if (select) | 447 if (select) |
| 460 selection.set_selection_start(GetSelectionStart()); | 448 position.set_selection_start(selection().start()); |
| 461 return MoveCursorTo(selection); | 449 return MoveCursorTo(position); |
| 462 } | 450 } |
| 463 | 451 |
| 464 bool RenderText::SelectRange(const ui::Range& range) { | 452 bool RenderText::SelectRange(const ui::Range& range) { |
| 465 size_t text_length = text().length(); | 453 ui::Range sel(std::min(range.start(), text().length()), |
| 466 size_t start = std::min(range.start(), text_length); | 454 std::min(range.end(), text().length())); |
| 467 size_t end = std::min(range.end(), text_length); | 455 if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end())) |
| 468 | |
| 469 if (!IsCursorablePosition(start) || !IsCursorablePosition(end)) | |
| 470 return false; | 456 return false; |
| 471 | 457 LogicalCursorDirection affinity = |
| 472 size_t pos = end; | 458 (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD; |
| 473 SelectionModel::CaretPlacement placement = SelectionModel::LEADING; | 459 SetSelectionModel(SelectionModel(sel, affinity)); |
| 474 if (start < end) { | |
| 475 pos = IndexOfAdjacentGrapheme(end, CURSOR_BACKWARD); | |
| 476 DCHECK_LT(pos, end); | |
| 477 placement = SelectionModel::TRAILING; | |
| 478 } else if (end == text_length) { | |
| 479 SelectionModel end_selection = | |
| 480 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd()); | |
| 481 pos = end_selection.caret_pos(); | |
| 482 placement = end_selection.caret_placement(); | |
| 483 } | |
| 484 SetSelectionModel(SelectionModel(start, end, pos, placement)); | |
| 485 return true; | 460 return true; |
| 486 } | 461 } |
| 487 | 462 |
| 488 bool RenderText::IsPointInSelection(const Point& point) { | 463 bool RenderText::IsPointInSelection(const Point& point) { |
| 489 if (EmptySelection()) | 464 if (selection().is_empty()) |
| 490 return false; | 465 return false; |
| 491 // TODO(xji): should this check whether the point is inside the visual | 466 SelectionModel cursor = FindCursorPosition(point); |
| 492 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points | 467 return RangeContainsCaret( |
| 493 // to the right half of 'c', is the point in selection? | 468 selection(), cursor.caret_pos(), cursor.caret_affinity()); |
| 494 size_t pos = FindCursorPosition(point).selection_end(); | |
| 495 return (pos >= MinOfSelection() && pos < MaxOfSelection()); | |
| 496 } | 469 } |
| 497 | 470 |
| 498 void RenderText::ClearSelection() { | 471 void RenderText::ClearSelection() { |
| 499 SelectionModel sel(selection_model()); | 472 SetSelectionModel(SelectionModel(cursor_position(), |
| 500 sel.set_selection_start(GetCursorPosition()); | 473 selection_model_.caret_affinity())); |
| 501 SetSelectionModel(sel); | |
| 502 } | 474 } |
| 503 | 475 |
| 504 void RenderText::SelectAll() { | 476 void RenderText::SelectAll() { |
| 505 SelectionModel sel = EdgeSelectionModel(CURSOR_RIGHT); | 477 SelectionModel all; |
| 506 sel.set_selection_start(EdgeSelectionModel(CURSOR_LEFT).selection_start()); | 478 if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT) |
| 507 SetSelectionModel(sel); | 479 all = SelectionModel(ui::Range(0, text().length()), CURSOR_FORWARD); |
| 480 else | |
| 481 all = SelectionModel(ui::Range(text().length(), 0), CURSOR_BACKWARD); | |
| 482 SetSelectionModel(all); | |
| 508 } | 483 } |
| 509 | 484 |
| 510 void RenderText::SelectWord() { | 485 void RenderText::SelectWord() { |
| 511 if (obscured_) { | 486 if (obscured_) { |
| 512 SelectAll(); | 487 SelectAll(); |
| 513 return; | 488 return; |
| 514 } | 489 } |
| 515 | 490 |
| 516 size_t cursor_position = GetCursorPosition(); | 491 size_t cursor_pos = cursor_position(); |
| 517 | 492 |
| 518 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 493 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 519 bool success = iter.Init(); | 494 bool success = iter.Init(); |
| 520 DCHECK(success); | 495 DCHECK(success); |
| 521 if (!success) | 496 if (!success) |
| 522 return; | 497 return; |
| 523 | 498 |
| 524 size_t selection_start = cursor_position; | 499 size_t selection_start = cursor_pos; |
| 525 for (; selection_start != 0; --selection_start) { | 500 for (; selection_start != 0; --selection_start) { |
| 526 if (iter.IsStartOfWord(selection_start) || | 501 if (iter.IsStartOfWord(selection_start) || |
| 527 iter.IsEndOfWord(selection_start)) | 502 iter.IsEndOfWord(selection_start)) |
| 528 break; | 503 break; |
| 529 } | 504 } |
| 530 | 505 |
| 531 if (selection_start == cursor_position) | 506 if (selection_start == cursor_pos) |
| 532 ++cursor_position; | 507 ++cursor_pos; |
| 533 | 508 |
| 534 for (; cursor_position < text().length(); ++cursor_position) { | 509 for (; cursor_pos < text().length(); ++cursor_pos) |
| 535 if (iter.IsEndOfWord(cursor_position) || | 510 if (iter.IsEndOfWord(cursor_pos) || iter.IsStartOfWord(cursor_pos)) |
| 536 iter.IsStartOfWord(cursor_position)) | |
| 537 break; | 511 break; |
| 538 } | |
| 539 | 512 |
| 540 MoveCursorTo(selection_start, false); | 513 MoveCursorTo(selection_start, false); |
| 541 MoveCursorTo(cursor_position, true); | 514 MoveCursorTo(cursor_pos, true); |
| 542 } | 515 } |
| 543 | 516 |
| 544 const ui::Range& RenderText::GetCompositionRange() const { | 517 const ui::Range& RenderText::GetCompositionRange() const { |
| 545 return composition_range_; | 518 return composition_range_; |
| 546 } | 519 } |
| 547 | 520 |
| 548 void RenderText::SetCompositionRange(const ui::Range& composition_range) { | 521 void RenderText::SetCompositionRange(const ui::Range& composition_range) { |
| 549 CHECK(!composition_range.IsValid() || | 522 CHECK(!composition_range.IsValid() || |
| 550 ui::Range(0, text_.length()).Contains(composition_range)); | 523 ui::Range(0, text_.length()).Contains(composition_range)); |
| 551 composition_range_.set_end(composition_range.end()); | 524 composition_range_.set_end(composition_range.end()); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 597 | 570 |
| 598 DrawCursor(canvas); | 571 DrawCursor(canvas); |
| 599 | 572 |
| 600 if (!text().empty()) { | 573 if (!text().empty()) { |
| 601 TRACE_EVENT0("gfx", "RenderText::Draw draw text"); | 574 TRACE_EVENT0("gfx", "RenderText::Draw draw text"); |
| 602 DrawVisualText(canvas); | 575 DrawVisualText(canvas); |
| 603 } | 576 } |
| 604 canvas->Restore(); | 577 canvas->Restore(); |
| 605 } | 578 } |
| 606 | 579 |
| 580 Rect RenderText::GetCursorBounds(const SelectionModel& caret, | |
| 581 bool insert_mode) { | |
| 582 EnsureLayout(); | |
| 583 | |
| 584 size_t caret_pos = caret.caret_pos(); | |
| 585 // In overtype mode, ignore the affinity and always indicate that we will | |
| 586 // overtype the next character. | |
| 587 LogicalCursorDirection caret_affinity = | |
| 588 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD; | |
| 589 int x = 0, width = 0, height = 0; | |
| 590 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) { | |
| 591 // The caret is attached to the boundary. Always return a zero-width caret, | |
| 592 // since there is nothing to overtype. | |
| 593 Size size = GetStringSize(); | |
| 594 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0)) | |
| 595 x = size.width(); | |
| 596 height = size.height(); | |
| 597 } else { | |
| 598 size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ? | |
| 599 caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD); | |
| 600 ui::Range xspan; | |
| 601 GetGlyphBounds(grapheme_start, &xspan, &height); | |
| 602 if (insert_mode) { | |
| 603 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start(); | |
| 604 } else { // overtype mode | |
| 605 x = xspan.GetMin(); | |
| 606 width = xspan.length(); | |
| 607 } | |
| 608 } | |
| 609 height = std::min(height, display_rect().height()); | |
| 610 int y = (display_rect().height() - height) / 2; | |
| 611 return Rect(ToViewPoint(Point(x, y)), Size(width, height)); | |
| 612 } | |
| 613 | |
| 607 const Rect& RenderText::GetUpdatedCursorBounds() { | 614 const Rect& RenderText::GetUpdatedCursorBounds() { |
| 608 UpdateCachedBoundsAndOffset(); | 615 UpdateCachedBoundsAndOffset(); |
| 609 return cursor_bounds_; | 616 return cursor_bounds_; |
| 610 } | 617 } |
| 611 | 618 |
| 612 SelectionModel RenderText::GetSelectionModelForSelectionStart() { | 619 SelectionModel RenderText::GetSelectionModelForSelectionStart() { |
| 613 size_t selection_start = GetSelectionStart(); | 620 const ui::Range& sel = selection(); |
| 614 size_t selection_end = GetCursorPosition(); | 621 if (sel.is_empty()) |
| 615 if (selection_start < selection_end) | 622 return selection_model_; |
| 616 return SelectionModel(selection_start, | 623 return SelectionModel(sel.start(), |
| 617 selection_start, | 624 sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD); |
| 618 SelectionModel::LEADING); | |
| 619 else if (selection_start > selection_end) | |
| 620 return SelectionModel( | |
| 621 selection_start, | |
| 622 IndexOfAdjacentGrapheme(selection_start, CURSOR_BACKWARD), | |
| 623 SelectionModel::TRAILING); | |
| 624 return selection_model_; | |
| 625 } | 625 } |
| 626 | 626 |
| 627 RenderText::RenderText() | 627 RenderText::RenderText() |
| 628 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), | 628 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), |
| 629 cursor_enabled_(true), | 629 cursor_enabled_(true), |
| 630 cursor_visible_(false), | 630 cursor_visible_(false), |
| 631 insert_mode_(true), | 631 insert_mode_(true), |
| 632 cursor_color_(kDefaultCursorColor), | 632 cursor_color_(kDefaultCursorColor), |
| 633 focused_(false), | 633 focused_(false), |
| 634 composition_range_(ui::Range::InvalidRange()), | 634 composition_range_(ui::Range::InvalidRange()), |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 651 EnsureLayout(); | 651 EnsureLayout(); |
| 652 | 652 |
| 653 if (break_type == LINE_BREAK || text().empty()) | 653 if (break_type == LINE_BREAK || text().empty()) |
| 654 return EdgeSelectionModel(direction); | 654 return EdgeSelectionModel(direction); |
| 655 if (break_type == CHARACTER_BREAK) | 655 if (break_type == CHARACTER_BREAK) |
| 656 return AdjacentCharSelectionModel(current, direction); | 656 return AdjacentCharSelectionModel(current, direction); |
| 657 DCHECK(break_type == WORD_BREAK); | 657 DCHECK(break_type == WORD_BREAK); |
| 658 return AdjacentWordSelectionModel(current, direction); | 658 return AdjacentWordSelectionModel(current, direction); |
| 659 } | 659 } |
| 660 | 660 |
| 661 SelectionModel RenderText::EdgeSelectionModel( | |
| 662 VisualCursorDirection direction) { | |
| 663 if (direction == GetVisualDirectionOfLogicalEnd()) | |
| 664 return SelectionModel(text().length(), CURSOR_FORWARD); | |
| 665 return SelectionModel(0, CURSOR_BACKWARD); | |
| 666 } | |
| 667 | |
| 661 void RenderText::SetSelectionModel(const SelectionModel& model) { | 668 void RenderText::SetSelectionModel(const SelectionModel& model) { |
| 662 DCHECK_LE(model.selection_start(), text().length()); | 669 DCHECK_LE(model.selection().GetMax(), text().length()); |
| 663 selection_model_.set_selection_start(model.selection_start()); | 670 selection_model_ = model; |
| 664 DCHECK_LE(model.selection_end(), text().length()); | |
| 665 selection_model_.set_selection_end(model.selection_end()); | |
| 666 DCHECK_LT(model.caret_pos(), std::max<size_t>(text().length(), 1)); | |
| 667 selection_model_.set_caret_pos(model.caret_pos()); | |
| 668 selection_model_.set_caret_placement(model.caret_placement()); | |
| 669 | |
| 670 cached_bounds_and_offset_valid_ = false; | 671 cached_bounds_and_offset_valid_ = false; |
| 671 } | 672 } |
| 672 | 673 |
| 673 string16 RenderText::GetDisplayText() const { | 674 string16 RenderText::GetDisplayText() const { |
| 674 if (!obscured_) | 675 if (!obscured_) |
| 675 return text_; | 676 return text_; |
| 676 size_t obscured_text_length = | 677 size_t obscured_text_length = |
| 677 static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length())); | 678 static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length())); |
| 678 return string16(obscured_text_length, kPasswordReplacementChar); | 679 return string16(obscured_text_length, kPasswordReplacementChar); |
| 679 } | 680 } |
| 680 | 681 |
| 681 void RenderText::ApplyCompositionAndSelectionStyles( | 682 void RenderText::ApplyCompositionAndSelectionStyles( |
| 682 StyleRanges* style_ranges) { | 683 StyleRanges* style_ranges) { |
| 683 // TODO(msw): This pattern ought to be reconsidered; what about composition | 684 // TODO(msw): This pattern ought to be reconsidered; what about composition |
| 684 // and selection overlaps, retain existing local style features? | 685 // and selection overlaps, retain existing local style features? |
| 685 // Apply a composition style override to a copy of the style ranges. | 686 // Apply a composition style override to a copy of the style ranges. |
| 686 if (composition_range_.IsValid() && !composition_range_.is_empty()) { | 687 if (composition_range_.IsValid() && !composition_range_.is_empty()) { |
| 687 StyleRange composition_style(default_style_); | 688 StyleRange composition_style(default_style_); |
| 688 composition_style.underline = true; | 689 composition_style.underline = true; |
| 689 composition_style.range.set_start(composition_range_.start()); | 690 composition_style.range = composition_range_; |
| 690 composition_style.range.set_end(composition_range_.end()); | |
| 691 ApplyStyleRangeImpl(style_ranges, composition_style); | 691 ApplyStyleRangeImpl(style_ranges, composition_style); |
| 692 } | 692 } |
| 693 // Apply a selection style override to a copy of the style ranges. | 693 // Apply a selection style override to a copy of the style ranges. |
| 694 if (!EmptySelection()) { | 694 if (!selection().is_empty()) { |
| 695 StyleRange selection_style(default_style_); | 695 StyleRange selection_style(default_style_); |
| 696 selection_style.foreground = NativeTheme::instance()->GetSystemColor( | 696 selection_style.foreground = NativeTheme::instance()->GetSystemColor( |
| 697 NativeTheme::kColorId_TextfieldSelectionColor); | 697 NativeTheme::kColorId_TextfieldSelectionColor); |
| 698 selection_style.range.set_start(MinOfSelection()); | 698 selection_style.range = ui::Range(selection().GetMin(), |
| 699 selection_style.range.set_end(MaxOfSelection()); | 699 selection().GetMax()); |
| 700 ApplyStyleRangeImpl(style_ranges, selection_style); | 700 ApplyStyleRangeImpl(style_ranges, selection_style); |
| 701 } | 701 } |
| 702 // Apply replacement-mode style override to a copy of the style ranges. | 702 // Apply replacement-mode style override to a copy of the style ranges. |
| 703 // | 703 // |
| 704 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to | 704 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to |
| 705 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline | 705 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline |
| 706 // character to be drawn correctly, we will need to re-layout the text. It's | 706 // character to be drawn correctly, we will need to re-layout the text. It's |
| 707 // not practical to do layout on every cursor blink. We need to fix Windows | 707 // not practical to do layout on every cursor blink. We need to fix Windows |
| 708 // port to apply styles during drawing phase like Linux port does. | 708 // port to apply styles during drawing phase like Linux port does. |
| 709 // http://crbug.com/110109 | 709 // http://crbug.com/110109 |
| 710 if (!insert_mode_ && cursor_visible() && focused()) { | 710 if (!insert_mode_ && cursor_visible() && focused()) { |
| 711 StyleRange replacement_mode_style(default_style_); | 711 StyleRange replacement_mode_style(default_style_); |
| 712 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor( | 712 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor( |
| 713 NativeTheme::kColorId_TextfieldSelectionColor); | 713 NativeTheme::kColorId_TextfieldSelectionColor); |
| 714 size_t cursor = GetCursorPosition(); | 714 size_t cursor = cursor_position(); |
| 715 replacement_mode_style.range.set_start(cursor); | 715 replacement_mode_style.range.set_start(cursor); |
| 716 replacement_mode_style.range.set_end( | 716 replacement_mode_style.range.set_end( |
| 717 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD)); | 717 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD)); |
| 718 ApplyStyleRangeImpl(style_ranges, replacement_mode_style); | 718 ApplyStyleRangeImpl(style_ranges, replacement_mode_style); |
| 719 } | 719 } |
| 720 } | 720 } |
| 721 | 721 |
| 722 Point RenderText::GetTextOrigin() { | 722 Point RenderText::GetTextOrigin() { |
| 723 Point origin = display_rect().origin(); | 723 Point origin = display_rect().origin(); |
| 724 origin = origin.Add(GetUpdatedDisplayOffset()); | 724 origin = origin.Add(GetUpdatedDisplayOffset()); |
| 725 origin = origin.Add(GetAlignmentOffset()); | 725 origin = origin.Add(GetAlignmentOffset()); |
| 726 return origin; | 726 return origin; |
| 727 } | 727 } |
| 728 | 728 |
| 729 Point RenderText::ToTextPoint(const Point& point) { | 729 Point RenderText::ToTextPoint(const Point& point) { |
| 730 return point.Subtract(GetTextOrigin()); | 730 return point.Subtract(GetTextOrigin()); |
| 731 } | 731 } |
| 732 | 732 |
| 733 Point RenderText::ToViewPoint(const Point& point) { | 733 Point RenderText::ToViewPoint(const Point& point) { |
| 734 return point.Add(GetTextOrigin()); | 734 return point.Add(GetTextOrigin()); |
| 735 } | 735 } |
| 736 | 736 |
| 737 int RenderText::GetContentWidth() { | 737 int RenderText::GetContentWidth() { |
| 738 return GetStringWidth() + (cursor_enabled_ ? 1 : 0); | 738 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0); |
| 739 } | 739 } |
| 740 | 740 |
| 741 Point RenderText::GetAlignmentOffset() { | 741 Point RenderText::GetAlignmentOffset() { |
| 742 if (horizontal_alignment() != ALIGN_LEFT) { | 742 if (horizontal_alignment() != ALIGN_LEFT) { |
| 743 int x_offset = display_rect().width() - GetContentWidth(); | 743 int x_offset = display_rect().width() - GetContentWidth(); |
| 744 if (horizontal_alignment() == ALIGN_CENTER) | 744 if (horizontal_alignment() == ALIGN_CENTER) |
| 745 x_offset /= 2; | 745 x_offset /= 2; |
| 746 return Point(x_offset, 0); | 746 return Point(x_offset, 0); |
| 747 } | 747 } |
| 748 return Point(); | 748 return Point(); |
| 749 } | 749 } |
| 750 | 750 |
| 751 Point RenderText::GetOriginForSkiaDrawing() { | 751 Point RenderText::GetOriginForSkiaDrawing() { |
| 752 Point origin(GetTextOrigin()); | 752 Point origin(GetTextOrigin()); |
| 753 // TODO(msw): Establish a vertical baseline for strings of mixed font heights. | 753 // TODO(msw): Establish a vertical baseline for strings of mixed font heights. |
| 754 const Font& font = GetFont(); | 754 const Font& font = GetFont(); |
| 755 int height = font.GetHeight(); | 755 int height = font.GetHeight(); |
| 756 DCHECK_LE(height, display_rect().height()); | 756 DCHECK_LE(height, display_rect().height()); |
| 757 // Center the text vertically in the display area. | 757 // Center the text vertically in the display area. |
| 758 origin.Offset(0, (display_rect().height() - height) / 2); | 758 origin.Offset(0, (display_rect().height() - height) / 2); |
| 759 // Offset by the font size to account for Skia expecting y to be the bottom. | 759 // Offset by the font size to account for Skia expecting y to be the bottom. |
| 760 origin.Offset(0, font.GetFontSize()); | 760 origin.Offset(0, font.GetFontSize()); |
| 761 return origin; | 761 return origin; |
| 762 } | 762 } |
| 763 | 763 |
| 764 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { | 764 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { |
| 765 if (!fade_head() && !fade_tail()) | 765 if (!fade_head() && !fade_tail()) |
| 766 return; | 766 return; |
| 767 | 767 |
| 768 const int text_width = GetStringWidth(); | 768 const int text_width = GetStringSize().width(); |
| 769 const int display_width = display_rect().width(); | 769 const int display_width = display_rect().width(); |
| 770 | 770 |
| 771 // If the text fits as-is, no need to fade. | 771 // If the text fits as-is, no need to fade. |
| 772 if (text_width <= display_width) | 772 if (text_width <= display_width) |
| 773 return; | 773 return; |
| 774 | 774 |
| 775 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); | 775 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); |
| 776 if (gradient_width == 0) | 776 if (gradient_width == 0) |
| 777 return; | 777 return; |
| 778 | 778 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 801 | 801 |
| 802 const SkColor color = default_style().foreground; | 802 const SkColor color = default_style().foreground; |
| 803 SkAutoTUnref<SkShader> shader( | 803 SkAutoTUnref<SkShader> shader( |
| 804 CreateFadeShader(text_rect, left_part, right_part, color)); | 804 CreateFadeShader(text_rect, left_part, right_part, color)); |
| 805 if (shader.get()) { | 805 if (shader.get()) { |
| 806 // |renderer| adds its own ref. So don't |release()| it from the ref ptr. | 806 // |renderer| adds its own ref. So don't |release()| it from the ref ptr. |
| 807 renderer->SetShader(shader.get()); | 807 renderer->SetShader(shader.get()); |
| 808 } | 808 } |
| 809 } | 809 } |
| 810 | 810 |
| 811 // static | |
| 812 bool RenderText::RangeContainsCaret(const ui::Range& range, | |
| 813 size_t caret_pos, | |
| 814 LogicalCursorDirection caret_affinity) { | |
| 815 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9). | |
| 816 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ? | |
| 817 caret_pos - 1 : caret_pos + 1; | |
| 818 return range.Contains(ui::Range(caret_pos, adjacent)); | |
| 819 } | |
| 820 | |
| 811 void RenderText::MoveCursorTo(size_t position, bool select) { | 821 void RenderText::MoveCursorTo(size_t position, bool select) { |
| 812 size_t cursor = std::min(position, text().length()); | 822 size_t cursor = std::min(position, text().length()); |
| 813 size_t caret_pos = IndexOfAdjacentGrapheme(cursor, CURSOR_BACKWARD); | 823 if (IsCursorablePosition(cursor)) |
| 814 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? | 824 SetSelectionModel(SelectionModel( |
| 815 SelectionModel::LEADING : SelectionModel::TRAILING; | 825 ui::Range(select ? selection().start() : cursor, cursor), |
| 816 size_t selection_start = select ? GetSelectionStart() : cursor; | 826 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); |
| 817 if (IsCursorablePosition(cursor)) { | |
| 818 SelectionModel sel(selection_start, cursor, caret_pos, placement); | |
| 819 SetSelectionModel(sel); | |
| 820 } | |
| 821 } | 827 } |
| 822 | 828 |
| 823 void RenderText::UpdateCachedBoundsAndOffset() { | 829 void RenderText::UpdateCachedBoundsAndOffset() { |
| 824 if (cached_bounds_and_offset_valid_) | 830 if (cached_bounds_and_offset_valid_) |
| 825 return; | 831 return; |
| 826 | 832 |
| 827 // First, set the valid flag true to calculate the current cursor bounds using | 833 // First, set the valid flag true to calculate the current cursor bounds using |
| 828 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 834 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| 829 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 835 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
| 830 cached_bounds_and_offset_valid_ = true; | 836 cached_bounds_and_offset_valid_ = true; |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 859 const int offset = negate_rtl * display_offset_.x(); | 865 const int offset = negate_rtl * display_offset_.x(); |
| 860 if (display_width > (content_width + offset)) | 866 if (display_width > (content_width + offset)) |
| 861 delta_offset = negate_rtl * (display_width - (content_width + offset)); | 867 delta_offset = negate_rtl * (display_width - (content_width + offset)); |
| 862 } | 868 } |
| 863 | 869 |
| 864 display_offset_.Offset(delta_offset, 0); | 870 display_offset_.Offset(delta_offset, 0); |
| 865 cursor_bounds_.Offset(delta_offset, 0); | 871 cursor_bounds_.Offset(delta_offset, 0); |
| 866 } | 872 } |
| 867 | 873 |
| 868 void RenderText::DrawSelection(Canvas* canvas) { | 874 void RenderText::DrawSelection(Canvas* canvas) { |
| 869 std::vector<Rect> sel = GetSubstringBounds( | 875 std::vector<Rect> sel = GetSubstringBounds(selection()); |
| 870 GetSelectionStart(), GetCursorPosition()); | |
| 871 NativeTheme::ColorId color_id = focused() ? | 876 NativeTheme::ColorId color_id = focused() ? |
| 872 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused : | 877 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused : |
| 873 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused; | 878 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused; |
| 874 SkColor color = NativeTheme::instance()->GetSystemColor(color_id); | 879 SkColor color = NativeTheme::instance()->GetSystemColor(color_id); |
| 875 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 880 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
| 876 canvas->FillRect(*i, color); | 881 canvas->FillRect(*i, color); |
| 877 } | 882 } |
| 878 | 883 |
| 879 void RenderText::DrawCursor(Canvas* canvas) { | 884 void RenderText::DrawCursor(Canvas* canvas) { |
| 880 // Paint cursor. Replace cursor is drawn as rectangle for now. | 885 // Paint cursor. Replace cursor is drawn as rectangle for now. |
| 881 // TODO(msw): Draw a better cursor with a better indication of association. | 886 // TODO(msw): Draw a better cursor with a better indication of association. |
| 882 if (cursor_enabled() && cursor_visible() && focused()) { | 887 if (cursor_enabled() && cursor_visible() && focused()) { |
| 883 const Rect& bounds = GetUpdatedCursorBounds(); | 888 const Rect& bounds = GetUpdatedCursorBounds(); |
| 884 if (bounds.width() != 0) | 889 if (bounds.width() != 0) |
| 885 canvas->FillRect(bounds, cursor_color_); | 890 canvas->FillRect(bounds, cursor_color_); |
| 886 else | 891 else |
| 887 canvas->DrawRect(bounds, cursor_color_); | 892 canvas->DrawRect(bounds, cursor_color_); |
| 888 } | 893 } |
| 889 } | 894 } |
| 890 | 895 |
| 891 } // namespace gfx | 896 } // namespace gfx |
| OLD | NEW |