Commit 693f0f09 authored by Xiaoqian Dai's avatar Xiaoqian Dai Committed by Commit Bot

Capture mode: Put cursor setting logic together.

Fullscreen, window, region capture mode now all require change mouse
cursor. This CL put mouse cursor setting logic in a central place and
auto decide which cursor to use based on the current event location.
It unified the cursor behavior on the capture bar and button, and also
fixed a few bugs in the original implementation, like the mouse cursor
can be disappear after clamshell->tablet->clamshell transition.

Bug: 1142937
Change-Id: I982e3be0fd9c513f108a8dd7cfe4e8aea60905e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2537939
Commit-Queue: Xiaoqian Dai <xdai@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828564}
parent cbc00eae
This diff is collapsed.
......@@ -97,7 +97,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
private:
friend class CaptureModeSessionTestApi;
class ScopedCursorSetter;
class CursorSetter;
// Gets the bounds of current window selected for |kWindow| capture source.
gfx::Rect GetSelectedWindowBounds() const;
......@@ -122,10 +122,6 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
FineTunePosition GetFineTunePosition(const gfx::Point& location_in_root,
bool is_touch) const;
// Returns the expected cursor type.
ui::mojom::CursorType GetCursorType(FineTunePosition position,
bool is_event_on_capture_bar) const;
// Handles updating the select region UI.
void OnLocatedEventPressed(const gfx::Point& location_in_root,
bool is_touch,
......@@ -191,6 +187,16 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// Returns true if we are currently in video recording countdown animation.
bool IsInCountDownAnimation() const;
// Updates the current cursor depending on current |location_in_screen| and
// current capture type and source. |is_touch| is used when calculating fine
// tune position in region capture mode. We'll have a larger hit test region
// for the touch events than the mouse events.
void UpdateCursor(const gfx::Point& location_in_screen, bool is_touch);
// Returns true if we're using custom image capture icon when |type| is
// kImage or using custom video capture icon when |type| is kVideo.
bool IsUsingCustomCursor(CaptureModeType type) const;
CaptureModeController* const controller_;
// The current root window on which the capture session is active, which may
......@@ -247,7 +253,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
base::flat_set<std::unique_ptr<WindowDimmer>> root_window_dimmers_;
// The object to specify the cursor type.
std::unique_ptr<ScopedCursorSetter> cursor_setter_;
std::unique_ptr<CursorSetter> cursor_setter_;
// True when dragging is in progress.
bool is_drag_in_progress_ = false;
......
......@@ -23,6 +23,7 @@
#include "ash/system/status_area_widget.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "ash/wm/window_state.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
......@@ -109,6 +110,10 @@ class CaptureModeSessionTestApi {
return session_->magnifier_glass_;
}
bool IsUsingCustomCursor(CaptureModeType type) const {
return session_->IsUsingCustomCursor(type);
}
private:
const CaptureModeSession* const session_;
};
......@@ -1006,7 +1011,9 @@ TEST_F(CaptureModeTest, RegionCursorStates) {
EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type());
event_generator->ClickLeftButton();
ASSERT_EQ(CaptureModeSource::kWindow, controller->source());
EXPECT_FALSE(cursor_manager->IsCursorLocked());
// The event on the capture bar to change capture source will still keep the
// cursor locked.
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
// Tests that on changing back to region capture mode, the cursor becomes
......@@ -1029,6 +1036,143 @@ TEST_F(CaptureModeTest, RegionCursorStates) {
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
}
TEST_F(CaptureModeTest, FullscreenCursorStates) {
using ui::mojom::CursorType;
auto* cursor_manager = Shell::Get()->cursor_manager();
CursorType original_cursor_type = cursor_manager->GetCursor().type();
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(CursorType::kPointer, original_cursor_type);
auto* event_generator = GetEventGenerator();
CaptureModeController* controller = StartCaptureSession(
CaptureModeSource::kFullscreen, CaptureModeType::kImage);
EXPECT_EQ(controller->type(), CaptureModeType::kImage);
EXPECT_TRUE(cursor_manager->IsCursorLocked());
event_generator->MoveMouseTo(gfx::Point(175, 175));
EXPECT_TRUE(cursor_manager->IsCursorVisible());
// Use image capture icon as the mouse cursor icon in image capture mode.
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
CaptureModeSessionTestApi test_api(controller->capture_mode_session());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kImage));
// Move the mouse over to capture label widget won't change the cursor since
// it's a label not a label button.
event_generator->MoveMouseTo(
test_api.capture_label_widget()->GetWindowBoundsInScreen().CenterPoint());
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kImage));
// Use pointer mouse if the event is on the capture bar.
ClickOnView(GetVideoToggleButton(), event_generator);
EXPECT_EQ(controller->type(), CaptureModeType::kVideo);
EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type());
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
// Use video record icon as the mouse cursor icon in video recording mode.
event_generator->MoveMouseTo(gfx::Point(175, 175));
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kVideo));
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
// Enter tablet mode, the cursor should be hidden.
TabletModeControllerTestApi().EnterTabletMode();
EXPECT_FALSE(cursor_manager->IsCursorVisible());
EXPECT_TRUE(cursor_manager->IsCursorLocked());
// Exit tablet mode, the cursor should appear again.
TabletModeControllerTestApi().LeaveTabletMode();
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kVideo));
EXPECT_TRUE(cursor_manager->IsCursorLocked());
// Stop capture mode, the cursor should be restored to its original state.
controller->Stop();
EXPECT_FALSE(controller->IsActive());
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
}
TEST_F(CaptureModeTest, WindowCursorStates) {
using ui::mojom::CursorType;
std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(200, 200)));
auto* cursor_manager = Shell::Get()->cursor_manager();
CursorType original_cursor_type = cursor_manager->GetCursor().type();
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(CursorType::kPointer, original_cursor_type);
auto* event_generator = GetEventGenerator();
CaptureModeController* controller =
StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kImage);
EXPECT_EQ(controller->type(), CaptureModeType::kImage);
// If the mouse is above the window, use the image capture icon.
event_generator->MoveMouseTo(gfx::Point(150, 150));
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
CaptureModeSessionTestApi test_api(controller->capture_mode_session());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kImage));
// If the mouse is not above the window, use the original mouse cursor.
event_generator->MoveMouseTo(gfx::Point(300, 300));
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
// Use pointer mouse if the event is on the capture bar.
ClickOnView(GetVideoToggleButton(), event_generator);
EXPECT_EQ(controller->type(), CaptureModeType::kVideo);
EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type());
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
// Use video record icon as the mouse cursor icon in video recording mode.
event_generator->MoveMouseTo(gfx::Point(150, 150));
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kVideo));
// If the mouse is not above the window, use the original mouse cursor.
event_generator->MoveMouseTo(gfx::Point(300, 300));
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
// Move above the window again, the cursor should change back to the video
// record icon.
event_generator->MoveMouseTo(gfx::Point(150, 150));
EXPECT_TRUE(cursor_manager->IsCursorLocked());
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kVideo));
// Enter tablet mode, the cursor should be hidden.
TabletModeControllerTestApi().EnterTabletMode();
EXPECT_FALSE(cursor_manager->IsCursorVisible());
EXPECT_TRUE(cursor_manager->IsCursorLocked());
// Exit tablet mode, the cursor should appear again.
TabletModeControllerTestApi().LeaveTabletMode();
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kCustom, cursor_manager->GetCursor().type());
EXPECT_TRUE(test_api.IsUsingCustomCursor(CaptureModeType::kVideo));
EXPECT_TRUE(cursor_manager->IsCursorLocked());
// Stop capture mode, the cursor should be restored to its original state.
controller->Stop();
EXPECT_FALSE(controller->IsActive());
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
}
// Tests that in Region mode, cursor compositing is used instead of the system
// cursor when the cursor is being dragged.
TEST_F(CaptureModeTest, RegionDragCursorCompositing) {
......
......@@ -4,27 +4,17 @@
#include "ash/capture_mode/capture_window_observer.h"
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_session.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_finder.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ui/base/cursor/cursor_factory.h"
#include "ui/base/cursor/cursor_util.h"
#include "ui/display/screen.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
CaptureWindowObserver::CaptureWindowObserver(
CaptureModeSession* capture_mode_session,
CaptureModeType type)
: capture_type_(type),
original_cursor_(Shell::Get()->cursor_manager()->GetCursor()),
capture_mode_session_(capture_mode_session) {
CaptureModeSession* capture_mode_session)
: capture_mode_session_(capture_mode_session) {
Shell::Get()->activation_client()->AddObserver(this);
}
......@@ -32,12 +22,6 @@ CaptureWindowObserver::~CaptureWindowObserver() {
auto* shell = Shell::Get();
shell->activation_client()->RemoveObserver(this);
StopObserving();
::wm::CursorManager* cursor_manager = shell->cursor_manager();
if (is_cursor_locked_) {
cursor_manager->UnlockCursor();
cursor_manager->SetCursor(original_cursor_);
is_cursor_locked_ = false;
}
}
void CaptureWindowObserver::UpdateSelectedWindowAtPosition(
......@@ -45,11 +29,6 @@ void CaptureWindowObserver::UpdateSelectedWindowAtPosition(
UpdateSelectedWindowAtPosition(location_in_screen, /*ignore_windows=*/{});
}
void CaptureWindowObserver::OnCaptureTypeChanged(CaptureModeType new_type) {
capture_type_ = new_type;
UpdateMouseCursor();
}
void CaptureWindowObserver::OnWindowBoundsChanged(
aura::Window* window,
const gfx::Rect& old_bounds,
......@@ -114,19 +93,10 @@ void CaptureWindowObserver::UpdateSelectedWindowAtPosition(
}
// Stop observing the current selected window if there is one.
aura::Window* previous_selected_window = window_;
StopObserving();
if (window)
StartObserving(window);
RepaintCaptureRegion();
// Change mouse cursor depending on capture type and capture window if
// applicable.
const bool should_update_cursor =
!previous_selected_window != !window_ &&
Shell::Get()->cursor_manager()->IsCursorVisible();
if (should_update_cursor)
UpdateMouseCursor();
}
void CaptureWindowObserver::RepaintCaptureRegion() {
......@@ -134,45 +104,4 @@ void CaptureWindowObserver::RepaintCaptureRegion() {
layer->SchedulePaint(layer->bounds());
}
void CaptureWindowObserver::UpdateMouseCursor() {
::wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
if (window_) {
// Change the mouse cursor to a capture icon or a recording icon.
ui::Cursor cursor(ui::mojom::CursorType::kCustom);
const display::Display display =
display::Screen::GetScreen()->GetDisplayNearestWindow(window_);
const float device_scale_factor = display.device_scale_factor();
// TODO: Adjust the icon color after spec is updated.
const gfx::ImageSkia icon = gfx::CreateVectorIcon(
capture_type_ == CaptureModeType::kImage ? kCaptureModeImageIcon
: kCaptureModeVideoIcon,
SK_ColorBLACK);
SkBitmap bitmap = *icon.bitmap();
gfx::Point hotspot(bitmap.width() / 2, bitmap.height() / 2);
ui::ScaleAndRotateCursorBitmapAndHotpoint(
device_scale_factor, display.panel_rotation(), &bitmap, &hotspot);
auto* cursor_factory = ui::CursorFactory::GetInstance();
ui::PlatformCursor platform_cursor =
cursor_factory->CreateImageCursor(cursor.type(), bitmap, hotspot);
cursor.SetPlatformCursor(platform_cursor);
cursor.set_custom_bitmap(bitmap);
cursor.set_custom_hotspot(hotspot);
cursor_factory->UnrefImageCursor(platform_cursor);
// Unlock the cursor first so that it can be changed.
if (is_cursor_locked_)
cursor_manager->UnlockCursor();
cursor_manager->SetCursor(cursor);
cursor_manager->LockCursor();
is_cursor_locked_ = true;
} else {
// Revert back to its previous mouse cursor setting.
if (is_cursor_locked_) {
cursor_manager->UnlockCursor();
is_cursor_locked_ = false;
}
cursor_manager->SetCursor(original_cursor_);
}
}
} // namespace ash
......@@ -23,13 +23,11 @@ namespace ash {
class CaptureModeSession;
// Class to observe the current selected to-be-captured window and update the
// capture region if applicable.
// Class to observe the current selected to-be-captured window.
class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver,
public ::wm::ActivationChangeObserver {
public:
CaptureWindowObserver(CaptureModeSession* capture_mode_session,
CaptureModeType type);
explicit CaptureWindowObserver(CaptureModeSession* capture_mode_session);
CaptureWindowObserver(const CaptureWindowObserver&) = delete;
CaptureWindowObserver& operator=(const CaptureWindowObserver&) = delete;
......@@ -40,10 +38,6 @@ class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver,
// its bounds will be highlighted.
void UpdateSelectedWindowAtPosition(const gfx::Point& location_in_screen);
// Called when capture type changes. The mouse cursor image may update
// accordingly.
void OnCaptureTypeChanged(CaptureModeType new_type);
// aura::WindowObserver:
void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
......@@ -72,23 +66,12 @@ class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver,
// Repaints the window capture region.
void RepaintCaptureRegion();
// Updates the mouse cursor to change it to a capture or record icon when the
// mouse hovers over an eligible window.
void UpdateMouseCursor();
// Current observed window.
aura::Window* window_ = nullptr;
// Stores current mouse or touch location in screen coordinate.
gfx::Point location_in_screen_;
// Current capture type.
CaptureModeType capture_type_;
// True if the current cursor is locked by this.
bool is_cursor_locked_ = false;
const gfx::NativeCursor original_cursor_;
// Pointer to current capture session. Not nullptr during this lifecycle.
CaptureModeSession* const capture_mode_session_;
};
......
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