Chromium Code Reviews| Index: ash/wm/dock/docked_window_layout_manager.cc |
| diff --git a/ash/wm/dock/docked_window_layout_manager.cc b/ash/wm/dock/docked_window_layout_manager.cc |
| index c991dc4393c6e44fe48011577c18e36a437d5a86..34b67580a07ed0da2518d84fd369e3334e638961 100644 |
| --- a/ash/wm/dock/docked_window_layout_manager.cc |
| +++ b/ash/wm/dock/docked_window_layout_manager.cc |
| @@ -29,6 +29,7 @@ namespace internal { |
| const int DockedWindowLayoutManager::kMinDockWidth = 200; |
| const int DockedWindowLayoutManager::kMaxDockWidth = 450; |
| const int DockedWindowLayoutManager::kMinDockGap = 2; |
| +const int kWindowIdealSpacing = 4; |
| namespace { |
| @@ -49,6 +50,11 @@ bool IsUsedByLayout(aura::Window* 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 |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -57,12 +63,14 @@ DockedWindowLayoutManager::DockedWindowLayoutManager( |
| aura::Window* dock_container) |
| : dock_container_(dock_container), |
| in_layout_(false), |
| + dragged_former_child_(NULL), |
| dragged_window_(NULL), |
| launcher_(NULL), |
| shelf_layout_manager_(NULL), |
| shelf_hidden_(false), |
| docked_width_(0), |
| - alignment_(DOCKED_ALIGNMENT_NONE) { |
| + alignment_(DOCKED_ALIGNMENT_NONE), |
| + last_active_(NULL) { |
| DCHECK(dock_container); |
| aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> |
| AddObserver(this); |
| @@ -129,6 +137,13 @@ void DockedWindowLayoutManager::FinishDragging() { |
| } |
| dragged_window_ = NULL; |
| + // We no longer need |dragged_former_child_| if it is added back; |
| + if (dragged_former_child_) { |
| + if (dragged_former_child_->parent() != dock_container_) |
| + dragged_former_child_->RemoveObserver(this); |
| + dragged_former_child_ = NULL; |
| + } |
| + |
| Relayout(); |
| UpdateDockBounds(); |
| } |
| @@ -226,7 +241,11 @@ void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) { |
| // If this is the first window getting docked - update alignment. |
| if (alignment_ == DOCKED_ALIGNMENT_NONE) |
| alignment_ = AlignmentOfWindow(child); |
| - child->AddObserver(this); |
| + // We no longer need |dragged_former_child_| if it is added back. |
| + if (child == dragged_former_child_) |
| + dragged_former_child_ = NULL; |
| + else |
| + child->AddObserver(this); |
| Relayout(); |
| UpdateDockBounds(); |
| } |
| @@ -248,7 +267,13 @@ void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { |
| if (!found_docked_window) |
| alignment_ = DOCKED_ALIGNMENT_NONE; |
| - child->RemoveObserver(this); |
| + // Keep track of former children if they are dragged as they can be reparented |
| + // temporarily just for the drag. |
| + if (child == dragged_window_) |
| + dragged_former_child_ = child; |
| + else |
| + child->RemoveObserver(this); |
| + |
| // Close the dock and expand workspace work area. |
| Relayout(); |
| @@ -326,6 +351,16 @@ void DockedWindowLayoutManager::OnWindowPropertyChanged(aura::Window* window, |
| RestoreWindow(window); |
| } |
| +void DockedWindowLayoutManager::OnWindowBoundsChanged( |
| + aura::Window* window, |
| + const gfx::Rect& old_bounds, |
| + const gfx::Rect& new_bounds) { |
| + if (window == dragged_former_child_) |
| + Relayout(); |
| + if (window == dragged_former_child_) |
| + LOG(INFO) << "OnWindowBoundsChanged"; |
| +} |
| + |
| //////////////////////////////////////////////////////////////////////////////// |
| // DockLayoutManager, aura::client::ActivationChangeObserver implementation: |
| @@ -408,6 +443,7 @@ void DockedWindowLayoutManager::Relayout() { |
| 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]); |
| @@ -423,35 +459,71 @@ void DockedWindowLayoutManager::Relayout() { |
| window->Hide(); |
| continue; |
| } |
| - |
| if (window->HasFocus() || |
| window->Contains( |
| aura::client::GetFocusClient(window)->GetFocusedWindow())) { |
| DCHECK(!active_window); |
| active_window = window; |
| } |
| + visible_windows.push_back(window); |
| + } |
| - // all docked windows other than the one currently dragged remain stuck |
| - // to the screen edge |
| + // Consider windows that were formerly children of the |dock_container_| |
| + // when fanning out other child windows. |
| + if (dragged_former_child_) |
| + visible_windows.push_back(dragged_former_child_); |
| + |
| + // 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 int delta = available_room / |
| + ((available_room > 0 || num_windows <= 1) ? |
| + num_windows + 1 : num_windows - 1); |
| + int 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(); |
| - if (window != dragged_window_) { |
| - 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())); |
| + // 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(), |
| + y_pos))); |
|
flackr
2013/07/18 22:16:53
The clamping within the work area should never be
varkha
2013/07/18 22:58:14
I think it may still need clamping in valid corner
|
| + 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); |
| @@ -475,8 +547,57 @@ void DockedWindowLayoutManager::UpdateDockBounds() { |
| } |
| void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) { |
| - // TODO(varkha): Implement restacking to ensure that all docked windows are at |
| - // least partially visible and selectable. |
| + if (!active_window) { |
| + if (!last_active_) |
| + return; |
| + active_window = last_active_; |
| + } |
| + |
| + // 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) { |
| + 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 = NULL; |
| + 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_ = active_window; |
| } |
| //////////////////////////////////////////////////////////////////////////////// |