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 8ae4c0bcb7779fe798f63172cffc44d92c298c34..e0b6df8820dd85e4fa6db4cb428de11e937a8f94 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,54 @@ bool IsUsedByLayout(aura::Window* window) { |
| window->type() != aura::client::WINDOW_TYPE_POPUP); |
| } |
| +struct VisibleWindowPositionInfo { |
| + VisibleWindowPositionInfo() |
| + : major_pos(0), |
| + major_length(0), |
| + window(NULL) {} |
| + |
| + int major_pos; |
| + int major_length; |
| + aura::Window* window; |
| +}; |
| + |
| +bool CompareWindowMajor(const VisibleWindowPositionInfo& win1, |
| + const VisibleWindowPositionInfo& win2) { |
| + return win1.major_pos < win2.major_pos; |
| +} |
| + |
| +void FanOutWindows(std::vector<VisibleWindowPositionInfo>::iterator first, |
| + std::vector<VisibleWindowPositionInfo>::iterator last, |
| + int min_major, |
| + int max_major) { |
| + int num_windows = last - first; |
| + if (num_windows <= 0) |
| + return; |
| + if (num_windows == 1) { |
| + (*first).major_pos = std::max(min_major, std::min( |
| + max_major, (*first).major_pos)); |
| + return; |
| + } |
| + |
| + // If there are more than one window, fan them out from minimum position to |
| + // maximum position leaving equal gaps or overlapping by the same amount. |
| + int available_room = max_major - min_major + (*first).major_length / 2 + |
| + (*(last - 1)).major_length - (*(last - 1)).major_length / 2; |
| + for (std::vector<VisibleWindowPositionInfo>::iterator iter = first; |
| + iter != last; ++iter) { |
| + available_room -= (*iter).major_length; |
| + } |
| + const int delta = available_room / |
| + (available_room > 0 ? num_windows + 1 : num_windows - 1); |
| + int major_pos = (available_room > 0) ? delta : 0; |
| + for (std::vector<VisibleWindowPositionInfo>::iterator iter = first; |
| + iter != last; ++iter) { |
| + major_pos += (*iter).major_length / 2; |
| + (*iter).major_pos = std::max(min_major, std::min(max_major, major_pos)); |
| + major_pos += (*iter).major_length - (*iter).major_length / 2 + delta; |
| + } |
| +} |
| + |
| } // namespace |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -57,12 +106,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); |
| @@ -133,6 +184,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(); |
| } |
| @@ -241,7 +299,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(); |
| } |
| @@ -265,7 +327,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(); |
| @@ -344,6 +412,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: |
| @@ -427,6 +505,7 @@ void DockedWindowLayoutManager::Relayout() { |
| gfx::Rect dock_bounds = dock_container_->bounds(); |
| aura::Window* active_window = NULL; |
| + std::vector<VisibleWindowPositionInfo> visible_windows; |
| docked_width_ = 0; |
| for (size_t i = 0; i < dock_container_->children().size(); ++i) { |
| aura::Window* window(dock_container_->children()[i]); |
| @@ -450,27 +529,68 @@ void DockedWindowLayoutManager::Relayout() { |
| active_window = window; |
| } |
| - // all docked windows other than the one currently dragged remain stuck |
| - // to the screen edge |
| + VisibleWindowPositionInfo position_info; |
| + position_info.major_length = window->GetTargetBounds().height(); |
| + position_info.major_pos = |
| + window->GetTargetBounds().y() + position_info.major_length / 2; |
| + position_info.window = window; |
| + visible_windows.push_back(position_info); |
| + } |
| + |
| + // Consider windows that were formerly children of the |dock_container_| |
| + // when fanning out other child windows. |
| + if (dragged_former_child_) { |
| + VisibleWindowPositionInfo position_info; |
| + position_info.major_length = |
| + dragged_former_child_->GetTargetBounds().height(); |
| + position_info.major_pos = dragged_former_child_->GetTargetBounds().y() + |
| + position_info.major_length / 2; |
| + position_info.window = dragged_former_child_; |
| + visible_windows.push_back(position_info); |
|
flackr
2013/07/16 22:05:45
The window position is entirely based on the major
varkha
2013/07/18 21:56:59
Done.
|
| + } |
| + |
| + // Sort windows by their center positions and fan out overlapping |
| + // windows. The fan out method may result in new overlapping windows however |
| + // it is unlikely that this overlap will completely obscure a window. |
| + std::sort(visible_windows.begin(), visible_windows.end(), CompareWindowMajor); |
| + if (!visible_windows.empty()) { |
| + gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow( |
| + dock_container_); |
| + const gfx::Rect work_area = display.work_area(); |
| + int min_major = work_area.y() + |
| + (*visible_windows.begin()).major_length / 2; |
| + int max_major = work_area.bottom() - |
| + (*visible_windows.rbegin()).major_length / 2; |
| + FanOutWindows(visible_windows.begin(), visible_windows.end(), |
| + min_major, max_major); |
|
flackr
2013/07/16 22:05:45
Since there's only one pass / one iteration, it wo
varkha
2013/07/18 21:56:59
Done.
|
| + } |
| + |
| + for (size_t i = 0; i < visible_windows.size(); ++i) { |
| + aura::Window* window(visible_windows[i].window); |
| + |
| + // All docked windows other than the one currently dragged remain stuck |
| + // to the screen edge. |
| + if (window == dragged_window_) |
| + continue; |
| 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())); |
| + bounds.set_y(visible_windows[i].major_pos - |
| + visible_windows[i].major_length / 2); |
| + 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())); |
| if (bounds != window->GetTargetBounds()) |
| LOG(INFO) << "Moved from " << window->GetTargetBounds().ToString() << |
| " to " << bounds.ToString(); |
| @@ -498,8 +618,60 @@ 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: |
| + // ,--,--,--,-------.--.--. |
| + // | | | | | | | |
| + // | | | | | | | |
|
flackr
2013/07/16 22:05:45
nit: Should be a vertical deck of cards.
varkha
2013/07/18 21:56:59
Done.
|
| + // |
| + // 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)); |
| + } |
| + // Consider windows that were formerly children of the |dock_container_| |
| + // when fanning out other child windows. |
| + if (dragged_former_child_) { |
| + gfx::Rect bounds = dragged_former_child_->bounds(); |
| + window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2, |
| + dragged_former_child_)); |
| + } |
| + |
| + |
|
flackr
2013/07/16 22:05:45
nit: Only one empty line.
varkha
2013/07/18 21:56:59
Done.
|
| + aura::Window* previous_window = NULL; |
| + for (std::map<int, aura::Window*>::const_iterator it = |
| + window_ordering.begin(); |
| + it != window_ordering.end() && it->second != active_window; ++it) { |
|
flackr
2013/07/16 22:05:45
How about using the midpoint of the active window
varkha
2013/07/18 21:56:59
Done.
|
| + if (previous_window && previous_window->parent() == dock_container_) |
| + 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->second != active_window; ++it) { |
| + if (previous_window && previous_window->parent() == dock_container_) |
| + dock_container_->StackChildAbove(it->second, previous_window); |
| + previous_window = it->second; |
| + } |
| + |
| + if (active_window && 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; |
| } |
| //////////////////////////////////////////////////////////////////////////////// |