• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/wm/caption_buttons/alternate_frame_size_button.h"
6 
7 #include "ash/metrics/user_metrics_recorder.h"
8 #include "ash/shell.h"
9 #include "ash/touch/touch_uma.h"
10 #include "ash/wm/window_state.h"
11 #include "ash/wm/workspace/snap_sizer.h"
12 #include "ui/gfx/vector2d.h"
13 #include "ui/views/widget/widget.h"
14 
15 namespace {
16 
17 // The default delay between the user pressing the size button and the buttons
18 // adjacent to the size button morphing into buttons for snapping left and
19 // right.
20 const int kSetButtonsToSnapModeDelayMs = 150;
21 
22 // The amount that a user can overshoot the snap left / snap right button and
23 // keep the snap left / snap right button pressed.
24 const int kPressedHitBoundsExpandX = 200;
25 const int kPressedHitBoundsExpandY = 50;
26 
27 }  // namespace
28 
29 namespace ash {
30 
AlternateFrameSizeButton(views::ButtonListener * listener,views::Widget * frame,AlternateFrameSizeButtonDelegate * delegate)31 AlternateFrameSizeButton::AlternateFrameSizeButton(
32     views::ButtonListener* listener,
33     views::Widget* frame,
34     AlternateFrameSizeButtonDelegate* delegate)
35     : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
36       frame_(frame),
37       delegate_(delegate),
38       set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs),
39       in_snap_mode_(false),
40       snap_type_(SNAP_NONE) {
41 }
42 
~AlternateFrameSizeButton()43 AlternateFrameSizeButton::~AlternateFrameSizeButton() {
44 }
45 
OnMousePressed(const ui::MouseEvent & event)46 bool AlternateFrameSizeButton::OnMousePressed(const ui::MouseEvent& event) {
47   // The minimize and close buttons are set to snap left and right when snapping
48   // is enabled. Do not enable snapping if the minimize button is not visible.
49   // The close button is always visible.
50   if (IsTriggerableEvent(event) &&
51       !in_snap_mode_ &&
52       delegate_->IsMinimizeButtonVisible()) {
53     StartSetButtonsToSnapModeTimer(event);
54   }
55   FrameCaptionButton::OnMousePressed(event);
56   return true;
57 }
58 
OnMouseDragged(const ui::MouseEvent & event)59 bool AlternateFrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) {
60   UpdatePressedButton(event);
61   FrameCaptionButton::OnMouseDragged(event);
62   return true;
63 }
64 
OnMouseReleased(const ui::MouseEvent & event)65 void AlternateFrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) {
66   if (!IsTriggerableEvent(event) || !CommitSnap(event))
67     FrameCaptionButton::OnMouseReleased(event);
68 }
69 
OnMouseCaptureLost()70 void AlternateFrameSizeButton::OnMouseCaptureLost() {
71   SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
72   FrameCaptionButton::OnMouseCaptureLost();
73 }
74 
OnGestureEvent(ui::GestureEvent * event)75 void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
76   if (event->details().touch_points() > 1) {
77     SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
78     return;
79   }
80 
81   if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
82     StartSetButtonsToSnapModeTimer(*event);
83     // Go through FrameCaptionButton's handling so that the button gets pressed.
84     FrameCaptionButton::OnGestureEvent(event);
85     return;
86   }
87 
88   if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
89       event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
90     UpdatePressedButton(*event);
91     event->SetHandled();
92     return;
93   }
94 
95   if (event->type() == ui::ET_GESTURE_TAP ||
96       event->type() == ui::ET_GESTURE_SCROLL_END ||
97       event->type() == ui::ET_SCROLL_FLING_START ||
98       event->type() == ui::ET_GESTURE_END) {
99     if (CommitSnap(*event)) {
100       if (event->type() == ui::ET_GESTURE_TAP) {
101         TouchUMA::GetInstance()->RecordGestureAction(
102             TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
103       }
104       event->SetHandled();
105       return;
106     }
107   }
108 
109   FrameCaptionButton::OnGestureEvent(event);
110 }
111 
StartSetButtonsToSnapModeTimer(const ui::LocatedEvent & event)112 void AlternateFrameSizeButton::StartSetButtonsToSnapModeTimer(
113     const ui::LocatedEvent& event) {
114   set_buttons_to_snap_mode_timer_event_location_ = event.location();
115   if (set_buttons_to_snap_mode_delay_ms_ == 0) {
116     SetButtonsToSnapMode();
117   } else {
118     set_buttons_to_snap_mode_timer_.Start(
119         FROM_HERE,
120         base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_),
121         this,
122         &AlternateFrameSizeButton::SetButtonsToSnapMode);
123   }
124 }
125 
SetButtonsToSnapMode()126 void AlternateFrameSizeButton::SetButtonsToSnapMode() {
127   if (in_snap_mode_)
128     return;
129   in_snap_mode_ = true;
130   delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
131                             CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
132                             AlternateFrameSizeButtonDelegate::ANIMATE_YES);
133 }
134 
UpdatePressedButton(const ui::LocatedEvent & event)135 void AlternateFrameSizeButton::UpdatePressedButton(
136     const ui::LocatedEvent& event) {
137   if (!in_snap_mode_) {
138     // Set the buttons adjacent to the size button to snap left and right early
139     // if the user drags past the drag threshold.
140     // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap
141     // mode as a result of an unsupported drag type (e.g. only the right mouse
142     // button is pressed).
143     gfx::Vector2d delta(
144         event.location() - set_buttons_to_snap_mode_timer_event_location_);
145     if (!set_buttons_to_snap_mode_timer_.IsRunning() ||
146         !views::View::ExceededDragThreshold(delta)) {
147       return;
148     }
149     SetButtonsToSnapMode();
150   }
151 
152   gfx::Point event_location_in_screen(event.location());
153   views::View::ConvertPointToScreen(this, &event_location_in_screen);
154 
155   gfx::Insets pressed_button_hittest_insets(-kPressedHitBoundsExpandY,
156                                             -kPressedHitBoundsExpandX,
157                                             -kPressedHitBoundsExpandY,
158                                             -kPressedHitBoundsExpandX);
159   const FrameCaptionButton* pressed_button = delegate_->PressButtonAt(
160       event_location_in_screen, pressed_button_hittest_insets);
161   snap_type_ = SNAP_NONE;
162   if (pressed_button) {
163     switch (pressed_button->icon()) {
164       case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
165         snap_type_ = SNAP_LEFT;
166         break;
167       case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
168         snap_type_ = SNAP_RIGHT;
169         break;
170       case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
171         // snap_type_ = SNAP_NONE
172         break;
173       case CAPTION_BUTTON_ICON_MINIMIZE:
174       case CAPTION_BUTTON_ICON_CLOSE:
175         NOTREACHED();
176         break;
177     }
178   }
179 }
180 
CommitSnap(const ui::LocatedEvent & event)181 bool AlternateFrameSizeButton::CommitSnap(const ui::LocatedEvent& event) {
182   // The position of |event| may be different than the position of the previous
183   // event.
184   UpdatePressedButton(event);
185 
186   if (in_snap_mode_ &&
187       (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
188     using internal::SnapSizer;
189     SnapSizer::SnapWindow(ash::wm::GetWindowState(frame_->GetNativeWindow()),
190                           snap_type_ == SNAP_LEFT ?
191                               SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE);
192     ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
193         snap_type_ == SNAP_LEFT ?
194             ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
195             ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
196     SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_NO);
197     return true;
198   }
199   SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
200   return false;
201 }
202 
SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::Animate animate)203 void AlternateFrameSizeButton::SetButtonsToNormalMode(
204     AlternateFrameSizeButtonDelegate::Animate animate) {
205   in_snap_mode_ = false;
206   snap_type_ = SNAP_NONE;
207   set_buttons_to_snap_mode_timer_.Stop();
208   delegate_->SetButtonsToNormal(animate);
209 }
210 
211 }  // namespace ash
212