| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/panels/panel_manager.h" |
| |
| #include <algorithm> |
| #include "base/logging.h" |
| #include "base/scoped_ptr.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/panels/panel.h" |
| #include "chrome/browser/ui/window_sizer.h" |
| |
| namespace { |
| // Invalid panel index. |
| const size_t kInvalidPanelIndex = static_cast<size_t>(-1); |
| |
| // Minimum width and height of a panel. |
| const int kPanelMinWidthPixels = 64; |
| const int kPanelMinHeightPixels = 24; |
| |
| // Default width and height of a panel. |
| const int kPanelDefaultWidthPixels = 240; |
| const int kPanelDefaultHeightPixels = 290; |
| |
| // Maxmium width and height of a panel based on the factor of the working |
| // area. |
| const double kPanelMaxWidthFactor = 1.0; |
| const double kPanelMaxHeightFactor = 0.5; |
| |
| // Horizontal spacing between two panels. |
| const int kPanelsHorizontalSpacing = 4; |
| |
| // Single instance of PanelManager. |
| scoped_ptr<PanelManager> panel_instance; |
| } // namespace |
| |
| // static |
| PanelManager* PanelManager::GetInstance() { |
| if (!panel_instance.get()) { |
| panel_instance.reset(new PanelManager()); |
| } |
| return panel_instance.get(); |
| } |
| |
| PanelManager::PanelManager() |
| : max_width_(0), |
| max_height_(0), |
| min_x_(0), |
| current_x_(0), |
| bottom_edge_y_(0), |
| dragging_panel_index_(kInvalidPanelIndex), |
| dragging_panel_original_x_(0) { |
| OnDisplayChanged(); |
| } |
| |
| PanelManager::~PanelManager() { |
| DCHECK(active_panels_.empty()); |
| DCHECK(pending_panels_.empty()); |
| DCHECK(panels_pending_to_remove_.empty()); |
| } |
| |
| void PanelManager::OnDisplayChanged() { |
| scoped_ptr<WindowSizer::MonitorInfoProvider> info_provider( |
| WindowSizer::CreateDefaultMonitorInfoProvider()); |
| gfx::Rect work_area = info_provider->GetPrimaryMonitorWorkArea(); |
| |
| min_x_ = work_area.x(); |
| current_x_ = work_area.right(); |
| bottom_edge_y_ = work_area.bottom(); |
| max_width_ = static_cast<int>(work_area.width() * kPanelMaxWidthFactor); |
| max_height_ = static_cast<int>(work_area.height() * kPanelMaxHeightFactor); |
| |
| Rearrange(active_panels_.begin()); |
| } |
| |
| Panel* PanelManager::CreatePanel(Browser* browser) { |
| gfx::Rect bounds = browser->override_bounds(); |
| bool is_within_bounds = ComputeBoundsForNextPanel(&bounds, true); |
| |
| Panel* panel = new Panel(browser, bounds); |
| if (is_within_bounds) |
| active_panels_.push_back(panel); |
| else |
| pending_panels_.push_back(panel); |
| |
| return panel; |
| } |
| |
| void PanelManager::ProcessPending() { |
| while (!pending_panels_.empty()) { |
| Panel* panel = pending_panels_.front(); |
| gfx::Rect bounds = panel->bounds(); |
| if (ComputeBoundsForNextPanel(&bounds, true)) { |
| // TODO(jianli): More work to support displaying pending panels. |
| active_panels_.push_back(panel); |
| pending_panels_.pop_front(); |
| } |
| } |
| } |
| |
| void PanelManager::Remove(Panel* panel) { |
| // If we're in the process of dragging, delay the removal. |
| if (dragging_panel_index_ != kInvalidPanelIndex) { |
| panels_pending_to_remove_.push_back(panel); |
| return; |
| } |
| |
| DoRemove(panel); |
| } |
| |
| void PanelManager::DelayedRemove() { |
| for (size_t i = 0; i < panels_pending_to_remove_.size(); ++i) |
| DoRemove(panels_pending_to_remove_[i]); |
| panels_pending_to_remove_.clear(); |
| } |
| |
| void PanelManager::DoRemove(Panel* panel) { |
| // Checks the active panel list. |
| ActivePanels::iterator iter = |
| find(active_panels_.begin(), active_panels_.end(), panel); |
| if (iter == active_panels_.end()) { |
| // Checks the pending panel list. |
| PendingPanels::iterator iter2 = |
| find(pending_panels_.begin(), pending_panels_.end(), panel); |
| if (iter2 != pending_panels_.end()) |
| pending_panels_.erase(iter2); |
| return; |
| } |
| |
| current_x_ = (*iter)->bounds().x() + (*iter)->bounds().width(); |
| Rearrange(active_panels_.erase(iter)); |
| |
| ProcessPending(); |
| } |
| |
| void PanelManager::StartDragging(Panel* panel) { |
| for (size_t i = 0; i < active_panels_.size(); ++i) { |
| if (active_panels_[i] == panel) { |
| dragging_panel_index_ = i; |
| dragging_panel_bounds_ = panel->bounds(); |
| dragging_panel_original_x_ = dragging_panel_bounds_.x(); |
| break; |
| } |
| } |
| } |
| |
| void PanelManager::Drag(int delta_x) { |
| DCHECK(dragging_panel_index_ != kInvalidPanelIndex); |
| |
| if (!delta_x) |
| return; |
| |
| // Moves this panel to the dragging position. |
| gfx::Rect new_bounds(active_panels_[dragging_panel_index_]->bounds()); |
| new_bounds.set_x(new_bounds.x() + delta_x); |
| active_panels_[dragging_panel_index_]->SetBounds(new_bounds); |
| |
| // Checks and processes other affected panels. |
| if (delta_x > 0) |
| DragPositive(delta_x); |
| else |
| DragNegative(delta_x); |
| } |
| |
| void PanelManager::DragNegative(int delta_x) { |
| DCHECK(delta_x < 0); |
| |
| Panel* dragging_panel = active_panels_[dragging_panel_index_]; |
| |
| // This is the left corner of the dragging panel. We use it to check against |
| // all the panels on its left. |
| int dragging_panel_x = dragging_panel->bounds().x() + delta_x; |
| |
| // This is the right corner which a panel will be moved to. |
| int right_x_to_move_to = |
| dragging_panel_bounds_.x() + dragging_panel_bounds_.width(); |
| |
| // Checks the panels to the left of the dragging panel. |
| size_t i = dragging_panel_index_; |
| size_t j = i + 1; |
| for (; j < active_panels_.size(); ++j, ++i) { |
| // Current panel will only be affected if the left corner of dragging |
| // panel goes beyond the middle position of the current panel. |
| if (dragging_panel_x > active_panels_[j]->bounds().x() + |
| active_panels_[j]->bounds().width() / 2) |
| break; |
| |
| // Moves current panel to the new position. |
| gfx::Rect bounds(active_panels_[j]->bounds()); |
| bounds.set_x(right_x_to_move_to - bounds.width()); |
| right_x_to_move_to -= bounds.width() + kPanelsHorizontalSpacing; |
| active_panels_[j]->SetBounds(bounds); |
| |
| // Adjusts the index of current panel. |
| active_panels_[i] = active_panels_[j]; |
| } |
| |
| // Adjusts the position and index of dragging panel as the result of moving |
| // other affected panels. |
| if (j != dragging_panel_index_ + 1) { |
| j--; |
| dragging_panel_bounds_.set_x(right_x_to_move_to - |
| dragging_panel_bounds_.width()); |
| active_panels_[j] = dragging_panel; |
| dragging_panel_index_ = j; |
| } |
| } |
| |
| void PanelManager::DragPositive(int delta_x) { |
| DCHECK(delta_x > 0); |
| |
| Panel* dragging_panel = active_panels_[dragging_panel_index_]; |
| |
| // This is the right corner of the dragging panel. We use it to check against |
| // all the panels on its right. |
| int dragging_panel_x = dragging_panel->bounds().x() + |
| dragging_panel->bounds().width() - 1 + delta_x; |
| |
| // This is the left corner which a panel will be moved to. |
| int left_x_to_move_to = dragging_panel_bounds_.x(); |
| |
| // Checks the panels to the right of the dragging panel. |
| int i = static_cast<int>(dragging_panel_index_); |
| int j = i - 1; |
| for (; j >= 0; --j, --i) { |
| // Current panel will only be affected if the right corner of dragging |
| // panel goes beyond the middle position of the current panel. |
| if (dragging_panel_x < active_panels_[j]->bounds().x() + |
| active_panels_[j]->bounds().width() / 2) |
| break; |
| |
| // Moves current panel to the new position. |
| gfx::Rect bounds(active_panels_[j]->bounds()); |
| bounds.set_x(left_x_to_move_to); |
| left_x_to_move_to += bounds.width() + kPanelsHorizontalSpacing; |
| active_panels_[j]->SetBounds(bounds); |
| |
| // Adjusts the index of current panel. |
| active_panels_[i] = active_panels_[j]; |
| } |
| |
| // Adjusts the position and index of dragging panel as the result of moving |
| // other affected panels. |
| if (j != static_cast<int>(dragging_panel_index_) - 1) { |
| j++; |
| dragging_panel_bounds_.set_x(left_x_to_move_to); |
| active_panels_[j] = dragging_panel; |
| dragging_panel_index_ = j; |
| } |
| } |
| |
| void PanelManager::EndDragging(bool cancelled) { |
| DCHECK(dragging_panel_index_ != kInvalidPanelIndex); |
| |
| if (cancelled) { |
| Drag(dragging_panel_original_x_ - |
| active_panels_[dragging_panel_index_]->bounds().x()); |
| } else { |
| active_panels_[dragging_panel_index_]->SetBounds(dragging_panel_bounds_); |
| } |
| |
| dragging_panel_index_ = kInvalidPanelIndex; |
| |
| DelayedRemove(); |
| } |
| |
| void PanelManager::Rearrange(ActivePanels::iterator iter_to_start) { |
| if (iter_to_start == active_panels_.end()) |
| return; |
| |
| for (ActivePanels::iterator iter = iter_to_start; |
| iter != active_panels_.end(); ++iter) { |
| gfx::Rect new_bounds((*iter)->bounds()); |
| ComputeBoundsForNextPanel(&new_bounds, false); |
| if (new_bounds != (*iter)->bounds()) |
| (*iter)->SetBounds(new_bounds); |
| } |
| } |
| |
| bool PanelManager::ComputeBoundsForNextPanel(gfx::Rect* bounds, |
| bool allow_size_change) { |
| int width = bounds->width(); |
| int height = bounds->height(); |
| |
| // Update the width and/or height to fit into our constraint. |
| if (allow_size_change) { |
| if (width == 0 && height == 0) { |
| width = kPanelDefaultWidthPixels; |
| height = kPanelDefaultHeightPixels; |
| } |
| |
| if (width < kPanelMinWidthPixels) |
| width = kPanelMinWidthPixels; |
| else if (width > max_width_) |
| width = max_width_; |
| |
| if (height < kPanelMinHeightPixels) |
| height = kPanelMinHeightPixels; |
| else if (height > max_height_) |
| height = max_height_; |
| } |
| |
| int x = current_x_ - width; |
| int y = bottom_edge_y_ - height; |
| |
| if (x < min_x_) |
| return false; |
| |
| current_x_ -= width + kPanelsHorizontalSpacing; |
| |
| bounds->SetRect(x, y, width, height); |
| return true; |
| } |
| |
| void PanelManager::MinimizeAll() { |
| for (ActivePanels::const_iterator iter = active_panels_.begin(); |
| iter != active_panels_.end(); ++iter) { |
| (*iter)->Minimize(); |
| } |
| } |
| |
| void PanelManager::RestoreAll() { |
| for (ActivePanels::const_iterator iter = active_panels_.begin(); |
| iter != active_panels_.end(); ++iter) { |
| (*iter)->Restore(); |
| } |
| } |
| |
| void PanelManager::RemoveAllActive() { |
| // This should not be called when we're in the process of dragging. |
| DCHECK(dragging_panel_index_ == kInvalidPanelIndex); |
| |
| // Start from the bottom to avoid reshuffling. |
| for (int i = static_cast<int>(active_panels_.size()) -1; i >= 0; --i) |
| active_panels_[i]->Close(); |
| |
| ProcessPending(); |
| } |
| |
| bool PanelManager::AreAllMinimized() const { |
| for (ActivePanels::const_iterator iter = active_panels_.begin(); |
| iter != active_panels_.end(); ++iter) { |
| if (!(*iter)->minimized()) |
| return false; |
| } |
| return true; |
| } |