0

capture_game: Building up game capture bar

This cl builds up the capture bar for the video recording requests from
the game dashboard. It only includes the basic functionals currently,
more details like the focus navigation will be covered in some
followed up cls.

Bug: b/280465234
Change-Id: I695d57298ff1da78e9b7017a7975301a419a9e59
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4513357
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Commit-Queue: Min Chen <minch@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1143453}
This commit is contained in:
minch
2023-05-12 19:35:14 +00:00
committed by Chromium LUCI CQ
parent 2643b19e60
commit 2671bec61b
16 changed files with 234 additions and 71 deletions

@ -358,6 +358,8 @@ component("ash") {
"capture_mode/capture_window_observer.h",
"capture_mode/folder_selection_dialog_controller.cc",
"capture_mode/folder_selection_dialog_controller.h",
"capture_mode/game_capture_bar_view.cc",
"capture_mode/game_capture_bar_view.h",
"capture_mode/key_combo_view.cc",
"capture_mode/key_combo_view.h",
"capture_mode/key_item_view.cc",

@ -5763,6 +5763,9 @@ Here are some things you can try to get started.
<message name="IDS_ASH_SCREEN_CAPTURE_SAVE_TO_LINUX_FILES" desc="The label of the menu item button for selecting the root of the Linux Files path to store the captured images and videos.">
Linux files
</message>
<message name="IDS_ASH_GAME_CAPTURE_START_RECORDING_BUTTON" desc="The label of the start recording button inside the game capture bar.">
Start recording
</message>
<!-- Snap Group -->
<message name="IDS_ASH_SNAP_GROUP_CLICK_TO_LOCK_WINDOWS" desc="Click to lock the windows.">

@ -0,0 +1 @@
06533ee3def0b1205425c681c0de96d690536e64

@ -39,6 +39,8 @@ constexpr int kBorderRadius = 20;
} // namespace
CaptureModeBarView::~CaptureModeBarView() = default;
CaptureModeTypeView* CaptureModeBarView::capture_type_view() const {
return nullptr;
}
@ -47,6 +49,10 @@ CaptureModeSourceView* CaptureModeBarView::capture_source_view() const {
return nullptr;
}
PillButton* CaptureModeBarView::GetStartRecordingButton() const {
return nullptr;
}
void CaptureModeBarView::OnCaptureSourceChanged(CaptureModeSource new_source) {
return;
}
@ -85,8 +91,6 @@ CaptureModeBarView::CaptureModeBarView()
shadow_->SetRoundedCornerRadius(kBorderRadius);
}
CaptureModeBarView::~CaptureModeBarView() = default;
void CaptureModeBarView::AppendCommonElements() {
settings_button_ = AddChildView(std::make_unique<IconButton>(
base::BindRepeating(&CaptureModeBarView::OnSettingsButtonPressed,

@ -15,6 +15,7 @@ namespace ash {
class CaptureModeSourceView;
class CaptureModeTypeView;
class PillButton;
class IconButton;
class SystemShadow;
@ -25,13 +26,18 @@ class ASH_EXPORT CaptureModeBarView : public views::View {
public:
METADATA_HEADER(CaptureModeBarView);
~CaptureModeBarView() override;
IconButton* settings_button() const { return settings_button_; }
IconButton* close_button() const { return close_button_; }
// TODO(minch): Renames these two functions to GetCaptureTypeView and
// GetCaptureSourceView and updates all the clients.
// These functions may return `nullptr` depending on the actual type of the
// bar.
virtual CaptureModeTypeView* capture_type_view() const;
virtual CaptureModeSourceView* capture_source_view() const;
virtual PillButton* GetStartRecordingButton() const;
// Called when either the capture mode source or type changes.
virtual void OnCaptureSourceChanged(CaptureModeSource new_source);
@ -42,7 +48,6 @@ class ASH_EXPORT CaptureModeBarView : public views::View {
protected:
CaptureModeBarView();
~CaptureModeBarView() override;
// Adds the common elements of different capture bars to the bar view.
void AppendCommonElements();

@ -12,6 +12,8 @@
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_metrics.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/game_capture_bar_view.h"
#include "ash/capture_mode/normal_capture_bar_view.h"
#include "ash/constants/ash_features.h"
#include "ash/projector/projector_controller_impl.h"
#include "ash/shelf/shelf.h"
@ -30,6 +32,9 @@ namespace {
// adjusted based on the current active behavior.
constexpr gfx::Size kFullBarSize{376, 64};
// Size of the game capture bar.
constexpr gfx::Size kGameCaptureBarSize{260, 64};
// Distance from the bottom of the capture bar to the bottom of the display, top
// of the hotseat or top of the shelf depending on the shelf alignment or
// hotseat visibility.
@ -184,6 +189,14 @@ class GameDashboardBehavior : public CaptureModeBehavior {
bool ShouldGifBeSupported() const override { return false; }
bool ShouldShowUserNudge() const override { return false; }
bool ShouldAutoSelectFirstCamera() const override { return true; }
std::unique_ptr<CaptureModeBarView> CreateCaptureModeBarView() override {
return std::make_unique<GameCaptureBarView>();
}
protected:
int GetCaptureBarWidth() const override {
return kGameCaptureBarSize.width();
}
};
} // namespace
@ -303,6 +316,11 @@ const char* CaptureModeBehavior::GetClientMetricComponent() const {
return "";
}
std::unique_ptr<CaptureModeBarView>
CaptureModeBehavior::CreateCaptureModeBarView() {
return std::make_unique<NormalCaptureBarView>(this);
}
gfx::Rect CaptureModeBehavior::GetCaptureBarBounds(aura::Window* root) const {
DCHECK(root);

@ -23,6 +23,8 @@ class Rect;
namespace ash {
class CaptureModeBarView;
// Contains the cached capture mode configurations that will be used for
// configurations restoration when initiating the corresponding capture mode
// session.
@ -89,6 +91,10 @@ class CaptureModeBehavior {
// indicate the histogram is for a projector-initiated capture mode session.
virtual const char* GetClientMetricComponent() const;
// Creates the capture mode bar view, which might look different depending on
// the actual type of the behavior.
virtual std::unique_ptr<CaptureModeBarView> CreateCaptureModeBarView();
// Gets the bounds in screen coordinates of the capture bar in the given
// `root` window. The returned bounds of the bar will vary depending on the
// actual type of the behavior.

@ -81,4 +81,21 @@ TEST_F(GameDashboardCaptureModeTest, GameDashboardBehavior) {
EXPECT_TRUE(active_behavior->ShouldAutoSelectFirstCamera());
}
TEST_F(GameDashboardCaptureModeTest, CaptureBar) {
StartGameCaptureModeSession();
views::Widget* bar_widget = GetCaptureModeBarWidget();
ASSERT_TRUE(bar_widget);
// Checks that the game capture bar only includes the start recording button,
// settings button and close button.
EXPECT_TRUE(GetStartRecordingButton());
EXPECT_FALSE(GetImageToggleButton());
EXPECT_FALSE(GetVideoToggleButton());
EXPECT_FALSE(GetFullscreenToggleButton());
EXPECT_FALSE(GetRegionToggleButton());
EXPECT_FALSE(GetWindowToggleButton());
EXPECT_TRUE(GetSettingsButton());
EXPECT_TRUE(GetCloseButton());
}
} // namespace ash

@ -4,6 +4,7 @@
#include "ash/capture_mode/capture_mode_session.h"
#include <memory>
#include <tuple>
#include <utility>
@ -499,7 +500,7 @@ void CaptureModeSession::Initialize() {
parent, active_behavior_->GetCaptureBarBounds(current_root_),
"CaptureModeBarWidget"));
capture_mode_bar_view_ = capture_mode_bar_widget_->SetContentsView(
std::make_unique<NormalCaptureBarView>(active_behavior_));
active_behavior_->CreateCaptureModeBarView());
capture_mode_bar_widget_->GetNativeWindow()->SetTitle(
l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_A11Y_TITLE));
capture_mode_bar_widget_->Show();
@ -532,11 +533,10 @@ void CaptureModeSession::Initialize() {
// `OnCaptureTypeChanged` may trigger `ShowCaptureToast` which depends on the
// capture bar.
// Also please note we should call `OnCaptureTypeChanged` in
// `CaptureModeTypeView` instead of `CaptureModeSession`, since this is during
// `CaptureModeBarView` instead of `CaptureModeSession`, since this is during
// the initialization of the capture session, the type change is not triggered
// by the user.
capture_mode_bar_view_->capture_type_view()->OnCaptureTypeChanged(
controller_->type());
capture_mode_bar_view_->OnCaptureTypeChanged(controller_->type());
MaybeCreateUserNudge();
if (active_behavior_->ShouldAutoSelectFirstCamera()) {

@ -584,8 +584,10 @@ bool CaptureModeSessionFocusCycler::OnSpacePressed() {
// currently has focus and we are already in region mode, as we still want to
// create a default region in this case.
CaptureModeBarView* bar_view = session_->capture_mode_bar_view_;
if (view->GetView() ==
bar_view->capture_source_view()->region_toggle_button() &&
if (const CaptureModeSourceView* capture_source_view =
bar_view->capture_source_view();
capture_source_view &&
view->GetView() == capture_source_view->region_toggle_button() &&
CaptureModeController::Get()->source() == CaptureModeSource::kRegion) {
return false;
}
@ -804,6 +806,9 @@ CaptureModeSessionFocusCycler::GetGroupItems(FocusGroup group) const {
CaptureModeBarView* bar_view = session_->capture_mode_bar_view_;
CaptureModeTypeView* type_view = bar_view->capture_type_view();
CaptureModeSourceView* source_view = bar_view->capture_source_view();
if (!type_view || !source_view) {
break;
}
for (auto* button :
{type_view->image_toggle_button(), type_view->video_toggle_button(),
source_view->fullscreen_toggle_button(),

@ -9,8 +9,10 @@
#include "ash/accessibility/autoclick/autoclick_controller.h"
#include "ash/capture_mode/capture_mode_bar_view.h"
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_session.h"
#include "ash/capture_mode/capture_mode_session_test_api.h"
#include "ash/capture_mode/capture_mode_source_view.h"
#include "ash/capture_mode/capture_mode_type_view.h"
#include "ash/capture_mode/test_capture_mode_delegate.h"
#include "ash/public/cpp/capture_mode/capture_mode_test_api.h"
#include "ash/public/cpp/projector/projector_controller.h"
@ -19,6 +21,7 @@
#include "ash/public/cpp/projector/speech_recognition_availability.h"
#include "ash/shell.h"
#include "ash/style/icon_button.h"
#include "ash/style/pill_button.h"
#include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "base/files/file_path.h"
@ -173,32 +176,18 @@ void ClickOrTapView(const views::View* view,
ClickOnView(view, event_generator);
}
views::Widget* GetCaptureModeBarWidget() {
auto* session = CaptureModeController::Get()->capture_mode_session();
DCHECK(session);
return session->capture_mode_bar_widget();
}
CaptureModeBarView* GetCaptureModeBarView() {
auto* session = CaptureModeController::Get()->capture_mode_session();
DCHECK(session);
return CaptureModeSessionTestApi(session).GetCaptureModeBarView();
}
IconButton* GetSettingsButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->settings_button();
}
IconButton* GetFullscreenToggleButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()
->capture_source_view()
->fullscreen_toggle_button();
}
IconButton* GetRegionToggleButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->capture_source_view()->region_toggle_button();
}
UserNudgeController* GetUserNudgeController() {
auto* session = CaptureModeController::Get()->capture_mode_session();
DCHECK(session);
@ -292,6 +281,62 @@ gfx::Image ReadAndDecodeImageFile(const base::FilePath& image_path) {
return image;
}
IconButton* GetImageToggleButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto* capture_type_view = GetCaptureModeBarView()->capture_type_view();
return capture_type_view ? capture_type_view->image_toggle_button() : nullptr;
}
IconButton* GetVideoToggleButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto* capture_type_view = GetCaptureModeBarView()->capture_type_view();
return capture_type_view ? capture_type_view->video_toggle_button() : nullptr;
}
IconButton* GetFullscreenToggleButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto* capture_source_view = GetCaptureModeBarView()->capture_source_view();
return capture_source_view ? capture_source_view->fullscreen_toggle_button()
: nullptr;
}
IconButton* GetRegionToggleButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto* capture_source_view = GetCaptureModeBarView()->capture_source_view();
return capture_source_view ? capture_source_view->region_toggle_button()
: nullptr;
}
IconButton* GetWindowToggleButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto* capture_source_view = GetCaptureModeBarView()->capture_source_view();
return capture_source_view ? capture_source_view->window_toggle_button()
: nullptr;
}
PillButton* GetStartRecordingButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->GetStartRecordingButton();
}
IconButton* GetSettingsButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->settings_button();
}
IconButton* GetCloseButton() {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->close_button();
}
// -----------------------------------------------------------------------------
// ProjectorCaptureModeIntegrationHelper:

@ -40,6 +40,7 @@ class View;
namespace ash {
class PillButton;
class IconButton;
class CaptureModeController;
class CaptureModeBarView;
@ -99,14 +100,10 @@ void ClickOrTapView(const views::View* view,
bool in_table_mode,
ui::test::EventGenerator* event_generator);
views::Widget* GetCaptureModeBarWidget();
CaptureModeBarView* GetCaptureModeBarView();
IconButton* GetSettingsButton();
IconButton* GetFullscreenToggleButton();
IconButton* GetRegionToggleButton();
UserNudgeController* GetUserNudgeController();
bool IsLayerStackedRightBelow(ui::Layer* layer, ui::Layer* sibling);
@ -136,6 +133,16 @@ void PressAndReleaseKeyOnVK(ui::test::EventGenerator* event_generator,
// bitmap was successfully read from disk or an empty gfx::Image otherwise.
gfx::Image ReadAndDecodeImageFile(const base::FilePath& image_path);
// Gets the buttons inside the capture bar view.
IconButton* GetImageToggleButton();
IconButton* GetVideoToggleButton();
IconButton* GetFullscreenToggleButton();
IconButton* GetRegionToggleButton();
IconButton* GetWindowToggleButton();
PillButton* GetStartRecordingButton();
IconButton* GetSettingsButton();
IconButton* GetCloseButton();
// Defines a helper class to allow setting up and testing the Projector feature
// in multiple test fixtures. Note that this helper initializes the Projector-
// related features in its constructor, so test fixtures that use this should

@ -235,12 +235,6 @@ class CaptureModeTest : public AshTestBase {
demo_tools_enabled_ = features::AreCaptureModeDemoToolsEnabled();
}
views::Widget* GetCaptureModeBarWidget() const {
auto* session = CaptureModeController::Get()->capture_mode_session();
DCHECK(session);
return session->capture_mode_bar_widget();
}
views::Widget* GetCaptureModeLabelWidget() const {
auto* session = CaptureModeController::Get()->capture_mode_session();
DCHECK(session);
@ -271,32 +265,6 @@ class CaptureModeTest : public AshTestBase {
return CaptureModeSessionTestApi(session).IsAllUisVisible();
}
IconButton* GetImageToggleButton() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->capture_type_view()->image_toggle_button();
}
IconButton* GetVideoToggleButton() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->capture_type_view()->video_toggle_button();
}
IconButton* GetWindowToggleButton() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()
->capture_source_view()
->window_toggle_button();
}
IconButton* GetCloseButton() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
return GetCaptureModeBarView()->close_button();
}
aura::Window* GetDimensionsLabelWindow() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
@ -4724,6 +4692,7 @@ TEST_F(CaptureModeTest, CaptureModeDefaultBehavior) {
EXPECT_TRUE(GetFullscreenToggleButton());
EXPECT_TRUE(GetRegionToggleButton());
EXPECT_TRUE(GetWindowToggleButton());
EXPECT_FALSE(GetStartRecordingButton());
EXPECT_TRUE(GetSettingsButton());
EXPECT_TRUE(GetCloseButton());
@ -5811,6 +5780,7 @@ TEST_P(ProjectorCaptureModeIntegrationTests, ProjectorBehavior) {
EXPECT_TRUE(GetFullscreenToggleButton());
EXPECT_TRUE(GetRegionToggleButton());
EXPECT_TRUE(GetWindowToggleButton());
EXPECT_FALSE(GetStartRecordingButton());
EXPECT_TRUE(GetSettingsButton());
EXPECT_TRUE(GetCloseButton());

@ -0,0 +1,38 @@
// 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/capture_mode/game_capture_bar_view.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/pill_button.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
namespace ash {
GameCaptureBarView::GameCaptureBarView()
: start_recording_button_(AddChildView(std::make_unique<PillButton>(
base::BindRepeating(&GameCaptureBarView::StartRecording,
base::Unretained(this)),
l10n_util::GetStringUTF16(
IDS_ASH_GAME_CAPTURE_START_RECORDING_BUTTON),
PillButton::kPrimaryWithoutIcon))) {
AppendCommonElements();
}
GameCaptureBarView::~GameCaptureBarView() = default;
PillButton* GameCaptureBarView::GetStartRecordingButton() const {
return start_recording_button_;
}
void GameCaptureBarView::StartRecording() {
// TODO(b/282193685): Start recording once the selected window is set and not
// changeable after game capture session starts.
}
BEGIN_METADATA(GameCaptureBarView, CaptureModeBarView)
END_METADATA
} // namespace ash

@ -0,0 +1,41 @@
// 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_CAPTURE_MODE_GAME_CAPTURE_BAR_VIEW_H_
#define ASH_CAPTURE_MODE_GAME_CAPTURE_BAR_VIEW_H_
#include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_bar_view.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
namespace ash {
class PillButton;
// A view that acts as the content view of the capture mode bar widget of a
// game capture session. The bar only includes a start recording button, the
// settings and close buttons.
class ASH_EXPORT GameCaptureBarView : public CaptureModeBarView {
public:
METADATA_HEADER(GameCaptureBarView);
GameCaptureBarView();
GameCaptureBarView(const GameCaptureBarView&) = delete;
GameCaptureBarView& operator=(const GameCaptureBarView&) = delete;
~GameCaptureBarView() override;
// CaptureModeBarView:
PillButton* GetStartRecordingButton() const override;
private:
// Called when clicking on the start recording button inside the bar.
void StartRecording();
raw_ptr<PillButton, ExperimentalAsh> start_recording_button_;
};
} // namespace ash
#endif // ASH_CAPTURE_MODE_GAME_CAPTURE_BAR_VIEW_H_

@ -17,10 +17,11 @@ class CaptureModeBehavior;
class CaptureModeSourceView;
class CaptureModeTypeView;
// A view that acts as the content view of the capture mode bar widget. It has
// a set of buttons to toggle between image and video capture, and another set
// of buttons to toggle between fullscreen, region, and window capture sources.
// It also contains a settings button. The structure looks like this:
// A view that acts as the content view of the capture mode bar widget for a
// normal capture session. It has a set of buttons to toggle between image and
// video capture, and another set of buttons to toggle between fullscreen,
// region, and window capture sources. It also contains a settings button. The
// structure looks like this:
//
// +---------------------------------------------------------------+
// | +----------------+ | | |