1010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// found in the LICENSE file.
4a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "ash/frame/caption_buttons/frame_size_button.h"
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/metrics/user_metrics_recorder.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/screen_util.h"
9a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ash/shell.h"
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ash/touch/touch_uma.h"
11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ash/wm/window_state.h"
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/wm/window_util.h"
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/wm/wm_event.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/workspace/phantom_window_controller.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/i18n/rtl.h"
16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ui/gfx/vector2d.h"
17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ui/views/widget/widget.h"
18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace {
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
21a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// The default delay between the user pressing the size button and the buttons
22a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// adjacent to the size button morphing into buttons for snapping left and
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// right.
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const int kSetButtonsToSnapModeDelayMs = 150;
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// The amount that a user can overshoot one of the caption buttons while in
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// "snap mode" and keep the button hovered/pressed.
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kMaxOvershootX = 200;
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kMaxOvershootY = 50;
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Returns true if a mouse drag while in "snap mode" at |location_in_screen|
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// would hover/press |button| or keep it hovered/pressed.
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool HitTestButton(const ash::FrameCaptionButton* button,
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                   const gfx::Point& location_in_screen) {
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Rect expanded_bounds_in_screen = button->GetBoundsInScreen();
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (button->state() == views::Button::STATE_HOVERED ||
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      button->state() == views::Button::STATE_PRESSED) {
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    expanded_bounds_in_screen.Inset(-kMaxOvershootX, -kMaxOvershootY);
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return expanded_bounds_in_screen.Contains(location_in_screen);
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
43a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}  // namespace
44a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace ash {
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
47010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)FrameSizeButton::FrameSizeButton(
48a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    views::ButtonListener* listener,
49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    views::Widget* frame,
50010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    FrameSizeButtonDelegate* delegate)
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      frame_(frame),
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      delegate_(delegate),
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs),
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      in_snap_mode_(false),
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      snap_type_(SNAP_NONE) {
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
59010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)FrameSizeButton::~FrameSizeButton() {
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
62010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)bool FrameSizeButton::OnMousePressed(const ui::MouseEvent& event) {
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // The minimize and close buttons are set to snap left and right when snapping
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // is enabled. Do not enable snapping if the minimize button is not visible.
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // The close button is always visible.
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (IsTriggerableEvent(event) &&
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      !in_snap_mode_ &&
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      delegate_->IsMinimizeButtonVisible()) {
69a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    StartSetButtonsToSnapModeTimer(event);
70a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  FrameCaptionButton::OnMousePressed(event);
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return true;
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
75010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)bool FrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) {
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UpdateSnapType(event);
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // By default a FrameCaptionButton reverts to STATE_NORMAL once the mouse
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // leaves its bounds. Skip FrameCaptionButton's handling when
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // |in_snap_mode_| == true because we want different behavior.
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!in_snap_mode_)
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    FrameCaptionButton::OnMouseDragged(event);
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return true;
83a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
85010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) {
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!IsTriggerableEvent(event) || !CommitSnap(event))
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    FrameCaptionButton::OnMouseReleased(event);
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
90010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::OnMouseCaptureLost() {
91010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
92a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  FrameCaptionButton::OnMouseCaptureLost();
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
95010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::OnMouseMoved(const ui::MouseEvent& event) {
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Ignore any synthetic mouse moves during a drag.
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!in_snap_mode_)
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    FrameCaptionButton::OnMouseMoved(event);
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
101010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (event->details().touch_points() > 1) {
103010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    StartSetButtonsToSnapModeTimer(*event);
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Go through FrameCaptionButton's handling so that the button gets pressed.
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    FrameCaptionButton::OnGestureEvent(event);
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    UpdateSnapType(*event);
117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    event->SetHandled();
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (event->type() == ui::ET_GESTURE_TAP ||
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      event->type() == ui::ET_GESTURE_SCROLL_END ||
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      event->type() == ui::ET_SCROLL_FLING_START ||
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      event->type() == ui::ET_GESTURE_END) {
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (CommitSnap(*event)) {
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (event->type() == ui::ET_GESTURE_TAP) {
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        TouchUMA::GetInstance()->RecordGestureAction(
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      }
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      event->SetHandled();
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  FrameCaptionButton::OnGestureEvent(event);
136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
138010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::StartSetButtonsToSnapModeTimer(
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const ui::LocatedEvent& event) {
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  set_buttons_to_snap_mode_timer_event_location_ = event.location();
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (set_buttons_to_snap_mode_delay_ms_ == 0) {
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    AnimateButtonsToSnapMode();
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  } else {
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    set_buttons_to_snap_mode_timer_.Start(
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        FROM_HERE,
146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_),
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        this,
148010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        &FrameSizeButton::AnimateButtonsToSnapMode);
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
152010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::AnimateButtonsToSnapMode() {
153010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_YES);
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
156010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::SetButtonsToSnapMode(
157010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    FrameSizeButtonDelegate::Animate animate) {
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  in_snap_mode_ = true;
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // When using a right-to-left layout the close button is left of the size
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // button and the minimize button is right of the size button.
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (base::i18n::IsRTL()) {
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              CAPTION_BUTTON_ICON_LEFT_SNAPPED,
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              animate);
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              animate);
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
171a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
173010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::UpdateSnapType(const ui::LocatedEvent& event) {
174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!in_snap_mode_) {
175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Set the buttons adjacent to the size button to snap left and right early
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // if the user drags past the drag threshold.
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // mode as a result of an unsupported drag type (e.g. only the right mouse
179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // button is pressed).
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    gfx::Vector2d delta(
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        event.location() - set_buttons_to_snap_mode_timer_event_location_);
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!set_buttons_to_snap_mode_timer_.IsRunning() ||
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        !views::View::ExceededDragThreshold(delta)) {
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    AnimateButtonsToSnapMode();
187a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
188a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
189a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  gfx::Point event_location_in_screen(event.location());
190a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  views::View::ConvertPointToScreen(this, &event_location_in_screen);
191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const FrameCaptionButton* to_hover =
192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      GetButtonToHover(event_location_in_screen);
193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool press_size_button =
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      to_hover || HitTestButton(this, event_location_in_screen);
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (to_hover) {
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Progress the minimize and close icon morph animations to the end if they
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // are in progress.
199010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_NO);
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  delegate_->SetHoveredAndPressedButtons(
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      to_hover, press_size_button ? this : NULL);
204a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  snap_type_ = SNAP_NONE;
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (to_hover) {
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    switch (to_hover->icon()) {
208a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
209a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        snap_type_ = SNAP_LEFT;
210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        break;
211a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        snap_type_ = SNAP_RIGHT;
213a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        break;
214a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
215a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case CAPTION_BUTTON_ICON_MINIMIZE:
216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case CAPTION_BUTTON_ICON_CLOSE:
21703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      case CAPTION_BUTTON_ICON_BACK:
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      case CAPTION_BUTTON_ICON_COUNT:
219a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        NOTREACHED();
220a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        break;
221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT) {
225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    aura::Window* window = frame_->GetNativeWindow();
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!phantom_window_controller_.get()) {
227c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      phantom_window_controller_.reset(new PhantomWindowController(window));
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    gfx::Rect phantom_bounds_in_parent = (snap_type_ == SNAP_LEFT) ?
230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        wm::GetDefaultLeftSnappedWindowBoundsInParent(window) :
231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        wm::GetDefaultRightSnappedWindowBoundsInParent(window);
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    phantom_window_controller_->Show(ScreenUtil::ConvertRectToScreen(
233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          window->parent(), phantom_bounds_in_parent));
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    phantom_window_controller_.reset();
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
237a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
238a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
239010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)const FrameCaptionButton* FrameSizeButton::GetButtonToHover(
240a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const gfx::Point& event_location_in_screen) const {
241a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const FrameCaptionButton* closest_button = delegate_->GetButtonClosestTo(
242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      event_location_in_screen);
243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if ((closest_button->icon() == CAPTION_BUTTON_ICON_LEFT_SNAPPED ||
244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       closest_button->icon() == CAPTION_BUTTON_ICON_RIGHT_SNAPPED) &&
245a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      HitTestButton(closest_button, event_location_in_screen)) {
246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return closest_button;
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return NULL;
249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
251010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)bool FrameSizeButton::CommitSnap(const ui::LocatedEvent& event) {
252a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // The position of |event| may be different than the position of the previous
253a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // event.
254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UpdateSnapType(event);
255a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
256a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (in_snap_mode_ &&
257a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    wm::WindowState* window_state =
259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        wm::GetWindowState(frame_->GetNativeWindow());
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    UserMetricsRecorder* metrics = Shell::GetInstance()->metrics();
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const wm::WMEvent snap_event(
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        snap_type_ == SNAP_LEFT ?
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        wm::WM_EVENT_SNAP_LEFT : wm::WM_EVENT_SNAP_RIGHT);
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    window_state->OnWMEvent(&snap_event);
265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    metrics->RecordUserMetricsAction(
266a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        snap_type_ == SNAP_LEFT ?
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
269010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_NO);
270a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return true;
271a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
272010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
273a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return false;
274a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
275a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
276010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void FrameSizeButton::SetButtonsToNormalMode(
277010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    FrameSizeButtonDelegate::Animate animate) {
278a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  in_snap_mode_ = false;
279a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  snap_type_ = SNAP_NONE;
280a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  set_buttons_to_snap_mode_timer_.Stop();
281a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  delegate_->SetButtonsToNormal(animate);
2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  phantom_window_controller_.reset();
283a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
284a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
285a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}  // namespace ash
286