| 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 |