Commit e271ea4d authored by Sammie Quon's avatar Sammie Quon Committed by Chromium LUCI CQ

capture_mode: Properly handle display rotation and bounds change.

For all modes for either rotation/bounds change:
  - End current drag
  - Redraw capture session layer
  - Reposition capture label and capture bar

Additionally for region:
  - Reset selection region if rotation changes
  - Clamp the selection region to root window bounds if size changes

Also fixes a bug where the user can select a large region on a display,
exit and then enter capture mode on a smaller display. This CL will
ensure the initial region is clamped to size of the smaller display.

Fixed: 1154465
Test: manual + added
Change-Id: I4dde6b97af9532d08f7e53348d7a8a28c0c38714
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2568669
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834529}
parent ebd57966
......@@ -4,8 +4,6 @@
#include "ash/capture_mode/capture_mode_controller.h"
#include <memory>
#include <string>
#include <utility>
#include "ash/capture_mode/capture_mode_metrics.h"
......
......@@ -390,6 +390,10 @@ CaptureModeSession::CaptureModeSession(CaptureModeController* controller)
parent->layer()->Add(layer());
layer()->SetBounds(parent->bounds());
// The last region selected could have been on a larger display. Ensure that
// the region is not larger than the current display.
ClampCaptureRegionToRootWindowSize();
capture_mode_bar_widget_->Init(
CreateWidgetParams(parent, CaptureModeBarView::GetBounds(current_root_),
"CaptureModeBarWidget"));
......@@ -584,10 +588,31 @@ void CaptureModeSession::OnWindowDestroying(aura::Window* window) {
void CaptureModeSession::OnDisplayMetricsChanged(
const display::Display& display,
uint32_t metrics) {
if (metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION) {
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
if (!(metrics & (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION |
DISPLAY_METRIC_DEVICE_SCALE_FACTOR))) {
return;
}
EndSelection(/*is_event_on_capture_bar=*/false,
/*region_intersects_capture_bar=*/false);
UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
/*is_touch=*/false);
// Ensure the region still fits the root window after display changes.
ClampCaptureRegionToRootWindowSize();
// Update the bounds of all created widgets and repaint the entire layer.
auto* parent = GetParentContainer(current_root_);
DCHECK_EQ(parent->layer(), layer()->parent());
layer()->SetBounds(parent->bounds());
DCHECK(capture_mode_bar_widget_);
capture_mode_bar_widget_->SetBounds(
CaptureModeBarView::GetBounds(current_root_));
if (capture_label_widget_)
UpdateCaptureLabelWidget();
layer()->SchedulePaint(layer()->bounds());
}
gfx::Rect CaptureModeSession::GetSelectedWindowBounds() const {
......@@ -776,7 +801,7 @@ void CaptureModeSession::OnLocatedEvent(ui::LocatedEvent* event,
SetMouseWarpEnabled(*old_mouse_warp_status_);
old_mouse_warp_status_.reset();
OnLocatedEventReleased(location, is_event_on_capture_bar,
OnLocatedEventReleased(is_event_on_capture_bar,
region_intersects_capture_bar);
break;
case ui::ET_MOUSE_MOVED:
......@@ -928,30 +953,15 @@ void CaptureModeSession::OnLocatedEventDragged(
}
void CaptureModeSession::OnLocatedEventReleased(
const gfx::Point& location_in_root,
bool is_event_on_capture_bar,
bool region_intersects_capture_bar) {
fine_tune_position_ = FineTunePosition::kNone;
anchor_points_.clear();
is_drag_in_progress_ = false;
Shell::Get()->UpdateCursorCompositingEnabled();
// TODO(richui): update this for tablet mode.
UpdateCaptureBarWidgetOpacity(
region_intersects_capture_bar && !is_event_on_capture_bar
? kCaptureBarOverlapOpacity
: 1.f,
/*on_release=*/true);
EndSelection(is_event_on_capture_bar, region_intersects_capture_bar);
// Do a repaint to show the affordance circles.
gfx::Rect damage_region = controller_->user_capture_region();
damage_region.Inset(gfx::Insets(-kDamageInsetDp));
layer()->SchedulePaint(damage_region);
UpdateDimensionsLabelWidget(/*is_resizing=*/false);
CloseMagnifierGlass();
if (!is_selecting_region_)
return;
......@@ -1395,4 +1405,29 @@ void CaptureModeSession::UpdateCaptureBarWidgetOpacity(float opacity,
capture_bar_layer->SetOpacity(opacity);
}
void CaptureModeSession::ClampCaptureRegionToRootWindowSize() {
gfx::Rect new_capture_region = controller_->user_capture_region();
new_capture_region.AdjustToFit(current_root_->bounds());
controller_->set_user_capture_region(new_capture_region);
}
void CaptureModeSession::EndSelection(bool is_event_on_capture_bar,
bool region_intersects_capture_bar) {
fine_tune_position_ = FineTunePosition::kNone;
anchor_points_.clear();
is_drag_in_progress_ = false;
Shell::Get()->UpdateCursorCompositingEnabled();
// TODO(richui): Update this for tablet mode.
UpdateCaptureBarWidgetOpacity(
region_intersects_capture_bar && !is_event_on_capture_bar
? kCaptureBarOverlapOpacity
: 1.f,
/*on_release=*/true);
UpdateDimensionsLabelWidget(/*is_resizing=*/false);
CloseMagnifierGlass();
}
} // namespace ash
......@@ -133,8 +133,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
bool is_touch,
bool is_event_on_capture_bar);
void OnLocatedEventDragged(const gfx::Point& location_in_root);
void OnLocatedEventReleased(const gfx::Point& location_in_root,
bool is_event_on_capture_bar,
void OnLocatedEventReleased(bool is_event_on_capture_bar,
bool region_intersects_capture_bar);
// Updates the capture region and the capture region widgets depending on the
......@@ -208,6 +207,16 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
// animation duration and tween type for mouse/touch release.
void UpdateCaptureBarWidgetOpacity(float opacity, bool on_release);
// Ensure the user region in |controller_| is within the bounds of the root
// window. This is called when creating |this| or when the display bounds have
// changed.
void ClampCaptureRegionToRootWindowSize();
// Ends a region selection. Cleans up internal state and updates the cursor,
// capture bar opacity and magnifier glass.
void EndSelection(bool is_event_on_capture_bar,
bool region_intersects_capture_bar);
CaptureModeController* const controller_;
// The current root window on which the capture session is active, which may
......
......@@ -1885,6 +1885,66 @@ TEST_F(CaptureModeTest, TabletTouchCaptureLabelWidgetWindowMode) {
EXPECT_FALSE(controller->IsActive());
}
// Tests that after rotating a display, the capture session widgets are updated
// and the capture region is reset.
TEST_F(CaptureModeTest, DisplayRotation) {
UpdateDisplay("1200x600");
auto* controller = StartImageRegionCapture();
SelectRegion(gfx::Rect(1200, 400));
// Rotate the primary display by 90 degrees. Test that the region and capture
// bar fit within the rotated bounds, and the capture label widget is still
// centered in the region.
Shell::Get()->display_manager()->SetDisplayRotation(
WindowTreeHostManager::GetPrimaryDisplayId(), display::Display::ROTATE_90,
display::Display::RotationSource::USER);
const gfx::Rect rotated_root_bounds(600, 1200);
EXPECT_TRUE(rotated_root_bounds.Contains(controller->user_capture_region()));
EXPECT_TRUE(rotated_root_bounds.Contains(
GetCaptureModeBarView()->GetBoundsInScreen()));
views::Widget* capture_label_widget =
CaptureModeSessionTestApi(controller->capture_mode_session())
.capture_label_widget();
ASSERT_TRUE(capture_label_widget);
EXPECT_EQ(controller->user_capture_region().CenterPoint(),
capture_label_widget->GetWindowBoundsInScreen().CenterPoint());
}
TEST_F(CaptureModeTest, DisplayBoundsChange) {
UpdateDisplay("1200x600");
auto* controller = StartImageRegionCapture();
SelectRegion(gfx::Rect(1200, 400));
// Shrink the display. The capture region should shrink, and the capture bar
// should be adjusted to be centered.
UpdateDisplay("600x600");
EXPECT_EQ(gfx::Rect(600, 400), controller->user_capture_region());
EXPECT_EQ(300,
GetCaptureModeBarView()->GetBoundsInScreen().CenterPoint().x());
}
TEST_F(CaptureModeTest, ReenterOnSmallerDisplay) {
UpdateDisplay("1200x600,1201+0-600x600");
// Start off with the primary display as the targeted display. Create a region
// that fits the primary display but would be too big for the secondary
// display.
auto* event_generator = GetEventGenerator();
MoveMouseToAndUpdateCursorDisplay(gfx::Point(600, 300), event_generator);
auto* controller = StartImageRegionCapture();
SelectRegion(gfx::Rect(1200, 400));
EXPECT_EQ(gfx::Rect(1200, 400), controller->user_capture_region());
controller->Stop();
// Make the secondary display the targeted display. Test that the region has
// shrunk to fit the display.
MoveMouseToAndUpdateCursorDisplay(gfx::Point(1500, 300), event_generator);
StartImageRegionCapture();
EXPECT_EQ(gfx::Rect(600, 400), controller->user_capture_region());
}
// A test class that uses a mock time task environment.
class CaptureModeMockTimeTest : public CaptureModeTest {
public:
......
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