Enable gaia services for enterprise autotests.
[chromium-blink-merge.git] / ash / frame / caption_buttons / frame_size_button.cc
blob05579bd13cf219b90d6b574cb7e13ec1fe1e040c
1 // Copyright 2014 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/frame/caption_buttons/frame_size_button.h"
7 #include "ash/metrics/user_metrics_recorder.h"
8 #include "ash/screen_util.h"
9 #include "ash/shell.h"
10 #include "ash/touch/touch_uma.h"
11 #include "ash/wm/window_state.h"
12 #include "ash/wm/window_util.h"
13 #include "ash/wm/wm_event.h"
14 #include "ash/wm/workspace/phantom_window_controller.h"
15 #include "base/i18n/rtl.h"
16 #include "ui/gfx/vector2d.h"
17 #include "ui/views/widget/widget.h"
19 namespace {
21 // The default delay between the user pressing the size button and the buttons
22 // adjacent to the size button morphing into buttons for snapping left and
23 // right.
24 const int kSetButtonsToSnapModeDelayMs = 150;
26 // The amount that a user can overshoot one of the caption buttons while in
27 // "snap mode" and keep the button hovered/pressed.
28 const int kMaxOvershootX = 200;
29 const int kMaxOvershootY = 50;
31 // Returns true if a mouse drag while in "snap mode" at |location_in_screen|
32 // would hover/press |button| or keep it hovered/pressed.
33 bool HitTestButton(const ash::FrameCaptionButton* button,
34 const gfx::Point& location_in_screen) {
35 gfx::Rect expanded_bounds_in_screen = button->GetBoundsInScreen();
36 if (button->state() == views::Button::STATE_HOVERED ||
37 button->state() == views::Button::STATE_PRESSED) {
38 expanded_bounds_in_screen.Inset(-kMaxOvershootX, -kMaxOvershootY);
40 return expanded_bounds_in_screen.Contains(location_in_screen);
43 } // namespace
45 namespace ash {
47 FrameSizeButton::FrameSizeButton(
48 views::ButtonListener* listener,
49 views::Widget* frame,
50 FrameSizeButtonDelegate* delegate)
51 : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
52 frame_(frame),
53 delegate_(delegate),
54 set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs),
55 in_snap_mode_(false),
56 snap_type_(SNAP_NONE) {
59 FrameSizeButton::~FrameSizeButton() {
62 bool FrameSizeButton::OnMousePressed(const ui::MouseEvent& event) {
63 // The minimize and close buttons are set to snap left and right when snapping
64 // is enabled. Do not enable snapping if the minimize button is not visible.
65 // The close button is always visible.
66 if (IsTriggerableEvent(event) &&
67 !in_snap_mode_ &&
68 delegate_->IsMinimizeButtonVisible()) {
69 StartSetButtonsToSnapModeTimer(event);
71 FrameCaptionButton::OnMousePressed(event);
72 return true;
75 bool FrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) {
76 UpdateSnapType(event);
77 // By default a FrameCaptionButton reverts to STATE_NORMAL once the mouse
78 // leaves its bounds. Skip FrameCaptionButton's handling when
79 // |in_snap_mode_| == true because we want different behavior.
80 if (!in_snap_mode_)
81 FrameCaptionButton::OnMouseDragged(event);
82 return true;
85 void FrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) {
86 if (!IsTriggerableEvent(event) || !CommitSnap(event))
87 FrameCaptionButton::OnMouseReleased(event);
90 void FrameSizeButton::OnMouseCaptureLost() {
91 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
92 FrameCaptionButton::OnMouseCaptureLost();
95 void FrameSizeButton::OnMouseMoved(const ui::MouseEvent& event) {
96 // Ignore any synthetic mouse moves during a drag.
97 if (!in_snap_mode_)
98 FrameCaptionButton::OnMouseMoved(event);
101 void FrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
102 if (event->details().touch_points() > 1) {
103 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
104 return;
107 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
108 StartSetButtonsToSnapModeTimer(*event);
109 // Go through FrameCaptionButton's handling so that the button gets pressed.
110 FrameCaptionButton::OnGestureEvent(event);
111 return;
114 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
115 event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
116 UpdateSnapType(*event);
117 event->SetHandled();
118 return;
121 if (event->type() == ui::ET_GESTURE_TAP ||
122 event->type() == ui::ET_GESTURE_SCROLL_END ||
123 event->type() == ui::ET_SCROLL_FLING_START ||
124 event->type() == ui::ET_GESTURE_END) {
125 if (CommitSnap(*event)) {
126 if (event->type() == ui::ET_GESTURE_TAP) {
127 TouchUMA::GetInstance()->RecordGestureAction(
128 TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
130 event->SetHandled();
131 return;
135 FrameCaptionButton::OnGestureEvent(event);
138 void FrameSizeButton::StartSetButtonsToSnapModeTimer(
139 const ui::LocatedEvent& event) {
140 set_buttons_to_snap_mode_timer_event_location_ = event.location();
141 if (set_buttons_to_snap_mode_delay_ms_ == 0) {
142 AnimateButtonsToSnapMode();
143 } else {
144 set_buttons_to_snap_mode_timer_.Start(
145 FROM_HERE,
146 base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_),
147 this,
148 &FrameSizeButton::AnimateButtonsToSnapMode);
152 void FrameSizeButton::AnimateButtonsToSnapMode() {
153 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_YES);
156 void FrameSizeButton::SetButtonsToSnapMode(
157 FrameSizeButtonDelegate::Animate animate) {
158 in_snap_mode_ = true;
160 // When using a right-to-left layout the close button is left of the size
161 // button and the minimize button is right of the size button.
162 if (base::i18n::IsRTL()) {
163 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
164 CAPTION_BUTTON_ICON_LEFT_SNAPPED,
165 animate);
166 } else {
167 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
168 CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
169 animate);
173 void FrameSizeButton::UpdateSnapType(const ui::LocatedEvent& event) {
174 if (!in_snap_mode_) {
175 // Set the buttons adjacent to the size button to snap left and right early
176 // if the user drags past the drag threshold.
177 // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap
178 // mode as a result of an unsupported drag type (e.g. only the right mouse
179 // button is pressed).
180 gfx::Vector2d delta(
181 event.location() - set_buttons_to_snap_mode_timer_event_location_);
182 if (!set_buttons_to_snap_mode_timer_.IsRunning() ||
183 !views::View::ExceededDragThreshold(delta)) {
184 return;
186 AnimateButtonsToSnapMode();
189 gfx::Point event_location_in_screen(event.location());
190 views::View::ConvertPointToScreen(this, &event_location_in_screen);
191 const FrameCaptionButton* to_hover =
192 GetButtonToHover(event_location_in_screen);
193 bool press_size_button =
194 to_hover || HitTestButton(this, event_location_in_screen);
196 if (to_hover) {
197 // Progress the minimize and close icon morph animations to the end if they
198 // are in progress.
199 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_NO);
202 delegate_->SetHoveredAndPressedButtons(
203 to_hover, press_size_button ? this : NULL);
205 snap_type_ = SNAP_NONE;
206 if (to_hover) {
207 switch (to_hover->icon()) {
208 case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
209 snap_type_ = SNAP_LEFT;
210 break;
211 case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
212 snap_type_ = SNAP_RIGHT;
213 break;
214 case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
215 case CAPTION_BUTTON_ICON_MINIMIZE:
216 case CAPTION_BUTTON_ICON_CLOSE:
217 case CAPTION_BUTTON_ICON_BACK:
218 case CAPTION_BUTTON_ICON_COUNT:
219 NOTREACHED();
220 break;
224 if (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT) {
225 aura::Window* window = frame_->GetNativeWindow();
226 if (!phantom_window_controller_.get()) {
227 phantom_window_controller_.reset(new PhantomWindowController(window));
229 gfx::Rect phantom_bounds_in_parent = (snap_type_ == SNAP_LEFT) ?
230 wm::GetDefaultLeftSnappedWindowBoundsInParent(window) :
231 wm::GetDefaultRightSnappedWindowBoundsInParent(window);
232 phantom_window_controller_->Show(ScreenUtil::ConvertRectToScreen(
233 window->parent(), phantom_bounds_in_parent));
234 } else {
235 phantom_window_controller_.reset();
239 const FrameCaptionButton* FrameSizeButton::GetButtonToHover(
240 const gfx::Point& event_location_in_screen) const {
241 const FrameCaptionButton* closest_button = delegate_->GetButtonClosestTo(
242 event_location_in_screen);
243 if ((closest_button->icon() == CAPTION_BUTTON_ICON_LEFT_SNAPPED ||
244 closest_button->icon() == CAPTION_BUTTON_ICON_RIGHT_SNAPPED) &&
245 HitTestButton(closest_button, event_location_in_screen)) {
246 return closest_button;
248 return NULL;
251 bool FrameSizeButton::CommitSnap(const ui::LocatedEvent& event) {
252 // The position of |event| may be different than the position of the previous
253 // event.
254 UpdateSnapType(event);
256 if (in_snap_mode_ &&
257 (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
258 wm::WindowState* window_state =
259 wm::GetWindowState(frame_->GetNativeWindow());
260 UserMetricsRecorder* metrics = Shell::GetInstance()->metrics();
261 const wm::WMEvent snap_event(
262 snap_type_ == SNAP_LEFT ?
263 wm::WM_EVENT_SNAP_LEFT : wm::WM_EVENT_SNAP_RIGHT);
264 window_state->OnWMEvent(&snap_event);
265 metrics->RecordUserMetricsAction(
266 snap_type_ == SNAP_LEFT ?
267 UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
268 UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
269 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_NO);
270 return true;
272 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
273 return false;
276 void FrameSizeButton::SetButtonsToNormalMode(
277 FrameSizeButtonDelegate::Animate animate) {
278 in_snap_mode_ = false;
279 snap_type_ = SNAP_NONE;
280 set_buttons_to_snap_mode_timer_.Stop();
281 delegate_->SetButtonsToNormal(animate);
282 phantom_window_controller_.reset();
285 } // namespace ash