Home | History | Annotate | Download | only in panels
      1 // Copyright (c) 2011 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/panel_manager.h"
      6 
      7 #include <algorithm>
      8 #include "base/logging.h"
      9 #include "base/scoped_ptr.h"
     10 #include "chrome/browser/ui/browser.h"
     11 #include "chrome/browser/ui/panels/panel.h"
     12 #include "chrome/browser/ui/window_sizer.h"
     13 
     14 namespace {
     15 // Invalid panel index.
     16 const size_t kInvalidPanelIndex = static_cast<size_t>(-1);
     17 
     18 // Minimum width and height of a panel.
     19 const int kPanelMinWidthPixels = 64;
     20 const int kPanelMinHeightPixels = 24;
     21 
     22 // Default width and height of a panel.
     23 const int kPanelDefaultWidthPixels = 240;
     24 const int kPanelDefaultHeightPixels = 290;
     25 
     26 // Maxmium width and height of a panel based on the factor of the working
     27 // area.
     28 const double kPanelMaxWidthFactor = 1.0;
     29 const double kPanelMaxHeightFactor = 0.5;
     30 
     31 // Horizontal spacing between two panels.
     32 const int kPanelsHorizontalSpacing = 4;
     33 
     34 // Single instance of PanelManager.
     35 scoped_ptr<PanelManager> panel_instance;
     36 } // namespace
     37 
     38 // static
     39 PanelManager* PanelManager::GetInstance() {
     40   if (!panel_instance.get()) {
     41     panel_instance.reset(new PanelManager());
     42   }
     43   return panel_instance.get();
     44 }
     45 
     46 PanelManager::PanelManager()
     47     : max_width_(0),
     48       max_height_(0),
     49       min_x_(0),
     50       current_x_(0),
     51       bottom_edge_y_(0),
     52       dragging_panel_index_(kInvalidPanelIndex),
     53       dragging_panel_original_x_(0) {
     54   OnDisplayChanged();
     55 }
     56 
     57 PanelManager::~PanelManager() {
     58   DCHECK(active_panels_.empty());
     59   DCHECK(pending_panels_.empty());
     60   DCHECK(panels_pending_to_remove_.empty());
     61 }
     62 
     63 void PanelManager::OnDisplayChanged() {
     64   scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider(
     65       WindowSizer::CreateDefaultMonitorInfoProvider());
     66   gfx::Rect work_area = info_provider->GetPrimaryMonitorWorkArea();
     67 
     68   min_x_ = work_area.x();
     69   current_x_ = work_area.right();
     70   bottom_edge_y_ = work_area.bottom();
     71   max_width_ = static_cast<int>(work_area.width() * kPanelMaxWidthFactor);
     72   max_height_ = static_cast<int>(work_area.height() * kPanelMaxHeightFactor);
     73 
     74   Rearrange(active_panels_.begin());
     75 }
     76 
     77 Panel* PanelManager::CreatePanel(Browser* browser) {
     78   gfx::Rect bounds = browser->override_bounds();
     79   bool is_within_bounds = ComputeBoundsForNextPanel(&bounds, true);
     80 
     81   Panel* panel = new Panel(browser, bounds);
     82   if (is_within_bounds)
     83     active_panels_.push_back(panel);
     84   else
     85     pending_panels_.push_back(panel);
     86 
     87   return panel;
     88 }
     89 
     90 void PanelManager::ProcessPending() {
     91   while (!pending_panels_.empty()) {
     92     Panel* panel = pending_panels_.front();
     93     gfx::Rect bounds = panel->bounds();
     94     if (ComputeBoundsForNextPanel(&bounds, true)) {
     95       // TODO(jianli): More work to support displaying pending panels.
     96       active_panels_.push_back(panel);
     97       pending_panels_.pop_front();
     98     }
     99   }
    100 }
    101 
    102 void PanelManager::Remove(Panel* panel) {
    103   // If we're in the process of dragging, delay the removal.
    104   if (dragging_panel_index_ != kInvalidPanelIndex) {
    105     panels_pending_to_remove_.push_back(panel);
    106     return;
    107   }
    108 
    109   DoRemove(panel);
    110 }
    111 
    112 void PanelManager::DelayedRemove() {
    113   for (size_t i = 0; i < panels_pending_to_remove_.size(); ++i)
    114     DoRemove(panels_pending_to_remove_[i]);
    115   panels_pending_to_remove_.clear();
    116 }
    117 
    118 void PanelManager::DoRemove(Panel* panel) {
    119   // Checks the active panel list.
    120   ActivePanels::iterator iter =
    121       find(active_panels_.begin(), active_panels_.end(), panel);
    122   if (iter == active_panels_.end()) {
    123     // Checks the pending panel list.
    124     PendingPanels::iterator iter2 =
    125         find(pending_panels_.begin(), pending_panels_.end(), panel);
    126     if (iter2 != pending_panels_.end())
    127       pending_panels_.erase(iter2);
    128     return;
    129   }
    130 
    131   current_x_ = (*iter)->bounds().x() + (*iter)->bounds().width();
    132   Rearrange(active_panels_.erase(iter));
    133 
    134   ProcessPending();
    135 }
    136 
    137 void PanelManager::StartDragging(Panel* panel) {
    138   for (size_t i = 0; i < active_panels_.size(); ++i) {
    139     if (active_panels_[i] == panel) {
    140       dragging_panel_index_ = i;
    141       dragging_panel_bounds_ = panel->bounds();
    142       dragging_panel_original_x_ = dragging_panel_bounds_.x();
    143       break;
    144     }
    145   }
    146 }
    147 
    148 void PanelManager::Drag(int delta_x) {
    149   DCHECK(dragging_panel_index_ != kInvalidPanelIndex);
    150 
    151   if (!delta_x)
    152     return;
    153 
    154   // Moves this panel to the dragging position.
    155   gfx::Rect new_bounds(active_panels_[dragging_panel_index_]->bounds());
    156   new_bounds.set_x(new_bounds.x() + delta_x);
    157   active_panels_[dragging_panel_index_]->SetBounds(new_bounds);
    158 
    159   // Checks and processes other affected panels.
    160   if (delta_x > 0)
    161     DragPositive(delta_x);
    162   else
    163     DragNegative(delta_x);
    164 }
    165 
    166 void PanelManager::DragNegative(int delta_x) {
    167   DCHECK(delta_x < 0);
    168 
    169   Panel* dragging_panel = active_panels_[dragging_panel_index_];
    170 
    171   // This is the left corner of the dragging panel. We use it to check against
    172   // all the panels on its left.
    173   int dragging_panel_x = dragging_panel->bounds().x() + delta_x;
    174 
    175   // This is the right corner which a panel will be moved to.
    176   int right_x_to_move_to =
    177       dragging_panel_bounds_.x() + dragging_panel_bounds_.width();
    178 
    179   // Checks the panels to the left of the dragging panel.
    180   size_t i = dragging_panel_index_;
    181   size_t j = i + 1;
    182   for (; j < active_panels_.size(); ++j, ++i) {
    183     // Current panel will only be affected if the left corner of dragging
    184     // panel goes beyond the middle position of the current panel.
    185     if (dragging_panel_x > active_panels_[j]->bounds().x() +
    186                            active_panels_[j]->bounds().width() / 2)
    187       break;
    188 
    189     // Moves current panel to the new position.
    190     gfx::Rect bounds(active_panels_[j]->bounds());
    191     bounds.set_x(right_x_to_move_to - bounds.width());
    192     right_x_to_move_to -= bounds.width() + kPanelsHorizontalSpacing;
    193     active_panels_[j]->SetBounds(bounds);
    194 
    195     // Adjusts the index of current panel.
    196     active_panels_[i] = active_panels_[j];
    197   }
    198 
    199   // Adjusts the position and index of dragging panel as the result of moving
    200   // other affected panels.
    201   if (j != dragging_panel_index_ + 1) {
    202     j--;
    203     dragging_panel_bounds_.set_x(right_x_to_move_to -
    204                                  dragging_panel_bounds_.width());
    205     active_panels_[j] = dragging_panel;
    206     dragging_panel_index_ = j;
    207   }
    208 }
    209 
    210 void PanelManager::DragPositive(int delta_x) {
    211   DCHECK(delta_x > 0);
    212 
    213   Panel* dragging_panel = active_panels_[dragging_panel_index_];
    214 
    215   // This is the right corner of the dragging panel. We use it to check against
    216   // all the panels on its right.
    217   int dragging_panel_x = dragging_panel->bounds().x() +
    218       dragging_panel->bounds().width() - 1 + delta_x;
    219 
    220   // This is the left corner which a panel will be moved to.
    221   int left_x_to_move_to = dragging_panel_bounds_.x();
    222 
    223   // Checks the panels to the right of the dragging panel.
    224   int i = static_cast<int>(dragging_panel_index_);
    225   int j = i - 1;
    226   for (; j >= 0; --j, --i) {
    227     // Current panel will only be affected if the right corner of dragging
    228     // panel goes beyond the middle position of the current panel.
    229     if (dragging_panel_x < active_panels_[j]->bounds().x() +
    230                            active_panels_[j]->bounds().width() / 2)
    231       break;
    232 
    233     // Moves current panel to the new position.
    234     gfx::Rect bounds(active_panels_[j]->bounds());
    235     bounds.set_x(left_x_to_move_to);
    236     left_x_to_move_to += bounds.width() + kPanelsHorizontalSpacing;
    237     active_panels_[j]->SetBounds(bounds);
    238 
    239     // Adjusts the index of current panel.
    240     active_panels_[i] = active_panels_[j];
    241   }
    242 
    243   // Adjusts the position and index of dragging panel as the result of moving
    244   // other affected panels.
    245   if (j != static_cast<int>(dragging_panel_index_) - 1) {
    246     j++;
    247     dragging_panel_bounds_.set_x(left_x_to_move_to);
    248     active_panels_[j] = dragging_panel;
    249     dragging_panel_index_ = j;
    250   }
    251 }
    252 
    253 void PanelManager::EndDragging(bool cancelled) {
    254   DCHECK(dragging_panel_index_ != kInvalidPanelIndex);
    255 
    256   if (cancelled) {
    257     Drag(dragging_panel_original_x_ -
    258          active_panels_[dragging_panel_index_]->bounds().x());
    259   } else {
    260     active_panels_[dragging_panel_index_]->SetBounds(dragging_panel_bounds_);
    261   }
    262 
    263   dragging_panel_index_ = kInvalidPanelIndex;
    264 
    265   DelayedRemove();
    266 }
    267 
    268 void PanelManager::Rearrange(ActivePanels::iterator iter_to_start) {
    269   if (iter_to_start == active_panels_.end())
    270     return;
    271 
    272   for (ActivePanels::iterator iter = iter_to_start;
    273        iter != active_panels_.end(); ++iter) {
    274     gfx::Rect new_bounds((*iter)->bounds());
    275     ComputeBoundsForNextPanel(&new_bounds, false);
    276     if (new_bounds != (*iter)->bounds())
    277       (*iter)->SetBounds(new_bounds);
    278   }
    279 }
    280 
    281 bool PanelManager::ComputeBoundsForNextPanel(gfx::Rect* bounds,
    282                                              bool allow_size_change) {
    283   int width = bounds->width();
    284   int height = bounds->height();
    285 
    286   // Update the width and/or height to fit into our constraint.
    287   if (allow_size_change) {
    288     if (width == 0 && height == 0) {
    289       width = kPanelDefaultWidthPixels;
    290       height = kPanelDefaultHeightPixels;
    291     }
    292 
    293     if (width < kPanelMinWidthPixels)
    294       width = kPanelMinWidthPixels;
    295     else if (width > max_width_)
    296       width = max_width_;
    297 
    298     if (height < kPanelMinHeightPixels)
    299       height = kPanelMinHeightPixels;
    300     else if (height > max_height_)
    301       height = max_height_;
    302   }
    303 
    304   int x = current_x_ - width;
    305   int y = bottom_edge_y_ - height;
    306 
    307   if (x < min_x_)
    308     return false;
    309 
    310   current_x_ -= width + kPanelsHorizontalSpacing;
    311 
    312   bounds->SetRect(x, y, width, height);
    313   return true;
    314 }
    315 
    316 void PanelManager::MinimizeAll() {
    317   for (ActivePanels::const_iterator iter = active_panels_.begin();
    318        iter != active_panels_.end(); ++iter) {
    319     (*iter)->Minimize();
    320   }
    321 }
    322 
    323 void PanelManager::RestoreAll() {
    324   for (ActivePanels::const_iterator iter = active_panels_.begin();
    325        iter != active_panels_.end(); ++iter) {
    326     (*iter)->Restore();
    327   }
    328 }
    329 
    330 void PanelManager::RemoveAllActive() {
    331   // This should not be called when we're in the process of dragging.
    332   DCHECK(dragging_panel_index_ == kInvalidPanelIndex);
    333 
    334   // Start from the bottom to avoid reshuffling.
    335   for (int i = static_cast<int>(active_panels_.size()) -1; i >= 0; --i)
    336     active_panels_[i]->Close();
    337 
    338   ProcessPending();
    339 }
    340 
    341 bool PanelManager::AreAllMinimized() const {
    342   for (ActivePanels::const_iterator iter = active_panels_.begin();
    343        iter != active_panels_.end(); ++iter) {
    344     if (!(*iter)->minimized())
    345       return false;
    346   }
    347   return true;
    348 }
    349