1// Copyright (c) 2012 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/wm/gestures/tray_gesture_handler.h"
6
7#include "ash/shell.h"
8#include "ash/system/tray/system_tray.h"
9#include "ash/system/tray/system_tray_bubble.h"
10#include "ui/aura/window.h"
11#include "ui/compositor/layer.h"
12#include "ui/events/event.h"
13#include "ui/gfx/transform.h"
14#include "ui/views/widget/widget.h"
15
16const int kMinBubbleHeight = 13;
17
18namespace ash {
19
20TrayGestureHandler::TrayGestureHandler()
21    : widget_(NULL),
22      gesture_drag_amount_(0) {
23  // TODO(oshima): Support multiple display case.
24  SystemTray* tray = Shell::GetInstance()->GetPrimarySystemTray();
25  tray->ShowDefaultView(BUBBLE_CREATE_NEW);
26  SystemTrayBubble* bubble = tray->GetSystemBubble();
27  if (!bubble)
28    return;
29  bubble->bubble_view()->set_gesture_dragging(true);
30  widget_ = bubble->bubble_view()->GetWidget();
31  widget_->AddObserver(this);
32
33  gfx::Rect bounds = widget_->GetWindowBoundsInScreen();
34  int height_change = bounds.height() - kMinBubbleHeight;
35  bounds.set_height(kMinBubbleHeight);
36  bounds.set_y(bounds.y() + height_change);
37  widget_->SetBounds(bounds);
38}
39
40TrayGestureHandler::~TrayGestureHandler() {
41  if (widget_)
42    widget_->RemoveObserver(this);
43}
44
45bool TrayGestureHandler::UpdateGestureDrag(const ui::GestureEvent& event) {
46  CHECK_EQ(ui::ET_GESTURE_SCROLL_UPDATE, event.type());
47  if (!widget_)
48    return false;
49
50  gesture_drag_amount_ += event.details().scroll_y();
51  if (gesture_drag_amount_ > 0 && gesture_drag_amount_ < kMinBubbleHeight) {
52    widget_->Close();
53    return false;
54  }
55
56  gfx::Rect bounds = widget_->GetWindowBoundsInScreen();
57  int new_height = std::min(
58      kMinBubbleHeight + std::max(0, static_cast<int>(-gesture_drag_amount_)),
59      widget_->GetContentsView()->GetPreferredSize().height());
60  int height_change = bounds.height() - new_height;
61  bounds.set_height(new_height);
62  bounds.set_y(bounds.y() + height_change);
63  widget_->SetBounds(bounds);
64  return true;
65}
66
67void TrayGestureHandler::CompleteGestureDrag(const ui::GestureEvent& event) {
68  if (!widget_)
69    return;
70
71  widget_->RemoveObserver(this);
72
73  // Close the widget if it hasn't been dragged enough.
74  bool should_close = false;
75  int height = widget_->GetWindowBoundsInScreen().height();
76  int preferred_height =
77      widget_->GetContentsView()->GetPreferredSize().height();
78  if (event.type() == ui::ET_GESTURE_SCROLL_END) {
79    const float kMinThresholdGestureDrag = 0.4f;
80    if (height < preferred_height * kMinThresholdGestureDrag)
81      should_close = true;
82  } else if (event.type() == ui::ET_SCROLL_FLING_START) {
83    const float kMinThresholdGestureDragExposeFling = 0.25f;
84    const float kMinThresholdGestureFling = 1000.f;
85    if (height < preferred_height * kMinThresholdGestureDragExposeFling &&
86        event.details().velocity_y() > -kMinThresholdGestureFling)
87      should_close = true;
88  } else {
89    NOTREACHED();
90  }
91
92  if (should_close) {
93    widget_->Close();
94  } else {
95    SystemTrayBubble* bubble =
96        Shell::GetInstance()->GetPrimarySystemTray()->GetSystemBubble();
97    if (bubble)
98      bubble->bubble_view()->set_gesture_dragging(false);
99  }
100}
101
102void TrayGestureHandler::OnWidgetDestroying(views::Widget* widget) {
103  CHECK_EQ(widget_, widget);
104  widget_ = NULL;
105}
106
107}  // namespace ash
108