Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 1 | // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 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/dock/docked_window_layout_manager.h" |
| 6 | |
| 7 | #include "ash/launcher/launcher.h" |
| 8 | #include "ash/screen_ash.h" |
| 9 | #include "ash/shelf/shelf_layout_manager.h" |
| 10 | #include "ash/shelf/shelf_types.h" |
| 11 | #include "ash/shelf/shelf_widget.h" |
| 12 | #include "ash/shell.h" |
| 13 | #include "ash/shell_window_ids.h" |
| 14 | #include "ash/wm/coordinate_conversion.h" |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 15 | #include "ash/wm/window_properties.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 16 | #include "ash/wm/window_util.h" |
| 17 | #include "base/auto_reset.h" |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 18 | #include "third_party/skia/include/core/SkColor.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 19 | #include "ui/aura/client/activation_client.h" |
| 20 | #include "ui/aura/client/aura_constants.h" |
| 21 | #include "ui/aura/focus_manager.h" |
| 22 | #include "ui/aura/root_window.h" |
| 23 | #include "ui/aura/window.h" |
| 24 | #include "ui/gfx/rect.h" |
| 25 | |
| 26 | namespace ash { |
| 27 | namespace internal { |
| 28 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 29 | // Minimum, maximum width of the dock area and a width of the gap |
| 30 | const int DockedWindowLayoutManager::kMinDockWidth = 200; |
| 31 | const int DockedWindowLayoutManager::kMaxDockWidth = 450; |
| 32 | const int DockedWindowLayoutManager::kMinDockGap = 2; |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 33 | const int kWindowIdealSpacing = 4; |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 34 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 35 | namespace { |
| 36 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 37 | const SkColor kDockBackgroundColor = SkColorSetARGB(0xff, 0x10, 0x10, 0x10); |
| 38 | const float kDockBackgroundOpacity = 0.5f; |
| 39 | |
| 40 | class DockedBackgroundWidget : public views::Widget { |
| 41 | public: |
| 42 | explicit DockedBackgroundWidget(aura::Window* container) { |
| 43 | InitWidget(container); |
| 44 | } |
| 45 | |
| 46 | private: |
| 47 | void InitWidget(aura::Window* parent) { |
| 48 | views::Widget::InitParams params; |
| 49 | params.type = views::Widget::InitParams::TYPE_POPUP; |
| 50 | params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; |
| 51 | params.can_activate = false; |
| 52 | params.keep_on_top = false; |
| 53 | params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 54 | params.parent = parent; |
| 55 | params.accept_events = false; |
| 56 | set_focus_on_creation(false); |
| 57 | Init(params); |
| 58 | DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow()); |
| 59 | views::View* content_view = new views::View; |
| 60 | content_view->set_background( |
| 61 | views::Background::CreateSolidBackground(kDockBackgroundColor)); |
| 62 | SetContentsView(content_view); |
| 63 | GetNativeWindow()->layer()->SetOpacity(kDockBackgroundOpacity); |
| 64 | Hide(); |
| 65 | } |
| 66 | |
| 67 | DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget); |
| 68 | }; |
| 69 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 70 | DockedWindowLayoutManager* GetDockLayoutManager(aura::Window* window, |
| 71 | const gfx::Point& location) { |
| 72 | gfx::Rect near_location(location, gfx::Size()); |
| 73 | aura::Window* dock = Shell::GetContainer( |
| 74 | wm::GetRootWindowMatching(near_location), |
| 75 | kShellWindowId_DockedContainer); |
| 76 | return static_cast<internal::DockedWindowLayoutManager*>( |
| 77 | dock->layout_manager()); |
| 78 | } |
| 79 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 80 | // Certain windows (minimized, hidden or popups) do not matter to docking. |
| 81 | bool IsUsedByLayout(aura::Window* window) { |
| 82 | return (window->IsVisible() && |
| 83 | !wm::IsWindowMinimized(window) && |
| 84 | window->type() != aura::client::WINDOW_TYPE_POPUP); |
| 85 | } |
| 86 | |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 87 | bool CompareWindowPos(const aura::Window* win1, const aura::Window* win2) { |
| 88 | return win1->GetTargetBounds().CenterPoint().y() < |
| 89 | win2->GetTargetBounds().CenterPoint().y(); |
| 90 | } |
| 91 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 92 | } // namespace |
| 93 | |
| 94 | //////////////////////////////////////////////////////////////////////////////// |
| 95 | // DockLayoutManager public implementation: |
| 96 | DockedWindowLayoutManager::DockedWindowLayoutManager( |
| 97 | aura::Window* dock_container) |
| 98 | : dock_container_(dock_container), |
| 99 | in_layout_(false), |
| 100 | dragged_window_(NULL), |
| 101 | launcher_(NULL), |
| 102 | shelf_layout_manager_(NULL), |
| 103 | shelf_hidden_(false), |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 104 | docked_width_(0), |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 105 | alignment_(DOCKED_ALIGNMENT_NONE), |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 106 | last_active_window_(NULL), |
| 107 | background_widget_(new DockedBackgroundWidget(dock_container_)) { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 108 | DCHECK(dock_container); |
| 109 | aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> |
| 110 | AddObserver(this); |
| 111 | Shell::GetInstance()->AddShellObserver(this); |
| 112 | } |
| 113 | |
| 114 | DockedWindowLayoutManager::~DockedWindowLayoutManager() { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 115 | Shutdown(); |
| 116 | } |
| 117 | |
| 118 | void DockedWindowLayoutManager::Shutdown() { |
| 119 | if (shelf_layout_manager_) { |
| 120 | shelf_layout_manager_->RemoveObserver(this); |
| 121 | } |
| 122 | shelf_layout_manager_ = NULL; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 123 | launcher_ = NULL; |
| 124 | for (size_t i = 0; i < dock_container_->children().size(); ++i) |
| 125 | dock_container_->children()[i]->RemoveObserver(this); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 126 | aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> |
| 127 | RemoveObserver(this); |
| 128 | Shell::GetInstance()->RemoveShellObserver(this); |
| 129 | } |
| 130 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 131 | void DockedWindowLayoutManager::AddObserver( |
| 132 | DockedWindowLayoutManagerObserver* observer) { |
| 133 | observer_list_.AddObserver(observer); |
| 134 | } |
| 135 | |
| 136 | void DockedWindowLayoutManager::RemoveObserver( |
| 137 | DockedWindowLayoutManagerObserver* observer) { |
| 138 | observer_list_.RemoveObserver(observer); |
| 139 | } |
| 140 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 141 | void DockedWindowLayoutManager::StartDragging(aura::Window* window) { |
| 142 | if (window->parent() != dock_container_) |
| 143 | return; |
| 144 | DCHECK(!dragged_window_); |
| 145 | dragged_window_ = window; |
| 146 | Relayout(); |
| 147 | } |
| 148 | |
| 149 | void DockedWindowLayoutManager::FinishDragging() { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 150 | if (!dragged_window_) |
| 151 | return; |
| 152 | |
| 153 | // If this is the first window getting docked - update alignment. |
| 154 | if (alignment_ == DOCKED_ALIGNMENT_NONE) { |
| 155 | alignment_ = AlignmentOfWindow(dragged_window_); |
| 156 | } else { |
| 157 | // There were some docked windows before. Check if the |dragged_window_| |
| 158 | // was the only one remaining and if so possibly switch alignment to |
| 159 | // opposite side. Ignore popups. |
| 160 | bool found_docked_window = false; |
| 161 | for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| 162 | aura::Window* window(dock_container_->children()[i]); |
| 163 | if (window != dragged_window_ && |
| 164 | window->type() != aura::client::WINDOW_TYPE_POPUP) { |
| 165 | found_docked_window = true; |
| 166 | break; |
| 167 | } |
| 168 | } |
| 169 | if (!found_docked_window) |
| 170 | alignment_ = AlignmentOfWindow(dragged_window_); |
| 171 | } |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 172 | |
| 173 | // We no longer need to observe |dragged_window_| unless it is added back; |
| 174 | if (dragged_window_) { |
| 175 | if (dragged_window_->parent() != dock_container_) |
| 176 | dragged_window_->RemoveObserver(this); |
| 177 | dragged_window_ = NULL; |
| 178 | } |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 179 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 180 | Relayout(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 181 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 182 | } |
| 183 | |
| 184 | // static |
| 185 | bool DockedWindowLayoutManager::ShouldWindowDock(aura::Window* window, |
| 186 | const gfx::Point& location) { |
| 187 | DockedWindowLayoutManager* layout_manager = GetDockLayoutManager(window, |
| 188 | location); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 189 | const DockedAlignment alignment = layout_manager->CalculateAlignment(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 190 | const gfx::Rect& bounds(window->GetBoundsInScreen()); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 191 | const gfx::Rect docked_bounds = (alignment == DOCKED_ALIGNMENT_NONE) ? |
| 192 | layout_manager->dock_container_->GetBoundsInScreen() : |
| 193 | layout_manager->docked_bounds_; |
| 194 | DockedAlignment dock_edge = DOCKED_ALIGNMENT_NONE; |
| 195 | if (alignment == DOCKED_ALIGNMENT_NONE) { |
| 196 | // Check if the window is touching the edge - it may need to get docked. |
| 197 | dock_edge = layout_manager->AlignmentOfWindow(window); |
| 198 | } else if ((docked_bounds.Intersects(bounds) && |
| 199 | docked_bounds.Contains(location)) || |
| 200 | alignment == layout_manager->AlignmentOfWindow(window)) { |
| 201 | // A window is being added to other docked windows (on the same side). |
| 202 | // Both bounds and pointer location are checked because some drags involve |
| 203 | // sticky edges and so the |location| may be outside of the |bounds|. |
| 204 | // It is also possible that all the docked windows are minimized or hidden |
| 205 | // in which case the dragged window needs to be exactly touching the same |
| 206 | // edge that those docked windows were aligned before they got minimized. |
| 207 | dock_edge = alignment; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 208 | } |
| 209 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 210 | // Do not allow docking on the same side as launcher shelf. |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 211 | if (layout_manager->launcher_ && layout_manager->launcher_->shelf_widget()) { |
| 212 | ShelfAlignment shelf_alignment = |
| 213 | layout_manager->launcher_->shelf_widget()->GetAlignment(); |
| 214 | if ((shelf_alignment == SHELF_ALIGNMENT_LEFT && |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 215 | dock_edge == DOCKED_ALIGNMENT_LEFT) || |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 216 | (shelf_alignment == SHELF_ALIGNMENT_RIGHT && |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 217 | dock_edge == DOCKED_ALIGNMENT_RIGHT)) { |
| 218 | dock_edge = DOCKED_ALIGNMENT_NONE; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 219 | } |
| 220 | } |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 221 | |
| 222 | // Do not allow docking if a window is vertically maximized (as is the case |
| 223 | // when it is snapped). |
| 224 | const gfx::Rect work_area = |
| 225 | Shell::GetScreen()->GetDisplayNearestWindow(window).work_area(); |
| 226 | if (bounds.y() == work_area.y() && bounds.height() == work_area.height()) |
| 227 | dock_edge = DOCKED_ALIGNMENT_NONE; |
| 228 | |
| 229 | return dock_edge != DOCKED_ALIGNMENT_NONE; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | void DockedWindowLayoutManager::SetLauncher(ash::Launcher* launcher) { |
| 233 | DCHECK(!launcher_); |
| 234 | DCHECK(!shelf_layout_manager_); |
| 235 | launcher_ = launcher; |
| 236 | if (launcher_->shelf_widget()) { |
| 237 | shelf_layout_manager_ = ash::internal::ShelfLayoutManager::ForLauncher( |
| 238 | launcher_->shelf_widget()->GetNativeWindow()); |
| 239 | WillChangeVisibilityState(shelf_layout_manager_->visibility_state()); |
| 240 | shelf_layout_manager_->AddObserver(this); |
| 241 | } |
| 242 | } |
| 243 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 244 | DockedAlignment DockedWindowLayoutManager::CalculateAlignment() const { |
| 245 | // Find a child that is not being dragged and is not a popup. |
| 246 | // If such exists the current alignment is returned - even if some of the |
| 247 | // children are hidden or minimized (so they can be restored without losing |
| 248 | // the docked state). |
| 249 | for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| 250 | aura::Window* window(dock_container_->children()[i]); |
| 251 | if (window != dragged_window_ && |
| 252 | window->type() != aura::client::WINDOW_TYPE_POPUP) { |
| 253 | return alignment_; |
| 254 | } |
| 255 | } |
| 256 | // No docked windows remain other than possibly the window being dragged. |
| 257 | // Return |NONE| to indicate that windows may get docked on either side. |
| 258 | return DOCKED_ALIGNMENT_NONE; |
| 259 | } |
| 260 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 261 | //////////////////////////////////////////////////////////////////////////////// |
| 262 | // DockLayoutManager, aura::LayoutManager implementation: |
| 263 | void DockedWindowLayoutManager::OnWindowResized() { |
| 264 | Relayout(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 265 | // When screen resizes we need to update the insets even when dock width or |
| 266 | // alignment does not change. |
| 267 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 268 | } |
| 269 | |
| 270 | void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 271 | if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| 272 | return; |
| 273 | |
| 274 | // If this is the first window getting docked - update alignment. |
| 275 | if (alignment_ == DOCKED_ALIGNMENT_NONE) |
| 276 | alignment_ = AlignmentOfWindow(child); |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 277 | // We need to observe a child unless it is a |dragged_window_| that just got |
| 278 | // added back in which case we are already observing it. |
| 279 | if (child != dragged_window_) |
| 280 | child->AddObserver(this); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 281 | Relayout(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 282 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 286 | if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| 287 | return; |
| 288 | |
| 289 | // Try to find a child that is not a popup and is not being dragged. |
| 290 | bool found_docked_window = false; |
| 291 | for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| 292 | aura::Window* window(dock_container_->children()[i]); |
| 293 | if (window != dragged_window_ && |
| 294 | window->type() != aura::client::WINDOW_TYPE_POPUP) { |
| 295 | found_docked_window = true; |
| 296 | break; |
| 297 | } |
| 298 | } |
| 299 | if (!found_docked_window) |
| 300 | alignment_ = DOCKED_ALIGNMENT_NONE; |
| 301 | |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 302 | // Keep track of former children if they are dragged as they can be reparented |
| 303 | // temporarily just for the drag. |
| 304 | if (child != dragged_window_) |
| 305 | child->RemoveObserver(this); |
| 306 | |
| 307 | if (last_active_window_ == child) |
| 308 | last_active_window_ = NULL; |
| 309 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 310 | // Close the dock and expand workspace work area. |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 311 | Relayout(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 312 | |
| 313 | // When a panel is removed from this layout manager it may still be dragged. |
| 314 | // Delay updating dock and workspace bounds until the drag is completed. This |
| 315 | // preserves the docked area width until the panel drag is finished. |
| 316 | if (child->type() != aura::client::WINDOW_TYPE_PANEL) |
| 317 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 318 | } |
| 319 | |
| 320 | void DockedWindowLayoutManager::OnChildWindowVisibilityChanged( |
| 321 | aura::Window* child, |
| 322 | bool visible) { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 323 | if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| 324 | return; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 325 | if (visible) |
| 326 | child->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); |
| 327 | Relayout(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 328 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 329 | } |
| 330 | |
| 331 | void DockedWindowLayoutManager::SetChildBounds( |
| 332 | aura::Window* child, |
| 333 | const gfx::Rect& requested_bounds) { |
| 334 | // Whenever one of our windows is moved or resized we need to enforce layout. |
| 335 | SetChildBoundsDirect(child, requested_bounds); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 336 | if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| 337 | return; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 338 | Relayout(); |
| 339 | } |
| 340 | |
| 341 | //////////////////////////////////////////////////////////////////////////////// |
| 342 | // DockLayoutManager, ash::ShellObserver implementation: |
| 343 | |
| 344 | void DockedWindowLayoutManager::OnShelfAlignmentChanged( |
| 345 | aura::RootWindow* root_window) { |
| 346 | if (dock_container_->GetRootWindow() != root_window) |
| 347 | return; |
| 348 | |
| 349 | if (!launcher_ || !launcher_->shelf_widget()) |
| 350 | return; |
| 351 | |
| 352 | if (alignment_ == DOCKED_ALIGNMENT_NONE) |
| 353 | return; |
| 354 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 355 | // Do not allow launcher and dock on the same side. Switch side that |
| 356 | // the dock is attached to and move all dock windows to that new side. |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 357 | ShelfAlignment shelf_alignment = launcher_->shelf_widget()->GetAlignment(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 358 | if (alignment_ == DOCKED_ALIGNMENT_LEFT && |
| 359 | shelf_alignment == SHELF_ALIGNMENT_LEFT) { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 360 | alignment_ = DOCKED_ALIGNMENT_RIGHT; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 361 | } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT && |
| 362 | shelf_alignment == SHELF_ALIGNMENT_RIGHT) { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 363 | alignment_ = DOCKED_ALIGNMENT_LEFT; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 364 | } |
| 365 | Relayout(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 366 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 367 | } |
| 368 | |
| 369 | ///////////////////////////////////////////////////////////////////////////// |
| 370 | // DockLayoutManager, WindowObserver implementation: |
| 371 | |
| 372 | void DockedWindowLayoutManager::OnWindowPropertyChanged(aura::Window* window, |
| 373 | const void* key, |
| 374 | intptr_t old) { |
| 375 | if (key != aura::client::kShowStateKey) |
| 376 | return; |
| 377 | // The window property will still be set, but no actual change will occur |
| 378 | // until WillChangeVisibilityState is called when the shelf is visible again |
| 379 | if (shelf_hidden_) |
| 380 | return; |
| 381 | if (wm::IsWindowMinimized(window)) |
| 382 | MinimizeWindow(window); |
| 383 | else |
| 384 | RestoreWindow(window); |
| 385 | } |
| 386 | |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 387 | void DockedWindowLayoutManager::OnWindowBoundsChanged( |
| 388 | aura::Window* window, |
| 389 | const gfx::Rect& old_bounds, |
| 390 | const gfx::Rect& new_bounds) { |
| 391 | if (window == dragged_window_) |
| 392 | Relayout(); |
| 393 | } |
| 394 | |
| 395 | void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) { |
| 396 | if (dragged_window_ == window) |
| 397 | dragged_window_ = NULL; |
| 398 | } |
| 399 | |
| 400 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 401 | //////////////////////////////////////////////////////////////////////////////// |
| 402 | // DockLayoutManager, aura::client::ActivationChangeObserver implementation: |
| 403 | |
| 404 | void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active, |
| 405 | aura::Window* lost_active) { |
| 406 | // Ignore if the window that is not managed by this was activated. |
| 407 | aura::Window* ancestor = NULL; |
| 408 | for (aura::Window* parent = gained_active; |
| 409 | parent; parent = parent->parent()) { |
| 410 | if (parent->parent() == dock_container_) { |
| 411 | ancestor = parent; |
| 412 | break; |
| 413 | } |
| 414 | } |
| 415 | if (ancestor) |
| 416 | UpdateStacking(ancestor); |
| 417 | } |
| 418 | |
| 419 | //////////////////////////////////////////////////////////////////////////////// |
| 420 | // DockLayoutManager, ShelfLayoutManagerObserver implementation: |
| 421 | |
| 422 | void DockedWindowLayoutManager::WillChangeVisibilityState( |
| 423 | ShelfVisibilityState new_state) { |
| 424 | // On entering / leaving full screen mode the shelf visibility state is |
| 425 | // changed to / from SHELF_HIDDEN. In this state, docked windows should hide |
| 426 | // to allow the full-screen application to use the full screen. |
| 427 | |
| 428 | // TODO(varkha): ShelfLayoutManager::UpdateVisibilityState sets state to |
| 429 | // SHELF_AUTO_HIDE when in immersive mode. We need to distinguish this from |
| 430 | // when shelf enters auto-hide state based on mouse hover when auto-hide |
| 431 | // setting is enabled and hide all windows (immersive mode should hide docked |
| 432 | // windows). |
| 433 | shelf_hidden_ = new_state == ash::SHELF_HIDDEN; |
| 434 | { |
| 435 | // prevent Relayout from getting called multiple times during this |
| 436 | base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); |
| 437 | for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| 438 | aura::Window* window = dock_container_->children()[i]; |
| 439 | if (shelf_hidden_) { |
| 440 | if (window->IsVisible()) |
| 441 | MinimizeWindow(window); |
| 442 | } else { |
| 443 | if (!wm::IsWindowMinimized(window)) |
| 444 | RestoreWindow(window); |
| 445 | } |
| 446 | } |
| 447 | } |
| 448 | Relayout(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 449 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 450 | } |
| 451 | |
| 452 | //////////////////////////////////////////////////////////////////////////////// |
| 453 | // DockLayoutManager private implementation: |
| 454 | |
| 455 | void DockedWindowLayoutManager::MinimizeWindow(aura::Window* window) { |
| 456 | window->Hide(); |
| 457 | if (wm::IsActiveWindow(window)) |
| 458 | wm::DeactivateWindow(window); |
| 459 | } |
| 460 | |
| 461 | void DockedWindowLayoutManager::RestoreWindow(aura::Window* window) { |
| 462 | window->Show(); |
| 463 | } |
| 464 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 465 | DockedAlignment DockedWindowLayoutManager::AlignmentOfWindow( |
| 466 | const aura::Window* window) const { |
| 467 | const gfx::Rect& bounds(window->GetBoundsInScreen()); |
| 468 | const gfx::Rect docked_bounds = dock_container_->GetBoundsInScreen(); |
| 469 | if (bounds.x() == docked_bounds.x()) |
| 470 | return DOCKED_ALIGNMENT_LEFT; |
| 471 | if (bounds.right() == docked_bounds.right()) |
| 472 | return DOCKED_ALIGNMENT_RIGHT; |
| 473 | return DOCKED_ALIGNMENT_NONE; |
| 474 | } |
| 475 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 476 | void DockedWindowLayoutManager::Relayout() { |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 477 | if (in_layout_ || alignment_ == DOCKED_ALIGNMENT_NONE) |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 478 | return; |
| 479 | base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); |
| 480 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 481 | gfx::Rect dock_bounds = dock_container_->bounds(); |
| 482 | aura::Window* active_window = NULL; |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 483 | aura::Window::Windows visible_windows; |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 484 | docked_width_ = 0; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 485 | for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| 486 | aura::Window* window(dock_container_->children()[i]); |
| 487 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 488 | if (!IsUsedByLayout(window)) |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 489 | continue; |
| 490 | |
| 491 | // If the shelf is currently hidden (full-screen mode), hide window until |
| 492 | // full-screen mode is exited. |
| 493 | if (shelf_hidden_) { |
| 494 | // The call to Hide does not set the minimize property, so the window will |
| 495 | // be restored when the shelf becomes visible again. |
| 496 | window->Hide(); |
| 497 | continue; |
| 498 | } |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 499 | if (window->HasFocus() || |
| 500 | window->Contains( |
| 501 | aura::client::GetFocusClient(window)->GetFocusedWindow())) { |
| 502 | DCHECK(!active_window); |
| 503 | active_window = window; |
| 504 | } |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 505 | visible_windows.push_back(window); |
| 506 | } |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 507 | |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 508 | // Consider windows that were formerly children of the |dock_container_| |
| 509 | // when fanning out other child windows. |
| 510 | if (dragged_window_ && dragged_window_->parent() != dock_container_) |
| 511 | visible_windows.push_back(dragged_window_); |
| 512 | |
| 513 | // Sort windows by their center positions and fan out overlapping |
| 514 | // windows. |
| 515 | std::sort(visible_windows.begin(), visible_windows.end(), CompareWindowPos); |
| 516 | gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow( |
| 517 | dock_container_); |
| 518 | const gfx::Rect work_area = display.work_area(); |
| 519 | int available_room = work_area.height(); |
| 520 | for (aura::Window::Windows::const_iterator iter = visible_windows.begin(); |
| 521 | iter != visible_windows.end(); ++iter) { |
| 522 | available_room -= (*iter)->bounds().height(); |
| 523 | } |
| 524 | const int num_windows = visible_windows.size(); |
| 525 | const float delta = (float)available_room / |
| 526 | ((available_room > 0 || num_windows <= 1) ? |
| 527 | num_windows + 1 : num_windows - 1); |
| 528 | float y_pos = (available_room > 0) ? delta : 0; |
| 529 | |
| 530 | for (aura::Window::Windows::const_iterator iter = visible_windows.begin(); |
| 531 | iter != visible_windows.end(); ++iter) { |
| 532 | aura::Window* window = *iter; |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 533 | gfx::Rect bounds = window->GetTargetBounds(); |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 534 | // Do not force position of a single window. |
| 535 | if (num_windows == 1) |
| 536 | y_pos = bounds.y(); |
| 537 | |
| 538 | // Fan out windows evenly distributing the overlap or remaining free space. |
| 539 | bounds.set_y(std::max(work_area.y(), |
| 540 | std::min(work_area.bottom() - bounds.height(), |
| 541 | static_cast<int>(y_pos + 0.5)))); |
| 542 | y_pos += bounds.height() + delta; |
| 543 | |
| 544 | // All docked windows other than the one currently dragged remain stuck |
| 545 | // to the screen edge. |
| 546 | if (window == dragged_window_) |
| 547 | continue; |
| 548 | switch (alignment_) { |
| 549 | case DOCKED_ALIGNMENT_LEFT: |
| 550 | bounds.set_x(0); |
| 551 | break; |
| 552 | case DOCKED_ALIGNMENT_RIGHT: |
| 553 | bounds.set_x(dock_bounds.right() - bounds.width()); |
| 554 | break; |
| 555 | case DOCKED_ALIGNMENT_NONE: |
| 556 | NOTREACHED() << "Relayout called when dock alignment is 'NONE'"; |
| 557 | break; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 558 | } |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 559 | // Keep the dock at least kMinDockWidth when all windows in it overhang. |
| 560 | docked_width_ = std::min(kMaxDockWidth, |
| 561 | std::max(docked_width_, |
| 562 | bounds.width() > kMaxDockWidth ? |
| 563 | kMinDockWidth : bounds.width())); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 564 | SetChildBoundsDirect(window, bounds); |
| 565 | } |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 566 | UpdateStacking(active_window); |
| 567 | } |
| 568 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 569 | void DockedWindowLayoutManager::UpdateDockBounds() { |
| 570 | int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0); |
| 571 | gfx::Rect bounds = gfx::Rect( |
| 572 | alignment_ == DOCKED_ALIGNMENT_RIGHT ? |
| 573 | dock_container_->bounds().right() - dock_inset: |
| 574 | dock_container_->bounds().x(), |
| 575 | dock_container_->bounds().y(), |
| 576 | dock_inset, |
| 577 | dock_container_->bounds().height()); |
| 578 | docked_bounds_ = bounds + |
| 579 | dock_container_->GetBoundsInScreen().OffsetFromOrigin(); |
| 580 | FOR_EACH_OBSERVER( |
| 581 | DockedWindowLayoutManagerObserver, |
| 582 | observer_list_, |
| 583 | OnDockBoundsChanging(bounds)); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 584 | |
| 585 | // Show or hide background for docked area. |
| 586 | gfx::Rect background_bounds(docked_bounds_); |
| 587 | const gfx::Rect work_area = |
| 588 | Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area(); |
| 589 | background_bounds.set_height(work_area.height()); |
| 590 | background_widget_->SetBounds(background_bounds); |
| 591 | if (docked_width_ > 0) |
| 592 | background_widget_->Show(); |
| 593 | else |
| 594 | background_widget_->Hide(); |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 595 | } |
| 596 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 597 | void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) { |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 598 | if (!active_window) { |
| 599 | if (!last_active_window_) |
| 600 | return; |
| 601 | active_window = last_active_window_; |
| 602 | } |
| 603 | |
| 604 | // We want to to stack the windows like a deck of cards: |
| 605 | // ,------. |
| 606 | // |,------.| |
| 607 | // |,------.| |
| 608 | // | active | |
| 609 | // | window | |
| 610 | // |`------'| |
| 611 | // |`------'| |
| 612 | // `------' |
| 613 | // We use the middle of each window to figure out how to stack the window. |
| 614 | // This allows us to update the stacking when a window is being dragged around |
| 615 | // by the titlebar. |
| 616 | std::map<int, aura::Window*> window_ordering; |
| 617 | for (aura::Window::Windows::const_iterator it = |
| 618 | dock_container_->children().begin(); |
| 619 | it != dock_container_->children().end(); ++it) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 620 | if (!IsUsedByLayout(*it)) |
| 621 | continue; |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 622 | gfx::Rect bounds = (*it)->bounds(); |
| 623 | window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2, |
| 624 | *it)); |
| 625 | } |
| 626 | int active_center_y = active_window->bounds().CenterPoint().y(); |
| 627 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 628 | aura::Window* previous_window = background_widget_->GetNativeWindow(); |
Ben Murdoch | bbcdd45 | 2013-07-25 10:06:34 +0100 | [diff] [blame] | 629 | for (std::map<int, aura::Window*>::const_iterator it = |
| 630 | window_ordering.begin(); |
| 631 | it != window_ordering.end() && it->first < active_center_y; ++it) { |
| 632 | if (previous_window) |
| 633 | dock_container_->StackChildAbove(it->second, previous_window); |
| 634 | previous_window = it->second; |
| 635 | } |
| 636 | |
| 637 | previous_window = NULL; |
| 638 | for (std::map<int, aura::Window*>::const_reverse_iterator it = |
| 639 | window_ordering.rbegin(); |
| 640 | it != window_ordering.rend() && it->first > active_center_y; ++it) { |
| 641 | if (previous_window) |
| 642 | dock_container_->StackChildAbove(it->second, previous_window); |
| 643 | previous_window = it->second; |
| 644 | } |
| 645 | |
| 646 | if (active_window->parent() == dock_container_) |
| 647 | dock_container_->StackChildAtTop(active_window); |
| 648 | if (dragged_window_ && dragged_window_->parent() == dock_container_) |
| 649 | dock_container_->StackChildAtTop(dragged_window_); |
| 650 | last_active_window_ = active_window; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 651 | } |
| 652 | |
| 653 | //////////////////////////////////////////////////////////////////////////////// |
| 654 | // keyboard::KeyboardControllerObserver implementation: |
| 655 | |
| 656 | void DockedWindowLayoutManager::OnKeyboardBoundsChanging( |
| 657 | const gfx::Rect& keyboard_bounds) { |
| 658 | // This bounds change will have caused a change to the Shelf which does not |
| 659 | // propagate automatically to this class, so manually recalculate bounds. |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 660 | UpdateDockBounds(); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 661 | } |
| 662 | |
| 663 | } // namespace internal |
| 664 | } // namespace ash |