// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONTEXT_H_
#define ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONTEXT_H_
#include <memory>
#include <string>
#include "ash/ash_export.h"
#include "ash/game_dashboard/game_dashboard_metrics.h"
#include "ash/wm/window_state_observer.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "ui/events/event_handler.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/unique_widget_ptr.h"
namespace aura {
class Window;
} // namespace aura
namespace views {
class Widget;
} // namespace views
namespace ash {
class GameDashboardButton;
class GameDashboardButtonRevealController;
class GameDashboardMainMenuCursorHandler;
class GameDashboardMainMenuView;
class GameDashboardToolbarView;
// This class manages Game Dashboard related UI for a given `aura::Window`, and
// its instance is managed by the `GameDashboardController`.
class ASH_EXPORT GameDashboardContext : public ui::EventHandler,
public views::ViewObserver,
public views::WidgetObserver,
public WindowStateObserver {
public:
explicit GameDashboardContext(aura::Window* game_window);
GameDashboardContext(const GameDashboardContext&) = delete;
GameDashboardContext& operator=(const GameDashboardContext&) = delete;
~GameDashboardContext() override;
aura::Window* game_window() { return game_window_.get(); }
const std::string& app_id() const { return app_id_; }
GameDashboardMainMenuView* main_menu_view() { return main_menu_view_; }
base::WeakPtr<GameDashboardContext> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
views::Widget* game_dashboard_button_widget() {
return game_dashboard_button_widget_.get();
}
GameDashboardToolbarSnapLocation toolbar_snap_location() const {
return toolbar_snap_location_;
}
void set_recording_from_main_menu(bool from_main_menu) {
recording_from_main_menu_ = from_main_menu;
}
// Returns true if the main menu is opened. `main_menu_widget_` is created
// only when the main menu is opened, otherwise it's null.
bool IsMainMenuOpen() const { return main_menu_widget_.get(); }
const std::u16string& GetRecordingDuration() const;
// Initializes the context. Creates and starts showing the Game Dashboard
// button. Also shows the welcome dialog, if
// `prefs::kGameDashboardShowWelcomeDialog` is true. Separating this logic
// ensures the constructor never references the context created before the
// instance is assigned. Note that this logic should be called once the
// context is created.
void Initialize();
// Stacks Game Dashboard UI widgets above `widget` if it is needed.
void MaybeStackAboveWidget(views::Widget* widget);
// Enables the Game Dashboard widgets' UI visibility if `enable` is true.
// Otherwise, hides the widgets, and uses `main_menu_toggle_method` to
// conditionally hide the toolbar.
void EnableFeatures(
bool enable,
GameDashboardMainMenuToggleMethod main_menu_toggle_method);
// Reassigns the new `toolbar_snap_location_` and performs an animation as the
// toolbar moves to its new location.
void SetGameDashboardToolbarSnapLocation(
GameDashboardToolbarSnapLocation new_location);
// Called by `GameDashboardController` when the game window bounds change.
// `from_animation` is true when window bounds are changing due to an
// animation.
void OnWindowBoundsChanged(bool from_animation);
// Updates for Game Controls flags.
void UpdateForGameControlsFlags();
// Toggles the creation/deletion of the main menu within the game window.
void ToggleMainMenuByAccelerator();
void ToggleMainMenu(GameDashboardMainMenuToggleMethod toggle_method);
// Closes the main menu. Clears `main_menu_widget_` and `main_menu_view_`.
void CloseMainMenu(GameDashboardMainMenuToggleMethod toggle_method);
// Toggles the creation/deletion of the toolbar within the game window.
// Returns the toolbar visibility state.
bool ToggleToolbar();
// Closes the toolbar. Clears `toolbar_widget_` and `toolbar_view_`.
void CloseToolbar();
// Conditionally, updates the toolbar widget's bounds and location, relative
// to the `game_window_`.
void MaybeUpdateToolbarWidgetBounds();
bool IsToolbarVisible() const;
// Returns the toolbar bounds if `IsToolbarVisible`, otherwise an empty rect.
gfx::Rect GetToolbarBoundsInScreen() const;
// Called only when `CaptureModeController` has started a recording session.
// If `is_recording_game_window` is true, then the recording session was
// initiated by the Game Dashboard and the `game_window_` is being recorded.
void OnRecordingStarted(bool is_recording_game_window);
// Called only when `CaptureModeController` has ended a recording session or
// if the recording session was aborted.
void OnRecordingEnded();
// Called when a recorded file has been finalized and fully saved, at which
// point a new recording is allowed to be started.
void OnVideoFileFinalized();
// Controls the Game Dashboard Button visibility.
void SetGameDashboardButtonVisibility(bool visible);
// Controls the Toolbar widget visibility.
void SetToolbarVisibility(bool visible);
// Conditionally, adds this context to the pre-target handler if it hasn't
// already been added.
void MaybeAddPreTargetHandler();
// Conditionally, removes this context from the pre-target handler if it
// hasn't already been removed.
void MaybeRemovePreTargetHandler();
// ui::EventHandler:
void OnEvent(ui::Event* event) override;
// views::ViewObserver:
void OnViewPreferredSizeChanged(views::View* observed_view) override;
// views::WidgetObserver:
void OnWidgetDestroyed(views::Widget* widget) override;
// WindowStateObserver:
void OnPreWindowStateTypeChange(WindowState* window_state,
chromeos::WindowStateType old_type) override;
void OnPostWindowStateTypeChange(WindowState* window_state,
chromeos::WindowStateType old_type) override;
private:
friend class GameDashboardContextTestApi;
// Registers a pretarget handler to always show the mouse cursor. Called when
// the user opens the main menu.
void AddCursorHandler();
// Unregisters the pretarget handler that always shows the mouse cursor.
// Called when the user closes the main menu.
void RemoveCursorHandler();
// Creates a Game Dashboard button widget and adds it as a sibling of the game
// window.
void CreateAndAddGameDashboardButtonWidget();
// Updates the Game Dashboard button widget's bounds and location, relative to
// the `game_window_`.
void UpdateGameDashboardButtonWidgetBounds();
// Called when `GameDashboardButton` is pressed, and toggles the main menu.
void OnGameDashboardButtonPressed();
// Shows the Game Dashboard welcome dialog, if it's enabled in the Game
// Dashboard settings.
void MaybeShowWelcomeDialog();
// Updates the Game Dashboard welcome dialog's bounds and location, relative
// to the `game_window_`.
void MaybeUpdateWelcomeDialogBounds();
// Determines the toolbar's physical location on screen based on the
// `toolbar_snap_location_` value.
const gfx::Rect CalculateToolbarWidgetBounds();
// Updates the toolbar widget's bounds and location utilizing an animation as
// it transfers from the previous location.
void AnimateToolbarWidgetBoundsChange(const gfx::Rect& target_screen_bounds);
// Shows the Game Dashboard toolbar if `prefs::kGameDashboardShowToolbar` is
// true.
void MaybeShowToolbar();
// Repeating timer callback that notifies `main_menu_view_` of the video
// recording session duration.
void OnUpdateRecordingTimer();
// Closes and deletes the Game Dashboard welcome dialog. After closing the
// dialog, if `show_toolbar` is true, call `MaybeShowToolbar`.
void CloseWelcomeDialogIfAny(bool show_toolbar = true);
// Callback when the `GameDashboardWelcomeDialog`'s timer has completed.
void OnWelcomeDialogTimerCompleted();
// Resets the `main_menu_view_`, removes the cursor handler, and updates the
// `game_dashboard_button_` UI.
void UpdateOnMainMenuClosed();
// Ensures that the main menu stacks above the toolbar.
void EnsureMainMenuAboveToolbar();
// Determines whether it's required to tab navigate from one Game Dashboard
// widget to another widget. Returns false if tab-navigating within the same
// widget.
bool ShouldNavigateToNewWidget(const ui::KeyEvent* event) const;
// Returns a list of visible Game Dashboard widgets that are available to be
// traversed.
std::vector<views::Widget*> GetTraversableWidgets() const;
// Manually moves focus to the `new_widget`. If `reverse` is true, focus will
// move backwards.
void MoveFocus(views::Widget* new_widget, ui::Event* event, bool reverse);
const raw_ptr<aura::Window> game_window_;
const std::string app_id_;
// Game Dashboard button widget for the Game Dashboard.
std::unique_ptr<views::Widget> game_dashboard_button_widget_;
// Delegate responsible for determining when to show/hide the Game Dashboard
// button when `game_window_` is in fullscreen. This a temporary scoped
// object that is around while `game_window_` is fullscreen.
std::unique_ptr<GameDashboardButtonRevealController>
game_dashboard_button_reveal_controller_;
// Expanded main menu for the Game Dashboard, which displays the main menu and
// the settings view.
views::UniqueWidgetPtr main_menu_widget_;
// The toolbar for the Game Dashboard.
std::unique_ptr<views::Widget> toolbar_widget_;
// The dialog displayed when the game window first opens.
std::unique_ptr<views::Widget> welcome_dialog_widget_;
// The indicator of the current corner that the toolbar is placed.
GameDashboardToolbarSnapLocation toolbar_snap_location_;
// The `GameDashboardButton` view in the `game_dashboard_button_widget_`.
// Owned by the views hierarchy.
raw_ptr<GameDashboardButton> game_dashboard_button_ = nullptr;
// The `GameDashboardMainMenuView` when the user presses the Game Dashboard
// button to display all Game Dashboard views. This displays the main menu and
// settings views. Owned by the views hierarchy.
raw_ptr<GameDashboardMainMenuView, DanglingUntriaged> main_menu_view_ =
nullptr;
// The `GameDashboardToolbarView` when the user makes the toolbar visible.
// Owned by the views hierarchy.
raw_ptr<GameDashboardToolbarView> toolbar_view_ = nullptr;
// Handles cursor management when the main menu is open.
std::unique_ptr<GameDashboardMainMenuCursorHandler> main_menu_cursor_handler_;
// A repeating timer to keep track of the recording session duration.
base::RepeatingTimer recording_timer_;
// Start time of when `recording_timer_` started.
base::Time recording_start_time_;
// Duration since `recording_timer_` started.
std::u16string recording_duration_;
// Indicates whether the Game Dashboard welcome dialog should be shown. This
// param ensures the welcome dialog is only shown once per game window
// startup.
bool show_welcome_dialog_ = false;
// Indicates where the recording feature starts from the main menu. It is
// false if the recording starts from the toolbar. It is null if the recording
// is started from somewhere else.
std::optional<bool> recording_from_main_menu_;
// Indicates whether this context has been added as a Shell's pre-target
// handler. This param ensures this context isn't added as a pre-target
// handler multiple times.
bool added_to_pre_target_handler_ = false;
base::ScopedObservation<WindowState, WindowStateObserver>
window_state_observation_{this};
base::WeakPtrFactory<GameDashboardContext> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONTEXT_H_