OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/window_selector.h" | 5 #include "ash/wm/window_selector.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "ash/screen_ash.h" | 9 #include "ash/screen_ash.h" |
10 #include "ash/shell.h" | 10 #include "ash/shell.h" |
11 #include "ash/shell_window_ids.h" | 11 #include "ash/shell_window_ids.h" |
12 #include "ash/wm/window_selector_delegate.h" | 12 #include "ash/wm/window_selector_delegate.h" |
13 #include "ash/wm/window_util.h" | 13 #include "ash/wm/window_util.h" |
14 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 15 #include "third_party/skia/include/core/SkColor.h" |
15 #include "ui/aura/client/aura_constants.h" | 16 #include "ui/aura/client/aura_constants.h" |
| 17 #include "ui/aura/client/screen_position_client.h" |
16 #include "ui/aura/root_window.h" | 18 #include "ui/aura/root_window.h" |
17 #include "ui/aura/window.h" | 19 #include "ui/aura/window.h" |
18 #include "ui/base/events/event.h" | 20 #include "ui/base/events/event.h" |
| 21 #include "ui/compositor/layer_animation_observer.h" |
19 #include "ui/compositor/scoped_layer_animation_settings.h" | 22 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 23 #include "ui/gfx/display.h" |
20 #include "ui/gfx/interpolated_transform.h" | 24 #include "ui/gfx/interpolated_transform.h" |
21 #include "ui/gfx/transform_util.h" | 25 #include "ui/gfx/transform_util.h" |
| 26 #include "ui/views/corewm/shadow_types.h" |
22 #include "ui/views/corewm/window_animations.h" | 27 #include "ui/views/corewm/window_animations.h" |
| 28 #include "ui/views/corewm/window_util.h" |
| 29 #include "ui/views/widget/widget.h" |
23 | 30 |
24 namespace ash { | 31 namespace ash { |
25 | 32 |
26 namespace { | 33 namespace { |
27 | 34 |
28 const float kCardAspectRatio = 4.0f / 3.0f; | 35 const float kCardAspectRatio = 4.0f / 3.0f; |
29 const int kWindowMargin = 20; | 36 const int kWindowMargin = 30; |
30 const int kMinCardsMajor = 3; | 37 const int kMinCardsMajor = 3; |
31 const int kOverviewTransitionMilliseconds = 100; | 38 const int kOverviewTransitionMilliseconds = 100; |
32 | 39 const SkColor kWindowSelectorSelectionColor = SK_ColorBLACK; |
33 // Applies a transform to |window| to fit within |target_bounds| while | 40 const float kWindowSelectorSelectionOpacity = 0.5f; |
34 // maintaining its aspect ratio. | 41 const int kWindowSelectorSelectionPadding = 15; |
35 void TransformWindowToFitBounds(aura::Window* window, | 42 |
36 const gfx::Rect& target_bounds) { | 43 // Creates a copy of |window| with |recreated_layer| in the |target_root|. |
37 const gfx::Rect bounds = window->bounds(); | 44 views::Widget* CreateCopyOfWindow(aura::RootWindow* target_root, |
| 45 aura::Window* src_window, |
| 46 ui::Layer* recreated_layer) { |
| 47 views::Widget* widget = new views::Widget; |
| 48 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); |
| 49 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
| 50 params.parent = src_window->parent(); |
| 51 params.can_activate = false; |
| 52 params.keep_on_top = true; |
| 53 widget->set_focus_on_creation(false); |
| 54 widget->Init(params); |
| 55 widget->SetVisibilityChangedAnimationsEnabled(false); |
| 56 std::string name = src_window->name() + " (Copy)"; |
| 57 widget->GetNativeWindow()->SetName(name); |
| 58 views::corewm::SetShadowType(widget->GetNativeWindow(), |
| 59 views::corewm::SHADOW_TYPE_RECTANGULAR); |
| 60 |
| 61 // Set the bounds in the target root window. |
| 62 gfx::Display target_display = |
| 63 Shell::GetScreen()->GetDisplayNearestWindow(target_root); |
| 64 aura::client::ScreenPositionClient* screen_position_client = |
| 65 aura::client::GetScreenPositionClient(src_window->GetRootWindow()); |
| 66 if (screen_position_client && target_display.is_valid()) { |
| 67 screen_position_client->SetBounds(widget->GetNativeWindow(), |
| 68 src_window->GetBoundsInScreen(), target_display); |
| 69 } else { |
| 70 widget->SetBounds(src_window->GetBoundsInScreen()); |
| 71 } |
| 72 widget->StackAbove(src_window); |
| 73 |
| 74 // Move the |recreated_layer| to the newly created window. |
| 75 recreated_layer->set_delegate(src_window->layer()->delegate()); |
| 76 gfx::Rect layer_bounds = recreated_layer->bounds(); |
| 77 layer_bounds.set_origin(gfx::Point(0, 0)); |
| 78 recreated_layer->SetBounds(layer_bounds); |
| 79 recreated_layer->SetVisible(false); |
| 80 recreated_layer->parent()->Remove(recreated_layer); |
| 81 |
| 82 aura::Window* window = widget->GetNativeWindow(); |
| 83 recreated_layer->SetVisible(true); |
| 84 window->layer()->Add(recreated_layer); |
| 85 window->layer()->StackAtTop(recreated_layer); |
| 86 window->layer()->SetOpacity(1); |
| 87 window->Show(); |
| 88 return widget; |
| 89 } |
| 90 |
| 91 // An observer which closes the widget and deletes the layer after an |
| 92 // animation finishes. |
| 93 class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver { |
| 94 public: |
| 95 CleanupWidgetAfterAnimationObserver(views::Widget* widget, ui::Layer* layer); |
| 96 |
| 97 virtual void OnLayerAnimationEnded( |
| 98 ui::LayerAnimationSequence* sequence) OVERRIDE; |
| 99 virtual void OnLayerAnimationAborted( |
| 100 ui::LayerAnimationSequence* sequence) OVERRIDE; |
| 101 virtual void OnLayerAnimationScheduled( |
| 102 ui::LayerAnimationSequence* sequence) OVERRIDE; |
| 103 |
| 104 protected: |
| 105 virtual ~CleanupWidgetAfterAnimationObserver(); |
| 106 |
| 107 private: |
| 108 views::Widget* widget_; |
| 109 ui::Layer* layer_; |
| 110 |
| 111 DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver); |
| 112 }; |
| 113 |
| 114 CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver( |
| 115 views::Widget* widget, |
| 116 ui::Layer* layer) |
| 117 : widget_(widget), |
| 118 layer_(layer) { |
| 119 widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this); |
| 120 } |
| 121 |
| 122 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded( |
| 123 ui::LayerAnimationSequence* sequence) { |
| 124 delete this; |
| 125 } |
| 126 |
| 127 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted( |
| 128 ui::LayerAnimationSequence* sequence) { |
| 129 delete this; |
| 130 } |
| 131 |
| 132 void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled( |
| 133 ui::LayerAnimationSequence* sequence) { |
| 134 } |
| 135 |
| 136 CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() { |
| 137 widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this); |
| 138 widget_->Close(); |
| 139 widget_ = NULL; |
| 140 if (layer_) { |
| 141 views::corewm::DeepDeleteLayers(layer_); |
| 142 layer_ = NULL; |
| 143 } |
| 144 } |
| 145 |
| 146 // The animation settings used for window selector animations. |
| 147 class WindowSelectorAnimationSettings |
| 148 : public ui::ScopedLayerAnimationSettings { |
| 149 public: |
| 150 WindowSelectorAnimationSettings(aura::Window* window) : |
| 151 ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()) { |
| 152 SetPreemptionStrategy( |
| 153 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| 154 SetTransitionDuration( |
| 155 base::TimeDelta::FromMilliseconds(kOverviewTransitionMilliseconds)); |
| 156 } |
| 157 |
| 158 virtual ~WindowSelectorAnimationSettings() { |
| 159 } |
| 160 }; |
| 161 |
| 162 } // namespace |
| 163 |
| 164 // TODO(flackr): Split up into separate file under subdirectory in ash/wm. |
| 165 class WindowSelectorWindow { |
| 166 public: |
| 167 explicit WindowSelectorWindow(aura::Window* window); |
| 168 virtual ~WindowSelectorWindow(); |
| 169 |
| 170 aura::Window* window() { return window_; } |
| 171 const aura::Window* window() const { return window_; } |
| 172 |
| 173 // Returns true if this window selector window contains the |target|. This is |
| 174 // used to determine if an event targetted this window. |
| 175 bool Contains(const aura::Window* target) const; |
| 176 |
| 177 // Restores this window on exit rather than returning it to a minimized state |
| 178 // if it was minimized on entering overview mode. |
| 179 void RestoreWindowOnExit(); |
| 180 |
| 181 // Informs the WindowSelectorWindow that the window being watched was |
| 182 // destroyed. This resets the internal window pointer to avoid calling |
| 183 // anything on the window at destruction time. |
| 184 void OnWindowDestroyed(); |
| 185 |
| 186 // Applies a transform to the window to fit within |target_bounds| while |
| 187 // maintaining its aspect ratio. |
| 188 void TransformToFitBounds(aura::RootWindow* root_window, |
| 189 const gfx::Rect& target_bounds); |
| 190 |
| 191 gfx::Rect bounds() { return fit_bounds_; } |
| 192 |
| 193 private: |
| 194 // A weak pointer to the real window in the overview. |
| 195 aura::Window* window_; |
| 196 |
| 197 // A copy of the window used to transition the window to another root. |
| 198 views::Widget* window_copy_; |
| 199 |
| 200 // A weak pointer to a deep copy of the window's layers. |
| 201 ui::Layer* layer_; |
| 202 |
| 203 // If true, the window was minimized and should be restored if the window |
| 204 // was not selected. |
| 205 bool minimized_; |
| 206 |
| 207 // The original transform of the window before entering overview mode. |
| 208 gfx::Transform original_transform_; |
| 209 |
| 210 // The bounds this window is fit to. |
| 211 gfx::Rect fit_bounds_; |
| 212 |
| 213 DISALLOW_COPY_AND_ASSIGN(WindowSelectorWindow); |
| 214 }; |
| 215 |
| 216 WindowSelectorWindow::WindowSelectorWindow(aura::Window* window) |
| 217 : window_(window), |
| 218 window_copy_(NULL), |
| 219 layer_(NULL), |
| 220 minimized_(window->GetProperty(aura::client::kShowStateKey) == |
| 221 ui::SHOW_STATE_MINIMIZED), |
| 222 original_transform_(window->layer()->transform()) { |
| 223 if (minimized_) |
| 224 window_->Show(); |
| 225 } |
| 226 |
| 227 WindowSelectorWindow::~WindowSelectorWindow() { |
| 228 if (window_) { |
| 229 WindowSelectorAnimationSettings animation_settings(window_); |
| 230 gfx::Transform transform; |
| 231 window_->SetTransform(original_transform_); |
| 232 if (minimized_) { |
| 233 // Setting opacity 0 and visible false ensures that the property change |
| 234 // to SHOW_STATE_MINIMIZED will not animate the window from its original |
| 235 // bounds to the minimized position. |
| 236 window_->layer()->SetOpacity(0); |
| 237 window_->layer()->SetVisible(false); |
| 238 window_->SetProperty(aura::client::kShowStateKey, |
| 239 ui::SHOW_STATE_MINIMIZED); |
| 240 } |
| 241 } |
| 242 // If a copy of the window was created, clean it up. |
| 243 if (window_copy_) { |
| 244 if (window_) { |
| 245 // If the initial window wasn't destroyed, the copy needs to be animated |
| 246 // out. CleanupWidgetAfterAnimationObserver will destroy the widget and |
| 247 // layer after the animation is complete. |
| 248 new CleanupWidgetAfterAnimationObserver(window_copy_, layer_); |
| 249 WindowSelectorAnimationSettings animation_settings( |
| 250 window_copy_->GetNativeWindow()); |
| 251 window_copy_->GetNativeWindow()->SetTransform(original_transform_); |
| 252 } else { |
| 253 window_copy_->Close(); |
| 254 if (layer_) |
| 255 views::corewm::DeepDeleteLayers(layer_); |
| 256 } |
| 257 window_copy_ = NULL; |
| 258 layer_ = NULL; |
| 259 } |
| 260 } |
| 261 |
| 262 bool WindowSelectorWindow::Contains(const aura::Window* window) const { |
| 263 if (window_copy_ && window_copy_->GetNativeWindow()->Contains(window)) |
| 264 return true; |
| 265 return window_->Contains(window); |
| 266 } |
| 267 |
| 268 void WindowSelectorWindow::RestoreWindowOnExit() { |
| 269 minimized_ = false; |
| 270 original_transform_ = gfx::Transform(); |
| 271 } |
| 272 |
| 273 void WindowSelectorWindow::OnWindowDestroyed() { |
| 274 window_ = NULL; |
| 275 } |
| 276 |
| 277 void WindowSelectorWindow::TransformToFitBounds( |
| 278 aura::RootWindow* root_window, |
| 279 const gfx::Rect& target_bounds) { |
| 280 fit_bounds_ = target_bounds; |
| 281 const gfx::Rect bounds = window_->GetBoundsInScreen(); |
38 float scale = std::min(1.0f, | 282 float scale = std::min(1.0f, |
39 std::min(static_cast<float>(target_bounds.width()) / bounds.width(), | 283 std::min(static_cast<float>(target_bounds.width()) / bounds.width(), |
40 static_cast<float>(target_bounds.height()) / bounds.height())); | 284 static_cast<float>(target_bounds.height()) / bounds.height())); |
41 gfx::Transform transform; | 285 gfx::Transform transform; |
42 gfx::Vector2d offset( | 286 gfx::Vector2d offset( |
43 0.5 * (target_bounds.width() - scale * bounds.width()), | 287 0.5 * (target_bounds.width() - scale * bounds.width()), |
44 0.5 * (target_bounds.height() - scale * bounds.height())); | 288 0.5 * (target_bounds.height() - scale * bounds.height())); |
45 transform.Translate(target_bounds.x() - bounds.x() + offset.x(), | 289 transform.Translate(target_bounds.x() - bounds.x() + offset.x(), |
46 target_bounds.y() - bounds.y() + offset.y()); | 290 target_bounds.y() - bounds.y() + offset.y()); |
47 transform.Scale(scale, scale); | 291 transform.Scale(scale, scale); |
48 // TODO(flackr): The window bounds or transform could change during overview | 292 if (root_window != window_->GetRootWindow()) { |
49 // mode. WindowSelector should create a copy of the window so that the | 293 if (!window_copy_) { |
50 // displayed windows are not affected by changes happening in the background. | 294 DCHECK(!layer_); |
51 // This will be necessary for alt-tab cycling as well, as some windows will | 295 layer_ = views::corewm::RecreateWindowLayers(window_, true); |
52 // be coming from other displays: http://crbug.com/263481. | 296 window_copy_ = CreateCopyOfWindow(root_window, window_, layer_); |
53 window->SetTransform(transform); | 297 } |
| 298 WindowSelectorAnimationSettings animation_settings( |
| 299 window_copy_->GetNativeWindow()); |
| 300 window_copy_->GetNativeWindow()->SetTransform(transform); |
| 301 } |
| 302 WindowSelectorAnimationSettings animation_settings(window_); |
| 303 window_->SetTransform(transform); |
54 } | 304 } |
55 | 305 |
56 } // namespace | 306 // A comparator for locating a given target window. |
| 307 struct WindowSelectorWindowComparator |
| 308 : public std::unary_function<WindowSelectorWindow*, bool> { |
| 309 explicit WindowSelectorWindowComparator(const aura::Window* target_window) |
| 310 : target(target_window) { |
| 311 } |
| 312 |
| 313 bool operator()(const WindowSelectorWindow* window) const { |
| 314 return target == window->window(); |
| 315 } |
| 316 |
| 317 const aura::Window* target; |
| 318 }; |
57 | 319 |
58 WindowSelector::WindowSelector(const WindowList& windows, | 320 WindowSelector::WindowSelector(const WindowList& windows, |
| 321 WindowSelector::Mode mode, |
59 WindowSelectorDelegate* delegate) | 322 WindowSelectorDelegate* delegate) |
60 : delegate_(delegate) { | 323 : mode_(mode), |
| 324 delegate_(delegate), |
| 325 selected_window_(0), |
| 326 selection_root_(NULL) { |
61 DCHECK(delegate_); | 327 DCHECK(delegate_); |
62 for (size_t i = 0; i < windows.size(); ++i) { | 328 for (size_t i = 0; i < windows.size(); ++i) { |
63 windows[i]->AddObserver(this); | 329 windows[i]->AddObserver(this); |
64 WindowDetails details; | 330 windows_.push_back(new WindowSelectorWindow(windows[i])); |
65 details.window = windows[i]; | |
66 details.minimized = windows[i]->GetProperty(aura::client::kShowStateKey) == | |
67 ui::SHOW_STATE_MINIMIZED; | |
68 details.original_transform = windows[i]->layer()->transform(); | |
69 if (details.minimized) { | |
70 windows[i]->Show(); | |
71 } | |
72 windows_.push_back(details); | |
73 } | 331 } |
| 332 if (mode == WindowSelector::CYCLE) |
| 333 selection_root_ = ash::Shell::GetActiveRootWindow(); |
74 PositionWindows(); | 334 PositionWindows(); |
75 ash::Shell::GetInstance()->AddPreTargetHandler(this); | 335 ash::Shell::GetInstance()->AddPreTargetHandler(this); |
76 } | 336 } |
77 | 337 |
78 WindowSelector::~WindowSelector() { | 338 WindowSelector::~WindowSelector() { |
79 for (size_t i = 0; i < windows_.size(); i++) { | 339 for (size_t i = 0; i < windows_.size(); i++) { |
80 ui::ScopedLayerAnimationSettings animation_settings( | 340 windows_[i]->window()->RemoveObserver(this); |
81 windows_[i].window->layer()->GetAnimator()); | |
82 animation_settings.SetPreemptionStrategy( | |
83 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
84 animation_settings.SetTransitionDuration( | |
85 base::TimeDelta::FromMilliseconds(kOverviewTransitionMilliseconds)); | |
86 windows_[i].window->RemoveObserver(this); | |
87 gfx::Transform transform; | |
88 windows_[i].window->SetTransform(windows_[i].original_transform); | |
89 if (windows_[i].minimized) { | |
90 // Setting opacity 0 and visible false ensures that the property change | |
91 // to SHOW_STATE_MINIMIZED will not animate the window from its original | |
92 // bounds to the minimized position. | |
93 windows_[i].window->layer()->SetOpacity(0); | |
94 windows_[i].window->layer()->SetVisible(false); | |
95 windows_[i].window->SetProperty(aura::client::kShowStateKey, | |
96 ui::SHOW_STATE_MINIMIZED); | |
97 } | |
98 } | 341 } |
99 ash::Shell::GetInstance()->RemovePreTargetHandler(this); | 342 ash::Shell::GetInstance()->RemovePreTargetHandler(this); |
100 } | 343 } |
101 | 344 |
| 345 void WindowSelector::Step(WindowSelector::Direction direction) { |
| 346 DCHECK(windows_.size() > 0); |
| 347 if (!selection_widget_) |
| 348 InitializeSelectionWidget(); |
| 349 selected_window_ = (selected_window_ + windows_.size() + |
| 350 (direction == WindowSelector::FORWARD ? 1 : -1)) % windows_.size(); |
| 351 UpdateSelectionLocation(true); |
| 352 } |
| 353 |
| 354 void WindowSelector::SelectWindow() { |
| 355 delegate_->OnWindowSelected(windows_[selected_window_]->window()); |
| 356 } |
| 357 |
102 void WindowSelector::OnEvent(ui::Event* event) { | 358 void WindowSelector::OnEvent(ui::Event* event) { |
103 // TODO(flackr): This will prevent anything else from working while overview | 359 // If the event is targetted at any of the windows in the overview, then |
104 // mode is active. This should only stop events from being sent to the windows | 360 // prevent it from propagating. |
105 // in the overview but still allow interaction with the launcher / tray and | 361 aura::Window* target = static_cast<aura::Window*>(event->target()); |
106 // hotkeys http://crbug.com/264289. | 362 for (size_t i = 0; i < windows_.size(); ++i) { |
107 EventHandler::OnEvent(event); | 363 if (windows_[i]->Contains(target)) { |
108 event->StopPropagation(); | 364 event->StopPropagation(); |
| 365 break; |
| 366 } |
| 367 } |
| 368 |
| 369 // This object may not be valid after this call as a selection event can |
| 370 // trigger deletion of the window selector. |
| 371 ui::EventHandler::OnEvent(event); |
109 } | 372 } |
110 | 373 |
111 void WindowSelector::OnMouseEvent(ui::MouseEvent* event) { | 374 void WindowSelector::OnMouseEvent(ui::MouseEvent* event) { |
112 if (event->type() != ui::ET_MOUSE_RELEASED) | 375 if (event->type() != ui::ET_MOUSE_RELEASED) |
113 return; | 376 return; |
114 aura::Window* target = static_cast<aura::Window*>(event->target()); | 377 WindowSelectorWindow* target = GetEventTarget(event); |
115 if (!target->HitTest(event->location())) | 378 if (!target) |
116 return; | 379 return; |
117 | 380 |
118 HandleSelectionEvent(event); | 381 HandleSelectionEvent(target); |
119 } | 382 } |
120 | 383 |
121 void WindowSelector::OnGestureEvent(ui::GestureEvent* event) { | 384 void WindowSelector::OnGestureEvent(ui::GestureEvent* event) { |
122 if (event->type() != ui::ET_GESTURE_TAP) | 385 if (event->type() != ui::ET_GESTURE_TAP) |
123 return; | 386 return; |
124 HandleSelectionEvent(event); | 387 WindowSelectorWindow* target = GetEventTarget(event); |
| 388 if (!target) |
| 389 return; |
| 390 |
| 391 HandleSelectionEvent(target); |
125 } | 392 } |
126 | 393 |
127 void WindowSelector::OnWindowDestroyed(aura::Window* window) { | 394 void WindowSelector::OnWindowDestroyed(aura::Window* window) { |
128 std::vector<WindowDetails>::iterator iter = | 395 ScopedVector<WindowSelectorWindow>::iterator iter = |
129 std::find(windows_.begin(), windows_.end(), window); | 396 std::find_if(windows_.begin(), windows_.end(), |
| 397 WindowSelectorWindowComparator(window)); |
130 DCHECK(iter != windows_.end()); | 398 DCHECK(iter != windows_.end()); |
| 399 size_t deleted_index = iter - windows_.begin(); |
| 400 (*iter)->OnWindowDestroyed(); |
131 windows_.erase(iter); | 401 windows_.erase(iter); |
132 if (windows_.empty()) { | 402 if (windows_.empty()) { |
133 delegate_->OnSelectionCanceled(); | 403 delegate_->OnSelectionCanceled(); |
134 return; | 404 return; |
135 } | 405 } |
| 406 if (selected_window_ >= deleted_index) { |
| 407 if (selected_window_ > deleted_index) |
| 408 selected_window_--; |
| 409 selected_window_ = selected_window_ % windows_.size(); |
| 410 UpdateSelectionLocation(true); |
| 411 } |
136 | 412 |
137 PositionWindows(); | 413 PositionWindows(); |
138 } | 414 } |
139 | 415 |
140 void WindowSelector::HandleSelectionEvent(ui::Event* event) { | 416 WindowSelectorWindow* WindowSelector::GetEventTarget(ui::LocatedEvent* event) { |
141 aura::Window* target = static_cast<aura::Window*>(event->target()); | 417 aura::Window* target = static_cast<aura::Window*>(event->target()); |
| 418 // If the target window doesn't actually contain the event location (i.e. |
| 419 // mouse down over the window and mouse up elsewhere) then do not select the |
| 420 // window. |
| 421 if (!target->HitTest(event->location())) |
| 422 return NULL; |
142 | 423 |
143 for (size_t i = 0; i < windows_.size(); i++) { | 424 for (size_t i = 0; i < windows_.size(); i++) { |
144 if (windows_[i].window->Contains(target)) { | 425 if (windows_[i]->Contains(target)) |
145 // The selected window should not be minimized when window selection is | 426 return windows_[i]; |
146 // ended. | 427 } |
147 windows_[i].minimized = false; | 428 return NULL; |
148 windows_[i].original_transform = gfx::Transform(); | 429 } |
149 | 430 |
150 // The delegate may delete the WindowSelector, assume the object may no | 431 void WindowSelector::HandleSelectionEvent(WindowSelectorWindow* target) { |
151 // longer valid after calling this. | 432 // The selected window should not be minimized when window selection is |
152 delegate_->OnWindowSelected(windows_[i].window); | 433 // ended. |
153 return; | 434 target->RestoreWindowOnExit(); |
154 } | 435 delegate_->OnWindowSelected(target->window()); |
| 436 } |
| 437 |
| 438 void WindowSelector::PositionWindows() { |
| 439 if (selection_root_) { |
| 440 DCHECK_EQ(mode_, CYCLE); |
| 441 std::vector<WindowSelectorWindow*> windows; |
| 442 for (size_t i = 0; i < windows_.size(); ++i) |
| 443 windows.push_back(windows_[i]); |
| 444 PositionWindowsOnRoot(selection_root_, windows); |
| 445 } else { |
| 446 DCHECK_EQ(mode_, OVERVIEW); |
| 447 Shell::RootWindowList root_window_list = Shell::GetAllRootWindows(); |
| 448 for (size_t i = 0; i < root_window_list.size(); ++i) |
| 449 PositionWindowsFromRoot(root_window_list[i]); |
155 } | 450 } |
156 } | 451 } |
157 | 452 |
158 void WindowSelector::PositionWindows() { | 453 void WindowSelector::PositionWindowsFromRoot(aura::RootWindow* root_window) { |
159 Shell::RootWindowList root_window_list = Shell::GetAllRootWindows(); | 454 std::vector<WindowSelectorWindow*> windows; |
160 for (size_t i = 0; i < root_window_list.size(); ++i) { | 455 for (size_t i = 0; i < windows_.size(); ++i) { |
161 PositionWindowsOnRoot(root_window_list[i]); | 456 if (windows_[i]->window()->GetRootWindow() == root_window) |
| 457 windows.push_back(windows_[i]); |
162 } | 458 } |
| 459 PositionWindowsOnRoot(root_window, windows); |
163 } | 460 } |
164 | 461 |
165 void WindowSelector::PositionWindowsOnRoot(aura::RootWindow* root_window) { | 462 void WindowSelector::PositionWindowsOnRoot( |
166 gfx::Size window_size; | 463 aura::RootWindow* root_window, |
167 gfx::Rect total_bounds = ScreenAsh::GetDisplayWorkAreaBoundsInParent( | 464 const std::vector<WindowSelectorWindow*>& windows) { |
168 Shell::GetContainer(root_window, | |
169 internal::kShellWindowId_DefaultContainer)); | |
170 | |
171 std::vector<WindowDetails> windows; | |
172 for (size_t i = 0; i < windows_.size(); ++i) { | |
173 if (windows_[i].window->GetRootWindow() == root_window) | |
174 windows.push_back(windows_[i]); | |
175 } | |
176 if (windows.empty()) | 465 if (windows.empty()) |
177 return; | 466 return; |
178 | 467 |
| 468 gfx::Size window_size; |
| 469 gfx::Rect total_bounds = ScreenAsh::ConvertRectToScreen(root_window, |
| 470 ScreenAsh::GetDisplayWorkAreaBoundsInParent( |
| 471 Shell::GetContainer(root_window, |
| 472 internal::kShellWindowId_DefaultContainer))); |
| 473 |
179 // Find the minimum number of windows per row that will fit all of the | 474 // Find the minimum number of windows per row that will fit all of the |
180 // windows on screen. | 475 // windows on screen. |
181 size_t columns = std::max( | 476 size_t columns = std::max( |
182 total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1, | 477 total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1, |
183 static_cast<int>(ceil(sqrt(total_bounds.width() * windows.size() / | 478 static_cast<int>(ceil(sqrt(total_bounds.width() * windows.size() / |
184 (kCardAspectRatio * total_bounds.height()))))); | 479 (kCardAspectRatio * total_bounds.height()))))); |
185 size_t rows = ((windows.size() + columns - 1) / columns); | 480 size_t rows = ((windows.size() + columns - 1) / columns); |
186 window_size.set_width(std::min( | 481 window_size.set_width(std::min( |
187 static_cast<int>(total_bounds.width() / columns), | 482 static_cast<int>(total_bounds.width() / columns), |
188 static_cast<int>(total_bounds.height() * kCardAspectRatio / rows))); | 483 static_cast<int>(total_bounds.height() * kCardAspectRatio / rows))); |
189 window_size.set_height(window_size.width() / kCardAspectRatio); | 484 window_size.set_height(window_size.width() / kCardAspectRatio); |
190 | 485 |
191 // Calculate the X and Y offsets necessary to center the grid. | 486 // Calculate the X and Y offsets necessary to center the grid. |
192 int x_offset = total_bounds.x() + ((windows.size() >= columns ? 0 : | 487 int x_offset = total_bounds.x() + ((windows.size() >= columns ? 0 : |
193 (columns - windows.size()) * window_size.width()) + | 488 (columns - windows.size()) * window_size.width()) + |
194 (total_bounds.width() - columns * window_size.width())) / 2; | 489 (total_bounds.width() - columns * window_size.width())) / 2; |
195 int y_offset = total_bounds.y() + (total_bounds.height() - | 490 int y_offset = total_bounds.y() + (total_bounds.height() - |
196 rows * window_size.height()) / 2; | 491 rows * window_size.height()) / 2; |
197 for (size_t i = 0; i < windows.size(); ++i) { | 492 for (size_t i = 0; i < windows.size(); ++i) { |
198 ui::ScopedLayerAnimationSettings animation_settings( | |
199 windows[i].window->layer()->GetAnimator()); | |
200 animation_settings.SetPreemptionStrategy( | |
201 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
202 animation_settings.SetTransitionDuration( | |
203 base::TimeDelta::FromMilliseconds(kOverviewTransitionMilliseconds)); | |
204 gfx::Transform transform; | 493 gfx::Transform transform; |
205 int column = i % columns; | 494 int column = i % columns; |
206 int row = i / columns; | 495 int row = i / columns; |
207 gfx::Rect target_bounds(window_size.width() * column + x_offset, | 496 gfx::Rect target_bounds(window_size.width() * column + x_offset, |
208 window_size.height() * row + y_offset, | 497 window_size.height() * row + y_offset, |
209 window_size.width(), | 498 window_size.width(), |
210 window_size.height()); | 499 window_size.height()); |
211 target_bounds.Inset(kWindowMargin, kWindowMargin); | 500 target_bounds.Inset(kWindowMargin, kWindowMargin); |
212 TransformWindowToFitBounds(windows[i].window, target_bounds); | 501 windows[i]->TransformToFitBounds(root_window, target_bounds); |
213 } | 502 } |
214 } | 503 } |
215 | 504 |
| 505 void WindowSelector::InitializeSelectionWidget() { |
| 506 selection_widget_.reset(new views::Widget); |
| 507 views::Widget::InitParams params; |
| 508 params.type = views::Widget::InitParams::TYPE_POPUP; |
| 509 params.can_activate = false; |
| 510 params.keep_on_top = false; |
| 511 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 512 params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; |
| 513 params.parent = Shell::GetContainer( |
| 514 selection_root_, |
| 515 internal::kShellWindowId_DefaultContainer); |
| 516 params.accept_events = false; |
| 517 selection_widget_->set_focus_on_creation(false); |
| 518 selection_widget_->Init(params); |
| 519 views::View* content_view = new views::View; |
| 520 content_view->set_background( |
| 521 views::Background::CreateSolidBackground(kWindowSelectorSelectionColor)); |
| 522 selection_widget_->SetContentsView(content_view); |
| 523 UpdateSelectionLocation(false); |
| 524 selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom( |
| 525 selection_widget_->GetNativeWindow()); |
| 526 selection_widget_->Show(); |
| 527 selection_widget_->GetNativeWindow()->layer()->SetOpacity( |
| 528 kWindowSelectorSelectionOpacity); |
| 529 } |
| 530 |
| 531 void WindowSelector::UpdateSelectionLocation(bool animate) { |
| 532 if (!selection_widget_) |
| 533 return; |
| 534 gfx::Rect target_bounds = windows_[selected_window_]->bounds(); |
| 535 target_bounds.Inset(-kWindowSelectorSelectionPadding, |
| 536 -kWindowSelectorSelectionPadding); |
| 537 if (animate) { |
| 538 WindowSelectorAnimationSettings animation_settings( |
| 539 selection_widget_->GetNativeWindow()); |
| 540 selection_widget_->SetBounds(target_bounds); |
| 541 } else { |
| 542 selection_widget_->SetBounds(target_bounds); |
| 543 } |
| 544 } |
| 545 |
216 } // namespace ash | 546 } // namespace ash |
OLD | NEW |