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 3 // found in the LICENSE file. 4 5 #include "ash/magnifier/partial_magnification_controller.h" 6 7 #include "ash/shell.h" 8 #include "ash/shell_window_ids.h" 9 #include "ui/aura/root_window.h" 10 #include "ui/views/corewm/compound_event_filter.h" 11 #include "ui/aura/window.h" 12 #include "ui/aura/window_property.h" 13 #include "ui/gfx/screen.h" 14 #include "ui/compositor/layer.h" 15 #include "ui/views/layout/fill_layout.h" 16 #include "ui/views/widget/widget.h" 17 #include "ui/views/widget/widget_delegate.h" 18 19 namespace { 20 21 const float kMinPartialMagnifiedScaleThreshold = 1.1f; 22 23 // Number of pixels to make the border of the magnified area. 24 const int kZoomInset = 16; 25 26 // Width of the magnified area. 27 const int kMagnifierWidth = 200; 28 29 // Height of the magnified area. 30 const int kMagnifierHeight = 200; 31 32 // Name of the magnifier window. 33 const char kPartialMagniferWindowName[] = "PartialMagnifierWindow"; 34 35 } // namespace 36 37 namespace ash { 38 39 PartialMagnificationController::PartialMagnificationController() 40 : is_on_zooming_(false), 41 is_enabled_(false), 42 scale_(kNonPartialMagnifiedScale), 43 zoom_widget_(NULL) { 44 Shell::GetInstance()->AddPreTargetHandler(this); 45 } 46 47 PartialMagnificationController::~PartialMagnificationController() { 48 CloseMagnifierWindow(); 49 50 Shell::GetInstance()->RemovePreTargetHandler(this); 51 } 52 53 void PartialMagnificationController::SetScale(float scale) { 54 if (!is_enabled_) 55 return; 56 57 scale_ = scale; 58 59 if (IsPartialMagnified()) { 60 CreateMagnifierWindow(); 61 } else { 62 CloseMagnifierWindow(); 63 } 64 } 65 66 void PartialMagnificationController::SetEnabled(bool enabled) { 67 if (enabled) { 68 is_enabled_ = enabled; 69 SetScale(kDefaultPartialMagnifiedScale); 70 } else { 71 SetScale(kNonPartialMagnifiedScale); 72 is_enabled_ = enabled; 73 } 74 } 75 76 //////////////////////////////////////////////////////////////////////////////// 77 // PartialMagnificationController: ui::EventHandler implementation 78 79 void PartialMagnificationController::OnMouseEvent(ui::MouseEvent* event) { 80 if (IsPartialMagnified() && event->type() == ui::ET_MOUSE_MOVED) { 81 aura::Window* target = static_cast<aura::Window*>(event->target()); 82 aura::RootWindow* current_root = target->GetRootWindow(); 83 // TODO(zork): Handle the case where the event is captured on a different 84 // display, such as when a menu is opened. 85 gfx::Rect root_bounds = current_root->bounds(); 86 87 if (root_bounds.Contains(event->root_location())) { 88 SwitchTargetRootWindow(current_root); 89 90 OnMouseMove(event->root_location()); 91 } 92 } 93 } 94 95 //////////////////////////////////////////////////////////////////////////////// 96 // PartialMagnificationController: aura::WindowObserver implementation 97 98 void PartialMagnificationController::OnWindowDestroying( 99 aura::Window* window) { 100 CloseMagnifierWindow(); 101 102 aura::RootWindow* new_root_window = GetCurrentRootWindow(); 103 if (new_root_window != window) 104 SwitchTargetRootWindow(new_root_window); 105 } 106 107 void PartialMagnificationController::OnWidgetDestroying( 108 views::Widget* widget) { 109 DCHECK_EQ(widget, zoom_widget_); 110 RemoveZoomWidgetObservers(); 111 zoom_widget_ = NULL; 112 } 113 114 void PartialMagnificationController::OnMouseMove( 115 const gfx::Point& location_in_root) { 116 gfx::Point origin(location_in_root); 117 118 origin.Offset(-kMagnifierWidth / 2, -kMagnifierHeight / 2); 119 120 if (zoom_widget_) { 121 zoom_widget_->SetBounds(gfx::Rect(origin.x(), origin.y(), 122 kMagnifierWidth, kMagnifierHeight)); 123 } 124 } 125 126 bool PartialMagnificationController::IsPartialMagnified() const { 127 return scale_ >= kMinPartialMagnifiedScaleThreshold; 128 } 129 130 void PartialMagnificationController::CreateMagnifierWindow() { 131 if (zoom_widget_) 132 return; 133 134 aura::RootWindow* root_window = GetCurrentRootWindow(); 135 if (!root_window) 136 return; 137 138 root_window->AddObserver(this); 139 140 gfx::Point mouse(root_window->GetLastMouseLocationInRoot()); 141 142 zoom_widget_ = new views::Widget; 143 views::Widget::InitParams params( 144 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); 145 params.can_activate = false; 146 params.accept_events = false; 147 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 148 params.parent = root_window; 149 zoom_widget_->Init(params); 150 zoom_widget_->SetBounds(gfx::Rect(mouse.x() - kMagnifierWidth / 2, 151 mouse.y() - kMagnifierHeight / 2, 152 kMagnifierWidth, kMagnifierHeight)); 153 zoom_widget_->set_focus_on_creation(false); 154 zoom_widget_->Show(); 155 156 aura::Window* window = zoom_widget_->GetNativeView(); 157 window->SetName(kPartialMagniferWindowName); 158 159 zoom_widget_->GetNativeView()->layer()->SetBounds( 160 gfx::Rect(0, 0, 161 kMagnifierWidth, 162 kMagnifierHeight)); 163 zoom_widget_->GetNativeView()->layer()->SetBackgroundZoom( 164 scale_, 165 kZoomInset); 166 167 zoom_widget_->AddObserver(this); 168 } 169 170 void PartialMagnificationController::CloseMagnifierWindow() { 171 if (zoom_widget_) { 172 RemoveZoomWidgetObservers(); 173 zoom_widget_->Close(); 174 zoom_widget_ = NULL; 175 } 176 } 177 178 void PartialMagnificationController::RemoveZoomWidgetObservers() { 179 DCHECK(zoom_widget_); 180 zoom_widget_->RemoveObserver(this); 181 aura::RootWindow* root_window = 182 zoom_widget_->GetNativeView()->GetRootWindow(); 183 DCHECK(root_window); 184 root_window->RemoveObserver(this); 185 } 186 187 void PartialMagnificationController::SwitchTargetRootWindow( 188 aura::RootWindow* new_root_window) { 189 if (zoom_widget_ && 190 new_root_window == zoom_widget_->GetNativeView()->GetRootWindow()) 191 return; 192 193 CloseMagnifierWindow(); 194 195 // Recreate the magnifier window by updating the scale factor. 196 SetScale(GetScale()); 197 } 198 199 aura::RootWindow* PartialMagnificationController::GetCurrentRootWindow() { 200 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 201 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); 202 iter != root_windows.end(); ++iter) { 203 aura::RootWindow* root_window = *iter; 204 if (root_window->ContainsPointInRoot( 205 root_window->GetLastMouseLocationInRoot())) 206 return root_window; 207 } 208 return NULL; 209 } 210 211 } // namespace ash 212