Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/views/controls/styled_label.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/string_util.h" | |
| 10 #include "ui/base/resource/resource_bundle.h" | |
| 11 #include "ui/base/text/text_elider.h" | |
| 12 #include "ui/views/controls/label.h" | |
| 13 #include "ui/views/controls/link.h" | |
| 14 #include "ui/views/controls/styled_label_listener.h" | |
| 15 | |
| 16 namespace views { | |
| 17 | |
| 18 bool StyledLabel::LinkRange::operator<(const StyledLabel::LinkRange& other) | |
|
msw
2013/03/14 23:24:30
nit: move the arg onto the next line so const isn'
Evan Stade
2013/03/15 02:42:27
is this really a part of our style guide? I see 89
sky
2013/03/15 03:32:18
It was in Google's c++ style guide, but was remove
msw
2013/03/15 08:38:02
Sorry about that, i'm behind the times!
| |
| 19 const { | |
| 20 // Intentionally reversed so the priority queue is sorted by smallest first. | |
| 21 return range.start() > other.range.start(); | |
| 22 } | |
| 23 | |
| 24 StyledLabel::StyledLabel(const string16& text, StyledLabelListener* listener) | |
| 25 : text_(text), | |
| 26 listener_(listener) {} | |
| 27 | |
| 28 StyledLabel::~StyledLabel() {} | |
| 29 | |
| 30 void StyledLabel::AddLink(const ui::Range& range) { | |
| 31 DCHECK(!range.is_reversed()); | |
| 32 DCHECK(!range.is_empty()); | |
| 33 DCHECK(ui::Range(0, text_.size()).Contains(range)); | |
| 34 link_ranges_.push(LinkRange(range)); | |
|
msw
2013/03/14 23:24:30
Should this InvalidateLayout? I don't think it nee
Evan Stade
2013/03/15 02:42:27
it does need to do both because adding a link may
| |
| 35 } | |
| 36 | |
| 37 gfx::Insets StyledLabel::GetInsets() const { | |
| 38 gfx::Insets insets = View::GetInsets(); | |
| 39 gfx::Insets focus_border_padding(1, 1, 1, 1); | |
|
msw
2013/03/14 23:24:30
nit: const
Evan Stade
2013/03/15 02:42:27
Done.
| |
| 40 insets += focus_border_padding; | |
| 41 return insets; | |
| 42 } | |
| 43 | |
| 44 int StyledLabel::GetHeightForWidth(int w) { | |
| 45 if (w != calculated_size_.width()) | |
| 46 calculated_size_ = gfx::Size(w, CalculateAndDoLayout(w, true)); | |
| 47 | |
| 48 return calculated_size_.height(); | |
| 49 } | |
| 50 | |
| 51 void StyledLabel::Layout() { | |
| 52 CalculateAndDoLayout(GetLocalBounds().width(), false); | |
| 53 } | |
| 54 | |
| 55 void StyledLabel::LinkClicked(Link* source, int event_flags) { | |
| 56 listener_->StyledLabelLinkClicked(link_targets_[source], event_flags); | |
| 57 } | |
| 58 | |
| 59 int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { | |
| 60 if (!dry_run) { | |
| 61 RemoveAllChildViews(true); | |
| 62 link_targets_.clear(); | |
| 63 } | |
| 64 | |
| 65 width -= GetInsets().width(); | |
| 66 if (width <= 0) | |
| 67 return 0; | |
| 68 | |
| 69 int line_height = CalculateLineHeight(); | |
|
msw
2013/03/14 23:24:30
nit: const
Evan Stade
2013/03/15 02:42:27
Done.
| |
| 70 // The index of the line we're on. | |
| 71 int line = 0; | |
| 72 // The x position (in pixels) of the line we're on, relative to content | |
| 73 // bounds. | |
| 74 int x = 0; | |
| 75 | |
| 76 string16 remaining_string = text_; | |
| 77 std::priority_queue<LinkRange> link_ranges = link_ranges_; | |
| 78 | |
| 79 // Iterate over the text, creating a bunch of labels and links and laying them | |
| 80 // out in the appropriate positions. | |
| 81 while (!remaining_string.empty()) { | |
| 82 // Don't put whitespace at beginning of a line. | |
| 83 if (x == 0) | |
| 84 TrimWhitespace(remaining_string, TRIM_LEADING, &remaining_string); | |
| 85 | |
| 86 ui::Range range(ui::Range::InvalidRange()); | |
| 87 if (!link_ranges.empty()) | |
| 88 range = link_ranges.top().range; | |
| 89 | |
| 90 gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height); | |
|
msw
2013/03/14 23:24:30
nit: const
Evan Stade
2013/03/15 02:42:27
Done.
| |
| 91 std::vector<string16> substrings; | |
| 92 const gfx::Font& font = | |
|
msw
2013/03/14 23:24:30
nit: Remove this Font init and the resource_bundle
Evan Stade
2013/03/15 02:42:27
Done.
| |
| 93 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); | |
| 94 ui::ElideRectangleText(remaining_string, | |
| 95 font, | |
| 96 chunk_bounds.width(), | |
| 97 chunk_bounds.height(), | |
| 98 ui::IGNORE_LONG_WORDS, | |
| 99 &substrings); | |
| 100 | |
| 101 string16 chunk = substrings[0]; | |
| 102 if (chunk.empty()) { | |
| 103 // Nothing fit on this line. Start a new line. If x is 0, there's no room | |
| 104 // for anything. Just abort. | |
| 105 if (x == 0) | |
| 106 break; | |
| 107 | |
| 108 x = 0; | |
| 109 line++; | |
| 110 continue; | |
| 111 } | |
| 112 | |
| 113 scoped_ptr<View> view; | |
| 114 size_t position = text_.size() - remaining_string.size(); | |
|
msw
2013/03/14 23:24:30
nit: const
Evan Stade
2013/03/15 02:42:27
Done.
| |
| 115 if (position >= range.start()) { | |
| 116 // This chunk is a link. | |
| 117 if (chunk.size() < range.length() && x != 0) { | |
| 118 // Don't wrap links. Try to fit them entirely on one line. | |
| 119 x = 0; | |
| 120 line++; | |
| 121 continue; | |
| 122 } | |
| 123 | |
| 124 chunk = chunk.substr(0, range.length()); | |
| 125 Link* link = new Link(chunk); | |
| 126 link->set_listener(this); | |
| 127 if (!dry_run) | |
| 128 link_targets_[link] = range; | |
| 129 view.reset(link); | |
| 130 link_ranges.pop(); | |
| 131 } else { | |
| 132 // This chunk is normal text. | |
| 133 if (position + chunk.size() > range.start()) | |
| 134 chunk = chunk.substr(0, range.start() - position); | |
| 135 | |
| 136 Label* label = new Label(chunk); | |
| 137 // Give the label a focus border so that its preferred size matches | |
| 138 // links' preferred sizes. | |
| 139 label->SetHasFocusBorder(true); | |
| 140 view.reset(label); | |
| 141 } | |
| 142 | |
| 143 // Lay out the views to overlap by 1 pixel to compensate for their border | |
| 144 // spacing. Otherwise, "<a>link</a>," will render as "link ,". | |
| 145 const int overlap = 1; | |
| 146 gfx::Size view_size = view->GetPreferredSize(); | |
|
msw
2013/03/14 23:24:30
nit: const
Evan Stade
2013/03/15 02:42:27
Done.
| |
| 147 DCHECK_EQ(line_height, view_size.height() - 2 * overlap); | |
|
msw
2013/03/14 23:24:30
nit: should line_height remove the focus border in
Evan Stade
2013/03/15 02:42:27
not sure I understand the question, but no, the li
msw
2013/03/15 08:38:02
Ah, I misread and thought that the lines were stac
| |
| 148 if (!dry_run) { | |
| 149 view->SetBoundsRect(gfx::Rect( | |
| 150 gfx::Point(GetInsets().left() + x - overlap, | |
| 151 GetInsets().top() + line * line_height - overlap), | |
| 152 view_size)); | |
| 153 AddChildView(view.release()); | |
| 154 } | |
| 155 x += view_size.width() - 2 * overlap; | |
| 156 | |
| 157 remaining_string = remaining_string.substr(chunk.size()); | |
| 158 } | |
| 159 | |
| 160 return (line + 1) * line_height + GetInsets().height(); | |
| 161 } | |
| 162 | |
| 163 int StyledLabel::CalculateLineHeight() { | |
| 164 Label label; | |
|
msw
2013/03/14 23:24:30
nit: static
Evan Stade
2013/03/15 02:42:27
class type statics are forbidden.
| |
| 165 return label.GetPreferredSize().height(); | |
| 166 } | |
| 167 | |
| 168 } // namespace views | |
| OLD | NEW |