Commit d322167b authored by Sammie Quon's avatar Sammie Quon Committed by Chromium LUCI CQ

capture_mode: Allow users to take snapshot if menu is left open.

Previously, the menu had capture so it would take some events from the
button. To use the bar, users would have to first click the bar to take
capture, and then click again to use a bar button. Doing so would also
close the menu.

This CL changes that by taking away capture from the menu on entering
capture mode. Since doing so will normally close the menu, this also
introduces a way for ash menus to ignore the mouse capture losses.

Bug: 1155223
Test: manual
Change-Id: I19d4ed01d874b4f21975f194ae96cd15af830f78
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2581027
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#842187}
parent a0da2cf9
...@@ -345,12 +345,17 @@ void CaptureModeController::Start(CaptureModeEntryType entry_type) { ...@@ -345,12 +345,17 @@ void CaptureModeController::Start(CaptureModeEntryType entry_type) {
kResetCaptureRegionDuration) { kResetCaptureRegionDuration) {
SetUserCaptureRegion(gfx::Rect(), /*by_user=*/false); SetUserCaptureRegion(gfx::Rect(), /*by_user=*/false);
} }
delegate_->OnSessionStateChanged(/*started=*/true);
capture_mode_session_ = std::make_unique<CaptureModeSession>(this); capture_mode_session_ = std::make_unique<CaptureModeSession>(this);
} }
void CaptureModeController::Stop() { void CaptureModeController::Stop() {
DCHECK(IsActive()); DCHECK(IsActive());
capture_mode_session_.reset(); capture_mode_session_.reset();
delegate_->OnSessionStateChanged(/*started=*/false);
} }
void CaptureModeController::SetUserCaptureRegion(const gfx::Rect& region, void CaptureModeController::SetUserCaptureRegion(const gfx::Rect& region,
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "cc/paint/paint_flags.h" #include "cc/paint/paint_flags.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/base/cursor/cursor_factory.h" #include "ui/base/cursor/cursor_factory.h"
#include "ui/base/cursor/cursor_util.h" #include "ui/base/cursor/cursor_util.h"
...@@ -445,12 +446,28 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller) ...@@ -445,12 +446,28 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
UpdateRootWindowDimmers(); UpdateRootWindowDimmers();
// A context menu may have input capture when entering a session. Remove
// capture from it, otherwise subsequent mouse events will cause it to close,
// and then we won't be able to take a screenshot of the menu. Store it so we
// can return capture to it when exiting the session.
auto* capture_client = aura::client::GetCaptureClient(current_root_);
input_capture_window_ = capture_client->GetCaptureWindow();
if (input_capture_window_) {
capture_client->ReleaseCapture(input_capture_window_);
input_capture_window_->AddObserver(this);
}
TabletModeController::Get()->AddObserver(this); TabletModeController::Get()->AddObserver(this);
current_root_->AddObserver(this); current_root_->AddObserver(this);
display::Screen::GetScreen()->AddObserver(this); display::Screen::GetScreen()->AddObserver(this);
} }
CaptureModeSession::~CaptureModeSession() { CaptureModeSession::~CaptureModeSession() {
if (input_capture_window_) {
input_capture_window_->RemoveObserver(this);
aura::client::GetCaptureClient(current_root_)
->SetCapture(input_capture_window_);
}
display::Screen::GetScreen()->RemoveObserver(this); display::Screen::GetScreen()->RemoveObserver(this);
current_root_->RemoveObserver(this); current_root_->RemoveObserver(this);
TabletModeController::Get()->RemoveObserver(this); TabletModeController::Get()->RemoveObserver(this);
...@@ -655,6 +672,11 @@ void CaptureModeSession::OnTabletModeEnded() { ...@@ -655,6 +672,11 @@ void CaptureModeSession::OnTabletModeEnded() {
} }
void CaptureModeSession::OnWindowDestroying(aura::Window* window) { void CaptureModeSession::OnWindowDestroying(aura::Window* window) {
if (window == input_capture_window_) {
input_capture_window_->RemoveObserver(this);
input_capture_window_ = nullptr;
return;
}
DCHECK_EQ(current_root_, window); DCHECK_EQ(current_root_, window);
MaybeChangeRoot(Shell::GetPrimaryRootWindow()); MaybeChangeRoot(Shell::GetPrimaryRootWindow());
} }
......
...@@ -305,6 +305,10 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -305,6 +305,10 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// The current focused fine tune position. This changes as user tabs while a // The current focused fine tune position. This changes as user tabs while a
// in capture region mode. // in capture region mode.
FineTunePosition focused_fine_tune_position_ = FineTunePosition::kNone; FineTunePosition focused_fine_tune_position_ = FineTunePosition::kNone;
// The window which had input capture prior to entering the session. It may be
// null if no such window existed.
aura::Window* input_capture_window_ = nullptr;
}; };
} // namespace ash } // namespace ash
......
...@@ -121,4 +121,6 @@ TestCaptureModeDelegate::LaunchRecordingService() { ...@@ -121,4 +121,6 @@ TestCaptureModeDelegate::LaunchRecordingService() {
void TestCaptureModeDelegate::BindAudioStreamFactory( void TestCaptureModeDelegate::BindAudioStreamFactory(
mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) {} mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) {}
void TestCaptureModeDelegate::OnSessionStateChanged(bool started) {}
} // namespace ash } // namespace ash
...@@ -41,6 +41,7 @@ class TestCaptureModeDelegate : public CaptureModeDelegate { ...@@ -41,6 +41,7 @@ class TestCaptureModeDelegate : public CaptureModeDelegate {
override; override;
void BindAudioStreamFactory( void BindAudioStreamFactory(
mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override; mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override;
void OnSessionStateChanged(bool started) override;
private: private:
std::unique_ptr<FakeRecordingService> fake_service_; std::unique_ptr<FakeRecordingService> fake_service_;
......
...@@ -85,6 +85,9 @@ class ASH_PUBLIC_EXPORT CaptureModeDelegate { ...@@ -85,6 +85,9 @@ class ASH_PUBLIC_EXPORT CaptureModeDelegate {
// Binds the given audio StreamFactory |receiver| to the audio service. // Binds the given audio StreamFactory |receiver| to the audio service.
virtual void BindAudioStreamFactory( virtual void BindAudioStreamFactory(
mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) = 0; mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) = 0;
// Called when a capture mode session starts or stops.
virtual void OnSessionStateChanged(bool started) = 0;
}; };
} // namespace ash } // namespace ash
......
...@@ -116,6 +116,14 @@ class ASH_EXPORT ShellTestApi { ...@@ -116,6 +116,14 @@ class ASH_EXPORT ShellTestApi {
// It returns nullptr when app-list is not shown. // It returns nullptr when app-list is not shown.
PaginationModel* GetAppListPaginationModel(); PaginationModel* GetAppListPaginationModel();
// Shows the context menu associated with the primary root window at point (1,
// 1).
void ShowContextMenu();
// Returns true if the context menu associated with the primary root window is
// shown.
bool IsContextMenuShown() const;
private: private:
Shell* shell_; // not owned Shell* shell_; // not owned
......
...@@ -252,4 +252,13 @@ PaginationModel* ShellTestApi::GetAppListPaginationModel() { ...@@ -252,4 +252,13 @@ PaginationModel* ShellTestApi::GetAppListPaginationModel() {
return view->GetAppsPaginationModel(); return view->GetAppsPaginationModel();
} }
void ShellTestApi::ShowContextMenu() {
Shell::GetPrimaryRootWindowController()->ShowContextMenu(
gfx::Point(1, 1), ui::MENU_SOURCE_MOUSE);
}
bool ShellTestApi::IsContextMenuShown() const {
return Shell::GetPrimaryRootWindowController()->IsContextMenuShown();
}
} // namespace ash } // namespace ash
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/capture_mode_test_api.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
class CaptureModeBrowserTest : public InProcessBrowserTest {
public:
CaptureModeBrowserTest() = default;
CaptureModeBrowserTest(const CaptureModeBrowserTest&) = delete;
CaptureModeBrowserTest& operator=(const CaptureModeBrowserTest&) = delete;
~CaptureModeBrowserTest() override = default;
// InProcessBrowserTest:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(ash::features::kCaptureMode);
InProcessBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest, ContextMenuStaysOpen) {
ash::ShellTestApi shell_test_api;
shell_test_api.ShowContextMenu();
ASSERT_TRUE(shell_test_api.IsContextMenuShown());
ash::CaptureModeTestApi().StartForWindow(/*for_video=*/false);
EXPECT_TRUE(shell_test_api.IsContextMenuShown());
}
...@@ -173,3 +173,7 @@ void ChromeCaptureModeDelegate::BindAudioStreamFactory( ...@@ -173,3 +173,7 @@ void ChromeCaptureModeDelegate::BindAudioStreamFactory(
mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) { mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) {
content::GetAudioService().BindStreamFactory(std::move(receiver)); content::GetAudioService().BindStreamFactory(std::move(receiver));
} }
void ChromeCaptureModeDelegate::OnSessionStateChanged(bool started) {
is_session_active_ = started;
}
...@@ -20,6 +20,8 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate { ...@@ -20,6 +20,8 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate {
static ChromeCaptureModeDelegate* Get(); static ChromeCaptureModeDelegate* Get();
bool is_session_active() const { return is_session_active_; }
// Sets |is_screen_capture_locked_| to the given |locked|, and interrupts any // Sets |is_screen_capture_locked_| to the given |locked|, and interrupts any
// on going video capture. // on going video capture.
void SetIsScreenCaptureLocked(bool locked); void SetIsScreenCaptureLocked(bool locked);
...@@ -47,6 +49,7 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate { ...@@ -47,6 +49,7 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate {
override; override;
void BindAudioStreamFactory( void BindAudioStreamFactory(
mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override; mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override;
void OnSessionStateChanged(bool started) override;
private: private:
// Used to temporarily disable capture mode in certain cases for which neither // Used to temporarily disable capture mode in certain cases for which neither
...@@ -60,6 +63,9 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate { ...@@ -60,6 +63,9 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate {
// locked. // locked.
// This is only non-null during recording. // This is only non-null during recording.
base::OnceClosure interrupt_video_recording_callback_; base::OnceClosure interrupt_video_recording_callback_;
// True when a capture mode session is currently active.
bool is_session_active_ = false;
}; };
#endif // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_ #endif // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_
...@@ -7,8 +7,8 @@ include_rules = [ ...@@ -7,8 +7,8 @@ include_rules = [
specific_include_rules = { specific_include_rules = {
"chrome_views_delegate_chromeos\.cc": [ "chrome_views_delegate_chromeos\.cc": [
"+ash/public/cpp/ash_features.h",
"+ash/shell.h", "+ash/shell.h",
"+ash/wm/window_state.h",
], ],
"qrcode_generator\.*": [ "qrcode_generator\.*": [
"+chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.h" "+chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.h"
......
...@@ -36,6 +36,7 @@ class ChromeViewsDelegate : public views::ViewsDelegate { ...@@ -36,6 +36,7 @@ class ChromeViewsDelegate : public views::ViewsDelegate {
#if BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(IS_CHROMEOS_ASH)
ProcessMenuAcceleratorResult ProcessAcceleratorWhileMenuShowing( ProcessMenuAcceleratorResult ProcessAcceleratorWhileMenuShowing(
const ui::Accelerator& accelerator) override; const ui::Accelerator& accelerator) override;
bool ShouldCloseMenuIfMouseCaptureLost() const override;
std::unique_ptr<views::NonClientFrameView> CreateDefaultNonClientFrameView( std::unique_ptr<views::NonClientFrameView> CreateDefaultNonClientFrameView(
views::Widget* widget) override; views::Widget* widget) override;
#endif #endif
......
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
#include "chrome/browser/ui/views/chrome_views_delegate.h" #include "chrome/browser/ui/views/chrome_views_delegate.h"
#include "ash/public/cpp/accelerators.h" #include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/task/current_thread.h" #include "base/task/current_thread.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/display/screen.h" #include "ui/display/screen.h"
...@@ -37,6 +39,12 @@ ChromeViewsDelegate::ProcessAcceleratorWhileMenuShowing( ...@@ -37,6 +39,12 @@ ChromeViewsDelegate::ProcessAcceleratorWhileMenuShowing(
return views::ViewsDelegate::ProcessMenuAcceleratorResult::LEAVE_MENU_OPEN; return views::ViewsDelegate::ProcessMenuAcceleratorResult::LEAVE_MENU_OPEN;
} }
bool ChromeViewsDelegate::ShouldCloseMenuIfMouseCaptureLost() const {
// Menu closes unless an ongoing screen capture session is underway.
return !(ash::features::IsCaptureModeEnabled() &&
ChromeCaptureModeDelegate::Get()->is_session_active());
}
std::unique_ptr<views::NonClientFrameView> std::unique_ptr<views::NonClientFrameView>
ChromeViewsDelegate::CreateDefaultNonClientFrameView(views::Widget* widget) { ChromeViewsDelegate::CreateDefaultNonClientFrameView(views::Widget* widget) {
return ash::Shell::Get()->CreateDefaultNonClientFrameView(widget); return ash::Shell::Get()->CreateDefaultNonClientFrameView(widget);
......
...@@ -2782,6 +2782,7 @@ if (!is_android) { ...@@ -2782,6 +2782,7 @@ if (!is_android) {
"../browser/ui/ash/accelerator_commands_browsertest.cc", "../browser/ui/ash/accelerator_commands_browsertest.cc",
"../browser/ui/ash/assistant/assistant_context_browsertest.cc", "../browser/ui/ash/assistant/assistant_context_browsertest.cc",
"../browser/ui/ash/back_gesture_browsertest.cc", "../browser/ui/ash/back_gesture_browsertest.cc",
"../browser/ui/ash/capture_mode_browsertest.cc",
"../browser/ui/ash/chrome_new_window_client_browsertest.cc", "../browser/ui/ash/chrome_new_window_client_browsertest.cc",
"../browser/ui/ash/chrome_screenshot_grabber_browsertest.cc", "../browser/ui/ash/chrome_screenshot_grabber_browsertest.cc",
"../browser/ui/ash/clipboard_history_browsertest.cc", "../browser/ui/ash/clipboard_history_browsertest.cc",
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "ui/views/controls/menu/menu_scroll_view_container.h" #include "ui/views/controls/menu/menu_scroll_view_container.h"
#include "ui/views/controls/menu/submenu_view.h" #include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/round_rect_painter.h" #include "ui/views/round_rect_painter.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_delegate.h"
...@@ -235,6 +236,10 @@ internal::RootView* MenuHost::CreateRootView() { ...@@ -235,6 +236,10 @@ internal::RootView* MenuHost::CreateRootView() {
void MenuHost::OnMouseCaptureLost() { void MenuHost::OnMouseCaptureLost() {
if (destroying_ || ignore_capture_lost_) if (destroying_ || ignore_capture_lost_)
return; return;
if (!ViewsDelegate::GetInstance()->ShouldCloseMenuIfMouseCaptureLost())
return;
MenuController* menu_controller = MenuController* menu_controller =
submenu_->GetMenuItem()->GetMenuController(); submenu_->GetMenuItem()->GetMenuController();
if (menu_controller && !menu_controller->drag_in_progress()) if (menu_controller && !menu_controller->drag_in_progress())
......
...@@ -73,6 +73,10 @@ ViewsDelegate::ProcessAcceleratorWhileMenuShowing( ...@@ -73,6 +73,10 @@ ViewsDelegate::ProcessAcceleratorWhileMenuShowing(
return ProcessMenuAcceleratorResult::LEAVE_MENU_OPEN; return ProcessMenuAcceleratorResult::LEAVE_MENU_OPEN;
} }
bool ViewsDelegate::ShouldCloseMenuIfMouseCaptureLost() const {
return true;
}
#if defined(OS_WIN) #if defined(OS_WIN)
HICON ViewsDelegate::GetDefaultWindowIcon() const { HICON ViewsDelegate::GetDefaultWindowIcon() const {
return nullptr; return nullptr;
......
...@@ -125,6 +125,10 @@ class VIEWS_EXPORT ViewsDelegate { ...@@ -125,6 +125,10 @@ class VIEWS_EXPORT ViewsDelegate {
virtual ProcessMenuAcceleratorResult ProcessAcceleratorWhileMenuShowing( virtual ProcessMenuAcceleratorResult ProcessAcceleratorWhileMenuShowing(
const ui::Accelerator& accelerator); const ui::Accelerator& accelerator);
// If a menu is showing and its window loses mouse capture, it will close if
// this returns true.
virtual bool ShouldCloseMenuIfMouseCaptureLost() const;
#if defined(OS_WIN) #if defined(OS_WIN)
// Retrieves the default window icon to use for windows if none is specified. // Retrieves the default window icon to use for windows if none is specified.
virtual HICON GetDefaultWindowIcon() const; virtual HICON GetDefaultWindowIcon() const;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment