Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(116)

Side by Side Diff: ash/frame/caption_buttons/frame_maximize_button.cc

Issue 263083005: Remove "Alternate frame caption button style" command line flag (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/frame/caption_buttons/frame_maximize_button.h"
6
7 #include "ash/frame/caption_buttons/frame_maximize_button_observer.h"
8 #include "ash/frame/caption_buttons/maximize_bubble_controller.h"
9 #include "ash/metrics/user_metrics_recorder.h"
10 #include "ash/screen_util.h"
11 #include "ash/shelf/shelf_widget.h"
12 #include "ash/shell.h"
13 #include "ash/touch/touch_uma.h"
14 #include "ash/wm/window_animations.h"
15 #include "ash/wm/window_state.h"
16 #include "ash/wm/window_util.h"
17 #include "ash/wm/wm_event.h"
18 #include "ash/wm/workspace/phantom_window_controller.h"
19 #include "grit/ash_strings.h"
20 #include "ui/aura/window.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/events/event.h"
24 #include "ui/events/event_handler.h"
25 #include "ui/gfx/image/image.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/views/widget/widget.h"
28 #include "ui/views/window/non_client_view.h"
29
30 namespace ash {
31
32 namespace {
33
34 // Delay before forcing an update of the snap location.
35 const int kUpdateDelayMS = 400;
36
37 // The delay of the bubble appearance.
38 const int kBubbleAppearanceDelayMS = 500;
39
40 // The minimum sanp size in percent of the screen width.
41 const int kMinSnapSizePercent = 50;
42 }
43
44 // EscapeEventFilter is installed on the RootWindow to track when the escape key
45 // is pressed. We use an EventFilter for this as the FrameMaximizeButton
46 // normally does not get focus.
47 class FrameMaximizeButton::EscapeEventFilter : public ui::EventHandler {
48 public:
49 explicit EscapeEventFilter(FrameMaximizeButton* button);
50 virtual ~EscapeEventFilter();
51
52 // EventFilter overrides:
53 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
54
55 private:
56 FrameMaximizeButton* button_;
57
58 DISALLOW_COPY_AND_ASSIGN(EscapeEventFilter);
59 };
60
61 FrameMaximizeButton::EscapeEventFilter::EscapeEventFilter(
62 FrameMaximizeButton* button)
63 : button_(button) {
64 Shell::GetInstance()->AddPreTargetHandler(this);
65 }
66
67 FrameMaximizeButton::EscapeEventFilter::~EscapeEventFilter() {
68 Shell::GetInstance()->RemovePreTargetHandler(this);
69 }
70
71 void FrameMaximizeButton::EscapeEventFilter::OnKeyEvent(
72 ui::KeyEvent* event) {
73 if (event->type() == ui::ET_KEY_PRESSED &&
74 event->key_code() == ui::VKEY_ESCAPE) {
75 button_->Cancel(false);
76 }
77 }
78
79 // FrameMaximizeButton ---------------------------------------------------------
80
81 FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener,
82 views::Widget* frame)
83 : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
84 frame_(frame),
85 observing_frame_(false),
86 is_snap_enabled_(false),
87 exceeded_drag_threshold_(false),
88 snap_type_(SNAP_NONE),
89 bubble_appearance_delay_ms_(kBubbleAppearanceDelayMS) {
90 }
91
92 FrameMaximizeButton::~FrameMaximizeButton() {
93 // Before the window gets destroyed, the maximizer dialog needs to be shut
94 // down since it would otherwise call into a deleted object.
95 maximizer_.reset();
96 if (observing_frame_)
97 OnWindowDestroying(frame_->GetNativeWindow());
98 }
99
100 void FrameMaximizeButton::AddObserver(FrameMaximizeButtonObserver* observer) {
101 observer_list_.AddObserver(observer);
102 }
103
104 void FrameMaximizeButton::RemoveObserver(
105 FrameMaximizeButtonObserver* observer) {
106 observer_list_.RemoveObserver(observer);
107 }
108
109 void FrameMaximizeButton::SnapButtonHovered(SnapType type) {
110 // Make sure to only show hover operations when no button is pressed and
111 // a similar snap operation in progress does not get re-applied.
112 if (is_snap_enabled_ || type == snap_type_)
113 return;
114 // Prime the mouse location with the center of the (local) button.
115 press_location_ = gfx::Point(width() / 2, height() / 2);
116 // Then get an adjusted mouse position to initiate the effect.
117 gfx::Point location = press_location_;
118 switch (type) {
119 case SNAP_LEFT:
120 location.set_x(location.x() - width());
121 break;
122 case SNAP_RIGHT:
123 location.set_x(location.x() + width());
124 break;
125 case SNAP_MINIMIZE:
126 location.set_y(location.y() + height());
127 break;
128 case SNAP_RESTORE:
129 // Simulate a mouse button move over the according button.
130 if (GetMaximizeBubbleFrameState() == FRAME_STATE_SNAP_LEFT)
131 location.set_x(location.x() - width());
132 else if (GetMaximizeBubbleFrameState() == FRAME_STATE_SNAP_RIGHT)
133 location.set_x(location.x() + width());
134 break;
135 case SNAP_MAXIMIZE:
136 break;
137 case SNAP_NONE:
138 Cancel(true);
139 return;
140 default:
141 // We should not come here.
142 NOTREACHED();
143 }
144 UpdateSnap(location);
145 }
146
147 void FrameMaximizeButton::ExecuteSnapAndCloseMenu(SnapType snap_type) {
148 Cancel(true);
149 // Tell our menu to close.
150 maximizer_.reset();
151 snap_type_ = snap_type;
152 Snap();
153 }
154
155 void FrameMaximizeButton::OnMaximizeBubbleShown(views::Widget* bubble) {
156 FOR_EACH_OBSERVER(FrameMaximizeButtonObserver,
157 observer_list_,
158 OnMaximizeBubbleShown(bubble));
159 }
160
161 void FrameMaximizeButton::DestroyMaximizeMenu() {
162 Cancel(false);
163 }
164
165 void FrameMaximizeButton::OnWindowBoundsChanged(
166 aura::Window* window,
167 const gfx::Rect& old_bounds,
168 const gfx::Rect& new_bounds) {
169 Cancel(false);
170 }
171
172 void FrameMaximizeButton::OnWindowPropertyChanged(aura::Window* window,
173 const void* key,
174 intptr_t old) {
175 Cancel(false);
176 }
177
178 void FrameMaximizeButton::OnWindowDestroying(aura::Window* window) {
179 maximizer_.reset();
180 if (observing_frame_) {
181 CHECK_EQ(frame_->GetNativeWindow(), window);
182 frame_->GetNativeWindow()->RemoveObserver(this);
183 frame_->RemoveObserver(this);
184 observing_frame_ = false;
185 }
186 }
187
188 void FrameMaximizeButton::OnWidgetActivationChanged(views::Widget* widget,
189 bool active) {
190 // Upon losing focus, the bubble menu and the phantom window should hide.
191 if (!active)
192 Cancel(false);
193 }
194
195 bool FrameMaximizeButton::OnMousePressed(const ui::MouseEvent& event) {
196 // If we are already in a mouse click / drag operation, a second button down
197 // call will cancel (this addresses crbug.com/143755).
198 if (is_snap_enabled_) {
199 Cancel(false);
200 } else {
201 is_snap_enabled_ = event.IsOnlyLeftMouseButton();
202 if (is_snap_enabled_)
203 ProcessStartEvent(event);
204 }
205 FrameCaptionButton::OnMousePressed(event);
206 return true;
207 }
208
209 void FrameMaximizeButton::OnMouseEntered(const ui::MouseEvent& event) {
210 FrameCaptionButton::OnMouseEntered(event);
211 if (!maximizer_) {
212 DCHECK(GetWidget());
213 if (!observing_frame_) {
214 observing_frame_ = true;
215 frame_->GetNativeWindow()->AddObserver(this);
216 frame_->AddObserver(this);
217 }
218 maximizer_.reset(new MaximizeBubbleController(
219 this,
220 GetMaximizeBubbleFrameState(),
221 bubble_appearance_delay_ms_));
222 }
223 }
224
225 void FrameMaximizeButton::OnMouseExited(const ui::MouseEvent& event) {
226 FrameCaptionButton::OnMouseExited(event);
227 // Remove the bubble menu when the button is not pressed and the mouse is not
228 // within the bubble.
229 if (!is_snap_enabled_ && maximizer_) {
230 if (maximizer_->GetBubbleWindow()) {
231 gfx::Point screen_location = Shell::GetScreen()->GetCursorScreenPoint();
232 if (!maximizer_->GetBubbleWindow()->GetBoundsInScreen().Contains(
233 screen_location)) {
234 maximizer_.reset();
235 // Make sure that all remaining snap hover states get removed.
236 SnapButtonHovered(SNAP_NONE);
237 }
238 } else {
239 // The maximize dialog does not show up immediately after creating the
240 // |maximizer_|. Destroy the dialog therefore before it shows up.
241 maximizer_.reset();
242 }
243 }
244 }
245
246 bool FrameMaximizeButton::OnMouseDragged(const ui::MouseEvent& event) {
247 if (is_snap_enabled_)
248 ProcessUpdateEvent(event);
249 return FrameCaptionButton::OnMouseDragged(event);
250 }
251
252 void FrameMaximizeButton::OnMouseReleased(const ui::MouseEvent& event) {
253 maximizer_.reset();
254 bool snap_was_enabled = is_snap_enabled_;
255 if (!ProcessEndEvent(event) && snap_was_enabled)
256 FrameCaptionButton::OnMouseReleased(event);
257 // At this point |this| might be already destroyed.
258 }
259
260 void FrameMaximizeButton::OnMouseCaptureLost() {
261 Cancel(false);
262 FrameCaptionButton::OnMouseCaptureLost();
263 }
264
265 void FrameMaximizeButton::OnGestureEvent(ui::GestureEvent* event) {
266 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
267 is_snap_enabled_ = true;
268 ProcessStartEvent(*event);
269 event->SetHandled();
270 return;
271 }
272
273 if (event->type() == ui::ET_GESTURE_TAP ||
274 (event->type() == ui::ET_GESTURE_SCROLL_END && is_snap_enabled_) ||
275 event->type() == ui::ET_SCROLL_FLING_START) {
276 // The position of the event may have changed from the previous event (both
277 // for TAP and SCROLL_END). So it is necessary to update the snap-state for
278 // the current event.
279 ProcessUpdateEvent(*event);
280 if (event->type() == ui::ET_GESTURE_TAP) {
281 snap_type_ = SnapTypeForLocation(event->location());
282 TouchUMA::GetInstance()->RecordGestureAction(
283 TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
284 }
285 ProcessEndEvent(*event);
286 event->SetHandled();
287 return;
288 }
289
290 if (is_snap_enabled_) {
291 if (event->type() == ui::ET_GESTURE_END &&
292 event->details().touch_points() == 1) {
293 // The position of the event may have changed from the previous event. So
294 // it is necessary to update the snap-state for the current event.
295 ProcessUpdateEvent(*event);
296 snap_type_ = SnapTypeForLocation(event->location());
297 ProcessEndEvent(*event);
298 event->SetHandled();
299 return;
300 }
301
302 if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE ||
303 event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
304 ProcessUpdateEvent(*event);
305 event->SetHandled();
306 return;
307 }
308 }
309
310 FrameCaptionButton::OnGestureEvent(event);
311 }
312
313 void FrameMaximizeButton::SetVisible(bool visible) {
314 views::View::SetVisible(visible);
315 }
316
317 void FrameMaximizeButton::ProcessStartEvent(const ui::LocatedEvent& event) {
318 DCHECK(is_snap_enabled_);
319 // Prepare the help menu.
320 if (!maximizer_) {
321 maximizer_.reset(new MaximizeBubbleController(
322 this,
323 GetMaximizeBubbleFrameState(),
324 bubble_appearance_delay_ms_));
325 } else {
326 // If the menu did not show up yet, we delay it even a bit more.
327 maximizer_->DelayCreation();
328 }
329 InstallEventFilter();
330 snap_type_ = SNAP_NONE;
331 press_location_ = event.location();
332 exceeded_drag_threshold_ = false;
333 update_timer_.Start(
334 FROM_HERE,
335 base::TimeDelta::FromMilliseconds(kUpdateDelayMS),
336 this,
337 &FrameMaximizeButton::UpdateSnapFromEventLocation);
338 }
339
340 void FrameMaximizeButton::ProcessUpdateEvent(const ui::LocatedEvent& event) {
341 DCHECK(is_snap_enabled_);
342 if (!exceeded_drag_threshold_) {
343 exceeded_drag_threshold_ = views::View::ExceededDragThreshold(
344 event.location() - press_location_);
345 }
346 if (exceeded_drag_threshold_)
347 UpdateSnap(event.location());
348 }
349
350 bool FrameMaximizeButton::ProcessEndEvent(const ui::LocatedEvent& event) {
351 update_timer_.Stop();
352 UninstallEventFilter();
353 bool should_snap = is_snap_enabled_;
354 is_snap_enabled_ = false;
355
356 // Remove our help bubble.
357 maximizer_.reset();
358
359 if (!should_snap || snap_type_ == SNAP_NONE)
360 return false;
361
362 SetState(views::CustomButton::STATE_NORMAL);
363 // SetState will not call SchedulePaint() if state was already set to
364 // STATE_NORMAL during a drag.
365 SchedulePaint();
366 phantom_window_.reset();
367 Snap();
368 return true;
369 }
370
371 void FrameMaximizeButton::Cancel(bool keep_menu_open) {
372 if (!keep_menu_open) {
373 maximizer_.reset();
374 UninstallEventFilter();
375 is_snap_enabled_ = false;
376 }
377 phantom_window_.reset();
378 snap_type_ = SNAP_NONE;
379 update_timer_.Stop();
380 SchedulePaint();
381 }
382
383 void FrameMaximizeButton::InstallEventFilter() {
384 if (escape_event_filter_)
385 return;
386
387 escape_event_filter_.reset(new EscapeEventFilter(this));
388 }
389
390 void FrameMaximizeButton::UninstallEventFilter() {
391 escape_event_filter_.reset(NULL);
392 }
393
394 void FrameMaximizeButton::UpdateSnapFromEventLocation() {
395 // If the drag threshold has been exceeded the snap location is up to date.
396 if (exceeded_drag_threshold_)
397 return;
398 exceeded_drag_threshold_ = true;
399 UpdateSnap(press_location_);
400 }
401
402 void FrameMaximizeButton::UpdateSnap(const gfx::Point& location) {
403 SnapType type = SnapTypeForLocation(location);
404 if (type == snap_type_)
405 return;
406
407 snap_type_ = type;
408 SchedulePaint();
409
410 if (snap_type_ == SNAP_NONE) {
411 phantom_window_.reset();
412 return;
413 }
414
415 if (!phantom_window_) {
416 phantom_window_.reset(
417 new PhantomWindowController(frame_->GetNativeWindow()));
418 }
419 if (maximizer_) {
420 phantom_window_->set_phantom_below_window(maximizer_->GetBubbleWindow());
421 maximizer_->SetSnapType(snap_type_);
422 }
423 phantom_window_->Show(ScreenBoundsForType(snap_type_));
424 }
425
426 SnapType FrameMaximizeButton::SnapTypeForLocation(
427 const gfx::Point& location) const {
428 MaximizeBubbleFrameState maximize_type = GetMaximizeBubbleFrameState();
429 gfx::Vector2d delta(location - press_location_);
430 if (!views::View::ExceededDragThreshold(delta))
431 return maximize_type != FRAME_STATE_FULL ? SNAP_MAXIMIZE : SNAP_RESTORE;
432 if (delta.x() < 0 && delta.y() > delta.x() && delta.y() < -delta.x())
433 return maximize_type == FRAME_STATE_SNAP_LEFT ? SNAP_RESTORE : SNAP_LEFT;
434 if (delta.x() > 0 && delta.y() > -delta.x() && delta.y() < delta.x())
435 return maximize_type == FRAME_STATE_SNAP_RIGHT ? SNAP_RESTORE : SNAP_RIGHT;
436 if (delta.y() > 0)
437 return SNAP_MINIMIZE;
438 return maximize_type != FRAME_STATE_FULL ? SNAP_MAXIMIZE : SNAP_RESTORE;
439 }
440
441 gfx::Rect FrameMaximizeButton::ScreenBoundsForType(SnapType type) const {
442 aura::Window* window = frame_->GetNativeWindow();
443 switch (type) {
444 case SNAP_LEFT:
445 return ScreenUtil::ConvertRectToScreen(
446 window->parent(),
447 wm::GetDefaultLeftSnappedWindowBoundsInParent(window));
448 case SNAP_RIGHT:
449 return ScreenUtil::ConvertRectToScreen(
450 window->parent(),
451 wm::GetDefaultRightSnappedWindowBoundsInParent(window));
452 case SNAP_MAXIMIZE:
453 return ScreenUtil::ConvertRectToScreen(
454 window->parent(),
455 ScreenUtil::GetMaximizedWindowBoundsInParent(window));
456 case SNAP_MINIMIZE: {
457 gfx::Rect rect = GetMinimizeAnimationTargetBoundsInScreen(window);
458 if (!rect.IsEmpty()) {
459 // PhantomWindowController insets slightly, outset it so the phantom
460 // doesn't appear inset.
461 rect.Inset(-8, -8);
462 }
463 return rect;
464 }
465 case SNAP_RESTORE: {
466 wm::WindowState* window_state = wm::GetWindowState(window);
467 return window_state->HasRestoreBounds() ?
468 window_state->GetRestoreBoundsInScreen() :
469 frame_->GetWindowBoundsInScreen();
470 }
471 case SNAP_NONE:
472 NOTREACHED();
473 }
474 return gfx::Rect();
475 }
476
477 void FrameMaximizeButton::Snap() {
478 Shell* shell = Shell::GetInstance();
479 wm::WindowState* window_state = wm::GetWindowState(frame_->GetNativeWindow());
480 switch (snap_type_) {
481 case SNAP_LEFT: {
482 const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
483 window_state->OnWMEvent(&event);
484 shell->metrics()->RecordUserMetricsAction(
485 UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT);
486 break;
487 }
488 case SNAP_RIGHT: {
489 const wm::WMEvent event(wm::WM_EVENT_SNAP_RIGHT);
490 window_state->OnWMEvent(&event);
491 shell->metrics()->RecordUserMetricsAction(
492 UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
493 break;
494 }
495 case SNAP_MAXIMIZE:
496 frame_->Maximize();
497 shell->metrics()->RecordUserMetricsAction(
498 UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE);
499 break;
500 case SNAP_MINIMIZE:
501 frame_->Minimize();
502 shell->metrics()->RecordUserMetricsAction(
503 UMA_WINDOW_MAXIMIZE_BUTTON_MINIMIZE);
504 break;
505 case SNAP_RESTORE:
506 frame_->Restore();
507 shell->metrics()->RecordUserMetricsAction(
508 UMA_WINDOW_MAXIMIZE_BUTTON_RESTORE);
509 break;
510 case SNAP_NONE:
511 NOTREACHED();
512 }
513 }
514
515 MaximizeBubbleFrameState
516 FrameMaximizeButton::GetMaximizeBubbleFrameState() const {
517 wm::WindowState* window_state =
518 wm::GetWindowState(frame_->GetNativeWindow());
519 // When there are no restore bounds, we are in normal mode.
520 if (!window_state->HasRestoreBounds())
521 return FRAME_STATE_NONE;
522 // The normal maximized test can be used.
523 if (frame_->IsMaximized())
524 return FRAME_STATE_FULL;
525 // For Left/right maximize we need to check the dimensions.
526 gfx::Rect bounds = frame_->GetWindowBoundsInScreen();
527 gfx::Rect screen = Shell::GetScreen()->GetDisplayNearestWindow(
528 frame_->GetNativeView()).work_area();
529 if (bounds.width() < (screen.width() * kMinSnapSizePercent) / 100)
530 return FRAME_STATE_NONE;
531 // We might still have a horizontally filled window at this point which we
532 // treat as no special state.
533 if (bounds.y() != screen.y() || bounds.height() != screen.height())
534 return FRAME_STATE_NONE;
535
536 // We have to be in a maximize mode at this point.
537 if (bounds.x() == screen.x())
538 return FRAME_STATE_SNAP_LEFT;
539 if (bounds.right() == screen.right())
540 return FRAME_STATE_SNAP_RIGHT;
541 // If we come here, it is likely caused by the fact that the
542 // "VerticalResizeDoubleClick" stored a restore rectangle. In that case
543 // we allow all maximize operations (and keep the restore rectangle).
544 return FRAME_STATE_NONE;
545 }
546
547 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698