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

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

Issue 665903003: Magnifier needs to follow the focus of the textfield. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rename IsInputFieldFocused to a better name. Created 6 years, 2 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/magnification_controller.h" 5 #include "ash/magnifier/magnification_controller.h"
6 6
7 #include "ash/accelerators/accelerator_controller.h" 7 #include "ash/accelerators/accelerator_controller.h"
8 #include "ash/accessibility_delegate.h" 8 #include "ash/accessibility_delegate.h"
9 #include "ash/ash_switches.h" 9 #include "ash/ash_switches.h"
10 #include "ash/display/root_window_transformers.h" 10 #include "ash/display/root_window_transformers.h"
11 #include "ash/host/ash_window_tree_host.h" 11 #include "ash/host/ash_window_tree_host.h"
12 #include "ash/host/root_window_transformer.h" 12 #include "ash/host/root_window_transformer.h"
13 #include "ash/root_window_controller.h" 13 #include "ash/root_window_controller.h"
14 #include "ash/shell.h" 14 #include "ash/shell.h"
15 #include "ash/system/tray/system_tray_delegate.h" 15 #include "ash/system/tray/system_tray_delegate.h"
16 #include "base/command_line.h" 16 #include "base/command_line.h"
17 #include "base/synchronization/waitable_event.h" 17 #include "base/synchronization/waitable_event.h"
18 #include "ui/aura/client/aura_constants.h"
18 #include "ui/aura/client/cursor_client.h" 19 #include "ui/aura/client/cursor_client.h"
19 #include "ui/aura/window.h" 20 #include "ui/aura/window.h"
20 #include "ui/aura/window_property.h"
21 #include "ui/aura/window_tree_host.h" 21 #include "ui/aura/window_tree_host.h"
22 #include "ui/base/ime/input_method.h"
23 #include "ui/base/ime/input_method_observer.h"
24 #include "ui/base/ime/text_input_client.h"
22 #include "ui/compositor/dip_util.h" 25 #include "ui/compositor/dip_util.h"
23 #include "ui/compositor/layer.h" 26 #include "ui/compositor/layer.h"
24 #include "ui/compositor/layer_animation_observer.h" 27 #include "ui/compositor/layer_animation_observer.h"
25 #include "ui/compositor/scoped_layer_animation_settings.h" 28 #include "ui/compositor/scoped_layer_animation_settings.h"
26 #include "ui/events/event.h" 29 #include "ui/events/event.h"
27 #include "ui/events/event_handler.h" 30 #include "ui/events/event_handler.h"
28 #include "ui/gfx/point3_f.h" 31 #include "ui/gfx/point3_f.h"
29 #include "ui/gfx/point_conversions.h" 32 #include "ui/gfx/point_conversions.h"
30 #include "ui/gfx/point_f.h" 33 #include "ui/gfx/point_f.h"
31 #include "ui/gfx/rect_conversions.h" 34 #include "ui/gfx/rect_conversions.h"
32 #include "ui/gfx/screen.h" 35 #include "ui/gfx/screen.h"
33 #include "ui/wm/core/compound_event_filter.h" 36 #include "ui/wm/core/compound_event_filter.h"
37 #include "ui/wm/core/coordinate_conversion.h"
34 38
35 namespace { 39 namespace {
36 40
37 const float kMaxMagnifiedScale = 4.0f; 41 const float kMaxMagnifiedScale = 4.0f;
38 const float kMaxMagnifiedScaleThreshold = 4.0f; 42 const float kMaxMagnifiedScaleThreshold = 4.0f;
39 const float kMinMagnifiedScaleThreshold = 1.1f; 43 const float kMinMagnifiedScaleThreshold = 1.1f;
40 const float kNonMagnifiedScale = 1.0f; 44 const float kNonMagnifiedScale = 1.0f;
41 45
42 const float kInitialMagnifiedScale = 2.0f; 46 const float kInitialMagnifiedScale = 2.0f;
43 const float kScrollScaleChangeFactor = 0.05f; 47 const float kScrollScaleChangeFactor = 0.05f;
44 48
45 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of 49 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
46 // |kPanningMergin| from the edge, the view-port moves. 50 // |kPanningMergin| from the edge, the view-port moves.
47 const int kPanningMergin = 100; 51 const int kPanningMergin = 100;
48 52
53 // Gives a little panning margin for following caret, so that we will move the
54 // view-port before the caret is completely out of sight.
55 const int kCaretPanningMargin = 10;
56
49 void MoveCursorTo(aura::WindowTreeHost* host, const gfx::Point& root_location) { 57 void MoveCursorTo(aura::WindowTreeHost* host, const gfx::Point& root_location) {
50 gfx::Point3F host_location_3f(root_location); 58 gfx::Point3F host_location_3f(root_location);
51 host->GetRootTransform().TransformPoint(&host_location_3f); 59 host->GetRootTransform().TransformPoint(&host_location_3f);
52 host->MoveCursorToHostLocation( 60 host->MoveCursorToHostLocation(
53 gfx::ToCeiledPoint(host_location_3f.AsPointF())); 61 gfx::ToCeiledPoint(host_location_3f.AsPointF()));
54 } 62 }
55 63
56 } // namespace 64 } // namespace
57 65
58 namespace ash { 66 namespace ash {
59 67
60 //////////////////////////////////////////////////////////////////////////////// 68 ////////////////////////////////////////////////////////////////////////////////
61 // MagnificationControllerImpl: 69 // MagnificationControllerImpl:
62 70
63 class MagnificationControllerImpl : virtual public MagnificationController, 71 class MagnificationControllerImpl : virtual public MagnificationController,
64 public ui::EventHandler, 72 public ui::EventHandler,
65 public ui::ImplicitAnimationObserver, 73 public ui::ImplicitAnimationObserver,
66 public aura::WindowObserver { 74 public aura::WindowObserver,
75 public ui::InputMethodObserver {
67 public: 76 public:
68 MagnificationControllerImpl(); 77 MagnificationControllerImpl();
69 virtual ~MagnificationControllerImpl(); 78 virtual ~MagnificationControllerImpl();
70 79
71 // MagnificationController overrides: 80 // MagnificationController overrides:
72 virtual void SetEnabled(bool enabled) override; 81 virtual void SetEnabled(bool enabled) override;
73 virtual bool IsEnabled() const override; 82 virtual bool IsEnabled() const override;
74 virtual void SetScale(float scale, bool animate) override; 83 virtual void SetScale(float scale, bool animate) override;
75 virtual float GetScale() const override { return scale_; } 84 virtual float GetScale() const override { return scale_; }
76 virtual void MoveWindow(int x, int y, bool animate) override; 85 virtual void MoveWindow(int x, int y, bool animate) override;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 gfx::Size GetHostSizeDIP() const; 146 gfx::Size GetHostSizeDIP() const;
138 147
139 // Correct the givin scale value if nessesary. 148 // Correct the givin scale value if nessesary.
140 void ValidateScale(float* scale); 149 void ValidateScale(float* scale);
141 150
142 // ui::EventHandler overrides: 151 // ui::EventHandler overrides:
143 virtual void OnMouseEvent(ui::MouseEvent* event) override; 152 virtual void OnMouseEvent(ui::MouseEvent* event) override;
144 virtual void OnScrollEvent(ui::ScrollEvent* event) override; 153 virtual void OnScrollEvent(ui::ScrollEvent* event) override;
145 virtual void OnTouchEvent(ui::TouchEvent* event) override; 154 virtual void OnTouchEvent(ui::TouchEvent* event) override;
146 155
156 ui::InputMethod* GetInputMethod();
157
158 // Moves the view port when |point| is located within
159 // |x_panning_margin| and |y_pannin_margin| to the edge of the visible
160 // window region. The view port will be moved so that the |point| will be
161 // moved to the point where it has |x_target_margin| and |y_target_margin|
162 // to the edge of the visible region.
163 void MoveMagnifierWindow(const gfx::Point& point,
164 int x_panning_margin,
165 int y_panning_margin,
166 int x_target_margin,
167 int y_target_margin);
168
169 // ui::InputMethodObserver overrides:
oshima 2014/10/23 18:36:49 // ui::InputMethodObserver:
jennyz 2014/10/23 19:34:56 Done.
170 virtual void OnTextInputTypeChanged(
171 const ui::TextInputClient* client) override {}
172 virtual void OnFocus() override {}
173 virtual void OnBlur() override {}
174 virtual void OnTextInputStateChanged(
175 const ui::TextInputClient* client) override {}
176 virtual void OnInputMethodDestroyed(
177 const ui::InputMethod* input_method) override {}
178 virtual void OnShowImeIfNeeded() override {}
179 virtual void OnCaretBoundsChanged(const ui::TextInputClient* client) override;
180
147 // Target root window. This must not be NULL. 181 // Target root window. This must not be NULL.
148 aura::Window* root_window_; 182 aura::Window* root_window_;
149 183
150 // True if the magnified window is currently animating a change. Otherwise, 184 // True if the magnified window is currently animating a change. Otherwise,
151 // false. 185 // false.
152 bool is_on_animation_; 186 bool is_on_animation_;
153 187
154 bool is_enabled_; 188 bool is_enabled_;
155 189
156 // True if the cursor needs to move the given position after the animation 190 // True if the cursor needs to move the given position after the animation
157 // will be finished. When using this, set |position_after_animation_| as well. 191 // will be finished. When using this, set |position_after_animation_| as well.
158 bool move_cursor_after_animation_; 192 bool move_cursor_after_animation_;
159 // Stores the position of cursor to be moved after animation. 193 // Stores the position of cursor to be moved after animation.
160 gfx::Point position_after_animation_; 194 gfx::Point position_after_animation_;
161 195
162 // Stores the last mouse cursor (or last touched) location. This value is 196 // Stores the last mouse cursor (or last touched) location. This value is
163 // used on zooming to keep this location visible. 197 // used on zooming to keep this location visible.
164 gfx::Point point_of_interest_; 198 gfx::Point point_of_interest_;
165 199
166 // Current scale, origin (left-top) position of the magnification window. 200 // Current scale, origin (left-top) position of the magnification window.
167 float scale_; 201 float scale_;
168 gfx::PointF origin_; 202 gfx::PointF origin_;
169 203
170 ScrollDirection scroll_direction_; 204 ScrollDirection scroll_direction_;
171 205
206 ui::InputMethod* input_method_;
oshima 2014/10/23 18:36:49 document ownership.
jennyz 2014/10/23 19:34:56 Done.
207
172 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl); 208 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl);
173 }; 209 };
174 210
175 //////////////////////////////////////////////////////////////////////////////// 211 ////////////////////////////////////////////////////////////////////////////////
176 // MagnificationControllerImpl: 212 // MagnificationControllerImpl:
177 213
178 MagnificationControllerImpl::MagnificationControllerImpl() 214 MagnificationControllerImpl::MagnificationControllerImpl()
179 : root_window_(Shell::GetPrimaryRootWindow()), 215 : root_window_(Shell::GetPrimaryRootWindow()),
180 is_on_animation_(false), 216 is_on_animation_(false),
181 is_enabled_(false), 217 is_enabled_(false),
182 move_cursor_after_animation_(false), 218 move_cursor_after_animation_(false),
183 scale_(kNonMagnifiedScale), 219 scale_(kNonMagnifiedScale),
184 scroll_direction_(SCROLL_NONE) { 220 scroll_direction_(SCROLL_NONE),
221 input_method_(NULL) {
185 Shell::GetInstance()->AddPreTargetHandler(this); 222 Shell::GetInstance()->AddPreTargetHandler(this);
186 root_window_->AddObserver(this); 223 root_window_->AddObserver(this);
187 point_of_interest_ = root_window_->bounds().CenterPoint(); 224 point_of_interest_ = root_window_->bounds().CenterPoint();
188 } 225 }
189 226
190 MagnificationControllerImpl::~MagnificationControllerImpl() { 227 MagnificationControllerImpl::~MagnificationControllerImpl() {
228 if (input_method_)
229 input_method_->RemoveObserver(this);
230
191 root_window_->RemoveObserver(this); 231 root_window_->RemoveObserver(this);
192 232
193 Shell::GetInstance()->RemovePreTargetHandler(this); 233 Shell::GetInstance()->RemovePreTargetHandler(this);
194 } 234 }
195 235
196 void MagnificationControllerImpl::RedrawKeepingMousePosition( 236 void MagnificationControllerImpl::RedrawKeepingMousePosition(
197 float scale, bool animate) { 237 float scale, bool animate) {
198 gfx::Point mouse_in_root = point_of_interest_; 238 gfx::Point mouse_in_root = point_of_interest_;
199 239
200 // mouse_in_root is invalid value when the cursor is hidden. 240 // mouse_in_root is invalid value when the cursor is hidden.
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 new_origin.Offset(0, kMoveOffset); 350 new_origin.Offset(0, kMoveOffset);
311 break; 351 break;
312 } 352 }
313 RedrawDIP(new_origin, scale_, true); 353 RedrawDIP(new_origin, scale_, true);
314 } 354 }
315 355
316 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) { 356 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) {
317 DCHECK(root_window_); 357 DCHECK(root_window_);
318 358
319 gfx::Point mouse(location); 359 gfx::Point mouse(location);
320
321 int x = origin_.x();
322 int y = origin_.y();
323 bool start_zoom = false;
324
325 const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
326 const int left = window_rect.x();
327 const int right = window_rect.right();
328 int margin = kPanningMergin / scale_; // No need to consider DPI. 360 int margin = kPanningMergin / scale_; // No need to consider DPI.
329 361 MoveMagnifierWindow(mouse, margin, margin, margin, margin);
330 int x_diff = 0;
331
332 if (mouse.x() < left + margin) {
333 // Panning left.
334 x_diff = mouse.x() - (left + margin);
335 start_zoom = true;
336 } else if (right - margin < mouse.x()) {
337 // Panning right.
338 x_diff = mouse.x() - (right - margin);
339 start_zoom = true;
340 }
341 x = left + x_diff;
342
343 const int top = window_rect.y();
344 const int bottom = window_rect.bottom();
345
346 int y_diff = 0;
347 if (mouse.y() < top + margin) {
348 // Panning up.
349 y_diff = mouse.y() - (top + margin);
350 start_zoom = true;
351 } else if (bottom - margin < mouse.y()) {
352 // Panning down.
353 y_diff = mouse.y() - (bottom - margin);
354 start_zoom = true;
355 }
356 y = top + y_diff;
357
358 if (start_zoom && !is_on_animation_) {
359 // No animation on panning.
360 bool animate = false;
361 bool ret = RedrawDIP(gfx::Point(x, y), scale_, animate);
362
363 if (ret) {
364 // If the magnified region is moved, hides the mouse cursor and moves it.
365 if (x_diff != 0 || y_diff != 0)
366 MoveCursorTo(root_window_->GetHost(), mouse);
367 }
368 }
369 } 362 }
370 363
371 void MagnificationControllerImpl::AfterAnimationMoveCursorTo( 364 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
372 const gfx::Point& location) { 365 const gfx::Point& location) {
373 DCHECK(root_window_); 366 DCHECK(root_window_);
374 367
375 aura::client::CursorClient* cursor_client = 368 aura::client::CursorClient* cursor_client =
376 aura::client::GetCursorClient(root_window_); 369 aura::client::GetCursorClient(root_window_);
377 if (cursor_client) { 370 if (cursor_client) {
378 // When cursor is invisible, do not move or show the cursor after the 371 // When cursor is invisible, do not move or show the cursor after the
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 503
511 void MagnificationControllerImpl::SetScrollDirection( 504 void MagnificationControllerImpl::SetScrollDirection(
512 ScrollDirection direction) { 505 ScrollDirection direction) {
513 scroll_direction_ = direction; 506 scroll_direction_ = direction;
514 StartOrStopScrollIfNecessary(); 507 StartOrStopScrollIfNecessary();
515 } 508 }
516 509
517 void MagnificationControllerImpl::SetEnabled(bool enabled) { 510 void MagnificationControllerImpl::SetEnabled(bool enabled) {
518 Shell* shell = Shell::GetInstance(); 511 Shell* shell = Shell::GetInstance();
519 if (enabled) { 512 if (enabled) {
513 if (!input_method_) {
514 input_method_ = GetInputMethod();
515 if (input_method_)
516 input_method_->AddObserver(this);
517 }
518
520 float scale = 519 float scale =
521 Shell::GetInstance()->accessibility_delegate()-> 520 Shell::GetInstance()->accessibility_delegate()->
522 GetSavedScreenMagnifierScale(); 521 GetSavedScreenMagnifierScale();
523 if (scale <= 0.0f) 522 if (scale <= 0.0f)
524 scale = kInitialMagnifiedScale; 523 scale = kInitialMagnifiedScale;
525 ValidateScale(&scale); 524 ValidateScale(&scale);
526 525
527 // Do nothing, if already enabled with same scale. 526 // Do nothing, if already enabled with same scale.
528 if (is_enabled_ && scale == scale_) 527 if (is_enabled_ && scale == scale_)
529 return; 528 return;
530 529
531 is_enabled_ = enabled; 530 is_enabled_ = enabled;
532 RedrawKeepingMousePosition(scale, true); 531 RedrawKeepingMousePosition(scale, true);
533 shell->accessibility_delegate()->SaveScreenMagnifierScale(scale); 532 shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
534 } else { 533 } else {
535 // Do nothing, if already disabled. 534 // Do nothing, if already disabled.
536 if (!is_enabled_) 535 if (!is_enabled_)
537 return; 536 return;
538 537
538 if (input_method_) {
539 input_method_->RemoveObserver(this);
540 input_method_ = NULL;
541 }
542
539 RedrawKeepingMousePosition(kNonMagnifiedScale, true); 543 RedrawKeepingMousePosition(kNonMagnifiedScale, true);
540 is_enabled_ = enabled; 544 is_enabled_ = enabled;
541 } 545 }
542 } 546 }
543 547
544 bool MagnificationControllerImpl::IsEnabled() const { 548 bool MagnificationControllerImpl::IsEnabled() const {
545 return is_enabled_; 549 return is_enabled_;
546 } 550 }
547 551
548 //////////////////////////////////////////////////////////////////////////////// 552 ////////////////////////////////////////////////////////////////////////////////
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
590 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent* event) { 594 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent* event) {
591 aura::Window* target = static_cast<aura::Window*>(event->target()); 595 aura::Window* target = static_cast<aura::Window*>(event->target());
592 aura::Window* current_root = target->GetRootWindow(); 596 aura::Window* current_root = target->GetRootWindow();
593 if (current_root == root_window_) { 597 if (current_root == root_window_) {
594 gfx::Rect root_bounds = current_root->bounds(); 598 gfx::Rect root_bounds = current_root->bounds();
595 if (root_bounds.Contains(event->root_location())) 599 if (root_bounds.Contains(event->root_location()))
596 point_of_interest_ = event->root_location(); 600 point_of_interest_ = event->root_location();
597 } 601 }
598 } 602 }
599 603
604 ui::InputMethod* MagnificationControllerImpl::GetInputMethod() {
605 return root_window_->GetProperty(aura::client::kRootWindowInputMethodKey);
oshima 2014/10/23 18:36:49 optinoal: you may just want to inline this.
jennyz 2014/10/23 19:34:56 Done.
606 }
607
608 void MagnificationControllerImpl::MoveMagnifierWindow(const gfx::Point& point,
609 int x_panning_margin,
610 int y_panning_margin,
611 int x_target_margin,
612 int y_target_margin) {
613 DCHECK(root_window_);
614 int x = origin_.x();
615 int y = origin_.y();
616 bool start_zoom = false;
617
618 const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
619 const int left = window_rect.x();
620 const int right = window_rect.right();
621
622 int x_diff = 0;
623 if (point.x() < left + x_panning_margin) {
624 // Panning left.
625 x_diff = point.x() - (left + x_target_margin);
626 start_zoom = true;
627 } else if (right - x_panning_margin < point.x()) {
628 // Panning right.
629 x_diff = point.x() - (right - x_target_margin);
630 start_zoom = true;
631 }
632 x = left + x_diff;
633
634 const int top = window_rect.y();
635 const int bottom = window_rect.bottom();
636
637 int y_diff = 0;
638 if (point.y() < top + y_panning_margin) {
639 // Panning up.
640 y_diff = point.y() - (top + y_target_margin);
641 start_zoom = true;
642 } else if (bottom - y_panning_margin < point.y()) {
643 // Panning down.
644 y_diff = point.y() - (bottom - y_target_margin);
645 start_zoom = true;
646 }
647 y = top + y_diff;
648 if (start_zoom && !is_on_animation_) {
649 // No animation on panning.
650 bool animate = false;
651 bool ret = RedrawDIP(gfx::Point(x, y), scale_, animate);
652
653 if (ret) {
654 // If the magnified region is moved, hides the mouse cursor and moves it.
655 if (x_diff != 0 || y_diff != 0)
656 MoveCursorTo(root_window_->GetHost(), point);
657 }
658 }
659 }
660
661 void MagnificationControllerImpl::OnCaretBoundsChanged(
662 const ui::TextInputClient* client) {
663 // caret bounds in screen coordinates.
664 const gfx::Rect caret_bounds = client->GetCaretBounds();
665 // Note: OnCaretBoundsChanged could be fired OnTextInputTypeChanged during
666 // which the caret position is not set a meaning position, and we do not
667 // need to adjust the view port position based on the bogus caret position.
668 // This is only a transition period, the caret position will be fixed upon
669 // focusing right after.
670 if (caret_bounds.width() == 0 && caret_bounds.height() == 0)
671 return;
672
673 gfx::Point caret_origin = caret_bounds.origin();
674 // caret_origin in |root_window_| coordinates.
675 wm::ConvertPointFromScreen(root_window_, &caret_origin);
676
677 // Visible window_rect in |root_window_| coordinates.
678 const gfx::Rect visible_window_rect =
679 gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
680
681 const int panning_margin = kCaretPanningMargin / scale_;
682 MoveMagnifierWindow(caret_origin,
683 panning_margin,
684 panning_margin,
685 visible_window_rect.width() / 2,
686 visible_window_rect.height() / 2);
687 }
688
600 //////////////////////////////////////////////////////////////////////////////// 689 ////////////////////////////////////////////////////////////////////////////////
601 // MagnificationController: 690 // MagnificationController:
602 691
603 // static 692 // static
604 MagnificationController* MagnificationController::CreateInstance() { 693 MagnificationController* MagnificationController::CreateInstance() {
605 return new MagnificationControllerImpl(); 694 return new MagnificationControllerImpl();
606 } 695 }
607 696
608 } // namespace ash 697 } // namespace ash
OLDNEW
« no previous file with comments | « no previous file | ash/magnifier/magnification_controller_unittest.cc » ('j') | ash/magnifier/magnification_controller_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698