OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ash/common/frame/caption_buttons/frame_size_button.h" | |
6 | |
7 #include "ash/common/wm/window_positioning_utils.h" | |
8 #include "ash/common/wm/window_state.h" | |
9 #include "ash/common/wm/wm_event.h" | |
10 #include "ash/common/wm/workspace/phantom_window_controller.h" | |
11 #include "ash/common/wm_shell.h" | |
12 #include "ash/common/wm_window.h" | |
13 #include "base/i18n/rtl.h" | |
14 #include "ui/gfx/geometry/vector2d.h" | |
15 #include "ui/views/widget/widget.h" | |
16 | |
17 namespace ash { | |
18 | |
19 namespace { | |
20 | |
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; | |
25 | |
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; | |
30 | |
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 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); | |
39 } | |
40 return expanded_bounds_in_screen.Contains(location_in_screen); | |
41 } | |
42 | |
43 } // namespace | |
44 | |
45 FrameSizeButton::FrameSizeButton(views::ButtonListener* listener, | |
46 views::Widget* frame, | |
47 FrameSizeButtonDelegate* delegate) | |
48 : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE), | |
49 frame_(frame), | |
50 delegate_(delegate), | |
51 set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs), | |
52 in_snap_mode_(false), | |
53 snap_type_(SNAP_NONE) {} | |
54 | |
55 FrameSizeButton::~FrameSizeButton() {} | |
56 | |
57 bool FrameSizeButton::OnMousePressed(const ui::MouseEvent& event) { | |
58 // The minimize and close buttons are set to snap left and right when snapping | |
59 // is enabled. Do not enable snapping if the minimize button is not visible. | |
60 // The close button is always visible. | |
61 if (IsTriggerableEvent(event) && !in_snap_mode_ && | |
62 delegate_->IsMinimizeButtonVisible()) { | |
63 StartSetButtonsToSnapModeTimer(event); | |
64 } | |
65 FrameCaptionButton::OnMousePressed(event); | |
66 return true; | |
67 } | |
68 | |
69 bool FrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) { | |
70 UpdateSnapType(event); | |
71 // By default a FrameCaptionButton reverts to STATE_NORMAL once the mouse | |
72 // leaves its bounds. Skip FrameCaptionButton's handling when | |
73 // |in_snap_mode_| == true because we want different behavior. | |
74 if (!in_snap_mode_) | |
75 FrameCaptionButton::OnMouseDragged(event); | |
76 return true; | |
77 } | |
78 | |
79 void FrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) { | |
80 if (!IsTriggerableEvent(event) || !CommitSnap(event)) | |
81 FrameCaptionButton::OnMouseReleased(event); | |
82 } | |
83 | |
84 void FrameSizeButton::OnMouseCaptureLost() { | |
85 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
86 FrameCaptionButton::OnMouseCaptureLost(); | |
87 } | |
88 | |
89 void FrameSizeButton::OnMouseMoved(const ui::MouseEvent& event) { | |
90 // Ignore any synthetic mouse moves during a drag. | |
91 if (!in_snap_mode_) | |
92 FrameCaptionButton::OnMouseMoved(event); | |
93 } | |
94 | |
95 void FrameSizeButton::OnGestureEvent(ui::GestureEvent* event) { | |
96 if (event->details().touch_points() > 1) { | |
97 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
98 return; | |
99 } | |
100 | |
101 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { | |
102 StartSetButtonsToSnapModeTimer(*event); | |
103 // Go through FrameCaptionButton's handling so that the button gets pressed. | |
104 FrameCaptionButton::OnGestureEvent(event); | |
105 return; | |
106 } | |
107 | |
108 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || | |
109 event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { | |
110 UpdateSnapType(*event); | |
111 event->SetHandled(); | |
112 return; | |
113 } | |
114 | |
115 if (event->type() == ui::ET_GESTURE_TAP || | |
116 event->type() == ui::ET_GESTURE_SCROLL_END || | |
117 event->type() == ui::ET_SCROLL_FLING_START || | |
118 event->type() == ui::ET_GESTURE_END) { | |
119 if (CommitSnap(*event)) { | |
120 event->SetHandled(); | |
121 return; | |
122 } | |
123 } | |
124 | |
125 FrameCaptionButton::OnGestureEvent(event); | |
126 } | |
127 | |
128 void FrameSizeButton::StartSetButtonsToSnapModeTimer( | |
129 const ui::LocatedEvent& event) { | |
130 set_buttons_to_snap_mode_timer_event_location_ = event.location(); | |
131 if (set_buttons_to_snap_mode_delay_ms_ == 0) { | |
132 AnimateButtonsToSnapMode(); | |
133 } else { | |
134 set_buttons_to_snap_mode_timer_.Start( | |
135 FROM_HERE, | |
136 base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_), | |
137 this, &FrameSizeButton::AnimateButtonsToSnapMode); | |
138 } | |
139 } | |
140 | |
141 void FrameSizeButton::AnimateButtonsToSnapMode() { | |
142 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
143 } | |
144 | |
145 void FrameSizeButton::SetButtonsToSnapMode( | |
146 FrameSizeButtonDelegate::Animate animate) { | |
147 in_snap_mode_ = true; | |
148 | |
149 // When using a right-to-left layout the close button is left of the size | |
150 // button and the minimize button is right of the size button. | |
151 if (base::i18n::IsRTL()) { | |
152 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, | |
153 CAPTION_BUTTON_ICON_LEFT_SNAPPED, animate); | |
154 } else { | |
155 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED, | |
156 CAPTION_BUTTON_ICON_RIGHT_SNAPPED, animate); | |
157 } | |
158 } | |
159 | |
160 void FrameSizeButton::UpdateSnapType(const ui::LocatedEvent& event) { | |
161 if (!in_snap_mode_) { | |
162 // Set the buttons adjacent to the size button to snap left and right early | |
163 // if the user drags past the drag threshold. | |
164 // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap | |
165 // mode as a result of an unsupported drag type (e.g. only the right mouse | |
166 // button is pressed). | |
167 gfx::Vector2d delta(event.location() - | |
168 set_buttons_to_snap_mode_timer_event_location_); | |
169 if (!set_buttons_to_snap_mode_timer_.IsRunning() || | |
170 !views::View::ExceededDragThreshold(delta)) { | |
171 return; | |
172 } | |
173 AnimateButtonsToSnapMode(); | |
174 } | |
175 | |
176 gfx::Point event_location_in_screen(event.location()); | |
177 views::View::ConvertPointToScreen(this, &event_location_in_screen); | |
178 const FrameCaptionButton* to_hover = | |
179 GetButtonToHover(event_location_in_screen); | |
180 bool press_size_button = | |
181 to_hover || HitTestButton(this, event_location_in_screen); | |
182 | |
183 if (to_hover) { | |
184 // Progress the minimize and close icon morph animations to the end if they | |
185 // are in progress. | |
186 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_NO); | |
187 } | |
188 | |
189 delegate_->SetHoveredAndPressedButtons(to_hover, | |
190 press_size_button ? this : NULL); | |
191 | |
192 snap_type_ = SNAP_NONE; | |
193 if (to_hover) { | |
194 switch (to_hover->icon()) { | |
195 case CAPTION_BUTTON_ICON_LEFT_SNAPPED: | |
196 snap_type_ = SNAP_LEFT; | |
197 break; | |
198 case CAPTION_BUTTON_ICON_RIGHT_SNAPPED: | |
199 snap_type_ = SNAP_RIGHT; | |
200 break; | |
201 case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE: | |
202 case CAPTION_BUTTON_ICON_MINIMIZE: | |
203 case CAPTION_BUTTON_ICON_CLOSE: | |
204 case CAPTION_BUTTON_ICON_BACK: | |
205 case CAPTION_BUTTON_ICON_LOCATION: | |
206 case CAPTION_BUTTON_ICON_COUNT: | |
207 NOTREACHED(); | |
208 break; | |
209 } | |
210 } | |
211 | |
212 if (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT) { | |
213 WmWindow* window = WmWindow::Get(frame_->GetNativeWindow()); | |
214 if (!phantom_window_controller_.get()) | |
215 phantom_window_controller_.reset(new PhantomWindowController(window)); | |
216 gfx::Rect phantom_bounds_in_parent = | |
217 (snap_type_ == SNAP_LEFT) | |
218 ? wm::GetDefaultLeftSnappedWindowBoundsInParent(window) | |
219 : wm::GetDefaultRightSnappedWindowBoundsInParent(window); | |
220 phantom_window_controller_->Show( | |
221 window->GetParent()->ConvertRectToScreen(phantom_bounds_in_parent)); | |
222 } else { | |
223 phantom_window_controller_.reset(); | |
224 } | |
225 } | |
226 | |
227 const FrameCaptionButton* FrameSizeButton::GetButtonToHover( | |
228 const gfx::Point& event_location_in_screen) const { | |
229 const FrameCaptionButton* closest_button = | |
230 delegate_->GetButtonClosestTo(event_location_in_screen); | |
231 if ((closest_button->icon() == CAPTION_BUTTON_ICON_LEFT_SNAPPED || | |
232 closest_button->icon() == CAPTION_BUTTON_ICON_RIGHT_SNAPPED) && | |
233 HitTestButton(closest_button, event_location_in_screen)) { | |
234 return closest_button; | |
235 } | |
236 return NULL; | |
237 } | |
238 | |
239 bool FrameSizeButton::CommitSnap(const ui::LocatedEvent& event) { | |
240 // The position of |event| may be different than the position of the previous | |
241 // event. | |
242 UpdateSnapType(event); | |
243 | |
244 if (in_snap_mode_ && (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) { | |
245 WmWindow* window = WmWindow::Get(frame_->GetNativeWindow()); | |
246 wm::WindowState* window_state = window->GetWindowState(); | |
247 const wm::WMEvent snap_event(snap_type_ == SNAP_LEFT | |
248 ? wm::WM_EVENT_SNAP_LEFT | |
249 : wm::WM_EVENT_SNAP_RIGHT); | |
250 window_state->OnWMEvent(&snap_event); | |
251 WmShell::Get()->RecordUserMetricsAction( | |
252 snap_type_ == SNAP_LEFT ? UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT | |
253 : UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT); | |
254 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_NO); | |
255 return true; | |
256 } | |
257 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
258 return false; | |
259 } | |
260 | |
261 void FrameSizeButton::SetButtonsToNormalMode( | |
262 FrameSizeButtonDelegate::Animate animate) { | |
263 in_snap_mode_ = false; | |
264 snap_type_ = SNAP_NONE; | |
265 set_buttons_to_snap_mode_timer_.Stop(); | |
266 delegate_->SetButtonsToNormal(animate); | |
267 phantom_window_controller_.reset(); | |
268 } | |
269 | |
270 } // namespace ash | |
OLD | NEW |