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; |
} |
//////////////////////////////////////////////////////////////////////////////// |