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