OLD | NEW |
---|---|
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/string_util.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" |
11 #include "ui/gfx/canvas.h" | |
12 #include "ui/gfx/render_text.h" | |
10 #include "ui/gfx/screen.h" | 13 #include "ui/gfx/screen.h" |
11 #include "ui/gfx/text_elider.h" | 14 #include "ui/gfx/text_elider.h" |
12 #include "ui/gfx/text_utils.h" | 15 #include "ui/gfx/text_utils.h" |
13 #include "ui/native_theme/native_theme.h" | 16 #include "ui/native_theme/native_theme.h" |
14 #include "ui/views/background.h" | 17 #include "ui/views/background.h" |
15 #include "ui/views/border.h" | 18 #include "ui/views/border.h" |
19 #include "ui/views/view.h" | |
16 #include "ui/views/widget/widget.h" | 20 #include "ui/views/widget/widget.h" |
17 | 21 |
18 namespace { | 22 namespace { |
19 | 23 |
20 // Max visual tooltip width. If a tooltip is greater than this width, it will | 24 // Max visual tooltip width. If a tooltip is greater than this width, it will |
21 // be wrapped. | 25 // be wrapped. |
22 const int kTooltipMaxWidthPixels = 400; | 26 const int kTooltipMaxWidthPixels = 400; |
23 | 27 |
24 const size_t kMaxLines = 10; | |
25 | |
26 // FIXME: get cursor offset from actual cursor size. | 28 // FIXME: get cursor offset from actual cursor size. |
27 const int kCursorOffsetX = 10; | 29 const int kCursorOffsetX = 10; |
28 const int kCursorOffsetY = 15; | 30 const int kCursorOffsetY = 15; |
29 | 31 |
30 // Creates a widget of type TYPE_TOOLTIP | 32 // Creates a widget of type TYPE_TOOLTIP |
31 views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { | 33 views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { |
32 views::Widget* widget = new views::Widget; | 34 views::Widget* widget = new views::Widget; |
33 views::Widget::InitParams params; | 35 views::Widget::InitParams params; |
34 // For aura, since we set the type to TYPE_TOOLTIP, the widget will get | 36 // For aura, since we set the type to TYPE_TOOLTIP, the widget will get |
35 // auto-parented to the right container. | 37 // auto-parented to the right container. |
36 params.type = views::Widget::InitParams::TYPE_TOOLTIP; | 38 params.type = views::Widget::InitParams::TYPE_TOOLTIP; |
37 params.context = tooltip_window; | 39 params.context = tooltip_window; |
38 DCHECK(params.context); | 40 DCHECK(params.context); |
39 params.keep_on_top = true; | 41 params.keep_on_top = true; |
40 params.accept_events = false; | 42 params.accept_events = false; |
41 widget->Init(params); | 43 widget->Init(params); |
42 return widget; | 44 return widget; |
43 } | 45 } |
44 | 46 |
45 } // namespace | 47 } // namespace |
46 | 48 |
47 namespace views { | 49 namespace views { |
48 namespace corewm { | 50 namespace corewm { |
49 | 51 |
52 // TODO(oshima): Consider to use views::Label. | |
53 class TooltipAura::TooltipView : public views::View { | |
54 public: | |
55 TooltipView() | |
56 : render_text_(gfx::RenderText::CreateInstance()), | |
57 max_width_(0) { | |
58 render_text_->SetMultiline(true); | |
59 ResetDisplayRect(); | |
60 } | |
61 | |
62 ~TooltipView() override {} | |
63 | |
64 // views:View: | |
65 void OnPaint(gfx::Canvas* canvas) override { | |
66 OnPaintBackground(canvas); | |
67 render_text_->SetDisplayRect(gfx::Rect(size())); | |
68 render_text_->Draw(canvas); | |
69 OnPaintBorder(canvas); | |
70 } | |
71 | |
72 gfx::Size GetPreferredSize() const override { | |
73 return render_text_->GetStringSize(); | |
74 } | |
75 | |
76 const char* GetClassName() const override { | |
77 return "TooltipView"; | |
78 } | |
79 | |
80 void SetText(const base::string16& text) { | |
81 render_text_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); | |
82 render_text_->SetText(text); | |
83 } | |
84 | |
85 void SetForegroundColor(SkColor color) { | |
86 render_text_->SetColor(color); | |
87 } | |
88 | |
89 void SetMaxWidth(int width) { | |
90 max_width_ = width; | |
91 ResetDisplayRect(); | |
92 } | |
93 | |
94 private: | |
95 void ResetDisplayRect() { | |
96 render_text_->SetDisplayRect(gfx::Rect(0, 0, max_width_, 100000)); | |
97 } | |
98 | |
99 gfx::RenderText* render_text_; | |
sky
2015/02/19 00:07:56
scoped_ptr
| |
100 int max_width_; | |
101 | |
102 DISALLOW_COPY_AND_ASSIGN(TooltipView); | |
103 }; | |
104 | |
50 TooltipAura::TooltipAura() | 105 TooltipAura::TooltipAura() |
51 : widget_(NULL), | 106 : tooltip_view_(new TooltipView), |
107 widget_(NULL), | |
52 tooltip_window_(NULL) { | 108 tooltip_window_(NULL) { |
53 label_.set_owned_by_client(); | |
54 label_.SetMultiLine(true); | |
55 label_.SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); | |
56 | 109 |
57 const int kHorizontalPadding = 3; | 110 const int kHorizontalPadding = 3; |
58 const int kVerticalPadding = 2; | 111 const int kVerticalPadding = 2; |
59 label_.SetBorder(Border::CreateEmptyBorder( | 112 tooltip_view_->SetBorder(Border::CreateEmptyBorder( |
60 kVerticalPadding, kHorizontalPadding, | 113 kVerticalPadding, kHorizontalPadding, |
61 kVerticalPadding, kHorizontalPadding)); | 114 kVerticalPadding, kHorizontalPadding)); |
62 } | 115 } |
63 | 116 |
64 TooltipAura::~TooltipAura() { | 117 TooltipAura::~TooltipAura() { |
65 DestroyWidget(); | 118 DestroyWidget(); |
66 } | 119 } |
67 | 120 |
68 // static | |
69 void TooltipAura::TrimTooltipToFit(const gfx::FontList& font_list, | |
70 int max_width, | |
71 base::string16* text, | |
72 int* width, | |
73 int* line_count) { | |
74 *width = 0; | |
75 *line_count = 0; | |
76 | |
77 // Determine the available width for the tooltip. | |
78 int available_width = std::min(kTooltipMaxWidthPixels, max_width); | |
79 | |
80 std::vector<base::string16> lines; | |
81 base::SplitString(*text, '\n', &lines); | |
82 std::vector<base::string16> result_lines; | |
83 | |
84 // Format each line to fit. | |
85 for (std::vector<base::string16>::iterator l = lines.begin(); | |
86 l != lines.end(); ++l) { | |
87 // We break the line at word boundaries, then stuff as many words as we can | |
88 // in the available width to the current line, and move the remaining words | |
89 // to a new line. | |
90 std::vector<base::string16> words; | |
91 base::SplitStringDontTrim(*l, ' ', &words); | |
92 int current_width = 0; | |
93 base::string16 line; | |
94 for (std::vector<base::string16>::iterator w = words.begin(); | |
95 w != words.end(); ++w) { | |
96 base::string16 word = *w; | |
97 if (w + 1 != words.end()) | |
98 word.push_back(' '); | |
99 int word_width = gfx::GetStringWidth(word, font_list); | |
100 if (current_width + word_width > available_width) { | |
101 // Current width will exceed the available width. Must start a new line. | |
102 if (!line.empty()) | |
103 result_lines.push_back(line); | |
104 current_width = 0; | |
105 line.clear(); | |
106 } | |
107 current_width += word_width; | |
108 line.append(word); | |
109 } | |
110 result_lines.push_back(line); | |
111 } | |
112 | |
113 // Clamp number of lines to |kMaxLines|. | |
114 if (result_lines.size() > kMaxLines) { | |
115 result_lines.resize(kMaxLines); | |
116 // Add ellipses character to last line. | |
117 result_lines[kMaxLines - 1] = gfx::TruncateString( | |
118 result_lines.back(), result_lines.back().length() - 1, gfx::WORD_BREAK); | |
119 } | |
120 *line_count = result_lines.size(); | |
121 | |
122 // Flatten the result. | |
123 base::string16 result; | |
124 for (std::vector<base::string16>::iterator l = result_lines.begin(); | |
125 l != result_lines.end(); ++l) { | |
126 if (!result.empty()) | |
127 result.push_back('\n'); | |
128 int line_width = gfx::GetStringWidth(*l, font_list); | |
129 // Since we only break at word boundaries, it could happen that due to some | |
130 // very long word, line_width is greater than the available_width. In such | |
131 // case, we simply truncate at available_width and add ellipses at the end. | |
132 if (line_width > available_width) { | |
133 *width = available_width; | |
134 result.append(gfx::ElideText(*l, font_list, available_width, | |
135 gfx::ELIDE_TAIL)); | |
136 } else { | |
137 *width = std::max(*width, line_width); | |
138 result.append(*l); | |
139 } | |
140 } | |
141 *text = result; | |
142 } | |
143 | |
144 void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, | 121 void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, |
145 const gfx::Size& tooltip_size) { | 122 const gfx::Size& tooltip_size) { |
146 gfx::Rect tooltip_rect(mouse_pos, tooltip_size); | 123 gfx::Rect tooltip_rect(mouse_pos, tooltip_size); |
147 tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); | 124 tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); |
148 gfx::Screen* screen = gfx::Screen::GetScreenFor(tooltip_window_); | 125 gfx::Screen* screen = gfx::Screen::GetScreenFor(tooltip_window_); |
149 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); | 126 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); |
150 | 127 |
151 // If tooltip is out of bounds on the x axis, we simply shift it | 128 // If tooltip is out of bounds on the x axis, we simply shift it |
152 // horizontally by the offset. | 129 // horizontally by the offset. |
153 if (tooltip_rect.right() > display_bounds.right()) { | 130 if (tooltip_rect.right() > display_bounds.right()) { |
(...skipping 22 matching lines...) Expand all Loading... | |
176 aura::Window* context) const { | 153 aura::Window* context) const { |
177 gfx::Screen* screen = gfx::Screen::GetScreenFor(context); | 154 gfx::Screen* screen = gfx::Screen::GetScreenFor(context); |
178 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); | 155 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); |
179 return std::min(kTooltipMaxWidthPixels, (display_bounds.width() + 1) / 2); | 156 return std::min(kTooltipMaxWidthPixels, (display_bounds.width() + 1) / 2); |
180 } | 157 } |
181 | 158 |
182 void TooltipAura::SetText(aura::Window* window, | 159 void TooltipAura::SetText(aura::Window* window, |
183 const base::string16& tooltip_text, | 160 const base::string16& tooltip_text, |
184 const gfx::Point& location) { | 161 const gfx::Point& location) { |
185 tooltip_window_ = window; | 162 tooltip_window_ = window; |
186 int max_width = 0; | 163 tooltip_view_->SetMaxWidth(GetMaxWidth(location, window)); |
187 int line_count = 0; | 164 tooltip_view_->SetText(tooltip_text); |
188 base::string16 trimmed_text(tooltip_text); | |
189 TrimTooltipToFit(label_.font_list(), GetMaxWidth(location, window), | |
190 &trimmed_text, &max_width, &line_count); | |
191 label_.SetText(trimmed_text); | |
192 | 165 |
193 if (!widget_) { | 166 if (!widget_) { |
194 widget_ = CreateTooltipWidget(tooltip_window_); | 167 widget_ = CreateTooltipWidget(tooltip_window_); |
195 widget_->SetContentsView(&label_); | 168 widget_->SetContentsView(tooltip_view_); |
196 widget_->AddObserver(this); | 169 widget_->AddObserver(this); |
197 } | 170 } |
198 | 171 |
199 label_.SizeToFit(max_width + label_.GetInsets().width()); | 172 SetTooltipBounds(location, tooltip_view_->GetPreferredSize()); |
200 SetTooltipBounds(location, label_.size()); | |
201 | 173 |
202 ui::NativeTheme* native_theme = widget_->GetNativeTheme(); | 174 ui::NativeTheme* native_theme = widget_->GetNativeTheme(); |
203 label_.set_background( | 175 tooltip_view_->set_background( |
204 views::Background::CreateSolidBackground( | 176 views::Background::CreateSolidBackground( |
205 native_theme->GetSystemColor( | 177 native_theme->GetSystemColor( |
206 ui::NativeTheme::kColorId_TooltipBackground))); | 178 ui::NativeTheme::kColorId_TooltipBackground))); |
207 | 179 tooltip_view_->SetForegroundColor(native_theme->GetSystemColor( |
208 label_.SetAutoColorReadabilityEnabled(false); | |
209 label_.SetEnabledColor(native_theme->GetSystemColor( | |
210 ui::NativeTheme::kColorId_TooltipText)); | 180 ui::NativeTheme::kColorId_TooltipText)); |
211 } | 181 } |
212 | 182 |
213 void TooltipAura::Show() { | 183 void TooltipAura::Show() { |
214 if (widget_) { | 184 if (widget_) { |
215 widget_->Show(); | 185 widget_->Show(); |
216 widget_->StackAtTop(); | 186 widget_->StackAtTop(); |
217 } | 187 } |
218 } | 188 } |
219 | 189 |
220 void TooltipAura::Hide() { | 190 void TooltipAura::Hide() { |
221 tooltip_window_ = NULL; | 191 tooltip_window_ = NULL; |
222 if (widget_) | 192 if (widget_) |
223 widget_->Hide(); | 193 widget_->Hide(); |
224 } | 194 } |
225 | 195 |
226 bool TooltipAura::IsVisible() { | 196 bool TooltipAura::IsVisible() { |
227 return widget_ && widget_->IsVisible(); | 197 return widget_ && widget_->IsVisible(); |
228 } | 198 } |
229 | 199 |
230 void TooltipAura::OnWidgetDestroying(views::Widget* widget) { | 200 void TooltipAura::OnWidgetDestroying(views::Widget* widget) { |
231 DCHECK_EQ(widget_, widget); | 201 DCHECK_EQ(widget_, widget); |
232 widget_ = NULL; | 202 widget_ = NULL; |
233 tooltip_window_ = NULL; | 203 tooltip_window_ = NULL; |
234 } | 204 } |
235 | 205 |
236 } // namespace corewm | 206 } // namespace corewm |
237 } // namespace views | 207 } // namespace views |
OLD | NEW |