Commit fd3bbf88 authored by Sammie Quon's avatar Sammie Quon Committed by Commit Bot

capture_mode: Multi-display behavior for capture mode.

Capture bar appears on the display with the mouse cursor. The other
displays are dimmed out. In fullscreen capture mode, users can move the
mouse to the other display to focus that display. In region capture
mode, moving the mouse to the other display does nothing, but pressing
the mouse on the display will focus that display.

Focusing a display will move the widgets and the layer owned by the
session to the display.

See videos in linked bug for demos.

Test: added test
Bug: 1133454
Change-Id: If237ca653736e94549d7005f09ecc9c8e94dc8c1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2440874
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#817148}
parent 522d21ad
...@@ -75,8 +75,8 @@ CaptureModeBarView::~CaptureModeBarView() = default; ...@@ -75,8 +75,8 @@ CaptureModeBarView::~CaptureModeBarView() = default;
gfx::Rect CaptureModeBarView::GetBounds(aura::Window* root) { gfx::Rect CaptureModeBarView::GetBounds(aura::Window* root) {
DCHECK(root); DCHECK(root);
auto bounds = root->GetBoundsInRootWindow(); auto bounds = root->GetBoundsInScreen();
const int y = bounds.height() - kDistanceFromScreenBottom - kBarSize.height(); const int y = bounds.bottom() - kDistanceFromScreenBottom - kBarSize.height();
bounds.ClampToCenteredSize(kBarSize); bounds.ClampToCenteredSize(kBarSize);
bounds.set_y(y); bounds.set_y(y);
return bounds; return bounds;
......
...@@ -52,7 +52,8 @@ class ASH_EXPORT CaptureModeBarView : public views::View, ...@@ -52,7 +52,8 @@ class ASH_EXPORT CaptureModeBarView : public views::View,
} }
CaptureModeCloseButton* close_button() const { return close_button_; } CaptureModeCloseButton* close_button() const { return close_button_; }
// Gets the ideal bounds of the bar of widget on the given |root| window. // Gets the ideal bounds in screen coordinates of the bar of widget on the
// given |root| window.
static gfx::Rect GetBounds(aura::Window* root); static gfx::Rect GetBounds(aura::Window* root);
// Called when either the capture mode source or type changes. // Called when either the capture mode source or type changes.
......
...@@ -256,10 +256,7 @@ void CaptureModeController::Start() { ...@@ -256,10 +256,7 @@ void CaptureModeController::Start() {
if (capture_mode_session_) if (capture_mode_session_)
return; return;
// TODO(afakhry): Use root window of the mouse cursor or the one for new capture_mode_session_ = std::make_unique<CaptureModeSession>(this);
// windows.
capture_mode_session_ =
std::make_unique<CaptureModeSession>(this, Shell::GetPrimaryRootWindow());
} }
void CaptureModeController::Stop() { void CaptureModeController::Stop() {
......
This diff is collapsed.
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
#include "ash/capture_mode/capture_mode_types.h" #include "ash/capture_mode/capture_mode_types.h"
#include "ash/magnifier/magnifier_glass.h" #include "ash/magnifier/magnifier_glass.h"
#include "ash/public/cpp/tablet_mode_observer.h" #include "ash/public/cpp/tablet_mode_observer.h"
#include "base/containers/flat_set.h"
#include "base/optional.h"
#include "ui/aura/window_observer.h"
#include "ui/compositor/layer_delegate.h" #include "ui/compositor/layer_delegate.h"
#include "ui/compositor/layer_owner.h" #include "ui/compositor/layer_owner.h"
#include "ui/events/event.h" #include "ui/events/event.h"
...@@ -27,6 +30,7 @@ namespace ash { ...@@ -27,6 +30,7 @@ namespace ash {
class CaptureModeBarView; class CaptureModeBarView;
class CaptureModeController; class CaptureModeController;
class CaptureWindowObserver; class CaptureWindowObserver;
class WindowDimmer;
// Encapsulates an active capture mode session (i.e. an instance of this class // Encapsulates an active capture mode session (i.e. an instance of this class
// lives as long as capture mode is active). It creates and owns the capture // lives as long as capture mode is active). It creates and owns the capture
...@@ -38,10 +42,11 @@ class CaptureWindowObserver; ...@@ -38,10 +42,11 @@ class CaptureWindowObserver;
class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
public ui::LayerDelegate, public ui::LayerDelegate,
public ui::EventHandler, public ui::EventHandler,
public TabletModeObserver { public TabletModeObserver,
public aura::WindowObserver {
public: public:
// Creates the bar widget on the given |root| window. // Creates the bar widget on a calculated root window.
CaptureModeSession(CaptureModeController* controller, aura::Window* root); explicit CaptureModeSession(CaptureModeController* controller);
CaptureModeSession(const CaptureModeSession&) = delete; CaptureModeSession(const CaptureModeSession&) = delete;
CaptureModeSession& operator=(const CaptureModeSession&) = delete; CaptureModeSession& operator=(const CaptureModeSession&) = delete;
~CaptureModeSession() override; ~CaptureModeSession() override;
...@@ -84,6 +89,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -84,6 +89,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
void OnTabletModeStarted() override; void OnTabletModeStarted() override;
void OnTabletModeEnded() override; void OnTabletModeEnded() override;
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override;
views::Widget* capture_label_widget_for_testing() const { views::Widget* capture_label_widget_for_testing() const {
return capture_label_widget_.get(); return capture_label_widget_.get();
} }
...@@ -114,7 +122,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -114,7 +122,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
void OnLocatedEvent(ui::LocatedEvent* event, bool is_touch); void OnLocatedEvent(ui::LocatedEvent* event, bool is_touch);
// Handles updating the select region UI. // Handles updating the select region UI.
void OnLocatedEventPressed(const gfx::Point& location_in_root, bool is_touch); void OnLocatedEventPressed(const gfx::Point& location_in_root,
bool is_touch,
bool is_event_on_capture_bar);
void OnLocatedEventDragged(const gfx::Point& location_in_root); void OnLocatedEventDragged(const gfx::Point& location_in_root);
void OnLocatedEventReleased(const gfx::Point& location_in_root); void OnLocatedEventReleased(const gfx::Point& location_in_root);
...@@ -159,6 +169,15 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -159,6 +169,15 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// child is visible. // child is visible.
bool ShouldCaptureLabelHandleEvent(aura::Window* event_target); bool ShouldCaptureLabelHandleEvent(aura::Window* event_target);
// Handles changing |root_window_| when the mouse cursor changes to another
// display, or if a display was removed. Moves the capture mode widgets to
// |new_root| depending on the capture mode source an whether it was a display
// removal.
void MaybeChangeRoot(aura::Window* new_root);
// Updates |root_window_dimmers_| to dim the correct root windows.
void UpdateRootWindowDimmers();
CaptureModeController* const controller_; CaptureModeController* const controller_;
// The current root window on which the capture session is active, which may // The current root window on which the capture session is active, which may
...@@ -202,12 +221,16 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -202,12 +221,16 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// underway. // underway.
std::vector<gfx::Point> anchor_points_; std::vector<gfx::Point> anchor_points_;
// Caches the old status of mouse warping before the session started to be // Caches the old status of mouse warping while dragging or resizing a
// restored at the end. // captured region.
bool old_mouse_warp_status_; base::Optional<bool> old_mouse_warp_status_;
// Observer to observe the current selected to-be-captured window. // Observer to observe the current selected to-be-captured window.
std::unique_ptr<CaptureWindowObserver> capture_window_observer_; std::unique_ptr<CaptureWindowObserver> capture_window_observer_;
// Contains the window dimmers which dim all the root windows except
// |current_root_|.
base::flat_set<std::unique_ptr<WindowDimmer>> root_window_dimmers_;
}; };
} // namespace ash } // namespace ash
......
...@@ -24,8 +24,10 @@ ...@@ -24,8 +24,10 @@
#include "ash/system/status_area_widget.h" #include "ash/system/status_area_widget.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
#include "ash/wm/window_state.h" #include "ash/wm/window_state.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "ui/events/keycodes/keyboard_codes_posix.h" #include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/geometry/vector2d.h"
...@@ -59,6 +61,16 @@ void SendKey(ui::KeyboardCode key_code, ...@@ -59,6 +61,16 @@ void SendKey(ui::KeyboardCode key_code,
event_generator->ReleaseKey(key_code, /*flags=*/0); event_generator->ReleaseKey(key_code, /*flags=*/0);
} }
// Moves the mouse and updates the cursor's display manually to imitate what a
// real mouse move event does in shell.
void MoveMouseToAndUpdateCursorDisplay(
const gfx::Point& point,
ui::test::EventGenerator* event_generator) {
Shell::Get()->cursor_manager()->SetDisplay(
display::Screen::GetScreen()->GetDisplayNearestPoint(point));
event_generator->MoveMouseTo(point);
}
} // namespace } // namespace
class CaptureModeTest : public AshTestBase { class CaptureModeTest : public AshTestBase {
...@@ -148,16 +160,22 @@ class CaptureModeTest : public AshTestBase { ...@@ -148,16 +160,22 @@ class CaptureModeTest : public AshTestBase {
return base::nullopt; return base::nullopt;
} }
// Start Capture Mode with source region and type image. CaptureModeController* StartCaptureSession(CaptureModeSource source,
CaptureModeController* StartImageRegionCapture() { CaptureModeType type) {
auto* controller = CaptureModeController::Get(); auto* controller = CaptureModeController::Get();
controller->SetSource(CaptureModeSource::kRegion); controller->SetSource(source);
controller->SetType(CaptureModeType::kImage); controller->SetType(type);
controller->Start(); controller->Start();
DCHECK(controller->IsActive()); DCHECK(controller->IsActive());
return controller; return controller;
} }
// Start Capture Mode with source region and type image.
CaptureModeController* StartImageRegionCapture() {
return StartCaptureSession(CaptureModeSource::kRegion,
CaptureModeType::kImage);
}
// Select a region by pressing and dragging the mouse. // Select a region by pressing and dragging the mouse.
void SelectRegion(const gfx::Rect& region, bool release_mouse = true) { void SelectRegion(const gfx::Rect& region, bool release_mouse = true) {
auto* controller = CaptureModeController::Get(); auto* controller = CaptureModeController::Get();
...@@ -698,4 +716,120 @@ TEST_F(CaptureModeTest, WindowCapture) { ...@@ -698,4 +716,120 @@ TEST_F(CaptureModeTest, WindowCapture) {
controller->Stop(); controller->Stop();
} }
// Tests that the capture bar is located on the root with the cursor when
// starting capture mode.
TEST_F(CaptureModeTest, MultiDisplayCaptureBarInitialLocation) {
UpdateDisplay("800x800,801+0-800x800");
auto* event_generator = GetEventGenerator();
MoveMouseToAndUpdateCursorDisplay(gfx::Point(1000, 500), event_generator);
auto* controller = StartImageRegionCapture();
EXPECT_TRUE(gfx::Rect(801, 0, 800, 800)
.Contains(controller->capture_mode_session()
->capture_mode_bar_view()
->GetBoundsInScreen()));
controller->Stop();
MoveMouseToAndUpdateCursorDisplay(gfx::Point(100, 500), event_generator);
StartImageRegionCapture();
EXPECT_TRUE(gfx::Rect(800, 800).Contains(controller->capture_mode_session()
->capture_mode_bar_view()
->GetBoundsInScreen()));
}
// Tests behavior of a capture mode session if the active display is removed.
TEST_F(CaptureModeTest, DisplayRemoval) {
UpdateDisplay("800x800,801+0-800x800");
// Start capture mode on the secondary display.
MoveMouseToAndUpdateCursorDisplay(gfx::Point(1000, 500), GetEventGenerator());
auto* controller = StartImageRegionCapture();
auto* session = controller->capture_mode_session();
EXPECT_TRUE(
gfx::Rect(801, 0, 800, 800)
.Contains(session->capture_mode_bar_view()->GetBoundsInScreen()));
ASSERT_EQ(Shell::GetAllRootWindows()[1], session->current_root());
// Remove secondary display.
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Spin the run loop so that we get a signal that the associated root window
// of the removed display is destroyed.
base::RunLoop().RunUntilIdle();
// Tests that the capture mode bar is now on the primary display.
EXPECT_TRUE(gfx::Rect(800, 800).Contains(
session->capture_mode_bar_view()->GetBoundsInScreen()));
ASSERT_EQ(Shell::GetAllRootWindows()[0], session->current_root());
}
// Tests that using fullscreen or window source, moving the mouse across
// displays will change the root window of the capture session.
TEST_F(CaptureModeTest, MultiDisplayFullscreenOrWindowSourceRootWindow) {
UpdateDisplay("800x800,801+0-800x800");
ASSERT_EQ(2u, Shell::GetAllRootWindows().size());
auto* event_generator = GetEventGenerator();
MoveMouseToAndUpdateCursorDisplay(gfx::Point(100, 500), event_generator);
for (auto source :
{CaptureModeSource::kFullscreen, CaptureModeSource::kWindow}) {
SCOPED_TRACE(source == CaptureModeSource::kFullscreen ? "Fullscreen source"
: "Window source");
auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen,
CaptureModeType::kImage);
auto* session = controller->capture_mode_session();
EXPECT_EQ(Shell::GetAllRootWindows()[0], session->current_root());
MoveMouseToAndUpdateCursorDisplay(gfx::Point(1000, 500), event_generator);
EXPECT_EQ(Shell::GetAllRootWindows()[1], session->current_root());
MoveMouseToAndUpdateCursorDisplay(gfx::Point(100, 500), event_generator);
EXPECT_EQ(Shell::GetAllRootWindows()[0], session->current_root());
controller->Stop();
}
}
// Tests that in region mode, moving the mouse across displays will not change
// the root window of the capture session, but clicking on a new display will.
TEST_F(CaptureModeTest, MultiDisplayRegionSourceRootWindow) {
UpdateDisplay("800x800,801+0-800x800");
ASSERT_EQ(2u, Shell::GetAllRootWindows().size());
auto* event_generator = GetEventGenerator();
MoveMouseToAndUpdateCursorDisplay(gfx::Point(100, 500), event_generator);
auto* controller = StartImageRegionCapture();
auto* session = controller->capture_mode_session();
EXPECT_EQ(Shell::GetAllRootWindows()[0], session->current_root());
// Tests that moving the mouse to the secondary display does not change the
// root.
MoveMouseToAndUpdateCursorDisplay(gfx::Point(1000, 500), event_generator);
EXPECT_EQ(Shell::GetAllRootWindows()[0], session->current_root());
// Tests that pressing the mouse changes the root. The capture bar stays on
// the primary display until the mouse is released.
event_generator->PressLeftButton();
EXPECT_EQ(Shell::GetAllRootWindows()[1], session->current_root());
EXPECT_TRUE(gfx::Rect(800, 800).Contains(controller->capture_mode_session()
->capture_mode_bar_view()
->GetBoundsInScreen()));
event_generator->ReleaseLeftButton();
EXPECT_EQ(Shell::GetAllRootWindows()[1], session->current_root());
EXPECT_TRUE(gfx::Rect(801, 0, 800, 800)
.Contains(controller->capture_mode_session()
->capture_mode_bar_view()
->GetBoundsInScreen()));
}
} // namespace ash } // namespace ash
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