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