Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3690)

Unified Diff: ash/wm/dock/docked_window_layout_manager.cc

Issue 19054013: Implement automatic layout and stacking for docked windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@issue_233331_sized
Patch Set: Implement automatic layout and stacking (published a flag) Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
}
////////////////////////////////////////////////////////////////////////////////

Powered by Google App Engine
This is Rietveld 408576698