From 9cc85e7f6620abd70fe5518260985e26c0307961 Mon Sep 17 00:00:00 2001 From: mukai Date: Wed, 17 Dec 2014 19:40:39 -0800 Subject: Refactor partial screenshot region selector (2nd) This is the reland of crrev.com/308508 which was reverted due to a failure of valgrind bot. Previously PartialScreenshotView is a view inside of a frameless window, which is actually problematic because it needs to deal with several window manager concepts such like mouse captures and activations. This is now built with ui::Layer and EventHandler, therefore it makes no effects. Also this CL moves the file location to a new directory ash/utility, because this is nothing related to the window manager. BUG=330348 R=oshima@chromium.org TEST=the new test covers with valgrind bot Review URL: https://codereview.chromium.org/813523002 Cr-Commit-Position: refs/heads/master@{#308942} --- ash/accelerators/accelerator_controller.cc | 4 +- ash/ash.gyp | 6 +- ash/utility/partial_screenshot_controller.cc | 266 +++++++++++++++++++++ ash/utility/partial_screenshot_controller.h | 93 +++++++ .../partial_screenshot_controller_unittest.cc | 139 +++++++++++ ash/wm/overlay_event_filter.cc | 1 - ash/wm/partial_screenshot_view.cc | 245 ------------------- ash/wm/partial_screenshot_view.h | 75 ------ ash/wm/partial_screenshot_view_unittest.cc | 124 ---------- 9 files changed, 503 insertions(+), 450 deletions(-) create mode 100644 ash/utility/partial_screenshot_controller.cc create mode 100644 ash/utility/partial_screenshot_controller.h create mode 100644 ash/utility/partial_screenshot_controller_unittest.cc delete mode 100644 ash/wm/partial_screenshot_view.cc delete mode 100644 ash/wm/partial_screenshot_view.h delete mode 100644 ash/wm/partial_screenshot_view_unittest.cc (limited to 'ash') diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc index 8441e97..c8c9932 100644 --- a/ash/accelerators/accelerator_controller.cc +++ b/ash/accelerators/accelerator_controller.cc @@ -41,11 +41,11 @@ #include "ash/system/tray/system_tray_notifier.h" #include "ash/system/web_notification/web_notification_tray.h" #include "ash/touch/touch_hud_debug.h" +#include "ash/utility/partial_screenshot_controller.h" #include "ash/volume_control_delegate.h" #include "ash/wm/maximize_mode/maximize_mode_controller.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/window_selector_controller.h" -#include "ash/wm/partial_screenshot_view.h" #include "ash/wm/power_button_controller.h" #include "ash/wm/window_cycle_controller.h" #include "ash/wm/window_state.h" @@ -405,7 +405,7 @@ void HandleSwitchIme(ImeControlDelegate* ime_control_delegate, void HandleTakePartialScreenshot(ScreenshotDelegate* screenshot_delegate) { base::RecordAction(UserMetricsAction("Accel_Take_Partial_Screenshot")); if (screenshot_delegate) { - ash::PartialScreenshotView::StartPartialScreenshot( + ash::PartialScreenshotController::StartPartialScreenshotSession( screenshot_delegate); } } diff --git a/ash/ash.gyp b/ash/ash.gyp index ca3191b..36246b3 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -470,6 +470,8 @@ 'touch/touch_uma.h', 'touch/touchscreen_util.cc', 'touch/touchscreen_util.h', + 'utility/partial_screenshot_controller.cc', + 'utility/partial_screenshot_controller.h', 'virtual_keyboard_controller.cc', 'virtual_keyboard_controller.h', 'volume_control_delegate.h', @@ -574,8 +576,6 @@ 'wm/panels/panel_window_event_handler.h', 'wm/panels/panel_window_resizer.cc', 'wm/panels/panel_window_resizer.h', - 'wm/partial_screenshot_view.cc', - 'wm/partial_screenshot_view.h', 'wm/power_button_controller.cc', 'wm/power_button_controller.h', 'wm/resize_handle_window_targeter.cc', @@ -830,6 +830,7 @@ 'touch/touch_observer_hud_unittest.cc', 'touch/touch_transformer_controller_unittest.cc', 'touch/touchscreen_util_unittest.cc', + 'utility/partial_screenshot_controller_unittest.cc', 'virtual_keyboard_controller_unittest.cc', 'wm/always_on_top_controller_unittest.cc', 'wm/app_list_controller_unittest.cc', @@ -848,7 +849,6 @@ 'wm/overview/window_selector_unittest.cc', 'wm/panels/panel_layout_manager_unittest.cc', 'wm/panels/panel_window_resizer_unittest.cc', - 'wm/partial_screenshot_view_unittest.cc', 'wm/resize_shadow_and_cursor_unittest.cc', 'wm/screen_dimmer_unittest.cc', 'wm/stacking_controller_unittest.cc', diff --git a/ash/utility/partial_screenshot_controller.cc b/ash/utility/partial_screenshot_controller.cc new file mode 100644 index 0000000..286fa0d --- /dev/null +++ b/ash/utility/partial_screenshot_controller.cc @@ -0,0 +1,266 @@ +// Copyright 2014 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/utility/partial_screenshot_controller.h" + +#include + +#include "ash/screenshot_delegate.h" +#include "ash/shell.h" +#include "ash/shell_window_ids.h" +#include "base/stl_util.h" +#include "ui/events/event_handler.h" +#include "ui/gfx/canvas.h" +#include "ui/wm/core/cursor_manager.h" + +namespace ash { + +namespace { + +PartialScreenshotController* instance = nullptr; + +// The size to increase the invalidated area in the layer to repaint. The area +// should be slightly bigger than the actual region because the region indicator +// rectangles are drawn outside of the selected region. +const int kInvalidateRegionAdditionalSize = 3; + +} // namespace + +class PartialScreenshotController::PartialScreenshotLayer + : public ui::LayerOwner, + public ui::LayerDelegate { + public: + PartialScreenshotLayer(ui::Layer* parent) { + SetLayer(new ui::Layer(ui::LAYER_TEXTURED)); + layer()->SetFillsBoundsOpaquely(false); + layer()->SetBounds(parent->bounds()); + parent->Add(layer()); + parent->StackAtTop(layer()); + layer()->SetVisible(true); + layer()->set_delegate(this); + } + ~PartialScreenshotLayer() {} + + const gfx::Rect& region() const { return region_; } + + void SetRegion(const gfx::Rect& region) { + // Invalidates the region which covers the current and new region. + gfx::Rect union_rect(region_); + union_rect.Union(region); + union_rect.Inset(-kInvalidateRegionAdditionalSize, + -kInvalidateRegionAdditionalSize); + union_rect.Intersects(layer()->bounds()); + + region_ = region; + layer()->SchedulePaint(union_rect); + } + + private: + // ui::LayerDelegate: + void OnPaintLayer(gfx::Canvas* canvas) override { + if (region_.IsEmpty()) + return; + + // Screenshot area representation: black rectangle with white + // rectangle inside. To avoid capturing these rectangles when mouse + // release, they should be outside of the actual capturing area. + gfx::Rect rect(region_); + rect.Inset(-1, -1); + canvas->DrawRect(rect, SK_ColorWHITE); + rect.Inset(-1, -1); + canvas->DrawRect(rect, SK_ColorBLACK); + } + + void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {} + + void OnDeviceScaleFactorChanged(float device_scale_factor) override {} + + base::Closure PrepareForLayerBoundsChange() override { + return base::Closure(); + } + + gfx::Rect region_; + + DISALLOW_COPY_AND_ASSIGN(PartialScreenshotLayer); +}; + +class PartialScreenshotController::ScopedCursorSetter { + public: + ScopedCursorSetter(::wm::CursorManager* cursor_manager, + gfx::NativeCursor cursor) + : cursor_manager_(nullptr) { + if (cursor_manager->IsCursorLocked()) + return; + gfx::NativeCursor original_cursor = cursor_manager->GetCursor(); + cursor_manager_ = cursor_manager; + cursor_manager_->SetCursor(cursor); + cursor_manager_->LockCursor(); + // SetCursor does not make any effects at this point but it sets back to the + // original cursor when unlocked. + cursor_manager_->SetCursor(original_cursor); + } + + ~ScopedCursorSetter() { + if (cursor_manager_) + cursor_manager_->UnlockCursor(); + } + + private: + ::wm::CursorManager* cursor_manager_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCursorSetter); +}; + +// static +void PartialScreenshotController::StartPartialScreenshotSession( + ScreenshotDelegate* screenshot_delegate) { + // Already in the session. + if (instance) + return; + instance = new PartialScreenshotController(screenshot_delegate); +} + +// static +PartialScreenshotController* PartialScreenshotController::GetInstanceForTest() { + return instance; +} + +PartialScreenshotController::PartialScreenshotController( + ScreenshotDelegate* screenshot_delegate) + : root_window_(nullptr), screenshot_delegate_(screenshot_delegate) { + DCHECK(screenshot_delegate_); + Shell* shell = Shell::GetInstance(); + shell->PrependPreTargetHandler(this); + shell->AddShellObserver(this); + Shell::GetScreen()->AddObserver(this); + + for (aura::Window* root : Shell::GetAllRootWindows()) { + layers_[root] = new PartialScreenshotLayer( + Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer()); + } + + cursor_setter_.reset( + new ScopedCursorSetter(shell->cursor_manager(), ui::kCursorCross)); +} + +PartialScreenshotController::~PartialScreenshotController() { + DCHECK_EQ(this, instance); + instance = nullptr; + + Shell::GetScreen()->RemoveObserver(this); + Shell::GetInstance()->RemovePreTargetHandler(this); + Shell::GetInstance()->RemoveShellObserver(this); + STLDeleteValues(&layers_); +} + +void PartialScreenshotController::MaybeStart(const ui::LocatedEvent& event) { + aura::Window* current_root = + static_cast(event.target())->GetRootWindow(); + if (root_window_) { + // It's already started. This can happen when the second finger touches + // the screen, or combination of the touch and mouse. We should grab the + // partial screenshot instead of restarting. + if (current_root == root_window_) { + Update(event); + Complete(); + } + } else { + root_window_ = current_root; + start_position_ = event.root_location(); + } +} + +void PartialScreenshotController::Complete() { + const gfx::Rect& region = layers_[root_window_]->region(); + if (!region.IsEmpty()) { + screenshot_delegate_->HandleTakePartialScreenshot( + root_window_, gfx::IntersectRects(root_window_->bounds(), region)); + } + Cancel(); +} + +void PartialScreenshotController::Cancel() { + delete this; +} + +void PartialScreenshotController::Update(const ui::LocatedEvent& event) { + // Update may happen without MaybeStart() if the partial screenshot session + // starts when dragging. + if (!root_window_) + MaybeStart(event); + + DCHECK(layers_.find(root_window_) != layers_.end()); + layers_[root_window_]->SetRegion( + gfx::Rect(std::min(start_position_.x(), event.root_location().x()), + std::min(start_position_.y(), event.root_location().y()), + ::abs(start_position_.x() - event.root_location().x()), + ::abs(start_position_.y() - event.root_location().y()))); +} + +void PartialScreenshotController::OnKeyEvent(ui::KeyEvent* event) { + if (event->type() == ui::ET_KEY_RELEASED && + event->key_code() == ui::VKEY_ESCAPE) { + Cancel(); + } + + // Intercepts all key events. + event->StopPropagation(); +} + +void PartialScreenshotController::OnMouseEvent(ui::MouseEvent* event) { + switch (event->type()) { + case ui::ET_MOUSE_PRESSED: + MaybeStart(*event); + break; + case ui::ET_MOUSE_DRAGGED: + Update(*event); + break; + case ui::ET_MOUSE_RELEASED: + Complete(); + break; + default: + // Do nothing. + break; + } + event->StopPropagation(); +} + +void PartialScreenshotController::OnTouchEvent(ui::TouchEvent* event) { + switch (event->type()) { + case ui::ET_TOUCH_PRESSED: + MaybeStart(*event); + break; + case ui::ET_TOUCH_MOVED: + Update(*event); + break; + case ui::ET_TOUCH_RELEASED: + Complete(); + break; + default: + // Do nothing. + break; + } + event->StopPropagation(); +} + +void PartialScreenshotController::OnAppTerminating() { + Cancel(); +} + +void PartialScreenshotController::OnDisplayAdded( + const gfx::Display& new_display) { + Cancel(); +} + +void PartialScreenshotController::OnDisplayRemoved( + const gfx::Display& old_display) { + Cancel(); +} + +void PartialScreenshotController::OnDisplayMetricsChanged( + const gfx::Display& display, + uint32_t changed_metrics) { +} + +} // namespace ash diff --git a/ash/utility/partial_screenshot_controller.h b/ash/utility/partial_screenshot_controller.h new file mode 100644 index 0000000..c1a4845 --- /dev/null +++ b/ash/utility/partial_screenshot_controller.h @@ -0,0 +1,93 @@ +// Copyright 2014 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_UTILITY_PARTIAL_SCREENSHOT_CONTROLLER_H_ +#define ASH_UTILITY_PARTIAL_SCREENSHOT_CONTROLLER_H_ + +#include + +#include "ash/ash_export.h" +#include "ash/shell_observer.h" +#include "base/memory/scoped_ptr.h" +#include "ui/events/event_handler.h" +#include "ui/gfx/display_observer.h" +#include "ui/gfx/point.h" + +namespace aura { +class Window; +} + +namespace ui { +class LocatedEvent; +} + +namespace ash { +class ScreenshotDelegate; + +// This class controls a session of taking partial screenshot, i.e.: drawing +// region rectangles during drag, and changing the mouse cursor to indicate +// the current mode. +// This class does not use aura::Window / views::Widget intentionally to avoid +// the conflicts of window manager features like mouse captures or window focus. +class ASH_EXPORT PartialScreenshotController : public ui::EventHandler, + public ShellObserver, + public gfx::DisplayObserver { + public: + // Starts the UI for taking partial screenshot; dragging to select a region. + // PartialScreenshotController manage their own lifetime so caller must not + // delete the returned values. + static void StartPartialScreenshotSession( + ScreenshotDelegate* screenshot_delegate); + + ~PartialScreenshotController() override; + + private: + friend class PartialScreenshotControllerTest; + + class ScopedCursorSetter; + class PartialScreenshotLayer; + + static PartialScreenshotController* GetInstanceForTest(); + + PartialScreenshotController(ScreenshotDelegate* screenshot_delegate); + + // Starts, ends, cancels, or updates the region selection. + void MaybeStart(const ui::LocatedEvent& event); + void Complete(); + void Cancel(); + void Update(const ui::LocatedEvent& event); + + // ui::EventHandler: + void OnKeyEvent(ui::KeyEvent* event) override; + void OnMouseEvent(ui::MouseEvent* event) override; + void OnTouchEvent(ui::TouchEvent* event) override; + + // ShellObserver: + void OnAppTerminating() override; + + // gfx::DisplayObserver: + void OnDisplayAdded(const gfx::Display& new_display) override; + void OnDisplayRemoved(const gfx::Display& old_display) override; + void OnDisplayMetricsChanged(const gfx::Display& display, + uint32_t changed_metrics) override; + + // The data to build the screenshot region. + gfx::Point start_position_; + aura::Window* root_window_; + + // Layers to create the visual effect of region selection. + std::map layers_; + + // The object to specify the crosshair cursor. + scoped_ptr cursor_setter_; + + // ScreenshotDelegate to take the actual screenshot. No ownership. + ScreenshotDelegate* screenshot_delegate_; + + DISALLOW_COPY_AND_ASSIGN(PartialScreenshotController); +}; + +} // namespace ash + +#endif // #ifndef ASH_WM_PARTIAL_SCREENSHOT_VIEW_H_ diff --git a/ash/utility/partial_screenshot_controller_unittest.cc b/ash/utility/partial_screenshot_controller_unittest.cc new file mode 100644 index 0000000..edd79ef --- /dev/null +++ b/ash/utility/partial_screenshot_controller_unittest.cc @@ -0,0 +1,139 @@ +// Copyright 2014 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/utility/partial_screenshot_controller.h" + +#include "ash/screenshot_delegate.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/test/test_screenshot_delegate.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/events/test/event_generator.h" + +namespace ash { + +class PartialScreenshotControllerTest : public test::AshTestBase { + public: + PartialScreenshotControllerTest() {} + ~PartialScreenshotControllerTest() override {} + + void SetUp() override { + test::AshTestBase::SetUp(); + PartialScreenshotController::StartPartialScreenshotSession( + GetScreenshotDelegate()); + } + + protected: + static PartialScreenshotController* GetInstance() { + return PartialScreenshotController::GetInstanceForTest(); + } + + const gfx::Point& GetStartPosition() const { + return GetInstance()->start_position_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(PartialScreenshotControllerTest); +}; + +TEST_F(PartialScreenshotControllerTest, BasicMouse) { + test::TestScreenshotDelegate* test_delegate = GetScreenshotDelegate(); + ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); + + generator.MoveMouseTo(100, 100); + generator.PressLeftButton(); + EXPECT_EQ("100,100", GetStartPosition().ToString()); + EXPECT_EQ(0, test_delegate->handle_take_partial_screenshot_count()); + + generator.MoveMouseTo(200, 200); + EXPECT_EQ(0, test_delegate->handle_take_partial_screenshot_count()); + + generator.ReleaseLeftButton(); + EXPECT_EQ("100,100 100x100", GetScreenshotDelegate()->last_rect().ToString()); + EXPECT_EQ(1, GetScreenshotDelegate()->handle_take_partial_screenshot_count()); + + RunAllPendingInMessageLoop(); + EXPECT_EQ(nullptr, GetInstance()); +} + +TEST_F(PartialScreenshotControllerTest, JustClick) { + test::TestScreenshotDelegate* test_delegate = GetScreenshotDelegate(); + ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); + + generator.MoveMouseTo(100, 100); + + // No moves, just clicking at the same position. + generator.ClickLeftButton(); + EXPECT_EQ(0, test_delegate->handle_take_partial_screenshot_count()); + + RunAllPendingInMessageLoop(); + EXPECT_EQ(nullptr, GetInstance()); +} + +TEST_F(PartialScreenshotControllerTest, BasicTouch) { + test::TestScreenshotDelegate* test_delegate = GetScreenshotDelegate(); + ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); + + generator.set_current_location(gfx::Point(100, 100)); + generator.PressTouch(); + EXPECT_EQ(0, test_delegate->handle_take_partial_screenshot_count()); + EXPECT_EQ("100,100", GetStartPosition().ToString()); + + generator.MoveTouch(gfx::Point(200, 200)); + EXPECT_EQ(0, test_delegate->handle_take_partial_screenshot_count()); + + generator.ReleaseTouch(); + EXPECT_EQ("100,100 100x100", GetScreenshotDelegate()->last_rect().ToString()); + EXPECT_EQ(1, GetScreenshotDelegate()->handle_take_partial_screenshot_count()); + + RunAllPendingInMessageLoop(); + EXPECT_EQ(nullptr, GetInstance()); +} + +TEST_F(PartialScreenshotControllerTest, TwoFingerTouch) { + test::TestScreenshotDelegate* test_delegate = GetScreenshotDelegate(); + ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); + + generator.set_current_location(gfx::Point(100, 100)); + generator.PressTouch(); + EXPECT_EQ(0, test_delegate->handle_take_partial_screenshot_count()); + EXPECT_EQ("100,100", GetStartPosition().ToString()); + + generator.set_current_location(gfx::Point(200, 200)); + generator.PressTouchId(1); + EXPECT_EQ("100,100 100x100", GetScreenshotDelegate()->last_rect().ToString()); + EXPECT_EQ(1, GetScreenshotDelegate()->handle_take_partial_screenshot_count()); + + RunAllPendingInMessageLoop(); + EXPECT_EQ(nullptr, GetInstance()); +} + +TEST_F(PartialScreenshotControllerTest, DontStartTwice) { + PartialScreenshotController* controller = GetInstance(); + + PartialScreenshotController::StartPartialScreenshotSession( + GetScreenshotDelegate()); + + // The same instance. + EXPECT_EQ(controller, GetInstance()); +} + +TEST_F(PartialScreenshotControllerTest, MultipleDisplays) { + if (!SupportsMultipleDisplays()) + return; + + EXPECT_NE(nullptr, GetInstance()); + UpdateDisplay("400x400,500x500"); + RunAllPendingInMessageLoop(); + EXPECT_EQ(nullptr, GetInstance()); + + PartialScreenshotController::StartPartialScreenshotSession( + GetScreenshotDelegate()); + EXPECT_NE(nullptr, GetInstance()); + UpdateDisplay("400x400"); + RunAllPendingInMessageLoop(); + EXPECT_EQ(nullptr, GetInstance()); +} + +} // namespace ash diff --git a/ash/wm/overlay_event_filter.cc b/ash/wm/overlay_event_filter.cc index 03c4947..b349bf9 100644 --- a/ash/wm/overlay_event_filter.cc +++ b/ash/wm/overlay_event_filter.cc @@ -4,7 +4,6 @@ #include "ash/wm/overlay_event_filter.h" -#include "ash/wm/partial_screenshot_view.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/events/event.h" diff --git a/ash/wm/partial_screenshot_view.cc b/ash/wm/partial_screenshot_view.cc deleted file mode 100644 index 233a286..0000000 --- a/ash/wm/partial_screenshot_view.cc +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (c) 2012 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/wm/partial_screenshot_view.h" - -#include - -#include "ash/display/mouse_cursor_event_filter.h" -#include "ash/screenshot_delegate.h" -#include "ash/shell.h" -#include "ash/shell_window_ids.h" -#include "ash/wm/overlay_event_filter.h" -#include "ui/aura/client/capture_client.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/base/cursor/cursor.h" -#include "ui/events/event.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/rect.h" -#include "ui/views/view.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_observer.h" - -namespace ash { - -// A self-owned object to handle the cancel and the finish of current partial -// screenshot session. -class PartialScreenshotView::OverlayDelegate - : public OverlayEventFilter::Delegate, - public views::WidgetObserver { - public: - OverlayDelegate() { - Shell::GetInstance()->overlay_filter()->Activate(this); - } - - void RegisterWidget(views::Widget* widget) { - widgets_.push_back(widget); - widget->AddObserver(this); - } - - // Overridden from OverlayEventFilter::Delegate: - void Cancel() override { - // Make sure the mouse_warp_mode allows warping. It can be stopped by a - // partial screenshot view. - MouseCursorEventFilter* mouse_cursor_filter = - Shell::GetInstance()->mouse_cursor_filter(); - mouse_cursor_filter->set_mouse_warp_mode( - MouseCursorEventFilter::WARP_ALWAYS); - for (size_t i = 0; i < widgets_.size(); ++i) - widgets_[i]->Close(); - } - - bool IsCancelingKeyEvent(ui::KeyEvent* event) override { - return event->key_code() == ui::VKEY_ESCAPE; - } - - aura::Window* GetWindow() override { - // Just returns NULL because this class does not handle key events in - // OverlayEventFilter, except for cancel keys. - return NULL; - } - - // Overridden from views::WidgetObserver: - void OnWidgetDestroying(views::Widget* widget) override { - widget->RemoveObserver(this); - widgets_.erase(std::remove(widgets_.begin(), widgets_.end(), widget)); - if (widgets_.empty()) - delete this; - } - - private: - ~OverlayDelegate() override { - Shell::GetInstance()->overlay_filter()->Deactivate(this); - } - - std::vector widgets_; - - DISALLOW_COPY_AND_ASSIGN(OverlayDelegate); -}; - -// static -std::vector -PartialScreenshotView::StartPartialScreenshot( - ScreenshotDelegate* screenshot_delegate) { - std::vector views; - - if (Shell::GetInstance()->overlay_filter()->IsActive()) - return views; - - OverlayDelegate* overlay_delegate = new OverlayDelegate(); - aura::Window::Windows root_windows = Shell::GetAllRootWindows(); - for (aura::Window::Windows::iterator it = root_windows.begin(); - it != root_windows.end(); ++it) { - PartialScreenshotView* new_view = new PartialScreenshotView( - overlay_delegate, screenshot_delegate); - new_view->Init(*it); - views.push_back(new_view); - } - return views; -} - -PartialScreenshotView::PartialScreenshotView( - PartialScreenshotView::OverlayDelegate* overlay_delegate, - ScreenshotDelegate* screenshot_delegate) - : is_dragging_(false), - overlay_delegate_(overlay_delegate), - screenshot_delegate_(screenshot_delegate) { -} - -PartialScreenshotView::~PartialScreenshotView() { - overlay_delegate_ = NULL; - screenshot_delegate_ = NULL; -} - -void PartialScreenshotView::Init(aura::Window* root_window) { - views::Widget* widget = new views::Widget; - views::Widget::InitParams params( - views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; - params.delegate = this; - // The partial screenshot rectangle has to be at the real top of - // the screen. - params.parent = - Shell::GetContainer(root_window, kShellWindowId_OverlayContainer); - - widget->Init(params); - widget->SetContentsView(this); - widget->SetBounds(root_window->GetBoundsInScreen()); - widget->GetNativeView()->SetName("PartialScreenshotView"); - widget->StackAtTop(); - widget->Show(); - // Releases the mouse capture to let mouse events come to the view. This - // will close the context menu. - aura::client::CaptureClient* capture_client = - aura::client::GetCaptureClient(root_window); - if (capture_client->GetCaptureWindow()) - capture_client->ReleaseCapture(capture_client->GetCaptureWindow()); - - overlay_delegate_->RegisterWidget(widget); -} - -gfx::Rect PartialScreenshotView::GetScreenshotRect() const { - int left = std::min(start_position_.x(), current_position_.x()); - int top = std::min(start_position_.y(), current_position_.y()); - int width = ::abs(start_position_.x() - current_position_.x()); - int height = ::abs(start_position_.y() - current_position_.y()); - return gfx::Rect(left, top, width, height); -} - -void PartialScreenshotView::OnSelectionStarted(const gfx::Point& position) { - start_position_ = position; -} - -void PartialScreenshotView::OnSelectionChanged(const gfx::Point& position) { - if (is_dragging_ && current_position_ == position) - return; - current_position_ = position; - SchedulePaint(); - is_dragging_ = true; -} - -void PartialScreenshotView::OnSelectionFinished() { - overlay_delegate_->Cancel(); - if (!is_dragging_) - return; - - is_dragging_ = false; - if (screenshot_delegate_) { - aura::Window*root_window = - GetWidget()->GetNativeWindow()->GetRootWindow(); - screenshot_delegate_->HandleTakePartialScreenshot( - root_window, - gfx::IntersectRects(root_window->bounds(), GetScreenshotRect())); - } -} - -gfx::NativeCursor PartialScreenshotView::GetCursor( - const ui::MouseEvent& event) { - // Always use "crosshair" cursor. - return ui::kCursorCross; -} - -void PartialScreenshotView::OnPaint(gfx::Canvas* canvas) { - if (is_dragging_) { - // Screenshot area representation: black rectangle with white - // rectangle inside. To avoid capturing these rectangles when mouse - // release, they should be outside of the actual capturing area. - gfx::Rect screenshot_rect = GetScreenshotRect(); - screenshot_rect.Inset(-1, -1, -1, -1); - canvas->DrawRect(screenshot_rect, SK_ColorWHITE); - screenshot_rect.Inset(-1, -1, -1, -1); - canvas->DrawRect(screenshot_rect, SK_ColorBLACK); - } -} - -bool PartialScreenshotView::OnMousePressed(const ui::MouseEvent& event) { - // Prevent moving across displays during drag. Capturing a screenshot across - // the displays is not supported yet. - // TODO(mukai): remove this restriction. - MouseCursorEventFilter* mouse_cursor_filter = - Shell::GetInstance()->mouse_cursor_filter(); - mouse_cursor_filter->set_mouse_warp_mode(MouseCursorEventFilter::WARP_NONE); - OnSelectionStarted(event.location()); - return true; -} - -bool PartialScreenshotView::OnMouseDragged(const ui::MouseEvent& event) { - OnSelectionChanged(event.location()); - return true; -} - -bool PartialScreenshotView::OnMouseWheel(const ui::MouseWheelEvent& event) { - // Do nothing but do not propagate events futhermore. - return true; -} - -void PartialScreenshotView::OnMouseReleased(const ui::MouseEvent& event) { - OnSelectionFinished(); -} - -void PartialScreenshotView::OnMouseCaptureLost() { - is_dragging_ = false; - OnSelectionFinished(); -} - -void PartialScreenshotView::OnGestureEvent(ui::GestureEvent* event) { - switch(event->type()) { - case ui::ET_GESTURE_TAP_DOWN: - OnSelectionStarted(event->location()); - break; - case ui::ET_GESTURE_SCROLL_UPDATE: - OnSelectionChanged(event->location()); - break; - case ui::ET_GESTURE_SCROLL_END: - case ui::ET_SCROLL_FLING_START: - OnSelectionFinished(); - break; - default: - break; - } - - event->SetHandled(); -} - -} // namespace ash diff --git a/ash/wm/partial_screenshot_view.h b/ash/wm/partial_screenshot_view.h deleted file mode 100644 index 71e0242..0000000 --- a/ash/wm/partial_screenshot_view.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2012 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_WM_PARTIAL_SCREENSHOT_VIEW_H_ -#define ASH_WM_PARTIAL_SCREENSHOT_VIEW_H_ - -#include - -#include "ash/ash_export.h" -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "ui/gfx/point.h" -#include "ui/views/widget/widget_delegate.h" - -namespace ash { -class ScreenshotDelegate; - -// The view of taking partial screenshot, i.e.: drawing region -// rectangles during drag, and changing the mouse cursor to indicate -// the current mode. -class ASH_EXPORT PartialScreenshotView : public views::WidgetDelegateView { - public: - // Starts the UI for taking partial screenshot; dragging to select a region. - // PartialScreenshotViews manage their own lifetime so caller must not delete - // the returned PartialScreenshotViews. - static std::vector - StartPartialScreenshot(ScreenshotDelegate* screenshot_delegate); - - private: - FRIEND_TEST_ALL_PREFIXES(PartialScreenshotViewTest, BasicMouse); - FRIEND_TEST_ALL_PREFIXES(PartialScreenshotViewTest, BasicTouch); - - class OverlayDelegate; - - PartialScreenshotView(OverlayDelegate* overlay_delegate, - ScreenshotDelegate* screenshot_delegate); - ~PartialScreenshotView() override; - - // Initializes partial screenshot UI widget for |root_window|. - void Init(aura::Window* root_window); - - // Returns the currently selected region. - gfx::Rect GetScreenshotRect() const; - - void OnSelectionStarted(const gfx::Point& position); - void OnSelectionChanged(const gfx::Point& position); - void OnSelectionFinished(); - - // Overridden from views::View: - gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; - void OnPaint(gfx::Canvas* canvas) override; - bool OnMousePressed(const ui::MouseEvent& event) override; - bool OnMouseDragged(const ui::MouseEvent& event) override; - bool OnMouseWheel(const ui::MouseWheelEvent& event) override; - void OnMouseReleased(const ui::MouseEvent& event) override; - void OnMouseCaptureLost() override; - void OnGestureEvent(ui::GestureEvent* event) override; - - bool is_dragging_; - gfx::Point start_position_; - gfx::Point current_position_; - - // The delegate to receive Cancel. No ownership. - OverlayDelegate* overlay_delegate_; - - // ScreenshotDelegate to take the actual screenshot. No ownership. - ScreenshotDelegate* screenshot_delegate_; - - DISALLOW_COPY_AND_ASSIGN(PartialScreenshotView); -}; - -} // namespace ash - -#endif // #ifndef ASH_WM_PARTIAL_SCREENSHOT_VIEW_H_ diff --git a/ash/wm/partial_screenshot_view_unittest.cc b/ash/wm/partial_screenshot_view_unittest.cc deleted file mode 100644 index 7ad5205..0000000 --- a/ash/wm/partial_screenshot_view_unittest.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2013 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/wm/partial_screenshot_view.h" - -#include "ash/screenshot_delegate.h" -#include "ash/shell.h" -#include "ash/shell_window_ids.h" -#include "ash/test/ash_test_base.h" -#include "ash/test/test_overlay_delegate.h" -#include "ash/test/test_screenshot_delegate.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/events/test/event_generator.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_observer.h" - -namespace ash { - -class PartialScreenshotViewTest : public test::AshTestBase, - public views::WidgetObserver { - public: - PartialScreenshotViewTest() : view_(NULL) {} - ~PartialScreenshotViewTest() override { - if (view_) - view_->GetWidget()->RemoveObserver(this); - } - - void StartPartialScreenshot() { - std::vector views = - PartialScreenshotView::StartPartialScreenshot(GetScreenshotDelegate()); - if (!views.empty()) { - view_ = views[0]; - view_->GetWidget()->AddObserver(this); - } - } - - protected: - PartialScreenshotView* view_; - - private: - // views::WidgetObserver: - void OnWidgetDestroying(views::Widget* widget) override { - if (view_ && view_->GetWidget() == widget) - view_ = NULL; - widget->RemoveObserver(this); - } - - DISALLOW_COPY_AND_ASSIGN(PartialScreenshotViewTest); -}; - -TEST_F(PartialScreenshotViewTest, BasicMouse) { - StartPartialScreenshot(); - ASSERT_TRUE(view_); - - ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); - - generator.MoveMouseTo(100, 100); - generator.PressLeftButton(); - EXPECT_FALSE(view_->is_dragging_); - EXPECT_EQ("100,100", view_->start_position_.ToString()); - - generator.MoveMouseTo(200, 200); - EXPECT_TRUE(view_->is_dragging_); - EXPECT_EQ("200,200", view_->current_position_.ToString()); - - generator.ReleaseLeftButton(); - EXPECT_FALSE(view_->is_dragging_); - EXPECT_EQ("100,100 100x100", GetScreenshotDelegate()->last_rect().ToString()); - EXPECT_EQ(1, GetScreenshotDelegate()->handle_take_partial_screenshot_count()); -} - -TEST_F(PartialScreenshotViewTest, BasicTouch) { - StartPartialScreenshot(); - ASSERT_TRUE(view_); - - ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); - - generator.set_current_location(gfx::Point(100,100)); - generator.GestureTapDownAndUp(gfx::Point(100,100)); - EXPECT_FALSE(view_->is_dragging_); - EXPECT_EQ(0, GetScreenshotDelegate()->handle_take_partial_screenshot_count()); - - generator.PressTouch(); - EXPECT_FALSE(view_->is_dragging_); - EXPECT_EQ("100,100", view_->start_position_.ToString()); - - generator.MoveTouch(gfx::Point(200, 200)); - EXPECT_TRUE(view_->is_dragging_); - EXPECT_EQ("200,200", view_->current_position_.ToString()); - - generator.ReleaseTouch(); - EXPECT_FALSE(view_->is_dragging_); - EXPECT_EQ(1, GetScreenshotDelegate()->handle_take_partial_screenshot_count()); - EXPECT_EQ("100,100 100x100", GetScreenshotDelegate()->last_rect().ToString()); -} - -// Partial screenshot session should not start when there is already another -// overlay. See: http://crbug.com/341958 -TEST_F(PartialScreenshotViewTest, DontStartOverOverlay) { - OverlayEventFilter* overlay_filter = Shell::GetInstance()->overlay_filter(); - test::TestOverlayDelegate delegate; - overlay_filter->Activate(&delegate); - EXPECT_EQ(&delegate, overlay_filter->delegate_); - - StartPartialScreenshot(); - EXPECT_TRUE(view_ == NULL); - EXPECT_EQ(&delegate, overlay_filter->delegate_); - overlay_filter->Deactivate(&delegate); - - StartPartialScreenshot(); - EXPECT_TRUE(view_ != NULL); - - // Someone else attempts to activate the overlay session, which should cancel - // the current partial screenshot session. - overlay_filter->Activate(&delegate); - RunAllPendingInMessageLoop(); - EXPECT_EQ(&delegate, overlay_filter->delegate_); - EXPECT_TRUE(view_ == NULL); - - overlay_filter->Deactivate(&delegate); -} - -} // namespace ash -- cgit v1.1