| OLD | NEW |
| (Empty) |
| 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 base::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 } | |
| OLD | NEW |