Commit f2842608 authored by chinsenj's avatar chinsenj Committed by Commit Bot

capture_mode: Add a magnifying glass to Capture Mode capture region.

When a user fine tunes their capture region, it's useful to have more
precision. This CL adds a magnifying glass to the capture region to
address this.

Test: manual + added test.
Fixed: 1133751
Change-Id: I24f9652d1f97b2fbf29da6b771c6cc465b36d4dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2453772
Commit-Queue: Jeremy Chinsen <chinsenj@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarSammie Quon <sammiequon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816601}
parent 9d3adbef
...@@ -279,6 +279,8 @@ component("ash") { ...@@ -279,6 +279,8 @@ component("ash") {
"capture_mode/capture_mode_type_view.cc", "capture_mode/capture_mode_type_view.cc",
"capture_mode/capture_mode_type_view.h", "capture_mode/capture_mode_type_view.h",
"capture_mode/capture_mode_types.h", "capture_mode/capture_mode_types.h",
"capture_mode/capture_mode_util.cc",
"capture_mode/capture_mode_util.h",
"capture_mode/capture_window_observer.cc", "capture_mode/capture_window_observer.cc",
"capture_mode/capture_window_observer.h", "capture_mode/capture_window_observer.h",
"capture_mode/stop_recording_button_tray.cc", "capture_mode/stop_recording_button_tray.cc",
......
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
#include "ash/capture_mode/capture_label_view.h" #include "ash/capture_mode/capture_label_view.h"
#include "ash/capture_mode/capture_mode_bar_view.h" #include "ash/capture_mode/capture_mode_bar_view.h"
#include "ash/capture_mode/capture_mode_controller.h" #include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_util.h"
#include "ash/capture_mode/capture_window_observer.h" #include "ash/capture_mode/capture_window_observer.h"
#include "ash/display/mouse_cursor_event_filter.h" #include "ash/display/mouse_cursor_event_filter.h"
#include "ash/magnifier/magnifier_glass.h"
#include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/shell_window_ids.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h" #include "ash/shell.h"
...@@ -49,6 +51,23 @@ constexpr int kAffordanceCircleRadiusDp = 5; ...@@ -49,6 +51,23 @@ constexpr int kAffordanceCircleRadiusDp = 5;
// The hit radius of the drag affordance circles touch events. // The hit radius of the drag affordance circles touch events.
constexpr int kAffordanceCircleTouchHitRadiusDp = 16; constexpr int kAffordanceCircleTouchHitRadiusDp = 16;
// Capture region magnifier parameters.
constexpr MagnifierGlass::Params kMagnifierParams{
/*scale=*/2.f,
/*radius=*/60,
/*border_size=*/2,
/*border_outline_thickness=*/0,
/*border_color=*/SK_ColorWHITE,
/*border_outline_color=*/SK_ColorTRANSPARENT,
/*bottom_shadow=*/
gfx::ShadowValue(gfx::Vector2d(0, 1),
2,
SkColorSetARGB(0x4C, 0x00, 0x00, 0x00)),
/*top_shadow=*/
gfx::ShadowValue(gfx::Vector2d(0, 1),
3,
SkColorSetARGB(0x26, 0x00, 0x00, 0x00))};
constexpr int kSizeLabelBorderRadius = 4; constexpr int kSizeLabelBorderRadius = 4;
constexpr int kSizeLabelHorizontalPadding = 8; constexpr int kSizeLabelHorizontalPadding = 8;
...@@ -92,34 +111,6 @@ aura::Window* GetParentContainer(aura::Window* root) { ...@@ -92,34 +111,6 @@ aura::Window* GetParentContainer(aura::Window* root) {
return root->GetChildById(kShellWindowId_OverlayContainer); return root->GetChildById(kShellWindowId_OverlayContainer);
} }
// Retrieves the point on the |rect| associated with |position|.
gfx::Point GetLocationForPosition(const gfx::Rect& rect,
FineTunePosition position) {
switch (position) {
case FineTunePosition::kTopLeft:
return rect.origin();
case FineTunePosition::kTopCenter:
return rect.top_center();
case FineTunePosition::kTopRight:
return rect.top_right();
case FineTunePosition::kRightCenter:
return rect.right_center();
case FineTunePosition::kBottomRight:
return rect.bottom_right();
case FineTunePosition::kBottomCenter:
return rect.bottom_center();
case FineTunePosition::kBottomLeft:
return rect.bottom_left();
case FineTunePosition::kLeftCenter:
return rect.left_center();
default:
break;
}
NOTREACHED();
return gfx::Point();
}
// Returns the smallest rect that contains all of |points|. // Returns the smallest rect that contains all of |points|.
gfx::Rect GetRectEnclosingPoints(const std::vector<gfx::Point>& points) { gfx::Rect GetRectEnclosingPoints(const std::vector<gfx::Point>& points) {
DCHECK_GE(points.size(), 2u); DCHECK_GE(points.size(), 2u);
...@@ -160,6 +151,7 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller, ...@@ -160,6 +151,7 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller,
: controller_(controller), : controller_(controller),
current_root_(root), current_root_(root),
capture_mode_bar_view_(new CaptureModeBarView()), capture_mode_bar_view_(new CaptureModeBarView()),
magnifier_glass_(kMagnifierParams),
old_mouse_warp_status_(SetMouseWarpEnabled(controller_->source() != old_mouse_warp_status_(SetMouseWarpEnabled(controller_->source() !=
CaptureModeSource::kRegion)) { CaptureModeSource::kRegion)) {
Shell::Get()->AddPreTargetHandler(this); Shell::Get()->AddPreTargetHandler(this);
...@@ -443,7 +435,7 @@ void CaptureModeSession::OnLocatedEventPressed( ...@@ -443,7 +435,7 @@ void CaptureModeSession::OnLocatedEventPressed(
// Calculate the position and anchor points of the current pressed event. // Calculate the position and anchor points of the current pressed event.
fine_tune_position_ = FineTunePosition::kNone; fine_tune_position_ = FineTunePosition::kNone;
// In the case of overlapping affordances, prioritize the bottomm right // In the case of overlapping affordances, prioritize the bottom right
// corner, then the rest of the corners, then the edges. // corner, then the rest of the corners, then the edges.
static const std::vector<FineTunePosition> drag_positions = { static const std::vector<FineTunePosition> drag_positions = {
FineTunePosition::kBottomRight, FineTunePosition::kBottomLeft, FineTunePosition::kBottomRight, FineTunePosition::kBottomLeft,
...@@ -456,12 +448,14 @@ void CaptureModeSession::OnLocatedEventPressed( ...@@ -456,12 +448,14 @@ void CaptureModeSession::OnLocatedEventPressed(
const int hit_radius_squared = hit_radius * hit_radius; const int hit_radius_squared = hit_radius * hit_radius;
for (FineTunePosition position : drag_positions) { for (FineTunePosition position : drag_positions) {
const gfx::Point position_location = const gfx::Point position_location =
GetLocationForPosition(controller_->user_capture_region(), position); capture_mode_util::GetLocationForFineTunePosition(
controller_->user_capture_region(), position);
// If |location_in_root| is within |hit_radius| of |position_location| for // If |location_in_root| is within |hit_radius| of |position_location| for
// both x and y, then |position| is the current pressed down affordance. // both x and y, then |position| is the current pressed down affordance.
if ((position_location - location_in_root).LengthSquared() <= if ((position_location - location_in_root).LengthSquared() <=
hit_radius_squared) { hit_radius_squared) {
fine_tune_position_ = position; fine_tune_position_ = position;
MaybeShowMagnifierGlassAtPoint(position_location);
break; break;
} }
} }
...@@ -516,6 +510,7 @@ void CaptureModeSession::OnLocatedEventDragged( ...@@ -516,6 +510,7 @@ void CaptureModeSession::OnLocatedEventDragged(
DCHECK(!points.empty()); DCHECK(!points.empty());
points.push_back(location_in_root); points.push_back(location_in_root);
UpdateCaptureRegion(GetRectEnclosingPoints(points), /*is_resizing=*/true); UpdateCaptureRegion(GetRectEnclosingPoints(points), /*is_resizing=*/true);
MaybeShowMagnifierGlassAtPoint(location_in_root);
} }
void CaptureModeSession::OnLocatedEventReleased( void CaptureModeSession::OnLocatedEventReleased(
...@@ -531,6 +526,7 @@ void CaptureModeSession::OnLocatedEventReleased( ...@@ -531,6 +526,7 @@ void CaptureModeSession::OnLocatedEventReleased(
layer()->SchedulePaint(damage_region); layer()->SchedulePaint(damage_region);
UpdateDimensionsLabelWidget(/*is_resizing=*/false); UpdateDimensionsLabelWidget(/*is_resizing=*/false);
CloseMagnifierGlass();
if (!is_selecting_region_) if (!is_selecting_region_)
return; return;
...@@ -624,6 +620,20 @@ void CaptureModeSession::UpdateDimensionsLabelBounds() { ...@@ -624,6 +620,20 @@ void CaptureModeSession::UpdateDimensionsLabelBounds() {
dimensions_label_widget_->SetBounds(bounds); dimensions_label_widget_->SetBounds(bounds);
} }
void CaptureModeSession::MaybeShowMagnifierGlassAtPoint(
const gfx::Point& location_in_root) {
if (!capture_mode_util::IsCornerFineTunePosition(fine_tune_position_))
return;
// TODO(richui): Hide cursor here.
magnifier_glass_.ShowFor(current_root_, location_in_root);
}
void CaptureModeSession::CloseMagnifierGlass() {
magnifier_glass_.Close();
// TODO(richui): Show cursor here.
}
std::vector<gfx::Point> CaptureModeSession::GetAnchorPointsForPosition( std::vector<gfx::Point> CaptureModeSession::GetAnchorPointsForPosition(
FineTunePosition position) { FineTunePosition position) {
std::vector<gfx::Point> anchor_points; std::vector<gfx::Point> anchor_points;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_types.h" #include "ash/capture_mode/capture_mode_types.h"
#include "ash/magnifier/magnifier_glass.h"
#include "ash/public/cpp/tablet_mode_observer.h" #include "ash/public/cpp/tablet_mode_observer.h"
#include "ui/compositor/layer_delegate.h" #include "ui/compositor/layer_delegate.h"
#include "ui/compositor/layer_owner.h" #include "ui/compositor/layer_owner.h"
...@@ -56,9 +57,6 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -56,9 +57,6 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
CaptureModeBarView* capture_mode_bar_view() const { CaptureModeBarView* capture_mode_bar_view() const {
return capture_mode_bar_view_; return capture_mode_bar_view_;
} }
views::Widget* dimensions_label_widget() {
return dimensions_label_widget_.get();
}
bool is_selecting_region() const { return is_selecting_region_; } bool is_selecting_region() const { return is_selecting_region_; }
// Gets the current window selected for |kWindow| capture source. Returns // Gets the current window selected for |kWindow| capture source. Returns
...@@ -89,6 +87,12 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -89,6 +87,12 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
views::Widget* capture_label_widget_for_testing() const { views::Widget* capture_label_widget_for_testing() const {
return capture_label_widget_.get(); return capture_label_widget_.get();
} }
views::Widget* dimensions_label_widget_for_testing() const {
return dimensions_label_widget_.get();
}
const MagnifierGlass& magnifier_glass_for_testing() const {
return magnifier_glass_;
}
private: private:
// Gets the bounds of current window selected for |kWindow| capture source. // Gets the bounds of current window selected for |kWindow| capture source.
...@@ -130,6 +134,14 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -130,6 +134,14 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// exist. // exist.
void UpdateDimensionsLabelBounds(); void UpdateDimensionsLabelBounds();
// If |fine_tune_position_| is not a corner, do nothing. Otherwise show
// |magnifier_glass_| at |location_in_root| in the current root window and
// hide the cursor.
void MaybeShowMagnifierGlassAtPoint(const gfx::Point& location_in_root);
// Closes |magnifier_glass_| and shows the cursor.
void CloseMagnifierGlass();
// Retrieves the anchor points on the current selected region associated with // Retrieves the anchor points on the current selected region associated with
// |position|. The anchor points are described as the points that do not // |position|. The anchor points are described as the points that do not
// change when resizing the capture region while dragging one of the drag // change when resizing the capture region while dragging one of the drag
...@@ -169,6 +181,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -169,6 +181,9 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// timer. // timer.
std::unique_ptr<views::Widget> capture_label_widget_; std::unique_ptr<views::Widget> capture_label_widget_;
// Magnifier glass used during a region capture session.
MagnifierGlass magnifier_glass_;
// Stores the data needed to select a region during a region capture session. // Stores the data needed to select a region during a region capture session.
// This variable indicates if the user is currently selecting a region to // This variable indicates if the user is currently selecting a region to
// capture, it will be true when the first mouse/touch presses down and will // capture, it will be true when the first mouse/touch presses down and will
......
...@@ -13,9 +13,11 @@ ...@@ -13,9 +13,11 @@
#include "ash/capture_mode/capture_mode_toggle_button.h" #include "ash/capture_mode/capture_mode_toggle_button.h"
#include "ash/capture_mode/capture_mode_type_view.h" #include "ash/capture_mode/capture_mode_type_view.h"
#include "ash/capture_mode/capture_mode_types.h" #include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/capture_mode_util.h"
#include "ash/capture_mode/stop_recording_button_tray.h" #include "ash/capture_mode/stop_recording_button_tray.h"
#include "ash/display/cursor_window_controller.h" #include "ash/display/cursor_window_controller.h"
#include "ash/display/window_tree_host_manager.h" #include "ash/display/window_tree_host_manager.h"
#include "ash/magnifier/magnifier_glass.h"
#include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/ash_features.h"
#include "ash/root_window_controller.h" #include "ash/root_window_controller.h"
#include "ash/shell.h" #include "ash/shell.h"
...@@ -125,6 +127,27 @@ class CaptureModeTest : public AshTestBase { ...@@ -125,6 +127,27 @@ class CaptureModeTest : public AshTestBase {
->close_button(); ->close_button();
} }
aura::Window* GetDimensionsLabelWindow() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto* widget = controller->capture_mode_session()
->dimensions_label_widget_for_testing();
return widget ? widget->GetNativeWindow() : nullptr;
}
base::Optional<gfx::Point> GetMagnifierGlassCenterPoint() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto& magnifier =
controller->capture_mode_session()->magnifier_glass_for_testing();
if (magnifier.host_widget_for_testing()) {
return magnifier.host_widget_for_testing()
->GetWindowBoundsInScreen()
.CenterPoint();
}
return base::nullopt;
}
// Start Capture Mode with source region and type image. // Start Capture Mode with source region and type image.
CaptureModeController* StartImageRegionCapture() { CaptureModeController* StartImageRegionCapture() {
auto* controller = CaptureModeController::Get(); auto* controller = CaptureModeController::Get();
...@@ -149,14 +172,6 @@ class CaptureModeTest : public AshTestBase { ...@@ -149,14 +172,6 @@ class CaptureModeTest : public AshTestBase {
EXPECT_EQ(region, controller->user_capture_region()); EXPECT_EQ(region, controller->user_capture_region());
} }
aura::Window* GetDimensionsLabelWindow() const {
auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive());
auto* widget =
controller->capture_mode_session()->dimensions_label_widget();
return widget ? widget->GetNativeWindow() : nullptr;
}
void WaitForCountDownToFinish() { void WaitForCountDownToFinish() {
auto* controller = CaptureModeController::Get(); auto* controller = CaptureModeController::Get();
DCHECK(controller->IsActive()); DCHECK(controller->IsActive());
...@@ -477,7 +492,78 @@ TEST_F(CaptureModeTest, CaptureRegionCoversCaptureModeBar) { ...@@ -477,7 +492,78 @@ TEST_F(CaptureModeTest, CaptureRegionCoversCaptureModeBar) {
EXPECT_FALSE(controller->IsActive()); EXPECT_FALSE(controller->IsActive());
} }
TEST_F(CaptureModeTest, DimensionsLabelLocation) { // Tests that the magnifying glass appears while fine tuning the capture region.
TEST_F(CaptureModeTest, CaptureRegionMagnifierWhenFineTuning) {
const gfx::Vector2d kDragDelta(50, 50);
UpdateDisplay("800x800");
// Start Capture Mode in a region in image mode.
StartImageRegionCapture();
// Press down and drag to select a region. The magnifier should not be
// visible yet.
gfx::Rect capture_region{200, 200, 400, 400};
SelectRegion(capture_region);
EXPECT_EQ(base::nullopt, GetMagnifierGlassCenterPoint());
auto check_magnifier_shows_properly = [this](const gfx::Point& origin,
const gfx::Point& destination,
bool should_show) {
// If |should_show|, check that the magnifying glass is centered on the
// mouse after press and during drag. If not |should_show|, check that
// the magnifying glass never shows. Should always be not visible when
// mouse button is released.
auto* event_generator = GetEventGenerator();
base::Optional<gfx::Point> expected_origin =
should_show ? base::make_optional(origin) : base::nullopt;
base::Optional<gfx::Point> expected_destination =
should_show ? base::make_optional(destination) : base::nullopt;
// Move cursor to |origin| and click.
event_generator->set_current_screen_location(origin);
event_generator->PressLeftButton();
EXPECT_EQ(expected_origin, GetMagnifierGlassCenterPoint());
// Drag to |destination| while holding left button.
event_generator->MoveMouseTo(destination);
EXPECT_EQ(expected_destination, GetMagnifierGlassCenterPoint());
// Drag back to |origin| while still holding left button.
event_generator->MoveMouseTo(origin);
EXPECT_EQ(expected_origin, GetMagnifierGlassCenterPoint());
// Release left button.
event_generator->ReleaseLeftButton();
EXPECT_EQ(base::nullopt, GetMagnifierGlassCenterPoint());
};
// Drag the capture region from within the existing selected region. The
// magnifier should not be visible at any point.
check_magnifier_shows_properly(gfx::Point(400, 250), gfx::Point(500, 350),
/*should_show=*/false);
// Check that each corner fine tune position shows the magnifier when
// dragging.
struct {
std::string trace;
FineTunePosition position;
} kFineTunePositions[] = {{"top_left", FineTunePosition::kTopLeft},
{"top_right", FineTunePosition::kTopRight},
{"bottom_right", FineTunePosition::kBottomRight},
{"bottom_left", FineTunePosition::kBottomLeft}};
for (const auto& fine_tune_position : kFineTunePositions) {
SCOPED_TRACE(fine_tune_position.trace);
const gfx::Point drag_affordance_location =
capture_mode_util::GetLocationForFineTunePosition(
capture_region, fine_tune_position.position);
check_magnifier_shows_properly(drag_affordance_location,
drag_affordance_location + kDragDelta,
/*should_show=*/true);
}
}
// Tests that the dimensions label properly renders for capture regions.
TEST_F(CaptureModeTest, CaptureRegionDimensionsLabelLocation) {
UpdateDisplay("800x800"); UpdateDisplay("800x800");
// Start Capture Mode in a region in image mode. // Start Capture Mode in a region in image mode.
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/capture_mode/capture_mode_util.h"
#include "base/notreached.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
namespace ash {
namespace capture_mode_util {
gfx::Point GetLocationForFineTunePosition(const gfx::Rect& rect,
FineTunePosition position) {
switch (position) {
case FineTunePosition::kTopLeft:
return rect.origin();
case FineTunePosition::kTopCenter:
return rect.top_center();
case FineTunePosition::kTopRight:
return rect.top_right();
case FineTunePosition::kRightCenter:
return rect.right_center();
case FineTunePosition::kBottomRight:
return rect.bottom_right();
case FineTunePosition::kBottomCenter:
return rect.bottom_center();
case FineTunePosition::kBottomLeft:
return rect.bottom_left();
case FineTunePosition::kLeftCenter:
return rect.left_center();
default:
break;
}
NOTREACHED();
return gfx::Point();
}
bool IsCornerFineTunePosition(FineTunePosition position) {
switch (position) {
case FineTunePosition::kTopLeft:
case FineTunePosition::kTopRight:
case FineTunePosition::kBottomRight:
case FineTunePosition::kBottomLeft:
return true;
default:
break;
}
return false;
}
} // namespace capture_mode_util
} // namespace ash
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_UTIL_H_
#define ASH_CAPTURE_MODE_CAPTURE_MODE_UTIL_H_
#include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_types.h"
namespace gfx {
class Point;
class Rect;
} // namespace gfx
namespace ash {
namespace capture_mode_util {
// Retrieves the point on the |rect| associated with |position|.
ASH_EXPORT gfx::Point GetLocationForFineTunePosition(const gfx::Rect& rect,
FineTunePosition position);
// Return whether |position| is a corner.
ASH_EXPORT bool IsCornerFineTunePosition(FineTunePosition position);
} // namespace capture_mode_util
} // namespace ash
#endif // ASH_CAPTURE_MODE_CAPTURE_MODE_UTIL_H_
...@@ -58,6 +58,8 @@ class ASH_EXPORT MagnifierGlass : public aura::WindowObserver, ...@@ -58,6 +58,8 @@ class ASH_EXPORT MagnifierGlass : public aura::WindowObserver,
// Closes the magnifier glass widget. // Closes the magnifier glass widget.
void Close(); void Close();
views::Widget* host_widget_for_testing() const { return host_widget_; }
private: private:
friend class PartialMagnificationControllerTestApi; friend class PartialMagnificationControllerTestApi;
class BorderRenderer; class BorderRenderer;
......
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