gd: Add Welcome Dialog
Adds support to display a welcome dialog for 4 seconds on every game window opened. Bug: b:304817405 Test: Added unit tests Demo: b/304817405#comment9 Change-Id: I1b9dfb3278f9f5dad1a4e7af0423edbc09b73523 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5106463 Commit-Queue: Gina Domergue <gdomergue@google.com> Reviewed-by: Prameet Shah <phshah@chromium.org> Reviewed-by: Ahmed Fakhry <afakhry@chromium.org> Cr-Commit-Position: refs/heads/main@{#1250993}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
16673b7e34
commit
51e76e01a2
ash
BUILD.gnash_strings.grd
ash_strings_grd
IDS_ASH_GAME_DASHBOARD_WELCOME_DIALOG_SHORTCUT.png.sha1IDS_ASH_GAME_DASHBOARD_WELCOME_DIALOG_SUB_LABEL.png.sha1
capture_mode
game_dashboard
@ -659,6 +659,7 @@ component("ash") {
|
||||
"frame_throttler/frame_throttling_observer.h",
|
||||
"game_dashboard/game_dashboard_button.cc",
|
||||
"game_dashboard/game_dashboard_button.h",
|
||||
"game_dashboard/game_dashboard_constants.h",
|
||||
"game_dashboard/game_dashboard_context.cc",
|
||||
"game_dashboard/game_dashboard_context.h",
|
||||
"game_dashboard/game_dashboard_controller.cc",
|
||||
@ -670,6 +671,8 @@ component("ash") {
|
||||
"game_dashboard/game_dashboard_toolbar_view.h",
|
||||
"game_dashboard/game_dashboard_utils.cc",
|
||||
"game_dashboard/game_dashboard_utils.h",
|
||||
"game_dashboard/game_dashboard_welcome_dialog.cc",
|
||||
"game_dashboard/game_dashboard_welcome_dialog.h",
|
||||
"game_dashboard/game_dashboard_widget.cc",
|
||||
"game_dashboard/game_dashboard_widget.h",
|
||||
"glanceables/classroom/glanceables_classroom_client.h",
|
||||
|
@ -7315,6 +7315,12 @@ To shut down the device, press and hold the power button on the device again.
|
||||
<message name="IDS_ASH_GAME_DASHBOARD_VISIBLE_STATUS" translateable="false" desc="The visible state for compact Game Dashboard tile sub-labels.">
|
||||
Visible
|
||||
</message>
|
||||
<message name="IDS_ASH_GAME_DASHBOARD_WELCOME_DIALOG_SHORTCUT" translateable="false" desc="The text displayed in the welcome dialog to show how to toggle the Game Dashboard.">
|
||||
Press <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> + g at anytime
|
||||
</message>
|
||||
<message name="IDS_ASH_GAME_DASHBOARD_WELCOME_DIALOG_SUB_LABEL" translateable="false" desc="The additional context added to the welcome dialog about the Game Dashboard.">
|
||||
Customize your gaming experience
|
||||
</message>
|
||||
|
||||
<!-- Game Dashboard / Game Controls strings -->
|
||||
<message name="IDS_ASH_GAME_DASHBOARD_GC_TILE_VISIBLE" desc="The sub-label for Game Controls tile when input mapping hint is visible.">
|
||||
|
@ -0,0 +1 @@
|
||||
35df49495339e436b0a61bcf783e089300ac7ddf
|
@ -0,0 +1 @@
|
||||
8801a2612e169a159f77f7aee64e0b1f8218d08c
|
@ -757,6 +757,9 @@ TEST_F(GameDashboardCaptureModeTest, CursorAndClickBehaviorWhenAnchored) {
|
||||
|
||||
// The game window should be the top most active window.
|
||||
wm::ActivateWindow(game_window());
|
||||
// TODO(b/316141148): Remove this call once the welcome dialog is disabled by
|
||||
// default for tests.
|
||||
WaitForSeconds(/*seconds=*/4);
|
||||
auto* controller = StartGameCaptureModeSession();
|
||||
|
||||
// Hover over empty space where there is no window.
|
||||
|
22
ash/game_dashboard/game_dashboard_constants.h
Normal file
22
ash/game_dashboard/game_dashboard_constants.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2024 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_CONSTANTS_H_
|
||||
#define ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONSTANTS_H_
|
||||
|
||||
namespace ash::game_dashboard {
|
||||
|
||||
// Toolbar padding from the border of the game window.
|
||||
inline constexpr int kToolbarEdgePadding = 10;
|
||||
|
||||
// Interior margin padding around the game window for the
|
||||
// `GameDashboardWelcomeDialog`.
|
||||
inline constexpr int kWelcomeDialogEdgePadding = 8;
|
||||
|
||||
// Welcome dialog fixed width.
|
||||
inline constexpr int kWelcomeDialogFixedWidth = 360;
|
||||
|
||||
} // namespace ash::game_dashboard
|
||||
|
||||
#endif // ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONSTANTS_H_
|
@ -8,10 +8,12 @@
|
||||
#include <string>
|
||||
|
||||
#include "ash/game_dashboard/game_dashboard_button.h"
|
||||
#include "ash/game_dashboard/game_dashboard_constants.h"
|
||||
#include "ash/game_dashboard/game_dashboard_controller.h"
|
||||
#include "ash/game_dashboard/game_dashboard_main_menu_view.h"
|
||||
#include "ash/game_dashboard/game_dashboard_toolbar_view.h"
|
||||
#include "ash/game_dashboard/game_dashboard_utils.h"
|
||||
#include "ash/game_dashboard/game_dashboard_welcome_dialog.h"
|
||||
#include "ash/game_dashboard/game_dashboard_widget.h"
|
||||
#include "ash/public/cpp/app_types_util.h"
|
||||
#include "ash/public/cpp/arc_game_controls_flag.h"
|
||||
@ -24,6 +26,7 @@
|
||||
#include "ui/base/l10n/time_format.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/events/types/event_type.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/geometry/transform.h"
|
||||
#include "ui/views/animation/animation_builder.h"
|
||||
@ -39,13 +42,15 @@ constexpr base::TimeDelta kCountUpTimerRefreshInterval = base::Seconds(1);
|
||||
|
||||
// Number of pixels to add to the top and bottom of the Game Dashboard button so
|
||||
// that it's centered within the frame header.
|
||||
static const int kGameDashboardButtonVerticalPaddingDp = 3;
|
||||
constexpr int kGameDashboardButtonVerticalPaddingDp = 3;
|
||||
|
||||
// Toolbar padding from the border of the game window.
|
||||
static const int kToolbarEdgePadding = 10;
|
||||
// Maximum width of the game window that centers the welcome dialog in the
|
||||
// window instead of right aligned.
|
||||
constexpr int kMaxCenteredWelcomeDialogWidth =
|
||||
1.5 * game_dashboard::kWelcomeDialogFixedWidth;
|
||||
|
||||
// The animation duration for the bounds change operation on the toolbar widget.
|
||||
static constexpr base::TimeDelta kToolbarBoundsChangeAnimationDuration =
|
||||
constexpr base::TimeDelta kToolbarBoundsChangeAnimationDuration =
|
||||
base::Milliseconds(150);
|
||||
|
||||
std::unique_ptr<GameDashboardWidget> CreateTransientChildWidget(
|
||||
@ -63,6 +68,7 @@ std::unique_ptr<GameDashboardWidget> CreateTransientChildWidget(
|
||||
params.parent = game_window;
|
||||
params.name = widget_name;
|
||||
params.activatable = activatable;
|
||||
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
|
||||
|
||||
auto widget = std::make_unique<GameDashboardWidget>();
|
||||
widget->Init(std::move(params));
|
||||
@ -80,7 +86,15 @@ GameDashboardContext::GameDashboardContext(aura::Window* game_window)
|
||||
: game_window_(game_window),
|
||||
toolbar_snap_location_(ToolbarSnapLocation::kTopRight) {
|
||||
DCHECK(game_window_);
|
||||
// TODO(b/316141148): Update `show_welcome_dialog_` to reflect the welcome
|
||||
// dialog state in the settings.
|
||||
show_welcome_dialog_ = true;
|
||||
CreateAndAddGameDashboardButtonWidget();
|
||||
// ARC windows handle displaying the welcome dialog once the
|
||||
// `game_dashboard_button_` becomes available.
|
||||
if (!IsArcWindow(game_window_)) {
|
||||
MaybeShowWelcomeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
GameDashboardContext::~GameDashboardContext() {
|
||||
@ -88,6 +102,7 @@ GameDashboardContext::~GameDashboardContext() {
|
||||
if (main_menu_widget_) {
|
||||
main_menu_widget_->CloseNow();
|
||||
}
|
||||
CloseWelcomeDialog();
|
||||
}
|
||||
|
||||
void GameDashboardContext::SetToolbarSnapLocation(
|
||||
@ -99,13 +114,20 @@ void GameDashboardContext::SetToolbarSnapLocation(
|
||||
void GameDashboardContext::OnWindowBoundsChanged() {
|
||||
UpdateGameDashboardButtonWidgetBounds();
|
||||
MaybeUpdateToolbarWidgetBounds();
|
||||
MaybeUpdateWelcomeDialogBounds();
|
||||
}
|
||||
|
||||
void GameDashboardContext::UpdateForGameControlsFlags() {
|
||||
CHECK(IsArcWindow(game_window_));
|
||||
|
||||
game_dashboard_button_->SetEnabled(
|
||||
game_dashboard_utils::ShouldEnableGameDashboardButton(game_window_));
|
||||
const bool should_enable_button =
|
||||
game_dashboard_utils::ShouldEnableGameDashboardButton(game_window_);
|
||||
game_dashboard_button_->SetEnabled(should_enable_button);
|
||||
if (should_enable_button) {
|
||||
// ARC windows handle displaying the welcome dialog once the
|
||||
// `game_dashboard_button_` becomes available.
|
||||
MaybeShowWelcomeDialog();
|
||||
}
|
||||
|
||||
if (toolbar_view_) {
|
||||
toolbar_view_->UpdateViewForGameControls(
|
||||
@ -227,6 +249,7 @@ void GameDashboardContext::OnViewPreferredSizeChanged(
|
||||
views::View* observed_view) {
|
||||
CHECK_EQ(game_dashboard_button_, observed_view);
|
||||
UpdateGameDashboardButtonWidgetBounds();
|
||||
MaybeUpdateWelcomeDialogBounds();
|
||||
}
|
||||
|
||||
void GameDashboardContext::OnWidgetDestroying(views::Widget* widget) {
|
||||
@ -278,47 +301,105 @@ void GameDashboardContext::UpdateGameDashboardButtonWidgetBounds() {
|
||||
void GameDashboardContext::OnGameDashboardButtonPressed() {
|
||||
// TODO(b/273640775): Add metrics to know when the Game Dashboard button was
|
||||
// physically pressed.
|
||||
// Close the welcome dialog if it's open when a user opens the main menu view.
|
||||
CloseWelcomeDialog();
|
||||
ToggleMainMenu();
|
||||
}
|
||||
|
||||
void GameDashboardContext::MaybeShowWelcomeDialog() {
|
||||
if (!show_welcome_dialog_) {
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(!welcome_dialog_widget_);
|
||||
show_welcome_dialog_ = false;
|
||||
auto view = std::make_unique<GameDashboardWelcomeDialog>();
|
||||
GameDashboardWelcomeDialog* welcome_dialog_view = view.get();
|
||||
welcome_dialog_widget_ = CreateTransientChildWidget(
|
||||
game_window_, "GameDashboardWelcomeDialog", std::move(view),
|
||||
/*activatable=*/views::Widget::InitParams::Activatable::kNo);
|
||||
welcome_dialog_widget_->AddObserver(this);
|
||||
MaybeUpdateWelcomeDialogBounds();
|
||||
welcome_dialog_widget_->Show();
|
||||
welcome_dialog_view->StartTimer(base::BindRepeating(
|
||||
&GameDashboardContext::CloseWelcomeDialog, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void GameDashboardContext::MaybeUpdateWelcomeDialogBounds() {
|
||||
if (!welcome_dialog_widget_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gfx::Rect game_bounds = game_window_->GetBoundsInScreen();
|
||||
const gfx::Size preferred_size =
|
||||
welcome_dialog_widget_->GetContentsView()->GetPreferredSize();
|
||||
const int frame_header_height = GetFrameHeaderHeight();
|
||||
int origin_x;
|
||||
|
||||
if (game_bounds.width() > kMaxCenteredWelcomeDialogWidth) {
|
||||
// Place welcome dialog right aligned in the game window.
|
||||
origin_x = game_bounds.right() - game_dashboard::kWelcomeDialogEdgePadding -
|
||||
preferred_size.width();
|
||||
} else {
|
||||
// Place welcome dialog centered in the game window.
|
||||
origin_x =
|
||||
game_bounds.x() + (game_bounds.width() - preferred_size.width()) / 2;
|
||||
}
|
||||
|
||||
welcome_dialog_widget_->SetBounds(gfx::Rect(
|
||||
gfx::Point(origin_x, game_bounds.y() +
|
||||
game_dashboard::kWelcomeDialogEdgePadding +
|
||||
frame_header_height),
|
||||
preferred_size));
|
||||
}
|
||||
|
||||
const gfx::Rect GameDashboardContext::CalculateToolbarWidgetBounds() {
|
||||
const gfx::Rect game_bounds = game_window_->GetBoundsInScreen();
|
||||
const gfx::Size preferred_size =
|
||||
toolbar_widget_->GetContentsView()->GetPreferredSize();
|
||||
auto* frame_header = chromeos::FrameHeader::Get(
|
||||
views::Widget::GetWidgetForNativeWindow(game_window_));
|
||||
const int frame_header_height =
|
||||
(frame_header && frame_header->view()->GetVisible())
|
||||
? frame_header->GetHeaderHeight()
|
||||
: 0;
|
||||
const int frame_header_height = GetFrameHeaderHeight();
|
||||
gfx::Point origin;
|
||||
|
||||
switch (toolbar_snap_location_) {
|
||||
case ToolbarSnapLocation::kTopRight:
|
||||
origin = gfx::Point(
|
||||
game_bounds.right() - kToolbarEdgePadding - preferred_size.width(),
|
||||
game_bounds.y() + kToolbarEdgePadding + frame_header_height);
|
||||
origin =
|
||||
gfx::Point(game_bounds.right() - game_dashboard::kToolbarEdgePadding -
|
||||
preferred_size.width(),
|
||||
game_bounds.y() + game_dashboard::kToolbarEdgePadding +
|
||||
frame_header_height);
|
||||
break;
|
||||
case ToolbarSnapLocation::kTopLeft:
|
||||
origin = gfx::Point(
|
||||
game_bounds.x() + kToolbarEdgePadding,
|
||||
game_bounds.y() + kToolbarEdgePadding + frame_header_height);
|
||||
origin =
|
||||
gfx::Point(game_bounds.x() + game_dashboard::kToolbarEdgePadding,
|
||||
game_bounds.y() + game_dashboard::kToolbarEdgePadding +
|
||||
frame_header_height);
|
||||
break;
|
||||
case ToolbarSnapLocation::kBottomRight:
|
||||
origin = gfx::Point(
|
||||
game_bounds.right() - kToolbarEdgePadding - preferred_size.width(),
|
||||
game_bounds.bottom() - kToolbarEdgePadding - preferred_size.height());
|
||||
game_bounds.right() - game_dashboard::kToolbarEdgePadding -
|
||||
preferred_size.width(),
|
||||
game_bounds.bottom() - game_dashboard::kToolbarEdgePadding -
|
||||
preferred_size.height());
|
||||
break;
|
||||
case ToolbarSnapLocation::kBottomLeft:
|
||||
origin = gfx::Point(
|
||||
game_bounds.x() + kToolbarEdgePadding,
|
||||
game_bounds.bottom() - kToolbarEdgePadding - preferred_size.height());
|
||||
origin = gfx::Point(game_bounds.x() + game_dashboard::kToolbarEdgePadding,
|
||||
game_bounds.bottom() -
|
||||
game_dashboard::kToolbarEdgePadding -
|
||||
preferred_size.height());
|
||||
break;
|
||||
}
|
||||
|
||||
return gfx::Rect(origin, preferred_size);
|
||||
}
|
||||
|
||||
int GameDashboardContext::GetFrameHeaderHeight() const {
|
||||
auto* frame_header = chromeos::FrameHeader::Get(
|
||||
views::Widget::GetWidgetForNativeWindow(game_window_));
|
||||
return (frame_header && frame_header->view()->GetVisible())
|
||||
? frame_header->GetHeaderHeight()
|
||||
: 0;
|
||||
}
|
||||
|
||||
void GameDashboardContext::AnimateToolbarWidgetBoundsChange(
|
||||
const gfx::Rect& target_screen_bounds) {
|
||||
DCHECK(toolbar_widget_);
|
||||
@ -362,4 +443,11 @@ void GameDashboardContext::OnUpdateRecordingTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
void GameDashboardContext::CloseWelcomeDialog() {
|
||||
if (welcome_dialog_widget_) {
|
||||
welcome_dialog_widget_->RemoveObserver(this);
|
||||
welcome_dialog_widget_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -107,6 +107,12 @@ class ASH_EXPORT GameDashboardContext : public views::ViewObserver,
|
||||
// views::WidgetObserver:
|
||||
void OnWidgetDestroying(views::Widget* widget) override;
|
||||
|
||||
// TODO(b/316141148): Remove this test function once it's possible to set
|
||||
// `show_welcome_dialog_` via a property.
|
||||
void SetShowWelcomeDialogForTesting(bool show_dialog) {
|
||||
show_welcome_dialog_ = show_dialog;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class GameDashboardContextTestApi;
|
||||
|
||||
@ -121,10 +127,21 @@ class ASH_EXPORT GameDashboardContext : public views::ViewObserver,
|
||||
// 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();
|
||||
|
||||
// Calculates the height of the app's frame header.
|
||||
int GetFrameHeaderHeight() const;
|
||||
|
||||
// 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);
|
||||
@ -133,6 +150,10 @@ class ASH_EXPORT GameDashboardContext : public views::ViewObserver,
|
||||
// recording session duration.
|
||||
void OnUpdateRecordingTimer();
|
||||
|
||||
// Closes and deletes the Game Dashboard welcome dialog once it's no longer
|
||||
// needed.
|
||||
void CloseWelcomeDialog();
|
||||
|
||||
const raw_ptr<aura::Window> game_window_;
|
||||
|
||||
// Game Dashboard button widget for the Game Dashboard.
|
||||
@ -144,6 +165,9 @@ class ASH_EXPORT GameDashboardContext : public views::ViewObserver,
|
||||
// The toolbar for the Game Dashboard.
|
||||
std::unique_ptr<GameDashboardWidget> toolbar_widget_;
|
||||
|
||||
// The dialog displayed when the game window first opens.
|
||||
std::unique_ptr<GameDashboardWidget> welcome_dialog_widget_;
|
||||
|
||||
// The indicator of the current corner that the toolbar is placed.
|
||||
ToolbarSnapLocation toolbar_snap_location_;
|
||||
|
||||
@ -170,6 +194,11 @@ class ASH_EXPORT GameDashboardContext : public views::ViewObserver,
|
||||
// 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;
|
||||
|
||||
base::WeakPtrFactory<GameDashboardContext> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
|
@ -137,6 +137,10 @@ AnchoredNudge* GameDashboardContextTestApi::GetGameControlsSetupNudge() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
views::Widget* GameDashboardContextTestApi::GetWelcomeDialogWidget() {
|
||||
return context_->welcome_dialog_widget_.get();
|
||||
}
|
||||
|
||||
void GameDashboardContextTestApi::OpenTheMainMenu() {
|
||||
ASSERT_FALSE(GetMainMenuView()) << "The main menu view is already open.";
|
||||
ASSERT_FALSE(GetMainMenuWidget()) << "The main menu widget is already open.";
|
||||
|
@ -75,6 +75,9 @@ class GameDashboardContextTestApi {
|
||||
// Returns the Game Controls setup nudge.
|
||||
AnchoredNudge* GetGameControlsSetupNudge();
|
||||
|
||||
// Returns the Game Dashboard welcome dialog widget.
|
||||
views::Widget* GetWelcomeDialogWidget();
|
||||
|
||||
// Opens the main menu.
|
||||
// Before opening the main menu, verifies that the main menu is closed.
|
||||
// After opening the main menu, verifies it opened and waits for the thread to
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "ash/capture_mode/capture_mode_types.h"
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/game_dashboard/game_dashboard_button.h"
|
||||
#include "ash/game_dashboard/game_dashboard_constants.h"
|
||||
#include "ash/game_dashboard/game_dashboard_context_test_api.h"
|
||||
#include "ash/game_dashboard/game_dashboard_controller.h"
|
||||
#include "ash/game_dashboard/game_dashboard_main_menu_view.h"
|
||||
@ -51,10 +52,6 @@ namespace ash {
|
||||
|
||||
using ToolbarSnapLocation = GameDashboardContext::ToolbarSnapLocation;
|
||||
|
||||
// Toolbar padding copied from `GameDashboardContext`.
|
||||
static const int kToolbarEdgePadding = 10;
|
||||
static constexpr gfx::Rect kAppBounds = gfx::Rect(50, 50, 800, 400);
|
||||
|
||||
// Sub-label strings.
|
||||
const std::u16string& hidden_label = u"Hidden";
|
||||
const std::u16string& visible_label = u"Visible";
|
||||
@ -69,9 +66,22 @@ class GameDashboardContextTest : public GameDashboardTestBase {
|
||||
~GameDashboardContextTest() override = default;
|
||||
|
||||
void TearDown() override {
|
||||
CloseGameWindow();
|
||||
GameDashboardTestBase::TearDown();
|
||||
}
|
||||
|
||||
void CloseGameWindow() {
|
||||
game_window_.reset();
|
||||
test_api_.reset();
|
||||
GameDashboardTestBase::TearDown();
|
||||
}
|
||||
|
||||
const gfx::Rect app_bounds() const { return app_bounds_; }
|
||||
|
||||
void SetAppBounds(gfx::Rect app_bounds) {
|
||||
CHECK(!game_window_)
|
||||
<< "App bounds cannot be changed after creating window. To set the app "
|
||||
"bounds, call CloseWindow() and re-call this function.";
|
||||
app_bounds_ = app_bounds;
|
||||
}
|
||||
|
||||
int GetToolbarHeight() {
|
||||
@ -94,17 +104,23 @@ class GameDashboardContextTest : public GameDashboardTestBase {
|
||||
// game window. Otherwise, it creates the window as a GeForceNow window.
|
||||
// For ARC game windows, if `set_arc_game_controls_flags_prop` is true, then
|
||||
// the `kArcGameControlsFlagsKey` window property will be set to
|
||||
// `ArcGameControlsFlag::kKnown`, otherwise the property will not be set.
|
||||
// `ArcGameControlsFlag::kKnown`, otherwise the property will not be set. If
|
||||
// `show_welcome_dialog` is true, the welcome dialog displays when the Game
|
||||
// Window first opens.
|
||||
void CreateGameWindow(bool is_arc_window,
|
||||
bool set_arc_game_controls_flags_prop = true) {
|
||||
bool set_arc_game_controls_flags_prop = true,
|
||||
bool show_welcome_dialog = false) {
|
||||
ASSERT_FALSE(game_window_);
|
||||
ASSERT_FALSE(test_api_);
|
||||
game_window_ = CreateAppWindow(
|
||||
(is_arc_window ? TestGameDashboardDelegate::kGameAppId
|
||||
: extension_misc::kGeForceNowAppId),
|
||||
(is_arc_window ? AppType::ARC_APP : AppType::NON_APP), kAppBounds);
|
||||
(is_arc_window ? AppType::ARC_APP : AppType::NON_APP), app_bounds());
|
||||
auto* context = GameDashboardController::Get()->GetGameDashboardContext(
|
||||
game_window_.get());
|
||||
// TODO(b/316141148): Update test logic to set `show_welcome_dialog_` to
|
||||
// false via a property instead of directly.
|
||||
context->SetShowWelcomeDialogForTesting(show_welcome_dialog);
|
||||
ASSERT_TRUE(context);
|
||||
test_api_ = std::make_unique<GameDashboardContextTestApi>(
|
||||
context, GetEventGenerator());
|
||||
@ -436,6 +452,9 @@ class GameDashboardContextTest : public GameDashboardTestBase {
|
||||
// completes before proceeding.
|
||||
base::RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
private:
|
||||
gfx::Rect app_bounds_ = gfx::Rect(50, 50, 800, 400);
|
||||
};
|
||||
|
||||
// Verifies Game Controls tile state.
|
||||
@ -796,6 +815,64 @@ TEST_F(GameDashboardContextTest, RecordingTimerStringFormat) {
|
||||
EXPECT_EQ(u"24:00:30", test_api_->GetRecordingDuration());
|
||||
}
|
||||
|
||||
// Verifies the welcome dialog displays when the game window first opens and
|
||||
// disappears after 4 seconds.
|
||||
TEST_F(GameDashboardContextTest, WelcomeDialogAutoDismisses) {
|
||||
// Open the game window with the welcome dialog enabled.
|
||||
CreateGameWindow(/*is_arc_window=*/true,
|
||||
/*set_arc_game_controls_flags_prop=*/true,
|
||||
/*show_welcome_dialog=*/true);
|
||||
|
||||
// Verify the welcome dialog is initially shown and is right aligned in the
|
||||
// app window.
|
||||
ASSERT_TRUE(test_api_->GetWelcomeDialogWidget());
|
||||
gfx::Rect welcome_dialog_bounds =
|
||||
test_api_->GetWelcomeDialogWidget()->GetWindowBoundsInScreen();
|
||||
EXPECT_EQ(welcome_dialog_bounds.x(),
|
||||
(game_window_->GetBoundsInScreen().right() -
|
||||
game_dashboard::kWelcomeDialogEdgePadding -
|
||||
game_dashboard::kWelcomeDialogFixedWidth));
|
||||
|
||||
// Dismiss welcome dialog after 4 seconds and verify the dialog is no longer
|
||||
// visible.
|
||||
task_environment()->FastForwardBy(base::Seconds(4));
|
||||
EXPECT_FALSE(test_api_->GetWelcomeDialogWidget());
|
||||
}
|
||||
|
||||
// Verifies the welcome dialog disappears when the main menu view is opened.
|
||||
TEST_F(GameDashboardContextTest, WelcomeDialogDismissOnMainMenuOpening) {
|
||||
// Open the game window with the welcome dialog enabled.
|
||||
// CloseGameWindow();
|
||||
CreateGameWindow(/*is_arc_window=*/true,
|
||||
/*set_arc_game_controls_flags_prop=*/true,
|
||||
/*show_welcome_dialog=*/true);
|
||||
ASSERT_TRUE(test_api_->GetWelcomeDialogWidget());
|
||||
|
||||
// Open the main menu and verify the welcome dialog dismisses.
|
||||
test_api_->OpenTheMainMenu();
|
||||
EXPECT_FALSE(test_api_->GetWelcomeDialogWidget());
|
||||
}
|
||||
|
||||
// Verifies the welcome dialog is centered when the app window width is small
|
||||
// enough.
|
||||
TEST_F(GameDashboardContextTest, WelcomeDialogWithSmallWindow) {
|
||||
// Open a new game window with a width of 450.
|
||||
SetAppBounds(gfx::Rect(50, 50, 450, 400));
|
||||
CreateGameWindow(/*is_arc_window=*/true,
|
||||
/*set_arc_game_controls_flags_prop=*/true,
|
||||
/*show_welcome_dialog=*/true);
|
||||
ASSERT_TRUE(test_api_->GetWelcomeDialogWidget());
|
||||
|
||||
// Verify the welcome dialog is centered.
|
||||
gfx::Rect welcome_dialog_bounds =
|
||||
test_api_->GetWelcomeDialogWidget()->GetWindowBoundsInScreen();
|
||||
EXPECT_EQ(welcome_dialog_bounds.x(),
|
||||
(game_window_->GetBoundsInScreen().x() +
|
||||
(game_window_->GetBoundsInScreen().width() -
|
||||
game_dashboard::kWelcomeDialogFixedWidth) /
|
||||
2));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GameTypeGameDashboardContextTest:
|
||||
// Test fixture to test both ARC and GeForceNow game window depending on the
|
||||
@ -825,7 +902,7 @@ TEST_P(GameTypeGameDashboardContextTest,
|
||||
GameDashboardButtonWidget_InitialLocation) {
|
||||
const gfx::Point expected_button_center_point(
|
||||
game_window_->GetBoundsInScreen().top_center().x(),
|
||||
kAppBounds.y() + frame_header_->GetHeaderHeight() / 2);
|
||||
app_bounds().y() + frame_header_->GetHeaderHeight() / 2);
|
||||
EXPECT_EQ(expected_button_center_point,
|
||||
test_api_->GetGameDashboardButtonWidget()
|
||||
->GetNativeWindow()
|
||||
@ -853,8 +930,7 @@ TEST_P(GameTypeGameDashboardContextTest,
|
||||
TEST_P(GameTypeGameDashboardContextTest, OpenGameDashboardButtonWidget) {
|
||||
// Close the window and create a new game window without setting the
|
||||
// `kArcGameControlsFlagsKey` property.
|
||||
game_window_.reset();
|
||||
test_api_.reset();
|
||||
CloseGameWindow();
|
||||
CreateGameWindow(IsArcGame(), /*set_arc_game_controls_flags_prop=*/false);
|
||||
|
||||
// Verifies the main menu is closed.
|
||||
@ -913,8 +989,9 @@ TEST_P(GameTypeGameDashboardContextTest, CloseMainMenuOutsideButtonWidget) {
|
||||
|
||||
// Close the main menu dialog by clicking outside the main menu view bounds.
|
||||
auto* event_generator = GetEventGenerator();
|
||||
const gfx::Point& new_location = {kAppBounds.x() + kAppBounds.width(),
|
||||
kAppBounds.y() + kAppBounds.height()};
|
||||
gfx::Rect game_bounds = app_bounds();
|
||||
const gfx::Point& new_location = {game_bounds.x() + game_bounds.width(),
|
||||
game_bounds.y() + game_bounds.height()};
|
||||
event_generator->set_current_screen_location(new_location);
|
||||
event_generator->ClickLeftButton();
|
||||
|
||||
@ -1185,10 +1262,11 @@ TEST_P(GameTypeGameDashboardContextTest, MoveToolbarOutOfBounds) {
|
||||
const int screen_point_bottom = screen_point_y + kScreenBounds.height();
|
||||
|
||||
// Verify the screen bounds are larger than the game bounds.
|
||||
ASSERT_LT(screen_point_x, kAppBounds.x());
|
||||
ASSERT_LT(screen_point_y, kAppBounds.y());
|
||||
ASSERT_GT(screen_point_right, kAppBounds.x() + kAppBounds.width());
|
||||
ASSERT_GT(screen_point_bottom, kAppBounds.y() + kAppBounds.height());
|
||||
auto game_bounds = app_bounds();
|
||||
ASSERT_LT(screen_point_x, game_bounds.x());
|
||||
ASSERT_LT(screen_point_y, game_bounds.y());
|
||||
ASSERT_GT(screen_point_right, game_bounds.x() + game_bounds.width());
|
||||
ASSERT_GT(screen_point_bottom, game_bounds.y() + game_bounds.height());
|
||||
|
||||
// Drag toolbar, moving the mouse past the game window to the top right corner
|
||||
// of the screen bounds, and verify the toolbar doesn't go past the game
|
||||
@ -1296,6 +1374,7 @@ TEST_P(GameTypeGameDashboardContextTest, VerifyToolbarPlacementInQuadrants) {
|
||||
const int y_offset = window_bounds.height() / 4;
|
||||
|
||||
// Verify initial placement in top right quadrant.
|
||||
auto game_bounds = app_bounds();
|
||||
const auto* native_window = test_api_->GetToolbarWidget()->GetNativeWindow();
|
||||
auto toolbar_bounds = native_window->GetBoundsInScreen();
|
||||
const auto toolbar_size =
|
||||
@ -1303,36 +1382,44 @@ TEST_P(GameTypeGameDashboardContextTest, VerifyToolbarPlacementInQuadrants) {
|
||||
const int frame_header_height = frame_header_->GetHeaderHeight();
|
||||
EXPECT_EQ(test_api_->GetToolbarSnapLocation(),
|
||||
ToolbarSnapLocation::kTopRight);
|
||||
EXPECT_EQ(toolbar_bounds.x(),
|
||||
kAppBounds.right() - kToolbarEdgePadding - toolbar_size.width());
|
||||
EXPECT_EQ(toolbar_bounds.y(),
|
||||
kAppBounds.y() + kToolbarEdgePadding + frame_header_height);
|
||||
EXPECT_EQ(toolbar_bounds.x(), game_bounds.right() -
|
||||
game_dashboard::kToolbarEdgePadding -
|
||||
toolbar_size.width());
|
||||
EXPECT_EQ(toolbar_bounds.y(), game_bounds.y() +
|
||||
game_dashboard::kToolbarEdgePadding +
|
||||
frame_header_height);
|
||||
|
||||
// Move toolbar to top left quadrant and verify toolbar placement.
|
||||
DragToolbarToPoint(Movement::kMouse, {window_center_point.x() - x_offset,
|
||||
window_center_point.y() - y_offset});
|
||||
EXPECT_EQ(test_api_->GetToolbarSnapLocation(), ToolbarSnapLocation::kTopLeft);
|
||||
toolbar_bounds = native_window->GetBoundsInScreen();
|
||||
EXPECT_EQ(toolbar_bounds.x(), kAppBounds.x() + kToolbarEdgePadding);
|
||||
EXPECT_EQ(toolbar_bounds.y(),
|
||||
kAppBounds.y() + kToolbarEdgePadding + frame_header_height);
|
||||
EXPECT_EQ(toolbar_bounds.x(),
|
||||
game_bounds.x() + game_dashboard::kToolbarEdgePadding);
|
||||
EXPECT_EQ(toolbar_bounds.y(), game_bounds.y() +
|
||||
game_dashboard::kToolbarEdgePadding +
|
||||
frame_header_height);
|
||||
|
||||
// Move toolbar to bottom right quadrant and verify toolbar placement.
|
||||
DragToolbarToPoint(Movement::kMouse, {window_center_point.x() + x_offset,
|
||||
window_center_point.y() + y_offset});
|
||||
toolbar_bounds = native_window->GetBoundsInScreen();
|
||||
EXPECT_EQ(toolbar_bounds.x(),
|
||||
kAppBounds.right() - kToolbarEdgePadding - toolbar_size.width());
|
||||
EXPECT_EQ(toolbar_bounds.y(),
|
||||
kAppBounds.bottom() - kToolbarEdgePadding - toolbar_size.height());
|
||||
EXPECT_EQ(toolbar_bounds.x(), game_bounds.right() -
|
||||
game_dashboard::kToolbarEdgePadding -
|
||||
toolbar_size.width());
|
||||
EXPECT_EQ(toolbar_bounds.y(), game_bounds.bottom() -
|
||||
game_dashboard::kToolbarEdgePadding -
|
||||
toolbar_size.height());
|
||||
|
||||
// Move toolbar to bottom left quadrant and verify toolbar placement.
|
||||
DragToolbarToPoint(Movement::kMouse, {window_center_point.x() - x_offset,
|
||||
window_center_point.y() + y_offset});
|
||||
toolbar_bounds = native_window->GetBoundsInScreen();
|
||||
EXPECT_EQ(toolbar_bounds.x(), kAppBounds.x() + kToolbarEdgePadding);
|
||||
EXPECT_EQ(toolbar_bounds.y(),
|
||||
kAppBounds.bottom() - kToolbarEdgePadding - toolbar_size.height());
|
||||
EXPECT_EQ(toolbar_bounds.x(),
|
||||
game_bounds.x() + game_dashboard::kToolbarEdgePadding);
|
||||
EXPECT_EQ(toolbar_bounds.y(), game_bounds.bottom() -
|
||||
game_dashboard::kToolbarEdgePadding -
|
||||
toolbar_size.height());
|
||||
}
|
||||
|
||||
// Verifies the toolbar's snap location is preserved even after the visibility
|
||||
|
179
ash/game_dashboard/game_dashboard_welcome_dialog.cc
Normal file
179
ash/game_dashboard/game_dashboard_welcome_dialog.cc
Normal file
@ -0,0 +1,179 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/game_dashboard/game_dashboard_welcome_dialog.h"
|
||||
|
||||
#include "ash/bubble/bubble_utils.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/style/typography.h"
|
||||
#include "chromeos/ui/vector_icons/vector_icons.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
|
||||
#include "ui/events/ash/keyboard_capability.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/border.h"
|
||||
#include "ui/views/controls/image_view.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
// Corner radius of the welcome dialog.
|
||||
constexpr float kDialogCornerRadius = 24.0f;
|
||||
// Fixed width of the welcome dialog.
|
||||
static constexpr int kDialogWidth = 360;
|
||||
// Radius of the icon and its background displayed in the dialog.
|
||||
constexpr float kIconBackgroundRadius = 40.0f;
|
||||
// The height and width of the dialog's icon.
|
||||
constexpr int kIconSize = 20;
|
||||
// Additional padding for the top, left, and right title container border.
|
||||
constexpr int kPrimaryContainerBorder = 12;
|
||||
// Border padding surrounding the inside of the entire welcome dialog.
|
||||
constexpr int kPrimaryLayoutInsideBorder = 8;
|
||||
// Padding between the `primary_container` and `shortcut_hint` rows.
|
||||
constexpr int kRowPadding = 20;
|
||||
// Radius of the container of the shortcut text.
|
||||
constexpr float kShortcutCornerRadius = 16.0f;
|
||||
// Padding surrounding the shortcut info text.
|
||||
constexpr int kShortcutTextBorder = 16;
|
||||
// Padding between the `title_container` and `icon_container`.
|
||||
constexpr int kTitleContainerPadding = 20;
|
||||
// Width of the container containing the text title and sub-label.
|
||||
constexpr int kTitleTextMaxWidth =
|
||||
kDialogWidth - kIconBackgroundRadius - kTitleContainerPadding -
|
||||
/*left and right dialog insets*/ 2 * kPrimaryLayoutInsideBorder -
|
||||
/*additional `primary_container` left and right padding*/
|
||||
2 * kPrimaryContainerBorder;
|
||||
// Maximum duration that the dialog should be displayed.
|
||||
constexpr base::TimeDelta kDialogDuration = base::Seconds(4);
|
||||
|
||||
} // namespace
|
||||
|
||||
GameDashboardWelcomeDialog::GameDashboardWelcomeDialog() {
|
||||
SetOrientation(views::LayoutOrientation::kVertical);
|
||||
SetIgnoreDefaultMainAxisMargins(true);
|
||||
SetDefault(views::kMarginsKey, gfx::Insets::TLBR(kRowPadding, 0, 0, 0));
|
||||
SetInteriorMargin(
|
||||
gfx::Insets::VH(kPrimaryLayoutInsideBorder, kPrimaryLayoutInsideBorder));
|
||||
SetBackground(views::CreateThemedRoundedRectBackground(
|
||||
cros_tokens::kCrosSysSystemBaseElevatedOpaque, kDialogCornerRadius));
|
||||
|
||||
AddTitleAndIconRow();
|
||||
AddShortcutInfoRow();
|
||||
}
|
||||
|
||||
GameDashboardWelcomeDialog::~GameDashboardWelcomeDialog() = default;
|
||||
|
||||
void GameDashboardWelcomeDialog::StartTimer(base::OnceClosure on_complete) {
|
||||
DCHECK(on_complete) << "OnceClosure must be passed to determine what to do "
|
||||
"when the timer completes.";
|
||||
timer_.Start(FROM_HERE, kDialogDuration, std::move(on_complete));
|
||||
}
|
||||
|
||||
// Creates a primary container that holds separate sub-containers for the text
|
||||
// and icon.
|
||||
// Note: When using `views::FlexLayoutView` it's common to wrap objects in
|
||||
// additional containers that need a separate alignment than the rest of the
|
||||
// elements. This creates the following:
|
||||
//
|
||||
// +----------------------------------------------------+
|
||||
// | primary_container |
|
||||
// | +--------------------------+-------------------+ |
|
||||
// | | title_container | icon_container | |
|
||||
// | | +--------------------+ | +--------+ | |
|
||||
// | | | title | | | | | |
|
||||
// | | +--------------------+ | | icon | | |
|
||||
// | | | sub_label | | | | | |
|
||||
// | | +--------------------+ | +--------+ | |
|
||||
// | +--------------------------+-------------------+ |
|
||||
// +----------------------------------------------------+
|
||||
void GameDashboardWelcomeDialog::AddTitleAndIconRow() {
|
||||
auto* primary_container =
|
||||
AddChildView(std::make_unique<views::FlexLayoutView>());
|
||||
primary_container->SetIgnoreDefaultMainAxisMargins(true);
|
||||
primary_container->SetDefault(views::kMarginsKey, gfx::Insets::VH(0, 0));
|
||||
primary_container->SetInteriorMargin(
|
||||
gfx::Insets::TLBR(kPrimaryContainerBorder, kPrimaryContainerBorder, 0,
|
||||
kPrimaryContainerBorder));
|
||||
primary_container->SetOrientation(views::LayoutOrientation::kHorizontal);
|
||||
|
||||
// Create title container as a child of the primary container.
|
||||
auto* title_container = primary_container->AddChildView(
|
||||
std::make_unique<views::FlexLayoutView>());
|
||||
title_container->SetOrientation(views::LayoutOrientation::kVertical);
|
||||
title_container->SetCrossAxisAlignment(views::LayoutAlignment::kStart);
|
||||
title_container->SetInteriorMargin(
|
||||
gfx::Insets::TLBR(0, 0, 0, kTitleContainerPadding));
|
||||
|
||||
// Add title label to the title container.
|
||||
auto* title = title_container->AddChildView(bubble_utils::CreateLabel(
|
||||
TypographyToken::kCrosButton1,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_TITLE),
|
||||
cros_tokens::kCrosSysOnSurface));
|
||||
title->SetMultiLine(true);
|
||||
title->SizeToFit(kTitleTextMaxWidth);
|
||||
title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
||||
|
||||
// Add sub-label to the title container.
|
||||
auto* sub_label = title_container->AddChildView(bubble_utils::CreateLabel(
|
||||
TypographyToken::kCrosAnnotation2,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_ASH_GAME_DASHBOARD_WELCOME_DIALOG_SUB_LABEL),
|
||||
cros_tokens::kCrosSysOnSurfaceVariant));
|
||||
// TODO(b/316138331): Investigate why multi-line support isn't working
|
||||
// properly.
|
||||
sub_label->SetMultiLine(true);
|
||||
sub_label->SizeToFit(kTitleTextMaxWidth);
|
||||
sub_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
||||
|
||||
// Create icon container as a child of the primary container.
|
||||
auto* icon_container = primary_container->AddChildView(
|
||||
std::make_unique<views::FlexLayoutView>());
|
||||
icon_container->SetCrossAxisAlignment(views::LayoutAlignment::kEnd);
|
||||
|
||||
// Add icon to the icon container.
|
||||
auto* icon = icon_container->AddChildView(
|
||||
std::make_unique<views::ImageView>(ui::ImageModel::FromVectorIcon(
|
||||
chromeos::kGameDashboardGamepadIcon, cros_tokens::kCrosSysOnPrimary,
|
||||
kIconSize)));
|
||||
icon->SetPreferredSize(
|
||||
gfx::Size(kIconBackgroundRadius, kIconBackgroundRadius));
|
||||
icon->SetBackground(views::CreateThemedRoundedRectBackground(
|
||||
cros_tokens::kCrosSysPrimary, kIconBackgroundRadius));
|
||||
}
|
||||
|
||||
// Creates a stylized label that holds the hint indicating how open the Game
|
||||
// Dashboard shortcut. This creates the following:
|
||||
//
|
||||
// +----------------------------------------------------+
|
||||
// | shortcut_hint |
|
||||
// +----------------------------------------------------+
|
||||
void GameDashboardWelcomeDialog::AddShortcutInfoRow() {
|
||||
const std::u16string shortcut_key = l10n_util::GetStringUTF16(
|
||||
Shell::Get()->keyboard_capability()->HasLauncherButtonOnAnyKeyboard()
|
||||
? IDS_ASH_SHORTCUT_MODIFIER_LAUNCHER
|
||||
: IDS_ASH_SHORTCUT_MODIFIER_SEARCH);
|
||||
auto* shortcut_hint = AddChildView(bubble_utils::CreateLabel(
|
||||
TypographyToken::kCrosButton2,
|
||||
l10n_util::GetStringFUTF16(IDS_ASH_GAME_DASHBOARD_WELCOME_DIALOG_SHORTCUT,
|
||||
shortcut_key),
|
||||
cros_tokens::kCrosSysPrimary));
|
||||
shortcut_hint->SetMultiLine(true);
|
||||
// TODO(b/316138331): Update max width to ensure it matches specs.
|
||||
shortcut_hint->SizeToFit(kTitleTextMaxWidth);
|
||||
shortcut_hint->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
||||
shortcut_hint->SetBackground(views::CreateThemedRoundedRectBackground(
|
||||
cros_tokens::kCrosSysSystemOnBase, kShortcutCornerRadius));
|
||||
shortcut_hint->SetBorder(views::CreateEmptyBorder(
|
||||
gfx::Insets::VH(kShortcutTextBorder, kShortcutTextBorder)));
|
||||
}
|
||||
|
||||
BEGIN_METADATA(GameDashboardWelcomeDialog, views::FlexLayoutView)
|
||||
END_METADATA
|
||||
|
||||
} // namespace ash
|
44
ash/game_dashboard/game_dashboard_welcome_dialog.h
Normal file
44
ash/game_dashboard/game_dashboard_welcome_dialog.h
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2024 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_WELCOME_DIALOG_H_
|
||||
#define ASH_GAME_DASHBOARD_GAME_DASHBOARD_WELCOME_DIALOG_H_
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "ui/views/layout/flex_layout_view.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// `GameDashboardWelcomeDialog` is a View displayed for a set duration of time
|
||||
// when first opening any game. It can be disabled via the Game Dashboard
|
||||
// Settings.
|
||||
class ASH_EXPORT GameDashboardWelcomeDialog : public views::FlexLayoutView {
|
||||
public:
|
||||
METADATA_HEADER(GameDashboardWelcomeDialog);
|
||||
|
||||
GameDashboardWelcomeDialog();
|
||||
GameDashboardWelcomeDialog(const GameDashboardWelcomeDialog&) = delete;
|
||||
GameDashboardWelcomeDialog& operator=(const GameDashboardWelcomeDialog) =
|
||||
delete;
|
||||
~GameDashboardWelcomeDialog() override;
|
||||
|
||||
// Starts the `timer_`, which will run the given `on_complete` once the time
|
||||
// specified by `kDialogDuration` has elapsed.
|
||||
void StartTimer(base::OnceClosure on_complete);
|
||||
|
||||
private:
|
||||
// Adds a stacked title/sub-label and an icon as a row to the welcome dialog.
|
||||
void AddTitleAndIconRow();
|
||||
|
||||
// Adds a row displaying how to open the dashboard.
|
||||
void AddShortcutInfoRow();
|
||||
|
||||
// Timer for how long to show the welcome dialog.
|
||||
base::OneShotTimer timer_;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_GAME_DASHBOARD_GAME_DASHBOARD_WELCOME_DIALOG_H_
|
Reference in New Issue
Block a user