Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / panels / docked_panel_collection.cc
blob152eb354af88476754f700db4eeede2dde1007dc
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/docked_panel_collection.h"
7 #include <math.h>
9 #include <algorithm>
10 #include <vector>
12 #include "base/auto_reset.h"
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/ui/panels/panel_drag_controller.h"
18 #include "chrome/browser/ui/panels/panel_manager.h"
19 #include "chrome/browser/ui/panels/panel_mouse_watcher.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/notification_source.h"
23 namespace {
24 // Width of spacing around panel collection and the left/right edges of the
25 // screen.
26 const int kPanelCollectionLeftMargin = 6;
27 const int kPanelCollectionRightMargin = 24;
29 // Occasionally some system, like Windows, might not bring up or down the bottom
30 // bar when the mouse enters or leaves the bottom screen area. This is the
31 // maximum time we will wait for the bottom bar visibility change notification.
32 // After the time expires, we bring up/down the titlebars as planned.
33 const int kMaxDelayWaitForBottomBarVisibilityChangeMs = 1000;
35 // See usage below.
36 #if defined(TOOLKIT_GTK)
37 const int kDelayBeforeCollapsingFromTitleOnlyStateMs = 2000;
38 #else
39 const int kDelayBeforeCollapsingFromTitleOnlyStateMs = 0;
40 #endif
42 // After focus changed, one panel lost active status, another got it,
43 // we refresh layout with a delay.
44 const int kRefreshLayoutAfterActivePanelChangeDelayMs = 600; // arbitrary
46 // As we refresh panel positions, some or all panels may move. We make sure
47 // we do not animate too many panels at once as this tends to perform poorly.
48 const int kNumPanelsToAnimateSimultaneously = 3;
50 } // namespace
52 DockedPanelCollection::DockedPanelCollection(PanelManager* panel_manager)
53 : PanelCollection(PanelCollection::DOCKED),
54 panel_manager_(panel_manager),
55 minimized_panel_count_(0),
56 are_titlebars_up_(false),
57 minimizing_all_(false),
58 delayed_titlebar_action_(NO_ACTION),
59 titlebar_action_factory_(this),
60 refresh_action_factory_(this) {
61 panel_manager_->display_settings_provider()->AddDesktopBarObserver(this);
62 OnDisplayChanged();
65 DockedPanelCollection::~DockedPanelCollection() {
66 DCHECK(panels_.empty());
67 DCHECK_EQ(0, minimized_panel_count_);
68 panel_manager_->display_settings_provider()->RemoveDesktopBarObserver(this);
71 void DockedPanelCollection::OnDisplayChanged() {
72 work_area_ =
73 panel_manager_->display_settings_provider()->GetPrimaryWorkArea();
74 work_area_.set_x(work_area_.x() + kPanelCollectionLeftMargin);
75 work_area_.set_width(work_area_.width() -
76 kPanelCollectionLeftMargin - kPanelCollectionRightMargin);
78 if (panels_.empty())
79 return;
81 for (Panels::const_iterator iter = panels_.begin();
82 iter != panels_.end(); ++iter) {
83 (*iter)->LimitSizeToWorkArea(work_area_);
86 RefreshLayout();
89 void DockedPanelCollection::AddPanel(Panel* panel,
90 PositioningMask positioning_mask) {
91 // This method does not handle minimized panels.
92 DCHECK_EQ(Panel::EXPANDED, panel->expansion_state());
94 DCHECK(panel->initialized());
95 DCHECK_NE(this, panel->collection());
96 panel->set_collection(this);
98 bool default_position = (positioning_mask & KNOWN_POSITION) == 0;
99 bool update_bounds = (positioning_mask & DO_NOT_UPDATE_BOUNDS) == 0;
101 if (default_position) {
102 gfx::Size full_size = panel->full_size();
103 gfx::Point pt = GetDefaultPositionForPanel(full_size);
104 panel->SetPanelBounds(gfx::Rect(pt, full_size));
105 panels_.push_back(panel);
106 } else {
107 DCHECK(update_bounds);
108 int x = panel->GetBounds().x();
109 Panels::iterator iter = panels_.begin();
110 for (; iter != panels_.end(); ++iter)
111 if (x > (*iter)->GetBounds().x())
112 break;
113 panels_.insert(iter, panel);
116 if (update_bounds) {
117 if ((positioning_mask & DELAY_LAYOUT_REFRESH) != 0)
118 ScheduleLayoutRefresh();
119 else
120 RefreshLayout();
124 gfx::Point DockedPanelCollection::GetDefaultPositionForPanel(
125 const gfx::Size& full_size) const {
126 int x = 0;
127 if (!panels_.empty() &&
128 panels_.back()->GetBounds().x() < work_area_.x()) {
129 // Panels go off screen. Make sure the default position will place
130 // the panel in view.
131 Panels::const_reverse_iterator iter = panels_.rbegin();
132 for (; iter != panels_.rend(); ++iter) {
133 if ((*iter)->GetBounds().x() >= work_area_.x()) {
134 x = (*iter)->GetBounds().x();
135 break;
138 // At least one panel should fit on the screen.
139 DCHECK(x > work_area_.x());
140 } else {
141 x = std::max(GetRightMostAvailablePosition() - full_size.width(),
142 work_area_.x());
144 return gfx::Point(x, work_area_.bottom() - full_size.height());
147 int DockedPanelCollection::StartingRightPosition() const {
148 return work_area_.right();
151 int DockedPanelCollection::GetRightMostAvailablePosition() const {
152 return panels_.empty() ? StartingRightPosition() :
153 (panels_.back()->GetBounds().x() - kPanelsHorizontalSpacing);
156 void DockedPanelCollection::RemovePanel(Panel* panel, RemovalReason reason) {
157 DCHECK_EQ(this, panel->collection());
158 panel->set_collection(NULL);
160 // Optimize for the common case of removing the last panel.
161 DCHECK(!panels_.empty());
162 if (panels_.back() == panel) {
163 panels_.pop_back();
165 // Update the saved panel placement if needed. This is because
166 // we might remove |saved_panel_placement_.left_panel|.
167 if (saved_panel_placement_.panel &&
168 saved_panel_placement_.left_panel == panel)
169 saved_panel_placement_.left_panel = NULL;
171 } else {
172 Panels::iterator iter = find(panels_.begin(), panels_.end(), panel);
173 DCHECK(iter != panels_.end());
174 iter = panels_.erase(iter);
176 // Update the saved panel placement if needed. This is because
177 // we might remove |saved_panel_placement_.left_panel|.
178 if (saved_panel_placement_.panel &&
179 saved_panel_placement_.left_panel == panel)
180 saved_panel_placement_.left_panel = *iter;
183 if (panel->expansion_state() != Panel::EXPANDED)
184 UpdateMinimizedPanelCount();
186 RefreshLayout();
189 void DockedPanelCollection::SavePanelPlacement(Panel* panel) {
190 DCHECK(!saved_panel_placement_.panel);
192 saved_panel_placement_.panel = panel;
194 // To recover panel to its original placement, we only need to track the panel
195 // that is placed after it.
196 Panels::iterator iter = find(panels_.begin(), panels_.end(), panel);
197 DCHECK(iter != panels_.end());
198 ++iter;
199 saved_panel_placement_.left_panel = (iter == panels_.end()) ? NULL : *iter;
202 void DockedPanelCollection::RestorePanelToSavedPlacement() {
203 DCHECK(saved_panel_placement_.panel);
205 Panel* panel = saved_panel_placement_.panel;
207 // Find next panel after this panel.
208 Panels::iterator iter = std::find(panels_.begin(), panels_.end(), panel);
209 DCHECK(iter != panels_.end());
210 Panels::iterator next_iter = iter;
211 next_iter++;
212 Panel* next_panel = (next_iter == panels_.end()) ? NULL : *iter;
214 // Restoring is only needed when this panel is not in the right position.
215 if (next_panel != saved_panel_placement_.left_panel) {
216 // Remove this panel from its current position.
217 panels_.erase(iter);
219 // Insert this panel into its previous position.
220 if (saved_panel_placement_.left_panel) {
221 Panels::iterator iter_to_insert_before = std::find(panels_.begin(),
222 panels_.end(), saved_panel_placement_.left_panel);
223 DCHECK(iter_to_insert_before != panels_.end());
224 panels_.insert(iter_to_insert_before, panel);
225 } else {
226 panels_.push_back(panel);
230 RefreshLayout();
232 DiscardSavedPanelPlacement();
235 void DockedPanelCollection::DiscardSavedPanelPlacement() {
236 DCHECK(saved_panel_placement_.panel);
237 saved_panel_placement_.panel = NULL;
238 saved_panel_placement_.left_panel = NULL;
241 panel::Resizability DockedPanelCollection::GetPanelResizability(
242 const Panel* panel) const {
243 return (panel->expansion_state() == Panel::EXPANDED) ?
244 panel::RESIZABLE_EXCEPT_BOTTOM : panel::NOT_RESIZABLE;
247 void DockedPanelCollection::OnPanelResizedByMouse(Panel* panel,
248 const gfx::Rect& new_bounds) {
249 DCHECK_EQ(this, panel->collection());
250 panel->set_full_size(new_bounds.size());
253 void DockedPanelCollection::OnPanelExpansionStateChanged(Panel* panel) {
254 gfx::Rect panel_bounds = panel->GetBounds();
255 AdjustPanelBoundsPerExpansionState(panel, &panel_bounds);
256 panel->SetPanelBounds(panel_bounds);
258 UpdateMinimizedPanelCount();
260 // Ensure minimized panel does not get the focus. If minimizing all,
261 // the active panel will be deactivated once when all panels are minimized
262 // rather than per minimized panel.
263 if (panel->expansion_state() != Panel::EXPANDED && !minimizing_all_ &&
264 panel->IsActive()) {
265 panel->Deactivate();
266 // The layout will refresh itself in response
267 // to (de)activation notification.
271 void DockedPanelCollection::AdjustPanelBoundsPerExpansionState(Panel* panel,
272 gfx::Rect* bounds) {
273 Panel::ExpansionState expansion_state = panel->expansion_state();
274 switch (expansion_state) {
275 case Panel::EXPANDED:
276 bounds->set_height(panel->full_size().height());
278 break;
279 case Panel::TITLE_ONLY:
280 bounds->set_height(panel->TitleOnlyHeight());
282 break;
283 case Panel::MINIMIZED:
284 bounds->set_height(panel::kMinimizedPanelHeight);
286 break;
287 default:
288 NOTREACHED();
289 break;
292 int bottom = GetBottomPositionForExpansionState(expansion_state);
293 bounds->set_y(bottom - bounds->height());
296 void DockedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) {
297 DCHECK_EQ(this, panel->collection());
298 Panel::ExpansionState state = panel->expansion_state();
299 if (panel->IsDrawingAttention()) {
300 // Bring up the titlebar to get user's attention.
301 if (state == Panel::MINIMIZED)
302 panel->SetExpansionState(Panel::TITLE_ONLY);
303 return;
306 // Panel is no longer drawing attention, but leave the panel in
307 // title-only mode if all titlebars are currently up.
308 if (state != Panel::TITLE_ONLY || are_titlebars_up_)
309 return;
311 // Leave titlebar up if panel is being dragged.
312 if (panel_manager_->drag_controller()->dragging_panel() == panel)
313 return;
315 // Leave titlebar up if mouse is in/below the panel.
316 const gfx::Point mouse_position =
317 panel_manager_->mouse_watcher()->GetMousePosition();
318 gfx::Rect bounds = panel->GetBounds();
319 if (bounds.x() <= mouse_position.x() &&
320 mouse_position.x() <= bounds.right() &&
321 mouse_position.y() >= bounds.y())
322 return;
324 // Bring down the titlebar now that panel is not drawing attention.
325 panel->SetExpansionState(Panel::MINIMIZED);
328 void DockedPanelCollection::OnPanelTitlebarClicked(Panel* panel,
329 panel::ClickModifier modifier) {
330 DCHECK_EQ(this, panel->collection());
331 if (!IsPanelMinimized(panel))
332 return;
334 if (modifier == panel::APPLY_TO_ALL)
335 RestoreAll();
336 else
337 RestorePanel(panel);
340 void DockedPanelCollection::ActivatePanel(Panel* panel) {
341 DCHECK_EQ(this, panel->collection());
343 // Make sure the panel is expanded when activated so the user input
344 // does not go into a collapsed window.
345 panel->SetExpansionState(Panel::EXPANDED);
347 // If the layout needs to be refreshed, it will happen in response to
348 // the activation notification (and with a slight delay to let things settle).
351 void DockedPanelCollection::MinimizePanel(Panel* panel) {
352 DCHECK_EQ(this, panel->collection());
354 if (panel->expansion_state() != Panel::EXPANDED)
355 return;
357 panel->SetExpansionState(panel->IsDrawingAttention() ?
358 Panel::TITLE_ONLY : Panel::MINIMIZED);
361 void DockedPanelCollection::RestorePanel(Panel* panel) {
362 DCHECK_EQ(this, panel->collection());
363 panel->SetExpansionState(Panel::EXPANDED);
366 void DockedPanelCollection::MinimizeAll() {
367 // Set minimizing_all_ to prevent deactivation of each panel when it
368 // is minimized. See comments in OnPanelExpansionStateChanged.
369 base::AutoReset<bool> pin(&minimizing_all_, true);
370 Panel* minimized_active_panel = NULL;
371 for (Panels::const_iterator iter = panels_.begin();
372 iter != panels_.end(); ++iter) {
373 if ((*iter)->IsActive())
374 minimized_active_panel = *iter;
375 MinimizePanel(*iter);
378 // When a single panel is minimized, it is deactivated to ensure that
379 // a minimized panel does not have focus. However, when minimizing all,
380 // the deactivation is only done once after all panels are minimized,
381 // rather than per minimized panel, both for efficiency and to avoid
382 // temporary activations of random not-yet-minimized panels.
383 if (minimized_active_panel) {
384 minimized_active_panel->Deactivate();
385 // Layout will be refreshed in response to (de)activation notification.
389 void DockedPanelCollection::RestoreAll() {
390 for (Panels::const_iterator iter = panels_.begin();
391 iter != panels_.end(); ++iter) {
392 RestorePanel(*iter);
396 void DockedPanelCollection::OnMinimizeButtonClicked(
397 Panel* panel, panel::ClickModifier modifier) {
398 if (modifier == panel::APPLY_TO_ALL)
399 MinimizeAll();
400 else
401 MinimizePanel(panel);
404 void DockedPanelCollection::OnRestoreButtonClicked(
405 Panel* panel, panel::ClickModifier modifier) {
406 if (modifier == panel::APPLY_TO_ALL)
407 RestoreAll();
408 else
409 RestorePanel(panel);
412 bool DockedPanelCollection::CanShowMinimizeButton(const Panel* panel) const {
413 return !IsPanelMinimized(panel);
416 bool DockedPanelCollection::CanShowRestoreButton(const Panel* panel) const {
417 return IsPanelMinimized(panel);
420 bool DockedPanelCollection::IsPanelMinimized(const Panel* panel) const {
421 return panel->expansion_state() != Panel::EXPANDED;
424 bool DockedPanelCollection::UsesAlwaysOnTopPanels() const {
425 return true;
428 void DockedPanelCollection::UpdateMinimizedPanelCount() {
429 int prev_minimized_panel_count = minimized_panel_count_;
430 minimized_panel_count_ = 0;
431 for (Panels::const_iterator panel_iter = panels_.begin();
432 panel_iter != panels_.end(); ++panel_iter) {
433 if ((*panel_iter)->expansion_state() != Panel::EXPANDED)
434 minimized_panel_count_++;
437 if (prev_minimized_panel_count == 0 && minimized_panel_count_ > 0)
438 panel_manager_->mouse_watcher()->AddObserver(this);
439 else if (prev_minimized_panel_count > 0 && minimized_panel_count_ == 0)
440 panel_manager_->mouse_watcher()->RemoveObserver(this);
442 DCHECK_LE(minimized_panel_count_, num_panels());
445 void DockedPanelCollection::ResizePanelWindow(
446 Panel* panel,
447 const gfx::Size& preferred_window_size) {
448 DCHECK_EQ(this, panel->collection());
449 // Make sure the new size does not violate panel's size restrictions.
450 gfx::Size new_size(preferred_window_size.width(),
451 preferred_window_size.height());
452 new_size = panel->ClampSize(new_size);
454 if (new_size == panel->full_size())
455 return;
457 panel->set_full_size(new_size);
459 RefreshLayout();
462 bool DockedPanelCollection::ShouldBringUpTitlebars(int mouse_x,
463 int mouse_y) const {
464 // We should always bring up the titlebar if the mouse is over the
465 // visible auto-hiding bottom bar.
466 DisplaySettingsProvider* provider =
467 panel_manager_->display_settings_provider();
468 if (provider->IsAutoHidingDesktopBarEnabled(
469 DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM) &&
470 provider->GetDesktopBarVisibility(
471 DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM) ==
472 DisplaySettingsProvider::DESKTOP_BAR_VISIBLE) {
473 int bottom_bar_bottom = work_area_.bottom();
474 int bottom_bar_y = bottom_bar_bottom - provider->GetDesktopBarThickness(
475 DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM);
476 if (bottom_bar_y <= mouse_y && mouse_y <= bottom_bar_bottom)
477 return true;
480 // Bring up titlebars if any panel needs the titlebar up.
481 Panel* dragging_panel = panel_manager_->drag_controller()->dragging_panel();
482 if (dragging_panel &&
483 dragging_panel->collection()->type() != PanelCollection::DOCKED)
484 dragging_panel = NULL;
485 for (Panels::const_iterator iter = panels_.begin();
486 iter != panels_.end(); ++iter) {
487 Panel* panel = *iter;
488 Panel::ExpansionState state = panel->expansion_state();
489 // Skip the expanded panel.
490 if (state == Panel::EXPANDED)
491 continue;
493 // If the panel is showing titlebar only, we want to keep it up when it is
494 // being dragged.
495 if (state == Panel::TITLE_ONLY && panel == dragging_panel)
496 return true;
498 // We do not want to bring up other minimized panels if the mouse is over
499 // the panel that pops up the titlebar to attract attention.
500 if (panel->IsDrawingAttention())
501 continue;
503 gfx::Rect bounds = panel->GetBounds();
504 if (bounds.x() <= mouse_x && mouse_x <= bounds.right() &&
505 mouse_y >= bounds.y())
506 return true;
508 return false;
511 void DockedPanelCollection::BringUpOrDownTitlebars(bool bring_up) {
512 if (are_titlebars_up_ == bring_up)
513 return;
515 are_titlebars_up_ = bring_up;
516 int task_delay_ms = 0;
518 // If the auto-hiding bottom bar exists, delay the action until the bottom
519 // bar is fully visible or hidden. We do not want both bottom bar and panel
520 // titlebar to move at the same time but with different speeds.
521 DisplaySettingsProvider* provider =
522 panel_manager_->display_settings_provider();
523 if (provider->IsAutoHidingDesktopBarEnabled(
524 DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM)) {
525 DisplaySettingsProvider::DesktopBarVisibility visibility =
526 provider->GetDesktopBarVisibility(
527 DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM);
528 if (visibility !=
529 (bring_up ? DisplaySettingsProvider::DESKTOP_BAR_VISIBLE
530 : DisplaySettingsProvider::DESKTOP_BAR_HIDDEN)) {
531 // Occasionally some system, like Windows, might not bring up or down the
532 // bottom bar when the mouse enters or leaves the bottom screen area.
533 // Thus, we schedule a delayed task to do the work if we do not receive
534 // the bottom bar visibility change notification within a certain period
535 // of time.
536 task_delay_ms = kMaxDelayWaitForBottomBarVisibilityChangeMs;
540 // On some OSes, the interaction with native Taskbars/Docks may be improved
541 // if the panels do not go back to minimized state too fast. For example,
542 // with a taskbar in auto-hide mode, the taskbar will cover the panel in
543 // title-only mode which appears on hover. Leaving it up for a little longer
544 // would allow the user to be able to click on it.
546 // Currently, no platforms use both delays.
547 DCHECK(task_delay_ms == 0 ||
548 kDelayBeforeCollapsingFromTitleOnlyStateMs == 0);
549 if (!bring_up && task_delay_ms == 0) {
550 task_delay_ms = kDelayBeforeCollapsingFromTitleOnlyStateMs;
553 // OnAutoHidingDesktopBarVisibilityChanged will handle this.
554 delayed_titlebar_action_ = bring_up ? BRING_UP : BRING_DOWN;
556 // If user moves the mouse in and out of mouse tracking area, we might have
557 // previously posted but not yet dispatched task in the queue. New action
558 // should always 'reset' the delays so cancel any tasks that haven't run yet
559 // and post a new one.
560 titlebar_action_factory_.InvalidateWeakPtrs();
561 base::MessageLoop::current()->PostDelayedTask(
562 FROM_HERE,
563 base::Bind(&DockedPanelCollection::DelayedBringUpOrDownTitlebarsCheck,
564 titlebar_action_factory_.GetWeakPtr()),
565 base::TimeDelta::FromMilliseconds(
566 PanelManager::AdjustTimeInterval(task_delay_ms)));
569 void DockedPanelCollection::DelayedBringUpOrDownTitlebarsCheck() {
570 // Task was already processed or cancelled - bail out.
571 if (delayed_titlebar_action_ == NO_ACTION)
572 return;
574 bool need_to_bring_up_titlebars = (delayed_titlebar_action_ == BRING_UP);
576 delayed_titlebar_action_ = NO_ACTION;
578 // Check if the action is still needed based on the latest mouse position. The
579 // user could move the mouse into the tracking area and then quickly move it
580 // out of the area. In case of this, cancel the action.
581 if (are_titlebars_up_ != need_to_bring_up_titlebars)
582 return;
584 DoBringUpOrDownTitlebars(need_to_bring_up_titlebars);
587 void DockedPanelCollection::DoBringUpOrDownTitlebars(bool bring_up) {
588 for (Panels::const_iterator iter = panels_.begin();
589 iter != panels_.end(); ++iter) {
590 Panel* panel = *iter;
592 // Skip any panel that is drawing the attention.
593 if (panel->IsDrawingAttention())
594 continue;
596 if (bring_up) {
597 if (panel->expansion_state() == Panel::MINIMIZED)
598 panel->SetExpansionState(Panel::TITLE_ONLY);
599 } else {
600 if (panel->expansion_state() == Panel::TITLE_ONLY)
601 panel->SetExpansionState(Panel::MINIMIZED);
606 int DockedPanelCollection::GetBottomPositionForExpansionState(
607 Panel::ExpansionState expansion_state) const {
608 int bottom = work_area_.bottom();
609 // If there is an auto-hiding desktop bar aligned to the bottom edge, we need
610 // to move the title-only panel above the auto-hiding desktop bar.
611 DisplaySettingsProvider* provider =
612 panel_manager_->display_settings_provider();
613 if (expansion_state == Panel::TITLE_ONLY &&
614 provider->IsAutoHidingDesktopBarEnabled(
615 DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM)) {
616 bottom -= provider->GetDesktopBarThickness(
617 DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM);
620 return bottom;
623 void DockedPanelCollection::OnMouseMove(const gfx::Point& mouse_position) {
624 bool bring_up_titlebars = ShouldBringUpTitlebars(mouse_position.x(),
625 mouse_position.y());
626 BringUpOrDownTitlebars(bring_up_titlebars);
629 void DockedPanelCollection::OnAutoHidingDesktopBarVisibilityChanged(
630 DisplaySettingsProvider::DesktopBarAlignment alignment,
631 DisplaySettingsProvider::DesktopBarVisibility visibility) {
632 if (delayed_titlebar_action_ == NO_ACTION)
633 return;
635 DisplaySettingsProvider::DesktopBarVisibility expected_visibility =
636 delayed_titlebar_action_ == BRING_UP
637 ? DisplaySettingsProvider::DESKTOP_BAR_VISIBLE
638 : DisplaySettingsProvider::DESKTOP_BAR_HIDDEN;
639 if (visibility != expected_visibility)
640 return;
642 DoBringUpOrDownTitlebars(delayed_titlebar_action_ == BRING_UP);
643 delayed_titlebar_action_ = NO_ACTION;
646 void DockedPanelCollection::OnAutoHidingDesktopBarThicknessChanged(
647 DisplaySettingsProvider::DesktopBarAlignment alignment, int thickness) {
648 RefreshLayout();
651 void DockedPanelCollection::RefreshLayout() {
652 int total_active_width = 0;
653 int total_inactive_width = 0;
655 for (Panels::const_iterator panel_iter = panels_.begin();
656 panel_iter != panels_.end(); ++panel_iter) {
657 Panel* panel = *panel_iter;
658 if (panel->IsActive())
659 total_active_width += panel->full_size().width();
660 else
661 total_inactive_width += panel->full_size().width();
664 double display_width_for_inactive_panels =
665 work_area_.width() - total_active_width -
666 kPanelsHorizontalSpacing * panels_.size();
667 double overflow_squeeze_factor = (total_inactive_width > 0) ?
668 std::min(display_width_for_inactive_panels / total_inactive_width, 1.0) :
669 1.0;
671 // We want to calculate all bounds first, then apply them in a specific order.
672 typedef std::pair<Panel*, gfx::Rect> PanelBoundsInfo;
673 // The next pair of variables will hold panels that move, respectively,
674 // to the right and to the left. We want to process them from the center
675 // outwards, so one is a stack and another is a queue.
676 std::vector<PanelBoundsInfo> moving_right;
677 std::queue<PanelBoundsInfo> moving_left;
679 int rightmost_position = StartingRightPosition();
680 for (Panels::const_iterator panel_iter = panels_.begin();
681 panel_iter != panels_.end(); ++panel_iter) {
682 Panel* panel = *panel_iter;
683 gfx::Rect old_bounds = panel->GetBounds();
684 gfx::Rect new_bounds = old_bounds;
685 AdjustPanelBoundsPerExpansionState(panel, &new_bounds);
687 new_bounds.set_width(
688 WidthToDisplayPanelInCollection(panel->IsActive(),
689 overflow_squeeze_factor,
690 panel->full_size().width()));
691 int x = rightmost_position - new_bounds.width();
692 new_bounds.set_x(x);
694 if (x < old_bounds.x() ||
695 (x == old_bounds.x() && new_bounds.width() <= old_bounds.width()))
696 moving_left.push(std::make_pair(panel, new_bounds));
697 else
698 moving_right.push_back(std::make_pair(panel, new_bounds));
700 rightmost_position = x - kPanelsHorizontalSpacing;
703 // Update panels going in both directions.
704 // This is important on Mac where bounds changes are slow and you see a
705 // "wave" instead of a smooth sliding effect.
706 int num_animated = 0;
707 bool going_right = true;
708 while (!moving_right.empty() || !moving_left.empty()) {
709 PanelBoundsInfo bounds_info;
710 // Alternate between processing the panels that moving left and right,
711 // starting from the center.
712 going_right = !going_right;
713 bool take_panel_on_right =
714 (going_right && !moving_right.empty()) ||
715 moving_left.empty();
716 if (take_panel_on_right) {
717 bounds_info = moving_right.back();
718 moving_right.pop_back();
719 } else {
720 bounds_info = moving_left.front();
721 moving_left.pop();
724 // Don't update the docked panel that is in preview mode.
725 Panel* panel = bounds_info.first;
726 gfx::Rect bounds = bounds_info.second;
727 if (!panel->in_preview_mode() && bounds != panel->GetBounds()) {
728 // We animate a limited number of panels, starting with the
729 // "most important" ones, that is, ones that are close to the center
730 // of the action. Other panels are moved instantly to improve performance.
731 if (num_animated < kNumPanelsToAnimateSimultaneously) {
732 panel->SetPanelBounds(bounds); // Animates.
733 ++num_animated;
734 } else {
735 panel->SetPanelBoundsInstantly(bounds);
740 content::NotificationService::current()->Notify(
741 chrome::NOTIFICATION_PANEL_COLLECTION_UPDATED,
742 content::Source<PanelCollection>(this),
743 content::NotificationService::NoDetails());
746 int DockedPanelCollection::WidthToDisplayPanelInCollection(
747 bool is_for_active_panel, double squeeze_factor, int full_width) const {
748 return is_for_active_panel ? full_width :
749 std::max(panel::kPanelMinWidth,
750 static_cast<int>(floor(full_width * squeeze_factor)));
753 void DockedPanelCollection::CloseAll() {
754 // This should only be called at the end of tests to clean up.
756 // Make a copy of the iterator as closing panels can modify the vector.
757 Panels panels_copy = panels_;
759 // Start from the bottom to avoid reshuffling.
760 for (Panels::reverse_iterator iter = panels_copy.rbegin();
761 iter != panels_copy.rend(); ++iter)
762 (*iter)->Close();
765 void DockedPanelCollection::UpdatePanelOnCollectionChange(Panel* panel) {
766 panel->set_attention_mode(Panel::USE_PANEL_ATTENTION);
767 panel->ShowShadow(true);
768 panel->UpdateMinimizeRestoreButtonVisibility();
769 panel->SetWindowCornerStyle(panel::TOP_ROUNDED);
772 void DockedPanelCollection::ScheduleLayoutRefresh() {
773 refresh_action_factory_.InvalidateWeakPtrs();
774 base::MessageLoop::current()->PostDelayedTask(
775 FROM_HERE,
776 base::Bind(&DockedPanelCollection::RefreshLayout,
777 refresh_action_factory_.GetWeakPtr()),
778 base::TimeDelta::FromMilliseconds(PanelManager::AdjustTimeInterval(
779 kRefreshLayoutAfterActivePanelChangeDelayMs)));
782 void DockedPanelCollection::OnPanelActiveStateChanged(Panel* panel) {
783 // Refresh layout, but wait till active states settle.
784 // This lets us avoid refreshing too many times when one panel loses
785 // focus and another gains it.
786 ScheduleLayoutRefresh();
789 gfx::Rect DockedPanelCollection::GetInitialPanelBounds(
790 const gfx::Rect& requested_bounds) const {
791 gfx::Rect initial_bounds = requested_bounds;
792 initial_bounds.set_origin(
793 GetDefaultPositionForPanel(requested_bounds.size()));
794 return initial_bounds;
797 bool DockedPanelCollection::HasPanel(Panel* panel) const {
798 return find(panels_.begin(), panels_.end(), panel) != panels_.end();