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 // This file implements utility functions for eliding and formatting UI text. | 5 // This file implements utility functions for eliding and formatting UI text. |
6 // | 6 // |
7 // Note that several of the functions declared in text_elider.h are implemented | 7 // Note that several of the functions declared in text_elider.h are implemented |
8 // in this file using helper classes in an unnamed namespace. | 8 // in this file using helper classes in an unnamed namespace. |
9 | 9 |
10 #include "ui/gfx/text_elider.h" | 10 #include "ui/gfx/text_elider.h" |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
470 RectangleText(const FontList& font_list, | 470 RectangleText(const FontList& font_list, |
471 float available_pixel_width, | 471 float available_pixel_width, |
472 int available_pixel_height, | 472 int available_pixel_height, |
473 WordWrapBehavior wrap_behavior, | 473 WordWrapBehavior wrap_behavior, |
474 std::vector<base::string16>* lines) | 474 std::vector<base::string16>* lines) |
475 : font_list_(font_list), | 475 : font_list_(font_list), |
476 line_height_(font_list.GetHeight()), | 476 line_height_(font_list.GetHeight()), |
477 available_pixel_width_(available_pixel_width), | 477 available_pixel_width_(available_pixel_width), |
478 available_pixel_height_(available_pixel_height), | 478 available_pixel_height_(available_pixel_height), |
479 wrap_behavior_(wrap_behavior), | 479 wrap_behavior_(wrap_behavior), |
480 current_width_(0), | |
481 current_height_(0), | 480 current_height_(0), |
482 last_line_ended_in_lf_(false), | 481 last_line_ended_in_lf_(false), |
483 lines_(lines), | 482 lines_(lines), |
484 insufficient_width_(false), | 483 insufficient_width_(false), |
485 insufficient_height_(false) {} | 484 insufficient_height_(false) {} |
486 | 485 |
487 // Perform deferred initializions following creation. Must be called | 486 // Perform deferred initializions following creation. Must be called |
488 // before any input can be added via AddString(). | 487 // before any input can be added via AddString(). |
489 void Init() { lines_->clear(); } | 488 void Init() { lines_->clear(); } |
490 | 489 |
(...skipping 16 matching lines...) Expand all Loading... |
507 | 506 |
508 // Wrap the specified word across multiple lines. | 507 // Wrap the specified word across multiple lines. |
509 int WrapWord(const base::string16& word); | 508 int WrapWord(const base::string16& word); |
510 | 509 |
511 // Add a long word - wrapping, eliding or truncating per the wrap behavior. | 510 // Add a long word - wrapping, eliding or truncating per the wrap behavior. |
512 int AddWordOverflow(const base::string16& word); | 511 int AddWordOverflow(const base::string16& word); |
513 | 512 |
514 // Add a word to the rectangluar region at the current position. | 513 // Add a word to the rectangluar region at the current position. |
515 int AddWord(const base::string16& word); | 514 int AddWord(const base::string16& word); |
516 | 515 |
517 // Append the specified |text| to the current output line, incrementing the | |
518 // running width by the specified amount. This is an optimization over | |
519 // |AddToCurrentLine()| when |text_width| is already known. | |
520 void AddToCurrentLineWithWidth(const base::string16& text, float text_width); | |
521 | |
522 // Append the specified |text| to the current output line. | 516 // Append the specified |text| to the current output line. |
523 void AddToCurrentLine(const base::string16& text); | 517 void AddToCurrentLine(const base::string16& text); |
524 | 518 |
525 // Set the current position to the beginning of the next line. | 519 // Set the current position to the beginning of the next line. |
526 bool NewLine(); | 520 bool NewLine(); |
527 | 521 |
528 // The font list used for measuring text width. | 522 // The font list used for measuring text width. |
529 const FontList& font_list_; | 523 const FontList& font_list_; |
530 | 524 |
531 // The height of each line of text. | 525 // The height of each line of text. |
532 const int line_height_; | 526 const int line_height_; |
533 | 527 |
534 // The number of pixels of available width in the rectangle. | 528 // The number of pixels of available width in the rectangle. |
535 const float available_pixel_width_; | 529 const float available_pixel_width_; |
536 | 530 |
537 // The number of pixels of available height in the rectangle. | 531 // The number of pixels of available height in the rectangle. |
538 const int available_pixel_height_; | 532 const int available_pixel_height_; |
539 | 533 |
540 // The wrap behavior for words that are too long to fit on a single line. | 534 // The wrap behavior for words that are too long to fit on a single line. |
541 const WordWrapBehavior wrap_behavior_; | 535 const WordWrapBehavior wrap_behavior_; |
542 | 536 |
543 // The current running width. | |
544 float current_width_; | |
545 | |
546 // The current running height. | 537 // The current running height. |
547 int current_height_; | 538 int current_height_; |
548 | 539 |
549 // The current line of text. | 540 // The current line of text. |
550 base::string16 current_line_; | 541 base::string16 current_line_; |
551 | 542 |
552 // Indicates whether the last line ended with \n. | 543 // Indicates whether the last line ended with \n. |
553 bool last_line_ended_in_lf_; | 544 bool last_line_ended_in_lf_; |
554 | 545 |
555 // The output vector of lines. | 546 // The output vector of lines. |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
594 } | 585 } |
595 if (last_line_ended_in_lf_) | 586 if (last_line_ended_in_lf_) |
596 lines_->push_back(base::string16()); | 587 lines_->push_back(base::string16()); |
597 return (insufficient_width_ ? INSUFFICIENT_SPACE_HORIZONTAL : 0) | | 588 return (insufficient_width_ ? INSUFFICIENT_SPACE_HORIZONTAL : 0) | |
598 (insufficient_height_ ? INSUFFICIENT_SPACE_VERTICAL : 0); | 589 (insufficient_height_ ? INSUFFICIENT_SPACE_VERTICAL : 0); |
599 } | 590 } |
600 | 591 |
601 void RectangleText::AddLine(const base::string16& line) { | 592 void RectangleText::AddLine(const base::string16& line) { |
602 const float line_width = GetStringWidthF(line, font_list_); | 593 const float line_width = GetStringWidthF(line, font_list_); |
603 if (line_width <= available_pixel_width_) { | 594 if (line_width <= available_pixel_width_) { |
604 AddToCurrentLineWithWidth(line, line_width); | 595 AddToCurrentLine(line); |
605 } else { | 596 } else { |
606 // Iterate over positions that are valid to break the line at. In general, | 597 // Iterate over positions that are valid to break the line at. In general, |
607 // these are word boundaries but after any punctuation following the word. | 598 // these are word boundaries but after any punctuation following the word. |
608 base::i18n::BreakIterator words(line, | 599 base::i18n::BreakIterator words(line, |
609 base::i18n::BreakIterator::BREAK_LINE); | 600 base::i18n::BreakIterator::BREAK_LINE); |
610 if (words.Init()) { | 601 if (words.Init()) { |
611 while (words.Advance()) { | 602 while (words.Advance()) { |
612 const bool truncate = !current_line_.empty(); | 603 const bool truncate = !current_line_.empty(); |
613 const base::string16& word = words.GetString(); | 604 const base::string16& word = words.GetString(); |
614 const int lines_added = AddWord(word); | 605 const int lines_added = AddWord(word); |
615 if (lines_added) { | 606 if (lines_added) { |
616 if (truncate) { | 607 if (truncate) { |
617 // Trim trailing whitespace from the line that was added. | 608 // Trim trailing whitespace from the line that was added. |
618 const int line = lines_->size() - lines_added; | 609 const int line = lines_->size() - lines_added; |
619 base::TrimWhitespace(lines_->at(line), base::TRIM_TRAILING, | 610 base::TrimWhitespace(lines_->at(line), base::TRIM_TRAILING, |
620 &lines_->at(line)); | 611 &lines_->at(line)); |
621 } | 612 } |
622 if (base::ContainsOnlyChars(word, base::kWhitespaceUTF16)) { | 613 if (base::ContainsOnlyChars(word, base::kWhitespaceUTF16)) { |
623 // Skip the first space if the previous line was carried over. | 614 // Skip the first space if the previous line was carried over. |
624 current_width_ = 0; | |
625 current_line_.clear(); | 615 current_line_.clear(); |
626 } | 616 } |
627 } | 617 } |
628 } | 618 } |
629 } else { | 619 } else { |
630 NOTREACHED() << "BreakIterator (words) init failed"; | 620 NOTREACHED() << "BreakIterator (words) init failed"; |
631 } | 621 } |
632 } | 622 } |
633 // Account for naturally-occuring newlines. | 623 // Account for naturally-occuring newlines. |
634 NewLine(); | 624 NewLine(); |
(...skipping 25 matching lines...) Expand all Loading... |
660 | 650 |
661 // Unless this is the very first word, put it on a new line. | 651 // Unless this is the very first word, put it on a new line. |
662 if (!current_line_.empty()) { | 652 if (!current_line_.empty()) { |
663 if (!NewLine()) | 653 if (!NewLine()) |
664 return 0; | 654 return 0; |
665 lines_added++; | 655 lines_added++; |
666 } | 656 } |
667 | 657 |
668 if (wrap_behavior_ == IGNORE_LONG_WORDS) { | 658 if (wrap_behavior_ == IGNORE_LONG_WORDS) { |
669 current_line_ = word; | 659 current_line_ = word; |
670 current_width_ = available_pixel_width_; | |
671 } else if (wrap_behavior_ == WRAP_LONG_WORDS) { | 660 } else if (wrap_behavior_ == WRAP_LONG_WORDS) { |
672 lines_added += WrapWord(word); | 661 lines_added += WrapWord(word); |
673 } else { | 662 } else { |
674 const ElideBehavior elide_behavior = | 663 const ElideBehavior elide_behavior = |
675 (wrap_behavior_ == ELIDE_LONG_WORDS ? ELIDE_TAIL : TRUNCATE); | 664 (wrap_behavior_ == ELIDE_LONG_WORDS ? ELIDE_TAIL : TRUNCATE); |
676 const base::string16 elided_word = | 665 const base::string16 elided_word = |
677 ElideText(word, font_list_, available_pixel_width_, elide_behavior); | 666 ElideText(word, font_list_, available_pixel_width_, elide_behavior); |
678 AddToCurrentLine(elided_word); | 667 AddToCurrentLine(elided_word); |
679 insufficient_width_ = true; | 668 insufficient_width_ = true; |
680 } | 669 } |
681 | 670 |
682 return lines_added; | 671 return lines_added; |
683 } | 672 } |
684 | 673 |
685 int RectangleText::AddWord(const base::string16& word) { | 674 int RectangleText::AddWord(const base::string16& word) { |
686 int lines_added = 0; | 675 int lines_added = 0; |
687 base::string16 trimmed; | 676 base::string16 trimmed; |
688 base::TrimWhitespace(word, base::TRIM_TRAILING, &trimmed); | 677 base::TrimWhitespace(word, base::TRIM_TRAILING, &trimmed); |
689 const float trimmed_width = GetStringWidthF(trimmed, font_list_); | 678 const float trimmed_width = GetStringWidthF(trimmed, font_list_); |
690 if (trimmed_width <= available_pixel_width_) { | 679 if (trimmed_width <= available_pixel_width_) { |
691 // Word can be made to fit, no need to fragment it. | 680 // Word can be made to fit, no need to fragment it. |
692 if ((current_width_ + trimmed_width > available_pixel_width_) && NewLine()) | 681 base::string16 line_with_new_word(current_line_); |
| 682 line_with_new_word.append(trimmed); |
| 683 float new_width = GetStringWidthF(line_with_new_word, font_list_); |
| 684 // We can't just add trimmed_width to current_width and compare it to |
| 685 // available_pixel_width. Sometimes sum of widths of two strings is not |
| 686 // equal to width of concatenation of these strings (see crbug.com/415213). |
| 687 if ((new_width > available_pixel_width_) && NewLine()) |
693 lines_added++; | 688 lines_added++; |
694 // Append the non-trimmed word, in case more words are added after. | 689 // Append the non-trimmed word, in case more words are added after. |
695 AddToCurrentLine(word); | 690 AddToCurrentLine(word); |
696 } else { | 691 } else { |
697 lines_added = AddWordOverflow(wrap_behavior_ == IGNORE_LONG_WORDS ? | 692 lines_added = AddWordOverflow(wrap_behavior_ == IGNORE_LONG_WORDS ? |
698 trimmed : word); | 693 trimmed : word); |
699 } | 694 } |
700 return lines_added; | 695 return lines_added; |
701 } | 696 } |
702 | 697 |
703 void RectangleText::AddToCurrentLine(const base::string16& text) { | 698 void RectangleText::AddToCurrentLine(const base::string16& text) { |
704 AddToCurrentLineWithWidth(text, GetStringWidthF(text, font_list_)); | |
705 } | |
706 | |
707 void RectangleText::AddToCurrentLineWithWidth(const base::string16& text, | |
708 float text_width) { | |
709 if (current_height_ >= available_pixel_height_) { | 699 if (current_height_ >= available_pixel_height_) { |
710 insufficient_height_ = true; | 700 insufficient_height_ = true; |
711 return; | 701 return; |
712 } | 702 } |
713 current_line_.append(text); | 703 current_line_.append(text); |
714 current_width_ += text_width; | |
715 } | 704 } |
716 | 705 |
717 bool RectangleText::NewLine() { | 706 bool RectangleText::NewLine() { |
718 bool line_added = false; | 707 bool line_added = false; |
719 if (current_height_ < available_pixel_height_) { | 708 if (current_height_ < available_pixel_height_) { |
720 lines_->push_back(current_line_); | 709 lines_->push_back(current_line_); |
721 current_line_.clear(); | 710 current_line_.clear(); |
722 line_added = true; | 711 line_added = true; |
723 } else { | 712 } else { |
724 insufficient_height_ = true; | 713 insufficient_height_ = true; |
725 } | 714 } |
726 current_height_ += line_height_; | 715 current_height_ += line_height_; |
727 current_width_ = 0; | |
728 return line_added; | 716 return line_added; |
729 } | 717 } |
730 | 718 |
731 } // namespace | 719 } // namespace |
732 | 720 |
733 bool ElideRectangleString(const base::string16& input, size_t max_rows, | 721 bool ElideRectangleString(const base::string16& input, size_t max_rows, |
734 size_t max_cols, bool strict, | 722 size_t max_cols, bool strict, |
735 base::string16* output) { | 723 base::string16* output) { |
736 RectangleString rect(max_rows, max_cols, strict, output); | 724 RectangleString rect(max_rows, max_cols, strict, output); |
737 rect.Init(); | 725 rect.Init(); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
815 index = char_iterator.getIndex(); | 803 index = char_iterator.getIndex(); |
816 } else { | 804 } else { |
817 // String has leading whitespace, return the elide string. | 805 // String has leading whitespace, return the elide string. |
818 return kElideString; | 806 return kElideString; |
819 } | 807 } |
820 | 808 |
821 return string.substr(0, index) + kElideString; | 809 return string.substr(0, index) + kElideString; |
822 } | 810 } |
823 | 811 |
824 } // namespace gfx | 812 } // namespace gfx |
OLD | NEW |