Revert 222512 "Group WM related properties to ash::wm::WindowSet..."
[chromium-blink-merge.git] / ash / wm / panels / panel_layout_manager.cc
blob2b00c882b239a9c5effab973c61a5905c0206c87
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.
5 #include "ash/wm/panels/panel_layout_manager.h"
7 #include <algorithm>
8 #include <map>
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"
40 namespace ash {
41 namespace internal {
43 namespace {
44 const int kPanelMarginEdge = 4;
45 const int kPanelMarginMiddle = 8;
46 const int kPanelIdealSpacing = 4;
48 const float kMaxHeightFactor = .80f;
49 const float kMaxWidthFactor = .50f;
51 // Duration for panel animations.
52 const int kPanelSlideDurationMilliseconds = 50;
53 const int kCalloutFadeDurationMilliseconds = 50;
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;
59 // Callout arrow dimensions.
60 const int kArrowWidth = 18;
61 const int kArrowHeight = 9;
63 class CalloutWidgetBackground : public views::Background {
64 public:
65 CalloutWidgetBackground() : alignment_(SHELF_ALIGNMENT_BOTTOM) {
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;
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);
101 ShelfAlignment alignment() {
102 return alignment_;
105 void set_alignment(ShelfAlignment alignment) {
106 alignment_ = alignment;
109 private:
110 ShelfAlignment alignment_;
112 DISALLOW_COPY_AND_ASSIGN(CalloutWidgetBackground);
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) {}
124 int min_major;
125 int max_major;
126 int major_pos;
127 int major_length;
128 aura::Window* window;
129 bool slide_in;
132 bool CompareWindowMajor(const VisiblePanelPositionInfo& win1,
133 const VisiblePanelPositionInfo& win2) {
134 return win1.major_pos < win2.major_pos;
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));
144 if (num_panels <= 1)
145 return;
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;
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;
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();
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;
200 return offset;
203 } // namespace
205 class PanelCalloutWidget : public views::Widget {
206 public:
207 explicit PanelCalloutWidget(aura::Window* container)
208 : background_(NULL) {
209 InitWidget(container);
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);
222 GetNativeWindow()->SetBounds(callout_bounds);
223 if (background_->alignment() != alignment) {
224 background_->set_alignment(alignment);
225 SchedulePaintInRect(gfx::Rect(gfx::Point(), callout_bounds.size()));
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);
252 // Weak pointer owned by this widget's content view.
253 CalloutWidgetBackground* background_;
255 DISALLOW_COPY_AND_ASSIGN(PanelCalloutWidget);
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);
277 PanelLayoutManager::~PanelLayoutManager() {
278 Shutdown();
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;
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);
299 void PanelLayoutManager::StartDragging(aura::Window* panel) {
300 DCHECK(!dragged_panel_);
301 dragged_panel_ = panel;
302 Relayout();
305 void PanelLayoutManager::FinishDragging() {
306 dragged_panel_ = NULL;
307 Relayout();
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);
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);
333 ////////////////////////////////////////////////////////////////////////////////
334 // PanelLayoutManager, aura::LayoutManager implementation:
335 void PanelLayoutManager::OnWindowResized() {
336 Relayout();
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;
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;
367 panel_windows_.push_back(panel_info);
368 child->AddObserver(this);
369 Relayout();
372 void PanelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
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);
384 child->RemoveObserver(this);
386 if (dragged_panel_ == child)
387 dragged_panel_ = NULL;
389 if (last_active_panel_ == child)
390 last_active_panel_ = NULL;
392 Relayout();
395 void PanelLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
396 bool visible) {
397 Relayout();
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);
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;
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);
430 SetChildBoundsDirect(child, bounds);
431 Relayout();
434 ////////////////////////////////////////////////////////////////////////////////
435 // PanelLayoutManager, ash::LauncherIconObserver implementation:
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();
444 ////////////////////////////////////////////////////////////////////////////////
445 // PanelLayoutManager, ash::ShellObserver implementation:
447 void PanelLayoutManager::OnShelfAlignmentChanged(
448 aura::RootWindow* root_window) {
449 if (panel_container_->GetRootWindow() == root_window)
450 Relayout();
453 /////////////////////////////////////////////////////////////////////////////
454 // PanelLayoutManager, WindowObserver implementation:
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);
473 void PanelLayoutManager::OnWindowVisibilityChanged(
474 aura::Window* window, bool visible) {
475 if (visible)
476 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
479 ////////////////////////////////////////////////////////////////////////////////
480 // PanelLayoutManager, aura::client::ActivationChangeObserver implementation:
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();
493 ////////////////////////////////////////////////////////////////////////////////
494 // PanelLayoutManager, DisplayController::Observer implementation:
496 void PanelLayoutManager::OnDisplayConfigurationChanged() {
497 Relayout();
500 ////////////////////////////////////////////////////////////////////////////////
501 // PanelLayoutManager, ShelfLayoutManagerObserver implementation:
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);
523 ////////////////////////////////////////////////////////////////////////////////
524 // PanelLayoutManager private implementation:
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;
547 if (wm::IsActiveWindow(panel))
548 wm::DeactivateWindow(panel);
549 Relayout();
552 void PanelLayoutManager::RestorePanel(aura::Window* panel) {
553 panel->Show();
554 Relayout();
557 void PanelLayoutManager::Relayout() {
558 if (!launcher_ || !launcher_->shelf_widget())
559 return;
561 if (in_layout_)
562 return;
563 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
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);
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;
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;
598 gfx::Rect icon_bounds =
599 launcher_->GetScreenBoundsOfItemIconForWindow(panel);
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;
608 if (panel->HasFocus() ||
609 panel->Contains(
610 aura::client::GetFocusClient(panel)->GetFocusedWindow())) {
611 DCHECK(!active_panel);
612 active_panel = panel;
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);
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;
652 FanOutPanels(visible_panels.begin() + first_overlapping_panel,
653 visible_panels.end());
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;
674 bool on_launcher = visible_panels[i].window->GetTargetBounds() == bounds;
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);
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;
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);
711 UpdateStacking(active_panel);
712 UpdateCallouts();
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_;
722 ShelfAlignment alignment = launcher_->alignment();
723 bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
724 alignment == SHELF_ALIGNMENT_BOTTOM;
726 // We want to to stack the panels like a deck of cards:
727 // ,--,--,--,-------.--.--.
728 // | | | | | | |
729 // | | | | | | |
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));
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;
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;
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;
769 void PanelLayoutManager::UpdateCallouts() {
770 ShelfAlignment alignment = launcher_->alignment();
771 bool horizontal = alignment == SHELF_ALIGNMENT_TOP ||
772 alignment == SHELF_ALIGNMENT_BOTTOM;
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;
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;
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());
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;
823 callout_bounds = ScreenAsh::ConvertRectFromScreen(
824 callout_widget->GetNativeWindow()->parent(),
825 callout_bounds);
827 SetChildBoundsDirect(callout_widget->GetNativeWindow(), callout_bounds);
828 panel_container_->StackChildAbove(callout_widget->GetNativeWindow(),
829 panel);
830 callout_widget->Show();
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);
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);
862 ////////////////////////////////////////////////////////////////////////////////
863 // keyboard::KeyboardControllerObserver implementation:
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();
872 } // namespace internal
873 } // namespace ash