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 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 325 // range ends beyond text_.length, it must be the last one. | 325 // range ends beyond text_.length, it must be the last one. |
| 326 style_ranges_.back().range.set_end(text_.length()); | 326 style_ranges_.back().range.set_end(text_.length()); |
| 327 } | 327 } |
| 328 #ifndef NDEBUG | 328 #ifndef NDEBUG |
| 329 CheckStyleRanges(style_ranges_, text_.length()); | 329 CheckStyleRanges(style_ranges_, text_.length()); |
| 330 #endif | 330 #endif |
| 331 cached_bounds_and_offset_valid_ = false; | 331 cached_bounds_and_offset_valid_ = false; |
| 332 | 332 |
| 333 // Reset selection model. SetText should always followed by SetSelectionModel | 333 // Reset selection model. SetText should always followed by SetSelectionModel |
| 334 // or SetCursorPosition in upper layer. | 334 // or SetCursorPosition in upper layer. |
| 335 SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING)); | 335 SetSelectionModel(SelectionModel()); |
| 336 | 336 |
| 337 UpdateLayout(); | 337 UpdateLayout(); |
| 338 } | 338 } |
| 339 | 339 |
| 340 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) { | 340 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) { |
| 341 if (horizontal_alignment_ != alignment) { | 341 if (horizontal_alignment_ != alignment) { |
| 342 horizontal_alignment_ = alignment; | 342 horizontal_alignment_ = alignment; |
| 343 display_offset_ = Point(); | 343 display_offset_ = Point(); |
| 344 cached_bounds_and_offset_valid_ = false; | 344 cached_bounds_and_offset_valid_ = false; |
| 345 } | 345 } |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 370 insert_mode_ = !insert_mode_; | 370 insert_mode_ = !insert_mode_; |
| 371 cached_bounds_and_offset_valid_ = false; | 371 cached_bounds_and_offset_valid_ = false; |
| 372 } | 372 } |
| 373 | 373 |
| 374 void RenderText::SetDisplayRect(const Rect& r) { | 374 void RenderText::SetDisplayRect(const Rect& r) { |
| 375 display_rect_ = r; | 375 display_rect_ = r; |
| 376 cached_bounds_and_offset_valid_ = false; | 376 cached_bounds_and_offset_valid_ = false; |
| 377 UpdateLayout(); | 377 UpdateLayout(); |
| 378 } | 378 } |
| 379 | 379 |
| 380 size_t RenderText::GetCursorPosition() const { | |
| 381 return selection_model_.selection_end(); | |
| 382 } | |
| 383 | |
| 384 void RenderText::SetCursorPosition(size_t position) { | 380 void RenderText::SetCursorPosition(size_t position) { |
| 385 MoveCursorTo(position, false); | 381 MoveCursorTo(position, false); |
| 386 } | 382 } |
| 387 | 383 |
| 388 void RenderText::MoveCursor(BreakType break_type, | 384 void RenderText::MoveCursor(BreakType break_type, |
| 389 VisualCursorDirection direction, | 385 VisualCursorDirection direction, |
| 390 bool select) { | 386 bool select) { |
| 391 SelectionModel position(selection_model()); | 387 SelectionModel position(cursor_position(), selection_model_.caret_affinity()); |
| 392 position.set_selection_start(GetCursorPosition()); | |
| 393 // Cancelling a selection moves to the edge of the selection. | 388 // Cancelling a selection moves to the edge of the selection. |
| 394 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 389 if (break_type != LINE_BREAK && !selection().is_empty() && !select) { |
| 395 SelectionModel selection_start = GetSelectionModelForSelectionStart(); | 390 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
| 396 int start_x = GetCursorBounds(selection_start, true).x(); | 391 int start_x = GetCursorBounds(selection_start, true).x(); |
| 397 int cursor_x = GetCursorBounds(position, true).x(); | 392 int cursor_x = GetCursorBounds(position, true).x(); |
| 398 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) | 393 // Use the selection start if it is left (when |direction| is CURSOR_LEFT) |
| 399 // or right (when |direction| is CURSOR_RIGHT) of the selection end. | 394 // or right (when |direction| is CURSOR_RIGHT) of the selection end. |
| 400 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) | 395 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x) |
| 401 position = selection_start; | 396 position = selection_start; |
| 402 // For word breaks, use the nearest word boundary in the appropriate | 397 // For word breaks, use the nearest word boundary in the appropriate |
| 403 // |direction|. | 398 // |direction|. |
| 404 if (break_type == WORD_BREAK) | 399 if (break_type == WORD_BREAK) |
| 405 position = GetAdjacentSelectionModel(position, break_type, direction); | 400 position = GetAdjacentSelectionModel(position, break_type, direction); |
| 406 } else { | 401 } else { |
| 407 position = GetAdjacentSelectionModel(position, break_type, direction); | 402 position = GetAdjacentSelectionModel(position, break_type, direction); |
| 408 } | 403 } |
| 409 if (select) | 404 if (select) |
| 410 position.set_selection_start(GetSelectionStart()); | 405 position.set_selection_start(selection().start()); |
| 411 MoveCursorTo(position); | 406 MoveCursorTo(position); |
| 412 } | 407 } |
| 413 | 408 |
| 414 bool RenderText::MoveCursorTo(const SelectionModel& model) { | 409 bool RenderText::MoveCursorTo(const SelectionModel& model) { |
| 415 SelectionModel sel(model); | 410 // Enforce valid selection model components. |
| 416 size_t text_length = text().length(); | 411 size_t text_length = text().length(); |
| 417 // Enforce valid selection model components. | 412 ui::Range range(std::min(model.selection().start(), text_length), |
|
msw
2012/02/16 00:09:21
nit: move range down to where it's used.
| |
| 418 if (sel.selection_start() > text_length) | 413 std::min(model.caret_pos(), text_length)); |
| 419 sel.set_selection_start(text_length); | 414 LogicalCursorDirection affinity = model.caret_affinity(); |
| 420 if (sel.selection_end() > text_length) | 415 if (model.caret_pos() > text_length) |
|
xji
2012/02/16 02:38:29
are "{" and "}" needed here for style correct-ness
| |
| 421 sel.set_selection_end(text_length); | 416 // Make the cursor appear at the edge of the text, not next to the final |
|
msw
2012/02/16 00:09:21
nit: Wrap multi-line if blocks in braces, even for
| |
| 417 // grapheme. | |
| 418 affinity = CURSOR_FORWARD; | |
| 419 | |
| 422 // The current model only supports caret positions at valid character indices. | 420 // The current model only supports caret positions at valid character indices. |
| 423 if (text_length == 0) { | 421 if (!IsCursorablePosition(range.start()) || |
| 424 sel.set_caret_pos(0); | 422 !IsCursorablePosition(range.end())) |
| 425 sel.set_caret_placement(SelectionModel::LEADING); | |
| 426 } else if (sel.caret_pos() >= text_length) { | |
| 427 SelectionModel end_selection = | |
| 428 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd()); | |
| 429 sel.set_caret_pos(end_selection.caret_pos()); | |
| 430 sel.set_caret_placement(end_selection.caret_placement()); | |
| 431 } | |
| 432 | |
| 433 if (!IsCursorablePosition(sel.selection_start()) || | |
| 434 !IsCursorablePosition(sel.selection_end()) || | |
| 435 !IsCursorablePosition(sel.caret_pos())) | |
| 436 return false; | 423 return false; |
| 437 | 424 |
| 438 bool changed = !sel.Equals(selection_model_); | 425 SelectionModel sel(range, affinity); |
| 426 bool changed = sel != selection_model_; | |
| 439 SetSelectionModel(sel); | 427 SetSelectionModel(sel); |
| 440 return changed; | 428 return changed; |
| 441 } | 429 } |
| 442 | 430 |
| 443 bool RenderText::MoveCursorTo(const Point& point, bool select) { | 431 bool RenderText::MoveCursorTo(const Point& point, bool select) { |
| 444 SelectionModel selection = FindCursorPosition(point); | 432 SelectionModel position = FindCursorPosition(point); |
| 445 if (select) | 433 if (select) |
| 446 selection.set_selection_start(GetSelectionStart()); | 434 position.set_selection_start(selection().start()); |
| 447 return MoveCursorTo(selection); | 435 return MoveCursorTo(position); |
| 448 } | 436 } |
| 449 | 437 |
| 450 bool RenderText::SelectRange(const ui::Range& range) { | 438 bool RenderText::SelectRange(const ui::Range& range) { |
| 451 size_t text_length = text().length(); | 439 ui::Range sel(std::min(range.start(), text().length()), |
| 452 size_t start = std::min(range.start(), text_length); | 440 std::min(range.end(), text().length())); |
| 453 size_t end = std::min(range.end(), text_length); | 441 if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end())) |
| 454 | |
| 455 if (!IsCursorablePosition(start) || !IsCursorablePosition(end)) | |
| 456 return false; | 442 return false; |
| 457 | 443 LogicalCursorDirection affinity = |
| 458 size_t pos = end; | 444 (sel.is_reversed() || (sel.is_empty() && sel.start() != 0)) ? |
|
xji
2012/02/16 02:38:29
why sel.start() == 0 is handled differently from s
benrg
2012/02/16 09:20:55
The original code attached the cursor to the last
xji
2012/02/16 19:52:55
I see.
| |
| 459 SelectionModel::CaretPlacement placement = SelectionModel::LEADING; | 445 CURSOR_FORWARD : CURSOR_BACKWARD; |
| 460 if (start < end) { | 446 SetSelectionModel(SelectionModel(sel, affinity)); |
| 461 pos = IndexOfAdjacentGrapheme(end, CURSOR_BACKWARD); | |
| 462 DCHECK_LT(pos, end); | |
| 463 placement = SelectionModel::TRAILING; | |
| 464 } else if (end == text_length) { | |
| 465 SelectionModel end_selection = | |
| 466 EdgeSelectionModel(GetVisualDirectionOfLogicalEnd()); | |
| 467 pos = end_selection.caret_pos(); | |
| 468 placement = end_selection.caret_placement(); | |
| 469 } | |
| 470 SetSelectionModel(SelectionModel(start, end, pos, placement)); | |
| 471 return true; | 447 return true; |
| 472 } | 448 } |
| 473 | 449 |
| 474 bool RenderText::IsPointInSelection(const Point& point) { | 450 bool RenderText::IsPointInSelection(const Point& point) { |
| 475 if (EmptySelection()) | 451 if (selection().is_empty()) |
| 476 return false; | 452 return false; |
| 477 // TODO(xji): should this check whether the point is inside the visual | 453 SelectionModel cursor = FindCursorPosition(point); |
| 478 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points | 454 return RangeContainsCaret( |
| 479 // to the right half of 'c', is the point in selection? | 455 selection(), cursor.caret_pos(), cursor.caret_affinity()); |
| 480 size_t pos = FindCursorPosition(point).selection_end(); | |
| 481 return (pos >= MinOfSelection() && pos < MaxOfSelection()); | |
| 482 } | 456 } |
| 483 | 457 |
| 484 void RenderText::ClearSelection() { | 458 void RenderText::ClearSelection() { |
| 485 SelectionModel sel(selection_model()); | 459 SetSelectionModel(SelectionModel(selection_model_.caret_pos(), |
| 486 sel.set_selection_start(GetCursorPosition()); | 460 selection_model_.caret_affinity())); |
| 487 SetSelectionModel(sel); | |
| 488 } | 461 } |
| 489 | 462 |
| 490 void RenderText::SelectAll() { | 463 void RenderText::SelectAll() { |
| 491 SelectionModel sel = EdgeSelectionModel(CURSOR_RIGHT); | 464 SelectionModel all; |
| 492 sel.set_selection_start(EdgeSelectionModel(CURSOR_LEFT).selection_start()); | 465 if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT) |
| 493 SetSelectionModel(sel); | 466 all = SelectionModel(ui::Range(0, text().length()), CURSOR_FORWARD); |
| 467 else | |
| 468 all = SelectionModel(ui::Range(text().length(), 0), CURSOR_BACKWARD); | |
| 469 SetSelectionModel(all); | |
| 494 } | 470 } |
| 495 | 471 |
| 496 void RenderText::SelectWord() { | 472 void RenderText::SelectWord() { |
| 497 size_t cursor_position = GetCursorPosition(); | 473 size_t cursor_position = selection_model_.caret_pos(); |
| 498 | 474 |
| 499 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 475 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 500 bool success = iter.Init(); | 476 bool success = iter.Init(); |
| 501 DCHECK(success); | 477 DCHECK(success); |
| 502 if (!success) | 478 if (!success) |
| 503 return; | 479 return; |
| 504 | 480 |
| 505 size_t selection_start = cursor_position; | 481 size_t selection_start = cursor_position; |
| 506 for (; selection_start != 0; --selection_start) { | 482 for (; selection_start != 0; --selection_start) { |
| 507 if (iter.IsStartOfWord(selection_start) || | 483 if (iter.IsStartOfWord(selection_start) || |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 578 | 554 |
| 579 DrawCursor(canvas); | 555 DrawCursor(canvas); |
| 580 | 556 |
| 581 if (!text().empty()) { | 557 if (!text().empty()) { |
| 582 TRACE_EVENT0("gfx", "RenderText::Draw draw text"); | 558 TRACE_EVENT0("gfx", "RenderText::Draw draw text"); |
| 583 DrawVisualText(canvas); | 559 DrawVisualText(canvas); |
| 584 } | 560 } |
| 585 canvas->Restore(); | 561 canvas->Restore(); |
| 586 } | 562 } |
| 587 | 563 |
| 564 Rect RenderText::GetCursorBounds(const SelectionModel& selection, | |
| 565 bool insert_mode) { | |
| 566 EnsureLayout(); | |
| 567 | |
| 568 size_t caret_pos = selection.caret_pos(); | |
| 569 LogicalCursorDirection caret_affinity = selection.caret_affinity(); | |
| 570 int x, width, height; | |
| 571 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) { | |
| 572 // The caret is attached to the boundary. Always return a zero-width caret, | |
| 573 // since there is nothing to overstrike. | |
| 574 Size size = GetStringSize(); | |
| 575 if ((GetTextDirection() == base::i18n::LEFT_TO_RIGHT) == (caret_pos == 0)) | |
| 576 x = 0; | |
| 577 else | |
| 578 x = size.width(); | |
| 579 width = 0; | |
| 580 height = size.height(); | |
| 581 } else { | |
| 582 if (caret_affinity == CURSOR_BACKWARD) | |
| 583 caret_pos = IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD); | |
| 584 ui::Range xspan; | |
| 585 GetGlyphBounds(caret_pos, &xspan, &height); | |
| 586 if (insert_mode) { | |
| 587 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start(); | |
| 588 width = 0; | |
| 589 } else { // overstrike mode | |
| 590 x = xspan.GetMin(); | |
| 591 width = xspan.length(); | |
| 592 } | |
| 593 } | |
| 594 height = std::min(height, display_rect().height()); | |
| 595 int y = (display_rect().height() - height) / 2; | |
| 596 return Rect(ToViewPoint(Point(x, y)), Size(width, height)); | |
| 597 } | |
| 598 | |
| 588 const Rect& RenderText::GetUpdatedCursorBounds() { | 599 const Rect& RenderText::GetUpdatedCursorBounds() { |
| 589 UpdateCachedBoundsAndOffset(); | 600 UpdateCachedBoundsAndOffset(); |
| 590 return cursor_bounds_; | 601 return cursor_bounds_; |
| 591 } | 602 } |
| 592 | 603 |
| 593 SelectionModel RenderText::GetSelectionModelForSelectionStart() { | 604 SelectionModel RenderText::GetSelectionModelForSelectionStart() { |
| 594 size_t selection_start = GetSelectionStart(); | 605 const ui::Range& selection = selection_model_.selection(); |
| 595 size_t selection_end = GetCursorPosition(); | 606 if (selection.is_empty()) { |
| 596 if (selection_start < selection_end) | 607 return selection_model_; |
| 597 return SelectionModel(selection_start, | 608 } else { |
| 598 selection_start, | |
| 599 SelectionModel::LEADING); | |
| 600 else if (selection_start > selection_end) | |
| 601 return SelectionModel( | 609 return SelectionModel( |
|
msw
2012/02/16 00:09:21
nit: Move this return out of the else block.
| |
| 602 selection_start, | 610 selection.start(), |
| 603 IndexOfAdjacentGrapheme(selection_start, CURSOR_BACKWARD), | 611 selection.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD); |
| 604 SelectionModel::TRAILING); | 612 } |
| 605 return selection_model_; | |
| 606 } | 613 } |
| 607 | 614 |
| 608 RenderText::RenderText() | 615 RenderText::RenderText() |
| 609 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), | 616 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), |
| 610 cursor_enabled_(true), | 617 cursor_enabled_(true), |
| 611 cursor_visible_(false), | 618 cursor_visible_(false), |
| 612 insert_mode_(true), | 619 insert_mode_(true), |
| 613 focused_(false), | 620 focused_(false), |
| 614 composition_range_(ui::Range::InvalidRange()), | 621 composition_range_(ui::Range::InvalidRange()), |
| 615 fade_head_(false), | 622 fade_head_(false), |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 629 EnsureLayout(); | 636 EnsureLayout(); |
| 630 | 637 |
| 631 if (break_type == LINE_BREAK || text().empty()) | 638 if (break_type == LINE_BREAK || text().empty()) |
| 632 return EdgeSelectionModel(direction); | 639 return EdgeSelectionModel(direction); |
| 633 if (break_type == CHARACTER_BREAK) | 640 if (break_type == CHARACTER_BREAK) |
| 634 return AdjacentCharSelectionModel(current, direction); | 641 return AdjacentCharSelectionModel(current, direction); |
| 635 DCHECK(break_type == WORD_BREAK); | 642 DCHECK(break_type == WORD_BREAK); |
| 636 return AdjacentWordSelectionModel(current, direction); | 643 return AdjacentWordSelectionModel(current, direction); |
| 637 } | 644 } |
| 638 | 645 |
| 646 SelectionModel RenderText::EdgeSelectionModel( | |
| 647 VisualCursorDirection direction) { | |
| 648 if (direction == GetVisualDirectionOfLogicalEnd()) | |
| 649 return SelectionModel(text().length(), CURSOR_FORWARD); | |
| 650 else | |
| 651 return SelectionModel(0, CURSOR_BACKWARD); | |
| 652 } | |
| 653 | |
| 639 void RenderText::SetSelectionModel(const SelectionModel& model) { | 654 void RenderText::SetSelectionModel(const SelectionModel& model) { |
| 640 DCHECK_LE(model.selection_start(), text().length()); | 655 DCHECK_LE(model.selection().GetMax(), text().length()); |
| 641 selection_model_.set_selection_start(model.selection_start()); | 656 selection_model_ = model; |
| 642 DCHECK_LE(model.selection_end(), text().length()); | |
| 643 selection_model_.set_selection_end(model.selection_end()); | |
| 644 DCHECK_LT(model.caret_pos(), std::max<size_t>(text().length(), 1)); | |
| 645 selection_model_.set_caret_pos(model.caret_pos()); | |
| 646 selection_model_.set_caret_placement(model.caret_placement()); | |
| 647 | |
| 648 cached_bounds_and_offset_valid_ = false; | 657 cached_bounds_and_offset_valid_ = false; |
| 649 } | 658 } |
| 650 | 659 |
| 651 void RenderText::ApplyCompositionAndSelectionStyles( | 660 void RenderText::ApplyCompositionAndSelectionStyles( |
| 652 StyleRanges* style_ranges) { | 661 StyleRanges* style_ranges) { |
| 653 // TODO(msw): This pattern ought to be reconsidered; what about composition | 662 // TODO(msw): This pattern ought to be reconsidered; what about composition |
| 654 // and selection overlaps, retain existing local style features? | 663 // and selection overlaps, retain existing local style features? |
| 655 // Apply a composition style override to a copy of the style ranges. | 664 // Apply a composition style override to a copy of the style ranges. |
| 656 if (composition_range_.IsValid() && !composition_range_.is_empty()) { | 665 if (composition_range_.IsValid() && !composition_range_.is_empty()) { |
| 657 StyleRange composition_style(default_style_); | 666 StyleRange composition_style(default_style_); |
| 658 composition_style.underline = true; | 667 composition_style.underline = true; |
| 659 composition_style.range.set_start(composition_range_.start()); | 668 composition_style.range = composition_range_; |
| 660 composition_style.range.set_end(composition_range_.end()); | |
| 661 ApplyStyleRangeImpl(style_ranges, composition_style); | 669 ApplyStyleRangeImpl(style_ranges, composition_style); |
| 662 } | 670 } |
| 663 // Apply a selection style override to a copy of the style ranges. | 671 // Apply a selection style override to a copy of the style ranges. |
| 664 if (!EmptySelection()) { | 672 if (!selection().is_empty()) { |
| 665 StyleRange selection_style(default_style_); | 673 StyleRange selection_style(default_style_); |
| 666 selection_style.foreground = NativeTheme::instance()->GetSystemColor( | 674 selection_style.foreground = NativeTheme::instance()->GetSystemColor( |
| 667 NativeTheme::kColorId_TextfieldSelectionColor); | 675 NativeTheme::kColorId_TextfieldSelectionColor); |
| 668 selection_style.range.set_start(MinOfSelection()); | 676 selection_style.range = ui::Range(selection().GetMin(), |
| 669 selection_style.range.set_end(MaxOfSelection()); | 677 selection().GetMax()); |
| 670 ApplyStyleRangeImpl(style_ranges, selection_style); | 678 ApplyStyleRangeImpl(style_ranges, selection_style); |
| 671 } | 679 } |
| 672 // Apply replacement-mode style override to a copy of the style ranges. | 680 // Apply replacement-mode style override to a copy of the style ranges. |
| 673 // | 681 // |
| 674 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to | 682 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to |
| 675 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline | 683 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline |
| 676 // character to be drawn correctly, we will need to re-layout the text. It's | 684 // character to be drawn correctly, we will need to re-layout the text. It's |
| 677 // not practical to do layout on every cursor blink. We need to fix Windows | 685 // not practical to do layout on every cursor blink. We need to fix Windows |
| 678 // port to apply styles during drawing phase like Linux port does. | 686 // port to apply styles during drawing phase like Linux port does. |
| 679 // http://crbug.com/110109 | 687 // http://crbug.com/110109 |
| 680 if (!insert_mode_ && cursor_visible() && focused()) { | 688 if (!insert_mode_ && cursor_visible() && focused()) { |
| 681 StyleRange replacement_mode_style(default_style_); | 689 StyleRange replacement_mode_style(default_style_); |
| 682 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor( | 690 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor( |
| 683 NativeTheme::kColorId_TextfieldSelectionColor); | 691 NativeTheme::kColorId_TextfieldSelectionColor); |
| 684 size_t cursor = GetCursorPosition(); | 692 size_t cursor = cursor_position(); |
| 685 replacement_mode_style.range.set_start(cursor); | 693 replacement_mode_style.range.set_start(cursor); |
| 686 replacement_mode_style.range.set_end( | 694 replacement_mode_style.range.set_end( |
| 687 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD)); | 695 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD)); |
| 688 ApplyStyleRangeImpl(style_ranges, replacement_mode_style); | 696 ApplyStyleRangeImpl(style_ranges, replacement_mode_style); |
| 689 } | 697 } |
| 690 } | 698 } |
| 691 | 699 |
| 692 Point RenderText::GetTextOrigin() { | 700 Point RenderText::GetTextOrigin() { |
| 693 Point origin = display_rect().origin(); | 701 Point origin = display_rect().origin(); |
| 694 origin = origin.Add(GetUpdatedDisplayOffset()); | 702 origin = origin.Add(GetUpdatedDisplayOffset()); |
| 695 origin = origin.Add(GetAlignmentOffset()); | 703 origin = origin.Add(GetAlignmentOffset()); |
| 696 return origin; | 704 return origin; |
| 697 } | 705 } |
| 698 | 706 |
| 699 Point RenderText::ToTextPoint(const Point& point) { | 707 Point RenderText::ToTextPoint(const Point& point) { |
| 700 return point.Subtract(GetTextOrigin()); | 708 return point.Subtract(GetTextOrigin()); |
| 701 } | 709 } |
| 702 | 710 |
| 703 Point RenderText::ToViewPoint(const Point& point) { | 711 Point RenderText::ToViewPoint(const Point& point) { |
| 704 return point.Add(GetTextOrigin()); | 712 return point.Add(GetTextOrigin()); |
| 705 } | 713 } |
| 706 | 714 |
| 707 int RenderText::GetContentWidth() { | 715 int RenderText::GetContentWidth() { |
| 708 return GetStringWidth() + (cursor_enabled_ ? 1 : 0); | 716 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0); |
| 709 } | 717 } |
| 710 | 718 |
| 711 Point RenderText::GetAlignmentOffset() { | 719 Point RenderText::GetAlignmentOffset() { |
| 712 if (horizontal_alignment() != ALIGN_LEFT) { | 720 if (horizontal_alignment() != ALIGN_LEFT) { |
| 713 int x_offset = display_rect().width() - GetContentWidth(); | 721 int x_offset = display_rect().width() - GetContentWidth(); |
| 714 if (horizontal_alignment() == ALIGN_CENTER) | 722 if (horizontal_alignment() == ALIGN_CENTER) |
| 715 x_offset /= 2; | 723 x_offset /= 2; |
| 716 return Point(x_offset, 0); | 724 return Point(x_offset, 0); |
| 717 } | 725 } |
| 718 return Point(); | 726 return Point(); |
| 719 } | 727 } |
| 720 | 728 |
| 721 Point RenderText::GetOriginForSkiaDrawing() { | 729 Point RenderText::GetOriginForSkiaDrawing() { |
| 722 Point origin(GetTextOrigin()); | 730 Point origin(GetTextOrigin()); |
| 723 // TODO(msw): Establish a vertical baseline for strings of mixed font heights. | 731 // TODO(msw): Establish a vertical baseline for strings of mixed font heights. |
| 724 const Font& font = GetFont(); | 732 const Font& font = GetFont(); |
| 725 int height = font.GetHeight(); | 733 int height = font.GetHeight(); |
| 726 DCHECK_LE(height, display_rect().height()); | 734 DCHECK_LE(height, display_rect().height()); |
| 727 // Center the text vertically in the display area. | 735 // Center the text vertically in the display area. |
| 728 origin.Offset(0, (display_rect().height() - height) / 2); | 736 origin.Offset(0, (display_rect().height() - height) / 2); |
| 729 // Offset by the font size to account for Skia expecting y to be the bottom. | 737 // Offset by the font size to account for Skia expecting y to be the bottom. |
| 730 origin.Offset(0, font.GetFontSize()); | 738 origin.Offset(0, font.GetFontSize()); |
| 731 return origin; | 739 return origin; |
| 732 } | 740 } |
| 733 | 741 |
| 734 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { | 742 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { |
| 735 if (!fade_head() && !fade_tail()) | 743 if (!fade_head() && !fade_tail()) |
| 736 return; | 744 return; |
| 737 | 745 |
| 738 const int text_width = GetStringWidth(); | 746 const int text_width = GetStringSize().width(); |
| 739 const int display_width = display_rect().width(); | 747 const int display_width = display_rect().width(); |
| 740 | 748 |
| 741 // If the text fits as-is, no need to fade. | 749 // If the text fits as-is, no need to fade. |
| 742 if (text_width <= display_width) | 750 if (text_width <= display_width) |
| 743 return; | 751 return; |
| 744 | 752 |
| 745 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); | 753 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width); |
| 746 if (gradient_width == 0) | 754 if (gradient_width == 0) |
| 747 return; | 755 return; |
| 748 | 756 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 771 | 779 |
| 772 const SkColor color = default_style().foreground; | 780 const SkColor color = default_style().foreground; |
| 773 SkAutoTUnref<SkShader> shader( | 781 SkAutoTUnref<SkShader> shader( |
| 774 CreateFadeShader(text_rect, left_part, right_part, color)); | 782 CreateFadeShader(text_rect, left_part, right_part, color)); |
| 775 if (shader.get()) { | 783 if (shader.get()) { |
| 776 // |renderer| adds its own ref. So don't |release()| it from the ref ptr. | 784 // |renderer| adds its own ref. So don't |release()| it from the ref ptr. |
| 777 renderer->SetShader(shader.get()); | 785 renderer->SetShader(shader.get()); |
| 778 } | 786 } |
| 779 } | 787 } |
| 780 | 788 |
| 789 // static | |
| 790 bool RenderText::RangeContainsCaret(const ui::Range& range, | |
| 791 size_t caret_pos, | |
| 792 LogicalCursorDirection caret_affinity) { | |
| 793 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9). | |
| 794 size_t adjacent = caret_affinity == CURSOR_BACKWARD ? | |
| 795 caret_pos - 1 : caret_pos + 1; | |
|
xji
2012/02/16 02:38:29
wrong indentation.
theoretically, it should not b
benrg
2012/02/16 09:20:55
It works in practice. It is kind of hacky. I'm thi
xji
2012/02/16 19:52:55
I guess you are talking about replacing GetRunCont
| |
| 796 return range.Contains(ui::Range(caret_pos, adjacent)); | |
| 797 } | |
| 798 | |
| 781 void RenderText::MoveCursorTo(size_t position, bool select) { | 799 void RenderText::MoveCursorTo(size_t position, bool select) { |
| 782 size_t cursor = std::min(position, text().length()); | 800 size_t cursor = std::min(position, text().length()); |
| 783 size_t caret_pos = IndexOfAdjacentGrapheme(cursor, CURSOR_BACKWARD); | |
| 784 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? | |
| 785 SelectionModel::LEADING : SelectionModel::TRAILING; | |
| 786 size_t selection_start = select ? GetSelectionStart() : cursor; | |
| 787 if (IsCursorablePosition(cursor)) { | 801 if (IsCursorablePosition(cursor)) { |
| 788 SelectionModel sel(selection_start, cursor, caret_pos, placement); | 802 ui::Range pos(cursor); |
| 789 SetSelectionModel(sel); | 803 if (select) |
| 804 pos.set_start(selection().start()); | |
| 805 LogicalCursorDirection affinity = cursor ? CURSOR_BACKWARD : CURSOR_FORWARD; | |
| 806 SetSelectionModel(SelectionModel(pos, affinity)); | |
| 790 } | 807 } |
| 791 } | 808 } |
| 792 | 809 |
| 793 void RenderText::UpdateCachedBoundsAndOffset() { | 810 void RenderText::UpdateCachedBoundsAndOffset() { |
| 794 if (cached_bounds_and_offset_valid_) | 811 if (cached_bounds_and_offset_valid_) |
| 795 return; | 812 return; |
| 796 | 813 |
| 797 // First, set the valid flag true to calculate the current cursor bounds using | 814 // First, set the valid flag true to calculate the current cursor bounds using |
| 798 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 815 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| 799 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 816 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 829 const int offset = negate_rtl * display_offset_.x(); | 846 const int offset = negate_rtl * display_offset_.x(); |
| 830 if (display_width > (content_width + offset)) | 847 if (display_width > (content_width + offset)) |
| 831 delta_offset = negate_rtl * (display_width - (content_width + offset)); | 848 delta_offset = negate_rtl * (display_width - (content_width + offset)); |
| 832 } | 849 } |
| 833 | 850 |
| 834 display_offset_.Offset(delta_offset, 0); | 851 display_offset_.Offset(delta_offset, 0); |
| 835 cursor_bounds_.Offset(delta_offset, 0); | 852 cursor_bounds_.Offset(delta_offset, 0); |
| 836 } | 853 } |
| 837 | 854 |
| 838 void RenderText::DrawSelection(Canvas* canvas) { | 855 void RenderText::DrawSelection(Canvas* canvas) { |
| 839 std::vector<Rect> sel = GetSubstringBounds( | 856 std::vector<Rect> sel = GetSubstringBounds(selection()); |
| 840 GetSelectionStart(), GetCursorPosition()); | |
| 841 NativeTheme::ColorId color_id = focused() ? | 857 NativeTheme::ColorId color_id = focused() ? |
| 842 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused : | 858 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused : |
| 843 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused; | 859 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused; |
| 844 SkColor color = NativeTheme::instance()->GetSystemColor(color_id); | 860 SkColor color = NativeTheme::instance()->GetSystemColor(color_id); |
| 845 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 861 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
| 846 canvas->FillRect(*i, color); | 862 canvas->FillRect(*i, color); |
| 847 } | 863 } |
| 848 | 864 |
| 849 void RenderText::DrawCursor(Canvas* canvas) { | 865 void RenderText::DrawCursor(Canvas* canvas) { |
| 850 // Paint cursor. Replace cursor is drawn as rectangle for now. | 866 // Paint cursor. Replace cursor is drawn as rectangle for now. |
| 851 // TODO(msw): Draw a better cursor with a better indication of association. | 867 // TODO(msw): Draw a better cursor with a better indication of association. |
| 852 if (cursor_enabled() && cursor_visible() && focused()) { | 868 if (cursor_enabled() && cursor_visible() && focused()) { |
| 853 const Rect& bounds = GetUpdatedCursorBounds(); | 869 const Rect& bounds = GetUpdatedCursorBounds(); |
| 854 if (bounds.width() != 0) | 870 if (bounds.width() != 0) |
| 855 canvas->FillRect(bounds, kCursorColor); | 871 canvas->FillRect(bounds, kCursorColor); |
| 856 else | 872 else |
| 857 canvas->DrawRect(bounds, kCursorColor); | 873 canvas->DrawRect(bounds, kCursorColor); |
| 858 } | 874 } |
| 859 } | 875 } |
| 860 | 876 |
| 861 } // namespace gfx | 877 } // namespace gfx |
| OLD | NEW |