Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(949)

Side by Side Diff: ash/magnifier/partial_magnification_controller.cc

Issue 2269383002: Magnifier border is now more visible on light backgrounds. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkgr
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ash/magnifier/partial_magnification_controller.h" 5 #include "ash/magnifier/partial_magnification_controller.h"
6 6
7 #include "ash/common/shell_window_ids.h"
8 #include "ash/shell.h" 7 #include "ash/shell.h"
9 #include "ui/aura/window.h"
10 #include "ui/aura/window_event_dispatcher.h" 8 #include "ui/aura/window_event_dispatcher.h"
11 #include "ui/aura/window_property.h"
12 #include "ui/aura/window_tree_host.h" 9 #include "ui/aura/window_tree_host.h"
13 #include "ui/compositor/layer.h" 10 #include "ui/compositor/layer.h"
14 #include "ui/views/layout/fill_layout.h" 11 #include "ui/compositor/paint_recorder.h"
12 #include "ui/events/event.h"
13 #include "ui/events/event_constants.h"
15 #include "ui/views/widget/widget.h" 14 #include "ui/views/widget/widget.h"
16 #include "ui/views/widget/widget_delegate.h" 15 #include "ui/wm/core/coordinate_conversion.h"
17 #include "ui/wm/core/compound_event_filter.h"
18 16
17 namespace ash {
19 namespace { 18 namespace {
20 19
21 const float kMinPartialMagnifiedScaleThreshold = 1.1f; 20 // Ratio of magnifier scale.
22 21 const float kMagnificationScale = 2.f;
23 // Number of pixels to make the border of the magnified area. 22 // Radius of the magnifying glass in DIP.
24 const int kZoomInset = 16; 23 const int kMagnifierRadius = 200;
25 24 // Size of the border around the magnifying glass in DIP.
26 // Width of the magnified area. 25 const int kBorderSize = 10;
27 const int kMagnifierWidth = 200; 26 // Thickness of the outline around magnifiying glass border.
28 27 const int kBorderOutlineThickness = 2;
29 // Height of the magnified area. 28 const SkColor kBorderColor = SK_ColorWHITE;
30 const int kMagnifierHeight = 200; 29 const SkColor kBorderOutlineColor = SK_ColorBLACK;
30 // Inset on the zoom filter.
31 const int kZoomInset = 0;
32 // Vertical offset between the center of the magnifier and the tip of the
33 // pointer. TODO(jdufault): The vertical offset should only apply to the window
34 // location, not the magnified contents. See crbug.com/637617.
35 const int kVerticalOffset = 0;
31 36
32 // Name of the magnifier window. 37 // Name of the magnifier window.
33 const char kPartialMagniferWindowName[] = "PartialMagnifierWindow"; 38 const char kPartialMagniferWindowName[] = "PartialMagnifierWindow";
34 39
40 gfx::Size GetWindowSize() {
41 return gfx::Size(kMagnifierRadius * 2, kMagnifierRadius * 2);
42 }
43
44 gfx::Rect GetBounds(gfx::Point mouse) {
45 gfx::Size size = GetWindowSize();
46 gfx::Point origin(mouse.x() - (size.width() / 2),
47 mouse.y() - (size.height() / 2) - kVerticalOffset);
48 return gfx::Rect(origin, size);
49 }
50
51 aura::Window* GetCurrentRootWindow() {
52 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
53 for (aura::Window* root_window : root_windows) {
54 if (root_window->ContainsPointInRoot(
55 root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot()))
56 return root_window;
57 }
58 return nullptr;
59 }
60
35 } // namespace 61 } // namespace
36 62
37 namespace ash { 63 // The content mask provides a clipping layer for the magnification window so we
64 // can show a circular magnifier.
65 class PartialMagnificationController::ContentMask : public ui::LayerDelegate {
66 public:
67 // If |stroke| is true, the circle will be a stroke. This is useful if we wish
68 // to clip a border.
69 ContentMask(bool stroke, gfx::Size mask_bounds)
70 : layer_(ui::LAYER_TEXTURED), stroke_(stroke) {
71 layer_.set_delegate(this);
72 layer_.SetFillsBoundsOpaquely(false);
73 layer_.SetBounds(gfx::Rect(mask_bounds));
74 }
38 75
39 PartialMagnificationController::PartialMagnificationController() 76 ~ContentMask() override { layer_.set_delegate(nullptr); }
40 : is_enabled_(false), 77
41 scale_(kNonPartialMagnifiedScale), 78 ui::Layer* layer() { return &layer_; }
42 zoom_widget_(NULL) { 79
80 private:
81 // Overridden from LayerDelegate.
82 void OnPaintLayer(const ui::PaintContext& context) override {
83 ui::PaintRecorder recorder(context, layer()->size());
84
85 SkPaint paint;
86 paint.setAlpha(255);
87 paint.setAntiAlias(true);
88 paint.setStrokeWidth(kBorderSize);
89 paint.setStyle(stroke_ ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
90
91 gfx::Rect rect(layer()->bounds().size());
92 recorder.canvas()->DrawCircle(rect.CenterPoint(),
93 rect.width() / 2 - kBorderSize / 2, paint);
94 }
95
96 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
97
98 void OnDeviceScaleFactorChanged(float device_scale_factor) override {
99 // Redrawing will take care of scale factor change.
100 }
101
102 base::Closure PrepareForLayerBoundsChange() override {
103 return base::Closure();
104 }
105
106 ui::Layer layer_;
107 bool stroke_;
108
109 DISALLOW_COPY_AND_ASSIGN(ContentMask);
110 };
111
112 // The border render draws the border as well as outline on both the outer and
113 // inner radius to increase visibility.
114 class PartialMagnificationController::BorderRenderer
115 : public ui::LayerDelegate {
116 public:
117 BorderRenderer(const gfx::Rect& bounds) { bounds_ = bounds; }
118
119 ~BorderRenderer() override {}
120
121 private:
122 // Overridden from LayerDelegate.
123 void OnPaintLayer(const ui::PaintContext& context) override {
124 ui::PaintRecorder recorder(context, bounds_.size());
125
126 // Draw the border.
127 SkPaint paint;
128 paint.setAntiAlias(true);
129 paint.setStrokeWidth(kBorderSize);
130 paint.setStyle(SkPaint::kStroke_Style);
131 paint.setColor(kBorderColor);
132 recorder.canvas()->DrawCircle(bounds_.CenterPoint(),
133 bounds_.width() / 2 - kBorderSize / 2, paint);
134
135 // Draw the border outlines.
136 paint.setStrokeWidth(kBorderOutlineThickness);
137 paint.setColor(kBorderOutlineColor);
138 recorder.canvas()->DrawCircle(
jdufault 2016/08/24 21:15:39 Add comment saying which circle is the inner outli
sammiequon 2016/08/25 19:47:39 Done.
139 bounds_.CenterPoint(),
140 bounds_.width() / 2 - kBorderOutlineThickness / 2, paint);
141 recorder.canvas()->DrawCircle(
142 bounds_.CenterPoint(),
143 bounds_.width() / 2 - kBorderSize + kBorderOutlineThickness / 2, paint);
144 }
145
146 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
147
148 void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
149
150 base::Closure PrepareForLayerBoundsChange() override {
151 return base::Closure();
152 }
153 gfx::Rect bounds_;
154
155 DISALLOW_COPY_AND_ASSIGN(BorderRenderer);
156 };
157
158 PartialMagnificationController::PartialMagnificationController() {
43 Shell::GetInstance()->AddPreTargetHandler(this); 159 Shell::GetInstance()->AddPreTargetHandler(this);
44 } 160 }
45 161
46 PartialMagnificationController::~PartialMagnificationController() { 162 PartialMagnificationController::~PartialMagnificationController() {
47 CloseMagnifierWindow(); 163 CloseMagnifierWindow();
48 164
49 Shell::GetInstance()->RemovePreTargetHandler(this); 165 Shell::GetInstance()->RemovePreTargetHandler(this);
50 } 166 }
51 167
52 void PartialMagnificationController::SetScale(float scale) { 168 void PartialMagnificationController::SetEnabled(bool enabled) {
53 if (!is_enabled_) 169 is_enabled_ = enabled;
170 SetActive(false);
171 }
172
173 void PartialMagnificationController::SwitchTargetRootWindowIfNeeded(
174 aura::Window* new_root_window) {
175 if (host_widget_ &&
176 new_root_window == host_widget_->GetNativeView()->GetRootWindow())
54 return; 177 return;
55 178
56 scale_ = scale; 179 if (!new_root_window)
180 new_root_window = GetCurrentRootWindow();
57 181
58 if (IsPartialMagnified()) { 182 if (is_enabled_ && is_active_) {
59 CreateMagnifierWindow();
60 } else {
61 CloseMagnifierWindow(); 183 CloseMagnifierWindow();
184 CreateMagnifierWindow(new_root_window);
62 } 185 }
63 } 186 }
64 187
65 void PartialMagnificationController::SetEnabled(bool enabled) { 188 void PartialMagnificationController::OnMouseEvent(ui::MouseEvent* event) {
66 if (enabled) { 189 OnLocatedEvent(event, event->pointer_details());
67 is_enabled_ = enabled;
68 SetScale(kDefaultPartialMagnifiedScale);
69 } else {
70 SetScale(kNonPartialMagnifiedScale);
71 is_enabled_ = enabled;
72 }
73 } 190 }
74 191
75 //////////////////////////////////////////////////////////////////////////////// 192 void PartialMagnificationController::OnTouchEvent(ui::TouchEvent* event) {
76 // PartialMagnificationController: ui::EventHandler implementation 193 OnLocatedEvent(event, event->pointer_details());
77
78 void PartialMagnificationController::OnMouseEvent(ui::MouseEvent* event) {
79 if (IsPartialMagnified() && event->type() == ui::ET_MOUSE_MOVED) {
80 aura::Window* target = static_cast<aura::Window*>(event->target());
81 aura::Window* current_root = target->GetRootWindow();
82 // TODO(zork): Handle the case where the event is captured on a different
83 // display, such as when a menu is opened.
84 gfx::Rect root_bounds = current_root->bounds();
85
86 if (root_bounds.Contains(event->root_location())) {
87 SwitchTargetRootWindow(current_root);
88
89 OnMouseMove(event->root_location());
90 }
91 }
92 } 194 }
93 195
94 ////////////////////////////////////////////////////////////////////////////////
95 // PartialMagnificationController: aura::WindowObserver implementation
96
97 void PartialMagnificationController::OnWindowDestroying(aura::Window* window) { 196 void PartialMagnificationController::OnWindowDestroying(aura::Window* window) {
98 CloseMagnifierWindow(); 197 CloseMagnifierWindow();
99 198
100 aura::Window* new_root_window = GetCurrentRootWindow(); 199 aura::Window* new_root_window = GetCurrentRootWindow();
101 if (new_root_window != window) 200 if (new_root_window != window)
102 SwitchTargetRootWindow(new_root_window); 201 SwitchTargetRootWindowIfNeeded(new_root_window);
103 } 202 }
104 203
105 void PartialMagnificationController::OnWidgetDestroying(views::Widget* widget) { 204 void PartialMagnificationController::OnWidgetDestroying(views::Widget* widget) {
106 DCHECK_EQ(widget, zoom_widget_); 205 DCHECK_EQ(widget, host_widget_);
107 RemoveZoomWidgetObservers(); 206 RemoveZoomWidgetObservers();
108 zoom_widget_ = NULL; 207 host_widget_ = nullptr;
109 } 208 }
110 209
111 void PartialMagnificationController::OnMouseMove( 210 void PartialMagnificationController::SetActive(bool active) {
112 const gfx::Point& location_in_root) { 211 // Fail if we're trying to activate while disabled.
113 gfx::Point origin(location_in_root); 212 DCHECK(is_enabled_ || !active);
114 213
115 origin.Offset(-kMagnifierWidth / 2, -kMagnifierHeight / 2); 214 is_active_ = active;
116 215 if (is_active_) {
117 if (zoom_widget_) { 216 CreateMagnifierWindow(GetCurrentRootWindow());
118 zoom_widget_->SetBounds( 217 } else {
119 gfx::Rect(origin.x(), origin.y(), kMagnifierWidth, kMagnifierHeight)); 218 CloseMagnifierWindow();
120 } 219 }
121 } 220 }
122 221
123 bool PartialMagnificationController::IsPartialMagnified() const { 222 void PartialMagnificationController::OnLocatedEvent(
124 return scale_ >= kMinPartialMagnifiedScaleThreshold; 223 ui::LocatedEvent* event,
224 const ui::PointerDetails& pointer_details) {
225 if (!is_enabled_)
226 return;
227
228 if (pointer_details.pointer_type != ui::EventPointerType::POINTER_TYPE_PEN)
229 return;
230
231 if (event->type() == ui::ET_MOUSE_PRESSED)
232 SetActive(true);
233
234 if (event->type() == ui::ET_MOUSE_RELEASED)
235 SetActive(false);
236
237 if (!is_active_)
238 return;
239
240 // If the previous root window was detached host_widget_ will be null;
241 // reconstruct it. We also need to change the root window if the cursor has
242 // crossed display boundries.
243 SwitchTargetRootWindowIfNeeded(GetCurrentRootWindow());
244
245 // If that failed for any reason return.
246 if (!host_widget_) {
247 SetActive(false);
248 return;
249 }
250
251 gfx::Point point = event->root_location();
252
253 // Remap point from where it was captured to the display it is actually on.
254 aura::Window* target = static_cast<aura::Window*>(event->target());
255 aura::Window* event_root = target->GetRootWindow();
256 aura::Window::ConvertPointToTarget(
257 event_root, host_widget_->GetNativeView()->GetRootWindow(), &point);
258
259 host_widget_->SetBounds(GetBounds(point));
260
261 event->StopPropagation();
125 } 262 }
126 263
127 void PartialMagnificationController::CreateMagnifierWindow() { 264 void PartialMagnificationController::CreateMagnifierWindow(
128 if (zoom_widget_) 265 aura::Window* root_window) {
129 return; 266 if (host_widget_ || !root_window)
130
131 aura::Window* root_window = GetCurrentRootWindow();
132 if (!root_window)
133 return; 267 return;
134 268
135 root_window->AddObserver(this); 269 root_window->AddObserver(this);
136 270
137 gfx::Point mouse( 271 gfx::Point mouse(
138 root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot()); 272 root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot());
139 273
140 zoom_widget_ = new views::Widget; 274 host_widget_ = new views::Widget;
141 views::Widget::InitParams params( 275 views::Widget::InitParams params(
142 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); 276 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
143 params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; 277 params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
144 params.accept_events = false; 278 params.accept_events = false;
279 params.bounds = GetBounds(mouse);
145 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 280 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
146 params.parent = root_window; 281 params.parent = root_window;
147 zoom_widget_->Init(params); 282 host_widget_->Init(params);
148 zoom_widget_->SetBounds(gfx::Rect(mouse.x() - kMagnifierWidth / 2, 283 host_widget_->set_focus_on_creation(false);
149 mouse.y() - kMagnifierHeight / 2, 284 host_widget_->Show();
150 kMagnifierWidth, kMagnifierHeight));
151 zoom_widget_->set_focus_on_creation(false);
152 zoom_widget_->Show();
153 285
154 aura::Window* window = zoom_widget_->GetNativeView(); 286 aura::Window* window = host_widget_->GetNativeView();
155 window->SetName(kPartialMagniferWindowName); 287 window->SetName(kPartialMagniferWindowName);
156 288
157 zoom_widget_->GetNativeView()->layer()->SetBounds( 289 ui::Layer* root_layer = host_widget_->GetNativeView()->layer();
158 gfx::Rect(0, 0, kMagnifierWidth, kMagnifierHeight));
159 zoom_widget_->GetNativeView()->layer()->SetBackgroundZoom(scale_, kZoomInset);
160 290
161 zoom_widget_->AddObserver(this); 291 zoom_layer_.reset(new ui::Layer(ui::LayerType::LAYER_SOLID_COLOR));
292 zoom_layer_->SetBounds(gfx::Rect(GetWindowSize()));
293 zoom_layer_->SetBackgroundZoom(kMagnificationScale, kZoomInset);
294 root_layer->Add(zoom_layer_.get());
295
296 border_layer_.reset(new ui::Layer(ui::LayerType::LAYER_TEXTURED));
297 border_layer_->SetBounds(gfx::Rect(GetWindowSize()));
298 border_layer_->set_delegate(new BorderRenderer(gfx::Rect(GetWindowSize())));
299 root_layer->Add(border_layer_.get());
300
301 border_mask_.reset(new ContentMask(true, GetWindowSize()));
302 border_layer_->SetMaskLayer(border_mask_->layer());
303
304 zoom_mask_.reset(new ContentMask(false, GetWindowSize()));
305 zoom_layer_->SetMaskLayer(zoom_mask_->layer());
306
307 host_widget_->AddObserver(this);
162 } 308 }
163 309
164 void PartialMagnificationController::CloseMagnifierWindow() { 310 void PartialMagnificationController::CloseMagnifierWindow() {
165 if (zoom_widget_) { 311 if (host_widget_) {
166 RemoveZoomWidgetObservers(); 312 RemoveZoomWidgetObservers();
167 zoom_widget_->Close(); 313 host_widget_->Close();
168 zoom_widget_ = NULL; 314 host_widget_ = nullptr;
169 } 315 }
170 } 316 }
171 317
172 void PartialMagnificationController::RemoveZoomWidgetObservers() { 318 void PartialMagnificationController::RemoveZoomWidgetObservers() {
173 DCHECK(zoom_widget_); 319 DCHECK(host_widget_);
174 zoom_widget_->RemoveObserver(this); 320 host_widget_->RemoveObserver(this);
175 aura::Window* root_window = zoom_widget_->GetNativeView()->GetRootWindow(); 321 aura::Window* root_window = host_widget_->GetNativeView()->GetRootWindow();
176 DCHECK(root_window); 322 DCHECK(root_window);
177 root_window->RemoveObserver(this); 323 root_window->RemoveObserver(this);
178 } 324 }
179 325
180 void PartialMagnificationController::SwitchTargetRootWindow(
181 aura::Window* new_root_window) {
182 if (zoom_widget_ &&
183 new_root_window == zoom_widget_->GetNativeView()->GetRootWindow())
184 return;
185
186 CloseMagnifierWindow();
187
188 // Recreate the magnifier window by updating the scale factor.
189 SetScale(GetScale());
190 }
191
192 aura::Window* PartialMagnificationController::GetCurrentRootWindow() {
193 aura::Window::Windows root_windows = Shell::GetAllRootWindows();
194 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
195 iter != root_windows.end(); ++iter) {
196 aura::Window* root_window = *iter;
197 if (root_window->ContainsPointInRoot(
198 root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot()))
199 return root_window;
200 }
201 return NULL;
202 }
203
204 } // namespace ash 326 } // namespace ash
OLDNEW
« no previous file with comments | « ash/magnifier/partial_magnification_controller.h ('k') | ash/magnifier/partial_magnification_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698