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 "ash/wm/panels/panel_layout_manager.h" 6 7 #include <algorithm> 8 #include <map> 9 10 #include "ash/launcher/launcher.h" 11 #include "ash/screen_ash.h" 12 #include "ash/shelf/shelf_layout_manager.h" 13 #include "ash/shelf/shelf_types.h" 14 #include "ash/shelf/shelf_widget.h" 15 #include "ash/shell.h" 16 #include "ash/shell_window_ids.h" 17 #include "ash/wm/window_animations.h" 18 #include "ash/wm/window_state.h" 19 #include "ash/wm/window_util.h" 20 #include "base/auto_reset.h" 21 #include "base/bind.h" 22 #include "base/bind_helpers.h" 23 #include "third_party/skia/include/core/SkColor.h" 24 #include "third_party/skia/include/core/SkPaint.h" 25 #include "third_party/skia/include/core/SkPath.h" 26 #include "ui/aura/client/activation_client.h" 27 #include "ui/aura/client/focus_client.h" 28 #include "ui/aura/client/window_tree_client.h" 29 #include "ui/aura/root_window.h" 30 #include "ui/aura/window.h" 31 #include "ui/aura/window_tracker.h" 32 #include "ui/compositor/scoped_layer_animation_settings.h" 33 #include "ui/gfx/canvas.h" 34 #include "ui/gfx/rect.h" 35 #include "ui/gfx/vector2d.h" 36 #include "ui/views/background.h" 37 #include "ui/views/widget/widget.h" 38 39 namespace ash { 40 namespace internal { 41 42 namespace { 43 const int kPanelIdealSpacing = 4; 44 45 const float kMaxHeightFactor = .80f; 46 const float kMaxWidthFactor = .50f; 47 48 // Duration for panel animations. 49 const int kPanelSlideDurationMilliseconds = 50; 50 const int kCalloutFadeDurationMilliseconds = 50; 51 52 // Offset used when sliding panel in/out of the launcher. Used for minimizing, 53 // restoring and the initial showing of a panel. 54 const int kPanelSlideInOffset = 20; 55 56 // Callout arrow dimensions. 57 const int kArrowWidth = 18; 58 const int kArrowHeight = 9; 59 60 class CalloutWidgetBackground : public views::Background { 61 public: 62 CalloutWidgetBackground() : alignment_(SHELF_ALIGNMENT_BOTTOM) { 63 } 64 65 virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE { 66 SkPath path; 67 switch (alignment_) { 68 case SHELF_ALIGNMENT_BOTTOM: 69 path.moveTo(SkIntToScalar(0), SkIntToScalar(0)); 70 path.lineTo(SkIntToScalar(kArrowWidth / 2), 71 SkIntToScalar(kArrowHeight)); 72 path.lineTo(SkIntToScalar(kArrowWidth), SkIntToScalar(0)); 73 break; 74 case SHELF_ALIGNMENT_LEFT: 75 path.moveTo(SkIntToScalar(kArrowHeight), SkIntToScalar(kArrowWidth)); 76 path.lineTo(SkIntToScalar(0), SkIntToScalar(kArrowWidth / 2)); 77 path.lineTo(SkIntToScalar(kArrowHeight), SkIntToScalar(0)); 78 break; 79 case SHELF_ALIGNMENT_TOP: 80 path.moveTo(SkIntToScalar(0), SkIntToScalar(kArrowHeight)); 81 path.lineTo(SkIntToScalar(kArrowWidth / 2), SkIntToScalar(0)); 82 path.lineTo(SkIntToScalar(kArrowWidth), SkIntToScalar(kArrowHeight)); 83 break; 84 case SHELF_ALIGNMENT_RIGHT: 85 path.moveTo(SkIntToScalar(0), SkIntToScalar(0)); 86 path.lineTo(SkIntToScalar(kArrowHeight), 87 SkIntToScalar(kArrowWidth / 2)); 88 path.lineTo(SkIntToScalar(0), SkIntToScalar(kArrowWidth)); 89 break; 90 } 91 // Hard code the arrow color for now. 92 SkPaint paint; 93 paint.setStyle(SkPaint::kFill_Style); 94 paint.setColor(SkColorSetARGB(0xff, 0xe5, 0xe5, 0xe5)); 95 canvas->DrawPath(path, paint); 96 } 97 98 ShelfAlignment alignment() { 99 return alignment_; 100 } 101 102 void set_alignment(ShelfAlignment alignment) { 103 alignment_ = alignment; 104 } 105 106 private: 107 ShelfAlignment alignment_; 108 109 DISALLOW_COPY_AND_ASSIGN(CalloutWidgetBackground); 110 }; 111 112 struct VisiblePanelPositionInfo { 113 VisiblePanelPositionInfo() 114 : min_major(0), 115 max_major(0), 116 major_pos(0), 117 major_length(0), 118 window(NULL), 119 slide_in(false) {} 120 121 int min_major; 122 int max_major; 123 int major_pos; 124 int major_length; 125 aura::Window* window; 126 bool slide_in; 127 }; 128 129 bool CompareWindowMajor(const VisiblePanelPositionInfo& win1, 130 const VisiblePanelPositionInfo& win2) { 131 return win1.major_pos < win2.major_pos; 132 } 133 134 void FanOutPanels(std::vector<VisiblePanelPositionInfo>::iterator first, 135 std::vector<VisiblePanelPositionInfo>::iterator last) { 136 int num_panels = last - first; 137 if (num_panels == 1) { 138 (*first).major_pos = std::max((*first).min_major, std::min( 139 (*first).max_major, (*first).major_pos)); 140 } 141 if (num_panels <= 1) 142 return; 143 144 if (num_panels == 2) { 145 // If there are two adjacent overlapping windows, separate them by the 146 // minimum major_length necessary. 147 std::vector<VisiblePanelPositionInfo>::iterator second = first + 1; 148 int separation = (*first).major_length / 2 + (*second).major_length / 2 + 149 kPanelIdealSpacing; 150 int overlap = (*first).major_pos + separation - (*second).major_pos; 151 (*first).major_pos = std::max((*first).min_major, 152 (*first).major_pos - overlap / 2); 153 (*second).major_pos = std::min((*second).max_major, 154 (*first).major_pos + separation); 155 // Recalculate the first panel position in case the second one was 156 // constrained on the right. 157 (*first).major_pos = std::max((*first).min_major, 158 (*second).major_pos - separation); 159 return; 160 } 161 162 // If there are more than two overlapping windows, fan them out from minimum 163 // position to maximum position equally spaced. 164 int delta = ((*(last - 1)).max_major - (*first).min_major) / (num_panels - 1); 165 int major_pos = (*first).min_major; 166 for (std::vector<VisiblePanelPositionInfo>::iterator iter = first; 167 iter != last; ++iter) { 168 (*iter).major_pos = std::max((*iter).min_major, 169 std::min((*iter).max_major, major_pos)); 170 major_pos += delta; 171 } 172 } 173 174 bool BoundsAdjacent(const gfx::Rect& bounds1, const gfx::Rect& bounds2) { 175 return bounds1.x() == bounds2.right() || 176 bounds1.y() == bounds2.bottom() || 177 bounds1.right() == bounds2.x() || 178 bounds1.bottom() == bounds2.y(); 179 } 180 181 gfx::Vector2d GetSlideInAnimationOffset(ShelfAlignment alignment) { 182 gfx::Vector2d offset; 183 switch (alignment) { 184 case SHELF_ALIGNMENT_BOTTOM: 185 offset.set_y(kPanelSlideInOffset); 186 break; 187 case SHELF_ALIGNMENT_LEFT: 188 offset.set_x(-kPanelSlideInOffset); 189 break; 190 case SHELF_ALIGNMENT_RIGHT: 191 offset.set_x(kPanelSlideInOffset); 192 break; 193 case SHELF_ALIGNMENT_TOP: 194 offset.set_y(-kPanelSlideInOffset); 195 break; 196 } 197 return offset; 198 } 199 200 } // namespace 201 202 class PanelCalloutWidget : public views::Widget { 203 public: 204 explicit PanelCalloutWidget(aura::Window* container) 205 : background_(NULL) { 206 InitWidget(container); 207 } 208 209 void SetAlignment(ShelfAlignment alignment) { 210 gfx::Rect callout_bounds = GetWindowBoundsInScreen(); 211 if (alignment == SHELF_ALIGNMENT_BOTTOM || 212 alignment == SHELF_ALIGNMENT_TOP) { 213 callout_bounds.set_width(kArrowWidth); 214 callout_bounds.set_height(kArrowHeight); 215 } else { 216 callout_bounds.set_width(kArrowHeight); 217 callout_bounds.set_height(kArrowWidth); 218 } 219 GetNativeWindow()->SetBounds(callout_bounds); 220 if (background_->alignment() != alignment) { 221 background_->set_alignment(alignment); 222 SchedulePaintInRect(gfx::Rect(gfx::Point(), callout_bounds.size())); 223 } 224 } 225 226 private: 227 void InitWidget(aura::Window* parent) { 228 views::Widget::InitParams params; 229 params.type = views::Widget::InitParams::TYPE_POPUP; 230 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 231 params.can_activate = false; 232 params.keep_on_top = true; 233 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 234 params.parent = parent; 235 params.bounds = ScreenAsh::ConvertRectToScreen(parent, gfx::Rect()); 236 params.bounds.set_width(kArrowWidth); 237 params.bounds.set_height(kArrowHeight); 238 // Why do we need this and can_activate = false? 239 set_focus_on_creation(false); 240 Init(params); 241 DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow()); 242 views::View* content_view = new views::View; 243 background_ = new CalloutWidgetBackground; 244 content_view->set_background(background_); 245 SetContentsView(content_view); 246 GetNativeWindow()->layer()->SetOpacity(0); 247 } 248 249 // Weak pointer owned by this widget's content view. 250 CalloutWidgetBackground* background_; 251 252 DISALLOW_COPY_AND_ASSIGN(PanelCalloutWidget); 253 }; 254 255 //////////////////////////////////////////////////////////////////////////////// 256 // PanelLayoutManager public implementation: 257 PanelLayoutManager::PanelLayoutManager(aura::Window* panel_container) 258 : panel_container_(panel_container), 259 in_add_window_(false), 260 in_layout_(false), 261 dragged_panel_(NULL), 262 launcher_(NULL), 263 shelf_layout_manager_(NULL), 264 last_active_panel_(NULL), 265 weak_factory_(this) { 266 DCHECK(panel_container); 267 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> 268 AddObserver(this); 269 Shell::GetInstance()->display_controller()->AddObserver(this); 270 Shell::GetInstance()->AddShellObserver(this); 271 } 272 273 PanelLayoutManager::~PanelLayoutManager() { 274 Shutdown(); 275 } 276 277 void PanelLayoutManager::Shutdown() { 278 if (shelf_layout_manager_) 279 shelf_layout_manager_->RemoveObserver(this); 280 shelf_layout_manager_ = NULL; 281 for (PanelList::iterator iter = panel_windows_.begin(); 282 iter != panel_windows_.end(); ++iter) { 283 delete iter->callout_widget; 284 } 285 panel_windows_.clear(); 286 if (launcher_) 287 launcher_->RemoveIconObserver(this); 288 launcher_ = NULL; 289 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> 290 RemoveObserver(this); 291 Shell::GetInstance()->display_controller()->RemoveObserver(this); 292 Shell::GetInstance()->RemoveShellObserver(this); 293 } 294 295 void PanelLayoutManager::StartDragging(aura::Window* panel) { 296 DCHECK(!dragged_panel_); 297 dragged_panel_ = panel; 298 Relayout(); 299 } 300 301 void PanelLayoutManager::FinishDragging() { 302 dragged_panel_ = NULL; 303 Relayout(); 304 } 305 306 void PanelLayoutManager::SetLauncher(ash::Launcher* launcher) { 307 DCHECK(!launcher_); 308 DCHECK(!shelf_layout_manager_); 309 launcher_ = launcher; 310 launcher_->AddIconObserver(this); 311 if (launcher_->shelf_widget()) { 312 shelf_layout_manager_ = ash::internal::ShelfLayoutManager::ForLauncher( 313 launcher_->shelf_widget()->GetNativeWindow()); 314 WillChangeVisibilityState(shelf_layout_manager_->visibility_state()); 315 shelf_layout_manager_->AddObserver(this); 316 } 317 } 318 319 void PanelLayoutManager::ToggleMinimize(aura::Window* panel) { 320 DCHECK(panel->parent() == panel_container_); 321 wm::WindowState* window_state = wm::GetWindowState(panel); 322 if (window_state->IsMinimized()) 323 window_state->Restore(); 324 else 325 window_state->Minimize(); 326 } 327 328 views::Widget* PanelLayoutManager::GetCalloutWidgetForPanel( 329 aura::Window* panel) { 330 DCHECK(panel->parent() == panel_container_); 331 PanelList::iterator found = 332 std::find(panel_windows_.begin(), panel_windows_.end(), panel); 333 DCHECK(found != panel_windows_.end()); 334 return found->callout_widget; 335 } 336 337 //////////////////////////////////////////////////////////////////////////////// 338 // PanelLayoutManager, aura::LayoutManager implementation: 339 void PanelLayoutManager::OnWindowResized() { 340 Relayout(); 341 } 342 343 void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) { 344 if (child->type() == aura::client::WINDOW_TYPE_POPUP) 345 return; 346 if (in_add_window_) 347 return; 348 base::AutoReset<bool> auto_reset_in_add_window(&in_add_window_, true); 349 if (!wm::GetWindowState(child)->panel_attached()) { 350 // This should only happen when a window is added to panel container as a 351 // result of bounds change from within the application during a drag. 352 // If so we have already stopped the drag and should reparent the panel 353 // back to appropriate container and ignore it. 354 // TODO(varkha): Updating bounds during a drag can cause problems and a more 355 // general solution is needed. See http://crbug.com/251813 . 356 aura::Window* old_parent = child->parent(); 357 aura::client::ParentWindowWithContext( 358 child, child, child->GetRootWindow()->GetBoundsInScreen()); 359 wm::ReparentTransientChildrenOfChild(child, old_parent, child->parent()); 360 DCHECK(child->parent()->id() != kShellWindowId_PanelContainer); 361 return; 362 } 363 PanelInfo panel_info; 364 panel_info.window = child; 365 panel_info.callout_widget = new PanelCalloutWidget(panel_container_); 366 if (child != dragged_panel_) { 367 // Set the panel to 0 opacity until it has been positioned to prevent it 368 // from flashing briefly at position (0, 0). 369 child->layer()->SetOpacity(0); 370 panel_info.slide_in = true; 371 } 372 panel_windows_.push_back(panel_info); 373 child->AddObserver(this); 374 wm::GetWindowState(child)->AddObserver(this); 375 Relayout(); 376 } 377 378 void PanelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) { 379 } 380 381 void PanelLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { 382 if (child->type() == aura::client::WINDOW_TYPE_POPUP) 383 return; 384 PanelList::iterator found = 385 std::find(panel_windows_.begin(), panel_windows_.end(), child); 386 if (found != panel_windows_.end()) { 387 delete found->callout_widget; 388 panel_windows_.erase(found); 389 } 390 child->RemoveObserver(this); 391 wm::GetWindowState(child)->RemoveObserver(this); 392 393 if (dragged_panel_ == child) 394 dragged_panel_ = NULL; 395 396 if (last_active_panel_ == child) 397 last_active_panel_ = NULL; 398 399 Relayout(); 400 } 401 402 void PanelLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child, 403 bool visible) { 404 Relayout(); 405 } 406 407 void PanelLayoutManager::SetChildBounds(aura::Window* child, 408 const gfx::Rect& requested_bounds) { 409 gfx::Rect bounds(requested_bounds); 410 const gfx::Rect& max_bounds = panel_container_->GetRootWindow()->bounds(); 411 const int max_width = max_bounds.width() * kMaxWidthFactor; 412 const int max_height = max_bounds.height() * kMaxHeightFactor; 413 if (bounds.width() > max_width) 414 bounds.set_width(max_width); 415 if (bounds.height() > max_height) 416 bounds.set_height(max_height); 417 418 // Reposition dragged panel in the panel order. 419 if (dragged_panel_ == child) { 420 PanelList::iterator dragged_panel_iter = 421 std::find(panel_windows_.begin(), panel_windows_.end(), dragged_panel_); 422 DCHECK(dragged_panel_iter != panel_windows_.end()); 423 PanelList::iterator new_position; 424 for (new_position = panel_windows_.begin(); 425 new_position != panel_windows_.end(); 426 ++new_position) { 427 const gfx::Rect& bounds = (*new_position).window->bounds(); 428 if (bounds.x() + bounds.width()/2 <= requested_bounds.x()) break; 429 } 430 if (new_position != dragged_panel_iter) { 431 PanelInfo dragged_panel_info = *dragged_panel_iter; 432 panel_windows_.erase(dragged_panel_iter); 433 panel_windows_.insert(new_position, dragged_panel_info); 434 } 435 } 436 437 SetChildBoundsDirect(child, bounds); 438 Relayout(); 439 } 440 441 //////////////////////////////////////////////////////////////////////////////// 442 // PanelLayoutManager, ShelfIconObserver implementation: 443 444 void PanelLayoutManager::OnShelfIconPositionsChanged() { 445 // TODO: As this is called for every animation step now. Relayout needs to be 446 // updated to use current icon position instead of use the ideal bounds so 447 // that the panels slide with their icons instead of jumping. 448 Relayout(); 449 } 450 451 //////////////////////////////////////////////////////////////////////////////// 452 // PanelLayoutManager, ash::ShellObserver implementation: 453 454 void PanelLayoutManager::OnShelfAlignmentChanged(aura::Window* root_window) { 455 if (panel_container_->GetRootWindow() == root_window) 456 Relayout(); 457 } 458 459 ///////////////////////////////////////////////////////////////////////////// 460 // PanelLayoutManager, WindowObserver implementation: 461 462 void PanelLayoutManager::OnWindowShowTypeChanged( 463 wm::WindowState* window_state, 464 wm::WindowShowType old_type) { 465 // If the shelf is currently hidden then windows will not actually be shown 466 // but the set to restore when the shelf becomes visible is updated. 467 if (restore_windows_on_shelf_visible_) { 468 if (window_state->IsMinimized()) { 469 MinimizePanel(window_state->window()); 470 restore_windows_on_shelf_visible_->Remove(window_state->window()); 471 } else { 472 restore_windows_on_shelf_visible_->Add(window_state->window()); 473 } 474 return; 475 } 476 477 if (window_state->IsMinimized()) 478 MinimizePanel(window_state->window()); 479 else 480 RestorePanel(window_state->window()); 481 } 482 483 void PanelLayoutManager::OnWindowVisibilityChanged( 484 aura::Window* window, bool visible) { 485 if (visible) 486 wm::GetWindowState(window)->Restore(); 487 } 488 489 //////////////////////////////////////////////////////////////////////////////// 490 // PanelLayoutManager, aura::client::ActivationChangeObserver implementation: 491 492 void PanelLayoutManager::OnWindowActivated(aura::Window* gained_active, 493 aura::Window* lost_active) { 494 // Ignore if the panel that is not managed by this was activated. 495 if (gained_active && 496 gained_active->type() == aura::client::WINDOW_TYPE_PANEL && 497 gained_active->parent() == panel_container_) { 498 UpdateStacking(gained_active); 499 UpdateCallouts(); 500 } 501 } 502 503 //////////////////////////////////////////////////////////////////////////////// 504 // PanelLayoutManager, DisplayController::Observer implementation: 505 506 void PanelLayoutManager::OnDisplayConfigurationChanged() { 507 Relayout(); 508 } 509 510 //////////////////////////////////////////////////////////////////////////////// 511 // PanelLayoutManager, ShelfLayoutManagerObserver implementation: 512 513 void PanelLayoutManager::WillChangeVisibilityState( 514 ShelfVisibilityState new_state) { 515 // On entering / leaving full screen mode the shelf visibility state is 516 // changed to / from SHELF_HIDDEN. In this state, panel windows should hide 517 // to allow the full-screen application to use the full screen. 518 bool shelf_hidden = new_state == ash::SHELF_HIDDEN; 519 if (!shelf_hidden) { 520 if (restore_windows_on_shelf_visible_) { 521 scoped_ptr<aura::WindowTracker> restore_windows( 522 restore_windows_on_shelf_visible_.Pass()); 523 for (aura::WindowTracker::Windows::const_iterator iter = 524 restore_windows->windows().begin(); iter != 525 restore_windows->windows().end(); ++iter) { 526 RestorePanel(*iter); 527 } 528 } 529 return; 530 } 531 532 if (restore_windows_on_shelf_visible_) 533 return; 534 scoped_ptr<aura::WindowTracker> minimized_windows(new aura::WindowTracker); 535 for (PanelList::iterator iter = panel_windows_.begin(); 536 iter != panel_windows_.end(); ++iter) { 537 if (iter->window->IsVisible()) { 538 minimized_windows->Add(iter->window); 539 wm::GetWindowState(iter->window)->Minimize(); 540 } 541 } 542 restore_windows_on_shelf_visible_ = minimized_windows.Pass(); 543 } 544 545 //////////////////////////////////////////////////////////////////////////////// 546 // PanelLayoutManager private implementation: 547 548 void PanelLayoutManager::MinimizePanel(aura::Window* panel) { 549 views::corewm::SetWindowVisibilityAnimationType( 550 panel, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); 551 ui::Layer* layer = panel->layer(); 552 ui::ScopedLayerAnimationSettings panel_slide_settings(layer->GetAnimator()); 553 panel_slide_settings.SetPreemptionStrategy( 554 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 555 panel_slide_settings.SetTransitionDuration( 556 base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds)); 557 gfx::Rect bounds(panel->bounds()); 558 bounds.Offset(GetSlideInAnimationOffset( 559 launcher_->shelf_widget()->GetAlignment())); 560 SetChildBoundsDirect(panel, bounds); 561 panel->Hide(); 562 PanelList::iterator found = 563 std::find(panel_windows_.begin(), panel_windows_.end(), panel); 564 if (found != panel_windows_.end()) { 565 layer->SetOpacity(0); 566 // The next time the window is visible it should slide into place. 567 found->slide_in = true; 568 } 569 if (wm::IsActiveWindow(panel)) 570 wm::DeactivateWindow(panel); 571 Relayout(); 572 } 573 574 void PanelLayoutManager::RestorePanel(aura::Window* panel) { 575 panel->Show(); 576 Relayout(); 577 } 578 579 void PanelLayoutManager::Relayout() { 580 if (!launcher_ || !launcher_->shelf_widget()) 581 return; 582 583 if (in_layout_) 584 return; 585 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); 586 587 ShelfAlignment alignment = launcher_->shelf_widget()->GetAlignment(); 588 bool horizontal = alignment == SHELF_ALIGNMENT_TOP || 589 alignment == SHELF_ALIGNMENT_BOTTOM; 590 gfx::Rect launcher_bounds = ash::ScreenAsh::ConvertRectFromScreen( 591 panel_container_, launcher_->shelf_widget()->GetWindowBoundsInScreen()); 592 int panel_start_bounds = kPanelIdealSpacing; 593 int panel_end_bounds = horizontal ? 594 panel_container_->bounds().width() - kPanelIdealSpacing : 595 panel_container_->bounds().height() - kPanelIdealSpacing; 596 aura::Window* active_panel = NULL; 597 std::vector<VisiblePanelPositionInfo> visible_panels; 598 for (PanelList::iterator iter = panel_windows_.begin(); 599 iter != panel_windows_.end(); ++iter) { 600 aura::Window* panel = iter->window; 601 iter->callout_widget->SetAlignment(alignment); 602 603 // Consider the dragged panel as part of the layout as long as it is 604 // touching the launcher. 605 if (!panel->IsVisible() || 606 (panel == dragged_panel_ && 607 !BoundsAdjacent(panel->bounds(), launcher_bounds))) { 608 continue; 609 } 610 611 // If the shelf is currently hidden (full-screen mode), minimize panel until 612 // full-screen mode is exited. 613 if (restore_windows_on_shelf_visible_) { 614 wm::GetWindowState(panel)->Minimize(); 615 restore_windows_on_shelf_visible_->Add(panel); 616 continue; 617 } 618 619 gfx::Rect icon_bounds = 620 launcher_->GetScreenBoundsOfItemIconForWindow(panel); 621 622 // If both the icon width and height are 0 then there is no icon in the 623 // launcher. If the launcher is hidden, one of the height or width will be 624 // 0 but the position in the launcher and major dimension is still reported 625 // correctly and the panel can be aligned above where the hidden icon is. 626 if (icon_bounds.width() == 0 && icon_bounds.height() == 0) 627 continue; 628 629 if (panel->HasFocus() || 630 panel->Contains( 631 aura::client::GetFocusClient(panel)->GetFocusedWindow())) { 632 DCHECK(!active_panel); 633 active_panel = panel; 634 } 635 icon_bounds = ScreenAsh::ConvertRectFromScreen(panel_container_, 636 icon_bounds); 637 gfx::Point icon_origin = icon_bounds.origin(); 638 VisiblePanelPositionInfo position_info; 639 int icon_start = horizontal ? icon_origin.x() : icon_origin.y(); 640 int icon_end = icon_start + (horizontal ? icon_bounds.width() : 641 icon_bounds.height()); 642 position_info.major_length = horizontal ? 643 panel->bounds().width() : panel->bounds().height(); 644 position_info.min_major = std::max( 645 panel_start_bounds + position_info.major_length / 2, 646 icon_end - position_info.major_length / 2); 647 position_info.max_major = std::min( 648 icon_start + position_info.major_length / 2, 649 panel_end_bounds - position_info.major_length / 2); 650 position_info.major_pos = (icon_start + icon_end) / 2; 651 position_info.window = panel; 652 position_info.slide_in = iter->slide_in; 653 iter->slide_in = false; 654 visible_panels.push_back(position_info); 655 } 656 657 // Sort panels by their X positions and fan out groups of overlapping panels. 658 // The fan out method may result in new overlapping panels however given that 659 // the panels start at least a full panel width apart this overlap will 660 // never completely obscure a panel. 661 // TODO(flackr): Rearrange panels if new overlaps are introduced. 662 std::sort(visible_panels.begin(), visible_panels.end(), CompareWindowMajor); 663 size_t first_overlapping_panel = 0; 664 for (size_t i = 1; i < visible_panels.size(); ++i) { 665 if (visible_panels[i - 1].major_pos + 666 visible_panels[i - 1].major_length / 2 < visible_panels[i].major_pos - 667 visible_panels[i].major_length / 2) { 668 FanOutPanels(visible_panels.begin() + first_overlapping_panel, 669 visible_panels.begin() + i); 670 first_overlapping_panel = i; 671 } 672 } 673 FanOutPanels(visible_panels.begin() + first_overlapping_panel, 674 visible_panels.end()); 675 676 for (size_t i = 0; i < visible_panels.size(); ++i) { 677 if (visible_panels[i].window == dragged_panel_) 678 continue; 679 bool slide_in = visible_panels[i].slide_in; 680 gfx::Rect bounds = visible_panels[i].window->GetTargetBounds(); 681 switch (alignment) { 682 case SHELF_ALIGNMENT_BOTTOM: 683 bounds.set_y(launcher_bounds.y() - bounds.height()); 684 break; 685 case SHELF_ALIGNMENT_LEFT: 686 bounds.set_x(launcher_bounds.right()); 687 break; 688 case SHELF_ALIGNMENT_RIGHT: 689 bounds.set_x(launcher_bounds.x() - bounds.width()); 690 break; 691 case SHELF_ALIGNMENT_TOP: 692 bounds.set_y(launcher_bounds.bottom()); 693 break; 694 } 695 bool on_launcher = visible_panels[i].window->GetTargetBounds() == bounds; 696 697 if (horizontal) { 698 bounds.set_x(visible_panels[i].major_pos - 699 visible_panels[i].major_length / 2); 700 } else { 701 bounds.set_y(visible_panels[i].major_pos - 702 visible_panels[i].major_length / 2); 703 } 704 705 ui::Layer* layer = visible_panels[i].window->layer(); 706 if (slide_in) { 707 // New windows shift up from the launcher into position. 708 gfx::Rect initial_bounds(bounds); 709 initial_bounds.Offset(GetSlideInAnimationOffset(alignment)); 710 SetChildBoundsDirect(visible_panels[i].window, initial_bounds); 711 // Set on launcher so that the panel animates into its target position. 712 on_launcher = true; 713 } 714 715 if (on_launcher) { 716 ui::ScopedLayerAnimationSettings panel_slide_settings( 717 layer->GetAnimator()); 718 panel_slide_settings.SetPreemptionStrategy( 719 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 720 panel_slide_settings.SetTransitionDuration( 721 base::TimeDelta::FromMilliseconds(kPanelSlideDurationMilliseconds)); 722 SetChildBoundsDirect(visible_panels[i].window, bounds); 723 if (slide_in) 724 layer->SetOpacity(1); 725 } else { 726 // If the launcher moved don't animate, move immediately to the new 727 // target location. 728 SetChildBoundsDirect(visible_panels[i].window, bounds); 729 } 730 } 731 732 UpdateStacking(active_panel); 733 UpdateCallouts(); 734 } 735 736 void PanelLayoutManager::UpdateStacking(aura::Window* active_panel) { 737 if (!active_panel) { 738 if (!last_active_panel_) 739 return; 740 active_panel = last_active_panel_; 741 } 742 743 ShelfAlignment alignment = launcher_->alignment(); 744 bool horizontal = alignment == SHELF_ALIGNMENT_TOP || 745 alignment == SHELF_ALIGNMENT_BOTTOM; 746 747 // We want to to stack the panels like a deck of cards: 748 // ,--,--,--,-------.--.--. 749 // | | | | | | | 750 // | | | | | | | 751 // 752 // We use the middle of each panel to figure out how to stack the panels. This 753 // allows us to update the stacking when a panel is being dragged around by 754 // the titlebar--even though it doesn't update the launcher icon positions, we 755 // still want the visual effect. 756 std::map<int, aura::Window*> window_ordering; 757 for (PanelList::const_iterator it = panel_windows_.begin(); 758 it != panel_windows_.end(); ++it) { 759 gfx::Rect bounds = it->window->bounds(); 760 window_ordering.insert(std::make_pair(horizontal ? 761 bounds.x() + bounds.width() / 2 : 762 bounds.y() + bounds.height() / 2, 763 it->window)); 764 } 765 766 aura::Window* previous_panel = NULL; 767 for (std::map<int, aura::Window*>::const_iterator it = 768 window_ordering.begin(); 769 it != window_ordering.end() && it->second != active_panel; ++it) { 770 if (previous_panel) 771 panel_container_->StackChildAbove(it->second, previous_panel); 772 previous_panel = it->second; 773 } 774 775 previous_panel = NULL; 776 for (std::map<int, aura::Window*>::const_reverse_iterator it = 777 window_ordering.rbegin(); 778 it != window_ordering.rend() && it->second != active_panel; ++it) { 779 if (previous_panel) 780 panel_container_->StackChildAbove(it->second, previous_panel); 781 previous_panel = it->second; 782 } 783 784 panel_container_->StackChildAtTop(active_panel); 785 if (dragged_panel_ && dragged_panel_->parent() == panel_container_) 786 panel_container_->StackChildAtTop(dragged_panel_); 787 last_active_panel_ = active_panel; 788 } 789 790 void PanelLayoutManager::UpdateCallouts() { 791 ShelfAlignment alignment = launcher_->alignment(); 792 bool horizontal = alignment == SHELF_ALIGNMENT_TOP || 793 alignment == SHELF_ALIGNMENT_BOTTOM; 794 795 for (PanelList::iterator iter = panel_windows_.begin(); 796 iter != panel_windows_.end(); ++iter) { 797 aura::Window* panel = iter->window; 798 views::Widget* callout_widget = iter->callout_widget; 799 800 gfx::Rect current_bounds = panel->GetBoundsInScreen(); 801 gfx::Rect bounds = ScreenAsh::ConvertRectToScreen(panel->parent(), 802 panel->GetTargetBounds()); 803 gfx::Rect icon_bounds = 804 launcher_->GetScreenBoundsOfItemIconForWindow(panel); 805 if (icon_bounds.IsEmpty() || !panel->layer()->GetTargetVisibility() || 806 panel == dragged_panel_) { 807 callout_widget->Hide(); 808 callout_widget->GetNativeWindow()->layer()->SetOpacity(0); 809 continue; 810 } 811 812 gfx::Rect callout_bounds = callout_widget->GetWindowBoundsInScreen(); 813 gfx::Vector2d slide_vector = bounds.origin() - current_bounds.origin(); 814 int slide_distance = horizontal ? slide_vector.x() : slide_vector.y(); 815 int distance_until_over_panel = 0; 816 if (horizontal) { 817 callout_bounds.set_x( 818 icon_bounds.x() + (icon_bounds.width() - callout_bounds.width()) / 2); 819 distance_until_over_panel = std::max( 820 current_bounds.x() - callout_bounds.x(), 821 callout_bounds.right() - current_bounds.right()); 822 } else { 823 callout_bounds.set_y( 824 icon_bounds.y() + (icon_bounds.height() - 825 callout_bounds.height()) / 2); 826 distance_until_over_panel = std::max( 827 current_bounds.y() - callout_bounds.y(), 828 callout_bounds.bottom() - current_bounds.bottom()); 829 } 830 switch (alignment) { 831 case SHELF_ALIGNMENT_BOTTOM: 832 callout_bounds.set_y(bounds.bottom()); 833 break; 834 case SHELF_ALIGNMENT_LEFT: 835 callout_bounds.set_x(bounds.x() - callout_bounds.width()); 836 break; 837 case SHELF_ALIGNMENT_RIGHT: 838 callout_bounds.set_x(bounds.right()); 839 break; 840 case SHELF_ALIGNMENT_TOP: 841 callout_bounds.set_y(bounds.y() - callout_bounds.height()); 842 break; 843 } 844 callout_bounds = ScreenAsh::ConvertRectFromScreen( 845 callout_widget->GetNativeWindow()->parent(), 846 callout_bounds); 847 848 SetChildBoundsDirect(callout_widget->GetNativeWindow(), callout_bounds); 849 panel_container_->StackChildAbove(callout_widget->GetNativeWindow(), 850 panel); 851 callout_widget->Show(); 852 853 ui::Layer* layer = callout_widget->GetNativeWindow()->layer(); 854 // If the panel is not over the callout position or has just become visible 855 // then fade in the callout. 856 if ((distance_until_over_panel > 0 || layer->GetTargetOpacity() < 1) && 857 panel->layer()->GetTargetTransform().IsIdentity()) { 858 if (distance_until_over_panel > 0 && 859 slide_distance >= distance_until_over_panel) { 860 layer->SetOpacity(0); 861 // If the panel is not yet over the callout, then delay fading in 862 // the callout until after the panel should be over it. 863 int delay = kPanelSlideDurationMilliseconds * 864 distance_until_over_panel / slide_distance; 865 layer->SetOpacity(0); 866 layer->GetAnimator()->StopAnimating(); 867 layer->GetAnimator()->SchedulePauseForProperties( 868 base::TimeDelta::FromMilliseconds(delay), 869 ui::LayerAnimationElement::OPACITY); 870 } 871 { 872 ui::ScopedLayerAnimationSettings callout_settings(layer->GetAnimator()); 873 callout_settings.SetPreemptionStrategy( 874 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); 875 callout_settings.SetTransitionDuration( 876 base::TimeDelta::FromMilliseconds( 877 kCalloutFadeDurationMilliseconds)); 878 layer->SetOpacity(1); 879 } 880 } 881 } 882 } 883 884 //////////////////////////////////////////////////////////////////////////////// 885 // keyboard::KeyboardControllerObserver implementation: 886 887 void PanelLayoutManager::OnKeyboardBoundsChanging( 888 const gfx::Rect& keyboard_bounds) { 889 // This bounds change will have caused a change to the Shelf which does not 890 // propogate automatically to this class, so manually recalculate bounds. 891 OnWindowResized(); 892 } 893 894 } // namespace internal 895 } // namespace ash 896