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"
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"
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
{
65 CalloutWidgetBackground() : alignment_(SHELF_ALIGNMENT_BOTTOM
) {
68 virtual void Paint(gfx::Canvas
* canvas
, views::View
* view
) const OVERRIDE
{
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));
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));
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
));
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
));
94 // Hard code the arrow color for now.
96 paint
.setStyle(SkPaint::kFill_Style
);
97 paint
.setColor(SkColorSetARGB(0xff, 0xe5, 0xe5, 0xe5));
98 canvas
->DrawPath(path
, paint
);
101 ShelfAlignment
alignment() {
105 void set_alignment(ShelfAlignment alignment
) {
106 alignment_
= alignment
;
110 ShelfAlignment alignment_
;
112 DISALLOW_COPY_AND_ASSIGN(CalloutWidgetBackground
);
115 struct VisiblePanelPositionInfo
{
116 VisiblePanelPositionInfo()
128 aura::Window
* window
;
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
));
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 +
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
);
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
));
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
;
187 case SHELF_ALIGNMENT_BOTTOM
:
188 offset
.set_y(kPanelSlideInOffset
);
190 case SHELF_ALIGNMENT_LEFT
:
191 offset
.set_x(-kPanelSlideInOffset
);
193 case SHELF_ALIGNMENT_RIGHT
:
194 offset
.set_x(kPanelSlideInOffset
);
196 case SHELF_ALIGNMENT_TOP
:
197 offset
.set_y(-kPanelSlideInOffset
);
205 class PanelCalloutWidget
: public views::Widget
{
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
);
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()));
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);
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),
264 dragged_panel_(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())->
273 Shell::GetInstance()->display_controller()->AddObserver(this);
274 Shell::GetInstance()->AddShellObserver(this);
277 PanelLayoutManager::~PanelLayoutManager() {
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();
291 launcher_
->RemoveIconObserver(this);
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
;
305 void PanelLayoutManager::FinishDragging() {
306 dragged_panel_
= NULL
;
310 void PanelLayoutManager::SetLauncher(ash::Launcher
* 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
);
329 panel
->SetProperty(aura::client::kShowStateKey
, ui::SHOW_STATE_MINIMIZED
);
333 ////////////////////////////////////////////////////////////////////////////////
334 // PanelLayoutManager, aura::LayoutManager implementation:
335 void PanelLayoutManager::OnWindowResized() {
339 void PanelLayoutManager::OnWindowAddedToLayout(aura::Window
* child
) {
340 if (child
->type() == aura::client::WINDOW_TYPE_POPUP
)
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
);
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);
372 void PanelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window
* child
) {
375 void PanelLayoutManager::OnWindowRemovedFromLayout(aura::Window
* child
) {
376 if (child
->type() == aura::client::WINDOW_TYPE_POPUP
)
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
;
395 void PanelLayoutManager::OnChildWindowVisibilityChanged(aura::Window
* child
,
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();
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
);
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.
444 ////////////////////////////////////////////////////////////////////////////////
445 // PanelLayoutManager, ash::ShellObserver implementation:
447 void PanelLayoutManager::OnShelfAlignmentChanged(
448 aura::RootWindow
* root_window
) {
449 if (panel_container_
->GetRootWindow() == root_window
)
453 /////////////////////////////////////////////////////////////////////////////
454 // PanelLayoutManager, WindowObserver implementation:
456 void PanelLayoutManager::OnWindowPropertyChanged(aura::Window
* window
,
459 if (key
!= aura::client::kShowStateKey
)
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.
465 ui::WindowShowState new_state
=
466 window
->GetProperty(aura::client::kShowStateKey
);
467 if (new_state
== ui::SHOW_STATE_MINIMIZED
)
468 MinimizePanel(window
);
470 RestorePanel(window
);
473 void PanelLayoutManager::OnWindowVisibilityChanged(
474 aura::Window
* window
, bool 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.
486 gained_active
->type() == aura::client::WINDOW_TYPE_PANEL
&&
487 gained_active
->parent() == panel_container_
) {
488 UpdateStacking(gained_active
);
493 ////////////////////////////////////////////////////////////////////////////////
494 // PanelLayoutManager, DisplayController::Observer implementation:
496 void PanelLayoutManager::OnDisplayConfigurationChanged() {
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
) {
512 if (iter
->window
->IsVisible())
513 MinimizePanel(iter
->window
);
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
);
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
);
552 void PanelLayoutManager::RestorePanel(aura::Window
* panel
) {
557 void PanelLayoutManager::Relayout() {
558 if (!launcher_
|| !launcher_
->shelf_widget())
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
))) {
589 // If the shelf is currently hidden (full-screen mode), hide panel until
590 // full-screen mode is exited.
592 // The call to Hide does not set the minimize property, so the window will
593 // be restored when the shelf becomes visible again.
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)
608 if (panel
->HasFocus() ||
610 aura::client::GetFocusClient(panel
)->GetFocusedWindow())) {
611 DCHECK(!active_panel
);
612 active_panel
= panel
;
614 icon_bounds
= ScreenAsh::ConvertRectFromScreen(panel_container_
,
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_
)
658 bool slide_in
= visible_panels
[i
].slide_in
;
659 gfx::Rect bounds
= visible_panels
[i
].window
->GetTargetBounds();
661 case SHELF_ALIGNMENT_BOTTOM
:
662 bounds
.set_y(launcher_bounds
.y() - bounds
.height());
664 case SHELF_ALIGNMENT_LEFT
:
665 bounds
.set_x(launcher_bounds
.right());
667 case SHELF_ALIGNMENT_RIGHT
:
668 bounds
.set_x(launcher_bounds
.x() - bounds
.width());
670 case SHELF_ALIGNMENT_TOP
:
671 bounds
.set_y(launcher_bounds
.bottom());
674 bool on_launcher
= visible_panels
[i
].window
->GetTargetBounds() == bounds
;
677 bounds
.set_x(visible_panels
[i
].major_pos
-
678 visible_panels
[i
].major_length
/ 2);
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();
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.
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
);
703 layer
->SetOpacity(1);
705 // If the launcher moved don't animate, move immediately to the new
707 SetChildBoundsDirect(visible_panels
[i
].window
, bounds
);
711 UpdateStacking(active_panel
);
715 void PanelLayoutManager::UpdateStacking(aura::Window
* active_panel
) {
717 if (!last_active_panel_
)
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 // ,--,--,--,-------.--.--.
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,
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
) {
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
) {
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);
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;
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());
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());
810 case SHELF_ALIGNMENT_BOTTOM
:
811 callout_bounds
.set_y(bounds
.bottom());
813 case SHELF_ALIGNMENT_LEFT
:
814 callout_bounds
.set_x(bounds
.x() - callout_bounds
.width());
816 case SHELF_ALIGNMENT_RIGHT
:
817 callout_bounds
.set_x(bounds
.right());
819 case SHELF_ALIGNMENT_TOP
:
820 callout_bounds
.set_y(bounds
.y() - callout_bounds
.height());
823 callout_bounds
= ScreenAsh::ConvertRectFromScreen(
824 callout_widget
->GetNativeWindow()->parent(),
827 SetChildBoundsDirect(callout_widget
->GetNativeWindow(), callout_bounds
);
828 panel_container_
->StackChildAbove(callout_widget
->GetNativeWindow(),
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.
872 } // namespace internal