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/root_window.h"
17 #include "ui/aura/window.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"
30 const int kTooltipTopBottomMargin = 3;
31 const int kTooltipLeftRightMargin = 10;
32 const int kTooltipAppearanceDelay = 200; // msec
33 const int kTooltipMinHeight = 29 - 2 * kTooltipTopBottomMargin;
34 const SkColor kTooltipTextColor = SkColorSetRGB(0x22, 0x22, 0x22);
36 // The maximum width of the tooltip bubble. Borrowed the value from
37 // ash/tooltip/tooltip_controller.cc
38 const int kTooltipMaxWidth = 250;
40 // The offset for the tooltip bubble - making sure that the bubble is flush
41 // with the shelf. The offset includes the arrow size in pixels as well as
42 // the activation bar and other spacing elements.
43 const int kArrowOffsetLeftRight = 11;
44 const int kArrowOffsetTopBottom = 7;
48 // The implementation of tooltip of the launcher.
49 class ShelfTooltipManager::ShelfTooltipBubble
50 : public views::BubbleDelegateView {
52 ShelfTooltipBubble(views::View* anchor,
53 views::BubbleBorder::Arrow arrow,
54 ShelfTooltipManager* host);
56 void SetText(const base::string16& text);
60 // views::WidgetDelegate overrides:
61 virtual void WindowClosing() OVERRIDE;
63 // views::View overrides:
64 virtual gfx::Size GetPreferredSize() OVERRIDE;
66 ShelfTooltipManager* host_;
69 DISALLOW_COPY_AND_ASSIGN(ShelfTooltipBubble);
72 ShelfTooltipManager::ShelfTooltipBubble::ShelfTooltipBubble(
74 views::BubbleBorder::Arrow arrow,
75 ShelfTooltipManager* host)
76 : views::BubbleDelegateView(anchor, arrow), host_(host) {
77 // Make sure that the bubble follows the animation of the shelf.
78 set_move_with_anchor(true);
79 gfx::Insets insets = gfx::Insets(kArrowOffsetTopBottom,
80 kArrowOffsetLeftRight,
81 kArrowOffsetTopBottom,
82 kArrowOffsetLeftRight);
83 // Launcher items can have an asymmetrical border for spacing reasons.
84 // Adjust anchor location for this.
86 insets += anchor->border()->GetInsets();
88 set_anchor_view_insets(insets);
89 set_close_on_esc(false);
90 set_close_on_deactivate(false);
91 set_use_focusless(true);
92 set_accept_events(false);
93 set_margins(gfx::Insets(kTooltipTopBottomMargin, kTooltipLeftRightMargin,
94 kTooltipTopBottomMargin, kTooltipLeftRightMargin));
95 set_shadow(views::BubbleBorder::SMALL_SHADOW);
96 SetLayoutManager(new views::FillLayout());
97 // The anchor may not have the widget in tests.
98 if (anchor->GetWidget() && anchor->GetWidget()->GetNativeView()) {
99 aura::Window* root_window =
100 anchor->GetWidget()->GetNativeView()->GetRootWindow();
101 set_parent_window(ash::Shell::GetInstance()->GetContainer(
102 root_window, ash::internal::kShellWindowId_SettingBubbleContainer));
104 label_ = new views::Label;
105 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
106 label_->SetEnabledColor(kTooltipTextColor);
107 AddChildView(label_);
108 views::BubbleDelegateView::CreateBubble(this);
111 void ShelfTooltipManager::ShelfTooltipBubble::SetText(
112 const base::string16& text) {
113 label_->SetText(text);
117 void ShelfTooltipManager::ShelfTooltipBubble::Close() {
120 GetWidget()->Close();
124 void ShelfTooltipManager::ShelfTooltipBubble::WindowClosing() {
125 views::BubbleDelegateView::WindowClosing();
127 host_->OnBubbleClosed(this);
130 gfx::Size ShelfTooltipManager::ShelfTooltipBubble::GetPreferredSize() {
131 gfx::Size pref_size = views::BubbleDelegateView::GetPreferredSize();
132 if (pref_size.height() < kTooltipMinHeight)
133 pref_size.set_height(kTooltipMinHeight);
134 if (pref_size.width() > kTooltipMaxWidth)
135 pref_size.set_width(kTooltipMaxWidth);
139 ShelfTooltipManager::ShelfTooltipManager(
140 ShelfLayoutManager* shelf_layout_manager,
141 ShelfView* shelf_view)
145 shelf_layout_manager_(shelf_layout_manager),
146 shelf_view_(shelf_view),
147 weak_factory_(this) {
148 if (shelf_layout_manager)
149 shelf_layout_manager->AddObserver(this);
150 if (Shell::HasInstance())
151 Shell::GetInstance()->AddPreTargetHandler(this);
154 ShelfTooltipManager::~ShelfTooltipManager() {
155 CancelHidingAnimation();
157 if (shelf_layout_manager_)
158 shelf_layout_manager_->RemoveObserver(this);
159 if (Shell::HasInstance())
160 Shell::GetInstance()->RemovePreTargetHandler(this);
163 void ShelfTooltipManager::ShowDelayed(views::View* anchor,
164 const base::string16& text) {
166 if (timer_.get() && timer_->IsRunning()) {
169 CancelHidingAnimation();
174 if (shelf_layout_manager_ && !shelf_layout_manager_->IsVisible())
177 CreateBubble(anchor, text);
181 void ShelfTooltipManager::ShowImmediately(views::View* anchor,
182 const base::string16& text) {
184 if (timer_.get() && timer_->IsRunning())
186 CancelHidingAnimation();
190 if (shelf_layout_manager_ && !shelf_layout_manager_->IsVisible())
193 CreateBubble(anchor, text);
197 void ShelfTooltipManager::Close() {
206 void ShelfTooltipManager::OnBubbleClosed(views::BubbleDelegateView* view) {
213 void ShelfTooltipManager::UpdateArrow() {
215 CancelHidingAnimation();
217 ShowImmediately(anchor_, text_);
221 void ShelfTooltipManager::ResetTimer() {
222 if (timer_.get() && timer_->IsRunning()) {
227 // We don't start the timer if the shelf isn't visible.
228 if (shelf_layout_manager_ && !shelf_layout_manager_->IsVisible())
231 CreateTimer(kTooltipAppearanceDelay);
234 void ShelfTooltipManager::StopTimer() {
238 bool ShelfTooltipManager::IsVisible() {
239 if (timer_.get() && timer_->IsRunning())
242 return widget_ && widget_->IsVisible();
245 void ShelfTooltipManager::CreateZeroDelayTimerForTest() {
249 void ShelfTooltipManager::OnMouseEvent(ui::MouseEvent* event) {
251 DCHECK(event->target());
252 if (!widget_ || !widget_->IsVisible())
258 // Pressing the mouse button anywhere should close the tooltip.
259 if (event->type() == ui::ET_MOUSE_PRESSED) {
264 aura::Window* target = static_cast<aura::Window*>(event->target());
265 if (widget_->GetNativeWindow()->GetRootWindow() != target->GetRootWindow()) {
270 gfx::Point location_in_shelf_view = event->location();
271 aura::Window::ConvertPointToTarget(
272 target, shelf_view_->GetWidget()->GetNativeWindow(),
273 &location_in_shelf_view);
275 if (shelf_view_->ShouldHideTooltip(location_in_shelf_view)) {
276 // Because this mouse event may arrive to |view_|, here we just schedule
277 // the closing event rather than directly calling Close().
282 void ShelfTooltipManager::OnTouchEvent(ui::TouchEvent* event) {
283 aura::Window* target = static_cast<aura::Window*>(event->target());
284 if (widget_ && widget_->IsVisible() && widget_->GetNativeWindow() != target)
288 void ShelfTooltipManager::OnGestureEvent(ui::GestureEvent* event) {
289 if (widget_ && widget_->IsVisible()) {
290 // Because this mouse event may arrive to |view_|, here we just schedule
291 // the closing event rather than directly calling Close().
296 void ShelfTooltipManager::OnCancelMode(ui::CancelModeEvent* event) {
300 void ShelfTooltipManager::WillDeleteShelf() {
301 shelf_layout_manager_ = NULL;
304 void ShelfTooltipManager::WillChangeVisibilityState(
305 ShelfVisibilityState new_state) {
306 if (new_state == SHELF_HIDDEN) {
312 void ShelfTooltipManager::OnAutoHideStateChanged(ShelfAutoHideState new_state) {
313 if (new_state == SHELF_AUTO_HIDE_HIDDEN) {
315 // AutoHide state change happens during an event filter, so immediate close
316 // may cause a crash in the HandleMouseEvent() after the filter. So we just
317 // schedule the Close here.
322 void ShelfTooltipManager::CancelHidingAnimation() {
323 if (!widget_ || !widget_->GetNativeView())
326 gfx::NativeView native_view = widget_->GetNativeView();
327 views::corewm::SetWindowVisibilityAnimationTransition(
328 native_view, views::corewm::ANIMATE_NONE);
331 void ShelfTooltipManager::CloseSoon() {
332 base::MessageLoopForUI::current()->PostTask(
334 base::Bind(&ShelfTooltipManager::Close, weak_factory_.GetWeakPtr()));
337 void ShelfTooltipManager::ShowInternal() {
339 view_->GetWidget()->Show();
344 void ShelfTooltipManager::CreateBubble(views::View* anchor,
345 const base::string16& text) {
350 views::BubbleBorder::Arrow arrow =
351 shelf_layout_manager_->SelectValueForShelfAlignment(
352 views::BubbleBorder::BOTTOM_CENTER,
353 views::BubbleBorder::LEFT_CENTER,
354 views::BubbleBorder::RIGHT_CENTER,
355 views::BubbleBorder::TOP_CENTER);
357 view_ = new ShelfTooltipBubble(anchor, arrow, this);
358 widget_ = view_->GetWidget();
359 view_->SetText(text_);
361 gfx::NativeView native_view = widget_->GetNativeView();
362 views::corewm::SetWindowVisibilityAnimationType(
363 native_view, views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
364 views::corewm::SetWindowVisibilityAnimationTransition(
365 native_view, views::corewm::ANIMATE_HIDE);
368 void ShelfTooltipManager::CreateTimer(int delay_in_ms) {
369 base::OneShotTimer<ShelfTooltipManager>* new_timer =
370 new base::OneShotTimer<ShelfTooltipManager>();
371 new_timer->Start(FROM_HERE,
372 base::TimeDelta::FromMilliseconds(delay_in_ms),
374 &ShelfTooltipManager::ShowInternal);
375 timer_.reset(new_timer);
378 } // namespace internal