Chromium Code Reviews| Index: chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc |
| diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc |
| index d85677d87eb7ee6fa97015f5afbf168b26417faa..0cdadf1796df76e8a593cf061c563715ebbc1d17 100644 |
| --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc |
| +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc |
| @@ -61,6 +61,10 @@ static int kUserFadeTimeMS = 110; |
| // The animation time in ms for a window which get teleported to another screen. |
| static int kTeleportAnimationTimeMS = 300; |
| +// The minimal possible animation time for animations which should happen |
| +// "instantly". |
| +static int kMinimalAnimationTime = 1; |
|
oshima
2014/04/03 19:21:41
kMinimalAnimationTimeMs (assuming it's millisecond
Mr4D (OOO till 08-26)
2014/04/03 22:57:00
Done.
|
| + |
| // Checks if a given event is a user event. |
| bool IsUserEvent(ui::Event* e) { |
|
oshima
2014/04/03 19:21:41
const ui::Event*
Mr4D (OOO till 08-26)
2014/04/03 22:57:00
Done.
|
| if (e) { |
| @@ -236,7 +240,10 @@ MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS( |
| notification_blocker_(new MultiUserNotificationBlockerChromeOS( |
| message_center::MessageCenter::Get(), this, current_user_id)), |
| suppress_visibility_changes_(false), |
| - animations_disabled_(false) { |
| + screen_cover_(NO_USER_COVERS_SCREEN), |
| + animation_step_(ANIMATION_STEP_ENDED), |
| + animations_disabled_(false), |
| + wallpaper_user_id_(current_user_id) { |
| // Add a session state observer to be able to monitor session changes. |
| if (ash::Shell::HasInstance()) |
| ash::Shell::GetInstance()->session_state_delegate()-> |
| @@ -411,28 +418,24 @@ void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) { |
| void MultiUserWindowManagerChromeOS::ActiveUserChanged( |
| const std::string& user_id) { |
| DCHECK(user_id != current_user_id_); |
| - current_user_id_ = user_id; |
| - // If there is an animation in progress finish the pending switch which also |
| - // kills the timer (if there is one). |
| - if (user_changed_animation_timer_.get()) |
| - TransitionUser(SHOW_NEW_USER); |
| - // Start the animation by hiding the old user. |
| - TransitionUser(HIDE_OLD_USER); |
| + // Kick off a new animation (this will first finish outstanding animations). |
| + StartUserTransitionAnimation(user_id); |
| - // If animations are disabled we immediately switch to the new user, otherwise |
| - // we create a timer which will fade in the new user once the other user has |
| + // Immediately switch to new layer if animations are disabled, otherwise |
| + // create a timer which will fade in the new user once the other user has |
| // been faded away. |
| if (animations_disabled_) { |
| - TransitionUser(SHOW_NEW_USER); |
| + while (ANIMATION_STEP_ENDED != animation_step_) |
| + AdvanceUserTransitionAnimation(); |
| } else { |
| user_changed_animation_timer_.reset(new base::Timer( |
| FROM_HERE, |
| base::TimeDelta::FromMilliseconds(kUserFadeTimeMS), |
| - base::Bind(&MultiUserWindowManagerChromeOS::TransitionUser, |
| - base::Unretained(this), |
| - SHOW_NEW_USER), |
| - false)); |
| + base::Bind( |
| + &MultiUserWindowManagerChromeOS::AdvanceUserTransitionAnimation, |
| + base::Unretained(this)), |
|
oshima
2014/04/03 19:21:41
can you change to use weak pointer? 110 ms is a bi
oshima
2014/04/03 20:13:20
never mind. I somehow thought it's passed to messa
|
| + true)); |
| user_changed_animation_timer_->Reset(); |
| } |
| } |
| @@ -580,140 +583,6 @@ bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern( |
| return true; |
| } |
| -void MultiUserWindowManagerChromeOS::TransitionUser( |
| - MultiUserWindowManagerChromeOS::AnimationStep animation_step) { |
| - TransitionWallpaper(animation_step); |
| - TransitionUserShelf(animation_step); |
| - |
| - // Disable the window position manager and the MRU window tracker temporarily. |
| - scoped_ptr<UserChangeActionDisabler> disabler(new UserChangeActionDisabler); |
| - |
| - // We need to show/hide the windows in the same order as they were created in |
| - // their parent window(s) to keep the layer / window hierarchy in sync. To |
| - // achieve that we first collect all parent windows and then enumerate all |
| - // windows in those parent windows and show or hide them accordingly. |
| - |
| - // Create a list of all parent windows we have to check and their parents. |
| - std::set<aura::Window*> parent_list; |
| - for (WindowToEntryMap::iterator it = window_to_entry_.begin(); |
| - it != window_to_entry_.end(); ++it) { |
| - aura::Window* parent = it->first->parent(); |
| - if (parent_list.find(parent) == parent_list.end()) |
| - parent_list.insert(parent); |
| - } |
| - |
| - // Traverse the found parent windows to handle their child windows in order of |
| - // their appearance. |
| - for (std::set<aura::Window*>::iterator it_parents = parent_list.begin(); |
| - it_parents != parent_list.end(); ++it_parents) { |
| - const aura::Window::Windows window_list = (*it_parents)->children(); |
| - for (aura::Window::Windows::const_iterator it_window = window_list.begin(); |
| - it_window != window_list.end(); ++it_window) { |
| - aura::Window* window = *it_window; |
| - WindowToEntryMap::iterator it_map = window_to_entry_.find(window); |
| - if (it_map != window_to_entry_.end()) { |
| - bool should_be_visible = |
| - it_map->second->show_for_user() == current_user_id_ && |
| - it_map->second->show(); |
| - bool is_visible = window->IsVisible(); |
| - ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); |
| - if (animation_step == SHOW_NEW_USER && |
| - it_map->second->owner() == current_user_id_ && |
| - it_map->second->show_for_user() != current_user_id_ && |
| - window_state->IsMinimized()) { |
| - // Pull back minimized visiting windows to the owners desktop. |
| - ShowWindowForUserIntern(window, current_user_id_); |
| - window_state->Unminimize(); |
| - } else if (should_be_visible != is_visible && |
| - should_be_visible == (animation_step == SHOW_NEW_USER)) { |
| - SetWindowVisibility(window, should_be_visible, kUserFadeTimeMS); |
| - } |
| - } |
| - } |
| - } |
| - |
| - // Activation and real switch are happening after the other user gets shown. |
| - if (animation_step == SHOW_NEW_USER) { |
| - // Finally we need to restore the previously active window. |
| - ash::MruWindowTracker::WindowList mru_list = |
| - ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList(); |
| - if (mru_list.size()) { |
| - aura::Window* window = mru_list[0]; |
| - ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); |
| - if (IsWindowOnDesktopOfUser(window, current_user_id_) && |
| - !window_state->IsMinimized()) { |
| - aura::client::ActivationClient* client = |
| - aura::client::GetActivationClient(window->GetRootWindow()); |
| - // Several unit tests come here without an activation client. |
| - if (client) |
| - client->ActivateWindow(window); |
| - } |
| - } |
| - |
| - // This is called directly here to make sure notification_blocker will see |
| - // the new window status. |
| - notification_blocker_->ActiveUserChanged(current_user_id_); |
| - |
| - // We can reset the timer at this point. |
| - // Note: The timer can be destroyed while it is performing its task. |
| - user_changed_animation_timer_.reset(); |
| - } |
| -} |
| - |
| -void MultiUserWindowManagerChromeOS::TransitionWallpaper( |
| - MultiUserWindowManagerChromeOS::AnimationStep animation_step) { |
| - // Handle the wallpaper switch. |
| - ash::UserWallpaperDelegate* wallpaper_delegate = |
| - ash::Shell::GetInstance()->user_wallpaper_delegate(); |
| - if (animation_step == HIDE_OLD_USER) { |
| - // Set the wallpaper cross dissolve animation duration to our complete |
| - // animation cycle for a fade in and fade out. |
| - wallpaper_delegate->SetAnimationDurationOverride(2 * kUserFadeTimeMS); |
| - chromeos::WallpaperManager::Get()->SetUserWallpaperDelayed( |
| - current_user_id_); |
| - } else { |
| - // Revert the wallpaper cross dissolve animation duration back to the |
| - // default. |
| - wallpaper_delegate->SetAnimationDurationOverride(0); |
| - } |
| -} |
| - |
| -void MultiUserWindowManagerChromeOS::TransitionUserShelf( |
| - MultiUserWindowManagerChromeOS::AnimationStep animation_step) { |
| - // The shelf animation duration override. |
| - int duration_override = kUserFadeTimeMS; |
| - // Handle the shelf order of items. This is done once the old user is hidden. |
| - if (animation_step == SHOW_NEW_USER) { |
| - // Some unit tests have no ChromeLauncherController. |
| - if (ChromeLauncherController::instance()) |
| - ChromeLauncherController::instance()->ActiveUserChanged(current_user_id_); |
| - // We kicked off the shelf animation in the command above. As such we can |
| - // disable the override now again. |
| - duration_override = 0; |
| - } |
| - |
| - if (animations_disabled_) |
| - return; |
| - |
| - ash::Shell::RootWindowControllerList controller = |
| - ash::Shell::GetInstance()->GetAllRootWindowControllers(); |
| - for (ash::Shell::RootWindowControllerList::iterator it1 = controller.begin(); |
| - it1 != controller.end(); ++it1) { |
| - (*it1)->GetShelfLayoutManager()->SetAnimationDurationOverride( |
| - duration_override); |
| - } |
| - |
| - // For each root window hide the shelf. |
| - if (animation_step == HIDE_OLD_USER) { |
| - aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows(); |
| - for (aura::Window::Windows::const_iterator iter = root_windows.begin(); |
| - iter != root_windows.end(); ++iter) { |
| - ash::Shell::GetInstance()->SetShelfAutoHideBehavior( |
| - ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, *iter); |
| - } |
| - } |
| -} |
| - |
| void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) { |
| // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can |
| // come here with no valid window. |
| @@ -869,4 +738,241 @@ void MultiUserWindowManagerChromeOS::SetWindowVisible( |
| DCHECK_EQ(visible, window->IsVisible()); |
| } |
| +void MultiUserWindowManagerChromeOS::StartUserTransitionAnimation( |
| + const std::string& user_id) { |
| + // If there is an animation in progress, finish it before the new one starts. |
| + while (ANIMATION_STEP_ENDED != animation_step_) |
| + AdvanceUserTransitionAnimation(); |
| + |
| + // Set up the transition parameters before the animation gets called the first |
| + // time. Note that the screen cover state needs to be analyzed and remembered |
| + // before the animation starts and after the user got set. |
| + current_user_id_ = user_id; |
| + screen_cover_ = GetScreenCover(); |
| + animation_step_ = ANIMATION_STEP_HIDE_OLD_USER; |
| + |
| + AdvanceUserTransitionAnimation(); |
| +} |
| + |
| +void MultiUserWindowManagerChromeOS::AdvanceUserTransitionAnimation() { |
| + DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED); |
| + |
| + TransitionWallpaper(animation_step_); |
| + TransitionUserShelf(animation_step_); |
| + TransitionWindows(animation_step_); |
| + |
| + // Advance to the next step. |
| + if (animation_step_ == ANIMATION_STEP_FINALIZE) { |
|
oshima
2014/04/03 19:21:41
optional: using switch would make it easy to under
Mr4D (OOO till 08-26)
2014/04/03 22:57:00
Done.
|
| + user_changed_animation_timer_.reset(); |
| + animation_step_ = ANIMATION_STEP_ENDED; |
| + } else { |
| + animation_step_ = animation_step_ == ANIMATION_STEP_HIDE_OLD_USER ? |
| + ANIMATION_STEP_SHOW_NEW_USER : ANIMATION_STEP_FINALIZE; |
| + } |
| +} |
| + |
| +void MultiUserWindowManagerChromeOS::TransitionWallpaper( |
| + MultiUserWindowManagerChromeOS::AnimationStep animation_step) { |
| + // Handle the wallpaper switch. |
| + ash::UserWallpaperDelegate* wallpaper_delegate = |
| + ash::Shell::GetInstance()->user_wallpaper_delegate(); |
| + if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) { |
| + // Set the wallpaper cross dissolve animation duration to our complete |
| + // animation cycle for a fade in and fade out. |
| + wallpaper_delegate->SetAnimationDurationOverride( |
| + NO_USER_COVERS_SCREEN == screen_cover_ ? (2 * kUserFadeTimeMS) : |
| + kMinimalAnimationTime); |
| + if (screen_cover_ != NEW_USER_COVERS_SCREEN) { |
| + chromeos::WallpaperManager::Get()->SetUserWallpaperNow(current_user_id_); |
| + wallpaper_user_id_ = NO_USER_COVERS_SCREEN == screen_cover_ ? |
| + (wallpaper_user_id_ + "->" + current_user_id_) : current_user_id_; |
| + } |
| + } else if (animation_step == ANIMATION_STEP_FINALIZE) { |
| + // Revert the wallpaper cross dissolve animation duration back to the |
| + // default. |
| + if (screen_cover_ == NEW_USER_COVERS_SCREEN) |
| + chromeos::WallpaperManager::Get()->SetUserWallpaperNow(current_user_id_); |
| + |
| + // Coming here the wallpaper user id is the final result. No matter how we |
| + // got here. |
| + wallpaper_user_id_ = current_user_id_; |
| + wallpaper_delegate->SetAnimationDurationOverride(0); |
| + } |
| +} |
| + |
| +void MultiUserWindowManagerChromeOS::TransitionUserShelf( |
| + MultiUserWindowManagerChromeOS::AnimationStep animation_step) { |
| + // The shelf animation duration override. |
| + int duration_override = kUserFadeTimeMS; |
| + // Handle the shelf order of items. This is done once the old user is hidden. |
| + if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) { |
| + // Some unit tests have no ChromeLauncherController. |
| + if (ChromeLauncherController::instance()) |
| + ChromeLauncherController::instance()->ActiveUserChanged(current_user_id_); |
| + // We kicked off the shelf animation in the command above. As such we can |
| + // disable the override now again. |
| + duration_override = 0; |
| + } |
| + |
| + if (animations_disabled_ || animation_step == ANIMATION_STEP_FINALIZE) |
| + return; |
| + |
| + ash::Shell::RootWindowControllerList controller = |
| + ash::Shell::GetInstance()->GetAllRootWindowControllers(); |
| + for (ash::Shell::RootWindowControllerList::iterator it1 = controller.begin(); |
| + it1 != controller.end(); ++it1) { |
| + (*it1)->GetShelfLayoutManager()->SetAnimationDurationOverride( |
| + duration_override); |
| + } |
| + |
| + // For each root window hide the shelf. |
| + if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) { |
| + aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows(); |
| + for (aura::Window::Windows::const_iterator iter = root_windows.begin(); |
| + iter != root_windows.end(); ++iter) { |
| + ash::Shell::GetInstance()->SetShelfAutoHideBehavior( |
| + ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, *iter); |
|
oshima
2014/04/03 19:21:41
q: how/where the user's shelf setting is restored?
Mr4D (OOO till 08-26)
2014/04/03 22:57:00
Added comment to explain.
|
| + } |
| + } |
| +} |
| + |
| +void MultiUserWindowManagerChromeOS::TransitionWindows( |
| + AnimationStep animation_step) { |
| + // Disable the window position manager and the MRU window tracker temporarily. |
| + scoped_ptr<UserChangeActionDisabler> disabler(new UserChangeActionDisabler); |
|
oshima
2014/04/03 19:21:41
This can be just created on stack ?
Mr4D (OOO till 08-26)
2014/04/03 22:57:00
Done.
|
| + |
| + if (animation_step == ANIMATION_STEP_HIDE_OLD_USER || |
| + (animation_step == ANIMATION_STEP_FINALIZE && |
| + screen_cover_ == BOTH_USERS_COVER_SCREEN)) { |
| + // We need to show/hide the windows in the same order as they were created |
| + // in their parent window(s) to keep the layer / window hierarchy in sync. |
| + // To achieve that we first collect all parent windows and then enumerate |
| + // all windows in those parent windows and show or hide them accordingly. |
| + |
| + // Create a list of all parent windows we have to check. |
| + std::set<aura::Window*> parent_list; |
| + for (WindowToEntryMap::iterator it = window_to_entry_.begin(); |
| + it != window_to_entry_.end(); ++it) { |
| + aura::Window* parent = it->first->parent(); |
| + if (parent_list.find(parent) == parent_list.end()) |
| + parent_list.insert(parent); |
| + } |
| + |
| + for (std::set<aura::Window*>::iterator it_parents = parent_list.begin(); |
| + it_parents != parent_list.end(); ++it_parents) { |
| + const aura::Window::Windows window_list = (*it_parents)->children(); |
| + // In case of |BOTH_USERS_COVER_SCREEN| the desktop might shine through |
| + // if all windows fade (in or out). To avoid this we only fade the topmost |
| + // covering window (in / out) and make / keep all other covering windows |
| + // visible while animating. |foreground_window_found| will get set when |
| + // the top fading window was found. |
| + bool foreground_window_found = false; |
| + // Covering windows which follow the fade direction will also fade - all |
| + // others will get immediately shown / kept shown until the animation is |
| + // finished. |
| + bool foreground_becomes_visible = false; |
| + for (aura::Window::Windows::const_reverse_iterator it_window = |
| + window_list.rbegin(); |
| + it_window != window_list.rend(); ++it_window) { |
| + aura::Window* window = *it_window; |
| + WindowToEntryMap::iterator it_map = window_to_entry_.find(window); |
| + if (it_map != window_to_entry_.end()) { |
| + bool should_be_visible = |
| + it_map->second->show_for_user() == current_user_id_ && |
| + it_map->second->show(); |
| + bool is_visible = window->IsVisible(); |
| + ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); |
| + if (it_map->second->owner() == current_user_id_ && |
| + it_map->second->show_for_user() != current_user_id_ && |
| + window_state->IsMinimized()) { |
| + // Pull back minimized visiting windows to the owners desktop. |
| + ShowWindowForUserIntern(window, current_user_id_); |
| + window_state->Unminimize(); |
| + } else if (should_be_visible != is_visible) { |
| + bool animate = true; |
| + int duration = animation_step == ANIMATION_STEP_FINALIZE ? |
| + kMinimalAnimationTime : (2 * kUserFadeTimeMS); |
| + if (animation_step != ANIMATION_STEP_FINALIZE && |
| + screen_cover_ == BOTH_USERS_COVER_SCREEN && |
| + CoversScreen(window)) { |
| + if (!foreground_window_found) { |
| + foreground_window_found = true; |
| + foreground_becomes_visible = should_be_visible; |
| + } else if (should_be_visible != foreground_becomes_visible) { |
| + // Covering windows behind the foreground window which are |
| + // inverting their visibility should immediately become visible |
| + // or stay visible until the animation is finished. |
| + duration = kMinimalAnimationTime; |
| + if (!should_be_visible) |
| + animate = false; |
| + } |
| + } |
| + if (animate) |
| + SetWindowVisibility(window, should_be_visible, duration); |
| + } |
| + } |
| + } |
| + } |
| + } |
| + |
| + // Activation and real switch are happening after the other user gets shown. |
| + if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) { |
| + // Finally we need to restore the previously active window. |
| + ash::MruWindowTracker::WindowList mru_list = |
| + ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList(); |
| + if (mru_list.size()) { |
|
oshima
2014/04/03 19:21:41
!empty()
Mr4D (OOO till 08-26)
2014/04/03 22:57:00
Done.
|
| + aura::Window* window = mru_list[0]; |
| + ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); |
| + if (IsWindowOnDesktopOfUser(window, current_user_id_) && |
| + !window_state->IsMinimized()) { |
| + aura::client::ActivationClient* client = |
| + aura::client::GetActivationClient(window->GetRootWindow()); |
| + // Several unit tests come here without an activation client. |
| + if (client) |
| + client->ActivateWindow(window); |
| + } |
| + } |
| + |
| + // This is called directly here to make sure notification_blocker will see |
| + // the new window status. |
| + notification_blocker_->ActiveUserChanged(current_user_id_); |
| + } |
| +} |
| + |
| +MultiUserWindowManagerChromeOS::TransitioningScreenCover |
| +MultiUserWindowManagerChromeOS::GetScreenCover() { |
| + TransitioningScreenCover cover = NO_USER_COVERS_SCREEN; |
| + for (WindowToEntryMap::iterator it_map = window_to_entry_.begin(); |
| + it_map != window_to_entry_.end(); |
| + ++it_map) { |
| + aura::Window* window = it_map->first; |
| + if (window->IsVisible() && CoversScreen(window)) { |
| + if (cover == NEW_USER_COVERS_SCREEN) |
| + return BOTH_USERS_COVER_SCREEN; |
| + else |
| + cover = OLD_USER_COVERS_SCREEN; |
| + } else if (IsWindowOnDesktopOfUser(window, current_user_id_) && |
| + CoversScreen(window)) { |
| + if (cover == OLD_USER_COVERS_SCREEN) |
| + return BOTH_USERS_COVER_SCREEN; |
| + else |
| + cover = NEW_USER_COVERS_SCREEN; |
| + } |
| + } |
| + return cover; |
| +} |
| + |
| +bool MultiUserWindowManagerChromeOS::CoversScreen(aura::Window* window) { |
| + // Full screen covers the screen naturally. Maximized however can be smaller |
| + // then the work area - so we do not check for that mode, but instead check if |
| + // the window is covering everything. |
| + if (ash::wm::GetWindowState(window)->IsFullscreen()) |
| + return true; |
| + gfx::Rect bounds = window->GetBoundsInRootWindow(); |
| + gfx::Rect work_area = gfx::Screen::GetScreenFor(window)-> |
| + GetDisplayNearestWindow(window).work_area(); |
| + bounds.Intersect(work_area); |
| + return work_area == bounds; |
| +} |
| + |
| } // namespace chrome |