Roll src/third_party/WebKit dbf9be3:8d6c3d5 (svn 202308:202312)
[chromium-blink-merge.git] / ash / utility / partial_screenshot_controller.cc
blobf7ac242eb9bcb6c9e93391c53f7b4d3dad1ab881
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/utility/partial_screenshot_controller.h"
7 #include <cmath>
9 #include "ash/display/mouse_cursor_event_filter.h"
10 #include "ash/screenshot_delegate.h"
11 #include "ash/shell.h"
12 #include "ash/shell_window_ids.h"
13 #include "base/stl_util.h"
14 #include "ui/compositor/paint_recorder.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_handler.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/wm/core/cursor_manager.h"
20 namespace ash {
22 namespace {
24 // The size to increase the invalidated area in the layer to repaint. The area
25 // should be slightly bigger than the actual region because the region indicator
26 // rectangles are drawn outside of the selected region.
27 const int kInvalidateRegionAdditionalSize = 3;
29 // This will prevent the user from taking a screenshot across multiple
30 // monitors. it will stop the mouse at the any edge of the screen. must
31 // swtich back on when the screenshot is complete.
32 void EnableMouseWarp(bool enable) {
33 Shell::GetInstance()->mouse_cursor_filter()->set_mouse_warp_enabled(enable);
36 } // namespace
38 class PartialScreenshotController::PartialScreenshotLayer
39 : public ui::LayerOwner,
40 public ui::LayerDelegate {
41 public:
42 PartialScreenshotLayer(ui::Layer* parent) {
43 SetLayer(new ui::Layer(ui::LAYER_TEXTURED));
44 layer()->SetFillsBoundsOpaquely(false);
45 layer()->SetBounds(parent->bounds());
46 parent->Add(layer());
47 parent->StackAtTop(layer());
48 layer()->SetVisible(true);
49 layer()->set_delegate(this);
51 ~PartialScreenshotLayer() override {}
53 const gfx::Rect& region() const { return region_; }
55 void SetRegion(const gfx::Rect& region) {
56 // Invalidates the region which covers the current and new region.
57 gfx::Rect union_rect(region_);
58 union_rect.Union(region);
59 union_rect.Inset(-kInvalidateRegionAdditionalSize,
60 -kInvalidateRegionAdditionalSize);
61 union_rect.Intersects(layer()->bounds());
63 region_ = region;
64 layer()->SchedulePaint(union_rect);
67 private:
68 // ui::LayerDelegate:
69 void OnPaintLayer(const ui::PaintContext& context) override {
70 if (region_.IsEmpty())
71 return;
73 // Screenshot area representation: black rectangle with white
74 // rectangle inside. To avoid capturing these rectangles when mouse
75 // release, they should be outside of the actual capturing area.
76 gfx::Rect rect(region_);
77 ui::PaintRecorder recorder(context, layer()->size());
78 rect.Inset(-1, -1);
79 recorder.canvas()->DrawRect(rect, SK_ColorWHITE);
80 rect.Inset(-1, -1);
81 recorder.canvas()->DrawRect(rect, SK_ColorBLACK);
84 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
86 void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
88 base::Closure PrepareForLayerBoundsChange() override {
89 return base::Closure();
92 gfx::Rect region_;
94 DISALLOW_COPY_AND_ASSIGN(PartialScreenshotLayer);
97 class PartialScreenshotController::ScopedCursorSetter {
98 public:
99 ScopedCursorSetter(::wm::CursorManager* cursor_manager,
100 gfx::NativeCursor cursor)
101 : cursor_manager_(nullptr) {
102 if (cursor_manager->IsCursorLocked())
103 return;
104 gfx::NativeCursor original_cursor = cursor_manager->GetCursor();
105 cursor_manager_ = cursor_manager;
106 cursor_manager_->SetCursor(cursor);
107 if (!cursor_manager_->IsCursorVisible())
108 cursor_manager_->ShowCursor();
109 cursor_manager_->LockCursor();
110 // SetCursor does not make any effects at this point but it sets back to
111 // the original cursor when unlocked.
112 cursor_manager_->SetCursor(original_cursor);
115 ~ScopedCursorSetter() {
116 if (cursor_manager_)
117 cursor_manager_->UnlockCursor();
120 private:
121 ::wm::CursorManager* cursor_manager_;
123 DISALLOW_COPY_AND_ASSIGN(ScopedCursorSetter);
126 PartialScreenshotController::PartialScreenshotController()
127 : root_window_(nullptr), screenshot_delegate_(nullptr) {
128 Shell* shell = Shell::GetInstance();
129 shell->PrependPreTargetHandler(this);
132 PartialScreenshotController::~PartialScreenshotController() {
133 if (screenshot_delegate_)
134 Cancel();
135 Shell::GetInstance()->RemovePreTargetHandler(this);
138 void PartialScreenshotController::StartPartialScreenshotSession(
139 ScreenshotDelegate* screenshot_delegate) {
140 // Already in a screenshot session.
141 if (screenshot_delegate_) {
142 DCHECK_EQ(screenshot_delegate_, screenshot_delegate);
143 return;
146 screenshot_delegate_ = screenshot_delegate;
147 Shell::GetScreen()->AddObserver(this);
148 for (aura::Window* root : Shell::GetAllRootWindows()) {
149 layers_[root] = new PartialScreenshotLayer(
150 Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer());
153 cursor_setter_.reset(new ScopedCursorSetter(
154 Shell::GetInstance()->cursor_manager(), ui::kCursorCross));
156 EnableMouseWarp(false);
159 void PartialScreenshotController::MaybeStart(const ui::LocatedEvent& event) {
160 aura::Window* current_root =
161 static_cast<aura::Window*>(event.target())->GetRootWindow();
162 if (root_window_) {
163 // It's already started. This can happen when the second finger touches
164 // the screen, or combination of the touch and mouse. We should grab the
165 // partial screenshot instead of restarting.
166 if (current_root == root_window_) {
167 Update(event);
168 Complete();
170 } else {
171 root_window_ = current_root;
172 start_position_ = event.root_location();
176 void PartialScreenshotController::Complete() {
177 const gfx::Rect& region = layers_[root_window_]->region();
178 if (!region.IsEmpty()) {
179 screenshot_delegate_->HandleTakePartialScreenshot(
180 root_window_, gfx::IntersectRects(root_window_->bounds(), region));
182 Cancel();
185 void PartialScreenshotController::Cancel() {
186 root_window_ = nullptr;
187 screenshot_delegate_ = nullptr;
188 Shell::GetScreen()->RemoveObserver(this);
189 STLDeleteValues(&layers_);
190 cursor_setter_.reset();
191 EnableMouseWarp(true);
194 void PartialScreenshotController::Update(const ui::LocatedEvent& event) {
195 // Update may happen without MaybeStart() if the partial screenshot session
196 // starts when dragging.
197 if (!root_window_)
198 MaybeStart(event);
200 DCHECK(layers_.find(root_window_) != layers_.end());
201 layers_[root_window_]->SetRegion(
202 gfx::Rect(std::min(start_position_.x(), event.root_location().x()),
203 std::min(start_position_.y(), event.root_location().y()),
204 ::abs(start_position_.x() - event.root_location().x()),
205 ::abs(start_position_.y() - event.root_location().y())));
208 void PartialScreenshotController::OnKeyEvent(ui::KeyEvent* event) {
209 if (!screenshot_delegate_)
210 return;
211 if (event->type() == ui::ET_KEY_RELEASED &&
212 event->key_code() == ui::VKEY_ESCAPE) {
213 Cancel();
216 // Intercepts all key events.
217 event->StopPropagation();
220 void PartialScreenshotController::OnMouseEvent(ui::MouseEvent* event) {
221 if (!screenshot_delegate_)
222 return;
223 switch (event->type()) {
224 case ui::ET_MOUSE_PRESSED:
225 MaybeStart(*event);
226 break;
227 case ui::ET_MOUSE_DRAGGED:
228 Update(*event);
229 break;
230 case ui::ET_MOUSE_RELEASED:
231 Complete();
232 break;
233 default:
234 // Do nothing.
235 break;
237 event->StopPropagation();
240 void PartialScreenshotController::OnTouchEvent(ui::TouchEvent* event) {
241 if (!screenshot_delegate_)
242 return;
243 switch (event->type()) {
244 case ui::ET_TOUCH_PRESSED:
245 MaybeStart(*event);
246 break;
247 case ui::ET_TOUCH_MOVED:
248 Update(*event);
249 break;
250 case ui::ET_TOUCH_RELEASED:
251 Complete();
252 break;
253 default:
254 // Do nothing.
255 break;
257 event->StopPropagation();
260 void PartialScreenshotController::OnDisplayAdded(
261 const gfx::Display& new_display) {
262 if (!screenshot_delegate_)
263 return;
264 Cancel();
267 void PartialScreenshotController::OnDisplayRemoved(
268 const gfx::Display& old_display) {
269 if (!screenshot_delegate_)
270 return;
271 Cancel();
274 void PartialScreenshotController::OnDisplayMetricsChanged(
275 const gfx::Display& display,
276 uint32_t changed_metrics) {
279 } // namespace ash