This source file includes following definitions.
- minimizing_all_
- OnDisplayChanged
- RefreshLayout
- RefreshLayoutWithTopPanelStartingAt
- GetTitle
- GetIcon
- PanelBoundsBatchUpdateCompleted
- GetEnclosingBounds
- MinimizePanelsForSpace
- AddPanel
- RemovePanel
- CloseAll
- OnPanelAttentionStateChanged
- OnPanelTitlebarClicked
- ResizePanelWindow
- ActivatePanel
- MinimizePanel
- RestorePanel
- MinimizeAll
- RestoreAll
- OnMinimizeButtonClicked
- OnRestoreButtonClicked
- CanShowMinimizeButton
- CanShowRestoreButton
- IsPanelMinimized
- UsesAlwaysOnTopPanels
- SavePanelPlacement
- RestorePanelToSavedPlacement
- DiscardSavedPanelPlacement
- GetPanelResizability
- OnPanelResizedByMouse
- HasPanel
- UpdatePanelOnCollectionChange
- OnPanelExpansionStateChanged
- OnPanelActiveStateChanged
- GetInitialPanelBounds
- GetPanelAbove
- GetPanelBelow
- MoveAllDraggingPanelsInstantly
- IsMinimized
- IsAnimatingPanelBounds
- UpdatePanelCornerStyle
- GetWorkArea
- GetCurrentAvailableTopSpace
- GetCurrentAvailableBottomSpace
- GetMaximiumAvailableBottomSpace
- GetStackWindowForPanel
#include "chrome/browser/ui/panels/stacked_panel_collection.h"
#include <algorithm>
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/panels/detached_panel_collection.h"
#include "chrome/browser/ui/panels/display_settings_provider.h"
#include "chrome/browser/ui/panels/native_panel_stack_window.h"
#include "chrome/browser/ui/panels/panel.h"
#include "chrome/browser/ui/panels/panel_constants.h"
#include "chrome/browser/ui/panels/panel_manager.h"
#include "extensions/common/extension.h"
StackedPanelCollection::StackedPanelCollection(PanelManager* panel_manager)
: PanelCollection(PanelCollection::STACKED),
panel_manager_(panel_manager),
primary_stack_window_(NULL),
secondary_stack_window_(NULL),
minimizing_all_(false) {
}
StackedPanelCollection::~StackedPanelCollection() {
DCHECK(panels_.empty());
DCHECK(most_recently_active_panels_.empty());
}
void StackedPanelCollection::OnDisplayChanged() {
if (panels_.empty())
return;
gfx::Rect enclosing_bounds = GetEnclosingBounds();
gfx::Rect work_area = panel_manager_->display_settings_provider()->
GetWorkAreaMatching(enclosing_bounds);
if (enclosing_bounds.height() > work_area.height()) {
int needed_space = enclosing_bounds.height() - work_area.height();
MinimizePanelsForSpace(needed_space);
}
gfx::Rect top_bounds = top_panel()->GetBounds();
int common_width = top_bounds.width();
if (common_width > work_area.width())
common_width = work_area.width();
int common_x = top_bounds.x();
if (common_x + common_width > work_area.right())
common_x = work_area.right() - common_width;
if (common_x < work_area.x())
common_x = work_area.x();
int total_height = bottom_panel()->GetBounds().bottom() - top_bounds.y();
int start_y = top_bounds.y();
if (start_y + total_height > work_area.bottom())
start_y = work_area.bottom() - total_height;
if (start_y < work_area.y())
start_y = work_area.y();
RefreshLayoutWithTopPanelStartingAt(
gfx::Point(common_x, start_y), common_width);
}
void StackedPanelCollection::RefreshLayout() {
if (panels_.empty())
return;
gfx::Rect top_bounds = top_panel()->GetBounds();
RefreshLayoutWithTopPanelStartingAt(top_bounds.origin(), top_bounds.width());
}
void StackedPanelCollection::RefreshLayoutWithTopPanelStartingAt(
const gfx::Point& start_position, int common_width) {
if (panels_.empty())
return;
if (panels_.size() == 1) {
Panel* panel = panels_.front();
gfx::Rect bounds = panel->GetBounds();
if (bounds.origin() != start_position) {
bounds.set_origin(start_position);
panel->SetPanelBounds(bounds);
}
return;
}
primary_stack_window_->BeginBatchUpdatePanelBounds(true);
if (secondary_stack_window_)
secondary_stack_window_->BeginBatchUpdatePanelBounds(true);
int y = start_position.y();
int common_x = start_position.x();
for (Panels::const_iterator iter = panels_.begin();
iter != panels_.end(); ++iter) {
Panel* panel = *iter;
panel->UpdateMinimizeRestoreButtonVisibility();
gfx::Rect bounds = panel->GetBounds();
if (panel->in_preview_mode()) {
y += bounds.height();
continue;
}
gfx::Size full_size = panel->full_size();
full_size.set_width(common_width);
panel->set_full_size(full_size);
bounds.SetRect(
common_x,
y,
common_width,
panel->expansion_state() == Panel::EXPANDED ?
panel->full_size().height() : panel->TitleOnlyHeight());
GetStackWindowForPanel(panel)->AddPanelBoundsForBatchUpdate(panel, bounds);
y += bounds.height();
}
primary_stack_window_->EndBatchUpdatePanelBounds();
if (secondary_stack_window_)
secondary_stack_window_->EndBatchUpdatePanelBounds();
}
base::string16 StackedPanelCollection::GetTitle() const {
if (panels_.empty())
return base::string16();
Panel* panel = panels_.front();
const extensions::Extension* extension = panel->GetExtension();
return base::UTF8ToUTF16(extension && !extension->name().empty() ?
extension->name() : panel->app_name());
}
gfx::Image StackedPanelCollection::GetIcon() const {
if (panels_.empty())
return gfx::Image();
return panels_.front()->app_icon();
}
void StackedPanelCollection::PanelBoundsBatchUpdateCompleted() {
if (!secondary_stack_window_ || panels_.empty())
return;
if (top_panel()->in_preview_mode() != bottom_panel()->in_preview_mode() ||
primary_stack_window_->IsAnimatingPanelBounds() ||
secondary_stack_window_->IsAnimatingPanelBounds())
return;
primary_stack_window_->MergeWith(secondary_stack_window_);
secondary_stack_window_->Close();
secondary_stack_window_ = NULL;
}
gfx::Rect StackedPanelCollection::GetEnclosingBounds() const {
gfx::Rect enclosing_bounds = top_panel()->GetBounds();
enclosing_bounds.set_height(
bottom_panel()->GetBounds().bottom() - enclosing_bounds.y());
return enclosing_bounds;
}
int StackedPanelCollection::MinimizePanelsForSpace(int needed_space) {
int available_space = GetCurrentAvailableBottomSpace();
Panel* active_panel = NULL;
if (!most_recently_active_panels_.empty()) {
Panel* most_recently_active_panel = most_recently_active_panels_.front();
if (most_recently_active_panel->IsActive())
active_panel = most_recently_active_panel;
}
for (Panels::const_reverse_iterator iter =
most_recently_active_panels_.rbegin();
iter != most_recently_active_panels_.rend() &&
available_space < needed_space;
++iter) {
Panel* current_panel = *iter;
if (current_panel != active_panel && !IsPanelMinimized(current_panel)) {
available_space +=
current_panel->GetBounds().height() - panel::kTitlebarHeight;
MinimizePanel(current_panel);
}
}
return available_space;
}
void StackedPanelCollection::AddPanel(Panel* panel,
PositioningMask positioning_mask) {
DCHECK_NE(this, panel->collection());
panel->set_collection(this);
Panel* adjacent_panel = NULL;
if (positioning_mask & PanelCollection::TOP_POSITION) {
adjacent_panel = top_panel();
panels_.push_front(panel);
} else {
if (positioning_mask & PanelCollection::COLLAPSE_TO_FIT) {
int needed_space = panel->GetBounds().height();
int available_space = MinimizePanelsForSpace(needed_space);
DCHECK(available_space >= needed_space);
}
adjacent_panel = bottom_panel();
panels_.push_back(panel);
}
if (panel->IsActive())
most_recently_active_panels_.push_front(panel);
else
most_recently_active_panels_.push_back(panel);
if (adjacent_panel)
UpdatePanelCornerStyle(adjacent_panel);
NativePanelStackWindow* stack_window;
if (top_panel()->in_preview_mode() == bottom_panel()->in_preview_mode() &&
top_panel()->GetBounds().x() == bottom_panel()->GetBounds().x() &&
top_panel()->GetBounds().width() == bottom_panel()->GetBounds().width()) {
if (!primary_stack_window_)
primary_stack_window_ = NativePanelStackWindow::Create(this);
stack_window = primary_stack_window_;
} else {
if (!secondary_stack_window_)
secondary_stack_window_ = NativePanelStackWindow::Create(this);
stack_window = secondary_stack_window_;
}
stack_window->AddPanel(panel);
if ((positioning_mask & NO_LAYOUT_REFRESH) == 0)
RefreshLayout();
}
void StackedPanelCollection::RemovePanel(Panel* panel, RemovalReason reason) {
bool is_top = panel == top_panel();
bool is_bottom = panel == bottom_panel();
bool top_panel_closed = false;
gfx::Point top_origin;
int top_width = 0;
if (reason == PanelCollection::PANEL_CLOSED && is_top) {
top_panel_closed = true;
top_origin = panel->GetBounds().origin();
top_width = panel->GetBounds().width();
}
panel->set_collection(NULL);
panels_.remove(panel);
most_recently_active_panels_.remove(panel);
if (is_top) {
Panel* new_top_panel = top_panel();
if (new_top_panel)
UpdatePanelCornerStyle(new_top_panel);
} else if (is_bottom) {
Panel* new_bottom_panel = bottom_panel();
if (new_bottom_panel)
UpdatePanelCornerStyle(new_bottom_panel);
}
if (reason == PanelCollection::PANEL_CLOSED &&
panel->IsActive() &&
!most_recently_active_panels_.empty()) {
for (Panels::const_iterator iter = most_recently_active_panels_.begin();
iter != most_recently_active_panels_.end(); ++iter) {
Panel* other_panel = *iter;
if (!IsPanelMinimized(other_panel)) {
other_panel->Activate();
break;
}
}
}
if (top_panel_closed)
RefreshLayoutWithTopPanelStartingAt(top_origin, top_width);
else if (reason == PanelCollection::PANEL_CLOSED)
RefreshLayout();
GetStackWindowForPanel(panel)->RemovePanel(panel);
if (secondary_stack_window_ && secondary_stack_window_->IsEmpty()) {
secondary_stack_window_->Close();
secondary_stack_window_ = NULL;
}
}
void StackedPanelCollection::CloseAll() {
Panels panels_copy = panels_;
for (Panels::const_iterator iter = panels_copy.begin();
iter != panels_copy.end(); ++iter)
(*iter)->Close();
if (primary_stack_window_) {
primary_stack_window_->Close();
primary_stack_window_ = NULL;
}
if (secondary_stack_window_) {
secondary_stack_window_->Close();
secondary_stack_window_ = NULL;
}
}
void StackedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) {
if ((panel->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0)
primary_stack_window_->DrawSystemAttention(panel->IsDrawingAttention());
}
void StackedPanelCollection::OnPanelTitlebarClicked(
Panel* panel, panel::ClickModifier modifier) {
bool expanded = panel->expansion_state() == Panel::EXPANDED;
if (modifier == panel::APPLY_TO_ALL) {
if (expanded)
MinimizeAll();
else
RestoreAll(panel);
} else {
if (expanded)
MinimizePanel(panel);
else
RestorePanel(panel);
}
}
void StackedPanelCollection::ResizePanelWindow(
Panel* panel,
const gfx::Size& preferred_window_size) {
}
void StackedPanelCollection::ActivatePanel(Panel* panel) {
if (panel->IsMinimized())
panel->SetExpansionState(Panel::EXPANDED);
}
void StackedPanelCollection::MinimizePanel(Panel* panel) {
panel->SetExpansionState(Panel::TITLE_ONLY);
}
void StackedPanelCollection::RestorePanel(Panel* panel) {
int needed_space = panel->full_size().height() - panel->TitleOnlyHeight();
int available_space = MinimizePanelsForSpace(needed_space);
int space_beyond_available = needed_space - available_space;
if (space_beyond_available > 0) {
int top_available_space = GetCurrentAvailableTopSpace();
int move_delta = (space_beyond_available > top_available_space) ?
top_available_space : space_beyond_available;
for (Panels::const_iterator iter = panels_.begin();
iter != panels_.end(); iter++) {
Panel* current_panel = *iter;
gfx::Rect bounds = current_panel->GetBounds();
bounds.set_y(bounds.y() - move_delta);
current_panel->SetPanelBounds(bounds);
}
available_space += move_delta;
}
space_beyond_available = needed_space - available_space;
if (space_beyond_available > 0) {
gfx::Size full_size = panel->full_size();
int reduced_height = full_size.height() - space_beyond_available;
if (reduced_height < panel::kPanelMinHeight)
reduced_height = panel::kPanelMinHeight;
full_size.set_height(reduced_height);
panel->set_full_size(full_size);
}
panel->SetExpansionState(Panel::EXPANDED);
}
void StackedPanelCollection::MinimizeAll() {
base::AutoReset<bool> pin(&minimizing_all_, true);
Panel* minimized_active_panel = NULL;
for (Panels::const_iterator iter = panels_.begin();
iter != panels_.end(); ++iter) {
if ((*iter)->IsActive())
minimized_active_panel = *iter;
MinimizePanel(*iter);
}
if (minimized_active_panel) {
minimized_active_panel->Deactivate();
}
}
void StackedPanelCollection::RestoreAll(Panel* panel_clicked) {
RestorePanel(panel_clicked);
for (Panels::const_iterator iter = most_recently_active_panels_.begin();
iter != most_recently_active_panels_.end(); ++iter) {
if (GetCurrentAvailableTopSpace() == 0 &&
GetCurrentAvailableBottomSpace() == 0) {
break;
}
Panel* panel = *iter;
if (panel != panel_clicked)
RestorePanel(panel);
}
}
void StackedPanelCollection::OnMinimizeButtonClicked(
Panel* panel, panel::ClickModifier modifier) {
DCHECK_EQ(top_panel(), panel);
primary_stack_window_->Minimize();
}
void StackedPanelCollection::OnRestoreButtonClicked(
Panel* panel, panel::ClickModifier modifier) {
NOTREACHED();
}
bool StackedPanelCollection::CanShowMinimizeButton(const Panel* panel) const {
return PanelManager::CanUseSystemMinimize() && panel == top_panel();
}
bool StackedPanelCollection::CanShowRestoreButton(const Panel* panel) const {
return false;
}
bool StackedPanelCollection::IsPanelMinimized(const Panel* panel) const {
return panel->expansion_state() != Panel::EXPANDED;
}
bool StackedPanelCollection::UsesAlwaysOnTopPanels() const {
return false;
}
void StackedPanelCollection::SavePanelPlacement(Panel* panel) {
DCHECK(!saved_panel_placement_.panel);
saved_panel_placement_.panel = panel;
if (top_panel() != panel)
saved_panel_placement_.top_panel = top_panel();
else
saved_panel_placement_.position = panel->GetBounds().origin();
saved_panel_placement_.top_panel = top_panel() != panel ? top_panel() : NULL;
}
void StackedPanelCollection::RestorePanelToSavedPlacement() {
DCHECK(saved_panel_placement_.panel);
if (saved_panel_placement_.top_panel) {
if (saved_panel_placement_.top_panel->stack() != this) {
DCHECK_EQ(PanelCollection::DETACHED,
saved_panel_placement_.top_panel->collection()->type());
panel_manager_->MovePanelToCollection(
saved_panel_placement_.top_panel,
this,
static_cast<PanelCollection::PositioningMask>(
PanelCollection::TOP_POSITION |
PanelCollection::NO_LAYOUT_REFRESH));
}
RefreshLayout();
} else {
DCHECK_EQ(top_panel(), saved_panel_placement_.panel);
RefreshLayoutWithTopPanelStartingAt(saved_panel_placement_.position,
top_panel()->GetBounds().width());
}
DiscardSavedPanelPlacement();
}
void StackedPanelCollection::DiscardSavedPanelPlacement() {
DCHECK(saved_panel_placement_.panel);
saved_panel_placement_.panel = NULL;
saved_panel_placement_.top_panel = NULL;
}
panel::Resizability StackedPanelCollection::GetPanelResizability(
const Panel* panel) const {
panel::Resizability resizability = static_cast<panel::Resizability>(
panel::RESIZABLE_LEFT | panel::RESIZABLE_RIGHT);
if (panel->IsMinimized())
return resizability;
if (panel == top_panel()) {
resizability = static_cast<panel::Resizability>(resizability |
panel::RESIZABLE_TOP | panel::RESIZABLE_TOP_LEFT |
panel::RESIZABLE_TOP_RIGHT);
}
if (panel == bottom_panel()) {
resizability = static_cast<panel::Resizability>(resizability |
panel::RESIZABLE_BOTTOM | panel::RESIZABLE_BOTTOM_LEFT |
panel::RESIZABLE_BOTTOM_RIGHT);
} else {
resizability = static_cast<panel::Resizability>(resizability |
panel::RESIZABLE_BOTTOM);
}
return resizability;
}
void StackedPanelCollection::OnPanelResizedByMouse(
Panel* resized_panel, const gfx::Rect& new_bounds) {
resized_panel->set_full_size(new_bounds.size());
DCHECK(!secondary_stack_window_);
primary_stack_window_->BeginBatchUpdatePanelBounds(false);
Panel* other_panel = resized_panel == top_panel() ? bottom_panel()
: top_panel();
gfx::Rect other_bounds = other_panel->GetBounds();
int delta_x = new_bounds.x() - other_bounds.x();
int delta_width = new_bounds.width() - other_bounds.width();
gfx::Rect previous_bounds;
bool resized_panel_found = false;
bool panel_below_resized_panel_updated = false;
for (Panels::const_iterator iter = panels_.begin();
iter != panels_.end(); iter++) {
Panel* panel = *iter;
if (panel == resized_panel) {
previous_bounds = new_bounds;
resized_panel_found = true;
primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, new_bounds);
continue;
}
gfx::Rect bounds = panel->GetBounds();
bounds.set_x(bounds.x() + delta_x);
bounds.set_width(bounds.width() + delta_width);
if (resized_panel_found) {
if (!panel_below_resized_panel_updated && !panel->IsMinimized()) {
int old_bottom = bounds.bottom();
bounds.set_y(previous_bounds.bottom());
bounds.set_height(old_bottom - bounds.y());
} else {
bounds.set_y(previous_bounds.bottom());
}
panel_below_resized_panel_updated = true;
}
if (!panel->IsMinimized())
panel->set_full_size(bounds.size());
primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, bounds);
previous_bounds = bounds;
}
primary_stack_window_->EndBatchUpdatePanelBounds();
}
bool StackedPanelCollection::HasPanel(Panel* panel) const {
return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
}
void StackedPanelCollection::UpdatePanelOnCollectionChange(Panel* panel) {
panel->set_attention_mode(
static_cast<Panel::AttentionMode>(Panel::USE_PANEL_ATTENTION |
Panel::USE_SYSTEM_ATTENTION));
panel->ShowShadow(false);
panel->UpdateMinimizeRestoreButtonVisibility();
UpdatePanelCornerStyle(panel);
}
void StackedPanelCollection::OnPanelExpansionStateChanged(Panel* panel) {
DCHECK_NE(Panel::MINIMIZED, panel->expansion_state());
if (panel->expansion_state() != Panel::EXPANDED && !minimizing_all_ &&
panel->IsActive()) {
panel->Deactivate();
}
RefreshLayout();
}
void StackedPanelCollection::OnPanelActiveStateChanged(Panel* panel) {
if (!panel->IsActive())
return;
Panels::iterator iter = std::find(most_recently_active_panels_.begin(),
most_recently_active_panels_.end(), panel);
DCHECK(iter != most_recently_active_panels_.end());
if (iter != most_recently_active_panels_.begin()) {
most_recently_active_panels_.erase(iter);
most_recently_active_panels_.push_front(panel);
}
GetStackWindowForPanel(panel)->OnPanelActivated(panel);
}
gfx::Rect StackedPanelCollection::GetInitialPanelBounds(
const gfx::Rect& requested_bounds) const {
DCHECK(!panels_.empty());
gfx::Rect bottom_panel_bounds = bottom_panel()->GetBounds();
return gfx::Rect(bottom_panel_bounds.x(),
bottom_panel_bounds.bottom(),
bottom_panel_bounds.width(),
requested_bounds.height());
}
Panel* StackedPanelCollection::GetPanelAbove(Panel* panel) const {
DCHECK(panel);
if (panels_.size() < 2)
return NULL;
Panels::const_iterator iter = panels_.begin();
Panel* above_panel = *iter;
for (; iter != panels_.end(); ++iter) {
if (*iter == panel)
return above_panel;
above_panel = *iter;
}
return NULL;
}
Panel* StackedPanelCollection::GetPanelBelow(Panel* panel) const {
DCHECK(panel);
if (panels_.size() < 2)
return NULL;
Panels::const_iterator iter =
std::find(panels_.begin(), panels_.end(), panel);
if (iter == panels_.end())
return NULL;
++iter;
return iter == panels_.end() ? NULL : *iter;
}
void StackedPanelCollection::MoveAllDraggingPanelsInstantly(
const gfx::Vector2d& delta_origin) {
for (Panels::const_iterator iter = panels_.begin();
iter != panels_.end(); iter++) {
Panel* panel = *iter;
if (panel->in_preview_mode()) {
GetStackWindowForPanel(panel)->MovePanelsBy(delta_origin);
return;
}
}
}
bool StackedPanelCollection::IsMinimized() const {
return primary_stack_window_->IsMinimized();
}
bool StackedPanelCollection::IsAnimatingPanelBounds(Panel* panel) const {
return GetStackWindowForPanel(panel)->IsAnimatingPanelBounds();
}
void StackedPanelCollection::UpdatePanelCornerStyle(Panel* panel) {
panel::CornerStyle corner_style;
bool at_top = panel == top_panel();
bool at_bottom = panel == bottom_panel();
if (at_top && at_bottom)
corner_style = panel::ALL_ROUNDED;
else if (at_top)
corner_style = panel::TOP_ROUNDED;
else if (at_bottom)
corner_style = panel::BOTTOM_ROUNDED;
else
corner_style = panel::NOT_ROUNDED;
panel->SetWindowCornerStyle(corner_style);
}
gfx::Rect StackedPanelCollection::GetWorkArea() const {
if (panels_.empty())
return panel_manager_->display_settings_provider()->GetPrimaryWorkArea();
return panel_manager_->display_settings_provider()->GetWorkAreaMatching(
GetEnclosingBounds());
}
int StackedPanelCollection::GetCurrentAvailableTopSpace() const {
gfx::Rect work_area = GetWorkArea();
if (panels_.empty())
return work_area.height();
int available_space = top_panel()->GetBounds().y() - work_area.y();
if (available_space < 0)
available_space = 0;
return available_space;
}
int StackedPanelCollection::GetCurrentAvailableBottomSpace() const {
gfx::Rect work_area = GetWorkArea();
if (panels_.empty())
return work_area.height();
int available_space = work_area.bottom() -
bottom_panel()->GetBounds().bottom();
if (available_space < 0)
available_space = 0;
return available_space;
}
int StackedPanelCollection::GetMaximiumAvailableBottomSpace() const {
gfx::Rect work_area = GetWorkArea();
if (panels_.empty())
return work_area.height();
int bottom = top_panel()->GetBounds().y();
for (Panels::const_iterator iter = panels_.begin();
iter != panels_.end(); iter++) {
Panel* panel = *iter;
if (iter == panels_.begin() && panel->IsActive())
bottom += panel->GetBounds().height();
else
bottom += panel::kTitlebarHeight;
}
int available_space = work_area.bottom() - bottom;
if (available_space < 0)
available_space = 0;
return available_space;
}
NativePanelStackWindow* StackedPanelCollection::GetStackWindowForPanel(
Panel* panel) const {
return secondary_stack_window_ && secondary_stack_window_->HasPanel(panel) ?
secondary_stack_window_ : primary_stack_window_;
}