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