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.
5 #include "chrome/browser/ui/panels/detached_panel_collection.h"
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"
14 // How much horizontal and vertical offset there is between newly opened
16 const int kPanelTilePixels
= 10;
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
22 const int kDetachedPanelStartingYPositionOnStackingEnabled
= 20;
25 DetachedPanelCollection::DetachedPanelCollection(PanelManager
* panel_manager
)
26 : PanelCollection(PanelCollection::DETACHED
),
27 panel_manager_(panel_manager
) {
30 DetachedPanelCollection::~DetachedPanelCollection() {
31 DCHECK(panels_
.empty());
34 void DetachedPanelCollection::OnDisplayChanged() {
35 DisplaySettingsProvider
* display_settings_provider
=
36 panel_manager_
->display_settings_provider();
38 for (Panels::const_iterator iter
= panels_
.begin();
39 iter
!= panels_
.end(); ++iter
) {
42 display_settings_provider
->GetWorkAreaMatching(panel
->GetBounds());
44 // Update size if needed.
45 panel
->LimitSizeToWorkArea(work_area
);
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());
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
);
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
) {
76 if (!panel
->in_preview_mode() &&
77 panel
->expansion_state() != Panel::EXPANDED
)
78 panel
->SetExpansionState(Panel::EXPANDED
);
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
);
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();
95 void DetachedPanelCollection::RemovePanel(Panel
* panel
, RemovalReason reason
) {
96 DCHECK_EQ(this, panel
->collection());
97 panel
->set_collection(NULL
);
98 panels_
.remove(panel
);
101 void DetachedPanelCollection::CloseAll() {
102 // Make a copy as closing panels can modify the iterator.
103 Panels panels_copy
= panels_
;
105 for (Panels::const_iterator iter
= panels_copy
.begin();
106 iter
!= panels_copy
.end(); ++iter
)
110 void DetachedPanelCollection::OnPanelAttentionStateChanged(Panel
* panel
) {
111 DCHECK_EQ(this, panel
->collection());
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.
121 void DetachedPanelCollection::ResizePanelWindow(
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());
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
);
132 // Update restored size.
133 if (new_size
!= panel
->full_size())
134 panel
->set_full_size(new_size
);
136 gfx::Rect bounds
= panel
->GetBounds();
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
);
142 if (bounds
!= panel
->GetBounds())
143 panel
->SetPanelBounds(bounds
);
146 void DetachedPanelCollection::ActivatePanel(Panel
* panel
) {
147 DCHECK_EQ(this, panel
->collection());
148 // No change in panel's appearance.
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.
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.
163 void DetachedPanelCollection::OnMinimizeButtonClicked(
164 Panel
* panel
, panel::ClickModifier modifier
) {
165 panel
->MinimizeBySystem();
168 void DetachedPanelCollection::OnRestoreButtonClicked(
169 Panel
* panel
, panel::ClickModifier modifier
) {
170 // No restore button is present.
174 bool DetachedPanelCollection::CanShowMinimizeButton(const Panel
* panel
) const {
175 // We also show minimize button for detached panel when stacking mode is
177 return PanelManager::IsPanelStackingEnabled() &&
178 PanelManager::CanUseSystemMinimize();
181 bool DetachedPanelCollection::CanShowRestoreButton(const Panel
* panel
) const {
182 // The minimize button is used for system minimize and thus there is no
187 bool DetachedPanelCollection::IsPanelMinimized(const Panel
* panel
) const {
188 DCHECK_EQ(this, panel
->collection());
189 // Detached panels do not minimize.
193 bool DetachedPanelCollection::UsesAlwaysOnTopPanels() const {
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();
203 void DetachedPanelCollection::RestorePanelToSavedPlacement() {
204 DCHECK(saved_panel_placement_
.panel
);
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
);
210 DiscardSavedPanelPlacement();
213 void DetachedPanelCollection::DiscardSavedPanelPlacement() {
214 DCHECK(saved_panel_placement_
.panel
);
215 saved_panel_placement_
.panel
= NULL
;
218 panel::Resizability
DetachedPanelCollection::GetPanelResizability(
219 const Panel
* panel
) const {
220 return panel::RESIZABLE_ALL
;
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());
229 bool DetachedPanelCollection::HasPanel(Panel
* panel
) const {
230 return std::find(panels_
.begin(), panels_
.end(), panel
) != panels_
.end();
233 void DetachedPanelCollection::SortPanels(PanelsComparer comparer
) {
234 panels_
.sort(comparer
);
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
->EnableResizeByMouse(true);
243 panel
->UpdateMinimizeRestoreButtonVisibility();
244 panel
->SetWindowCornerStyle(panel::ALL_ROUNDED
);
247 void DetachedPanelCollection::OnPanelExpansionStateChanged(Panel
* panel
) {
248 // This should only be reached when a minimized stacked panel is dragged out
249 // of the stack to become detached. For this case, the panel needs to be
251 DCHECK_EQ(Panel::EXPANDED
, panel
->expansion_state());
253 gfx::Rect bounds
= panel
->GetBounds();
254 bounds
.set_height(panel
->full_size().height());
255 panel
->SetPanelBounds(bounds
);
258 void DetachedPanelCollection::OnPanelActiveStateChanged(Panel
* panel
) {
261 gfx::Rect
DetachedPanelCollection::GetInitialPanelBounds(
262 const gfx::Rect
& requested_bounds
) const {
263 if (!PanelManager::IsPanelStackingEnabled())
264 return requested_bounds
;
266 gfx::Rect work_area
= panel_manager_
->display_settings_provider()->
267 GetWorkAreaMatching(requested_bounds
);
268 gfx::Rect initial_bounds
= requested_bounds
;
269 initial_bounds
.set_y(
270 work_area
.y() + kDetachedPanelStartingYPositionOnStackingEnabled
);
271 return initial_bounds
;
274 gfx::Point
DetachedPanelCollection::GetDefaultPanelOrigin() {
275 if (!default_panel_origin_
.x() && !default_panel_origin_
.y()) {
276 gfx::Rect work_area
=
277 panel_manager_
->display_settings_provider()->GetPrimaryWorkArea();
278 default_panel_origin_
.SetPoint(kPanelTilePixels
+ work_area
.x(),
279 kPanelTilePixels
+ work_area
.y());
281 return default_panel_origin_
;
284 void DetachedPanelCollection::ComputeNextDefaultPanelOrigin() {
285 default_panel_origin_
.Offset(kPanelTilePixels
, kPanelTilePixels
);
286 gfx::Rect work_area
=
287 panel_manager_
->display_settings_provider()->GetPrimaryWorkArea();
288 if (!work_area
.Contains(default_panel_origin_
)) {
289 default_panel_origin_
.SetPoint(kPanelTilePixels
+ work_area
.x(),
290 kPanelTilePixels
+ work_area
.y());