Upstream version 6.35.121.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/focus_client.h"
27 #include "ui/aura/client/window_tree_client.h"
28 #include "ui/aura/window.h"
29 #include "ui/aura/window_event_dispatcher.h"
30 #include "ui/aura/window_tracker.h"
31 #include "ui/compositor/scoped_layer_animation_settings.h"
32 #include "ui/gfx/canvas.h"
33 #include "ui/gfx/rect.h"
34 #include "ui/gfx/vector2d.h"
35 #include "ui/views/background.h"
36 #include "ui/views/widget/widget.h"
37 #include "ui/wm/public/activation_client.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     params.accept_events = false;
239     // Why do we need this and can_activate = false?
240     set_focus_on_creation(false);
241     Init(params);
242     DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow());
243     views::View* content_view = new views::View;
244     background_ = new CalloutWidgetBackground;
245     content_view->set_background(background_);
246     SetContentsView(content_view);
247     GetNativeWindow()->layer()->SetOpacity(0);
248   }
249
250   // Weak pointer owned by this widget's content view.
251   CalloutWidgetBackground* background_;
252
253   DISALLOW_COPY_AND_ASSIGN(PanelCalloutWidget);
254 };
255
256 ////////////////////////////////////////////////////////////////////////////////
257 // PanelLayoutManager public implementation:
258 PanelLayoutManager::PanelLayoutManager(aura::Window* panel_container)
259     : panel_container_(panel_container),
260       in_add_window_(false),
261       in_layout_(false),
262       dragged_panel_(NULL),
263       shelf_(NULL),
264       shelf_layout_manager_(NULL),
265       last_active_panel_(NULL),
266       weak_factory_(this) {
267   DCHECK(panel_container);
268   aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
269       AddObserver(this);
270   Shell::GetInstance()->display_controller()->AddObserver(this);
271   Shell::GetInstance()->AddShellObserver(this);
272 }
273
274 PanelLayoutManager::~PanelLayoutManager() {
275   Shutdown();
276 }
277
278 void PanelLayoutManager::Shutdown() {
279   if (shelf_layout_manager_)
280     shelf_layout_manager_->RemoveObserver(this);
281   shelf_layout_manager_ = NULL;
282   for (PanelList::iterator iter = panel_windows_.begin();
283        iter != panel_windows_.end(); ++iter) {
284     delete iter->callout_widget;
285   }
286   panel_windows_.clear();
287   if (shelf_)
288     shelf_->RemoveIconObserver(this);
289   shelf_ = NULL;
290   aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
291       RemoveObserver(this);
292   Shell::GetInstance()->display_controller()->RemoveObserver(this);
293   Shell::GetInstance()->RemoveShellObserver(this);
294 }
295
296 void PanelLayoutManager::StartDragging(aura::Window* panel) {
297   DCHECK(!dragged_panel_);
298   dragged_panel_ = panel;
299   Relayout();
300 }
301
302 void PanelLayoutManager::FinishDragging() {
303   dragged_panel_ = NULL;
304   Relayout();
305 }
306
307 void PanelLayoutManager::SetShelf(Shelf* shelf) {
308   DCHECK(!shelf_);
309   DCHECK(!shelf_layout_manager_);
310   shelf_ = shelf;
311   shelf_->AddIconObserver(this);
312   if (shelf_->shelf_widget()) {
313     shelf_layout_manager_ = ash::internal::ShelfLayoutManager::ForShelf(
314         shelf_->shelf_widget()->GetNativeWindow());
315     WillChangeVisibilityState(shelf_layout_manager_->visibility_state());
316     shelf_layout_manager_->AddObserver(this);
317   }
318 }
319
320 void PanelLayoutManager::ToggleMinimize(aura::Window* panel) {
321   DCHECK(panel->parent() == panel_container_);
322   wm::WindowState* window_state = wm::GetWindowState(panel);
323   if (window_state->IsMinimized())
324     window_state->Restore();
325   else
326     window_state->Minimize();
327 }
328
329 views::Widget* PanelLayoutManager::GetCalloutWidgetForPanel(
330     aura::Window* panel) {
331   DCHECK(panel->parent() == panel_container_);
332   PanelList::iterator found =
333       std::find(panel_windows_.begin(), panel_windows_.end(), panel);
334   DCHECK(found != panel_windows_.end());
335   return found->callout_widget;
336 }
337
338 ////////////////////////////////////////////////////////////////////////////////
339 // PanelLayoutManager, aura::LayoutManager implementation:
340 void PanelLayoutManager::OnWindowResized() {
341   Relayout();
342 }
343
344 void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
345   if (child->type() == ui::wm::WINDOW_TYPE_POPUP)
346     return;
347   if (in_add_window_)
348     return;
349   base::AutoReset<bool> auto_reset_in_add_window(&in_add_window_, true);
350   if (!wm::GetWindowState(child)->panel_attached()) {
351     // This should only happen when a window is added to panel container as a
352     // result of bounds change from within the application during a drag.
353     // If so we have already stopped the drag and should reparent the panel
354     // back to appropriate container and ignore it.
355     // TODO(varkha): Updating bounds during a drag can cause problems and a more
356     // general solution is needed. See http://crbug.com/251813 .
357     aura::Window* old_parent = child->parent();
358     aura::client::ParentWindowWithContext(
359         child, child, child->GetRootWindow()->GetBoundsInScreen());
360     wm::ReparentTransientChildrenOfChild(child, old_parent, child->parent());
361     DCHECK(child->parent()->id() != kShellWindowId_PanelContainer);
362     return;
363   }
364   PanelInfo panel_info;
365   panel_info.window = child;
366   panel_info.callout_widget = new PanelCalloutWidget(panel_container_);
367   if (child != dragged_panel_) {
368     // Set the panel to 0 opacity until it has been positioned to prevent it
369     // from flashing briefly at position (0, 0).
370     child->layer()->SetOpacity(0);
371     panel_info.slide_in = true;
372   }
373   panel_windows_.push_back(panel_info);
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   wm::GetWindowState(child)->RemoveObserver(this);
391
392   if (dragged_panel_ == child)
393     dragged_panel_ = NULL;
394
395   if (last_active_panel_ == child)
396     last_active_panel_ = NULL;
397
398   Relayout();
399 }
400
401 void PanelLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
402                                                         bool visible) {
403   if (visible)
404     wm::GetWindowState(child)->Restore();
405   Relayout();
406 }
407
408 void PanelLayoutManager::SetChildBounds(aura::Window* child,
409                                         const gfx::Rect& requested_bounds) {
410   gfx::Rect bounds(requested_bounds);
411   const gfx::Rect& max_bounds = panel_container_->GetRootWindow()->bounds();
412   const int max_width = max_bounds.width() * kMaxWidthFactor;
413   const int max_height = max_bounds.height() * kMaxHeightFactor;
414   if (bounds.width() > max_width)
415     bounds.set_width(max_width);
416   if (bounds.height() > max_height)
417     bounds.set_height(max_height);
418
419   // Reposition dragged panel in the panel order.
420   if (dragged_panel_ == child) {
421     PanelList::iterator dragged_panel_iter =
422       std::find(panel_windows_.begin(), panel_windows_.end(), dragged_panel_);
423     DCHECK(dragged_panel_iter != panel_windows_.end());
424     PanelList::iterator new_position;
425     for (new_position = panel_windows_.begin();
426          new_position != panel_windows_.end();
427          ++new_position) {
428       const gfx::Rect& bounds = (*new_position).window->bounds();
429       if (bounds.x() + bounds.width()/2 <= requested_bounds.x()) break;
430     }
431     if (new_position != dragged_panel_iter) {
432       PanelInfo dragged_panel_info = *dragged_panel_iter;
433       panel_windows_.erase(dragged_panel_iter);
434       panel_windows_.insert(new_position, dragged_panel_info);
435     }
436   }
437
438   SetChildBoundsDirect(child, bounds);
439   Relayout();
440 }
441
442 ////////////////////////////////////////////////////////////////////////////////
443 // PanelLayoutManager, ShelfIconObserver implementation:
444
445 void PanelLayoutManager::OnShelfIconPositionsChanged() {
446   // TODO: As this is called for every animation step now. Relayout needs to be
447   // updated to use current icon position instead of use the ideal bounds so
448   // that the panels slide with their icons instead of jumping.
449   Relayout();
450 }
451
452 ////////////////////////////////////////////////////////////////////////////////
453 // PanelLayoutManager, ash::ShellObserver implementation:
454
455 void PanelLayoutManager::OnShelfAlignmentChanged(aura::Window* root_window) {
456   if (panel_container_->GetRootWindow() == root_window)
457     Relayout();
458 }
459
460 /////////////////////////////////////////////////////////////////////////////
461 // PanelLayoutManager, WindowObserver implementation:
462
463 void PanelLayoutManager::OnPostWindowStateTypeChange(
464     wm::WindowState* window_state,
465     wm::WindowStateType old_type) {
466   // If the shelf is currently hidden then windows will not actually be shown
467   // but the set to restore when the shelf becomes visible is updated.
468   if (restore_windows_on_shelf_visible_) {
469     if (window_state->IsMinimized()) {
470       MinimizePanel(window_state->window());
471       restore_windows_on_shelf_visible_->Remove(window_state->window());
472     } else {
473       restore_windows_on_shelf_visible_->Add(window_state->window());
474     }
475     return;
476   }
477
478   if (window_state->IsMinimized())
479     MinimizePanel(window_state->window());
480   else
481     RestorePanel(window_state->window());
482 }
483
484 ////////////////////////////////////////////////////////////////////////////////
485 // PanelLayoutManager, aura::client::ActivationChangeObserver implementation:
486
487 void PanelLayoutManager::OnWindowActivated(aura::Window* gained_active,
488                                            aura::Window* lost_active) {
489   // Ignore if the panel that is not managed by this was activated.
490   if (gained_active && gained_active->type() == ui::wm::WINDOW_TYPE_PANEL &&
491       gained_active->parent() == panel_container_) {
492     UpdateStacking(gained_active);
493     UpdateCallouts();
494   }
495 }
496
497 ////////////////////////////////////////////////////////////////////////////////
498 // PanelLayoutManager, DisplayController::Observer implementation:
499
500 void PanelLayoutManager::OnDisplayConfigurationChanged() {
501   Relayout();
502 }
503
504 ////////////////////////////////////////////////////////////////////////////////
505 // PanelLayoutManager, ShelfLayoutManagerObserver implementation:
506
507 void PanelLayoutManager::WillChangeVisibilityState(
508     ShelfVisibilityState new_state) {
509   // On entering / leaving full screen mode the shelf visibility state is
510   // changed to / from SHELF_HIDDEN. In this state, panel windows should hide
511   // to allow the full-screen application to use the full screen.
512   bool shelf_hidden = new_state == ash::SHELF_HIDDEN;
513   if (!shelf_hidden) {
514     if (restore_windows_on_shelf_visible_) {
515       scoped_ptr<aura::WindowTracker> restore_windows(
516           restore_windows_on_shelf_visible_.Pass());
517       for (aura::WindowTracker::Windows::const_iterator iter =
518            restore_windows->windows().begin(); iter !=
519            restore_windows->windows().end(); ++iter) {
520         RestorePanel(*iter);
521       }
522     }
523     return;
524   }
525
526   if (restore_windows_on_shelf_visible_)
527     return;
528   scoped_ptr<aura::WindowTracker> minimized_windows(new aura::WindowTracker);
529   for (PanelList::iterator iter = panel_windows_.begin();
530        iter != panel_windows_.end(); ++iter) {
531     if (iter->window->IsVisible()) {
532       minimized_windows->Add(iter->window);
533       wm::GetWindowState(iter->window)->Minimize();
534     }
535   }
536   restore_windows_on_shelf_visible_ = minimized_windows.Pass();
537 }
538
539 ////////////////////////////////////////////////////////////////////////////////
540 // PanelLayoutManager private implementation:
541
542 void PanelLayoutManager::MinimizePanel(aura::Window* panel) {
543   ::wm::SetWindowVisibilityAnimationType(
544       panel, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
545   ui::Layer* layer = panel->layer();
546   ui::ScopedLayerAnimationSettings panel_slide_settings(layer->GetAnimator());
547   panel_slide_settings.SetPreemptionStrategy(
548       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
549   panel_slide_settings.SetTransitionDuration(
550       base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds));
551   gfx::Rect bounds(panel->bounds());
552   bounds.Offset(GetSlideInAnimationOffset(
553       shelf_->shelf_widget()->GetAlignment()));
554   SetChildBoundsDirect(panel, bounds);
555   panel->Hide();
556   PanelList::iterator found =
557       std::find(panel_windows_.begin(), panel_windows_.end(), panel);
558   if (found != panel_windows_.end()) {
559     layer->SetOpacity(0);
560     // The next time the window is visible it should slide into place.
561     found->slide_in = true;
562   }
563   if (wm::IsActiveWindow(panel))
564     wm::DeactivateWindow(panel);
565   Relayout();
566 }
567
568 void PanelLayoutManager::RestorePanel(aura::Window* panel) {
569   panel->Show();
570   Relayout();
571 }
572
573 void PanelLayoutManager::Relayout() {
574   if (!shelf_ || !shelf_->shelf_widget())
575     return;
576
577   if (in_layout_)
578     return;
579   base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
580
581   ShelfAlignment alignment = shelf_->shelf_widget()->GetAlignment();
582   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
583                     alignment == SHELF_ALIGNMENT_BOTTOM;
584   gfx::Rect shelf_bounds = ash::ScreenUtil::ConvertRectFromScreen(
585       panel_container_, shelf_->shelf_widget()->GetWindowBoundsInScreen());
586   int panel_start_bounds = kPanelIdealSpacing;
587   int panel_end_bounds = horizontal ?
588       panel_container_->bounds().width() - kPanelIdealSpacing :
589       panel_container_->bounds().height() - kPanelIdealSpacing;
590   aura::Window* active_panel = NULL;
591   std::vector<VisiblePanelPositionInfo> visible_panels;
592   for (PanelList::iterator iter = panel_windows_.begin();
593        iter != panel_windows_.end(); ++iter) {
594     aura::Window* panel = iter->window;
595     iter->callout_widget->SetAlignment(alignment);
596
597     // Consider the dragged panel as part of the layout as long as it is
598     // touching the shelf.
599     if (!panel->IsVisible() ||
600         (panel == dragged_panel_ &&
601          !BoundsAdjacent(panel->bounds(), shelf_bounds))) {
602       continue;
603     }
604
605     // If the shelf is currently hidden (full-screen mode), minimize panel until
606     // full-screen mode is exited. When a panel is dragged from another display
607     // the shelf state does not update before the panel is added so we exclude
608     // the dragged panel.
609     if (panel != dragged_panel_ && restore_windows_on_shelf_visible_) {
610       wm::GetWindowState(panel)->Minimize();
611       restore_windows_on_shelf_visible_->Add(panel);
612       continue;
613     }
614
615     gfx::Rect icon_bounds = shelf_->GetScreenBoundsOfItemIconForWindow(panel);
616
617     // If both the icon width and height are 0 then there is no icon in the
618     // shelf. If the shelf is hidden, one of the height or width will be
619     // 0 but the position in the shelf and major dimension is still reported
620     // correctly and the panel can be aligned above where the hidden icon is.
621     if (icon_bounds.width() == 0 && icon_bounds.height() == 0)
622       continue;
623
624     if (panel->HasFocus() ||
625         panel->Contains(
626             aura::client::GetFocusClient(panel)->GetFocusedWindow())) {
627       DCHECK(!active_panel);
628       active_panel = panel;
629     }
630     icon_bounds = ScreenUtil::ConvertRectFromScreen(panel_container_,
631                                                     icon_bounds);
632     gfx::Point icon_origin = icon_bounds.origin();
633     VisiblePanelPositionInfo position_info;
634     int icon_start = horizontal ? icon_origin.x() : icon_origin.y();
635     int icon_end = icon_start + (horizontal ? icon_bounds.width() :
636                                  icon_bounds.height());
637     position_info.major_length = horizontal ?
638         panel->bounds().width() : panel->bounds().height();
639     position_info.min_major = std::max(
640         panel_start_bounds + position_info.major_length / 2,
641         icon_end - position_info.major_length / 2);
642     position_info.max_major = std::min(
643         icon_start + position_info.major_length / 2,
644         panel_end_bounds - position_info.major_length / 2);
645     position_info.major_pos = (icon_start + icon_end) / 2;
646     position_info.window = panel;
647     position_info.slide_in = iter->slide_in;
648     iter->slide_in = false;
649     visible_panels.push_back(position_info);
650   }
651
652   // Sort panels by their X positions and fan out groups of overlapping panels.
653   // The fan out method may result in new overlapping panels however given that
654   // the panels start at least a full panel width apart this overlap will
655   // never completely obscure a panel.
656   // TODO(flackr): Rearrange panels if new overlaps are introduced.
657   std::sort(visible_panels.begin(), visible_panels.end(), CompareWindowMajor);
658   size_t first_overlapping_panel = 0;
659   for (size_t i = 1; i < visible_panels.size(); ++i) {
660     if (visible_panels[i - 1].major_pos +
661         visible_panels[i - 1].major_length / 2 < visible_panels[i].major_pos -
662         visible_panels[i].major_length / 2) {
663       FanOutPanels(visible_panels.begin() + first_overlapping_panel,
664                    visible_panels.begin() + i);
665       first_overlapping_panel = i;
666     }
667   }
668   FanOutPanels(visible_panels.begin() + first_overlapping_panel,
669                visible_panels.end());
670
671   for (size_t i = 0; i < visible_panels.size(); ++i) {
672     if (visible_panels[i].window == dragged_panel_)
673       continue;
674     bool slide_in = visible_panels[i].slide_in;
675     gfx::Rect bounds = visible_panels[i].window->GetTargetBounds();
676     switch (alignment) {
677       case SHELF_ALIGNMENT_BOTTOM:
678         bounds.set_y(shelf_bounds.y() - bounds.height());
679         break;
680       case SHELF_ALIGNMENT_LEFT:
681         bounds.set_x(shelf_bounds.right());
682         break;
683       case SHELF_ALIGNMENT_RIGHT:
684         bounds.set_x(shelf_bounds.x() - bounds.width());
685         break;
686       case SHELF_ALIGNMENT_TOP:
687         bounds.set_y(shelf_bounds.bottom());
688         break;
689     }
690     bool on_shelf = visible_panels[i].window->GetTargetBounds() == bounds;
691
692     if (horizontal) {
693       bounds.set_x(visible_panels[i].major_pos -
694                    visible_panels[i].major_length / 2);
695     } else {
696       bounds.set_y(visible_panels[i].major_pos -
697                    visible_panels[i].major_length / 2);
698     }
699
700     ui::Layer* layer = visible_panels[i].window->layer();
701     if (slide_in) {
702       // New windows shift up from the shelf into position.
703       gfx::Rect initial_bounds(bounds);
704       initial_bounds.Offset(GetSlideInAnimationOffset(alignment));
705       SetChildBoundsDirect(visible_panels[i].window, initial_bounds);
706       // Set on shelf so that the panel animates into its target position.
707       on_shelf = true;
708     }
709
710     if (on_shelf) {
711       ui::ScopedLayerAnimationSettings panel_slide_settings(
712           layer->GetAnimator());
713       panel_slide_settings.SetPreemptionStrategy(
714           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
715       panel_slide_settings.SetTransitionDuration(
716           base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds));
717       SetChildBoundsDirect(visible_panels[i].window, bounds);
718       if (slide_in)
719         layer->SetOpacity(1);
720     } else {
721       // If the shelf moved don't animate, move immediately to the new
722       // target location.
723       SetChildBoundsDirect(visible_panels[i].window, bounds);
724     }
725   }
726
727   UpdateStacking(active_panel);
728   UpdateCallouts();
729 }
730
731 void PanelLayoutManager::UpdateStacking(aura::Window* active_panel) {
732   if (!active_panel) {
733     if (!last_active_panel_)
734       return;
735     active_panel = last_active_panel_;
736   }
737
738   ShelfAlignment alignment = shelf_->alignment();
739   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
740                     alignment == SHELF_ALIGNMENT_BOTTOM;
741
742   // We want to to stack the panels like a deck of cards:
743   // ,--,--,--,-------.--.--.
744   // |  |  |  |       |  |  |
745   // |  |  |  |       |  |  |
746   //
747   // We use the middle of each panel to figure out how to stack the panels. This
748   // allows us to update the stacking when a panel is being dragged around by
749   // the titlebar--even though it doesn't update the shelf icon positions, we
750   // still want the visual effect.
751   std::map<int, aura::Window*> window_ordering;
752   for (PanelList::const_iterator it = panel_windows_.begin();
753        it != panel_windows_.end(); ++it) {
754     gfx::Rect bounds = it->window->bounds();
755     window_ordering.insert(std::make_pair(horizontal ?
756                                               bounds.x() + bounds.width() / 2 :
757                                               bounds.y() + bounds.height() / 2,
758                                           it->window));
759   }
760
761   aura::Window* previous_panel = NULL;
762   for (std::map<int, aura::Window*>::const_iterator it =
763        window_ordering.begin();
764        it != window_ordering.end() && it->second != active_panel; ++it) {
765     if (previous_panel)
766       panel_container_->StackChildAbove(it->second, previous_panel);
767     previous_panel = it->second;
768   }
769
770   previous_panel = NULL;
771   for (std::map<int, aura::Window*>::const_reverse_iterator it =
772        window_ordering.rbegin();
773        it != window_ordering.rend() && it->second != active_panel; ++it) {
774     if (previous_panel)
775       panel_container_->StackChildAbove(it->second, previous_panel);
776     previous_panel = it->second;
777   }
778
779   panel_container_->StackChildAtTop(active_panel);
780   if (dragged_panel_ && dragged_panel_->parent() == panel_container_)
781     panel_container_->StackChildAtTop(dragged_panel_);
782   last_active_panel_ = active_panel;
783 }
784
785 void PanelLayoutManager::UpdateCallouts() {
786   ShelfAlignment alignment = shelf_->alignment();
787   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
788                     alignment == SHELF_ALIGNMENT_BOTTOM;
789
790   for (PanelList::iterator iter = panel_windows_.begin();
791        iter != panel_windows_.end(); ++iter) {
792     aura::Window* panel = iter->window;
793     views::Widget* callout_widget = iter->callout_widget;
794
795     gfx::Rect current_bounds = panel->GetBoundsInScreen();
796     gfx::Rect bounds = ScreenUtil::ConvertRectToScreen(
797         panel->parent(),
798         panel->GetTargetBounds());
799     gfx::Rect icon_bounds = shelf_->GetScreenBoundsOfItemIconForWindow(panel);
800     if (icon_bounds.IsEmpty() || !panel->layer()->GetTargetVisibility() ||
801         panel == dragged_panel_) {
802       callout_widget->Hide();
803       callout_widget->GetNativeWindow()->layer()->SetOpacity(0);
804       continue;
805     }
806
807     gfx::Rect callout_bounds = callout_widget->GetWindowBoundsInScreen();
808     gfx::Vector2d slide_vector = bounds.origin() - current_bounds.origin();
809     int slide_distance = horizontal ? slide_vector.x() : slide_vector.y();
810     int distance_until_over_panel = 0;
811     if (horizontal) {
812       callout_bounds.set_x(
813           icon_bounds.x() + (icon_bounds.width() - callout_bounds.width()) / 2);
814       distance_until_over_panel = std::max(
815           current_bounds.x() - callout_bounds.x(),
816           callout_bounds.right() - current_bounds.right());
817     } else {
818       callout_bounds.set_y(
819           icon_bounds.y() + (icon_bounds.height() -
820                              callout_bounds.height()) / 2);
821       distance_until_over_panel = std::max(
822           current_bounds.y() - callout_bounds.y(),
823           callout_bounds.bottom() - current_bounds.bottom());
824     }
825     switch (alignment) {
826       case SHELF_ALIGNMENT_BOTTOM:
827         callout_bounds.set_y(bounds.bottom());
828         break;
829       case SHELF_ALIGNMENT_LEFT:
830         callout_bounds.set_x(bounds.x() - callout_bounds.width());
831         break;
832       case SHELF_ALIGNMENT_RIGHT:
833         callout_bounds.set_x(bounds.right());
834         break;
835       case SHELF_ALIGNMENT_TOP:
836         callout_bounds.set_y(bounds.y() - callout_bounds.height());
837         break;
838     }
839     callout_bounds = ScreenUtil::ConvertRectFromScreen(
840         callout_widget->GetNativeWindow()->parent(),
841         callout_bounds);
842
843     SetChildBoundsDirect(callout_widget->GetNativeWindow(), callout_bounds);
844     panel_container_->StackChildAbove(callout_widget->GetNativeWindow(),
845                                       panel);
846
847     ui::Layer* layer = callout_widget->GetNativeWindow()->layer();
848     // If the panel is not over the callout position or has just become visible
849     // then fade in the callout.
850     if ((distance_until_over_panel > 0 || layer->GetTargetOpacity() < 1)) {
851       if (distance_until_over_panel > 0 &&
852           slide_distance >= distance_until_over_panel) {
853         // If the panel is not yet over the callout, then delay fading in
854         // the callout until after the panel should be over it.
855         int delay = kPanelSlideDurationMilliseconds *
856             distance_until_over_panel / slide_distance;
857         layer->SetOpacity(0);
858         layer->GetAnimator()->StopAnimating();
859         layer->GetAnimator()->SchedulePauseForProperties(
860             base::TimeDelta::FromMilliseconds(delay),
861             ui::LayerAnimationElement::OPACITY);
862       }
863       ui::ScopedLayerAnimationSettings callout_settings(layer->GetAnimator());
864       callout_settings.SetPreemptionStrategy(
865           ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
866       callout_settings.SetTransitionDuration(
867           base::TimeDelta::FromMilliseconds(
868               kCalloutFadeDurationMilliseconds));
869       layer->SetOpacity(1);
870     }
871
872     // Show after changing the opacity animation. This way we don't have a
873     // state where the widget is visible but the opacity is 0.
874     callout_widget->Show();
875   }
876 }
877
878 ////////////////////////////////////////////////////////////////////////////////
879 // keyboard::KeyboardControllerObserver implementation:
880
881 void PanelLayoutManager::OnKeyboardBoundsChanging(
882     const gfx::Rect& keyboard_bounds) {
883   // This bounds change will have caused a change to the Shelf which does not
884   // propogate automatically to this class, so manually recalculate bounds.
885   OnWindowResized();
886 }
887
888 }  // namespace internal
889 }  // namespace ash