diff --git a/ash/game_dashboard/game_dashboard_button_reveal_controller.cc b/ash/game_dashboard/game_dashboard_button_reveal_controller.cc index a6ab639cf0bb00..1eaa3e0572e427 100644 --- a/ash/game_dashboard/game_dashboard_button_reveal_controller.cc +++ b/ash/game_dashboard/game_dashboard_button_reveal_controller.cc @@ -7,10 +7,13 @@ #include "ash/game_dashboard/game_dashboard_context.h" #include "ash/game_dashboard/game_dashboard_utils.h" #include "ash/wm/window_state.h" +#include "base/functional/bind.h" +#include "base/functional/callback_forward.h" #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h" #include "ui/aura/window.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/point.h" +#include "ui/views/animation/animation_builder.h" #include "ui/views/widget/widget.h" namespace ash { @@ -21,6 +24,9 @@ namespace { // game dashboard button revealing. constexpr base::TimeDelta kMouseRevealDelay = base::Milliseconds(200); +constexpr base::TimeDelta kSlideAnimationDuration = base::Milliseconds(200); +constexpr base::TimeDelta kNoSlideAnimationDuration = base::Milliseconds(0); + } // namespace GameDashboardButtonRevealController::GameDashboardButtonRevealController( @@ -29,12 +35,38 @@ GameDashboardButtonRevealController::GameDashboardButtonRevealController( DCHECK(context_); context_->game_window()->AddPreTargetHandler( this, ui::EventTarget::Priority::kSystem); + UpdateVisibility(/*target_visibility=*/false, /*animate=*/false); } GameDashboardButtonRevealController::~GameDashboardButtonRevealController() { + UpdateVisibility(/*target_visibility=*/true, /*animate=*/false); context_->game_window()->RemovePreTargetHandler(this); } +void GameDashboardButtonRevealController::UpdateVisibility( + bool target_visibility, + bool animate) { + context_->SetGameDashboardButtonVisibility(/*visible=*/true); + views::AnimationBuilder() + .SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy:: + IMMEDIATELY_ANIMATE_TO_NEW_TARGET) + .OnEnded( + base::BindOnce(&GameDashboardButtonRevealController::OnAnimationEnd, + weak_ptr_factory_.GetWeakPtr(), target_visibility)) + .Once() + .SetDuration(animate ? kSlideAnimationDuration + : kNoSlideAnimationDuration) + .SetTransform( + context_->game_dashboard_button_widget()->GetLayer(), + target_visibility + ? gfx::Transform() + : gfx::Transform::MakeTranslation( + /*tx=*/0, + /*ty=*/-game_dashboard_utils::GetFrameHeaderHeight( + context_->game_window())), + gfx::Tween::EASE_OUT); +} + void GameDashboardButtonRevealController::OnMouseEvent(ui::MouseEvent* event) { const auto event_type = event->type(); if (event_type != ui::ET_MOUSE_MOVED && event_type != ui::ET_MOUSE_RELEASED && @@ -59,7 +91,7 @@ void GameDashboardButtonRevealController::OnMouseEvent(ui::MouseEvent* event) { top_edge_hover_timer_.Stop(); // If the main menu is closed, try to hide the game dashboard button. if (CanHideGameDashboardButton(mouse_screen_location)) { - context_->SetGameDashboardButtonVisibility(/*visible=*/false); + UpdateVisibility(/*target_visibility=*/false, /*animate=*/true); } } @@ -103,7 +135,16 @@ bool GameDashboardButtonRevealController::IsMouseOutsideHeaderBounds( void GameDashboardButtonRevealController::OnTopEdgeHoverTimeout() { if (CanShowGameDashboardButton( display::Screen::GetScreen()->GetCursorScreenPoint())) { - context_->SetGameDashboardButtonVisibility(/*visible=*/true); + UpdateVisibility(/*target_visibility=*/true, /*animate=*/true); + } +} + +void GameDashboardButtonRevealController::OnAnimationEnd( + bool target_visibility) { + if (!target_visibility) { + // The slide up animation has ended. Make the Game Dashboard button + // widget not visible. + context_->SetGameDashboardButtonVisibility(/*visible=*/false); } } diff --git a/ash/game_dashboard/game_dashboard_button_reveal_controller.h b/ash/game_dashboard/game_dashboard_button_reveal_controller.h index 4e7f8a44d37fc3..6721eb71914a24 100644 --- a/ash/game_dashboard/game_dashboard_button_reveal_controller.h +++ b/ash/game_dashboard/game_dashboard_button_reveal_controller.h @@ -30,6 +30,13 @@ class GameDashboardButtonRevealController : public ui::EventHandler { void StopTopEdgeTimer() { top_edge_hover_timer_.Stop(); } + // Updates the visibility of the Game Dashboard button widget. If + // `target_visibility` is true, then the widget will be visible, otherwise it + // will not be visible. If `animate` is true, the widget will slide up/down + // with a short animation to `target_visibility`, otherwise, the widget will + // move without any animation. + void UpdateVisibility(bool target_visibility, bool animate); + // ui::EventHandler: void OnMouseEvent(ui::MouseEvent* event) override; @@ -59,10 +66,16 @@ class GameDashboardButtonRevealController : public ui::EventHandler { // Called when the `top_edge_hover_timer_` is fired. void OnTopEdgeHoverTimeout(); + // Callbacks when the animation has ended in `UpdateVisibility()`. + void OnAnimationEnd(bool target_visibility); + const raw_ptr context_; // Timer to track cursor being held at the top edge of the screen. base::OneShotTimer top_edge_hover_timer_; + + base::WeakPtrFactory weak_ptr_factory_{ + this}; }; } // namespace ash diff --git a/ash/game_dashboard/game_dashboard_context.cc b/ash/game_dashboard/game_dashboard_context.cc index ed198ac4be989b..c3f6ed4e61aced 100644 --- a/ash/game_dashboard/game_dashboard_context.cc +++ b/ash/game_dashboard/game_dashboard_context.cc @@ -205,7 +205,14 @@ void GameDashboardContext::UpdateForGameControlsFlags() { } void GameDashboardContext::ToggleMainMenuByAccelerator() { - SetGameDashboardButtonVisibility(/*visible=*/true); + if (game_dashboard_button_reveal_controller_) { + // Window is in fullscreen, and `game_dashboard_button_widget_` may not be + // visible. Reset its position and make it visible. Don't animate the button + // so it and the main menu show up at the same time. + game_dashboard_button_reveal_controller_->UpdateVisibility( + /*target_visibility=*/true, /*animate=*/false); + } + ToggleMainMenu(GameDashboardMainMenuToggleMethod::kSearchPlusG); } @@ -411,7 +418,9 @@ void GameDashboardContext::OnPreWindowStateTypeChange( chromeos::WindowStateType old_type) { // Hide the Game Dashboard button before the window switches to fullscreen. if (window_state->IsFullscreen()) { - SetGameDashboardButtonVisibility(/*visible=*/false); + DCHECK(!game_dashboard_button_reveal_controller_); + // The `GameDashboardButtonRevealController`'s ctor will hide + // `game_dashboard_button_widget_`. game_dashboard_button_reveal_controller_ = std::make_unique(this); } @@ -420,9 +429,11 @@ void GameDashboardContext::OnPreWindowStateTypeChange( void GameDashboardContext::OnPostWindowStateTypeChange( WindowState* window_state, chromeos::WindowStateType old_type) { - if (!window_state->IsFullscreen()) { + if (!window_state->IsFullscreen() && + game_dashboard_button_reveal_controller_) { + // When exiting fullscreen, GameDashboardButtonRevealController dtor will + // make `game_dashboard_button_widget_` visible and reset its position. game_dashboard_button_reveal_controller_.reset(); - SetGameDashboardButtonVisibility(/*visible=*/true); } } diff --git a/ash/game_dashboard/game_dashboard_context_unittest.cc b/ash/game_dashboard/game_dashboard_context_unittest.cc index c571b2a4831657..f1d42b73ec72d5 100644 --- a/ash/game_dashboard/game_dashboard_context_unittest.cc +++ b/ash/game_dashboard/game_dashboard_context_unittest.cc @@ -1306,6 +1306,7 @@ TEST_F(GameDashboardContextTest, GameDashboardButtonFullscreen_MouseOver) { auto* event_generator = GetEventGenerator(); auto app_bounds = game_window_->GetBoundsInScreen(); auto* window_state = WindowState::Get(game_window_.get()); + ASSERT_TRUE(window_state->IsNormalStateType()); views::Widget* button_widget = test_api_->GetGameDashboardButtonWidget(); CHECK(button_widget); @@ -1316,6 +1317,7 @@ TEST_F(GameDashboardContextTest, GameDashboardButtonFullscreen_MouseOver) { ASSERT_TRUE(window_state->IsFullscreen()); ASSERT_FALSE(button_widget->IsVisible()); ASSERT_TRUE(test_api_->GetGameDashboardButtonRevealController()); + ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible()); // Move mouse to top edge of window. event_generator->MoveMouseTo(app_bounds.top_center()); @@ -1324,10 +1326,12 @@ TEST_F(GameDashboardContextTest, GameDashboardButtonFullscreen_MouseOver) { ASSERT_TRUE(top_edge_hover_timer.IsRunning()); top_edge_hover_timer.FireNow(); ASSERT_TRUE(button_widget->IsVisible()); + ASSERT_TRUE(test_api_->GetGameDashboardButtonWidget()->IsVisible()); // Move mouse to the center of the app, and verify Game Dashboard button // widget is not visible. event_generator->MoveMouseTo(app_bounds.CenterPoint()); + ASSERT_FALSE(test_api_->GetGameDashboardButtonWidget()->IsVisible()); ASSERT_FALSE(button_widget->IsVisible()); }