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/wm/workspace/phantom_window_controller.h" 6 7 #include "ash/shell.h" 8 #include "ash/shell_window_ids.h" 9 #include "ash/wm/coordinate_conversion.h" 10 #include "third_party/skia/include/core/SkCanvas.h" 11 #include "ui/aura/root_window.h" 12 #include "ui/aura/window.h" 13 #include "ui/compositor/layer.h" 14 #include "ui/compositor/scoped_layer_animation_settings.h" 15 #include "ui/gfx/animation/slide_animation.h" 16 #include "ui/gfx/canvas.h" 17 #include "ui/gfx/skia_util.h" 18 #include "ui/views/background.h" 19 #include "ui/views/painter.h" 20 #include "ui/views/view.h" 21 #include "ui/views/widget/widget.h" 22 23 namespace ash { 24 namespace internal { 25 26 // EdgePainter ---------------------------------------------------------------- 27 28 namespace { 29 30 // Paints the background of the phantom window for window snapping. 31 class EdgePainter : public views::Painter { 32 public: 33 EdgePainter(); 34 virtual ~EdgePainter(); 35 36 // views::Painter: 37 virtual gfx::Size GetMinimumSize() const OVERRIDE; 38 virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE; 39 40 private: 41 DISALLOW_COPY_AND_ASSIGN(EdgePainter); 42 }; 43 44 } // namespace 45 46 47 EdgePainter::EdgePainter() { 48 } 49 50 EdgePainter::~EdgePainter() { 51 } 52 53 gfx::Size EdgePainter::GetMinimumSize() const { 54 return gfx::Size(); 55 } 56 57 void EdgePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { 58 const int kInsetSize = 4; 59 int x = kInsetSize; 60 int y = kInsetSize; 61 int w = size.width() - kInsetSize * 2; 62 int h = size.height() - kInsetSize * 2; 63 bool inset = (w > 0 && h > 0); 64 if (!inset) { 65 x = 0; 66 y = 0; 67 w = size.width(); 68 h = size.height(); 69 } 70 SkPaint paint; 71 paint.setColor(SkColorSetARGB(100, 0, 0, 0)); 72 paint.setStyle(SkPaint::kFill_Style); 73 paint.setAntiAlias(true); 74 const int kRoundRectSize = 4; 75 canvas->sk_canvas()->drawRoundRect( 76 gfx::RectToSkRect(gfx::Rect(x, y, w, h)), 77 SkIntToScalar(kRoundRectSize), SkIntToScalar(kRoundRectSize), paint); 78 if (!inset) 79 return; 80 81 paint.setColor(SkColorSetARGB(200, 255, 255, 255)); 82 paint.setStyle(SkPaint::kStroke_Style); 83 paint.setStrokeWidth(SkIntToScalar(2)); 84 canvas->sk_canvas()->drawRoundRect( 85 gfx::RectToSkRect(gfx::Rect(x, y, w, h)), SkIntToScalar(kRoundRectSize), 86 SkIntToScalar(kRoundRectSize), paint); 87 } 88 89 90 // PhantomWindowController ---------------------------------------------------- 91 92 PhantomWindowController::PhantomWindowController(aura::Window* window) 93 : window_(window), 94 phantom_below_window_(NULL), 95 phantom_widget_(NULL), 96 phantom_widget_start_(NULL) { 97 } 98 99 PhantomWindowController::~PhantomWindowController() { 100 Hide(); 101 } 102 103 void PhantomWindowController::Show(const gfx::Rect& bounds_in_screen) { 104 if (bounds_in_screen == bounds_in_screen_) 105 return; 106 bounds_in_screen_ = bounds_in_screen; 107 aura::Window* target_root = wm::GetRootWindowMatching(bounds_in_screen); 108 // Show the phantom at the current bounds of the window. We'll animate to the 109 // target bounds. If phantom exists, update the start bounds. 110 if (!phantom_widget_) 111 start_bounds_ = window_->GetBoundsInScreen(); 112 else 113 start_bounds_ = phantom_widget_->GetWindowBoundsInScreen(); 114 if (phantom_widget_ && 115 phantom_widget_->GetNativeWindow()->GetRootWindow() != target_root) { 116 phantom_widget_->Close(); 117 phantom_widget_ = NULL; 118 } 119 if (!phantom_widget_) 120 phantom_widget_ = CreatePhantomWidget(target_root, start_bounds_); 121 122 // Create a secondary widget in a second screen if start_bounds_ lie at least 123 // partially in that other screen. This allows animations to start or restart 124 // in one root window and progress into another root. 125 aura::Window* start_root = wm::GetRootWindowMatching(start_bounds_); 126 if (start_root == target_root) { 127 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 128 for (size_t i = 0; i < root_windows.size(); ++i) { 129 if (root_windows[i] != target_root && 130 root_windows[i]->GetBoundsInScreen().Intersects(start_bounds_)) { 131 start_root = root_windows[i]; 132 break; 133 } 134 } 135 } 136 if (phantom_widget_start_ && 137 (phantom_widget_start_->GetNativeWindow()->GetRootWindow() != start_root 138 || start_root == target_root)) { 139 phantom_widget_start_->Close(); 140 phantom_widget_start_ = NULL; 141 } 142 if (!phantom_widget_start_ && start_root != target_root) 143 phantom_widget_start_ = CreatePhantomWidget(start_root, start_bounds_); 144 145 animation_.reset(new gfx::SlideAnimation(this)); 146 animation_->SetTweenType(gfx::Tween::EASE_IN); 147 const int kAnimationDurationMS = 200; 148 animation_->SetSlideDuration(kAnimationDurationMS); 149 animation_->Show(); 150 } 151 152 void PhantomWindowController::Hide() { 153 if (phantom_widget_) 154 phantom_widget_->Close(); 155 phantom_widget_ = NULL; 156 if (phantom_widget_start_) 157 phantom_widget_start_->Close(); 158 phantom_widget_start_ = NULL; 159 } 160 161 bool PhantomWindowController::IsShowing() const { 162 return phantom_widget_ != NULL; 163 } 164 165 void PhantomWindowController::AnimationProgressed( 166 const gfx::Animation* animation) { 167 const gfx::Rect current_bounds = 168 animation->CurrentValueBetween(start_bounds_, bounds_in_screen_); 169 if (phantom_widget_start_) 170 phantom_widget_start_->SetBounds(current_bounds); 171 phantom_widget_->SetBounds(current_bounds); 172 } 173 174 views::Widget* PhantomWindowController::CreatePhantomWidget( 175 aura::Window* root_window, 176 const gfx::Rect& bounds_in_screen) { 177 views::Widget* phantom_widget = new views::Widget; 178 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); 179 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 180 // PhantomWindowController is used by FrameMaximizeButton to highlight the 181 // launcher button. Put the phantom in the same window as the launcher so that 182 // the phantom is visible. 183 params.parent = Shell::GetContainer(root_window, 184 kShellWindowId_ShelfContainer); 185 params.can_activate = false; 186 params.keep_on_top = true; 187 phantom_widget->set_focus_on_creation(false); 188 phantom_widget->Init(params); 189 phantom_widget->SetVisibilityChangedAnimationsEnabled(false); 190 phantom_widget->GetNativeWindow()->SetName("PhantomWindow"); 191 phantom_widget->GetNativeWindow()->set_id(kShellWindowId_PhantomWindow); 192 views::View* content_view = new views::View; 193 content_view->set_background( 194 views::Background::CreateBackgroundPainter(true, new EdgePainter)); 195 phantom_widget->SetContentsView(content_view); 196 phantom_widget->SetBounds(bounds_in_screen); 197 if (phantom_below_window_) 198 phantom_widget->StackBelow(phantom_below_window_); 199 else 200 phantom_widget->StackAbove(window_); 201 202 // Show the widget after all the setups. 203 phantom_widget->Show(); 204 205 // Fade the window in. 206 ui::Layer* widget_layer = phantom_widget->GetNativeWindow()->layer(); 207 widget_layer->SetOpacity(0); 208 ui::ScopedLayerAnimationSettings scoped_setter(widget_layer->GetAnimator()); 209 widget_layer->SetOpacity(1); 210 return phantom_widget; 211 } 212 213 } // namespace internal 214 } // namespace ash 215