diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-12-18 16:25:09 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-12-18 16:25:09 +0000 |
commit | a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7 (patch) | |
tree | dafc1c6417406a7fbd422ad0bb890e96909ef564 /ash | |
parent | d5f893c0bc79db3066bb5ae5d3d972ba1be7dd5f (diff) | |
download | chromium_org-a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7.tar.gz |
Merge from Chromium at DEPS revision 240154
This commit was generated by merge_to_master.py.
Change-Id: I8f2ba858cf0e7f413dddedc2ae91dc37f7136c2e
Diffstat (limited to 'ash')
218 files changed, 5518 insertions, 2659 deletions
diff --git a/ash/OWNERS b/ash/OWNERS index a3e7809a2b..e7dd72d402 100644 --- a/ash/OWNERS +++ b/ash/OWNERS @@ -3,6 +3,9 @@ jamescook@chromium.org oshima@chromium.org sky@chromium.org +# These are for the common case of adding or renaming a few items. If you're +# doing structural changes, please get a review from a reviewer above. +per-file ash.gyp=* per-file ash_strings.grd=* per-file ash_chromeos_strings.grdp=* per-file ash_switches.*=* diff --git a/ash/accelerators/accelerator_commands.cc b/ash/accelerators/accelerator_commands.cc index 003562e2b5..b50959f498 100644 --- a/ash/accelerators/accelerator_commands.cc +++ b/ash/accelerators/accelerator_commands.cc @@ -9,6 +9,7 @@ #include "ash/wm/window_cycle_controller.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" +#include "content/public/browser/user_metrics.h" namespace ash { namespace accelerators { @@ -25,8 +26,6 @@ bool ToggleMinimized() { wm::WindowState* window_state = wm::GetWindowState(window); if (!window_state->CanMinimize()) return false; - ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction( - ash::UMA_MINIMIZE_PER_KEY); window_state->Minimize(); return true; } @@ -35,6 +34,8 @@ void ToggleMaximized() { wm::WindowState* window_state = wm::GetActiveWindowState(); if (!window_state) return; + content::RecordAction( + content::UserMetricsAction("Accel_Toggle_Maximized")); // Get out of fullscreen when in fullscreen mode. if (window_state->IsFullscreen()) ToggleFullscreen(); diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc index d9c2d7955d..0ac3f7ef4b 100644 --- a/ash/accelerators/accelerator_controller.cc +++ b/ash/accelerators/accelerator_controller.cc @@ -20,7 +20,6 @@ #include "ash/focus_cycler.h" #include "ash/ime_control_delegate.h" #include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_delegate.h" #include "ash/magnifier/magnification_controller.h" #include "ash/magnifier/partial_magnification_controller.h" #include "ash/media_delegate.h" @@ -30,6 +29,7 @@ #include "ash/rotator/screen_rotation.h" #include "ash/screenshot_delegate.h" #include "ash/session_state_delegate.h" +#include "ash/shelf/shelf_delegate.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" @@ -82,6 +82,7 @@ namespace ash { namespace { using internal::DisplayInfo; +using content::UserMetricsAction; bool DebugShortcutsEnabled() { #if defined(NDEBUG) @@ -92,7 +93,38 @@ bool DebugShortcutsEnabled() { #endif } -void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) { +bool HandleAccessibleFocusCycle(bool reverse) { + if (reverse) { + content::RecordAction(UserMetricsAction("Accel_Accessible_Focus_Previous")); + } else { + content::RecordAction(UserMetricsAction("Accel_Accessible_Focus_Next")); + } + + if (!Shell::GetInstance()->accessibility_delegate()-> + IsSpokenFeedbackEnabled()) { + return false; + } + aura::Window* active_window = ash::wm::GetActiveWindow(); + if (!active_window) + return false; + views::Widget* widget = + views::Widget::GetWidgetForNativeWindow(active_window); + if (!widget) + return false; + views::FocusManager* focus_manager = widget->GetFocusManager(); + if (!focus_manager) + return false; + views::View* view = focus_manager->GetFocusedView(); + if (!view) + return false; + if (!strcmp(view->GetClassName(), views::WebView::kViewClassName)) + return false; + + focus_manager->AdvanceFocus(reverse); + return true; +} + +bool HandleCycleBackwardMRU(const ui::Accelerator& accelerator) { Shell* shell = Shell::GetInstance(); if (accelerator.key_code() == ui::VKEY_TAB) @@ -101,13 +133,14 @@ void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) { if (switches::UseOverviewMode()) { shell->window_selector_controller()->HandleCycleWindow( WindowSelector::BACKWARD); - return; + return true; } shell->window_cycle_controller()->HandleCycleWindow( WindowCycleController::BACKWARD, accelerator.IsAltDown()); + return true; } -void HandleCycleForwardMRU(const ui::Accelerator& accelerator) { +bool HandleCycleForwardMRU(const ui::Accelerator& accelerator) { Shell* shell = Shell::GetInstance(); if (accelerator.key_code() == ui::VKEY_TAB) @@ -116,13 +149,14 @@ void HandleCycleForwardMRU(const ui::Accelerator& accelerator) { if (switches::UseOverviewMode()) { shell->window_selector_controller()->HandleCycleWindow( WindowSelector::FORWARD); - return; + return true; } shell->window_cycle_controller()->HandleCycleWindow( WindowCycleController::FORWARD, accelerator.IsAltDown()); + return true; } -void HandleCycleLinear(const ui::Accelerator& accelerator) { +bool HandleCycleLinear(const ui::Accelerator& accelerator) { Shell* shell = Shell::GetInstance(); // TODO(jamescook): When overview becomes the default the AcceleratorAction @@ -130,98 +164,195 @@ void HandleCycleLinear(const ui::Accelerator& accelerator) { if (switches::UseOverviewMode()) { content::RecordAction(content::UserMetricsAction("Accel_Overview_F5")); shell->window_selector_controller()->ToggleOverview(); - return; + return true; } if (accelerator.key_code() == ui::VKEY_MEDIA_LAUNCH_APP1) content::RecordAction(content::UserMetricsAction("Accel_NextWindow_F5")); shell->window_cycle_controller()->HandleLinearCycleWindow(); + return true; } -bool HandleAccessibleFocusCycle(bool reverse) { - if (!Shell::GetInstance()->accessibility_delegate()-> - IsSpokenFeedbackEnabled()) { +bool HandleDisableCapsLock(ui::KeyboardCode key_code, + ui::EventType previous_event_type, + ui::KeyboardCode previous_key_code) { + Shell* shell = Shell::GetInstance(); + + if (previous_event_type == ui::ET_KEY_RELEASED || + (previous_key_code != ui::VKEY_LSHIFT && + previous_key_code != ui::VKEY_SHIFT && + previous_key_code != ui::VKEY_RSHIFT)) { + // If something else was pressed between the Shift key being pressed + // and released, then ignore the release of the Shift key. return false; } - aura::Window* active_window = ash::wm::GetActiveWindow(); - if (!active_window) - return false; - views::Widget* widget = - views::Widget::GetWidgetForNativeWindow(active_window); - if (!widget) - return false; - views::FocusManager* focus_manager = widget->GetFocusManager(); - if (!focus_manager) - return false; - views::View* view = focus_manager->GetFocusedView(); - if (!view) - return false; - if (!strcmp(view->GetClassName(), views::WebView::kViewClassName)) - return false; + content::RecordAction(UserMetricsAction("Accel_Disable_Caps_Lock")); + if (shell->caps_lock_delegate()->IsCapsLockEnabled()) { + shell->caps_lock_delegate()->SetCapsLockEnabled(false); + return true; + } + return false; +} - focus_manager->AdvanceFocus(reverse); +bool HandleFocusLauncher() { + Shell* shell = Shell::GetInstance(); + content::RecordAction(content::UserMetricsAction("Accel_Focus_Launcher")); + return shell->focus_cycler()->FocusWidget( + Launcher::ForPrimaryDisplay()->shelf_widget()); +} + +bool HandleLaunchAppN(int n) { + content::RecordAction(UserMetricsAction("Accel_Launch_App")); + Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(n); return true; } -void HandleSilenceSpokenFeedback() { - AccessibilityDelegate* delegate = - Shell::GetInstance()->accessibility_delegate(); - if (!delegate->IsSpokenFeedbackEnabled()) - return; - delegate->SilenceSpokenFeedback(); +bool HandleLaunchLastApp() { + content::RecordAction(UserMetricsAction("Accel_Launch_Last_App")); + Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1); + return true; } -#if defined(OS_CHROMEOS) -bool HandleLock() { - Shell::GetInstance()->session_state_delegate()->LockScreen(); +// Magnify the screen +bool HandleMagnifyScreen(int delta_index) { + if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) { + // TODO(yoshiki): Move the following logic to MagnificationController. + float scale = + ash::Shell::GetInstance()->magnification_controller()->GetScale(); + // Calculate rounded logarithm (base kMagnificationScaleFactor) of scale. + int scale_index = + std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5); + + int new_scale_index = std::max(0, std::min(8, scale_index + delta_index)); + + ash::Shell::GetInstance()->magnification_controller()-> + SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true); + } else if (ash::Shell::GetInstance()-> + partial_magnification_controller()->is_enabled()) { + float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1; + ash::Shell::GetInstance()->partial_magnification_controller()-> + SetScale(scale); + } + return true; } -bool HandleFileManager() { - Shell::GetInstance()->new_window_delegate()->OpenFileManager(); +bool HandleMediaNextTrack() { + Shell::GetInstance()->media_delegate()->HandleMediaNextTrack(); return true; } -bool HandleCrosh() { - Shell::GetInstance()->new_window_delegate()->OpenCrosh(); +bool HandleMediaPlayPause() { + Shell::GetInstance()->media_delegate()->HandleMediaPlayPause(); return true; } -bool HandleToggleSpokenFeedback() { - Shell::GetInstance()->accessibility_delegate()-> - ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW); +bool HandleMediaPrevTrack() { + Shell::GetInstance()->media_delegate()->HandleMediaPrevTrack(); return true; } -bool SwitchToNextUser() { - if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) - return false; - ash::SessionStateDelegate* delegate = - ash::Shell::GetInstance()->session_state_delegate(); - if (delegate->NumberOfLoggedInUsers() <= 1) +bool HandleNewIncognitoWindow() { + content::RecordAction(UserMetricsAction("Accel_New_Incognito_Window")); + bool incognito_allowed = + Shell::GetInstance()->delegate()->IsIncognitoAllowed(); + if (incognito_allowed) + Shell::GetInstance()->new_window_delegate()->NewWindow( + true /* is_incognito */); + return incognito_allowed; +} + +bool HandleNewTab(ui::KeyboardCode key_code) { + if (key_code == ui::VKEY_T) + content::RecordAction(content::UserMetricsAction("Accel_NewTab_T")); + Shell::GetInstance()->new_window_delegate()->NewTab(); + return true; +} + +bool HandleNewWindow() { + content::RecordAction(content::UserMetricsAction("Accel_New_Window")); + Shell::GetInstance()->new_window_delegate()->NewWindow( + false /* is_incognito */); + return true; +} + +bool HandleNextIme(ImeControlDelegate* ime_control_delegate, + ui::EventType previous_event_type, + ui::KeyboardCode previous_key_code) { + // This check is necessary e.g. not to process the Shift+Alt+ + // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/ + // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab + // is released. + if (previous_event_type == ui::ET_KEY_RELEASED && + // Workaround for crbug.com/139556. CJK IME users tend to press + // Enter (or Space) and Shift+Alt almost at the same time to commit + // an IME string and then switch from the IME to the English layout. + // This workaround allows the user to trigger NEXT_IME even if the + // user presses Shift+Alt before releasing Enter. + // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way. + previous_key_code != ui::VKEY_RETURN && + previous_key_code != ui::VKEY_SPACE) { + // We totally ignore this accelerator. + // TODO(mazda): Fix crbug.com/158217 return false; - MultiProfileUMA::RecordSwitchActiveUser( - MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR); - delegate->SwitchActiveUserToNext(); + } + content::RecordAction(UserMetricsAction("Accel_Next_Ime")); + if (ime_control_delegate) + return ime_control_delegate->HandleNextIme(); + return false; +} + +bool HandleOpenFeedbackPage() { + content::RecordAction(UserMetricsAction("Accel_Open_Feedback_Page")); + ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage(); return true; } -#endif // defined(OS_CHROMEOS) +bool HandlePositionCenter() { + content::RecordAction(UserMetricsAction("Accel_Window_Position_Center")); + aura::Window* window = wm::GetActiveWindow(); + // Docked windows do not support centering and ignore accelerator. + if (window && !wm::GetWindowState(window)->IsDocked()) { + wm::CenterWindow(window); + return true; + } + return false; +} + +bool HandlePreviousIme(ImeControlDelegate* ime_control_delegate, + const ui::Accelerator& accelerator) { + content::RecordAction(UserMetricsAction("Accel_Previous_Ime")); + if (ime_control_delegate) + return ime_control_delegate->HandlePreviousIme(accelerator); + return false; +} + +bool HandleRestoreTab() { + content::RecordAction(content::UserMetricsAction("Accel_Restore_Tab")); + Shell::GetInstance()->new_window_delegate()->RestoreTab(); + return true; +} bool HandleRotatePaneFocus(Shell::Direction direction) { Shell* shell = Shell::GetInstance(); switch (direction) { - case Shell::FORWARD: + // TODO(stevet): Not sure if this is the same as IDC_FOCUS_NEXT_PANE. + case Shell::FORWARD: { + content::RecordAction(UserMetricsAction("Accel_Focus_Next_Pane")); shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD); break; - case Shell::BACKWARD: + } + case Shell::BACKWARD: { + content::RecordAction(UserMetricsAction("Accel_Focus_Previous_Pane")); shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD); break; + } } return true; } // Rotate the active window. bool HandleRotateActiveWindow() { + content::RecordAction(UserMetricsAction("Accel_Rotate_Window")); aura::Window* active_window = wm::GetActiveWindow(); if (active_window) { // The rotation animation bases its target transform on the current @@ -252,12 +383,44 @@ gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) { return gfx::Display::ROTATE_0; } +// Rotates the screen. +bool HandleRotateScreen() { + content::RecordAction(UserMetricsAction("Accel_Rotate_Window")); + gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint(); + gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point); + const DisplayInfo& display_info = + Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); + Shell::GetInstance()->display_manager()->SetDisplayRotation( + display.id(), GetNextRotation(display_info.rotation())); + return true; +} + +bool HandleScaleReset() { + internal::DisplayManager* display_manager = + Shell::GetInstance()->display_manager(); + int64 display_id = display_manager->GetDisplayIdForUIScaling(); + if (display_id == gfx::Display::kInvalidDisplayID) + return false; + + content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Reset")); + + display_manager->SetDisplayUIScale(display_id, 1.0f); + return true; +} + bool HandleScaleUI(bool up) { internal::DisplayManager* display_manager = Shell::GetInstance()->display_manager(); int64 display_id = display_manager->GetDisplayIdForUIScaling(); if (display_id == gfx::Display::kInvalidDisplayID) return false; + + if (up) { + content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Up")); + } else { + content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Down")); + } + const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id); float next_scale = internal::DisplayManager::GetNextUIScale(display_info, up); @@ -265,24 +428,145 @@ bool HandleScaleUI(bool up) { return true; } -bool HandleScaleReset() { - internal::DisplayManager* display_manager = - Shell::GetInstance()->display_manager(); - int64 display_id = display_manager->GetDisplayIdForUIScaling(); - if (display_id == gfx::Display::kInvalidDisplayID) +bool HandleSwapPrimaryDisplay() { + content::RecordAction(UserMetricsAction("Accel_Swap_Primary_Display")); + Shell::GetInstance()->display_controller()->SwapPrimaryDisplay(); + return true; +} + +bool HandleShowKeyboardOverlay() { + content::RecordAction(UserMetricsAction("Accel_Show_Keyboard_Overlay")); + ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay(); + + return true; +} + +void HandleShowMessageCenterBubble() { + content::RecordAction(UserMetricsAction("Accel_Show_Message_Center_Bubble")); + internal::RootWindowController* controller = + internal::RootWindowController::ForTargetRootWindow(); + internal::StatusAreaWidget* status_area_widget = + controller->shelf()->status_area_widget(); + if (status_area_widget) { + WebNotificationTray* notification_tray = + status_area_widget->web_notification_tray(); + if (notification_tray->visible()) + notification_tray->ShowMessageCenterBubble(); + } +} + +bool HandleShowOak() { + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAshEnableOak)) { + oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow()); + return true; + } + return false; +} + +bool HandleShowSystemTrayBubble() { + content::RecordAction(UserMetricsAction("Accel_Show_System_Tray_Bubble")); + internal::RootWindowController* controller = + internal::RootWindowController::ForTargetRootWindow(); + if (!controller->GetSystemTray()->HasSystemBubble()) { + controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW); + return true; + } + return false; +} + +bool HandleShowTaskManager() { + content::RecordAction(UserMetricsAction("Accel_Show_Task_Manager")); + Shell::GetInstance()->new_window_delegate()->ShowTaskManager(); + return true; +} + +void HandleSilenceSpokenFeedback() { + content::RecordAction(UserMetricsAction("Accel_Silence_Spoken_Feedback")); + + AccessibilityDelegate* delegate = + Shell::GetInstance()->accessibility_delegate(); + if (!delegate->IsSpokenFeedbackEnabled()) + return; + delegate->SilenceSpokenFeedback(); +} + +bool HandleSwitchIme(ImeControlDelegate* ime_control_delegate, + const ui::Accelerator& accelerator) { + content::RecordAction(UserMetricsAction("Accel_Switch_Ime")); + if (ime_control_delegate) + return ime_control_delegate->HandleSwitchIme(accelerator); + return false; +} + +bool HandleTakePartialScreenshot(ScreenshotDelegate* screenshot_delegate) { + content::RecordAction(UserMetricsAction("Accel_Take_Partial_Screenshot")); + if (screenshot_delegate) { + ash::PartialScreenshotView::StartPartialScreenshot( + screenshot_delegate); + } + // Return true to prevent propagation of the key event because + // this key combination is reserved for partial screenshot. + return true; +} + +bool HandleTakeScreenshot(ScreenshotDelegate* screenshot_delegate) { + content::RecordAction(UserMetricsAction("Accel_Take_Screenshot")); + if (screenshot_delegate && + screenshot_delegate->CanTakeScreenshot()) { + screenshot_delegate->HandleTakeScreenshotForAllRootWindows(); + } + // Return true to prevent propagation of the key event. + return true; +} + +bool HandleToggleAppList(ui::KeyboardCode key_code, + ui::EventType previous_event_type, + ui::KeyboardCode previous_key_code, + const ui::Accelerator& accelerator) { + // If something else was pressed between the Search key (LWIN) + // being pressed and released, then ignore the release of the + // Search key. + if (key_code == ui::VKEY_LWIN && + (previous_event_type == ui::ET_KEY_RELEASED || + previous_key_code != ui::VKEY_LWIN)) return false; - display_manager->SetDisplayUIScale(display_id, 1.0f); + if (key_code == ui::VKEY_LWIN) + content::RecordAction(content::UserMetricsAction("Accel_Search_LWin")); + // When spoken feedback is enabled, we should neither toggle the list nor + // consume the key since Search+Shift is one of the shortcuts the a11y + // feature uses. crbug.com/132296 + DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code()); + if (Shell::GetInstance()->accessibility_delegate()-> + IsSpokenFeedbackEnabled()) + return false; + ash::Shell::GetInstance()->ToggleAppList(NULL); return true; } -// Rotates the screen. -bool HandleRotateScreen() { - gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint(); - gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point); - const DisplayInfo& display_info = - Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); - Shell::GetInstance()->display_manager()->SetDisplayRotation( - display.id(), GetNextRotation(display_info.rotation())); +bool HandleToggleCapsLock(ui::KeyboardCode key_code, + ui::EventType previous_event_type, + ui::KeyboardCode previous_key_code) { + Shell* shell = Shell::GetInstance(); + if (key_code == ui::VKEY_LWIN) { + // If something else was pressed between the Search key (LWIN) + // being pressed and released, then ignore the release of the + // Search key. + // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495 + if (previous_event_type == ui::ET_KEY_RELEASED || + previous_key_code != ui::VKEY_LWIN) + return false; + } + content::RecordAction(UserMetricsAction("Accel_Toggle_Caps_Lock")); + shell->caps_lock_delegate()->ToggleCapsLock(); + return true; +} + +bool HandleToggleFullscreen(ui::KeyboardCode key_code) { + if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) { + content::RecordAction(UserMetricsAction("Accel_Fullscreen_F4")); + } + accelerators::ToggleFullscreen(); return true; } @@ -291,45 +575,127 @@ bool HandleToggleRootWindowFullScreen() { return true; } -// Magnify the screen -bool HandleMagnifyScreen(int delta_index) { - if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) { - // TODO(yoshiki): Move the following logic to MagnificationController. - float scale = - ash::Shell::GetInstance()->magnification_controller()->GetScale(); - // Calculate rounded logarithm (base kMagnificationScaleFactor) of scale. - int scale_index = - std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5); - - int new_scale_index = std::max(0, std::min(8, scale_index + delta_index)); +bool HandleWindowSnap(int action) { + wm::WindowState* window_state = wm::GetActiveWindowState(); + // Disable window snapping shortcut key for full screen window due to + // http://crbug.com/135487. + if (!window_state || + window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL || + window_state->IsFullscreen()) { + return false; + } - ash::Shell::GetInstance()->magnification_controller()-> - SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true); - } else if (ash::Shell::GetInstance()-> - partial_magnification_controller()->is_enabled()) { - float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1; - ash::Shell::GetInstance()->partial_magnification_controller()-> - SetScale(scale); + if (action == WINDOW_SNAP_LEFT) { + content::RecordAction(UserMetricsAction("Accel_Window_Snap_Left")); + } else { + content::RecordAction(UserMetricsAction("Accel_Window_Snap_Right")); } + internal::SnapSizer::SnapWindow(window_state, + action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE : + internal::SnapSizer::RIGHT_EDGE); return true; } -bool HandleMediaNextTrack() { - Shell::GetInstance()->media_delegate()->HandleMediaNextTrack(); +bool HandleWindowMinimize() { + content::RecordAction( + content::UserMetricsAction("Accel_Toggle_Minimized_Minus")); + return accelerators::ToggleMinimized(); +} + +#if defined(OS_CHROMEOS) +bool HandleAddRemoveDisplay() { + content::RecordAction(UserMetricsAction("Accel_Add_Remove_Display")); + Shell::GetInstance()->display_manager()->AddRemoveDisplay(); return true; } -bool HandleMediaPlayPause() { - Shell::GetInstance()->media_delegate()->HandleMediaPlayPause(); +bool HandleCrosh() { + content::RecordAction(UserMetricsAction("Accel_Open_Crosh")); + + Shell::GetInstance()->new_window_delegate()->OpenCrosh(); return true; } -bool HandleMediaPrevTrack() { - Shell::GetInstance()->media_delegate()->HandleMediaPrevTrack(); +bool HandleFileManager() { + content::RecordAction(UserMetricsAction("Accel_Open_File_Manager")); + + Shell::GetInstance()->new_window_delegate()->OpenFileManager(); + return true; +} + +bool HandleLock(ui::KeyboardCode key_code) { + content::RecordAction(UserMetricsAction("Accel_LockScreen_L")); + Shell::GetInstance()->session_state_delegate()->LockScreen(); + return true; +} + +bool HandleCycleUser(SessionStateDelegate::CycleUser cycle_user) { + if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) + return false; + ash::SessionStateDelegate* delegate = + ash::Shell::GetInstance()->session_state_delegate(); + if (delegate->NumberOfLoggedInUsers() <= 1) + return false; + MultiProfileUMA::RecordSwitchActiveUser( + MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR); + switch (cycle_user) { + case SessionStateDelegate::CYCLE_TO_NEXT_USER: + content::RecordAction(UserMetricsAction("Accel_Switch_To_Next_User")); + break; + case SessionStateDelegate::CYCLE_TO_PREVIOUS_USER: + content::RecordAction(UserMetricsAction("Accel_Switch_To_Previous_User")); + break; + } + delegate->CycleActiveUser(cycle_user); + return true; +} + +bool HandleToggleMirrorMode() { + content::RecordAction(UserMetricsAction("Accel_Toggle_Mirror_Mode")); + Shell::GetInstance()->display_controller()->ToggleMirrorMode(); + return true; +} + +bool HandleToggleSpokenFeedback() { + content::RecordAction(UserMetricsAction("Accel_Toggle_Spoken_Feedback")); + + Shell::GetInstance()->accessibility_delegate()-> + ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW); return true; } +bool HandleTouchHudClear() { + internal::RootWindowController* controller = + internal::RootWindowController::ForTargetRootWindow(); + if (controller->touch_hud_debug()) { + controller->touch_hud_debug()->Clear(); + return true; + } + return false; +} + +bool HandleTouchHudModeChange() { + internal::RootWindowController* controller = + internal::RootWindowController::ForTargetRootWindow(); + if (controller->touch_hud_debug()) { + controller->touch_hud_debug()->ChangeToNextMode(); + return true; + } + return false; +} + +bool HandleTouchHudProjectToggle() { + content::RecordAction(UserMetricsAction("Accel_Touch_Hud_Clear")); + bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled(); + Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled); + return true; +} + +#endif // defined(OS_CHROMEOS) + +// Debug print methods. + bool HandlePrintLayerHierarchy() { aura::Window::Windows root_windows = Shell::GetAllRootWindows(); for (size_t i = 0; i < root_windows.size(); ++i) { @@ -565,25 +931,18 @@ bool AcceleratorController::PerformAction(int action, case ACCESSIBLE_FOCUS_PREVIOUS: return HandleAccessibleFocusCycle(true); case CYCLE_BACKWARD_MRU: - HandleCycleBackwardMRU(accelerator); - return true; + return HandleCycleBackwardMRU(accelerator); case CYCLE_FORWARD_MRU: - HandleCycleForwardMRU(accelerator); - return true; + return HandleCycleForwardMRU(accelerator); case CYCLE_LINEAR: - HandleCycleLinear(accelerator); - return true; + return HandleCycleLinear(accelerator); #if defined(OS_CHROMEOS) case ADD_REMOVE_DISPLAY: - Shell::GetInstance()->display_manager()->AddRemoveDisplay(); - return true; + return HandleAddRemoveDisplay(); case TOGGLE_MIRROR_MODE: - Shell::GetInstance()->display_controller()->ToggleMirrorMode(); - return true; + return HandleToggleMirrorMode(); case LOCK_SCREEN: - if (key_code == ui::VKEY_L) - content::RecordAction(content::UserMetricsAction("Accel_LockScreen_L")); - return HandleLock(); + return HandleLock(key_code); case OPEN_FILE_MANAGER: return HandleFileManager(); case OPEN_CROSH: @@ -592,129 +951,53 @@ bool AcceleratorController::PerformAction(int action, HandleSilenceSpokenFeedback(); break; case SWAP_PRIMARY_DISPLAY: - Shell::GetInstance()->display_controller()->SwapPrimaryDisplay(); - return true; + return HandleSwapPrimaryDisplay(); case SWITCH_TO_NEXT_USER: - return SwitchToNextUser(); + return HandleCycleUser(SessionStateDelegate::CYCLE_TO_NEXT_USER); + case SWITCH_TO_PREVIOUS_USER: + return HandleCycleUser(SessionStateDelegate::CYCLE_TO_PREVIOUS_USER); case TOGGLE_SPOKEN_FEEDBACK: return HandleToggleSpokenFeedback(); case TOGGLE_WIFI: Shell::GetInstance()->system_tray_notifier()->NotifyRequestToggleWifi(); return true; - case TOUCH_HUD_CLEAR: { - internal::RootWindowController* controller = - internal::RootWindowController::ForTargetRootWindow(); - if (controller->touch_hud_debug()) { - controller->touch_hud_debug()->Clear(); - return true; - } - return false; - } - case TOUCH_HUD_MODE_CHANGE: { - internal::RootWindowController* controller = - internal::RootWindowController::ForTargetRootWindow(); - if (controller->touch_hud_debug()) { - controller->touch_hud_debug()->ChangeToNextMode(); - return true; - } - return false; - } - case TOUCH_HUD_PROJECTION_TOGGLE: { - bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled(); - Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled); - return true; - } + case TOUCH_HUD_CLEAR: + return HandleTouchHudClear(); + case TOUCH_HUD_MODE_CHANGE: + return HandleTouchHudModeChange(); + case TOUCH_HUD_PROJECTION_TOGGLE: + return HandleTouchHudProjectToggle(); case DISABLE_GPU_WATCHDOG: content::GpuDataManager::GetInstance()->DisableGpuWatchdog(); return true; -#endif +#endif // OS_CHROMEOS case OPEN_FEEDBACK_PAGE: - ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage(); - return true; + return HandleOpenFeedbackPage(); case EXIT: // UMA metrics are recorded in the handler. exit_warning_handler_.HandleAccelerator(); return true; - case NEW_INCOGNITO_WINDOW: { - bool incognito_allowed = - Shell::GetInstance()->delegate()->IsIncognitoAllowed(); - if (incognito_allowed) - Shell::GetInstance()->new_window_delegate()->NewWindow( - true /* is_incognito */); - return incognito_allowed; - } + case NEW_INCOGNITO_WINDOW: + return HandleNewIncognitoWindow(); case NEW_TAB: - if (key_code == ui::VKEY_T) - content::RecordAction(content::UserMetricsAction("Accel_NewTab_T")); - Shell::GetInstance()->new_window_delegate()->NewTab(); - return true; + return HandleNewTab(key_code); case NEW_WINDOW: - Shell::GetInstance()->new_window_delegate()->NewWindow( - false /* is_incognito */); - return true; + return HandleNewWindow(); case RESTORE_TAB: - Shell::GetInstance()->new_window_delegate()->RestoreTab(); - return true; + return HandleRestoreTab(); case TAKE_SCREENSHOT: - if (screenshot_delegate_.get() && - screenshot_delegate_->CanTakeScreenshot()) { - screenshot_delegate_->HandleTakeScreenshotForAllRootWindows(); - } - // Return true to prevent propagation of the key event. - return true; + return HandleTakeScreenshot(screenshot_delegate_.get()); case TAKE_PARTIAL_SCREENSHOT: - if (screenshot_delegate_) { - ash::PartialScreenshotView::StartPartialScreenshot( - screenshot_delegate_.get()); - } - // Return true to prevent propagation of the key event because - // this key combination is reserved for partial screenshot. - return true; + return HandleTakePartialScreenshot(screenshot_delegate_.get()); case TOGGLE_APP_LIST: - // If something else was pressed between the Search key (LWIN) - // being pressed and released, then ignore the release of the - // Search key. - if (key_code == ui::VKEY_LWIN && - (previous_event_type == ui::ET_KEY_RELEASED || - previous_key_code != ui::VKEY_LWIN)) - return false; - if (key_code == ui::VKEY_LWIN) - content::RecordAction(content::UserMetricsAction("Accel_Search_LWin")); - // When spoken feedback is enabled, we should neither toggle the list nor - // consume the key since Search+Shift is one of the shortcuts the a11y - // feature uses. crbug.com/132296 - DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code()); - if (Shell::GetInstance()->accessibility_delegate()-> - IsSpokenFeedbackEnabled()) - return false; - ash::Shell::GetInstance()->ToggleAppList(NULL); - return true; + return HandleToggleAppList( + key_code, previous_event_type, previous_key_code, accelerator); case DISABLE_CAPS_LOCK: - if (previous_event_type == ui::ET_KEY_RELEASED || - (previous_key_code != ui::VKEY_LSHIFT && - previous_key_code != ui::VKEY_SHIFT && - previous_key_code != ui::VKEY_RSHIFT)) { - // If something else was pressed between the Shift key being pressed - // and released, then ignore the release of the Shift key. - return false; - } - if (shell->caps_lock_delegate()->IsCapsLockEnabled()) { - shell->caps_lock_delegate()->SetCapsLockEnabled(false); - return true; - } - return false; + return HandleDisableCapsLock( + key_code, previous_event_type, previous_key_code); case TOGGLE_CAPS_LOCK: - if (key_code == ui::VKEY_LWIN) { - // If something else was pressed between the Search key (LWIN) - // being pressed and released, then ignore the release of the - // Search key. - // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495 - if (previous_event_type == ui::ET_KEY_RELEASED || - previous_key_code != ui::VKEY_LWIN) - return false; - } - shell->caps_lock_delegate()->ToggleCapsLock(); - return true; + return HandleToggleCapsLock( + key_code, previous_event_type, previous_key_code); case BRIGHTNESS_DOWN: if (brightness_control_delegate_) return brightness_control_delegate_->HandleBrightnessDown(accelerator); @@ -749,145 +1032,61 @@ bool AcceleratorController::PerformAction(int action, return volume_delegate && volume_delegate->HandleVolumeUp(accelerator); } case FOCUS_LAUNCHER: - return shell->focus_cycler()->FocusWidget( - Launcher::ForPrimaryDisplay()->shelf_widget()); + return HandleFocusLauncher(); case FOCUS_NEXT_PANE: return HandleRotatePaneFocus(Shell::FORWARD); case FOCUS_PREVIOUS_PANE: return HandleRotatePaneFocus(Shell::BACKWARD); case SHOW_KEYBOARD_OVERLAY: - ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay(); - return true; + return HandleShowKeyboardOverlay(); case SHOW_OAK: - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kAshEnableOak)) { - oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow()); - return true; - } + return HandleShowOak(); + case SHOW_SYSTEM_TRAY_BUBBLE: + return HandleShowSystemTrayBubble(); + case SHOW_MESSAGE_CENTER_BUBBLE: + HandleShowMessageCenterBubble(); break; - case SHOW_SYSTEM_TRAY_BUBBLE: { - internal::RootWindowController* controller = - internal::RootWindowController::ForTargetRootWindow(); - if (!controller->GetSystemTray()->HasSystemBubble()) { - controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW); - return true; - } - break; - } - case SHOW_MESSAGE_CENTER_BUBBLE: { - internal::RootWindowController* controller = - internal::RootWindowController::ForTargetRootWindow(); - internal::StatusAreaWidget* status_area_widget = - controller->shelf()->status_area_widget(); - if (status_area_widget) { - WebNotificationTray* notification_tray = - status_area_widget->web_notification_tray(); - if (notification_tray->visible()) - notification_tray->ShowMessageCenterBubble(); - } - break; - } case SHOW_TASK_MANAGER: - Shell::GetInstance()->new_window_delegate()->ShowTaskManager(); - return true; + return HandleShowTaskManager(); case NEXT_IME: - // This check is necessary e.g. not to process the Shift+Alt+ - // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/ - // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab - // is released. - if (previous_event_type == ui::ET_KEY_RELEASED && - // Workaround for crbug.com/139556. CJK IME users tend to press - // Enter (or Space) and Shift+Alt almost at the same time to commit - // an IME string and then switch from the IME to the English layout. - // This workaround allows the user to trigger NEXT_IME even if the - // user presses Shift+Alt before releasing Enter. - // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way. - previous_key_code != ui::VKEY_RETURN && - previous_key_code != ui::VKEY_SPACE) { - // We totally ignore this accelerator. - // TODO(mazda): Fix crbug.com/158217 - return false; - } - if (ime_control_delegate_) - return ime_control_delegate_->HandleNextIme(); - break; + return HandleNextIme( + ime_control_delegate_.get(), previous_event_type, previous_key_code); case PREVIOUS_IME: - if (ime_control_delegate_) - return ime_control_delegate_->HandlePreviousIme(accelerator); - break; + return HandlePreviousIme(ime_control_delegate_.get(), accelerator); case PRINT_UI_HIERARCHIES: return HandlePrintUIHierarchies(); case SWITCH_IME: - if (ime_control_delegate_) - return ime_control_delegate_->HandleSwitchIme(accelerator); - break; + return HandleSwitchIme(ime_control_delegate_.get(), accelerator); case LAUNCH_APP_0: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(0); - return true; + return HandleLaunchAppN(0); case LAUNCH_APP_1: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(1); - return true; + return HandleLaunchAppN(1); case LAUNCH_APP_2: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(2); - return true; + return HandleLaunchAppN(2); case LAUNCH_APP_3: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(3); - return true; + return HandleLaunchAppN(3); case LAUNCH_APP_4: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(4); - return true; + return HandleLaunchAppN(4); case LAUNCH_APP_5: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(5); - return true; + return HandleLaunchAppN(5); case LAUNCH_APP_6: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(6); - return true; + return HandleLaunchAppN(6); case LAUNCH_APP_7: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(7); - return true; + return HandleLaunchAppN(7); case LAUNCH_LAST_APP: - Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1); - return true; + return HandleLaunchLastApp(); case WINDOW_SNAP_LEFT: - case WINDOW_SNAP_RIGHT: { - wm::WindowState* window_state = wm::GetActiveWindowState(); - // Disable window snapping shortcut key for full screen window due to - // http://crbug.com/135487. - if (!window_state || - window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL || - window_state->IsFullscreen()) { - break; - } - - internal::SnapSizer::SnapWindow(window_state, - action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE : - internal::SnapSizer::RIGHT_EDGE); - return true; - } + case WINDOW_SNAP_RIGHT: + return HandleWindowSnap(action); case WINDOW_MINIMIZE: - return accelerators::ToggleMinimized(); - case TOGGLE_FULLSCREEN: { - if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) { - content::RecordAction( - content::UserMetricsAction("Accel_Fullscreen_F4")); - } - accelerators::ToggleFullscreen(); - return true; - } - case TOGGLE_MAXIMIZED: { + return HandleWindowMinimize(); + case TOGGLE_FULLSCREEN: + return HandleToggleFullscreen(key_code); + case TOGGLE_MAXIMIZED: accelerators::ToggleMaximized(); return true; - } - case WINDOW_POSITION_CENTER: { - content::RecordAction(content::UserMetricsAction("Accel_Center")); - aura::Window* window = wm::GetActiveWindow(); - // Docked windows do not support centering and ignore accelerator. - if (window && !wm::GetWindowState(window)->IsDocked()) { - wm::CenterWindow(window); - return true; - } - break; - } + case WINDOW_POSITION_CENTER: + return HandlePositionCenter(); case SCALE_UI_UP: return HandleScaleUI(true /* up */); case SCALE_UI_DOWN: diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc index ce3476b888..8569171ca4 100644 --- a/ash/accelerators/accelerator_controller_unittest.cc +++ b/ash/accelerators/accelerator_controller_unittest.cc @@ -505,7 +505,7 @@ TEST_F(AcceleratorControllerTest, WindowSnap) { EXPECT_EQ(window->bounds().ToString(), snap_right.ToString()); } { - gfx::Rect normal_bounds = window->bounds(); + gfx::Rect normal_bounds = window_state->GetRestoreBoundsInParent(); GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy); EXPECT_TRUE(window_state->IsMaximized()); @@ -513,6 +513,8 @@ TEST_F(AcceleratorControllerTest, WindowSnap) { GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy); EXPECT_FALSE(window_state->IsMaximized()); + // Window gets restored to its restore bounds since side-maximized state + // is treated as a "maximized" state. EXPECT_EQ(normal_bounds.ToString(), window->bounds().ToString()); GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy); diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc index 4ffc1ab54a..cd3a580473 100644 --- a/ash/accelerators/accelerator_table.cc +++ b/ash/accelerators/accelerator_table.cc @@ -81,7 +81,9 @@ const AcceleratorData kAcceleratorData[] = { { true, ui::VKEY_Z, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, TOGGLE_SPOKEN_FEEDBACK }, { true, ui::VKEY_CONTROL, ui::EF_CONTROL_DOWN, SILENCE_SPOKEN_FEEDBACK}, - { true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, + { true, ui::VKEY_OEM_COMMA, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, + SWITCH_TO_PREVIOUS_USER }, + { true, ui::VKEY_OEM_PERIOD, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_NEXT_USER }, #endif // defined(OS_CHROMEOS) { true, ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, OPEN_FEEDBACK_PAGE }, diff --git a/ash/accelerators/accelerator_table.h b/ash/accelerators/accelerator_table.h index 21bbf6ae6b..d12096037b 100644 --- a/ash/accelerators/accelerator_table.h +++ b/ash/accelerators/accelerator_table.h @@ -135,6 +135,7 @@ enum AcceleratorAction { OPEN_CROSH, OPEN_FILE_MANAGER, SWITCH_TO_NEXT_USER, + SWITCH_TO_PREVIOUS_USER, #endif }; diff --git a/ash/ash.gyp b/ash/ash.gyp index 091465ec35..089ea15d9a 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -156,10 +156,6 @@ 'keyboard_uma_event_filter.h', 'launcher/launcher.cc', 'launcher/launcher.h', - 'launcher/launcher_delegate.h', - 'launcher/launcher_item_delegate_manager.cc', - 'launcher/launcher_item_delegate_manager.h', - 'launcher/launcher_item_delegate.h', 'launcher/launcher_types.cc', 'launcher/launcher_types.h', 'magnifier/magnification_controller.cc', @@ -210,10 +206,15 @@ 'shelf/shelf_button.cc', 'shelf/shelf_button.h', 'shelf/shelf_button_host.h', + 'shelf/shelf_delegate.h', 'shelf/shelf_icon_observer.h', + 'shelf/shelf_item_delegate.h', + 'shelf/shelf_item_delegate_manager.cc', + 'shelf/shelf_item_delegate_manager.h', 'shelf/shelf_layout_manager.cc', 'shelf/shelf_layout_manager.h', 'shelf/shelf_layout_manager_observer.h', + 'shelf/shelf_menu_model.h', 'shelf/shelf_model.cc', 'shelf/shelf_model.h', 'shelf/shelf_model_observer.h', @@ -228,6 +229,8 @@ 'shelf/shelf_view.h', 'shelf/shelf_widget.cc', 'shelf/shelf_widget.h', + 'shelf/shelf_window_watcher.cc', + 'shelf/shelf_window_watcher.h', 'shell.cc', 'shell.h', 'shell_delegate.h', @@ -422,8 +425,14 @@ 'wm/base_layout_manager.h', 'wm/boot_splash_screen_chromeos.cc', 'wm/boot_splash_screen_chromeos.h', + 'wm/caption_buttons/alternate_frame_size_button.cc', + 'wm/caption_buttons/alternate_frame_size_button.h', + 'wm/caption_buttons/alternate_frame_size_button_delegate.h', 'wm/caption_buttons/bubble_contents_button_row.cc', 'wm/caption_buttons/bubble_contents_button_row.h', + 'wm/caption_buttons/caption_button_types.h', + 'wm/caption_buttons/frame_caption_button.cc', + 'wm/caption_buttons/frame_caption_button.h', 'wm/caption_buttons/frame_caption_button_container_view.cc', 'wm/caption_buttons/frame_caption_button_container_view.h', 'wm/caption_buttons/frame_maximize_button.cc', @@ -433,7 +442,6 @@ 'wm/caption_buttons/maximize_bubble_controller.h', 'wm/caption_buttons/maximize_bubble_controller_bubble.cc', 'wm/caption_buttons/maximize_bubble_controller_bubble.h', - 'wm/caption_buttons/maximize_bubble_frame_state.h', 'wm/coordinate_conversion.cc', 'wm/coordinate_conversion.h', 'wm/custom_frame_view_ash.cc', @@ -568,10 +576,6 @@ 'wm/wm_types.h', 'wm/workspace_controller.cc', 'wm/workspace_controller.h', - 'wm/workspace/colored_window_controller.cc', - 'wm/workspace/colored_window_controller.h', - 'wm/workspace/desktop_background_fade_controller.cc', - 'wm/workspace/desktop_background_fade_controller.h', 'wm/workspace/magnetism_matcher.cc', 'wm/workspace/magnetism_matcher.h', 'wm/workspace/multi_window_resize_controller.cc', @@ -651,32 +655,30 @@ 'test/ash_test_helper.h', 'test/cursor_manager_test_api.cc', 'test/cursor_manager_test_api.h', - 'test/launcher_test_api.cc', - 'test/launcher_test_api.h', - 'test/shelf_view_test_api.cc', - 'test/shelf_view_test_api.h', 'test/display_manager_test_api.cc', 'test/display_manager_test_api.h', - 'test/launcher_item_delegate_manager_test_api.cc', - 'test/launcher_item_delegate_manager_test_api.h', + 'test/launcher_test_api.cc', + 'test/launcher_test_api.h', 'test/mirror_window_test_api.cc', 'test/mirror_window_test_api.h', 'test/overflow_bubble_view_test_api.cc', 'test/overflow_bubble_view_test_api.h', + 'test/shelf_item_delegate_manager_test_api.cc', + 'test/shelf_item_delegate_manager_test_api.h', + 'test/shelf_view_test_api.cc', + 'test/shelf_view_test_api.h', 'test/shell_test_api.cc', 'test/shell_test_api.h', 'test/test_activation_delegate.cc', 'test/test_activation_delegate.h', - 'test/test_user_wallpaper_delegate.cc', - 'test/test_user_wallpaper_delegate.h', - 'test/test_launcher_delegate.cc', - 'test/test_launcher_delegate.h', - 'test/test_launcher_item_delegate.cc', - 'test/test_launcher_item_delegate.h', 'test/test_screenshot_delegate.cc', 'test/test_screenshot_delegate.cc', 'test/test_session_state_delegate.cc', 'test/test_session_state_delegate.h', + 'test/test_shelf_delegate.cc', + 'test/test_shelf_delegate.h', + 'test/test_shelf_item_delegate.cc', + 'test/test_shelf_item_delegate.h', 'test/test_shell_delegate.cc', 'test/test_shell_delegate.h', 'test/test_suite.cc', @@ -685,6 +687,8 @@ 'test/test_suite_init.mm', 'test/test_system_tray_delegate.cc', 'test/test_system_tray_delegate.h', + 'test/test_user_wallpaper_delegate.cc', + 'test/test_user_wallpaper_delegate.h', 'test/ui_controls_factory_ash.cc', 'test/ui_controls_factory_ash.h', ], @@ -786,15 +790,16 @@ 'shelf/shelf_tooltip_manager_unittest.cc', 'shelf/shelf_view_unittest.cc', 'shelf/shelf_widget_unittest.cc', + 'shelf/shelf_window_watcher_unittest.cc', 'shell/app_list.cc', 'shell/bubble.cc', 'shell/context_menu.cc', 'shell/context_menu.h', - 'shell/launcher_delegate_impl.cc', - 'shell/launcher_delegate_impl.h', 'shell/lock_view.cc', 'shell/panel_window.cc', 'shell/panel_window.h', + 'shell/shelf_delegate_impl.cc', + 'shell/shelf_delegate_impl.h', 'shell/shell_delegate_impl.cc', 'shell/shell_delegate_impl.h', 'shell/widgets.cc', @@ -802,8 +807,8 @@ 'shell/window_type_launcher.h', 'shell/window_watcher.cc', 'shell/window_watcher.h', - 'shell/window_watcher_launcher_item_delegate.cc', - 'shell/window_watcher_launcher_item_delegate.h', + 'shell/window_watcher_shelf_item_delegate.cc', + 'shell/window_watcher_shelf_item_delegate.h', 'shell/window_watcher_unittest.cc', 'shell_unittest.cc', 'system/chromeos/managed/tray_locally_managed_user_unittest.cc', @@ -815,6 +820,7 @@ 'system/chromeos/tray_display_unittest.cc', 'system/date/date_view_unittest.cc', 'system/tray/system_tray_unittest.cc', + 'system/tray/tray_details_view_unittest.cc', 'system/user/tray_user_unittest.cc', 'system/web_notification/web_notification_tray_unittest.cc', 'test/ash_test_helper_unittest.cc', @@ -824,6 +830,7 @@ 'wm/app_list_controller_unittest.cc', 'wm/ash_native_cursor_manager_unittest.cc', 'wm/base_layout_manager_unittest.cc', + 'wm/caption_buttons/alternate_frame_size_button_unittest.cc', 'wm/caption_buttons/frame_caption_button_container_view_unittest.cc', 'wm/caption_buttons/frame_maximize_button_unittest.cc', 'wm/dock/docked_window_layout_manager_unittest.cc', @@ -958,11 +965,11 @@ 'shell/example_factory.h', 'shell/keyboard_controller_proxy_stub.cc', 'shell/keyboard_controller_proxy_stub.h', - 'shell/launcher_delegate_impl.cc', - 'shell/launcher_delegate_impl.h', 'shell/lock_view.cc', 'shell/panel_window.cc', 'shell/panel_window.h', + 'shell/shelf_delegate_impl.cc', + 'shell/shelf_delegate_impl.h', 'shell/shell_delegate_impl.cc', 'shell/shell_delegate_impl.h', 'shell/shell_main.cc', @@ -975,8 +982,8 @@ 'shell/window_type_launcher.h', 'shell/window_watcher.cc', 'shell/window_watcher.h', - 'shell/window_watcher_launcher_item_delegate.cc', - 'shell/window_watcher_launcher_item_delegate.h', + 'shell/window_watcher_shelf_item_delegate.cc', + 'shell/window_watcher_shelf_item_delegate.h', '../content/app/startup_helper_win.cc', '../ui/views/test/test_views_delegate.cc', ], diff --git a/ash/ash_constants.cc b/ash/ash_constants.cc index a1404d0608..82501f2085 100644 --- a/ash/ash_constants.cc +++ b/ash/ash_constants.cc @@ -9,8 +9,6 @@ namespace ash { -DEFINE_WINDOW_PROPERTY_KEY(bool, kConstrainedWindowKey, false); - const int kResizeAreaCornerSize = 16; const int kResizeOutsideBoundsSize = 6; const int kResizeOutsideBoundsScaleForTouch = 5; diff --git a/ash/ash_constants.h b/ash/ash_constants.h index 8e889851ea..cad4354bcb 100644 --- a/ash/ash_constants.h +++ b/ash/ash_constants.h @@ -13,11 +13,6 @@ typedef unsigned int SkColor; namespace ash { -// The window is a constrained window and lives therefore entirely within -// another aura window. -ASH_EXPORT extern const aura::WindowProperty<bool>* const - kConstrainedWindowKey; - // In the window corners, the resize areas don't actually expand bigger, but the // 16 px at the end of each edge triggers diagonal resizing. ASH_EXPORT extern const int kResizeAreaCornerSize; diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc index 88f569d3b3..29463d0889 100644 --- a/ash/ash_switches.cc +++ b/ash/ash_switches.cc @@ -83,9 +83,7 @@ const char kAshEnableAdvancedGestures[] = "ash-enable-advanced-gestures"; // Use alternate visual style for the caption buttons (minimize, maximize, // restore, close). The alternate style: // - Adds a dedicated button for minimize. -// - Increases the height of the maximized header. // - Removes the maximize button's help bubble. -// - Switches snapping a window left/right to be always 50%. const char kAshEnableAlternateFrameCaptionButtonStyle[] = "ash-enable-alternate-caption-button"; @@ -206,8 +204,11 @@ const char kAshDisableDragAndDropAppListToLauncher[] = "ash-disable-drag-and-drop-applist-to-launcher"; bool UseAlternateFrameCaptionButtonStyle() { - return CommandLine::ForCurrentProcess()-> - HasSwitch(kAshEnableAlternateFrameCaptionButtonStyle); + // For the sake of simplicity, the alternate caption button style is only + // used if snapped windows are always 50% of the screen's width. + CommandLine* command_line = CommandLine::ForCurrentProcess(); + return command_line->HasSwitch(kAshEnableAlternateFrameCaptionButtonStyle) && + !command_line->HasSwitch(kAshMultipleSnapWindowWidths); } bool UseAlternateShelfLayout() { diff --git a/ash/display/display_error_observer_chromeos.cc b/ash/display/display_error_observer_chromeos.cc index 3b619ef63f..34e1bc2e5d 100644 --- a/ash/display/display_error_observer_chromeos.cc +++ b/ash/display/display_error_observer_chromeos.cc @@ -49,7 +49,9 @@ void DisplayErrorObserver::OnDisplayModeChangeFailed( base::string16(), // message bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY), base::string16(), // display_source - message_center::NotifierId(system_notifier::NOTIFIER_DISPLAY_ERROR), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierDisplayError), message_center::RichNotificationData(), NULL)); message_center::MessageCenter::Get()->AddNotification(notification.Pass()); diff --git a/ash/display/resolution_notification_controller.cc b/ash/display/resolution_notification_controller.cc index 3069c83e52..0914772ac0 100644 --- a/ash/display/resolution_notification_controller.cc +++ b/ash/display/resolution_notification_controller.cc @@ -229,7 +229,8 @@ void ResolutionNotificationController::CreateOrUpdateNotification( bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY), base::string16() /* display_source */, message_center::NotifierId( - system_notifier::NOTIFIER_DISPLAY_RESOLUTION_CHANGE), + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierDisplayResolutionChange), data, new ResolutionChangeNotificationDelegate( this, change_info_->timeout_count > 0))); diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc index 07ab4f3933..1d5d636347 100644 --- a/ash/drag_drop/drag_drop_controller.cc +++ b/ash/drag_drop/drag_drop_controller.cc @@ -461,14 +461,10 @@ void DragDropController::OnGestureEvent(ui::GestureEvent* event) { } void DragDropController::OnWindowDestroyed(aura::Window* window) { - if (drag_window_ == window) { - drag_window_->RemoveObserver(this); + if (drag_window_ == window) drag_window_ = NULL; - } - if (drag_source_window_ == window) { - drag_source_window_->RemoveObserver(this); + if (drag_source_window_ == window) drag_source_window_ = NULL; - } } //////////////////////////////////////////////////////////////////////////////// diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc index 0adf72ac01..58926e8c3b 100644 --- a/ash/launcher/launcher.cc +++ b/ash/launcher/launcher.cc @@ -8,11 +8,11 @@ #include <cmath> #include "ash/focus_cycler.h" -#include "ash/launcher/launcher_delegate.h" -#include "ash/launcher/launcher_item_delegate.h" -#include "ash/launcher/launcher_item_delegate_manager.h" #include "ash/root_window_controller.h" #include "ash/screen_ash.h" +#include "ash/shelf/shelf_delegate.h" +#include "ash/shelf/shelf_item_delegate.h" +#include "ash/shelf/shelf_item_delegate_manager.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_navigator.h" @@ -43,11 +43,11 @@ namespace ash { const char Launcher::kNativeViewName[] = "ShelfView"; Launcher::Launcher(ShelfModel* shelf_model, - LauncherDelegate* launcher_delegate, + ShelfDelegate* shelf_delegate, ShelfWidget* shelf_widget) : shelf_view_(NULL), alignment_(shelf_widget->GetAlignment()), - delegate_(launcher_delegate), + delegate_(shelf_delegate), shelf_widget_(shelf_widget) { shelf_view_ = new internal::ShelfView( shelf_model, delegate_, shelf_widget_->shelf_layout_manager()); @@ -108,10 +108,10 @@ void Launcher::ActivateLauncherItem(int index) { ui::EF_NONE, false); - const ash::LauncherItem& item = shelf_view_->model()->items()[index]; - ash::LauncherItemDelegate* item_delegate = - Shell::GetInstance()->launcher_item_delegate_manager()-> - GetLauncherItemDelegate(item.id); + const LauncherItem& item = shelf_view_->model()->items()[index]; + ShelfItemDelegate* item_delegate = + Shell::GetInstance()->shelf_item_delegate_manager()->GetShelfItemDelegate( + item.id); item_delegate->ItemSelected(event); } diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h index 5f1da0b111..5c50b8ce7d 100644 --- a/ash/launcher/launcher.h +++ b/ash/launcher/launcher.h @@ -41,7 +41,7 @@ namespace test { class LauncherTestAPI; } -class LauncherDelegate; +class ShelfDelegate; class ShelfIconObserver; class ShelfModel; class ShelfWidget; @@ -50,9 +50,7 @@ class ASH_EXPORT Launcher { public: static const char kNativeViewName[]; - Launcher(ShelfModel* shelf_model, - LauncherDelegate* launcher_delegate, - ShelfWidget* shelf_widget); + Launcher(ShelfModel* model, ShelfDelegate* delegate, ShelfWidget* widget); virtual ~Launcher(); // Return the launcher for the primary display. NULL if no user is @@ -122,7 +120,7 @@ class ASH_EXPORT Launcher { ShelfAlignment alignment_; - LauncherDelegate* delegate_; + ShelfDelegate* delegate_; ShelfWidget* shelf_widget_; diff --git a/ash/launcher/launcher_item_delegate_manager.cc b/ash/launcher/launcher_item_delegate_manager.cc deleted file mode 100644 index efd885717f..0000000000 --- a/ash/launcher/launcher_item_delegate_manager.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/launcher/launcher_item_delegate_manager.h" - -#include "ash/launcher/launcher_item_delegate.h" -#include "ash/shelf/shelf_model.h" -#include "ash/shell.h" -#include "base/logging.h" -#include "base/stl_util.h" - -namespace ash { - -LauncherItemDelegateManager::LauncherItemDelegateManager(ShelfModel* model) - : model_(model) { - DCHECK(model_); - model_->AddObserver(this); -} - -LauncherItemDelegateManager::~LauncherItemDelegateManager() { - model_->RemoveObserver(this); - STLDeleteContainerPairSecondPointers(id_to_item_delegate_map_.begin(), - id_to_item_delegate_map_.end()); -} - -void LauncherItemDelegateManager::SetLauncherItemDelegate( - ash::LauncherID id, - scoped_ptr<LauncherItemDelegate> item_delegate) { - // If another LauncherItemDelegate is already registered for |id|, we assume - // that this request is replacing LauncherItemDelegate for |id| with - // |item_delegate|. - RemoveLauncherItemDelegate(id); - id_to_item_delegate_map_[id] = item_delegate.release(); -} - -LauncherItemDelegate* LauncherItemDelegateManager::GetLauncherItemDelegate( - ash::LauncherID id) { - if (model_->ItemIndexByID(id) != -1) { - // Each LauncherItem has to have a LauncherItemDelegate. - DCHECK(id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()); - return id_to_item_delegate_map_[id]; - } - return NULL; -} - -void LauncherItemDelegateManager::ShelfItemAdded(int index) { -} - -void LauncherItemDelegateManager::ShelfItemRemoved(int index, LauncherID id) { - RemoveLauncherItemDelegate(id); -} - -void LauncherItemDelegateManager::ShelfItemMoved(int start_index, - int target_index) { -} - -void LauncherItemDelegateManager::ShelfItemChanged( - int index, - const LauncherItem& old_item) { -} - -void LauncherItemDelegateManager::ShelfStatusChanged() { -} - -void LauncherItemDelegateManager::RemoveLauncherItemDelegate( - ash::LauncherID id) { - if (id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()) { - delete id_to_item_delegate_map_[id]; - id_to_item_delegate_map_.erase(id); - } -} - -} // namespace ash diff --git a/ash/launcher/launcher_item_delegate_manager.h b/ash/launcher/launcher_item_delegate_manager.h deleted file mode 100644 index 1768cd964e..0000000000 --- a/ash/launcher/launcher_item_delegate_manager.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_ -#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_ - -#include <map> - -#include "ash/ash_export.h" -#include "ash/launcher/launcher_types.h" -#include "ash/shelf/shelf_model_observer.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" - -namespace ash { -class LauncherItemDelegate; -class ShelfModel; - -namespace test { -class LauncherItemDelegateManagerTestAPI; -} - -// LauncherItemDelegateManager manages the set of LauncherItemDelegates for the -// launcher. LauncherItemDelegateManager does not create LauncherItemDelegates, -// rather it is expected that someone else invokes SetLauncherItemDelegate -// appropriately. On the other hand, LauncherItemDelegateManager destroys -// LauncherItemDelegates when the corresponding item from the model is removed. -class ASH_EXPORT LauncherItemDelegateManager : public ShelfModelObserver { - public: - explicit LauncherItemDelegateManager(ShelfModel* model); - virtual ~LauncherItemDelegateManager(); - - // Set |item_delegate| for |id| and take an ownership. - void SetLauncherItemDelegate( - ash::LauncherID id, - scoped_ptr<ash::LauncherItemDelegate> item_delegate); - - // Returns LauncherItemDelegate for |item_type|. Always returns non-NULL. - LauncherItemDelegate* GetLauncherItemDelegate(ash::LauncherID id); - - // ShelfModelObserver overrides: - virtual void ShelfItemAdded(int model_index) OVERRIDE; - virtual void ShelfItemRemoved(int index, ash::LauncherID id) OVERRIDE; - virtual void ShelfItemMoved(int start_index, int targetindex) OVERRIDE; - virtual void ShelfItemChanged(int index, - const LauncherItem& old_item) OVERRIDE; - virtual void ShelfStatusChanged() OVERRIDE; - - private: - friend class ash::test::LauncherItemDelegateManagerTestAPI; - - typedef std::map<ash::LauncherID, LauncherItemDelegate*> - LauncherIDToItemDelegateMap; - - // Remove and destroy LauncherItemDelegate for |id|. - void RemoveLauncherItemDelegate(ash::LauncherID id); - - // Owned by Shell. - ShelfModel* model_; - - LauncherIDToItemDelegateMap id_to_item_delegate_map_; - - DISALLOW_COPY_AND_ASSIGN(LauncherItemDelegateManager); -}; - -} // namespace ash - -#endif // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_ diff --git a/ash/launcher/launcher_types.cc b/ash/launcher/launcher_types.cc index 46fc827ade..67dc5729c9 100644 --- a/ash/launcher/launcher_types.cc +++ b/ash/launcher/launcher_types.cc @@ -8,14 +8,25 @@ namespace ash { const int kLauncherPreferredSize = 48; const int kLauncherBackgroundAlpha = 204; +const int kInvalidImageResourceID = -1; +const int kInvalidLauncherID = 0; +const int kTimeToSwitchBackgroundMs = 1000; LauncherItem::LauncherItem() : type(TYPE_UNDEFINED), - id(0), + id(kInvalidLauncherID), status(STATUS_CLOSED) { } LauncherItem::~LauncherItem() { } +LauncherItemDetails::LauncherItemDetails() + : type(TYPE_UNDEFINED), + image_resource_id(kInvalidImageResourceID) { +} + +LauncherItemDetails::~LauncherItemDetails() { +} + } // namespace ash diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h index c60062902a..bbb1bbc41d 100644 --- a/ash/launcher/launcher_types.h +++ b/ash/launcher/launcher_types.h @@ -8,6 +8,7 @@ #include <vector> #include "ash/ash_export.h" +#include "base/strings/string16.h" #include "ui/gfx/image/image_skia.h" namespace ash { @@ -21,6 +22,14 @@ ASH_EXPORT extern const int kLauncherPreferredSize; // Max alpha of the launcher background. ASH_EXPORT extern const int kLauncherBackgroundAlpha; +// Invalid image resource id used for LauncherItemDetails. +extern const int kInvalidImageResourceID; + +extern const int kInvalidLauncherID; + +// Animation duration for switching black shelf and dock background on and off. +ASH_EXPORT extern const int kTimeToSwitchBackgroundMs; + // Type the LauncherItem represents. enum LauncherItemType { // Represents a running app panel. @@ -81,6 +90,22 @@ enum CycleDirection { CYCLE_BACKWARD }; +// LauncherItemDetails may be set on Window (by way of +// SetLauncherItemDetailsForWindow) to make the window appear in the shelf. See +// ShelfWindowWatcher for details. +struct ASH_EXPORT LauncherItemDetails { + LauncherItemDetails(); + ~LauncherItemDetails(); + + LauncherItemType type; + + // Resource id of the image to display on the shelf. + int image_resource_id; + + // Title of the item. + base::string16 title; +}; + } // namespace ash #endif // ASH_LAUNCHER_LAUNCHER_TYPES_H_ diff --git a/ash/launcher/launcher_unittest.cc b/ash/launcher/launcher_unittest.cc index 6359508ade..24eca33499 100644 --- a/ash/launcher/launcher_unittest.cc +++ b/ash/launcher/launcher_unittest.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_item_delegate_manager.h" #include "ash/shelf/shelf_button.h" +#include "ash/shelf/shelf_item_delegate_manager.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_view.h" #include "ash/shelf/shelf_widget.h" @@ -12,7 +12,7 @@ #include "ash/test/ash_test_base.h" #include "ash/test/launcher_test_api.h" #include "ash/test/shelf_view_test_api.h" -#include "ash/test/test_launcher_item_delegate.h" +#include "ash/test/test_shelf_item_delegate.h" #include "ash/wm/window_util.h" #include "ui/aura/root_window.h" #include "ui/gfx/display.h" @@ -51,7 +51,7 @@ class LauncherTest : public ash::test::AshTestBase { shelf_view_ = test.shelf_view(); shelf_model_ = shelf_view_->model(); item_delegate_manager_ = - Shell::GetInstance()->launcher_item_delegate_manager(); + Shell::GetInstance()->shelf_item_delegate_manager(); test_.reset(new ash::test::ShelfViewTestAPI(shelf_view_)); } @@ -72,7 +72,7 @@ class LauncherTest : public ash::test::AshTestBase { return shelf_model_; } - LauncherItemDelegateManager* item_manager() { + ShelfItemDelegateManager* item_manager() { return item_delegate_manager_; } @@ -84,8 +84,8 @@ class LauncherTest : public ash::test::AshTestBase { Launcher* launcher_; ShelfView* shelf_view_; ShelfModel* shelf_model_; - LauncherItemDelegateManager* item_delegate_manager_; - scoped_ptr<ash::test::ShelfViewTestAPI> test_; + ShelfItemDelegateManager* item_delegate_manager_; + scoped_ptr<test::ShelfViewTestAPI> test_; DISALLOW_COPY_AND_ASSIGN(LauncherTest); }; @@ -121,10 +121,10 @@ TEST_F(LauncherTest, checkHoverAfterMenu) { item.status = STATUS_RUNNING; int index = shelf_model()->Add(item); - scoped_ptr<LauncherItemDelegate> delegate( - new ash::test::TestLauncherItemDelegate(NULL)); - item_manager()->SetLauncherItemDelegate(shelf_model()->items()[index].id, - delegate.Pass()); + scoped_ptr<ShelfItemDelegate> delegate( + new test::TestShelfItemDelegate(NULL)); + item_manager()->SetShelfItemDelegate(shelf_model()->items()[index].id, + delegate.Pass()); ASSERT_EQ(++button_count, test_api()->GetButtonCount()); ShelfButton* button = test_api()->GetButton(index); diff --git a/ash/multi_profile_uma.cc b/ash/multi_profile_uma.cc index f6d9840f5a..cbf3b059a3 100644 --- a/ash/multi_profile_uma.cc +++ b/ash/multi_profile_uma.cc @@ -30,10 +30,28 @@ void MultiProfileUMA::RecordSwitchActiveUser(SwitchActiveUserAction action) { } // static +void MultiProfileUMA::RecordTeleportWindowType(TeleportWindowType window_type) { + UMA_HISTOGRAM_ENUMERATION("MultiProfile.TeleportWindowType", + window_type, + NUM_TELEPORT_WINDOW_TYPES); +} + +// static void MultiProfileUMA::RecordTeleportAction(TeleportWindowAction action) { UMA_HISTOGRAM_ENUMERATION("MultiProfile.TeleportWindow", action, NUM_TELEPORT_WINDOW_ACTIONS); } +// static +void MultiProfileUMA::RecordUserCount(int number_of_users) { + UMA_HISTOGRAM_COUNTS_100("MultiProfile.UsersPerSession", number_of_users); +} + +// static +void MultiProfileUMA::RecordDiscardedTab(int number_of_users) { + UMA_HISTOGRAM_COUNTS_100("MultiProfile.DiscardedTabsPerUser", + number_of_users); +} + } // namespace ash diff --git a/ash/multi_profile_uma.h b/ash/multi_profile_uma.h index 1c70482b67..937b4d818e 100644 --- a/ash/multi_profile_uma.h +++ b/ash/multi_profile_uma.h @@ -35,6 +35,17 @@ class ASH_EXPORT MultiProfileUMA { NUM_SWITCH_ACTIVE_USER_ACTIONS }; + enum TeleportWindowType { + TELEPORT_WINDOW_BROWSER = 0, + TELEPORT_WINDOW_INCOGNITO_BROWSER, + TELEPORT_WINDOW_V1_APP, + TELEPORT_WINDOW_V2_APP, + TELEPORT_WINDOW_PANEL, + TELEPORT_WINDOW_POPUP, + TELEPORT_WINDOW_UNKNOWN, + NUM_TELEPORT_WINDOW_TYPES + }; + enum TeleportWindowAction { TELEPORT_WINDOW_DRAG_AND_DROP = 0, TELEPORT_WINDOW_CAPTION_MENU, @@ -52,9 +63,18 @@ class ASH_EXPORT MultiProfileUMA { // Record switching the active user and what UI path was taken. static void RecordSwitchActiveUser(SwitchActiveUserAction action); + // Record the type of window which got teleported to another desk. + static void RecordTeleportWindowType(TeleportWindowType window_type); + // Record the way and how many times a window got teleported to another desk. static void RecordTeleportAction(TeleportWindowAction action); + // Record number of users joined into a session. Called upon logout. + static void RecordUserCount(int number_of_users); + + // Record a discarded tab in the number of running users bucket. + static void RecordDiscardedTab(int number_of_users); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(MultiProfileUMA); }; diff --git a/ash/popup_message.h b/ash/popup_message.h index 040b9fe7e1..72a0cf5453 100644 --- a/ash/popup_message.h +++ b/ash/popup_message.h @@ -13,6 +13,7 @@ namespace views { class BubbleDelegateView; +class Widget; } namespace ash { diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd index 5447785b74..6fec31bb14 100644 --- a/ash/resources/ash_resources.grd +++ b/ash/resources/ash_resources.grd @@ -20,6 +20,7 @@ BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE SAME CONDITIONALS. --> <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BACKGROUND" file="common/launcher/launcher_background.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_CORNER" file="common/launcher/launcher_corner.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_DIMMING" file="common/launcher/launcher_dimming.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST" file="common/launcher/launcher_appmenu.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_ALTERNATE" file="common/alt_launcher/status_app_menu_icon.png" /> diff --git a/ash/resources/default_100_percent/common/launcher/launcher_corner.png b/ash/resources/default_100_percent/common/launcher/launcher_corner.png Binary files differnew file mode 100644 index 0000000000..d730065dd1 --- /dev/null +++ b/ash/resources/default_100_percent/common/launcher/launcher_corner.png diff --git a/ash/resources/default_200_percent/common/launcher/launcher_corner.png b/ash/resources/default_200_percent/common/launcher/launcher_corner.png Binary files differnew file mode 100644 index 0000000000..e5f8c9633a --- /dev/null +++ b/ash/resources/default_200_percent/common/launcher/launcher_corner.png diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc index 45c0f7a637..4775accf97 100644 --- a/ash/root_window_controller.cc +++ b/ash/root_window_controller.cc @@ -518,23 +518,27 @@ void RootWindowController::UpdateShelfVisibility() { shelf_->shelf_layout_manager()->UpdateVisibilityState(); } -const aura::Window* RootWindowController::GetTopmostFullscreenWindow() const { +const aura::Window* RootWindowController::GetWindowForFullscreenMode() const { const aura::Window::Windows& windows = GetContainer(kShellWindowId_DefaultContainer)->children(); + const aura::Window* topmost_window = NULL; for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); iter != windows.rend(); ++iter) { - if (wm::GetWindowState(*iter)->IsFullscreen()) - return *iter; + if (((*iter)->type() == aura::client::WINDOW_TYPE_NORMAL || + (*iter)->type() == aura::client::WINDOW_TYPE_PANEL) && + (*iter)->layer()->GetTargetVisibility()) { + topmost_window = *iter; + break; + } + } + while (topmost_window) { + if (wm::GetWindowState(topmost_window)->IsFullscreen()) + return topmost_window; + topmost_window = topmost_window->transient_parent(); } return NULL; } -aura::Window* RootWindowController::GetTopmostFullscreenWindow() { - return const_cast<aura::Window*>( - const_cast<const RootWindowController*>(this)-> - GetTopmostFullscreenWindow()); -} - void RootWindowController::ActivateKeyboard( keyboard::KeyboardController* keyboard_controller) { if (!keyboard::IsKeyboardEnabled() || diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h index 076bfe6a4f..6e2d545442 100644 --- a/ash/root_window_controller.h +++ b/ash/root_window_controller.h @@ -221,10 +221,9 @@ class ASH_EXPORT RootWindowController : public ShellObserver { // Initialize touch HUDs if necessary. void InitTouchHuds(); - // Returns the window, if any, which is in fullscreen mode. If multiple - // windows are in fullscreen state, the topmost one is preferred. - const aura::Window* GetTopmostFullscreenWindow() const; - aura::Window* GetTopmostFullscreenWindow(); + // Returns the topmost window or one of its transient parents, if any of them + // are in fullscreen mode. + const aura::Window* GetWindowForFullscreenMode() const; // Activate virtual keyboard on current root window controller. void ActivateKeyboard(keyboard::KeyboardController* keyboard_controller); diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc index d547a8a335..2ff7362629 100644 --- a/ash/root_window_controller_unittest.cc +++ b/ash/root_window_controller_unittest.cc @@ -454,7 +454,7 @@ TEST_F(RootWindowControllerTest, ModalContainerBlockedSession) { } } -TEST_F(RootWindowControllerTest, GetTopmostFullscreenWindow) { +TEST_F(RootWindowControllerTest, GetWindowForFullscreenMode) { UpdateDisplay("600x600"); internal::RootWindowController* controller = Shell::GetInstance()->GetPrimaryRootWindowController(); @@ -467,39 +467,23 @@ TEST_F(RootWindowControllerTest, GetTopmostFullscreenWindow) { Widget* w3 = Widget::CreateWindowWithParentAndBounds(NULL, w2->GetNativeWindow(), gfx::Rect(0, 0, 100, 100)); - // Test that GetTopmostFullscreenWindow() finds the fullscreen window when one + // Test that GetWindowForFullscreenMode() finds the fullscreen window when one // of its transient children is active. w3->Activate(); - EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow()); + EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode()); - // Since there's only one desktop workspace, it always returns the same - // fullscreen window. + // If the topmost window is not fullscreen, it returns NULL. w1->Activate(); - EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow()); -} + EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode()); + w1->Close(); + w3->Close(); -TEST_F(RootWindowControllerTest, MultipleFullscreenWindows) { - UpdateDisplay("600x600"); - internal::RootWindowController* controller = - Shell::GetInstance()->GetPrimaryRootWindowController(); - - Widget* w1 = CreateTestWidget(gfx::Rect(0, 0, 100, 100)); - w1->Maximize(); - Widget* w2 = CreateTestWidget(gfx::Rect(0, 0, 100, 100)); - w2->SetFullscreen(true); - Widget* w3 = CreateTestWidget(gfx::Rect(0, 0, 100, 100)); - w3->SetFullscreen(true); - - // Test that GetTopmostFullscreenWindow() finds the active fullscreen window. + // Only w2 remains, if minimized GetWindowForFullscreenMode should return + // NULL. w2->Activate(); - EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow()); - w3->Activate(); - EXPECT_EQ(w3->GetNativeWindow(), controller->GetTopmostFullscreenWindow()); - - // If the active window is not fullscreen, it still returns the topmost - // fullscreen window, which is the last active one. - w1->Activate(); - EXPECT_EQ(w3->GetNativeWindow(), controller->GetTopmostFullscreenWindow()); + EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode()); + w2->Minimize(); + EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode()); } // Test that user session window can't be focused if user session blocked by diff --git a/ash/scoped_target_root_window.h b/ash/scoped_target_root_window.h index 0e7a8b18d7..c0ab7ef755 100644 --- a/ash/scoped_target_root_window.h +++ b/ash/scoped_target_root_window.h @@ -4,6 +4,7 @@ #ifndef ASH_SCOPED_TARGET_ROOT_WINDOW_H_ #define ASH_SCOPED_TARGET_ROOT_WINDOW_H_ +#include "ash/ash_export.h" #include "base/basictypes.h" namespace aura { @@ -18,7 +19,7 @@ namespace internal { // in the same window where a user interaction happened. // An example usage is to specify the target root window when creating // a new window using launcher's icon. -class ScopedTargetRootWindow { +class ASH_EXPORT ScopedTargetRootWindow { public: explicit ScopedTargetRootWindow(aura::Window* root_window); ~ScopedTargetRootWindow(); diff --git a/ash/session_state_delegate.h b/ash/session_state_delegate.h index 47570ff911..91c6229133 100644 --- a/ash/session_state_delegate.h +++ b/ash/session_state_delegate.h @@ -33,6 +33,12 @@ typedef std::vector<std::string> UserIdList; // Delegate for checking and modifying the session state. class ASH_EXPORT SessionStateDelegate { public: + // Defines the cycle direction for |CycleActiveUser|. + enum CycleUser { + CYCLE_TO_NEXT_USER = 0, // Cycle to the next user. + CYCLE_TO_PREVIOUS_USER, // Cycle to the previous user. + }; + virtual ~SessionStateDelegate() {}; // Returns the maximum possible number of logged in users. @@ -96,9 +102,9 @@ class ASH_EXPORT SessionStateDelegate { // (if that user has already signed in). virtual void SwitchActiveUser(const std::string& user_id) = 0; - // Switches the active user to the next user, with the same ordering as - // GetLoggedInUsers. - virtual void SwitchActiveUserToNext() = 0; + // Switches the active user to the next or previous user, with the same + // ordering as GetLoggedInUsers. + virtual void CycleActiveUser(CycleUser cycle_user) = 0; // Adds or removes sessions state observer. virtual void AddSessionStateObserver(SessionStateObserver* observer) = 0; diff --git a/ash/session_state_delegate_stub.cc b/ash/session_state_delegate_stub.cc index 6daa5930d5..cea4132523 100644 --- a/ash/session_state_delegate_stub.cc +++ b/ash/session_state_delegate_stub.cc @@ -82,7 +82,7 @@ void SessionStateDelegateStub::GetLoggedInUsers(UserIdList* users) { void SessionStateDelegateStub::SwitchActiveUser(const std::string& user_id) { } -void SessionStateDelegateStub::SwitchActiveUserToNext() { +void SessionStateDelegateStub::CycleActiveUser(CycleUser cycle_user) { } void SessionStateDelegateStub::AddSessionStateObserver( diff --git a/ash/session_state_delegate_stub.h b/ash/session_state_delegate_stub.h index 6fe1eb9aea..f496e6bbc8 100644 --- a/ash/session_state_delegate_stub.h +++ b/ash/session_state_delegate_stub.h @@ -38,7 +38,7 @@ class SessionStateDelegateStub : public SessionStateDelegate { ash::MultiProfileIndex index) const OVERRIDE; virtual void GetLoggedInUsers(UserIdList* users) OVERRIDE; virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE; - virtual void SwitchActiveUserToNext() OVERRIDE; + virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE; virtual void AddSessionStateObserver( ash::SessionStateObserver* observer) OVERRIDE; virtual void RemoveSessionStateObserver( diff --git a/ash/shelf/alternate_app_list_button.cc b/ash/shelf/alternate_app_list_button.cc index 4ce283418b..bf442d1abc 100644 --- a/ash/shelf/alternate_app_list_button.cc +++ b/ash/shelf/alternate_app_list_button.cc @@ -4,8 +4,10 @@ #include "ash/shelf/alternate_app_list_button.h" +#include "ash/ash_constants.h" #include "ash/ash_switches.h" #include "ash/launcher/launcher_types.h" +#include "ash/shelf/shelf_button.h" #include "ash/shelf/shelf_button_host.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_widget.h" @@ -22,6 +24,7 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/views/controls/button/image_button.h" +#include "ui/views/painter.h" namespace ash { namespace internal { @@ -39,6 +42,8 @@ AlternateAppListButton::AlternateAppListButton(views::ButtonListener* listener, SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE)); SetSize(gfx::Size(ShelfLayoutManager::kShelfSize, ShelfLayoutManager::kShelfSize)); + SetFocusPainter(views::Painter::CreateSolidFocusPainter( + kFocusBorderColor, gfx::Insets(1, 1, 1, 1))); } AlternateAppListButton::~AlternateAppListButton() { @@ -156,7 +161,7 @@ void AlternateAppListButton::OnPaint(gfx::Canvas* canvas) { forground_bounds.x(), forground_bounds.y()); - OnPaintFocusBorder(canvas); + views::Painter::PaintFocusPainter(this, canvas, focus_painter()); } void AlternateAppListButton::GetAccessibleState( diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc index a9612bfe5f..697ed772b4 100644 --- a/ash/shelf/app_list_button.cc +++ b/ash/shelf/app_list_button.cc @@ -6,6 +6,7 @@ #include <vector> +#include "ash/ash_constants.h" #include "ash/launcher/launcher_types.h" #include "ash/shelf/shelf_button_host.h" #include "grit/ash_resources.h" @@ -17,6 +18,7 @@ #include "ui/compositor/layer_animation_element.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/views/painter.h" namespace ash { namespace internal { @@ -43,6 +45,8 @@ AppListButton::AppListButton(views::ButtonListener* listener, SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE)); SetSize(gfx::Size(kLauncherPreferredSize, kLauncherPreferredSize)); SetImageAlignment(ImageButton::ALIGN_CENTER, ImageButton::ALIGN_TOP); + SetFocusPainter(views::Painter::CreateSolidFocusPainter( + kFocusBorderColor, gfx::Insets(1, 1, 1, 1))); } AppListButton::~AppListButton() { diff --git a/ash/shelf/app_list_shelf_item_delegate.cc b/ash/shelf/app_list_shelf_item_delegate.cc index b9fd49dae2..7013d2be1f 100644 --- a/ash/shelf/app_list_shelf_item_delegate.cc +++ b/ash/shelf/app_list_shelf_item_delegate.cc @@ -19,7 +19,7 @@ AppListShelfItemDelegate::AppListShelfItemDelegate() { } AppListShelfItemDelegate::~AppListShelfItemDelegate() { - // LauncherItemDelegateManager owns and destroys this class. + // ShelfItemDelegateManager owns and destroys this class. } bool AppListShelfItemDelegate::ItemSelected(const ui::Event& event) { @@ -41,7 +41,7 @@ ui::MenuModel* AppListShelfItemDelegate::CreateContextMenu( return NULL; } -LauncherMenuModel* AppListShelfItemDelegate::CreateApplicationMenu( +ShelfMenuModel* AppListShelfItemDelegate::CreateApplicationMenu( int event_flags) { // AppList does not show an application menu. return NULL; diff --git a/ash/shelf/app_list_shelf_item_delegate.h b/ash/shelf/app_list_shelf_item_delegate.h index 750a84bbb8..0396821b65 100644 --- a/ash/shelf/app_list_shelf_item_delegate.h +++ b/ash/shelf/app_list_shelf_item_delegate.h @@ -5,26 +5,25 @@ #ifndef ASH_SHELF_APP_LIST_SHELF_ITEM_DELEGATE_H_ #define ASH_SHELF_APP_LIST_SHELF_ITEM_DELEGATE_H_ -#include "ash/launcher/launcher_item_delegate.h" +#include "ash/shelf/shelf_item_delegate.h" #include "base/basictypes.h" #include "base/compiler_specific.h" namespace ash { namespace internal { -// LauncherItemDelegate for TYPE_APP_LIST. -class AppListShelfItemDelegate : public LauncherItemDelegate { +// ShelfItemDelegate for TYPE_APP_LIST. +class AppListShelfItemDelegate : public ShelfItemDelegate { public: AppListShelfItemDelegate(); virtual ~AppListShelfItemDelegate(); - // ash::LauncherItemDelegate overrides: + // ShelfItemDelegate: virtual bool ItemSelected(const ui::Event& event) OVERRIDE; virtual base::string16 GetTitle() OVERRIDE; - virtual ui::MenuModel* CreateContextMenu( - aura::Window* root_window) OVERRIDE; - virtual LauncherMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE; + virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE; + virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE; virtual bool IsDraggable() OVERRIDE; virtual bool ShouldShowTooltip() OVERRIDE; diff --git a/ash/shelf/background_animator.cc b/ash/shelf/background_animator.cc index 35e5fba3a7..8137d60289 100644 --- a/ash/shelf/background_animator.cc +++ b/ash/shelf/background_animator.cc @@ -34,11 +34,12 @@ void BackgroundAnimator::SetDuration(int time_in_ms) { animation_.SetSlideDuration(time_in_ms); } -void BackgroundAnimator::SetPaintsBackground(bool value, ChangeType type) { +void BackgroundAnimator::SetPaintsBackground( + bool value, BackgroundAnimatorChangeType type) { if (paints_background_ == value) return; paints_background_ = value; - if (type == CHANGE_IMMEDIATE && !animation_.is_animating()) { + if (type == BACKGROUND_CHANGE_IMMEDIATE && !animation_.is_animating()) { animation_.Reset(value ? 1.0f : 0.0f); AnimationProgressed(&animation_); return; diff --git a/ash/shelf/background_animator.h b/ash/shelf/background_animator.h index 7436b30f1f..805f7d5e5d 100644 --- a/ash/shelf/background_animator.h +++ b/ash/shelf/background_animator.h @@ -11,6 +11,13 @@ #include "ui/gfx/animation/slide_animation.h" namespace ash { + +// How the background can be changed. +enum BackgroundAnimatorChangeType { + BACKGROUND_CHANGE_ANIMATE, + BACKGROUND_CHANGE_IMMEDIATE +}; + namespace internal { // Delegate is notified any time the background changes. @@ -25,12 +32,6 @@ class ASH_EXPORT BackgroundAnimatorDelegate { // BackgroundAnimator is used by the shelf to animate the background (alpha). class ASH_EXPORT BackgroundAnimator : public gfx::AnimationDelegate { public: - // How the background can be changed. - enum ChangeType { - CHANGE_ANIMATE, - CHANGE_IMMEDIATE - }; - BackgroundAnimator(BackgroundAnimatorDelegate* delegate, int min_alpha, int max_alpha); @@ -42,7 +43,7 @@ class ASH_EXPORT BackgroundAnimator : public gfx::AnimationDelegate { // Sets whether a background is rendered. Initial value is false. If |type| // is |CHANGE_IMMEDIATE| and an animation is not in progress this notifies // the delegate immediately (synchronously from this method). - void SetPaintsBackground(bool value, ChangeType type); + void SetPaintsBackground(bool value, BackgroundAnimatorChangeType type); bool paints_background() const { return paints_background_; } // Current alpha. diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc index 4b10b8faf2..93a44e1a1a 100644 --- a/ash/shelf/shelf_button.cc +++ b/ash/shelf/shelf_button.cc @@ -6,6 +6,7 @@ #include <algorithm> +#include "ash/ash_constants.h" #include "ash/ash_switches.h" #include "ash/shelf/shelf_button_host.h" #include "ash/shelf/shelf_layout_manager.h" @@ -470,6 +471,15 @@ void ShelfButton::OnBlur() { CustomButton::OnBlur(); } +void ShelfButton::OnPaint(gfx::Canvas* canvas) { + CustomButton::OnPaint(canvas); + if (HasFocus()) { + gfx::Rect paint_bounds(GetLocalBounds()); + paint_bounds.Inset(1, 1, 1, 1); + canvas->DrawSolidFocusRect(paint_bounds, kFocusBorderColor); + } +} + void ShelfButton::OnGestureEvent(ui::GestureEvent* event) { switch (event->type()) { case ui::ET_GESTURE_TAP_DOWN: diff --git a/ash/shelf/shelf_button.h b/ash/shelf/shelf_button.h index 1bfc510d90..60158f2ea9 100644 --- a/ash/shelf/shelf_button.h +++ b/ash/shelf/shelf_button.h @@ -107,6 +107,7 @@ class ASH_EXPORT ShelfButton : public views::CustomButton { virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; // ui::EventHandler overrides: virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; diff --git a/ash/launcher/launcher_delegate.h b/ash/shelf/shelf_delegate.h index 3868becd8f..e168c47027 100644 --- a/ash/launcher/launcher_delegate.h +++ b/ash/shelf/shelf_delegate.h @@ -1,25 +1,21 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ASH_LAUNCHER_LAUNCHER_DELEGATE_H_ -#define ASH_LAUNCHER_LAUNCHER_DELEGATE_H_ +#ifndef ASH_SHELF_SHELF_DELEGATE_H_ +#define ASH_SHELF_SHELF_DELEGATE_H_ #include "ash/ash_export.h" #include "ash/launcher/launcher_types.h" -namespace aura { -class Window; -} - namespace ash { class Launcher; // Delegate for the Launcher. -class ASH_EXPORT LauncherDelegate { +class ASH_EXPORT ShelfDelegate { public: // Launcher owns the delegate. - virtual ~LauncherDelegate() {} + virtual ~ShelfDelegate() {} // Callback used to allow delegate to perform initialization actions that // depend on the Launcher being in a known state. @@ -53,4 +49,4 @@ class ASH_EXPORT LauncherDelegate { } // namespace ash -#endif // ASH_LAUNCHER_LAUNCHER_DELEGATE_H_ +#endif // ASH_SHELF_SHELF_DELEGATE_H_ diff --git a/ash/launcher/launcher_item_delegate.h b/ash/shelf/shelf_item_delegate.h index c83de47b81..72815cf80f 100644 --- a/ash/launcher/launcher_item_delegate.h +++ b/ash/shelf/shelf_item_delegate.h @@ -2,42 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_ -#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_ +#ifndef ASH_SHELF_SHELF_ITEM_DELEGATE_H_ +#define ASH_SHELF_SHELF_ITEM_DELEGATE_H_ #include "ash/ash_export.h" -#include "ash/launcher/launcher_types.h" #include "base/strings/string16.h" -#include "ui/base/models/simple_menu_model.h" namespace aura { -class RootWindow; +class Window; } namespace ui { class Event; +class MenuModel; } namespace ash { -// A special menu model which keeps track of an "active" menu item. -class ASH_EXPORT LauncherMenuModel : public ui::SimpleMenuModel { - public: - explicit LauncherMenuModel(ui::SimpleMenuModel::Delegate* delegate) - : ui::SimpleMenuModel(delegate) {} - - // Returns |true| when the given |command_id| is active and needs to be drawn - // in a special state. - virtual bool IsCommandActive(int command_id) const = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(LauncherMenuModel); -}; +class ShelfMenuModel; // Delegate for the LauncherItem. -class ASH_EXPORT LauncherItemDelegate { +class ASH_EXPORT ShelfItemDelegate { public: - virtual ~LauncherItemDelegate() {} + virtual ~ShelfItemDelegate() {} // Invoked when the user clicks on a window entry in the launcher. // |event| is the click event. The |event| is dispatched by a view @@ -65,7 +52,7 @@ class ASH_EXPORT LauncherItemDelegate { // - A list containing the title and the active list of items. // The caller takes ownership of the returned model. // |event_flags| specifies the flags of the event which triggered this menu. - virtual ash::LauncherMenuModel* CreateApplicationMenu(int event_flags) = 0; + virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) = 0; // Whether the launcher item is draggable. virtual bool IsDraggable() = 0; @@ -76,4 +63,4 @@ class ASH_EXPORT LauncherItemDelegate { } // namespace ash -#endif // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_ +#endif // ASH_SHELF_SHELF_ITEM_DELEGATE_H_ diff --git a/ash/shelf/shelf_item_delegate_manager.cc b/ash/shelf/shelf_item_delegate_manager.cc new file mode 100644 index 0000000000..79124caac1 --- /dev/null +++ b/ash/shelf/shelf_item_delegate_manager.cc @@ -0,0 +1,72 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/shelf/shelf_item_delegate_manager.h" + +#include "ash/shelf/shelf_item_delegate.h" +#include "ash/shelf/shelf_model.h" +#include "ash/shell.h" +#include "base/logging.h" +#include "base/stl_util.h" + +namespace ash { + +ShelfItemDelegateManager::ShelfItemDelegateManager(ShelfModel* model) + : model_(model) { + DCHECK(model_); + model_->AddObserver(this); +} + +ShelfItemDelegateManager::~ShelfItemDelegateManager() { + model_->RemoveObserver(this); + STLDeleteContainerPairSecondPointers(id_to_item_delegate_map_.begin(), + id_to_item_delegate_map_.end()); +} + +void ShelfItemDelegateManager::SetShelfItemDelegate( + LauncherID id, + scoped_ptr<ShelfItemDelegate> item_delegate) { + // If another ShelfItemDelegate is already registered for |id|, we assume + // that this request is replacing ShelfItemDelegate for |id| with + // |item_delegate|. + RemoveShelfItemDelegate(id); + id_to_item_delegate_map_[id] = item_delegate.release(); +} + +ShelfItemDelegate* ShelfItemDelegateManager::GetShelfItemDelegate( + LauncherID id) { + if (model_->ItemIndexByID(id) != -1) { + // Each LauncherItem has to have a ShelfItemDelegate. + DCHECK(id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()); + return id_to_item_delegate_map_[id]; + } + return NULL; +} + +void ShelfItemDelegateManager::ShelfItemAdded(int index) { +} + +void ShelfItemDelegateManager::ShelfItemRemoved(int index, LauncherID id) { + RemoveShelfItemDelegate(id); +} + +void ShelfItemDelegateManager::ShelfItemMoved(int start_index, + int target_index) { +} + +void ShelfItemDelegateManager::ShelfItemChanged(int index, + const LauncherItem& old_item) { +} + +void ShelfItemDelegateManager::ShelfStatusChanged() { +} + +void ShelfItemDelegateManager::RemoveShelfItemDelegate(LauncherID id) { + if (id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()) { + delete id_to_item_delegate_map_[id]; + id_to_item_delegate_map_.erase(id); + } +} + +} // namespace ash diff --git a/ash/shelf/shelf_item_delegate_manager.h b/ash/shelf/shelf_item_delegate_manager.h new file mode 100644 index 0000000000..dc4df7339b --- /dev/null +++ b/ash/shelf/shelf_item_delegate_manager.h @@ -0,0 +1,67 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_ +#define ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_ + +#include <map> + +#include "ash/ash_export.h" +#include "ash/launcher/launcher_types.h" +#include "ash/shelf/shelf_model_observer.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" + +namespace ash { +class ShelfItemDelegate; +class ShelfModel; + +namespace test { +class ShelfItemDelegateManagerTestAPI; +} + +// ShelfItemDelegateManager manages the set of ShelfItemDelegates for the +// launcher. ShelfItemDelegateManager does not create ShelfItemDelegates, +// rather it is expected that someone else invokes SetShelfItemDelegate +// appropriately. On the other hand, ShelfItemDelegateManager destroys +// ShelfItemDelegates when the corresponding item from the model is removed. +class ASH_EXPORT ShelfItemDelegateManager : public ShelfModelObserver { + public: + explicit ShelfItemDelegateManager(ShelfModel* model); + virtual ~ShelfItemDelegateManager(); + + // Set |item_delegate| for |id| and take an ownership. + void SetShelfItemDelegate(LauncherID id, + scoped_ptr<ShelfItemDelegate> item_delegate); + + // Returns ShelfItemDelegate for |item_type|. Always returns non-NULL. + ShelfItemDelegate* GetShelfItemDelegate(LauncherID id); + + // ShelfModelObserver overrides: + virtual void ShelfItemAdded(int model_index) OVERRIDE; + virtual void ShelfItemRemoved(int index, LauncherID id) OVERRIDE; + virtual void ShelfItemMoved(int start_index, int targetindex) OVERRIDE; + virtual void ShelfItemChanged(int index, + const LauncherItem& old_item) OVERRIDE; + virtual void ShelfStatusChanged() OVERRIDE; + + private: + friend class test::ShelfItemDelegateManagerTestAPI; + + typedef std::map<LauncherID, ShelfItemDelegate*> LauncherIDToItemDelegateMap; + + // Remove and destroy ShelfItemDelegate for |id|. + void RemoveShelfItemDelegate(LauncherID id); + + // Owned by Shell. + ShelfModel* model_; + + LauncherIDToItemDelegateMap id_to_item_delegate_map_; + + DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegateManager); +}; + +} // namespace ash + +#endif // ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_ diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc index 566db942d2..116e014013 100644 --- a/ash/shelf/shelf_layout_manager.cc +++ b/ash/shelf/shelf_layout_manager.cc @@ -175,9 +175,8 @@ class ShelfLayoutManager::UpdateShelfObserver } virtual void OnImplicitAnimationsCompleted() OVERRIDE { - if (shelf_) { - shelf_->UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE); - } + if (shelf_) + shelf_->UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); delete this; } @@ -319,8 +318,11 @@ void ShelfLayoutManager::UpdateVisibilityState() { // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN. WorkspaceWindowState window_state(workspace_controller_->GetWindowState()); switch (window_state) { - case WORKSPACE_WINDOW_STATE_FULL_SCREEN: - if (FullscreenWithHiddenShelf()) { + case WORKSPACE_WINDOW_STATE_FULL_SCREEN: { + const aura::Window* fullscreen_window = GetRootWindowController( + root_window_)->GetWindowForFullscreenMode(); + if (fullscreen_window && wm::GetWindowState(fullscreen_window)-> + hide_shelf_when_fullscreen()) { SetState(SHELF_HIDDEN); } else { // The shelf is sometimes not hidden when in immersive fullscreen. @@ -328,9 +330,12 @@ void ShelfLayoutManager::UpdateVisibilityState() { SetState(SHELF_AUTO_HIDE); } break; + } + case WORKSPACE_WINDOW_STATE_MAXIMIZED: SetState(CalculateShelfVisibility()); break; + case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF: case WORKSPACE_WINDOW_STATE_DEFAULT: SetState(CalculateShelfVisibility()); @@ -366,7 +371,7 @@ void ShelfLayoutManager::UpdateAutoHideState() { void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) { window_overlaps_shelf_ = value; - UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE); + UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); } void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) { @@ -385,7 +390,7 @@ void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) { gesture_drag_amount_ = 0.f; gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ? auto_hide_state() : SHELF_AUTO_HIDE_SHOWN; - UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE); + UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); } ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag( @@ -538,16 +543,6 @@ bool ShelfLayoutManager::IsHorizontalAlignment() const { GetAlignment() == SHELF_ALIGNMENT_TOP; } -bool ShelfLayoutManager::FullscreenWithHiddenShelf() const { - RootWindowController* controller = GetRootWindowController(root_window_); - if (!controller) - return false; - const aura::Window* window = controller->GetTopmostFullscreenWindow(); - if (!window) - return false; - return wm::GetWindowState(window)->hide_shelf_when_fullscreen(); -} - // static ShelfLayoutManager* ShelfLayoutManager::ForLauncher(aura::Window* window) { ShelfWidget* shelf = RootWindowController::ForLauncher(window)->shelf(); @@ -598,8 +593,7 @@ void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) { State old_state = state_; state_ = state; - BackgroundAnimator::ChangeType change_type = - BackgroundAnimator::CHANGE_ANIMATE; + BackgroundAnimatorChangeType change_type = BACKGROUND_CHANGE_ANIMATE; bool delay_background_change = false; // Do not animate the background when: @@ -610,7 +604,7 @@ void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) { if (state.visibility_state == SHELF_VISIBLE && state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED && old_state.visibility_state != SHELF_VISIBLE) { - change_type = BackgroundAnimator::CHANGE_IMMEDIATE; + change_type = BACKGROUND_CHANGE_IMMEDIATE; } else { // Delay the animation when the shelf was hidden, and has just been made // visible (e.g. using a gesture-drag). @@ -930,8 +924,11 @@ void ShelfLayoutManager::UpdateTargetBoundsForGesture( } void ShelfLayoutManager::UpdateShelfBackground( - BackgroundAnimator::ChangeType type) { - shelf_->SetPaintsBackground(GetShelfBackgroundType(), type); + BackgroundAnimatorChangeType type) { + const ShelfBackgroundType background_type(GetShelfBackgroundType()); + shelf_->SetPaintsBackground(background_type, type); + FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, + OnBackgroundUpdated(background_type, type)); } ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const { @@ -1134,6 +1131,7 @@ void ShelfLayoutManager::OnDockBoundsChanging( if (dock_bounds_ != dock_bounds) { dock_bounds_ = dock_bounds; OnWindowResized(); + UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE); } } diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h index 13f90b6ccc..5d0496b8f9 100644 --- a/ash/shelf/shelf_layout_manager.h +++ b/ash/shelf/shelf_layout_manager.h @@ -122,6 +122,9 @@ class ASH_EXPORT ShelfLayoutManager : // Returns the ideal bounds of the shelf assuming it is visible. gfx::Rect GetIdealBounds(); + // Returns the docked area bounds. + const gfx::Rect& dock_bounds() const { return dock_bounds_; } + // Stops any animations and sets the bounds of the launcher and status // widgets. void LayoutShelf(); @@ -212,10 +215,6 @@ class ASH_EXPORT ShelfLayoutManager : // Is the shelf's alignment horizontal? bool IsHorizontalAlignment() const; - // Returns true if there is a fullscreen window and the shelf needs to be - // hidden for the topmost fullscreen window. - bool FullscreenWithHiddenShelf() const; - // Returns a ShelfLayoutManager on the display which has a launcher for // given |window|. See RootWindowController::ForLauncher for more info. static ShelfLayoutManager* ForLauncher(aura::Window* window); @@ -292,7 +291,7 @@ class ASH_EXPORT ShelfLayoutManager : void UpdateTargetBoundsForGesture(TargetBounds* target_bounds) const; // Updates the background of the shelf. - void UpdateShelfBackground(BackgroundAnimator::ChangeType type); + void UpdateShelfBackground(BackgroundAnimatorChangeType type); // Returns how the shelf background is painted. ShelfBackgroundType GetShelfBackgroundType() const; @@ -331,7 +330,7 @@ class ASH_EXPORT ShelfLayoutManager : virtual void OnKeyboardBoundsChanging( const gfx::Rect& keyboard_bounds) OVERRIDE; - // Overridden from dock::DockObserver: + // Overridden from DockedWindowLayoutManagerObserver: virtual void OnDockBoundsChanging( const gfx::Rect& dock_bounds, DockedWindowLayoutManagerObserver::Reason reason) OVERRIDE; diff --git a/ash/shelf/shelf_layout_manager_observer.h b/ash/shelf/shelf_layout_manager_observer.h index 6209980e15..8a2d5830df 100644 --- a/ash/shelf/shelf_layout_manager_observer.h +++ b/ash/shelf/shelf_layout_manager_observer.h @@ -6,6 +6,7 @@ #define ASH_SHELF_SHELF_LAYOUT_MANAGER_OBSERVER_H_ #include "ash/ash_export.h" +#include "ash/shelf/background_animator.h" #include "ash/shelf/shelf_types.h" namespace aura { @@ -30,6 +31,11 @@ class ASH_EXPORT ShelfLayoutManagerObserver { // Called when the auto hide behavior is changed. virtual void OnAutoHideBehaviorChanged(aura::Window* root_window, ShelfAutoHideBehavior new_behavior) {} + + // Called when shelf background animation is started. + virtual void OnBackgroundUpdated( + ShelfBackgroundType background_type, + BackgroundAnimatorChangeType change_type) {} }; } // namespace ash diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc index 1009ce98d7..8dac93d1d2 100644 --- a/ash/shelf/shelf_layout_manager_unittest.cc +++ b/ash/shelf/shelf_layout_manager_unittest.cc @@ -1326,6 +1326,68 @@ TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfHiddenState) { EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); } +// Tests that the shelf is only hidden for a fullscreen window at the front and +// toggles visibility when another window is activated. +TEST_F(ShelfLayoutManagerTest, FullscreenWindowInFrontHidesShelf) { + ShelfLayoutManager* shelf = GetShelfLayoutManager(); + + // Create a window and make it full screen. + aura::Window* window1 = CreateTestWindow(); + window1->SetBounds(gfx::Rect(0, 0, 100, 100)); + window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); + window1->Show(); + + aura::Window* window2 = CreateTestWindow(); + window2->SetBounds(gfx::Rect(0, 0, 100, 100)); + window2->Show(); + + wm::GetWindowState(window1)->Activate(); + EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); + + wm::GetWindowState(window2)->Activate(); + EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state()); + + wm::GetWindowState(window1)->Activate(); + EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); +} + +// Test the behavior of the shelf when a window on one display is fullscreen +// but the other display has the active window. +TEST_F(ShelfLayoutManagerTest, FullscreenWindowOnSecondDisplay) { + if (!SupportsMultipleDisplays()) + return; + + UpdateDisplay("800x600,800x600"); + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); + aura::Window::Windows root_windows = Shell::GetAllRootWindows(); + Shell::RootWindowControllerList root_window_controllers = + Shell::GetAllRootWindowControllers(); + + // Create windows on either display. + aura::Window* window1 = CreateTestWindow(); + window1->SetBoundsInScreen( + gfx::Rect(0, 0, 100, 100), + display_manager->GetDisplayAt(0)); + window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); + window1->Show(); + + aura::Window* window2 = CreateTestWindow(); + window2->SetBoundsInScreen( + gfx::Rect(800, 0, 100, 100), + display_manager->GetDisplayAt(1)); + window2->Show(); + + EXPECT_EQ(root_windows[0], window1->GetRootWindow()); + EXPECT_EQ(root_windows[1], window2->GetRootWindow()); + + wm::GetWindowState(window2)->Activate(); + EXPECT_EQ(SHELF_HIDDEN, + root_window_controllers[0]->GetShelfLayoutManager()->visibility_state()); + EXPECT_EQ(SHELF_VISIBLE, + root_window_controllers[1]->GetShelfLayoutManager()->visibility_state()); +} + + #if defined(OS_WIN) // RootWindow and Display can't resize on Windows Ash. http://crbug.com/165962 #define MAYBE_SetAlignment DISABLED_SetAlignment diff --git a/ash/shelf/shelf_menu_model.h b/ash/shelf/shelf_menu_model.h new file mode 100644 index 0000000000..18e1dd033e --- /dev/null +++ b/ash/shelf/shelf_menu_model.h @@ -0,0 +1,29 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SHELF_SHELF_MENU_MODEL_H_ +#define ASH_SHELF_SHELF_MENU_MODEL_H_ + +#include "ash/ash_export.h" +#include "ui/base/models/simple_menu_model.h" + +namespace ash { + +// A special menu model which keeps track of an "active" menu item. +class ASH_EXPORT ShelfMenuModel : public ui::SimpleMenuModel { + public: + explicit ShelfMenuModel(ui::SimpleMenuModel::Delegate* delegate) + : ui::SimpleMenuModel(delegate) {} + + // Returns |true| when the given |command_id| is active and needs to be drawn + // in a special state. + virtual bool IsCommandActive(int command_id) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ShelfMenuModel); +}; + +} // namespace ash + +#endif // ASH_SHELF_SHELF_MENU_MODEL_H_ diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc index 8e5b780e23..dd20a178a2 100644 --- a/ash/shelf/shelf_tooltip_manager.cc +++ b/ash/shelf/shelf_tooltip_manager.cc @@ -29,7 +29,7 @@ namespace internal { namespace { const int kTooltipTopBottomMargin = 3; const int kTooltipLeftRightMargin = 10; -const int kTooltipAppearanceDelay = 200; // msec +const int kTooltipAppearanceDelay = 1000; // msec const int kTooltipMinHeight = 29 - 2 * kTooltipTopBottomMargin; const SkColor kTooltipTextColor = SkColorSetRGB(0x22, 0x22, 0x22); diff --git a/ash/shelf/shelf_util.cc b/ash/shelf/shelf_util.cc index 2e86814f01..77031d803a 100644 --- a/ash/shelf/shelf_util.cc +++ b/ash/shelf/shelf_util.cc @@ -4,14 +4,20 @@ #include "ash/shelf/shelf_util.h" -#include "ui/aura/window.h" #include "ui/aura/window_property.h" DECLARE_WINDOW_PROPERTY_TYPE(ash::LauncherID); +DECLARE_WINDOW_PROPERTY_TYPE(ash::LauncherItemDetails*); namespace ash { -DEFINE_LOCAL_WINDOW_PROPERTY_KEY(LauncherID, kLauncherID, 0); +DEFINE_LOCAL_WINDOW_PROPERTY_KEY(LauncherID, kLauncherID, kInvalidLauncherID); + +// ash::LauncherItemDetails for kLauncherItemDetaildKey is owned by the window +// and will be freed automatically. +DEFINE_OWNED_WINDOW_PROPERTY_KEY(LauncherItemDetails, + kLauncherItemDetailsKey, + NULL); void SetLauncherIDForWindow(LauncherID id, aura::Window* window) { if (!window) @@ -25,4 +31,20 @@ LauncherID GetLauncherIDForWindow(aura::Window* window) { return window->GetProperty(kLauncherID); } +void SetLauncherItemDetailsForWindow(aura::Window* window, + const LauncherItemDetails& details) { + // |item_details| is owned by |window|. + LauncherItemDetails* item_details = new LauncherItemDetails(details); + window->SetProperty(kLauncherItemDetailsKey, item_details); +} + +void ClearLauncherItemDetailsForWindow(aura::Window* window) { + window->ClearProperty(kLauncherItemDetailsKey); +} + +const LauncherItemDetails* GetLauncherItemDetailsForWindow( + aura::Window* window) { + return window->GetProperty(kLauncherItemDetailsKey); +} + } // namespace ash diff --git a/ash/shelf/shelf_util.h b/ash/shelf/shelf_util.h index 30953a76ce..6fb3d5efc1 100644 --- a/ash/shelf/shelf_util.h +++ b/ash/shelf/shelf_util.h @@ -7,6 +7,7 @@ #include "ash/ash_export.h" #include "ash/launcher/launcher_types.h" +#include "ui/aura/window.h" namespace aura { class Window; @@ -14,6 +15,9 @@ class Window; namespace ash { +extern const aura::WindowProperty<ash::LauncherItemDetails*>* const + kLauncherItemDetailsKey; + // Associates LauncherItem of |id| with specified |window|. ASH_EXPORT void SetLauncherIDForWindow(LauncherID id, aura::Window* window); @@ -23,6 +27,21 @@ ASH_EXPORT void SetLauncherIDForWindow(LauncherID id, aura::Window* window); // currently active tab. ASH_EXPORT LauncherID GetLauncherIDForWindow(aura::Window* window); +// Sets LauncherItemDetails for |window|. +ASH_EXPORT void SetLauncherItemDetailsForWindow( + aura::Window* window, + const LauncherItemDetails& details); + +// Clears LauncherItemDetails for |window|. +// If |window| has a LauncherItem by SetLauncherItemDetailsForWindow(), it will +// be removed. +ASH_EXPORT void ClearLauncherItemDetailsForWindow(aura::Window* window); + +// Returns LauncherItemDetails for |window| or NULL if it doesn't have. +// Returned LauncherItemDetails object is owned by the |window|. +ASH_EXPORT const LauncherItemDetails* GetLauncherItemDetailsForWindow( + aura::Window* window); + } // namespace ash #endif // ASH_SHELF_SHELF_UTIL_H_ diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index 9393368234..feab088a99 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc @@ -9,9 +9,6 @@ #include "ash/ash_constants.h" #include "ash/ash_switches.h" #include "ash/drag_drop/drag_image_view.h" -#include "ash/launcher/launcher_delegate.h" -#include "ash/launcher/launcher_item_delegate.h" -#include "ash/launcher/launcher_item_delegate_manager.h" #include "ash/root_window_controller.h" #include "ash/scoped_target_root_window.h" #include "ash/shelf/alternate_app_list_button.h" @@ -20,8 +17,12 @@ #include "ash/shelf/overflow_bubble_view.h" #include "ash/shelf/overflow_button.h" #include "ash/shelf/shelf_button.h" +#include "ash/shelf/shelf_delegate.h" #include "ash/shelf/shelf_icon_observer.h" +#include "ash/shelf/shelf_item_delegate.h" +#include "ash/shelf/shelf_item_delegate_manager.h" #include "ash/shelf/shelf_layout_manager.h" +#include "ash/shelf/shelf_menu_model.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_tooltip_manager.h" #include "ash/shelf/shelf_widget.h" @@ -50,7 +51,6 @@ #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/focus/focus_search.h" -#include "ui/views/focus_border.h" #include "ui/views/view_model.h" #include "ui/views/view_model_utils.h" #include "ui/views/widget/widget.h" @@ -114,16 +114,18 @@ const int kRipOffDistance = 48; // The rip off drag and drop proxy image should get scaled by this factor. const float kDragAndDropProxyScale = 1.5f; +// The opacity represents that this partially disappeared item will get removed. +const float kDraggedImageOpacity = 0.5f; + namespace { // The MenuModelAdapter gets slightly changed to adapt the menu appearance to // our requirements. -class LauncherMenuModelAdapter - : public views::MenuModelAdapter { +class ShelfMenuModelAdapter : public views::MenuModelAdapter { public: - explicit LauncherMenuModelAdapter(ash::LauncherMenuModel* menu_model); + explicit ShelfMenuModelAdapter(ShelfMenuModel* menu_model); - // Overriding MenuModelAdapter's MenuDelegate implementation. + // views::MenuModelAdapter: virtual const gfx::Font* GetLabelFont(int command_id) const OVERRIDE; virtual bool IsCommandEnabled(int id) const OVERRIDE; virtual void GetHorizontalIconMargins(int id, @@ -140,18 +142,17 @@ class LauncherMenuModelAdapter virtual bool ShouldReserveSpaceForSubmenuIndicator() const OVERRIDE; private: - ash::LauncherMenuModel* launcher_menu_model_; + ShelfMenuModel* menu_model_; - DISALLOW_COPY_AND_ASSIGN(LauncherMenuModelAdapter); + DISALLOW_COPY_AND_ASSIGN(ShelfMenuModelAdapter); }; -LauncherMenuModelAdapter::LauncherMenuModelAdapter( - ash::LauncherMenuModel* menu_model) +ShelfMenuModelAdapter::ShelfMenuModelAdapter(ShelfMenuModel* menu_model) : MenuModelAdapter(menu_model), - launcher_menu_model_(menu_model) {} + menu_model_(menu_model) { +} -const gfx::Font* LauncherMenuModelAdapter::GetLabelFont( - int command_id) const { +const gfx::Font* ShelfMenuModelAdapter::GetLabelFont(int command_id) const { if (command_id != kCommandIdOfMenuName) return MenuModelAdapter::GetLabelFont(command_id); @@ -159,14 +160,13 @@ const gfx::Font* LauncherMenuModelAdapter::GetLabelFont( return &rb.GetFont(ui::ResourceBundle::BoldFont); } -bool LauncherMenuModelAdapter::IsCommandEnabled(int id) const { +bool ShelfMenuModelAdapter::IsCommandEnabled(int id) const { return id != kCommandIdOfMenuName; } -bool LauncherMenuModelAdapter::GetForegroundColor( - int command_id, - bool is_hovered, - SkColor* override_color) const { +bool ShelfMenuModelAdapter::GetForegroundColor(int command_id, + bool is_hovered, + SkColor* override_color) const { if (command_id != kCommandIdOfMenuName) return false; @@ -174,11 +174,10 @@ bool LauncherMenuModelAdapter::GetForegroundColor( return true; } -bool LauncherMenuModelAdapter::GetBackgroundColor( - int command_id, - bool is_hovered, - SkColor* override_color) const { - if (!launcher_menu_model_->IsCommandActive(command_id)) +bool ShelfMenuModelAdapter::GetBackgroundColor(int command_id, + bool is_hovered, + SkColor* override_color) const { + if (!menu_model_->IsCommandActive(command_id)) return false; *override_color = is_hovered ? kFocusedActiveListItemBackgroundColor : @@ -186,21 +185,20 @@ bool LauncherMenuModelAdapter::GetBackgroundColor( return true; } -void LauncherMenuModelAdapter::GetHorizontalIconMargins( - int command_id, - int icon_size, - int* left_margin, - int* right_margin) const { +void ShelfMenuModelAdapter::GetHorizontalIconMargins(int command_id, + int icon_size, + int* left_margin, + int* right_margin) const { *left_margin = kHorizontalIconSpacing; *right_margin = (command_id != kCommandIdOfMenuName) ? kHorizontalIconSpacing : -(icon_size + kHorizontalNoIconInsetSpacing); } -int LauncherMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) { +int ShelfMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) { return kMaximumAppMenuItemLength; } -bool LauncherMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const { +bool ShelfMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const { return false; } @@ -243,22 +241,6 @@ class LauncherFocusSearch : public views::FocusSearch { DISALLOW_COPY_AND_ASSIGN(LauncherFocusSearch); }; -class ShelfButtonFocusBorder : public views::FocusBorder { - public: - ShelfButtonFocusBorder() {} - virtual ~ShelfButtonFocusBorder() {} - - private: - // views::FocusBorder overrides: - virtual void Paint(const View& view, gfx::Canvas* canvas) const OVERRIDE { - gfx::Rect rect(view.GetLocalBounds()); - rect.Inset(1, 1); - canvas->DrawRect(rect, kFocusBorderColor); - } - - DISALLOW_COPY_AND_ASSIGN(ShelfButtonFocusBorder); -}; - // AnimationDelegate used when inserting a new item. This steadily increases the // opacity of the layer as the animation progress. class FadeInAnimationDelegate @@ -370,8 +352,8 @@ class ShelfView::StartFadeAnimationDelegate }; ShelfView::ShelfView(ShelfModel* model, - LauncherDelegate* delegate, - ShelfLayoutManager* shelf_layout_manager) + ShelfDelegate* delegate, + ShelfLayoutManager* manager) : model_(model), delegate_(delegate), view_model_(new views::ViewModel), @@ -393,15 +375,17 @@ ShelfView::ShelfView(ShelfModel* model, drag_and_drop_launcher_id_(0), dragged_off_shelf_(false), snap_back_from_rip_off_view_(NULL), - item_manager_(Shell::GetInstance()->launcher_item_delegate_manager()), - layout_manager_(shelf_layout_manager), - overflow_mode_(false) { + item_manager_(Shell::GetInstance()->shelf_item_delegate_manager()), + layout_manager_(manager), + overflow_mode_(false), + main_shelf_(NULL), + dragged_off_from_overflow_to_shelf_(false) { DCHECK(model_); bounds_animator_.reset(new views::BoundsAnimator(this)); bounds_animator_->AddObserver(this); set_context_menu_controller(this); focus_search_.reset(new LauncherFocusSearch(view_model_.get())); - tooltip_.reset(new ShelfTooltipManager(shelf_layout_manager, this)); + tooltip_.reset(new ShelfTooltipManager(manager, this)); } ShelfView::~ShelfView() { @@ -602,7 +586,11 @@ bool ShelfView::StartDrag(const std::string& app_id, // Check if the application is known and pinned - if not, we have to pin it so // that we can re-arrange the launcher order accordingly. Note that items have // to be pinned to give them the same (order) possibilities as a shortcut. - if (!drag_and_drop_launcher_id_ || !delegate_->IsAppPinned(app_id)) { + // When an item is dragged from overflow to shelf, IsShowingOverflowBubble() + // returns true. At this time, we don't need to pin the item. + if (!IsShowingOverflowBubble() && + (!drag_and_drop_launcher_id_ || + !delegate_->IsAppPinned(app_id))) { delegate_->PinAppWithID(app_id); drag_and_drop_launcher_id_ = delegate_->GetLauncherIDForAppID(drag_and_drop_app_id_); @@ -695,6 +683,27 @@ void ShelfView::LayoutToIdealBounds() { overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds); } +void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() { + // The overflow button is not shown in overflow mode. + overflow_button_->SetVisible(false); + int last_button_index = model_->FirstPanelIndex() - 1; + DCHECK_LT(last_visible_index_, view_model_->view_size()); + for (int i = 0; i < view_model_->view_size(); ++i) { + bool visible = i >= first_visible_index_ && + i <= last_visible_index_; + if (!ash::switches::UseAlternateShelfLayout()) + visible &= i != last_button_index; + + // To track the dragging of |drag_view_| continuously, its visibility + // should be always true regardless of its position. + if (dragged_off_from_overflow_to_shelf_ && + view_model_->view_at(i) == drag_view_) + view_model_->view_at(i)->SetVisible(true); + else + view_model_->view_at(i)->SetVisible(visible); + } +} + void ShelfView::CalculateIdealBounds(IdealBounds* bounds) { int available_size = layout_manager_->PrimaryAxisValue(width(), height()); DCHECK(model_->item_count() == view_model_->view_size()); @@ -730,16 +739,7 @@ void ShelfView::CalculateIdealBounds(IdealBounds* bounds) { } if (is_overflow_mode()) { - // The overflow button is not shown in overflow mode. - overflow_button_->SetVisible(false); - DCHECK_LT(last_visible_index_, view_model_->view_size()); - for (int i = 0; i < view_model_->view_size(); ++i) { - bool visible = i >= first_visible_index_ && - i <= last_visible_index_; - if (!ash::switches::UseAlternateShelfLayout()) - visible &= i != last_button_index; - view_model_->view_at(i)->SetVisible(visible); - } + UpdateAllButtonsVisibilityInOverflowMode(); return; } @@ -952,7 +952,6 @@ views::View* ShelfView::CreateViewForItem(const LauncherItem& item) { break; } view->set_context_menu_controller(this); - view->set_focus_border(new ShelfButtonFocusBorder); DCHECK(view); ConfigureChildView(view); @@ -979,7 +978,7 @@ void ShelfView::PrepareForDrag(Pointer pointer, const ui::LocatedEvent& event) { } // If the item is no longer draggable, bail out. - LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate( + ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate( model_->items()[start_drag_index_].id); if (!item_delegate->IsDraggable()) { CancelDrag(-1); @@ -997,7 +996,7 @@ void ShelfView::ContinueDrag(const ui::LocatedEvent& event) { int current_index = view_model_->GetIndexOfView(drag_view_); DCHECK_NE(-1, current_index); - LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate( + ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate( model_->items()[current_index].id); if (!item_delegate->IsDraggable()) { CancelDrag(-1); @@ -1069,6 +1068,8 @@ void ShelfView::ContinueDrag(const ui::LocatedEvent& event) { bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) { int current_index = view_model_->GetIndexOfView(drag_view_); DCHECK_NE(-1, current_index); + std::string dragged_app_id = + delegate_->GetAppIDForLauncherID(model_->items()[current_index].id); gfx::Point screen_location = event.root_location(); ash::wm::ConvertPointToScreen(GetWidget()->GetNativeWindow()->GetRootWindow(), @@ -1080,17 +1081,47 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) { // If the shelf/overflow bubble bounds contains |screen_location| we insert // the item back into the shelf. if (GetBoundsForDragInsertInScreen().Contains(screen_location)) { + if (dragged_off_from_overflow_to_shelf_) { + // During the dragging an item from Shelf to Overflow, it can enter here + // directly because both are located very closly. + main_shelf_->EndDrag(true); + // Stops the animation of |drag_view_| and sets its bounds explicitly + // becase ContinueDrag() stops its animation. Without this, unexpected + // bounds will be set. + bounds_animator_->StopAnimatingView(drag_view_); + int drag_view_index = view_model_->GetIndexOfView(drag_view_); + drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index)); + dragged_off_from_overflow_to_shelf_ = false; + } // Destroy our proxy view item. DestroyDragIconProxy(); // Re-insert the item and return simply false since the caller will handle // the move as in any normal case. dragged_off_shelf_ = false; drag_view_->layer()->SetOpacity(1.0f); - // Overflow bubble should be enlarged immediately when an item is - // re-inserted. + // The size of Overflow bubble should be updated immediately when an item + // is re-inserted. if (is_overflow_mode()) PreferredSizeChanged(); return false; + } else if (is_overflow_mode() && + main_shelf_->GetBoundsForDragInsertInScreen().Contains( + screen_location)) { + if (!dragged_off_from_overflow_to_shelf_) { + dragged_off_from_overflow_to_shelf_ = true; + drag_image_->SetOpacity(1.0f); + main_shelf_->StartDrag(dragged_app_id, screen_location); + } else { + main_shelf_->Drag(screen_location); + } + } else if (dragged_off_from_overflow_to_shelf_) { + // Makes the |drag_image_| partially disappear again. + dragged_off_from_overflow_to_shelf_ = false; + drag_image_->SetOpacity(kDraggedImageOpacity); + main_shelf_->EndDrag(true); + bounds_animator_->StopAnimatingView(drag_view_); + int drag_view_index = view_model_->GetIndexOfView(drag_view_); + drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index)); } // Move our proxy view item. UpdateDragIconProxy(screen_location); @@ -1122,7 +1153,7 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) { } // Make the item partially disappear to show that it will get removed if // dropped. - drag_image_->SetOpacity(0.5f); + drag_image_->SetOpacity(kDraggedImageOpacity); } return true; } @@ -1150,9 +1181,13 @@ void ShelfView::FinalizeRipOffDrag(bool cancel) { bool snap_back = false; // Items which cannot be dragged off will be handled as a cancel. if (!cancel) { - // Make sure we do not try to remove un-removable items like items which - // were not pinned or have to be always there. - if (RemovableByRipOff(current_index) != REMOVABLE) { + if (dragged_off_from_overflow_to_shelf_) { + dragged_off_from_overflow_to_shelf_ = false; + main_shelf_->EndDrag(false); + drag_view_->layer()->SetOpacity(1.0f); + } else if (RemovableByRipOff(current_index) != REMOVABLE) { + // Make sure we do not try to remove un-removable items like items which + // were not pinned or have to be always there. cancel = true; snap_back = true; } else { @@ -1164,7 +1199,12 @@ void ShelfView::FinalizeRipOffDrag(bool cancel) { } } if (cancel || snap_back) { - if (!cancelling_drag_model_changed_) { + if (dragged_off_from_overflow_to_shelf_) { + dragged_off_from_overflow_to_shelf_ = false; + // Main shelf handles revert of dragged item. + main_shelf_->EndDrag(true); + drag_view_->layer()->SetOpacity(1.0f); + } else if (!cancelling_drag_model_changed_) { // Only do something if the change did not come through a model change. gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen(); gfx::Point relative_to = GetBoundsInScreen().origin(); @@ -1253,6 +1293,7 @@ void ShelfView::ToggleOverflowBubble() { overflow_view->Init(); overflow_view->set_owner_overflow_bubble(overflow_bubble_.get()); overflow_view->OnShelfAlignmentChanged(); + overflow_view->main_shelf_ = this; UpdateOverflowRange(overflow_view); overflow_bubble_->Show(overflow_button_, overflow_view); @@ -1421,7 +1462,11 @@ gfx::Size ShelfView::GetPreferredSize() { // When an item is dragged off from the overflow bubble, it is moved to last // position and and changed to invisible. Overflow bubble size should be // shrunk to fit only for visible items. - if (is_overflow_mode() && dragged_off_shelf_ && + // If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible + // items in the shelf. + if (is_overflow_mode() && + dragged_off_shelf_ && + !dragged_off_from_overflow_to_shelf_ && RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) == REMOVABLE) last_button_index--; @@ -1614,7 +1659,7 @@ void ShelfView::PointerPressedOnButton(views::View* view, if (index == -1) return; - LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate( + ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate( model_->items()[index].id); if (view_model_->view_size() <= 1 || !item_delegate->IsDraggable()) return; // View is being deleted or not draggable, ignore request. @@ -1690,7 +1735,7 @@ base::string16 ShelfView::GetAccessibleName(const views::View* view) { if (view_index == -1) return base::string16(); - LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate( + ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate( model_->items()[view_index].id); return item_delegate->GetTitle(); } @@ -1748,9 +1793,8 @@ void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) { break; } - LauncherItemDelegate* item_delegate = - item_manager_->GetLauncherItemDelegate( - model_->items()[view_index].id); + ShelfItemDelegate* item_delegate = + item_manager_->GetShelfItemDelegate(model_->items()[view_index].id); if (!item_delegate->ItemSelected(event)) ShowListMenuForView(model_->items()[view_index], sender, event); } @@ -1759,9 +1803,9 @@ void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) { bool ShelfView::ShowListMenuForView(const LauncherItem& item, views::View* source, const ui::Event& event) { - scoped_ptr<ash::LauncherMenuModel> menu_model; - LauncherItemDelegate* item_delegate = - item_manager_->GetLauncherItemDelegate(item.id); + scoped_ptr<ShelfMenuModel> menu_model; + ShelfItemDelegate* item_delegate = + item_manager_->GetShelfItemDelegate(item.id); menu_model.reset(item_delegate->CreateApplicationMenu(event.flags())); // Make sure we have a menu and it has at least two items in addition to the @@ -1770,7 +1814,7 @@ bool ShelfView::ShowListMenuForView(const LauncherItem& item, return false; ShowMenu(scoped_ptr<views::MenuModelAdapter>( - new LauncherMenuModelAdapter(menu_model.get())), + new ShelfMenuModelAdapter(menu_model.get())), source, gfx::Point(), false, @@ -1783,9 +1827,8 @@ void ShelfView::ShowContextMenuForView(views::View* source, ui::MenuSourceType source_type) { int view_index = view_model_->GetIndexOfView(source); // TODO(simon.hong81): Create LauncherContextMenu for applist in its - // LauncherItemDelegate. - if (view_index != -1 && - model_->items()[view_index].type == TYPE_APP_LIST) { + // ShelfItemDelegate. + if (view_index != -1 && model_->items()[view_index].type == TYPE_APP_LIST) { view_index = -1; } @@ -1794,7 +1837,7 @@ void ShelfView::ShowContextMenuForView(views::View* source, return; } scoped_ptr<ui::MenuModel> menu_model; - LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate( + ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate( model_->items()[view_index].id); menu_model.reset(item_delegate->CreateContextMenu( source->GetWidget()->GetNativeView()->GetRootWindow())); @@ -1955,8 +1998,8 @@ bool ShelfView::ShouldShowTooltipForView(const views::View* view) const { const LauncherItem* item = LauncherItemForView(view); if (!item) return true; - LauncherItemDelegate* item_delegate = - item_manager_->GetLauncherItemDelegate(item->id); + ShelfItemDelegate* item_delegate = + item_manager_->GetShelfItemDelegate(item->id); return item_delegate->ShouldShowTooltip(); } diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index 4d6545e3a2..1f090e612a 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h @@ -32,11 +32,11 @@ namespace test { class ShelfViewTestAPI; } -class LauncherDelegate; -struct LauncherItem; -class LauncherItemDelegateManager; +class ShelfDelegate; class ShelfIconObserver; +class ShelfItemDelegateManager; class ShelfModel; +struct LauncherItem; namespace internal { @@ -62,8 +62,8 @@ class ASH_EXPORT ShelfView : public views::View, public app_list::ApplicationDragAndDropHost { public: ShelfView(ShelfModel* model, - LauncherDelegate* delegate, - ShelfLayoutManager* shelf_layout_manager); + ShelfDelegate* delegate, + ShelfLayoutManager* manager); virtual ~ShelfView(); ShelfTooltipManager* tooltip_manager() { return tooltip_.get(); } @@ -168,6 +168,9 @@ class ASH_EXPORT ShelfView : public views::View, // Sets the bounds of each view to its ideal bounds. void LayoutToIdealBounds(); + // Update all button's visibility in overflow. + void UpdateAllButtonsVisibilityInOverflowMode(); + // Calculates the ideal bounds. The bounds of each button corresponding to an // item in the model is set in |view_model_|. void CalculateIdealBounds(IdealBounds* bounds); @@ -335,7 +338,7 @@ class ASH_EXPORT ShelfView : public views::View, ShelfModel* model_; // Delegate; owned by Launcher. - LauncherDelegate* delegate_; + ShelfDelegate* delegate_; // Used to manage the set of active launcher buttons. There is a view per // item in |model_|. @@ -431,8 +434,8 @@ class ASH_EXPORT ShelfView : public views::View, // The rip off view when a snap back operation is underway. views::View* snap_back_from_rip_off_view_; - // Holds LauncherItemDelegateManager. - LauncherItemDelegateManager* item_manager_; + // Holds ShelfItemDelegateManager. + ShelfItemDelegateManager* item_manager_; // Holds ShelfLayoutManager. ShelfLayoutManager* layout_manager_; @@ -440,6 +443,12 @@ class ASH_EXPORT ShelfView : public views::View, // True when this ShelfView is used for Overflow Bubble. bool overflow_mode_; + // Holds a pointer to main ShelfView when a ShelfView is in overflow mode. + ShelfView* main_shelf_; + + // True when ripped item from overflow bubble is entered into Shelf. + bool dragged_off_from_overflow_to_shelf_; + DISALLOW_COPY_AND_ASSIGN(ShelfView); }; diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index 28812d192d..d3e2b1d2e6 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc @@ -9,13 +9,13 @@ #include "ash/ash_switches.h" #include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_item_delegate_manager.h" #include "ash/launcher/launcher_types.h" #include "ash/root_window_controller.h" #include "ash/shelf/overflow_bubble.h" #include "ash/shelf/overflow_bubble_view.h" #include "ash/shelf/shelf_button.h" #include "ash/shelf/shelf_icon_observer.h" +#include "ash/shelf/shelf_item_delegate_manager.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_tooltip_manager.h" @@ -27,12 +27,13 @@ #include "ash/test/overflow_bubble_view_test_api.h" #include "ash/test/shelf_view_test_api.h" #include "ash/test/shell_test_api.h" -#include "ash/test/test_launcher_delegate.h" -#include "ash/test/test_launcher_item_delegate.h" +#include "ash/test/test_shelf_delegate.h" +#include "ash/test/test_shelf_item_delegate.h" #include "base/basictypes.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" #include "grit/ash_resources.h" #include "ui/aura/root_window.h" #include "ui/aura/test/aura_test_base.h" @@ -81,7 +82,7 @@ class TestShelfIconObserver : public ShelfIconObserver { DISALLOW_COPY_AND_ASSIGN(TestShelfIconObserver); }; -class ShelfViewIconObserverTest : public ash::test::AshTestBase { +class ShelfViewIconObserverTest : public AshTestBase { public: ShelfViewIconObserverTest() {} virtual ~ShelfViewIconObserverTest() {} @@ -119,9 +120,8 @@ class ShelfViewIconObserverTest : public ash::test::AshTestBase { }; TEST_F(ShelfViewIconObserverTest, AddRemove) { - ash::test::TestLauncherDelegate* launcher_delegate = - ash::test::TestLauncherDelegate::instance(); - ASSERT_TRUE(launcher_delegate); + TestShelfDelegate* shelf_delegate = TestShelfDelegate::instance(); + ASSERT_TRUE(shelf_delegate); views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; @@ -130,7 +130,7 @@ TEST_F(ShelfViewIconObserverTest, AddRemove) { scoped_ptr<views::Widget> widget(new views::Widget()); widget->Init(params); - launcher_delegate->AddLauncherItem(widget->GetNativeWindow()); + shelf_delegate->AddLauncherItem(widget->GetNativeWindow()); shelf_view_test()->RunMessageLoopUntilAnimationsDone(); EXPECT_TRUE(observer()->change_notified()); observer()->Reset(); @@ -156,9 +156,8 @@ TEST_F(ShelfViewIconObserverTest, MAYBE_AddRemoveWithMultipleDisplays) { UpdateDisplay("400x400,400x400"); TestShelfIconObserver second_observer(LauncherForSecondaryDisplay()); - ash::test::TestLauncherDelegate* launcher_delegate = - ash::test::TestLauncherDelegate::instance(); - ASSERT_TRUE(launcher_delegate); + TestShelfDelegate* shelf_delegate = TestShelfDelegate::instance(); + ASSERT_TRUE(shelf_delegate); views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; @@ -167,7 +166,7 @@ TEST_F(ShelfViewIconObserverTest, MAYBE_AddRemoveWithMultipleDisplays) { scoped_ptr<views::Widget> widget(new views::Widget()); widget->Init(params); - launcher_delegate->AddLauncherItem(widget->GetNativeWindow()); + shelf_delegate->AddLauncherItem(widget->GetNativeWindow()); shelf_view_test()->RunMessageLoopUntilAnimationsDone(); EXPECT_TRUE(observer()->change_notified()); EXPECT_TRUE(second_observer.change_notified()); @@ -184,7 +183,7 @@ TEST_F(ShelfViewIconObserverTest, MAYBE_AddRemoveWithMultipleDisplays) { } TEST_F(ShelfViewIconObserverTest, BoundsChanged) { - ash::ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf(); + ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf(); Launcher* launcher = Launcher::ForPrimaryDisplay(); gfx::Size shelf_size = shelf->GetWindowBoundsInScreen().size(); @@ -199,24 +198,63 @@ TEST_F(ShelfViewIconObserverTest, BoundsChanged) { //////////////////////////////////////////////////////////////////////////////// // ShelfView tests. -// LauncherItemDelegate for ShelfViewTest.OverflowBubbleSize only. -// This class should only be used for re-insert test because it cannot handle -// unpin request. -class TestLauncherDelegateForShelfView : public TestLauncherDelegate { +// Simple ShelfDelegate implmentation for ShelfViewTest.OverflowBubbleSize +// and CheckDragAndDropFromOverflowBubbleToShelf +class TestShelfDelegateForShelfView : public ShelfDelegate { public: - explicit TestLauncherDelegateForShelfView(ShelfModel* model) - : TestLauncherDelegate(model) {} - virtual ~TestLauncherDelegateForShelfView() {} + explicit TestShelfDelegateForShelfView(ShelfModel* model) + : model_(model) {} + virtual ~TestShelfDelegateForShelfView() {} + + // ShelfDelegate overrides: + virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE {} + + virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE {} + + virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE { + LauncherID id = 0; + EXPECT_TRUE(base::StringToInt(app_id, &id)); + return id; + } + + virtual const std::string& GetAppIDForLauncherID(LauncherID id) OVERRIDE { + // Use |app_id_| member variable because returning a reference to local + // variable is not allowed. + app_id_ = base::IntToString(id); + return app_id_; + } + + virtual void PinAppWithID(const std::string& app_id) OVERRIDE { + } - // TestLauncherDelegate overrides: virtual bool IsAppPinned(const std::string& app_id) OVERRIDE { // Returns true for ShelfViewTest.OverflowBubbleSize. To test ripping off in // that test, an item is already pinned state. return true; } + virtual bool CanPin() const OVERRIDE { + return true; + } + + virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE { + LauncherID id = 0; + EXPECT_TRUE(base::StringToInt(app_id, &id)); + ASSERT_GT(id, 0); + int index = model_->ItemIndexByID(id); + ASSERT_GE(index, 0); + + model_->RemoveItemAt(index); + } + private: - DISALLOW_COPY_AND_ASSIGN(TestLauncherDelegateForShelfView); + ShelfModel* model_; + + // Temp member variable for returning a value. See the comment in the + // GetAppIDForLauncherID(). + std::string app_id_; + + DISALLOW_COPY_AND_ASSIGN(TestShelfDelegateForShelfView); }; class ShelfViewTest : public AshTestBase { @@ -238,8 +276,7 @@ class ShelfViewTest : public AshTestBase { test_api_.reset(new ShelfViewTestAPI(shelf_view_)); test_api_->SetAnimationDuration(1); // Speeds up animation for test. - item_manager_ = - ash::Shell::GetInstance()->launcher_item_delegate_manager(); + item_manager_ = Shell::GetInstance()->shelf_item_delegate_manager(); DCHECK(item_manager_); // Add browser shortcut launcher item at index 0 for test. @@ -252,10 +289,9 @@ class ShelfViewTest : public AshTestBase { } protected: - void CreateAndSetLauncherItemDelegateForID(LauncherID id) { - scoped_ptr<LauncherItemDelegate> delegate( - new ash::test::TestLauncherItemDelegate(NULL)); - item_manager_->SetLauncherItemDelegate(id, delegate.Pass()); + void CreateAndSetShelfItemDelegateForID(LauncherID id) { + scoped_ptr<ShelfItemDelegate> delegate(new TestShelfItemDelegate(NULL)); + item_manager_->SetShelfItemDelegate(id, delegate.Pass()); } LauncherID AddBrowserShortcut() { @@ -264,7 +300,7 @@ class ShelfViewTest : public AshTestBase { LauncherID id = model_->next_id(); model_->AddAt(browser_index_, browser_shortcut); - CreateAndSetLauncherItemDelegateForID(id); + CreateAndSetShelfItemDelegateForID(id); test_api_->RunMessageLoopUntilAnimationsDone(); return id; } @@ -276,7 +312,7 @@ class ShelfViewTest : public AshTestBase { LauncherID id = model_->next_id(); model_->Add(item); - CreateAndSetLauncherItemDelegateForID(id); + CreateAndSetShelfItemDelegateForID(id); test_api_->RunMessageLoopUntilAnimationsDone(); return id; } @@ -294,7 +330,7 @@ class ShelfViewTest : public AshTestBase { LauncherID id = model_->next_id(); model_->Add(item); - CreateAndSetLauncherItemDelegateForID(id); + CreateAndSetShelfItemDelegateForID(id); return id; } @@ -305,7 +341,7 @@ class ShelfViewTest : public AshTestBase { LauncherID id = model_->next_id(); model_->Add(item); - CreateAndSetLauncherItemDelegateForID(id); + CreateAndSetShelfItemDelegateForID(id); return id; } @@ -336,8 +372,8 @@ class ShelfViewTest : public AshTestBase { for (size_t model_index = 0; model_index < model_->items().size(); ++model_index) { - ash::LauncherItem item = model_->items()[model_index]; - ash::LauncherID id = item.id; + LauncherItem item = model_->items()[model_index]; + LauncherID id = item.id; EXPECT_EQ(id_map[map_index].first, id); EXPECT_EQ(id_map[map_index].second, GetButtonByID(id)); ++map_index; @@ -419,14 +455,115 @@ class ShelfViewTest : public AshTestBase { return shelf_view_->tooltip_manager()->anchor_; } + void AddButtonsUntilOverflow() { + int items_added = 0; + while (!test_api_->IsOverflowButtonVisible()) { + AddAppShortcut(); + ++items_added; + ASSERT_LT(items_added, 10000); + } + } + void ShowTooltip() { shelf_view_->tooltip_manager()->ShowInternal(); } + void TestDraggingAnItemFromOverflowToShelf(bool cancel) { + test_api_->ShowOverflowBubble(); + ASSERT_TRUE(test_api_->overflow_bubble() && + test_api_->overflow_bubble()->IsShowing()); + + ash::test::ShelfViewTestAPI test_api_for_overflow( + test_api_->overflow_bubble()->shelf_view()); + + int total_item_count = model_->item_count(); + + int last_visible_item_id_in_shelf = + model_->items()[test_api_->GetLastVisibleIndex()].id; + int second_last_visible_item_id_in_shelf = + model_->items()[test_api_->GetLastVisibleIndex() - 1].id; + int first_visible_item_id_in_overflow = + model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id; + int second_last_visible_item_id_in_overflow = + model_->items()[test_api_for_overflow.GetLastVisibleIndex() - 1].id; + + int drag_item_index = + test_api_for_overflow.GetLastVisibleIndex(); + LauncherID drag_item_id = model_->items()[drag_item_index].id; + internal::ShelfButton* drag_button = + test_api_for_overflow.GetButton(drag_item_index); + gfx::Point center_point_of_drag_item = + drag_button->GetBoundsInScreen().CenterPoint(); + + aura::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(), + center_point_of_drag_item); + // Rip an item off to OverflowBubble. + generator.PressLeftButton(); + gfx::Point rip_off_point(center_point_of_drag_item.x(), 0); + generator.MoveMouseTo(rip_off_point); + test_api_for_overflow.RunMessageLoopUntilAnimationsDone(); + ASSERT_TRUE(test_api_for_overflow.IsDraggingShelfItem()); + ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf()); + + // Move a dragged item into Shelf at |drop_index|. + int drop_index = 1; + gfx::Point drop_point = + test_api_->GetButton(drop_index)->GetBoundsInScreen().CenterPoint(); + int item_width = test_api_for_overflow.GetButtonSize(); + // To insert at |drop_index|, more smaller x-axis value of |drop_point| + // should be used. + gfx::Point modified_drop_point(drop_point.x() - item_width / 4, + drop_point.y()); + generator.MoveMouseTo(modified_drop_point); + test_api_for_overflow.RunMessageLoopUntilAnimationsDone(); + test_api_->RunMessageLoopUntilAnimationsDone(); + ASSERT_TRUE(test_api_for_overflow.IsDraggingShelfItem()); + ASSERT_TRUE(test_api_for_overflow.DraggedItemFromOverflowToShelf()); + + if (cancel) + drag_button->OnMouseCaptureLost(); + else + generator.ReleaseLeftButton(); + + test_api_for_overflow.RunMessageLoopUntilAnimationsDone(); + test_api_->RunMessageLoopUntilAnimationsDone(); + ASSERT_FALSE(test_api_for_overflow.IsDraggingShelfItem()); + ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf()); + + // Compare pre-stored items' id with newly positioned items' after dragging + // is canceled or finished. + if (cancel) { + EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex()].id, + last_visible_item_id_in_shelf); + EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex() - 1].id, + second_last_visible_item_id_in_shelf); + EXPECT_EQ( + model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id, + first_visible_item_id_in_overflow); + EXPECT_EQ( + model_->items()[test_api_for_overflow.GetLastVisibleIndex() - 1].id, + second_last_visible_item_id_in_overflow); + } else { + LauncherID drop_item_id = model_->items()[drop_index].id; + EXPECT_EQ(drop_item_id, drag_item_id); + EXPECT_EQ(model_->item_count(), total_item_count); + EXPECT_EQ( + model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id, + last_visible_item_id_in_shelf); + EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex()].id, + second_last_visible_item_id_in_shelf); + EXPECT_EQ( + model_->items()[test_api_for_overflow.GetFirstVisibleIndex() + 1].id, + first_visible_item_id_in_overflow); + EXPECT_EQ(model_->items()[test_api_for_overflow.GetLastVisibleIndex()].id, + second_last_visible_item_id_in_overflow); + } + } + ShelfModel* model_; internal::ShelfView* shelf_view_; int browser_index_; - LauncherItemDelegateManager* item_manager_; + ShelfItemDelegateManager* item_manager_; scoped_ptr<ShelfViewTestAPI> test_api_; @@ -1018,10 +1155,10 @@ TEST_F(ShelfViewTest, LauncherItemStatus) { int index = model_->ItemIndexByID(last_added); internal::ShelfButton* button = GetButtonByID(last_added); ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state()); - item.status = ash::STATUS_ACTIVE; + item.status = STATUS_ACTIVE; model_->Set(index, item); ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state()); - item.status = ash::STATUS_ATTENTION; + item.status = STATUS_ATTENTION; model_->Set(index, item); ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state()); } @@ -1071,10 +1208,10 @@ TEST_F(ShelfViewTest, LauncherItemStatusPlatformApp) { int index = model_->ItemIndexByID(last_added); internal::ShelfButton* button = GetButtonByID(last_added); ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state()); - item.status = ash::STATUS_ACTIVE; + item.status = STATUS_ACTIVE; model_->Set(index, item); ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state()); - item.status = ash::STATUS_ATTENTION; + item.status = STATUS_ATTENTION; model_->Set(index, item); ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state()); } @@ -1160,9 +1297,8 @@ TEST_F(ShelfViewTest, RemovingItemClosesTooltip) { EXPECT_FALSE(tooltip_manager->IsVisible()); // Change the shelf layout. This should not crash. - ash::Shell::GetInstance()->SetShelfAlignment( - ash::SHELF_ALIGNMENT_LEFT, - ash::Shell::GetPrimaryRootWindow()); + Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_LEFT, + Shell::GetPrimaryRootWindow()); } // Changing the shelf alignment closes any open tooltip. @@ -1181,9 +1317,8 @@ TEST_F(ShelfViewTest, ShelfAlignmentClosesTooltip) { EXPECT_TRUE(tooltip_manager->IsVisible()); // Changing shelf alignment hides the tooltip. - ash::Shell::GetInstance()->SetShelfAlignment( - ash::SHELF_ALIGNMENT_LEFT, - ash::Shell::GetPrimaryRootWindow()); + Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_LEFT, + Shell::GetPrimaryRootWindow()); EXPECT_FALSE(tooltip_manager->IsVisible()); } @@ -1339,29 +1474,23 @@ TEST_F(ShelfViewTest, ResizeDuringOverflowAddAnimation) { // Checks the overflow bubble size when an item is ripped off and re-inserted. TEST_F(ShelfViewTest, OverflowBubbleSize) { - // Replace LauncherDelegate. + // Replace ShelfDelegate. test::ShellTestApi test_api(Shell::GetInstance()); - test_api.SetLauncherDelegate(NULL); - LauncherDelegate *delegate = new TestLauncherDelegateForShelfView(model_); - test_api.SetLauncherDelegate(delegate); + test_api.SetShelfDelegate(NULL); + ShelfDelegate *delegate = new TestShelfDelegateForShelfView(model_); + test_api.SetShelfDelegate(delegate); test::LauncherTestAPI( - Launcher::ForPrimaryDisplay()).SetLauncherDelegate(delegate); - test_api_->SetLauncherDelegate(delegate); + Launcher::ForPrimaryDisplay()).SetShelfDelegate(delegate); + test_api_->SetShelfDelegate(delegate); - // Add buttons until overflow. - int items_added = 0; - while (!test_api_->IsOverflowButtonVisible()) { - AddAppShortcut(); - ++items_added; - ASSERT_LT(items_added, 10000); - } + AddButtonsUntilOverflow(); // Show overflow bubble. test_api_->ShowOverflowBubble(); ASSERT_TRUE(test_api_->overflow_bubble() && test_api_->overflow_bubble()->IsShowing()); - ash::test::ShelfViewTestAPI test_for_overflow_view( + ShelfViewTestAPI test_for_overflow_view( test_api_->overflow_bubble()->shelf_view()); int ripped_index = test_for_overflow_view.GetLastVisibleIndex(); @@ -1369,9 +1498,9 @@ TEST_F(ShelfViewTest, OverflowBubbleSize) { int item_width = test_for_overflow_view.GetButtonSize() + test_for_overflow_view.GetButtonSpacing(); - aura::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(), + aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), gfx::Point()); - ash::internal::ShelfButton* button = + internal::ShelfButton* button = test_for_overflow_view.GetButton(ripped_index); // Rip off the last visible item. gfx::Point start_point = button->GetBoundsInScreen().CenterPoint(); @@ -1423,13 +1552,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsOfScrolledOverflowBubble) { EXPECT_EQ(2, model_->item_count()); - // Add buttons until overflow. - int items_added = 0; - while (!test_api_->IsOverflowButtonVisible()) { - AddAppShortcut(); - ++items_added; - ASSERT_LT(items_added, 10000); - } + AddButtonsUntilOverflow(); // Show overflow bubble. test_api_->ShowOverflowBubble(); @@ -1451,14 +1574,14 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsOfScrolledOverflowBubble) { ASSERT_TRUE(test_api_->overflow_bubble() && test_api_->overflow_bubble()->IsShowing()); - ash::test::ShelfViewTestAPI test_for_overflow_view( + ShelfViewTestAPI test_for_overflow_view( test_api_->overflow_bubble()->shelf_view()); int first_index = test_for_overflow_view.GetFirstVisibleIndex(); int last_index = test_for_overflow_view.GetLastVisibleIndex(); - ash::internal::ShelfButton* first_button = + internal::ShelfButton* first_button = test_for_overflow_view.GetButton(first_index); - ash::internal::ShelfButton* last_button = + internal::ShelfButton* last_button = test_for_overflow_view.GetButton(last_index); gfx::Point first_point = first_button->GetBoundsInScreen().CenterPoint(); gfx::Point last_point = last_button->GetBoundsInScreen().CenterPoint(); @@ -1485,7 +1608,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) { UpdateDisplay("800x600,800x600"); Launcher* secondary_launcher = - Launcher::ForWindow(ash::Shell::GetAllRootWindows()[1]); + Launcher::ForWindow(Shell::GetAllRootWindows()[1]); internal::ShelfView* shelf_view_for_secondary = test::LauncherTestAPI(secondary_launcher).shelf_view(); @@ -1497,13 +1620,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) { // Speeds up animation for test. test_api_for_secondary.SetAnimationDuration(1); - // Add buttons until overflow. - int items_added = 0; - while (!test_api_->IsOverflowButtonVisible()) { - AddAppShortcut(); - ++items_added; - ASSERT_LT(items_added, 10000); - } + AddButtonsUntilOverflow(); // Test #1: Test drag insertion bounds of primary shelf. // Show overflow bubble. @@ -1511,10 +1628,10 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) { ASSERT_TRUE(test_api_->overflow_bubble() && test_api_->overflow_bubble()->IsShowing()); - ash::test::ShelfViewTestAPI test_api_for_overflow_view( + ShelfViewTestAPI test_api_for_overflow_view( test_api_->overflow_bubble()->shelf_view()); - ash::internal::ShelfButton* button = test_api_for_overflow_view.GetButton( + internal::ShelfButton* button = test_api_for_overflow_view.GetButton( test_api_for_overflow_view.GetLastVisibleIndex()); // Checks that a point in shelf is contained in drag insert bounds. @@ -1532,10 +1649,10 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) { ASSERT_TRUE(test_api_for_secondary.overflow_bubble() && test_api_for_secondary.overflow_bubble()->IsShowing()); - ash::test::ShelfViewTestAPI test_api_for_overflow_view_of_secondary( + ShelfViewTestAPI test_api_for_overflow_view_of_secondary( test_api_for_secondary.overflow_bubble()->shelf_view()); - ash::internal::ShelfButton* button_in_secondary = + internal::ShelfButton* button_in_secondary = test_api_for_overflow_view_of_secondary.GetButton( test_api_for_overflow_view_of_secondary.GetLastVisibleIndex()); @@ -1554,6 +1671,23 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) { EXPECT_FALSE(drag_reinsert_bounds_in_secondary.Contains(point_in_shelf_view)); } +// Checks various drag and drop operations from OverflowBubble to Shelf. +TEST_F(ShelfViewTest, CheckDragAndDropFromOverflowBubbleToShelf) { + // Replace LauncherDelegate. + test::ShellTestApi test_api(Shell::GetInstance()); + test_api.SetShelfDelegate(NULL); + ShelfDelegate *delegate = new TestShelfDelegateForShelfView(model_); + test_api.SetShelfDelegate(delegate); + test::LauncherTestAPI( + Launcher::ForPrimaryDisplay()).SetShelfDelegate(delegate); + test_api_->SetShelfDelegate(delegate); + + AddButtonsUntilOverflow(); + + TestDraggingAnItemFromOverflowToShelf(false); + TestDraggingAnItemFromOverflowToShelf(true); +} + class ShelfViewVisibleBoundsTest : public ShelfViewTest, public testing::WithParamInterface<bool> { public: diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc index 8dc737ab54..d483c07628 100644 --- a/ash/shelf/shelf_widget.cc +++ b/ash/shelf/shelf_widget.cc @@ -6,9 +6,9 @@ #include "ash/ash_switches.h" #include "ash/focus_cycler.h" -#include "ash/launcher/launcher_delegate.h" #include "ash/root_window_controller.h" #include "ash/session_state_delegate.h" +#include "ash/shelf/shelf_delegate.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_navigator.h" @@ -46,7 +46,6 @@ const int kDimAlpha = 128; // The time to dim and un-dim. const int kTimeToDimMs = 3000; // Slow in dimming. const int kTimeToUnDimMs = 200; // Fast in activating. -const int kTimeToSwitchBackgroundMs = 1000; // Class used to slightly dim shelf items when maximized and visible. class DimmerView : public views::View, @@ -145,7 +144,7 @@ DimmerView::DimmerView(ash::ShelfWidget* shelf_widget, // Make sure it is undimmed at the beginning and then fire off the dimming // animation. background_animator_.SetPaintsBackground(false, - ash::internal::BackgroundAnimator::CHANGE_IMMEDIATE); + ash::BACKGROUND_CHANGE_IMMEDIATE); SetHovered(false); } @@ -161,8 +160,7 @@ void DimmerView::SetHovered(bool hovered) { background_animator_.SetDuration(hovered ? kTimeToUnDimMs : kTimeToDimMs); background_animator_.SetPaintsBackground(!hovered, disable_dimming_animations_for_test_ ? - ash::internal::BackgroundAnimator::CHANGE_IMMEDIATE : - ash::internal::BackgroundAnimator::CHANGE_ANIMATE); + ash::BACKGROUND_CHANGE_IMMEDIATE : ash::BACKGROUND_CHANGE_ANIMATE); } void DimmerView::ForceUndimming(bool force) { @@ -391,22 +389,55 @@ void ShelfWidget::DelegateView::OnPaintBackground(gfx::Canvas* canvas) { SkBitmapOperations::ROTATION_90_CW, SkBitmapOperations::ROTATION_270_CW, SkBitmapOperations::ROTATION_180_CW)); - - gfx::Rect black_rect = - shelf_->shelf_layout_manager()->SelectValueForShelfAlignment( - gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels), - gfx::Rect(0, 0, kNumBlackPixels, height()), - gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()), - gfx::Rect(0, 0, width(), kNumBlackPixels)); - + const gfx::Rect dock_bounds(shelf_->shelf_layout_manager()->dock_bounds()); SkPaint paint; paint.setAlpha(alpha_); canvas->DrawImageInt( launcher_background, 0, 0, launcher_background.width(), launcher_background.height(), - 0, 0, width(), height(), + (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() && + dock_bounds.x() == 0 && dock_bounds.width() > 0) ? + dock_bounds.width() : 0, 0, + SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() ? + width() - dock_bounds.width() : width(), height(), false, paint); + if (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() && + dock_bounds.width() > 0) { + // The part of the shelf background that is in the corner below the docked + // windows close to the work area is an arched gradient that blends + // vertically oriented docked background and horizontal shelf. + gfx::ImageSkia launcher_corner = + *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_CORNER); + if (dock_bounds.x() == 0) { + launcher_corner = gfx::ImageSkiaOperations::CreateRotatedImage( + launcher_corner, SkBitmapOperations::ROTATION_90_CW); + } + canvas->DrawImageInt( + launcher_corner, + 0, 0, launcher_corner.width(), launcher_corner.height(), + dock_bounds.x() > 0 ? dock_bounds.x() : dock_bounds.width() - height(), + 0, + height(), height(), + false, + paint); + // The part of the shelf background that is just below the docked windows + // is drawn using the last (lowest) 1-pixel tall strip of the image asset. + // This avoids showing the border 3D shadow between the shelf and the dock. + canvas->DrawImageInt( + launcher_background, + 0, launcher_background.height() - 1, launcher_background.width(), 1, + dock_bounds.x() > 0 ? dock_bounds.x() + height() : 0, 0, + dock_bounds.width() - height(), height(), + false, + paint); + } + gfx::Rect black_rect = + shelf_->shelf_layout_manager()->SelectValueForShelfAlignment( + gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels), + gfx::Rect(0, 0, kNumBlackPixels, height()), + gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()), + gfx::Rect(0, 0, width(), kNumBlackPixels)); canvas->FillRect(black_rect, SK_ColorBLACK); } @@ -521,12 +552,12 @@ ShelfWidget::~ShelfWidget() { void ShelfWidget::SetPaintsBackground( ShelfBackgroundType background_type, - internal::BackgroundAnimator::ChangeType change_type) { + BackgroundAnimatorChangeType change_type) { ui::Layer* opaque_background = delegate_view_->opaque_background(); float target_opacity = (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f; scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation; - if (change_type != internal::BackgroundAnimator::CHANGE_IMMEDIATE) { + if (change_type != BACKGROUND_CHANGE_IMMEDIATE) { opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings( opaque_background->GetAnimator())); opaque_background_animation->SetTransitionDuration( @@ -536,9 +567,11 @@ void ShelfWidget::SetPaintsBackground( // TODO(mukai): use ui::Layer on both opaque_background and normal background // retire background_animator_ at all. It would be simpler. + // See also DockedBackgroundWidget::SetPaintsBackground. background_animator_.SetPaintsBackground( background_type != SHELF_BACKGROUND_DEFAULT, change_type); + delegate_view_->SchedulePaint(); } ShelfBackgroundType ShelfWidget::GetBackgroundType() const { @@ -605,12 +638,12 @@ void ShelfWidget::CreateLauncher() { Shell* shell = Shell::GetInstance(); // This needs to be called before shelf_model(). - LauncherDelegate* launcher_delegate = shell->GetLauncherDelegate(); - if (!launcher_delegate) + ShelfDelegate* shelf_delegate = shell->GetShelfDelegate(); + if (!shelf_delegate) return; // Not ready to create Launcher launcher_.reset(new Launcher(shell->shelf_model(), - shell->GetLauncherDelegate(), + shell->GetShelfDelegate(), this)); SetFocusCycler(shell->focus_cycler()); diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h index c7adb74255..83d4a8d701 100644 --- a/ash/shelf/shelf_widget.h +++ b/ash/shelf/shelf_widget.h @@ -43,12 +43,11 @@ class ASH_EXPORT ShelfWidget : public views::Widget, ShelfAlignment GetAlignment() const; // Sets the shelf's background type. - void SetPaintsBackground( - ShelfBackgroundType background_type, - internal::BackgroundAnimator::ChangeType change_type); + void SetPaintsBackground(ShelfBackgroundType background_type, + BackgroundAnimatorChangeType change_type); ShelfBackgroundType GetBackgroundType() const; - // Causes shelf items to be slightly dimmed (eg when a window is maximized). + // Causes shelf items to be slightly dimmed (e.g. when a window is maximized). void SetDimsShelf(bool dimming); bool GetDimsShelf() const; diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc new file mode 100644 index 0000000000..f177e48c29 --- /dev/null +++ b/ash/shelf/shelf_window_watcher.cc @@ -0,0 +1,214 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/shelf/shelf_window_watcher.h" + +#include "ash/display/display_controller.h" +#include "ash/shelf/shelf_model.h" +#include "ash/shelf/shelf_util.h" +#include "ash/shell.h" +#include "ash/shell_window_ids.h" +#include "ash/wm/window_util.h" +#include "ui/aura/client/activation_client.h" +#include "ui/aura/window.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/screen.h" + +namespace { + +// Sets LauncherItem property by using the value of |details|. +void SetLauncherItemDetailsForLauncherItem( + ash::LauncherItem* item, + const ash::LauncherItemDetails& details) { + item->type = details.type; + if (details.image_resource_id != ash::kInvalidImageResourceID) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + item->image = *rb.GetImageSkiaNamed(details.image_resource_id); + } +} + +// Returns true if |window| has a LauncherItem added by ShelfWindowWatcher. +bool HasLauncherItemForWindow(aura::Window* window) { + if (ash::GetLauncherItemDetailsForWindow(window) != NULL && + ash::GetLauncherIDForWindow(window) != ash::kInvalidLauncherID) + return true; + return false; +} + +} // namespace + +namespace ash { +namespace internal { + +ShelfWindowWatcher::RootWindowObserver::RootWindowObserver( + ShelfWindowWatcher* window_watcher) + : window_watcher_(window_watcher) { +} + +ShelfWindowWatcher::RootWindowObserver::~RootWindowObserver() { +} + +void ShelfWindowWatcher::RootWindowObserver::OnWindowDestroying( + aura::Window* window) { + window_watcher_->OnRootWindowRemoved(window); +} + +ShelfWindowWatcher::ShelfWindowWatcher(ShelfModel* model) + : model_(model), + root_window_observer_(this), + observed_windows_(this), + observed_root_windows_(&root_window_observer_), + observed_activation_clients_(this) { + // We can't assume all RootWindows have the same ActivationClient. + // Add a RootWindow and its ActivationClient to the observed list. + aura::Window::Windows root_windows = Shell::GetAllRootWindows(); + for (aura::Window::Windows::const_iterator it = root_windows.begin(); + it != root_windows.end(); ++it) + OnRootWindowAdded(*it); + + Shell::GetScreen()->AddObserver(this); +} + +ShelfWindowWatcher::~ShelfWindowWatcher() { + Shell::GetScreen()->RemoveObserver(this); +} + +void ShelfWindowWatcher::AddLauncherItem(aura::Window* window) { + const LauncherItemDetails* item_details = + GetLauncherItemDetailsForWindow(window); + LauncherItem item; + item.status = ash::wm::IsActiveWindow(window) ? STATUS_ACTIVE: STATUS_RUNNING; + SetLauncherItemDetailsForLauncherItem(&item, *item_details); + SetLauncherIDForWindow(model_->next_id(), window); + // TODO(simonhong): Create LauncherItemDelegate for LauncherItem. + model_->Add(item); +} + +void ShelfWindowWatcher::RemoveLauncherItem(aura::Window* window) { + model_->RemoveItemAt(model_->ItemIndexByID(GetLauncherIDForWindow(window))); + SetLauncherIDForWindow(kInvalidLauncherID, window); +} + +void ShelfWindowWatcher::OnRootWindowAdded(aura::Window* root_window) { + // |observed_activation_clients_| can have the same ActivationClient multiple + // times - which would be handled by the |observed_activation_clients_|. + observed_activation_clients_.Add( + aura::client::GetActivationClient(root_window)); + observed_root_windows_.Add(root_window); + + aura::Window* default_container = Shell::GetContainer( + root_window, + kShellWindowId_DefaultContainer); + observed_windows_.Add(default_container); + for (size_t i = 0; i < default_container->children().size(); ++i) + observed_windows_.Add(default_container->children()[i]); +} + +void ShelfWindowWatcher::OnRootWindowRemoved(aura::Window* root_window) { + observed_root_windows_.Remove(root_window); + observed_activation_clients_.Remove( + aura::client::GetActivationClient(root_window)); +} + +void ShelfWindowWatcher::UpdateLauncherItemStatus(aura::Window* window, + bool is_active) { + int index = GetLauncherItemIndexForWindow(window); + DCHECK_GE(index, 0); + + LauncherItem item = model_->items()[index]; + item.status = is_active ? STATUS_ACTIVE : STATUS_RUNNING; + model_->Set(index, item); +} + +int ShelfWindowWatcher::GetLauncherItemIndexForWindow( + aura::Window* window) const { + return model_->ItemIndexByID(GetLauncherIDForWindow(window)); +} + +void ShelfWindowWatcher::OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) { + if (gained_active && HasLauncherItemForWindow(gained_active)) + UpdateLauncherItemStatus(gained_active, true); + if (lost_active && HasLauncherItemForWindow(lost_active)) + UpdateLauncherItemStatus(lost_active, false); +} + +void ShelfWindowWatcher::OnWindowAdded(aura::Window* window) { + observed_windows_.Add(window); + // Add LauncherItem if |window| already has a LauncherItemDetails when it is + // created. Don't make a new LauncherItem for the re-parented |window| that + // already has a LauncherItem. + if (GetLauncherIDForWindow(window) == ash::kInvalidLauncherID && + GetLauncherItemDetailsForWindow(window)) + AddLauncherItem(window); +} + +void ShelfWindowWatcher::OnWillRemoveWindow(aura::Window* window) { + // Remove a child window of default container and its item if it has. + if (observed_windows_.IsObserving(window)) + observed_windows_.Remove(window); + + if (HasLauncherItemForWindow(window)) + RemoveLauncherItem(window); +} + +void ShelfWindowWatcher::OnWindowDestroying(aura::Window* window) { + // Remove the default container. + if (observed_windows_.IsObserving(window)) + observed_windows_.Remove(window); +} + +void ShelfWindowWatcher::OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) { + if (key != kLauncherItemDetailsKey) + return; + + if (GetLauncherItemDetailsForWindow(window) == NULL) { + // Removes LauncherItem for |window| when it has a LauncherItem. + if (reinterpret_cast<LauncherItemDetails*>(old) != NULL) + RemoveLauncherItem(window); + return; + } + + // When LauncherItemDetails is changed, update LauncherItem. + if (HasLauncherItemForWindow(window)) { + int index = GetLauncherItemIndexForWindow(window); + DCHECK_GE(index, 0); + LauncherItem item = model_->items()[index]; + const LauncherItemDetails* details = + GetLauncherItemDetailsForWindow(window); + SetLauncherItemDetailsForLauncherItem(&item, *details); + model_->Set(index, item); + return; + } + + // Creates a new LauncherItem for |window|. + AddLauncherItem(window); +} + +void ShelfWindowWatcher::OnDisplayBoundsChanged(const gfx::Display& display) { +} + +void ShelfWindowWatcher::OnDisplayAdded(const gfx::Display& new_display) { + // Add a new RootWindow and its ActivationClient to observed list. + aura::Window* root_window = Shell::GetInstance()->display_controller()-> + GetRootWindowForDisplayId(new_display.id()); + + // When the primary root window's display get removed, the existing root + // window is taken over by the new display and the observer is already set. + if (!observed_root_windows_.IsObserving(root_window)) + OnRootWindowAdded(root_window); +} + +void ShelfWindowWatcher::OnDisplayRemoved(const gfx::Display& old_display) { + // When this is called, RootWindow of |old_display| is already removed. + // Instead, we remove an observer from RootWindow and ActivationClient in the + // OnRootWindowDestroyed(). + // Do nothing here. +} + +} // namespace internal +} // namespace ash diff --git a/ash/shelf/shelf_window_watcher.h b/ash/shelf/shelf_window_watcher.h new file mode 100644 index 0000000000..d63c6582ad --- /dev/null +++ b/ash/shelf/shelf_window_watcher.h @@ -0,0 +1,114 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SHELF_SHELF_WINDOW_WATCHER_H_ +#define ASH_SHELF_SHELF_WINDOW_WATCHER_H_ + +#include "ash/shelf/scoped_observer_with_duplicated_sources.h" +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/scoped_observer.h" +#include "ui/aura/client/activation_change_observer.h" +#include "ui/aura/window_observer.h" +#include "ui/gfx/display_observer.h" + +namespace aura { + +class Window; + +namespace client { +class ActivationClient; +} + +} // namespace aura + +namespace ash { + +class ShelfModel; + +namespace internal { + +// ShelfWindowWatcher creates and handles a LauncherItem for windows that have +// a LauncherItemDetails property in the default container. +class ShelfWindowWatcher : public aura::client::ActivationChangeObserver, + public aura::WindowObserver, + public gfx::DisplayObserver { + public: + ShelfWindowWatcher(ShelfModel* model); + virtual ~ShelfWindowWatcher(); + + private: + class RootWindowObserver : public aura::WindowObserver { + public: + explicit RootWindowObserver(ShelfWindowWatcher* window_watcher); + virtual ~RootWindowObserver(); + + private: + // aura::WindowObserver overrides: + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + + // Owned by Shell. + ShelfWindowWatcher* window_watcher_; + + DISALLOW_COPY_AND_ASSIGN(RootWindowObserver); + }; + + // Creates a LauncherItem for |window| that has LauncherItemDetails. + void AddLauncherItem(aura::Window* window); + + // Removes a LauncherItem for |window|. + void RemoveLauncherItem(aura::Window* window); + + // Adds observer to default container and ActivationClient of |root_window|. + void OnRootWindowAdded(aura::Window* root_window); + + // Removes observer from ActivationClient of |root_window|. + void OnRootWindowRemoved(aura::Window* root_window); + + // Updates the status of LauncherItem for |window|. + void UpdateLauncherItemStatus(aura::Window* window, bool is_active); + + // Returns the index of LauncherItem associated with |window|. + int GetLauncherItemIndexForWindow(aura::Window* window) const; + + // aura::client::ActivationChangeObserver overrides: + virtual void OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) OVERRIDE; + + // aura::WindowObserver overrides: + virtual void OnWindowAdded(aura::Window* window) OVERRIDE; + virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE; + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + virtual void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) OVERRIDE; + + // gfx::DisplayObserver overrides: + virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE; + virtual void OnDisplayAdded(const gfx::Display& display) OVERRIDE; + virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE; + + // Owned by Shell. + ShelfModel* model_; + + RootWindowObserver root_window_observer_; + + // Holds all observed windows. + ScopedObserver<aura::Window, aura::WindowObserver> observed_windows_; + + // Holds all observed root windows. + ScopedObserver<aura::Window, aura::WindowObserver> observed_root_windows_; + + // Holds all observed activation clients. + ScopedObserverWithDuplicatedSources<aura::client::ActivationClient, + aura::client::ActivationChangeObserver> observed_activation_clients_; + + DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcher); +}; + +} // namespace internal +} // namespace ash + +#endif // ASH_SHELF_SHELF_WINDOW_WATCHER_H_ diff --git a/ash/shelf/shelf_window_watcher_unittest.cc b/ash/shelf/shelf_window_watcher_unittest.cc new file mode 100644 index 0000000000..1217aae2e5 --- /dev/null +++ b/ash/shelf/shelf_window_watcher_unittest.cc @@ -0,0 +1,175 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/shelf/shelf_window_watcher.h" + +#include "ash/launcher/launcher_types.h" +#include "ash/shelf/shelf_model.h" +#include "ash/shelf/shelf_util.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/test/shell_test_api.h" +#include "ash/wm/window_state.h" +#include "ash/wm/window_util.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/window.h" + +namespace ash { +namespace internal { + +class ShelfWindowWatcherTest : public test::AshTestBase { + public: + ShelfWindowWatcherTest() : model_(NULL) {} + virtual ~ShelfWindowWatcherTest() {} + + virtual void SetUp() OVERRIDE { + test::AshTestBase::SetUp(); + model_ = test::ShellTestApi(Shell::GetInstance()).shelf_model(); + } + + virtual void TearDown() OVERRIDE { + model_ = NULL; + test::AshTestBase::TearDown(); + } + + ash::LauncherID CreateLauncherItem(aura::Window* window) { + LauncherID id = model_->next_id(); + ash::LauncherItemDetails item_details; + item_details.type = TYPE_PLATFORM_APP; + SetLauncherItemDetailsForWindow(window, item_details); + return id; + } + + void UpdateLauncherItem(aura::Window* window) { + } + + protected: + ShelfModel* model_; + + private: + DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcherTest); +}; + +TEST_F(ShelfWindowWatcherTest, CreateAndRemoveLauncherItem) { + // ShelfModel only has an APP_LIST item. + EXPECT_EQ(1, model_->item_count()); + + scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0)); + scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0)); + + // Create a LauncherItem for w1. + LauncherID id_w1 = CreateLauncherItem(w1.get()); + EXPECT_EQ(2, model_->item_count()); + + int index_w1 = model_->ItemIndexByID(id_w1); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status); + + // Create a LauncherItem for w2. + LauncherID id_w2 = CreateLauncherItem(w2.get()); + EXPECT_EQ(3, model_->item_count()); + + int index_w2 = model_->ItemIndexByID(id_w2); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status); + + // LauncherItem is removed when assoicated window is destroyed. + ClearLauncherItemDetailsForWindow(w1.get()); + EXPECT_EQ(2, model_->item_count()); + ClearLauncherItemDetailsForWindow(w2.get()); + EXPECT_EQ(1, model_->item_count()); + // Clears twice doesn't do anything. + ClearLauncherItemDetailsForWindow(w2.get()); + EXPECT_EQ(1, model_->item_count()); + +} + +TEST_F(ShelfWindowWatcherTest, ActivateWindow) { + // ShelfModel only have APP_LIST item. + EXPECT_EQ(1, model_->item_count()); + scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0)); + scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0)); + + // Create a LauncherItem for w1. + LauncherID id_w1 = CreateLauncherItem(w1.get()); + EXPECT_EQ(2, model_->item_count()); + int index_w1 = model_->ItemIndexByID(id_w1); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status); + + // Create a LauncherItem for w2. + LauncherID id_w2 = CreateLauncherItem(w2.get()); + EXPECT_EQ(3, model_->item_count()); + int index_w2 = model_->ItemIndexByID(id_w2); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status); + + // LauncherItem for w1 is active when w1 is activated. + wm::ActivateWindow(w1.get()); + EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w1].status); + + // LauncherItem for w2 is active state when w2 is activated. + wm::ActivateWindow(w2.get()); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status); + EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w2].status); +} + +TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) { + // ShelfModel only has an APP_LIST item. + EXPECT_EQ(1, model_->item_count()); + + scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); + + // Create a LauncherItem for |window|. + LauncherID id = CreateLauncherItem(window.get()); + EXPECT_EQ(2, model_->item_count()); + + int index = model_->ItemIndexByID(id); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status); + + // Update LauncherItem for |window|. + LauncherItemDetails details; + details.type = TYPE_PLATFORM_APP; + + SetLauncherItemDetailsForWindow(window.get(), details); + // No new item is created after updating a launcher item. + EXPECT_EQ(2, model_->item_count()); + // index and id are not changed after updating a launcher item. + EXPECT_EQ(index, model_->ItemIndexByID(id)); + EXPECT_EQ(id, model_->items()[index].id); +} + +TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) { + // ShelfModel only has an APP_LIST item. + EXPECT_EQ(1, model_->item_count()); + + scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); + wm::WindowState* window_state = wm::GetWindowState(window.get()); + + // Create a LauncherItem for |window|. + LauncherID id = CreateLauncherItem(window.get()); + EXPECT_EQ(2, model_->item_count()); + + int index = model_->ItemIndexByID(id); + EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status); + + // Maximize window |window|. + EXPECT_FALSE(window_state->IsMaximized()); + window_state->Maximize(); + EXPECT_TRUE(window_state->IsMaximized()); + // No new item is created after maximizing a window |window|. + EXPECT_EQ(2, model_->item_count()); + // index and id are not changed after maximizing a window |window|. + EXPECT_EQ(index, model_->ItemIndexByID(id)); + EXPECT_EQ(id, model_->items()[index].id); + + // Restore window |window|. + window_state->Restore(); + EXPECT_FALSE(window_state->IsMaximized()); + // No new item is created after restoring a window |window|. + EXPECT_EQ(2, model_->item_count()); + // index and id are not changed after maximizing a window |window|. + EXPECT_EQ(index, model_->ItemIndexByID(id)); + EXPECT_EQ(id, model_->items()[index].id); +} + +} // namespace internal +} // namespace ash diff --git a/ash/shell.cc b/ash/shell.cc index d5428cff55..4cd9fd9932 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -30,9 +30,6 @@ #include "ash/high_contrast/high_contrast_controller.h" #include "ash/host/root_window_host_factory.h" #include "ash/keyboard_uma_event_filter.h" -#include "ash/launcher/launcher_delegate.h" -#include "ash/launcher/launcher_item_delegate.h" -#include "ash/launcher/launcher_item_delegate_manager.h" #include "ash/magnifier/magnification_controller.h" #include "ash/magnifier/partial_magnification_controller.h" #include "ash/media_delegate.h" @@ -41,9 +38,13 @@ #include "ash/screen_ash.h" #include "ash/session_state_delegate.h" #include "ash/shelf/app_list_shelf_item_delegate.h" +#include "ash/shelf/shelf_delegate.h" +#include "ash/shelf/shelf_item_delegate.h" +#include "ash/shelf/shelf_item_delegate_manager.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_widget.h" +#include "ash/shelf/shelf_window_watcher.h" #include "ash/shell_delegate.h" #include "ash/shell_factory.h" #include "ash/shell_window_ids.h" @@ -91,6 +92,7 @@ #include "ui/base/ui_base_switches.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator.h" +#include "ui/events/event_target_iterator.h" #include "ui/gfx/display.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/screen.h" @@ -496,29 +498,29 @@ SystemTray* Shell::GetPrimarySystemTray() { return GetPrimaryRootWindowController()->GetSystemTray(); } -LauncherDelegate* Shell::GetLauncherDelegate() { - if (!launcher_delegate_) { +ShelfDelegate* Shell::GetShelfDelegate() { + if (!shelf_delegate_) { shelf_model_.reset(new ShelfModel); - // Creates LauncherItemDelegateManager before LauncherDelegate. - launcher_item_delegate_manager_.reset( - new LauncherItemDelegateManager(shelf_model_.get())); + // Creates ShelfItemDelegateManager before ShelfDelegate. + shelf_item_delegate_manager_.reset( + new ShelfItemDelegateManager(shelf_model_.get())); - launcher_delegate_.reset( - delegate_->CreateLauncherDelegate(shelf_model_.get())); - scoped_ptr<LauncherItemDelegate> controller( + shelf_delegate_.reset(delegate_->CreateShelfDelegate(shelf_model_.get())); + scoped_ptr<ShelfItemDelegate> controller( new internal::AppListShelfItemDelegate); // Finding the shelf model's location of the app list and setting its - // LauncherItemDelegate. + // ShelfItemDelegate. int app_list_index = shelf_model_->GetItemIndexForType(TYPE_APP_LIST); DCHECK_GE(app_list_index, 0); LauncherID app_list_id = shelf_model_->items()[app_list_index].id; DCHECK(app_list_id); - launcher_item_delegate_manager_->SetLauncherItemDelegate( - app_list_id, - controller.Pass()); + shelf_item_delegate_manager_->SetShelfItemDelegate(app_list_id, + controller.Pass()); + shelf_window_watcher_.reset( + new internal::ShelfWindowWatcher(shelf_model_.get())); } - return launcher_delegate_.get(); + return shelf_delegate_.get(); } void Shell::SetTouchHudProjectionEnabled(bool enabled) { @@ -629,42 +631,57 @@ Shell::~Shell() { // Drag-and-drop must be canceled prior to close all windows. drag_drop_controller_.reset(); + // Controllers who have WindowObserver added must be deleted + // before |display_controller_| is deleted. + +#if defined(OS_CHROMEOS) + // VideoActivityNotifier must be deleted before |video_detector_| is + // deleted because it's observing video activity through + // VideoDetectorObserver interface. + video_activity_notifier_.reset(); +#endif // defined(OS_CHROMEOS) + video_detector_.reset(); + + shadow_controller_.reset(); + resize_shadow_controller_.reset(); + + window_cycle_controller_.reset(); + mru_window_tracker_.reset(); + + // |shelf_window_watcher_| has a weak pointer to |shelf_Model_| + // and has window observers. + shelf_window_watcher_.reset(); + // Destroy all child windows including widgets. display_controller_->CloseChildWindows(); display_controller_->CloseNonDesktopDisplay(); + // Chrome implementation of shelf delegate depends on FocusClient, + // so must be deleted before |focus_client_|. + shelf_delegate_.reset(); + focus_client_.reset(); + // Destroy SystemTrayNotifier after destroying SystemTray as TrayItems // needs to remove observers from it. system_tray_notifier_.reset(); -#if defined(OS_CHROMEOS) - // Destroy VideoActivityNotifier before destroying VideoDetector. - video_activity_notifier_.reset(); -#endif // defined(OS_CHROMEOS) - // These need a valid Shell instance to clean up properly, so explicitly // delete them before invalidating the instance. // Alphabetical. TODO(oshima): sort. magnification_controller_.reset(); partial_magnification_controller_.reset(); - resize_shadow_controller_.reset(); - shadow_controller_.reset(); tooltip_controller_.reset(); event_client_.reset(); - window_cycle_controller_.reset(); nested_dispatcher_controller_.reset(); user_action_client_.reset(); visibility_controller_.reset(); - launcher_delegate_.reset(); - // |launcher_item_delegate_manager_| observes |shelf_model_|. It must be + // |shelf_item_delegate_manager_| observes |shelf_model_|. It must be // destroyed before |shelf_model_| is destroyed. - launcher_item_delegate_manager_.reset(); + shelf_item_delegate_manager_.reset(); shelf_model_.reset(); - video_detector_.reset(); power_button_controller_.reset(); lock_state_controller_.reset(); - mru_window_tracker_.reset(); resolution_notification_controller_.reset(); desktop_background_controller_.reset(); @@ -996,6 +1013,15 @@ ui::EventTarget* Shell::GetParentTarget() { return aura::Env::GetInstance(); } +scoped_ptr<ui::EventTargetIterator> Shell::GetChildIterator() const { + return scoped_ptr<ui::EventTargetIterator>(); +} + +ui::EventTargeter* Shell::GetEventTargeter() { + NOTREACHED(); + return NULL; +} + void Shell::OnEvent(ui::Event* event) { } diff --git a/ash/shell.h b/ash/shell.h index 1c4e779ac4..5c49f6b8bb 100644 --- a/ash/shell.h +++ b/ash/shell.h @@ -85,9 +85,7 @@ class DisplayController; class FirstRunHelper; class HighContrastController; class Launcher; -class LauncherDelegate; -class LauncherItemDelegate; -class LauncherItemDelegateManager; +class ShelfDelegate; class LockStateController; class MagnificationController; class MediaDelegate; @@ -99,6 +97,7 @@ class PowerButtonController; class RootWindowHostFactory; class ScreenAsh; class SessionStateDelegate; +class ShelfItemDelegateManager; class ShelfModel; class ShellDelegate; class ShellObserver; @@ -137,6 +136,7 @@ class ResolutionNotificationController; class RootWindowController; class ScopedTargetRootWindow; class ScreenPositionController; +class ShelfWindowWatcher; class SlowAnimationEventFilter; class StatusAreaWidget; class SystemGestureEventFilter; @@ -408,8 +408,8 @@ class ASH_EXPORT Shell return activation_client_; } - LauncherItemDelegateManager* launcher_item_delegate_manager() { - return launcher_item_delegate_manager_.get(); + ShelfItemDelegateManager* shelf_item_delegate_manager() { + return shelf_item_delegate_manager_.get(); } ScreenAsh* screen() { return screen_; } @@ -512,7 +512,7 @@ class ASH_EXPORT Shell } // Returns the launcher delegate, creating if necesary. - LauncherDelegate* GetLauncherDelegate(); + ShelfDelegate* GetShelfDelegate(); void SetTouchHudProjectionEnabled(bool enabled); @@ -559,6 +559,8 @@ class ASH_EXPORT Shell // Overridden from ui::EventTarget: virtual bool CanAcceptEvent(const ui::Event& event) OVERRIDE; virtual EventTarget* GetParentTarget() OVERRIDE; + virtual scoped_ptr<ui::EventTargetIterator> GetChildIterator() const OVERRIDE; + virtual ui::EventTargeter* GetEventTargeter() OVERRIDE; virtual void OnEvent(ui::Event* event) OVERRIDE; // Overridden from aura::client::ActivationChangeObserver: @@ -598,8 +600,9 @@ class ASH_EXPORT Shell scoped_ptr<AccessibilityDelegate> accessibility_delegate_; scoped_ptr<NewWindowDelegate> new_window_delegate_; scoped_ptr<MediaDelegate> media_delegate_; - scoped_ptr<LauncherDelegate> launcher_delegate_; - scoped_ptr<LauncherItemDelegateManager> launcher_item_delegate_manager_; + scoped_ptr<ShelfDelegate> shelf_delegate_; + scoped_ptr<ShelfItemDelegateManager> shelf_item_delegate_manager_; + scoped_ptr<internal::ShelfWindowWatcher> shelf_window_watcher_; scoped_ptr<ShelfModel> shelf_model_; scoped_ptr<WindowPositioner> window_positioner_; diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc index 07f888eb7b..1359826eeb 100644 --- a/ash/shell/app_list.cc +++ b/ash/shell/app_list.cc @@ -23,6 +23,7 @@ #include "ui/app_list/app_list_view_delegate.h" #include "ui/app_list/search_box_model.h" #include "ui/app_list/search_result.h" +#include "ui/app_list/speech_ui_model.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" #include "ui/gfx/image/image_skia.h" @@ -251,6 +252,10 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate { return NULL; } + virtual app_list::SpeechUIModel* GetSpeechUI() OVERRIDE { + return &speech_ui_; + } + virtual void GetShortcutPathForApp( const std::string& app_id, const base::Callback<void(const base::FilePath&)>& callback) OVERRIDE { @@ -338,6 +343,7 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate { } scoped_ptr<app_list::AppListModel> model_; + app_list::SpeechUIModel speech_ui_; Users users_; DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegate); diff --git a/ash/shell/launcher_delegate_impl.cc b/ash/shell/launcher_delegate_impl.cc deleted file mode 100644 index 9eba738f5a..0000000000 --- a/ash/shell/launcher_delegate_impl.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/shell/launcher_delegate_impl.h" - -#include "ash/shell.h" -#include "ash/shell/toplevel_window.h" -#include "ash/shell/window_watcher.h" -#include "ash/wm/window_util.h" -#include "base/strings/string_util.h" -#include "grit/ash_resources.h" - -namespace ash { -namespace shell { - -LauncherDelegateImpl::LauncherDelegateImpl(WindowWatcher* watcher) - : watcher_(watcher) { -} - -LauncherDelegateImpl::~LauncherDelegateImpl() { -} - -void LauncherDelegateImpl::OnLauncherCreated(Launcher* launcher) { -} - -void LauncherDelegateImpl::OnLauncherDestroyed(Launcher* launcher) { -} - -LauncherID LauncherDelegateImpl::GetLauncherIDForAppID( - const std::string& app_id) { - return 0; -} - -const std::string& LauncherDelegateImpl::GetAppIDForLauncherID(LauncherID id) { - return EmptyString(); -} - -void LauncherDelegateImpl::PinAppWithID(const std::string& app_id) { -} - -bool LauncherDelegateImpl::IsAppPinned(const std::string& app_id) { - return false; -} - -bool LauncherDelegateImpl::CanPin() const { - return false; -} - -void LauncherDelegateImpl::UnpinAppWithID(const std::string& app_id) { -} - -} // namespace shell -} // namespace ash diff --git a/ash/shell/shelf_delegate_impl.cc b/ash/shell/shelf_delegate_impl.cc new file mode 100644 index 0000000000..0112009c37 --- /dev/null +++ b/ash/shell/shelf_delegate_impl.cc @@ -0,0 +1,53 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/shell/shelf_delegate_impl.h" + +#include "ash/shell.h" +#include "ash/shell/toplevel_window.h" +#include "ash/shell/window_watcher.h" +#include "ash/wm/window_util.h" +#include "base/strings/string_util.h" +#include "grit/ash_resources.h" + +namespace ash { +namespace shell { + +ShelfDelegateImpl::ShelfDelegateImpl(WindowWatcher* watcher) + : watcher_(watcher) { +} + +ShelfDelegateImpl::~ShelfDelegateImpl() { +} + +void ShelfDelegateImpl::OnLauncherCreated(Launcher* launcher) { +} + +void ShelfDelegateImpl::OnLauncherDestroyed(Launcher* launcher) { +} + +LauncherID ShelfDelegateImpl::GetLauncherIDForAppID(const std::string& app_id) { + return 0; +} + +const std::string& ShelfDelegateImpl::GetAppIDForLauncherID(LauncherID id) { + return base::EmptyString(); +} + +void ShelfDelegateImpl::PinAppWithID(const std::string& app_id) { +} + +bool ShelfDelegateImpl::IsAppPinned(const std::string& app_id) { + return false; +} + +bool ShelfDelegateImpl::CanPin() const { + return false; +} + +void ShelfDelegateImpl::UnpinAppWithID(const std::string& app_id) { +} + +} // namespace shell +} // namespace ash diff --git a/ash/shell/launcher_delegate_impl.h b/ash/shell/shelf_delegate_impl.h index c7b54ed2f5..ed6dc6d7d3 100644 --- a/ash/shell/launcher_delegate_impl.h +++ b/ash/shell/shelf_delegate_impl.h @@ -1,11 +1,11 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_ -#define ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_ +#ifndef ASH_SHELL_SHELF_DELEGATE_IMPL_H_ +#define ASH_SHELL_SHELF_DELEGATE_IMPL_H_ -#include "ash/launcher/launcher_delegate.h" +#include "ash/shelf/shelf_delegate.h" #include "base/compiler_specific.h" namespace aura { @@ -17,14 +17,14 @@ namespace shell { class WindowWatcher; -class LauncherDelegateImpl : public ash::LauncherDelegate { +class ShelfDelegateImpl : public ShelfDelegate { public: - explicit LauncherDelegateImpl(WindowWatcher* watcher); - virtual ~LauncherDelegateImpl(); + explicit ShelfDelegateImpl(WindowWatcher* watcher); + virtual ~ShelfDelegateImpl(); void set_watcher(WindowWatcher* watcher) { watcher_ = watcher; } - // LauncherDelegate overrides: + // ShelfDelegate overrides: virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE; virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE; virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE; @@ -38,10 +38,10 @@ class LauncherDelegateImpl : public ash::LauncherDelegate { // Used to update Launcher. Owned by main. WindowWatcher* watcher_; - DISALLOW_COPY_AND_ASSIGN(LauncherDelegateImpl); + DISALLOW_COPY_AND_ASSIGN(ShelfDelegateImpl); }; } // namespace shell } // namespace ash -#endif // ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_ +#endif // ASH_SHELL_SHELF_DELEGATE_IMPL_H_ diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc index acc8e3d0df..5dfe7b2a42 100644 --- a/ash/shell/shell_delegate_impl.cc +++ b/ash/shell/shell_delegate_impl.cc @@ -16,7 +16,7 @@ #include "ash/shell/context_menu.h" #include "ash/shell/example_factory.h" #include "ash/shell/keyboard_controller_proxy_stub.h" -#include "ash/shell/launcher_delegate_impl.h" +#include "ash/shell/shelf_delegate_impl.h" #include "ash/shell/toplevel_window.h" #include "ash/shell_window_ids.h" #include "ash/system/tray/default_system_tray_delegate.h" @@ -69,7 +69,7 @@ class MediaDelegateImpl : public MediaDelegate { ShellDelegateImpl::ShellDelegateImpl() : watcher_(NULL), - launcher_delegate_(NULL), + shelf_delegate_(NULL), browser_context_(NULL) { } @@ -78,8 +78,8 @@ ShellDelegateImpl::~ShellDelegateImpl() { void ShellDelegateImpl::SetWatcher(WindowWatcher* watcher) { watcher_ = watcher; - if (launcher_delegate_) - launcher_delegate_->set_watcher(watcher); + if (shelf_delegate_) + shelf_delegate_->set_watcher(watcher); } bool ShellDelegateImpl::IsFirstRunAfterBoot() const { @@ -121,9 +121,9 @@ app_list::AppListViewDelegate* ShellDelegateImpl::CreateAppListViewDelegate() { return ash::shell::CreateAppListViewDelegate(); } -LauncherDelegate* ShellDelegateImpl::CreateLauncherDelegate(ShelfModel* model) { - launcher_delegate_ = new LauncherDelegateImpl(watcher_); - return launcher_delegate_; +ShelfDelegate* ShellDelegateImpl::CreateShelfDelegate(ShelfModel* model) { + shelf_delegate_ = new ShelfDelegateImpl(watcher_); + return shelf_delegate_; } ash::SystemTrayDelegate* ShellDelegateImpl::CreateSystemTrayDelegate() { diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h index 500bef2dc4..ecc7ee6eb5 100644 --- a/ash/shell/shell_delegate_impl.h +++ b/ash/shell/shell_delegate_impl.h @@ -21,7 +21,7 @@ class KeyboardControllerProxy; namespace ash { namespace shell { -class LauncherDelegateImpl; +class ShelfDelegateImpl; class WindowWatcher; class ShellDelegateImpl : public ash::ShellDelegate { @@ -45,7 +45,7 @@ class ShellDelegateImpl : public ash::ShellDelegate { CreateKeyboardControllerProxy() OVERRIDE; virtual content::BrowserContext* GetCurrentBrowserContext() OVERRIDE; virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE; - virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) OVERRIDE; + virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) OVERRIDE; virtual ash::SystemTrayDelegate* CreateSystemTrayDelegate() OVERRIDE; virtual ash::UserWallpaperDelegate* CreateUserWallpaperDelegate() OVERRIDE; virtual ash::CapsLockDelegate* CreateCapsLockDelegate() OVERRIDE; @@ -64,7 +64,7 @@ class ShellDelegateImpl : public ash::ShellDelegate { // Used to update Launcher. Owned by main. WindowWatcher* watcher_; - LauncherDelegateImpl* launcher_delegate_; + ShelfDelegateImpl* shelf_delegate_; content::BrowserContext* browser_context_; DISALLOW_COPY_AND_ASSIGN(ShellDelegateImpl); diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc index e701e25299..f90c4f3d1c 100644 --- a/ash/shell/window_type_launcher.cc +++ b/ash/shell/window_type_launcher.cc @@ -344,7 +344,8 @@ void WindowTypeLauncher::ButtonPressed(views::Button* sender, ASCIIToUTF16("Notification message body."), gfx::Image(), ASCIIToUTF16("www.testshell.org"), - message_center::NotifierId(), + message_center::NotifierId( + message_center::NotifierId::APPLICATION, "test-id"), message_center::RichNotificationData(), NULL /* delegate */)); diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc index 761a9cc0c9..988985a5e4 100644 --- a/ash/shell/window_watcher.cc +++ b/ash/shell/window_watcher.cc @@ -6,12 +6,12 @@ #include "ash/display/display_controller.h" #include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_item_delegate_manager.h" +#include "ash/shelf/shelf_item_delegate_manager.h" #include "ash/shelf/shelf_model.h" #include "ash/shelf/shelf_util.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" -#include "ash/shell/window_watcher_launcher_item_delegate.h" +#include "ash/shell/window_watcher_shelf_item_delegate.h" #include "ash/shell_window_ids.h" #include "ui/aura/root_window.h" #include "ui/aura/window.h" @@ -117,11 +117,11 @@ void WindowWatcher::OnWindowAdded(aura::Window* new_window) { model->Add(item); - ash::LauncherItemDelegateManager* manager = - ash::Shell::GetInstance()->launcher_item_delegate_manager(); - scoped_ptr<LauncherItemDelegate> delegate( - new WindowWatcherLauncherItemDelegate(id, this)); - manager->SetLauncherItemDelegate(id, delegate.Pass()); + ShelfItemDelegateManager* manager = + Shell::GetInstance()->shelf_item_delegate_manager(); + scoped_ptr<ShelfItemDelegate> delegate( + new WindowWatcherShelfItemDelegate(id, this)); + manager->SetShelfItemDelegate(id, delegate.Pass()); SetLauncherIDForWindow(id, new_window); } diff --git a/ash/shell/window_watcher_launcher_item_delegate.cc b/ash/shell/window_watcher_launcher_item_delegate.cc deleted file mode 100644 index 76e39fd3f9..0000000000 --- a/ash/shell/window_watcher_launcher_item_delegate.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/shell/window_watcher_launcher_item_delegate.h" - -#include "ash/shell/window_watcher.h" -#include "ash/wm/window_util.h" -#include "ui/aura/window.h" - -namespace ash { -namespace shell { - -WindowWatcherLauncherItemDelegate::WindowWatcherLauncherItemDelegate( - ash::LauncherID id, - ash::shell::WindowWatcher* watcher) - : id_(id), - watcher_(watcher) { - DCHECK_GT(id_, 0); - DCHECK(watcher_); -} - -WindowWatcherLauncherItemDelegate::~WindowWatcherLauncherItemDelegate() { -} - -bool WindowWatcherLauncherItemDelegate::ItemSelected(const ui::Event& event) { - aura::Window* window = watcher_->GetWindowByID(id_); - if (window->type() == aura::client::WINDOW_TYPE_PANEL) - ash::wm::MoveWindowToEventRoot(window, event); - window->Show(); - ash::wm::ActivateWindow(window); - return false; -} - -base::string16 WindowWatcherLauncherItemDelegate::GetTitle() { - return watcher_->GetWindowByID(id_)->title(); -} - -ui::MenuModel* WindowWatcherLauncherItemDelegate::CreateContextMenu( - aura::Window* root_window) { - return NULL; -} - -ash::LauncherMenuModel* -WindowWatcherLauncherItemDelegate::CreateApplicationMenu( - int event_flags) { - return NULL; -} - -bool WindowWatcherLauncherItemDelegate::IsDraggable() { - return true; -} - -bool WindowWatcherLauncherItemDelegate::ShouldShowTooltip() { - return true; -} - -} // namespace shell -} // namespace ash diff --git a/ash/shell/window_watcher_launcher_item_delegate.h b/ash/shell/window_watcher_launcher_item_delegate.h deleted file mode 100644 index 280f3b435e..0000000000 --- a/ash/shell/window_watcher_launcher_item_delegate.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_ -#define ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_ - -#include "ash/launcher/launcher_item_delegate.h" -#include "base/basictypes.h" -#include "base/compiler_specific.h" - -namespace ash { -namespace shell { - -class WindowWatcher; - -// LauncherItemDelegate implementation used by WindowWatcher. -class WindowWatcherLauncherItemDelegate : public ash::LauncherItemDelegate { - public: - WindowWatcherLauncherItemDelegate(ash::LauncherID id, - ash::shell::WindowWatcher* watcher); - virtual ~WindowWatcherLauncherItemDelegate(); - - // ash::LauncherItemDelegate overrides: - virtual bool ItemSelected(const ui::Event& event) OVERRIDE; - virtual base::string16 GetTitle() OVERRIDE; - virtual ui::MenuModel* CreateContextMenu( - aura::Window* root_window) OVERRIDE; - virtual ash::LauncherMenuModel* CreateApplicationMenu( - int event_flags) OVERRIDE; - virtual bool IsDraggable() OVERRIDE; - virtual bool ShouldShowTooltip() OVERRIDE; - - private: - ash::LauncherID id_; - ash::shell::WindowWatcher* watcher_; - - DISALLOW_COPY_AND_ASSIGN(WindowWatcherLauncherItemDelegate); -}; - -} // namespace shell -} // namespace ash - -#endif // ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_ diff --git a/ash/shell/window_watcher_shelf_item_delegate.cc b/ash/shell/window_watcher_shelf_item_delegate.cc new file mode 100644 index 0000000000..ecb4308c94 --- /dev/null +++ b/ash/shell/window_watcher_shelf_item_delegate.cc @@ -0,0 +1,57 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/shell/window_watcher_shelf_item_delegate.h" + +#include "ash/shell/window_watcher.h" +#include "ash/wm/window_util.h" +#include "ui/aura/window.h" + +namespace ash { +namespace shell { + +WindowWatcherShelfItemDelegate::WindowWatcherShelfItemDelegate( + LauncherID id, + WindowWatcher* watcher) + : id_(id), watcher_(watcher) { + DCHECK_GT(id_, 0); + DCHECK(watcher_); +} + +WindowWatcherShelfItemDelegate::~WindowWatcherShelfItemDelegate() { +} + +bool WindowWatcherShelfItemDelegate::ItemSelected(const ui::Event& event) { + aura::Window* window = watcher_->GetWindowByID(id_); + if (window->type() == aura::client::WINDOW_TYPE_PANEL) + wm::MoveWindowToEventRoot(window, event); + window->Show(); + wm::ActivateWindow(window); + return false; +} + +base::string16 WindowWatcherShelfItemDelegate::GetTitle() { + return watcher_->GetWindowByID(id_)->title(); +} + +ui::MenuModel* WindowWatcherShelfItemDelegate::CreateContextMenu( + aura::Window* root_window) { + return NULL; +} + +ShelfMenuModel* WindowWatcherShelfItemDelegate::CreateApplicationMenu( + int event_flags) { + return NULL; +} + +bool WindowWatcherShelfItemDelegate::IsDraggable() { + return true; +} + +bool WindowWatcherShelfItemDelegate::ShouldShowTooltip() { + return true; +} + +} // namespace shell +} // namespace ash diff --git a/ash/shell/window_watcher_shelf_item_delegate.h b/ash/shell/window_watcher_shelf_item_delegate.h new file mode 100644 index 0000000000..cf8f6a7931 --- /dev/null +++ b/ash/shell/window_watcher_shelf_item_delegate.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_ +#define ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_ + +#include "ash/launcher/launcher_types.h" +#include "ash/shelf/shelf_item_delegate.h" +#include "base/basictypes.h" +#include "base/compiler_specific.h" + +namespace ash { +namespace shell { + +class WindowWatcher; + +// ShelfItemDelegate implementation used by WindowWatcher. +class WindowWatcherShelfItemDelegate : public ShelfItemDelegate { + public: + WindowWatcherShelfItemDelegate(LauncherID id, WindowWatcher* watcher); + virtual ~WindowWatcherShelfItemDelegate(); + + // ShelfItemDelegate: + virtual bool ItemSelected(const ui::Event& event) OVERRIDE; + virtual base::string16 GetTitle() OVERRIDE; + virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE; + virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE; + virtual bool IsDraggable() OVERRIDE; + virtual bool ShouldShowTooltip() OVERRIDE; + + private: + LauncherID id_; + WindowWatcher* watcher_; + + DISALLOW_COPY_AND_ASSIGN(WindowWatcherShelfItemDelegate); +}; + +} // namespace shell +} // namespace ash + +#endif // ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_ diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h index e84af7ef67..41c44060cd 100644 --- a/ash/shell_delegate.h +++ b/ash/shell_delegate.h @@ -44,15 +44,15 @@ namespace ash { class AccessibilityDelegate; class CapsLockDelegate; -class LauncherDelegate; -struct LauncherItem; class MediaDelegate; class NewWindowDelegate; class RootWindowHostFactory; class SessionStateDelegate; +class ShelfDelegate; class ShelfModel; class SystemTrayDelegate; class UserWallpaperDelegate; +struct LauncherItem; enum UserMetricsAction { UMA_ACCEL_KEYBOARD_BRIGHTNESS_DOWN_F6, @@ -68,7 +68,6 @@ enum UserMetricsAction { UMA_GESTURE_OVERVIEW, UMA_LAUNCHER_CLICK_ON_APP, UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON, - UMA_MINIMIZE_PER_KEY, UMA_MOUSE_DOWN, UMA_SHELF_ALIGNMENT_SET_BOTTOM, UMA_SHELF_ALIGNMENT_SET_LEFT, @@ -145,9 +144,9 @@ class ASH_EXPORT ShellDelegate { // the created delegate. virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() = 0; - // Creates a new LauncherDelegate. Shell takes ownership of the returned + // Creates a new ShelfDelegate. Shell takes ownership of the returned // value. - virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) = 0; + virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) = 0; // Creates a system-tray delegate. Shell takes ownership of the delegate. virtual SystemTrayDelegate* CreateSystemTrayDelegate() = 0; diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc index 5d75e945ab..e9b042d90d 100644 --- a/ash/system/bluetooth/tray_bluetooth.cc +++ b/ash/system/bluetooth/tray_bluetooth.cc @@ -353,7 +353,7 @@ class BluetoothDetailedView : public TrayDetailsView, ash::SystemTrayDelegate* delegate = ash::Shell::GetInstance()->system_tray_delegate(); if (sender == footer()->content()) { - owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING); + TransitionToDefaultView(); } else if (sender == manage_devices_) { delegate->ManageBluetoothDevices(); } else if (sender == enable_bluetooth_) { diff --git a/ash/system/chromeos/audio/tray_audio.cc b/ash/system/chromeos/audio/tray_audio.cc index da83aee885..5922f004ba 100644 --- a/ash/system/chromeos/audio/tray_audio.cc +++ b/ash/system/chromeos/audio/tray_audio.cc @@ -518,7 +518,7 @@ class AudioDetailedView : public TrayDetailsView, // Overridden from ViewClickListener. virtual void OnViewClicked(views::View* sender) OVERRIDE { if (sender == footer()->content()) { - owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING); + TransitionToDefaultView(); } else { AudioDeviceMap::iterator iter = device_map_.find(sender); if (iter == device_map_.end()) diff --git a/ash/system/chromeos/label_tray_view.cc b/ash/system/chromeos/label_tray_view.cc index 5af294efe0..0ae4604680 100644 --- a/ash/system/chromeos/label_tray_view.cc +++ b/ash/system/chromeos/label_tray_view.cc @@ -9,6 +9,7 @@ #include "ash/system/tray/view_click_listener.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/font.h" +#include "ui/views/border.h" #include "ui/views/controls/label.h" #include "ui/views/layout/fill_layout.h" diff --git a/ash/system/chromeos/managed/tray_locally_managed_user.cc b/ash/system/chromeos/managed/tray_locally_managed_user.cc index 9a8fd481d6..bd3e380b25 100644 --- a/ash/system/chromeos/managed/tray_locally_managed_user.cc +++ b/ash/system/chromeos/managed/tray_locally_managed_user.cc @@ -10,6 +10,7 @@ #include "ash/system/tray/system_tray_notifier.h" #include "ash/system/tray/tray_notification_view.h" #include "ash/system/user/login_status.h" +#include "base/callback.h" #include "base/logging.h" #include "grit/ash_resources.h" #include "ui/base/resource/resource_bundle.h" @@ -77,18 +78,14 @@ void TrayLocallyManagedUser::UpdateAfterLoginStatusChange( void TrayLocallyManagedUser::CreateOrUpdateNotification( const base::string16& new_message) { ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); - scoped_ptr<Notification> notification(new Notification( - message_center::NOTIFICATION_TYPE_SIMPLE, - kNotificationId, - string16() /* no title */, - new_message, - bundle.GetImageNamed(IDR_AURA_UBER_TRAY_MANAGED_USER), - base::string16() /* display_source */, - message_center::NotifierId( - system_notifier::NOTIFIER_LOCALLY_MANAGED_USER), - message_center::RichNotificationData(), - NULL /* no delegate */)); - notification->SetSystemPriority(); + scoped_ptr<Notification> notification( + message_center::Notification::CreateSystemNotification( + kNotificationId, + string16() /* no title */, + new_message, + bundle.GetImageNamed(IDR_AURA_UBER_TRAY_MANAGED_USER), + system_notifier::kNotifierLocallyManagedUser, + base::Closure() /* null callback */)); message_center::MessageCenter::Get()->AddNotification(notification.Pass()); } diff --git a/ash/system/chromeos/network/network_connect.cc b/ash/system/chromeos/network/network_connect.cc index 4013c77349..d8f524aea4 100644 --- a/ash/system/chromeos/network/network_connect.cc +++ b/ash/system/chromeos/network/network_connect.cc @@ -439,7 +439,7 @@ void ShowMobileSetup(const std::string& service_path) { UTF8ToUTF16(cellular->name())), ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED), - ash::system_notifier::NOTIFIER_NETWORK, + ash::system_notifier::kNotifierNetwork, base::Bind(&ash::network_connect::ShowNetworkSettings, service_path))); return; diff --git a/ash/system/chromeos/network/network_state_list_detailed_view.cc b/ash/system/chromeos/network/network_state_list_detailed_view.cc index ffd8ad5833..ff45d77b12 100644 --- a/ash/system/chromeos/network/network_state_list_detailed_view.cc +++ b/ash/system/chromeos/network/network_state_list_detailed_view.cc @@ -299,8 +299,7 @@ void NetworkStateListDetailedView::OnViewClicked(views::View* sender) { ResetInfoBubble(); if (sender == footer()->content()) { - RootWindowController::ForWindow(GetWidget()->GetNativeView())-> - GetSystemTray()->ShowDefaultView(BUBBLE_USE_EXISTING); + TransitionToDefaultView(); return; } diff --git a/ash/system/chromeos/network/network_state_notifier.cc b/ash/system/chromeos/network/network_state_notifier.cc index c8beb32a6a..ac69a8ab55 100644 --- a/ash/system/chromeos/network/network_state_notifier.cc +++ b/ash/system/chromeos/network/network_state_notifier.cc @@ -67,7 +67,7 @@ void ShowErrorNotification(const std::string& notification_id, title, message, icon, - ash::system_notifier::NOTIFIER_NETWORK_ERROR, + ash::system_notifier::kNotifierNetworkError, callback)); } @@ -184,7 +184,7 @@ void NetworkStateNotifier::UpdateCellularActivating( l10n_util::GetStringFUTF16(IDS_NETWORK_CELLULAR_ACTIVATED, UTF8ToUTF16((cellular->name()))), icon, - system_notifier::NOTIFIER_NETWORK, + system_notifier::kNotifierNetwork, base::Bind(&ash::network_connect::ShowNetworkSettings, cellular->path()))); } diff --git a/ash/system/chromeos/network/tray_sms.cc b/ash/system/chromeos/network/tray_sms.cc index 9adb5f9c91..7b00c276e7 100644 --- a/ash/system/chromeos/network/tray_sms.cc +++ b/ash/system/chromeos/network/tray_sms.cc @@ -234,7 +234,7 @@ class TraySms::SmsDetailedView : public TrayDetailsView, // Overridden from ViewClickListener. virtual void OnViewClicked(views::View* sender) OVERRIDE { if (sender == footer()->content()) - owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING); + TransitionToDefaultView(); } DISALLOW_COPY_AND_ASSIGN(SmsDetailedView); diff --git a/ash/system/chromeos/power/tray_power.cc b/ash/system/chromeos/power/tray_power.cc index 1eb004840c..1fdd6a95ed 100644 --- a/ash/system/chromeos/power/tray_power.cc +++ b/ash/system/chromeos/power/tray_power.cc @@ -199,7 +199,9 @@ bool TrayPower::MaybeShowUsbChargerNotification() { IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT), rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER), base::string16(), - message_center::NotifierId(system_notifier::NOTIFIER_POWER), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierPower), message_center::RichNotificationData(), NULL)); message_center_->AddNotification(notification.Pass()); diff --git a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc index 2998b3b08a..0bba2b2585 100644 --- a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc +++ b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc @@ -63,7 +63,9 @@ void ScreenCaptureTrayItem::CreateOrUpdateNotification() { base::string16() /* body is blank */, resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY), base::string16() /* display_source */, - message_center::NotifierId(system_notifier::NOTIFIER_SCREEN_CAPTURE), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierScreenCapture), data, new tray::ScreenNotificationDelegate(this))); notification->SetSystemPriority(); diff --git a/ash/system/chromeos/screen_security/screen_share_tray_item.cc b/ash/system/chromeos/screen_security/screen_share_tray_item.cc index bf70c44019..bceae5958c 100644 --- a/ash/system/chromeos/screen_security/screen_share_tray_item.cc +++ b/ash/system/chromeos/screen_security/screen_share_tray_item.cc @@ -73,7 +73,9 @@ void ScreenShareTrayItem::CreateOrUpdateNotification() { base::string16() /* body is blank */, resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY), base::string16() /* display_source */, - message_center::NotifierId(system_notifier::NOTIFIER_SCREEN_SHARE), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierScreenShare), data, new tray::ScreenNotificationDelegate(this))); notification->SetSystemPriority(); diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc index bf0b3dc76a..35627e1ae2 100644 --- a/ash/system/chromeos/tray_display.cc +++ b/ash/system/chromeos/tray_display.cc @@ -107,12 +107,21 @@ base::string16 GetAllDisplayInfo() { } void OpenSettings() { - user::LoginStatus login_status = - Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus(); - if (login_status == user::LOGGED_IN_USER || - login_status == user::LOGGED_IN_OWNER || - login_status == user::LOGGED_IN_GUEST) { - Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings(); + // switch is intentionally introduced without default, to cause an error when + // a new type of login status is introduced. + switch (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus()) { + case user::LOGGED_IN_NONE: + case user::LOGGED_IN_LOCKED: + return; + + case user::LOGGED_IN_USER: + case user::LOGGED_IN_OWNER: + case user::LOGGED_IN_GUEST: + case user::LOGGED_IN_RETAIL_MODE: + case user::LOGGED_IN_PUBLIC: + case user::LOGGED_IN_LOCALLY_MANAGED: + case user::LOGGED_IN_KIOSK_APP: + Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings(); } } @@ -264,7 +273,6 @@ class DisplayView : public internal::ActionableView { int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 - kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width(); label_->SizeToFit(label_max_width); - PreferredSizeChanged(); } views::ImageView* image_; @@ -373,7 +381,9 @@ void TrayDisplay::CreateOrUpdateNotification( additional_message, bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY), base::string16(), // display_source - message_center::NotifierId(system_notifier::NOTIFIER_DISPLAY), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierDisplay), message_center::RichNotificationData(), new message_center::HandleNotificationClickedDelegate( base::Bind(&OpenSettings)))); diff --git a/ash/system/date/date_view.cc b/ash/system/date/date_view.cc index 24428d1d87..ae4a546530 100644 --- a/ash/system/date/date_view.cc +++ b/ash/system/date/date_view.cc @@ -17,6 +17,7 @@ #include "third_party/icu/source/i18n/unicode/dtptngen.h" #include "third_party/icu/source/i18n/unicode/smpdtfmt.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/views/border.h" #include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/grid_layout.h" diff --git a/ash/system/drive/tray_drive.cc b/ash/system/drive/tray_drive.cc index 028aa29bb5..4305d5af0e 100644 --- a/ash/system/drive/tray_drive.cc +++ b/ash/system/drive/tray_drive.cc @@ -364,7 +364,7 @@ class DriveDetailedView : public TrayDetailsView, virtual void OnViewClicked(views::View* sender) OVERRIDE { SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); if (sender == footer()->content()) { - owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING); + TransitionToDefaultView(); } else if (sender == settings_) { delegate->ShowDriveSettings(); } diff --git a/ash/system/ime/tray_ime.cc b/ash/system/ime/tray_ime.cc index 87e254a1cf..d58b1ef398 100644 --- a/ash/system/ime/tray_ime.cc +++ b/ash/system/ime/tray_ime.cc @@ -147,7 +147,7 @@ class IMEDetailedView : public TrayDetailsView, virtual void OnViewClicked(views::View* sender) OVERRIDE { SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); if (sender == footer()->content()) { - owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING); + TransitionToDefaultView(); } else if (sender == settings_) { delegate->ShowIMESettings(); } else { @@ -231,7 +231,9 @@ void TrayIME::UpdateOrCreateNotification() { base::string16(), // message bundle.GetImageNamed(IDR_AURA_UBER_TRAY_IME), base::string16(), // display_source - message_center::NotifierId(system_notifier::NOTIFIER_INPUT_METHOD), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierInputMethod), message_center::RichNotificationData(), new message_center::HandleNotificationClickedDelegate( base::Bind(&TrayIME::PopupDetailedView, diff --git a/ash/system/locale/locale_notification_controller.cc b/ash/system/locale/locale_notification_controller.cc index 7d66d5b77f..419baafb40 100644 --- a/ash/system/locale/locale_notification_controller.cc +++ b/ash/system/locale/locale_notification_controller.cc @@ -117,7 +117,9 @@ void LocaleNotificationController::OnLocaleChanged( IDS_ASH_STATUS_TRAY_LOCALE_CHANGE_MESSAGE, from, to), bundle.GetImageNamed(IDR_AURA_UBER_TRAY_LOCALE), base::string16() /* display_source */, - message_center::NotifierId(system_notifier::NOTIFIER_LOCALE), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierLocale), optional, new LocaleNotificationDelegate(delegate))); message_center::MessageCenter::Get()->AddNotification(notification.Pass()); diff --git a/ash/system/session_length_limit/tray_session_length_limit.cc b/ash/system/session_length_limit/tray_session_length_limit.cc index fe8ff7ebc1..cdf69d5d60 100644 --- a/ash/system/session_length_limit/tray_session_length_limit.cc +++ b/ash/system/session_length_limit/tray_session_length_limit.cc @@ -103,7 +103,8 @@ void CreateOrUpdateNotification(const base::TimeDelta& remaining_time, bundle.GetImageNamed(IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER), base::string16() /* display_source */, message_center::NotifierId( - system_notifier::NOTIFIER_SESSION_LENGTH_TIMEOUT), + message_center::NotifierId::SYSTEM_COMPONENT, + system_notifier::kNotifierSessionLengthTimeout), data, NULL /* delegate */)); notification->SetSystemPriority(); diff --git a/ash/system/system_notifier.cc b/ash/system/system_notifier.cc index 554c0f945c..e4d65d2733 100644 --- a/ash/system/system_notifier.cc +++ b/ash/system/system_notifier.cc @@ -13,33 +13,67 @@ namespace { // See http://dev.chromium.org/chromium-os/chromiumos-design-docs/ // system-notifications for the reasoning. -const AshSystemComponentNotifierType kAlwaysShownNotifierIds[] = { - NOTIFIER_DISPLAY, - NOTIFIER_DISPLAY_ERROR, - NOTIFIER_POWER, +const char* kAlwaysShownNotifierIds[] = { + kNotifierDisplay, + kNotifierDisplayError, + kNotifierPower, + NULL }; -} // namespace - -std::string SystemComponentTypeToString(AshSystemComponentNotifierType type) { - if (type == NOTIFIER_SCREENSHOT) - return "screenshot"; - - // TODO(mukai): fill the names of other components. - NOTIMPLEMENTED(); - return std::string(); -} +const char* kAshSystemNotifiers[] = { + kNotifierDisplay, + kNotifierDisplayResolutionChange, + kNotifierDisplayError, + kNotifierInputMethod, + kNotifierLocale, + kNotifierLocallyManagedUser, + kNotifierMultiProfileFirstRun, + kNotifierNetwork, + kNotifierNetworkError, + kNotifierScreenshot, + kNotifierScreenCapture, + kNotifierScreenShare, + kNotifierSessionLengthTimeout, + kNotifierPower, + NULL +}; -bool ShouldAlwaysShowPopups(const message_center::NotifierId& notifier_id) { +bool MatchSystemNotifierId(const message_center::NotifierId& notifier_id, + const char* id_list[]) { if (notifier_id.type != message_center::NotifierId::SYSTEM_COMPONENT) return false; - for (size_t i = 0; i < arraysize(kAlwaysShownNotifierIds); ++i) { - if (notifier_id.system_component_type == kAlwaysShownNotifierIds[i]) + for (size_t i = 0; id_list[i] != NULL; ++i) { + if (notifier_id.id == id_list[i]) return true; } return false; } +} // namespace + +const char kNotifierDisplay[] = "ash.display"; +const char kNotifierDisplayResolutionChange[] = "ash.display.resolution-change"; +const char kNotifierDisplayError[] = "ash.display.error"; +const char kNotifierInputMethod[] = "ash.input-method"; +const char kNotifierLocale[] = "ash.locale"; +const char kNotifierLocallyManagedUser[] = "ash.locally-managed-user"; +const char kNotifierMultiProfileFirstRun[] = "ash.multi-profile.first-run"; +const char kNotifierNetwork[] = "ash.network"; +const char kNotifierNetworkError[] = "ash.network.error"; +const char kNotifierScreenshot[] = "ash.screenshot"; +const char kNotifierScreenCapture[] = "ash.screen-capture"; +const char kNotifierScreenShare[] = "ash.screen-share"; +const char kNotifierSessionLengthTimeout[] = "ash.session-length-timeout"; +const char kNotifierPower[] = "ash.power"; + +bool ShouldAlwaysShowPopups(const message_center::NotifierId& notifier_id) { + return MatchSystemNotifierId(notifier_id, kAlwaysShownNotifierIds); +} + +bool IsAshSystemNotifier(const message_center::NotifierId& notifier_id) { + return MatchSystemNotifierId(notifier_id, kAshSystemNotifiers); +} + } // namespace system_notifier } // namespace ash diff --git a/ash/system/system_notifier.h b/ash/system/system_notifier.h index 3d823c8851..53ac77db7d 100644 --- a/ash/system/system_notifier.h +++ b/ash/system/system_notifier.h @@ -13,27 +13,21 @@ namespace ash { namespace system_notifier { -enum AshSystemComponentNotifierType { - NOTIFIER_NO_SYSTEM_COMPONENT = -1, - - // Alphabetical order. - NOTIFIER_DISPLAY, - NOTIFIER_DISPLAY_RESOLUTION_CHANGE, - NOTIFIER_DISPLAY_ERROR, - NOTIFIER_INPUT_METHOD, - NOTIFIER_LOCALE, - NOTIFIER_LOCALLY_MANAGED_USER, - NOTIFIER_NETWORK, - NOTIFIER_NETWORK_ERROR, - NOTIFIER_SCREENSHOT, - NOTIFIER_SCREEN_CAPTURE, - NOTIFIER_SCREEN_SHARE, - NOTIFIER_SESSION_LENGTH_TIMEOUT, - NOTIFIER_POWER, -}; - -ASH_EXPORT std::string SystemComponentTypeToString( - AshSystemComponentNotifierType type); +// The list of ash system notifier IDs. Alphabetical order. +ASH_EXPORT extern const char kNotifierDisplay[]; +ASH_EXPORT extern const char kNotifierDisplayResolutionChange[]; +ASH_EXPORT extern const char kNotifierDisplayError[]; +ASH_EXPORT extern const char kNotifierInputMethod[]; +ASH_EXPORT extern const char kNotifierLocale[]; +ASH_EXPORT extern const char kNotifierLocallyManagedUser[]; +ASH_EXPORT extern const char kNotifierMultiProfileFirstRun[]; +ASH_EXPORT extern const char kNotifierNetwork[]; +ASH_EXPORT extern const char kNotifierNetworkError[]; +ASH_EXPORT extern const char kNotifierScreenshot[]; +ASH_EXPORT extern const char kNotifierScreenCapture[]; +ASH_EXPORT extern const char kNotifierScreenShare[]; +ASH_EXPORT extern const char kNotifierSessionLengthTimeout[]; +ASH_EXPORT extern const char kNotifierPower[]; // Returns true if notifications from |notifier_id| should always appear as // popups. "Always appear" means the popups should appear even in login screen, @@ -41,6 +35,10 @@ ASH_EXPORT std::string SystemComponentTypeToString( ASH_EXPORT bool ShouldAlwaysShowPopups( const message_center::NotifierId& notifier_id); +// Returns true if |notifier_id| is the system notifier from Ash. +ASH_EXPORT bool IsAshSystemNotifier( + const message_center::NotifierId& notifier_id); + } // namespace system_notifier } // namespace ash diff --git a/ash/system/tray/actionable_view.cc b/ash/system/tray/actionable_view.cc index 1fae3ba1e1..169b46da80 100644 --- a/ash/system/tray/actionable_view.cc +++ b/ash/system/tray/actionable_view.cc @@ -22,10 +22,14 @@ ActionableView::ActionableView() ActionableView::~ActionableView() { } -void ActionableView::DrawBorder(gfx::Canvas* canvas, const gfx::Rect& bounds) { - gfx::Rect rect = bounds; - rect.Inset(1, 1, 3, 3); - canvas->DrawRect(rect, kFocusBorderColor); +void ActionableView::OnPaintFocus(gfx::Canvas* canvas) { + gfx::Rect rect(GetFocusBounds()); + rect.Inset(1, 1, 3, 2); + canvas->DrawSolidFocusRect(rect, kFocusBorderColor); +} + +gfx::Rect ActionableView::GetFocusBounds() { + return GetLocalBounds(); } const char* ActionableView::GetClassName() const { @@ -59,9 +63,27 @@ void ActionableView::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } -void ActionableView::OnPaintFocusBorder(gfx::Canvas* canvas) { +void ActionableView::GetAccessibleState(ui::AccessibleViewState* state) { + state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; + state->name = accessible_name_; +} + +void ActionableView::OnPaint(gfx::Canvas* canvas) { + View::OnPaint(canvas); if (HasFocus()) - DrawBorder(canvas, GetLocalBounds()); + OnPaintFocus(canvas); +} + +void ActionableView::OnFocus() { + View::OnFocus(); + // We render differently when focused. + SchedulePaint(); +} + +void ActionableView::OnBlur() { + View::OnBlur(); + // We render differently when focused. + SchedulePaint(); } void ActionableView::OnGestureEvent(ui::GestureEvent* event) { @@ -69,10 +91,5 @@ void ActionableView::OnGestureEvent(ui::GestureEvent* event) { event->SetHandled(); } -void ActionableView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; - state->name = accessible_name_; -} - } // namespace internal } // namespace ash diff --git a/ash/system/tray/actionable_view.h b/ash/system/tray/actionable_view.h index 0d0f1a1c30..f2b3ec1deb 100644 --- a/ash/system/tray/actionable_view.h +++ b/ash/system/tray/actionable_view.h @@ -21,6 +21,8 @@ namespace internal { // Exported for SystemTray. class ASH_EXPORT ActionableView : public views::View { public: + static const char kViewClassName[]; + ActionableView(); virtual ~ActionableView(); @@ -28,10 +30,11 @@ class ASH_EXPORT ActionableView : public views::View { void SetAccessibleName(const base::string16& name); const base::string16& accessible_name() const { return accessible_name_; } - static const char kViewClassName[]; - protected: - void DrawBorder(gfx::Canvas* canvas, const gfx::Rect& bounds); + void OnPaintFocus(gfx::Canvas* canvas); + + // Returns the bounds to paint the focus rectangle in. + virtual gfx::Rect GetFocusBounds(); // Performs an action when user clicks on the view (on mouse-press event), or // presses a key when this view is in focus. Returns true if the event has @@ -45,7 +48,9 @@ class ASH_EXPORT ActionableView : public views::View { virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseCaptureLost() OVERRIDE; virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; - virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; // Overridden from ui::EventHandler. virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; diff --git a/ash/system/tray/fixed_sized_scroll_view.cc b/ash/system/tray/fixed_sized_scroll_view.cc index 59b524b752..d3fff9ad1a 100644 --- a/ash/system/tray/fixed_sized_scroll_view.cc +++ b/ash/system/tray/fixed_sized_scroll_view.cc @@ -9,7 +9,6 @@ namespace internal { FixedSizedScrollView::FixedSizedScrollView() { set_notify_enter_exit_on_child(true); - set_focus_border(NULL); } FixedSizedScrollView::~FixedSizedScrollView() { @@ -54,9 +53,5 @@ void FixedSizedScrollView::OnBoundsChanged(const gfx::Rect& previous_bounds) { contents()->SetBoundsRect(bounds); } -void FixedSizedScrollView::OnPaintFocusBorder(gfx::Canvas* canvas) { - // Do not paint the focus border. -} - } // namespace internal } // namespace ash diff --git a/ash/system/tray/fixed_sized_scroll_view.h b/ash/system/tray/fixed_sized_scroll_view.h index c5d9bc0a09..aff53c217a 100644 --- a/ash/system/tray/fixed_sized_scroll_view.h +++ b/ash/system/tray/fixed_sized_scroll_view.h @@ -33,7 +33,6 @@ class FixedSizedScrollView : public views::ScrollView { protected: // Overridden from views::View: virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; - virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; private: gfx::Size fixed_size_; diff --git a/ash/system/tray/hover_highlight_view.cc b/ash/system/tray/hover_highlight_view.cc index f68ea12b67..8afa2de400 100644 --- a/ash/system/tray/hover_highlight_view.cc +++ b/ash/system/tray/hover_highlight_view.cc @@ -11,6 +11,7 @@ #include "ui/base/accessibility/accessible_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" +#include "ui/views/border.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" diff --git a/ash/system/tray/special_popup_row.cc b/ash/system/tray/special_popup_row.cc index 896d1516df..fd10f4721b 100644 --- a/ash/system/tray/special_popup_row.cc +++ b/ash/system/tray/special_popup_row.cc @@ -13,6 +13,7 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/rect.h" +#include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/layout/box_layout.h" #include "ui/views/painter.h" diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc index 045526c5dd..b5d8cc3032 100644 --- a/ash/system/tray/system_tray.cc +++ b/ash/system/tray/system_tray.cc @@ -101,11 +101,11 @@ class SystemBubbleWrapper { } is_persistent_ = is_persistent; - // If ChromeVox is enabled, focus the default item. - AccessibilityDelegate* delegate = - Shell::GetInstance()->accessibility_delegate(); - if (delegate->IsSpokenFeedbackEnabled()) - bubble_->FocusDefault(); + // If ChromeVox is enabled, focus the default item if no item is focused. + if (Shell::GetInstance()->accessibility_delegate()-> + IsSpokenFeedbackEnabled()) { + bubble_->FocusDefaultIfNeeded(); + } } // Convenience accessors: @@ -461,11 +461,11 @@ void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items, notification_bubble_.reset(); if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) { system_bubble_->bubble()->UpdateView(items, bubble_type); - // If ChromeVox is enabled, focus the default item. - AccessibilityDelegate* delegate = - Shell::GetInstance()->accessibility_delegate(); - if (delegate->IsSpokenFeedbackEnabled()) - system_bubble_->bubble()->FocusDefault(); + // If ChromeVox is enabled, focus the default item if no item is focused. + if (Shell::GetInstance()->accessibility_delegate()-> + IsSpokenFeedbackEnabled()) { + system_bubble_->bubble()->FocusDefaultIfNeeded(); + } } else { // Remember if the menu is a single property (like e.g. volume) or the // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case diff --git a/ash/system/tray/system_tray_bubble.cc b/ash/system/tray/system_tray_bubble.cc index 362e996553..1aa60bc554 100644 --- a/ash/system/tray/system_tray_bubble.cc +++ b/ash/system/tray/system_tray_bubble.cc @@ -271,9 +271,9 @@ void SystemTrayBubble::InitView(views::View* anchor, } } -void SystemTrayBubble::FocusDefault() { +void SystemTrayBubble::FocusDefaultIfNeeded() { views::FocusManager* manager = bubble_view_->GetFocusManager(); - if (!manager) + if (!manager || manager->GetFocusedView()) return; views::View* view = manager->GetNextFocusableView(NULL, NULL, false, false); @@ -352,11 +352,14 @@ bool SystemTrayBubble::ShouldShowLauncher() const { void SystemTrayBubble::CreateItemViews(user::LoginStatus login_status) { std::vector<views::View*> item_views; + views::View* focus_view = NULL; for (size_t i = 0; i < items_.size(); ++i) { views::View* view = NULL; switch (bubble_type_) { case BUBBLE_TYPE_DEFAULT: view = items_[i]->CreateDefaultView(login_status); + if (items_[i]->restore_focus()) + focus_view = view; break; case BUBBLE_TYPE_DETAILED: view = items_[i]->CreateDetailedView(login_status); @@ -377,6 +380,8 @@ void SystemTrayBubble::CreateItemViews(user::LoginStatus login_status) { item_views[i], is_default_bubble, is_default_bubble && (i < item_views.size() - 2))); } + if (focus_view) + focus_view->RequestFocus(); } } // namespace internal diff --git a/ash/system/tray/system_tray_bubble.h b/ash/system/tray/system_tray_bubble.h index 7232805342..b84c3ea022 100644 --- a/ash/system/tray/system_tray_bubble.h +++ b/ash/system/tray/system_tray_bubble.h @@ -43,8 +43,8 @@ class SystemTrayBubble { user::LoginStatus login_status, views::TrayBubbleView::InitParams* init_params); - // Focus the default item. - void FocusDefault(); + // Focus the default item if no item is focused. Othewise, do nothing. + void FocusDefaultIfNeeded(); BubbleType bubble_type() const { return bubble_type_; } views::TrayBubbleView* bubble_view() const { return bubble_view_; } diff --git a/ash/system/tray/system_tray_item.cc b/ash/system/tray/system_tray_item.cc index b2acf08c07..f5d5b717c7 100644 --- a/ash/system/tray/system_tray_item.cc +++ b/ash/system/tray/system_tray_item.cc @@ -12,7 +12,8 @@ namespace ash { SystemTrayItem::SystemTrayItem(SystemTray* system_tray) - : system_tray_(system_tray) { + : system_tray_(system_tray), + restore_focus_(false) { } SystemTrayItem::~SystemTrayItem() { diff --git a/ash/system/tray/system_tray_item.h b/ash/system/tray/system_tray_item.h index a0d7d1b0e8..b42f3f5b35 100644 --- a/ash/system/tray/system_tray_item.h +++ b/ash/system/tray/system_tray_item.h @@ -108,8 +108,14 @@ class ASH_EXPORT SystemTrayItem { // Returns the system tray that this item belongs to. SystemTray* system_tray() const { return system_tray_; } + bool restore_focus() const { return restore_focus_; } + void set_restore_focus(bool restore_focus) { + restore_focus_ = restore_focus; + } + private: SystemTray* system_tray_; + bool restore_focus_; DISALLOW_COPY_AND_ASSIGN(SystemTrayItem); }; diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc index 3939b70105..80cac07e36 100644 --- a/ash/system/tray/tray_background_view.cc +++ b/ash/system/tray/tray_background_view.cc @@ -333,10 +333,10 @@ TrayBackgroundView::TrayBackgroundView( set_notify_enter_exit_on_child(true); // Initially we want to paint the background, but without the hover effect. - hide_background_animator_.SetPaintsBackground(true, - internal::BackgroundAnimator::CHANGE_IMMEDIATE); - hover_background_animator_.SetPaintsBackground(false, - internal::BackgroundAnimator::CHANGE_IMMEDIATE); + hide_background_animator_.SetPaintsBackground( + true, BACKGROUND_CHANGE_IMMEDIATE); + hover_background_animator_.SetPaintsBackground( + false, BACKGROUND_CHANGE_IMMEDIATE); tray_container_ = new TrayContainer(shelf_alignment_); SetContents(tray_container_); @@ -362,8 +362,8 @@ void TrayBackgroundView::OnMouseEntered(const ui::MouseEvent& event) { if (!background_ || draw_background_as_active_ || ash::switches::UseAlternateShelfLayout()) return; - hover_background_animator_.SetPaintsBackground(true, - internal::BackgroundAnimator::CHANGE_ANIMATE); + hover_background_animator_.SetPaintsBackground( + true, BACKGROUND_CHANGE_ANIMATE); } void TrayBackgroundView::OnMouseExited(const ui::MouseEvent& event) { @@ -371,22 +371,14 @@ void TrayBackgroundView::OnMouseExited(const ui::MouseEvent& event) { if (!background_ || draw_background_as_active_ || ash::switches::UseAlternateShelfLayout()) return; - hover_background_animator_.SetPaintsBackground(false, - internal::BackgroundAnimator::CHANGE_ANIMATE); + hover_background_animator_.SetPaintsBackground( + false, BACKGROUND_CHANGE_ANIMATE); } void TrayBackgroundView::ChildPreferredSizeChanged(views::View* child) { PreferredSizeChanged(); } -void TrayBackgroundView::OnPaintFocusBorder(gfx::Canvas* canvas) { - // The tray itself expands to the right and bottom edge of the screen to make - // sure clicking on the edges brings up the popup. However, the focus border - // should be only around the container. - if (HasFocus()) - DrawBorder(canvas, GetContentsBounds()); -} - void TrayBackgroundView::GetAccessibleState(ui::AccessibleViewState* state) { state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; state->name = GetAccessibleNameForTray(); @@ -403,6 +395,13 @@ bool TrayBackgroundView::PerformAction(const ui::Event& event) { return false; } +gfx::Rect TrayBackgroundView::GetFocusBounds() { + // The tray itself expands to the right and bottom edge of the screen to make + // sure clicking on the edges brings up the popup. However, the focus border + // should be only around the container. + return GetContentsBounds(); +} + void TrayBackgroundView::UpdateBackground(int alpha) { // The animator should never fire when the alternate shelf layout is used. if (!background_ || draw_background_as_active_) @@ -419,8 +418,7 @@ void TrayBackgroundView::SetContents(views::View* contents) { } void TrayBackgroundView::SetPaintsBackground( - bool value, - internal::BackgroundAnimator::ChangeType change_type) { + bool value, BackgroundAnimatorChangeType change_type) { DCHECK(!ash::switches::UseAlternateShelfLayout()); hide_background_animator_.SetPaintsBackground(value, change_type); } diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h index b459684429..5adaf4ec0c 100644 --- a/ash/system/tray/tray_background_view.h +++ b/ash/system/tray/tray_background_view.h @@ -70,12 +70,12 @@ class ASH_EXPORT TrayBackgroundView : public ActionableView, virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE; - virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; virtual void AboutToRequestFocusFromTabTraversal(bool reverse) OVERRIDE; // Overridden from internal::ActionableView. virtual bool PerformAction(const ui::Event& event) OVERRIDE; + virtual gfx::Rect GetFocusBounds() OVERRIDE; // Overridden from internal::BackgroundAnimatorDelegate. virtual void UpdateBackground(int alpha) OVERRIDE; @@ -108,9 +108,8 @@ class ASH_EXPORT TrayBackgroundView : public ActionableView, // Sets whether the tray paints a background. Default is true, but is set to // false if a window overlaps the shelf. - void SetPaintsBackground( - bool value, - internal::BackgroundAnimator::ChangeType change_type); + void SetPaintsBackground(bool value, + BackgroundAnimatorChangeType change_type); // Initializes animations for the bubble. void InitializeBubbleAnimations(views::Widget* bubble_widget); diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc index dd33cdf05a..72efe4ffe0 100644 --- a/ash/system/tray/tray_details_view.cc +++ b/ash/system/tray/tray_details_view.cc @@ -5,10 +5,12 @@ #include "ash/system/tray/tray_details_view.h" #include "ash/system/tray/fixed_sized_scroll_view.h" +#include "ash/system/tray/system_tray.h" #include "ash/system/tray/system_tray_item.h" #include "ash/system/tray/tray_constants.h" #include "ui/gfx/canvas.h" #include "ui/views/background.h" +#include "ui/views/border.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/layout/box_layout.h" @@ -114,6 +116,16 @@ void TrayDetailsView::Reset() { scroll_content_ = NULL; } +void TrayDetailsView::TransitionToDefaultView() { + // Cache pointer to owner in this function scope. TrayDetailsView will be + // deleted after called ShowDefaultView. + SystemTrayItem* owner = owner_; + if (footer_ && footer_->content() && footer_->content()->HasFocus()) + owner->set_restore_focus(true); + owner->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING); + owner->set_restore_focus(false); +} + void TrayDetailsView::Layout() { if (!scroller_ || !footer_ || bounds().IsEmpty()) { views::View::Layout(); diff --git a/ash/system/tray/tray_details_view.h b/ash/system/tray/tray_details_view.h index 073873ad0d..28e9082c58 100644 --- a/ash/system/tray/tray_details_view.h +++ b/ash/system/tray/tray_details_view.h @@ -5,6 +5,7 @@ #ifndef ASH_SYSTEM_TRAY_TRAY_DETAILS_VIEW_H_ #define ASH_SYSTEM_TRAY_TRAY_DETAILS_VIEW_H_ +#include "ash/ash_export.h" #include "ash/system/tray/special_popup_row.h" #include "ui/views/view.h" @@ -22,7 +23,7 @@ class FixedSizedScrollView; class ScrollBorder; class ViewClickListener; -class TrayDetailsView : public views::View { +class ASH_EXPORT TrayDetailsView : public views::View { public: explicit TrayDetailsView(SystemTrayItem* owner); virtual ~TrayDetailsView(); @@ -41,6 +42,11 @@ class TrayDetailsView : public views::View { // Removes (and destroys) all child views. void Reset(); + // Transition to default view from details view. If |footer_| has focus before + // transition, the default view should focus on the owner of this details + // view. + void TransitionToDefaultView(); + SystemTrayItem* owner() const { return owner_; } SpecialPopupRow* footer() const { return footer_; } FixedSizedScrollView* scroller() const { return scroller_; } diff --git a/ash/system/tray/tray_details_view_unittest.cc b/ash/system/tray/tray_details_view_unittest.cc new file mode 100644 index 0000000000..49a637e808 --- /dev/null +++ b/ash/system/tray/tray_details_view_unittest.cc @@ -0,0 +1,149 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/system/tray/tray_details_view.h" + +#include "ash/root_window_controller.h" +#include "ash/shelf/shelf_widget.h" +#include "ash/shell.h" +#include "ash/system/status_area_widget.h" +#include "ash/system/tray/system_tray.h" +#include "ash/system/tray/system_tray_item.h" +#include "ash/system/tray/tray_details_view.h" +#include "ash/system/tray/view_click_listener.h" +#include "ash/test/ash_test_base.h" +#include "base/run_loop.h" +#include "grit/ash_strings.h" +#include "ui/aura/window.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace ash { +namespace test { + +namespace { + +SystemTray* GetSystemTray() { + return Shell::GetPrimaryRootWindowController()->shelf()-> + status_area_widget()->system_tray(); +} + +class TestDetailsView : public internal::TrayDetailsView, + public internal::ViewClickListener { + public: + explicit TestDetailsView(SystemTrayItem* owner) + : internal::TrayDetailsView(owner) {} + + virtual ~TestDetailsView() {} + + void CreateFooterAndFocus() { + // Uses bluetooth label for testing purpose. It can be changed to any + // string_id. + CreateSpecialRow(IDS_ASH_STATUS_TRAY_BLUETOOTH, this); + footer()->content()->RequestFocus(); + } + + // Overridden from internal::ViewClickListener: + virtual void OnViewClicked(views::View* sender) OVERRIDE {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestDetailsView); +}; + +// Trivial item implementation that tracks its views for testing. +class TestItem : public SystemTrayItem { + public: + TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL) {} + + // Overridden from SystemTrayItem: + virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE { + tray_view_ = new views::View; + return tray_view_; + } + virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE { + default_view_ = new views::View; + default_view_->set_focusable(true); + return default_view_; + } + virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE { + detailed_view_ = new TestDetailsView(this); + return detailed_view_; + } + virtual void DestroyTrayView() OVERRIDE { + tray_view_ = NULL; + } + virtual void DestroyDefaultView() OVERRIDE { + default_view_ = NULL; + } + virtual void DestroyDetailedView() OVERRIDE { + detailed_view_ = NULL; + } + + views::View* tray_view() const { return tray_view_; } + views::View* default_view() const { return default_view_; } + TestDetailsView* detailed_view() const { return detailed_view_; } + + private: + views::View* tray_view_; + views::View* default_view_; + TestDetailsView* detailed_view_; + + DISALLOW_COPY_AND_ASSIGN(TestItem); +}; + +} // namespace + +typedef AshTestBase TrayDetailsViewTest; + +TEST_F(TrayDetailsViewTest, TransitionToDefaultViewTest) { + SystemTray* tray = GetSystemTray(); + ASSERT_TRUE(tray->GetWidget()); + + TestItem* test_item_1 = new TestItem; + TestItem* test_item_2 = new TestItem; + tray->AddTrayItem(test_item_1); + tray->AddTrayItem(test_item_2); + + // Ensure the tray views are created. + ASSERT_TRUE(test_item_1->tray_view() != NULL); + ASSERT_TRUE(test_item_2->tray_view() != NULL); + + // Show the default view. + tray->ShowDefaultView(BUBBLE_CREATE_NEW); + RunAllPendingInMessageLoop(); + + // Show the detailed view of item 2. + tray->ShowDetailedView(test_item_2, 0, true, BUBBLE_USE_EXISTING); + EXPECT_TRUE(test_item_2->detailed_view()); + RunAllPendingInMessageLoop(); + EXPECT_FALSE(test_item_2->default_view()); + + // Transition back to default view, the default view of item 2 should have + // focus. + test_item_2->detailed_view()->CreateFooterAndFocus(); + test_item_2->detailed_view()->TransitionToDefaultView(); + RunAllPendingInMessageLoop(); + + EXPECT_TRUE(test_item_2->default_view()); + EXPECT_FALSE(test_item_2->detailed_view()); + EXPECT_TRUE(test_item_2->default_view()->HasFocus()); + + // Show the detailed view of item 2 again. + tray->ShowDetailedView(test_item_2, 0, true, BUBBLE_USE_EXISTING); + EXPECT_TRUE(test_item_2->detailed_view()); + RunAllPendingInMessageLoop(); + EXPECT_FALSE(test_item_2->default_view()); + + // Transition back to default view, the default view of item 2 should NOT have + // focus. + test_item_2->detailed_view()->TransitionToDefaultView(); + RunAllPendingInMessageLoop(); + + EXPECT_TRUE(test_item_2->default_view()); + EXPECT_FALSE(test_item_2->detailed_view()); + EXPECT_FALSE(test_item_2->default_view()->HasFocus()); +} + +} // namespace test +} // namespace ash diff --git a/ash/system/tray/tray_empty.cc b/ash/system/tray/tray_empty.cc index 27fb7302ed..f15f7b31e7 100644 --- a/ash/system/tray/tray_empty.cc +++ b/ash/system/tray/tray_empty.cc @@ -6,6 +6,7 @@ #include "ui/views/layout/box_layout.h" #include "ui/views/background.h" +#include "ui/views/border.h" #include "ui/views/view.h" namespace { diff --git a/ash/system/tray/tray_notification_view.cc b/ash/system/tray/tray_notification_view.cc index aa4b1d1a8a..c09589285e 100644 --- a/ash/system/tray/tray_notification_view.cc +++ b/ash/system/tray/tray_notification_view.cc @@ -10,6 +10,7 @@ #include "grit/ui_resources.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image_skia.h" +#include "ui/views/background.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/layout/grid_layout.h" diff --git a/ash/system/tray/tray_popup_header_button.cc b/ash/system/tray/tray_popup_header_button.cc index 7e20c00e4e..e9430e57df 100644 --- a/ash/system/tray/tray_popup_header_button.cc +++ b/ash/system/tray/tray_popup_header_button.cc @@ -8,6 +8,7 @@ #include "ash/system/tray/tray_constants.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" +#include "ui/views/painter.h" namespace ash { namespace internal { @@ -37,6 +38,10 @@ TrayPopupHeaderButton::TrayPopupHeaderButton(views::ButtonListener* listener, SetAccessibleName(bundle.GetLocalizedString(accessible_name_id)); set_focusable(true); set_request_focus_on_press(false); + + SetFocusPainter(views::Painter::CreateSolidFocusPainter( + kFocusBorderColor, + gfx::Insets(1, 2, 2, 3))); } TrayPopupHeaderButton::~TrayPopupHeaderButton() {} @@ -57,13 +62,6 @@ void TrayPopupHeaderButton::OnPaintBorder(gfx::Canvas* canvas) { ash::kBorderDarkColor); } -void TrayPopupHeaderButton::OnPaintFocusBorder(gfx::Canvas* canvas) { - if (HasFocus()) { - canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3), - kFocusBorderColor); - } -} - void TrayPopupHeaderButton::StateChanged() { SchedulePaint(); } diff --git a/ash/system/tray/tray_popup_header_button.h b/ash/system/tray/tray_popup_header_button.h index f9209c86c7..37849bc2e9 100644 --- a/ash/system/tray/tray_popup_header_button.h +++ b/ash/system/tray/tray_popup_header_button.h @@ -32,7 +32,6 @@ class ASH_EXPORT TrayPopupHeaderButton : public views::ToggleImageButton { virtual const char* GetClassName() const OVERRIDE; virtual gfx::Size GetPreferredSize() OVERRIDE; virtual void OnPaintBorder(gfx::Canvas* canvas) OVERRIDE; - virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; // Overridden from views::CustomButton: virtual void StateChanged() OVERRIDE; diff --git a/ash/system/tray/tray_popup_label_button.cc b/ash/system/tray/tray_popup_label_button.cc index 43cf3bdfbb..2f97c372c4 100644 --- a/ash/system/tray/tray_popup_label_button.cc +++ b/ash/system/tray/tray_popup_label_button.cc @@ -8,6 +8,7 @@ #include "ash/system/tray/tray_popup_label_button_border.h" #include "ui/gfx/canvas.h" #include "ui/gfx/rect.h" +#include "ui/views/painter.h" namespace ash { namespace internal { @@ -20,16 +21,12 @@ TrayPopupLabelButton::TrayPopupLabelButton(views::ButtonListener* listener, set_request_focus_on_press(false); set_animate_on_state_change(false); SetHorizontalAlignment(gfx::ALIGN_CENTER); + SetFocusPainter(views::Painter::CreateSolidFocusPainter( + kFocusBorderColor, + gfx::Insets(1, 1, 2, 2))); } TrayPopupLabelButton::~TrayPopupLabelButton() {} -void TrayPopupLabelButton::OnPaintFocusBorder(gfx::Canvas* canvas) { - if (HasFocus()) { - canvas->DrawRect(gfx::Rect(1, 1, width() - 3, height() - 3), - ash::kFocusBorderColor); - } -} - } // namespace internal } // namespace ash diff --git a/ash/system/tray/tray_popup_label_button.h b/ash/system/tray/tray_popup_label_button.h index 8d21abd98a..a1f9db6b7b 100644 --- a/ash/system/tray/tray_popup_label_button.h +++ b/ash/system/tray/tray_popup_label_button.h @@ -21,9 +21,6 @@ class TrayPopupLabelButton : public views::LabelButton { virtual ~TrayPopupLabelButton(); private: - // Overridden from views::LabelButton: - virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; - DISALLOW_COPY_AND_ASSIGN(TrayPopupLabelButton); }; diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc index 63fd21505b..f064a5f925 100644 --- a/ash/system/tray_accessibility.cc +++ b/ash/system/tray_accessibility.cc @@ -229,7 +229,7 @@ void AccessibilityDetailedView::OnViewClicked(views::View* sender) { AccessibilityDelegate* delegate = Shell::GetInstance()->accessibility_delegate(); if (sender == footer()->content()) { - owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING); + TransitionToDefaultView(); } else if (sender == spoken_feedback_view_) { delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE); } else if (sender == high_contrast_view_) { diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc index 8d1499dd88..e0d9587351 100644 --- a/ash/system/user/tray_user.cc +++ b/ash/system/user/tray_user.cc @@ -481,12 +481,12 @@ PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner, // user. base::string16 display_name = Shell::GetInstance()->session_state_delegate()->GetUserDisplayName(0); - RemoveChars(display_name, kDisplayNameMark, &display_name); + base::RemoveChars(display_name, kDisplayNameMark, &display_name); display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0]; // Retrieve the domain managing the device and wrap it with markers. base::string16 domain = UTF8ToUTF16( Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain()); - RemoveChars(domain, kDisplayNameMark, &domain); + base::RemoveChars(domain, kDisplayNameMark, &domain); base::i18n::WrapStringWithLTRFormatting(&domain); // Retrieve the label text, inserting the display name and domain. text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL, diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc index 09e615e0a3..b0b75d87df 100644 --- a/ash/system/web_notification/web_notification_tray.cc +++ b/ash/system/web_notification/web_notification_tray.cc @@ -416,6 +416,9 @@ bool WebNotificationTray::ShowPopups() { } void WebNotificationTray::HidePopups() { + DCHECK(popup_collection_.get()); + + popup_collection_->MarkAllPopupsShown(); popup_collection_.reset(); work_area_observer_->StopObserving(); } diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/web_notification/web_notification_tray_unittest.cc index 5ae2e49dea..b64e2b8f7f 100644 --- a/ash/system/web_notification/web_notification_tray_unittest.cc +++ b/ash/system/web_notification/web_notification_tray_unittest.cc @@ -208,6 +208,16 @@ TEST_F(WebNotificationTrayTest, WebNotificationPopupBubble) { // Removing the visible notification should hide the popup bubble. RemoveNotification("test_id3"); EXPECT_FALSE(GetTray()->IsPopupVisible()); + + // Now test that we can show multiple popups and then show the message center. + AddNotification("test_id4"); + AddNotification("test_id5"); + EXPECT_TRUE(GetTray()->IsPopupVisible()); + + GetTray()->message_center_tray_->ShowMessageCenterBubble(); + GetTray()->message_center_tray_->HideMessageCenterBubble(); + + EXPECT_FALSE(GetTray()->IsPopupVisible()); } using message_center::NotificationList; diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc index cd4174a4ee..335972d40b 100644 --- a/ash/test/ash_test_base.cc +++ b/ash/test/ash_test_base.cc @@ -138,8 +138,7 @@ void AshTestBase::SetUp() { metro_viewer_host_.reset( new TestMetroViewerProcessHost(ipc_thread_->message_loop_proxy())); CHECK(metro_viewer_host_->LaunchViewerAndWaitForConnection( - win8::test::kDefaultTestAppUserModelId, - L"test_open")); + win8::test::kDefaultTestAppUserModelId)); aura::RemoteRootWindowHostWin* root_window_host = aura::RemoteRootWindowHostWin::Instance(); CHECK(root_window_host != NULL); diff --git a/ash/test/ash_unittests.cc b/ash/test/ash_unittests.cc index a49945201c..040e40f33f 100644 --- a/ash/test/ash_unittests.cc +++ b/ash/test/ash_unittests.cc @@ -8,8 +8,10 @@ int main(int argc, char** argv) { ash::test::AuraShellTestSuite test_suite(argc, argv); - return base::LaunchUnitTests(argc, - argv, - base::Bind(&ash::test::AuraShellTestSuite::Run, - base::Unretained(&test_suite))); + + return base::LaunchUnitTestsSerially( + argc, + argv, + base::Bind(&ash::test::AuraShellTestSuite::Run, + base::Unretained(&test_suite))); } diff --git a/ash/test/launcher_item_delegate_manager_test_api.h b/ash/test/launcher_item_delegate_manager_test_api.h deleted file mode 100644 index d916a0558c..0000000000 --- a/ash/test/launcher_item_delegate_manager_test_api.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_ -#define ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_ - -#include "base/basictypes.h" - -namespace ash { - -class LauncherItemDelegateManager; - -namespace test { - -// Accesses private methods from a LauncherItemDelegateManager for testing. -class LauncherItemDelegateManagerTestAPI { - public: - explicit LauncherItemDelegateManagerTestAPI( - LauncherItemDelegateManager* manager); - - // Clear all exsiting LauncherItemDelegate for test. - void RemoveAllLauncherItemDelegateForTest(); - - private: - LauncherItemDelegateManager* manager_; // Not owned. - - DISALLOW_COPY_AND_ASSIGN(LauncherItemDelegateManagerTestAPI); -}; - -} // namespace test -} // namespace ash - -#endif // ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_ diff --git a/ash/test/launcher_test_api.cc b/ash/test/launcher_test_api.cc index 86137f4847..2e56a86231 100644 --- a/ash/test/launcher_test_api.cc +++ b/ash/test/launcher_test_api.cc @@ -20,7 +20,7 @@ internal::ShelfView* LauncherTestAPI::shelf_view() { return launcher_->shelf_view_; } -void LauncherTestAPI::SetLauncherDelegate(LauncherDelegate* delegate) { +void LauncherTestAPI::SetShelfDelegate(ShelfDelegate* delegate) { launcher_->delegate_ = delegate; } diff --git a/ash/test/launcher_test_api.h b/ash/test/launcher_test_api.h index c680e93639..d83833922e 100644 --- a/ash/test/launcher_test_api.h +++ b/ash/test/launcher_test_api.h @@ -10,7 +10,7 @@ namespace ash { class Launcher; -class LauncherDelegate; +class ShelfDelegate; namespace internal { class ShelfView; @@ -28,8 +28,8 @@ class LauncherTestAPI { // An accessor for |shelf_view|. internal::ShelfView* shelf_view(); - // Set LauncherDelegate. - void SetLauncherDelegate(LauncherDelegate* delegate); + // Set ShelfDelegate. + void SetShelfDelegate(ShelfDelegate* delegate); private: Launcher* launcher_; diff --git a/ash/test/launcher_item_delegate_manager_test_api.cc b/ash/test/shelf_item_delegate_manager_test_api.cc index 159f7dafa9..740164d692 100644 --- a/ash/test/launcher_item_delegate_manager_test_api.cc +++ b/ash/test/shelf_item_delegate_manager_test_api.cc @@ -2,23 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ash/test/launcher_item_delegate_manager_test_api.h" +#include "ash/test/shelf_item_delegate_manager_test_api.h" -#include "ash/launcher/launcher_item_delegate.h" -#include "ash/launcher/launcher_item_delegate_manager.h" +#include "ash/shelf/shelf_item_delegate.h" +#include "ash/shelf/shelf_item_delegate_manager.h" #include "base/stl_util.h" namespace ash { namespace test { -LauncherItemDelegateManagerTestAPI::LauncherItemDelegateManagerTestAPI( - LauncherItemDelegateManager* manager) +ShelfItemDelegateManagerTestAPI::ShelfItemDelegateManagerTestAPI( + ShelfItemDelegateManager* manager) : manager_(manager) { DCHECK(manager_); } -void -LauncherItemDelegateManagerTestAPI::RemoveAllLauncherItemDelegateForTest() { +void ShelfItemDelegateManagerTestAPI::RemoveAllShelfItemDelegateForTest() { STLDeleteContainerPairSecondPointers( manager_->id_to_item_delegate_map_.begin(), manager_->id_to_item_delegate_map_.end()); diff --git a/ash/test/shelf_item_delegate_manager_test_api.h b/ash/test/shelf_item_delegate_manager_test_api.h new file mode 100644 index 0000000000..183bab583c --- /dev/null +++ b/ash/test/shelf_item_delegate_manager_test_api.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_ +#define ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_ + +#include "base/basictypes.h" + +namespace ash { + +class ShelfItemDelegateManager; + +namespace test { + +// Accesses private methods from a ShelfItemDelegateManager for testing. +class ShelfItemDelegateManagerTestAPI { + public: + explicit ShelfItemDelegateManagerTestAPI(ShelfItemDelegateManager* manager); + + // Clear all exsiting ShelfItemDelegate for test. + void RemoveAllShelfItemDelegateForTest(); + + private: + ShelfItemDelegateManager* manager_; // Not owned. + + DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegateManagerTestAPI); +}; + +} // namespace test +} // namespace ash + +#endif // ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_ diff --git a/ash/test/shelf_view_test_api.cc b/ash/test/shelf_view_test_api.cc index 568eda9281..b0e0dff17a 100644 --- a/ash/test/shelf_view_test_api.cc +++ b/ash/test/shelf_view_test_api.cc @@ -120,7 +120,7 @@ bool ShelfViewTestAPI::SameDragType(LauncherItemType typea, return shelf_view_->SameDragType(typea, typeb); } -void ShelfViewTestAPI::SetLauncherDelegate(LauncherDelegate* delegate) { +void ShelfViewTestAPI::SetShelfDelegate(ShelfDelegate* delegate) { shelf_view_->delegate_ = delegate; } @@ -128,5 +128,13 @@ gfx::Rect ShelfViewTestAPI::GetBoundsForDragInsertInScreen() { return shelf_view_->GetBoundsForDragInsertInScreen(); } +bool ShelfViewTestAPI::IsDraggingShelfItem() { + return shelf_view_->dragging(); +} + +bool ShelfViewTestAPI::DraggedItemFromOverflowToShelf() { + return shelf_view_->dragged_off_from_overflow_to_shelf_; +} + } // namespace test } // namespace ash diff --git a/ash/test/shelf_view_test_api.h b/ash/test/shelf_view_test_api.h index d26c02d984..56ca723913 100644 --- a/ash/test/shelf_view_test_api.h +++ b/ash/test/shelf_view_test_api.h @@ -15,7 +15,7 @@ class Size; namespace ash { -class LauncherDelegate; +class ShelfDelegate; namespace internal { class OverflowBubble; @@ -77,12 +77,18 @@ class ShelfViewTestAPI { // Wrapper for ShelfView::SameDragType. bool SameDragType(LauncherItemType typea, LauncherItemType typeb) const; - // Sets LauncherDelegate. - void SetLauncherDelegate(LauncherDelegate* delegate); + // Sets ShelfDelegate. + void SetShelfDelegate(ShelfDelegate* delegate); // Returns re-insertable bounds in screen. gfx::Rect GetBoundsForDragInsertInScreen(); + // Returns true if item is ripped off. + bool IsDraggingShelfItem(); + + // Returns true if an item is ripped off and entered into shelf. + bool DraggedItemFromOverflowToShelf(); + private: internal::ShelfView* shelf_view_; diff --git a/ash/test/shell_test_api.cc b/ash/test/shell_test_api.cc index 353a76ced2..5fe130e8f1 100644 --- a/ash/test/shell_test_api.cc +++ b/ash/test/shell_test_api.cc @@ -4,8 +4,8 @@ #include "ash/test/shell_test_api.h" -#include "ash/launcher/launcher_delegate.h" #include "ash/root_window_controller.h" +#include "ash/shelf/shelf_delegate.h" #include "ash/shell.h" #if defined(OS_CHROMEOS) @@ -67,8 +67,8 @@ void ShellTestApi::DisableOutputConfiguratorAnimation() { #endif // defined(OS_CHROMEOS) } -void ShellTestApi::SetLauncherDelegate(LauncherDelegate* delegate) { - shell_->launcher_delegate_.reset(delegate); +void ShellTestApi::SetShelfDelegate(ShelfDelegate* delegate) { + shell_->shelf_delegate_.reset(delegate); } } // namespace test diff --git a/ash/test/shell_test_api.h b/ash/test/shell_test_api.h index 9457e7511f..269eb78459 100644 --- a/ash/test/shell_test_api.h +++ b/ash/test/shell_test_api.h @@ -15,7 +15,7 @@ class InputMethodEventFilter; namespace ash { class AshNativeCursorManager; -class LauncherDelegate; +class ShelfDelegate; class ShelfModel; class Shell; @@ -47,8 +47,8 @@ public: void DisableOutputConfiguratorAnimation(); - // Set LauncherDelegate. - void SetLauncherDelegate(LauncherDelegate* delegate); + // Set ShelfDelegate. + void SetShelfDelegate(ShelfDelegate* delegate); private: Shell* shell_; // not owned diff --git a/ash/test/test_launcher_delegate.cc b/ash/test/test_launcher_delegate.cc deleted file mode 100644 index 67e8be3b58..0000000000 --- a/ash/test/test_launcher_delegate.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/test/test_launcher_delegate.h" - -#include "ash/launcher/launcher_item_delegate_manager.h" -#include "ash/shelf/shelf_model.h" -#include "ash/shelf/shelf_util.h" -#include "ash/shell.h" -#include "ash/test/test_launcher_item_delegate.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "grit/ash_resources.h" -#include "ui/aura/window.h" - -namespace ash { -namespace test { - -TestLauncherDelegate* TestLauncherDelegate::instance_ = NULL; - -TestLauncherDelegate::TestLauncherDelegate(ShelfModel* model) - : model_(model) { - CHECK(!instance_); - instance_ = this; -} - -TestLauncherDelegate::~TestLauncherDelegate() { - instance_ = NULL; -} - -void TestLauncherDelegate::AddLauncherItem(aura::Window* window) { - AddLauncherItem(window, STATUS_CLOSED); -} - -void TestLauncherDelegate::AddLauncherItem( - aura::Window* window, - LauncherItemStatus status) { - ash::LauncherItem item; - if (window->type() == aura::client::WINDOW_TYPE_PANEL) - item.type = ash::TYPE_APP_PANEL; - else - item.type = ash::TYPE_PLATFORM_APP; - LauncherID id = model_->next_id(); - item.status = status; - model_->Add(item); - window->AddObserver(this); - - ash::LauncherItemDelegateManager* manager = - ash::Shell::GetInstance()->launcher_item_delegate_manager(); - // |manager| owns TestLauncherItemDelegate. - scoped_ptr<LauncherItemDelegate> delegate( - new TestLauncherItemDelegate(window)); - manager->SetLauncherItemDelegate(id, delegate.Pass()); - SetLauncherIDForWindow(id, window); -} - -void TestLauncherDelegate::RemoveLauncherItemForWindow(aura::Window* window) { - ash::LauncherID id = GetLauncherIDForWindow(window); - if (id == 0) - return; - int index = model_->ItemIndexByID(id); - DCHECK_NE(-1, index); - model_->RemoveItemAt(index); - window->RemoveObserver(this); -} - -void TestLauncherDelegate::OnWindowDestroying(aura::Window* window) { - RemoveLauncherItemForWindow(window); -} - -void TestLauncherDelegate::OnWindowHierarchyChanging( - const HierarchyChangeParams& params) { - // The window may be legitimately reparented while staying open if it moves - // to another display or container. If the window does not have a new parent - // then remove the launcher item. - if (!params.new_parent) - RemoveLauncherItemForWindow(params.target); -} - -void TestLauncherDelegate::OnLauncherCreated(Launcher* launcher) { -} - -void TestLauncherDelegate::OnLauncherDestroyed(Launcher* launcher) { -} - -LauncherID TestLauncherDelegate::GetLauncherIDForAppID( - const std::string& app_id) { - return 0; -} - -const std::string& TestLauncherDelegate::GetAppIDForLauncherID(LauncherID id) { - return EmptyString(); -} - -void TestLauncherDelegate::PinAppWithID(const std::string& app_id) { -} - -bool TestLauncherDelegate::CanPin() const { - return true; -} - -bool TestLauncherDelegate::IsAppPinned(const std::string& app_id) { - return false; -} - -void TestLauncherDelegate::UnpinAppWithID(const std::string& app_id) { -} - -} // namespace test -} // namespace ash diff --git a/ash/test/test_launcher_item_delegate.cc b/ash/test/test_launcher_item_delegate.cc deleted file mode 100644 index 3913c6841c..0000000000 --- a/ash/test/test_launcher_item_delegate.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/test/test_launcher_item_delegate.h" - -#include "ash/wm/window_util.h" -#include "ui/aura/window.h" - -namespace ash { -namespace test { - -TestLauncherItemDelegate::TestLauncherItemDelegate(aura::Window* window) - : window_(window) { -} - -TestLauncherItemDelegate::~TestLauncherItemDelegate() { -} - -bool TestLauncherItemDelegate::ItemSelected(const ui::Event& event) { - if (window_) { - if (window_->type() == aura::client::WINDOW_TYPE_PANEL) - ash::wm::MoveWindowToEventRoot(window_, event); - window_->Show(); - ash::wm::ActivateWindow(window_); - } - return false; -} - -base::string16 TestLauncherItemDelegate::GetTitle() { - return window_ ? window_->title() : base::string16(); -} - -ui::MenuModel* TestLauncherItemDelegate::CreateContextMenu( - aura::Window* root_window) { - return NULL; -} - -ash::LauncherMenuModel* TestLauncherItemDelegate::CreateApplicationMenu( - int event_flags) { - return NULL; -} - -bool TestLauncherItemDelegate::IsDraggable() { - return true; -} - -bool TestLauncherItemDelegate::ShouldShowTooltip() { - return true; -} - -} // namespace test -} // namespace ash diff --git a/ash/test/test_launcher_item_delegate.h b/ash/test/test_launcher_item_delegate.h deleted file mode 100644 index a2f631044f..0000000000 --- a/ash/test/test_launcher_item_delegate.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_ -#define ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_ - -#include "ash/launcher/launcher_item_delegate.h" -#include "base/basictypes.h" -#include "base/compiler_specific.h" - -namespace aura { -class Window; -} - -namespace ash { -namespace test { - -// Test implementation of ash::LauncherItemDelegate. -class TestLauncherItemDelegate : public ash::LauncherItemDelegate { - public: - explicit TestLauncherItemDelegate(aura::Window* window); - virtual ~TestLauncherItemDelegate(); - - // ash::LauncherItemDelegate overrides: - virtual bool ItemSelected(const ui::Event& event) OVERRIDE; - virtual base::string16 GetTitle() OVERRIDE; - virtual ui::MenuModel* CreateContextMenu( - aura::Window* root_window) OVERRIDE; - virtual ash::LauncherMenuModel* CreateApplicationMenu( - int event_flags) OVERRIDE; - virtual bool IsDraggable() OVERRIDE; - virtual bool ShouldShowTooltip() OVERRIDE; - - private: - aura::Window* window_; - - DISALLOW_COPY_AND_ASSIGN(TestLauncherItemDelegate); -}; - -} // namespace test -} // namespace ash - -#endif // ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_ diff --git a/ash/test/test_metro_viewer_process_host.cc b/ash/test/test_metro_viewer_process_host.cc index 3a58433a4b..a5e3fbfa40 100644 --- a/ash/test/test_metro_viewer_process_host.cc +++ b/ash/test/test_metro_viewer_process_host.cc @@ -42,5 +42,9 @@ void TestMetroViewerProcessHost::OnHandleSearchRequest( const string16& search_string) { } +void TestMetroViewerProcessHost::OnWindowSizeChanged(uint32 width, + uint32 height) { +} + } // namespace test } // namespace ash diff --git a/ash/test/test_metro_viewer_process_host.h b/ash/test/test_metro_viewer_process_host.h index e6d0a648cd..7c3770e38a 100644 --- a/ash/test/test_metro_viewer_process_host.h +++ b/ash/test/test_metro_viewer_process_host.h @@ -26,6 +26,7 @@ class TestMetroViewerProcessHost : public win8::MetroViewerProcessHost { virtual void OnSetTargetSurface(gfx::NativeViewId target_surface) OVERRIDE; virtual void OnOpenURL(const string16& url) OVERRIDE; virtual void OnHandleSearchRequest(const string16& search_string) OVERRIDE; + virtual void OnWindowSizeChanged(uint32 width, uint32 height) OVERRIDE; scoped_ptr<AcceleratedSurface> backing_surface_; diff --git a/ash/test/test_session_state_delegate.cc b/ash/test/test_session_state_delegate.cc index 8032f4ff44..5c168078b4 100644 --- a/ash/test/test_session_state_delegate.cc +++ b/ash/test/test_session_state_delegate.cc @@ -147,7 +147,7 @@ void TestSessionStateDelegate::SwitchActiveUser(const std::string& user_id) { activated_user_ = user_id; } -void TestSessionStateDelegate::SwitchActiveUserToNext() { +void TestSessionStateDelegate::CycleActiveUser(CycleUser cycle_user) { activated_user_ = "someone@tray"; } diff --git a/ash/test/test_session_state_delegate.h b/ash/test/test_session_state_delegate.h index fcd375d2fe..8c26c8884d 100644 --- a/ash/test/test_session_state_delegate.h +++ b/ash/test/test_session_state_delegate.h @@ -41,7 +41,7 @@ class TestSessionStateDelegate : public SessionStateDelegate { ash::MultiProfileIndex index) const OVERRIDE; virtual void GetLoggedInUsers(UserIdList* users) OVERRIDE; virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE; - virtual void SwitchActiveUserToNext() OVERRIDE; + virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE; virtual void AddSessionStateObserver( ash::SessionStateObserver* observer) OVERRIDE; virtual void RemoveSessionStateObserver( diff --git a/ash/test/test_shelf_delegate.cc b/ash/test/test_shelf_delegate.cc new file mode 100644 index 0000000000..c880d2433f --- /dev/null +++ b/ash/test/test_shelf_delegate.cc @@ -0,0 +1,108 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/test/test_shelf_delegate.h" + +#include "ash/shelf/shelf_item_delegate_manager.h" +#include "ash/shelf/shelf_model.h" +#include "ash/shelf/shelf_util.h" +#include "ash/shell.h" +#include "ash/test/test_shelf_item_delegate.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "grit/ash_resources.h" +#include "ui/aura/window.h" + +namespace ash { +namespace test { + +TestShelfDelegate* TestShelfDelegate::instance_ = NULL; + +TestShelfDelegate::TestShelfDelegate(ShelfModel* model) + : model_(model) { + CHECK(!instance_); + instance_ = this; +} + +TestShelfDelegate::~TestShelfDelegate() { + instance_ = NULL; +} + +void TestShelfDelegate::AddLauncherItem(aura::Window* window) { + AddLauncherItem(window, STATUS_CLOSED); +} + +void TestShelfDelegate::AddLauncherItem(aura::Window* window, + LauncherItemStatus status) { + LauncherItem item; + if (window->type() == aura::client::WINDOW_TYPE_PANEL) + item.type = TYPE_APP_PANEL; + else + item.type = TYPE_PLATFORM_APP; + LauncherID id = model_->next_id(); + item.status = status; + model_->Add(item); + window->AddObserver(this); + + ShelfItemDelegateManager* manager = + Shell::GetInstance()->shelf_item_delegate_manager(); + // |manager| owns TestShelfItemDelegate. + scoped_ptr<ShelfItemDelegate> delegate(new TestShelfItemDelegate(window)); + manager->SetShelfItemDelegate(id, delegate.Pass()); + SetLauncherIDForWindow(id, window); +} + +void TestShelfDelegate::RemoveLauncherItemForWindow(aura::Window* window) { + LauncherID id = GetLauncherIDForWindow(window); + if (id == 0) + return; + int index = model_->ItemIndexByID(id); + DCHECK_NE(-1, index); + model_->RemoveItemAt(index); + window->RemoveObserver(this); +} + +void TestShelfDelegate::OnWindowDestroying(aura::Window* window) { + RemoveLauncherItemForWindow(window); +} + +void TestShelfDelegate::OnWindowHierarchyChanging( + const HierarchyChangeParams& params) { + // The window may be legitimately reparented while staying open if it moves + // to another display or container. If the window does not have a new parent + // then remove the launcher item. + if (!params.new_parent) + RemoveLauncherItemForWindow(params.target); +} + +void TestShelfDelegate::OnLauncherCreated(Launcher* launcher) { +} + +void TestShelfDelegate::OnLauncherDestroyed(Launcher* launcher) { +} + +LauncherID TestShelfDelegate::GetLauncherIDForAppID(const std::string& app_id) { + return 0; +} + +const std::string& TestShelfDelegate::GetAppIDForLauncherID(LauncherID id) { + return base::EmptyString(); +} + +void TestShelfDelegate::PinAppWithID(const std::string& app_id) { +} + +bool TestShelfDelegate::CanPin() const { + return true; +} + +bool TestShelfDelegate::IsAppPinned(const std::string& app_id) { + return false; +} + +void TestShelfDelegate::UnpinAppWithID(const std::string& app_id) { +} + +} // namespace test +} // namespace ash diff --git a/ash/test/test_launcher_delegate.h b/ash/test/test_shelf_delegate.h index 11328bc5b2..e47545349b 100644 --- a/ash/test/test_launcher_delegate.h +++ b/ash/test/test_shelf_delegate.h @@ -1,14 +1,14 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ASH_TEST_TEST_LAUNCHER_DELEGATE_H_ -#define ASH_TEST_TEST_LAUNCHER_DELEGATE_H_ +#ifndef ASH_TEST_TEST_SHELF_DELEGATE_H_ +#define ASH_TEST_TEST_SHELF_DELEGATE_H_ #include <map> #include <set> -#include "ash/launcher/launcher_delegate.h" +#include "ash/shelf/shelf_delegate.h" #include "base/compiler_specific.h" #include "ui/aura/window_observer.h" @@ -18,26 +18,25 @@ class ShelfModel; namespace test { -// Test implementation of LauncherDelegate. +// Test implementation of ShelfDelegate. // Tests may create icons for windows by calling AddLauncherItem -class TestLauncherDelegate : public LauncherDelegate, - public aura::WindowObserver { +class TestShelfDelegate : public ShelfDelegate, public aura::WindowObserver { public: - explicit TestLauncherDelegate(ShelfModel* model); - virtual ~TestLauncherDelegate(); + explicit TestShelfDelegate(ShelfModel* model); + virtual ~TestShelfDelegate(); void AddLauncherItem(aura::Window* window); void AddLauncherItem(aura::Window* window, LauncherItemStatus status); void RemoveLauncherItemForWindow(aura::Window* window); - static TestLauncherDelegate* instance() { return instance_; } + static TestShelfDelegate* instance() { return instance_; } // WindowObserver implementation virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; virtual void OnWindowHierarchyChanging( const HierarchyChangeParams& params) OVERRIDE; - // LauncherDelegate implementation. + // ShelfDelegate implementation. virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE; virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE; virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE; @@ -48,14 +47,14 @@ class TestLauncherDelegate : public LauncherDelegate, virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE; private: - static TestLauncherDelegate* instance_; + static TestShelfDelegate* instance_; ShelfModel* model_; - DISALLOW_COPY_AND_ASSIGN(TestLauncherDelegate); + DISALLOW_COPY_AND_ASSIGN(TestShelfDelegate); }; } // namespace test } // namespace ash -#endif // ASH_TEST_TEST_LAUNCHER_DELEGATE_H_ +#endif // ASH_TEST_TEST_SHELF_DELEGATE_H_ diff --git a/ash/test/test_shelf_item_delegate.cc b/ash/test/test_shelf_item_delegate.cc new file mode 100644 index 0000000000..b09d9d79d4 --- /dev/null +++ b/ash/test/test_shelf_item_delegate.cc @@ -0,0 +1,52 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/test/test_shelf_item_delegate.h" + +#include "ash/wm/window_util.h" +#include "ui/aura/window.h" + +namespace ash { +namespace test { + +TestShelfItemDelegate::TestShelfItemDelegate(aura::Window* window) + : window_(window) { +} + +TestShelfItemDelegate::~TestShelfItemDelegate() { +} + +bool TestShelfItemDelegate::ItemSelected(const ui::Event& event) { + if (window_) { + if (window_->type() == aura::client::WINDOW_TYPE_PANEL) + wm::MoveWindowToEventRoot(window_, event); + window_->Show(); + wm::ActivateWindow(window_); + } + return false; +} + +base::string16 TestShelfItemDelegate::GetTitle() { + return window_ ? window_->title() : base::string16(); +} + +ui::MenuModel* TestShelfItemDelegate::CreateContextMenu( + aura::Window* root_window) { + return NULL; +} + +ShelfMenuModel* TestShelfItemDelegate::CreateApplicationMenu(int event_flags) { + return NULL; +} + +bool TestShelfItemDelegate::IsDraggable() { + return true; +} + +bool TestShelfItemDelegate::ShouldShowTooltip() { + return true; +} + +} // namespace test +} // namespace ash diff --git a/ash/test/test_shelf_item_delegate.h b/ash/test/test_shelf_item_delegate.h new file mode 100644 index 0000000000..af8a26ade0 --- /dev/null +++ b/ash/test/test_shelf_item_delegate.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_ +#define ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_ + +#include "ash/shelf/shelf_item_delegate.h" +#include "base/basictypes.h" +#include "base/compiler_specific.h" + +namespace aura { +class Window; +} + +namespace ash { +namespace test { + +// Test implementation of ShelfItemDelegate. +class TestShelfItemDelegate : public ShelfItemDelegate { + public: + explicit TestShelfItemDelegate(aura::Window* window); + virtual ~TestShelfItemDelegate(); + + // ShelfItemDelegate: + virtual bool ItemSelected(const ui::Event& event) OVERRIDE; + virtual base::string16 GetTitle() OVERRIDE; + virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE; + virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE; + virtual bool IsDraggable() OVERRIDE; + virtual bool ShouldShowTooltip() OVERRIDE; + + private: + aura::Window* window_; + + DISALLOW_COPY_AND_ASSIGN(TestShelfItemDelegate); +}; + +} // namespace test +} // namespace ash + +#endif // ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_ diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc index 40284d9f5b..fcf9562031 100644 --- a/ash/test/test_shell_delegate.cc +++ b/ash/test/test_shell_delegate.cc @@ -15,8 +15,8 @@ #include "ash/shell.h" #include "ash/shell/keyboard_controller_proxy_stub.h" #include "ash/shell_window_ids.h" -#include "ash/test/test_launcher_delegate.h" #include "ash/test/test_session_state_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/test/test_system_tray_delegate.h" #include "ash/test/test_user_wallpaper_delegate.h" #include "ash/wm/window_state.h" @@ -101,8 +101,8 @@ app_list::AppListViewDelegate* TestShellDelegate::CreateAppListViewDelegate() { return new app_list::test::AppListTestViewDelegate; } -LauncherDelegate* TestShellDelegate::CreateLauncherDelegate(ShelfModel* model) { - return new TestLauncherDelegate(model); +ShelfDelegate* TestShellDelegate::CreateShelfDelegate(ShelfModel* model) { + return new TestShelfDelegate(model); } SystemTrayDelegate* TestShellDelegate::CreateSystemTrayDelegate() { diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h index 3edd95cc2c..1a34462b09 100644 --- a/ash/test/test_shell_delegate.h +++ b/ash/test/test_shell_delegate.h @@ -41,7 +41,7 @@ class TestShellDelegate : public ShellDelegate { CreateKeyboardControllerProxy() OVERRIDE; virtual content::BrowserContext* GetCurrentBrowserContext() OVERRIDE; virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE; - virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) OVERRIDE; + virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) OVERRIDE; virtual SystemTrayDelegate* CreateSystemTrayDelegate() OVERRIDE; virtual UserWallpaperDelegate* CreateUserWallpaperDelegate() OVERRIDE; virtual CapsLockDelegate* CreateCapsLockDelegate() OVERRIDE; diff --git a/ash/touch/touch_uma.cc b/ash/touch/touch_uma.cc index 29f61850ed..730911f7bd 100644 --- a/ash/touch/touch_uma.cc +++ b/ash/touch/touch_uma.cc @@ -55,8 +55,10 @@ enum UMAEventType { UMA_ET_GESTURE_PINCH_UPDATE_3, UMA_ET_GESTURE_PINCH_UPDATE_4P, UMA_ET_GESTURE_LONG_TAP, + UMA_ET_GESTURE_SHOW_PRESS, + UMA_ET_GESTURE_TAP_CANCEL, // NOTE: Add new event types only immediately above this line. Make sure to - // update the enum list in tools/histogram/histograms.xml accordingly. + // update the enum list in tools/metrics/histogram/histograms.xml accordingly. UMA_ET_COUNT }; @@ -157,6 +159,10 @@ UMAEventType UMAEventTypeFromEvent(const ui::Event& event) { return UMA_ET_GESTURE_MULTIFINGER_SWIPE_3; return UMA_ET_GESTURE_MULTIFINGER_SWIPE; } + case ui::ET_GESTURE_TAP_CANCEL: + return UMA_ET_GESTURE_TAP_CANCEL; + case ui::ET_GESTURE_SHOW_PRESS: + return UMA_ET_GESTURE_SHOW_PRESS; case ui::ET_SCROLL: return UMA_ET_SCROLL; case ui::ET_SCROLL_FLING_START: @@ -164,6 +170,7 @@ UMAEventType UMAEventTypeFromEvent(const ui::Event& event) { case ui::ET_SCROLL_FLING_CANCEL: return UMA_ET_SCROLL_FLING_CANCEL; default: + NOTREACHED(); return UMA_ET_UNKNOWN; } } @@ -292,7 +299,7 @@ void TouchUMA::RecordTouchEvent(aura::Window* target, if (details->last_start_time_.count(event.touch_id())) { base::TimeDelta duration = event.time_stamp() - details->last_start_time_[event.touch_id()]; - UMA_HISTOGRAM_COUNTS_100("Ash.TouchDuration", duration.InMilliseconds()); + UMA_HISTOGRAM_TIMES("Ash.TouchDuration", duration); // Look for touches that were [almost] stationary for a long time. const double kLongStationaryTouchDuration = 10; diff --git a/ash/wm/app_list_controller.cc b/ash/wm/app_list_controller.cc index df3f724e60..2f0ce0420f 100644 --- a/ash/wm/app_list_controller.cc +++ b/ash/wm/app_list_controller.cc @@ -290,6 +290,10 @@ void AppListController::ProcessLocatedEvent(ui::LocatedEvent* event) { ash::internal::kShellWindowId_MenuContainer); if (menu_container->Contains(target)) return; + aura::Window* keyboard_container = root_controller->GetContainer( + ash::internal::kShellWindowId_VirtualKeyboardContainer); + if (keyboard_container->Contains(target)) + return; } } diff --git a/ash/wm/caption_buttons/alternate_frame_size_button.cc b/ash/wm/caption_buttons/alternate_frame_size_button.cc new file mode 100644 index 0000000000..3c5669f532 --- /dev/null +++ b/ash/wm/caption_buttons/alternate_frame_size_button.cc @@ -0,0 +1,211 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/caption_buttons/alternate_frame_size_button.h" + +#include "ash/shell.h" +#include "ash/shell_delegate.h" +#include "ash/touch/touch_uma.h" +#include "ash/wm/window_state.h" +#include "ash/wm/workspace/snap_sizer.h" +#include "ui/gfx/vector2d.h" +#include "ui/views/widget/widget.h" + +namespace { + +// The default delay between the user pressing the size button and the buttons +// adjacent to the size button morphing into buttons for snapping left and +// right. +const int kSetButtonsToSnapModeDelayMs = 150; + +// The amount that a user can overshoot the snap left / snap right button and +// keep the snap left / snap right button pressed. +const int kPressedHitBoundsExpandX = 200; +const int kPressedHitBoundsExpandY = 50; + +} // namespace + +namespace ash { + +AlternateFrameSizeButton::AlternateFrameSizeButton( + views::ButtonListener* listener, + views::Widget* frame, + AlternateFrameSizeButtonDelegate* delegate) + : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE), + frame_(frame), + delegate_(delegate), + set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs), + in_snap_mode_(false), + snap_type_(SNAP_NONE) { +} + +AlternateFrameSizeButton::~AlternateFrameSizeButton() { +} + +bool AlternateFrameSizeButton::OnMousePressed(const ui::MouseEvent& event) { + // The minimize and close buttons are set to snap left and right when snapping + // is enabled. Do not enable snapping if the minimize button is not visible. + // The close button is always visible. + if (IsTriggerableEvent(event) && + !in_snap_mode_ && + delegate_->IsMinimizeButtonVisible()) { + StartSetButtonsToSnapModeTimer(event); + } + FrameCaptionButton::OnMousePressed(event); + return true; +} + +bool AlternateFrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) { + UpdatePressedButton(event); + FrameCaptionButton::OnMouseDragged(event); + return true; +} + +void AlternateFrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) { + if (!IsTriggerableEvent(event) || !CommitSnap(event)) + FrameCaptionButton::OnMouseReleased(event); +} + +void AlternateFrameSizeButton::OnMouseCaptureLost() { + SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES); + FrameCaptionButton::OnMouseCaptureLost(); +} + +void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) { + if (event->details().touch_points() > 1) { + SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES); + return; + } + + if (event->type() == ui::ET_GESTURE_TAP_DOWN) { + StartSetButtonsToSnapModeTimer(*event); + // Go through FrameCaptionButton's handling so that the button gets pressed. + FrameCaptionButton::OnGestureEvent(event); + return; + } + + if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || + event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { + UpdatePressedButton(*event); + event->SetHandled(); + return; + } + + if (event->type() == ui::ET_GESTURE_TAP || + event->type() == ui::ET_GESTURE_SCROLL_END || + event->type() == ui::ET_SCROLL_FLING_START || + event->type() == ui::ET_GESTURE_END) { + if (CommitSnap(*event)) { + if (event->type() == ui::ET_GESTURE_TAP) { + TouchUMA::GetInstance()->RecordGestureAction( + TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP); + } + event->SetHandled(); + return; + } + } + + FrameCaptionButton::OnGestureEvent(event); +} + +void AlternateFrameSizeButton::StartSetButtonsToSnapModeTimer( + const ui::LocatedEvent& event) { + set_buttons_to_snap_mode_timer_event_location_ = event.location(); + if (set_buttons_to_snap_mode_delay_ms_ == 0) { + SetButtonsToSnapMode(); + } else { + set_buttons_to_snap_mode_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_), + this, + &AlternateFrameSizeButton::SetButtonsToSnapMode); + } +} + +void AlternateFrameSizeButton::SetButtonsToSnapMode() { + if (in_snap_mode_) + return; + in_snap_mode_ = true; + delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED, + CAPTION_BUTTON_ICON_RIGHT_SNAPPED, + AlternateFrameSizeButtonDelegate::ANIMATE_YES); +} + +void AlternateFrameSizeButton::UpdatePressedButton( + const ui::LocatedEvent& event) { + if (!in_snap_mode_) { + // Set the buttons adjacent to the size button to snap left and right early + // if the user drags past the drag threshold. + // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap + // mode as a result of an unsupported drag type (e.g. only the right mouse + // button is pressed). + gfx::Vector2d delta( + event.location() - set_buttons_to_snap_mode_timer_event_location_); + if (!set_buttons_to_snap_mode_timer_.IsRunning() || + !views::View::ExceededDragThreshold(delta)) { + return; + } + SetButtonsToSnapMode(); + } + + gfx::Point event_location_in_screen(event.location()); + views::View::ConvertPointToScreen(this, &event_location_in_screen); + + gfx::Insets pressed_button_hittest_insets(-kPressedHitBoundsExpandY, + -kPressedHitBoundsExpandX, + -kPressedHitBoundsExpandY, + -kPressedHitBoundsExpandX); + const FrameCaptionButton* pressed_button = delegate_->PressButtonAt( + event_location_in_screen, pressed_button_hittest_insets); + snap_type_ = SNAP_NONE; + if (pressed_button) { + switch (pressed_button->icon()) { + case CAPTION_BUTTON_ICON_LEFT_SNAPPED: + snap_type_ = SNAP_LEFT; + break; + case CAPTION_BUTTON_ICON_RIGHT_SNAPPED: + snap_type_ = SNAP_RIGHT; + break; + case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE: + // snap_type_ = SNAP_NONE + break; + case CAPTION_BUTTON_ICON_MINIMIZE: + case CAPTION_BUTTON_ICON_CLOSE: + NOTREACHED(); + break; + } + } +} + +bool AlternateFrameSizeButton::CommitSnap(const ui::LocatedEvent& event) { + // The position of |event| may be different than the position of the previous + // event. + UpdatePressedButton(event); + + if (in_snap_mode_ && + (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) { + using internal::SnapSizer; + SnapSizer::SnapWindow(ash::wm::GetWindowState(frame_->GetNativeWindow()), + snap_type_ == SNAP_LEFT ? + SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE); + ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction( + snap_type_ == SNAP_LEFT ? + ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT : + ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT); + SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_NO); + return true; + } + SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES); + return false; +} + +void AlternateFrameSizeButton::SetButtonsToNormalMode( + AlternateFrameSizeButtonDelegate::Animate animate) { + in_snap_mode_ = false; + snap_type_ = SNAP_NONE; + set_buttons_to_snap_mode_timer_.Stop(); + delegate_->SetButtonsToNormal(animate); +} + +} // namespace ash diff --git a/ash/wm/caption_buttons/alternate_frame_size_button.h b/ash/wm/caption_buttons/alternate_frame_size_button.h new file mode 100644 index 0000000000..586eabbeb9 --- /dev/null +++ b/ash/wm/caption_buttons/alternate_frame_size_button.h @@ -0,0 +1,100 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_ +#define ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_ + +#include "ash/ash_export.h" +#include "ash/wm/caption_buttons/alternate_frame_size_button_delegate.h" +#include "ash/wm/caption_buttons/frame_caption_button.h" +#include "ash/wm/workspace/snap_types.h" +#include "base/timer/timer.h" + +namespace views { +class Widget; +} + +namespace ash { +class AlternateFrameSizeButtonDelegate; + +// The maximize/restore button when using the alternate button style. +// When the mouse is pressed over the size button or the size button is touched: +// - The minimize and close buttons are set to snap left and snap right +// respectively. +// - The pressed button is updated during the drag to reflect the button +// underneath the mouse cursor. (The size button is potentially unpressed). +// When the drag terminates, the action for the pressed button is executed. +// For the sake of simplicity, the size button is the event handler for a click +// starting on the size button and the entire drag (including when the size +// button is unpressed). +class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton { + public: + AlternateFrameSizeButton(views::ButtonListener* listener, + views::Widget* frame, + AlternateFrameSizeButtonDelegate* delegate); + + virtual ~AlternateFrameSizeButton(); + + // views::CustomButton overrides: + virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; + virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; + virtual void OnMouseCaptureLost() OVERRIDE; + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + + void set_delay_to_set_buttons_to_snap_mode(int delay_ms) { + set_buttons_to_snap_mode_delay_ms_ = delay_ms; + } + + private: + // Starts |set_buttons_to_snap_mode_timer_|. + void StartSetButtonsToSnapModeTimer(const ui::LocatedEvent& event); + + // Sets the buttons adjacent to the size button to snap left and right. + void SetButtonsToSnapMode(); + + // Updates the pressed button based on |event_location|. + void UpdatePressedButton(const ui::LocatedEvent& event); + + // Snaps |frame_| according to |snap_type_|. Returns true if |frame_| was + // snapped. + bool CommitSnap(const ui::LocatedEvent& event); + + // Sets the buttons adjacent to the size button to minimize and close again. + // Clears any state set while snapping was enabled. |animate| indicates + // whether the buttons should animate back to their original icons. + void SetButtonsToNormalMode( + AlternateFrameSizeButtonDelegate::Animate animate); + + // Widget that the size button acts on. + views::Widget* frame_; + + // Not owned. + AlternateFrameSizeButtonDelegate* delegate_; + + // Location of the event which started |set_buttons_to_snap_mode_timer_| in + // view coordinates. + gfx::Point set_buttons_to_snap_mode_timer_event_location_; + + // The delay between the user pressing the size button and the buttons + // adjacent to the size button morphing into buttons for snapping left and + // right. + int set_buttons_to_snap_mode_delay_ms_; + + base::OneShotTimer<AlternateFrameSizeButton> set_buttons_to_snap_mode_timer_; + + // Whether the buttons adjacent to the size button snap the window left and + // right. + bool in_snap_mode_; + + // The action of the currently pressed button. If |snap_type_| == SNAP_NONE, + // the size button's default action is run when clicked. + SnapType snap_type_; + + DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButton); +}; + +} // namespace ash + +#endif // ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_ diff --git a/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h b/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h new file mode 100644 index 0000000000..c1472b21bf --- /dev/null +++ b/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h @@ -0,0 +1,57 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_ +#define ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_ + +#include "ash/ash_export.h" +#include "ash/wm/caption_buttons/caption_button_types.h" + +namespace gfx { +class Insets; +class Point; +} + +namespace ash { +class FrameCaptionButton; + +// Delegate interface for AlternateFrameSizeButton. +class ASH_EXPORT AlternateFrameSizeButtonDelegate { + public: + enum Animate { + ANIMATE_YES, + ANIMATE_NO + }; + + // Returns whether the minimize button is visible. + virtual bool IsMinimizeButtonVisible() const = 0; + + // Reset the caption button views::Button::ButtonState back to normal. If + // |animate| is ANIMATE_YES, the buttons will crossfade back to their + // original icons. + virtual void SetButtonsToNormal(Animate animate) = 0; + + // Sets the minimize and close button icons. The buttons will crossfade to + // their new icons if |animate| is ANIMATE_YES. + virtual void SetButtonIcons(CaptionButtonIcon left_button_action, + CaptionButtonIcon right_button_action, + Animate animate) = 0; + + // Presses the button at |position_in_screen| and unpresses any other pressed + // caption buttons. + // |pressed_button_hittest_insets| indicates how much the hittest insets for + // the currently pressed button should be expanded if no button was found at + // |position_in_screen| using the normal button hittest insets. + // Returns the button which was pressed. + virtual const FrameCaptionButton* PressButtonAt( + const gfx::Point& position_in_screen, + const gfx::Insets& pressed_button_hittest_insets) const = 0; + + protected: + virtual ~AlternateFrameSizeButtonDelegate() {} +}; + +} // namespace ash + +#endif // ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_ diff --git a/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc b/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc new file mode 100644 index 0000000000..75c0f50604 --- /dev/null +++ b/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc @@ -0,0 +1,367 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/caption_buttons/alternate_frame_size_button.h" + +#include "ash/ash_switches.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/wm/caption_buttons/frame_caption_button.h" +#include "ash/wm/caption_buttons/frame_caption_button_container_view.h" +#include "ash/wm/window_state.h" +#include "ash/wm/workspace/snap_sizer.h" +#include "base/command_line.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" +#include "ui/events/gestures/gesture_configuration.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace ash { +namespace test { + +namespace { + +class TestWidgetDelegate : public views::WidgetDelegateView { + public: + TestWidgetDelegate() {} + virtual ~TestWidgetDelegate() {} + + // Overridden from views::WidgetDelegate: + virtual views::View* GetContentsView() OVERRIDE { + return this; + } + virtual bool CanResize() const OVERRIDE { + return true; + } + virtual bool CanMaximize() const OVERRIDE { + return true; + } + + ash::FrameCaptionButtonContainerView* caption_button_container() { + return caption_button_container_; + } + + private: + // Overridden from views::View: + virtual void Layout() OVERRIDE { + caption_button_container_->Layout(); + + // Right align the caption button container. + gfx::Size preferred_size = caption_button_container_->GetPreferredSize(); + caption_button_container_->SetBounds(width() - preferred_size.width(), 0, + preferred_size.width(), preferred_size.height()); + } + + virtual void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) OVERRIDE { + if (details.is_add && details.child == this) { + caption_button_container_ = new FrameCaptionButtonContainerView( + GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + AddChildView(caption_button_container_); + } + } + + // Not owned. + ash::FrameCaptionButtonContainerView* caption_button_container_; + + DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); +}; + +} // namespace + +class AlternateFrameSizeButtonTest : public AshTestBase { + public: + AlternateFrameSizeButtonTest() {} + virtual ~AlternateFrameSizeButtonTest() {} + + // Returns the center point of |view| in screen coordinates. + gfx::Point CenterPointInScreen(views::View* view) { + return view->GetBoundsInScreen().CenterPoint(); + } + + // Returns true if the window is snapped to |edge|. + bool IsSnapped(internal::SnapSizer::Edge edge) const { + ash::wm::WindowShowType show_type = window_state()->window_show_type(); + if (edge == internal::SnapSizer::LEFT_EDGE) + return show_type == ash::wm::SHOW_TYPE_LEFT_SNAPPED; + else + return show_type == ash::wm::SHOW_TYPE_RIGHT_SNAPPED; + } + + // Returns true if all three buttons are in the normal state. + bool AllButtonsInNormalState() const { + return minimize_button_->state() == views::Button::STATE_NORMAL && + size_button_->state() == views::Button::STATE_NORMAL && + close_button_->state() == views::Button::STATE_NORMAL; + } + + // Creates a widget with |delegate|. The returned widget takes ownership of + // |delegate|. + views::Widget* CreateWidget(views::WidgetDelegate* delegate) { + views::Widget* widget = new views::Widget; + views::Widget::InitParams params( + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.context = CurrentContext(); + params.delegate = delegate; + params.bounds = gfx::Rect(10, 10, 100, 100); + widget->Init(params); + widget->Show(); + return widget; + } + + // AshTestBase overrides: + virtual void SetUp() OVERRIDE { + AshTestBase::SetUp(); + + CommandLine* command_line = CommandLine::ForCurrentProcess(); + command_line->AppendSwitch( + switches::kAshEnableAlternateFrameCaptionButtonStyle); + CHECK(!command_line->HasSwitch(switches::kAshMultipleSnapWindowWidths)); + + TestWidgetDelegate* delegate = new TestWidgetDelegate(); + window_state_ = ash::wm::GetWindowState( + CreateWidget(delegate)->GetNativeWindow()); + + FrameCaptionButtonContainerView::TestApi test( + delegate->caption_button_container()); + + minimize_button_ = test.minimize_button(); + size_button_ = test.size_button(); + static_cast<AlternateFrameSizeButton*>( + size_button_)->set_delay_to_set_buttons_to_snap_mode(0); + close_button_ = test.close_button(); + } + + ash::wm::WindowState* window_state() { return window_state_; } + const ash::wm::WindowState* window_state() const { return window_state_; } + + FrameCaptionButton* minimize_button() { return minimize_button_; } + FrameCaptionButton* size_button() { return size_button_; } + FrameCaptionButton* close_button() { return close_button_; } + + private: + // Not owned. + ash::wm::WindowState* window_state_; + FrameCaptionButton* minimize_button_; + FrameCaptionButton* size_button_; + FrameCaptionButton* close_button_; + + DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButtonTest); +}; + +// Tests that pressing the left mouse button or tapping down on the size button +// puts the button into the pressed state. +TEST_F(AlternateFrameSizeButtonTest, PressedState) { + aura::test::EventGenerator& generator = GetEventGenerator(); + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressLeftButton(); + EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); + generator.ReleaseLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state()); + + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressTouchId(3); + EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); + generator.ReleaseTouchId(3); + RunAllPendingInMessageLoop(); + EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state()); +} + +// Tests that clicking on the size button toggles between the maximized and +// normal state. +TEST_F(AlternateFrameSizeButtonTest, ClickSizeButtonTogglesMaximize) { + EXPECT_FALSE(window_state()->IsMaximized()); + + aura::test::EventGenerator& generator = GetEventGenerator(); + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.ClickLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(window_state()->IsMaximized()); + + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.ClickLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_FALSE(window_state()->IsMaximized()); + + generator.GestureTapAt(CenterPointInScreen(size_button())); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(window_state()->IsMaximized()); + + generator.GestureTapAt(CenterPointInScreen(size_button())); + RunAllPendingInMessageLoop(); + EXPECT_FALSE(window_state()->IsMaximized()); +} + +// Test that clicking + dragging to a button adjacent to the size button snaps +// the window left or right. +TEST_F(AlternateFrameSizeButtonTest, ButtonDrag) { + EXPECT_TRUE(window_state()->IsNormalShowState()); + EXPECT_FALSE(window_state()->IsSnapped()); + + // 1) Test by dragging the mouse. + // Snap right. + aura::test::EventGenerator& generator = GetEventGenerator(); + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressLeftButton(); + generator.MoveMouseTo(CenterPointInScreen(close_button())); + generator.ReleaseLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE)); + + // Snap left. + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressLeftButton(); + generator.MoveMouseTo(CenterPointInScreen(minimize_button())); + generator.ReleaseLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE)); + + // 2) Test with scroll gestures. + // Snap right. + generator.GestureScrollSequence( + CenterPointInScreen(size_button()), + CenterPointInScreen(close_button()), + base::TimeDelta::FromMilliseconds(100), + 3); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE)); + + // Snap left. + generator.GestureScrollSequence( + CenterPointInScreen(size_button()), + CenterPointInScreen(minimize_button()), + base::TimeDelta::FromMilliseconds(100), + 3); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE)); + + // 3) Test with tap gestures. + const int touch_default_radius = + ui::GestureConfiguration::default_radius(); + ui::GestureConfiguration::set_default_radius(0); + // Snap right. + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button())); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE)); + // Snap left. + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button())); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE)); + ui::GestureConfiguration::set_default_radius(touch_default_radius); +} + +// Test that clicking, dragging, and overshooting the minimize button a bit +// horizontally still snaps the window left. +TEST_F(AlternateFrameSizeButtonTest, SnapLeftOvershootMinimize) { + EXPECT_TRUE(window_state()->IsNormalShowState()); + EXPECT_FALSE(window_state()->IsSnapped()); + + aura::test::EventGenerator& generator = GetEventGenerator(); + generator.MoveMouseTo(CenterPointInScreen(size_button())); + + generator.PressLeftButton(); + // Move to the minimize button. + generator.MoveMouseTo(CenterPointInScreen(minimize_button())); + // Overshoot the minimize button. + generator.MoveMouseBy(-minimize_button()->width(), 0); + generator.ReleaseLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE)); +} + +// Test that right clicking the size button has no effect. +TEST_F(AlternateFrameSizeButtonTest, RightMouseButton) { + EXPECT_TRUE(window_state()->IsNormalShowState()); + EXPECT_FALSE(window_state()->IsSnapped()); + + aura::test::EventGenerator& generator = GetEventGenerator(); + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressRightButton(); + generator.ReleaseRightButton(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(window_state()->IsNormalShowState()); + EXPECT_FALSE(window_state()->IsSnapped()); +} + +// Test that upon releasing the mouse button after having pressed the size +// button +// - The state of all the caption buttons is reset. +// - The icon displayed by all of the caption buttons is reset. +TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) { + EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); + EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); + EXPECT_TRUE(AllButtonsInNormalState()); + + // Pressing the size button should result in the size button being pressed and + // the minimize and close button icons changing. + aura::test::EventGenerator& generator = GetEventGenerator(); + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressLeftButton(); + EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state()); + EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); + EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); + EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); + EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); + + // Dragging the mouse over the minimize button should press the minimize + // button and the minimize and close button icons should stay changed. + generator.MoveMouseTo(CenterPointInScreen(minimize_button())); + EXPECT_EQ(views::Button::STATE_PRESSED, minimize_button()->state()); + EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state()); + EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); + EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); + EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); + + // Release the mouse, snapping the window left. + generator.ReleaseLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE)); + + // None of the buttons should stay pressed and the buttons should have their + // regular icons. + EXPECT_TRUE(AllButtonsInNormalState()); + EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); + EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); + + // Repeat test but release button where it does not affect the window's state + // because the code path is different. + generator.MoveMouseTo(CenterPointInScreen(size_button())); + generator.PressLeftButton(); + EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state()); + EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); + EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); + EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); + EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); + + const gfx::Rect& kWorkAreaBoundsInScreen = + ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); + generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left()); + + // None of the buttons should be pressed because we are really far away from + // any of the caption buttons. The minimize and close button icons should + // be changed because the mouse is pressed. + EXPECT_TRUE(AllButtonsInNormalState()); + EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); + EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); + + // Release the mouse. The window should stay snapped left. + generator.ReleaseLeftButton(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE)); + + // The buttons should stay unpressed and the buttons should now have their + // regular icons. + EXPECT_TRUE(AllButtonsInNormalState()); + EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); + EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); +} + +} // namespace test +} // namespace ash diff --git a/ash/wm/caption_buttons/caption_button_types.h b/ash/wm/caption_buttons/caption_button_types.h new file mode 100644 index 0000000000..b34a861508 --- /dev/null +++ b/ash/wm/caption_buttons/caption_button_types.h @@ -0,0 +1,30 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_ +#define ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_ + +namespace ash { + +// These are the types of maximization we know. +enum MaximizeBubbleFrameState { + FRAME_STATE_NONE = 0, + FRAME_STATE_FULL = 1, // This is the full maximized state. + FRAME_STATE_SNAP_LEFT = 2, + FRAME_STATE_SNAP_RIGHT = 3 +}; + +// These are the icon types that a caption button can have. The size button's +// action (SnapType) can be different from its icon. +enum CaptionButtonIcon { + CAPTION_BUTTON_ICON_MINIMIZE, + CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, + CAPTION_BUTTON_ICON_CLOSE, + CAPTION_BUTTON_ICON_LEFT_SNAPPED, + CAPTION_BUTTON_ICON_RIGHT_SNAPPED, +}; + +} // namespace ash + +#endif // ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_ diff --git a/ash/wm/caption_buttons/frame_caption_button.cc b/ash/wm/caption_buttons/frame_caption_button.cc new file mode 100644 index 0000000000..5a6a835683 --- /dev/null +++ b/ash/wm/caption_buttons/frame_caption_button.cc @@ -0,0 +1,177 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/caption_buttons/frame_caption_button.h" + +#include "grit/ash_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/canvas.h" + +namespace ash { + +namespace { + +// The duration of the crossfade animation when swapping the button's icon. +const int kCrossfadeDurationMs = 200; + +} // namespace + +// static +const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton"; + +FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener, + CaptionButtonIcon icon) + : ImageButton(listener), + icon_(icon), + style_(STYLE_SHORT_RESTORED), + last_paint_scale_(1.0f), + animation_(new gfx::SlideAnimation(this)) { + animation_->Reset(1); + UpdateImages(); +} + +FrameCaptionButton::~FrameCaptionButton() { +} + +void FrameCaptionButton::SetIcon(CaptionButtonIcon icon, Animate animate) { + if (icon_ == icon) + return; + + if (animate == ANIMATE_YES) { + gfx::Canvas canvas(size(), last_paint_scale_, false); + OnPaint(&canvas); + crossfade_image_ = gfx::ImageSkia(canvas.ExtractImageRep()); + + icon_ = icon; + UpdateImages(); + + animation_->Reset(0); + animation_->SetSlideDuration(kCrossfadeDurationMs); + animation_->Show(); + } else { + animation_->Reset(1); + icon_ = icon; + UpdateImages(); + } +} + +void FrameCaptionButton::SetStyle(Style style) { + if (style_ == style) + return; + animation_->Reset(1); + style_ = style; + UpdateImages(); +} + +const char* FrameCaptionButton::GetClassName() const { + return kViewClassName; +} + +void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) { + last_paint_scale_ = canvas->image_scale(); + int alpha = static_cast<int>(animation_->GetCurrentValue() * 255); + int crossfade_alpha = 255 - alpha; + if (crossfade_alpha > 0 && !crossfade_image_.isNull()) { + gfx::Canvas composed_canvas(size(), last_paint_scale_, false); + SkPaint paint; + paint.setAlpha(crossfade_alpha); + paint.setXfermodeMode(SkXfermode::kPlus_Mode); + composed_canvas.DrawImageInt(crossfade_image_, 0, 0, paint); + paint.setAlpha(alpha); + ImageButton::OnPaint(&composed_canvas); + + canvas->DrawImageInt( + gfx::ImageSkia(composed_canvas.ExtractImageRep()), 0, 0); + } else { + ImageButton::OnPaint(canvas); + } +} + +void FrameCaptionButton::OnGestureEvent(ui::GestureEvent* event) { + // ImageButton does not become pressed when the user drags off and then back + // onto the button. Make FrameCaptionButton pressed in this case because this + // behavior is more consistent with AlternateFrameSizeButton. + if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || + event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { + if (HitTestPoint(event->location())) { + SetState(STATE_PRESSED); + RequestFocus(); + event->StopPropagation(); + } else { + SetState(STATE_NORMAL); + } + } else if (event->type() == ui::ET_GESTURE_SCROLL_END) { + if (HitTestPoint(event->location())) { + SetState(STATE_HOVERED); + NotifyClick(*event); + event->StopPropagation(); + } + } + ImageButton::OnGestureEvent(event); +} + +void FrameCaptionButton::StateChanged() { + if (state_ == STATE_HOVERED || state_ == STATE_PRESSED) + animation_->Reset(1); +} + +void FrameCaptionButton::UpdateImages() { + switch (icon_) { + case CAPTION_BUTTON_ICON_MINIMIZE: + SetImages(IDR_AURA_WINDOW_MINIMIZE_SHORT, + IDR_AURA_WINDOW_MINIMIZE_SHORT_H, + IDR_AURA_WINDOW_MINIMIZE_SHORT_P); + break; + case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE: + case CAPTION_BUTTON_ICON_LEFT_SNAPPED: + case CAPTION_BUTTON_ICON_RIGHT_SNAPPED: + if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) { + SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P); + } else if (style_ == STYLE_SHORT_RESTORED) { + SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P); + } else { + SetImages(IDR_AURA_WINDOW_MAXIMIZE, + IDR_AURA_WINDOW_MAXIMIZE_H, + IDR_AURA_WINDOW_MAXIMIZE_P); + } + break; + case CAPTION_BUTTON_ICON_CLOSE: + if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) { + SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P); + } else if (style_ == STYLE_SHORT_RESTORED) { + SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P); + } else { + SetImages(IDR_AURA_WINDOW_CLOSE, + IDR_AURA_WINDOW_CLOSE_H, + IDR_AURA_WINDOW_CLOSE_P); + } + break; + } + + SchedulePaint(); +} + +void FrameCaptionButton::SetImages(int normal_image_id, + int hot_image_id, + int pushed_image_id) { + ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); + SetImage(STATE_NORMAL, resource_bundle.GetImageSkiaNamed(normal_image_id)); + SetImage(STATE_HOVERED, resource_bundle.GetImageSkiaNamed(hot_image_id)); + SetImage(STATE_PRESSED, resource_bundle.GetImageSkiaNamed(pushed_image_id)); +} + +void FrameCaptionButton::AnimationProgressed(const gfx::Animation* animation) { + SchedulePaint(); +} + +} // namespace ash diff --git a/ash/wm/caption_buttons/frame_caption_button.h b/ash/wm/caption_buttons/frame_caption_button.h new file mode 100644 index 0000000000..61ed922241 --- /dev/null +++ b/ash/wm/caption_buttons/frame_caption_button.h @@ -0,0 +1,94 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_ +#define ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_ + +#include "ash/ash_export.h" +#include "ash/wm/caption_buttons/caption_button_types.h" +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/views/controls/button/image_button.h" + +namespace gfx { +class SlideAnimation; +} + +namespace ash { + +// Base class for the window caption buttons (minimize, maximize, restore, +// close). +class ASH_EXPORT FrameCaptionButton : public views::ImageButton { + public: + enum Animate { + ANIMATE_YES, + ANIMATE_NO + }; + + enum Style { + // Restored tabbed browser windows. + STYLE_TALL_RESTORED, + + // All other restored windows. + STYLE_SHORT_RESTORED, + + // Maximized or fullscreen windows. + STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN + }; + + static const char kViewClassName[]; + + FrameCaptionButton(views::ButtonListener* listener, CaptionButtonIcon icon); + virtual ~FrameCaptionButton(); + + // Sets the button's icon. If |animate| is ANIMATE_YES, the button crossfades + // to the new icon. + void SetIcon(CaptionButtonIcon icon, Animate animate); + + // Sets the button's style. The transition to the new style is not animated. + void SetStyle(Style style); + + // views::View overrides: + virtual const char* GetClassName() const OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + + CaptionButtonIcon icon() const { + return icon_; + } + + protected: + // views::CustomButton overrides: + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + virtual void StateChanged() OVERRIDE; + + private: + // Updates the button's images based on the current icon and style. + void UpdateImages(); + + // Sets the button's images based on the given ids. + void SetImages(int normal_image_id, int hot_image_id, int pushed_image_id); + + // gfx::AnimationDelegate override: + virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE; + + // The button's current icon. + CaptionButtonIcon icon_; + + // The button's current style. + Style style_; + + // The scale at which the button was previously painted. + float last_paint_scale_; + + // The image to crossfade from. + gfx::ImageSkia crossfade_image_; + + scoped_ptr<gfx::SlideAnimation> animation_; + + DISALLOW_COPY_AND_ASSIGN(FrameCaptionButton); +}; + +} // namespace ash + +#endif // ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_ diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view.cc b/ash/wm/caption_buttons/frame_caption_button_container_view.cc index 40708b86c7..2e4d323dac 100644 --- a/ash/wm/caption_buttons/frame_caption_button_container_view.cc +++ b/ash/wm/caption_buttons/frame_caption_button_container_view.cc @@ -7,8 +7,9 @@ #include "ash/ash_switches.h" #include "ash/shell.h" #include "ash/shell_delegate.h" +#include "ash/wm/caption_buttons/alternate_frame_size_button.h" +#include "ash/wm/caption_buttons/frame_caption_button.h" #include "ash/wm/caption_buttons/frame_maximize_button.h" -#include "ash/wm/window_state.h" #include "grit/ash_resources.h" #include "grit/ui_strings.h" // Accessibility names #include "ui/base/hit_test.h" @@ -16,7 +17,8 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/gfx/canvas.h" -#include "ui/views/border.h" +#include "ui/gfx/insets.h" +#include "ui/gfx/point.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -54,7 +56,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( bool alternate_style = switches::UseAlternateFrameCaptionButtonStyle(); // Insert the buttons left to right. - minimize_button_ = new views::ImageButton(this); + minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); minimize_button_->SetAccessibleName( l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); // Hide |minimize_button_| when using the non-alternate button style because @@ -65,7 +67,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( AddChildView(minimize_button_); if (alternate_style) - size_button_ = new views::ImageButton(this); + size_button_ = new AlternateFrameSizeButton(this, frame, this); else size_button_ = new FrameMaximizeButton(this, frame); size_button_->SetAccessibleName( @@ -73,7 +75,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( size_button_->SetVisible(frame_->widget_delegate()->CanMaximize()); AddChildView(size_button_); - close_button_ = new views::ImageButton(this); + close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE); close_button_->SetAccessibleName( l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); AddChildView(close_button_); @@ -92,8 +94,7 @@ FrameCaptionButtonContainerView::GetOldStyleSizeButton() { } void FrameCaptionButtonContainerView::ResetWindowControls() { - minimize_button_->SetState(views::CustomButton::STATE_NORMAL); - size_button_->SetState(views::CustomButton::STATE_NORMAL); + SetButtonsToNormal(ANIMATE_NO); } int FrameCaptionButtonContainerView::NonClientHitTest( @@ -128,45 +129,19 @@ gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() { } void FrameCaptionButtonContainerView::Layout() { - SetButtonImages(minimize_button_, - IDR_AURA_WINDOW_MINIMIZE_SHORT, - IDR_AURA_WINDOW_MINIMIZE_SHORT_H, - IDR_AURA_WINDOW_MINIMIZE_SHORT_P); + FrameCaptionButton::Style style = FrameCaptionButton::STYLE_SHORT_RESTORED; if (header_style_ == HEADER_STYLE_SHORT) { - // The new assets only make sense if the window is maximized or fullscreen - // because we usually use a black header in this case. - if ((frame_->IsMaximized() || frame_->IsFullscreen()) && - wm::GetWindowState( - frame_->GetNativeWindow())->tracked_by_workspace()) { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P); - SetButtonImages(close_button_, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P); - } else { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P); - SetButtonImages(close_button_, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P); - } + if (frame_->IsMaximized() || frame_->IsFullscreen()) + style = FrameCaptionButton::STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN; + // Else: FrameCaptionButton::STYLE_SHORT_RESTORED; } else { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZE, - IDR_AURA_WINDOW_MAXIMIZE_H, - IDR_AURA_WINDOW_MAXIMIZE_P); - SetButtonImages(close_button_, - IDR_AURA_WINDOW_CLOSE, - IDR_AURA_WINDOW_CLOSE_H, - IDR_AURA_WINDOW_CLOSE_P); + style = FrameCaptionButton::STYLE_TALL_RESTORED; } + minimize_button_->SetStyle(style); + size_button_->SetStyle(style); + close_button_->SetStyle(style); + int x = 0; for (int i = 0; i < child_count(); ++i) { views::View* child = child_at(i); @@ -210,15 +185,14 @@ void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender, ui::ScopedAnimationDurationScaleMode::SLOW_DURATION)); } + // Abort any animations of the button icons. + SetButtonsToNormal(ANIMATE_NO); + ash::UserMetricsAction action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; if (sender == minimize_button_) { - // The minimize button may move out from under the cursor. - ResetWindowControls(); frame_->Minimize(); } else if (sender == size_button_) { - // The size button may move out from under the cursor. - ResetWindowControls(); if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen. frame_->SetFullscreen(false); action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN; @@ -238,18 +212,65 @@ void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender, ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(action); } -void FrameCaptionButtonContainerView::SetButtonImages( - views::ImageButton* button, - int normal_image_id, - int hot_image_id, - int pushed_image_id) { - ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); - button->SetImage(views::CustomButton::STATE_NORMAL, - resource_bundle.GetImageSkiaNamed(normal_image_id)); - button->SetImage(views::CustomButton::STATE_HOVERED, - resource_bundle.GetImageSkiaNamed(hot_image_id)); - button->SetImage(views::CustomButton::STATE_PRESSED, - resource_bundle.GetImageSkiaNamed(pushed_image_id)); +bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const { + return minimize_button_->visible(); +} + +void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) { + SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE, + animate); + minimize_button_->SetState(views::Button::STATE_NORMAL); + size_button_->SetState(views::Button::STATE_NORMAL); + close_button_->SetState(views::Button::STATE_NORMAL); +} + +void FrameCaptionButtonContainerView::SetButtonIcons( + CaptionButtonIcon minimize_button_icon, + CaptionButtonIcon close_button_icon, + Animate animate) { + FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ? + FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO; + minimize_button_->SetIcon(minimize_button_icon, fcb_animate); + close_button_->SetIcon(close_button_icon, fcb_animate); +} + +const FrameCaptionButton* +FrameCaptionButtonContainerView::PressButtonAt( + const gfx::Point& position_in_screen, + const gfx::Insets& pressed_hittest_outer_insets) const { + DCHECK(switches::UseAlternateFrameCaptionButtonStyle()); + gfx::Point position(position_in_screen); + views::View::ConvertPointFromScreen(this, &position); + + FrameCaptionButton* buttons[] = { + close_button_, size_button_, minimize_button_ + }; + FrameCaptionButton* pressed_button = NULL; + for (size_t i = 0; i < arraysize(buttons); ++i) { + FrameCaptionButton* button = buttons[i]; + if (!button->visible()) + continue; + + if (button->state() == views::Button::STATE_PRESSED) { + gfx::Rect expanded_bounds = button->bounds(); + expanded_bounds.Inset(pressed_hittest_outer_insets); + if (expanded_bounds.Contains(position)) { + pressed_button = button; + // Do not break in order to give preference to buttons which are + // closer to |position_in_screen| than the currently pressed button. + // TODO(pkotwicz): Make the caption buttons not overlap. + } + } else if (ConvertPointToViewAndHitTest(this, button, position)) { + pressed_button = button; + break; + } + } + + for (size_t i = 0; i < arraysize(buttons); ++i) { + buttons[i]->SetState(buttons[i] == pressed_button ? + views::Button::STATE_PRESSED : views::Button::STATE_NORMAL); + } + return pressed_button; } } // namespace ash diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view.h b/ash/wm/caption_buttons/frame_caption_button_container_view.h index f8e1a92fcb..4fd663b5d7 100644 --- a/ash/wm/caption_buttons/frame_caption_button_container_view.h +++ b/ash/wm/caption_buttons/frame_caption_button_container_view.h @@ -6,23 +6,25 @@ #define ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_ #include "ash/ash_export.h" +#include "ash/wm/caption_buttons/alternate_frame_size_button_delegate.h" #include "ui/gfx/image/image_skia.h" #include "ui/views/controls/button/button.h" #include "ui/views/view.h" namespace views { -class ImageButton; class Widget; } namespace ash { +class FrameCaptionButton; class FrameMaximizeButton; // Container view for the frame caption buttons. It performs the appropriate // action when a caption button is clicked. class ASH_EXPORT FrameCaptionButtonContainerView : public views::View, - public views::ButtonListener { + public views::ButtonListener, + public AlternateFrameSizeButtonDelegate { public: static const char kViewClassName[]; @@ -54,15 +56,15 @@ class ASH_EXPORT FrameCaptionButtonContainerView : container_view_(container_view) { } - views::ImageButton* minimize_button() const { + FrameCaptionButton* minimize_button() const { return container_view_->minimize_button_; } - views::ImageButton* size_button() const { + FrameCaptionButton* size_button() const { return container_view_->size_button_; } - views::ImageButton* close_button() const { + FrameCaptionButton* close_button() const { return container_view_->close_button_; } @@ -102,11 +104,15 @@ class ASH_EXPORT FrameCaptionButtonContainerView virtual void ButtonPressed(views::Button* sender, const ui::Event& event) OVERRIDE; - // Sets the images for a button based on the given ids. - void SetButtonImages(views::ImageButton* button, - int normal_image_id, - int hot_image_id, - int pushed_image_id); + // AlternateFrameSizeButton::Delegate overrides: + virtual bool IsMinimizeButtonVisible() const OVERRIDE; + virtual void SetButtonsToNormal(Animate animate) OVERRIDE; + virtual void SetButtonIcons(CaptionButtonIcon minimize_button_icon, + CaptionButtonIcon close_button_icon, + Animate animate) OVERRIDE; + virtual const FrameCaptionButton* PressButtonAt( + const gfx::Point& position_in_screen, + const gfx::Insets& pressed_hittest_outer_insets) const OVERRIDE; // The widget that the buttons act on. views::Widget* frame_; @@ -118,9 +124,9 @@ class ASH_EXPORT FrameCaptionButtonContainerView // The buttons. In the normal button style, at most one of |minimize_button_| // and |size_button_| is visible. - views::ImageButton* minimize_button_; - views::ImageButton* size_button_; - views::ImageButton* close_button_; + FrameCaptionButton* minimize_button_; + FrameCaptionButton* size_button_; + FrameCaptionButton* close_button_; DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerView); }; diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc b/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc index 6737b92d25..589123125e 100644 --- a/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc +++ b/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc @@ -6,13 +6,12 @@ #include "ash/ash_switches.h" #include "ash/test/ash_test_base.h" +#include "ash/wm/caption_buttons/frame_caption_button.h" #include "base/command_line.h" #include "grit/ash_resources.h" #include "ui/aura/root_window.h" #include "ui/base/resource/resource_bundle.h" #include "ui/views/border.h" -#include "ui/views/controls/button/custom_button.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -68,8 +67,8 @@ class FrameCaptionButtonContainerViewTest : public ash::test::AshTestBase { // Tests that |leftmost| and |rightmost| are at |container|'s edges. bool CheckButtonsAtEdges(FrameCaptionButtonContainerView* container, - const views::CustomButton& leftmost, - const views::CustomButton& rightmost) { + const ash::FrameCaptionButton& leftmost, + const ash::FrameCaptionButton& rightmost) { gfx::Rect expected(container->GetPreferredSize()); gfx::Rect container_size(container->GetPreferredSize()); @@ -89,7 +88,7 @@ class FrameCaptionButtonContainerViewTest : public ash::test::AshTestBase { } // Returns true if the images for |button|'s states match the passed in ids. - bool ImagesMatch(views::ImageButton* button, + bool ImagesMatch(ash::FrameCaptionButton* button, int normal_image_id, int hovered_image_id, int pressed_image_id) { @@ -244,7 +243,6 @@ class FrameCaptionButtonContainerViewTestAlternateStyle FrameCaptionButtonContainerViewTest::SetUp(); CommandLine::ForCurrentProcess()->AppendSwitch( switches::kAshEnableAlternateFrameCaptionButtonStyle); - ASSERT_TRUE(switches::UseAlternateFrameCaptionButtonStyle()); } private: @@ -254,6 +252,11 @@ class FrameCaptionButtonContainerViewTestAlternateStyle // Test how the alternate button style affects which buttons are visible in the // default case. TEST_F(FrameCaptionButtonContainerViewTestAlternateStyle, ButtonVisibility) { + // Using the alternate caption button style is dependant on all snapped + // windows being 50% of the screen's width. + if (!switches::UseAlternateFrameCaptionButtonStyle()) + return; + // Both the minimize button and the maximize button should be visible when // both minimizing and maximizing are allowed when using the alternate // button style. diff --git a/ash/wm/caption_buttons/frame_maximize_button.cc b/ash/wm/caption_buttons/frame_maximize_button.cc index 14526a0136..5a610e58d7 100644 --- a/ash/wm/caption_buttons/frame_maximize_button.cc +++ b/ash/wm/caption_buttons/frame_maximize_button.cc @@ -4,7 +4,6 @@ #include "ash/wm/caption_buttons/frame_maximize_button.h" -#include "ash/launcher/launcher.h" #include "ash/screen_ash.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" @@ -82,7 +81,7 @@ void FrameMaximizeButton::EscapeEventFilter::OnKeyEvent( FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener, views::Widget* frame) - : ImageButton(listener), + : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE), frame_(frame), observing_frame_(false), is_snap_enabled_(false), @@ -527,16 +526,10 @@ gfx::Point FrameMaximizeButton::LocationForSnapSizer( void FrameMaximizeButton::Snap(SnapSizer* snap_sizer) { Shell* shell = Shell::GetInstance(); - wm::WindowState* window_state = wm::GetWindowState(frame_->GetNativeWindow()); switch (snap_type_) { case SNAP_LEFT: case SNAP_RIGHT: { - // Others might also have set up a restore rectangle already. If so, we - // should not overwrite the restore rectangle. - gfx::Rect current_bounds_in_screen = frame_->GetWindowBoundsInScreen(); snap_sizer->SnapWindowToTargetBounds(); - if (!window_state->HasRestoreBounds()) - window_state->SetRestoreBoundsInScreen(current_bounds_in_screen); shell->delegate()->RecordUserMetricsAction( snap_type_ == SNAP_LEFT ? UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT : diff --git a/ash/wm/caption_buttons/frame_maximize_button.h b/ash/wm/caption_buttons/frame_maximize_button.h index c60f7a570d..a1bb991a98 100644 --- a/ash/wm/caption_buttons/frame_maximize_button.h +++ b/ash/wm/caption_buttons/frame_maximize_button.h @@ -6,13 +6,13 @@ #define ASH_WM_CAPTION_BUTTONS_FRAME_MAXIMIZE_BUTTON_H_ #include "ash/ash_export.h" -#include "ash/wm/caption_buttons/maximize_bubble_frame_state.h" +#include "ash/wm/caption_buttons/caption_button_types.h" +#include "ash/wm/caption_buttons/frame_caption_button.h" #include "ash/wm/workspace/snap_types.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "base/timer/timer.h" #include "ui/aura/window_observer.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/widget/widget_observer.h" namespace views { @@ -30,7 +30,7 @@ class SnapSizer; class MaximizeBubbleController; // Button used for the maximize control on the frame. Handles snapping logic. -class ASH_EXPORT FrameMaximizeButton : public views::ImageButton, +class ASH_EXPORT FrameMaximizeButton : public FrameCaptionButton, public views::WidgetObserver, public aura::WindowObserver { public: diff --git a/ash/wm/caption_buttons/maximize_bubble_controller.h b/ash/wm/caption_buttons/maximize_bubble_controller.h index 9a842e5c43..53a07dd295 100644 --- a/ash/wm/caption_buttons/maximize_bubble_controller.h +++ b/ash/wm/caption_buttons/maximize_bubble_controller.h @@ -6,7 +6,7 @@ #define ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_CONTROLLER_H_ #include "ash/ash_export.h" -#include "ash/wm/caption_buttons/maximize_bubble_frame_state.h" +#include "ash/wm/caption_buttons/caption_button_types.h" #include "ash/wm/workspace/snap_types.h" #include "base/memory/scoped_ptr.h" diff --git a/ash/wm/caption_buttons/maximize_bubble_frame_state.h b/ash/wm/caption_buttons/maximize_bubble_frame_state.h deleted file mode 100644 index 1737818255..0000000000 --- a/ash/wm/caption_buttons/maximize_bubble_frame_state.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_ -#define ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_ - -namespace ash { - -// These are the types of maximization we know. -enum MaximizeBubbleFrameState { - FRAME_STATE_NONE = 0, - FRAME_STATE_FULL = 1, // This is the full maximized state. - FRAME_STATE_SNAP_LEFT = 2, - FRAME_STATE_SNAP_RIGHT = 3 -}; - -} // namespace views - -#endif // ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_ diff --git a/ash/wm/custom_frame_view_ash.cc b/ash/wm/custom_frame_view_ash.cc index ef2bc108c7..268a4d2b82 100644 --- a/ash/wm/custom_frame_view_ash.cc +++ b/ash/wm/custom_frame_view_ash.cc @@ -4,13 +4,20 @@ #include "ash/wm/custom_frame_view_ash.h" +#include "ash/ash_switches.h" #include "ash/wm/caption_buttons/frame_caption_button_container_view.h" #include "ash/wm/caption_buttons/frame_maximize_button.h" #include "ash/wm/caption_buttons/frame_maximize_button_observer.h" #include "ash/wm/frame_border_hit_test_controller.h" #include "ash/wm/header_painter.h" #include "ash/wm/immersive_fullscreen_controller.h" +#include "ash/wm/window_state.h" +#include "ash/wm/window_state_delegate.h" +#include "base/command_line.h" #include "grit/ash_resources.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" #include "ui/gfx/rect.h" @@ -30,6 +37,87 @@ const gfx::Font& GetTitleFont() { return *title_font; } +/////////////////////////////////////////////////////////////////////////////// +// CustomFrameViewAshWindowStateDelegate + +// Handles a user's fullscreen request (Shift+F4/F4). Puts the window into +// immersive fullscreen if the kAshEnableImmersiveFullscreenForAllWindows +// flag is set. +class CustomFrameViewAshWindowStateDelegate + : public ash::wm::WindowStateDelegate, + public ash::wm::WindowStateObserver, + public aura::WindowObserver { + public: + CustomFrameViewAshWindowStateDelegate( + ash::wm::WindowState* window_state, + ash::CustomFrameViewAsh* custom_frame_view) + : window_state_(NULL) { +#if defined(OS_CHROMEOS) + // TODO(pkotwicz): Investigate if immersive fullscreen can be enabled for + // Windows Ash. + if (CommandLine::ForCurrentProcess()->HasSwitch( + ash::switches::kAshEnableImmersiveFullscreenForAllWindows)) { + immersive_fullscreen_controller_.reset( + new ash::ImmersiveFullscreenController); + custom_frame_view->InitImmersiveFullscreenControllerForView( + immersive_fullscreen_controller_.get()); + + // Add a window state observer to exit fullscreen properly in case + // fullscreen is exited without going through + // WindowState::ToggleFullscreen(). This is the case when exiting + // immersive fullscreen via the "Restore" window control. + // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048 + window_state_ = window_state; + window_state_->AddObserver(this); + window_state_->window()->AddObserver(this); + } +#endif + } + virtual ~CustomFrameViewAshWindowStateDelegate() { + if (window_state_) { + window_state_->RemoveObserver(this); + window_state_->window()->RemoveObserver(this); + } + } + private: + // Overridden from ash::wm::WindowStateDelegate: + virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE { + bool enter_fullscreen = !window_state->IsFullscreen(); + if (enter_fullscreen) { + window_state->window()->SetProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_FULLSCREEN); + } else { + window_state->Restore(); + } + if (immersive_fullscreen_controller_) + immersive_fullscreen_controller_->SetEnabled(enter_fullscreen); + return true; + } + // Overridden from aura::WindowObserver: + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { + window_state_->RemoveObserver(this); + window_state_->window()->RemoveObserver(this); + window_state_ = NULL; + } + // Overridden from ash::wm::WindowStateObserver: + virtual void OnWindowShowTypeChanged( + ash::wm::WindowState* window_state, + ash::wm::WindowShowType old_type) OVERRIDE { + if (!window_state->IsFullscreen() && + !window_state->IsMinimized() && + immersive_fullscreen_controller_.get() && + immersive_fullscreen_controller_->IsEnabled()) { + immersive_fullscreen_controller_->SetEnabled(false); + } + } + + ash::wm::WindowState* window_state_; + scoped_ptr<ash::ImmersiveFullscreenController> + immersive_fullscreen_controller_; + + DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshWindowStateDelegate); +}; + } // namespace namespace ash { @@ -177,7 +265,7 @@ void CustomFrameViewAsh::HeaderView::Layout() { void CustomFrameViewAsh::HeaderView::OnPaint(gfx::Canvas* canvas) { int theme_image_id = 0; - if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO)) + if (frame_->IsMaximized() || frame_->IsFullscreen()) theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL; else if (paint_as_active_) theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE; @@ -302,6 +390,15 @@ CustomFrameViewAsh::CustomFrameViewAsh(views::Widget* frame) // |header_view_| is set as the non client view's overlay view so that it can // overlay the web contents in immersive fullscreen. frame->non_client_view()->SetOverlayView(new OverlayView(header_view_)); + + // A delegate for a more complex way of fullscreening the window may already + // be set. This is the case for packaged apps. + wm::WindowState* window_state = wm::GetWindowState(frame->GetNativeWindow()); + if (!window_state->HasDelegate()) { + window_state->SetDelegate(scoped_ptr<wm::WindowStateDelegate>( + new CustomFrameViewAshWindowStateDelegate( + window_state, this)).Pass()); + } } CustomFrameViewAsh::~CustomFrameViewAsh() { diff --git a/ash/wm/dock/docked_window_layout_manager.cc b/ash/wm/dock/docked_window_layout_manager.cc index f4a2e2629b..3298f05602 100644 --- a/ash/wm/dock/docked_window_layout_manager.cc +++ b/ash/wm/dock/docked_window_layout_manager.cc @@ -21,15 +21,21 @@ #include "base/auto_reset.h" #include "base/command_line.h" #include "base/metrics/histogram.h" +#include "grit/ash_resources.h" #include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkPaint.h" #include "ui/aura/client/activation_client.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/client/window_tree_client.h" #include "ui/aura/root_window.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/rect.h" +#include "ui/views/background.h" namespace ash { namespace internal { @@ -45,24 +51,90 @@ const int DockedWindowLayoutManager::kMinDockGap = 2; const int DockedWindowLayoutManager::kIdealWidth = 250; const int kMinimumHeight = 250; const int kSlideDurationMs = 120; -const int kFadeDurationMs = 720; +const int kFadeDurationMs = 60; +const int kMinimizeDurationMs = 720; -namespace { - -const SkColor kDockBackgroundColor = SkColorSetARGB(0xff, 0x10, 0x10, 0x10); -const float kDockBackgroundOpacity = 0.5f; - -class DockedBackgroundWidget : public views::Widget { +class DockedBackgroundWidget : public views::Widget, + public internal::BackgroundAnimatorDelegate { public: - explicit DockedBackgroundWidget(aura::Window* container) { + explicit DockedBackgroundWidget(aura::Window* container) + : alignment_(DOCKED_ALIGNMENT_NONE), + background_animator_(this, 0, kLauncherBackgroundAlpha), + alpha_(0), + opaque_background_(ui::LAYER_SOLID_COLOR) { InitWidget(container); } + // Sets widget bounds and sizes opaque background layer to fill the widget. + void SetBackgroundBounds(const gfx::Rect bounds, DockedAlignment alignment) { + SetBounds(bounds); + opaque_background_.SetBounds(gfx::Rect(bounds.size())); + alignment_ = alignment; + } + + // Sets the docked area background type and starts transition animation. + void SetPaintsBackground( + ShelfBackgroundType background_type, + BackgroundAnimatorChangeType change_type) { + float target_opacity = + (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f; + scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation; + if (change_type != BACKGROUND_CHANGE_IMMEDIATE) { + opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings( + opaque_background_.GetAnimator())); + opaque_background_animation->SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs)); + } + opaque_background_.SetOpacity(target_opacity); + + // TODO(varkha): use ui::Layer on both opaque_background and normal + // background retire background_animator_ at all. It would be simpler. + // See also ShelfWidget::SetPaintsBackground. + background_animator_.SetPaintsBackground( + background_type != SHELF_BACKGROUND_DEFAULT, + change_type); + SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size())); + } + + // views::Widget: + virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE { + const gfx::ImageSkia& launcher_background( + alignment_ == DOCKED_ALIGNMENT_LEFT ? + launcher_background_left_ : launcher_background_right_); + gfx::Rect rect = gfx::Rect(GetWindowBoundsInScreen().size()); + SkPaint paint; + paint.setAlpha(alpha_); + canvas->DrawImageInt( + launcher_background, + 0, 0, launcher_background.width(), launcher_background.height(), + alignment_ == DOCKED_ALIGNMENT_LEFT ? + rect.width() - launcher_background.width() : 0, 0, + launcher_background.width(), rect.height(), + false, + paint); + canvas->DrawImageInt( + launcher_background, + alignment_ == DOCKED_ALIGNMENT_LEFT ? + 0 : launcher_background.width() - 1, 0, + 1, launcher_background.height(), + alignment_ == DOCKED_ALIGNMENT_LEFT ? + 0 : launcher_background.width(), 0, + rect.width() - launcher_background.width(), rect.height(), + false, + paint); + } + + // BackgroundAnimatorDelegate: + virtual void UpdateBackground(int alpha) OVERRIDE { + alpha_ = alpha; + SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size())); + } + private: void InitWidget(aura::Window* parent) { views::Widget::InitParams params; params.type = views::Widget::InitParams::TYPE_POPUP; - params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; params.can_activate = false; params.keep_on_top = false; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; @@ -71,17 +143,41 @@ class DockedBackgroundWidget : public views::Widget { set_focus_on_creation(false); Init(params); GetNativeWindow()->SetProperty(internal::kStayInSameRootWindowKey, true); - DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow()); - views::View* content_view = new views::View; - content_view->set_background( - views::Background::CreateSolidBackground(kDockBackgroundColor)); - SetContentsView(content_view); + opaque_background_.SetColor(SK_ColorBLACK); + opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size())); + opaque_background_.SetOpacity(0.0f); + GetNativeWindow()->layer()->Add(&opaque_background_); Hide(); + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + gfx::ImageSkia launcher_background = + *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_BACKGROUND); + launcher_background_left_ = gfx::ImageSkiaOperations::CreateRotatedImage( + launcher_background, SkBitmapOperations::ROTATION_90_CW); + launcher_background_right_ = gfx::ImageSkiaOperations::CreateRotatedImage( + launcher_background, SkBitmapOperations::ROTATION_270_CW); } + DockedAlignment alignment_; + + // The animator for the background transitions. + internal::BackgroundAnimator background_animator_; + + // The alpha to use for drawing image assets covering the docked background. + int alpha_; + + // Solid black background that can be made fully opaque. + ui::Layer opaque_background_; + + // Backgrounds created from shelf background by 90 or 270 degree rotation. + gfx::ImageSkia launcher_background_left_; + gfx::ImageSkia launcher_background_right_; + DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget); }; +namespace { + // Returns true if a window is a popup or a transient child. bool IsPopupOrTransient(const aura::Window* window) { return (window->type() == aura::client::WINDOW_TYPE_POPUP || @@ -97,10 +193,10 @@ bool IsUsedByLayout(const aura::Window* window) { void UndockWindow(aura::Window* window) { gfx::Rect previous_bounds = window->bounds(); - aura::Window* previous_parent = window->parent(); + aura::Window* old_parent = window->parent(); aura::client::ParentWindowWithContext(window, window, gfx::Rect()); - if (window->parent() != previous_parent) - wm::ReparentTransientChildrenOfChild(window->parent(), window); + if (window->parent() != old_parent) + wm::ReparentTransientChildrenOfChild(window, old_parent, window->parent()); // Start maximize or fullscreen (affecting packaged apps) animation from // previous window bounds. window->layer()->SetBounds(previous_bounds); @@ -224,7 +320,46 @@ struct CompareWindowPos { } // namespace //////////////////////////////////////////////////////////////////////////////// -// DockLayoutManager public implementation: +// A class that observes launcher shelf for bounds changes. +class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver { + public: + explicit ShelfWindowObserver( + DockedWindowLayoutManager* docked_layout_manager) + : docked_layout_manager_(docked_layout_manager) { + DCHECK(docked_layout_manager_->launcher()->shelf_widget()); + docked_layout_manager_->launcher()->shelf_widget()->GetNativeView() + ->AddObserver(this); + } + + virtual ~ShelfWindowObserver() { + if (docked_layout_manager_->launcher() && + docked_layout_manager_->launcher()->shelf_widget()) + docked_layout_manager_->launcher()->shelf_widget()->GetNativeView() + ->RemoveObserver(this); + } + + // aura::WindowObserver: + virtual void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE { + shelf_bounds_in_screen_ = ScreenAsh::ConvertRectToScreen( + window->parent(), new_bounds); + docked_layout_manager_->OnShelfBoundsChanged(); + } + + const gfx::Rect& shelf_bounds_in_screen() const { + return shelf_bounds_in_screen_; + } + + private: + DockedWindowLayoutManager* docked_layout_manager_; + gfx::Rect shelf_bounds_in_screen_; + + DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver); +}; + +//////////////////////////////////////////////////////////////////////////////// +// DockedWindowLayoutManager public implementation: DockedWindowLayoutManager::DockedWindowLayoutManager( aura::Window* dock_container, WorkspaceController* workspace_controller) : dock_container_(dock_container), @@ -252,6 +387,12 @@ DockedWindowLayoutManager::~DockedWindowLayoutManager() { } void DockedWindowLayoutManager::Shutdown() { + if (launcher_ && launcher_->shelf_widget()) { + ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher( + launcher_->shelf_widget()->GetNativeWindow()); + shelf_layout_manager->RemoveObserver(this); + shelf_observer_.reset(); + } launcher_ = NULL; for (size_t i = 0; i < dock_container_->children().size(); ++i) { aura::Window* child = dock_container_->children()[i]; @@ -332,6 +473,12 @@ void DockedWindowLayoutManager::FinishDragging(DockedAction action, void DockedWindowLayoutManager::SetLauncher(ash::Launcher* launcher) { DCHECK(!launcher_); launcher_ = launcher; + if (launcher_->shelf_widget()) { + ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher( + launcher_->shelf_widget()->GetNativeWindow()); + shelf_layout_manager->AddObserver(this); + shelf_observer_.reset(new ShelfWindowObserver(this)); + } } DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow( @@ -416,8 +563,13 @@ bool DockedWindowLayoutManager::CanDockWindow(aura::Window* window, return true; } +void DockedWindowLayoutManager::OnShelfBoundsChanged() { + Relayout(); + UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED); +} + //////////////////////////////////////////////////////////////////////////////// -// DockLayoutManager, aura::LayoutManager implementation: +// DockedWindowLayoutManager, aura::LayoutManager implementation: void DockedWindowLayoutManager::OnWindowResized() { MaybeMinimizeChildrenExcept(dragged_window_); Relayout(); @@ -482,10 +634,16 @@ void DockedWindowLayoutManager::SetChildBounds( const gfx::Rect& requested_bounds) { // Whenever one of our windows is moved or resized enforce layout. SetChildBoundsDirect(child, requested_bounds); + if (IsPopupOrTransient(child)) + return; + ShelfLayoutManager* shelf_layout = internal::ShelfLayoutManager::ForLauncher( + dock_container_); + if (shelf_layout) + shelf_layout->UpdateVisibilityState(); } //////////////////////////////////////////////////////////////////////////////// -// DockLayoutManager, ash::ShellObserver implementation: +// DockedWindowLayoutManager, ash::ShellObserver implementation: void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() { Relayout(); @@ -551,7 +709,15 @@ void DockedWindowLayoutManager::OnShelfAlignmentChanged( } ///////////////////////////////////////////////////////////////////////////// -// DockLayoutManager, WindowStateObserver implementation: +// DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation: +void DockedWindowLayoutManager::OnBackgroundUpdated( + ShelfBackgroundType background_type, + BackgroundAnimatorChangeType change_type) { + background_widget_->SetPaintsBackground(background_type, change_type); +} + +///////////////////////////////////////////////////////////////////////////// +// DockedWindowLayoutManager, WindowStateObserver implementation: void DockedWindowLayoutManager::OnWindowShowTypeChanged( wm::WindowState* window_state, @@ -565,18 +731,19 @@ void DockedWindowLayoutManager::OnWindowShowTypeChanged( return; if (window_state->IsMinimized()) { MinimizeDockedWindow(window_state); - } else if (window_state->IsMaximizedOrFullscreen()) { - // Reparenting changes the source bounds for the animation if a window is - // visible so hide it here and show later when it is already in the desktop. - UndockWindow(window); - RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN); + } else if (window_state->IsMaximizedOrFullscreen() || + window_state->IsSnapped()) { + if (window != dragged_window_) { + UndockWindow(window); + RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN); + } } else if (old_type == wm::SHOW_TYPE_MINIMIZED) { RestoreDockedWindow(window_state); } } ///////////////////////////////////////////////////////////////////////////// -// DockLayoutManager, WindowObserver implementation: +// DockedWindowLayoutManager, WindowObserver implementation: void DockedWindowLayoutManager::OnWindowBoundsChanged( aura::Window* window, @@ -615,7 +782,8 @@ void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) { //////////////////////////////////////////////////////////////////////////////// -// DockLayoutManager, aura::client::ActivationChangeObserver implementation: +// DockedWindowLayoutManager, aura::client::ActivationChangeObserver +// implementation: void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active, aura::Window* lost_active) { @@ -635,7 +803,7 @@ void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active, } //////////////////////////////////////////////////////////////////////////////// -// DockLayoutManager private implementation: +// DockedWindowLayoutManager private implementation: void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept( aura::Window* child) { @@ -653,10 +821,17 @@ void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept( if (window == child || !IsUsedByLayout(window)) continue; int room_needed = GetWindowHeightCloseTo(window, 0) + kMinDockGap; - if (available_room > room_needed) + if (available_room > room_needed) { available_room -= room_needed; - else + } else { + // Slow down minimizing animations. Lock duration so that it is not + // overridden by other ScopedLayerAnimationSettings down the stack. + ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); + settings.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kMinimizeDurationMs)); + settings.LockTransitionDuration(); wm::GetWindowState(window)->Minimize(); + } } } @@ -673,7 +848,7 @@ void DockedWindowLayoutManager::RestoreDockedWindow( wm::WindowState* window_state) { aura::Window* window = window_state->window(); DCHECK(!IsPopupOrTransient(window)); - // Always place restored window at the top shuffling the other windows down. + // Always place restored window at the bottom shuffling the other windows up. // TODO(varkha): add a separate container for docked windows to keep track // of ordering. gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow( @@ -687,7 +862,7 @@ void DockedWindowLayoutManager::RestoreDockedWindow( return; } gfx::Rect bounds(window->bounds()); - bounds.set_y(work_area.y() - bounds.height()); + bounds.set_y(work_area.bottom()); window->SetBounds(bounds); window->Show(); MaybeMinimizeChildrenExcept(window); @@ -805,8 +980,10 @@ void DockedWindowLayoutManager::Relayout() { } // Position docked windows as well as the window being dragged. - const gfx::Rect work_area = + gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area(); + if (shelf_observer_) + work_area.Subtract(shelf_observer_->shelf_bounds_in_screen()); int available_room = CalculateWindowHeightsAndRemainingRoom(work_area, &visible_windows); FanOutChildren(work_area, @@ -983,14 +1160,14 @@ void DockedWindowLayoutManager::UpdateDockBounds( observer_list_, OnDockBoundsChanging(bounds, reason)); // Show or hide background for docked area. - background_widget_->SetBounds(docked_bounds_); - if (docked_width_ > 0) { + gfx::Rect background_bounds(docked_bounds_); + if (shelf_observer_) + background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen()); + background_widget_->SetBackgroundBounds(background_bounds, alignment_); + if (docked_width_ > 0) background_widget_->Show(); - background_widget_->GetNativeWindow()->layer()->SetOpacity( - kDockBackgroundOpacity); - } else { + else background_widget_->Hide(); - } } void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) { diff --git a/ash/wm/dock/docked_window_layout_manager.h b/ash/wm/dock/docked_window_layout_manager.h index 9cb38265c7..8894bed42f 100644 --- a/ash/wm/dock/docked_window_layout_manager.h +++ b/ash/wm/dock/docked_window_layout_manager.h @@ -41,6 +41,7 @@ namespace ash { class Launcher; namespace internal { +class DockedBackgroundWidget; class DockedWindowLayoutManagerObserver; class DockedWindowResizerTest; class ShelfLayoutManager; @@ -74,6 +75,7 @@ class ASH_EXPORT DockedWindowLayoutManager public aura::WindowObserver, public aura::client::ActivationChangeObserver, public keyboard::KeyboardControllerObserver, + public ShelfLayoutManagerObserver, public wm::WindowStateObserver { public: // Maximum width of the docked windows area. @@ -132,6 +134,9 @@ class ASH_EXPORT DockedWindowLayoutManager // Returns true if currently dragged window is docked at the screen edge. bool is_dragged_window_docked() const { return is_dragged_window_docked_; } + // Updates docked layout when launcher shelf bounds change. + void OnShelfBoundsChanged(); + // aura::LayoutManager: virtual void OnWindowResized() OVERRIDE; virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE; @@ -148,6 +153,11 @@ class ASH_EXPORT DockedWindowLayoutManager aura::Window* root_window) OVERRIDE; virtual void OnShelfAlignmentChanged(aura::Window* root_window) OVERRIDE; + // ShelfLayoutManagerObserver: + virtual void OnBackgroundUpdated( + ShelfBackgroundType background_type, + BackgroundAnimatorChangeType change_type) OVERRIDE; + // wm::WindowStateObserver: virtual void OnWindowShowTypeChanged(wm::WindowState* window_state, wm::WindowShowType old_type) OVERRIDE; @@ -165,6 +175,7 @@ class ASH_EXPORT DockedWindowLayoutManager aura::Window* lost_active) OVERRIDE; private: + class ShelfWindowObserver; friend class DockedWindowLayoutManagerTest; friend class DockedWindowResizerTest; @@ -255,6 +266,7 @@ class ASH_EXPORT DockedWindowLayoutManager // The launcher to respond to launcher alignment changes. Launcher* launcher_; + // Workspace controller that can be checked for fullscreen mode. WorkspaceController* workspace_controller_; // Tracks if any window in the same root window is in fullscreen mode. @@ -279,8 +291,11 @@ class ASH_EXPORT DockedWindowLayoutManager // Used in UMA metrics. base::Time last_action_time_; + // Observes launcher shelf for bounds changes. + scoped_ptr<ShelfWindowObserver> shelf_observer_; + // Widget used to paint a background for the docked area. - scoped_ptr<views::Widget> background_widget_; + scoped_ptr<DockedBackgroundWidget> background_widget_; // Observers of dock bounds changes. ObserverList<DockedWindowLayoutManagerObserver> observer_list_; diff --git a/ash/wm/dock/docked_window_layout_manager_unittest.cc b/ash/wm/dock/docked_window_layout_manager_unittest.cc index 9d31f4dd56..4b626e4927 100644 --- a/ash/wm/dock/docked_window_layout_manager_unittest.cc +++ b/ash/wm/dock/docked_window_layout_manager_unittest.cc @@ -18,7 +18,7 @@ #include "ash/test/launcher_test_api.h" #include "ash/test/shelf_view_test_api.h" #include "ash/test/shell_test_api.h" -#include "ash/test/test_launcher_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/wm/coordinate_conversion.h" #include "ash/wm/panels/panel_layout_manager.h" #include "ash/wm/window_resizer.h" @@ -48,7 +48,7 @@ class DockedWindowLayoutManagerTest virtual void SetUp() OVERRIDE { AshTestBase::SetUp(); UpdateDisplay("600x600"); - ASSERT_TRUE(test::TestLauncherDelegate::instance()); + ASSERT_TRUE(test::TestShelfDelegate::instance()); shelf_view_test_.reset(new test::ShelfViewTestAPI( test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).shelf_view())); @@ -72,9 +72,9 @@ class DockedWindowLayoutManagerTest aura::Window* window = CreateTestWindowInShellWithDelegateAndType( NULL, window_type_, 0, bounds); if (window_type_ == aura::client::WINDOW_TYPE_PANEL) { - test::TestLauncherDelegate* launcher_delegate = - test::TestLauncherDelegate::instance(); - launcher_delegate->AddLauncherItem(window); + test::TestShelfDelegate* shelf_delegate = + test::TestShelfDelegate::instance(); + shelf_delegate->AddLauncherItem(window); PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(GetPanelContainer(window)-> layout_manager()); @@ -89,9 +89,9 @@ class DockedWindowLayoutManagerTest aura::Window* window = CreateTestWindowInShellWithDelegateAndType( delegate, window_type_, 0, bounds); if (window_type_ == aura::client::WINDOW_TYPE_PANEL) { - test::TestLauncherDelegate* launcher_delegate = - test::TestLauncherDelegate::instance(); - launcher_delegate->AddLauncherItem(window); + test::TestShelfDelegate* shelf_delegate = + test::TestShelfDelegate::instance(); + shelf_delegate->AddLauncherItem(window); PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(GetPanelContainer(window)-> layout_manager()); diff --git a/ash/wm/dock/docked_window_resizer.cc b/ash/wm/dock/docked_window_resizer.cc index 3380287f38..cd06beec37 100644 --- a/ash/wm/dock/docked_window_resizer.cc +++ b/ash/wm/dock/docked_window_resizer.cc @@ -75,23 +75,14 @@ void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) { } gfx::Point offset; gfx::Rect bounds(CalculateBoundsForDrag(details_, location)); - bool set_tracked_by_workspace = MaybeSnapToEdge(bounds, &offset); - - // Temporarily clear kWindowTrackedByWorkspaceKey for windows that are snapped - // to screen edges e.g. when they are docked. This prevents the windows from - // getting snapped to other nearby windows during the drag. - wm::WindowState* window_state = wm::GetWindowState(GetTarget()); - bool was_tracked_by_workspace = window_state->tracked_by_workspace(); - if (set_tracked_by_workspace) - window_state->SetTrackedByWorkspace(false); - gfx::Point modified_location(location.x() + offset.x(), - location.y() + offset.y()); + MaybeSnapToEdge(bounds, &offset); + gfx::Point modified_location(location); + modified_location.Offset(offset.x(), offset.y()); base::WeakPtr<DockedWindowResizer> resizer(weak_ptr_factory_.GetWeakPtr()); next_window_resizer_->Drag(modified_location, event_flags); if (!resizer) return; - window_state->SetTrackedByWorkspace(was_tracked_by_workspace); DockedWindowLayoutManager* new_dock_layout = GetDockedLayoutManagerAtPoint(last_location_); @@ -125,25 +116,12 @@ void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) { } void DockedWindowResizer::CompleteDrag(int event_flags) { - // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they - // don't get forced into the workspace that may be shrunken because of docked - // windows. - wm::WindowState* window_state = wm::GetWindowState(GetTarget()); - bool was_tracked_by_workspace = window_state->tracked_by_workspace(); - window_state->SetTrackedByWorkspace(false); // The root window can change when dragging into a different screen. next_window_resizer_->CompleteDrag(event_flags); FinishedDragging(); - window_state->SetTrackedByWorkspace(was_tracked_by_workspace); } void DockedWindowResizer::RevertDrag() { - // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they - // don't get forced into the workspace that may be shrunken because of docked - // windows. - wm::WindowState* window_state = wm::GetWindowState(GetTarget()); - bool was_tracked_by_workspace = window_state->tracked_by_workspace(); - window_state->SetTrackedByWorkspace(false); next_window_resizer_->RevertDrag(); // Restore docked state to what it was before the drag if necessary. if (is_docked_ != was_docked_) { @@ -154,7 +132,6 @@ void DockedWindowResizer::RevertDrag() { dock_layout_->UndockDraggedWindow(); } FinishedDragging(); - window_state->SetTrackedByWorkspace(was_tracked_by_workspace); } aura::Window* DockedWindowResizer::GetTarget() { @@ -188,11 +165,11 @@ DockedWindowResizer::DockedWindowResizer(WindowResizer* next_window_resizer, is_docked_ = was_docked_; } -bool DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds, +void DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds, gfx::Point* offset) { // Windows only snap magnetically when they were previously docked. if (!was_docked_) - return false; + return; DockedAlignment dock_alignment = dock_layout_->CalculateAlignment(); gfx::Rect dock_bounds = ScreenAsh::ConvertRectFromScreen( GetTarget()->parent(), @@ -207,18 +184,15 @@ bool DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds, const int distance = bounds.x() - dock_bounds.x(); if (distance < kSnapToDockDistance && distance > 0) { offset->set_x(-distance); - return true; + return; } } if (dock_alignment == DOCKED_ALIGNMENT_RIGHT || dock_alignment == DOCKED_ALIGNMENT_NONE) { const int distance = dock_bounds.right() - bounds.right(); - if (distance < kSnapToDockDistance && distance > 0) { + if (distance < kSnapToDockDistance && distance > 0) offset->set_x(distance); - return true; - } } - return false; } void DockedWindowResizer::StartedDragging() { @@ -246,7 +220,9 @@ void DockedWindowResizer::StartedDragging() { aura::Window* docked_container = Shell::GetContainer( GetTarget()->GetRootWindow(), kShellWindowId_DockedContainer); - wm::ReparentChildWithTransientChildren(docked_container, GetTarget()); + wm::ReparentChildWithTransientChildren(GetTarget(), + GetTarget()->parent(), + docked_container); } if (is_docked_) dock_layout_->DockDraggedWindow(GetTarget()); @@ -313,7 +289,9 @@ DockedAction DockedWindowResizer::MaybeReparentWindowOnDragCompletion( if ((is_resized || !is_attached_panel) && is_docked_ != (window->parent() == dock_container)) { if (is_docked_) { - wm::ReparentChildWithTransientChildren(dock_container, window); + wm::ReparentChildWithTransientChildren(window, + window->parent(), + dock_container); action = DOCKED_ACTION_DOCK; } else if (window->parent()->id() == kShellWindowId_DockedContainer) { // Reparent the window back to workspace. @@ -326,8 +304,11 @@ DockedAction DockedWindowResizer::MaybeReparentWindowOnDragCompletion( // Reparenting will cause Relayout and possible dock shrinking. aura::Window* previous_parent = window->parent(); aura::client::ParentWindowWithContext(window, window, near_last_location); - if (window->parent() != previous_parent) - wm::ReparentTransientChildrenOfChild(window->parent(), window); + if (window->parent() != previous_parent) { + wm::ReparentTransientChildrenOfChild(window, + previous_parent, + window->parent()); + } action = was_docked_ ? DOCKED_ACTION_UNDOCK : DOCKED_ACTION_NONE; } } else { diff --git a/ash/wm/dock/docked_window_resizer.h b/ash/wm/dock/docked_window_resizer.h index b5e5ece697..929259e59e 100644 --- a/ash/wm/dock/docked_window_resizer.h +++ b/ash/wm/dock/docked_window_resizer.h @@ -54,9 +54,9 @@ class ASH_EXPORT DockedWindowResizer : public WindowResizer { DockedWindowResizer(WindowResizer* next_window_resizer, const Details& details); - // Checks if the provided window bounds should snap to the side of a screen. - // If so the offset returned gives the necessary adjustment to snap. - bool MaybeSnapToEdge(const gfx::Rect& bounds, gfx::Point* offset); + // If the provided window bounds should snap to the side of a screen, + // returns the offset that gives the necessary adjustment to snap. + void MaybeSnapToEdge(const gfx::Rect& bounds, gfx::Point* offset); // Tracks the window's initial position and attachment at the start of a drag // and informs the DockLayoutManager that a drag has started if necessary. diff --git a/ash/wm/dock/docked_window_resizer_unittest.cc b/ash/wm/dock/docked_window_resizer_unittest.cc index 0c1a4c6f3a..82b726e1fe 100644 --- a/ash/wm/dock/docked_window_resizer_unittest.cc +++ b/ash/wm/dock/docked_window_resizer_unittest.cc @@ -17,15 +17,17 @@ #include "ash/test/ash_test_base.h" #include "ash/test/cursor_manager_test_api.h" #include "ash/test/shell_test_api.h" -#include "ash/test/test_launcher_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/wm/coordinate_conversion.h" #include "ash/wm/dock/docked_window_layout_manager.h" #include "ash/wm/drag_window_resizer.h" #include "ash/wm/panels/panel_layout_manager.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" +#include "ash/wm/workspace/snap_sizer.h" #include "base/command_line.h" #include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/window_tree_client.h" #include "ui/aura/root_window.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/base/hit_test.h" @@ -76,9 +78,9 @@ class DockedWindowResizerTest 0, bounds); if (window_type_ == aura::client::WINDOW_TYPE_PANEL) { - test::TestLauncherDelegate* launcher_delegate = - test::TestLauncherDelegate::instance(); - launcher_delegate->AddLauncherItem(window); + test::TestShelfDelegate* shelf_delegate = + test::TestShelfDelegate::instance(); + shelf_delegate->AddLauncherItem(window); PanelLayoutManager* manager = static_cast<PanelLayoutManager*>( Shell::GetContainer(window->GetRootWindow(), @@ -89,6 +91,28 @@ class DockedWindowResizerTest return window; } + aura::Window* CreateModalWindow(const gfx::Rect& bounds) { + aura::Window* window = new aura::Window(&delegate_); + window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM); + window->SetType(aura::client::WINDOW_TYPE_NORMAL); + window->Init(ui::LAYER_TEXTURED); + window->Show(); + + if (bounds.IsEmpty()) { + ParentWindowInPrimaryRootWindow(window); + } else { + gfx::Display display = + Shell::GetScreen()->GetDisplayMatching(bounds); + aura::Window* root = ash::Shell::GetInstance()->display_controller()-> + GetRootWindowForDisplayId(display.id()); + gfx::Point origin = bounds.origin(); + wm::ConvertPointFromScreen(root, &origin); + window->SetBounds(gfx::Rect(origin, bounds.size())); + aura::client::ParentWindowWithContext(window, root, bounds); + } + return window; + } + static WindowResizer* CreateSomeWindowResizer( aura::Window* window, const gfx::Point& point_in_parent, @@ -238,7 +262,7 @@ TEST_P(DockedWindowResizerTest, AttachRightPrecise) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0); - // The window should be attached and snapped to the right edge. + // The window should be docked at the right edge. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -253,7 +277,7 @@ TEST_P(DockedWindowResizerTest, AttachRightOvershoot) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), +4); - // The window should be attached and snapped to the right edge. + // The window should be docked at the right edge. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -294,7 +318,7 @@ TEST_P(DockedWindowResizerTest, AttachLeftPrecise) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 0); - // The window should be attached and snapped to the left dock. + // The window should be docked at the left edge. EXPECT_EQ(window->GetRootWindow()->bounds().x(), window->GetBoundsInScreen().x()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -309,7 +333,7 @@ TEST_P(DockedWindowResizerTest, AttachLeftOvershoot) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), -4); - // The window should be attached and snapped to the left dock. + // The window should be docked at the left edge. EXPECT_EQ(window->GetRootWindow()->bounds().x(), window->GetBoundsInScreen().x()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -341,7 +365,7 @@ TEST_P(DockedWindowResizerTest, AttachRightChangeShelf) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0); - // The window should be attached and snapped to the right edge. + // The window should be docked at the right edge. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -381,7 +405,7 @@ TEST_P(DockedWindowResizerTest, AttachTryDetach) { gfx::Rect(0, 0, ideal_width() + 10, 201))); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0); - // The window should be attached and docked at the right edge. + // The window should be docked at the right edge. // Its width should shrink to ideal width. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); @@ -423,7 +447,7 @@ TEST_P(DockedWindowResizerTest, AttachMinimizeRestore) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0); - // The window should be attached and snapped to the right edge. + // The window should be docked at the right edge. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -449,7 +473,7 @@ TEST_P(DockedWindowResizerTest, AttachMaximize) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0); - // The window should be attached and snapped to the right edge. + // The window should be docked at the right edge. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -473,7 +497,7 @@ TEST_P(DockedWindowResizerTest, AttachTwoWindows) { DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 50); - // Both windows should be attached and snapped to the right edge. + // Both windows should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->bounds().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -509,7 +533,7 @@ TEST_P(DockedWindowResizerTest, AttachOneAutoHideShelf) { scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); - // w1 should be attached and snapped to the right edge. + // w1 should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->bounds().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -554,7 +578,7 @@ TEST_P(DockedWindowResizerTest, AttachOnTwoSides) { gfx::Rect initial_bounds(w2->bounds()); DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w2.get(), 50); - // The first window should be attached and snapped to the right edge. + // The first window should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->bounds().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -575,7 +599,7 @@ TEST_P(DockedWindowResizerTest, RevertDragRestoresAttachment) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0); - // The window should be attached and snapped to the right edge. + // The window should be docked at the right edge. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -621,7 +645,7 @@ TEST_P(DockedWindowResizerTest, DragAcrossDisplays) { EXPECT_EQ(root_windows[0], window->GetRootWindow()); DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0); - // The window should be attached and snapped to the right edge. + // The window should be docked at the right edge. EXPECT_EQ(window->GetRootWindow()->bounds().right(), window->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); @@ -686,7 +710,7 @@ TEST_P(DockedWindowResizerTest, AttachTwoWindowsDetachOne) { ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width()); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); - // A window should be attached and snapped to the right edge. + // A window should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->bounds().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -696,7 +720,7 @@ TEST_P(DockedWindowResizerTest, AttachTwoWindowsDetachOne) { EXPECT_EQ(w1->bounds().width(), docked_width(manager)); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100); - // Both windows should now be attached and snapped to the right edge. + // Both windows should now be docked at the right edge. EXPECT_EQ(w2->GetRootWindow()->bounds().right(), w2->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id()); @@ -758,7 +782,7 @@ TEST_P(DockedWindowResizerTest, AttachWindowMaximizeOther) { ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width()); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); - // A window should be attached and snapped to the right edge. + // A window should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->bounds().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -857,7 +881,7 @@ TEST_P(DockedWindowResizerTest, AttachOneTestSticky) { ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width()); DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w1.get(), 20); - // A window should be attached and snapped to the left edge. + // A window should be docked at the left edge. EXPECT_EQ(w1->GetRootWindow()->bounds().x(), w1->GetBoundsInScreen().x()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -956,7 +980,7 @@ TEST_P(DockedWindowResizerTest, ResizeOneOfTwoWindows) { ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width()); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); - // A window should be attached and snapped to the right edge. + // A window should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->bounds().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -966,7 +990,7 @@ TEST_P(DockedWindowResizerTest, ResizeOneOfTwoWindows) { EXPECT_EQ(w1->bounds().width(), docked_width(manager)); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100); - // Both windows should now be attached and snapped to the right edge. + // Both windows should now be docked at the right edge. EXPECT_EQ(w2->GetRootWindow()->bounds().right(), w2->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id()); @@ -1097,7 +1121,7 @@ TEST_P(DockedWindowResizerTest, ResizingKeepsWidth) { scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); - // A window should be attached and snapped to the right edge. + // A window should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -1226,7 +1250,7 @@ TEST_P(DockedWindowResizerTest, DragToShelf) { ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).width()); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); - // A window should be attached and snapped to the right edge. + // A window should be docked at the right edge. EXPECT_EQ(w1->GetRootWindow()->bounds().right(), w1->GetBoundsInScreen().right()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); @@ -1278,7 +1302,7 @@ TEST_P(DockedWindowResizerTest, DragWindowWithTransientChild) { DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20); - // A window should be attached and snapped to the right edge. + // A window should be docked at the right edge. EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id()); EXPECT_EQ(internal::kShellWindowId_DockedContainer, child->parent()->id()); @@ -1302,6 +1326,105 @@ TEST_P(DockedWindowResizerTest, DragWindowWithTransientChild) { child->GetBoundsInScreen().origin().ToString()); } +// Tests that reparenting windows during the drag does not affect system modal +// windows that are transient children of the dragged windows. +TEST_P(DockedWindowResizerTest, DragWindowWithModalTransientChild) { + if (!SupportsHostWindowResize()) + return; + + // Create a window. + scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); + gfx::Rect bounds(window->bounds()); + + // Start dragging the window. + ASSERT_NO_FATAL_FAILURE(DragStart(window.get())); + gfx::Vector2d move_vector(40, test_panels() ? -60 : 60); + DragMove(move_vector.x(), move_vector.y()); + EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id()); + + // While still dragging create a modal window and make it a transient child of + // the |window|. + scoped_ptr<aura::Window> child(CreateModalWindow(gfx::Rect(20, 20, 150, 20))); + window->AddTransientChild(child.get()); + EXPECT_EQ(window.get(), child->transient_parent()); + EXPECT_EQ(internal::kShellWindowId_SystemModalContainer, + child->parent()->id()); + + // End the drag, the |window| should have moved (if it is a panel it will + // no longer be attached to the shelf since we dragged it above). + DragEnd(); + bounds.Offset(move_vector); + EXPECT_EQ(bounds.ToString(), window->GetBoundsInScreen().ToString()); + + // The original |window| should be in the default container (not docked or + // attached). + EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id()); + // The transient |child| should still be in system modal container. + EXPECT_EQ(internal::kShellWindowId_SystemModalContainer, + child->parent()->id()); + // The |child| should not have moved. + EXPECT_EQ(gfx::Point(20, 20).ToString(), + child->GetBoundsInScreen().origin().ToString()); + // The |child| should still be a transient child of |window|. + EXPECT_EQ(window.get(), child->transient_parent()); +} + +// Tests that side snapping a window undocks it, closes the dock and then snaps. +TEST_P(DockedWindowResizerTest, SideSnapDocked) { + if (!SupportsHostWindowResize() || test_panels()) + return; + + scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); + wm::WindowState* window_state = wm::GetWindowState(w1.get()); + DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); + // A window should be docked at the right edge. + EXPECT_EQ(w1->GetRootWindow()->bounds().right(), + w1->GetBoundsInScreen().right()); + EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); + DockedWindowLayoutManager* manager = + static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager()); + EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager)); + EXPECT_EQ(w1->bounds().width(), docked_width(manager)); + EXPECT_TRUE(window_state->IsDocked()); + EXPECT_FALSE(window_state->IsSnapped()); + + // Side snap at right edge. + internal::SnapSizer::SnapWindow(window_state, + internal::SnapSizer::RIGHT_EDGE); + // The window should be snapped at the right edge and the dock should close. + gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get())); + EXPECT_EQ(0, docked_width(manager)); + EXPECT_EQ(work_area.height(), w1->bounds().height()); + EXPECT_EQ(work_area.right(), w1->bounds().right()); + EXPECT_EQ(internal::kShellWindowId_DefaultContainer, w1->parent()->id()); + EXPECT_FALSE(window_state->IsDocked()); + EXPECT_TRUE(window_state->IsSnapped()); + + // Dock again. + DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20); + // A window should be docked at the right edge. + EXPECT_EQ(w1->GetRootWindow()->bounds().right(), + w1->GetBoundsInScreen().right()); + EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id()); + EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager)); + EXPECT_EQ(w1->bounds().width(), docked_width(manager)); + EXPECT_TRUE(window_state->IsDocked()); + EXPECT_FALSE(window_state->IsSnapped()); + + // Side snap at left edge. + internal::SnapSizer::SnapWindow(window_state, + internal::SnapSizer::LEFT_EDGE); + // The window should be snapped at the right edge and the dock should close. + EXPECT_EQ(work_area.ToString(), + ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).ToString()); + EXPECT_EQ(0, docked_width(manager)); + EXPECT_EQ(work_area.height(), w1->bounds().height()); + EXPECT_EQ(work_area.x(), w1->bounds().x()); + EXPECT_EQ(internal::kShellWindowId_DefaultContainer, w1->parent()->id()); + EXPECT_FALSE(window_state->IsDocked()); + EXPECT_TRUE(window_state->IsSnapped()); +} + // Tests run twice - on both panels and normal windows INSTANTIATE_TEST_CASE_P(NormalOrPanel, DockedWindowResizerTest, diff --git a/ash/wm/gestures/long_press_affordance_handler.cc b/ash/wm/gestures/long_press_affordance_handler.cc index 45a995e692..2a96f0707f 100644 --- a/ash/wm/gestures/long_press_affordance_handler.cc +++ b/ash/wm/gestures/long_press_affordance_handler.cc @@ -65,6 +65,7 @@ views::Widget* CreateAffordanceWidget(aura::Window* root_window) { params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; params.keep_on_top = true; params.accept_events = false; + params.can_activate = false; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.context = root_window; params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; diff --git a/ash/wm/gestures/shelf_gesture_handler.cc b/ash/wm/gestures/shelf_gesture_handler.cc index 9dcd3f2794..f9fb1b8d43 100644 --- a/ash/wm/gestures/shelf_gesture_handler.cc +++ b/ash/wm/gestures/shelf_gesture_handler.cc @@ -43,7 +43,7 @@ bool ShelfGestureHandler::ProcessGestureEvent(const ui::GestureEvent& event) { ShelfLayoutManager* shelf = controller->GetShelfLayoutManager(); - const aura::Window* fullscreen = controller->GetTopmostFullscreenWindow(); + const aura::Window* fullscreen = controller->GetWindowForFullscreenMode(); if (fullscreen && ash::wm::GetWindowState(fullscreen)->hide_shelf_when_fullscreen()) { return false; diff --git a/ash/wm/header_painter.cc b/ash/wm/header_painter.cc index fb0b2c4ce8..bf87015b17 100644 --- a/ash/wm/header_painter.cc +++ b/ash/wm/header_painter.cc @@ -9,7 +9,6 @@ #include "ash/root_window_controller.h" #include "ash/wm/caption_buttons/frame_caption_button_container_view.h" #include "ash/wm/solo_window_tracker.h" -#include "ash/wm/window_state.h" #include "base/logging.h" // DCHECK #include "grit/ash_resources.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -165,10 +164,8 @@ HeaderPainter::HeaderPainter() HeaderPainter::~HeaderPainter() { // Sometimes we are destroyed before the window closes, so ensure we clean up. - if (window_) { + if (window_) window_->RemoveObserver(this); - wm::GetWindowState(window_)->RemoveObserver(this); - } } void HeaderPainter::Init( @@ -203,7 +200,6 @@ void HeaderPainter::Init( // Observer removes itself in OnWindowDestroying() below, or in the destructor // if we go away before the window. window_->AddObserver(this); - wm::GetWindowState(window_)->AddObserver(this); // Solo-window header updates are handled by the WorkspaceLayoutManager when // this window is added to the desktop. @@ -262,17 +258,6 @@ int HeaderPainter::GetThemeBackgroundXInset() const { return kThemeFrameImageInsetX; } -bool HeaderPainter::ShouldUseMinimalHeaderStyle(Themed header_themed) const { - // Use the minimalistic header style whenever |frame_| is maximized or - // fullscreen EXCEPT: - // - If the user has installed a theme with custom images for the header. - // - For windows which are not tracked by the workspace code (which are used - // for tab dragging). - return (frame_->IsMaximized() || frame_->IsFullscreen()) && - header_themed == THEMED_NO && - wm::GetWindowState(frame_->GetNativeWindow())->tracked_by_workspace(); -} - void HeaderPainter::PaintHeader(gfx::Canvas* canvas, HeaderMode header_mode, int theme_frame_id, @@ -433,7 +418,7 @@ void HeaderPainter::PaintTitleBar(gfx::Canvas* canvas, views::WidgetDelegate* delegate = frame_->widget_delegate(); if (delegate && delegate->ShouldShowWindowTitle()) { gfx::Rect title_bounds = GetTitleBounds(title_font); - SkColor title_color = frame_->IsMaximized() ? + SkColor title_color = (frame_->IsMaximized() || frame_->IsFullscreen()) ? kMaximizedWindowTitleTextColor : kNonMaximizedWindowTitleTextColor; canvas->DrawStringInt(delegate->GetWindowTitle(), title_font, @@ -489,16 +474,6 @@ void HeaderPainter::OnThemeChanged() { } /////////////////////////////////////////////////////////////////////////////// -// WindowStateObserver overrides: -void HeaderPainter::OnTrackedByWorkspaceChanged(wm::WindowState* window_state, - bool old) { - // When 'TrackedByWorkspace' changes, we are going to paint the header - // differently. Schedule a paint to ensure everything is updated correctly. - if (window_state->tracked_by_workspace()) - header_view_->SchedulePaint(); -} - -/////////////////////////////////////////////////////////////////////////////// // aura::WindowObserver overrides: void HeaderPainter::OnWindowDestroying(aura::Window* destroying) { @@ -507,7 +482,6 @@ void HeaderPainter::OnWindowDestroying(aura::Window* destroying) { // Must be removed here and not in the destructor, as the aura::Window is // already destroyed when our destructor runs. window_->RemoveObserver(this); - wm::GetWindowState(window_)->RemoveObserver(this); window_ = NULL; } @@ -550,11 +524,7 @@ int HeaderPainter::GetCaptionButtonContainerCenterY() const { } int HeaderPainter::GetHeaderCornerRadius() const { - // Use square corners for maximized and fullscreen windows when they are - // tracked by the workspace code. (Windows which are not tracked by the - // workspace code are used for tab dragging.) - bool square_corners = ((frame_->IsMaximized() || frame_->IsFullscreen())) && - wm::GetWindowState(frame_->GetNativeWindow())->tracked_by_workspace(); + bool square_corners = (frame_->IsMaximized() || frame_->IsFullscreen()); const int kCornerRadius = 2; return square_corners ? 0 : kCornerRadius; } @@ -571,8 +541,8 @@ int HeaderPainter::GetHeaderOpacity( return kFullyOpaque; } - // The header is fully opaque when using the minimalistic header style. - if (ShouldUseMinimalHeaderStyle(THEMED_NO)) + // Maximized and fullscreen windows are fully opaque. + if (frame_->IsMaximized() || frame_->IsFullscreen()) return kFullyOpaque; // Solo header is very transparent. diff --git a/ash/wm/header_painter.h b/ash/wm/header_painter.h index ea326af9d6..accc6e370d 100644 --- a/ash/wm/header_painter.h +++ b/ash/wm/header_painter.h @@ -6,7 +6,6 @@ #define ASH_WM_HEADER_PAINTER_H_ #include "ash/ash_export.h" -#include "ash/wm/window_state_observer.h" #include "base/basictypes.h" #include "base/compiler_specific.h" // OVERRIDE #include "base/gtest_prod_util.h" @@ -36,8 +35,7 @@ class FrameCaptionButtonContainerView; // Helper class for painting the window header. class ASH_EXPORT HeaderPainter : public aura::WindowObserver, - public gfx::AnimationDelegate, - public wm::WindowStateObserver { + public gfx::AnimationDelegate { public: // Opacity values for the window header in various states, from 0 to 255. static int kActiveWindowOpacity; @@ -49,11 +47,6 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver, INACTIVE }; - enum Themed { - THEMED_YES, - THEMED_NO - }; - HeaderPainter(); virtual ~HeaderPainter(); @@ -92,9 +85,6 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver, // Returns the amount that the theme background should be inset. int GetThemeBackgroundXInset() const; - // Returns true if the header should be painted using a minimalistic style. - bool ShouldUseMinimalHeaderStyle(Themed header_themed) const; - // Paints the header. // |theme_frame_overlay_id| is 0 if no overlay image should be used. void PaintHeader(gfx::Canvas* canvas, @@ -124,6 +114,11 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver, header_height_ = header_height; } + // Returns the header height. + int header_height() const { + return header_height_; + } + // Schedule a re-paint of the entire title. void SchedulePaintForTitle(const gfx::Font& title_font); @@ -136,27 +131,12 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE; - // ash::WindowStateObserver override: - virtual void OnTrackedByWorkspaceChanged(wm::WindowState* window_state, - bool old) OVERRIDE; - // Overridden from gfx::AnimationDelegate virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE; private: - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, CreateAndDeleteSingleWindow); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeader); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderWithApp); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderWithPanel); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderModal); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderConstrained); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderNotDrawn); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderMultiDisplay); FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, GetHeaderOpacity); FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, TitleIconAlignment); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, ChildWindowVisibility); - FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, - NoCrashShutdownWithAlwaysOnTopWindow); // Returns the header bounds in the coordinates of |header_view_|. The header // is assumed to be positioned at the top left corner of |header_view_| and to diff --git a/ash/wm/header_painter_unittest.cc b/ash/wm/header_painter_unittest.cc index 551658a8d0..8ce8917a58 100644 --- a/ash/wm/header_painter_unittest.cc +++ b/ash/wm/header_painter_unittest.cc @@ -123,29 +123,6 @@ TEST_F(HeaderPainterTest, GetHeaderOpacity) { 0)); } -// Test that the minimal header style is used in the proper situations. -TEST_F(HeaderPainterTest, MinimalHeaderStyle) { - // Create a widget and a painter for it. - scoped_ptr<Widget> w(CreateTestWidget()); - scoped_ptr<HeaderPainter> p(CreateTestPainter(w.get())); - w->Show(); - - // Regular non-maximized windows should not use the minimal header style. - EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO)); - - // Regular maximized windows should use the minimal header style. - w->Maximize(); - EXPECT_TRUE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO)); - - // Test cases where the maximized window should not use the minimal header - // style. - EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_YES)); - - wm::GetWindowState(w->GetNativeWindow())->SetTrackedByWorkspace(false); - EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO)); - wm::GetWindowState(w->GetNativeWindow())->SetTrackedByWorkspace(true); -} - // Ensure the title text is vertically aligned with the window icon. TEST_F(HeaderPainterTest, TitleIconAlignment) { scoped_ptr<Widget> w(CreateTestWidget()); diff --git a/ash/wm/immersive_fullscreen_controller.cc b/ash/wm/immersive_fullscreen_controller.cc index ba6eb3c0c1..28aa1dcd0d 100644 --- a/ash/wm/immersive_fullscreen_controller.cc +++ b/ash/wm/immersive_fullscreen_controller.cc @@ -287,8 +287,12 @@ void ImmersiveFullscreenController::SetEnabled(bool enabled) { if (reveal_state_ == REVEALED) { // Reveal was unsuccessful. Reacquire the revealed locks if appropriate. - UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); + UpdateLocatedEventRevealedLock(NULL); UpdateFocusRevealedLock(); + } else { + // Clearing focus is important because it closes focus-related popups like + // the touch selection handles. + widget_->GetFocusManager()->ClearFocus(); } } else { // Stop cursor-at-top tracking. @@ -332,7 +336,7 @@ void ImmersiveFullscreenController::SetupForTest() { } gfx::Point cursor_pos(0, bottommost_in_screen + 100); aura::Env::GetInstance()->set_last_mouse_location(cursor_pos); - UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); + UpdateLocatedEventRevealedLock(NULL); } //////////////////////////////////////////////////////////////////////////////// @@ -349,30 +353,29 @@ void ImmersiveFullscreenController::OnMouseEvent(ui::MouseEvent* event) { return; } - // Mouse hover should not initiate revealing the top-of-window views while - // |native_window_| is inactive. - if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) - return; + // Mouse hover can initiate revealing the top-of-window views while |widget_| + // is inactive. - // Mouse hover should not initiate revealing the top-of-window views while - // a window has mouse capture. - if (aura::client::GetCaptureWindow(native_window_)) - return; - - if (IsRevealed()) - UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO); - - // Trigger a reveal if the cursor pauses at the top of the screen for a - // while. - if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) + if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) { + top_edge_hover_timer_.Stop(); + UpdateLocatedEventRevealedLock(event); + } else if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) { + // Trigger a reveal if the cursor pauses at the top of the screen for a + // while. UpdateTopEdgeHoverTimer(event); + } } void ImmersiveFullscreenController::OnTouchEvent(ui::TouchEvent* event) { if (!enabled_ || event->type() != ui::ET_TOUCH_PRESSED) return; - UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO); + // Touch should not initiate revealing the top-of-window views while |widget_| + // is inactive. + if (!widget_->IsActive()) + return; + + UpdateLocatedEventRevealedLock(event); } void ImmersiveFullscreenController::OnGestureEvent(ui::GestureEvent* event) { @@ -380,15 +383,17 @@ void ImmersiveFullscreenController::OnGestureEvent(ui::GestureEvent* event) { return; // Touch gestures should not initiate revealing the top-of-window views while - // |native_window_| is inactive. - if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) + // |widget_| is inactive. + if (!widget_->IsActive()) return; switch (event->type()) { case ui::ET_GESTURE_SCROLL_BEGIN: if (ShouldHandleGestureEvent(GetEventLocationInScreen(*event))) { gesture_begun_ = true; - event->SetHandled(); + // Do not consume the event. Otherwise, we end up consuming all + // ui::ET_GESTURE_SCROLL_BEGIN events in the top-of-window views + // when the top-of-window views are revealed. } break; case ui::ET_GESTURE_SCROLL_UPDATE: @@ -436,21 +441,7 @@ void ImmersiveFullscreenController::OnWidgetDestroying(views::Widget* widget) { void ImmersiveFullscreenController::OnWidgetActivationChanged( views::Widget* widget, bool active) { - // Mouse hover should not initiate revealing the top-of-window views while - // |native_window_| is inactive. - top_edge_hover_timer_.Stop(); - UpdateFocusRevealedLock(); - - // Allow the top-of-window views to stay revealed if all of the revealed locks - // were released in the process of activating |widget| but the mouse is still - // hovered above the top-of-window views. For instance, if the bubble which - // has been keeping the top-of-window views revealed is hidden but the mouse - // is hovered above the top-of-window views, the top-of-window views should - // stay revealed. We cannot call UpdateLocatedEventRevealedLock() from - // BubbleManager::UpdateRevealedLock() because |widget| is not yet active - // at that time. - UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_YES); } //////////////////////////////////////////////////////////////////////////////// @@ -520,19 +511,17 @@ void ImmersiveFullscreenController::EnableWindowObservers(bool enable) { return; observers_enabled_ = enable; - views::Widget* widget = - views::Widget::GetWidgetForNativeWindow(native_window_); - views::FocusManager* focus_manager = widget->GetFocusManager(); + views::FocusManager* focus_manager = widget_->GetFocusManager(); if (enable) { - widget->AddObserver(this); + widget_->AddObserver(this); focus_manager->AddFocusChangeListener(this); Shell::GetInstance()->AddPreTargetHandler(this); native_window_->AddObserver(this); RecreateBubbleManager(); } else { - widget->RemoveObserver(this); + widget_->RemoveObserver(this); focus_manager->RemoveFocusChangeListener(this); Shell::GetInstance()->RemovePreTargetHandler(this); native_window_->RemoveObserver(this); @@ -550,12 +539,22 @@ void ImmersiveFullscreenController::EnableWindowObservers(bool enable) { void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer( ui::MouseEvent* event) { DCHECK(enabled_); - // Stop the timer if the top-of-window views are already revealed. - if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) { - top_edge_hover_timer_.Stop(); + DCHECK(reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED); + + // Check whether |native_window_| is the event target's parent window instead + // of checking for activation. This allows the timer to be started when + // |widget_| is inactive but prevents starting the timer if the mouse is over + // a portion of the top edge obscured by an unrelated widget. + if (!top_edge_hover_timer_.IsRunning() && + !native_window_->Contains(static_cast<aura::Window*>(event->target()))) { return; } + // Mouse hover should not initiate revealing the top-of-window views while a + // window has mouse capture. + if (aura::client::GetCaptureWindow(native_window_)) + return; + gfx::Point location_in_screen = GetEventLocationInScreen(*event); if (ShouldIgnoreMouseEventAtLocation(location_in_screen)) return; @@ -591,8 +590,7 @@ void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer( } void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock( - ui::LocatedEvent* event, - AllowRevealWhileClosing allow_reveal_while_closing) { + ui::LocatedEvent* event) { if (!enabled_) return; DCHECK(!event || event->IsMouseEvent() || event->IsTouchEvent()); @@ -601,18 +599,14 @@ void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock( // views are sliding closed or are closed with the following exceptions: // - Hovering at y = 0 which is handled in OnMouseEvent(). // - Doing a SWIPE_OPEN edge gesture which is handled in OnGestureEvent(). - if (reveal_state_ == CLOSED || - (reveal_state_ == SLIDING_CLOSED && - allow_reveal_while_closing == ALLOW_REVEAL_WHILE_CLOSING_NO)) { + if (reveal_state_ == CLOSED || reveal_state_ == SLIDING_CLOSED) return; - } - // Neither the mouse nor touch should keep the top-of-window views revealed if - // |native_window_| is not active. - if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) { - located_event_revealed_lock_.reset(); - return; - } + // For the sake of simplicity, ignore |widget_|'s activation in computing + // whether the top-of-window views should stay revealed. Ideally, the + // top-of-window views would stay revealed only when the mouse cursor is + // hovered above a non-obscured portion of the top-of-window views. The + // top-of-window views may be partially obscured when |widget_| is inactive. // Ignore all events while a window has capture. This keeps the top-of-window // views revealed during a drag. @@ -679,10 +673,8 @@ void ImmersiveFullscreenController::UpdateFocusRevealedLock() { return; bool hold_lock = false; - views::Widget* widget = - views::Widget::GetWidgetForNativeWindow(native_window_); - if (widget->IsActive()) { - views::View* focused_view = widget->GetFocusManager()->GetFocusedView(); + if (widget_->IsActive()) { + views::View* focused_view = widget_->GetFocusManager()->GetFocusedView(); if (top_container_->Contains(focused_view)) hold_lock = true; } else { @@ -729,7 +721,7 @@ bool ImmersiveFullscreenController::UpdateRevealedLocksForSwipe( // Swipes while |native_window_| is inactive should have been filtered out in // OnGestureEvent(). - DCHECK(views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()); + DCHECK(widget_->IsActive()); if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) { if (swipe_type == SWIPE_OPEN && !located_event_revealed_lock_.get()) { @@ -743,11 +735,13 @@ bool ImmersiveFullscreenController::UpdateRevealedLocksForSwipe( located_event_revealed_lock_.reset(); focus_revealed_lock_.reset(); - if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) + if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) { + widget_->GetFocusManager()->ClearFocus(); return true; + } // Ending the reveal was unsuccessful. Reaquire the locks if appropriate. - UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); + UpdateLocatedEventRevealedLock(NULL); UpdateFocusRevealedLock(); } } @@ -808,7 +802,7 @@ void ImmersiveFullscreenController::OnSlideOpenAnimationCompleted() { // The user may not have moved the mouse since the reveal was initiated. // Update the revealed lock to reflect the mouse's current state. - UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO); + UpdateLocatedEventRevealedLock(NULL); } void ImmersiveFullscreenController::MaybeEndReveal(Animate animate) { @@ -880,6 +874,7 @@ bool ImmersiveFullscreenController::ShouldIgnoreMouseEventAtLocation( bool ImmersiveFullscreenController::ShouldHandleGestureEvent( const gfx::Point& location) const { + DCHECK(widget_->IsActive()); if (reveal_state_ == REVEALED) { std::vector<gfx::Rect> hit_bounds_in_screen( delegate_->GetVisibleBoundsInScreen()); diff --git a/ash/wm/immersive_fullscreen_controller.h b/ash/wm/immersive_fullscreen_controller.h index 9d0f757c1e..7e0ca78bd9 100644 --- a/ash/wm/immersive_fullscreen_controller.h +++ b/ash/wm/immersive_fullscreen_controller.h @@ -143,10 +143,6 @@ class ASH_EXPORT ImmersiveFullscreenController private: friend class ImmersiveFullscreenControllerTest; - enum AllowRevealWhileClosing { - ALLOW_REVEAL_WHILE_CLOSING_YES, - ALLOW_REVEAL_WHILE_CLOSING_NO - }; enum Animate { ANIMATE_NO, ANIMATE_SLOW, @@ -176,12 +172,7 @@ class ASH_EXPORT ImmersiveFullscreenController // Updates |located_event_revealed_lock_| based on the current mouse state and // the current touch state. // |event| is NULL if the source event is not known. - // |allow_reveal_while_closing| indicates whether the mouse and touch - // are allowed to initiate a reveal while the top-of-window views are sliding - // closed. - void UpdateLocatedEventRevealedLock( - ui::LocatedEvent* event, - AllowRevealWhileClosing allow_reveal_while_closing); + void UpdateLocatedEventRevealedLock(ui::LocatedEvent* event); // Acquires |located_event_revealed_lock_| if it is not already held. void AcquireLocatedEventRevealedLock(); diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc index 9d0b21b69e..672e976456 100644 --- a/ash/wm/immersive_fullscreen_controller_unittest.cc +++ b/ash/wm/immersive_fullscreen_controller_unittest.cc @@ -138,27 +138,12 @@ class ImmersiveFullscreenControllerTest : public ash::test::AshTestBase { new MockImmersiveFullscreenControllerDelegate(top_container_)); controller_.reset(new ImmersiveFullscreenController); controller_->Init(delegate_.get(), widget_, top_container_); - SetAnimationsDisabled(true); + controller_->SetupForTest(); // The mouse is moved so that it is not over |top_container_| by // AshTestBase. } - // Enable or disable the ImmersiveFullscreenController's animations. When the - // ImmersiveFullscreenController's animations are disabled, some behavior is - // slightly different. In particular, the behavior is different when there - // is a transfer in which lock keeps the top-of-window views revealed (eg - // bubble keeps top-of-window views revealed -> mouse keeps top-of-window - // views revealed). It is necessary to temporarily enable the - // ImmersiveFullscreenController's animations to get the correct behavior in - // tests. - void SetAnimationsDisabled(bool disabled) { - controller_->animations_disabled_for_test_ = disabled; - // Force any in progress animations to finish. - if (disabled) - controller_->animation_->End(); - } - // Attempt to reveal the top-of-window views via |modality|. // The top-of-window views can only be revealed via mouse hover or a gesture. void AttemptReveal(Modality modality) { @@ -398,7 +383,7 @@ TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) { top_container_bounds_in_screen.bottom() + 50); EXPECT_FALSE(controller()->IsRevealed()); - // The mouse position cannot cause a reveal when TopContainerView's widget + // The mouse position cannot cause a reveal when the top container's widget // has capture. views::Widget* widget = top_container()->GetWidget(); widget->SetCapture(top_container()); @@ -406,7 +391,7 @@ TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) { EXPECT_FALSE(controller()->IsRevealed()); widget->ReleaseCapture(); - // The mouse position cannot end the reveal while TopContainerView's widget + // The mouse position cannot end the reveal while the top container's widget // has capture. AttemptReveal(MODALITY_MOUSE); EXPECT_TRUE(controller()->IsRevealed()); @@ -420,6 +405,64 @@ TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) { EXPECT_FALSE(controller()->IsRevealed()); } +// Test mouse event processing for top-of-screen reveal triggering when the +// top container's widget is inactive. +TEST_F(ImmersiveFullscreenControllerTest, Inactive) { + // Set up initial state. + views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds( + NULL, + CurrentContext(), + gfx::Rect(0, 0, 200, 200)); + popup_widget->Show(); + ASSERT_FALSE(top_container()->GetWidget()->IsActive()); + + controller()->SetEnabled(true); + ASSERT_TRUE(controller()->IsEnabled()); + ASSERT_FALSE(controller()->IsRevealed()); + + gfx::Rect top_container_bounds_in_screen = + top_container()->GetBoundsInScreen(); + gfx::Rect popup_bounds_in_screen = popup_widget->GetWindowBoundsInScreen(); + ASSERT_EQ(top_container_bounds_in_screen.origin().ToString(), + popup_bounds_in_screen.origin().ToString()); + ASSERT_GT(top_container_bounds_in_screen.right(), + popup_bounds_in_screen.right()); + + // The top-of-window views should stay hidden if the cursor is at the top edge + // but above an obscured portion of the top-of-window views. + MoveMouse(popup_bounds_in_screen.x(), + top_container_bounds_in_screen.y()); + EXPECT_FALSE(controller()->IsRevealed()); + + // The top-of-window views should reveal if the cursor is at the top edge and + // above an unobscured portion of the top-of-window views. + MoveMouse(top_container_bounds_in_screen.right() - 1, + top_container_bounds_in_screen.y()); + EXPECT_TRUE(controller()->IsRevealed()); + + // The top-of-window views should stay revealed if the cursor is moved off + // of the top edge. + MoveMouse(top_container_bounds_in_screen.right() - 1, + top_container_bounds_in_screen.bottom() - 1); + EXPECT_TRUE(controller()->IsRevealed()); + + // Moving way off of the top-of-window views should end the immersive reveal. + MoveMouse(top_container_bounds_in_screen.right() - 1, + top_container_bounds_in_screen.bottom() + 50); + EXPECT_FALSE(controller()->IsRevealed()); + + // Moving way off of the top-of-window views in a region where the + // top-of-window views are obscured should also end the immersive reveal. + // Ideally, the immersive reveal would end immediately when the cursor moves + // to an obscured portion of the top-of-window views. + MoveMouse(top_container_bounds_in_screen.right() - 1, + top_container_bounds_in_screen.y()); + EXPECT_TRUE(controller()->IsRevealed()); + MoveMouse(top_container_bounds_in_screen.x(), + top_container_bounds_in_screen.bottom() + 50); + EXPECT_FALSE(controller()->IsRevealed()); +} + // Test mouse event processing for top-of-screen reveal triggering when the user // has a vertical display layout (primary display above/below secondary display) // and the immersive fullscreen window is on the bottom display. @@ -495,6 +538,20 @@ TEST_F(ImmersiveFullscreenControllerTest, MouseEventsVerticalDisplayLayout) { // the bottom region of the secondary display. event_generator.MoveMouseTo(x, y_top_edge - 20); EXPECT_FALSE(controller()->IsRevealed()); + + // Test that it is possible to reveal the top-of-window views by overshooting + // the top edge slightly when the top container's widget is not active. + views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds( + NULL, + CurrentContext(), + gfx::Rect(0, 200, 100, 100)); + popup_widget->Show(); + ASSERT_FALSE(top_container()->GetWidget()->IsActive()); + ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects( + popup_widget->GetWindowBoundsInScreen())); + event_generator.MoveMouseTo(x, y_top_edge + 1); + MoveMouse(x, y_top_edge - 2); + EXPECT_TRUE(controller()->IsRevealed()); } // Test behavior when the mouse becomes hovered without moving. @@ -517,9 +574,9 @@ TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) { EXPECT_FALSE(controller()->IsRevealed()); // 2) Test that if the mouse becomes hovered without moving because of a - // reveal in ImmersiveFullscreenController::controller()->SetEnabled(true) - // and there are no locks keeping the top-of-window views revealed, that mouse - // hover does not prevent the top-of-window views from closing. + // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no + // locks keeping the top-of-window views revealed, that mouse hover does not + // prevent the top-of-window views from closing. controller()->SetEnabled(false); SetHovered(true); EXPECT_FALSE(controller()->IsRevealed()); @@ -527,10 +584,9 @@ TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) { EXPECT_FALSE(controller()->IsRevealed()); // 3) Test that if the mouse becomes hovered without moving because of a - // reveal in ImmersiveFullscreenController::controller()->SetEnabled(true) - // and there is a lock keeping the top-of-window views revealed, that the - // top-of-window views do not hide till the mouse moves off of the - // top-of-window views. + // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a + // lock keeping the top-of-window views revealed, that the top-of-window views + // do not hide till the mouse moves off of the top-of-window views. controller()->SetEnabled(false); SetHovered(true); lock.reset(controller()->GetRevealedLock( @@ -593,7 +649,11 @@ TEST_F(ImmersiveFullscreenControllerTest, EndRevealViaGesture) { EXPECT_TRUE(controller()->IsRevealed()); AttemptUnreveal(MODALITY_GESTURE); EXPECT_FALSE(controller()->IsRevealed()); - top_container()->GetFocusManager()->ClearFocus(); + + // The top-of-window views should no longer have focus. Clearing focus is + // important because it closes focus-related popup windows like the touch + // selection handles. + EXPECT_FALSE(top_container()->HasFocus()); // If some other code is holding onto a lock, a gesture should not be able to // end the reveal. @@ -676,10 +736,9 @@ TEST_F(ImmersiveFullscreenControllerTest, Focus) { EXPECT_FALSE(controller()->IsRevealed()); } -// Test how activation affects whether the top-of-window views are revealed. -// The behavior when a bubble is activated is tested in -// ImmersiveFullscreenControllerTest.Bubbles. -TEST_F(ImmersiveFullscreenControllerTest, Activation) { +// Test how transient windows affect whether the top-of-window views are +// revealed. +TEST_F(ImmersiveFullscreenControllerTest, Transient) { views::Widget* top_container_widget = top_container()->GetWidget(); controller()->SetEnabled(true); @@ -692,36 +751,34 @@ TEST_F(ImmersiveFullscreenControllerTest, Activation) { transient_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; transient_params.parent = top_container_widget->GetNativeView(); - transient_params.bounds = gfx::Rect(0, 0, 100, 100); + transient_params.bounds = gfx::Rect(0, 100, 100, 100); scoped_ptr<views::Widget> transient_widget(new views::Widget()); transient_widget->Init(transient_params); - transient_widget->Show(); EXPECT_FALSE(controller()->IsRevealed()); - top_container_widget->Activate(); AttemptReveal(MODALITY_MOUSE); EXPECT_TRUE(controller()->IsRevealed()); - transient_widget->Activate(); + transient_widget->Show(); SetHovered(false); EXPECT_TRUE(controller()->IsRevealed()); transient_widget.reset(); EXPECT_FALSE(controller()->IsRevealed()); - // 2) Test that activating a non-transient window ends the reveal if any. + // 2) Test that activating a non-transient window does not keep the + // top-of-window views revealed. views::Widget::InitParams non_transient_params; non_transient_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; non_transient_params.context = top_container_widget->GetNativeView(); - non_transient_params.bounds = gfx::Rect(0, 0, 100, 100); + non_transient_params.bounds = gfx::Rect(0, 100, 100, 100); scoped_ptr<views::Widget> non_transient_widget(new views::Widget()); non_transient_widget->Init(non_transient_params); - non_transient_widget->Show(); EXPECT_FALSE(controller()->IsRevealed()); - top_container_widget->Activate(); AttemptReveal(MODALITY_MOUSE); EXPECT_TRUE(controller()->IsRevealed()); - non_transient_widget->Activate(); + non_transient_widget->Show(); + SetHovered(false); EXPECT_FALSE(controller()->IsRevealed()); } @@ -774,11 +831,7 @@ TEST_F(ImmersiveFullscreenControllerTest, Bubbles) { bubble_widget3->Show(); SetHovered(true); EXPECT_TRUE(controller()->IsRevealed()); - - SetAnimationsDisabled(false); - // Activating |top_container_widget| will close |bubble_widget3|. top_container_widget->Activate(); - SetAnimationsDisabled(true); EXPECT_TRUE(controller()->IsRevealed()); // 3) Test that the top-of-window views stay revealed as long as at least one diff --git a/ash/wm/mru_window_tracker_unittest.cc b/ash/wm/mru_window_tracker_unittest.cc index 0cebd9e285..924060db7b 100644 --- a/ash/wm/mru_window_tracker_unittest.cc +++ b/ash/wm/mru_window_tracker_unittest.cc @@ -4,7 +4,7 @@ #include "ash/shell.h" #include "ash/test/ash_test_base.h" -#include "ash/test/test_launcher_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" diff --git a/ash/wm/overview/window_overview.cc b/ash/wm/overview/window_overview.cc index e7d914b6b3..4542f18ad4 100644 --- a/ash/wm/overview/window_overview.cc +++ b/ash/wm/overview/window_overview.cc @@ -21,6 +21,7 @@ #include "ui/aura/window.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event.h" +#include "ui/views/background.h" #include "ui/views/widget/widget.h" namespace ash { diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc index 79d428f204..db5255b3c1 100644 --- a/ash/wm/overview/window_selector.cc +++ b/ash/wm/overview/window_selector.cc @@ -300,6 +300,7 @@ WindowSelector::WindowSelector(const WindowList& windows, } if (mode == WindowSelector::CYCLE) { + cycle_start_time_ = base::Time::Now(); event_handler_.reset(new WindowSelectorEventFilter(this)); if (timer_enabled_) start_overview_timer_.Reset(); @@ -327,6 +328,11 @@ WindowSelector::~WindowSelector() { // Clearing the window list resets the ignored_by_shelf flag on the windows. windows_.clear(); UpdateShelfVisibility(); + + if (!cycle_start_time_.is_null()) { + UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.CycleTime", + base::Time::Now() - cycle_start_time_); + } } void WindowSelector::Step(WindowSelector::Direction direction) { diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h index 84b5aa9e02..faee73d1d7 100644 --- a/ash/wm/overview/window_selector.h +++ b/ash/wm/overview/window_selector.h @@ -118,6 +118,9 @@ class ASH_EXPORT WindowSelector base::DelayTimer<WindowSelector> start_overview_timer_; scoped_ptr<WindowOverview> window_overview_; + // The time when window cycling was started. + base::Time cycle_start_time_; + // Weak pointer to the selector delegate which will be called when a // selection is made. WindowSelectorDelegate* delegate_; diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc index a148357669..fcbb9ae7b8 100644 --- a/ash/wm/overview/window_selector_controller.cc +++ b/ash/wm/overview/window_selector_controller.cc @@ -72,18 +72,6 @@ bool WindowSelectorController::IsSelecting() { void WindowSelectorController::OnWindowSelected(aura::Window* window) { window_selector_.reset(); - - // If there is a fullscreen window on this display and it was not selected - // it should exit fullscreen mode. - internal::RootWindowController* controller = - internal::GetRootWindowController(window->GetRootWindow()); - aura::Window* fullscreen_window = NULL; - if (controller) - fullscreen_window = controller->GetTopmostFullscreenWindow(); - if (fullscreen_window && fullscreen_window != window) { - wm::GetWindowState(fullscreen_window)->ToggleFullscreen(); - } - wm::ActivateWindow(window); last_selection_time_ = base::Time::Now(); Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(false); diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index a8e8d08ecc..988a586b0f 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc @@ -11,7 +11,7 @@ #include "ash/test/launcher_test_api.h" #include "ash/test/shelf_view_test_api.h" #include "ash/test/shell_test_api.h" -#include "ash/test/test_launcher_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/window_selector.h" #include "ash/wm/overview/window_selector_controller.h" @@ -105,7 +105,7 @@ class WindowSelectorTest : public test::AshTestBase { virtual void SetUp() OVERRIDE { test::AshTestBase::SetUp(); - ASSERT_TRUE(test::TestLauncherDelegate::instance()); + ASSERT_TRUE(test::TestShelfDelegate::instance()); shelf_view_test_.reset(new test::ShelfViewTestAPI( test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).shelf_view())); @@ -127,7 +127,7 @@ class WindowSelectorTest : public test::AshTestBase { aura::Window* CreatePanelWindow(const gfx::Rect& bounds) { aura::Window* window = CreateTestWindowInShellWithDelegateAndType( NULL, aura::client::WINDOW_TYPE_PANEL, 0, bounds); - test::TestLauncherDelegate::instance()->AddLauncherItem(window); + test::TestShelfDelegate::instance()->AddLauncherItem(window); shelf_view_test()->RunMessageLoopUntilAnimationsDone(); return window; } @@ -284,12 +284,13 @@ TEST_F(WindowSelectorTest, FullscreenWindow) { EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); EXPECT_FALSE(panel1->IsVisible()); - // Entering overview and selecting another window should exit fullscreen. + // Entering overview and selecting another window, the previous window remains + // fullscreen. // TODO(flackr): Currently the panel remains hidden, but should become visible // again. ToggleOverview(); ClickWindow(window2.get()); - EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen()); + EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen()); } // Tests that the shelf dimming state is removed while in overview and restored diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc index bafe29f188..3c01e520f9 100644 --- a/ash/wm/panels/panel_frame_view.cc +++ b/ash/wm/panels/panel_frame_view.cc @@ -29,6 +29,7 @@ PanelFrameView::PanelFrameView(views::Widget* frame, FrameType frame_type) title_font_(gfx::Font(views::NativeWidgetAura::GetWindowTitleFont())), frame_border_hit_test_controller_( new FrameBorderHitTestController(frame_)) { + DCHECK(!frame_->widget_delegate()->CanMaximize()); if (frame_type != FRAME_NONE) InitHeaderPainter(); } @@ -115,9 +116,7 @@ void PanelFrameView::OnPaint(gfx::Canvas* canvas) { return; bool paint_as_active = ShouldPaintAsActive(); int theme_frame_id = 0; - if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO)) - theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL; - else if (paint_as_active) + if (paint_as_active) theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE; else theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_INACTIVE; diff --git a/ash/wm/panels/panel_layout_manager.cc b/ash/wm/panels/panel_layout_manager.cc index ee6a75e2fa..ec7027b612 100644 --- a/ash/wm/panels/panel_layout_manager.cc +++ b/ash/wm/panels/panel_layout_manager.cc @@ -353,9 +353,10 @@ void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) { // back to appropriate container and ignore it. // TODO(varkha): Updating bounds during a drag can cause problems and a more // general solution is needed. See http://crbug.com/251813 . + aura::Window* old_parent = child->parent(); aura::client::ParentWindowWithContext( child, child, child->GetRootWindow()->GetBoundsInScreen()); - wm::ReparentTransientChildrenOfChild(child->parent(), child); + wm::ReparentTransientChildrenOfChild(child, old_parent, child->parent()); DCHECK(child->parent()->id() != kShellWindowId_PanelContainer); return; } diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc index 0a71814cb6..5351afa2b1 100644 --- a/ash/wm/panels/panel_layout_manager_unittest.cc +++ b/ash/wm/panels/panel_layout_manager_unittest.cc @@ -21,7 +21,7 @@ #include "ash/test/launcher_test_api.h" #include "ash/test/shelf_view_test_api.h" #include "ash/test/shell_test_api.h" -#include "ash/test/test_launcher_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/window_util.h" #include "base/basictypes.h" @@ -50,7 +50,7 @@ class PanelLayoutManagerTest : public test::AshTestBase { virtual void SetUp() OVERRIDE { test::AshTestBase::SetUp(); - ASSERT_TRUE(test::TestLauncherDelegate::instance()); + ASSERT_TRUE(test::TestShelfDelegate::instance()); shelf_view_test_.reset(new test::ShelfViewTestAPI( GetShelfView(Launcher::ForPrimaryDisplay()))); @@ -67,12 +67,11 @@ class PanelLayoutManagerTest : public test::AshTestBase { aura::client::WINDOW_TYPE_PANEL, 0, bounds); - test::TestLauncherDelegate* launcher_delegate = - test::TestLauncherDelegate::instance(); - launcher_delegate->AddLauncherItem(window); - PanelLayoutManager* manager = - static_cast<PanelLayoutManager*>(GetPanelContainer(window)-> - layout_manager()); + test::TestShelfDelegate* shelf_delegate = + test::TestShelfDelegate::instance(); + shelf_delegate->AddLauncherItem(window); + PanelLayoutManager* manager = static_cast<PanelLayoutManager*>( + GetPanelContainer(window)->layout_manager()); manager->Relayout(); shelf_view_test()->RunMessageLoopUntilAnimationsDone(); return window; diff --git a/ash/wm/panels/panel_window_resizer.cc b/ash/wm/panels/panel_window_resizer.cc index d7c0652373..739544f97d 100644 --- a/ash/wm/panels/panel_window_resizer.cc +++ b/ash/wm/panels/panel_window_resizer.cc @@ -198,9 +198,10 @@ void PanelWindowResizer::StartedDragging() { // is reparented to a container in the root window that has that window. aura::Window* target = GetTarget(); aura::Window* target_root = target->GetRootWindow(); + aura::Window* old_parent = target->parent(); aura::client::ParentWindowWithContext( target, target_root, target_root->GetBoundsInScreen()); - wm::ReparentTransientChildrenOfChild(target->parent(), target); + wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent()); } } @@ -215,9 +216,10 @@ void PanelWindowResizer::FinishDragging() { // is reparented to a container in the root window that has that location. aura::Window* target = GetTarget(); aura::Window* target_root = target->GetRootWindow(); + aura::Window* old_parent = target->parent(); aura::client::ParentWindowWithContext( target, target_root, gfx::Rect(last_location_, gfx::Size())); - wm::ReparentTransientChildrenOfChild(target->parent(), GetTarget()); + wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent()); } // If we started the drag in one root window and moved into another root diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc index 0e96afd257..f264b2b2b9 100644 --- a/ash/wm/panels/panel_window_resizer_unittest.cc +++ b/ash/wm/panels/panel_window_resizer_unittest.cc @@ -16,7 +16,7 @@ #include "ash/test/ash_test_base.h" #include "ash/test/cursor_manager_test_api.h" #include "ash/test/shell_test_api.h" -#include "ash/test/test_launcher_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/wm/drag_window_resizer.h" #include "ash/wm/panels/panel_layout_manager.h" #include "ash/wm/window_state.h" @@ -41,7 +41,7 @@ class PanelWindowResizerTest : public test::AshTestBase { UpdateDisplay("600x400"); test::ShellTestApi test_api(Shell::GetInstance()); model_ = test_api.shelf_model(); - launcher_delegate_ = test::TestLauncherDelegate::instance(); + shelf_delegate_ = test::TestShelfDelegate::instance(); } virtual void TearDown() OVERRIDE { @@ -65,7 +65,7 @@ class PanelWindowResizerTest : public test::AshTestBase { aura::client::WINDOW_TYPE_PANEL, 0, bounds); - launcher_delegate_->AddLauncherItem(window); + shelf_delegate_->AddLauncherItem(window); PanelLayoutManager* manager = static_cast<PanelLayoutManager*>( Shell::GetContainer(window->GetRootWindow(), @@ -188,7 +188,7 @@ class PanelWindowResizerTest : public test::AshTestBase { scoped_ptr<WindowResizer> resizer_; internal::PanelLayoutManager* panel_layout_manager_; ShelfModel* model_; - test::TestLauncherDelegate* launcher_delegate_; + test::TestShelfDelegate* shelf_delegate_; DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTest); }; diff --git a/ash/wm/session_state_animator.cc b/ash/wm/session_state_animator.cc index 38091bf114..de8cd42d4d 100644 --- a/ash/wm/session_state_animator.cc +++ b/ash/wm/session_state_animator.cc @@ -613,22 +613,5 @@ void SessionStateAnimator::RunAnimationForWindow( } } -void SessionStateAnimator::CreateForeground() { - if (foreground_) - return; - aura::Window* window = Shell::GetContainer( - Shell::GetPrimaryRootWindow(), - internal::kShellWindowId_PowerButtonAnimationContainer); - HideWindowImmediately(window, NULL); - foreground_.reset( - new ColoredWindowController(window, "SessionStateAnimatorForeground")); - foreground_->SetColor(SK_ColorWHITE); - foreground_->GetWidget()->Show(); -} - -void SessionStateAnimator::DropForeground() { - foreground_.reset(); -} - } // namespace internal } // namespace ash diff --git a/ash/wm/session_state_animator.h b/ash/wm/session_state_animator.h index 19ead2f26c..4cb183e3ce 100644 --- a/ash/wm/session_state_animator.h +++ b/ash/wm/session_state_animator.h @@ -6,7 +6,6 @@ #define ASH_WM_SESSION_STATE_ANIMATOR_H_ #include "ash/ash_export.h" -#include "ash/wm/workspace/colored_window_controller.h" #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/timer/timer.h" @@ -140,12 +139,6 @@ class ASH_EXPORT SessionStateAnimator { static void GetContainers(int container_mask, aura::Window::Windows* containers); - // Create |foreground_| layer if it doesn't already exist, but makes it - // completely transparent. - void CreateForeground(); - // Destroy |foreground_| when it is not needed anymore. - void DropForeground(); - // Apply animation |type| to all containers included in |container_mask| with // specified |speed|. void StartAnimation(int container_mask, @@ -178,10 +171,6 @@ class ASH_EXPORT SessionStateAnimator { AnimationSpeed speed, ui::LayerAnimationObserver* observer); - // White foreground that is used during shutdown animation to "fade - // everything into white". - scoped_ptr<ColoredWindowController> foreground_; - DISALLOW_COPY_AND_ASSIGN(SessionStateAnimator); }; diff --git a/ash/wm/solo_window_tracker.cc b/ash/wm/solo_window_tracker.cc index 71299d2cc5..633d3bd450 100644 --- a/ash/wm/solo_window_tracker.cc +++ b/ash/wm/solo_window_tracker.cc @@ -60,7 +60,7 @@ bool IsValidCandidate(aura::Window* window) { window->layer() && window->layer()->type() != ui::LAYER_NOT_DRAWN && window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE && - !window->GetProperty(ash::kConstrainedWindowKey); + !window->GetProperty(aura::client::kConstrainedWindowKey); } // Schedule's a paint of the window's entire bounds. diff --git a/ash/wm/solo_window_tracker_unittest.cc b/ash/wm/solo_window_tracker_unittest.cc index 5792008d92..431ae00955 100644 --- a/ash/wm/solo_window_tracker_unittest.cc +++ b/ash/wm/solo_window_tracker_unittest.cc @@ -29,10 +29,14 @@ namespace { class WindowRepaintChecker : public aura::WindowObserver { public: explicit WindowRepaintChecker(aura::Window* window) - : is_paint_scheduled_(false) { - window->AddObserver(this); + : window_(window), + is_paint_scheduled_(false) { + window_->AddObserver(this); } + virtual ~WindowRepaintChecker() { + if (window_) + window_->RemoveObserver(this); } bool IsPaintScheduledAndReset() { @@ -47,10 +51,12 @@ class WindowRepaintChecker : public aura::WindowObserver { const gfx::Rect& region) OVERRIDE { is_paint_scheduled_ = true; } - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { - window->RemoveObserver(this); + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { + DCHECK_EQ(window_, window); + window_ = NULL; } + aura::Window* window_; bool is_paint_scheduled_; DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker); @@ -272,7 +278,7 @@ TEST_F(SoloWindowTrackerTest, Constrained) { // Create a fake constrained window. scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); - w2->SetProperty(ash::kConstrainedWindowKey, true); + w2->SetProperty(aura::client::kConstrainedWindowKey, true); w2->Show(); // Despite two windows, the first window should still be considered "solo" diff --git a/ash/wm/sticky_keys.cc b/ash/wm/sticky_keys.cc index efdfd16017..7910a072ad 100644 --- a/ash/wm/sticky_keys.cc +++ b/ash/wm/sticky_keys.cc @@ -172,6 +172,7 @@ StickyKeysHandler::StickyKeysHandler(ui::EventFlags target_modifier_flag, current_state_(DISABLED), event_from_myself_(false), preparing_to_enable_(false), + scroll_delta_(0), delegate_(delegate) { } @@ -223,11 +224,28 @@ bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { if (event_from_myself_ || current_state_ == DISABLED) return false; DCHECK(current_state_ == ENABLED || current_state_ == LOCKED); - preparing_to_enable_ = false; - AppendModifier(event); - if (current_state_ == ENABLED) { + + // We detect a direction change if the current |scroll_delta_| is assigned + // and the offset of the current scroll event has the opposing sign. + bool direction_changed = false; + if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) { + int offset = event->y_offset(); + if (scroll_delta_) + direction_changed = offset * scroll_delta_ <= 0; + scroll_delta_ = offset; + } + + if (!direction_changed) + AppendModifier(event); + + // We want to modify all the scroll events in the scroll sequence, which ends + // with a fling start event. We also stop when the scroll sequence changes + // direction. + if (current_state_ == ENABLED && + (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) { current_state_ = DISABLED; + scroll_delta_ = 0; DispatchEventAndReleaseModifier(event); return true; } @@ -268,8 +286,9 @@ bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) { case TARGET_MODIFIER_UP: if (preparing_to_enable_) { preparing_to_enable_ = false; + scroll_delta_ = 0; current_state_ = ENABLED; - modifier_up_event_.reset(event->Copy()); + modifier_up_event_.reset(new ui::KeyEvent(*event)); return true; } return false; diff --git a/ash/wm/sticky_keys.h b/ash/wm/sticky_keys.h index 7b8f6f1dee..1a2789214b 100644 --- a/ash/wm/sticky_keys.h +++ b/ash/wm/sticky_keys.h @@ -222,6 +222,11 @@ class ASH_EXPORT StickyKeysHandler { // the ENABLED state. bool preparing_to_enable_; + // Tracks the scroll direction of the current scroll sequence. Sticky keys + // stops modifying the scroll events of the sequence when the direction + // changes. If no sequence is tracked, the value is 0. + int scroll_delta_; + // The modifier up key event to be sent on non modifier key on ENABLED state. scoped_ptr<ui::KeyEvent> modifier_up_event_; diff --git a/ash/wm/sticky_keys_unittest.cc b/ash/wm/sticky_keys_unittest.cc index e8e8975c19..6f11347ecb 100644 --- a/ash/wm/sticky_keys_unittest.cc +++ b/ash/wm/sticky_keys_unittest.cc @@ -44,7 +44,7 @@ class EventBuffer : public ui::EventHandler { private: // ui::EventHandler overrides: virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { - events_.push_back(event->Copy()); + events_.push_back(new ui::KeyEvent(*event)); } virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { @@ -92,7 +92,7 @@ class MockStickyKeysHandlerDelegate : delegate_->OnShortcutPressed(); } - events_.push_back(event->Copy()); + events_.push_back(new ui::KeyEvent(*event)); } virtual void DispatchMouseEvent(ui::MouseEvent* event, @@ -203,6 +203,21 @@ class StickyKeysTest : public test::AshTestBase, return event; } + ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta, + bool is_cancel) { + scoped_xevent_.InitFlingScrollEvent( + kScrollDeviceId, // deviceid + 0, // x_velocity + fling_delta, // y_velocity + 0, // x_velocity_ordinal + fling_delta, // y_velocity_ordinal + is_cancel); // is_cancel + ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_); + ui::Event::DispatcherApi dispatcher(event); + dispatcher.set_target(target_); + return event; + } + // Creates a synthesized KeyEvent that is not backed by a native event. ui::KeyEvent* GenerateSynthesizedKeyEvent( bool is_key_press, ui::KeyboardCode code) { @@ -501,9 +516,9 @@ TEST_F(StickyKeysTest, MouseEventLocked) { EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); } -TEST_F(StickyKeysTest, ScrollEvents) { +TEST_F(StickyKeysTest, ScrollEventOneshot) { ui::SetUpScrollDeviceForTest(kScrollDeviceId); - // Australlian scrolling is enabled by default for some reason. + // Disable Australlian scrolling. ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true); scoped_ptr<ui::ScrollEvent> ev; @@ -518,20 +533,32 @@ TEST_F(StickyKeysTest, ScrollEvents) { // Enable sticky keys. EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); - kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); - kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); + SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); - // Test scroll event is correctly modified. - ev.reset(GenerateScrollEvent(scroll_deltas[i])); + // Test a scroll sequence. Sticky keys should only be disabled at the end + // of the scroll sequence. Fling cancel event starts the scroll sequence. + ev.reset(GenerateFlingScrollEvent(0, true)); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); + + // Scrolls should all be modified but not disable sticky keys. + for (int j = 0; j < 3; ++j) { + ev.reset(GenerateScrollEvent(scroll_deltas[i])); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); + } + + // Fling start event ends scroll sequence. + ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false)); sticky_key.HandleScrollEvent(ev.get()); EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); ASSERT_EQ(2U, mock_delegate->GetEventCount()); - EXPECT_EQ(ui::ET_SCROLL, mock_delegate->GetEvent(0)->type()); + EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type()); EXPECT_FLOAT_EQ(scroll_deltas[i], static_cast<const ui::ScrollEvent*>( mock_delegate->GetEvent(0))->y_offset()); @@ -540,23 +567,78 @@ TEST_F(StickyKeysTest, ScrollEvents) { static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) ->key_code()); } +} - // Lock sticky keys. +TEST_F(StickyKeysTest, ScrollDirectionChanged) { + ui::SetUpScrollDeviceForTest(kScrollDeviceId); + // Disable Australlian scrolling. + ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true); + + scoped_ptr<ui::ScrollEvent> ev; + scoped_ptr<ui::KeyEvent> kev; + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + + // Test direction change with both boundary value and negative value. + const int direction_change_values[2] = {0, -10}; for (int i = 0; i < 2; ++i) { - kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); - kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); + SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); + EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); + + // Fling cancel starts scroll sequence. + ev.reset(GenerateFlingScrollEvent(0, true)); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); + + // Test that changing directions in a scroll sequence will + // return sticky keys to DISABLED state. + for (int j = 0; j < 3; ++j) { + ev.reset(GenerateScrollEvent(10)); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); + } + + ev.reset(GenerateScrollEvent(direction_change_values[i])); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); } +} + +TEST_F(StickyKeysTest, ScrollEventLocked) { + ui::SetUpScrollDeviceForTest(kScrollDeviceId); + // Disable Australlian scrolling. + ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true); + + scoped_ptr<ui::ScrollEvent> ev; + scoped_ptr<ui::KeyEvent> kev; + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + + // Lock sticky keys. + SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); + SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); + EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); // Test scroll events are correctly modified in locked state. for (int i = 0; i < 5; ++i) { + // Fling cancel starts scroll sequence. + ev.reset(GenerateFlingScrollEvent(0, true)); + sticky_key.HandleScrollEvent(ev.get()); + ev.reset(GenerateScrollEvent(10)); sticky_key.HandleScrollEvent(ev.get()); EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); ev.reset(GenerateScrollEvent(-10)); sticky_key.HandleScrollEvent(ev.get()); EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + + // Fling start ends scroll sequence. + ev.reset(GenerateFlingScrollEvent(-10, false)); + sticky_key.HandleScrollEvent(ev.get()); } EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); diff --git a/ash/wm/system_gesture_event_filter_unittest.cc b/ash/wm/system_gesture_event_filter_unittest.cc index 328c9ca60b..aaf6634cd5 100644 --- a/ash/wm/system_gesture_event_filter_unittest.cc +++ b/ash/wm/system_gesture_event_filter_unittest.cc @@ -15,7 +15,7 @@ #include "ash/test/ash_test_base.h" #include "ash/test/display_manager_test_api.h" #include "ash/test/shell_test_api.h" -#include "ash/test/test_launcher_delegate.h" +#include "ash/test/test_shelf_delegate.h" #include "ash/volume_control_delegate.h" #include "ash/wm/gestures/long_press_affordance_handler.h" #include "ash/wm/window_state.h" diff --git a/ash/wm/system_modal_container_layout_manager.cc b/ash/wm/system_modal_container_layout_manager.cc index 99889d2f59..2c2cdebb57 100644 --- a/ash/wm/system_modal_container_layout_manager.cc +++ b/ash/wm/system_modal_container_layout_manager.cc @@ -21,6 +21,7 @@ #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event.h" #include "ui/gfx/screen.h" +#include "ui/views/background.h" #include "ui/views/corewm/compound_event_filter.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc index 13abd4a8c2..894e11b018 100644 --- a/ash/wm/window_animations.cc +++ b/ash/wm/window_animations.cc @@ -59,9 +59,6 @@ const float kWindowAnimation_ShowOpacity = 1.f; // TODO(sky): if we end up sticking with 0, nuke the code doing the rotation. const float kWindowAnimation_MinimizeRotate = 0.f; -// Tween type when cross fading a workspace window. -const gfx::Tween::Type kCrossFadeTweenType = gfx::Tween::EASE_IN_OUT; - // Scales for AshWindow above/below current workspace. const float kLayerScaleAboveSize = 1.1f; const float kLayerScaleBelowSize = .9f; @@ -108,8 +105,8 @@ void AddLayerAnimationsForMinimize(aura::Window* window, bool show) { rotation_about_pivot->SetReversed(show); - base::TimeDelta duration = base::TimeDelta::FromMilliseconds( - kLayerAnimationsForMinimizeDurationMS); + base::TimeDelta duration = window->layer()->GetAnimator()-> + GetTransitionDuration(); scoped_ptr<ui::LayerAnimationElement> transition( ui::LayerAnimationElement::CreateInterpolatedTransformElement( @@ -139,6 +136,10 @@ void AddLayerAnimationsForMinimize(aura::Window* window, bool show) { void AnimateShowWindow_Minimize(aura::Window* window) { window->layer()->set_delegate(window); window->layer()->SetOpacity(kWindowAnimation_HideOpacity); + ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); + base::TimeDelta duration = base::TimeDelta::FromMilliseconds( + kLayerAnimationsForMinimizeDurationMS); + settings.SetTransitionDuration(duration); AddLayerAnimationsForMinimize(window, true); // Now that the window has been restored, we need to clear its animation style @@ -324,7 +325,8 @@ base::TimeDelta CrossFadeImpl(aura::Window* window, const bool old_on_top = (old_bounds.width() > new_bounds.width()); // Shorten the animation if there's not much visual movement. - const base::TimeDelta duration = GetCrossFadeDuration(old_bounds, new_bounds); + const base::TimeDelta duration = GetCrossFadeDuration(window, + old_bounds, new_bounds); // Scale up the old layer while translating to new position. { @@ -416,23 +418,10 @@ void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds) { CrossFadeImpl(window, old_layer, gfx::Tween::EASE_OUT); } -void CrossFadeWindowBetweenWorkspaces(aura::Window* new_workspace, - aura::Window* window, - ui::Layer* old_layer) { - ui::Layer* layer_parent = new_workspace->layer()->parent(); - layer_parent->Add(old_layer); - const bool restoring = old_layer->bounds().width() > window->bounds().width(); - if (restoring) - layer_parent->StackAbove(old_layer, new_workspace->layer()); - else - layer_parent->StackBelow(old_layer, new_workspace->layer()); - - CrossFadeImpl(window, old_layer, kCrossFadeTweenType); -} - -base::TimeDelta GetCrossFadeDuration(const gfx::Rect& old_bounds, +base::TimeDelta GetCrossFadeDuration(aura::Window* window, + const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { - if (views::corewm::WindowAnimationsDisabled(NULL)) + if (views::corewm::WindowAnimationsDisabled(window)) return base::TimeDelta(); int old_area = old_bounds.width() * old_bounds.height(); diff --git a/ash/wm/window_animations.h b/ash/wm/window_animations.h index b0e4a2efec..f084bcc838 100644 --- a/ash/wm/window_animations.h +++ b/ash/wm/window_animations.h @@ -44,17 +44,10 @@ extern const int kCrossFadeDurationMS; ASH_EXPORT void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds); -// Cross fades |layer| (which is a clone of |window|s layer before it was -// resized) to |window|s current bounds. |new_workspace| is the Window of the -// workspace |window| was added to. -// This takes ownership of |layer|. -ASH_EXPORT void CrossFadeWindowBetweenWorkspaces(aura::Window* new_workspace, - aura::Window* window, - ui::Layer* layer); - // Returns the duration of the cross-fade animation based on the |old_bounds| -// and |new_bounds| of the window. -ASH_EXPORT base::TimeDelta GetCrossFadeDuration(const gfx::Rect& old_bounds, +// and |new_bounds| of the |window|. +ASH_EXPORT base::TimeDelta GetCrossFadeDuration(aura::Window* window, + const gfx::Rect& old_bounds, const gfx::Rect& new_bounds); ASH_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, diff --git a/ash/wm/window_animations_unittest.cc b/ash/wm/window_animations_unittest.cc index c2ea1f6365..eeeb120b09 100644 --- a/ash/wm/window_animations_unittest.cc +++ b/ash/wm/window_animations_unittest.cc @@ -6,13 +6,16 @@ #include "ash/shell_window_ids.h" #include "ash/test/ash_test_base.h" +#include "ash/wm/window_state.h" #include "ash/wm/workspace_controller.h" #include "base/time/time.h" #include "ui/aura/test/test_windows.h" #include "ui/aura/window.h" #include "ui/compositor/layer.h" +#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animator.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/animation/animation_container_element.h" using aura::Window; @@ -33,6 +36,37 @@ class WindowAnimationsTest : public ash::test::AshTestBase { DISALLOW_COPY_AND_ASSIGN(WindowAnimationsTest); }; +// Listens to animation scheduled notifications. Remembers the transition +// duration of the first sequence. +class MinimizeAnimationObserver : public ui::LayerAnimationObserver { + public: + explicit MinimizeAnimationObserver(ui::LayerAnimator* animator) + : animator_(animator) { + animator_->AddObserver(this); + // RemoveObserver is called when the first animation is scheduled and so + // there should be no need for now to remove it in destructor. + }; + base::TimeDelta duration() { return duration_; } + + protected: + // ui::LayerAnimationObserver: + virtual void OnLayerAnimationScheduled( + ui::LayerAnimationSequence* sequence) OVERRIDE { + duration_ = animator_->GetTransitionDuration(); + animator_->RemoveObserver(this); + } + virtual void OnLayerAnimationEnded( + ui::LayerAnimationSequence* sequence) OVERRIDE {} + virtual void OnLayerAnimationAborted( + ui::LayerAnimationSequence* sequence) OVERRIDE {} + + private: + ui::LayerAnimator* animator_; + base::TimeDelta duration_; + + DISALLOW_COPY_AND_ASSIGN(MinimizeAnimationObserver); +}; + TEST_F(WindowAnimationsTest, HideShowBrightnessGrayscaleAnimation) { scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); window->Show(); @@ -133,5 +167,77 @@ TEST_F(WindowAnimationsTest, CrossFadeToBounds) { Step(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1)); } +TEST_F(WindowAnimationsTest, LockAnimationDuration) { + ui::ScopedAnimationDurationScaleMode normal_duration_mode( + ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); + + scoped_ptr<Window> window(CreateTestWindowInShellWithId(0)); + Layer* layer = window->layer(); + window->SetBounds(gfx::Rect(5, 10, 320, 240)); + window->Show(); + + // Test that it is possible to override transition duration when it is not + // locked. + { + ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator()); + settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000)); + { + ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator()); + // Duration is not locked so it gets overridden. + settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50)); + wm::GetWindowState(window.get())->Minimize(); + EXPECT_TRUE(layer->GetAnimator()->is_animating()); + // Expect duration from the inner scope + EXPECT_EQ(50, + layer->GetAnimator()->GetTransitionDuration().InMilliseconds()); + } + window->Show(); + layer->GetAnimator()->StopAnimating(); + } + + // Test that it is possible to lock transition duration + { + ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator()); + settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000)); + // Duration is locked in outer scope. + settings1.LockTransitionDuration(); + { + ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator()); + // Transition duration setting is ignored. + settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50)); + wm::GetWindowState(window.get())->Minimize(); + EXPECT_TRUE(layer->GetAnimator()->is_animating()); + // Expect duration from the outer scope + EXPECT_EQ(1000, + layer->GetAnimator()->GetTransitionDuration().InMilliseconds()); + } + window->Show(); + layer->GetAnimator()->StopAnimating(); + } + + // Test that duration respects default. + { + // Query default duration. + MinimizeAnimationObserver observer(layer->GetAnimator()); + wm::GetWindowState(window.get())->Minimize(); + EXPECT_TRUE(layer->GetAnimator()->is_animating()); + base::TimeDelta default_duration(observer.duration()); + window->Show(); + layer->GetAnimator()->StopAnimating(); + + ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); + settings.LockTransitionDuration(); + // Setting transition duration is ignored since duration is locked + settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000)); + wm::GetWindowState(window.get())->Minimize(); + EXPECT_TRUE(layer->GetAnimator()->is_animating()); + // Expect default duration (200ms for stock ash minimizing animation). + EXPECT_EQ(default_duration.InMilliseconds(), + layer->GetAnimator()->GetTransitionDuration().InMilliseconds()); + window->Show(); + layer->GetAnimator()->StopAnimating(); + } +} + } // namespace internal } // namespace ash diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc index 428804f66f..d6bf24a59c 100644 --- a/ash/wm/window_cycle_list.cc +++ b/ash/wm/window_cycle_list.cc @@ -72,8 +72,6 @@ int WindowCycleList::GetWindowIndex(aura::Window* window) { } void WindowCycleList::OnWindowDestroyed(aura::Window* window) { - window->RemoveObserver(this); - WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window); DCHECK(i != windows_.end()); int removed_index = static_cast<int>(i - windows_.begin()); diff --git a/ash/wm/window_positioner.cc b/ash/wm/window_positioner.cc index 48e70a2a3d..9b31113f8c 100644 --- a/ash/wm/window_positioner.cc +++ b/ash/wm/window_positioner.cc @@ -58,8 +58,7 @@ bool UseAutoWindowManager(const aura::Window* window) { if (disable_auto_positioning) return false; const wm::WindowState* window_state = wm::GetWindowState(window); - return window_state->tracked_by_workspace() && - window_state->window_position_managed(); + return !window_state->is_dragged() && window_state->window_position_managed(); } // Check if a given |window| can be managed. This includes that it's state is diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc index 1a06a58da1..4bc1484f9b 100644 --- a/ash/wm/window_state.cc +++ b/ash/wm/window_state.cc @@ -4,6 +4,7 @@ #include "ash/wm/window_state.h" +#include "ash/ash_switches.h" #include "ash/root_window_controller.h" #include "ash/screen_ash.h" #include "ash/shell_window_ids.h" @@ -12,6 +13,8 @@ #include "ash/wm/window_state_observer.h" #include "ash/wm/window_util.h" #include "ash/wm/wm_types.h" +#include "base/auto_reset.h" +#include "base/command_line.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" @@ -29,7 +32,6 @@ bool WindowState::IsMaximizedOrFullscreenState(ui::WindowShowState show_state) { WindowState::WindowState(aura::Window* window) : window_(window), - tracked_by_workspace_(true), window_position_managed_(false), bounds_changed_by_user_(false), panel_attached_(true), @@ -42,13 +44,28 @@ WindowState::WindowState(aura::Window* window) hide_shelf_when_fullscreen_(true), animate_to_fullscreen_(true), minimum_visibility_(false), + in_set_window_show_type_(false), window_show_type_(ToWindowShowType(GetShowState())) { window_->AddObserver(this); + +#if defined(OS_CHROMEOS) + // NOTE(pkotwicz): Animating to immersive fullscreen does not look good. When + // the kAshEnableImmersiveFullscreenForAllWindows flag is set most windows + // can be put into immersive fullscreen. It is not worth the added complexity + // to only animate to fullscreen if the window is put into immersive + // fullscreen. + animate_to_fullscreen_ = !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAshEnableImmersiveFullscreenForAllWindows); +#endif } WindowState::~WindowState() { } +bool WindowState::HasDelegate() const { + return delegate_; +} + void WindowState::SetDelegate(scoped_ptr<WindowStateDelegate> delegate) { DCHECK(!delegate_.get()); delegate_ = delegate.Pass(); @@ -88,6 +105,11 @@ bool WindowState::IsDocked() const { window_->parent()->id() == internal::kShellWindowId_DockedContainer; } +bool WindowState::IsSnapped() const { + return window_show_type_ == SHOW_TYPE_LEFT_SNAPPED || + window_show_type_ == SHOW_TYPE_RIGHT_SNAPPED; +} + bool WindowState::CanMaximize() const { return window_->GetProperty(aura::client::kCanMaximizeKey); } @@ -136,7 +158,7 @@ void WindowState::SnapLeft(const gfx::Rect& bounds) { } void WindowState::SnapRight(const gfx::Rect& bounds) { - SnapWindow(SHOW_TYPE_LEFT_SNAPPED, bounds); + SnapWindow(SHOW_TYPE_RIGHT_SNAPPED, bounds); } void WindowState::Minimize() { @@ -235,54 +257,67 @@ void WindowState::RemoveObserver(WindowStateObserver* observer) { observer_list_.RemoveObserver(observer); } -void WindowState::SetTrackedByWorkspace(bool tracked_by_workspace) { - if (tracked_by_workspace_ == tracked_by_workspace) - return; - bool old = tracked_by_workspace_; - tracked_by_workspace_ = tracked_by_workspace; - FOR_EACH_OBSERVER(WindowStateObserver, observer_list_, - OnTrackedByWorkspaceChanged(this, old)); -} - void WindowState::OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) { DCHECK_EQ(window, window_); - if (key == aura::client::kShowStateKey) { - window_show_type_ = ToWindowShowType(GetShowState()); - ui::WindowShowState old_state = static_cast<ui::WindowShowState>(old); - // TODO(oshima): Notify only when the state has changed. - // Doing so break a few tests now. - FOR_EACH_OBSERVER( - WindowStateObserver, observer_list_, - OnWindowShowTypeChanged(this, ToWindowShowType(old_state))); - } -} - -void WindowState::OnWindowDestroying(aura::Window* window) { - window_->RemoveObserver(this); + if (key == aura::client::kShowStateKey) + SetWindowShowType(ToWindowShowType(GetShowState())); } void WindowState::SnapWindow(WindowShowType left_or_right, const gfx::Rect& bounds) { - if (IsMaximizedOrFullscreen()) { - // Before we can set the bounds we need to restore the window. - // Restoring the window will set the window to its restored bounds. - // To avoid an unnecessary bounds changes (which may have side effects) - // we set the restore bounds to the bounds we want, restore the window, - // then reset the restore bounds. This way no unnecessary bounds - // changes occurs and the original restore bounds is remembered. - gfx::Rect restore_bounds_in_screen = - GetRestoreBoundsInScreen(); - SetRestoreBoundsInParent(bounds); - Restore(); - SetRestoreBoundsInScreen(restore_bounds_in_screen); - } else { + if (window_show_type_ == left_or_right) { window_->SetBounds(bounds); + return; } + + // Compute the bounds that the window will restore to. If the window does not + // already have restore bounds, it will be restored (when un-snapped) to the + // last bounds that it had before getting snapped. + gfx::Rect restore_bounds_in_screen(HasRestoreBounds() ? + GetRestoreBoundsInScreen() : window_->GetBoundsInScreen()); + // Set the window's restore bounds so that WorkspaceLayoutManager knows + // which width to use when the snapped window is moved to the edge. + SetRestoreBoundsInParent(bounds); + + bool was_maximized = IsMaximizedOrFullscreen(); + // Before we can set the bounds we need to restore the window. + // Restoring the window will set the window to its restored bounds set above. + // Restore will cause OnWindowPropertyChanged() so it needs to be done + // before notifying that the WindowShowType has changed to |left_or_right|. + if (was_maximized) + Restore(); DCHECK(left_or_right == SHOW_TYPE_LEFT_SNAPPED || left_or_right == SHOW_TYPE_RIGHT_SNAPPED); - window_show_type_ = left_or_right; + SetWindowShowType(left_or_right); + // TODO(varkha): Ideally the bounds should be changed in a LayoutManager upon + // observing the WindowShowType change. + // If the window is a child of kShellWindowId_DockedContainer such as during + // a drag, the window's bounds are not set in + // WorkspaceLayoutManager::OnWindowShowTypeChanged(). Set them here. Skip + // setting the bounds otherwise to avoid stopping the slide animation which + // was started as a result of OnWindowShowTypeChanged(). + if (IsDocked()) + window_->SetBounds(bounds); + SetRestoreBoundsInScreen(restore_bounds_in_screen); +} + +void WindowState::SetWindowShowType(WindowShowType new_window_show_type) { + if (in_set_window_show_type_) + return; + base::AutoReset<bool> resetter(&in_set_window_show_type_, true); + + ui::WindowShowState new_window_state = + ToWindowShowState(new_window_show_type); + if (new_window_state != GetShowState()) + window_->SetProperty(aura::client::kShowStateKey, new_window_state); + WindowShowType old_window_show_type = window_show_type_; + window_show_type_ = new_window_show_type; + if (old_window_show_type != window_show_type_) { + FOR_EACH_OBSERVER(WindowStateObserver, observer_list_, + OnWindowShowTypeChanged(this, old_window_show_type)); + } } WindowState* GetActiveWindowState() { diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h index 2bbdd035b0..618349c86d 100644 --- a/ash/wm/window_state.h +++ b/ash/wm/window_state.h @@ -49,6 +49,7 @@ class ASH_EXPORT WindowState : public aura::WindowObserver { aura::Window* window() { return window_; } const aura::Window* window() const { return window_; } + bool HasDelegate() const; void SetDelegate(scoped_ptr<WindowStateDelegate> delegate); // Returns the window's current show state. @@ -69,6 +70,7 @@ class ASH_EXPORT WindowState : public aura::WindowObserver { bool IsNormalShowState() const; bool IsActive() const; bool IsDocked() const; + bool IsSnapped() const; // Checks if the window can change its state accordingly. bool CanMaximize() const; @@ -172,12 +174,8 @@ class ASH_EXPORT WindowState : public aura::WindowObserver { void AddObserver(WindowStateObserver* observer); void RemoveObserver(WindowStateObserver* observer); - // Whether the window is tracked by workspace code. Default is - // true. If set to false the workspace does not switch the current - // workspace, nor does it attempt to impose constraints on the - // bounds of the window. This is intended for tab dragging. - bool tracked_by_workspace() const { return tracked_by_workspace_; } - void SetTrackedByWorkspace(bool tracked_by_workspace); + // Whether the window is being dragged. + bool is_dragged() const { return !!window_resizer_; } // Whether or not the window's position can be managed by the // auto management logic. @@ -250,18 +248,19 @@ class ASH_EXPORT WindowState : public aura::WindowObserver { virtual void OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) OVERRIDE; - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; private: // Snaps the window to left or right of the desktop with given bounds. void SnapWindow(WindowShowType left_or_right, const gfx::Rect& bounds); + // Sets the window show type and updates the show state if necessary. + void SetWindowShowType(WindowShowType new_window_show_type); + // The owner of this window settings. aura::Window* window_; scoped_ptr<WindowStateDelegate> delegate_; - bool tracked_by_workspace_; bool window_position_managed_; bool bounds_changed_by_user_; bool panel_attached_; @@ -283,6 +282,9 @@ class ASH_EXPORT WindowState : public aura::WindowObserver { ObserverList<WindowStateObserver> observer_list_; + // True when in SetWindowShowType(). This is used to avoid reentrance. + bool in_set_window_show_type_; + WindowShowType window_show_type_; DISALLOW_COPY_AND_ASSIGN(WindowState); diff --git a/ash/wm/window_state_observer.h b/ash/wm/window_state_observer.h index 75008a8c1d..f177d5fbeb 100644 --- a/ash/wm/window_state_observer.h +++ b/ash/wm/window_state_observer.h @@ -14,11 +14,6 @@ class WindowState; class ASH_EXPORT WindowStateObserver { public: - // Called when the tracked_by_workspace has changed. - virtual void OnTrackedByWorkspaceChanged( - WindowState* window, - bool old_value) {} - // Called when the window's show type has changed. This is different from // kWindowShowStatekey property change as this will be invoked when the window // gets left/right maximized, and auto positioned. |old_type| is the value diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc index 8d320a9c2e..def4b6d5ba 100644 --- a/ash/wm/window_util.cc +++ b/ash/wm/window_util.cc @@ -7,6 +7,7 @@ #include <vector> #include "ash/ash_constants.h" +#include "ash/screen_ash.h" #include "ash/shell.h" #include "ash/wm/window_properties.h" #include "ash/wm/window_state.h" @@ -62,12 +63,19 @@ void CenterWindow(aura::Window* window) { const gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(window); gfx::Rect center = display.work_area(); - gfx::Size size = window_state->HasRestoreBounds() ? - window_state->GetRestoreBoundsInScreen().size() : - window->bounds().size(); - center.ClampToCenteredSize(size); - window_state->SetRestoreBoundsInScreen(center); - window_state->Restore(); + gfx::Size size = window->bounds().size(); + if (window_state->IsSnapped()) { + if (window_state->HasRestoreBounds()) + size = window_state->GetRestoreBoundsInScreen().size(); + center.ClampToCenteredSize(size); + window_state->SetRestoreBoundsInScreen(center); + window_state->Restore(); + } else { + center = ScreenAsh::ConvertRectFromScreen(window->parent(), + center); + center.ClampToCenteredSize(size); + window->SetBounds(center); + } } void AdjustBoundsToEnsureMinimumWindowVisibility(const gfx::Rect& visible_area, @@ -115,16 +123,22 @@ bool MoveWindowToEventRoot(aura::Window* window, const ui::Event& event) { return true; } -void ReparentChildWithTransientChildren(aura::Window* window, - aura::Window* child) { - window->AddChild(child); - ReparentTransientChildrenOfChild(window, child); +void ReparentChildWithTransientChildren(aura::Window* child, + aura::Window* old_parent, + aura::Window* new_parent) { + if (child->parent() == old_parent) + new_parent->AddChild(child); + ReparentTransientChildrenOfChild(child, old_parent, new_parent); } -void ReparentTransientChildrenOfChild(aura::Window* window, - aura::Window* child) { - for (size_t i = 0; i < child->transient_children().size(); ++i) - ReparentChildWithTransientChildren(window, child->transient_children()[i]); +void ReparentTransientChildrenOfChild(aura::Window* child, + aura::Window* old_parent, + aura::Window* new_parent) { + for (size_t i = 0; i < child->transient_children().size(); ++i) { + ReparentChildWithTransientChildren(child->transient_children()[i], + old_parent, + new_parent); + } } } // namespace wm diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h index fb12959359..fd77505c40 100644 --- a/ash/wm/window_util.h +++ b/ash/wm/window_util.h @@ -68,13 +68,18 @@ ASH_EXPORT void AdjustBoundsToEnsureWindowVisibility( ASH_EXPORT bool MoveWindowToEventRoot(aura::Window* window, const ui::Event& event); -// Adds |child| and all its transient children to |window|. -void ReparentChildWithTransientChildren(aura::Window* window, - aura::Window* child); - -// Changes the parent of all transient children of a |child| to |window|. -void ReparentTransientChildrenOfChild(aura::Window* window, - aura::Window* child); +// Changes the parent of a |child| and all its transient children that are +// themselves children of |old_parent| to |new_parent|. +void ReparentChildWithTransientChildren(aura::Window* child, + aura::Window* old_parent, + aura::Window* new_parent); + +// Changes the parent of all transient children of a |child| to |new_parent|. +// Does not change parent of the transient children that are not themselves +// children of |old_parent|. +void ReparentTransientChildrenOfChild(aura::Window* child, + aura::Window* old_parent, + aura::Window* new_parent); } // namespace wm } // namespace ash diff --git a/ash/wm/workspace/colored_window_controller.cc b/ash/wm/workspace/colored_window_controller.cc deleted file mode 100644 index bffff5486f..0000000000 --- a/ash/wm/workspace/colored_window_controller.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/wm/workspace/colored_window_controller.h" - -#include "ash/shell_window_ids.h" -#include "ash/wm/window_state.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/root_window.h" -#include "ui/gfx/canvas.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_delegate.h" - -namespace ash { -namespace internal { - -// View implementation responsible for rendering the background. -class ColoredWindowController::View : public views::WidgetDelegateView { - public: - explicit View(ColoredWindowController* controller); - virtual ~View(); - - // Closes the hosting widget. - void Close(); - - // WidgetDelegate overrides: - virtual views::View* GetContentsView() OVERRIDE; - - private: - ColoredWindowController* controller_; - - DISALLOW_COPY_AND_ASSIGN(View); -}; - -ColoredWindowController::View::View(ColoredWindowController* controller) - : controller_(controller) { -} - -ColoredWindowController::View::~View() { - if (controller_) - controller_->view_ = NULL; -} - -void ColoredWindowController::View::Close() { - controller_ = NULL; - GetWidget()->Close(); -} - -views::View* ColoredWindowController::View::GetContentsView() { - return this; -} - -ColoredWindowController::ColoredWindowController(aura::Window* parent, - const std::string& window_name) - : view_(new View(this)) { - views::Widget* widget = new views::Widget; - views::Widget::InitParams params( - views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.delegate = view_; - params.parent = parent; - params.can_activate = false; - params.accept_events = false; - params.layer_type = ui::LAYER_SOLID_COLOR; - widget->Init(params); - // Do this so the parent doesn't attempt to enforce any bounds constraints on - // us. - wm::GetWindowState(widget->GetNativeView())->SetTrackedByWorkspace(false); - widget->GetNativeView()->SetProperty(aura::client::kAnimationsDisabledKey, - true); - widget->GetNativeView()->SetName(window_name); - // The bounds should match the parent exactly. We don't go through - // Widget::SetBounds() as that may try to place on a different display. - widget->GetNativeWindow()->SetBounds(gfx::Rect(parent->bounds())); -} - -ColoredWindowController::~ColoredWindowController() { - if (view_) - view_->Close(); -} - -void ColoredWindowController::SetColor(SkColor color) { - if (view_) - view_->GetWidget()->GetNativeView()->layer()->SetColor(color); -} - -views::Widget* ColoredWindowController::GetWidget() { - return view_ ? view_->GetWidget() : NULL; -} - -} // namespace internal -} // namespace ash diff --git a/ash/wm/workspace/colored_window_controller.h b/ash/wm/workspace/colored_window_controller.h deleted file mode 100644 index d70a83b7a3..0000000000 --- a/ash/wm/workspace/colored_window_controller.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_ -#define ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_ - -#include <string> - -#include "ash/ash_export.h" -#include "base/basictypes.h" - -typedef unsigned int SkColor; - -namespace aura { -class Window; -} - -namespace views { -class Widget; -} - -namespace ash { -namespace internal { - -// ColoredWindowController creates a Widget whose layer is LAYER_SOLID_COLOR. -// The Widget is sized to the supplied Window and parented to the specified -// Window. It is used for animations. -class ASH_EXPORT ColoredWindowController { - public: - ColoredWindowController(aura::Window* parent, const std::string& window_name); - ~ColoredWindowController(); - - // Changes the background color. - void SetColor(SkColor color); - - views::Widget* GetWidget(); - - private: - class View; - - // View responsible for rendering the background. This is non-NULL if the - // widget containing it is deleted. It is owned by the widget. - View* view_; - - DISALLOW_COPY_AND_ASSIGN(ColoredWindowController); -}; - -} // namespace internal -} // namespace ash - -#endif // ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_ diff --git a/ash/wm/workspace/desktop_background_fade_controller.cc b/ash/wm/workspace/desktop_background_fade_controller.cc deleted file mode 100644 index abbdf578c8..0000000000 --- a/ash/wm/workspace/desktop_background_fade_controller.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/wm/workspace/desktop_background_fade_controller.h" - -#include "ash/wm/window_animations.h" -#include "ash/wm/workspace/colored_window_controller.h" -#include "base/time/time.h" -#include "ui/aura/window.h" -#include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/views/widget/widget.h" - -namespace ash { -namespace internal { - -DesktopBackgroundFadeController::DesktopBackgroundFadeController( - aura::Window* parent, - aura::Window* position_above, - base::TimeDelta duration, - Direction direction) { - SkColor start_color, target_color; - gfx::Tween::Type tween_type; - if (direction == FADE_OUT) { - start_color = SkColorSetARGB(0, 0, 0, 0); - target_color = SK_ColorBLACK; - tween_type = gfx::Tween::EASE_IN_OUT; - } else { - start_color = SK_ColorBLACK; - target_color = SkColorSetARGB(0, 0, 0, 0); - tween_type = gfx::Tween::EASE_IN_OUT; - } - - window_controller_.reset( - new ColoredWindowController(parent, "DesktopFade")); - - // Force the window to be directly on top of the desktop. - aura::Window* fade_window = window_controller_->GetWidget()->GetNativeView(); - parent->StackChildBelow(fade_window, position_above); - parent->StackChildAbove(fade_window, position_above); - window_controller_->SetColor(start_color); - views::corewm::SetWindowVisibilityAnimationTransition( - window_controller_->GetWidget()->GetNativeView(), - views::corewm::ANIMATE_NONE); - window_controller_->GetWidget()->Show(); - { - ui::ScopedLayerAnimationSettings scoped_setter( - fade_window->layer()->GetAnimator()); - scoped_setter.AddObserver(this); - scoped_setter.SetTweenType(tween_type); - scoped_setter.SetTransitionDuration(duration); - window_controller_->SetColor(target_color); - } -} - -DesktopBackgroundFadeController::~DesktopBackgroundFadeController() { - StopObservingImplicitAnimations(); -} - -void DesktopBackgroundFadeController::OnImplicitAnimationsCompleted() { - window_controller_.reset(); -} - -} // namespace internal -} // namespace ash diff --git a/ash/wm/workspace/desktop_background_fade_controller.h b/ash/wm/workspace/desktop_background_fade_controller.h deleted file mode 100644 index 3cc0137da6..0000000000 --- a/ash/wm/workspace/desktop_background_fade_controller.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_ -#define ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_ - -#include "ash/ash_export.h" -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "ui/compositor/layer_animation_observer.h" - -namespace aura { -class Window; -} - -namespace base { -class TimeDelta; -} - -namespace ash { -namespace internal { - -class ColoredWindowController; - -// DesktopBackgroundFadeController handles fading in or out the desktop. It is -// used when maximizing or restoring a window. It is implemented as a colored -// layer whose opacity varies. This results in fading in or out all the windows -// the DesktopBackgroundFadeController is placed on top of. This is used -// instead of varying the opacity for two reasons: -// . The window showing background and the desktop workspace do not have a -// common parent that can be animated. This could be fixed, but wouldn't -// address the following. -// . When restoring the window is moved back to the desktop workspace. If we -// animated the opacity of the desktop workspace the cross fade would be -// effected. -class ASH_EXPORT DesktopBackgroundFadeController - : public ui::ImplicitAnimationObserver { - public: - // Direction to fade. - enum Direction { - FADE_IN, - FADE_OUT, - }; - - // Creates a new DesktopBackgroundFadeController. |parent| is the Window to -// parent the newly created window to. The newly created window is stacked -// directly on top of |position_above|. The window animating the fade is -// destroyed as soon as the animation completes. - DesktopBackgroundFadeController(aura::Window* parent, - aura::Window* position_above, - base::TimeDelta duration, - Direction direction); - virtual ~DesktopBackgroundFadeController(); - - private: - // ImplicitAnimationObserver overrides: - virtual void OnImplicitAnimationsCompleted() OVERRIDE; - - scoped_ptr<ColoredWindowController> window_controller_; - - DISALLOW_COPY_AND_ASSIGN(DesktopBackgroundFadeController); -}; - -} // namespace internal -} // namespace ash - -#endif // ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_ diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc index 0e69c85ccc..83a0e3fdcb 100644 --- a/ash/wm/workspace/phantom_window_controller.cc +++ b/ash/wm/workspace/phantom_window_controller.cc @@ -15,11 +15,11 @@ #include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/canvas.h" #include "ui/gfx/skia_util.h" +#include "ui/views/background.h" #include "ui/views/painter.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" - namespace ash { namespace internal { diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc index a0ae8cc370..fe7d748d19 100644 --- a/ash/wm/workspace/workspace_layout_manager.cc +++ b/ash/wm/workspace/workspace_layout_manager.cc @@ -20,6 +20,7 @@ #include "ui/aura/window.h" #include "ui/aura/window_observer.h" #include "ui/base/ui_base_types.h" +#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event.h" #include "ui/views/corewm/window_util.h" @@ -71,7 +72,9 @@ WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window) shelf_(NULL), window_(window), work_area_in_parent_(ScreenAsh::GetDisplayWorkAreaBoundsInParent( - window->parent())) { + window->parent())), + is_fullscreen_(GetRootWindowController( + window->GetRootWindow())->GetWindowForFullscreenMode() != NULL) { } WorkspaceLayoutManager::~WorkspaceLayoutManager() { @@ -84,7 +87,8 @@ void WorkspaceLayoutManager::SetShelf(internal::ShelfLayoutManager* shelf) { void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) { AdjustWindowBoundsWhenAdded(wm::GetWindowState(child)); BaseLayoutManager::OnWindowAddedToLayout(child); - UpdateDesktopVisibility(); + UpdateShelfVisibility(); + UpdateFullscreenState(); WindowPositioner::RearrangeVisibleWindowOnShow(child); } @@ -96,7 +100,8 @@ void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(Window* child) { void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) { BaseLayoutManager::OnWindowRemovedFromLayout(child); - UpdateDesktopVisibility(); + UpdateShelfVisibility(); + UpdateFullscreenState(); } void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child, @@ -105,34 +110,32 @@ void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child, if (child->TargetVisibility()) { WindowPositioner::RearrangeVisibleWindowOnShow(child); } else { - if (wm::GetWindowState(child)->IsFullscreen()) { - ash::Shell::GetInstance()->NotifyFullscreenStateChange( - false, child->GetRootWindow()); - } + if (wm::GetWindowState(child)->IsFullscreen()) + UpdateFullscreenState(); WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child); } - UpdateDesktopVisibility(); + UpdateShelfVisibility(); } void WorkspaceLayoutManager::SetChildBounds( Window* child, const gfx::Rect& requested_bounds) { - if (!wm::GetWindowState(child)->tracked_by_workspace()) { + wm::WindowState* window_state = wm::GetWindowState(child); + if (window_state->is_dragged()) { SetChildBoundsDirect(child, requested_bounds); - return; - } - gfx::Rect child_bounds(requested_bounds); - // Some windows rely on this to set their initial bounds. - if (!SetMaximizedOrFullscreenBounds(wm::GetWindowState(child))) { + } else if (!SetMaximizedOrFullscreenBounds(window_state)) { + // Some windows rely on this to set their initial bounds. // Non-maximized/full-screen windows have their size constrained to the // work-area. + gfx::Rect child_bounds(requested_bounds); child_bounds.set_width(std::min(work_area_in_parent_.width(), child_bounds.width())); - child_bounds.set_height( - std::min(work_area_in_parent_.height(), child_bounds.height())); + child_bounds.set_height(std::min(work_area_in_parent_.height(), + child_bounds.height())); + AdjustSnappedBounds(window_state, &child_bounds); SetChildBoundsDirect(child, child_bounds); } - UpdateDesktopVisibility(); + UpdateShelfVisibility(); } void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() { @@ -154,11 +157,10 @@ void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window, } } -void WorkspaceLayoutManager::OnTrackedByWorkspaceChanged( - wm::WindowState* window_state, - bool old){ - if (window_state->tracked_by_workspace()) - SetMaximizedOrFullscreenBounds(window_state); +void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) { + BaseLayoutManager::OnWindowStackingChanged(window); + UpdateShelfVisibility(); + UpdateFullscreenState(); } void WorkspaceLayoutManager::OnWindowShowTypeChanged( @@ -189,9 +191,7 @@ void WorkspaceLayoutManager::OnWindowShowTypeChanged( if (old_state != new_state && (new_state == ui::SHOW_STATE_FULLSCREEN || old_state == ui::SHOW_STATE_FULLSCREEN)) { - ash::Shell::GetInstance()->NotifyFullscreenStateChange( - new_state == ui::SHOW_STATE_FULLSCREEN, - window_state->window()->GetRootWindow()); + UpdateFullscreenState(); } UpdateBoundsFromShowState(window_state, old_state); @@ -206,7 +206,7 @@ void WorkspaceLayoutManager::ShowStateChanged( wm::WindowState* state, ui::WindowShowState last_show_state) { BaseLayoutManager::ShowStateChanged(state, last_show_state); - UpdateDesktopVisibility(); + UpdateShelfVisibility(); } void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange( @@ -219,7 +219,7 @@ void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange( void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange( wm::WindowState* window_state, AdjustWindowReason reason) { - if (!window_state->tracked_by_workspace()) + if (window_state->is_dragged()) return; // Do not cross fade here: the window's layer hierarchy may be messed up for @@ -249,8 +249,9 @@ void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange( work_area_in_parent_, &bounds); break; } + AdjustSnappedBounds(window_state, &bounds); if (window_state->window()->bounds() != bounds) - window_state->window()->SetBounds(bounds); + SetChildBoundsAnimated(window_state->window(), bounds); } void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded( @@ -263,7 +264,7 @@ void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded( if (window_state->window()->bounds().IsEmpty()) return; - if (!window_state->tracked_by_workspace()) + if (window_state->is_dragged()) return; if (SetMaximizedOrFullscreenBounds(window_state)) @@ -281,21 +282,33 @@ void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded( ash::wm::AdjustBoundsToEnsureWindowVisibility( display_area, min_width, min_height, &bounds); + AdjustSnappedBounds(window_state, &bounds); if (window->bounds() != bounds) window->SetBounds(bounds); } -void WorkspaceLayoutManager::UpdateDesktopVisibility() { +void WorkspaceLayoutManager::UpdateShelfVisibility() { if (shelf_) shelf_->UpdateVisibilityState(); } +void WorkspaceLayoutManager::UpdateFullscreenState() { + bool is_fullscreen = GetRootWindowController( + window_->GetRootWindow())->GetWindowForFullscreenMode() != NULL; + if (is_fullscreen != is_fullscreen_) { + ash::Shell::GetInstance()->NotifyFullscreenStateChange( + is_fullscreen, window_->GetRootWindow()); + is_fullscreen_ = is_fullscreen; + } +} + void WorkspaceLayoutManager::UpdateBoundsFromShowState( wm::WindowState* window_state, ui::WindowShowState last_show_state) { aura::Window* window = window_state->window(); // See comment in SetMaximizedOrFullscreenBounds() as to why we use parent in // these calculation. + // TODO(varkha): Change the switch statement below to use wm::WindowShowType. switch (window_state->GetShowState()) { case ui::SHOW_STATE_DEFAULT: case ui::SHOW_STATE_NORMAL: { @@ -317,13 +330,20 @@ void WorkspaceLayoutManager::UpdateBoundsFromShowState( bounds_in_parent.SetRect(0, 0, 0, 0); } if (!bounds_in_parent.IsEmpty()) { - gfx::Rect new_bounds = BaseLayoutManager::BoundsWithScreenEdgeVisible( - window->parent()->parent(), - bounds_in_parent); - if (last_show_state == ui::SHOW_STATE_MINIMIZED) - SetChildBoundsDirect(window, new_bounds); - else - CrossFadeToBounds(window, new_bounds); + if ((last_show_state == ui::SHOW_STATE_DEFAULT || + last_show_state == ui::SHOW_STATE_NORMAL) && + window_state->IsSnapped()) { + AdjustSnappedBounds(window_state, &bounds_in_parent); + SetChildBoundsAnimated(window, bounds_in_parent); + } else { + gfx::Rect new_bounds = BaseLayoutManager::BoundsWithScreenEdgeVisible( + window->parent()->parent(), + bounds_in_parent); + if (last_show_state == ui::SHOW_STATE_MINIMIZED) + SetChildBoundsDirect(window, new_bounds); + else + CrossFadeToBounds(window, new_bounds); + } } window_state->ClearRestoreBounds(); break; @@ -363,8 +383,7 @@ void WorkspaceLayoutManager::UpdateBoundsFromShowState( bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds( wm::WindowState* window_state) { - if (!window_state->tracked_by_workspace()) - return false; + DCHECK(!window_state->is_dragged()); // During animations there is a transform installed on the workspace // windows. For this reason this code uses the parent so that the transform is @@ -385,5 +404,33 @@ bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds( return false; } +void WorkspaceLayoutManager::AdjustSnappedBounds(wm::WindowState* window_state, + gfx::Rect* bounds) { + if (window_state->is_dragged() || !window_state->IsSnapped()) + return; + gfx::Rect maximized_bounds = ScreenAsh::GetMaximizedWindowBoundsInParent( + window_state->window()->parent()->parent()); + if (window_state->window_show_type() == wm::SHOW_TYPE_LEFT_SNAPPED) + bounds->set_x(maximized_bounds.x()); + else if (window_state->window_show_type() == wm::SHOW_TYPE_RIGHT_SNAPPED) + bounds->set_x(maximized_bounds.right() - bounds->width()); + bounds->set_y(maximized_bounds.y()); + // TODO(varkha): Set width to 50% here for snapped windows. + bounds->set_height(maximized_bounds.height()); +} + +void WorkspaceLayoutManager::SetChildBoundsAnimated(Window* child, + const gfx::Rect& bounds) { + const int kBoundsChangeSlideDurationMs = 120; + + ui::Layer* layer = child->layer(); + ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator()); + slide_settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + slide_settings.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kBoundsChangeSlideDurationMs)); + SetChildBoundsDirect(child, bounds); +} + } // namespace internal } // namespace ash diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h index 30a88f2e4a..ab4876c5b2 100644 --- a/ash/wm/workspace/workspace_layout_manager.h +++ b/ash/wm/workspace/workspace_layout_manager.h @@ -55,10 +55,7 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager { virtual void OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) OVERRIDE; - - // ash::WindowSettings::Observer overrides: - virtual void OnTrackedByWorkspaceChanged(wm::WindowState* window_state, - bool old) OVERRIDE; + virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE; // WindowStateObserver overrides: virtual void OnWindowShowTypeChanged(wm::WindowState* window_state, @@ -76,7 +73,12 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager { void AdjustWindowBoundsWhenAdded(wm::WindowState* window_state); - void UpdateDesktopVisibility(); + // Updates the visibility state of the shelf. + void UpdateShelfVisibility(); + + // Updates the fullscreen state of the workspace and notifies Shell if it + // has changed. + void UpdateFullscreenState(); // Updates the bounds of the window for a show state change from // |last_show_state|. @@ -87,6 +89,13 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager { // window are set and true is returned. Does nothing otherwise. bool SetMaximizedOrFullscreenBounds(wm::WindowState* window_state); + // Adjusts the |bounds| so that they are flush with the edge of the + // workspace if the window represented by |window_state| is side snapped. + void AdjustSnappedBounds(wm::WindowState* window_state, gfx::Rect* bounds); + + // Animates the window bounds to |bounds|. + void SetChildBoundsAnimated(aura::Window* child, const gfx::Rect& bounds); + internal::ShelfLayoutManager* shelf_; aura::Window* window_; @@ -94,6 +103,9 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager { // workspace switch. gfx::Rect work_area_in_parent_; + // True if this workspace is currently in fullscreen mode. + bool is_fullscreen_; + DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager); }; diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc index 4302f1dc44..d12fb15bb7 100644 --- a/ash/wm/workspace/workspace_layout_manager_unittest.cc +++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc @@ -11,6 +11,7 @@ #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" +#include "ash/shell_observer.h" #include "ash/test/ash_test_base.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" @@ -47,6 +48,38 @@ class MaximizeDelegateView : public views::WidgetDelegateView { DISALLOW_COPY_AND_ASSIGN(MaximizeDelegateView); }; +class TestShellObserver : public ShellObserver { + public: + TestShellObserver() : call_count_(0), + is_fullscreen_(false) { + Shell::GetInstance()->AddShellObserver(this); + } + + virtual ~TestShellObserver() { + Shell::GetInstance()->RemoveShellObserver(this); + } + + virtual void OnFullscreenStateChanged(bool is_fullscreen, + aura::Window* root_window) OVERRIDE { + call_count_++; + is_fullscreen_ = is_fullscreen; + } + + int call_count() const { + return call_count_; + } + + bool is_fullscreen() const { + return is_fullscreen_; + } + + private: + int call_count_; + bool is_fullscreen_; + + DISALLOW_COPY_AND_ASSIGN(TestShellObserver); +}; + } // namespace typedef test::AshTestBase WorkspaceLayoutManagerTest; @@ -407,4 +440,42 @@ TEST_F(WorkspaceLayoutManagerTest, SizeToWorkArea) { window->bounds().ToString()); } +TEST_F(WorkspaceLayoutManagerTest, NotifyFullscreenChanges) { + TestShellObserver observer; + scoped_ptr<aura::Window> window1( + CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40))); + scoped_ptr<aura::Window> window2( + CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40))); + wm::WindowState* window_state1 = wm::GetWindowState(window1.get()); + wm::WindowState* window_state2 = wm::GetWindowState(window2.get()); + window_state2->Activate(); + + window_state2->ToggleFullscreen(); + EXPECT_EQ(1, observer.call_count()); + EXPECT_TRUE(observer.is_fullscreen()); + + // When window1 moves to the front the fullscreen state should change. + window_state1->Activate(); + EXPECT_EQ(2, observer.call_count()); + EXPECT_FALSE(observer.is_fullscreen()); + + // It should change back if window2 becomes active again. + window_state2->Activate(); + EXPECT_EQ(3, observer.call_count()); + EXPECT_TRUE(observer.is_fullscreen()); + + window_state2->ToggleFullscreen(); + EXPECT_EQ(4, observer.call_count()); + EXPECT_FALSE(observer.is_fullscreen()); + + window_state2->ToggleFullscreen(); + EXPECT_EQ(5, observer.call_count()); + EXPECT_TRUE(observer.is_fullscreen()); + + // Closing the window should change the fullscreen state. + window2.reset(); + EXPECT_EQ(6, observer.call_count()); + EXPECT_FALSE(observer.is_fullscreen()); +} + } // namespace ash diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index 3617da5225..b8cd504944 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc @@ -74,7 +74,7 @@ scoped_ptr<WindowResizer> CreateWindowResizer( // is set by tab dragging code. if (!window_state->IsNormalShowState() && (window_component != HTCAPTION || - window_state->tracked_by_workspace())) { + !window_state->is_dragged())) { return scoped_ptr<WindowResizer>(); } window_resizer = internal::WorkspaceWindowResizer::Create( @@ -436,6 +436,7 @@ void WorkspaceWindowResizer::CompleteDrag(int event_flags) { if (!did_move_or_resize_ || details_.window_component != HTCAPTION) return; + bool snapped = false; // When the window is not in the normal show state, we do not snap the window. // This happens when the user minimizes or maximizes the window by keyboard // shortcut while dragging it. If the window is the result of dragging a tab @@ -458,8 +459,11 @@ void WorkspaceWindowResizer::CompleteDrag(int event_flags) { if (window_state()->CanResize() && !dock_layout_->is_dragged_window_docked()) { snap_sizer_->SnapWindowToTargetBounds(); + snapped = true; } } + if (window_state()->IsSnapped() && !snapped) + window_state()->Restore(); } void WorkspaceWindowResizer::RevertDrag() { @@ -749,9 +753,9 @@ bool WorkspaceWindowResizer::UpdateMagnetismWindow(const gfx::Rect& bounds, magnetism_window_ = NULL; } - // Avoid magnetically snapping to popups, menus, tooltips, controls and - // windows that are not tracked by workspace. - if (!window_state()->CanResize() || !window_state()->tracked_by_workspace()) + // Avoid magnetically snapping windows that are not resizable. + // TODO(oshima): change this to window.type() == TYPE_NORMAL. + if (!window_state()->CanResize()) return false; aura::Window::Windows root_windows = Shell::GetAllRootWindows(); diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc index c049103ff3..599c4ea98e 100644 --- a/ash/wm/workspace/workspace_window_resizer_unittest.cc +++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc @@ -596,6 +596,11 @@ TEST_F(WorkspaceWindowResizerTest, MouseMoveWithTouchDrag) { // Assertions around dragging to the left/right edge of the screen. TEST_F(WorkspaceWindowResizerTest, Edge) { + if (!SupportsHostWindowResize()) + return; + + // Resize host window to force insets update. + UpdateDisplay("800x700"); // TODO(varkha): Insets are reset after every drag because of // http://crbug.com/292238. // Window is wide enough not to get docked right away. @@ -636,11 +641,12 @@ TEST_F(WorkspaceWindowResizerTest, Edge) { } // Test if the restore bounds is correct in multiple displays. - window_state->ClearRestoreBounds(); - if (!SupportsMultipleDisplays()) return; + // Restore the window to clear snapped state. + window_state->Restore(); + UpdateDisplay("800x600,500x600"); aura::Window::Windows root_windows = Shell::GetAllRootWindows(); EXPECT_EQ(root_windows[0], window_->GetRootWindow()); @@ -654,7 +660,7 @@ TEST_F(WorkspaceWindowResizerTest, Edge) { scoped_ptr<WindowResizer> resizer(CreateResizerForTest( window_.get(), gfx::Point(), HTCAPTION)); ASSERT_TRUE(resizer.get()); - resizer->Drag(CalculateDragPoint(*resizer, 499, 00), 0); + resizer->Drag(CalculateDragPoint(*resizer, 499, 0), 0); int bottom = ScreenAsh::GetDisplayWorkAreaBoundsInParent(window_.get()).bottom(); resizer->CompleteDrag(0); @@ -783,8 +789,9 @@ TEST_F(WorkspaceWindowResizerTest, DontDragOffBottomWithMultiDisplay) { window_->SetBounds(gfx::Rect(100, 200, 300, 20)); DCHECK_LT(window_->bounds().height(), WorkspaceWindowResizer::kMinOnscreenHeight); + // Drag down avoiding dragging along the edge as that would side-snap. scoped_ptr<WindowResizer> resizer(CreateResizerForTest( - window_.get(), gfx::Point(), HTCAPTION)); + window_.get(), gfx::Point(10, 0), HTCAPTION)); ASSERT_TRUE(resizer.get()); resizer->Drag(CalculateDragPoint(*resizer, 0, 400), 0); int expected_y = kRootHeight - window_->bounds().height() - 10; @@ -802,8 +809,9 @@ TEST_F(WorkspaceWindowResizerTest, DontDragOffBottomWithMultiDisplay) { { window_->SetBounds(gfx::Rect(100, 200, 300, 400)); scoped_ptr<WindowResizer> resizer(CreateResizerForTest( - window_.get(), gfx::Point(), HTCAPTION)); + window_.get(), gfx::Point(10, 0), HTCAPTION)); ASSERT_TRUE(resizer.get()); + // Drag down avoiding dragging along the edge as that would side-snap. resizer->Drag(CalculateDragPoint(*resizer, 0, 400), 0); int expected_y = kRootHeight - WorkspaceWindowResizer::kMinOnscreenHeight - 10; diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc index 8d37858140..7bf6a28be9 100644 --- a/ash/wm/workspace_controller.cc +++ b/ash/wm/workspace_controller.cc @@ -4,6 +4,7 @@ #include "ash/wm/workspace_controller.h" +#include "ash/root_window_controller.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" @@ -30,6 +31,15 @@ namespace { // animation (when logging in). const int kInitialPauseTimeMS = 750; +// Returns true if the |window| is docked and visible. +bool IsDockedAndVisible(const aura::Window* window) { + return (window->parent()->id() == kShellWindowId_DockedContainer && + window->IsVisible() && + !wm::GetWindowState(window)->IsMinimized() && + window->type() != aura::client::WINDOW_TYPE_POPUP && + !window->transient_parent()); +} + } // namespace WorkspaceController::WorkspaceController(aura::Window* viewport) @@ -57,31 +67,42 @@ WorkspaceController::~WorkspaceController() { WorkspaceWindowState WorkspaceController::GetWindowState() const { if (!shelf_) return WORKSPACE_WINDOW_STATE_DEFAULT; + const aura::Window* topmost_fullscreen_window = GetRootWindowController( + viewport_->GetRootWindow())->GetWindowForFullscreenMode(); + if (topmost_fullscreen_window && + !wm::GetWindowState(topmost_fullscreen_window)->ignored_by_shelf()) { + return WORKSPACE_WINDOW_STATE_FULL_SCREEN; + } + // These are the container ids of containers which may contain windows that + // may overlap the launcher shelf and affect its transparency. + const int kWindowContainerIds[] = { + internal::kShellWindowId_DefaultContainer, + internal::kShellWindowId_DockedContainer, + }; const gfx::Rect shelf_bounds(shelf_->GetIdealBounds()); - const aura::Window::Windows& windows(viewport_->children()); bool window_overlaps_launcher = false; - bool has_maximized_window = false; - for (aura::Window::Windows::const_iterator i = windows.begin(); - i != windows.end(); ++i) { - wm::WindowState* window_state = wm::GetWindowState(*i); - if (window_state->ignored_by_shelf()) - continue; - ui::Layer* layer = (*i)->layer(); - if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f) - continue; - if (window_state->IsMaximized()) { - // An untracked window may still be fullscreen so we keep iterating when - // we hit a maximized window. - has_maximized_window = true; - } else if (window_state->IsFullscreen()) { - return WORKSPACE_WINDOW_STATE_FULL_SCREEN; + for (size_t idx = 0; idx < arraysize(kWindowContainerIds); idx++) { + const aura::Window* container = Shell::GetContainer( + viewport_->GetRootWindow(), kWindowContainerIds[idx]); + const aura::Window::Windows& windows(container->children()); + for (aura::Window::Windows::const_iterator i = windows.begin(); + i != windows.end(); ++i) { + wm::WindowState* window_state = wm::GetWindowState(*i); + if (window_state->ignored_by_shelf()) + continue; + ui::Layer* layer = (*i)->layer(); + if (!layer->GetTargetVisibility()) + continue; + if (window_state->IsMaximized()) + return WORKSPACE_WINDOW_STATE_MAXIMIZED; + if (!window_overlaps_launcher && + ((*i)->bounds().Intersects(shelf_bounds) || + IsDockedAndVisible(*i))) { + window_overlaps_launcher = true; + } } - if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds)) - window_overlaps_launcher = true; } - if (has_maximized_window) - return WORKSPACE_WINDOW_STATE_MAXIMIZED; return window_overlaps_launcher ? WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF : diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc index 48e08fc415..861578f648 100644 --- a/ash/wm/workspace_controller_unittest.cc +++ b/ash/wm/workspace_controller_unittest.cc @@ -103,7 +103,6 @@ class WorkspaceControllerTest : public test::AshTestBase { aura::Window* window = CreateTestWindow(); window->SetBounds(bounds); wm::WindowState* window_state = wm::GetWindowState(window); - window_state->SetTrackedByWorkspace(true); window_state->set_window_position_managed(true); window->Show(); return window; @@ -688,47 +687,6 @@ TEST_F(WorkspaceControllerTest, TransientParent) { EXPECT_EQ(w2->parent(), w1->parent()); } -// Verifies changing TrackedByWorkspace works. -TEST_F(WorkspaceControllerTest, TrackedByWorkspace) { - // Create a fullscreen window. - scoped_ptr<Window> w1(CreateTestWindow()); - w1->Show(); - wm::ActivateWindow(w1.get()); - w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); - EXPECT_TRUE(wm::IsActiveWindow(w1.get())); - EXPECT_TRUE(w1->IsVisible()); - - // Create a second fullscreen window and mark it not tracked by workspace - // manager. - scoped_ptr<Window> w2(CreateTestWindowUnparented()); - w2->SetBounds(gfx::Rect(1, 6, 25, 30)); - w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); - ParentWindowInPrimaryRootWindow(w2.get()); - w2->Show(); - wm::GetWindowState(w2.get())->SetTrackedByWorkspace(false); - wm::ActivateWindow(w2.get()); - - // Activating |w2| should force it to have the same parent as |w1|. - EXPECT_EQ(w1->parent(), w2->parent()); - EXPECT_TRUE(wm::IsActiveWindow(w2.get())); - EXPECT_TRUE(w1->IsVisible()); - EXPECT_TRUE(w2->IsVisible()); - - // Because |w2| isn't tracked we should be able to set the bounds of it. - gfx::Rect bounds(w2->bounds()); - bounds.Offset(4, 5); - w2->SetBounds(bounds); - EXPECT_EQ(bounds.ToString(), w2->bounds().ToString()); - - // Transition it to tracked by worskpace. It should end up in the desktop - // workspace. - wm::GetWindowState(w2.get())->SetTrackedByWorkspace(true); - EXPECT_TRUE(wm::IsActiveWindow(w2.get())); - EXPECT_TRUE(w1->IsVisible()); - EXPECT_TRUE(w2->IsVisible()); - EXPECT_EQ(w1->parent(), w2->parent()); -} - // Test the placement of newly created windows. TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) { if (!SupportsHostWindowResize()) @@ -1271,123 +1229,83 @@ class DragMaximizedNonTrackedWindowObserver } // namespace -// Verifies setting tracked by workspace to false and then dragging a fullscreen -// window doesn't result in changing the window hierarchy (which typically -// indicates new workspaces have been created). -TEST_F(WorkspaceControllerTest, DragFullscreenNonTrackedWindow) { - aura::test::EventGenerator generator( - Shell::GetPrimaryRootWindow(), gfx::Point()); - generator.MoveMouseTo(5, 5); - - aura::test::TestWindowDelegate delegate; - delegate.set_window_component(HTCAPTION); - scoped_ptr<Window> w1( - aura::test::CreateTestWindowWithDelegate(&delegate, - aura::client::WINDOW_TYPE_NORMAL, - gfx::Rect(5, 6, 7, 8), - NULL)); - ParentWindowInPrimaryRootWindow(w1.get()); - w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); - w1->Show(); - wm::ActivateWindow(w1.get()); - DragMaximizedNonTrackedWindowObserver observer(w1.get()); - w1->parent()->parent()->AddObserver(&observer); - const gfx::Rect max_bounds(w1->bounds()); +// Verifies that a new maximized window becomes visible after its activation +// is requested, even though it does not become activated because a system +// modal window is active. +TEST_F(WorkspaceControllerTest, SwitchFromModal) { + scoped_ptr<Window> modal_window(CreateTestWindowUnparented()); + modal_window->SetBounds(gfx::Rect(10, 11, 21, 22)); + modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM); + ParentWindowInPrimaryRootWindow(modal_window.get()); + modal_window->Show(); + wm::ActivateWindow(modal_window.get()); - generator.PressLeftButton(); - generator.MoveMouseTo(100, 100); - // The bounds shouldn't change (drag should result in nothing happening - // now. - EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString()); + scoped_ptr<Window> maximized_window(CreateTestWindow()); + maximized_window->SetProperty( + aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); + maximized_window->Show(); + wm::ActivateWindow(maximized_window.get()); + EXPECT_TRUE(maximized_window->IsVisible()); +} - generator.ReleaseLeftButton(); - EXPECT_EQ(0, observer.change_count()); +namespace { - // Set tracked to false and repeat, now the window should move. - wm::GetWindowState(w1.get())->SetTrackedByWorkspace(false); - generator.MoveMouseTo(5, 5); - generator.PressLeftButton(); - generator.MoveMouseBy(100, 100); - EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100, - max_bounds.width(), max_bounds.height()).ToString(), - w1->bounds().ToString()); +// Subclass of WorkspaceControllerTest that runs tests with docked windows +// enabled and disabled. +class WorkspaceControllerTestDragging + : public WorkspaceControllerTest, + public testing::WithParamInterface<bool> { + public: + WorkspaceControllerTestDragging() {} + virtual ~WorkspaceControllerTestDragging() {} + + // testing::Test: + virtual void SetUp() OVERRIDE { + WorkspaceControllerTest::SetUp(); + if (!docked_windows_enabled()) { + CommandLine::ForCurrentProcess()->AppendSwitch( + ash::switches::kAshDisableDockedWindows); + } + } - generator.ReleaseLeftButton(); - wm::GetWindowState(w1.get())->SetTrackedByWorkspace(true); - // Marking the window tracked again should snap back to origin. - EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString()); - EXPECT_EQ(0, observer.change_count()); + bool docked_windows_enabled() const { return GetParam(); } - w1->parent()->parent()->RemoveObserver(&observer); -} + private: + DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging); +}; -// Verifies setting tracked by workspace to false and then dragging a maximized -// window can change the bound. -TEST_F(WorkspaceControllerTest, DragMaximizedNonTrackedWindow) { - aura::test::EventGenerator generator( - Shell::GetPrimaryRootWindow(), gfx::Point()); - generator.MoveMouseTo(5, 5); +} // namespace +// Verifies that when dragging a window over the shelf overlap is detected +// during and after the drag. +TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) { aura::test::TestWindowDelegate delegate; delegate.set_window_component(HTCAPTION); scoped_ptr<Window> w1( aura::test::CreateTestWindowWithDelegate(&delegate, aura::client::WINDOW_TYPE_NORMAL, - gfx::Rect(5, 6, 7, 8), + gfx::Rect(5, 5, 100, 50), NULL)); ParentWindowInPrimaryRootWindow(w1.get()); - w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); - w1->Show(); - wm::ActivateWindow(w1.get()); - DragMaximizedNonTrackedWindowObserver observer(w1.get()); - w1->parent()->parent()->AddObserver(&observer); - const gfx::Rect max_bounds(w1->bounds()); - - generator.PressLeftButton(); - generator.MoveMouseTo(100, 100); - // The bounds shouldn't change (drag should result in nothing happening - // now. - EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString()); - generator.ReleaseLeftButton(); - EXPECT_EQ(0, observer.change_count()); + ShelfLayoutManager* shelf = shelf_layout_manager(); + shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); - // Set tracked to false and repeat, now the window should move. - wm::GetWindowState(w1.get())->SetTrackedByWorkspace(false); - generator.MoveMouseTo(5, 5); + // Drag near the shelf + aura::test::EventGenerator generator( + Shell::GetPrimaryRootWindow(), gfx::Point()); + generator.MoveMouseTo(10, 10); generator.PressLeftButton(); - generator.MoveMouseBy(100, 100); - EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100, - max_bounds.width(), max_bounds.height()).ToString(), - w1->bounds().ToString()); + generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20); + // Shelf should detect overlap. Overlap state stays after mouse is released. + EXPECT_TRUE(GetWindowOverlapsShelf()); generator.ReleaseLeftButton(); - wm::GetWindowState(w1.get())->SetTrackedByWorkspace(true); - // Marking the window tracked again should snap back to origin. - EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString()); - EXPECT_EQ(0, observer.change_count()); - - w1->parent()->parent()->RemoveObserver(&observer); + EXPECT_TRUE(GetWindowOverlapsShelf()); } -// Verifies that a new maximized window becomes visible after its activation -// is requested, even though it does not become activated because a system -// modal window is active. -TEST_F(WorkspaceControllerTest, SwitchFromModal) { - scoped_ptr<Window> modal_window(CreateTestWindowUnparented()); - modal_window->SetBounds(gfx::Rect(10, 11, 21, 22)); - modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM); - ParentWindowInPrimaryRootWindow(modal_window.get()); - modal_window->Show(); - wm::ActivateWindow(modal_window.get()); - - scoped_ptr<Window> maximized_window(CreateTestWindow()); - maximized_window->SetProperty( - aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); - maximized_window->Show(); - wm::ActivateWindow(maximized_window.get()); - EXPECT_TRUE(maximized_window->IsVisible()); -} +INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging, + ::testing::Bool()); } // namespace internal } // namespace ash |