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

Side by Side Diff: ui/views/corewm/tooltip_aura.cc

Issue 340543004: views: Support longer tooltips. Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: one more simplification Created 6 years, 6 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
« no previous file with comments | « ui/views/corewm/tooltip_aura.h ('k') | ui/views/corewm/tooltip_aura_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 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/corewm/tooltip_aura.h" 5 #include "ui/views/corewm/tooltip_aura.h"
6 6
7 #include "base/strings/string_split.h" 7 #include "base/strings/string_split.h"
8 #include "base/strings/utf_string_conversions.h"
8 #include "ui/aura/window.h" 9 #include "ui/aura/window.h"
9 #include "ui/aura/window_tree_host.h" 10 #include "ui/aura/window_tree_host.h"
10 #include "ui/base/resource/resource_bundle.h" 11 #include "ui/base/resource/resource_bundle.h"
11 #include "ui/gfx/screen.h" 12 #include "ui/gfx/screen.h"
12 #include "ui/gfx/text_elider.h" 13 #include "ui/gfx/text_elider.h"
13 #include "ui/gfx/text_utils.h" 14 #include "ui/gfx/text_utils.h"
14 #include "ui/native_theme/native_theme.h" 15 #include "ui/native_theme/native_theme.h"
15 #include "ui/views/background.h" 16 #include "ui/views/background.h"
16 #include "ui/views/border.h" 17 #include "ui/views/border.h"
17 #include "ui/views/widget/widget.h" 18 #include "ui/views/widget/widget.h"
18 19
19 namespace { 20 namespace {
20 21
21 const int kTooltipHorizontalPadding = 3; 22 const int kTooltipHorizontalPadding = 3;
22 23
23 // Max visual tooltip width. If a tooltip is greater than this width, it will
24 // be wrapped.
25 const int kTooltipMaxWidthPixels = 400;
26
27 const size_t kMaxLines = 10;
28
29 // TODO(derat): This padding is needed on Chrome OS devices but seems excessive 24 // TODO(derat): This padding is needed on Chrome OS devices but seems excessive
30 // when running the same binary on a Linux workstation; presumably there's a 25 // when running the same binary on a Linux workstation; presumably there's a
31 // difference in font metrics. Rationalize this. 26 // difference in font metrics. Rationalize this.
32 const int kTooltipVerticalPadding = 2; 27 const int kTooltipVerticalPadding = 2;
33 28
34 // FIXME: get cursor offset from actual cursor size. 29 // FIXME: get cursor offset from actual cursor size.
35 const int kCursorOffsetX = 10; 30 const int kCursorOffsetX = 10;
36 const int kCursorOffsetY = 15; 31 const int kCursorOffsetY = 15;
37 32
38 // Creates a widget of type TYPE_TOOLTIP 33 // Creates a widget of type TYPE_TOOLTIP
39 views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { 34 views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) {
40 views::Widget* widget = new views::Widget; 35 views::Widget* widget = new views::Widget;
41 views::Widget::InitParams params; 36 views::Widget::InitParams params;
42 // For aura, since we set the type to TYPE_TOOLTIP, the widget will get 37 // For aura, since we set the type to TYPE_TOOLTIP, the widget will get
43 // auto-parented to the right container. 38 // auto-parented to the right container.
44 params.type = views::Widget::InitParams::TYPE_TOOLTIP; 39 params.type = views::Widget::InitParams::TYPE_TOOLTIP;
45 params.context = tooltip_window; 40 params.context = tooltip_window;
46 DCHECK(params.context); 41 DCHECK(params.context);
47 params.keep_on_top = true; 42 params.keep_on_top = true;
48 params.accept_events = false; 43 params.accept_events = false;
49 widget->Init(params); 44 widget->Init(params);
50 return widget; 45 return widget;
51 } 46 }
52 47
53 } // namespace 48 } // namespace
54 49
55 namespace views { 50 namespace views {
56 namespace corewm { 51 namespace corewm {
57 52
53 const int TooltipAura::kPreferredWidthPixels = 400;
54
58 TooltipAura::TooltipAura(gfx::ScreenType screen_type) 55 TooltipAura::TooltipAura(gfx::ScreenType screen_type)
59 : screen_type_(screen_type), 56 : screen_type_(screen_type),
60 widget_(NULL), 57 widget_(NULL),
61 tooltip_window_(NULL) { 58 tooltip_window_(NULL) {
62 label_.set_owned_by_client(); 59 label_.set_owned_by_client();
63 label_.SetMultiLine(true); 60 label_.SetMultiLine(true);
64 } 61 }
65 62
66 TooltipAura::~TooltipAura() { 63 TooltipAura::~TooltipAura() {
67 DestroyWidget(); 64 DestroyWidget();
68 } 65 }
69 66
70 // static 67 // static
71 void TooltipAura::TrimTooltipToFit(const gfx::FontList& font_list, 68 void TooltipAura::TrimTooltipToFit(const gfx::FontList& font_list,
sky 2014/06/18 03:20:07 I think we should convert this to use views::Label
Daniel Erat 2014/06/18 04:37:11 i don't know the history here, but i believe that
varunjain 2014/06/22 20:00:30 Sorry I do not remember why label was not used dir
72 int max_width, 69 const gfx::Size& max_size,
73 base::string16* text, 70 base::string16* text,
74 int* width, 71 int* width,
75 int* line_count) { 72 int* line_count) {
76 *width = 0; 73 *width = 0;
77 *line_count = 0; 74 *line_count = 0;
78 75
79 // Determine the available width for the tooltip.
80 int available_width = std::min(kTooltipMaxWidthPixels, max_width);
81
82 std::vector<base::string16> lines; 76 std::vector<base::string16> lines;
83 base::SplitString(*text, '\n', &lines); 77 base::SplitString(*text, '\n', &lines);
84 std::vector<base::string16> result_lines; 78
79 // TODO: Use ICU to split into words.
80 int max_word_width = 0;
81 std::vector<std::vector<base::string16> > words_by_line;
82 for (size_t i = 0; i < lines.size(); ++i) {
83 std::vector<base::string16> words;
84 base::SplitStringDontTrim(lines[i], ' ', &words);
85 for (size_t j = 0; j < words.size(); ++j) {
86 const base::string16& word = words[j];
87 max_word_width = std::max(gfx::GetStringWidth(word, font_list),
88 max_word_width);
89 }
90 words_by_line.push_back(words);
91 }
92
93 // Determine the available width for the tooltip.
94 const int available_width = std::min(max_size.width(),
95 std::max(max_word_width, kPreferredWidthPixels));
85 96
86 // Format each line to fit. 97 // Format each line to fit.
87 for (std::vector<base::string16>::iterator l = lines.begin(); 98 std::vector<base::string16> result_lines;
88 l != lines.end(); ++l) { 99 const base::string16 space(base::ASCIIToUTF16(" "));
100 for (size_t i = 0; i < words_by_line.size(); ++i) {
89 // We break the line at word boundaries, then stuff as many words as we can 101 // We break the line at word boundaries, then stuff as many words as we can
90 // in the available width to the current line, and move the remaining words 102 // in the available width to the current line, and move the remaining words
91 // to a new line. 103 // to a new line.
92 std::vector<base::string16> words; 104 const std::vector<base::string16>& words = words_by_line[i];
93 base::SplitStringDontTrim(*l, ' ', &words);
94 int current_width = 0;
95 base::string16 line; 105 base::string16 line;
96 for (std::vector<base::string16>::iterator w = words.begin(); 106 for (size_t j = 0; j < words.size(); ++j) {
97 w != words.end(); ++w) { 107 const base::string16& word = words[j];
98 base::string16 word = *w; 108 if (!line.empty()) {
99 if (w + 1 != words.end()) 109 // Put the word on the end of the line if it fits.
100 word.push_back(' '); 110 base::string16 proposed_line = line + space + word;
101 int word_width = gfx::GetStringWidth(word, font_list); 111 if (gfx::GetStringWidth(proposed_line, font_list) <= available_width) {
102 if (current_width + word_width > available_width) { 112 line = proposed_line;
103 // Current width will exceed the available width. Must start a new line. 113 continue;
104 if (!line.empty()) 114 }
105 result_lines.push_back(line); 115 // Otherwise, save the in-progress line and start a new one.
106 current_width = 0; 116 result_lines.push_back(line);
107 line.clear();
108 } 117 }
109 current_width += word_width; 118 line = word;
110 line.append(word);
111 } 119 }
112 result_lines.push_back(line); 120 if (!line.empty())
121 result_lines.push_back(line);
113 } 122 }
114 123
115 // Clamp number of lines to |kMaxLines|. 124 // Clamp the number of lines.
116 if (result_lines.size() > kMaxLines) { 125 const size_t max_lines = static_cast<size_t>(
117 result_lines.resize(kMaxLines); 126 std::max(1, max_size.height() / font_list.GetHeight()));
127 if (result_lines.size() > max_lines) {
128 result_lines.resize(max_lines);
118 // Add ellipses character to last line. 129 // Add ellipses character to last line.
119 result_lines[kMaxLines - 1] = gfx::TruncateString( 130 result_lines[max_lines - 1] = gfx::TruncateString(
120 result_lines.back(), result_lines.back().length() - 1); 131 result_lines.back(), result_lines.back().length() - 1);
121 } 132 }
122 *line_count = result_lines.size(); 133 *line_count = result_lines.size();
123 134
124 // Flatten the result. 135 // Flatten the result.
125 base::string16 result; 136 base::string16 result;
126 for (std::vector<base::string16>::iterator l = result_lines.begin(); 137 for (size_t i = 0; i < result_lines.size(); ++i) {
127 l != result_lines.end(); ++l) {
128 if (!result.empty()) 138 if (!result.empty())
129 result.push_back('\n'); 139 result.push_back('\n');
130 int line_width = gfx::GetStringWidth(*l, font_list); 140
141 base::string16 line = result_lines[i];
142 int line_width = gfx::GetStringWidth(line, font_list);
143
131 // Since we only break at word boundaries, it could happen that due to some 144 // Since we only break at word boundaries, it could happen that due to some
132 // very long word, line_width is greater than the available_width. In such 145 // very long word, line_width is greater than the available_width. In such
133 // case, we simply truncate at available_width and add ellipses at the end. 146 // case, we simply truncate at available_width and add ellipses at the end.
134 if (line_width > available_width) { 147 if (line_width > available_width) {
135 *width = available_width; 148 line = gfx::ElideText(line, font_list, available_width, gfx::ELIDE_TAIL);
136 result.append(gfx::ElideText(*l, font_list, available_width, 149 line_width = gfx::GetStringWidth(line, font_list);
137 gfx::ELIDE_TAIL));
138 } else {
139 *width = std::max(*width, line_width);
140 result.append(*l);
141 } 150 }
151
152 result.append(line);
153 *width = std::max(*width, line_width);
142 } 154 }
143 *text = result; 155 *text = result;
144 } 156 }
145 157
146 int TooltipAura::GetMaxWidth(const gfx::Point& location) const { 158 gfx::Size TooltipAura::GetMaxSize(const gfx::Point& location) const {
147 // TODO(varunjain): implementation duplicated in tooltip_manager_aura. Figure
148 // out a way to merge.
149 gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_); 159 gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_);
150 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); 160 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds());
151 return (display_bounds.width() + 1) / 2; 161 return gfx::Size(display_bounds.width() * 3 / 4,
sky 2014/06/18 03:20:07 I'm inclined to give the tooltip the full width. W
162 display_bounds.height() * 3 / 4);
152 } 163 }
153 164
154 void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, 165 void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos,
155 int tooltip_width, 166 const gfx::Size& tooltip_size) {
156 int tooltip_height) { 167 gfx::Rect tooltip_rect(mouse_pos, tooltip_size);
157 gfx::Rect tooltip_rect(mouse_pos.x(), mouse_pos.y(), tooltip_width,
158 tooltip_height);
159 168
160 tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); 169 tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY);
161 gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_); 170 gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_);
162 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); 171 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds());
163 172
164 // If tooltip is out of bounds on the x axis, we simply shift it 173 // If tooltip is out of bounds on the x axis, we simply shift it
165 // horizontally by the offset. 174 // horizontally by the offset.
166 if (tooltip_rect.right() > display_bounds.right()) { 175 if (tooltip_rect.right() > display_bounds.right()) {
167 int h_offset = tooltip_rect.right() - display_bounds.right(); 176 int h_offset = tooltip_rect.right() - display_bounds.right();
168 tooltip_rect.Offset(-h_offset, 0); 177 tooltip_rect.Offset(-h_offset, 0);
169 } 178 }
170 179
171 // If tooltip is out of bounds on the y axis, we flip it to appear above the 180 // If tooltip is out of bounds on the y axis, we flip it to appear above the
172 // mouse cursor instead of below. 181 // mouse cursor instead of below.
173 if (tooltip_rect.bottom() > display_bounds.bottom()) 182 if (tooltip_rect.bottom() > display_bounds.bottom())
174 tooltip_rect.set_y(mouse_pos.y() - tooltip_height); 183 tooltip_rect.set_y(mouse_pos.y() - tooltip_rect.height());
175 184
176 tooltip_rect.AdjustToFit(display_bounds); 185 tooltip_rect.AdjustToFit(display_bounds);
177 widget_->SetBounds(tooltip_rect); 186 widget_->SetBounds(tooltip_rect);
178 } 187 }
179 188
180 void TooltipAura::DestroyWidget() { 189 void TooltipAura::DestroyWidget() {
181 if (widget_) { 190 if (widget_) {
182 widget_->RemoveObserver(this); 191 widget_->RemoveObserver(this);
183 widget_->Close(); 192 widget_->Close();
184 widget_ = NULL; 193 widget_ = NULL;
185 } 194 }
186 } 195 }
187 196
188 void TooltipAura::SetText(aura::Window* window, 197 void TooltipAura::SetText(aura::Window* window,
189 const base::string16& tooltip_text, 198 const base::string16& tooltip_text,
190 const gfx::Point& location) { 199 const gfx::Point& location) {
191 tooltip_window_ = window; 200 tooltip_window_ = window;
192 int max_width, line_count; 201 int max_width, line_count;
193 base::string16 trimmed_text(tooltip_text); 202 base::string16 trimmed_text(tooltip_text);
194 TrimTooltipToFit(label_.font_list(), GetMaxWidth(location), &trimmed_text, 203 TrimTooltipToFit(label_.font_list(), GetMaxSize(location), &trimmed_text,
195 &max_width, &line_count); 204 &max_width, &line_count);
196 label_.SetText(trimmed_text); 205 label_.SetText(trimmed_text);
197 206
198 int width = max_width + 2 * kTooltipHorizontalPadding; 207 int width = max_width + 2 * kTooltipHorizontalPadding;
199 int height = label_.GetHeightForWidth(max_width) + 208 int height = label_.GetHeightForWidth(max_width) +
200 2 * kTooltipVerticalPadding; 209 2 * kTooltipVerticalPadding;
201 210
202 if (!widget_) { 211 if (!widget_) {
203 widget_ = CreateTooltipWidget(tooltip_window_); 212 widget_ = CreateTooltipWidget(tooltip_window_);
204 widget_->SetContentsView(&label_); 213 widget_->SetContentsView(&label_);
205 widget_->AddObserver(this); 214 widget_->AddObserver(this);
206 } 215 }
207 216
208 SetTooltipBounds(location, width, height); 217 SetTooltipBounds(location, gfx::Size(width, height));
209 218
210 ui::NativeTheme* native_theme = widget_->GetNativeTheme(); 219 ui::NativeTheme* native_theme = widget_->GetNativeTheme();
211 label_.set_background( 220 label_.set_background(
212 views::Background::CreateSolidBackground( 221 views::Background::CreateSolidBackground(
213 native_theme->GetSystemColor( 222 native_theme->GetSystemColor(
214 ui::NativeTheme::kColorId_TooltipBackground))); 223 ui::NativeTheme::kColorId_TooltipBackground)));
215 224
216 label_.SetAutoColorReadabilityEnabled(false); 225 label_.SetAutoColorReadabilityEnabled(false);
217 label_.SetEnabledColor(native_theme->GetSystemColor( 226 label_.SetEnabledColor(native_theme->GetSystemColor(
218 ui::NativeTheme::kColorId_TooltipText)); 227 ui::NativeTheme::kColorId_TooltipText));
(...skipping 17 matching lines...) Expand all
236 } 245 }
237 246
238 void TooltipAura::OnWidgetDestroying(views::Widget* widget) { 247 void TooltipAura::OnWidgetDestroying(views::Widget* widget) {
239 DCHECK_EQ(widget_, widget); 248 DCHECK_EQ(widget_, widget);
240 widget_ = NULL; 249 widget_ = NULL;
241 tooltip_window_ = NULL; 250 tooltip_window_ = NULL;
242 } 251 }
243 252
244 } // namespace corewm 253 } // namespace corewm
245 } // namespace views 254 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/corewm/tooltip_aura.h ('k') | ui/views/corewm/tooltip_aura_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698