Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / ash / frame / caption_buttons / frame_size_button.cc
blob094d41ca7467be09c9b9e6c643620536dc8b1d01
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/wm/window_state.h"
11 #include "ash/wm/window_util.h"
12 #include "ash/wm/wm_event.h"
13 #include "ash/wm/workspace/phantom_window_controller.h"
14 #include "base/i18n/rtl.h"
15 #include "ui/gfx/geometry/vector2d.h"
16 #include "ui/views/widget/widget.h"
18 namespace {
20 // The default delay between the user pressing the size button and the buttons
21 // adjacent to the size button morphing into buttons for snapping left and
22 // right.
23 const int kSetButtonsToSnapModeDelayMs = 150;
25 // The amount that a user can overshoot one of the caption buttons while in
26 // "snap mode" and keep the button hovered/pressed.
27 const int kMaxOvershootX = 200;
28 const int kMaxOvershootY = 50;
30 // Returns true if a mouse drag while in "snap mode" at |location_in_screen|
31 // would hover/press |button| or keep it hovered/pressed.
32 bool HitTestButton(const ash::FrameCaptionButton* button,
33 const gfx::Point& location_in_screen) {
34 gfx::Rect expanded_bounds_in_screen = button->GetBoundsInScreen();
35 if (button->state() == views::Button::STATE_HOVERED ||
36 button->state() == views::Button::STATE_PRESSED) {
37 expanded_bounds_in_screen.Inset(-kMaxOvershootX, -kMaxOvershootY);
39 return expanded_bounds_in_screen.Contains(location_in_screen);
42 } // namespace
44 namespace ash {
46 FrameSizeButton::FrameSizeButton(
47 views::ButtonListener* listener,
48 views::Widget* frame,
49 FrameSizeButtonDelegate* delegate)
50 : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
51 frame_(frame),
52 delegate_(delegate),
53 set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs),
54 in_snap_mode_(false),
55 snap_type_(SNAP_NONE) {
58 FrameSizeButton::~FrameSizeButton() {
61 bool FrameSizeButton::OnMousePressed(const ui::MouseEvent& event) {
62 // The minimize and close buttons are set to snap left and right when snapping
63 // is enabled. Do not enable snapping if the minimize button is not visible.
64 // The close button is always visible.
65 if (IsTriggerableEvent(event) &&
66 !in_snap_mode_ &&
67 delegate_->IsMinimizeButtonVisible()) {
68 StartSetButtonsToSnapModeTimer(event);
70 FrameCaptionButton::OnMousePressed(event);
71 return true;
74 bool FrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) {
75 UpdateSnapType(event);
76 // By default a FrameCaptionButton reverts to STATE_NORMAL once the mouse
77 // leaves its bounds. Skip FrameCaptionButton's handling when
78 // |in_snap_mode_| == true because we want different behavior.
79 if (!in_snap_mode_)
80 FrameCaptionButton::OnMouseDragged(event);
81 return true;
84 void FrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) {
85 if (!IsTriggerableEvent(event) || !CommitSnap(event))
86 FrameCaptionButton::OnMouseReleased(event);
89 void FrameSizeButton::OnMouseCaptureLost() {
90 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
91 FrameCaptionButton::OnMouseCaptureLost();
94 void FrameSizeButton::OnMouseMoved(const ui::MouseEvent& event) {
95 // Ignore any synthetic mouse moves during a drag.
96 if (!in_snap_mode_)
97 FrameCaptionButton::OnMouseMoved(event);
100 void FrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
101 if (event->details().touch_points() > 1) {
102 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
103 return;
106 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
107 StartSetButtonsToSnapModeTimer(*event);
108 // Go through FrameCaptionButton's handling so that the button gets pressed.
109 FrameCaptionButton::OnGestureEvent(event);
110 return;
113 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
114 event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
115 UpdateSnapType(*event);
116 event->SetHandled();
117 return;
120 if (event->type() == ui::ET_GESTURE_TAP ||
121 event->type() == ui::ET_GESTURE_SCROLL_END ||
122 event->type() == ui::ET_SCROLL_FLING_START ||
123 event->type() == ui::ET_GESTURE_END) {
124 if (CommitSnap(*event)) {
125 event->SetHandled();
126 return;
130 FrameCaptionButton::OnGestureEvent(event);
133 void FrameSizeButton::StartSetButtonsToSnapModeTimer(
134 const ui::LocatedEvent& event) {
135 set_buttons_to_snap_mode_timer_event_location_ = event.location();
136 if (set_buttons_to_snap_mode_delay_ms_ == 0) {
137 AnimateButtonsToSnapMode();
138 } else {
139 set_buttons_to_snap_mode_timer_.Start(
140 FROM_HERE,
141 base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_),
142 this,
143 &FrameSizeButton::AnimateButtonsToSnapMode);
147 void FrameSizeButton::AnimateButtonsToSnapMode() {
148 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_YES);
151 void FrameSizeButton::SetButtonsToSnapMode(
152 FrameSizeButtonDelegate::Animate animate) {
153 in_snap_mode_ = true;
155 // When using a right-to-left layout the close button is left of the size
156 // button and the minimize button is right of the size button.
157 if (base::i18n::IsRTL()) {
158 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
159 CAPTION_BUTTON_ICON_LEFT_SNAPPED,
160 animate);
161 } else {
162 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
163 CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
164 animate);
168 void FrameSizeButton::UpdateSnapType(const ui::LocatedEvent& event) {
169 if (!in_snap_mode_) {
170 // Set the buttons adjacent to the size button to snap left and right early
171 // if the user drags past the drag threshold.
172 // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap
173 // mode as a result of an unsupported drag type (e.g. only the right mouse
174 // button is pressed).
175 gfx::Vector2d delta(
176 event.location() - set_buttons_to_snap_mode_timer_event_location_);
177 if (!set_buttons_to_snap_mode_timer_.IsRunning() ||
178 !views::View::ExceededDragThreshold(delta)) {
179 return;
181 AnimateButtonsToSnapMode();
184 gfx::Point event_location_in_screen(event.location());
185 views::View::ConvertPointToScreen(this, &event_location_in_screen);
186 const FrameCaptionButton* to_hover =
187 GetButtonToHover(event_location_in_screen);
188 bool press_size_button =
189 to_hover || HitTestButton(this, event_location_in_screen);
191 if (to_hover) {
192 // Progress the minimize and close icon morph animations to the end if they
193 // are in progress.
194 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_NO);
197 delegate_->SetHoveredAndPressedButtons(
198 to_hover, press_size_button ? this : NULL);
200 snap_type_ = SNAP_NONE;
201 if (to_hover) {
202 switch (to_hover->icon()) {
203 case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
204 snap_type_ = SNAP_LEFT;
205 break;
206 case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
207 snap_type_ = SNAP_RIGHT;
208 break;
209 case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
210 case CAPTION_BUTTON_ICON_MINIMIZE:
211 case CAPTION_BUTTON_ICON_CLOSE:
212 case CAPTION_BUTTON_ICON_BACK:
213 case CAPTION_BUTTON_ICON_LOCATION:
214 case CAPTION_BUTTON_ICON_COUNT:
215 NOTREACHED();
216 break;
220 if (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT) {
221 aura::Window* window = frame_->GetNativeWindow();
222 if (!phantom_window_controller_.get()) {
223 phantom_window_controller_.reset(new PhantomWindowController(window));
225 gfx::Rect phantom_bounds_in_parent = (snap_type_ == SNAP_LEFT) ?
226 wm::GetDefaultLeftSnappedWindowBoundsInParent(window) :
227 wm::GetDefaultRightSnappedWindowBoundsInParent(window);
228 phantom_window_controller_->Show(ScreenUtil::ConvertRectToScreen(
229 window->parent(), phantom_bounds_in_parent));
230 } else {
231 phantom_window_controller_.reset();
235 const FrameCaptionButton* FrameSizeButton::GetButtonToHover(
236 const gfx::Point& event_location_in_screen) const {
237 const FrameCaptionButton* closest_button = delegate_->GetButtonClosestTo(
238 event_location_in_screen);
239 if ((closest_button->icon() == CAPTION_BUTTON_ICON_LEFT_SNAPPED ||
240 closest_button->icon() == CAPTION_BUTTON_ICON_RIGHT_SNAPPED) &&
241 HitTestButton(closest_button, event_location_in_screen)) {
242 return closest_button;
244 return NULL;
247 bool FrameSizeButton::CommitSnap(const ui::LocatedEvent& event) {
248 // The position of |event| may be different than the position of the previous
249 // event.
250 UpdateSnapType(event);
252 if (in_snap_mode_ &&
253 (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
254 wm::WindowState* window_state =
255 wm::GetWindowState(frame_->GetNativeWindow());
256 UserMetricsRecorder* metrics = Shell::GetInstance()->metrics();
257 const wm::WMEvent snap_event(
258 snap_type_ == SNAP_LEFT ?
259 wm::WM_EVENT_SNAP_LEFT : wm::WM_EVENT_SNAP_RIGHT);
260 window_state->OnWMEvent(&snap_event);
261 metrics->RecordUserMetricsAction(
262 snap_type_ == SNAP_LEFT ?
263 UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
264 UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
265 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_NO);
266 return true;
268 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES);
269 return false;
272 void FrameSizeButton::SetButtonsToNormalMode(
273 FrameSizeButtonDelegate::Animate animate) {
274 in_snap_mode_ = false;
275 snap_type_ = SNAP_NONE;
276 set_buttons_to_snap_mode_timer_.Stop();
277 delegate_->SetButtonsToNormal(animate);
278 phantom_window_controller_.reset();
281 } // namespace ash