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 "chrome/browser/ui/panels/stacked_panel_collection.h"
      6 
      7 #include <algorithm>
      8 #include "base/auto_reset.h"
      9 #include "base/logging.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/ui/panels/detached_panel_collection.h"
     12 #include "chrome/browser/ui/panels/display_settings_provider.h"
     13 #include "chrome/browser/ui/panels/native_panel_stack_window.h"
     14 #include "chrome/browser/ui/panels/panel.h"
     15 #include "chrome/browser/ui/panels/panel_constants.h"
     16 #include "chrome/browser/ui/panels/panel_manager.h"
     17 #include "extensions/common/extension.h"
     18 
     19 StackedPanelCollection::StackedPanelCollection(PanelManager* panel_manager)
     20     : PanelCollection(PanelCollection::STACKED),
     21       panel_manager_(panel_manager),
     22       primary_stack_window_(NULL),
     23       secondary_stack_window_(NULL),
     24       minimizing_all_(false) {
     25 }
     26 
     27 StackedPanelCollection::~StackedPanelCollection() {
     28   DCHECK(panels_.empty());
     29   DCHECK(most_recently_active_panels_.empty());
     30 }
     31 
     32 void StackedPanelCollection::OnDisplayChanged() {
     33   if (panels_.empty())
     34     return;
     35 
     36   gfx::Rect enclosing_bounds = GetEnclosingBounds();
     37   gfx::Rect work_area = panel_manager_->display_settings_provider()->
     38       GetWorkAreaMatching(enclosing_bounds);
     39 
     40   // If the height of the whole stack is bigger than the height of the new work
     41   // area, try to reduce the stack height by collapsing panels. In rare case,
     42   // all panels are collapsed and there is still not enough space. We simply
     43   // let the stack go beyond the work area limit.
     44   if (enclosing_bounds.height() > work_area.height()) {
     45     int needed_space = enclosing_bounds.height() - work_area.height();
     46     MinimizePanelsForSpace(needed_space);
     47   }
     48 
     49   gfx::Rect top_bounds = top_panel()->GetBounds();
     50   int common_width = top_bounds.width();
     51   if (common_width > work_area.width())
     52     common_width = work_area.width();
     53 
     54   int common_x = top_bounds.x();
     55   if (common_x + common_width > work_area.right())
     56     common_x = work_area.right() - common_width;
     57   if (common_x < work_area.x())
     58     common_x = work_area.x();
     59 
     60   int total_height = bottom_panel()->GetBounds().bottom() - top_bounds.y();
     61   int start_y = top_bounds.y();
     62   if (start_y + total_height > work_area.bottom())
     63     start_y = work_area.bottom() - total_height;
     64   if (start_y < work_area.y())
     65     start_y = work_area.y();
     66 
     67   RefreshLayoutWithTopPanelStartingAt(
     68       gfx::Point(common_x, start_y), common_width);
     69 }
     70 
     71 void StackedPanelCollection::RefreshLayout() {
     72   if (panels_.empty())
     73     return;
     74   gfx::Rect top_bounds = top_panel()->GetBounds();
     75   RefreshLayoutWithTopPanelStartingAt(top_bounds.origin(), top_bounds.width());
     76 }
     77 
     78 void StackedPanelCollection::RefreshLayoutWithTopPanelStartingAt(
     79     const gfx::Point& start_position, int common_width) {
     80   if (panels_.empty())
     81     return;
     82 
     83   // If only one panel is left in the stack, we only need to check if it should
     84   // be moved to |start_y| position.
     85   if (panels_.size() == 1) {
     86     Panel* panel = panels_.front();
     87     gfx::Rect bounds = panel->GetBounds();
     88     if (bounds.origin() != start_position) {
     89       bounds.set_origin(start_position);
     90       panel->SetPanelBounds(bounds);
     91     }
     92     return;
     93   }
     94 
     95   // We do not update bounds for affected panels one by one. Instead, all
     96   // changes are bundled and performed synchronously.
     97   primary_stack_window_->BeginBatchUpdatePanelBounds(true);
     98   if (secondary_stack_window_)
     99     secondary_stack_window_->BeginBatchUpdatePanelBounds(true);
    100 
    101   int y = start_position.y();
    102   int common_x = start_position.x();
    103   for (Panels::const_iterator iter = panels_.begin();
    104        iter != panels_.end(); ++iter) {
    105     Panel* panel = *iter;
    106 
    107     // The visibility of minimize button might need to be updated due to that
    108     // top panel might change when a panel is being added or removed from
    109     // the stack.
    110     panel->UpdateMinimizeRestoreButtonVisibility();
    111 
    112     // Don't update the stacked panel that is in preview mode.
    113     gfx::Rect bounds = panel->GetBounds();
    114     if (panel->in_preview_mode()) {
    115       y += bounds.height();
    116       continue;
    117     }
    118 
    119     // Update the restored size.
    120     gfx::Size full_size = panel->full_size();
    121     full_size.set_width(common_width);
    122     panel->set_full_size(full_size);
    123 
    124     // Recompute the bounds.
    125     bounds.SetRect(
    126         common_x,
    127         y,
    128         common_width,
    129         panel->expansion_state() == Panel::EXPANDED ?
    130             panel->full_size().height() : panel->TitleOnlyHeight());
    131 
    132     GetStackWindowForPanel(panel)->AddPanelBoundsForBatchUpdate(panel, bounds);
    133 
    134     y += bounds.height();
    135   }
    136 
    137   primary_stack_window_->EndBatchUpdatePanelBounds();
    138   if (secondary_stack_window_)
    139     secondary_stack_window_->EndBatchUpdatePanelBounds();
    140 }
    141 
    142 base::string16 StackedPanelCollection::GetTitle() const {
    143   if (panels_.empty())
    144     return base::string16();
    145 
    146   Panel* panel = panels_.front();
    147   const extensions::Extension* extension = panel->GetExtension();
    148   return base::UTF8ToUTF16(extension && !extension->name().empty() ?
    149       extension->name() : panel->app_name());
    150 }
    151 
    152 gfx::Image StackedPanelCollection::GetIcon() const {
    153   if (panels_.empty())
    154     return gfx::Image();
    155   return panels_.front()->app_icon();
    156 }
    157 
    158 void StackedPanelCollection::PanelBoundsBatchUpdateCompleted() {
    159   if (!secondary_stack_window_ || panels_.empty())
    160     return;
    161 
    162   if (top_panel()->in_preview_mode() != bottom_panel()->in_preview_mode() ||
    163       primary_stack_window_->IsAnimatingPanelBounds() ||
    164       secondary_stack_window_->IsAnimatingPanelBounds())
    165     return;
    166 
    167   // Move all panels from secondary stack window to primary stack window.
    168   primary_stack_window_->MergeWith(secondary_stack_window_);
    169   secondary_stack_window_->Close();
    170   secondary_stack_window_ = NULL;
    171 }
    172 
    173 gfx::Rect StackedPanelCollection::GetEnclosingBounds() const {
    174   gfx::Rect enclosing_bounds = top_panel()->GetBounds();
    175   enclosing_bounds.set_height(
    176       bottom_panel()->GetBounds().bottom() - enclosing_bounds.y());
    177   return enclosing_bounds;
    178 }
    179 
    180 int StackedPanelCollection::MinimizePanelsForSpace(int needed_space) {
    181   int available_space = GetCurrentAvailableBottomSpace();
    182 
    183   // Only the most recently active panel might be active.
    184   Panel* active_panel = NULL;
    185   if (!most_recently_active_panels_.empty()) {
    186     Panel* most_recently_active_panel = most_recently_active_panels_.front();
    187     if (most_recently_active_panel->IsActive())
    188       active_panel = most_recently_active_panel;
    189   }
    190 
    191   for (Panels::const_reverse_iterator iter =
    192            most_recently_active_panels_.rbegin();
    193        iter != most_recently_active_panels_.rend() &&
    194            available_space < needed_space;
    195        ++iter) {
    196     Panel* current_panel = *iter;
    197     if (current_panel != active_panel && !IsPanelMinimized(current_panel)) {
    198       available_space +=
    199           current_panel->GetBounds().height() - panel::kTitlebarHeight;
    200       MinimizePanel(current_panel);
    201     }
    202   }
    203   return available_space;
    204 }
    205 
    206 void StackedPanelCollection::AddPanel(Panel* panel,
    207                                       PositioningMask positioning_mask) {
    208   DCHECK_NE(this, panel->collection());
    209   panel->set_collection(this);
    210   Panel* adjacent_panel = NULL;
    211   if (positioning_mask & PanelCollection::TOP_POSITION) {
    212     adjacent_panel = top_panel();
    213     panels_.push_front(panel);
    214   } else {
    215     // To fit the new panel within the working area, collapse unfocused panels
    216     // in the least recent active order until there is enough space.
    217     if (positioning_mask & PanelCollection::COLLAPSE_TO_FIT) {
    218       int needed_space = panel->GetBounds().height();
    219       int available_space = MinimizePanelsForSpace(needed_space);
    220       DCHECK(available_space >= needed_space);
    221     }
    222 
    223     adjacent_panel = bottom_panel();
    224     panels_.push_back(panel);
    225   }
    226 
    227   if (panel->IsActive())
    228     most_recently_active_panels_.push_front(panel);
    229   else
    230     most_recently_active_panels_.push_back(panel);
    231 
    232   if (adjacent_panel)
    233     UpdatePanelCornerStyle(adjacent_panel);
    234 
    235   // The secondary stack window should be used when one of the following occurs:
    236   // 1) Some panels but not all panels are being dragged. This is because
    237   //    those panels being dragged might not be fully aligned with other panels
    238   //    not being dragged.
    239   // 2) The newly added panel is not fully aligned with the existing panel, in
    240   //    terms of both x and width.
    241   NativePanelStackWindow* stack_window;
    242   if (top_panel()->in_preview_mode() == bottom_panel()->in_preview_mode() &&
    243       top_panel()->GetBounds().x() == bottom_panel()->GetBounds().x() &&
    244       top_panel()->GetBounds().width() == bottom_panel()->GetBounds().width()) {
    245     if (!primary_stack_window_)
    246       primary_stack_window_ = NativePanelStackWindow::Create(this);
    247     stack_window = primary_stack_window_;
    248   } else {
    249     if (!secondary_stack_window_)
    250       secondary_stack_window_ = NativePanelStackWindow::Create(this);
    251     stack_window = secondary_stack_window_;
    252   }
    253   stack_window->AddPanel(panel);
    254 
    255   if ((positioning_mask & NO_LAYOUT_REFRESH) == 0)
    256     RefreshLayout();
    257 }
    258 
    259 void StackedPanelCollection::RemovePanel(Panel* panel, RemovalReason reason) {
    260   bool is_top = panel == top_panel();
    261   bool is_bottom = panel == bottom_panel();
    262 
    263   // If the top panel is being closed, all panels below it should move up. To
    264   // do this, the top y position of top panel needs to be tracked first.
    265   bool top_panel_closed = false;
    266   gfx::Point top_origin;
    267   int top_width = 0;
    268   if (reason == PanelCollection::PANEL_CLOSED && is_top) {
    269     top_panel_closed = true;
    270     top_origin = panel->GetBounds().origin();
    271     top_width = panel->GetBounds().width();
    272   }
    273 
    274   panel->set_collection(NULL);
    275   panels_.remove(panel);
    276   most_recently_active_panels_.remove(panel);
    277 
    278   if (is_top) {
    279     Panel* new_top_panel = top_panel();
    280     if (new_top_panel)
    281       UpdatePanelCornerStyle(new_top_panel);
    282   } else if (is_bottom) {
    283     Panel* new_bottom_panel = bottom_panel();
    284     if (new_bottom_panel)
    285       UpdatePanelCornerStyle(new_bottom_panel);
    286   }
    287 
    288   // If an active panel is being closed, try to focus the next recently active
    289   // panel in the stack that is not minimized.
    290   if (reason == PanelCollection::PANEL_CLOSED &&
    291       panel->IsActive() &&
    292       !most_recently_active_panels_.empty()) {
    293     for (Panels::const_iterator iter = most_recently_active_panels_.begin();
    294        iter != most_recently_active_panels_.end(); ++iter) {
    295       Panel* other_panel = *iter;
    296       if (!IsPanelMinimized(other_panel)) {
    297         other_panel->Activate();
    298         break;
    299       }
    300     }
    301   }
    302 
    303   // If the top panel is closed, move up all other panels to stay at the same
    304   // y position as the top panel being closed.
    305   if (top_panel_closed)
    306     RefreshLayoutWithTopPanelStartingAt(top_origin, top_width);
    307   else if (reason == PanelCollection::PANEL_CLOSED)
    308     RefreshLayout();
    309 
    310   // Remove the panel from the corresponding stack window.
    311   GetStackWindowForPanel(panel)->RemovePanel(panel);
    312 
    313   // Close the secondary stack window if no panel is is shown inside it.
    314   // Note that we do not need to do this for primary stack window since the
    315   // whole stack will be gone when only one panel is left.
    316   if (secondary_stack_window_ && secondary_stack_window_->IsEmpty()) {
    317     secondary_stack_window_->Close();
    318     secondary_stack_window_ = NULL;
    319   }
    320 }
    321 
    322 void StackedPanelCollection::CloseAll() {
    323   // Make a copy as closing panels can modify the iterator.
    324   Panels panels_copy = panels_;
    325 
    326   for (Panels::const_iterator iter = panels_copy.begin();
    327        iter != panels_copy.end(); ++iter)
    328     (*iter)->Close();
    329 
    330   if (primary_stack_window_) {
    331     primary_stack_window_->Close();
    332     primary_stack_window_ = NULL;
    333   }
    334   if (secondary_stack_window_) {
    335     secondary_stack_window_->Close();
    336     secondary_stack_window_ = NULL;
    337   }
    338 }
    339 
    340 void StackedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) {
    341   if ((panel->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0)
    342     primary_stack_window_->DrawSystemAttention(panel->IsDrawingAttention());
    343 }
    344 
    345 void StackedPanelCollection::OnPanelTitlebarClicked(
    346     Panel* panel, panel::ClickModifier modifier) {
    347   bool expanded = panel->expansion_state() == Panel::EXPANDED;
    348   if (modifier == panel::APPLY_TO_ALL) {
    349     if (expanded)
    350       MinimizeAll();
    351     else
    352       RestoreAll(panel);
    353   } else {
    354     if (expanded)
    355       MinimizePanel(panel);
    356     else
    357       RestorePanel(panel);
    358   }
    359 }
    360 
    361 void StackedPanelCollection::ResizePanelWindow(
    362     Panel* panel,
    363     const gfx::Size& preferred_window_size) {
    364 }
    365 
    366 void StackedPanelCollection::ActivatePanel(Panel* panel) {
    367   // Make sure the panel is expanded when activated so the user input
    368   // does not go into a collapsed window.
    369   if (panel->IsMinimized())
    370     panel->SetExpansionState(Panel::EXPANDED);
    371 }
    372 
    373 void StackedPanelCollection::MinimizePanel(Panel* panel) {
    374   panel->SetExpansionState(Panel::TITLE_ONLY);
    375 }
    376 
    377 void StackedPanelCollection::RestorePanel(Panel* panel) {
    378   // Ensure that the panel could fit within the work area after it is expanded.
    379   // First, try to collapse the unfocused panel in the least recent active
    380   // order in order to get enough space.
    381   int needed_space = panel->full_size().height() - panel->TitleOnlyHeight();
    382   int available_space = MinimizePanelsForSpace(needed_space);
    383 
    384   // If there is still not enough space, try to move up the stack.
    385   int space_beyond_available = needed_space - available_space;
    386   if (space_beyond_available > 0) {
    387     int top_available_space = GetCurrentAvailableTopSpace();
    388     int move_delta = (space_beyond_available > top_available_space) ?
    389         top_available_space : space_beyond_available;
    390     for (Panels::const_iterator iter = panels_.begin();
    391          iter != panels_.end(); iter++) {
    392       Panel* current_panel = *iter;
    393       gfx::Rect bounds = current_panel->GetBounds();
    394       bounds.set_y(bounds.y() - move_delta);
    395       current_panel->SetPanelBounds(bounds);
    396     }
    397     available_space += move_delta;
    398   }
    399 
    400   // If there is still not enough space, shrink the restored height to make it
    401   // fit at the last resort. Note that the restored height cannot be shrunk less
    402   // than the minimum panel height. If this is the case, we will just let it
    403   // expand beyond the screen boundary.
    404   space_beyond_available = needed_space - available_space;
    405   if (space_beyond_available > 0) {
    406     gfx::Size full_size = panel->full_size();
    407     int reduced_height = full_size.height() - space_beyond_available;
    408     if (reduced_height < panel::kPanelMinHeight)
    409       reduced_height = panel::kPanelMinHeight;
    410     full_size.set_height(reduced_height);
    411     panel->set_full_size(full_size);
    412   }
    413 
    414   panel->SetExpansionState(Panel::EXPANDED);
    415 }
    416 
    417 void StackedPanelCollection::MinimizeAll() {
    418   // Set minimizing_all_ to prevent deactivation of each panel when it
    419   // is minimized. See comments in OnPanelExpansionStateChanged.
    420   base::AutoReset<bool> pin(&minimizing_all_, true);
    421   Panel* minimized_active_panel = NULL;
    422   for (Panels::const_iterator iter = panels_.begin();
    423        iter != panels_.end(); ++iter) {
    424     if ((*iter)->IsActive())
    425       minimized_active_panel = *iter;
    426     MinimizePanel(*iter);
    427   }
    428 
    429   // When a single panel is minimized, it is deactivated to ensure that
    430   // a minimized panel does not have focus. However, when minimizing all,
    431   // the deactivation is only done once after all panels are minimized,
    432   // rather than per minimized panel, both for efficiency and to avoid
    433   // temporary activations of random not-yet-minimized panels.
    434   if (minimized_active_panel) {
    435     minimized_active_panel->Deactivate();
    436     // Layout will be refreshed in response to (de)activation notification.
    437   }
    438 }
    439 
    440 void StackedPanelCollection::RestoreAll(Panel* panel_clicked) {
    441   // Expand the panel being clicked first. This is to make sure at least one
    442   // panel that is clicked by the user will be expanded.
    443   RestorePanel(panel_clicked);
    444 
    445   // Try to expand all other panels starting from the most recently active
    446   // panel.
    447   for (Panels::const_iterator iter = most_recently_active_panels_.begin();
    448        iter != most_recently_active_panels_.end(); ++iter) {
    449     // If the stack already extends to both top and bottom of the work area,
    450     // stop now since we cannot fit any more expanded panels.
    451     if (GetCurrentAvailableTopSpace() == 0 &&
    452         GetCurrentAvailableBottomSpace() == 0) {
    453       break;
    454     }
    455 
    456     Panel* panel = *iter;
    457     if (panel != panel_clicked)
    458       RestorePanel(panel);
    459   }
    460 }
    461 
    462 void StackedPanelCollection::OnMinimizeButtonClicked(
    463     Panel* panel, panel::ClickModifier modifier) {
    464   // The minimize button is only present in the top panel.
    465   DCHECK_EQ(top_panel(), panel);
    466 
    467   primary_stack_window_->Minimize();
    468 }
    469 
    470 void StackedPanelCollection::OnRestoreButtonClicked(
    471     Panel* panel, panel::ClickModifier modifier) {
    472   NOTREACHED();
    473 }
    474 
    475 bool StackedPanelCollection::CanShowMinimizeButton(const Panel* panel) const {
    476   // Only the top panel in the stack shows the minimize button.
    477   return PanelManager::CanUseSystemMinimize() && panel == top_panel();
    478 }
    479 
    480 bool StackedPanelCollection::CanShowRestoreButton(const Panel* panel) const {
    481   return false;
    482 }
    483 
    484 bool StackedPanelCollection::IsPanelMinimized(const Panel* panel) const {
    485   return panel->expansion_state() != Panel::EXPANDED;
    486 }
    487 
    488 bool StackedPanelCollection::UsesAlwaysOnTopPanels() const {
    489   return false;
    490 }
    491 
    492 void StackedPanelCollection::SavePanelPlacement(Panel* panel) {
    493   DCHECK(!saved_panel_placement_.panel);
    494   saved_panel_placement_.panel = panel;
    495 
    496   if (top_panel() != panel)
    497     saved_panel_placement_.top_panel = top_panel();
    498   else
    499     saved_panel_placement_.position = panel->GetBounds().origin();
    500 
    501   saved_panel_placement_.top_panel = top_panel() != panel ? top_panel() : NULL;
    502 }
    503 
    504 void StackedPanelCollection::RestorePanelToSavedPlacement() {
    505   DCHECK(saved_panel_placement_.panel);
    506 
    507   if (saved_panel_placement_.top_panel) {
    508     // Restore the top panel if it has been moved out of the stack. This could
    509     // happen when there're 2 panels in the stack and the bottom panel is being
    510     // dragged out of the stack and thus cause both panels become detached.
    511     if (saved_panel_placement_.top_panel->stack() != this) {
    512       DCHECK_EQ(PanelCollection::DETACHED,
    513                 saved_panel_placement_.top_panel->collection()->type());
    514       panel_manager_->MovePanelToCollection(
    515           saved_panel_placement_.top_panel,
    516           this,
    517           static_cast<PanelCollection::PositioningMask>(
    518               PanelCollection::TOP_POSITION |
    519               PanelCollection::NO_LAYOUT_REFRESH));
    520     }
    521     RefreshLayout();
    522   } else {
    523     // Restore the position when the top panel is being dragged.
    524     DCHECK_EQ(top_panel(), saved_panel_placement_.panel);
    525     RefreshLayoutWithTopPanelStartingAt(saved_panel_placement_.position,
    526                                         top_panel()->GetBounds().width());
    527   }
    528 
    529   DiscardSavedPanelPlacement();
    530 }
    531 
    532 void StackedPanelCollection::DiscardSavedPanelPlacement() {
    533   DCHECK(saved_panel_placement_.panel);
    534   saved_panel_placement_.panel = NULL;
    535   saved_panel_placement_.top_panel = NULL;
    536 }
    537 
    538 panel::Resizability StackedPanelCollection::GetPanelResizability(
    539     const Panel* panel) const {
    540   // The panel in the stack can be resized by the following rules:
    541   // * If collapsed, it can only be resized by its left or right edge.
    542   // * Otherwise, it can be resized by its left or right edge plus:
    543   //   % top edge and corners, if it is at the top;
    544   //   % bottom edge, if it is not at the bottom.
    545   //   % bottom edge and corners, if it is at the bottom.
    546   panel::Resizability resizability = static_cast<panel::Resizability>(
    547       panel::RESIZABLE_LEFT | panel::RESIZABLE_RIGHT);
    548   if (panel->IsMinimized())
    549     return resizability;
    550   if (panel == top_panel()) {
    551     resizability = static_cast<panel::Resizability>(resizability |
    552         panel::RESIZABLE_TOP | panel::RESIZABLE_TOP_LEFT |
    553         panel::RESIZABLE_TOP_RIGHT);
    554   }
    555   if (panel == bottom_panel()) {
    556     resizability = static_cast<panel::Resizability>(resizability |
    557         panel::RESIZABLE_BOTTOM | panel::RESIZABLE_BOTTOM_LEFT |
    558         panel::RESIZABLE_BOTTOM_RIGHT);
    559   } else {
    560     resizability = static_cast<panel::Resizability>(resizability |
    561         panel::RESIZABLE_BOTTOM);
    562   }
    563   return resizability;
    564 }
    565 
    566 void StackedPanelCollection::OnPanelResizedByMouse(
    567     Panel* resized_panel, const gfx::Rect& new_bounds) {
    568   resized_panel->set_full_size(new_bounds.size());
    569 
    570   DCHECK(!secondary_stack_window_);
    571   primary_stack_window_->BeginBatchUpdatePanelBounds(false);
    572 
    573   // The delta x and width can be computed from the difference between
    574   // the panel being resized and any other panel.
    575   Panel* other_panel = resized_panel == top_panel() ? bottom_panel()
    576                                                     : top_panel();
    577   gfx::Rect other_bounds = other_panel->GetBounds();
    578   int delta_x = new_bounds.x() - other_bounds.x();
    579   int delta_width = new_bounds.width() - other_bounds.width();
    580 
    581   gfx::Rect previous_bounds;
    582   bool resized_panel_found = false;
    583   bool panel_below_resized_panel_updated = false;
    584   for (Panels::const_iterator iter = panels_.begin();
    585        iter != panels_.end(); iter++) {
    586     Panel* panel = *iter;
    587     if (panel == resized_panel) {
    588       // |new_bounds| should be used since the panel bounds have not been
    589       // updated yet.
    590       previous_bounds = new_bounds;
    591       resized_panel_found = true;
    592       primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, new_bounds);
    593       continue;
    594     }
    595 
    596     gfx::Rect bounds = panel->GetBounds();
    597     bounds.set_x(bounds.x() + delta_x);
    598     bounds.set_width(bounds.width() + delta_width);
    599 
    600     // If the panel below the panel being resized is expanded, update its
    601     // height to offset the height change of the panel being resized.
    602     // For example, the stack has P1 and P2 (from top to bottom). P1's height
    603     // is 100 and P2's height is 120. If P1's bottom increases by 10, P2's
    604     // height needs to shrink by 10.
    605     if (resized_panel_found) {
    606       if (!panel_below_resized_panel_updated && !panel->IsMinimized()) {
    607         int old_bottom = bounds.bottom();
    608         bounds.set_y(previous_bounds.bottom());
    609         bounds.set_height(old_bottom - bounds.y());
    610       } else {
    611         bounds.set_y(previous_bounds.bottom());
    612       }
    613       panel_below_resized_panel_updated = true;
    614     }
    615 
    616     if (!panel->IsMinimized())
    617       panel->set_full_size(bounds.size());
    618 
    619     primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, bounds);
    620     previous_bounds = bounds;
    621   }
    622 
    623   primary_stack_window_->EndBatchUpdatePanelBounds();
    624 }
    625 
    626 bool StackedPanelCollection::HasPanel(Panel* panel) const {
    627   return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
    628 }
    629 
    630 void StackedPanelCollection::UpdatePanelOnCollectionChange(Panel* panel) {
    631   panel->set_attention_mode(
    632       static_cast<Panel::AttentionMode>(Panel::USE_PANEL_ATTENTION |
    633                                         Panel::USE_SYSTEM_ATTENTION));
    634   panel->ShowShadow(false);
    635   panel->UpdateMinimizeRestoreButtonVisibility();
    636   UpdatePanelCornerStyle(panel);
    637 }
    638 
    639 void StackedPanelCollection::OnPanelExpansionStateChanged(Panel* panel) {
    640   DCHECK_NE(Panel::MINIMIZED, panel->expansion_state());
    641 
    642   // Ensure minimized panel does not get the focus. If minimizing all,
    643   // the active panel will be deactivated once when all panels are minimized
    644   // rather than per minimized panel.
    645   if (panel->expansion_state() != Panel::EXPANDED && !minimizing_all_ &&
    646       panel->IsActive()) {
    647     panel->Deactivate();
    648   }
    649 
    650   // The bounds change per expansion state will be done in RefreshLayout.
    651   RefreshLayout();
    652 }
    653 
    654 void StackedPanelCollection::OnPanelActiveStateChanged(Panel* panel) {
    655   if (!panel->IsActive())
    656     return;
    657 
    658   // Move the panel to the front if not yet.
    659   Panels::iterator iter = std::find(most_recently_active_panels_.begin(),
    660       most_recently_active_panels_.end(), panel);
    661   DCHECK(iter != most_recently_active_panels_.end());
    662   if (iter != most_recently_active_panels_.begin()) {
    663     most_recently_active_panels_.erase(iter);
    664     most_recently_active_panels_.push_front(panel);
    665   }
    666 
    667   GetStackWindowForPanel(panel)->OnPanelActivated(panel);
    668 }
    669 
    670 gfx::Rect StackedPanelCollection::GetInitialPanelBounds(
    671       const gfx::Rect& requested_bounds) const {
    672   DCHECK(!panels_.empty());
    673   gfx::Rect bottom_panel_bounds = bottom_panel()->GetBounds();
    674   return gfx::Rect(bottom_panel_bounds.x(),
    675                    bottom_panel_bounds.bottom(),
    676                    bottom_panel_bounds.width(),
    677                    requested_bounds.height());
    678 }
    679 
    680 Panel* StackedPanelCollection::GetPanelAbove(Panel* panel) const {
    681   DCHECK(panel);
    682 
    683   if (panels_.size() < 2)
    684     return NULL;
    685   Panels::const_iterator iter = panels_.begin();
    686   Panel* above_panel = *iter;
    687   for (; iter != panels_.end(); ++iter) {
    688     if (*iter == panel)
    689       return above_panel;
    690     above_panel = *iter;
    691   }
    692   return NULL;
    693 }
    694 
    695 Panel* StackedPanelCollection::GetPanelBelow(Panel* panel) const {
    696   DCHECK(panel);
    697 
    698   if (panels_.size() < 2)
    699     return NULL;
    700   Panels::const_iterator iter =
    701       std::find(panels_.begin(), panels_.end(), panel);
    702   if (iter == panels_.end())
    703     return NULL;
    704   ++iter;
    705   return iter == panels_.end() ? NULL : *iter;
    706 }
    707 
    708 void StackedPanelCollection::MoveAllDraggingPanelsInstantly(
    709     const gfx::Vector2d& delta_origin) {
    710   for (Panels::const_iterator iter = panels_.begin();
    711        iter != panels_.end(); iter++) {
    712     Panel* panel = *iter;
    713     if (panel->in_preview_mode()) {
    714       GetStackWindowForPanel(panel)->MovePanelsBy(delta_origin);
    715       return;
    716     }
    717   }
    718 }
    719 
    720 bool StackedPanelCollection::IsMinimized() const {
    721   return primary_stack_window_->IsMinimized();
    722 }
    723 
    724 bool StackedPanelCollection::IsAnimatingPanelBounds(Panel* panel) const {
    725   return GetStackWindowForPanel(panel)->IsAnimatingPanelBounds();
    726 }
    727 
    728 void StackedPanelCollection::UpdatePanelCornerStyle(Panel* panel) {
    729   panel::CornerStyle corner_style;
    730   bool at_top = panel == top_panel();
    731   bool at_bottom = panel == bottom_panel();
    732   if (at_top && at_bottom)
    733     corner_style = panel::ALL_ROUNDED;
    734   else if (at_top)
    735     corner_style = panel::TOP_ROUNDED;
    736   else if (at_bottom)
    737     corner_style = panel::BOTTOM_ROUNDED;
    738   else
    739     corner_style = panel::NOT_ROUNDED;
    740   panel->SetWindowCornerStyle(corner_style);
    741 }
    742 
    743 gfx::Rect StackedPanelCollection::GetWorkArea() const {
    744   if (panels_.empty())
    745     return panel_manager_->display_settings_provider()->GetPrimaryWorkArea();
    746   return panel_manager_->display_settings_provider()->GetWorkAreaMatching(
    747       GetEnclosingBounds());
    748 }
    749 
    750 int StackedPanelCollection::GetCurrentAvailableTopSpace() const {
    751   gfx::Rect work_area = GetWorkArea();
    752   if (panels_.empty())
    753     return work_area.height();
    754 
    755   int available_space = top_panel()->GetBounds().y() - work_area.y();
    756   if (available_space < 0)
    757     available_space = 0;
    758   return available_space;
    759 }
    760 
    761 int StackedPanelCollection::GetCurrentAvailableBottomSpace() const {
    762   gfx::Rect work_area = GetWorkArea();
    763   if (panels_.empty())
    764     return work_area.height();
    765 
    766   int available_space = work_area.bottom() -
    767       bottom_panel()->GetBounds().bottom();
    768   if (available_space < 0)
    769     available_space = 0;
    770   return available_space;
    771 }
    772 
    773 int StackedPanelCollection::GetMaximiumAvailableBottomSpace() const {
    774   gfx::Rect work_area = GetWorkArea();
    775   if (panels_.empty())
    776     return work_area.height();
    777 
    778   int bottom = top_panel()->GetBounds().y();
    779   for (Panels::const_iterator iter = panels_.begin();
    780        iter != panels_.end(); iter++) {
    781     Panel* panel = *iter;
    782     // Only the most recently active panel might be active.
    783     if (iter == panels_.begin() && panel->IsActive())
    784       bottom += panel->GetBounds().height();
    785     else
    786       bottom += panel::kTitlebarHeight;
    787   }
    788   int available_space = work_area.bottom() - bottom;
    789   if (available_space < 0)
    790     available_space = 0;
    791   return available_space;
    792 }
    793 
    794 NativePanelStackWindow* StackedPanelCollection::GetStackWindowForPanel(
    795     Panel* panel) const {
    796   return secondary_stack_window_ && secondary_stack_window_->HasPanel(panel) ?
    797       secondary_stack_window_ : primary_stack_window_;
    798 }
    799