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 "ash/shelf/shelf_tooltip_manager.h"
7 #include "ash/shelf/shelf_layout_manager.h"
8 #include "ash/shelf/shelf_view.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/window_animations.h"
12 #include "base/bind.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/time/time.h"
15 #include "base/timer/timer.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_event_dispatcher.h"
18 #include "ui/events/event.h"
19 #include "ui/events/event_constants.h"
20 #include "ui/gfx/insets.h"
21 #include "ui/views/bubble/bubble_delegate.h"
22 #include "ui/views/bubble/bubble_frame_view.h"
23 #include "ui/views/controls/label.h"
24 #include "ui/views/layout/fill_layout.h"
25 #include "ui/views/widget/widget.h"
29 const int kTooltipTopBottomMargin
= 3;
30 const int kTooltipLeftRightMargin
= 10;
31 const int kTooltipAppearanceDelay
= 1000; // msec
32 const int kTooltipMinHeight
= 29 - 2 * kTooltipTopBottomMargin
;
33 const SkColor kTooltipTextColor
= SkColorSetRGB(0x22, 0x22, 0x22);
35 // The maximum width of the tooltip bubble. Borrowed the value from
36 // ash/tooltip/tooltip_controller.cc
37 const int kTooltipMaxWidth
= 250;
39 // The offset for the tooltip bubble - making sure that the bubble is flush
40 // with the shelf. The offset includes the arrow size in pixels as well as
41 // the activation bar and other spacing elements.
42 const int kArrowOffsetLeftRight
= 11;
43 const int kArrowOffsetTopBottom
= 7;
47 // The implementation of tooltip of the launcher.
48 class ShelfTooltipManager::ShelfTooltipBubble
49 : public views::BubbleDelegateView
{
51 ShelfTooltipBubble(views::View
* anchor
,
52 views::BubbleBorder::Arrow arrow
,
53 ShelfTooltipManager
* host
);
55 void SetText(const base::string16
& text
);
59 // views::WidgetDelegate overrides:
60 virtual void WindowClosing() OVERRIDE
;
62 // views::View overrides:
63 virtual gfx::Size
GetPreferredSize() const OVERRIDE
;
65 ShelfTooltipManager
* host_
;
68 DISALLOW_COPY_AND_ASSIGN(ShelfTooltipBubble
);
71 ShelfTooltipManager::ShelfTooltipBubble::ShelfTooltipBubble(
73 views::BubbleBorder::Arrow arrow
,
74 ShelfTooltipManager
* host
)
75 : views::BubbleDelegateView(anchor
, arrow
), host_(host
) {
76 gfx::Insets insets
= gfx::Insets(kArrowOffsetTopBottom
,
77 kArrowOffsetLeftRight
,
78 kArrowOffsetTopBottom
,
79 kArrowOffsetLeftRight
);
80 // Shelf items can have an asymmetrical border for spacing reasons.
81 // Adjust anchor location for this.
83 insets
+= anchor
->border()->GetInsets();
85 set_anchor_view_insets(insets
);
86 set_close_on_esc(false);
87 set_close_on_deactivate(false);
88 set_can_activate(false);
89 set_accept_events(false);
90 set_margins(gfx::Insets(kTooltipTopBottomMargin
, kTooltipLeftRightMargin
,
91 kTooltipTopBottomMargin
, kTooltipLeftRightMargin
));
92 set_shadow(views::BubbleBorder::SMALL_SHADOW
);
93 SetLayoutManager(new views::FillLayout());
94 // The anchor may not have the widget in tests.
95 if (anchor
->GetWidget() && anchor
->GetWidget()->GetNativeView()) {
96 aura::Window
* root_window
=
97 anchor
->GetWidget()->GetNativeView()->GetRootWindow();
98 set_parent_window(ash::Shell::GetInstance()->GetContainer(
99 root_window
, ash::kShellWindowId_SettingBubbleContainer
));
101 label_
= new views::Label
;
102 label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
103 label_
->SetEnabledColor(kTooltipTextColor
);
104 AddChildView(label_
);
105 views::BubbleDelegateView::CreateBubble(this);
108 void ShelfTooltipManager::ShelfTooltipBubble::SetText(
109 const base::string16
& text
) {
110 label_
->SetText(text
);
114 void ShelfTooltipManager::ShelfTooltipBubble::Close() {
117 GetWidget()->Close();
121 void ShelfTooltipManager::ShelfTooltipBubble::WindowClosing() {
122 views::BubbleDelegateView::WindowClosing();
124 host_
->OnBubbleClosed(this);
127 gfx::Size
ShelfTooltipManager::ShelfTooltipBubble::GetPreferredSize() const {
128 gfx::Size pref_size
= views::BubbleDelegateView::GetPreferredSize();
129 if (pref_size
.height() < kTooltipMinHeight
)
130 pref_size
.set_height(kTooltipMinHeight
);
131 if (pref_size
.width() > kTooltipMaxWidth
)
132 pref_size
.set_width(kTooltipMaxWidth
);
136 ShelfTooltipManager::ShelfTooltipManager(
137 ShelfLayoutManager
* shelf_layout_manager
,
138 ShelfView
* shelf_view
)
142 shelf_layout_manager_(shelf_layout_manager
),
143 shelf_view_(shelf_view
),
144 weak_factory_(this) {
145 if (shelf_layout_manager
)
146 shelf_layout_manager
->AddObserver(this);
147 if (Shell::HasInstance())
148 Shell::GetInstance()->AddPreTargetHandler(this);
151 ShelfTooltipManager::~ShelfTooltipManager() {
152 CancelHidingAnimation();
154 if (shelf_layout_manager_
)
155 shelf_layout_manager_
->RemoveObserver(this);
156 if (Shell::HasInstance())
157 Shell::GetInstance()->RemovePreTargetHandler(this);
160 void ShelfTooltipManager::ShowDelayed(views::View
* anchor
,
161 const base::string16
& text
) {
163 if (timer_
.get() && timer_
->IsRunning()) {
166 CancelHidingAnimation();
171 if (shelf_layout_manager_
&& !shelf_layout_manager_
->IsVisible())
174 CreateBubble(anchor
, text
);
178 void ShelfTooltipManager::ShowImmediately(views::View
* anchor
,
179 const base::string16
& text
) {
181 if (timer_
.get() && timer_
->IsRunning())
183 CancelHidingAnimation();
187 if (shelf_layout_manager_
&& !shelf_layout_manager_
->IsVisible())
190 CreateBubble(anchor
, text
);
194 void ShelfTooltipManager::Close() {
203 void ShelfTooltipManager::OnBubbleClosed(views::BubbleDelegateView
* view
) {
210 void ShelfTooltipManager::UpdateArrow() {
212 CancelHidingAnimation();
214 ShowImmediately(anchor_
, text_
);
218 void ShelfTooltipManager::ResetTimer() {
219 if (timer_
.get() && timer_
->IsRunning()) {
224 // We don't start the timer if the shelf isn't visible.
225 if (shelf_layout_manager_
&& !shelf_layout_manager_
->IsVisible())
228 CreateTimer(kTooltipAppearanceDelay
);
231 void ShelfTooltipManager::StopTimer() {
235 bool ShelfTooltipManager::IsVisible() {
236 if (timer_
.get() && timer_
->IsRunning())
239 return widget_
&& widget_
->IsVisible();
242 void ShelfTooltipManager::CreateZeroDelayTimerForTest() {
246 void ShelfTooltipManager::OnMouseEvent(ui::MouseEvent
* event
) {
248 DCHECK(event
->target());
249 if (!widget_
|| !widget_
->IsVisible())
255 // Pressing the mouse button anywhere should close the tooltip.
256 if (event
->type() == ui::ET_MOUSE_PRESSED
) {
261 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
262 if (widget_
->GetNativeWindow()->GetRootWindow() != target
->GetRootWindow()) {
267 gfx::Point location_in_shelf_view
= event
->location();
268 aura::Window::ConvertPointToTarget(
269 target
, shelf_view_
->GetWidget()->GetNativeWindow(),
270 &location_in_shelf_view
);
272 if (shelf_view_
->ShouldHideTooltip(location_in_shelf_view
)) {
273 // Because this mouse event may arrive to |view_|, here we just schedule
274 // the closing event rather than directly calling Close().
279 void ShelfTooltipManager::OnTouchEvent(ui::TouchEvent
* event
) {
280 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
281 if (widget_
&& widget_
->IsVisible() && widget_
->GetNativeWindow() != target
)
285 void ShelfTooltipManager::OnGestureEvent(ui::GestureEvent
* event
) {
286 if (widget_
&& widget_
->IsVisible()) {
287 // Because this mouse event may arrive to |view_|, here we just schedule
288 // the closing event rather than directly calling Close().
293 void ShelfTooltipManager::OnCancelMode(ui::CancelModeEvent
* event
) {
297 void ShelfTooltipManager::WillDeleteShelf() {
298 shelf_layout_manager_
= NULL
;
301 void ShelfTooltipManager::WillChangeVisibilityState(
302 ShelfVisibilityState new_state
) {
303 if (new_state
== SHELF_HIDDEN
) {
309 void ShelfTooltipManager::OnAutoHideStateChanged(ShelfAutoHideState new_state
) {
310 if (new_state
== SHELF_AUTO_HIDE_HIDDEN
) {
312 // AutoHide state change happens during an event filter, so immediate close
313 // may cause a crash in the HandleMouseEvent() after the filter. So we just
314 // schedule the Close here.
319 void ShelfTooltipManager::CancelHidingAnimation() {
320 if (!widget_
|| !widget_
->GetNativeView())
323 gfx::NativeView native_view
= widget_
->GetNativeView();
324 wm::SetWindowVisibilityAnimationTransition(
325 native_view
, wm::ANIMATE_NONE
);
328 void ShelfTooltipManager::CloseSoon() {
329 base::MessageLoopForUI::current()->PostTask(
331 base::Bind(&ShelfTooltipManager::Close
, weak_factory_
.GetWeakPtr()));
334 void ShelfTooltipManager::ShowInternal() {
336 view_
->GetWidget()->Show();
341 void ShelfTooltipManager::CreateBubble(views::View
* anchor
,
342 const base::string16
& text
) {
347 views::BubbleBorder::Arrow arrow
=
348 shelf_layout_manager_
->SelectValueForShelfAlignment(
349 views::BubbleBorder::BOTTOM_CENTER
,
350 views::BubbleBorder::LEFT_CENTER
,
351 views::BubbleBorder::RIGHT_CENTER
,
352 views::BubbleBorder::TOP_CENTER
);
354 view_
= new ShelfTooltipBubble(anchor
, arrow
, this);
355 widget_
= view_
->GetWidget();
356 view_
->SetText(text_
);
358 gfx::NativeView native_view
= widget_
->GetNativeView();
359 wm::SetWindowVisibilityAnimationType(
360 native_view
, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL
);
361 wm::SetWindowVisibilityAnimationTransition(
362 native_view
, wm::ANIMATE_HIDE
);
365 void ShelfTooltipManager::CreateTimer(int delay_in_ms
) {
366 base::OneShotTimer
<ShelfTooltipManager
>* new_timer
=
367 new base::OneShotTimer
<ShelfTooltipManager
>();
368 new_timer
->Start(FROM_HERE
,
369 base::TimeDelta::FromMilliseconds(delay_in_ms
),
371 &ShelfTooltipManager::ShowInternal
);
372 timer_
.reset(new_timer
);