• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DetachedPanelCollection(PanelManager * panel_manager)25 DetachedPanelCollection::DetachedPanelCollection(PanelManager* panel_manager)
26     : PanelCollection(PanelCollection::DETACHED),
27       panel_manager_(panel_manager) {
28 }
29 
~DetachedPanelCollection()30 DetachedPanelCollection::~DetachedPanelCollection() {
31   DCHECK(panels_.empty());
32 }
33 
OnDisplayChanged()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 
RefreshLayout()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 
AddPanel(Panel * panel,PositioningMask positioning_mask)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 
RemovePanel(Panel * panel,RemovalReason reason)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 
CloseAll()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 
OnPanelAttentionStateChanged(Panel * panel)110 void DetachedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) {
111   DCHECK_EQ(this, panel->collection());
112   // Nothing to do.
113 }
114 
OnPanelTitlebarClicked(Panel * panel,panel::ClickModifier modifier)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 
ResizePanelWindow(Panel * panel,const gfx::Size & preferred_window_size)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 
ActivatePanel(Panel * panel)146 void DetachedPanelCollection::ActivatePanel(Panel* panel) {
147   DCHECK_EQ(this, panel->collection());
148   // No change in panel's appearance.
149 }
150 
MinimizePanel(Panel * panel)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 
RestorePanel(Panel * panel)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 
OnMinimizeButtonClicked(Panel * panel,panel::ClickModifier modifier)163 void DetachedPanelCollection::OnMinimizeButtonClicked(
164     Panel* panel, panel::ClickModifier modifier) {
165   panel->MinimizeBySystem();
166 }
167 
OnRestoreButtonClicked(Panel * panel,panel::ClickModifier modifier)168 void DetachedPanelCollection::OnRestoreButtonClicked(
169     Panel* panel, panel::ClickModifier modifier) {
170   // No restore button is present.
171   NOTREACHED();
172 }
173 
CanShowMinimizeButton(const Panel * panel) const174 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 
CanShowRestoreButton(const Panel * panel) const181 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 
IsPanelMinimized(const Panel * panel) const187 bool DetachedPanelCollection::IsPanelMinimized(const Panel* panel) const {
188   DCHECK_EQ(this, panel->collection());
189   // Detached panels do not minimize.
190   return false;
191 }
192 
UsesAlwaysOnTopPanels() const193 bool DetachedPanelCollection::UsesAlwaysOnTopPanels() const {
194   return false;
195 }
196 
SavePanelPlacement(Panel * panel)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 
RestorePanelToSavedPlacement()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 
DiscardSavedPanelPlacement()213 void DetachedPanelCollection::DiscardSavedPanelPlacement() {
214   DCHECK(saved_panel_placement_.panel);
215   saved_panel_placement_.panel = NULL;
216 }
217 
GetPanelResizability(const Panel * panel) const218 panel::Resizability DetachedPanelCollection::GetPanelResizability(
219     const Panel* panel) const {
220   return panel::RESIZABLE_ALL;
221 }
222 
OnPanelResizedByMouse(Panel * panel,const gfx::Rect & new_bounds)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 
HasPanel(Panel * panel) const229 bool DetachedPanelCollection::HasPanel(Panel* panel) const {
230   return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
231 }
232 
SortPanels(PanelsComparer comparer)233 void DetachedPanelCollection::SortPanels(PanelsComparer comparer) {
234   panels_.sort(comparer);
235 }
236 
UpdatePanelOnCollectionChange(Panel * panel)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 
OnPanelExpansionStateChanged(Panel * panel)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 
OnPanelActiveStateChanged(Panel * panel)257 void DetachedPanelCollection::OnPanelActiveStateChanged(Panel* panel) {
258 }
259 
GetInitialPanelBounds(const gfx::Rect & requested_bounds) const260 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 
GetDefaultPanelOrigin()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 
ComputeNextDefaultPanelOrigin()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