| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/views/corewm/tooltip_aura.h" | |
| 6 | |
| 7 #include "base/strings/string_split.h" | |
| 8 #include "ui/aura/window.h" | |
| 9 #include "ui/aura/window_tree_host.h" | |
| 10 #include "ui/gfx/screen.h" | |
| 11 #include "ui/gfx/text_elider.h" | |
| 12 #include "ui/gfx/text_utils.h" | |
| 13 #include "ui/native_theme/native_theme.h" | |
| 14 #include "ui/views/background.h" | |
| 15 #include "ui/views/border.h" | |
| 16 #include "ui/views/widget/widget.h" | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 // Max visual tooltip width. If a tooltip is greater than this width, it will | |
| 21 // be wrapped. | |
| 22 const int kTooltipMaxWidthPixels = 400; | |
| 23 | |
| 24 const size_t kMaxLines = 10; | |
| 25 | |
| 26 // FIXME: get cursor offset from actual cursor size. | |
| 27 const int kCursorOffsetX = 10; | |
| 28 const int kCursorOffsetY = 15; | |
| 29 | |
| 30 // Creates a widget of type TYPE_TOOLTIP | |
| 31 views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { | |
| 32 views::Widget* widget = new views::Widget; | |
| 33 views::Widget::InitParams params; | |
| 34 // For aura, since we set the type to TYPE_TOOLTIP, the widget will get | |
| 35 // auto-parented to the right container. | |
| 36 params.type = views::Widget::InitParams::TYPE_TOOLTIP; | |
| 37 params.context = tooltip_window; | |
| 38 DCHECK(params.context); | |
| 39 params.keep_on_top = true; | |
| 40 params.accept_events = false; | |
| 41 widget->Init(params); | |
| 42 return widget; | |
| 43 } | |
| 44 | |
| 45 } // namespace | |
| 46 | |
| 47 namespace views { | |
| 48 namespace corewm { | |
| 49 | |
| 50 TooltipAura::TooltipAura(gfx::ScreenType screen_type) | |
| 51 : screen_type_(screen_type), | |
| 52 widget_(NULL), | |
| 53 tooltip_window_(NULL) { | |
| 54 label_.set_owned_by_client(); | |
| 55 label_.SetMultiLine(true); | |
| 56 | |
| 57 const int kHorizontalPadding = 3; | |
| 58 const int kVerticalPadding = 2; | |
| 59 label_.SetBorder(Border::CreateEmptyBorder( | |
| 60 kVerticalPadding, kHorizontalPadding, | |
| 61 kVerticalPadding, kHorizontalPadding)); | |
| 62 } | |
| 63 | |
| 64 TooltipAura::~TooltipAura() { | |
| 65 DestroyWidget(); | |
| 66 } | |
| 67 | |
| 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 int TooltipAura::GetMaxWidth(const gfx::Point& location) const { | |
| 145 // TODO(varunjain): implementation duplicated in tooltip_manager_aura. Figure | |
| 146 // out a way to merge. | |
| 147 gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_); | |
| 148 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); | |
| 149 return (display_bounds.width() + 1) / 2; | |
| 150 } | |
| 151 | |
| 152 void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, | |
| 153 const gfx::Size& tooltip_size) { | |
| 154 gfx::Rect tooltip_rect(mouse_pos, tooltip_size); | |
| 155 tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); | |
| 156 gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_); | |
| 157 gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); | |
| 158 | |
| 159 // If tooltip is out of bounds on the x axis, we simply shift it | |
| 160 // horizontally by the offset. | |
| 161 if (tooltip_rect.right() > display_bounds.right()) { | |
| 162 int h_offset = tooltip_rect.right() - display_bounds.right(); | |
| 163 tooltip_rect.Offset(-h_offset, 0); | |
| 164 } | |
| 165 | |
| 166 // If tooltip is out of bounds on the y axis, we flip it to appear above the | |
| 167 // mouse cursor instead of below. | |
| 168 if (tooltip_rect.bottom() > display_bounds.bottom()) | |
| 169 tooltip_rect.set_y(mouse_pos.y() - tooltip_size.height()); | |
| 170 | |
| 171 tooltip_rect.AdjustToFit(display_bounds); | |
| 172 widget_->SetBounds(tooltip_rect); | |
| 173 } | |
| 174 | |
| 175 void TooltipAura::DestroyWidget() { | |
| 176 if (widget_) { | |
| 177 widget_->RemoveObserver(this); | |
| 178 widget_->Close(); | |
| 179 widget_ = NULL; | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 void TooltipAura::SetText(aura::Window* window, | |
| 184 const base::string16& tooltip_text, | |
| 185 const gfx::Point& location) { | |
| 186 tooltip_window_ = window; | |
| 187 int max_width = 0; | |
| 188 int line_count = 0; | |
| 189 base::string16 trimmed_text(tooltip_text); | |
| 190 TrimTooltipToFit(label_.font_list(), GetMaxWidth(location), &trimmed_text, | |
| 191 &max_width, &line_count); | |
| 192 label_.SetText(trimmed_text); | |
| 193 | |
| 194 if (!widget_) { | |
| 195 widget_ = CreateTooltipWidget(tooltip_window_); | |
| 196 widget_->SetContentsView(&label_); | |
| 197 widget_->AddObserver(this); | |
| 198 } | |
| 199 | |
| 200 label_.SizeToFit(max_width + label_.GetInsets().width()); | |
| 201 SetTooltipBounds(location, label_.size()); | |
| 202 | |
| 203 ui::NativeTheme* native_theme = widget_->GetNativeTheme(); | |
| 204 label_.set_background( | |
| 205 views::Background::CreateSolidBackground( | |
| 206 native_theme->GetSystemColor( | |
| 207 ui::NativeTheme::kColorId_TooltipBackground))); | |
| 208 | |
| 209 label_.SetAutoColorReadabilityEnabled(false); | |
| 210 label_.SetEnabledColor(native_theme->GetSystemColor( | |
| 211 ui::NativeTheme::kColorId_TooltipText)); | |
| 212 } | |
| 213 | |
| 214 void TooltipAura::Show() { | |
| 215 if (widget_) { | |
| 216 widget_->Show(); | |
| 217 widget_->StackAtTop(); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 void TooltipAura::Hide() { | |
| 222 tooltip_window_ = NULL; | |
| 223 if (widget_) | |
| 224 widget_->Hide(); | |
| 225 } | |
| 226 | |
| 227 bool TooltipAura::IsVisible() { | |
| 228 return widget_ && widget_->IsVisible(); | |
| 229 } | |
| 230 | |
| 231 void TooltipAura::OnWidgetDestroying(views::Widget* widget) { | |
| 232 DCHECK_EQ(widget_, widget); | |
| 233 widget_ = NULL; | |
| 234 tooltip_window_ = NULL; | |
| 235 } | |
| 236 | |
| 237 } // namespace corewm | |
| 238 } // namespace views | |
| OLD | NEW |