OLD | NEW |
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/wm/workspace/phantom_window_controller.h" | 5 #include "ash/wm/workspace/phantom_window_controller.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 | 8 |
9 #include "ash/ash_switches.h" | |
10 #include "ash/shell.h" | 9 #include "ash/shell.h" |
11 #include "ash/shell_window_ids.h" | 10 #include "ash/shell_window_ids.h" |
12 #include "ash/wm/coordinate_conversion.h" | 11 #include "ash/wm/coordinate_conversion.h" |
13 #include "grit/ash_resources.h" | 12 #include "grit/ash_resources.h" |
14 #include "third_party/skia/include/core/SkCanvas.h" | |
15 #include "ui/aura/window.h" | 13 #include "ui/aura/window.h" |
16 #include "ui/aura/window_event_dispatcher.h" | |
17 #include "ui/compositor/layer.h" | 14 #include "ui/compositor/layer.h" |
18 #include "ui/compositor/scoped_layer_animation_settings.h" | 15 #include "ui/compositor/scoped_layer_animation_settings.h" |
19 #include "ui/gfx/canvas.h" | |
20 #include "ui/gfx/skia_util.h" | |
21 #include "ui/views/background.h" | 16 #include "ui/views/background.h" |
22 #include "ui/views/painter.h" | 17 #include "ui/views/painter.h" |
23 #include "ui/views/view.h" | 18 #include "ui/views/view.h" |
24 #include "ui/views/widget/widget.h" | 19 #include "ui/views/widget/widget.h" |
25 | 20 |
26 namespace ash { | 21 namespace ash { |
27 namespace { | 22 namespace { |
28 | 23 |
29 // The duration of the show animation. | 24 // The duration of the show animation. |
30 const int kAnimationDurationMs = 200; | 25 const int kAnimationDurationMs = 200; |
31 | 26 |
32 // The size of the phantom window at the beginning of the show animation in | 27 // The size of the phantom window at the beginning of the show animation in |
33 // relation to the size of the phantom window at the end of the animation when | 28 // relation to the size of the phantom window at the end of the animation. |
34 // using the alternate caption button style. | 29 const float kStartBoundsRatio = 0.85f; |
35 const float kAlternateStyleStartBoundsRatio = 0.85f; | |
36 | 30 |
37 // The amount of pixels that the phantom window's shadow should extend past | 31 // The amount of pixels that the phantom window's shadow should extend past |
38 // the bounds passed into Show(). There is no shadow when not using the | 32 // the bounds passed into Show(). |
39 // alternate caption button style. | 33 const int kShadowThickness = 15; |
40 const int kAlternateStyleShadowThickness = 15; | |
41 | 34 |
42 // The minimum size of a phantom window including the shadow when using the | 35 // The minimum size of a phantom window including the shadow. The minimum size |
43 // alternate caption button style. The minimum size is derived from the size of | 36 // is derived from the size of the IDR_AURA_PHANTOM_WINDOW image assets. |
44 // the IDR_AURA_PHANTOM_WINDOW image assets. | 37 const int kMinSizeWithShadow = 100; |
45 const int kAlternateStyleMinSizeWithShadow = 100; | |
46 | 38 |
47 // Adjusts the phantom window's bounds so that the bounds: | 39 // Adjusts the phantom window's bounds so that the bounds: |
48 // - Include the size of the shadow. | 40 // - Include the size of the shadow. |
49 // - Have a size equal to or larger than the minimize phantom window size. | 41 // - Have a size equal to or larger than the minimum phantom window size. |
50 gfx::Rect GetAdjustedBoundsForAlternateStyle(const gfx::Rect& bounds) { | 42 gfx::Rect GetAdjustedBounds(const gfx::Rect& bounds) { |
51 int x_inset = std::max( | 43 int x_inset = std::max( |
52 static_cast<int>( | 44 static_cast<int>(ceil((kMinSizeWithShadow - bounds.width()) / 2.0f)), |
53 ceil((kAlternateStyleMinSizeWithShadow - bounds.width()) / 2.0f)), | 45 kShadowThickness); |
54 kAlternateStyleShadowThickness); | |
55 int y_inset = std::max( | 46 int y_inset = std::max( |
56 static_cast<int>( | 47 static_cast<int>(ceil((kMinSizeWithShadow - bounds.height()) / 2.0f)), |
57 ceil((kAlternateStyleMinSizeWithShadow - bounds.height()) / 2.0f)), | 48 kShadowThickness); |
58 kAlternateStyleShadowThickness); | |
59 | 49 |
60 gfx::Rect adjusted_bounds(bounds); | 50 gfx::Rect adjusted_bounds(bounds); |
61 adjusted_bounds.Inset(-x_inset, -y_inset); | 51 adjusted_bounds.Inset(-x_inset, -y_inset); |
62 return adjusted_bounds; | 52 return adjusted_bounds; |
63 } | 53 } |
64 | 54 |
65 // Starts an animation of |widget| to |new_bounds_in_screen|. No-op if |widget| | 55 // Starts an animation of |widget| to |new_bounds_in_screen|. No-op if |widget| |
66 // is NULL. | 56 // is NULL. |
67 void AnimateToBounds(views::Widget* widget, | 57 void AnimateToBounds(views::Widget* widget, |
68 const gfx::Rect& new_bounds_in_screen) { | 58 const gfx::Rect& new_bounds_in_screen) { |
69 if (!widget) | 59 if (!widget) |
70 return; | 60 return; |
71 | 61 |
72 ui::ScopedLayerAnimationSettings scoped_setter( | 62 ui::ScopedLayerAnimationSettings scoped_setter( |
73 widget->GetNativeWindow()->layer()->GetAnimator()); | 63 widget->GetNativeWindow()->layer()->GetAnimator()); |
74 scoped_setter.SetTweenType(gfx::Tween::EASE_IN); | 64 scoped_setter.SetTweenType(gfx::Tween::EASE_IN); |
75 scoped_setter.SetPreemptionStrategy( | 65 scoped_setter.SetPreemptionStrategy( |
76 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | 66 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
77 scoped_setter.SetTransitionDuration( | 67 scoped_setter.SetTransitionDuration( |
78 base::TimeDelta::FromMilliseconds(kAnimationDurationMs)); | 68 base::TimeDelta::FromMilliseconds(kAnimationDurationMs)); |
79 widget->SetBounds(new_bounds_in_screen); | 69 widget->SetBounds(new_bounds_in_screen); |
80 } | 70 } |
81 | 71 |
82 // EdgePainter ---------------------------------------------------------------- | |
83 | |
84 // Paints the background of the phantom window for window snapping. | |
85 class EdgePainter : public views::Painter { | |
86 public: | |
87 EdgePainter(); | |
88 virtual ~EdgePainter(); | |
89 | |
90 // views::Painter: | |
91 virtual gfx::Size GetMinimumSize() const OVERRIDE; | |
92 virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE; | |
93 | |
94 private: | |
95 DISALLOW_COPY_AND_ASSIGN(EdgePainter); | |
96 }; | |
97 | |
98 EdgePainter::EdgePainter() { | |
99 } | |
100 | |
101 EdgePainter::~EdgePainter() { | |
102 } | |
103 | |
104 gfx::Size EdgePainter::GetMinimumSize() const { | |
105 return gfx::Size(); | |
106 } | |
107 | |
108 void EdgePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { | |
109 const int kInsetSize = 4; | |
110 int x = kInsetSize; | |
111 int y = kInsetSize; | |
112 int w = size.width() - kInsetSize * 2; | |
113 int h = size.height() - kInsetSize * 2; | |
114 bool inset = (w > 0 && h > 0); | |
115 if (!inset) { | |
116 x = 0; | |
117 y = 0; | |
118 w = size.width(); | |
119 h = size.height(); | |
120 } | |
121 SkPaint paint; | |
122 paint.setColor(SkColorSetARGB(100, 0, 0, 0)); | |
123 paint.setStyle(SkPaint::kFill_Style); | |
124 paint.setAntiAlias(true); | |
125 const int kRoundRectSize = 4; | |
126 canvas->sk_canvas()->drawRoundRect( | |
127 gfx::RectToSkRect(gfx::Rect(x, y, w, h)), | |
128 SkIntToScalar(kRoundRectSize), SkIntToScalar(kRoundRectSize), paint); | |
129 if (!inset) | |
130 return; | |
131 | |
132 paint.setColor(SkColorSetARGB(200, 255, 255, 255)); | |
133 paint.setStyle(SkPaint::kStroke_Style); | |
134 paint.setStrokeWidth(SkIntToScalar(2)); | |
135 canvas->sk_canvas()->drawRoundRect( | |
136 gfx::RectToSkRect(gfx::Rect(x, y, w, h)), SkIntToScalar(kRoundRectSize), | |
137 SkIntToScalar(kRoundRectSize), paint); | |
138 } | |
139 | |
140 } // namespace | 72 } // namespace |
141 | 73 |
142 // PhantomWindowController ---------------------------------------------------- | 74 // PhantomWindowController ---------------------------------------------------- |
143 | 75 |
144 PhantomWindowController::PhantomWindowController(aura::Window* window) | 76 PhantomWindowController::PhantomWindowController(aura::Window* window) |
145 : window_(window), | 77 : window_(window) { |
146 phantom_below_window_(NULL) { | |
147 } | 78 } |
148 | 79 |
149 PhantomWindowController::~PhantomWindowController() { | 80 PhantomWindowController::~PhantomWindowController() { |
150 } | 81 } |
151 | 82 |
152 void PhantomWindowController::Show(const gfx::Rect& bounds_in_screen) { | 83 void PhantomWindowController::Show(const gfx::Rect& bounds_in_screen) { |
153 if (switches::UseAlternateFrameCaptionButtonStyle()) | 84 gfx::Rect adjusted_bounds_in_screen = GetAdjustedBounds(bounds_in_screen); |
154 ShowAlternate(bounds_in_screen); | |
155 else | |
156 ShowLegacy(bounds_in_screen); | |
157 } | |
158 | |
159 void PhantomWindowController::ShowAlternate(const gfx::Rect& bounds_in_screen) { | |
160 gfx::Rect adjusted_bounds_in_screen = | |
161 GetAdjustedBoundsForAlternateStyle(bounds_in_screen); | |
162 if (adjusted_bounds_in_screen == target_bounds_in_screen_) | 85 if (adjusted_bounds_in_screen == target_bounds_in_screen_) |
163 return; | 86 return; |
164 target_bounds_in_screen_ = adjusted_bounds_in_screen; | 87 target_bounds_in_screen_ = adjusted_bounds_in_screen; |
165 | 88 |
166 gfx::Rect start_bounds_in_screen = target_bounds_in_screen_; | 89 gfx::Rect start_bounds_in_screen = target_bounds_in_screen_; |
167 int start_width = std::max( | 90 int start_width = std::max( |
168 kAlternateStyleMinSizeWithShadow, | 91 kMinSizeWithShadow, |
169 static_cast<int>( | 92 static_cast<int>(start_bounds_in_screen.width() * kStartBoundsRatio)); |
170 start_bounds_in_screen.width() * kAlternateStyleStartBoundsRatio)); | |
171 int start_height = std::max( | 93 int start_height = std::max( |
172 kAlternateStyleMinSizeWithShadow, | 94 kMinSizeWithShadow, |
173 static_cast<int>( | 95 static_cast<int>(start_bounds_in_screen.height() * kStartBoundsRatio)); |
174 start_bounds_in_screen.height() * kAlternateStyleStartBoundsRatio)); | |
175 start_bounds_in_screen.Inset( | 96 start_bounds_in_screen.Inset( |
176 floor((start_bounds_in_screen.width() - start_width) / 2.0f), | 97 floor((start_bounds_in_screen.width() - start_width) / 2.0f), |
177 floor((start_bounds_in_screen.height() - start_height) / 2.0f)); | 98 floor((start_bounds_in_screen.height() - start_height) / 2.0f)); |
178 phantom_widget_in_target_root_ = CreatePhantomWidget( | 99 phantom_widget_ = CreatePhantomWidget( |
179 wm::GetRootWindowMatching(target_bounds_in_screen_), | 100 wm::GetRootWindowMatching(target_bounds_in_screen_), |
180 start_bounds_in_screen); | 101 start_bounds_in_screen); |
181 | 102 |
182 AnimateToBounds(phantom_widget_in_target_root_.get(), | 103 AnimateToBounds(phantom_widget_.get(), target_bounds_in_screen_); |
183 target_bounds_in_screen_); | |
184 } | |
185 | |
186 void PhantomWindowController::ShowLegacy(const gfx::Rect& bounds_in_screen) { | |
187 if (bounds_in_screen == target_bounds_in_screen_) | |
188 return; | |
189 target_bounds_in_screen_ = bounds_in_screen; | |
190 | |
191 gfx::Rect start_bounds_in_screen; | |
192 if (!phantom_widget_in_target_root_) { | |
193 start_bounds_in_screen = window_->GetBoundsInScreen(); | |
194 } else { | |
195 start_bounds_in_screen = | |
196 phantom_widget_in_target_root_->GetWindowBoundsInScreen(); | |
197 } | |
198 | |
199 aura::Window* target_root = | |
200 wm::GetRootWindowMatching(target_bounds_in_screen_); | |
201 if (!phantom_widget_in_target_root_ || | |
202 phantom_widget_in_target_root_->GetNativeWindow()->GetRootWindow() != | |
203 target_root) { | |
204 phantom_widget_in_target_root_ = | |
205 CreatePhantomWidget(target_root, start_bounds_in_screen); | |
206 } | |
207 AnimateToBounds(phantom_widget_in_target_root_.get(), | |
208 target_bounds_in_screen_); | |
209 | |
210 // Create a secondary widget in a second screen if |start_bounds_in_screen| | |
211 // lies at least partially in another screen. This allows animations to start | |
212 // or restart in one root window and progress to another root. | |
213 aura::Window* start_root = wm::GetRootWindowMatching(start_bounds_in_screen); | |
214 if (start_root == target_root) { | |
215 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); | |
216 for (size_t i = 0; i < root_windows.size(); ++i) { | |
217 if (root_windows[i] != target_root && | |
218 root_windows[i]->GetBoundsInScreen().Intersects( | |
219 start_bounds_in_screen)) { | |
220 start_root = root_windows[i]; | |
221 break; | |
222 } | |
223 } | |
224 } | |
225 if (start_root == target_root) { | |
226 phantom_widget_in_start_root_.reset(); | |
227 } else { | |
228 if (!phantom_widget_in_start_root_ || | |
229 phantom_widget_in_start_root_->GetNativeWindow()->GetRootWindow() != | |
230 start_root) { | |
231 phantom_widget_in_start_root_ = | |
232 CreatePhantomWidget(start_root, start_bounds_in_screen); | |
233 } | |
234 AnimateToBounds(phantom_widget_in_start_root_.get(), | |
235 target_bounds_in_screen_); | |
236 } | |
237 } | 104 } |
238 | 105 |
239 scoped_ptr<views::Widget> PhantomWindowController::CreatePhantomWidget( | 106 scoped_ptr<views::Widget> PhantomWindowController::CreatePhantomWidget( |
240 aura::Window* root_window, | 107 aura::Window* root_window, |
241 const gfx::Rect& bounds_in_screen) { | 108 const gfx::Rect& bounds_in_screen) { |
242 scoped_ptr<views::Widget> phantom_widget(new views::Widget); | 109 scoped_ptr<views::Widget> phantom_widget(new views::Widget); |
243 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); | 110 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); |
244 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; | 111 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
245 // PhantomWindowController is used by FrameMaximizeButton to highlight the | 112 // PhantomWindowController is used by FrameMaximizeButton to highlight the |
246 // launcher button. Put the phantom in the same window as the launcher so that | 113 // launcher button. Put the phantom in the same window as the launcher so that |
247 // the phantom is visible. | 114 // the phantom is visible. |
248 params.parent = Shell::GetContainer(root_window, | 115 params.parent = Shell::GetContainer(root_window, |
249 kShellWindowId_ShelfContainer); | 116 kShellWindowId_ShelfContainer); |
250 params.can_activate = false; | 117 params.can_activate = false; |
251 params.keep_on_top = true; | 118 params.keep_on_top = true; |
252 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 119 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
253 phantom_widget->set_focus_on_creation(false); | 120 phantom_widget->set_focus_on_creation(false); |
254 phantom_widget->Init(params); | 121 phantom_widget->Init(params); |
255 phantom_widget->SetVisibilityChangedAnimationsEnabled(false); | 122 phantom_widget->SetVisibilityChangedAnimationsEnabled(false); |
256 phantom_widget->GetNativeWindow()->SetName("PhantomWindow"); | 123 phantom_widget->GetNativeWindow()->SetName("PhantomWindow"); |
257 phantom_widget->GetNativeWindow()->set_id(kShellWindowId_PhantomWindow); | 124 phantom_widget->GetNativeWindow()->set_id(kShellWindowId_PhantomWindow); |
258 phantom_widget->SetBounds(bounds_in_screen); | 125 phantom_widget->SetBounds(bounds_in_screen); |
259 if (phantom_below_window_) | 126 phantom_widget->StackAbove(window_); |
260 phantom_widget->StackBelow(phantom_below_window_); | |
261 else | |
262 phantom_widget->StackAbove(window_); | |
263 | 127 |
264 views::Painter* background_painter = NULL; | 128 const int kImages[] = IMAGE_GRID(IDR_AURA_PHANTOM_WINDOW); |
265 if (switches::UseAlternateFrameCaptionButtonStyle()) { | 129 views::Painter* background_painter = |
266 const int kImages[] = IMAGE_GRID(IDR_AURA_PHANTOM_WINDOW); | 130 views::Painter::CreateImageGridPainter(kImages); |
267 background_painter = views::Painter::CreateImageGridPainter(kImages); | |
268 } else { | |
269 background_painter = new EdgePainter; | |
270 } | |
271 views::View* content_view = new views::View; | 131 views::View* content_view = new views::View; |
272 content_view->set_background( | 132 content_view->set_background( |
273 views::Background::CreateBackgroundPainter(true, background_painter)); | 133 views::Background::CreateBackgroundPainter(true, background_painter)); |
274 phantom_widget->SetContentsView(content_view); | 134 phantom_widget->SetContentsView(content_view); |
275 | 135 |
276 // Show the widget after all the setups. | 136 // Show the widget after all the setups. |
277 phantom_widget->Show(); | 137 phantom_widget->Show(); |
278 | 138 |
279 // Fade the window in. | 139 // Fade the window in. |
280 ui::Layer* widget_layer = phantom_widget->GetNativeWindow()->layer(); | 140 ui::Layer* widget_layer = phantom_widget->GetNativeWindow()->layer(); |
281 widget_layer->SetOpacity(0); | 141 widget_layer->SetOpacity(0); |
282 ui::ScopedLayerAnimationSettings scoped_setter(widget_layer->GetAnimator()); | 142 ui::ScopedLayerAnimationSettings scoped_setter(widget_layer->GetAnimator()); |
283 scoped_setter.SetTransitionDuration( | 143 scoped_setter.SetTransitionDuration( |
284 base::TimeDelta::FromMilliseconds(kAnimationDurationMs)); | 144 base::TimeDelta::FromMilliseconds(kAnimationDurationMs)); |
285 widget_layer->SetOpacity(1); | 145 widget_layer->SetOpacity(1); |
286 | 146 |
287 return phantom_widget.Pass(); | 147 return phantom_widget.Pass(); |
288 } | 148 } |
289 | 149 |
290 } // namespace ash | 150 } // namespace ash |
OLD | NEW |