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.
5 #include "ui/views/corewm/tooltip_aura.h"
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "ui/aura/window.h"
10 #include "ui/aura/window_tree_host.h"
11 #include "ui/gfx/canvas.h"
12 #include "ui/gfx/render_text.h"
13 #include "ui/gfx/screen.h"
14 #include "ui/gfx/text_elider.h"
15 #include "ui/gfx/text_utils.h"
16 #include "ui/native_theme/native_theme.h"
17 #include "ui/views/background.h"
18 #include "ui/views/border.h"
19 #include "ui/views/view.h"
20 #include "ui/views/widget/widget.h"
24 // Max visual tooltip width. If a tooltip is greater than this width, it will
26 const int kTooltipMaxWidthPixels
= 400;
28 // FIXME: get cursor offset from actual cursor size.
29 const int kCursorOffsetX
= 10;
30 const int kCursorOffsetY
= 15;
32 // Creates a widget of type TYPE_TOOLTIP
33 views::Widget
* CreateTooltipWidget(aura::Window
* tooltip_window
) {
34 views::Widget
* widget
= new views::Widget
;
35 views::Widget::InitParams params
;
36 // For aura, since we set the type to TYPE_TOOLTIP, the widget will get
37 // auto-parented to the right container.
38 params
.type
= views::Widget::InitParams::TYPE_TOOLTIP
;
39 params
.context
= tooltip_window
;
40 DCHECK(params
.context
);
41 params
.keep_on_top
= true;
42 params
.accept_events
= false;
52 // TODO(oshima): Consider to use views::Label.
53 class TooltipAura::TooltipView
: public views::View
{
56 : render_text_(gfx::RenderText::CreateInstance()),
58 const int kHorizontalPadding
= 3;
59 const int kVerticalPadding
= 2;
60 SetBorder(Border::CreateEmptyBorder(
61 kVerticalPadding
, kHorizontalPadding
,
62 kVerticalPadding
, kHorizontalPadding
));
64 set_owned_by_client();
65 render_text_
->SetWordWrapBehavior(gfx::WRAP_LONG_WORDS
);
66 render_text_
->SetMultiline(true);
71 ~TooltipView() override
{}
74 void OnPaint(gfx::Canvas
* canvas
) override
{
75 OnPaintBackground(canvas
);
76 gfx::Size text_size
= size();
77 gfx::Insets insets
= border()->GetInsets();
78 text_size
.Enlarge(-insets
.width(), -insets
.height());
79 render_text_
->SetDisplayRect(gfx::Rect(text_size
));
81 canvas
->Translate(gfx::Vector2d(insets
.left(), insets
.top()));
82 render_text_
->Draw(canvas
);
84 OnPaintBorder(canvas
);
87 gfx::Size
GetPreferredSize() const override
{
88 gfx::Size view_size
= render_text_
->GetStringSize();
89 gfx::Insets insets
= border()->GetInsets();
90 view_size
.Enlarge(insets
.width(), insets
.height());
94 const char* GetClassName() const override
{
98 void SetText(const base::string16
& text
) {
99 render_text_
->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD
);
100 render_text_
->SetText(text
);
104 void SetForegroundColor(SkColor color
) {
105 render_text_
->SetColor(color
);
108 void SetMaxWidth(int width
) {
114 void ResetDisplayRect() {
115 gfx::Insets insets
= border()->GetInsets();
116 int max_text_width
= max_width_
- insets
.width();
117 render_text_
->SetDisplayRect(gfx::Rect(0, 0, max_text_width
, 100000));
120 scoped_ptr
<gfx::RenderText
> render_text_
;
123 DISALLOW_COPY_AND_ASSIGN(TooltipView
);
126 TooltipAura::TooltipAura()
127 : tooltip_view_(new TooltipView
),
129 tooltip_window_(NULL
) {
132 TooltipAura::~TooltipAura() {
136 void TooltipAura::SetTooltipBounds(const gfx::Point
& mouse_pos
,
137 const gfx::Size
& tooltip_size
) {
138 gfx::Rect
tooltip_rect(mouse_pos
, tooltip_size
);
139 tooltip_rect
.Offset(kCursorOffsetX
, kCursorOffsetY
);
140 gfx::Screen
* screen
= gfx::Screen::GetScreenFor(tooltip_window_
);
141 gfx::Rect
display_bounds(screen
->GetDisplayNearestPoint(mouse_pos
).bounds());
143 // If tooltip is out of bounds on the x axis, we simply shift it
144 // horizontally by the offset.
145 if (tooltip_rect
.right() > display_bounds
.right()) {
146 int h_offset
= tooltip_rect
.right() - display_bounds
.right();
147 tooltip_rect
.Offset(-h_offset
, 0);
150 // If tooltip is out of bounds on the y axis, we flip it to appear above the
151 // mouse cursor instead of below.
152 if (tooltip_rect
.bottom() > display_bounds
.bottom())
153 tooltip_rect
.set_y(mouse_pos
.y() - tooltip_size
.height());
155 tooltip_rect
.AdjustToFit(display_bounds
);
156 widget_
->SetBounds(tooltip_rect
);
159 void TooltipAura::DestroyWidget() {
161 widget_
->RemoveObserver(this);
167 int TooltipAura::GetMaxWidth(const gfx::Point
& location
,
168 aura::Window
* context
) const {
169 gfx::Screen
* screen
= gfx::Screen::GetScreenFor(context
);
170 gfx::Rect
display_bounds(screen
->GetDisplayNearestPoint(location
).bounds());
171 return std::min(kTooltipMaxWidthPixels
, (display_bounds
.width() + 1) / 2);
174 void TooltipAura::SetText(aura::Window
* window
,
175 const base::string16
& tooltip_text
,
176 const gfx::Point
& location
) {
177 tooltip_window_
= window
;
178 tooltip_view_
->SetMaxWidth(GetMaxWidth(location
, window
));
179 tooltip_view_
->SetText(tooltip_text
);
182 widget_
= CreateTooltipWidget(tooltip_window_
);
183 widget_
->SetContentsView(tooltip_view_
.get());
184 widget_
->AddObserver(this);
187 SetTooltipBounds(location
, tooltip_view_
->GetPreferredSize());
189 ui::NativeTheme
* native_theme
= widget_
->GetNativeTheme();
190 tooltip_view_
->set_background(
191 views::Background::CreateSolidBackground(
192 native_theme
->GetSystemColor(
193 ui::NativeTheme::kColorId_TooltipBackground
)));
194 tooltip_view_
->SetForegroundColor(native_theme
->GetSystemColor(
195 ui::NativeTheme::kColorId_TooltipText
));
198 void TooltipAura::Show() {
201 widget_
->StackAtTop();
205 void TooltipAura::Hide() {
206 tooltip_window_
= NULL
;
211 bool TooltipAura::IsVisible() {
212 return widget_
&& widget_
->IsVisible();
215 void TooltipAura::OnWidgetDestroying(views::Widget
* widget
) {
216 DCHECK_EQ(widget_
, widget
);
218 tooltip_window_
= NULL
;
221 } // namespace corewm