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/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 launcher. 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 = ScreenAsh::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       launcher_(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 (launcher_)
    287     launcher_->RemoveIconObserver(this);
    288   launcher_ = 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::SetLauncher(ash::Launcher* launcher) {
    307   DCHECK(!launcher_);
    308   DCHECK(!shelf_layout_manager_);
    309   launcher_ = launcher;
    310   launcher_->AddIconObserver(this);
    311   if (launcher_->shelf_widget()) {
    312     shelf_layout_manager_ = ash::internal::ShelfLayoutManager::ForLauncher(
    313         launcher_->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() == aura::client::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() == aura::client::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 &&
    496       gained_active->type() == aura::client::WINDOW_TYPE_PANEL &&
    497       gained_active->parent() == panel_container_) {
    498     UpdateStacking(gained_active);
    499     UpdateCallouts();
    500   }
    501 }
    502 
    503 ////////////////////////////////////////////////////////////////////////////////
    504 // PanelLayoutManager, DisplayController::Observer implementation:
    505 
    506 void PanelLayoutManager::OnDisplayConfigurationChanged() {
    507   Relayout();
    508 }
    509 
    510 ////////////////////////////////////////////////////////////////////////////////
    511 // PanelLayoutManager, ShelfLayoutManagerObserver implementation:
    512 
    513 void PanelLayoutManager::WillChangeVisibilityState(
    514     ShelfVisibilityState new_state) {
    515   // On entering / leaving full screen mode the shelf visibility state is
    516   // changed to / from SHELF_HIDDEN. In this state, panel windows should hide
    517   // to allow the full-screen application to use the full screen.
    518   bool shelf_hidden = new_state == ash::SHELF_HIDDEN;
    519   if (!shelf_hidden) {
    520     if (restore_windows_on_shelf_visible_) {
    521       scoped_ptr<aura::WindowTracker> restore_windows(
    522           restore_windows_on_shelf_visible_.Pass());
    523       for (aura::WindowTracker::Windows::const_iterator iter =
    524            restore_windows->windows().begin(); iter !=
    525            restore_windows->windows().end(); ++iter) {
    526         RestorePanel(*iter);
    527       }
    528     }
    529     return;
    530   }
    531 
    532   if (restore_windows_on_shelf_visible_)
    533     return;
    534   scoped_ptr<aura::WindowTracker> minimized_windows(new aura::WindowTracker);
    535   for (PanelList::iterator iter = panel_windows_.begin();
    536        iter != panel_windows_.end(); ++iter) {
    537     if (iter->window->IsVisible()) {
    538       minimized_windows->Add(iter->window);
    539       wm::GetWindowState(iter->window)->Minimize();
    540     }
    541   }
    542   restore_windows_on_shelf_visible_ = minimized_windows.Pass();
    543 }
    544 
    545 ////////////////////////////////////////////////////////////////////////////////
    546 // PanelLayoutManager private implementation:
    547 
    548 void PanelLayoutManager::MinimizePanel(aura::Window* panel) {
    549   views::corewm::SetWindowVisibilityAnimationType(
    550       panel, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
    551   ui::Layer* layer = panel->layer();
    552   ui::ScopedLayerAnimationSettings panel_slide_settings(layer->GetAnimator());
    553   panel_slide_settings.SetPreemptionStrategy(
    554       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    555   panel_slide_settings.SetTransitionDuration(
    556       base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds));
    557   gfx::Rect bounds(panel->bounds());
    558   bounds.Offset(GetSlideInAnimationOffset(
    559       launcher_->shelf_widget()->GetAlignment()));
    560   SetChildBoundsDirect(panel, bounds);
    561   panel->Hide();
    562   PanelList::iterator found =
    563       std::find(panel_windows_.begin(), panel_windows_.end(), panel);
    564   if (found != panel_windows_.end()) {
    565     layer->SetOpacity(0);
    566     // The next time the window is visible it should slide into place.
    567     found->slide_in = true;
    568   }
    569   if (wm::IsActiveWindow(panel))
    570     wm::DeactivateWindow(panel);
    571   Relayout();
    572 }
    573 
    574 void PanelLayoutManager::RestorePanel(aura::Window* panel) {
    575   panel->Show();
    576   Relayout();
    577 }
    578 
    579 void PanelLayoutManager::Relayout() {
    580   if (!launcher_ || !launcher_->shelf_widget())
    581     return;
    582 
    583   if (in_layout_)
    584     return;
    585   base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
    586 
    587   ShelfAlignment alignment = launcher_->shelf_widget()->GetAlignment();
    588   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
    589                     alignment == SHELF_ALIGNMENT_BOTTOM;
    590   gfx::Rect launcher_bounds = ash::ScreenAsh::ConvertRectFromScreen(
    591       panel_container_, launcher_->shelf_widget()->GetWindowBoundsInScreen());
    592   int panel_start_bounds = kPanelIdealSpacing;
    593   int panel_end_bounds = horizontal ?
    594       panel_container_->bounds().width() - kPanelIdealSpacing :
    595       panel_container_->bounds().height() - kPanelIdealSpacing;
    596   aura::Window* active_panel = NULL;
    597   std::vector<VisiblePanelPositionInfo> visible_panels;
    598   for (PanelList::iterator iter = panel_windows_.begin();
    599        iter != panel_windows_.end(); ++iter) {
    600     aura::Window* panel = iter->window;
    601     iter->callout_widget->SetAlignment(alignment);
    602 
    603     // Consider the dragged panel as part of the layout as long as it is
    604     // touching the launcher.
    605     if (!panel->IsVisible() ||
    606         (panel == dragged_panel_ &&
    607          !BoundsAdjacent(panel->bounds(), launcher_bounds))) {
    608       continue;
    609     }
    610 
    611     // If the shelf is currently hidden (full-screen mode), minimize panel until
    612     // full-screen mode is exited.
    613     if (restore_windows_on_shelf_visible_) {
    614       wm::GetWindowState(panel)->Minimize();
    615       restore_windows_on_shelf_visible_->Add(panel);
    616       continue;
    617     }
    618 
    619     gfx::Rect icon_bounds =
    620         launcher_->GetScreenBoundsOfItemIconForWindow(panel);
    621 
    622     // If both the icon width and height are 0 then there is no icon in the
    623     // launcher. If the launcher is hidden, one of the height or width will be
    624     // 0 but the position in the launcher and major dimension is still reported
    625     // correctly and the panel can be aligned above where the hidden icon is.
    626     if (icon_bounds.width() == 0 && icon_bounds.height() == 0)
    627       continue;
    628 
    629     if (panel->HasFocus() ||
    630         panel->Contains(
    631             aura::client::GetFocusClient(panel)->GetFocusedWindow())) {
    632       DCHECK(!active_panel);
    633       active_panel = panel;
    634     }
    635     icon_bounds = ScreenAsh::ConvertRectFromScreen(panel_container_,
    636                                                    icon_bounds);
    637     gfx::Point icon_origin = icon_bounds.origin();
    638     VisiblePanelPositionInfo position_info;
    639     int icon_start = horizontal ? icon_origin.x() : icon_origin.y();
    640     int icon_end = icon_start + (horizontal ? icon_bounds.width() :
    641                                  icon_bounds.height());
    642     position_info.major_length = horizontal ?
    643         panel->bounds().width() : panel->bounds().height();
    644     position_info.min_major = std::max(
    645         panel_start_bounds + position_info.major_length / 2,
    646         icon_end - position_info.major_length / 2);
    647     position_info.max_major = std::min(
    648         icon_start + position_info.major_length / 2,
    649         panel_end_bounds - position_info.major_length / 2);
    650     position_info.major_pos = (icon_start + icon_end) / 2;
    651     position_info.window = panel;
    652     position_info.slide_in = iter->slide_in;
    653     iter->slide_in = false;
    654     visible_panels.push_back(position_info);
    655   }
    656 
    657   // Sort panels by their X positions and fan out groups of overlapping panels.
    658   // The fan out method may result in new overlapping panels however given that
    659   // the panels start at least a full panel width apart this overlap will
    660   // never completely obscure a panel.
    661   // TODO(flackr): Rearrange panels if new overlaps are introduced.
    662   std::sort(visible_panels.begin(), visible_panels.end(), CompareWindowMajor);
    663   size_t first_overlapping_panel = 0;
    664   for (size_t i = 1; i < visible_panels.size(); ++i) {
    665     if (visible_panels[i - 1].major_pos +
    666         visible_panels[i - 1].major_length / 2 < visible_panels[i].major_pos -
    667         visible_panels[i].major_length / 2) {
    668       FanOutPanels(visible_panels.begin() + first_overlapping_panel,
    669                    visible_panels.begin() + i);
    670       first_overlapping_panel = i;
    671     }
    672   }
    673   FanOutPanels(visible_panels.begin() + first_overlapping_panel,
    674                visible_panels.end());
    675 
    676   for (size_t i = 0; i < visible_panels.size(); ++i) {
    677     if (visible_panels[i].window == dragged_panel_)
    678       continue;
    679     bool slide_in = visible_panels[i].slide_in;
    680     gfx::Rect bounds = visible_panels[i].window->GetTargetBounds();
    681     switch (alignment) {
    682       case SHELF_ALIGNMENT_BOTTOM:
    683         bounds.set_y(launcher_bounds.y() - bounds.height());
    684         break;
    685       case SHELF_ALIGNMENT_LEFT:
    686         bounds.set_x(launcher_bounds.right());
    687         break;
    688       case SHELF_ALIGNMENT_RIGHT:
    689         bounds.set_x(launcher_bounds.x() - bounds.width());
    690         break;
    691       case SHELF_ALIGNMENT_TOP:
    692         bounds.set_y(launcher_bounds.bottom());
    693         break;
    694     }
    695     bool on_launcher = visible_panels[i].window->GetTargetBounds() == bounds;
    696 
    697     if (horizontal) {
    698       bounds.set_x(visible_panels[i].major_pos -
    699                    visible_panels[i].major_length / 2);
    700     } else {
    701       bounds.set_y(visible_panels[i].major_pos -
    702                    visible_panels[i].major_length / 2);
    703     }
    704 
    705     ui::Layer* layer = visible_panels[i].window->layer();
    706     if (slide_in) {
    707       // New windows shift up from the launcher  into position.
    708       gfx::Rect initial_bounds(bounds);
    709       initial_bounds.Offset(GetSlideInAnimationOffset(alignment));
    710       SetChildBoundsDirect(visible_panels[i].window, initial_bounds);
    711       // Set on launcher so that the panel animates into its target position.
    712       on_launcher = true;
    713     }
    714 
    715     if (on_launcher) {
    716       ui::ScopedLayerAnimationSettings panel_slide_settings(
    717           layer->GetAnimator());
    718       panel_slide_settings.SetPreemptionStrategy(
    719           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    720       panel_slide_settings.SetTransitionDuration(
    721           base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds));
    722       SetChildBoundsDirect(visible_panels[i].window, bounds);
    723       if (slide_in)
    724         layer->SetOpacity(1);
    725     } else {
    726       // If the launcher moved don't animate, move immediately to the new
    727       // target location.
    728       SetChildBoundsDirect(visible_panels[i].window, bounds);
    729     }
    730   }
    731 
    732   UpdateStacking(active_panel);
    733   UpdateCallouts();
    734 }
    735 
    736 void PanelLayoutManager::UpdateStacking(aura::Window* active_panel) {
    737   if (!active_panel) {
    738     if (!last_active_panel_)
    739       return;
    740     active_panel = last_active_panel_;
    741   }
    742 
    743   ShelfAlignment alignment = launcher_->alignment();
    744   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
    745                     alignment == SHELF_ALIGNMENT_BOTTOM;
    746 
    747   // We want to to stack the panels like a deck of cards:
    748   // ,--,--,--,-------.--.--.
    749   // |  |  |  |       |  |  |
    750   // |  |  |  |       |  |  |
    751   //
    752   // We use the middle of each panel to figure out how to stack the panels. This
    753   // allows us to update the stacking when a panel is being dragged around by
    754   // the titlebar--even though it doesn't update the launcher icon positions, we
    755   // still want the visual effect.
    756   std::map<int, aura::Window*> window_ordering;
    757   for (PanelList::const_iterator it = panel_windows_.begin();
    758        it != panel_windows_.end(); ++it) {
    759     gfx::Rect bounds = it->window->bounds();
    760     window_ordering.insert(std::make_pair(horizontal ?
    761                                               bounds.x() + bounds.width() / 2 :
    762                                               bounds.y() + bounds.height() / 2,
    763                                           it->window));
    764   }
    765 
    766   aura::Window* previous_panel = NULL;
    767   for (std::map<int, aura::Window*>::const_iterator it =
    768        window_ordering.begin();
    769        it != window_ordering.end() && it->second != active_panel; ++it) {
    770     if (previous_panel)
    771       panel_container_->StackChildAbove(it->second, previous_panel);
    772     previous_panel = it->second;
    773   }
    774 
    775   previous_panel = NULL;
    776   for (std::map<int, aura::Window*>::const_reverse_iterator it =
    777        window_ordering.rbegin();
    778        it != window_ordering.rend() && it->second != active_panel; ++it) {
    779     if (previous_panel)
    780       panel_container_->StackChildAbove(it->second, previous_panel);
    781     previous_panel = it->second;
    782   }
    783 
    784   panel_container_->StackChildAtTop(active_panel);
    785   if (dragged_panel_ && dragged_panel_->parent() == panel_container_)
    786     panel_container_->StackChildAtTop(dragged_panel_);
    787   last_active_panel_ = active_panel;
    788 }
    789 
    790 void PanelLayoutManager::UpdateCallouts() {
    791   ShelfAlignment alignment = launcher_->alignment();
    792   bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
    793                     alignment == SHELF_ALIGNMENT_BOTTOM;
    794 
    795   for (PanelList::iterator iter = panel_windows_.begin();
    796        iter != panel_windows_.end(); ++iter) {
    797     aura::Window* panel = iter->window;
    798     views::Widget* callout_widget = iter->callout_widget;
    799 
    800     gfx::Rect current_bounds = panel->GetBoundsInScreen();
    801     gfx::Rect bounds = ScreenAsh::ConvertRectToScreen(panel->parent(),
    802                                                       panel->GetTargetBounds());
    803     gfx::Rect icon_bounds =
    804         launcher_->GetScreenBoundsOfItemIconForWindow(panel);
    805     if (icon_bounds.IsEmpty() || !panel->layer()->GetTargetVisibility() ||
    806         panel == dragged_panel_) {
    807       callout_widget->Hide();
    808       callout_widget->GetNativeWindow()->layer()->SetOpacity(0);
    809       continue;
    810     }
    811 
    812     gfx::Rect callout_bounds = callout_widget->GetWindowBoundsInScreen();
    813     gfx::Vector2d slide_vector = bounds.origin() - current_bounds.origin();
    814     int slide_distance = horizontal ? slide_vector.x() : slide_vector.y();
    815     int distance_until_over_panel = 0;
    816     if (horizontal) {
    817       callout_bounds.set_x(
    818           icon_bounds.x() + (icon_bounds.width() - callout_bounds.width()) / 2);
    819       distance_until_over_panel = std::max(
    820           current_bounds.x() - callout_bounds.x(),
    821           callout_bounds.right() - current_bounds.right());
    822     } else {
    823       callout_bounds.set_y(
    824           icon_bounds.y() + (icon_bounds.height() -
    825                              callout_bounds.height()) / 2);
    826       distance_until_over_panel = std::max(
    827           current_bounds.y() - callout_bounds.y(),
    828           callout_bounds.bottom() - current_bounds.bottom());
    829     }
    830     switch (alignment) {
    831       case SHELF_ALIGNMENT_BOTTOM:
    832         callout_bounds.set_y(bounds.bottom());
    833         break;
    834       case SHELF_ALIGNMENT_LEFT:
    835         callout_bounds.set_x(bounds.x() - callout_bounds.width());
    836         break;
    837       case SHELF_ALIGNMENT_RIGHT:
    838         callout_bounds.set_x(bounds.right());
    839         break;
    840       case SHELF_ALIGNMENT_TOP:
    841         callout_bounds.set_y(bounds.y() - callout_bounds.height());
    842         break;
    843     }
    844     callout_bounds = ScreenAsh::ConvertRectFromScreen(
    845         callout_widget->GetNativeWindow()->parent(),
    846         callout_bounds);
    847 
    848     SetChildBoundsDirect(callout_widget->GetNativeWindow(), callout_bounds);
    849     panel_container_->StackChildAbove(callout_widget->GetNativeWindow(),
    850                                       panel);
    851     callout_widget->Show();
    852 
    853     ui::Layer* layer = callout_widget->GetNativeWindow()->layer();
    854     // If the panel is not over the callout position or has just become visible
    855     // then fade in the callout.
    856     if ((distance_until_over_panel > 0 || layer->GetTargetOpacity() < 1) &&
    857         panel->layer()->GetTargetTransform().IsIdentity()) {
    858       if (distance_until_over_panel > 0 &&
    859           slide_distance >= distance_until_over_panel) {
    860         layer->SetOpacity(0);
    861         // If the panel is not yet over the callout, then delay fading in
    862         // the callout until after the panel should be over it.
    863         int delay = kPanelSlideDurationMilliseconds *
    864             distance_until_over_panel / slide_distance;
    865         layer->SetOpacity(0);
    866         layer->GetAnimator()->StopAnimating();
    867         layer->GetAnimator()->SchedulePauseForProperties(
    868             base::TimeDelta::FromMilliseconds(delay),
    869             ui::LayerAnimationElement::OPACITY);
    870       }
    871       {
    872         ui::ScopedLayerAnimationSettings callout_settings(layer->GetAnimator());
    873         callout_settings.SetPreemptionStrategy(
    874             ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
    875         callout_settings.SetTransitionDuration(
    876             base::TimeDelta::FromMilliseconds(
    877                 kCalloutFadeDurationMilliseconds));
    878         layer->SetOpacity(1);
    879       }
    880     }
    881   }
    882 }
    883 
    884 ////////////////////////////////////////////////////////////////////////////////
    885 // keyboard::KeyboardControllerObserver implementation:
    886 
    887 void PanelLayoutManager::OnKeyboardBoundsChanging(
    888     const gfx::Rect& keyboard_bounds) {
    889   // This bounds change will have caused a change to the Shelf which does not
    890   // propogate automatically to this class, so manually recalculate bounds.
    891   OnWindowResized();
    892 }
    893 
    894 }  // namespace internal
    895 }  // namespace ash
    896