Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / ash / wm / panels / panel_layout_manager.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/wm/panels/panel_layout_manager.h"
6
7 #include <algorithm>
8 #include <map>
9
10 #include "ash/screen_util.h"
11 #include "ash/shelf/shelf.h"
12 #include "ash/shelf/shelf_layout_manager.h"
13 #include "ash/shelf/shelf_types.h"
14 #include "ash/shelf/shelf_widget.h"
15 #include "ash/shell.h"
16 #include "ash/shell_window_ids.h"
17 #include "ash/wm/window_animations.h"
18 #include "ash/wm/window_state.h"
19 #include "ash/wm/window_util.h"
20 #include "base/auto_reset.h"
21 #include "base/bind.h"
22 #include "base/bind_helpers.h"
23 #include "third_party/skia/include/core/SkColor.h"
24 #include "third_party/skia/include/core/SkPaint.h"
25 #include "third_party/skia/include/core/SkPath.h"
26 #include "ui/aura/client/activation_client.h"
27 #include "ui/aura/client/focus_client.h"
28 #include "ui/aura/client/window_tree_client.h"
29 #include "ui/aura/root_window.h"
30 #include "ui/aura/window.h"
31 #include "ui/aura/window_tracker.h"
32 #include "ui/compositor/scoped_layer_animation_settings.h"
33 #include "ui/gfx/canvas.h"
34 #include "ui/gfx/rect.h"
35 #include "ui/gfx/vector2d.h"
36 #include "ui/views/background.h"
37 #include "ui/views/widget/widget.h"
38
39 namespace ash {
40 namespace internal {
41
42 namespace {
43 const int kPanelIdealSpacing = 4;
44
45 const float kMaxHeightFactor = .80f;
46 const float kMaxWidthFactor = .50f;
47
48 // Duration for panel animations.
49 const int kPanelSlideDurationMilliseconds = 50;
50 const int kCalloutFadeDurationMilliseconds = 50;
51
52 // Offset used when sliding panel in/out of the shelf. Used for minimizing,
53 // restoring and the initial showing of a panel.
54 const int kPanelSlideInOffset = 20;
55
56 // Callout arrow dimensions.
57 const int kArrowWidth = 18;
58 const int kArrowHeight = 9;
59
60 class CalloutWidgetBackground : public views::Background {
61  public:
62   CalloutWidgetBackground() : alignment_(SHELF_ALIGNMENT_BOTTOM) {
63   }
64
65   virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
66     SkPath path;
67     switch (alignment_) {
68       case SHELF_ALIGNMENT_BOTTOM:
69         path.moveTo(SkIntToScalar(0), SkIntToScalar(0));
70         path.lineTo(SkIntToScalar(kArrowWidth / 2),
71                     SkIntToScalar(kArrowHeight));
72         path.lineTo(SkIntToScalar(kArrowWidth), SkIntToScalar(0));
73         break;
74       case SHELF_ALIGNMENT_LEFT:
75         path.moveTo(SkIntToScalar(kArrowHeight), SkIntToScalar(kArrowWidth));
76         path.lineTo(SkIntToScalar(0), SkIntToScalar(kArrowWidth / 2));
77         path.lineTo(SkIntToScalar(kArrowHeight), SkIntToScalar(0));
78         break;
79       case SHELF_ALIGNMENT_TOP:
80         path.moveTo(SkIntToScalar(0), SkIntToScalar(kArrowHeight));
81         path.lineTo(SkIntToScalar(kArrowWidth / 2), SkIntToScalar(0));
82         path.lineTo(SkIntToScalar(kArrowWidth), SkIntToScalar(kArrowHeight));
83         break;
84       case SHELF_ALIGNMENT_RIGHT:
85         path.moveTo(SkIntToScalar(0), SkIntToScalar(0));
86         path.lineTo(SkIntToScalar(kArrowHeight),
87                     SkIntToScalar(kArrowWidth / 2));
88         path.lineTo(SkIntToScalar(0), SkIntToScalar(kArrowWidth));
89         break;
90     }
91     // Hard code the arrow color for now.
92     SkPaint paint;
93     paint.setStyle(SkPaint::kFill_Style);
94     paint.setColor(SkColorSetARGB(0xff, 0xe5, 0xe5, 0xe5));
95     canvas->DrawPath(path, paint);
96   }
97
98   ShelfAlignment alignment() {
99     return alignment_;
100   }
101
102   void set_alignment(ShelfAlignment alignment) {
103     alignment_ = alignment;
104   }
105
106  private:
107   ShelfAlignment alignment_;
108
109   DISALLOW_COPY_AND_ASSIGN(CalloutWidgetBackground);
110 };
111
112 struct VisiblePanelPositionInfo {
113   VisiblePanelPositionInfo()
114       : min_major(0),
115         max_major(0),
116         major_pos(0),
117         major_length(0),
118         window(NULL),
119         slide_in(false) {}
120
121   int min_major;
122   int max_major;
123   int major_pos;
124   int major_length;
125   aura::Window* window;
126   bool slide_in;
127 };
128
129 bool CompareWindowMajor(const VisiblePanelPositionInfo& win1,
130                         const VisiblePanelPositionInfo& win2) {
131   return win1.major_pos < win2.major_pos;
132 }
133
134 void FanOutPanels(std::vector<VisiblePanelPositionInfo>::iterator first,
135                   std::vector<VisiblePanelPositionInfo>::iterator last) {
136   int num_panels = last - first;
137   if (num_panels == 1) {
138     (*first).major_pos = std::max((*first).min_major, std::min(
139         (*first).max_major, (*first).major_pos));
140   }
141   if (num_panels <= 1)
142     return;
143
144   if (num_panels == 2) {
145     // If there are two adjacent overlapping windows, separate them by the
146     // minimum major_length necessary.
147     std::vector<VisiblePanelPositionInfo>::iterator second = first + 1;
148     int separation = (*first).major_length / 2 + (*second).major_length / 2 +
149                      kPanelIdealSpacing;
150     int overlap = (*first).major_pos + separation - (*second).major_pos;
151     (*first).major_pos = std::max((*first).min_major,
152                                   (*first).major_pos - overlap / 2);
153     (*second).major_pos = std::min((*second).max_major,
154                                    (*first).major_pos + separation);
155     // Recalculate the first panel position in case the second one was
156     // constrained on the right.
157     (*first).major_pos = std::max((*first).min_major,
158                                   (*second).major_pos - separation);
159     return;
160   }
161
162   // If there are more than two overlapping windows, fan them out from minimum
163   // position to maximum position equally spaced.
164   int delta = ((*(last - 1)).max_major - (*first).min_major) / (num_panels - 1);
165   int major_pos = (*first).min_major;
166   for (std::vector<VisiblePanelPositionInfo>::iterator iter = first;
167       iter != last; ++iter) {
168     (*iter).major_pos = std::max((*iter).min_major,
169                                  std::min((*iter).max_major, major_pos));
170     major_pos += delta;
171   }
172 }
173
174 bool BoundsAdjacent(const gfx::Rect& bounds1, const gfx::Rect& bounds2) {
175   return bounds1.x() == bounds2.right() ||
176          bounds1.y() == bounds2.bottom() ||
177          bounds1.right() == bounds2.x() ||
178          bounds1.bottom() == bounds2.y();
179 }
180
181 gfx::Vector2d GetSlideInAnimationOffset(ShelfAlignment alignment) {
182   gfx::Vector2d offset;
183   switch (alignment) {
184     case SHELF_ALIGNMENT_BOTTOM:
185       offset.set_y(kPanelSlideInOffset);
186       break;
187     case SHELF_ALIGNMENT_LEFT:
188       offset.set_x(-kPanelSlideInOffset);
189       break;
190     case SHELF_ALIGNMENT_RIGHT:
191       offset.set_x(kPanelSlideInOffset);
192       break;
193     case SHELF_ALIGNMENT_TOP:
194       offset.set_y(-kPanelSlideInOffset);
195       break;
196   }
197   return offset;
198 }
199
200 }  // namespace
201
202 class PanelCalloutWidget : public views::Widget {
203  public:
204   explicit PanelCalloutWidget(aura::Window* container)
205       : background_(NULL) {
206     InitWidget(container);
207   }
208
209   void SetAlignment(ShelfAlignment alignment) {
210     gfx::Rect callout_bounds = GetWindowBoundsInScreen();
211     if (alignment == SHELF_ALIGNMENT_BOTTOM ||
212         alignment == SHELF_ALIGNMENT_TOP) {
213       callout_bounds.set_width(kArrowWidth);
214       callout_bounds.set_height(kArrowHeight);
215     } else {
216       callout_bounds.set_width(kArrowHeight);
217       callout_bounds.set_height(kArrowWidth);
218     }
219     GetNativeWindow()->SetBounds(callout_bounds);
220     if (background_->alignment() != alignment) {
221       background_->set_alignment(alignment);
222       SchedulePaintInRect(gfx::Rect(gfx::Point(), callout_bounds.size()));
223     }
224   }
225
226  private:
227   void InitWidget(aura::Window* parent) {
228     views::Widget::InitParams params;
229     params.type = views::Widget::InitParams::TYPE_POPUP;
230     params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
231     params.can_activate = false;
232     params.keep_on_top = true;
233     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
234     params.parent = parent;
235     params.bounds = ScreenUtil::ConvertRectToScreen(parent, gfx::Rect());
236     params.bounds.set_width(kArrowWidth);
237     params.bounds.set_height(kArrowHeight);
238     // Why do we need this and can_activate = false?
239     set_focus_on_creation(false);
240     Init(params);
241     DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow());
242     views::View* content_view = new views::View;
243     background_ = new CalloutWidgetBackground;
244     content_view->set_background(background_);
245     SetContentsView(content_view);
246     GetNativeWindow()->layer()->SetOpacity(0);
247   }
248
249   // Weak pointer owned by this widget's content view.
250   CalloutWidgetBackground* background_;
251
252   DISALLOW_COPY_AND_ASSIGN(PanelCalloutWidget);
253 };
254
255 ////////////////////////////////////////////////////////////////////////////////
256 // PanelLayoutManager public implementation:
257 PanelLayoutManager::PanelLayoutManager(aura::Window* panel_container)
258     : panel_container_(panel_container),
259       in_add_window_(false),
260       in_layout_(false),
261       dragged_panel_(NULL),
262       shelf_(NULL),
263       shelf_layout_manager_(NULL),
264       last_active_panel_(NULL),
265       weak_factory_(this) {
266   DCHECK(panel_container);
267   aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
268       AddObserver(this);
269   Shell::GetInstance()->display_controller()->AddObserver(this);
270   Shell::GetInstance()->AddShellObserver(this);
271 }
272
273 PanelLayoutManager::~PanelLayoutManager() {
274   Shutdown();
275 }
276
277 void PanelLayoutManager::Shutdown() {
278   if (shelf_layout_manager_)
279     shelf_layout_manager_->RemoveObserver(this);
280   shelf_layout_manager_ = NULL;
281   for (PanelList::iterator iter = panel_windows_.begin();
282        iter != panel_windows_.end(); ++iter) {
283     delete iter->callout_widget;
284   }
285   panel_windows_.clear();
286   if (shelf_)
287     shelf_->RemoveIconObserver(this);
288   shelf_ = NULL;
289   aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
290       RemoveObserver(this);
291   Shell::GetInstance()->display_controller()->RemoveObserver(this);
292   Shell::GetInstance()->RemoveShellObserver(this);
293 }
294
295 void PanelLayoutManager::StartDragging(aura::Window* panel) {
296   DCHECK(!dragged_panel_);
297   dragged_panel_ = panel;
298   Relayout();
299 }
300
301 void PanelLayoutManager::FinishDragging() {
302   dragged_panel_ = NULL;
303   Relayout();
304 }
305
306 void PanelLayoutManager::SetShelf(Shelf* shelf) {
307   DCHECK(!shelf_);
308   DCHECK(!shelf_layout_manager_);
309   shelf_ = shelf;
310   shelf_->AddIconObserver(this);
311   if (shelf_->shelf_widget()) {
312     shelf_layout_manager_ = ash::internal::ShelfLayoutManager::ForShelf(
313         shelf_->shelf_widget()->GetNativeWindow());
314     WillChangeVisibilityState(shelf_layout_manager_->visibility_state());
315     shelf_layout_manager_->AddObserver(this);
316   }
317 }
318
319 void PanelLayoutManager::ToggleMinimize(aura::Window* panel) {
320   DCHECK(panel->parent() == panel_container_);
321   wm::WindowState* window_state = wm::GetWindowState(panel);
322   if (window_state->IsMinimized())
323     window_state->Restore();
324   else
325     window_state->Minimize();
326 }
327
328 views::Widget* PanelLayoutManager::GetCalloutWidgetForPanel(
329     aura::Window* panel) {
330   DCHECK(panel->parent() == panel_container_);
331   PanelList::iterator found =
332       std::find(panel_windows_.begin(), panel_windows_.end(), panel);
333   DCHECK(found != panel_windows_.end());
334   return found->callout_widget;
335 }
336
337 ////////////////////////////////////////////////////////////////////////////////
338 // PanelLayoutManager, aura::LayoutManager implementation:
339 void PanelLayoutManager::OnWindowResized() {
340   Relayout();
341 }
342
343 void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
344   if (child->type() == ui::wm::WINDOW_TYPE_POPUP)
345     return;
346   if (in_add_window_)
347     return;
348   base::AutoReset<bool> auto_reset_in_add_window(&in_add_window_, true);
349   if (!wm::GetWindowState(child)->panel_attached()) {
350     // This should only happen when a window is added to panel container as a
351     // result of bounds change from within the application during a drag.
352     // If so we have already stopped the drag and should reparent the panel
353     // back to appropriate container and ignore it.
354     // TODO(varkha): Updating bounds during a drag can cause problems and a more
355     // general solution is needed. See http://crbug.com/251813 .
356     aura::Window* old_parent = child->parent();
357     aura::client::ParentWindowWithContext(
358         child, child, child->GetRootWindow()->GetBoundsInScreen());
359     wm::ReparentTransientChildrenOfChild(child, old_parent, child->parent());
360     DCHECK(child->parent()->id() != kShellWindowId_PanelContainer);
361     return;
362   }
363   PanelInfo panel_info;
364   panel_info.window = child;
365   panel_info.callout_widget = new PanelCalloutWidget(panel_container_);
366   if (child != dragged_panel_) {
367     // Set the panel to 0 opacity until it has been positioned to prevent it
368     // from flashing briefly at position (0, 0).
369     child->layer()->SetOpacity(0);
370     panel_info.slide_in = true;
371   }
372   panel_windows_.push_back(panel_info);
373   child->AddObserver(this);
374   wm::GetWindowState(child)->AddObserver(this);
375   Relayout();
376 }
377
378 void PanelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
379 }
380
381 void PanelLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
382   if (child->type() == ui::wm::WINDOW_TYPE_POPUP)
383     return;
384   PanelList::iterator found =
385       std::find(panel_windows_.begin(), panel_windows_.end(), child);
386   if (found != panel_windows_.end()) {
387     delete found->callout_widget;
388     panel_windows_.erase(found);
389   }
390   child->RemoveObserver(this);
391   wm::GetWindowState(child)->RemoveObserver(this);
392
393   if (dragged_panel_ == child)
394     dragged_panel_ = NULL;
395
396   if (last_active_panel_ == child)
397     last_active_panel_ = NULL;
398
399   Relayout();
400 }
401
402 void PanelLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
403                                                         bool visible) {
404   Relayout();
405 }
406
407 void PanelLayoutManager::SetChildBounds(aura::Window* child,
408                                         const gfx::Rect& requested_bounds) {
409   gfx::Rect bounds(requested_bounds);
410   const gfx::Rect& max_bounds = panel_container_->GetRootWindow()->bounds();
411   const int max_width = max_bounds.width() * kMaxWidthFactor;
412   const int max_height = max_bounds.height() * kMaxHeightFactor;
413   if (bounds.width() > max_width)
414     bounds.set_width(max_width);
415   if (bounds.height() > max_height)
416     bounds.set_height(max_height);
417
418   // Reposition dragged panel in the panel order.
419   if (dragged_panel_ == child) {
420     PanelList::iterator dragged_panel_iter =
421       std::find(panel_windows_.begin(), panel_windows_.end(), dragged_panel_);
422     DCHECK(dragged_panel_iter != panel_windows_.end());
423     PanelList::iterator new_position;
424     for (new_position = panel_windows_.begin();
425          new_position != panel_windows_.end();
426          ++new_position) {
427       const gfx::Rect& bounds = (*new_position).window->bounds();
428       if (bounds.x() + bounds.width()/2 <= requested_bounds.x()) break;
429     }
430     if (new_position != dragged_panel_iter) {
431       PanelInfo dragged_panel_info = *dragged_panel_iter;
432       panel_windows_.erase(dragged_panel_iter);
433       panel_windows_.insert(new_position, dragged_panel_info);
434     }
435   }
436
437   SetChildBoundsDirect(child, bounds);
438   Relayout();
439 }
440
441 ////////////////////////////////////////////////////////////////////////////////
442 // PanelLayoutManager, ShelfIconObserver implementation:
443
444 void PanelLayoutManager::OnShelfIconPositionsChanged() {
445   // TODO: As this is called for every animation step now. Relayout needs to be
446   // updated to use current icon position instead of use the ideal bounds so
447   // that the panels slide with their icons instead of jumping.
448   Relayout();
449 }
450
451 ////////////////////////////////////////////////////////////////////////////////
452 // PanelLayoutManager, ash::ShellObserver implementation:
453
454 void PanelLayoutManager::OnShelfAlignmentChanged(aura::Window* root_window) {
455   if (panel_container_->GetRootWindow() == root_window)
456     Relayout();
457 }
458
459 /////////////////////////////////////////////////////////////////////////////
460 // PanelLayoutManager, WindowObserver implementation:
461
462 void PanelLayoutManager::OnWindowShowTypeChanged(
463     wm::WindowState* window_state,
464     wm::WindowShowType old_type) {
465   // If the shelf is currently hidden then windows will not actually be shown
466   // but the set to restore when the shelf becomes visible is updated.
467   if (restore_windows_on_shelf_visible_) {
468     if (window_state->IsMinimized()) {
469       MinimizePanel(window_state->window());
470       restore_windows_on_shelf_visible_->Remove(window_state->window());
471     } else {
472       restore_windows_on_shelf_visible_->Add(window_state->window());
473     }
474     return;
475   }
476
477   if (window_state->IsMinimized())
478     MinimizePanel(window_state->window());
479   else
480     RestorePanel(window_state->window());
481 }
482
483 void PanelLayoutManager::OnWindowVisibilityChanged(
484     aura::Window* window, bool visible) {
485   if (visible)
486     wm::GetWindowState(window)->Restore();
487 }
488
489 ////////////////////////////////////////////////////////////////////////////////
490 // PanelLayoutManager, aura::client::ActivationChangeObserver implementation:
491
492 void PanelLayoutManager::OnWindowActivated(aura::Window* gained_active,
493                                            aura::Window* lost_active) {
494   // Ignore if the panel that is not managed by this was activated.
495   if (gained_active && gained_active->type() == ui::wm::WINDOW_TYPE_PANEL &&
496       gained_active->parent() == panel_container_) {
497     UpdateStacking(gained_active);
498     UpdateCallouts();
499   }
500 }
501
502 ////////////////////////////////////////////////////////////////////////////////
503 // PanelLayoutManager, DisplayController::Observer implementation:
504
505 void PanelLayoutManager::OnDisplayConfigurationChanged() {
506   Relayout();
507 }
508
509 ////////////////////////////////////////////////////////////////////////////////
510 // PanelLayoutManager, ShelfLayoutManagerObserver implementation:
511
512 void PanelLayoutManager::WillChangeVisibilityState(
513     ShelfVisibilityState new_state) {
514   // On entering / leaving full screen mode the shelf visibility state is
515   // changed to / from SHELF_HIDDEN. In this state, panel windows should hide
516   // to allow the full-screen application to use the full screen.
517   bool shelf_hidden = new_state == ash::SHELF_HIDDEN;
518   if (!shelf_hidden) {
519     if (restore_windows_on_shelf_visible_) {
520       scoped_ptr<aura::WindowTracker> restore_windows(
521           restore_windows_on_shelf_visible_.Pass());
522       for (aura::WindowTracker::Windows::const_iterator iter =
523            restore_windows->windows().begin(); iter !=
524            restore_windows->windows().end(); ++iter) {
525         RestorePanel(*iter);
526       }
527     }
528     return;
529   }
530
531   if (restore_windows_on_shelf_visible_)
532     return;
533   scoped_ptr<aura::WindowTracker> minimized_windows(new aura::WindowTracker);
534   for (PanelList::iterator iter = panel_windows_.begin();
535        iter != panel_windows_.end(); ++iter) {
536     if (iter->window->IsVisible()) {
537       minimized_windows->Add(iter->window);
538       wm::GetWindowState(iter->window)->Minimize();
539     }
540   }
541   restore_windows_on_shelf_visible_ = minimized_windows.Pass();
542 }
543
544 ////////////////////////////////////////////////////////////////////////////////
545 // PanelLayoutManager private implementation:
546
547 void PanelLayoutManager::MinimizePanel(aura::Window* panel) {
548   views::corewm::SetWindowVisibilityAnimationType(
549       panel, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
550   ui::Layer* layer = panel->layer();
551   ui::ScopedLayerAnimationSettings panel_slide_settings(layer->GetAnimator());
552   panel_slide_settings.SetPreemptionStrategy(
553       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
554   panel_slide_settings.SetTransitionDuration(
555       base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds));
556   gfx::Rect bounds(panel->bounds());
557   bounds.Offset(GetSlideInAnimationOffset(
558       shelf_->shelf_widget()->GetAlignment()));
559   SetChildBoundsDirect(panel, bounds);
560   panel->Hide();
561   PanelList::iterator found =
562       std::find(panel_windows_.begin(), panel_windows_.end(), panel);
563   if (found != panel_windows_.end()) {
564     layer->SetOpacity(0);
565     // The next time the window is visible it should slide into place.
566     found->slide_in = true;
567   }
568   if (wm::IsActiveWindow(panel))
569     wm::DeactivateWindow(panel);
570   Relayout();
571 }
572
573 void PanelLayoutManager::RestorePanel(aura::Window* panel) {
574   panel->Show();
575   Relayout();
576 }
577
578 void PanelLayoutManager::Relayout() {
579   if (!shelf_ || !shelf_->shelf_widget())
580     return;
581
582   if (in_layout_)
583     return;
584   base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
585
586   ShelfAlignment alignment = shelf_->shelf_widget()->GetAlignment();
587   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
588                     alignment == SHELF_ALIGNMENT_BOTTOM;
589   gfx::Rect shelf_bounds = ash::ScreenUtil::ConvertRectFromScreen(
590       panel_container_, shelf_->shelf_widget()->GetWindowBoundsInScreen());
591   int panel_start_bounds = kPanelIdealSpacing;
592   int panel_end_bounds = horizontal ?
593       panel_container_->bounds().width() - kPanelIdealSpacing :
594       panel_container_->bounds().height() - kPanelIdealSpacing;
595   aura::Window* active_panel = NULL;
596   std::vector<VisiblePanelPositionInfo> visible_panels;
597   for (PanelList::iterator iter = panel_windows_.begin();
598        iter != panel_windows_.end(); ++iter) {
599     aura::Window* panel = iter->window;
600     iter->callout_widget->SetAlignment(alignment);
601
602     // Consider the dragged panel as part of the layout as long as it is
603     // touching the shelf.
604     if (!panel->IsVisible() ||
605         (panel == dragged_panel_ &&
606          !BoundsAdjacent(panel->bounds(), shelf_bounds))) {
607       continue;
608     }
609
610     // If the shelf is currently hidden (full-screen mode), minimize panel until
611     // full-screen mode is exited.
612     if (restore_windows_on_shelf_visible_) {
613       wm::GetWindowState(panel)->Minimize();
614       restore_windows_on_shelf_visible_->Add(panel);
615       continue;
616     }
617
618     gfx::Rect icon_bounds = shelf_->GetScreenBoundsOfItemIconForWindow(panel);
619
620     // If both the icon width and height are 0 then there is no icon in the
621     // shelf. If the shelf is hidden, one of the height or width will be
622     // 0 but the position in the shelf and major dimension is still reported
623     // correctly and the panel can be aligned above where the hidden icon is.
624     if (icon_bounds.width() == 0 && icon_bounds.height() == 0)
625       continue;
626
627     if (panel->HasFocus() ||
628         panel->Contains(
629             aura::client::GetFocusClient(panel)->GetFocusedWindow())) {
630       DCHECK(!active_panel);
631       active_panel = panel;
632     }
633     icon_bounds = ScreenUtil::ConvertRectFromScreen(panel_container_,
634                                                     icon_bounds);
635     gfx::Point icon_origin = icon_bounds.origin();
636     VisiblePanelPositionInfo position_info;
637     int icon_start = horizontal ? icon_origin.x() : icon_origin.y();
638     int icon_end = icon_start + (horizontal ? icon_bounds.width() :
639                                  icon_bounds.height());
640     position_info.major_length = horizontal ?
641         panel->bounds().width() : panel->bounds().height();
642     position_info.min_major = std::max(
643         panel_start_bounds + position_info.major_length / 2,
644         icon_end - position_info.major_length / 2);
645     position_info.max_major = std::min(
646         icon_start + position_info.major_length / 2,
647         panel_end_bounds - position_info.major_length / 2);
648     position_info.major_pos = (icon_start + icon_end) / 2;
649     position_info.window = panel;
650     position_info.slide_in = iter->slide_in;
651     iter->slide_in = false;
652     visible_panels.push_back(position_info);
653   }
654
655   // Sort panels by their X positions and fan out groups of overlapping panels.
656   // The fan out method may result in new overlapping panels however given that
657   // the panels start at least a full panel width apart this overlap will
658   // never completely obscure a panel.
659   // TODO(flackr): Rearrange panels if new overlaps are introduced.
660   std::sort(visible_panels.begin(), visible_panels.end(), CompareWindowMajor);
661   size_t first_overlapping_panel = 0;
662   for (size_t i = 1; i < visible_panels.size(); ++i) {
663     if (visible_panels[i - 1].major_pos +
664         visible_panels[i - 1].major_length / 2 < visible_panels[i].major_pos -
665         visible_panels[i].major_length / 2) {
666       FanOutPanels(visible_panels.begin() + first_overlapping_panel,
667                    visible_panels.begin() + i);
668       first_overlapping_panel = i;
669     }
670   }
671   FanOutPanels(visible_panels.begin() + first_overlapping_panel,
672                visible_panels.end());
673
674   for (size_t i = 0; i < visible_panels.size(); ++i) {
675     if (visible_panels[i].window == dragged_panel_)
676       continue;
677     bool slide_in = visible_panels[i].slide_in;
678     gfx::Rect bounds = visible_panels[i].window->GetTargetBounds();
679     switch (alignment) {
680       case SHELF_ALIGNMENT_BOTTOM:
681         bounds.set_y(shelf_bounds.y() - bounds.height());
682         break;
683       case SHELF_ALIGNMENT_LEFT:
684         bounds.set_x(shelf_bounds.right());
685         break;
686       case SHELF_ALIGNMENT_RIGHT:
687         bounds.set_x(shelf_bounds.x() - bounds.width());
688         break;
689       case SHELF_ALIGNMENT_TOP:
690         bounds.set_y(shelf_bounds.bottom());
691         break;
692     }
693     bool on_shelf = visible_panels[i].window->GetTargetBounds() == bounds;
694
695     if (horizontal) {
696       bounds.set_x(visible_panels[i].major_pos -
697                    visible_panels[i].major_length / 2);
698     } else {
699       bounds.set_y(visible_panels[i].major_pos -
700                    visible_panels[i].major_length / 2);
701     }
702
703     ui::Layer* layer = visible_panels[i].window->layer();
704     if (slide_in) {
705       // New windows shift up from the shelf into position.
706       gfx::Rect initial_bounds(bounds);
707       initial_bounds.Offset(GetSlideInAnimationOffset(alignment));
708       SetChildBoundsDirect(visible_panels[i].window, initial_bounds);
709       // Set on shelf so that the panel animates into its target position.
710       on_shelf = true;
711     }
712
713     if (on_shelf) {
714       ui::ScopedLayerAnimationSettings panel_slide_settings(
715           layer->GetAnimator());
716       panel_slide_settings.SetPreemptionStrategy(
717           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
718       panel_slide_settings.SetTransitionDuration(
719           base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds));
720       SetChildBoundsDirect(visible_panels[i].window, bounds);
721       if (slide_in)
722         layer->SetOpacity(1);
723     } else {
724       // If the shelf moved don't animate, move immediately to the new
725       // target location.
726       SetChildBoundsDirect(visible_panels[i].window, bounds);
727     }
728   }
729
730   UpdateStacking(active_panel);
731   UpdateCallouts();
732 }
733
734 void PanelLayoutManager::UpdateStacking(aura::Window* active_panel) {
735   if (!active_panel) {
736     if (!last_active_panel_)
737       return;
738     active_panel = last_active_panel_;
739   }
740
741   ShelfAlignment alignment = shelf_->alignment();
742   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
743                     alignment == SHELF_ALIGNMENT_BOTTOM;
744
745   // We want to to stack the panels like a deck of cards:
746   // ,--,--,--,-------.--.--.
747   // |  |  |  |       |  |  |
748   // |  |  |  |       |  |  |
749   //
750   // We use the middle of each panel to figure out how to stack the panels. This
751   // allows us to update the stacking when a panel is being dragged around by
752   // the titlebar--even though it doesn't update the shelf icon positions, we
753   // still want the visual effect.
754   std::map<int, aura::Window*> window_ordering;
755   for (PanelList::const_iterator it = panel_windows_.begin();
756        it != panel_windows_.end(); ++it) {
757     gfx::Rect bounds = it->window->bounds();
758     window_ordering.insert(std::make_pair(horizontal ?
759                                               bounds.x() + bounds.width() / 2 :
760                                               bounds.y() + bounds.height() / 2,
761                                           it->window));
762   }
763
764   aura::Window* previous_panel = NULL;
765   for (std::map<int, aura::Window*>::const_iterator it =
766        window_ordering.begin();
767        it != window_ordering.end() && it->second != active_panel; ++it) {
768     if (previous_panel)
769       panel_container_->StackChildAbove(it->second, previous_panel);
770     previous_panel = it->second;
771   }
772
773   previous_panel = NULL;
774   for (std::map<int, aura::Window*>::const_reverse_iterator it =
775        window_ordering.rbegin();
776        it != window_ordering.rend() && it->second != active_panel; ++it) {
777     if (previous_panel)
778       panel_container_->StackChildAbove(it->second, previous_panel);
779     previous_panel = it->second;
780   }
781
782   panel_container_->StackChildAtTop(active_panel);
783   if (dragged_panel_ && dragged_panel_->parent() == panel_container_)
784     panel_container_->StackChildAtTop(dragged_panel_);
785   last_active_panel_ = active_panel;
786 }
787
788 void PanelLayoutManager::UpdateCallouts() {
789   ShelfAlignment alignment = shelf_->alignment();
790   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
791                     alignment == SHELF_ALIGNMENT_BOTTOM;
792
793   for (PanelList::iterator iter = panel_windows_.begin();
794        iter != panel_windows_.end(); ++iter) {
795     aura::Window* panel = iter->window;
796     views::Widget* callout_widget = iter->callout_widget;
797
798     gfx::Rect current_bounds = panel->GetBoundsInScreen();
799     gfx::Rect bounds = ScreenUtil::ConvertRectToScreen(
800         panel->parent(),
801         panel->GetTargetBounds());
802     gfx::Rect icon_bounds = shelf_->GetScreenBoundsOfItemIconForWindow(panel);
803     if (icon_bounds.IsEmpty() || !panel->layer()->GetTargetVisibility() ||
804         panel == dragged_panel_) {
805       callout_widget->Hide();
806       callout_widget->GetNativeWindow()->layer()->SetOpacity(0);
807       continue;
808     }
809
810     gfx::Rect callout_bounds = callout_widget->GetWindowBoundsInScreen();
811     gfx::Vector2d slide_vector = bounds.origin() - current_bounds.origin();
812     int slide_distance = horizontal ? slide_vector.x() : slide_vector.y();
813     int distance_until_over_panel = 0;
814     if (horizontal) {
815       callout_bounds.set_x(
816           icon_bounds.x() + (icon_bounds.width() - callout_bounds.width()) / 2);
817       distance_until_over_panel = std::max(
818           current_bounds.x() - callout_bounds.x(),
819           callout_bounds.right() - current_bounds.right());
820     } else {
821       callout_bounds.set_y(
822           icon_bounds.y() + (icon_bounds.height() -
823                              callout_bounds.height()) / 2);
824       distance_until_over_panel = std::max(
825           current_bounds.y() - callout_bounds.y(),
826           callout_bounds.bottom() - current_bounds.bottom());
827     }
828     switch (alignment) {
829       case SHELF_ALIGNMENT_BOTTOM:
830         callout_bounds.set_y(bounds.bottom());
831         break;
832       case SHELF_ALIGNMENT_LEFT:
833         callout_bounds.set_x(bounds.x() - callout_bounds.width());
834         break;
835       case SHELF_ALIGNMENT_RIGHT:
836         callout_bounds.set_x(bounds.right());
837         break;
838       case SHELF_ALIGNMENT_TOP:
839         callout_bounds.set_y(bounds.y() - callout_bounds.height());
840         break;
841     }
842     callout_bounds = ScreenUtil::ConvertRectFromScreen(
843         callout_widget->GetNativeWindow()->parent(),
844         callout_bounds);
845
846     SetChildBoundsDirect(callout_widget->GetNativeWindow(), callout_bounds);
847     panel_container_->StackChildAbove(callout_widget->GetNativeWindow(),
848                                       panel);
849     callout_widget->Show();
850
851     ui::Layer* layer = callout_widget->GetNativeWindow()->layer();
852     // If the panel is not over the callout position or has just become visible
853     // then fade in the callout.
854     if ((distance_until_over_panel > 0 || layer->GetTargetOpacity() < 1) &&
855         panel->layer()->GetTargetTransform().IsIdentity()) {
856       if (distance_until_over_panel > 0 &&
857           slide_distance >= distance_until_over_panel) {
858         layer->SetOpacity(0);
859         // If the panel is not yet over the callout, then delay fading in
860         // the callout until after the panel should be over it.
861         int delay = kPanelSlideDurationMilliseconds *
862             distance_until_over_panel / slide_distance;
863         layer->SetOpacity(0);
864         layer->GetAnimator()->StopAnimating();
865         layer->GetAnimator()->SchedulePauseForProperties(
866             base::TimeDelta::FromMilliseconds(delay),
867             ui::LayerAnimationElement::OPACITY);
868       }
869       {
870         ui::ScopedLayerAnimationSettings callout_settings(layer->GetAnimator());
871         callout_settings.SetPreemptionStrategy(
872             ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
873         callout_settings.SetTransitionDuration(
874             base::TimeDelta::FromMilliseconds(
875                 kCalloutFadeDurationMilliseconds));
876         layer->SetOpacity(1);
877       }
878     }
879   }
880 }
881
882 ////////////////////////////////////////////////////////////////////////////////
883 // keyboard::KeyboardControllerObserver implementation:
884
885 void PanelLayoutManager::OnKeyboardBoundsChanging(
886     const gfx::Rect& keyboard_bounds) {
887   // This bounds change will have caused a change to the Shelf which does not
888   // propogate automatically to this class, so manually recalculate bounds.
889   OnWindowResized();
890 }
891
892 }  // namespace internal
893 }  // namespace ash