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/detached_panel_collection.h"
      6 
      7 #include <algorithm>
      8 #include "base/logging.h"
      9 #include "chrome/browser/ui/panels/display_settings_provider.h"
     10 #include "chrome/browser/ui/panels/panel_drag_controller.h"
     11 #include "chrome/browser/ui/panels/panel_manager.h"
     12 
     13 namespace {
     14 // How much horizontal and vertical offset there is between newly opened
     15 // detached panels.
     16 const int kPanelTilePixels = 10;
     17 
     18 // When the stacking mode is enabled, the detached panel will be positioned
     19 // near the top of the working area such that the subsequent panel could be
     20 // stacked to the bottom of the detached panel. This value is experimental
     21 // and subjective.
     22 const int kDetachedPanelStartingYPositionOnStackingEnabled = 20;
     23 }  // namespace
     24 
     25 DetachedPanelCollection::DetachedPanelCollection(PanelManager* panel_manager)
     26     : PanelCollection(PanelCollection::DETACHED),
     27       panel_manager_(panel_manager) {
     28 }
     29 
     30 DetachedPanelCollection::~DetachedPanelCollection() {
     31   DCHECK(panels_.empty());
     32 }
     33 
     34 void DetachedPanelCollection::OnDisplayChanged() {
     35   DisplaySettingsProvider* display_settings_provider =
     36       panel_manager_->display_settings_provider();
     37 
     38   for (Panels::const_iterator iter = panels_.begin();
     39        iter != panels_.end(); ++iter) {
     40     Panel* panel = *iter;
     41     gfx::Rect work_area =
     42         display_settings_provider->GetWorkAreaMatching(panel->GetBounds());
     43 
     44     // Update size if needed.
     45     panel->LimitSizeToWorkArea(work_area);
     46 
     47     // Update bounds to make sure the panel falls completely within the work
     48     // area. Note that the origin of the work area might also change.
     49     gfx::Rect bounds = panel->GetBounds();
     50     if (panel->full_size() != bounds.size()) {
     51       bounds.set_size(panel->full_size());
     52       if (bounds.right() > work_area.right())
     53         bounds.set_x(work_area.right() - bounds.width());
     54       if (bounds.bottom() > work_area.bottom())
     55         bounds.set_y(work_area.bottom() - bounds.height());
     56     }
     57     if (bounds.x() < work_area.x())
     58       bounds.set_x(work_area.x());
     59     if (bounds.y() < work_area.y())
     60       bounds.set_y(work_area.y());
     61     panel->SetPanelBoundsInstantly(bounds);
     62   }
     63 }
     64 
     65 void DetachedPanelCollection::RefreshLayout() {
     66   // A detached panel would still maintain its minimized state when it was
     67   // moved out the stack and the drag has not ended. When the drag ends, it
     68   // needs to be expanded. This could occur in the following scenarios:
     69   // 1) It was originally a minimized panel that was dragged out of a stack.
     70   // 2) It was originally a minimized panel that was the top panel in a stack.
     71   //    The panel below it was dragged out of the stack which also caused
     72   //    the top panel became detached.
     73   for (Panels::const_iterator iter = panels_.begin();
     74        iter != panels_.end(); ++iter) {
     75     Panel* panel = *iter;
     76     if (!panel->in_preview_mode() &&
     77         panel->expansion_state() != Panel::EXPANDED)
     78       panel->SetExpansionState(Panel::EXPANDED);
     79   }
     80 }
     81 
     82 void DetachedPanelCollection::AddPanel(Panel* panel,
     83                                   PositioningMask positioning_mask) {
     84   // positioning_mask is ignored since the detached panel is free-floating.
     85   DCHECK_NE(this, panel->collection());
     86   panel->set_collection(this);
     87   panels_.push_back(panel);
     88 
     89   // Offset the default position of the next detached panel if the current
     90   // default position is used.
     91   if (panel->GetBounds().origin() == default_panel_origin_)
     92     ComputeNextDefaultPanelOrigin();
     93 }
     94 
     95 void DetachedPanelCollection::RemovePanel(Panel* panel, RemovalReason reason) {
     96   DCHECK_EQ(this, panel->collection());
     97   panel->set_collection(NULL);
     98   panels_.remove(panel);
     99 }
    100 
    101 void DetachedPanelCollection::CloseAll() {
    102   // Make a copy as closing panels can modify the iterator.
    103   Panels panels_copy = panels_;
    104 
    105   for (Panels::const_iterator iter = panels_copy.begin();
    106        iter != panels_copy.end(); ++iter)
    107     (*iter)->Close();
    108 }
    109 
    110 void DetachedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) {
    111   DCHECK_EQ(this, panel->collection());
    112   // Nothing to do.
    113 }
    114 
    115 void DetachedPanelCollection::OnPanelTitlebarClicked(Panel* panel,
    116                                                 panel::ClickModifier modifier) {
    117   DCHECK_EQ(this, panel->collection());
    118   // Click on detached panel titlebars does not do anything.
    119 }
    120 
    121 void DetachedPanelCollection::ResizePanelWindow(
    122     Panel* panel,
    123     const gfx::Size& preferred_window_size) {
    124   // We should get this call only of we have the panel.
    125   DCHECK_EQ(this, panel->collection());
    126 
    127   // Make sure the new size does not violate panel's size restrictions.
    128   gfx::Size new_size(preferred_window_size.width(),
    129                      preferred_window_size.height());
    130   new_size = panel->ClampSize(new_size);
    131 
    132   // Update restored size.
    133   if (new_size != panel->full_size())
    134     panel->set_full_size(new_size);
    135 
    136   gfx::Rect bounds = panel->GetBounds();
    137 
    138   // When we resize a detached panel, its origin does not move.
    139   // So we set height and width only.
    140   bounds.set_size(new_size);
    141 
    142   if (bounds != panel->GetBounds())
    143     panel->SetPanelBounds(bounds);
    144 }
    145 
    146 void DetachedPanelCollection::ActivatePanel(Panel* panel) {
    147   DCHECK_EQ(this, panel->collection());
    148   // No change in panel's appearance.
    149 }
    150 
    151 void DetachedPanelCollection::MinimizePanel(Panel* panel) {
    152   DCHECK_EQ(this, panel->collection());
    153   // Detached panels do not minimize. However, extensions may call this API
    154   // regardless of which collection the panel is in. So we just quietly return.
    155 }
    156 
    157 void DetachedPanelCollection::RestorePanel(Panel* panel) {
    158   DCHECK_EQ(this, panel->collection());
    159   // Detached panels do not minimize. However, extensions may call this API
    160   // regardless of which collection the panel is in. So we just quietly return.
    161 }
    162 
    163 void DetachedPanelCollection::OnMinimizeButtonClicked(
    164     Panel* panel, panel::ClickModifier modifier) {
    165   panel->MinimizeBySystem();
    166 }
    167 
    168 void DetachedPanelCollection::OnRestoreButtonClicked(
    169     Panel* panel, panel::ClickModifier modifier) {
    170   // No restore button is present.
    171   NOTREACHED();
    172 }
    173 
    174 bool DetachedPanelCollection::CanShowMinimizeButton(const Panel* panel) const {
    175   // We also show minimize button for detached panel when stacking mode is
    176   // enabled.
    177   return PanelManager::IsPanelStackingEnabled() &&
    178          PanelManager::CanUseSystemMinimize();
    179 }
    180 
    181 bool DetachedPanelCollection::CanShowRestoreButton(const Panel* panel) const {
    182   // The minimize button is used for system minimize and thus there is no
    183   // restore button.
    184   return false;
    185 }
    186 
    187 bool DetachedPanelCollection::IsPanelMinimized(const Panel* panel) const {
    188   DCHECK_EQ(this, panel->collection());
    189   // Detached panels do not minimize.
    190   return false;
    191 }
    192 
    193 bool DetachedPanelCollection::UsesAlwaysOnTopPanels() const {
    194   return false;
    195 }
    196 
    197 void DetachedPanelCollection::SavePanelPlacement(Panel* panel) {
    198   DCHECK(!saved_panel_placement_.panel);
    199   saved_panel_placement_.panel = panel;
    200   saved_panel_placement_.position = panel->GetBounds().origin();
    201 }
    202 
    203 void DetachedPanelCollection::RestorePanelToSavedPlacement() {
    204   DCHECK(saved_panel_placement_.panel);
    205 
    206   gfx::Rect new_bounds(saved_panel_placement_.panel->GetBounds());
    207   new_bounds.set_origin(saved_panel_placement_.position);
    208   saved_panel_placement_.panel->SetPanelBounds(new_bounds);
    209 
    210   DiscardSavedPanelPlacement();
    211 }
    212 
    213 void DetachedPanelCollection::DiscardSavedPanelPlacement() {
    214   DCHECK(saved_panel_placement_.panel);
    215   saved_panel_placement_.panel = NULL;
    216 }
    217 
    218 panel::Resizability DetachedPanelCollection::GetPanelResizability(
    219     const Panel* panel) const {
    220   return panel::RESIZABLE_ALL;
    221 }
    222 
    223 void DetachedPanelCollection::OnPanelResizedByMouse(
    224     Panel* panel, const gfx::Rect& new_bounds) {
    225   DCHECK_EQ(this, panel->collection());
    226   panel->set_full_size(new_bounds.size());
    227 }
    228 
    229 bool DetachedPanelCollection::HasPanel(Panel* panel) const {
    230   return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
    231 }
    232 
    233 void DetachedPanelCollection::SortPanels(PanelsComparer comparer) {
    234   panels_.sort(comparer);
    235 }
    236 
    237 void DetachedPanelCollection::UpdatePanelOnCollectionChange(Panel* panel) {
    238   panel->set_attention_mode(
    239       static_cast<Panel::AttentionMode>(Panel::USE_PANEL_ATTENTION |
    240                                         Panel::USE_SYSTEM_ATTENTION));
    241   panel->ShowShadow(true);
    242   panel->UpdateMinimizeRestoreButtonVisibility();
    243   panel->SetWindowCornerStyle(panel::ALL_ROUNDED);
    244 }
    245 
    246 void DetachedPanelCollection::OnPanelExpansionStateChanged(Panel* panel) {
    247   // This should only be reached when a minimized stacked panel is dragged out
    248   // of the stack to become detached. For this case, the panel needs to be
    249   // restored.
    250   DCHECK_EQ(Panel::EXPANDED, panel->expansion_state());
    251 
    252   gfx::Rect bounds = panel->GetBounds();
    253   bounds.set_height(panel->full_size().height());
    254   panel->SetPanelBounds(bounds);
    255 }
    256 
    257 void DetachedPanelCollection::OnPanelActiveStateChanged(Panel* panel) {
    258 }
    259 
    260 gfx::Rect DetachedPanelCollection::GetInitialPanelBounds(
    261       const gfx::Rect& requested_bounds) const {
    262   if (!PanelManager::IsPanelStackingEnabled())
    263     return requested_bounds;
    264 
    265   gfx::Rect work_area = panel_manager_->display_settings_provider()->
    266       GetWorkAreaMatching(requested_bounds);
    267   gfx::Rect initial_bounds = requested_bounds;
    268   initial_bounds.set_y(
    269       work_area.y() + kDetachedPanelStartingYPositionOnStackingEnabled);
    270   return initial_bounds;
    271 }
    272 
    273 gfx::Point DetachedPanelCollection::GetDefaultPanelOrigin() {
    274   if (!default_panel_origin_.x() && !default_panel_origin_.y()) {
    275     gfx::Rect work_area =
    276         panel_manager_->display_settings_provider()->GetPrimaryWorkArea();
    277     default_panel_origin_.SetPoint(kPanelTilePixels + work_area.x(),
    278                                    kPanelTilePixels + work_area.y());
    279   }
    280   return default_panel_origin_;
    281 }
    282 
    283 void DetachedPanelCollection::ComputeNextDefaultPanelOrigin() {
    284   default_panel_origin_.Offset(kPanelTilePixels, kPanelTilePixels);
    285   gfx::Rect work_area =
    286       panel_manager_->display_settings_provider()->GetPrimaryWorkArea();
    287   if (!work_area.Contains(default_panel_origin_)) {
    288     default_panel_origin_.SetPoint(kPanelTilePixels + work_area.x(),
    289                                    kPanelTilePixels + work_area.y());
    290   }
    291 }
    292