| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ash/wm/workspace/frame_maximize_button.h" | 5 #include "ash/wm/workspace/frame_maximize_button.h" |
| 6 | 6 |
| 7 #include "ash/launcher/launcher.h" | 7 #include "ash/launcher/launcher.h" |
| 8 #include "ash/screen_ash.h" | 8 #include "ash/screen_ash.h" |
| 9 #include "ash/shell.h" | 9 #include "ash/shell.h" |
| 10 #include "ash/wm/property_util.h" | 10 #include "ash/wm/property_util.h" |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 namespace ash { | 28 namespace ash { |
| 29 | 29 |
| 30 namespace { | 30 namespace { |
| 31 | 31 |
| 32 // Delay before forcing an update of the snap location. | 32 // Delay before forcing an update of the snap location. |
| 33 const int kUpdateDelayMS = 400; | 33 const int kUpdateDelayMS = 400; |
| 34 | 34 |
| 35 // The delay of the bubble appearance. | 35 // The delay of the bubble appearance. |
| 36 const int kBubbleAppearanceDelayMS = 500; | 36 const int kBubbleAppearanceDelayMS = 500; |
| 37 | 37 |
| 38 // The minimum sanp size in percent of the screen width. |
| 39 const int kMinSnapSizePercent = 50; |
| 38 } | 40 } |
| 39 | 41 |
| 40 // EscapeEventFilter is installed on the RootWindow to track when the escape key | 42 // EscapeEventFilter is installed on the RootWindow to track when the escape key |
| 41 // is pressed. We use an EventFilter for this as the FrameMaximizeButton | 43 // is pressed. We use an EventFilter for this as the FrameMaximizeButton |
| 42 // normally does not get focus. | 44 // normally does not get focus. |
| 43 class FrameMaximizeButton::EscapeEventFilter : public aura::EventFilter { | 45 class FrameMaximizeButton::EscapeEventFilter : public aura::EventFilter { |
| 44 public: | 46 public: |
| 45 explicit EscapeEventFilter(FrameMaximizeButton* button); | 47 explicit EscapeEventFilter(FrameMaximizeButton* button); |
| 46 virtual ~EscapeEventFilter(); | 48 virtual ~EscapeEventFilter(); |
| 47 | 49 |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 void FrameMaximizeButton::OnMouseEntered(const ui::MouseEvent& event) { | 207 void FrameMaximizeButton::OnMouseEntered(const ui::MouseEvent& event) { |
| 206 ImageButton::OnMouseEntered(event); | 208 ImageButton::OnMouseEntered(event); |
| 207 if (!maximizer_.get()) { | 209 if (!maximizer_.get()) { |
| 208 DCHECK(GetWidget()); | 210 DCHECK(GetWidget()); |
| 209 if (!window_) { | 211 if (!window_) { |
| 210 window_ = frame_->GetWidget()->GetNativeWindow(); | 212 window_ = frame_->GetWidget()->GetNativeWindow(); |
| 211 window_->AddObserver(this); | 213 window_->AddObserver(this); |
| 212 } | 214 } |
| 213 maximizer_.reset(new MaximizeBubbleController( | 215 maximizer_.reset(new MaximizeBubbleController( |
| 214 this, | 216 this, |
| 215 frame_->GetWidget()->IsMaximized(), | 217 GetMaximizeBubbleFrameState(), |
| 216 bubble_appearance_delay_ms_)); | 218 bubble_appearance_delay_ms_)); |
| 217 } | 219 } |
| 218 } | 220 } |
| 219 | 221 |
| 220 void FrameMaximizeButton::OnMouseExited(const ui::MouseEvent& event) { | 222 void FrameMaximizeButton::OnMouseExited(const ui::MouseEvent& event) { |
| 221 ImageButton::OnMouseExited(event); | 223 ImageButton::OnMouseExited(event); |
| 222 // Remove the bubble menu when the button is not pressed and the mouse is not | 224 // Remove the bubble menu when the button is not pressed and the mouse is not |
| 223 // within the bubble. | 225 // within the bubble. |
| 224 if (!is_snap_enabled_ && maximizer_.get()) { | 226 if (!is_snap_enabled_ && maximizer_.get()) { |
| 225 if (maximizer_->GetBubbleWindow()) { | 227 if (maximizer_->GetBubbleWindow()) { |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 | 299 |
| 298 return ImageButton::OnGestureEvent(event); | 300 return ImageButton::OnGestureEvent(event); |
| 299 } | 301 } |
| 300 | 302 |
| 301 void FrameMaximizeButton::ProcessStartEvent(const ui::LocatedEvent& event) { | 303 void FrameMaximizeButton::ProcessStartEvent(const ui::LocatedEvent& event) { |
| 302 DCHECK(is_snap_enabled_); | 304 DCHECK(is_snap_enabled_); |
| 303 // Prepare the help menu. | 305 // Prepare the help menu. |
| 304 if (!maximizer_.get()) { | 306 if (!maximizer_.get()) { |
| 305 maximizer_.reset(new MaximizeBubbleController( | 307 maximizer_.reset(new MaximizeBubbleController( |
| 306 this, | 308 this, |
| 307 frame_->GetWidget()->IsMaximized(), | 309 GetMaximizeBubbleFrameState(), |
| 308 bubble_appearance_delay_ms_)); | 310 bubble_appearance_delay_ms_)); |
| 309 } else { | 311 } else { |
| 310 // If the menu did not show up yet, we delay it even a bit more. | 312 // If the menu did not show up yet, we delay it even a bit more. |
| 311 maximizer_->DelayCreation(); | 313 maximizer_->DelayCreation(); |
| 312 } | 314 } |
| 313 snap_sizer_.reset(NULL); | 315 snap_sizer_.reset(NULL); |
| 314 InstallEventFilter(); | 316 InstallEventFilter(); |
| 315 snap_type_ = SNAP_NONE; | 317 snap_type_ = SNAP_NONE; |
| 316 press_location_ = event.location(); | 318 press_location_ = event.location(); |
| 317 exceeded_drag_threshold_ = false; | 319 exceeded_drag_threshold_ = false; |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 426 if (maximizer_.get()) { | 428 if (maximizer_.get()) { |
| 427 phantom_window_->set_phantom_below_window(maximizer_->GetBubbleWindow()); | 429 phantom_window_->set_phantom_below_window(maximizer_->GetBubbleWindow()); |
| 428 maximizer_->SetSnapType(snap_type_); | 430 maximizer_->SetSnapType(snap_type_); |
| 429 } | 431 } |
| 430 phantom_window_->Show( | 432 phantom_window_->Show( |
| 431 ScreenBoundsForType(snap_type_, *snap_sizer_.get()), NULL); | 433 ScreenBoundsForType(snap_type_, *snap_sizer_.get()), NULL); |
| 432 } | 434 } |
| 433 | 435 |
| 434 SnapType FrameMaximizeButton::SnapTypeForLocation( | 436 SnapType FrameMaximizeButton::SnapTypeForLocation( |
| 435 const gfx::Point& location) const { | 437 const gfx::Point& location) const { |
| 438 MaximizeBubbleFrameState maximize_type = GetMaximizeBubbleFrameState(); |
| 436 int delta_x = location.x() - press_location_.x(); | 439 int delta_x = location.x() - press_location_.x(); |
| 437 int delta_y = location.y() - press_location_.y(); | 440 int delta_y = location.y() - press_location_.y(); |
| 438 if (!views::View::ExceededDragThreshold(delta_x, delta_y)) | 441 if (!views::View::ExceededDragThreshold(delta_x, delta_y)) |
| 439 return !frame_->GetWidget()->IsMaximized() ? SNAP_MAXIMIZE : SNAP_RESTORE; | 442 return maximize_type != FRAME_STATE_FULL ? SNAP_MAXIMIZE : SNAP_RESTORE; |
| 440 else if (delta_x < 0 && delta_y > delta_x && delta_y < -delta_x) | 443 else if (delta_x < 0 && delta_y > delta_x && delta_y < -delta_x) |
| 441 return SNAP_LEFT; | 444 return maximize_type == FRAME_STATE_SNAP_LEFT ? SNAP_RESTORE : SNAP_LEFT; |
| 442 else if (delta_x > 0 && delta_y > -delta_x && delta_y < delta_x) | 445 else if (delta_x > 0 && delta_y > -delta_x && delta_y < delta_x) |
| 443 return SNAP_RIGHT; | 446 return maximize_type == FRAME_STATE_SNAP_RIGHT ? SNAP_RESTORE : SNAP_RIGHT; |
| 444 else if (delta_y > 0) | 447 else if (delta_y > 0) |
| 445 return SNAP_MINIMIZE; | 448 return SNAP_MINIMIZE; |
| 446 return !frame_->GetWidget()->IsMaximized() ? SNAP_MAXIMIZE : SNAP_RESTORE; | 449 return maximize_type != FRAME_STATE_FULL ? SNAP_MAXIMIZE : SNAP_RESTORE; |
| 447 } | 450 } |
| 448 | 451 |
| 449 gfx::Rect FrameMaximizeButton::ScreenBoundsForType( | 452 gfx::Rect FrameMaximizeButton::ScreenBoundsForType( |
| 450 SnapType type, | 453 SnapType type, |
| 451 const SnapSizer& snap_sizer) const { | 454 const SnapSizer& snap_sizer) const { |
| 452 aura::Window* window = frame_->GetWidget()->GetNativeWindow(); | 455 aura::Window* window = frame_->GetWidget()->GetNativeWindow(); |
| 453 switch (type) { | 456 switch (type) { |
| 454 case SNAP_LEFT: | 457 case SNAP_LEFT: |
| 455 case SNAP_RIGHT: | 458 case SNAP_RIGHT: |
| 456 return ScreenAsh::ConvertRectToScreen( | 459 return ScreenAsh::ConvertRectToScreen( |
| 457 frame_->GetWidget()->GetNativeView()->parent(), | 460 frame_->GetWidget()->GetNativeView()->parent(), |
| 458 snap_sizer.target_bounds()); | 461 snap_sizer.target_bounds()); |
| 459 case SNAP_MAXIMIZE: | 462 case SNAP_MAXIMIZE: |
| 460 return ScreenAsh::ConvertRectToScreen( | 463 return ScreenAsh::ConvertRectToScreen( |
| 461 window->parent(), | 464 window->parent(), |
| 462 ScreenAsh::GetMaximizedWindowBoundsInParent(window)); | 465 ScreenAsh::GetMaximizedWindowBoundsInParent(window)); |
| 463 case SNAP_MINIMIZE: { | 466 case SNAP_MINIMIZE: { |
| 464 Launcher* launcher = Shell::GetInstance()->launcher(); | 467 Launcher* launcher = Shell::GetInstance()->launcher(); |
| 465 gfx::Rect item_rect(launcher->GetScreenBoundsOfItemIconForWindow(window)); | 468 gfx::Rect item_rect(launcher->GetScreenBoundsOfItemIconForWindow( |
| 469 window)); |
| 466 if (!item_rect.IsEmpty()) { | 470 if (!item_rect.IsEmpty()) { |
| 467 // PhantomWindowController insets slightly, outset it so the phantom | 471 // PhantomWindowController insets slightly, outset it so the phantom |
| 468 // doesn't appear inset. | 472 // doesn't appear inset. |
| 469 item_rect.Inset(-8, -8); | 473 item_rect.Inset(-8, -8); |
| 470 return item_rect; | 474 return item_rect; |
| 471 } | 475 } |
| 472 return launcher->widget()->GetWindowBoundsInScreen(); | 476 return launcher->widget()->GetWindowBoundsInScreen(); |
| 473 } | 477 } |
| 474 case SNAP_RESTORE: { | 478 case SNAP_RESTORE: { |
| 475 const gfx::Rect* restore = GetRestoreBoundsInScreen(window); | 479 const gfx::Rect* restore = GetRestoreBoundsInScreen(window); |
| 476 return restore ? | 480 return restore ? |
| 477 *restore : frame_->GetWidget()->GetWindowBoundsInScreen(); | 481 *restore : frame_->GetWidget()->GetWindowBoundsInScreen(); |
| 478 } | 482 } |
| 479 case SNAP_NONE: | 483 case SNAP_NONE: |
| 480 NOTREACHED(); | 484 NOTREACHED(); |
| 481 } | 485 } |
| 482 return gfx::Rect(); | 486 return gfx::Rect(); |
| 483 } | 487 } |
| 484 | 488 |
| 485 gfx::Point FrameMaximizeButton::LocationForSnapSizer( | 489 gfx::Point FrameMaximizeButton::LocationForSnapSizer( |
| 486 const gfx::Point& location) const { | 490 const gfx::Point& location) const { |
| 487 gfx::Point result(location); | 491 gfx::Point result(location); |
| 488 views::View::ConvertPointToScreen(this, &result); | 492 views::View::ConvertPointToScreen(this, &result); |
| 489 return result; | 493 return result; |
| 490 } | 494 } |
| 491 | 495 |
| 492 void FrameMaximizeButton::Snap(const SnapSizer& snap_sizer) { | 496 void FrameMaximizeButton::Snap(const SnapSizer& snap_sizer) { |
| 497 views::Widget* widget = frame_->GetWidget(); |
| 493 switch (snap_type_) { | 498 switch (snap_type_) { |
| 494 case SNAP_LEFT: | 499 case SNAP_LEFT: |
| 495 case SNAP_RIGHT: | 500 case SNAP_RIGHT: { |
| 496 if (frame_->GetWidget()->IsMaximized()) { | 501 // Get the window coordinates on the screen for restore purposes. |
| 497 ash::SetRestoreBoundsInScreen(frame_->GetWidget()->GetNativeWindow(), | 502 gfx::Rect restore = widget->GetNativeWindow()->bounds(); |
| 503 if (widget->IsMaximized()) { |
| 504 // In case of maximized we have a restore boundary. |
| 505 DCHECK(ash::GetRestoreBoundsInScreen(widget->GetNativeWindow())); |
| 506 // If it was maximized we need to recover the old restore set. |
| 507 restore = *ash::GetRestoreBoundsInScreen(widget->GetNativeWindow()); |
| 508 // Set the restore size we want to restore to. |
| 509 ash::SetRestoreBoundsInScreen(widget->GetNativeWindow(), |
| 498 ScreenBoundsForType(snap_type_, | 510 ScreenBoundsForType(snap_type_, |
| 499 snap_sizer)); | 511 snap_sizer)); |
| 500 frame_->GetWidget()->Restore(); | 512 widget->Restore(); |
| 501 } else { | 513 } else { |
| 502 frame_->GetWidget()->SetBounds(ScreenBoundsForType(snap_type_, | 514 // Others might also have set up a restore rectangle already. If so, |
| 503 snap_sizer)); | 515 // we should not overwrite the restore rectangle. |
| 516 bool restore_set = |
| 517 GetRestoreBoundsInScreen(widget->GetNativeWindow()) != NULL; |
| 518 widget->SetBounds(ScreenBoundsForType(snap_type_, snap_sizer)); |
| 519 if (restore_set) |
| 520 break; |
| 521 } |
| 522 // Remember the widow's bounds for restoration. |
| 523 ash::SetRestoreBoundsInScreen(widget->GetNativeWindow(), restore); |
| 504 } | 524 } |
| 505 break; | 525 break; |
| 506 case SNAP_MAXIMIZE: | 526 case SNAP_MAXIMIZE: |
| 507 frame_->GetWidget()->Maximize(); | 527 widget->Maximize(); |
| 508 break; | 528 break; |
| 509 case SNAP_MINIMIZE: | 529 case SNAP_MINIMIZE: |
| 510 frame_->GetWidget()->Minimize(); | 530 widget->Minimize(); |
| 511 break; | 531 break; |
| 512 case SNAP_RESTORE: | 532 case SNAP_RESTORE: |
| 513 frame_->GetWidget()->Restore(); | 533 widget->Restore(); |
| 514 break; | 534 break; |
| 515 case SNAP_NONE: | 535 case SNAP_NONE: |
| 516 NOTREACHED(); | 536 NOTREACHED(); |
| 517 } | 537 } |
| 518 } | 538 } |
| 519 | 539 |
| 540 MaximizeBubbleFrameState |
| 541 FrameMaximizeButton::GetMaximizeBubbleFrameState() const { |
| 542 // When there are no restore bounds, we are in normal mode. |
| 543 if (!ash::GetRestoreBoundsInScreen( |
| 544 frame_->GetWidget()->GetNativeWindow())) |
| 545 return FRAME_STATE_NONE; |
| 546 // The normal maximized test can be used. |
| 547 if (frame_->GetWidget()->IsMaximized()) |
| 548 return FRAME_STATE_FULL; |
| 549 // For Left/right maximize we need to check the dimensions. |
| 550 gfx::Rect bounds = frame_->GetWidget()->GetWindowBoundsInScreen(); |
| 551 gfx::Rect screen = gfx::Screen::GetDisplayMatching(bounds).work_area(); |
| 552 if (bounds.width() < (screen.width() * kMinSnapSizePercent) / 100) |
| 553 return FRAME_STATE_NONE; |
| 554 // We have to be in a maximize mode at this point. |
| 555 DCHECK(bounds.y() == screen.y()); |
| 556 DCHECK(bounds.height() >= screen.height()); |
| 557 if (bounds.x() == screen.x()) |
| 558 return FRAME_STATE_SNAP_LEFT; |
| 559 if (bounds.right() == screen.right()) |
| 560 return FRAME_STATE_SNAP_RIGHT; |
| 561 // If we come here, it is likely caused by the fact that the |
| 562 // "VerticalResizeDoubleClick" stored a restore rectangle. In that case |
| 563 // we allow all maximize operations (and keep the restore rectangle). |
| 564 return FRAME_STATE_NONE; |
| 565 } |
| 566 |
| 520 } // namespace ash | 567 } // namespace ash |
| OLD | NEW |