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 @@ ...@@ -22,6 +22,8 @@
#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/window.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.h"
#include "ui/compositor/layer_type.h" #include "ui/compositor/layer_type.h"
#include "ui/compositor/paint_recorder.h" #include "ui/compositor/paint_recorder.h"
...@@ -182,58 +184,129 @@ aura::Window* GetPreferredRootWindow() { ...@@ -182,58 +184,129 @@ aura::Window* GetPreferredRootWindow() {
return Shell::GetRootWindowForDisplayId(display_id); 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 } // namespace
class CaptureModeSession::ScopedCursorSetter { class CaptureModeSession::CursorSetter {
public: public:
explicit ScopedCursorSetter(ui::mojom::CursorType cursor) CursorSetter()
: cursor_manager_(Shell::Get()->cursor_manager()), : cursor_manager_(Shell::Get()->cursor_manager()),
original_cursor_(cursor_manager_->GetCursor()), original_cursor_(cursor_manager_->GetCursor()),
original_cursor_visible_(cursor_manager_->IsCursorVisible()), original_cursor_visible_(cursor_manager_->IsCursorVisible()),
original_cursor_locked_(cursor_manager_->IsCursorLocked()) { original_cursor_locked_(cursor_manager_->IsCursorLocked()) {}
UpdateCursor(cursor);
}
ScopedCursorSetter(const ScopedCursorSetter&) = delete; CursorSetter(const CursorSetter&) = delete;
ScopedCursorSetter& operator=(const ScopedCursorSetter&) = delete; CursorSetter& operator=(const CursorSetter&) = delete;
~ScopedCursorSetter() { ~CursorSetter() { ResetCursor(); }
// Only unlock the cursor if it wasn't locked before.
// Note that this will always make the cursor visible if it is not |kNone|.
void UpdateCursor(const ui::Cursor& cursor) {
if (original_cursor_locked_) if (original_cursor_locked_)
return; 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_->UnlockCursor();
cursor_manager_->SetCursor(original_cursor_); if (new_cursor_type == ui::mojom::CursorType::kNone) {
if (original_cursor_visible_) {
cursor_manager_->ShowCursor();
} else {
cursor_manager_->HideCursor(); 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|. // Resets to its original cursor.
void UpdateCursor(ui::mojom::CursorType cursor) { void ResetCursor() {
// Only unlock the cursor if it wasn't locked before.
if (original_cursor_locked_) if (original_cursor_locked_)
return; return;
const bool currently_locked = cursor_manager_->IsCursorLocked(); // Only reset cursor if it hasn't been reset before.
if (was_cursor_reset_to_original_)
if (cursor_manager_->GetCursor().type() == cursor &&
cursor_manager_->IsCursorVisible()) {
if (!currently_locked)
cursor_manager_->LockCursor();
return; return;
}
if (currently_locked) if (cursor_manager_->IsCursorLocked())
cursor_manager_->UnlockCursor(); cursor_manager_->UnlockCursor();
if (cursor == ui::mojom::CursorType::kNone) { cursor_manager_->SetCursor(original_cursor_);
cursor_manager_->HideCursor(); if (original_cursor_visible_)
} else {
cursor_manager_->SetCursor(cursor);
cursor_manager_->ShowCursor(); cursor_manager_->ShowCursor();
} else
cursor_manager_->LockCursor(); cursor_manager_->HideCursor();
was_cursor_reset_to_original_ = true;
} }
bool IsCursorVisible() const { return cursor_manager_->IsCursorVisible(); } bool IsCursorVisible() const { return cursor_manager_->IsCursorVisible(); }
...@@ -242,9 +315,17 @@ class CaptureModeSession::ScopedCursorSetter { ...@@ -242,9 +315,17 @@ class CaptureModeSession::ScopedCursorSetter {
if (original_cursor_locked_ || !IsCursorVisible()) if (original_cursor_locked_ || !IsCursorVisible())
return; return;
if (cursor_manager_->IsCursorLocked())
cursor_manager_->UnlockCursor(); cursor_manager_->UnlockCursor();
cursor_manager_->HideCursor(); cursor_manager_->HideCursor();
cursor_manager_->LockCursor(); 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: private:
...@@ -254,12 +335,22 @@ class CaptureModeSession::ScopedCursorSetter { ...@@ -254,12 +335,22 @@ class CaptureModeSession::ScopedCursorSetter {
// If the original cursor is already locked, don't make any changes to it. // If the original cursor is already locked, don't make any changes to it.
const bool original_cursor_locked_; 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) CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
: controller_(controller), : controller_(controller),
current_root_(GetPreferredRootWindow()), current_root_(GetPreferredRootWindow()),
magnifier_glass_(kMagnifierParams) { magnifier_glass_(kMagnifierParams),
cursor_setter_(std::make_unique<CursorSetter>()) {
Shell::Get()->AddPreTargetHandler(this); Shell::Get()->AddPreTargetHandler(this);
SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED)); SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
...@@ -279,14 +370,10 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller) ...@@ -279,14 +370,10 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
UpdateCaptureLabelWidget(); UpdateCaptureLabelWidget();
RefreshStackingOrder(parent); RefreshStackingOrder(parent);
if (controller_->source() == CaptureModeSource::kRegion) { UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
cursor_setter_ = /*is_touch=*/false);
std::make_unique<ScopedCursorSetter>(ui::mojom::CursorType::kCell); if (controller_->source() == CaptureModeSource::kWindow)
} capture_window_observer_ = std::make_unique<CaptureWindowObserver>(this);
if (controller_->source() == CaptureModeSource::kWindow) {
capture_window_observer_ =
std::make_unique<CaptureWindowObserver>(this, controller_->type());
}
UpdateRootWindowDimmers(); UpdateRootWindowDimmers();
...@@ -321,35 +408,27 @@ aura::Window* CaptureModeSession::GetSelectedWindow() const { ...@@ -321,35 +408,27 @@ aura::Window* CaptureModeSession::GetSelectedWindow() const {
void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) { void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) {
capture_source_changed_ = true; capture_source_changed_ = true;
if (new_source == CaptureModeSource::kWindow) { if (new_source == CaptureModeSource::kWindow)
capture_window_observer_ = capture_window_observer_ = std::make_unique<CaptureWindowObserver>(this);
std::make_unique<CaptureWindowObserver>(this, controller_->type()); else
} else {
capture_window_observer_.reset(); capture_window_observer_.reset();
}
if (new_source == CaptureModeSource::kRegion) { 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);
num_capture_region_adjusted_ = 0; num_capture_region_adjusted_ = 0;
} else {
cursor_setter_.reset();
}
capture_mode_bar_view_->OnCaptureSourceChanged(new_source); capture_mode_bar_view_->OnCaptureSourceChanged(new_source);
UpdateDimensionsLabelWidget(/*is_resizing=*/false); UpdateDimensionsLabelWidget(/*is_resizing=*/false);
layer()->SchedulePaint(layer()->bounds()); layer()->SchedulePaint(layer()->bounds());
UpdateCaptureLabelWidget(); UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
} }
void CaptureModeSession::OnCaptureTypeChanged(CaptureModeType new_type) { void CaptureModeSession::OnCaptureTypeChanged(CaptureModeType new_type) {
if (controller_->source() == CaptureModeSource::kWindow)
capture_window_observer_->OnCaptureTypeChanged(new_type);
capture_mode_bar_view_->OnCaptureTypeChanged(new_type); capture_mode_bar_view_->OnCaptureTypeChanged(new_type);
UpdateCaptureLabelWidget(); UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
} }
void CaptureModeSession::ReportSessionHistograms() { void CaptureModeSession::ReportSessionHistograms() {
...@@ -454,10 +533,14 @@ void CaptureModeSession::OnTouchEvent(ui::TouchEvent* event) { ...@@ -454,10 +533,14 @@ void CaptureModeSession::OnTouchEvent(ui::TouchEvent* event) {
void CaptureModeSession::OnTabletModeStarted() { void CaptureModeSession::OnTabletModeStarted() {
UpdateCaptureLabelWidget(); UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
} }
void CaptureModeSession::OnTabletModeEnded() { void CaptureModeSession::OnTabletModeEnded() {
UpdateCaptureLabelWidget(); UpdateCaptureLabelWidget();
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
} }
void CaptureModeSession::OnWindowDestroying(aura::Window* window) { void CaptureModeSession::OnWindowDestroying(aura::Window* window) {
...@@ -568,6 +651,12 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, ...@@ -568,6 +651,12 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
aura::Window::ConvertPointToTarget(event_target, current_root_, &location); aura::Window::ConvertPointToTarget(event_target, current_root_, &location);
wm::ConvertPointToScreen(event_target, &screen_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 = const bool is_event_on_capture_bar =
capture_mode_bar_widget_->GetWindowBoundsInScreen().Contains( capture_mode_bar_widget_->GetWindowBoundsInScreen().Contains(
screen_location); screen_location);
...@@ -578,8 +667,10 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, ...@@ -578,8 +667,10 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
const bool is_capture_window = capture_source == CaptureModeSource::kWindow; const bool is_capture_window = capture_source == CaptureModeSource::kWindow;
if (is_capture_fullscreen || is_capture_window) { if (is_capture_fullscreen || is_capture_window) {
// Do not handle any event located on the capture mode bar. // 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; return;
}
event->SetHandled(); event->SetHandled();
event->StopPropagation(); event->StopPropagation();
...@@ -592,6 +683,7 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, ...@@ -592,6 +683,7 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
capture_window_observer_->UpdateSelectedWindowAtPosition( capture_window_observer_->UpdateSelectedWindowAtPosition(
screen_location); screen_location);
} }
UpdateCursor(screen_location, is_touch);
break; break;
} }
case ui::ET_MOUSE_RELEASED: case ui::ET_MOUSE_RELEASED:
...@@ -607,13 +699,6 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, ...@@ -607,13 +699,6 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
DCHECK_EQ(CaptureModeSource::kRegion, capture_source); DCHECK_EQ(CaptureModeSource::kRegion, capture_source);
DCHECK(cursor_setter_); 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 // Allow events that are located on the capture mode bar to pass through so we
// can click the buttons. // can click the buttons.
if (!is_event_on_capture_bar) { if (!is_event_on_capture_bar) {
...@@ -640,15 +725,10 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event, ...@@ -640,15 +725,10 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
OnLocatedEventReleased(location, is_event_on_capture_bar); OnLocatedEventReleased(location, is_event_on_capture_bar);
break; 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: default:
break; break;
} }
UpdateCursor(screen_location, is_touch);
} }
FineTunePosition CaptureModeSession::GetFineTunePosition( FineTunePosition CaptureModeSession::GetFineTunePosition(
...@@ -683,36 +763,6 @@ FineTunePosition CaptureModeSession::GetFineTunePosition( ...@@ -683,36 +763,6 @@ FineTunePosition CaptureModeSession::GetFineTunePosition(
return FineTunePosition::kNone; 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( void CaptureModeSession::OnLocatedEventPressed(
const gfx::Point& location_in_root, const gfx::Point& location_in_root,
bool is_touch, bool is_touch,
...@@ -821,9 +871,6 @@ void CaptureModeSession::OnLocatedEventReleased( ...@@ -821,9 +871,6 @@ void CaptureModeSession::OnLocatedEventReleased(
is_drag_in_progress_ = false; is_drag_in_progress_ = false;
Shell::Get()->UpdateCursorCompositingEnabled(); 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. // Do a repaint to show the affordance circles.
gfx::Rect damage_region = controller_->user_capture_region(); gfx::Rect damage_region = controller_->user_capture_region();
...@@ -935,11 +982,6 @@ void CaptureModeSession::MaybeShowMagnifierGlassAtPoint( ...@@ -935,11 +982,6 @@ void CaptureModeSession::MaybeShowMagnifierGlassAtPoint(
const gfx::Point& location_in_root) { const gfx::Point& location_in_root) {
if (!capture_mode_util::IsCornerFineTunePosition(fine_tune_position_)) if (!capture_mode_util::IsCornerFineTunePosition(fine_tune_position_))
return; 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); magnifier_glass_.ShowFor(current_root_, location_in_root);
} }
...@@ -1193,4 +1235,70 @@ bool CaptureModeSession::IsInCountDownAnimation() const { ...@@ -1193,4 +1235,70 @@ bool CaptureModeSession::IsInCountDownAnimation() const {
return label_view->IsInCountDownAnimation(); 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 } // namespace ash
...@@ -97,7 +97,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -97,7 +97,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
private: private:
friend class CaptureModeSessionTestApi; friend class CaptureModeSessionTestApi;
class ScopedCursorSetter; class CursorSetter;
// Gets the bounds of current window selected for |kWindow| capture source. // Gets the bounds of current window selected for |kWindow| capture source.
gfx::Rect GetSelectedWindowBounds() const; gfx::Rect GetSelectedWindowBounds() const;
...@@ -122,10 +122,6 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -122,10 +122,6 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
FineTunePosition GetFineTunePosition(const gfx::Point& location_in_root, FineTunePosition GetFineTunePosition(const gfx::Point& location_in_root,
bool is_touch) const; 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. // Handles updating the select region UI.
void OnLocatedEventPressed(const gfx::Point& location_in_root, void OnLocatedEventPressed(const gfx::Point& location_in_root,
bool is_touch, bool is_touch,
...@@ -191,6 +187,16 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -191,6 +187,16 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// Returns true if we are currently in video recording countdown animation. // Returns true if we are currently in video recording countdown animation.
bool IsInCountDownAnimation() const; 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_; 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
...@@ -247,7 +253,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -247,7 +253,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
base::flat_set<std::unique_ptr<WindowDimmer>> root_window_dimmers_; base::flat_set<std::unique_ptr<WindowDimmer>> root_window_dimmers_;
// The object to specify the cursor type. // 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. // True when dragging is in progress.
bool is_drag_in_progress_ = false; bool is_drag_in_progress_ = false;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#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/tablet_mode/tablet_mode_controller.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 "ash/wm/window_state.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/scoped_observation.h" #include "base/scoped_observation.h"
...@@ -109,6 +110,10 @@ class CaptureModeSessionTestApi { ...@@ -109,6 +110,10 @@ class CaptureModeSessionTestApi {
return session_->magnifier_glass_; return session_->magnifier_glass_;
} }
bool IsUsingCustomCursor(CaptureModeType type) const {
return session_->IsUsingCustomCursor(type);
}
private: private:
const CaptureModeSession* const session_; const CaptureModeSession* const session_;
}; };
...@@ -1006,7 +1011,9 @@ TEST_F(CaptureModeTest, RegionCursorStates) { ...@@ -1006,7 +1011,9 @@ TEST_F(CaptureModeTest, RegionCursorStates) {
EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type()); EXPECT_EQ(CursorType::kPointer, cursor_manager->GetCursor().type());
event_generator->ClickLeftButton(); event_generator->ClickLeftButton();
ASSERT_EQ(CaptureModeSource::kWindow, controller->source()); 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()); EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type());
// Tests that on changing back to region capture mode, the cursor becomes // Tests that on changing back to region capture mode, the cursor becomes
...@@ -1029,6 +1036,143 @@ TEST_F(CaptureModeTest, RegionCursorStates) { ...@@ -1029,6 +1036,143 @@ TEST_F(CaptureModeTest, RegionCursorStates) {
EXPECT_EQ(original_cursor_type, cursor_manager->GetCursor().type()); 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 // Tests that in Region mode, cursor compositing is used instead of the system
// cursor when the cursor is being dragged. // cursor when the cursor is being dragged.
TEST_F(CaptureModeTest, RegionDragCursorCompositing) { TEST_F(CaptureModeTest, RegionDragCursorCompositing) {
......
...@@ -4,27 +4,17 @@ ...@@ -4,27 +4,17 @@
#include "ash/capture_mode/capture_window_observer.h" #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/capture_mode/capture_mode_session.h"
#include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_finder.h" #include "ash/public/cpp/window_finder.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.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" #include "ui/wm/public/activation_client.h"
namespace ash { namespace ash {
CaptureWindowObserver::CaptureWindowObserver( CaptureWindowObserver::CaptureWindowObserver(
CaptureModeSession* capture_mode_session, CaptureModeSession* capture_mode_session)
CaptureModeType type) : capture_mode_session_(capture_mode_session) {
: capture_type_(type),
original_cursor_(Shell::Get()->cursor_manager()->GetCursor()),
capture_mode_session_(capture_mode_session) {
Shell::Get()->activation_client()->AddObserver(this); Shell::Get()->activation_client()->AddObserver(this);
} }
...@@ -32,12 +22,6 @@ CaptureWindowObserver::~CaptureWindowObserver() { ...@@ -32,12 +22,6 @@ CaptureWindowObserver::~CaptureWindowObserver() {
auto* shell = Shell::Get(); auto* shell = Shell::Get();
shell->activation_client()->RemoveObserver(this); shell->activation_client()->RemoveObserver(this);
StopObserving(); 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( void CaptureWindowObserver::UpdateSelectedWindowAtPosition(
...@@ -45,11 +29,6 @@ void CaptureWindowObserver::UpdateSelectedWindowAtPosition( ...@@ -45,11 +29,6 @@ void CaptureWindowObserver::UpdateSelectedWindowAtPosition(
UpdateSelectedWindowAtPosition(location_in_screen, /*ignore_windows=*/{}); UpdateSelectedWindowAtPosition(location_in_screen, /*ignore_windows=*/{});
} }
void CaptureWindowObserver::OnCaptureTypeChanged(CaptureModeType new_type) {
capture_type_ = new_type;
UpdateMouseCursor();
}
void CaptureWindowObserver::OnWindowBoundsChanged( void CaptureWindowObserver::OnWindowBoundsChanged(
aura::Window* window, aura::Window* window,
const gfx::Rect& old_bounds, const gfx::Rect& old_bounds,
...@@ -114,19 +93,10 @@ void CaptureWindowObserver::UpdateSelectedWindowAtPosition( ...@@ -114,19 +93,10 @@ void CaptureWindowObserver::UpdateSelectedWindowAtPosition(
} }
// Stop observing the current selected window if there is one. // Stop observing the current selected window if there is one.
aura::Window* previous_selected_window = window_;
StopObserving(); StopObserving();
if (window) if (window)
StartObserving(window); StartObserving(window);
RepaintCaptureRegion(); 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() { void CaptureWindowObserver::RepaintCaptureRegion() {
...@@ -134,45 +104,4 @@ void CaptureWindowObserver::RepaintCaptureRegion() { ...@@ -134,45 +104,4 @@ void CaptureWindowObserver::RepaintCaptureRegion() {
layer->SchedulePaint(layer->bounds()); 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 } // namespace ash
...@@ -23,13 +23,11 @@ namespace ash { ...@@ -23,13 +23,11 @@ namespace ash {
class CaptureModeSession; class CaptureModeSession;
// Class to observe the current selected to-be-captured window and update the // Class to observe the current selected to-be-captured window.
// capture region if applicable.
class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver, class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver,
public ::wm::ActivationChangeObserver { public ::wm::ActivationChangeObserver {
public: public:
CaptureWindowObserver(CaptureModeSession* capture_mode_session, explicit CaptureWindowObserver(CaptureModeSession* capture_mode_session);
CaptureModeType type);
CaptureWindowObserver(const CaptureWindowObserver&) = delete; CaptureWindowObserver(const CaptureWindowObserver&) = delete;
CaptureWindowObserver& operator=(const CaptureWindowObserver&) = delete; CaptureWindowObserver& operator=(const CaptureWindowObserver&) = delete;
...@@ -40,10 +38,6 @@ class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver, ...@@ -40,10 +38,6 @@ class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver,
// its bounds will be highlighted. // its bounds will be highlighted.
void UpdateSelectedWindowAtPosition(const gfx::Point& location_in_screen); 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: // aura::WindowObserver:
void OnWindowBoundsChanged(aura::Window* window, void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds, const gfx::Rect& old_bounds,
...@@ -72,23 +66,12 @@ class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver, ...@@ -72,23 +66,12 @@ class ASH_EXPORT CaptureWindowObserver : public aura::WindowObserver,
// Repaints the window capture region. // Repaints the window capture region.
void RepaintCaptureRegion(); 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. // Current observed window.
aura::Window* window_ = nullptr; aura::Window* window_ = nullptr;
// Stores current mouse or touch location in screen coordinate. // Stores current mouse or touch location in screen coordinate.
gfx::Point location_in_screen_; 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. // Pointer to current capture session. Not nullptr during this lifecycle.
CaptureModeSession* const capture_mode_session_; 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