- add sources.
[platform/framework/web/crosswalk.git] / src / ash / shelf / shelf_tooltip_manager.cc
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 "ash/shelf/shelf_tooltip_manager.h"
6
7 #include "ash/shelf/shelf_layout_manager.h"
8 #include "ash/shelf/shelf_view.h"
9 #include "ash/shell.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"
26
27 namespace ash {
28 namespace internal {
29 namespace {
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);
35
36 // The maximum width of the tooltip bubble.  Borrowed the value from
37 // ash/tooltip/tooltip_controller.cc
38 const int kTooltipMaxWidth = 250;
39
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;
45
46 }  // namespace
47
48 // The implementation of tooltip of the launcher.
49 class ShelfTooltipManager::ShelfTooltipBubble
50     : public views::BubbleDelegateView {
51  public:
52   ShelfTooltipBubble(views::View* anchor,
53                         views::BubbleBorder::Arrow arrow,
54                         ShelfTooltipManager* host);
55
56   void SetText(const base::string16& text);
57   void Close();
58
59  private:
60   // views::WidgetDelegate overrides:
61   virtual void WindowClosing() OVERRIDE;
62
63   // views::View overrides:
64   virtual gfx::Size GetPreferredSize() OVERRIDE;
65
66   ShelfTooltipManager* host_;
67   views::Label* label_;
68
69   DISALLOW_COPY_AND_ASSIGN(ShelfTooltipBubble);
70 };
71
72 ShelfTooltipManager::ShelfTooltipBubble::ShelfTooltipBubble(
73     views::View* anchor,
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.
85   if (anchor->border())
86     insets += anchor->border()->GetInsets();
87
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));
103   }
104   label_ = new views::Label;
105   label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
106   label_->SetEnabledColor(kTooltipTextColor);
107   AddChildView(label_);
108   views::BubbleDelegateView::CreateBubble(this);
109 }
110
111 void ShelfTooltipManager::ShelfTooltipBubble::SetText(
112     const base::string16& text) {
113   label_->SetText(text);
114   SizeToContents();
115 }
116
117 void ShelfTooltipManager::ShelfTooltipBubble::Close() {
118   if (GetWidget()) {
119     host_ = NULL;
120     GetWidget()->Close();
121   }
122 }
123
124 void ShelfTooltipManager::ShelfTooltipBubble::WindowClosing() {
125   views::BubbleDelegateView::WindowClosing();
126   if (host_)
127     host_->OnBubbleClosed(this);
128 }
129
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);
136   return pref_size;
137 }
138
139 ShelfTooltipManager::ShelfTooltipManager(
140     ShelfLayoutManager* shelf_layout_manager,
141     ShelfView* shelf_view)
142     : view_(NULL),
143       widget_(NULL),
144       anchor_(NULL),
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);
152 }
153
154 ShelfTooltipManager::~ShelfTooltipManager() {
155   CancelHidingAnimation();
156   Close();
157   if (shelf_layout_manager_)
158     shelf_layout_manager_->RemoveObserver(this);
159   if (Shell::HasInstance())
160     Shell::GetInstance()->RemovePreTargetHandler(this);
161 }
162
163 void ShelfTooltipManager::ShowDelayed(views::View* anchor,
164                                       const base::string16& text) {
165   if (view_) {
166     if (timer_.get() && timer_->IsRunning()) {
167       return;
168     } else {
169       CancelHidingAnimation();
170       Close();
171     }
172   }
173
174   if (shelf_layout_manager_ && !shelf_layout_manager_->IsVisible())
175     return;
176
177   CreateBubble(anchor, text);
178   ResetTimer();
179 }
180
181 void ShelfTooltipManager::ShowImmediately(views::View* anchor,
182                                           const base::string16& text) {
183   if (view_) {
184     if (timer_.get() && timer_->IsRunning())
185       StopTimer();
186     CancelHidingAnimation();
187     Close();
188   }
189
190   if (shelf_layout_manager_ && !shelf_layout_manager_->IsVisible())
191     return;
192
193   CreateBubble(anchor, text);
194   ShowInternal();
195 }
196
197 void ShelfTooltipManager::Close() {
198   StopTimer();
199   if (view_) {
200     view_->Close();
201     view_ = NULL;
202     widget_ = NULL;
203   }
204 }
205
206 void ShelfTooltipManager::OnBubbleClosed(views::BubbleDelegateView* view) {
207   if (view == view_) {
208     view_ = NULL;
209     widget_ = NULL;
210   }
211 }
212
213 void ShelfTooltipManager::UpdateArrow() {
214   if (view_) {
215     CancelHidingAnimation();
216     Close();
217     ShowImmediately(anchor_, text_);
218   }
219 }
220
221 void ShelfTooltipManager::ResetTimer() {
222   if (timer_.get() && timer_->IsRunning()) {
223     timer_->Reset();
224     return;
225   }
226
227   // We don't start the timer if the shelf isn't visible.
228   if (shelf_layout_manager_ && !shelf_layout_manager_->IsVisible())
229     return;
230
231   CreateTimer(kTooltipAppearanceDelay);
232 }
233
234 void ShelfTooltipManager::StopTimer() {
235   timer_.reset();
236 }
237
238 bool ShelfTooltipManager::IsVisible() {
239   if (timer_.get() && timer_->IsRunning())
240     return false;
241
242   return widget_ && widget_->IsVisible();
243 }
244
245 void ShelfTooltipManager::CreateZeroDelayTimerForTest() {
246   CreateTimer(0);
247 }
248
249 void ShelfTooltipManager::OnMouseEvent(ui::MouseEvent* event) {
250   DCHECK(event);
251   DCHECK(event->target());
252   if (!widget_ || !widget_->IsVisible())
253     return;
254
255   DCHECK(view_);
256   DCHECK(shelf_view_);
257
258   // Pressing the mouse button anywhere should close the tooltip.
259   if (event->type() == ui::ET_MOUSE_PRESSED) {
260     CloseSoon();
261     return;
262   }
263
264   aura::Window* target = static_cast<aura::Window*>(event->target());
265   if (widget_->GetNativeWindow()->GetRootWindow() != target->GetRootWindow()) {
266     CloseSoon();
267     return;
268   }
269
270   gfx::Point location_in_shelf_view = event->location();
271   aura::Window::ConvertPointToTarget(
272       target, shelf_view_->GetWidget()->GetNativeWindow(),
273       &location_in_shelf_view);
274
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().
278     CloseSoon();
279   }
280 }
281
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)
285     Close();
286 }
287
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().
292     CloseSoon();
293   }
294 }
295
296 void ShelfTooltipManager::OnCancelMode(ui::CancelModeEvent* event) {
297   Close();
298 }
299
300 void ShelfTooltipManager::WillDeleteShelf() {
301   shelf_layout_manager_ = NULL;
302 }
303
304 void ShelfTooltipManager::WillChangeVisibilityState(
305     ShelfVisibilityState new_state) {
306   if (new_state == SHELF_HIDDEN) {
307     StopTimer();
308     Close();
309   }
310 }
311
312 void ShelfTooltipManager::OnAutoHideStateChanged(ShelfAutoHideState new_state) {
313   if (new_state == SHELF_AUTO_HIDE_HIDDEN) {
314     StopTimer();
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.
318     CloseSoon();
319   }
320 }
321
322 void ShelfTooltipManager::CancelHidingAnimation() {
323   if (!widget_ || !widget_->GetNativeView())
324     return;
325
326   gfx::NativeView native_view = widget_->GetNativeView();
327   views::corewm::SetWindowVisibilityAnimationTransition(
328       native_view, views::corewm::ANIMATE_NONE);
329 }
330
331 void ShelfTooltipManager::CloseSoon() {
332   base::MessageLoopForUI::current()->PostTask(
333       FROM_HERE,
334       base::Bind(&ShelfTooltipManager::Close, weak_factory_.GetWeakPtr()));
335 }
336
337 void ShelfTooltipManager::ShowInternal() {
338   if (view_)
339     view_->GetWidget()->Show();
340
341   timer_.reset();
342 }
343
344 void ShelfTooltipManager::CreateBubble(views::View* anchor,
345                                        const base::string16& text) {
346   DCHECK(!view_);
347
348   anchor_ = anchor;
349   text_ = 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);
356
357   view_ = new ShelfTooltipBubble(anchor, arrow, this);
358   widget_ = view_->GetWidget();
359   view_->SetText(text_);
360
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);
366 }
367
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),
373                    this,
374                    &ShelfTooltipManager::ShowInternal);
375   timer_.reset(new_timer);
376 }
377
378 }  // namespace internal
379 }  // namespace ash