blob: d73a0ccac7db422060a702be568cd6cbf2ca7855 [file] [log] [blame]
Ben Murdochca12bfa2013-07-23 11:17:05 +01001// Copyright (c) 2013 The Chromium Authors. All rights reserved.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +01002// 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 Murdochca12bfa2013-07-23 11:17:05 +010015#include "ash/wm/window_properties.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010016#include "ash/wm/window_util.h"
17#include "base/auto_reset.h"
Ben Murdochbb1529c2013-08-08 10:24:53 +010018#include "third_party/skia/include/core/SkColor.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010019#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
26namespace ash {
27namespace internal {
28
Ben Murdochca12bfa2013-07-23 11:17:05 +010029// Minimum, maximum width of the dock area and a width of the gap
30const int DockedWindowLayoutManager::kMinDockWidth = 200;
31const int DockedWindowLayoutManager::kMaxDockWidth = 450;
32const int DockedWindowLayoutManager::kMinDockGap = 2;
Ben Murdochbbcdd452013-07-25 10:06:34 +010033const int kWindowIdealSpacing = 4;
Ben Murdochca12bfa2013-07-23 11:17:05 +010034
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010035namespace {
36
Ben Murdochbb1529c2013-08-08 10:24:53 +010037const SkColor kDockBackgroundColor = SkColorSetARGB(0xff, 0x10, 0x10, 0x10);
38const float kDockBackgroundOpacity = 0.5f;
39
40class 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)7d4cd472013-06-19 11:58:07 +010070DockedWindowLayoutManager* 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 Murdochca12bfa2013-07-23 11:17:05 +010080// Certain windows (minimized, hidden or popups) do not matter to docking.
81bool IsUsedByLayout(aura::Window* window) {
82 return (window->IsVisible() &&
83 !wm::IsWindowMinimized(window) &&
84 window->type() != aura::client::WINDOW_TYPE_POPUP);
85}
86
Ben Murdochbbcdd452013-07-25 10:06:34 +010087bool 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)7d4cd472013-06-19 11:58:07 +010092} // namespace
93
94////////////////////////////////////////////////////////////////////////////////
95// DockLayoutManager public implementation:
96DockedWindowLayoutManager::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 Murdochca12bfa2013-07-23 11:17:05 +0100104 docked_width_(0),
Ben Murdochbbcdd452013-07-25 10:06:34 +0100105 alignment_(DOCKED_ALIGNMENT_NONE),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100106 last_active_window_(NULL),
107 background_widget_(new DockedBackgroundWidget(dock_container_)) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100108 DCHECK(dock_container);
109 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
110 AddObserver(this);
111 Shell::GetInstance()->AddShellObserver(this);
112}
113
114DockedWindowLayoutManager::~DockedWindowLayoutManager() {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100115 Shutdown();
116}
117
118void DockedWindowLayoutManager::Shutdown() {
119 if (shelf_layout_manager_) {
120 shelf_layout_manager_->RemoveObserver(this);
121 }
122 shelf_layout_manager_ = NULL;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100123 launcher_ = NULL;
124 for (size_t i = 0; i < dock_container_->children().size(); ++i)
125 dock_container_->children()[i]->RemoveObserver(this);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100126 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
127 RemoveObserver(this);
128 Shell::GetInstance()->RemoveShellObserver(this);
129}
130
Ben Murdochca12bfa2013-07-23 11:17:05 +0100131void DockedWindowLayoutManager::AddObserver(
132 DockedWindowLayoutManagerObserver* observer) {
133 observer_list_.AddObserver(observer);
134}
135
136void DockedWindowLayoutManager::RemoveObserver(
137 DockedWindowLayoutManagerObserver* observer) {
138 observer_list_.RemoveObserver(observer);
139}
140
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100141void 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
149void DockedWindowLayoutManager::FinishDragging() {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100150 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 Murdochbbcdd452013-07-25 10:06:34 +0100172
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 Murdochca12bfa2013-07-23 11:17:05 +0100179
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100180 Relayout();
Ben Murdochca12bfa2013-07-23 11:17:05 +0100181 UpdateDockBounds();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100182}
183
184// static
185bool DockedWindowLayoutManager::ShouldWindowDock(aura::Window* window,
186 const gfx::Point& location) {
187 DockedWindowLayoutManager* layout_manager = GetDockLayoutManager(window,
188 location);
Ben Murdochca12bfa2013-07-23 11:17:05 +0100189 const DockedAlignment alignment = layout_manager->CalculateAlignment();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100190 const gfx::Rect& bounds(window->GetBoundsInScreen());
Ben Murdochca12bfa2013-07-23 11:17:05 +0100191 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)7d4cd472013-06-19 11:58:07 +0100208 }
209
Ben Murdochca12bfa2013-07-23 11:17:05 +0100210 // Do not allow docking on the same side as launcher shelf.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100211 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 Murdochca12bfa2013-07-23 11:17:05 +0100215 dock_edge == DOCKED_ALIGNMENT_LEFT) ||
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100216 (shelf_alignment == SHELF_ALIGNMENT_RIGHT &&
Ben Murdochca12bfa2013-07-23 11:17:05 +0100217 dock_edge == DOCKED_ALIGNMENT_RIGHT)) {
218 dock_edge = DOCKED_ALIGNMENT_NONE;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100219 }
220 }
Ben Murdochca12bfa2013-07-23 11:17:05 +0100221
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)7d4cd472013-06-19 11:58:07 +0100230}
231
232void 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 Murdochca12bfa2013-07-23 11:17:05 +0100244DockedAlignment 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)7d4cd472013-06-19 11:58:07 +0100261////////////////////////////////////////////////////////////////////////////////
262// DockLayoutManager, aura::LayoutManager implementation:
263void DockedWindowLayoutManager::OnWindowResized() {
264 Relayout();
Ben Murdochca12bfa2013-07-23 11:17:05 +0100265 // When screen resizes we need to update the insets even when dock width or
266 // alignment does not change.
267 UpdateDockBounds();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100268}
269
270void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100271 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 Murdochbbcdd452013-07-25 10:06:34 +0100277 // 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)7d4cd472013-06-19 11:58:07 +0100281 Relayout();
Ben Murdochca12bfa2013-07-23 11:17:05 +0100282 UpdateDockBounds();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100283}
284
285void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100286 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 Murdochbbcdd452013-07-25 10:06:34 +0100302 // 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 Murdochca12bfa2013-07-23 11:17:05 +0100310 // Close the dock and expand workspace work area.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100311 Relayout();
Ben Murdochca12bfa2013-07-23 11:17:05 +0100312
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)7d4cd472013-06-19 11:58:07 +0100318}
319
320void DockedWindowLayoutManager::OnChildWindowVisibilityChanged(
321 aura::Window* child,
322 bool visible) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100323 if (child->type() == aura::client::WINDOW_TYPE_POPUP)
324 return;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100325 if (visible)
326 child->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
327 Relayout();
Ben Murdochca12bfa2013-07-23 11:17:05 +0100328 UpdateDockBounds();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100329}
330
331void 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 Murdochca12bfa2013-07-23 11:17:05 +0100336 if (child->type() == aura::client::WINDOW_TYPE_POPUP)
337 return;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100338 Relayout();
339}
340
341////////////////////////////////////////////////////////////////////////////////
342// DockLayoutManager, ash::ShellObserver implementation:
343
344void 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)7d4cd472013-06-19 11:58:07 +0100355 // 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 Murdochca12bfa2013-07-23 11:17:05 +0100357 ShelfAlignment shelf_alignment = launcher_->shelf_widget()->GetAlignment();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100358 if (alignment_ == DOCKED_ALIGNMENT_LEFT &&
359 shelf_alignment == SHELF_ALIGNMENT_LEFT) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100360 alignment_ = DOCKED_ALIGNMENT_RIGHT;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100361 } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT &&
362 shelf_alignment == SHELF_ALIGNMENT_RIGHT) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100363 alignment_ = DOCKED_ALIGNMENT_LEFT;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100364 }
365 Relayout();
Ben Murdochca12bfa2013-07-23 11:17:05 +0100366 UpdateDockBounds();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100367}
368
369/////////////////////////////////////////////////////////////////////////////
370// DockLayoutManager, WindowObserver implementation:
371
372void 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 Murdochbbcdd452013-07-25 10:06:34 +0100387void 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
395void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) {
396 if (dragged_window_ == window)
397 dragged_window_ = NULL;
398}
399
400
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100401////////////////////////////////////////////////////////////////////////////////
402// DockLayoutManager, aura::client::ActivationChangeObserver implementation:
403
404void 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
422void 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 Murdochca12bfa2013-07-23 11:17:05 +0100449 UpdateDockBounds();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100450}
451
452////////////////////////////////////////////////////////////////////////////////
453// DockLayoutManager private implementation:
454
455void DockedWindowLayoutManager::MinimizeWindow(aura::Window* window) {
456 window->Hide();
457 if (wm::IsActiveWindow(window))
458 wm::DeactivateWindow(window);
459}
460
461void DockedWindowLayoutManager::RestoreWindow(aura::Window* window) {
462 window->Show();
463}
464
Ben Murdochca12bfa2013-07-23 11:17:05 +0100465DockedAlignment 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)7d4cd472013-06-19 11:58:07 +0100476void DockedWindowLayoutManager::Relayout() {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100477 if (in_layout_ || alignment_ == DOCKED_ALIGNMENT_NONE)
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100478 return;
479 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
480
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100481 gfx::Rect dock_bounds = dock_container_->bounds();
482 aura::Window* active_window = NULL;
Ben Murdochbbcdd452013-07-25 10:06:34 +0100483 aura::Window::Windows visible_windows;
Ben Murdochca12bfa2013-07-23 11:17:05 +0100484 docked_width_ = 0;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100485 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
486 aura::Window* window(dock_container_->children()[i]);
487
Ben Murdochca12bfa2013-07-23 11:17:05 +0100488 if (!IsUsedByLayout(window))
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100489 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)7d4cd472013-06-19 11:58:07 +0100499 if (window->HasFocus() ||
500 window->Contains(
501 aura::client::GetFocusClient(window)->GetFocusedWindow())) {
502 DCHECK(!active_window);
503 active_window = window;
504 }
Ben Murdochbbcdd452013-07-25 10:06:34 +0100505 visible_windows.push_back(window);
506 }
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100507
Ben Murdochbbcdd452013-07-25 10:06:34 +0100508 // 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 Murdochca12bfa2013-07-23 11:17:05 +0100533 gfx::Rect bounds = window->GetTargetBounds();
Ben Murdochbbcdd452013-07-25 10:06:34 +0100534 // 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)7d4cd472013-06-19 11:58:07 +0100558 }
Ben Murdochbbcdd452013-07-25 10:06:34 +0100559 // 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)7d4cd472013-06-19 11:58:07 +0100564 SetChildBoundsDirect(window, bounds);
565 }
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100566 UpdateStacking(active_window);
567}
568
Ben Murdochca12bfa2013-07-23 11:17:05 +0100569void 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 Murdochbb1529c2013-08-08 10:24:53 +0100584
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 Murdochca12bfa2013-07-23 11:17:05 +0100595}
596
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100597void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
Ben Murdochbbcdd452013-07-25 10:06:34 +0100598 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 Murdochbb1529c2013-08-08 10:24:53 +0100620 if (!IsUsedByLayout(*it))
621 continue;
Ben Murdochbbcdd452013-07-25 10:06:34 +0100622 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 Murdochbb1529c2013-08-08 10:24:53 +0100628 aura::Window* previous_window = background_widget_->GetNativeWindow();
Ben Murdochbbcdd452013-07-25 10:06:34 +0100629 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)7d4cd472013-06-19 11:58:07 +0100651}
652
653////////////////////////////////////////////////////////////////////////////////
654// keyboard::KeyboardControllerObserver implementation:
655
656void 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 Murdochca12bfa2013-07-23 11:17:05 +0100660 UpdateDockBounds();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100661}
662
663} // namespace internal
664} // namespace ash