Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / ash / utility / partial_screenshot_controller.cc
blob661d40565411d8f100741fcc938e050427539f45
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/screenshot_delegate.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "base/stl_util.h"
13 #include "ui/compositor/paint_context.h"
14 #include "ui/compositor/paint_recorder.h"
15 #include "ui/events/event_handler.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/wm/core/cursor_manager.h"
19 namespace ash {
21 namespace {
23 // The size to increase the invalidated area in the layer to repaint. The area
24 // should be slightly bigger than the actual region because the region indicator
25 // rectangles are drawn outside of the selected region.
26 const int kInvalidateRegionAdditionalSize = 3;
28 } // namespace
30 class PartialScreenshotController::PartialScreenshotLayer
31 : public ui::LayerOwner,
32 public ui::LayerDelegate {
33 public:
34 PartialScreenshotLayer(ui::Layer* parent) {
35 SetLayer(new ui::Layer(ui::LAYER_TEXTURED));
36 layer()->SetFillsBoundsOpaquely(false);
37 layer()->SetBounds(parent->bounds());
38 parent->Add(layer());
39 parent->StackAtTop(layer());
40 layer()->SetVisible(true);
41 layer()->set_delegate(this);
43 ~PartialScreenshotLayer() override {}
45 const gfx::Rect& region() const { return region_; }
47 void SetRegion(const gfx::Rect& region) {
48 // Invalidates the region which covers the current and new region.
49 gfx::Rect union_rect(region_);
50 union_rect.Union(region);
51 union_rect.Inset(-kInvalidateRegionAdditionalSize,
52 -kInvalidateRegionAdditionalSize);
53 union_rect.Intersects(layer()->bounds());
55 region_ = region;
56 layer()->SchedulePaint(union_rect);
59 private:
60 // ui::LayerDelegate:
61 void OnPaintLayer(const ui::PaintContext& context) override {
62 if (region_.IsEmpty())
63 return;
65 // Screenshot area representation: black rectangle with white
66 // rectangle inside. To avoid capturing these rectangles when mouse
67 // release, they should be outside of the actual capturing area.
68 ui::PaintRecorder recorder(context);
69 gfx::Rect rect(region_);
70 rect.Inset(-1, -1);
71 recorder.canvas()->DrawRect(rect, SK_ColorWHITE);
72 rect.Inset(-1, -1);
73 recorder.canvas()->DrawRect(rect, SK_ColorBLACK);
76 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
78 void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
80 base::Closure PrepareForLayerBoundsChange() override {
81 return base::Closure();
84 gfx::Rect region_;
86 DISALLOW_COPY_AND_ASSIGN(PartialScreenshotLayer);
89 class PartialScreenshotController::ScopedCursorSetter {
90 public:
91 ScopedCursorSetter(::wm::CursorManager* cursor_manager,
92 gfx::NativeCursor cursor)
93 : cursor_manager_(nullptr) {
94 if (cursor_manager->IsCursorLocked())
95 return;
96 gfx::NativeCursor original_cursor = cursor_manager->GetCursor();
97 cursor_manager_ = cursor_manager;
98 cursor_manager_->SetCursor(cursor);
99 cursor_manager_->LockCursor();
100 // SetCursor does not make any effects at this point but it sets back to the
101 // original cursor when unlocked.
102 cursor_manager_->SetCursor(original_cursor);
105 ~ScopedCursorSetter() {
106 if (cursor_manager_)
107 cursor_manager_->UnlockCursor();
110 private:
111 ::wm::CursorManager* cursor_manager_;
113 DISALLOW_COPY_AND_ASSIGN(ScopedCursorSetter);
116 PartialScreenshotController::PartialScreenshotController()
117 : root_window_(nullptr), screenshot_delegate_(nullptr) {
118 Shell* shell = Shell::GetInstance();
119 shell->PrependPreTargetHandler(this);
122 PartialScreenshotController::~PartialScreenshotController() {
123 if (screenshot_delegate_)
124 Cancel();
125 Shell::GetInstance()->RemovePreTargetHandler(this);
128 void PartialScreenshotController::StartPartialScreenshotSession(
129 ScreenshotDelegate* screenshot_delegate) {
130 // Already in a screenshot session.
131 if (screenshot_delegate_) {
132 DCHECK_EQ(screenshot_delegate_, screenshot_delegate);
133 return;
136 screenshot_delegate_ = screenshot_delegate;
137 Shell::GetScreen()->AddObserver(this);
138 for (aura::Window* root : Shell::GetAllRootWindows()) {
139 layers_[root] = new PartialScreenshotLayer(
140 Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer());
143 cursor_setter_.reset(new ScopedCursorSetter(
144 Shell::GetInstance()->cursor_manager(), ui::kCursorCross));
147 void PartialScreenshotController::MaybeStart(const ui::LocatedEvent& event) {
148 aura::Window* current_root =
149 static_cast<aura::Window*>(event.target())->GetRootWindow();
150 if (root_window_) {
151 // It's already started. This can happen when the second finger touches
152 // the screen, or combination of the touch and mouse. We should grab the
153 // partial screenshot instead of restarting.
154 if (current_root == root_window_) {
155 Update(event);
156 Complete();
158 } else {
159 root_window_ = current_root;
160 start_position_ = event.root_location();
164 void PartialScreenshotController::Complete() {
165 const gfx::Rect& region = layers_[root_window_]->region();
166 if (!region.IsEmpty()) {
167 screenshot_delegate_->HandleTakePartialScreenshot(
168 root_window_, gfx::IntersectRects(root_window_->bounds(), region));
170 Cancel();
173 void PartialScreenshotController::Cancel() {
174 root_window_ = nullptr;
175 screenshot_delegate_ = nullptr;
176 Shell::GetScreen()->RemoveObserver(this);
177 STLDeleteValues(&layers_);
178 cursor_setter_.reset();
181 void PartialScreenshotController::Update(const ui::LocatedEvent& event) {
182 // Update may happen without MaybeStart() if the partial screenshot session
183 // starts when dragging.
184 if (!root_window_)
185 MaybeStart(event);
187 DCHECK(layers_.find(root_window_) != layers_.end());
188 layers_[root_window_]->SetRegion(
189 gfx::Rect(std::min(start_position_.x(), event.root_location().x()),
190 std::min(start_position_.y(), event.root_location().y()),
191 ::abs(start_position_.x() - event.root_location().x()),
192 ::abs(start_position_.y() - event.root_location().y())));
195 void PartialScreenshotController::OnKeyEvent(ui::KeyEvent* event) {
196 if (!screenshot_delegate_)
197 return;
198 if (event->type() == ui::ET_KEY_RELEASED &&
199 event->key_code() == ui::VKEY_ESCAPE) {
200 Cancel();
203 // Intercepts all key events.
204 event->StopPropagation();
207 void PartialScreenshotController::OnMouseEvent(ui::MouseEvent* event) {
208 if (!screenshot_delegate_)
209 return;
210 switch (event->type()) {
211 case ui::ET_MOUSE_PRESSED:
212 MaybeStart(*event);
213 break;
214 case ui::ET_MOUSE_DRAGGED:
215 Update(*event);
216 break;
217 case ui::ET_MOUSE_RELEASED:
218 Complete();
219 break;
220 default:
221 // Do nothing.
222 break;
224 event->StopPropagation();
227 void PartialScreenshotController::OnTouchEvent(ui::TouchEvent* event) {
228 if (!screenshot_delegate_)
229 return;
230 switch (event->type()) {
231 case ui::ET_TOUCH_PRESSED:
232 MaybeStart(*event);
233 break;
234 case ui::ET_TOUCH_MOVED:
235 Update(*event);
236 break;
237 case ui::ET_TOUCH_RELEASED:
238 Complete();
239 break;
240 default:
241 // Do nothing.
242 break;
244 event->StopPropagation();
247 void PartialScreenshotController::OnDisplayAdded(
248 const gfx::Display& new_display) {
249 if (!screenshot_delegate_)
250 return;
251 Cancel();
254 void PartialScreenshotController::OnDisplayRemoved(
255 const gfx::Display& old_display) {
256 if (!screenshot_delegate_)
257 return;
258 Cancel();
261 void PartialScreenshotController::OnDisplayMetricsChanged(
262 const gfx::Display& display,
263 uint32_t changed_metrics) {
266 } // namespace ash