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
......@@ -22,6 +22,8 @@
#include "base/stl_util.h"
#include "cc/paint/paint_flags.h"
#include "ui/aura/window.h"
#include "ui/base/cursor/cursor_factory.h"
#include "ui/base/cursor/cursor_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_type.h"
#include "ui/compositor/paint_recorder.h"
......@@ -182,58 +184,129 @@ aura::Window* GetPreferredRootWindow() {
return Shell::GetRootWindowForDisplayId(display_id);
}
// In fullscreen or window capture mode, the mouse will change to a camera
// image icon if we're capturing image, or a video record image icon if we're
// capturing video.
ui::Cursor GetCursorForFullscreenOrWindowCapture(bool capture_image) {
ui::Cursor cursor(ui::mojom::CursorType::kCustom);
const display::Display display =
display::Screen::GetScreen()->GetDisplayNearestWindow(
GetPreferredRootWindow());
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_image ? 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);
return cursor;
}
// Returns the expected cursor type for |position| in region capture.
ui::mojom::CursorType GetCursorTypeForFineTunePosition(
FineTunePosition position) {
switch (position) {
case FineTunePosition::kTopLeft:
return ui::mojom::CursorType::kNorthWestResize;
case FineTunePosition::kBottomRight:
return ui::mojom::CursorType::kSouthEastResize;
case FineTunePosition::kTopCenter:
case FineTunePosition::kBottomCenter:
return ui::mojom::CursorType::kNorthSouthResize;
case FineTunePosition::kTopRight:
return ui::mojom::CursorType::kNorthEastResize;
case FineTunePosition::kBottomLeft:
return ui::mojom::CursorType::kSouthWestResize;
case FineTunePosition::kLeftCenter:
case FineTunePosition::kRightCenter:
return ui::mojom::CursorType::kEastWestResize;
case FineTunePosition::kCenter:
return ui::mojom::CursorType::kMove;
default:
return ui::mojom::CursorType::kCell;
}
}
} // namespace
class CaptureModeSession::ScopedCursorSetter {
class CaptureModeSession::CursorSetter {
public:
explicit ScopedCursorSetter(ui::mojom::CursorType cursor)
CursorSetter()
: cursor_manager_(Shell::Get()->cursor_manager()),
original_cursor_(cursor_manager_->GetCursor()),
original_cursor_visible_(cursor_manager_->IsCursorVisible()),
original_cursor_locked_(cursor_manager_->IsCursorLocked()) {
UpdateCursor(cursor);
}
original_cursor_locked_(cursor_manager_->IsCursorLocked()) {}
ScopedCursorSetter(const ScopedCursorSetter&) = delete;
ScopedCursorSetter& operator=(const ScopedCursorSetter&) = delete;
CursorSetter(const CursorSetter&) = delete;
CursorSetter& operator=(const CursorSetter&) = delete;
~ScopedCursorSetter() {
// Only unlock the cursor if it wasn't locked before.
~CursorSetter() { ResetCursor(); }
// Note that this will always make the cursor visible if it is not |kNone|.
void UpdateCursor(const ui::Cursor& cursor) {
if (original_cursor_locked_)
return;
const ui::mojom::CursorType current_cursor_type =
cursor_manager_->GetCursor().type();
const ui::mojom::CursorType new_cursor_type = cursor.type();
const CaptureModeType capture_type = CaptureModeController::Get()->type();
// For custom cursor, update the cursor if we need to change between image
// capture and video capture.
const bool is_cursor_changed =
current_cursor_type != new_cursor_type ||
(current_cursor_type == ui::mojom::CursorType::kCustom &&
custom_cursor_capture_type_ != capture_type);
const bool is_cursor_visibility_changed =
cursor_manager_->IsCursorVisible() !=
(new_cursor_type != ui::mojom::CursorType::kNone);
if (new_cursor_type == ui::mojom::CursorType::kCustom)
custom_cursor_capture_type_ = capture_type;
if (!is_cursor_changed && !is_cursor_visibility_changed)
return;
if (cursor_manager_->IsCursorLocked())
cursor_manager_->UnlockCursor();
cursor_manager_->SetCursor(original_cursor_);
if (original_cursor_visible_) {
cursor_manager_->ShowCursor();
} else {
if (new_cursor_type == ui::mojom::CursorType::kNone) {
cursor_manager_->HideCursor();
} else {
cursor_manager_->SetCursor(cursor);
cursor_manager_->ShowCursor();
}
cursor_manager_->LockCursor();
was_cursor_reset_to_original_ = false;
}
// Note that this will always make the cursor visible if it is not |kNone|.
void UpdateCursor(ui::mojom::CursorType cursor) {
// Resets to its original cursor.
void ResetCursor() {
// Only unlock the cursor if it wasn't locked before.
if (original_cursor_locked_)
return;
const bool currently_locked = cursor_manager_->IsCursorLocked();
if (cursor_manager_->GetCursor().type() == cursor &&
cursor_manager_->IsCursorVisible()) {
if (!currently_locked)
cursor_manager_->LockCursor();
// Only reset cursor if it hasn't been reset before.
if (was_cursor_reset_to_original_)
return;
}
if (currently_locked)
if (cursor_manager_->IsCursorLocked())
cursor_manager_->UnlockCursor();
if (cursor == ui::mojom::CursorType::kNone) {
cursor_manager_->HideCursor();
} else {
cursor_manager_->SetCursor(cursor);
cursor_manager_->SetCursor(original_cursor_);
if (original_cursor_visible_)
cursor_manager_->ShowCursor();
}
cursor_manager_->LockCursor();
else
cursor_manager_->HideCursor();
was_cursor_reset_to_original_ = true;
}
bool IsCursorVisible() const { return cursor_manager_->IsCursorVisible(); }
......@@ -242,9 +315,17 @@ class CaptureModeSession::ScopedCursorSetter {
if (original_cursor_locked_ || !IsCursorVisible())
return;
if (cursor_manager_->IsCursorLocked())
cursor_manager_->UnlockCursor();
cursor_manager_->HideCursor();
cursor_manager_->LockCursor();
was_cursor_reset_to_original_ = false;
}
bool IsUsingCustomCursor(CaptureModeType type) const {
return cursor_manager_->GetCursor().type() ==
ui::mojom::CursorType::kCustom &&
custom_cursor_capture_type_ == type;
}
private:
......@@ -254,12 +335,22 @@ class CaptureModeSession::ScopedCursorSetter {
// If the original cursor is already locked, don't make any changes to it.
const bool original_cursor_locked_;
// The current custom cursor type. kImage if we're using image capture icon as
// the mouse cursor, and kVideo if we're using video record icon as the mouse
// cursor.
CaptureModeType custom_cursor_capture_type_ = CaptureModeType::kImage;
// True if the cursor has reset back to its original cursor. It's to prevent
// Reset() from setting the cursor to |original_cursor_| more than once.
bool was_cursor_reset_to_original_ = true;
};
CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
: controller_(controller),
current_root_(GetPreferredRootWindow()),
magnifier_glass_(kMagnifierParams) {
magnifier_glass_(kMagnifierParams),
cursor_setter_(std::make_unique<CursorSetter>()) {
Shell::Get()->AddPreTargetHandler(this);
SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
......@@ -279,14 +370,10 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
UpdateCaptureLabelWidget();
RefreshStackingOrder(parent);
if (controller_->source() == CaptureModeSource::kRegion) {
cursor_setter_ =
std::make_unique<ScopedCursorSetter>(ui::mojom::CursorType::kCell);
}
if (controller_->source() == CaptureModeSource::kWindow) {
capture_window_observer_ =
std::make_unique<CaptureWindowObserver>(this, controller_->type());
}
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
if (controller_->source() == CaptureModeSource::kWindow)
capture_window_observer_ = std::make_unique<CaptureWindowObserver>(this);
UpdateRootWindowDimmers();
......@@ -321,35 +408,27 @@ aura::Window* CaptureModeSession::GetSelectedWindow() const {
void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) {
capture_source_changed_ = true;
if (new_source == CaptureModeSource::kWindow) {
capture_window_observer_ =
std::make_unique<CaptureWindowObserver>(this, controller_->type());
} else {
if (new_source == CaptureModeSource::kWindow)
capture_window_observer_ = std::make_unique<CaptureWindowObserver>(this);
else
capture_window_observer_.reset();
}
if (new_source == CaptureModeSource::kRegion) {
cursor_setter_ = std::make_unique<ScopedCursorSetter>(
capture_mode_bar_widget_->GetWindowBoundsInScreen().Contains(
previous_location_in_root_)
? ui::mojom::CursorType::kPointer
: ui::mojom::CursorType::kCell);
if (new_source == CaptureModeSource::kRegion)
num_capture_region_adjusted_ = 0;
} else {
cursor_setter_.reset();
}
capture_mode_bar_view_->OnCaptureSourceChanged(new_source);
UpdateDimensionsLabelWidget(/*is_resizing=*/false);
layer()->SchedulePaint(layer()->bounds());
UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
}
void CaptureModeSession::OnCaptureTypeChanged(CaptureModeType new_type) {
if (controller_->source() == CaptureModeSource::kWindow)
capture_window_observer_->OnCaptureTypeChanged(new_type);
capture_mode_bar_view_->OnCaptureTypeChanged(new_type);
UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
}
void CaptureModeSession::ReportSessionHistograms() {
......@@ -454,10 +533,14 @@ void CaptureModeSession::OnTouchEvent(ui::TouchEvent* event) {
void CaptureModeSession::OnTabletModeStarted() {
UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
}
void CaptureModeSession::OnTabletModeEnded() {
UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
}
void CaptureModeSession::OnWindowDestroying(aura::Window* window) {
......@@ -568,6 +651,12 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
aura::Window::ConvertPointToTarget(event_target, current_root_, &location);
wm::ConvertPointToScreen(event_target, &screen_location);
// Let the capture button handle any events it can handle first.
if (ShouldCaptureLabelHandleEvent(event_target)) {
UpdateCursor(screen_location, is_touch);
return;
}
const bool is_event_on_capture_bar =
capture_mode_bar_widget_->GetWindowBoundsInScreen().Contains(
screen_location);
......@@ -578,8 +667,10 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
const bool is_capture_window = capture_source == CaptureModeSource::kWindow;
if (is_capture_fullscreen || is_capture_window) {
// Do not handle any event located on the capture mode bar.
if (is_event_on_capture_bar)
if (is_event_on_capture_bar) {
UpdateCursor(screen_location, is_touch);
return;
}
event->SetHandled();
event->StopPropagation();
......@@ -592,6 +683,7 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
capture_window_observer_->UpdateSelectedWindowAtPosition(
screen_location);
}
UpdateCursor(screen_location, is_touch);
break;
}
case ui::ET_MOUSE_RELEASED:
......@@ -607,13 +699,6 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
DCHECK_EQ(CaptureModeSource::kRegion, capture_source);
DCHECK(cursor_setter_);
// Let the capture button handle any events it can handle first.
if (ShouldCaptureLabelHandleEvent(event_target)) {
cursor_setter_->UpdateCursor(ui::mojom::CursorType::kHand);
return;
}
// Allow events that are located on the capture mode bar to pass through so we
// can click the buttons.
if (!is_event_on_capture_bar) {
......@@ -640,15 +725,10 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
OnLocatedEventReleased(location, is_event_on_capture_bar);
break;
case ui::ET_MOUSE_MOVED:
if (cursor_setter_->IsCursorVisible()) {
cursor_setter_->UpdateCursor(GetCursorType(
GetFineTunePosition(location, is_touch), is_event_on_capture_bar));
}
break;
default:
break;
}
UpdateCursor(screen_location, is_touch);
}
FineTunePosition CaptureModeSession::GetFineTunePosition(
......@@ -683,36 +763,6 @@ FineTunePosition CaptureModeSession::GetFineTunePosition(
return FineTunePosition::kNone;
}
ui::mojom::CursorType CaptureModeSession::GetCursorType(
FineTunePosition position,
bool is_event_on_capture_bar) const {
if (is_event_on_capture_bar ||
controller_->source() != CaptureModeSource::kRegion) {
return ui::mojom::CursorType::kPointer;
}
switch (position) {
case FineTunePosition::kTopLeft:
return ui::mojom::CursorType::kNorthWestResize;
case FineTunePosition::kBottomRight:
return ui::mojom::CursorType::kSouthEastResize;
case FineTunePosition::kTopCenter:
case FineTunePosition::kBottomCenter:
return ui::mojom::CursorType::kNorthSouthResize;
case FineTunePosition::kTopRight:
return ui::mojom::CursorType::kNorthEastResize;
case FineTunePosition::kBottomLeft:
return ui::mojom::CursorType::kSouthWestResize;
case FineTunePosition::kLeftCenter:
case FineTunePosition::kRightCenter:
return ui::mojom::CursorType::kEastWestResize;
case FineTunePosition::kCenter:
return ui::mojom::CursorType::kMove;
default:
return ui::mojom::CursorType::kCell;
}
}
void CaptureModeSession::OnLocatedEventPressed(
const gfx::Point& location_in_root,
bool is_touch,
......@@ -821,9 +871,6 @@ void CaptureModeSession::OnLocatedEventReleased(
is_drag_in_progress_ = false;
Shell::Get()->UpdateCursorCompositingEnabled();
cursor_setter_->UpdateCursor(
GetCursorType(GetFineTunePosition(location_in_root, /*is_touch=*/false),
is_event_on_capture_bar));
// Do a repaint to show the affordance circles.
gfx::Rect damage_region = controller_->user_capture_region();
......@@ -935,11 +982,6 @@ void CaptureModeSession::MaybeShowMagnifierGlassAtPoint(
const gfx::Point& location_in_root) {
if (!capture_mode_util::IsCornerFineTunePosition(fine_tune_position_))
return;
DCHECK(cursor_setter_);
// Cursor will be reshown the next time we call UpdateCursor (which should be
// in OnLocatedEventReleased).
cursor_setter_->HideCursor();
magnifier_glass_.ShowFor(current_root_, location_in_root);
}
......@@ -1193,4 +1235,70 @@ bool CaptureModeSession::IsInCountDownAnimation() const {
return label_view->IsInCountDownAnimation();
}
void CaptureModeSession::UpdateCursor(const gfx::Point& location_in_screen,
bool is_touch) {
// Hide mouse cursor in tablet mode.
if (TabletModeController::Get()->InTabletMode()) {
cursor_setter_->HideCursor();
return;
}
// If the current mouse is on capture bar, use the pointer mouse cursor.
const bool is_event_on_capture_bar =
capture_mode_bar_widget_->GetWindowBoundsInScreen().Contains(
location_in_screen);
if (is_event_on_capture_bar) {
cursor_setter_->UpdateCursor(ui::mojom::CursorType::kPointer);
return;
}
// If the current mouse event is on capture label button, and capture label
// button can handle the event, show the hand mouse cursor.
const bool is_event_on_capture_button =
capture_label_widget_->GetWindowBoundsInScreen().Contains(
location_in_screen) &&
static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView())
->ShouldHandleEvent();
if (is_event_on_capture_button) {
cursor_setter_->UpdateCursor(ui::mojom::CursorType::kHand);
return;
}
const CaptureModeSource source = controller_->source();
if (source == CaptureModeSource::kWindow && !GetSelectedWindow()) {
// If we're in window capture mode and there is no select window at the
// moment, we should use the original mouse.
cursor_setter_->ResetCursor();
return;
}
if (source == CaptureModeSource::kFullscreen ||
source == CaptureModeSource::kWindow) {
// For fullscreen and other window capture cases, we should either use
// image capture icon or screen record icon as the mouse icon.
cursor_setter_->UpdateCursor(GetCursorForFullscreenOrWindowCapture(
controller_->type() == CaptureModeType::kImage));
return;
}
DCHECK_EQ(source, CaptureModeSource::kRegion);
if (fine_tune_position_ != FineTunePosition::kNone) {
// We're in fine tuning process.
if (capture_mode_util::IsCornerFineTunePosition(fine_tune_position_)) {
cursor_setter_->HideCursor();
} else {
cursor_setter_->UpdateCursor(
GetCursorTypeForFineTunePosition(fine_tune_position_));
}
} else {
// Otherwise update the cursor depending on the current cursor location.
cursor_setter_->UpdateCursor(GetCursorTypeForFineTunePosition(
GetFineTunePosition(location_in_screen, is_touch)));
}
}
bool CaptureModeSession::IsUsingCustomCursor(CaptureModeType type) const {
return cursor_setter_->IsUsingCustomCursor(type);
}
} // namespace ash
......@@ -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