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 set_owned_by_client(); |
| 59 render_text_->SetMultiline(true); |
| 60 ResetDisplayRect(); |
| 61 } |
| 62 |
| 63 ~TooltipView() override {} |
| 64 |
| 65 // views:View: |
| 66 void OnPaint(gfx::Canvas* canvas) override { |
| 67 OnPaintBackground(canvas); |
| 68 render_text_->SetDisplayRect(gfx::Rect(size())); |
| 69 render_text_->Draw(canvas); |
| 70 OnPaintBorder(canvas); |
| 71 } |
| 72 |
| 73 gfx::Size GetPreferredSize() const override { |
| 74 return render_text_->GetStringSize(); |
| 75 } |
| 76 |
| 77 const char* GetClassName() const override { |
| 78 return "TooltipView"; |
| 79 } |
| 80 |
| 81 void SetText(const base::string16& text) { |
| 82 render_text_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); |
| 83 render_text_->SetText(text); |
| 84 } |
| 85 |
| 86 void SetForegroundColor(SkColor color) { |
| 87 render_text_->SetColor(color); |
| 88 } |
| 89 |
| 90 void SetMaxWidth(int width) { |
| 91 max_width_ = width; |
| 92 ResetDisplayRect(); |
| 93 } |
| 94 |
| 95 private: |
| 96 void ResetDisplayRect() { |
| 97 render_text_->SetDisplayRect(gfx::Rect(0, 0, max_width_, 100000)); |
| 98 } |
| 99 |
| 100 scoped_ptr<gfx::RenderText> render_text_; |
| 101 int max_width_; |
| 102 |
| 103 DISALLOW_COPY_AND_ASSIGN(TooltipView); |
| 104 }; |
| 105 |
50 TooltipAura::TooltipAura() | 106 TooltipAura::TooltipAura() |
51 : widget_(NULL), | 107 : tooltip_view_(new TooltipView), |
| 108 widget_(NULL), |
52 tooltip_window_(NULL) { | 109 tooltip_window_(NULL) { |
53 label_.set_owned_by_client(); | |
54 label_.SetMultiLine(true); | |
55 label_.SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); | |
56 | 110 |
57 const int kHorizontalPadding = 3; | 111 const int kHorizontalPadding = 3; |
58 const int kVerticalPadding = 2; | 112 const int kVerticalPadding = 2; |
59 label_.SetBorder(Border::CreateEmptyBorder( | 113 tooltip_view_->SetBorder(Border::CreateEmptyBorder( |
60 kVerticalPadding, kHorizontalPadding, | 114 kVerticalPadding, kHorizontalPadding, |
61 kVerticalPadding, kHorizontalPadding)); | 115 kVerticalPadding, kHorizontalPadding)); |
62 } | 116 } |
63 | 117 |
64 TooltipAura::~TooltipAura() { | 118 TooltipAura::~TooltipAura() { |
65 DestroyWidget(); | 119 DestroyWidget(); |
66 } | 120 } |
67 | 121 |
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, | 122 void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, |
145 const gfx::Size& tooltip_size) { | 123 const gfx::Size& tooltip_size) { |
146 gfx::Rect tooltip_rect(mouse_pos, tooltip_size); | 124 gfx::Rect tooltip_rect(mouse_pos, tooltip_size); |
147 tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); | 125 tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); |
148 gfx::Screen* screen = gfx::Screen::GetScreenFor(tooltip_window_); | 126 gfx::Screen* screen = gfx::Screen::GetScreenFor(tooltip_window_); |
149 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); | 127 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); |
150 | 128 |
151 // If tooltip is out of bounds on the x axis, we simply shift it | 129 // If tooltip is out of bounds on the x axis, we simply shift it |
152 // horizontally by the offset. | 130 // horizontally by the offset. |
153 if (tooltip_rect.right() > display_bounds.right()) { | 131 if (tooltip_rect.right() > display_bounds.right()) { |
(...skipping 22 matching lines...) Expand all Loading... |
176 aura::Window* context) const { | 154 aura::Window* context) const { |
177 gfx::Screen* screen = gfx::Screen::GetScreenFor(context); | 155 gfx::Screen* screen = gfx::Screen::GetScreenFor(context); |
178 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); | 156 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); |
179 return std::min(kTooltipMaxWidthPixels, (display_bounds.width() + 1) / 2); | 157 return std::min(kTooltipMaxWidthPixels, (display_bounds.width() + 1) / 2); |
180 } | 158 } |
181 | 159 |
182 void TooltipAura::SetText(aura::Window* window, | 160 void TooltipAura::SetText(aura::Window* window, |
183 const base::string16& tooltip_text, | 161 const base::string16& tooltip_text, |
184 const gfx::Point& location) { | 162 const gfx::Point& location) { |
185 tooltip_window_ = window; | 163 tooltip_window_ = window; |
186 int max_width = 0; | 164 tooltip_view_->SetMaxWidth(GetMaxWidth(location, window)); |
187 int line_count = 0; | 165 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 | 166 |
193 if (!widget_) { | 167 if (!widget_) { |
194 widget_ = CreateTooltipWidget(tooltip_window_); | 168 widget_ = CreateTooltipWidget(tooltip_window_); |
195 widget_->SetContentsView(&label_); | 169 widget_->SetContentsView(tooltip_view_.get()); |
196 widget_->AddObserver(this); | 170 widget_->AddObserver(this); |
197 } | 171 } |
198 | 172 |
199 label_.SizeToFit(max_width + label_.GetInsets().width()); | 173 SetTooltipBounds(location, tooltip_view_->GetPreferredSize()); |
200 SetTooltipBounds(location, label_.size()); | |
201 | 174 |
202 ui::NativeTheme* native_theme = widget_->GetNativeTheme(); | 175 ui::NativeTheme* native_theme = widget_->GetNativeTheme(); |
203 label_.set_background( | 176 tooltip_view_->set_background( |
204 views::Background::CreateSolidBackground( | 177 views::Background::CreateSolidBackground( |
205 native_theme->GetSystemColor( | 178 native_theme->GetSystemColor( |
206 ui::NativeTheme::kColorId_TooltipBackground))); | 179 ui::NativeTheme::kColorId_TooltipBackground))); |
207 | 180 tooltip_view_->SetForegroundColor(native_theme->GetSystemColor( |
208 label_.SetAutoColorReadabilityEnabled(false); | |
209 label_.SetEnabledColor(native_theme->GetSystemColor( | |
210 ui::NativeTheme::kColorId_TooltipText)); | 181 ui::NativeTheme::kColorId_TooltipText)); |
211 } | 182 } |
212 | 183 |
213 void TooltipAura::Show() { | 184 void TooltipAura::Show() { |
214 if (widget_) { | 185 if (widget_) { |
215 widget_->Show(); | 186 widget_->Show(); |
216 widget_->StackAtTop(); | 187 widget_->StackAtTop(); |
217 } | 188 } |
218 } | 189 } |
219 | 190 |
220 void TooltipAura::Hide() { | 191 void TooltipAura::Hide() { |
221 tooltip_window_ = NULL; | 192 tooltip_window_ = NULL; |
222 if (widget_) | 193 if (widget_) |
223 widget_->Hide(); | 194 widget_->Hide(); |
224 } | 195 } |
225 | 196 |
226 bool TooltipAura::IsVisible() { | 197 bool TooltipAura::IsVisible() { |
227 return widget_ && widget_->IsVisible(); | 198 return widget_ && widget_->IsVisible(); |
228 } | 199 } |
229 | 200 |
230 void TooltipAura::OnWidgetDestroying(views::Widget* widget) { | 201 void TooltipAura::OnWidgetDestroying(views::Widget* widget) { |
231 DCHECK_EQ(widget_, widget); | 202 DCHECK_EQ(widget_, widget); |
232 widget_ = NULL; | 203 widget_ = NULL; |
233 tooltip_window_ = NULL; | 204 tooltip_window_ = NULL; |
234 } | 205 } |
235 | 206 |
236 } // namespace corewm | 207 } // namespace corewm |
237 } // namespace views | 208 } // namespace views |
OLD | NEW |