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 19namespace { 20 21const float kMinPartialMagnifiedScaleThreshold = 1.1f; 22 23// Number of pixels to make the border of the magnified area. 24const int kZoomInset = 16; 25 26// Width of the magnified area. 27const int kMagnifierWidth = 200; 28 29// Height of the magnified area. 30const int kMagnifierHeight = 200; 31 32// Name of the magnifier window. 33const char kPartialMagniferWindowName[] = "PartialMagnifierWindow"; 34 35} // namespace 36 37namespace ash { 38 39PartialMagnificationController::PartialMagnificationController() 40 : is_on_zooming_(false), 41 is_enabled_(false), 42 scale_(kNonPartialMagnifiedScale), 43 zoom_widget_(NULL) { 44 Shell::GetInstance()->AddPreTargetHandler(this); 45} 46 47PartialMagnificationController::~PartialMagnificationController() { 48 CloseMagnifierWindow(); 49 50 Shell::GetInstance()->RemovePreTargetHandler(this); 51} 52 53void 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 66void 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 79void 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 98void 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 107void PartialMagnificationController::OnWidgetDestroying( 108 views::Widget* widget) { 109 DCHECK_EQ(widget, zoom_widget_); 110 RemoveZoomWidgetObservers(); 111 zoom_widget_ = NULL; 112} 113 114void 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 126bool PartialMagnificationController::IsPartialMagnified() const { 127 return scale_ >= kMinPartialMagnifiedScaleThreshold; 128} 129 130void 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 170void PartialMagnificationController::CloseMagnifierWindow() { 171 if (zoom_widget_) { 172 RemoveZoomWidgetObservers(); 173 zoom_widget_->Close(); 174 zoom_widget_ = NULL; 175 } 176} 177 178void 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 187void 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 199aura::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