Chromium Code Reviews| 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 |