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/stacked_panel_collection.h" 6 7 #include <algorithm> 8 #include "base/auto_reset.h" 9 #include "base/logging.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "chrome/browser/ui/panels/detached_panel_collection.h" 12 #include "chrome/browser/ui/panels/display_settings_provider.h" 13 #include "chrome/browser/ui/panels/native_panel_stack_window.h" 14 #include "chrome/browser/ui/panels/panel.h" 15 #include "chrome/browser/ui/panels/panel_constants.h" 16 #include "chrome/browser/ui/panels/panel_manager.h" 17 #include "extensions/common/extension.h" 18 19 StackedPanelCollection::StackedPanelCollection(PanelManager* panel_manager) 20 : PanelCollection(PanelCollection::STACKED), 21 panel_manager_(panel_manager), 22 primary_stack_window_(NULL), 23 secondary_stack_window_(NULL), 24 minimizing_all_(false) { 25 } 26 27 StackedPanelCollection::~StackedPanelCollection() { 28 DCHECK(panels_.empty()); 29 DCHECK(most_recently_active_panels_.empty()); 30 } 31 32 void StackedPanelCollection::OnDisplayChanged() { 33 if (panels_.empty()) 34 return; 35 36 gfx::Rect enclosing_bounds = GetEnclosingBounds(); 37 gfx::Rect work_area = panel_manager_->display_settings_provider()-> 38 GetWorkAreaMatching(enclosing_bounds); 39 40 // If the height of the whole stack is bigger than the height of the new work 41 // area, try to reduce the stack height by collapsing panels. In rare case, 42 // all panels are collapsed and there is still not enough space. We simply 43 // let the stack go beyond the work area limit. 44 if (enclosing_bounds.height() > work_area.height()) { 45 int needed_space = enclosing_bounds.height() - work_area.height(); 46 MinimizePanelsForSpace(needed_space); 47 } 48 49 gfx::Rect top_bounds = top_panel()->GetBounds(); 50 int common_width = top_bounds.width(); 51 if (common_width > work_area.width()) 52 common_width = work_area.width(); 53 54 int common_x = top_bounds.x(); 55 if (common_x + common_width > work_area.right()) 56 common_x = work_area.right() - common_width; 57 if (common_x < work_area.x()) 58 common_x = work_area.x(); 59 60 int total_height = bottom_panel()->GetBounds().bottom() - top_bounds.y(); 61 int start_y = top_bounds.y(); 62 if (start_y + total_height > work_area.bottom()) 63 start_y = work_area.bottom() - total_height; 64 if (start_y < work_area.y()) 65 start_y = work_area.y(); 66 67 RefreshLayoutWithTopPanelStartingAt( 68 gfx::Point(common_x, start_y), common_width); 69 } 70 71 void StackedPanelCollection::RefreshLayout() { 72 if (panels_.empty()) 73 return; 74 gfx::Rect top_bounds = top_panel()->GetBounds(); 75 RefreshLayoutWithTopPanelStartingAt(top_bounds.origin(), top_bounds.width()); 76 } 77 78 void StackedPanelCollection::RefreshLayoutWithTopPanelStartingAt( 79 const gfx::Point& start_position, int common_width) { 80 if (panels_.empty()) 81 return; 82 83 // If only one panel is left in the stack, we only need to check if it should 84 // be moved to |start_y| position. 85 if (panels_.size() == 1) { 86 Panel* panel = panels_.front(); 87 gfx::Rect bounds = panel->GetBounds(); 88 if (bounds.origin() != start_position) { 89 bounds.set_origin(start_position); 90 panel->SetPanelBounds(bounds); 91 } 92 return; 93 } 94 95 // We do not update bounds for affected panels one by one. Instead, all 96 // changes are bundled and performed synchronously. 97 primary_stack_window_->BeginBatchUpdatePanelBounds(true); 98 if (secondary_stack_window_) 99 secondary_stack_window_->BeginBatchUpdatePanelBounds(true); 100 101 int y = start_position.y(); 102 int common_x = start_position.x(); 103 for (Panels::const_iterator iter = panels_.begin(); 104 iter != panels_.end(); ++iter) { 105 Panel* panel = *iter; 106 107 // The visibility of minimize button might need to be updated due to that 108 // top panel might change when a panel is being added or removed from 109 // the stack. 110 panel->UpdateMinimizeRestoreButtonVisibility(); 111 112 // Don't update the stacked panel that is in preview mode. 113 gfx::Rect bounds = panel->GetBounds(); 114 if (panel->in_preview_mode()) { 115 y += bounds.height(); 116 continue; 117 } 118 119 // Update the restored size. 120 gfx::Size full_size = panel->full_size(); 121 full_size.set_width(common_width); 122 panel->set_full_size(full_size); 123 124 // Recompute the bounds. 125 bounds.SetRect( 126 common_x, 127 y, 128 common_width, 129 panel->expansion_state() == Panel::EXPANDED ? 130 panel->full_size().height() : panel->TitleOnlyHeight()); 131 132 GetStackWindowForPanel(panel)->AddPanelBoundsForBatchUpdate(panel, bounds); 133 134 y += bounds.height(); 135 } 136 137 primary_stack_window_->EndBatchUpdatePanelBounds(); 138 if (secondary_stack_window_) 139 secondary_stack_window_->EndBatchUpdatePanelBounds(); 140 } 141 142 base::string16 StackedPanelCollection::GetTitle() const { 143 if (panels_.empty()) 144 return base::string16(); 145 146 Panel* panel = panels_.front(); 147 const extensions::Extension* extension = panel->GetExtension(); 148 return UTF8ToUTF16(extension && !extension->name().empty() ? 149 extension->name() : panel->app_name()); 150 } 151 152 gfx::Image StackedPanelCollection::GetIcon() const { 153 if (panels_.empty()) 154 return gfx::Image(); 155 return panels_.front()->app_icon(); 156 } 157 158 void StackedPanelCollection::PanelBoundsBatchUpdateCompleted() { 159 if (!secondary_stack_window_ || panels_.empty()) 160 return; 161 162 if (top_panel()->in_preview_mode() != bottom_panel()->in_preview_mode() || 163 primary_stack_window_->IsAnimatingPanelBounds() || 164 secondary_stack_window_->IsAnimatingPanelBounds()) 165 return; 166 167 // Move all panels from secondary stack window to primary stack window. 168 primary_stack_window_->MergeWith(secondary_stack_window_); 169 secondary_stack_window_->Close(); 170 secondary_stack_window_ = NULL; 171 } 172 173 gfx::Rect StackedPanelCollection::GetEnclosingBounds() const { 174 gfx::Rect enclosing_bounds = top_panel()->GetBounds(); 175 enclosing_bounds.set_height( 176 bottom_panel()->GetBounds().bottom() - enclosing_bounds.y()); 177 return enclosing_bounds; 178 } 179 180 int StackedPanelCollection::MinimizePanelsForSpace(int needed_space) { 181 int available_space = GetCurrentAvailableBottomSpace(); 182 183 // Only the most recently active panel might be active. 184 Panel* active_panel = NULL; 185 if (!most_recently_active_panels_.empty()) { 186 Panel* most_recently_active_panel = most_recently_active_panels_.front(); 187 if (most_recently_active_panel->IsActive()) 188 active_panel = most_recently_active_panel; 189 } 190 191 for (Panels::const_reverse_iterator iter = 192 most_recently_active_panels_.rbegin(); 193 iter != most_recently_active_panels_.rend() && 194 available_space < needed_space; 195 ++iter) { 196 Panel* current_panel = *iter; 197 if (current_panel != active_panel && !IsPanelMinimized(current_panel)) { 198 available_space += 199 current_panel->GetBounds().height() - panel::kTitlebarHeight; 200 MinimizePanel(current_panel); 201 } 202 } 203 return available_space; 204 } 205 206 void StackedPanelCollection::AddPanel(Panel* panel, 207 PositioningMask positioning_mask) { 208 DCHECK_NE(this, panel->collection()); 209 panel->set_collection(this); 210 Panel* adjacent_panel = NULL; 211 if (positioning_mask & PanelCollection::TOP_POSITION) { 212 adjacent_panel = top_panel(); 213 panels_.push_front(panel); 214 } else { 215 // To fit the new panel within the working area, collapse unfocused panels 216 // in the least recent active order until there is enough space. 217 if (positioning_mask & PanelCollection::COLLAPSE_TO_FIT) { 218 int needed_space = panel->GetBounds().height(); 219 int available_space = MinimizePanelsForSpace(needed_space); 220 DCHECK(available_space >= needed_space); 221 } 222 223 adjacent_panel = bottom_panel(); 224 panels_.push_back(panel); 225 } 226 227 if (panel->IsActive()) 228 most_recently_active_panels_.push_front(panel); 229 else 230 most_recently_active_panels_.push_back(panel); 231 232 if (adjacent_panel) 233 UpdatePanelCornerStyle(adjacent_panel); 234 235 // The secondary stack window should be used when one of the following occurs: 236 // 1) Some panels but not all panels are being dragged. This is because 237 // those panels being dragged might not be fully aligned with other panels 238 // not being dragged. 239 // 2) The newly added panel is not fully aligned with the existing panel, in 240 // terms of both x and width. 241 NativePanelStackWindow* stack_window; 242 if (top_panel()->in_preview_mode() == bottom_panel()->in_preview_mode() && 243 top_panel()->GetBounds().x() == bottom_panel()->GetBounds().x() && 244 top_panel()->GetBounds().width() == bottom_panel()->GetBounds().width()) { 245 if (!primary_stack_window_) 246 primary_stack_window_ = NativePanelStackWindow::Create(this); 247 stack_window = primary_stack_window_; 248 } else { 249 if (!secondary_stack_window_) 250 secondary_stack_window_ = NativePanelStackWindow::Create(this); 251 stack_window = secondary_stack_window_; 252 } 253 stack_window->AddPanel(panel); 254 255 if ((positioning_mask & NO_LAYOUT_REFRESH) == 0) 256 RefreshLayout(); 257 } 258 259 void StackedPanelCollection::RemovePanel(Panel* panel, RemovalReason reason) { 260 bool is_top = panel == top_panel(); 261 bool is_bottom = panel == bottom_panel(); 262 263 // If the top panel is being closed, all panels below it should move up. To 264 // do this, the top y position of top panel needs to be tracked first. 265 bool top_panel_closed = false; 266 gfx::Point top_origin; 267 int top_width = 0; 268 if (reason == PanelCollection::PANEL_CLOSED && is_top) { 269 top_panel_closed = true; 270 top_origin = panel->GetBounds().origin(); 271 top_width = panel->GetBounds().width(); 272 } 273 274 panel->set_collection(NULL); 275 panels_.remove(panel); 276 most_recently_active_panels_.remove(panel); 277 278 if (is_top) { 279 Panel* new_top_panel = top_panel(); 280 if (new_top_panel) 281 UpdatePanelCornerStyle(new_top_panel); 282 } else if (is_bottom) { 283 Panel* new_bottom_panel = bottom_panel(); 284 if (new_bottom_panel) 285 UpdatePanelCornerStyle(new_bottom_panel); 286 } 287 288 // If an active panel is being closed, try to focus the next recently active 289 // panel in the stack that is not minimized. 290 if (reason == PanelCollection::PANEL_CLOSED && 291 panel->IsActive() && 292 !most_recently_active_panels_.empty()) { 293 for (Panels::const_iterator iter = most_recently_active_panels_.begin(); 294 iter != most_recently_active_panels_.end(); ++iter) { 295 Panel* other_panel = *iter; 296 if (!IsPanelMinimized(other_panel)) { 297 other_panel->Activate(); 298 break; 299 } 300 } 301 } 302 303 // If the top panel is closed, move up all other panels to stay at the same 304 // y position as the top panel being closed. 305 if (top_panel_closed) 306 RefreshLayoutWithTopPanelStartingAt(top_origin, top_width); 307 else if (reason == PanelCollection::PANEL_CLOSED) 308 RefreshLayout(); 309 310 // Remove the panel from the corresponding stack window. 311 GetStackWindowForPanel(panel)->RemovePanel(panel); 312 313 // Close the secondary stack window if no panel is is shown inside it. 314 // Note that we do not need to do this for primary stack window since the 315 // whole stack will be gone when only one panel is left. 316 if (secondary_stack_window_ && secondary_stack_window_->IsEmpty()) { 317 secondary_stack_window_->Close(); 318 secondary_stack_window_ = NULL; 319 } 320 } 321 322 void StackedPanelCollection::CloseAll() { 323 // Make a copy as closing panels can modify the iterator. 324 Panels panels_copy = panels_; 325 326 for (Panels::const_iterator iter = panels_copy.begin(); 327 iter != panels_copy.end(); ++iter) 328 (*iter)->Close(); 329 330 if (primary_stack_window_) { 331 primary_stack_window_->Close(); 332 primary_stack_window_ = NULL; 333 } 334 if (secondary_stack_window_) { 335 secondary_stack_window_->Close(); 336 secondary_stack_window_ = NULL; 337 } 338 } 339 340 void StackedPanelCollection::OnPanelAttentionStateChanged(Panel* panel) { 341 if ((panel->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) 342 primary_stack_window_->DrawSystemAttention(panel->IsDrawingAttention()); 343 } 344 345 void StackedPanelCollection::OnPanelTitlebarClicked( 346 Panel* panel, panel::ClickModifier modifier) { 347 bool expanded = panel->expansion_state() == Panel::EXPANDED; 348 if (modifier == panel::APPLY_TO_ALL) { 349 if (expanded) 350 MinimizeAll(); 351 else 352 RestoreAll(panel); 353 } else { 354 if (expanded) 355 MinimizePanel(panel); 356 else 357 RestorePanel(panel); 358 } 359 } 360 361 void StackedPanelCollection::ResizePanelWindow( 362 Panel* panel, 363 const gfx::Size& preferred_window_size) { 364 } 365 366 void StackedPanelCollection::ActivatePanel(Panel* panel) { 367 // Make sure the panel is expanded when activated so the user input 368 // does not go into a collapsed window. 369 if (panel->IsMinimized()) 370 panel->SetExpansionState(Panel::EXPANDED); 371 } 372 373 void StackedPanelCollection::MinimizePanel(Panel* panel) { 374 panel->SetExpansionState(Panel::TITLE_ONLY); 375 } 376 377 void StackedPanelCollection::RestorePanel(Panel* panel) { 378 // Ensure that the panel could fit within the work area after it is expanded. 379 // First, try to collapse the unfocused panel in the least recent active 380 // order in order to get enough space. 381 int needed_space = panel->full_size().height() - panel->TitleOnlyHeight(); 382 int available_space = MinimizePanelsForSpace(needed_space); 383 384 // If there is still not enough space, try to move up the stack. 385 int space_beyond_available = needed_space - available_space; 386 if (space_beyond_available > 0) { 387 int top_available_space = GetCurrentAvailableTopSpace(); 388 int move_delta = (space_beyond_available > top_available_space) ? 389 top_available_space : space_beyond_available; 390 for (Panels::const_iterator iter = panels_.begin(); 391 iter != panels_.end(); iter++) { 392 Panel* current_panel = *iter; 393 gfx::Rect bounds = current_panel->GetBounds(); 394 bounds.set_y(bounds.y() - move_delta); 395 current_panel->SetPanelBounds(bounds); 396 } 397 available_space += move_delta; 398 } 399 400 // If there is still not enough space, shrink the restored height to make it 401 // fit at the last resort. Note that the restored height cannot be shrunk less 402 // than the minimum panel height. If this is the case, we will just let it 403 // expand beyond the screen boundary. 404 space_beyond_available = needed_space - available_space; 405 if (space_beyond_available > 0) { 406 gfx::Size full_size = panel->full_size(); 407 int reduced_height = full_size.height() - space_beyond_available; 408 if (reduced_height < panel::kPanelMinHeight) 409 reduced_height = panel::kPanelMinHeight; 410 full_size.set_height(reduced_height); 411 panel->set_full_size(full_size); 412 } 413 414 panel->SetExpansionState(Panel::EXPANDED); 415 } 416 417 void StackedPanelCollection::MinimizeAll() { 418 // Set minimizing_all_ to prevent deactivation of each panel when it 419 // is minimized. See comments in OnPanelExpansionStateChanged. 420 base::AutoReset<bool> pin(&minimizing_all_, true); 421 Panel* minimized_active_panel = NULL; 422 for (Panels::const_iterator iter = panels_.begin(); 423 iter != panels_.end(); ++iter) { 424 if ((*iter)->IsActive()) 425 minimized_active_panel = *iter; 426 MinimizePanel(*iter); 427 } 428 429 // When a single panel is minimized, it is deactivated to ensure that 430 // a minimized panel does not have focus. However, when minimizing all, 431 // the deactivation is only done once after all panels are minimized, 432 // rather than per minimized panel, both for efficiency and to avoid 433 // temporary activations of random not-yet-minimized panels. 434 if (minimized_active_panel) { 435 minimized_active_panel->Deactivate(); 436 // Layout will be refreshed in response to (de)activation notification. 437 } 438 } 439 440 void StackedPanelCollection::RestoreAll(Panel* panel_clicked) { 441 // Expand the panel being clicked first. This is to make sure at least one 442 // panel that is clicked by the user will be expanded. 443 RestorePanel(panel_clicked); 444 445 // Try to expand all other panels starting from the most recently active 446 // panel. 447 for (Panels::const_iterator iter = most_recently_active_panels_.begin(); 448 iter != most_recently_active_panels_.end(); ++iter) { 449 // If the stack already extends to both top and bottom of the work area, 450 // stop now since we cannot fit any more expanded panels. 451 if (GetCurrentAvailableTopSpace() == 0 && 452 GetCurrentAvailableBottomSpace() == 0) { 453 break; 454 } 455 456 Panel* panel = *iter; 457 if (panel != panel_clicked) 458 RestorePanel(panel); 459 } 460 } 461 462 void StackedPanelCollection::OnMinimizeButtonClicked( 463 Panel* panel, panel::ClickModifier modifier) { 464 // The minimize button is only present in the top panel. 465 DCHECK_EQ(top_panel(), panel); 466 467 primary_stack_window_->Minimize(); 468 } 469 470 void StackedPanelCollection::OnRestoreButtonClicked( 471 Panel* panel, panel::ClickModifier modifier) { 472 NOTREACHED(); 473 } 474 475 bool StackedPanelCollection::CanShowMinimizeButton(const Panel* panel) const { 476 // Only the top panel in the stack shows the minimize button. 477 return PanelManager::CanUseSystemMinimize() && panel == top_panel(); 478 } 479 480 bool StackedPanelCollection::CanShowRestoreButton(const Panel* panel) const { 481 return false; 482 } 483 484 bool StackedPanelCollection::IsPanelMinimized(const Panel* panel) const { 485 return panel->expansion_state() != Panel::EXPANDED; 486 } 487 488 bool StackedPanelCollection::UsesAlwaysOnTopPanels() const { 489 return false; 490 } 491 492 void StackedPanelCollection::SavePanelPlacement(Panel* panel) { 493 DCHECK(!saved_panel_placement_.panel); 494 saved_panel_placement_.panel = panel; 495 496 if (top_panel() != panel) 497 saved_panel_placement_.top_panel = top_panel(); 498 else 499 saved_panel_placement_.position = panel->GetBounds().origin(); 500 501 saved_panel_placement_.top_panel = top_panel() != panel ? top_panel() : NULL; 502 } 503 504 void StackedPanelCollection::RestorePanelToSavedPlacement() { 505 DCHECK(saved_panel_placement_.panel); 506 507 if (saved_panel_placement_.top_panel) { 508 // Restore the top panel if it has been moved out of the stack. This could 509 // happen when there're 2 panels in the stack and the bottom panel is being 510 // dragged out of the stack and thus cause both panels become detached. 511 if (saved_panel_placement_.top_panel->stack() != this) { 512 DCHECK_EQ(PanelCollection::DETACHED, 513 saved_panel_placement_.top_panel->collection()->type()); 514 panel_manager_->MovePanelToCollection( 515 saved_panel_placement_.top_panel, 516 this, 517 static_cast<PanelCollection::PositioningMask>( 518 PanelCollection::TOP_POSITION | 519 PanelCollection::NO_LAYOUT_REFRESH)); 520 } 521 RefreshLayout(); 522 } else { 523 // Restore the position when the top panel is being dragged. 524 DCHECK_EQ(top_panel(), saved_panel_placement_.panel); 525 RefreshLayoutWithTopPanelStartingAt(saved_panel_placement_.position, 526 top_panel()->GetBounds().width()); 527 } 528 529 DiscardSavedPanelPlacement(); 530 } 531 532 void StackedPanelCollection::DiscardSavedPanelPlacement() { 533 DCHECK(saved_panel_placement_.panel); 534 saved_panel_placement_.panel = NULL; 535 saved_panel_placement_.top_panel = NULL; 536 } 537 538 panel::Resizability StackedPanelCollection::GetPanelResizability( 539 const Panel* panel) const { 540 // The panel in the stack can be resized by the following rules: 541 // * If collapsed, it can only be resized by its left or right edge. 542 // * Otherwise, it can be resized by its left or right edge plus: 543 // % top edge and corners, if it is at the top; 544 // % bottom edge, if it is not at the bottom. 545 // % bottom edge and corners, if it is at the bottom. 546 panel::Resizability resizability = static_cast<panel::Resizability>( 547 panel::RESIZABLE_LEFT | panel::RESIZABLE_RIGHT); 548 if (panel->IsMinimized()) 549 return resizability; 550 if (panel == top_panel()) { 551 resizability = static_cast<panel::Resizability>(resizability | 552 panel::RESIZABLE_TOP | panel::RESIZABLE_TOP_LEFT | 553 panel::RESIZABLE_TOP_RIGHT); 554 } 555 if (panel == bottom_panel()) { 556 resizability = static_cast<panel::Resizability>(resizability | 557 panel::RESIZABLE_BOTTOM | panel::RESIZABLE_BOTTOM_LEFT | 558 panel::RESIZABLE_BOTTOM_RIGHT); 559 } else { 560 resizability = static_cast<panel::Resizability>(resizability | 561 panel::RESIZABLE_BOTTOM); 562 } 563 return resizability; 564 } 565 566 void StackedPanelCollection::OnPanelResizedByMouse( 567 Panel* resized_panel, const gfx::Rect& new_bounds) { 568 resized_panel->set_full_size(new_bounds.size()); 569 570 DCHECK(!secondary_stack_window_); 571 primary_stack_window_->BeginBatchUpdatePanelBounds(false); 572 573 // The delta x and width can be computed from the difference between 574 // the panel being resized and any other panel. 575 Panel* other_panel = resized_panel == top_panel() ? bottom_panel() 576 : top_panel(); 577 gfx::Rect other_bounds = other_panel->GetBounds(); 578 int delta_x = new_bounds.x() - other_bounds.x(); 579 int delta_width = new_bounds.width() - other_bounds.width(); 580 581 gfx::Rect previous_bounds; 582 bool resized_panel_found = false; 583 bool panel_below_resized_panel_updated = false; 584 for (Panels::const_iterator iter = panels_.begin(); 585 iter != panels_.end(); iter++) { 586 Panel* panel = *iter; 587 if (panel == resized_panel) { 588 // |new_bounds| should be used since the panel bounds have not been 589 // updated yet. 590 previous_bounds = new_bounds; 591 resized_panel_found = true; 592 primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, new_bounds); 593 continue; 594 } 595 596 gfx::Rect bounds = panel->GetBounds(); 597 bounds.set_x(bounds.x() + delta_x); 598 bounds.set_width(bounds.width() + delta_width); 599 600 // If the panel below the panel being resized is expanded, update its 601 // height to offset the height change of the panel being resized. 602 // For example, the stack has P1 and P2 (from top to bottom). P1's height 603 // is 100 and P2's height is 120. If P1's bottom increases by 10, P2's 604 // height needs to shrink by 10. 605 if (resized_panel_found) { 606 if (!panel_below_resized_panel_updated && !panel->IsMinimized()) { 607 int old_bottom = bounds.bottom(); 608 bounds.set_y(previous_bounds.bottom()); 609 bounds.set_height(old_bottom - bounds.y()); 610 } else { 611 bounds.set_y(previous_bounds.bottom()); 612 } 613 panel_below_resized_panel_updated = true; 614 } 615 616 if (!panel->IsMinimized()) 617 panel->set_full_size(bounds.size()); 618 619 primary_stack_window_->AddPanelBoundsForBatchUpdate(panel, bounds); 620 previous_bounds = bounds; 621 } 622 623 primary_stack_window_->EndBatchUpdatePanelBounds(); 624 } 625 626 bool StackedPanelCollection::HasPanel(Panel* panel) const { 627 return std::find(panels_.begin(), panels_.end(), panel) != panels_.end(); 628 } 629 630 void StackedPanelCollection::UpdatePanelOnCollectionChange(Panel* panel) { 631 panel->set_attention_mode( 632 static_cast<Panel::AttentionMode>(Panel::USE_PANEL_ATTENTION | 633 Panel::USE_SYSTEM_ATTENTION)); 634 panel->ShowShadow(false); 635 panel->UpdateMinimizeRestoreButtonVisibility(); 636 UpdatePanelCornerStyle(panel); 637 } 638 639 void StackedPanelCollection::OnPanelExpansionStateChanged(Panel* panel) { 640 DCHECK_NE(Panel::MINIMIZED, panel->expansion_state()); 641 642 // Ensure minimized panel does not get the focus. If minimizing all, 643 // the active panel will be deactivated once when all panels are minimized 644 // rather than per minimized panel. 645 if (panel->expansion_state() != Panel::EXPANDED && !minimizing_all_ && 646 panel->IsActive()) { 647 panel->Deactivate(); 648 } 649 650 // The bounds change per expansion state will be done in RefreshLayout. 651 RefreshLayout(); 652 } 653 654 void StackedPanelCollection::OnPanelActiveStateChanged(Panel* panel) { 655 if (!panel->IsActive()) 656 return; 657 658 // Move the panel to the front if not yet. 659 Panels::iterator iter = std::find(most_recently_active_panels_.begin(), 660 most_recently_active_panels_.end(), panel); 661 DCHECK(iter != most_recently_active_panels_.end()); 662 if (iter != most_recently_active_panels_.begin()) { 663 most_recently_active_panels_.erase(iter); 664 most_recently_active_panels_.push_front(panel); 665 } 666 667 GetStackWindowForPanel(panel)->OnPanelActivated(panel); 668 } 669 670 gfx::Rect StackedPanelCollection::GetInitialPanelBounds( 671 const gfx::Rect& requested_bounds) const { 672 DCHECK(!panels_.empty()); 673 gfx::Rect bottom_panel_bounds = bottom_panel()->GetBounds(); 674 return gfx::Rect(bottom_panel_bounds.x(), 675 bottom_panel_bounds.bottom(), 676 bottom_panel_bounds.width(), 677 requested_bounds.height()); 678 } 679 680 Panel* StackedPanelCollection::GetPanelAbove(Panel* panel) const { 681 DCHECK(panel); 682 683 if (panels_.size() < 2) 684 return NULL; 685 Panels::const_iterator iter = panels_.begin(); 686 Panel* above_panel = *iter; 687 for (; iter != panels_.end(); ++iter) { 688 if (*iter == panel) 689 return above_panel; 690 above_panel = *iter; 691 } 692 return NULL; 693 } 694 695 Panel* StackedPanelCollection::GetPanelBelow(Panel* panel) const { 696 DCHECK(panel); 697 698 if (panels_.size() < 2) 699 return NULL; 700 Panels::const_iterator iter = 701 std::find(panels_.begin(), panels_.end(), panel); 702 if (iter == panels_.end()) 703 return NULL; 704 ++iter; 705 return iter == panels_.end() ? NULL : *iter; 706 } 707 708 void StackedPanelCollection::MoveAllDraggingPanelsInstantly( 709 const gfx::Vector2d& delta_origin) { 710 for (Panels::const_iterator iter = panels_.begin(); 711 iter != panels_.end(); iter++) { 712 Panel* panel = *iter; 713 if (panel->in_preview_mode()) { 714 GetStackWindowForPanel(panel)->MovePanelsBy(delta_origin); 715 return; 716 } 717 } 718 } 719 720 bool StackedPanelCollection::IsMinimized() const { 721 return primary_stack_window_->IsMinimized(); 722 } 723 724 bool StackedPanelCollection::IsAnimatingPanelBounds(Panel* panel) const { 725 return GetStackWindowForPanel(panel)->IsAnimatingPanelBounds(); 726 } 727 728 void StackedPanelCollection::UpdatePanelCornerStyle(Panel* panel) { 729 panel::CornerStyle corner_style; 730 bool at_top = panel == top_panel(); 731 bool at_bottom = panel == bottom_panel(); 732 if (at_top && at_bottom) 733 corner_style = panel::ALL_ROUNDED; 734 else if (at_top) 735 corner_style = panel::TOP_ROUNDED; 736 else if (at_bottom) 737 corner_style = panel::BOTTOM_ROUNDED; 738 else 739 corner_style = panel::NOT_ROUNDED; 740 panel->SetWindowCornerStyle(corner_style); 741 } 742 743 gfx::Rect StackedPanelCollection::GetWorkArea() const { 744 if (panels_.empty()) 745 return panel_manager_->display_settings_provider()->GetPrimaryWorkArea(); 746 return panel_manager_->display_settings_provider()->GetWorkAreaMatching( 747 GetEnclosingBounds()); 748 } 749 750 int StackedPanelCollection::GetCurrentAvailableTopSpace() const { 751 gfx::Rect work_area = GetWorkArea(); 752 if (panels_.empty()) 753 return work_area.height(); 754 755 int available_space = top_panel()->GetBounds().y() - work_area.y(); 756 if (available_space < 0) 757 available_space = 0; 758 return available_space; 759 } 760 761 int StackedPanelCollection::GetCurrentAvailableBottomSpace() const { 762 gfx::Rect work_area = GetWorkArea(); 763 if (panels_.empty()) 764 return work_area.height(); 765 766 int available_space = work_area.bottom() - 767 bottom_panel()->GetBounds().bottom(); 768 if (available_space < 0) 769 available_space = 0; 770 return available_space; 771 } 772 773 int StackedPanelCollection::GetMaximiumAvailableBottomSpace() const { 774 gfx::Rect work_area = GetWorkArea(); 775 if (panels_.empty()) 776 return work_area.height(); 777 778 int bottom = top_panel()->GetBounds().y(); 779 for (Panels::const_iterator iter = panels_.begin(); 780 iter != panels_.end(); iter++) { 781 Panel* panel = *iter; 782 // Only the most recently active panel might be active. 783 if (iter == panels_.begin() && panel->IsActive()) 784 bottom += panel->GetBounds().height(); 785 else 786 bottom += panel::kTitlebarHeight; 787 } 788 int available_space = work_area.bottom() - bottom; 789 if (available_space < 0) 790 available_space = 0; 791 return available_space; 792 } 793 794 NativePanelStackWindow* StackedPanelCollection::GetStackWindowForPanel( 795 Panel* panel) const { 796 return secondary_stack_window_ && secondary_stack_window_->HasPanel(panel) ? 797 secondary_stack_window_ : primary_stack_window_; 798 } 799