OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/workspace/frame_maximize_button.h" | |
6 | |
7 #include "ash/launcher/launcher.h" | |
8 #include "ash/screen_ash.h" | |
9 #include "ash/shelf/shelf_widget.h" | |
10 #include "ash/shell.h" | |
11 #include "ash/shell_delegate.h" | |
12 #include "ash/touch/touch_uma.h" | |
13 #include "ash/wm/maximize_bubble_controller.h" | |
14 #include "ash/wm/property_util.h" | |
15 #include "ash/wm/window_animations.h" | |
16 #include "ash/wm/window_settings.h" | |
17 #include "ash/wm/workspace/phantom_window_controller.h" | |
18 #include "ash/wm/workspace/snap_sizer.h" | |
19 #include "grit/ash_strings.h" | |
20 #include "ui/aura/client/aura_constants.h" | |
21 #include "ui/aura/window.h" | |
22 #include "ui/base/events/event.h" | |
23 #include "ui/base/events/event_handler.h" | |
24 #include "ui/base/l10n/l10n_util.h" | |
25 #include "ui/base/resource/resource_bundle.h" | |
26 #include "ui/gfx/image/image.h" | |
27 #include "ui/gfx/screen.h" | |
28 #include "ui/views/widget/widget.h" | |
29 #include "ui/views/window/non_client_view.h" | |
30 | |
31 using ash::internal::SnapSizer; | |
32 | |
33 namespace ash { | |
34 | |
35 namespace { | |
36 | |
37 // Delay before forcing an update of the snap location. | |
38 const int kUpdateDelayMS = 400; | |
39 | |
40 // The delay of the bubble appearance. | |
41 const int kBubbleAppearanceDelayMS = 500; | |
42 | |
43 // The minimum sanp size in percent of the screen width. | |
44 const int kMinSnapSizePercent = 50; | |
45 } | |
46 | |
47 // EscapeEventFilter is installed on the RootWindow to track when the escape key | |
48 // is pressed. We use an EventFilter for this as the FrameMaximizeButton | |
49 // normally does not get focus. | |
50 class FrameMaximizeButton::EscapeEventFilter : public ui::EventHandler { | |
51 public: | |
52 explicit EscapeEventFilter(FrameMaximizeButton* button); | |
53 virtual ~EscapeEventFilter(); | |
54 | |
55 // EventFilter overrides: | |
56 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; | |
57 | |
58 private: | |
59 FrameMaximizeButton* button_; | |
60 | |
61 DISALLOW_COPY_AND_ASSIGN(EscapeEventFilter); | |
62 }; | |
63 | |
64 FrameMaximizeButton::EscapeEventFilter::EscapeEventFilter( | |
65 FrameMaximizeButton* button) | |
66 : button_(button) { | |
67 Shell::GetInstance()->AddPreTargetHandler(this); | |
68 } | |
69 | |
70 FrameMaximizeButton::EscapeEventFilter::~EscapeEventFilter() { | |
71 Shell::GetInstance()->RemovePreTargetHandler(this); | |
72 } | |
73 | |
74 void FrameMaximizeButton::EscapeEventFilter::OnKeyEvent( | |
75 ui::KeyEvent* event) { | |
76 if (event->type() == ui::ET_KEY_PRESSED && | |
77 event->key_code() == ui::VKEY_ESCAPE) { | |
78 button_->Cancel(false); | |
79 } | |
80 } | |
81 | |
82 // FrameMaximizeButton --------------------------------------------------------- | |
83 | |
84 FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener, | |
85 views::NonClientFrameView* frame) | |
86 : ImageButton(listener), | |
87 frame_(frame), | |
88 is_snap_enabled_(false), | |
89 exceeded_drag_threshold_(false), | |
90 widget_(NULL), | |
91 press_is_gesture_(false), | |
92 snap_type_(SNAP_NONE), | |
93 bubble_appearance_delay_ms_(kBubbleAppearanceDelayMS) { | |
94 // TODO(sky): nuke this. It's temporary while we don't have good images. | |
95 SetImageAlignment(ALIGN_LEFT, ALIGN_BOTTOM); | |
96 | |
97 if (ash::Shell::IsForcedMaximizeMode()) | |
98 views::View::SetVisible(false); | |
99 } | |
100 | |
101 FrameMaximizeButton::~FrameMaximizeButton() { | |
102 // Before the window gets destroyed, the maximizer dialog needs to be shut | |
103 // down since it would otherwise call into a deleted object. | |
104 maximizer_.reset(); | |
105 if (widget_) | |
106 OnWindowDestroying(widget_->GetNativeWindow()); | |
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_ && snap_sizer_)) | |
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 // Note: There is no hover with touch - we can therefore pass false for touch | |
145 // operations. | |
146 UpdateSnap(location, true, false); | |
147 } | |
148 | |
149 void FrameMaximizeButton::ExecuteSnapAndCloseMenu(SnapType snap_type) { | |
150 // We can come here with no snap type set in case that the mouse opened the | |
151 // maximize button and a touch event "touched" a button. | |
152 if (snap_type_ == SNAP_NONE) | |
153 SnapButtonHovered(snap_type); | |
154 | |
155 Cancel(true); | |
156 // Tell our menu to close. | |
157 maximizer_.reset(); | |
158 snap_type_ = snap_type; | |
159 // Since Snap might destroy |this|, but the snap_sizer needs to be destroyed, | |
160 // The ownership of the snap_sizer is taken now. | |
161 scoped_ptr<SnapSizer> snap_sizer(snap_sizer_.release()); | |
162 Snap(*snap_sizer.get()); | |
163 } | |
164 | |
165 void FrameMaximizeButton::DestroyMaximizeMenu() { | |
166 Cancel(false); | |
167 } | |
168 | |
169 void FrameMaximizeButton::OnWindowBoundsChanged( | |
170 aura::Window* window, | |
171 const gfx::Rect& old_bounds, | |
172 const gfx::Rect& new_bounds) { | |
173 Cancel(false); | |
174 } | |
175 | |
176 void FrameMaximizeButton::OnWindowPropertyChanged(aura::Window* window, | |
177 const void* key, | |
178 intptr_t old) { | |
179 Cancel(false); | |
180 } | |
181 | |
182 void FrameMaximizeButton::OnWindowDestroying(aura::Window* window) { | |
183 maximizer_.reset(); | |
184 if (widget_) { | |
185 CHECK_EQ(widget_->GetNativeWindow(), window); | |
186 widget_->GetNativeWindow()->RemoveObserver(this); | |
187 widget_->RemoveObserver(this); | |
188 widget_ = NULL; | |
189 } | |
190 } | |
191 | |
192 void FrameMaximizeButton::OnWidgetActivationChanged(views::Widget* widget, | |
193 bool active) { | |
194 // Upon losing focus, the control bubble should hide. | |
195 if (!active && maximizer_) | |
196 maximizer_.reset(); | |
197 } | |
198 | |
199 bool FrameMaximizeButton::OnMousePressed(const ui::MouseEvent& event) { | |
200 // If we are already in a mouse click / drag operation, a second button down | |
201 // call will cancel (this addresses crbug.com/143755). | |
202 if (is_snap_enabled_) { | |
203 Cancel(false); | |
204 } else { | |
205 is_snap_enabled_ = event.IsOnlyLeftMouseButton(); | |
206 if (is_snap_enabled_) | |
207 ProcessStartEvent(event); | |
208 } | |
209 ImageButton::OnMousePressed(event); | |
210 return true; | |
211 } | |
212 | |
213 void FrameMaximizeButton::OnMouseEntered(const ui::MouseEvent& event) { | |
214 ImageButton::OnMouseEntered(event); | |
215 if (!maximizer_) { | |
216 DCHECK(GetWidget()); | |
217 if (!widget_) { | |
218 widget_ = frame_->GetWidget(); | |
219 widget_->GetNativeWindow()->AddObserver(this); | |
220 widget_->AddObserver(this); | |
221 } | |
222 maximizer_.reset(new MaximizeBubbleController( | |
223 this, | |
224 GetMaximizeBubbleFrameState(), | |
225 bubble_appearance_delay_ms_)); | |
226 } | |
227 } | |
228 | |
229 void FrameMaximizeButton::OnMouseExited(const ui::MouseEvent& event) { | |
230 ImageButton::OnMouseExited(event); | |
231 // Remove the bubble menu when the button is not pressed and the mouse is not | |
232 // within the bubble. | |
233 if (!is_snap_enabled_ && maximizer_) { | |
234 if (maximizer_->GetBubbleWindow()) { | |
235 gfx::Point screen_location = Shell::GetScreen()->GetCursorScreenPoint(); | |
236 if (!maximizer_->GetBubbleWindow()->GetBoundsInScreen().Contains( | |
237 screen_location)) { | |
238 maximizer_.reset(); | |
239 // Make sure that all remaining snap hover states get removed. | |
240 SnapButtonHovered(SNAP_NONE); | |
241 } | |
242 } else { | |
243 // The maximize dialog does not show up immediately after creating the | |
244 // |mazimizer_|. Destroy the dialog therefore before it shows up. | |
245 maximizer_.reset(); | |
246 } | |
247 } | |
248 } | |
249 | |
250 bool FrameMaximizeButton::OnMouseDragged(const ui::MouseEvent& event) { | |
251 if (is_snap_enabled_) | |
252 ProcessUpdateEvent(event); | |
253 return ImageButton::OnMouseDragged(event); | |
254 } | |
255 | |
256 void FrameMaximizeButton::OnMouseReleased(const ui::MouseEvent& event) { | |
257 maximizer_.reset(); | |
258 bool snap_was_enabled = is_snap_enabled_; | |
259 if (!ProcessEndEvent(event) && snap_was_enabled) | |
260 ImageButton::OnMouseReleased(event); | |
261 // At this point |this| might be already destroyed. | |
262 } | |
263 | |
264 void FrameMaximizeButton::OnMouseCaptureLost() { | |
265 Cancel(false); | |
266 ImageButton::OnMouseCaptureLost(); | |
267 } | |
268 | |
269 void FrameMaximizeButton::OnGestureEvent(ui::GestureEvent* event) { | |
270 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { | |
271 is_snap_enabled_ = true; | |
272 ProcessStartEvent(*event); | |
273 event->SetHandled(); | |
274 return; | |
275 } | |
276 | |
277 if (event->type() == ui::ET_GESTURE_TAP || | |
278 (event->type() == ui::ET_GESTURE_SCROLL_END && is_snap_enabled_) || | |
279 event->type() == ui::ET_SCROLL_FLING_START) { | |
280 // The position of the event may have changed from the previous event (both | |
281 // for TAP and SCROLL_END). So it is necessary to update the snap-state for | |
282 // the current event. | |
283 ProcessUpdateEvent(*event); | |
284 if (event->type() == ui::ET_GESTURE_TAP) { | |
285 snap_type_ = SnapTypeForLocation(event->location()); | |
286 TouchUMA::GetInstance()->RecordGestureAction( | |
287 TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP); | |
288 } | |
289 ProcessEndEvent(*event); | |
290 event->SetHandled(); | |
291 return; | |
292 } | |
293 | |
294 if (is_snap_enabled_) { | |
295 if (event->type() == ui::ET_GESTURE_END && | |
296 event->details().touch_points() == 1) { | |
297 // The position of the event may have changed from the previous event. So | |
298 // it is necessary to update the snap-state for the current event. | |
299 ProcessUpdateEvent(*event); | |
300 snap_type_ = SnapTypeForLocation(event->location()); | |
301 ProcessEndEvent(*event); | |
302 event->SetHandled(); | |
303 return; | |
304 } | |
305 | |
306 if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE || | |
307 event->type() == ui::ET_GESTURE_SCROLL_BEGIN) { | |
308 ProcessUpdateEvent(*event); | |
309 event->SetHandled(); | |
310 return; | |
311 } | |
312 } | |
313 | |
314 ImageButton::OnGestureEvent(event); | |
315 } | |
316 | |
317 void FrameMaximizeButton::SetVisible(bool visible) { | |
318 // In the enforced maximized mode we do not allow to be made visible. | |
319 if (ash::Shell::IsForcedMaximizeMode()) | |
320 return; | |
321 | |
322 views::View::SetVisible(visible); | |
323 } | |
324 | |
325 void FrameMaximizeButton::ProcessStartEvent(const ui::LocatedEvent& event) { | |
326 DCHECK(is_snap_enabled_); | |
327 // Prepare the help menu. | |
328 if (!maximizer_) { | |
329 maximizer_.reset(new MaximizeBubbleController( | |
330 this, | |
331 GetMaximizeBubbleFrameState(), | |
332 bubble_appearance_delay_ms_)); | |
333 } else { | |
334 // If the menu did not show up yet, we delay it even a bit more. | |
335 maximizer_->DelayCreation(); | |
336 } | |
337 snap_sizer_.reset(NULL); | |
338 InstallEventFilter(); | |
339 snap_type_ = SNAP_NONE; | |
340 press_location_ = event.location(); | |
341 press_is_gesture_ = event.IsGestureEvent(); | |
342 exceeded_drag_threshold_ = false; | |
343 update_timer_.Start( | |
344 FROM_HERE, | |
345 base::TimeDelta::FromMilliseconds(kUpdateDelayMS), | |
346 this, | |
347 &FrameMaximizeButton::UpdateSnapFromEventLocation); | |
348 } | |
349 | |
350 void FrameMaximizeButton::ProcessUpdateEvent(const ui::LocatedEvent& event) { | |
351 DCHECK(is_snap_enabled_); | |
352 if (!exceeded_drag_threshold_) { | |
353 exceeded_drag_threshold_ = views::View::ExceededDragThreshold( | |
354 event.location() - press_location_); | |
355 } | |
356 if (exceeded_drag_threshold_) | |
357 UpdateSnap(event.location(), false, event.IsGestureEvent()); | |
358 } | |
359 | |
360 bool FrameMaximizeButton::ProcessEndEvent(const ui::LocatedEvent& event) { | |
361 update_timer_.Stop(); | |
362 UninstallEventFilter(); | |
363 bool should_snap = is_snap_enabled_; | |
364 is_snap_enabled_ = false; | |
365 | |
366 // Remove our help bubble. | |
367 maximizer_.reset(); | |
368 | |
369 if (!should_snap || snap_type_ == SNAP_NONE) | |
370 return false; | |
371 | |
372 SetState(views::CustomButton::STATE_NORMAL); | |
373 // SetState will not call SchedulePaint() if state was already set to | |
374 // STATE_NORMAL during a drag. | |
375 SchedulePaint(); | |
376 phantom_window_.reset(); | |
377 // Since Snap might destroy |this|, but the snap_sizer needs to be destroyed, | |
378 // The ownership of the snap_sizer is taken now. | |
379 scoped_ptr<SnapSizer> snap_sizer(snap_sizer_.release()); | |
380 Snap(*snap_sizer.get()); | |
381 return true; | |
382 } | |
383 | |
384 void FrameMaximizeButton::Cancel(bool keep_menu_open) { | |
385 if (!keep_menu_open) { | |
386 maximizer_.reset(); | |
387 UninstallEventFilter(); | |
388 is_snap_enabled_ = false; | |
389 snap_sizer_.reset(); | |
390 } | |
391 phantom_window_.reset(); | |
392 snap_type_ = SNAP_NONE; | |
393 update_timer_.Stop(); | |
394 SchedulePaint(); | |
395 } | |
396 | |
397 void FrameMaximizeButton::InstallEventFilter() { | |
398 if (escape_event_filter_) | |
399 return; | |
400 | |
401 escape_event_filter_.reset(new EscapeEventFilter(this)); | |
402 } | |
403 | |
404 void FrameMaximizeButton::UninstallEventFilter() { | |
405 escape_event_filter_.reset(NULL); | |
406 } | |
407 | |
408 void FrameMaximizeButton::UpdateSnapFromEventLocation() { | |
409 // If the drag threshold has been exceeded the snap location is up to date. | |
410 if (exceeded_drag_threshold_) | |
411 return; | |
412 exceeded_drag_threshold_ = true; | |
413 UpdateSnap(press_location_, false, press_is_gesture_); | |
414 } | |
415 | |
416 void FrameMaximizeButton::UpdateSnap(const gfx::Point& location, | |
417 bool select_default, | |
418 bool is_touch) { | |
419 SnapType type = SnapTypeForLocation(location); | |
420 if (type == snap_type_) { | |
421 if (snap_sizer_) { | |
422 snap_sizer_->Update(LocationForSnapSizer(location)); | |
423 phantom_window_->Show(ScreenAsh::ConvertRectToScreen( | |
424 frame_->GetWidget()->GetNativeView()->parent(), | |
425 snap_sizer_->target_bounds())); | |
426 } | |
427 return; | |
428 } | |
429 | |
430 snap_type_ = type; | |
431 snap_sizer_.reset(); | |
432 SchedulePaint(); | |
433 | |
434 if (snap_type_ == SNAP_NONE) { | |
435 phantom_window_.reset(); | |
436 return; | |
437 } | |
438 | |
439 if (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT) { | |
440 SnapSizer::Edge snap_edge = snap_type_ == SNAP_LEFT ? | |
441 SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE; | |
442 SnapSizer::InputType input_type = | |
443 is_touch ? SnapSizer::TOUCH_MAXIMIZE_BUTTON_INPUT : | |
444 SnapSizer::OTHER_INPUT; | |
445 snap_sizer_.reset(new SnapSizer(frame_->GetWidget()->GetNativeWindow(), | |
446 LocationForSnapSizer(location), | |
447 snap_edge, | |
448 input_type)); | |
449 if (select_default) | |
450 snap_sizer_->SelectDefaultSizeAndDisableResize(); | |
451 } | |
452 if (!phantom_window_) { | |
453 phantom_window_.reset(new internal::PhantomWindowController( | |
454 frame_->GetWidget()->GetNativeWindow())); | |
455 } | |
456 if (maximizer_) { | |
457 phantom_window_->set_phantom_below_window(maximizer_->GetBubbleWindow()); | |
458 maximizer_->SetSnapType(snap_type_); | |
459 } | |
460 phantom_window_->Show( | |
461 ScreenBoundsForType(snap_type_, *snap_sizer_.get())); | |
462 } | |
463 | |
464 SnapType FrameMaximizeButton::SnapTypeForLocation( | |
465 const gfx::Point& location) const { | |
466 MaximizeBubbleFrameState maximize_type = GetMaximizeBubbleFrameState(); | |
467 gfx::Vector2d delta(location - press_location_); | |
468 if (!views::View::ExceededDragThreshold(delta)) | |
469 return maximize_type != FRAME_STATE_FULL ? SNAP_MAXIMIZE : SNAP_RESTORE; | |
470 if (delta.x() < 0 && delta.y() > delta.x() && delta.y() < -delta.x()) | |
471 return maximize_type == FRAME_STATE_SNAP_LEFT ? SNAP_RESTORE : SNAP_LEFT; | |
472 if (delta.x() > 0 && delta.y() > -delta.x() && delta.y() < delta.x()) | |
473 return maximize_type == FRAME_STATE_SNAP_RIGHT ? SNAP_RESTORE : SNAP_RIGHT; | |
474 if (delta.y() > 0) | |
475 return SNAP_MINIMIZE; | |
476 return maximize_type != FRAME_STATE_FULL ? SNAP_MAXIMIZE : SNAP_RESTORE; | |
477 } | |
478 | |
479 gfx::Rect FrameMaximizeButton::ScreenBoundsForType( | |
480 SnapType type, | |
481 const SnapSizer& snap_sizer) const { | |
482 aura::Window* window = frame_->GetWidget()->GetNativeWindow(); | |
483 switch (type) { | |
484 case SNAP_LEFT: | |
485 case SNAP_RIGHT: | |
486 return ScreenAsh::ConvertRectToScreen( | |
487 frame_->GetWidget()->GetNativeView()->parent(), | |
488 snap_sizer.target_bounds()); | |
489 case SNAP_MAXIMIZE: | |
490 return ScreenAsh::ConvertRectToScreen( | |
491 window->parent(), | |
492 ScreenAsh::GetMaximizedWindowBoundsInParent(window)); | |
493 case SNAP_MINIMIZE: { | |
494 gfx::Rect rect = GetMinimizeAnimationTargetBoundsInScreen(window); | |
495 if (!rect.IsEmpty()) { | |
496 // PhantomWindowController insets slightly, outset it so the phantom | |
497 // doesn't appear inset. | |
498 rect.Inset(-8, -8); | |
499 } | |
500 return rect; | |
501 } | |
502 case SNAP_RESTORE: { | |
503 const gfx::Rect* restore = GetRestoreBoundsInScreen(window); | |
504 return restore ? | |
505 *restore : frame_->GetWidget()->GetWindowBoundsInScreen(); | |
506 } | |
507 case SNAP_NONE: | |
508 NOTREACHED(); | |
509 } | |
510 return gfx::Rect(); | |
511 } | |
512 | |
513 gfx::Point FrameMaximizeButton::LocationForSnapSizer( | |
514 const gfx::Point& location) const { | |
515 gfx::Point result(location); | |
516 views::View::ConvertPointToScreen(this, &result); | |
517 return result; | |
518 } | |
519 | |
520 void FrameMaximizeButton::Snap(const SnapSizer& snap_sizer) { | |
521 ash::Shell* shell = ash::Shell::GetInstance(); | |
522 views::Widget* widget = frame_->GetWidget(); | |
523 switch (snap_type_) { | |
524 case SNAP_LEFT: | |
525 case SNAP_RIGHT: { | |
526 shell->delegate()->RecordUserMetricsAction( | |
527 snap_type_ == SNAP_LEFT ? | |
528 ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT : | |
529 ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT); | |
530 // Get the bounds in screen coordinates for restore purposes. | |
531 gfx::Rect restore = widget->GetWindowBoundsInScreen(); | |
532 if (widget->IsMaximized() || widget->IsFullscreen()) { | |
533 aura::Window* window = widget->GetNativeWindow(); | |
534 // In case of maximized we have a restore boundary. | |
535 DCHECK(ash::GetRestoreBoundsInScreen(window)); | |
536 // If it was maximized we need to recover the old restore set. | |
537 restore = *ash::GetRestoreBoundsInScreen(window); | |
538 | |
539 // The auto position manager will kick in when this is the only window. | |
540 // To avoid interference with it we tell it temporarily to not change | |
541 // the coordinates of this window. | |
542 wm::WindowSettings* settings = wm::GetWindowSettings(window); | |
543 bool was_managed = settings->window_position_managed(); | |
544 settings->set_window_position_managed(false); | |
545 | |
546 // Set the restore size we want to restore to. | |
547 ash::SetRestoreBoundsInScreen(window, | |
548 ScreenBoundsForType(snap_type_, | |
549 snap_sizer)); | |
550 widget->Restore(); | |
551 | |
552 // After the window is where we want it to be we allow the window to be | |
553 // auto managed again. | |
554 settings->set_window_position_managed(was_managed); | |
555 } else { | |
556 // Others might also have set up a restore rectangle already. If so, | |
557 // we should not overwrite the restore rectangle. | |
558 bool restore_set = | |
559 GetRestoreBoundsInScreen(widget->GetNativeWindow()) != NULL; | |
560 widget->SetBounds(ScreenBoundsForType(snap_type_, snap_sizer)); | |
561 if (restore_set) | |
562 break; | |
563 } | |
564 // Remember the widow's bounds for restoration. | |
565 ash::SetRestoreBoundsInScreen(widget->GetNativeWindow(), restore); | |
566 break; | |
567 } | |
568 case SNAP_MAXIMIZE: | |
569 widget->Maximize(); | |
570 shell->delegate()->RecordUserMetricsAction( | |
571 ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE); | |
572 break; | |
573 case SNAP_MINIMIZE: | |
574 widget->Minimize(); | |
575 shell->delegate()->RecordUserMetricsAction( | |
576 ash::UMA_WINDOW_MAXIMIZE_BUTTON_MINIMIZE); | |
577 break; | |
578 case SNAP_RESTORE: | |
579 widget->Restore(); | |
580 shell->delegate()->RecordUserMetricsAction( | |
581 ash::UMA_WINDOW_MAXIMIZE_BUTTON_RESTORE); | |
582 break; | |
583 case SNAP_NONE: | |
584 NOTREACHED(); | |
585 } | |
586 } | |
587 | |
588 MaximizeBubbleFrameState | |
589 FrameMaximizeButton::GetMaximizeBubbleFrameState() const { | |
590 // When there are no restore bounds, we are in normal mode. | |
591 if (!ash::GetRestoreBoundsInScreen( | |
592 frame_->GetWidget()->GetNativeWindow())) | |
593 return FRAME_STATE_NONE; | |
594 // The normal maximized test can be used. | |
595 if (frame_->GetWidget()->IsMaximized()) | |
596 return FRAME_STATE_FULL; | |
597 // For Left/right maximize we need to check the dimensions. | |
598 gfx::Rect bounds = frame_->GetWidget()->GetWindowBoundsInScreen(); | |
599 gfx::Rect screen = Shell::GetScreen()->GetDisplayNearestWindow( | |
600 frame_->GetWidget()->GetNativeView()).work_area(); | |
601 if (bounds.width() < (screen.width() * kMinSnapSizePercent) / 100) | |
602 return FRAME_STATE_NONE; | |
603 // We might still have a horizontally filled window at this point which we | |
604 // treat as no special state. | |
605 if (bounds.y() != screen.y() || bounds.height() != screen.height()) | |
606 return FRAME_STATE_NONE; | |
607 | |
608 // We have to be in a maximize mode at this point. | |
609 if (bounds.x() == screen.x()) | |
610 return FRAME_STATE_SNAP_LEFT; | |
611 if (bounds.right() == screen.right()) | |
612 return FRAME_STATE_SNAP_RIGHT; | |
613 // If we come here, it is likely caused by the fact that the | |
614 // "VerticalResizeDoubleClick" stored a restore rectangle. In that case | |
615 // we allow all maximize operations (and keep the restore rectangle). | |
616 return FRAME_STATE_NONE; | |
617 } | |
618 | |
619 } // namespace ash | |
OLD | NEW |