OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 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/window_selector.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "ash/screen_ash.h" |
| 10 #include "ash/shell.h" |
| 11 #include "ash/shell_window_ids.h" |
| 12 #include "ash/wm/window_selector_delegate.h" |
| 13 #include "ash/wm/window_util.h" |
| 14 #include "base/memory/scoped_ptr.h" |
| 15 #include "ui/aura/client/aura_constants.h" |
| 16 #include "ui/aura/root_window.h" |
| 17 #include "ui/aura/window.h" |
| 18 #include "ui/base/events/event.h" |
| 19 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 20 #include "ui/gfx/interpolated_transform.h" |
| 21 #include "ui/gfx/transform_util.h" |
| 22 #include "ui/views/corewm/window_animations.h" |
| 23 |
| 24 namespace ash { |
| 25 |
| 26 namespace { |
| 27 |
| 28 const float kCardAspectRatio = 4.0f / 3.0f; |
| 29 const int kWindowMargin = 20; |
| 30 const int kMinCardsMajor = 3; |
| 31 const int kOverviewTransitionMilliseconds = 100; |
| 32 |
| 33 // Applies a transform to |window| to fit within |target_bounds| while |
| 34 // maintaining its aspect ratio. |
| 35 void TransformWindowToFitBounds(aura::Window* window, |
| 36 const gfx::Rect& target_bounds) { |
| 37 const gfx::Rect bounds = window->bounds(); |
| 38 float scale = std::min(1.0f, |
| 39 std::min(static_cast<float>(target_bounds.width()) / bounds.width(), |
| 40 static_cast<float>(target_bounds.height()) / bounds.height())); |
| 41 gfx::Transform transform; |
| 42 gfx::Vector2d offset( |
| 43 0.5 * (target_bounds.width() - scale * bounds.width()), |
| 44 0.5 * (target_bounds.height() - scale * bounds.height())); |
| 45 transform.Translate(target_bounds.x() - bounds.x() + offset.x(), |
| 46 target_bounds.y() - bounds.y() + offset.y()); |
| 47 transform.Scale(scale, scale); |
| 48 // TODO(flackr): The window bounds or transform could change during overview |
| 49 // mode. WindowSelector should create a copy of the window so that the |
| 50 // displayed windows are not affected by changes happening in the background. |
| 51 // This will be necessary for alt-tab cycling as well, as some windows will |
| 52 // be coming from other displays: http://crbug.com/263481. |
| 53 window->SetTransform(transform); |
| 54 } |
| 55 |
| 56 } // namespace |
| 57 |
| 58 WindowSelector::WindowSelector(const WindowList& windows, |
| 59 WindowSelectorDelegate* delegate) |
| 60 : delegate_(delegate) { |
| 61 DCHECK(delegate_); |
| 62 for (size_t i = 0; i < windows.size(); ++i) { |
| 63 windows[i]->AddObserver(this); |
| 64 WindowDetails details; |
| 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 } |
| 74 PositionWindows(); |
| 75 ash::Shell::GetInstance()->AddPreTargetHandler(this); |
| 76 } |
| 77 |
| 78 WindowSelector::~WindowSelector() { |
| 79 for (size_t i = 0; i < windows_.size(); i++) { |
| 80 ui::ScopedLayerAnimationSettings animation_settings( |
| 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 } |
| 99 ash::Shell::GetInstance()->RemovePreTargetHandler(this); |
| 100 } |
| 101 |
| 102 void WindowSelector::OnEvent(ui::Event* event) { |
| 103 // TODO(flackr): This will prevent anything else from working while overview |
| 104 // mode is active. This should only stop events from being sent to the windows |
| 105 // in the overview but still allow interaction with the launcher / tray and |
| 106 // hotkeys http://crbug.com/264289. |
| 107 EventHandler::OnEvent(event); |
| 108 event->StopPropagation(); |
| 109 } |
| 110 |
| 111 void WindowSelector::OnMouseEvent(ui::MouseEvent* event) { |
| 112 if (event->type() != ui::ET_MOUSE_RELEASED) |
| 113 return; |
| 114 aura::Window* target = static_cast<aura::Window*>(event->target()); |
| 115 if (!target->HitTest(event->location())) |
| 116 return; |
| 117 |
| 118 HandleSelectionEvent(event); |
| 119 } |
| 120 |
| 121 void WindowSelector::OnGestureEvent(ui::GestureEvent* event) { |
| 122 if (event->type() != ui::ET_GESTURE_TAP) |
| 123 return; |
| 124 HandleSelectionEvent(event); |
| 125 } |
| 126 |
| 127 void WindowSelector::OnWindowDestroyed(aura::Window* window) { |
| 128 std::vector<WindowDetails>::iterator iter = |
| 129 std::find(windows_.begin(), windows_.end(), window); |
| 130 DCHECK(iter != windows_.end()); |
| 131 windows_.erase(iter); |
| 132 if (windows_.empty()) { |
| 133 delegate_->OnSelectionCanceled(); |
| 134 return; |
| 135 } |
| 136 |
| 137 PositionWindows(); |
| 138 } |
| 139 |
| 140 void WindowSelector::HandleSelectionEvent(ui::Event* event) { |
| 141 aura::Window* target = static_cast<aura::Window*>(event->target()); |
| 142 |
| 143 for (size_t i = 0; i < windows_.size(); i++) { |
| 144 if (windows_[i].window->Contains(target)) { |
| 145 // The selected window should not be minimized when window selection is |
| 146 // ended. |
| 147 windows_[i].minimized = false; |
| 148 windows_[i].original_transform = gfx::Transform(); |
| 149 |
| 150 // The delegate may delete the WindowSelector, assume the object may no |
| 151 // longer valid after calling this. |
| 152 delegate_->OnWindowSelected(windows_[i].window); |
| 153 return; |
| 154 } |
| 155 } |
| 156 } |
| 157 |
| 158 void WindowSelector::PositionWindows() { |
| 159 Shell::RootWindowList root_window_list = Shell::GetAllRootWindows(); |
| 160 for (size_t i = 0; i < root_window_list.size(); ++i) { |
| 161 PositionWindowsOnRoot(root_window_list[i]); |
| 162 } |
| 163 } |
| 164 |
| 165 void WindowSelector::PositionWindowsOnRoot(aura::RootWindow* root_window) { |
| 166 gfx::Size window_size; |
| 167 gfx::Rect total_bounds = ScreenAsh::GetDisplayWorkAreaBoundsInParent( |
| 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()) |
| 177 return; |
| 178 |
| 179 // Find the minimum number of windows per row that will fit all of the |
| 180 // windows on screen. |
| 181 size_t columns = std::max( |
| 182 total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1, |
| 183 static_cast<int>(ceil(sqrt(total_bounds.width() * windows.size() / |
| 184 (kCardAspectRatio * total_bounds.height()))))); |
| 185 size_t rows = ((windows.size() + columns - 1) / columns); |
| 186 window_size.set_width(std::min( |
| 187 static_cast<int>(total_bounds.width() / columns), |
| 188 static_cast<int>(total_bounds.height() * kCardAspectRatio / rows))); |
| 189 window_size.set_height(window_size.width() / kCardAspectRatio); |
| 190 |
| 191 // Calculate the X and Y offsets necessary to center the grid. |
| 192 int x_offset = total_bounds.x() + ((windows.size() >= columns ? 0 : |
| 193 (columns - windows.size()) * window_size.width()) + |
| 194 (total_bounds.width() - columns * window_size.width())) / 2; |
| 195 int y_offset = total_bounds.y() + (total_bounds.height() - |
| 196 rows * window_size.height()) / 2; |
| 197 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; |
| 205 int column = i % columns; |
| 206 int row = i / columns; |
| 207 gfx::Rect target_bounds(window_size.width() * column + x_offset, |
| 208 window_size.height() * row + y_offset, |
| 209 window_size.width(), |
| 210 window_size.height()); |
| 211 target_bounds.Inset(kWindowMargin, kWindowMargin); |
| 212 TransformWindowToFitBounds(windows[i].window, target_bounds); |
| 213 } |
| 214 } |
| 215 |
| 216 } // namespace ash |
OLD | NEW |