| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/wm/dock/docked_window_layout_manager.h" |
| |
| #include "ash/launcher/launcher.h" |
| #include "ash/screen_ash.h" |
| #include "ash/shelf/shelf_layout_manager.h" |
| #include "ash/shelf/shelf_types.h" |
| #include "ash/shelf/shelf_widget.h" |
| #include "ash/shell.h" |
| #include "ash/shell_window_ids.h" |
| #include "ash/wm/coordinate_conversion.h" |
| #include "ash/wm/window_properties.h" |
| #include "ash/wm/window_util.h" |
| #include "base/auto_reset.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/aura/client/activation_client.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/focus_manager.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/aura/window.h" |
| #include "ui/gfx/rect.h" |
| |
| namespace ash { |
| namespace internal { |
| |
| // Minimum, maximum width of the dock area and a width of the gap |
| const int DockedWindowLayoutManager::kMinDockWidth = 200; |
| const int DockedWindowLayoutManager::kMaxDockWidth = 450; |
| const int DockedWindowLayoutManager::kMinDockGap = 2; |
| const int kWindowIdealSpacing = 4; |
| |
| namespace { |
| |
| const SkColor kDockBackgroundColor = SkColorSetARGB(0xff, 0x10, 0x10, 0x10); |
| const float kDockBackgroundOpacity = 0.5f; |
| |
| class DockedBackgroundWidget : public views::Widget { |
| public: |
| explicit DockedBackgroundWidget(aura::Window* container) { |
| InitWidget(container); |
| } |
| |
| private: |
| void InitWidget(aura::Window* parent) { |
| views::Widget::InitParams params; |
| params.type = views::Widget::InitParams::TYPE_POPUP; |
| params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; |
| params.can_activate = false; |
| params.keep_on_top = false; |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.parent = parent; |
| params.accept_events = false; |
| set_focus_on_creation(false); |
| Init(params); |
| DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow()); |
| views::View* content_view = new views::View; |
| content_view->set_background( |
| views::Background::CreateSolidBackground(kDockBackgroundColor)); |
| SetContentsView(content_view); |
| GetNativeWindow()->layer()->SetOpacity(kDockBackgroundOpacity); |
| Hide(); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget); |
| }; |
| |
| DockedWindowLayoutManager* GetDockLayoutManager(aura::Window* window, |
| const gfx::Point& location) { |
| gfx::Rect near_location(location, gfx::Size()); |
| aura::Window* dock = Shell::GetContainer( |
| wm::GetRootWindowMatching(near_location), |
| kShellWindowId_DockedContainer); |
| return static_cast<internal::DockedWindowLayoutManager*>( |
| dock->layout_manager()); |
| } |
| |
| // Certain windows (minimized, hidden or popups) do not matter to docking. |
| bool IsUsedByLayout(aura::Window* window) { |
| return (window->IsVisible() && |
| !wm::IsWindowMinimized(window) && |
| window->type() != aura::client::WINDOW_TYPE_POPUP); |
| } |
| |
| bool CompareWindowPos(const aura::Window* win1, const aura::Window* win2) { |
| return win1->GetTargetBounds().CenterPoint().y() < |
| win2->GetTargetBounds().CenterPoint().y(); |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager public implementation: |
| DockedWindowLayoutManager::DockedWindowLayoutManager( |
| aura::Window* dock_container) |
| : dock_container_(dock_container), |
| in_layout_(false), |
| dragged_window_(NULL), |
| launcher_(NULL), |
| shelf_layout_manager_(NULL), |
| shelf_hidden_(false), |
| docked_width_(0), |
| alignment_(DOCKED_ALIGNMENT_NONE), |
| last_active_window_(NULL), |
| background_widget_(new DockedBackgroundWidget(dock_container_)) { |
| DCHECK(dock_container); |
| aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> |
| AddObserver(this); |
| Shell::GetInstance()->AddShellObserver(this); |
| } |
| |
| DockedWindowLayoutManager::~DockedWindowLayoutManager() { |
| Shutdown(); |
| } |
| |
| void DockedWindowLayoutManager::Shutdown() { |
| if (shelf_layout_manager_) { |
| shelf_layout_manager_->RemoveObserver(this); |
| } |
| shelf_layout_manager_ = NULL; |
| launcher_ = NULL; |
| for (size_t i = 0; i < dock_container_->children().size(); ++i) |
| dock_container_->children()[i]->RemoveObserver(this); |
| aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> |
| RemoveObserver(this); |
| Shell::GetInstance()->RemoveShellObserver(this); |
| } |
| |
| void DockedWindowLayoutManager::AddObserver( |
| DockedWindowLayoutManagerObserver* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void DockedWindowLayoutManager::RemoveObserver( |
| DockedWindowLayoutManagerObserver* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| void DockedWindowLayoutManager::StartDragging(aura::Window* window) { |
| if (window->parent() != dock_container_) |
| return; |
| DCHECK(!dragged_window_); |
| dragged_window_ = window; |
| Relayout(); |
| } |
| |
| void DockedWindowLayoutManager::FinishDragging() { |
| if (!dragged_window_) |
| return; |
| |
| // If this is the first window getting docked - update alignment. |
| if (alignment_ == DOCKED_ALIGNMENT_NONE) { |
| alignment_ = AlignmentOfWindow(dragged_window_); |
| } else { |
| // There were some docked windows before. Check if the |dragged_window_| |
| // was the only one remaining and if so possibly switch alignment to |
| // opposite side. Ignore popups. |
| bool found_docked_window = false; |
| for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| aura::Window* window(dock_container_->children()[i]); |
| if (window != dragged_window_ && |
| window->type() != aura::client::WINDOW_TYPE_POPUP) { |
| found_docked_window = true; |
| break; |
| } |
| } |
| if (!found_docked_window) |
| alignment_ = AlignmentOfWindow(dragged_window_); |
| } |
| |
| // We no longer need to observe |dragged_window_| unless it is added back; |
| if (dragged_window_) { |
| if (dragged_window_->parent() != dock_container_) |
| dragged_window_->RemoveObserver(this); |
| dragged_window_ = NULL; |
| } |
| |
| Relayout(); |
| UpdateDockBounds(); |
| } |
| |
| // static |
| bool DockedWindowLayoutManager::ShouldWindowDock(aura::Window* window, |
| const gfx::Point& location) { |
| DockedWindowLayoutManager* layout_manager = GetDockLayoutManager(window, |
| location); |
| const DockedAlignment alignment = layout_manager->CalculateAlignment(); |
| const gfx::Rect& bounds(window->GetBoundsInScreen()); |
| const gfx::Rect docked_bounds = (alignment == DOCKED_ALIGNMENT_NONE) ? |
| layout_manager->dock_container_->GetBoundsInScreen() : |
| layout_manager->docked_bounds_; |
| DockedAlignment dock_edge = DOCKED_ALIGNMENT_NONE; |
| if (alignment == DOCKED_ALIGNMENT_NONE) { |
| // Check if the window is touching the edge - it may need to get docked. |
| dock_edge = layout_manager->AlignmentOfWindow(window); |
| } else if ((docked_bounds.Intersects(bounds) && |
| docked_bounds.Contains(location)) || |
| alignment == layout_manager->AlignmentOfWindow(window)) { |
| // A window is being added to other docked windows (on the same side). |
| // Both bounds and pointer location are checked because some drags involve |
| // sticky edges and so the |location| may be outside of the |bounds|. |
| // It is also possible that all the docked windows are minimized or hidden |
| // in which case the dragged window needs to be exactly touching the same |
| // edge that those docked windows were aligned before they got minimized. |
| dock_edge = alignment; |
| } |
| |
| // Do not allow docking on the same side as launcher shelf. |
| if (layout_manager->launcher_ && layout_manager->launcher_->shelf_widget()) { |
| ShelfAlignment shelf_alignment = |
| layout_manager->launcher_->shelf_widget()->GetAlignment(); |
| if ((shelf_alignment == SHELF_ALIGNMENT_LEFT && |
| dock_edge == DOCKED_ALIGNMENT_LEFT) || |
| (shelf_alignment == SHELF_ALIGNMENT_RIGHT && |
| dock_edge == DOCKED_ALIGNMENT_RIGHT)) { |
| dock_edge = DOCKED_ALIGNMENT_NONE; |
| } |
| } |
| |
| // Do not allow docking if a window is vertically maximized (as is the case |
| // when it is snapped). |
| const gfx::Rect work_area = |
| Shell::GetScreen()->GetDisplayNearestWindow(window).work_area(); |
| if (bounds.y() == work_area.y() && bounds.height() == work_area.height()) |
| dock_edge = DOCKED_ALIGNMENT_NONE; |
| |
| return dock_edge != DOCKED_ALIGNMENT_NONE; |
| } |
| |
| void DockedWindowLayoutManager::SetLauncher(ash::Launcher* launcher) { |
| DCHECK(!launcher_); |
| DCHECK(!shelf_layout_manager_); |
| launcher_ = launcher; |
| if (launcher_->shelf_widget()) { |
| shelf_layout_manager_ = ash::internal::ShelfLayoutManager::ForLauncher( |
| launcher_->shelf_widget()->GetNativeWindow()); |
| WillChangeVisibilityState(shelf_layout_manager_->visibility_state()); |
| shelf_layout_manager_->AddObserver(this); |
| } |
| } |
| |
| DockedAlignment DockedWindowLayoutManager::CalculateAlignment() const { |
| // Find a child that is not being dragged and is not a popup. |
| // If such exists the current alignment is returned - even if some of the |
| // children are hidden or minimized (so they can be restored without losing |
| // the docked state). |
| for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| aura::Window* window(dock_container_->children()[i]); |
| if (window != dragged_window_ && |
| window->type() != aura::client::WINDOW_TYPE_POPUP) { |
| return alignment_; |
| } |
| } |
| // No docked windows remain other than possibly the window being dragged. |
| // Return |NONE| to indicate that windows may get docked on either side. |
| return DOCKED_ALIGNMENT_NONE; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager, aura::LayoutManager implementation: |
| void DockedWindowLayoutManager::OnWindowResized() { |
| Relayout(); |
| // When screen resizes we need to update the insets even when dock width or |
| // alignment does not change. |
| UpdateDockBounds(); |
| } |
| |
| void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) { |
| if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| return; |
| |
| // If this is the first window getting docked - update alignment. |
| if (alignment_ == DOCKED_ALIGNMENT_NONE) |
| alignment_ = AlignmentOfWindow(child); |
| // We need to observe a child unless it is a |dragged_window_| that just got |
| // added back in which case we are already observing it. |
| if (child != dragged_window_) |
| child->AddObserver(this); |
| Relayout(); |
| UpdateDockBounds(); |
| } |
| |
| void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { |
| if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| return; |
| |
| // Try to find a child that is not a popup and is not being dragged. |
| bool found_docked_window = false; |
| for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| aura::Window* window(dock_container_->children()[i]); |
| if (window != dragged_window_ && |
| window->type() != aura::client::WINDOW_TYPE_POPUP) { |
| found_docked_window = true; |
| break; |
| } |
| } |
| if (!found_docked_window) |
| alignment_ = DOCKED_ALIGNMENT_NONE; |
| |
| // Keep track of former children if they are dragged as they can be reparented |
| // temporarily just for the drag. |
| if (child != dragged_window_) |
| child->RemoveObserver(this); |
| |
| if (last_active_window_ == child) |
| last_active_window_ = NULL; |
| |
| // Close the dock and expand workspace work area. |
| Relayout(); |
| |
| // When a panel is removed from this layout manager it may still be dragged. |
| // Delay updating dock and workspace bounds until the drag is completed. This |
| // preserves the docked area width until the panel drag is finished. |
| if (child->type() != aura::client::WINDOW_TYPE_PANEL) |
| UpdateDockBounds(); |
| } |
| |
| void DockedWindowLayoutManager::OnChildWindowVisibilityChanged( |
| aura::Window* child, |
| bool visible) { |
| if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| return; |
| if (visible) |
| child->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); |
| Relayout(); |
| UpdateDockBounds(); |
| } |
| |
| void DockedWindowLayoutManager::SetChildBounds( |
| aura::Window* child, |
| const gfx::Rect& requested_bounds) { |
| // Whenever one of our windows is moved or resized we need to enforce layout. |
| SetChildBoundsDirect(child, requested_bounds); |
| if (child->type() == aura::client::WINDOW_TYPE_POPUP) |
| return; |
| Relayout(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager, ash::ShellObserver implementation: |
| |
| void DockedWindowLayoutManager::OnShelfAlignmentChanged( |
| aura::RootWindow* root_window) { |
| if (dock_container_->GetRootWindow() != root_window) |
| return; |
| |
| if (!launcher_ || !launcher_->shelf_widget()) |
| return; |
| |
| if (alignment_ == DOCKED_ALIGNMENT_NONE) |
| return; |
| |
| // Do not allow launcher and dock on the same side. Switch side that |
| // the dock is attached to and move all dock windows to that new side. |
| ShelfAlignment shelf_alignment = launcher_->shelf_widget()->GetAlignment(); |
| if (alignment_ == DOCKED_ALIGNMENT_LEFT && |
| shelf_alignment == SHELF_ALIGNMENT_LEFT) { |
| alignment_ = DOCKED_ALIGNMENT_RIGHT; |
| } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT && |
| shelf_alignment == SHELF_ALIGNMENT_RIGHT) { |
| alignment_ = DOCKED_ALIGNMENT_LEFT; |
| } |
| Relayout(); |
| UpdateDockBounds(); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager, WindowObserver implementation: |
| |
| void DockedWindowLayoutManager::OnWindowPropertyChanged(aura::Window* window, |
| const void* key, |
| intptr_t old) { |
| if (key != aura::client::kShowStateKey) |
| return; |
| // The window property will still be set, but no actual change will occur |
| // until WillChangeVisibilityState is called when the shelf is visible again |
| if (shelf_hidden_) |
| return; |
| if (wm::IsWindowMinimized(window)) |
| MinimizeWindow(window); |
| else |
| RestoreWindow(window); |
| } |
| |
| void DockedWindowLayoutManager::OnWindowBoundsChanged( |
| aura::Window* window, |
| const gfx::Rect& old_bounds, |
| const gfx::Rect& new_bounds) { |
| if (window == dragged_window_) |
| Relayout(); |
| } |
| |
| void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) { |
| if (dragged_window_ == window) |
| dragged_window_ = NULL; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager, aura::client::ActivationChangeObserver implementation: |
| |
| void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active, |
| aura::Window* lost_active) { |
| // Ignore if the window that is not managed by this was activated. |
| aura::Window* ancestor = NULL; |
| for (aura::Window* parent = gained_active; |
| parent; parent = parent->parent()) { |
| if (parent->parent() == dock_container_) { |
| ancestor = parent; |
| break; |
| } |
| } |
| if (ancestor) |
| UpdateStacking(ancestor); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager, ShelfLayoutManagerObserver implementation: |
| |
| void DockedWindowLayoutManager::WillChangeVisibilityState( |
| ShelfVisibilityState new_state) { |
| // On entering / leaving full screen mode the shelf visibility state is |
| // changed to / from SHELF_HIDDEN. In this state, docked windows should hide |
| // to allow the full-screen application to use the full screen. |
| |
| // TODO(varkha): ShelfLayoutManager::UpdateVisibilityState sets state to |
| // SHELF_AUTO_HIDE when in immersive mode. We need to distinguish this from |
| // when shelf enters auto-hide state based on mouse hover when auto-hide |
| // setting is enabled and hide all windows (immersive mode should hide docked |
| // windows). |
| shelf_hidden_ = new_state == ash::SHELF_HIDDEN; |
| { |
| // prevent Relayout from getting called multiple times during this |
| base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); |
| for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| aura::Window* window = dock_container_->children()[i]; |
| if (shelf_hidden_) { |
| if (window->IsVisible()) |
| MinimizeWindow(window); |
| } else { |
| if (!wm::IsWindowMinimized(window)) |
| RestoreWindow(window); |
| } |
| } |
| } |
| Relayout(); |
| UpdateDockBounds(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager private implementation: |
| |
| void DockedWindowLayoutManager::MinimizeWindow(aura::Window* window) { |
| window->Hide(); |
| if (wm::IsActiveWindow(window)) |
| wm::DeactivateWindow(window); |
| } |
| |
| void DockedWindowLayoutManager::RestoreWindow(aura::Window* window) { |
| window->Show(); |
| } |
| |
| DockedAlignment DockedWindowLayoutManager::AlignmentOfWindow( |
| const aura::Window* window) const { |
| const gfx::Rect& bounds(window->GetBoundsInScreen()); |
| const gfx::Rect docked_bounds = dock_container_->GetBoundsInScreen(); |
| if (bounds.x() == docked_bounds.x()) |
| return DOCKED_ALIGNMENT_LEFT; |
| if (bounds.right() == docked_bounds.right()) |
| return DOCKED_ALIGNMENT_RIGHT; |
| return DOCKED_ALIGNMENT_NONE; |
| } |
| |
| void DockedWindowLayoutManager::Relayout() { |
| if (in_layout_ || alignment_ == DOCKED_ALIGNMENT_NONE) |
| return; |
| base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); |
| |
| gfx::Rect dock_bounds = dock_container_->bounds(); |
| aura::Window* active_window = NULL; |
| aura::Window::Windows visible_windows; |
| docked_width_ = 0; |
| for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| aura::Window* window(dock_container_->children()[i]); |
| |
| if (!IsUsedByLayout(window)) |
| continue; |
| |
| // If the shelf is currently hidden (full-screen mode), hide window until |
| // full-screen mode is exited. |
| if (shelf_hidden_) { |
| // The call to Hide does not set the minimize property, so the window will |
| // be restored when the shelf becomes visible again. |
| window->Hide(); |
| continue; |
| } |
| if (window->HasFocus() || |
| window->Contains( |
| aura::client::GetFocusClient(window)->GetFocusedWindow())) { |
| DCHECK(!active_window); |
| active_window = window; |
| } |
| visible_windows.push_back(window); |
| } |
| |
| // Consider windows that were formerly children of the |dock_container_| |
| // when fanning out other child windows. |
| if (dragged_window_ && dragged_window_->parent() != dock_container_) |
| visible_windows.push_back(dragged_window_); |
| |
| // Sort windows by their center positions and fan out overlapping |
| // windows. |
| std::sort(visible_windows.begin(), visible_windows.end(), CompareWindowPos); |
| gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow( |
| dock_container_); |
| const gfx::Rect work_area = display.work_area(); |
| int available_room = work_area.height(); |
| for (aura::Window::Windows::const_iterator iter = visible_windows.begin(); |
| iter != visible_windows.end(); ++iter) { |
| available_room -= (*iter)->bounds().height(); |
| } |
| const int num_windows = visible_windows.size(); |
| const float delta = (float)available_room / |
| ((available_room > 0 || num_windows <= 1) ? |
| num_windows + 1 : num_windows - 1); |
| float y_pos = (available_room > 0) ? delta : 0; |
| |
| for (aura::Window::Windows::const_iterator iter = visible_windows.begin(); |
| iter != visible_windows.end(); ++iter) { |
| aura::Window* window = *iter; |
| gfx::Rect bounds = window->GetTargetBounds(); |
| // Do not force position of a single window. |
| if (num_windows == 1) |
| y_pos = bounds.y(); |
| |
| // Fan out windows evenly distributing the overlap or remaining free space. |
| bounds.set_y(std::max(work_area.y(), |
| std::min(work_area.bottom() - bounds.height(), |
| static_cast<int>(y_pos + 0.5)))); |
| y_pos += bounds.height() + delta; |
| |
| // All docked windows other than the one currently dragged remain stuck |
| // to the screen edge. |
| if (window == dragged_window_) |
| continue; |
| switch (alignment_) { |
| case DOCKED_ALIGNMENT_LEFT: |
| bounds.set_x(0); |
| break; |
| case DOCKED_ALIGNMENT_RIGHT: |
| bounds.set_x(dock_bounds.right() - bounds.width()); |
| break; |
| case DOCKED_ALIGNMENT_NONE: |
| NOTREACHED() << "Relayout called when dock alignment is 'NONE'"; |
| break; |
| } |
| // Keep the dock at least kMinDockWidth when all windows in it overhang. |
| docked_width_ = std::min(kMaxDockWidth, |
| std::max(docked_width_, |
| bounds.width() > kMaxDockWidth ? |
| kMinDockWidth : bounds.width())); |
| SetChildBoundsDirect(window, bounds); |
| } |
| UpdateStacking(active_window); |
| } |
| |
| void DockedWindowLayoutManager::UpdateDockBounds() { |
| int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0); |
| gfx::Rect bounds = gfx::Rect( |
| alignment_ == DOCKED_ALIGNMENT_RIGHT ? |
| dock_container_->bounds().right() - dock_inset: |
| dock_container_->bounds().x(), |
| dock_container_->bounds().y(), |
| dock_inset, |
| dock_container_->bounds().height()); |
| docked_bounds_ = bounds + |
| dock_container_->GetBoundsInScreen().OffsetFromOrigin(); |
| FOR_EACH_OBSERVER( |
| DockedWindowLayoutManagerObserver, |
| observer_list_, |
| OnDockBoundsChanging(bounds)); |
| |
| // Show or hide background for docked area. |
| gfx::Rect background_bounds(docked_bounds_); |
| const gfx::Rect work_area = |
| Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area(); |
| background_bounds.set_height(work_area.height()); |
| background_widget_->SetBounds(background_bounds); |
| if (docked_width_ > 0) |
| background_widget_->Show(); |
| else |
| background_widget_->Hide(); |
| } |
| |
| void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) { |
| if (!active_window) { |
| if (!last_active_window_) |
| return; |
| active_window = last_active_window_; |
| } |
| |
| // We want to to stack the windows like a deck of cards: |
| // ,------. |
| // |,------.| |
| // |,------.| |
| // | active | |
| // | window | |
| // |`------'| |
| // |`------'| |
| // `------' |
| // We use the middle of each window to figure out how to stack the window. |
| // This allows us to update the stacking when a window is being dragged around |
| // by the titlebar. |
| std::map<int, aura::Window*> window_ordering; |
| for (aura::Window::Windows::const_iterator it = |
| dock_container_->children().begin(); |
| it != dock_container_->children().end(); ++it) { |
| if (!IsUsedByLayout(*it)) |
| continue; |
| gfx::Rect bounds = (*it)->bounds(); |
| window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2, |
| *it)); |
| } |
| int active_center_y = active_window->bounds().CenterPoint().y(); |
| |
| aura::Window* previous_window = background_widget_->GetNativeWindow(); |
| for (std::map<int, aura::Window*>::const_iterator it = |
| window_ordering.begin(); |
| it != window_ordering.end() && it->first < active_center_y; ++it) { |
| if (previous_window) |
| dock_container_->StackChildAbove(it->second, previous_window); |
| previous_window = it->second; |
| } |
| |
| previous_window = NULL; |
| for (std::map<int, aura::Window*>::const_reverse_iterator it = |
| window_ordering.rbegin(); |
| it != window_ordering.rend() && it->first > active_center_y; ++it) { |
| if (previous_window) |
| dock_container_->StackChildAbove(it->second, previous_window); |
| previous_window = it->second; |
| } |
| |
| if (active_window->parent() == dock_container_) |
| dock_container_->StackChildAtTop(active_window); |
| if (dragged_window_ && dragged_window_->parent() == dock_container_) |
| dock_container_->StackChildAtTop(dragged_window_); |
| last_active_window_ = active_window; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // keyboard::KeyboardControllerObserver implementation: |
| |
| void DockedWindowLayoutManager::OnKeyboardBoundsChanging( |
| const gfx::Rect& keyboard_bounds) { |
| // This bounds change will have caused a change to the Shelf which does not |
| // propagate automatically to this class, so manually recalculate bounds. |
| UpdateDockBounds(); |
| } |
| |
| } // namespace internal |
| } // namespace ash |