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

Unified Diff: ui/display/display_layout.cc

Issue 2573673003: Detect and fix overlapping displays (Closed)
Patch Set: Fix compile on Windows Created 4 years 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
« no previous file with comments | « ui/display/display_layout.h ('k') | ui/display/manager/display_manager.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/display/display_layout.cc
diff --git a/ui/display/display_layout.cc b/ui/display/display_layout.cc
index bf20ae4b07f77ad021451f195c8c29426dc8e686..4beea7b10079e98649092ad723a853c8f83b8a0a 100644
--- a/ui/display/display_layout.cc
+++ b/ui/display/display_layout.cc
@@ -36,11 +36,184 @@ bool IsIdInList(int64_t id, const DisplayIdList& list) {
return iter != list.end();
}
+// Ruturns nullptr if display with |id| is not found.
Display* FindDisplayById(Displays* display_list, int64_t id) {
auto iter =
std::find_if(display_list->begin(), display_list->end(),
[id](const Display& display) { return display.id() == id; });
- return &(*iter);
+ return iter == display_list->end() ? nullptr : &(*iter);
+}
+
+enum class IntersectionAxis {
+ POSITIVE_X,
+ NEGATIVE_X,
+ POSITIVE_Y,
+ NEGATIVE_Y,
+};
oshima 2017/01/09 21:46:46 can you document how logic works?
afakhry 2017/01/12 21:00:56 I added a comment above DeIntersectDisplays(), and
+
+// Returns true if the child and parent displays are sharing a border.
+bool AreDisplaysTouching(const Display& child_display,
+ const Display& parent_display,
+ DisplayPlacement::Position child_position) {
+ const gfx::Rect& a_bounds = child_display.bounds();
+ const gfx::Rect& b_bounds = parent_display.bounds();
oshima 2017/01/09 21:46:46 you can get intersects and check if the width is 1
afakhry 2017/01/12 21:00:56 Unfortunately, that doesn't work. The intersection
+
+ int rx = std::max(a_bounds.x(), b_bounds.x());
+ int ry = std::max(a_bounds.y(), b_bounds.y());
+ int rr = std::min(a_bounds.right(), b_bounds.right());
+ int rb = std::min(a_bounds.bottom(), b_bounds.bottom());
+
+ if (child_position == DisplayPlacement::TOP ||
+ child_position == DisplayPlacement::BOTTOM) {
+ return rb == ry;
+ }
+
+ return rr == rx;
+}
+
+void DeIntersectDisplaysAlong(IntersectionAxis axis,
+ int64_t primary_id,
+ std::vector<Display*>* displays,
+ std::vector<DisplayPlacement>* placement_list,
+ std::set<int64_t>* updated_displays) {
+ // Start processing in the required direction from the origin (i.e. from the
+ // primary display). This is needed as we never offset the primary display.
+ auto primary_itr =
+ std::find_if(displays->begin(), displays->end(),
+ [primary_id](Display* d) { return d->id() == primary_id; });
+ const int begin =
+ primary_itr == displays->end() ? 0 : primary_itr - displays->begin();
+ const int end = static_cast<int>(displays->size());
+ int increment = 1;
+
+ if (axis == IntersectionAxis::NEGATIVE_X ||
+ axis == IntersectionAxis::NEGATIVE_Y) {
+ increment = -1;
+ }
+
+ for (int i = begin; i >= 0 && i < end; i += increment) {
+ const Display* source_display = (*displays)[i];
+ const gfx::Rect source_bounds = source_display->bounds();
+ for (int j = i + increment; j >= 0 && j < end; j += increment) {
+ Display* target_display = (*displays)[j];
+ const gfx::Rect target_bounds = target_display->bounds();
+
+ gfx::Rect intersection = source_bounds;
+ intersection.Intersect(target_bounds);
+ if (intersection.IsEmpty())
oshima 2017/01/09 21:46:46 can't you also skip if they're touching?
afakhry 2017/01/12 21:00:56 Yes, but I need the intersection Rect anyways belo
+ continue;
+
+ // Offset the target display in the direction of the smaller overlap side
+ // if it's on the axis we're working on.
+ int offset_x = 0;
+ int offset_y = 0;
+ gfx::Point new_origin = target_bounds.origin();
+ if (intersection.width() <= intersection.height()) {
+ if (axis == IntersectionAxis::POSITIVE_X)
+ offset_x = source_bounds.right() - intersection.x();
+ else if (axis == IntersectionAxis::NEGATIVE_X)
+ offset_x = -(target_bounds.right() - intersection.x());
+ } else if (intersection.width() > intersection.height()) {
+ if (axis == IntersectionAxis::POSITIVE_Y)
+ offset_y = source_bounds.bottom() - intersection.y();
+ else if (axis == IntersectionAxis::NEGATIVE_Y)
+ offset_y = -(target_bounds.bottom() - intersection.y());
+ }
+
+ if (offset_x == 0 && offset_y == 0)
+ continue;
+
+ new_origin.Offset(offset_x, offset_y);
+ gfx::Insets insets = target_display->GetWorkAreaInsets();
+ target_display->set_bounds(gfx::Rect(new_origin, target_bounds.size()));
+ target_display->UpdateWorkAreaFromInsets(insets);
+
+ if (updated_displays)
+ updated_displays->insert(target_display->id());
+
+ // The offset target display may have moved such that it no longer touches
+ // its parent. Reparent if necessary.
+ auto target_display_placement_itr =
+ std::find_if(placement_list->begin(), placement_list->end(),
+ [&target_display](const DisplayPlacement& p) {
+ return p.display_id == target_display->id();
+ });
+ if (target_display_placement_itr == placement_list->end())
oshima 2017/01/09 21:46:46 this should happen only for primary, right?
afakhry 2017/01/12 21:00:56 As a matter of fact, this should never happen! We
+ continue;
+ if (target_display_placement_itr->parent_display_id ==
+ source_display->id()) {
+ continue;
+ }
+
+ auto parent_display_itr =
+ std::find_if(displays->begin(), displays->end(),
+ [&target_display_placement_itr](Display* display) {
+ return display->id() ==
+ target_display_placement_itr->parent_display_id;
+ });
+ if (parent_display_itr == displays->end())
oshima 2017/01/09 21:46:46 Can this happen?
afakhry 2017/01/12 21:00:56 It shouldn't. Replaced with a DCHECK.
+ continue;
+
+ if (AreDisplaysTouching(*target_display, *(*parent_display_itr),
+ target_display_placement_itr->position)) {
+ continue;
+ }
+
+ // Reparent the target to source and update the position. No need to
+ // update the offset here as it will be done later when UpdateOffsets()
+ // is called.
+ target_display_placement_itr->parent_display_id = source_display->id();
oshima 2017/01/09 21:46:46 this should not happen to the primary, otherwise i
afakhry 2017/01/12 21:00:56 The primary display is never the target display. S
+ if (axis == IntersectionAxis::POSITIVE_X ||
+ axis == IntersectionAxis::NEGATIVE_X) {
+ target_display_placement_itr->position =
+ axis == IntersectionAxis::POSITIVE_X ? DisplayPlacement::RIGHT
+ : DisplayPlacement::LEFT;
+ } else {
+ target_display_placement_itr->position =
+ axis == IntersectionAxis::POSITIVE_Y ? DisplayPlacement::BOTTOM
+ : DisplayPlacement::TOP;
+ }
+ }
+ }
+}
+
+void DeIntersectDisplays(int64_t primary_id,
+ Displays* display_list,
+ std::vector<DisplayPlacement>* placement_list,
+ std::set<int64_t>* updated_displays) {
+ std::vector<Display*> displays;
+ for (auto& display : *display_list)
+ displays.push_back(&display);
+
+ // Sort the displays by "Y" then by descending "X" and process in both
+ // directions.
+ std::sort(displays.begin(), displays.end(), [](Display* d1, Display* d2) {
+ if (d1->bounds().y() == d2->bounds().y()) {
+ // This makes displays with same y be sorted from right to left.
+ return d1->bounds().x() > d2->bounds().x();
+ }
+
+ return d1->bounds().y() < d2->bounds().y();
+ });
+ DeIntersectDisplaysAlong(IntersectionAxis::POSITIVE_Y, primary_id, &displays,
+ placement_list, updated_displays);
+ DeIntersectDisplaysAlong(IntersectionAxis::NEGATIVE_Y, primary_id, &displays,
+ placement_list, updated_displays);
+
+ // Sort the displays by "X" then by ascending "Y" and process in both
+ // directions.
+ std::sort(displays.begin(), displays.end(), [](Display* d1, Display* d2) {
+ if (d1->bounds().x() == d2->bounds().x()) {
+ // This makes displays with same x be sorted from top to bottom.
+ return d1->bounds().y() < d2->bounds().y();
+ }
+
+ return d1->bounds().x() < d2->bounds().x();
+ });
+ DeIntersectDisplaysAlong(IntersectionAxis::POSITIVE_X, primary_id, &displays,
+ placement_list, updated_displays);
+ DeIntersectDisplaysAlong(IntersectionAxis::NEGATIVE_X, primary_id, &displays,
+ placement_list, updated_displays);
oshima 2017/01/09 21:46:46 can't adjusting X will cause new overlap?
afakhry 2017/01/12 21:00:56 Yes, in a very rare case. To fix that, I modified
}
} // namespace
@@ -182,9 +355,10 @@ DisplayLayout::~DisplayLayout() {}
void DisplayLayout::ApplyToDisplayList(Displays* display_list,
std::vector<int64_t>* updated_ids,
- int minimum_offset_overlap) const {
+ int minimum_offset_overlap) {
// Layout from primary, then dependent displays.
std::set<int64_t> parents;
+ std::set<int64_t> updated_displays;
parents.insert(primary_id);
while (parents.size()) {
int64_t parent_id = *parents.begin();
@@ -192,14 +366,26 @@ void DisplayLayout::ApplyToDisplayList(Displays* display_list,
for (const DisplayPlacement& placement : placement_list) {
if (placement.parent_display_id == parent_id) {
if (ApplyDisplayPlacement(placement, display_list,
- minimum_offset_overlap) &&
- updated_ids) {
- updated_ids->push_back(placement.display_id);
+ minimum_offset_overlap)) {
+ updated_displays.insert(placement.display_id);
}
parents.insert(placement.display_id);
}
}
}
+
+ // Now that all the placements have been applied, we must detect and fix any
+ // overlapping displays.
+ DeIntersectDisplays(primary_id, display_list, &placement_list,
+ &updated_displays);
+
+ // The offsets might have changed and we must update them.
+ UpdateOffsets(display_list);
+
+ if (updated_ids) {
+ updated_ids->insert(updated_ids->begin(), updated_displays.begin(),
+ updated_displays.end());
+ }
}
// static
@@ -362,4 +548,26 @@ bool DisplayLayout::ApplyDisplayPlacement(const DisplayPlacement& placement,
return old_bounds != target_display->bounds();
}
+void DisplayLayout::UpdateOffsets(Displays* display_list) {
+ for (auto& placement : placement_list) {
+ const Display* child_display =
+ FindDisplayById(display_list, placement.display_id);
+ const Display* parent_display =
+ FindDisplayById(display_list, placement.parent_display_id);
+
+ if (!child_display || !parent_display)
+ continue;
+
+ const gfx::Rect& child_bounds = child_display->bounds();
+ const gfx::Rect& parent_bounds = parent_display->bounds();
+
+ if (placement.position == DisplayPlacement::TOP ||
+ placement.position == DisplayPlacement::BOTTOM) {
+ placement.offset = child_bounds.x() - parent_bounds.x();
+ } else {
+ placement.offset = child_bounds.y() - parent_bounds.y();
+ }
+ }
+}
+
} // namespace display
« no previous file with comments | « ui/display/display_layout.h ('k') | ui/display/manager/display_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698