0

gd: Create a custom main menu button.

Creating a custom button for the main menu button. The "default" button
state shows the gamepad icon and the title. The "recording" button
type shows a recording duration and updates the title.

Move the gamepad icon from chrome/app to ash, for reuse.

Bug: b:286085493
Test: Ran unit test and manual testing.
Change-Id: I5088ae10819a2b17179adf0c2b3e4e1fd5118f7b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4709317
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Auto-Submit: Prameet Shah <phshah@chromium.org>
Commit-Queue: Prameet Shah <phshah@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1198542}
This commit is contained in:
Prameet Shah
2023-09-19 19:20:17 +00:00
committed by Chromium LUCI CQ
parent 9c72c6c375
commit 93d7ddbc7c
21 changed files with 409 additions and 44 deletions

@ -624,6 +624,8 @@ component("ash") {
"frame_throttler/frame_throttling_controller.cc",
"frame_throttler/frame_throttling_controller.h",
"frame_throttler/frame_throttling_observer.h",
"game_dashboard/game_dashboard_button.cc",
"game_dashboard/game_dashboard_button.h",
"game_dashboard/game_dashboard_context.cc",
"game_dashboard/game_dashboard_context.h",
"game_dashboard/game_dashboard_controller.cc",

@ -7083,8 +7083,11 @@ To shut down the device, press and hold the power button on the device again.
<message name="IDS_ASH_GAME_DASHBOARD_HELP_TOOLTIP" translateable="false" desc="Tooltip for the Game Dashboard help icon button.">
Help center
</message>
<message name="IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_TITLE" desc="The title of the main Game Dashboard button.">
Game Dashboard
<message name="IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_RECORDING" desc="The title of the Game Dashboard button when the window is being recorded, and also shows the recording duration.">
<ph name="DURATION">$1<ex>00:00</ex></ph>&#x0020;&#x0020;Recording
</message>
<message name="IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_TITLE" desc="The default title of the Game Dashboard button.">
Game dashboard
</message>
<message name="IDS_ASH_GAME_DASHBOARD_HIDDEN_STATUS" translateable="false" desc="The hidden state for compact Game Dashboard tile sub-labels.">
Hidden

@ -0,0 +1 @@
f2c0634c74b7ba723ca89c982215138c0f531eaa

@ -1 +1 @@
a3687baed80e7c5db6fb27eced97143e808d182b
91a7c8d80714026aafacb5089da88a08df4fb248

@ -19,6 +19,7 @@
#include "ash/display/window_tree_host_manager.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_widget.h"
#include "ash/public/cpp/capture_mode/capture_mode_test_api.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/screen_util.h"

@ -0,0 +1,151 @@
// 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.
#include "ash/game_dashboard/game_dashboard_button.h"
#include <memory>
#include <string>
#include "ash/bubble/bubble_utils.h"
#include "ash/game_dashboard/game_dashboard_context.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/rounded_container.h"
#include "ash/style/typography.h"
#include "base/check.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "chromeos/ui/frame/frame_header.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/compositor/layer.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/background.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
constexpr int kIconHeight = 20;
constexpr gfx::RoundedCornersF kRoundedCornerRadius =
gfx::RoundedCornersF(12.0f);
constexpr gfx::Insets kButtonBorderInsets = gfx::Insets::TLBR(0, 12, 0, 8);
constexpr gfx::Insets kGamepadIconMargins = gfx::Insets::TLBR(0, 0, 0, 8);
constexpr gfx::Insets kDropdownArrowMargins = gfx::Insets::TLBR(0, 6, 0, 0);
} // namespace
GameDashboardButton::GameDashboardButton(PressedCallback callback)
: views::Button(callback) {
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>());
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kCenter);
SetBorder(views::CreateEmptyBorder(kButtonBorderInsets));
SetPaintToLayer();
layer()->SetRoundedCornerRadius(kRoundedCornerRadius);
layer()->SetFillsBoundsOpaquely(false);
// Add the gamepad icon view.
gamepad_icon_view_ = AddChildView(std::make_unique<views::ImageView>());
gamepad_icon_view_->SetProperty(views::kMarginsKey, kGamepadIconMargins);
// Add the title view.
title_view_ = AddChildView(
bubble_utils::CreateLabel(ash::TypographyToken::kCrosButton2));
// Add the dropdown icon view.
dropdown_icon_view_ = AddChildView(std::make_unique<views::ImageView>());
dropdown_icon_view_->SetProperty(views::kMarginsKey, kDropdownArrowMargins);
UpdateViews();
}
GameDashboardButton::~GameDashboardButton() = default;
void GameDashboardButton::SetToggled(bool toggled) {
if (toggled == toggled_) {
return;
}
toggled_ = toggled;
UpdateDropDownArrow();
}
void GameDashboardButton::OnRecordingStarted() {
CHECK(!is_recording_);
is_recording_ = true;
UpdateViews();
}
void GameDashboardButton::OnRecordingEnded() {
if (!is_recording_) {
return;
}
is_recording_ = false;
UpdateViews();
}
void GameDashboardButton::UpdateRecordingDuration(
const std::u16string& duration) {
DCHECK(title_view_);
SetTitle(l10n_util::GetStringFUTF16(
IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_RECORDING, duration));
}
void GameDashboardButton::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged();
}
void GameDashboardButton::UpdateDropDownArrow() {
DCHECK(dropdown_icon_view_);
const gfx::VectorIcon& dropdown_icon =
toggled_ ? kGdDropUpArrowIcon : kGdDropDownArrowIcon;
const auto icon_color = is_recording_
? cros_tokens::kCrosSysSystemOnNegativeContainer
: cros_tokens::kCrosSysOnPrimaryContainer;
dropdown_icon_view_->SetImage(
ui::ImageModel::FromVectorIcon(dropdown_icon, icon_color, kIconHeight));
}
void GameDashboardButton::UpdateViews() {
ui::ColorId container_color;
ui::ColorId icon_and_label_color;
if (is_recording_) {
container_color = cros_tokens::kCrosSysSystemNegativeContainer;
icon_and_label_color = cros_tokens::kCrosSysSystemOnNegativeContainer;
// Don't update `title_view_` because it will be updated by
// `UpdateRecordingDuration()`.
} else {
container_color = cros_tokens::kCrosSysHighlightShape;
icon_and_label_color = cros_tokens::kCrosSysOnPrimaryContainer;
SetTitle(l10n_util::GetStringUTF16(
IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_TITLE));
}
SetBackground(views::CreateThemedSolidBackground(container_color));
gamepad_icon_view_->SetImage(ui::ImageModel::FromVectorIcon(
chromeos::kGameDashboardGamepadIcon, icon_and_label_color, kIconHeight));
title_view_->SetEnabledColorId(icon_and_label_color);
UpdateDropDownArrow();
}
void GameDashboardButton::SetTitle(const std::u16string& title_text) {
SetTooltipText(title_text);
title_view_->SetText(title_text);
}
BEGIN_METADATA(GameDashboardButton, views::Button)
END_METADATA
} // namespace ash

@ -0,0 +1,106 @@
// 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_BUTTON_H_
#define ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_H_
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/controls/button/button.h"
namespace views {
class ImageView;
class Label;
class View;
} // namespace views
namespace ash {
// The main button for the Game Dashboard, which acts as an entry point for
// features in GameDashboardMainMenuView.
//
// The button looks like:
// +-----------+-------+-----------+
// | icon_view | label | icon_view |
// +-----------+-------+-----------+
//
// There are 2 states: Default and Recording
//
// The 'Default' button indicates an idle Game Dashboard menu. Clicking on
// the button will toggle the `GameDashboardMainMenuView`, where the Recording
// Game tile is in the default state.
//
// The 'Recording' button state indicates that the game window is being
// recorded, which is shown after a user has initiated a game window recording.
// The label view is updated to show a count up timer, representing the
// duration, and "Recording" as its status. Clicking on the button will toggle
// the `GameDashboardMainMenuView`, where the Recording Game tile allows the
// user to stop the recording.
//
// The first "icon_view" always shows the gamepad icon.
// The second "icon_view" shows a dropdown arrow. Calling `SetToggled()` with
// true will replace the second "icon_view" with an the up arrow. Called with
// false, it will show the down arrow.
class GameDashboardButton : public views::Button {
public:
METADATA_HEADER(GameDashboardButton);
explicit GameDashboardButton(PressedCallback callback);
GameDashboardButton(const GameDashboardButton&) = delete;
GameDashboardButton& operator=(const GameDashboardButton&) = delete;
~GameDashboardButton() override;
bool is_recording() const { return is_recording_; }
bool toggled() const { return toggled_; }
// Updates the `toggled_` state of the button.
void SetToggled(bool toggled);
// Called when the game window recording has started.
void OnRecordingStarted();
// Called when the game window recording has ended.
void OnRecordingEnded();
// Updates `title_view_`'s text with `duration`.
void UpdateRecordingDuration(const std::u16string& duration);
// views::View:
void ChildPreferredSizeChanged(views::View* child) override;
private:
friend class GameDashboardContextTestApi;
// Updates the `dropdown_icon_view_` icon. If `toggled_` is true, it'll show
// the up arrow, otherwise the down arrow.
void UpdateDropDownArrow();
// Updates `is_recording_` with `is_recording`, then updates all the views.
void UpdateRecordingState(bool is_recording);
// Updates all the views in the button. If `is_recording_` is true, the
// UI is updated to show the 'Recording' button, indicating that there's an
// active video recording session. Otherwise, it will show the 'Default'
// button.
void UpdateViews();
// Sets the `title_view` and the tooltip text to `title_text`.
void SetTitle(const std::u16string& title_text);
// Owned by views hierarchy.
raw_ptr<views::ImageView, ExperimentalAsh> gamepad_icon_view_;
raw_ptr<views::Label, ExperimentalAsh> title_view_;
raw_ptr<views::ImageView, ExperimentalAsh> dropdown_icon_view_;
// If true, the game window is being recorded, otherwise false.
bool is_recording_ = false;
// The button toggle state.
bool toggled_ = false;
};
} // namespace ash
#endif // ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_H_

@ -7,15 +7,14 @@
#include <memory>
#include <string>
#include "ash/game_dashboard/game_dashboard_button.h"
#include "ash/game_dashboard/game_dashboard_main_menu_view.h"
#include "ash/game_dashboard/game_dashboard_toolbar_view.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/pill_button.h"
#include "ash/game_dashboard/game_dashboard_widget.h"
#include "base/i18n/time_formatting.h"
#include "base/timer/timer.h"
#include "chromeos/ui/frame/frame_header.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/transform.h"
@ -74,6 +73,7 @@ GameDashboardContext::GameDashboardContext(aura::Window* game_window)
}
GameDashboardContext::~GameDashboardContext() {
game_dashboard_button_->RemoveObserver(this);
if (main_menu_widget_) {
main_menu_widget_->CloseNow();
}
@ -91,10 +91,7 @@ void GameDashboardContext::OnWindowBoundsChanged() {
}
void GameDashboardContext::SetGameDashboardButtonEnabled(bool enable) {
DCHECK(game_dashboard_button_widget_);
auto* contents_view = game_dashboard_button_widget_->GetContentsView();
DCHECK(contents_view);
contents_view->SetEnabled(enable);
game_dashboard_button_->SetEnabled(enable);
}
void GameDashboardContext::ToggleMainMenu() {
@ -106,6 +103,7 @@ void GameDashboardContext::ToggleMainMenu() {
base::WrapUnique(views::BubbleDialogDelegateView::CreateBubble(
std::move(widget_delegate)));
main_menu_widget_->Show();
game_dashboard_button_->SetToggled(true);
} else {
CloseMainMenu();
}
@ -116,6 +114,7 @@ void GameDashboardContext::CloseMainMenu() {
DCHECK(main_menu_widget_.get());
main_menu_view_ = nullptr;
main_menu_widget_.reset();
game_dashboard_button_->SetToggled(false);
}
bool GameDashboardContext::ToggleToolbar() {
@ -155,11 +154,10 @@ bool GameDashboardContext::IsToolbarVisible() const {
void GameDashboardContext::OnRecordingStarted(bool is_recording_game_window) {
if (is_recording_game_window) {
// TODO(b/273641154): Update the the Game Dashboard button to the recording
// state.
CHECK(!recording_timer_.IsRunning());
DCHECK(recording_start_time_.is_null());
DCHECK(recording_duration_.empty());
game_dashboard_button_->OnRecordingStarted();
recording_start_time_ = base::Time::Now();
OnUpdateRecordingTimer();
recording_timer_.Start(FROM_HERE, kCountUpTimerRefreshInterval, this,
@ -178,8 +176,7 @@ void GameDashboardContext::OnRecordingEnded() {
recording_timer_.Stop();
recording_start_time_ = base::Time();
recording_duration_.clear();
// TODO(b/273641154): Update the the Game Dashboard button to the default
// state.
game_dashboard_button_->OnRecordingEnded();
if (main_menu_view_) {
main_menu_view_->OnRecordingEnded();
}
@ -188,15 +185,21 @@ void GameDashboardContext::OnRecordingEnded() {
}
}
void GameDashboardContext::OnViewPreferredSizeChanged(
views::View* observed_view) {
CHECK_EQ(game_dashboard_button_, observed_view);
UpdateGameDashboardButtonWidgetBounds();
}
void GameDashboardContext::CreateAndAddGameDashboardButtonWidget() {
auto game_dashboard_button = std::make_unique<GameDashboardButton>(
base::BindRepeating(&GameDashboardContext::OnGameDashboardButtonPressed,
weak_ptr_factory_.GetWeakPtr()));
game_dashboard_button->AddObserver(this);
DCHECK(!game_dashboard_button_);
game_dashboard_button_ = game_dashboard_button.get();
game_dashboard_button_widget_ = CreateTransientChildWidget(
game_window_, "GameDashboardButton",
std::make_unique<PillButton>(
base::BindRepeating(
&GameDashboardContext::OnGameDashboardButtonPressed,
weak_ptr_factory_.GetWeakPtr()),
l10n_util::GetStringUTF16(
IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_TITLE)));
game_window_, "GameDashboardButton", std::move(game_dashboard_button));
DCHECK_EQ(
game_window_,
wm::GetTransientParent(game_dashboard_button_widget_->GetNativeWindow()));
@ -304,11 +307,8 @@ void GameDashboardContext::OnUpdateRecordingTimer() {
if (delta < base::Hours(1)) {
base::ReplaceFirstSubstringAfterOffset(&duration, 0, u"0:", u"");
}
recording_duration_ = duration;
// TODO(b/273641154): Update the the Game Dashboard button with the recording
// duration.
game_dashboard_button_->UpdateRecordingDuration(duration);
if (main_menu_view_) {
main_menu_view_->UpdateRecordingDuration(duration);
}

@ -7,12 +7,12 @@
#include <memory>
#include "ash/game_dashboard/game_dashboard_widget.h"
#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget.h"
namespace aura {
class Window;
@ -20,12 +20,14 @@ class Window;
namespace ash {
class GameDashboardButton;
class GameDashboardMainMenuView;
class GameDashboardToolbarView;
class GameDashboardWidget;
// This class manages Game Dashboard related UI for a given `aura::Window`, and
// its instance is managed by the `GameDashboardController`.
class ASH_EXPORT GameDashboardContext {
class ASH_EXPORT GameDashboardContext : public views::ViewObserver {
public:
// Indicator for the 4 quadrants that the toolbar is able to be placed.
enum class ToolbarSnapLocation {
@ -38,7 +40,7 @@ class ASH_EXPORT GameDashboardContext {
explicit GameDashboardContext(aura::Window* game_window);
GameDashboardContext(const GameDashboardContext&) = delete;
GameDashboardContext& operator=(const GameDashboardContext&) = delete;
~GameDashboardContext();
~GameDashboardContext() override;
aura::Window* game_window() { return game_window_.get(); }
@ -90,6 +92,9 @@ class ASH_EXPORT GameDashboardContext {
// if the recording session was aborted.
void OnRecordingEnded();
// views::ViewObserver:
void OnViewPreferredSizeChanged(views::View* observed_view) override;
private:
friend class GameDashboardContextTestApi;
@ -101,8 +106,7 @@ class ASH_EXPORT GameDashboardContext {
// the `game_window_`.
void UpdateGameDashboardButtonWidgetBounds();
// Called when the button in the `game_dashboard_button_widget_` is pressed,
// and toggles the main menu.
// Called when `GameDashboardButton` is pressed, and toggles the main menu.
void OnGameDashboardButtonPressed();
// Determines the toolbar's physical location on screen based on the
@ -131,8 +135,14 @@ class ASH_EXPORT GameDashboardContext {
// The indicator of the current corner that the toolbar is placed.
ToolbarSnapLocation toolbar_snap_location_;
// The `GameDashboardButton` view in the `game_dashboard_button_widget_`.
// Owned by the views hierarchy.
raw_ptr<GameDashboardButton, ExperimentalAsh> game_dashboard_button_ =
nullptr;
// The `GameDashboardMainMenuView` when the user presses the Game Dashboard
// button. Owned by the views hierarchy.
// button.
// Owned by the views hierarchy.
raw_ptr<GameDashboardMainMenuView, DanglingUntriaged | ExperimentalAsh>
main_menu_view_ = nullptr;

@ -7,6 +7,7 @@
#include <string>
#include "ash/capture_mode/capture_mode_test_util.h"
#include "ash/game_dashboard/game_dashboard_button.h"
#include "ash/game_dashboard/game_dashboard_context.h"
#include "ash/game_dashboard/game_dashboard_main_menu_view.h"
#include "ash/game_dashboard/game_dashboard_toolbar_view.h"
@ -45,11 +46,15 @@ GameDashboardWidget* GameDashboardContextTestApi::GetGameDashboardButtonWidget()
return context_->game_dashboard_button_widget();
}
PillButton* GameDashboardContextTestApi::GetGameDashboardButton() const {
auto* game_dashboard_button_widget = GetGameDashboardButtonWidget();
CHECK(game_dashboard_button_widget);
return views::AsViewClass<PillButton>(
game_dashboard_button_widget->GetContentsView());
GameDashboardButton* GameDashboardContextTestApi::GetGameDashboardButton()
const {
return context_->game_dashboard_button_;
}
views::Label* GameDashboardContextTestApi::GetGameDashboardButtonTitle() const {
auto* game_dashboard_button = GetGameDashboardButton();
CHECK(game_dashboard_button);
return game_dashboard_button->title_view_;
}
views::Widget* GameDashboardContextTestApi::GetMainMenuWidget() {

@ -20,6 +20,7 @@ class EventGenerator;
namespace views {
class Button;
class Label;
class LabelButton;
class View;
class Widget;
@ -28,6 +29,7 @@ class Widget;
namespace ash {
class FeatureTile;
class GameDashboardButton;
class GameDashboardMainMenuView;
class GameDashboardToolbarView;
class GameDashboardWidget;
@ -49,9 +51,10 @@ class GameDashboardContextTestApi {
const base::RepeatingTimer& GetRecordingTimer() const;
const std::u16string& GetRecordingDuration() const;
// Returns the Game Dashboard button widget and button.
// Returns the Game Dashboard button widget, button, and title view.
GameDashboardWidget* GetGameDashboardButtonWidget() const;
PillButton* GetGameDashboardButton() const;
GameDashboardButton* GetGameDashboardButton() const;
views::Label* GetGameDashboardButtonTitle() const;
// Returns the main menu widget and all its views.
views::Widget* GetMainMenuWidget();

@ -5,11 +5,13 @@
#include "ash/game_dashboard/game_dashboard_context.h"
#include <memory>
#include <string>
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_test_util.h"
#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_context_test_api.h"
#include "ash/game_dashboard/game_dashboard_controller.h"
#include "ash/game_dashboard/game_dashboard_main_menu_view.h"
@ -22,6 +24,7 @@
#include "ash/public/cpp/style/dark_light_mode_controller.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/color_palette_controller.h"
#include "ash/style/icon_button.h"
#include "ash/style/mojom/color_scheme.mojom-shared.h"
@ -39,6 +42,7 @@
#include "extensions/common/constants.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/views/controls/button/button.h"
@ -211,6 +215,31 @@ class GameDashboardContextTest : public GameDashboardTestBase {
ToolbarSnapLocation::kTopLeft);
}
// Verifies the Game Dashboard button is in the respective state for the given
// `test_api`. If `is_recording` is true, then the Game Dashboard button must
// be in the recording state, and the recording timer is running. Otherwise,
// it should be in the default state and the timer should not be running.
void VerifyGameDashboardButtonState(GameDashboardContextTestApi* test_api,
bool is_recording) {
EXPECT_EQ(is_recording, test_api->GetGameDashboardButton()->is_recording());
std::u16string expected_title;
if (is_recording) {
expected_title = l10n_util::GetStringFUTF16(
IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_RECORDING,
test_api->GetRecordingDuration());
} else {
expected_title = l10n_util::GetStringUTF16(
IDS_ASH_GAME_DASHBOARD_GAME_DASHBOARD_BUTTON_TITLE);
}
EXPECT_EQ(expected_title,
test_api->GetGameDashboardButtonTitle()->GetText());
}
void VerifyGameDashboardButtonState(bool is_recording) {
VerifyGameDashboardButtonState(test_api_.get(), is_recording);
}
// Starts recording `recording_window_test_api`'s window, and verifies its
// record game buttons are enabled and toggled on, while the record game
// buttons in `other_window_test_api` are disabled and toggled off.
@ -242,6 +271,12 @@ class GameDashboardContextTest : public GameDashboardTestBase {
EXPECT_FALSE(recording_window_timer.IsRunning());
EXPECT_FALSE(other_window_timer.IsRunning());
// Verify the game dashboard buttons are not in the recording state.
VerifyGameDashboardButtonState(recording_window_test_api,
/*is_recording=*/false);
VerifyGameDashboardButtonState(other_window_test_api,
/*is_recording=*/false);
// Activate the recording_window.
auto* recording_window =
recording_window_test_api->context()->game_window();
@ -260,6 +295,12 @@ class GameDashboardContextTest : public GameDashboardTestBase {
EXPECT_TRUE(recording_window_timer.IsRunning());
EXPECT_FALSE(other_window_timer.IsRunning());
// Verify the game dashboard button state.
VerifyGameDashboardButtonState(recording_window_test_api,
/*is_recording=*/true);
VerifyGameDashboardButtonState(other_window_test_api,
/*is_recording=*/false);
// Retrieve the record game buttons from both windows.
auto* recording_window_record_game_tile =
recording_window_test_api->GetMainMenuRecordGameTile();
@ -310,6 +351,12 @@ class GameDashboardContextTest : public GameDashboardTestBase {
EXPECT_FALSE(recording_window_timer.IsRunning());
EXPECT_FALSE(other_window_timer.IsRunning());
// Verify the game dashboard buttons are no longer in the recording state.
VerifyGameDashboardButtonState(recording_window_test_api,
/*is_recording=*/false);
VerifyGameDashboardButtonState(other_window_test_api,
/*is_recording=*/false);
// Close the toolbar and main menu in both windows.
for (auto* test_api : {recording_window_test_api, other_window_test_api}) {
wm::ActivateWindow(test_api->context()->game_window());
@ -750,6 +797,9 @@ TEST_P(GameTypeGameDashboardContextTest,
test_api_->OpenTheMainMenu();
// Verify the game dashboard button is initially not in the recording state.
VerifyGameDashboardButtonState(/*is_recording=*/false);
// Retrieve the record game tile from the main menu, and verify it's
// enabled and toggled off.
auto* main_menu_record_game_button = test_api_->GetMainMenuRecordGameTile();
@ -777,6 +827,9 @@ TEST_P(GameTypeGameDashboardContextTest,
EXPECT_FALSE(toolbar_record_game_button->GetEnabled());
EXPECT_FALSE(toolbar_record_game_button->toggled());
// Verify the game dashboard button is not in the recording state.
VerifyGameDashboardButtonState(/*is_recording=*/false);
// Stop video recording.
CaptureModeTestApi().StopVideoRecording();
EXPECT_FALSE(capture_mode_controller->is_recording_in_progress());
@ -786,6 +839,9 @@ TEST_P(GameTypeGameDashboardContextTest,
EXPECT_FALSE(main_menu_record_game_button->IsToggled());
EXPECT_TRUE(toolbar_record_game_button->GetEnabled());
EXPECT_FALSE(toolbar_record_game_button->toggled());
// Verify the game dashboard button is still in not in the recording state.
VerifyGameDashboardButtonState(/*is_recording=*/false);
}
// Verifies the toolbar opens and closes when the toolbar button in the main
@ -1206,6 +1262,7 @@ TEST_P(GameDashboardStartAndStopCaptureSessionTest, RecordGameFromMainMenu) {
test_api_->OpenTheMainMenu();
EXPECT_FALSE(capture_mode_controller->is_recording_in_progress());
EXPECT_FALSE(timer.IsRunning());
VerifyGameDashboardButtonState(/*is_recording=*/false);
if (should_start_from_main_menu_) {
// Retrieve the record game tile from the main menu.
@ -1229,6 +1286,7 @@ TEST_P(GameDashboardStartAndStopCaptureSessionTest, RecordGameFromMainMenu) {
EXPECT_TRUE(capture_mode_controller->is_recording_in_progress());
EXPECT_TRUE(timer.IsRunning());
VerifyGameDashboardButtonState(/*is_recording=*/true);
if (should_stop_from_main_menu_) {
// Stop the video recording from the main menu.
@ -1248,6 +1306,7 @@ TEST_P(GameDashboardStartAndStopCaptureSessionTest, RecordGameFromMainMenu) {
}
EXPECT_FALSE(capture_mode_controller->is_recording_in_progress());
EXPECT_FALSE(timer.IsRunning());
VerifyGameDashboardButtonState(/*is_recording=*/false);
WaitForCaptureFileToBeSaved();
}

@ -11,6 +11,7 @@
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/game_dashboard/game_dashboard_context.h"
#include "ash/game_dashboard/game_dashboard_utils.h"
#include "ash/game_dashboard/game_dashboard_widget.h"
#include "ash/public/cpp/app_types_util.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/shell.h"

@ -12,6 +12,7 @@
#include "ash/game_dashboard/game_dashboard_context.h"
#include "ash/game_dashboard/game_dashboard_controller.h"
#include "ash/game_dashboard/game_dashboard_utils.h"
#include "ash/game_dashboard/game_dashboard_widget.h"
#include "ash/public/cpp/app_types_util.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/public/cpp/window_properties.h"

@ -116,6 +116,8 @@ aggregate_vector_icons("ash_vector_icons") {
"focus_mode_lamp.icon",
"folder.icon",
"four_files.icon",
"gd_drop_down_arrow.icon",
"gd_drop_up_arrow.icon",
"gd_game_controls.icon",
"gd_help.icon",
"gd_record_game.icon",

@ -0,0 +1,10 @@
// 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.
CANVAS_DIMENSIONS, 20,
MOVE_TO, 6, 8,
LINE_TO, 10, 12,
LINE_TO, 14, 8,
H_LINE_TO, 6,
CLOSE

@ -0,0 +1,10 @@
// 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.
CANVAS_DIMENSIONS, 20,
MOVE_TO, 6, 12,
LINE_TO, 10, 8,
LINE_TO, 14, 12,
H_LINE_TO, 6,
CLOSE

@ -328,7 +328,6 @@ aggregate_vector_icons("chrome_vector_icons") {
"game_controls_done.icon",
"game_controls_dpad_keyboard.icon",
"game_controls_edit_pen.icon",
"game_controls_gamepad.icon",
"game_controls_single_button.icon",
"mouse_left_click_edit.icon",
"mouse_left_click_view.icon",

@ -8,13 +8,13 @@
#include "ash/app_list/app_list_util.h"
#include "ash/style/style_util.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ash/arc/input_overlay/arc_input_overlay_uma.h"
#include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h"
#include "chrome/browser/ash/arc/input_overlay/touch_injector.h"
#include "chrome/browser/ash/arc/input_overlay/util.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "chromeos/ui/vector_icons/vector_icons.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/color/color_id.h"
@ -159,8 +159,8 @@ void MenuEntryView::Init() {
SetImageModel(
views::Button::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(kGameControlsGamepadIcon, SK_ColorBLACK,
kMenuEntryIconSize));
ui::ImageModel::FromVectorIcon(chromeos::kGameDashboardGamepadIcon,
SK_ColorBLACK, kMenuEntryIconSize));
SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);

@ -40,6 +40,7 @@ aggregate_vector_icons("chromeos_ui_vector_icons") {
"filetype_video.icon",
"filetype_word.icon",
"float_window.icon",
"game_dashboard_gamepad.icon",
"keyboard_shortcuts.icon",
"notification_assistant.icon",
"notification_supervised_user.icon",