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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/display/display_layout.h" 5 #include "ui/display/display_layout.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <set> 8 #include <set>
9 #include <sstream> 9 #include <sstream>
10 10
(...skipping 18 matching lines...) Expand all
29 // to change this value in case to support even larger displays. 29 // to change this value in case to support even larger displays.
30 const int kMaxValidOffset = 10000; 30 const int kMaxValidOffset = 10000;
31 31
32 bool IsIdInList(int64_t id, const DisplayIdList& list) { 32 bool IsIdInList(int64_t id, const DisplayIdList& list) {
33 const auto iter = 33 const auto iter =
34 std::find_if(list.begin(), list.end(), 34 std::find_if(list.begin(), list.end(),
35 [id](int64_t display_id) { return display_id == id; }); 35 [id](int64_t display_id) { return display_id == id; });
36 return iter != list.end(); 36 return iter != list.end();
37 } 37 }
38 38
39 // Ruturns nullptr if display with |id| is not found.
39 Display* FindDisplayById(Displays* display_list, int64_t id) { 40 Display* FindDisplayById(Displays* display_list, int64_t id) {
40 auto iter = 41 auto iter =
41 std::find_if(display_list->begin(), display_list->end(), 42 std::find_if(display_list->begin(), display_list->end(),
42 [id](const Display& display) { return display.id() == id; }); 43 [id](const Display& display) { return display.id() == id; });
43 return &(*iter); 44 return iter == display_list->end() ? nullptr : &(*iter);
45 }
46
47 enum class IntersectionAxis {
48 POSITIVE_X,
49 NEGATIVE_X,
50 POSITIVE_Y,
51 NEGATIVE_Y,
52 };
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
53
54 // Returns true if the child and parent displays are sharing a border.
55 bool AreDisplaysTouching(const Display& child_display,
56 const Display& parent_display,
57 DisplayPlacement::Position child_position) {
58 const gfx::Rect& a_bounds = child_display.bounds();
59 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
60
61 int rx = std::max(a_bounds.x(), b_bounds.x());
62 int ry = std::max(a_bounds.y(), b_bounds.y());
63 int rr = std::min(a_bounds.right(), b_bounds.right());
64 int rb = std::min(a_bounds.bottom(), b_bounds.bottom());
65
66 if (child_position == DisplayPlacement::TOP ||
67 child_position == DisplayPlacement::BOTTOM) {
68 return rb == ry;
69 }
70
71 return rr == rx;
72 }
73
74 void DeIntersectDisplaysAlong(IntersectionAxis axis,
75 int64_t primary_id,
76 std::vector<Display*>* displays,
77 std::vector<DisplayPlacement>* placement_list,
78 std::set<int64_t>* updated_displays) {
79 // Start processing in the required direction from the origin (i.e. from the
80 // primary display). This is needed as we never offset the primary display.
81 auto primary_itr =
82 std::find_if(displays->begin(), displays->end(),
83 [primary_id](Display* d) { return d->id() == primary_id; });
84 const int begin =
85 primary_itr == displays->end() ? 0 : primary_itr - displays->begin();
86 const int end = static_cast<int>(displays->size());
87 int increment = 1;
88
89 if (axis == IntersectionAxis::NEGATIVE_X ||
90 axis == IntersectionAxis::NEGATIVE_Y) {
91 increment = -1;
92 }
93
94 for (int i = begin; i >= 0 && i < end; i += increment) {
95 const Display* source_display = (*displays)[i];
96 const gfx::Rect source_bounds = source_display->bounds();
97 for (int j = i + increment; j >= 0 && j < end; j += increment) {
98 Display* target_display = (*displays)[j];
99 const gfx::Rect target_bounds = target_display->bounds();
100
101 gfx::Rect intersection = source_bounds;
102 intersection.Intersect(target_bounds);
103 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
104 continue;
105
106 // Offset the target display in the direction of the smaller overlap side
107 // if it's on the axis we're working on.
108 int offset_x = 0;
109 int offset_y = 0;
110 gfx::Point new_origin = target_bounds.origin();
111 if (intersection.width() <= intersection.height()) {
112 if (axis == IntersectionAxis::POSITIVE_X)
113 offset_x = source_bounds.right() - intersection.x();
114 else if (axis == IntersectionAxis::NEGATIVE_X)
115 offset_x = -(target_bounds.right() - intersection.x());
116 } else if (intersection.width() > intersection.height()) {
117 if (axis == IntersectionAxis::POSITIVE_Y)
118 offset_y = source_bounds.bottom() - intersection.y();
119 else if (axis == IntersectionAxis::NEGATIVE_Y)
120 offset_y = -(target_bounds.bottom() - intersection.y());
121 }
122
123 if (offset_x == 0 && offset_y == 0)
124 continue;
125
126 new_origin.Offset(offset_x, offset_y);
127 gfx::Insets insets = target_display->GetWorkAreaInsets();
128 target_display->set_bounds(gfx::Rect(new_origin, target_bounds.size()));
129 target_display->UpdateWorkAreaFromInsets(insets);
130
131 if (updated_displays)
132 updated_displays->insert(target_display->id());
133
134 // The offset target display may have moved such that it no longer touches
135 // its parent. Reparent if necessary.
136 auto target_display_placement_itr =
137 std::find_if(placement_list->begin(), placement_list->end(),
138 [&target_display](const DisplayPlacement& p) {
139 return p.display_id == target_display->id();
140 });
141 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
142 continue;
143 if (target_display_placement_itr->parent_display_id ==
144 source_display->id()) {
145 continue;
146 }
147
148 auto parent_display_itr =
149 std::find_if(displays->begin(), displays->end(),
150 [&target_display_placement_itr](Display* display) {
151 return display->id() ==
152 target_display_placement_itr->parent_display_id;
153 });
154 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.
155 continue;
156
157 if (AreDisplaysTouching(*target_display, *(*parent_display_itr),
158 target_display_placement_itr->position)) {
159 continue;
160 }
161
162 // Reparent the target to source and update the position. No need to
163 // update the offset here as it will be done later when UpdateOffsets()
164 // is called.
165 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
166 if (axis == IntersectionAxis::POSITIVE_X ||
167 axis == IntersectionAxis::NEGATIVE_X) {
168 target_display_placement_itr->position =
169 axis == IntersectionAxis::POSITIVE_X ? DisplayPlacement::RIGHT
170 : DisplayPlacement::LEFT;
171 } else {
172 target_display_placement_itr->position =
173 axis == IntersectionAxis::POSITIVE_Y ? DisplayPlacement::BOTTOM
174 : DisplayPlacement::TOP;
175 }
176 }
177 }
178 }
179
180 void DeIntersectDisplays(int64_t primary_id,
181 Displays* display_list,
182 std::vector<DisplayPlacement>* placement_list,
183 std::set<int64_t>* updated_displays) {
184 std::vector<Display*> displays;
185 for (auto& display : *display_list)
186 displays.push_back(&display);
187
188 // Sort the displays by "Y" then by descending "X" and process in both
189 // directions.
190 std::sort(displays.begin(), displays.end(), [](Display* d1, Display* d2) {
191 if (d1->bounds().y() == d2->bounds().y()) {
192 // This makes displays with same y be sorted from right to left.
193 return d1->bounds().x() > d2->bounds().x();
194 }
195
196 return d1->bounds().y() < d2->bounds().y();
197 });
198 DeIntersectDisplaysAlong(IntersectionAxis::POSITIVE_Y, primary_id, &displays,
199 placement_list, updated_displays);
200 DeIntersectDisplaysAlong(IntersectionAxis::NEGATIVE_Y, primary_id, &displays,
201 placement_list, updated_displays);
202
203 // Sort the displays by "X" then by ascending "Y" and process in both
204 // directions.
205 std::sort(displays.begin(), displays.end(), [](Display* d1, Display* d2) {
206 if (d1->bounds().x() == d2->bounds().x()) {
207 // This makes displays with same x be sorted from top to bottom.
208 return d1->bounds().y() < d2->bounds().y();
209 }
210
211 return d1->bounds().x() < d2->bounds().x();
212 });
213 DeIntersectDisplaysAlong(IntersectionAxis::POSITIVE_X, primary_id, &displays,
214 placement_list, updated_displays);
215 DeIntersectDisplaysAlong(IntersectionAxis::NEGATIVE_X, primary_id, &displays,
216 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
44 } 217 }
45 218
46 } // namespace 219 } // namespace
47 220
48 //////////////////////////////////////////////////////////////////////////////// 221 ////////////////////////////////////////////////////////////////////////////////
49 // DisplayPlacement 222 // DisplayPlacement
50 223
51 DisplayPlacement::DisplayPlacement() 224 DisplayPlacement::DisplayPlacement()
52 : DisplayPlacement(kInvalidDisplayId, 225 : DisplayPlacement(kInvalidDisplayId,
53 kInvalidDisplayId, 226 kInvalidDisplayId,
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 //////////////////////////////////////////////////////////////////////////////// 348 ////////////////////////////////////////////////////////////////////////////////
176 // DisplayLayout 349 // DisplayLayout
177 350
178 DisplayLayout::DisplayLayout() 351 DisplayLayout::DisplayLayout()
179 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {} 352 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {}
180 353
181 DisplayLayout::~DisplayLayout() {} 354 DisplayLayout::~DisplayLayout() {}
182 355
183 void DisplayLayout::ApplyToDisplayList(Displays* display_list, 356 void DisplayLayout::ApplyToDisplayList(Displays* display_list,
184 std::vector<int64_t>* updated_ids, 357 std::vector<int64_t>* updated_ids,
185 int minimum_offset_overlap) const { 358 int minimum_offset_overlap) {
186 // Layout from primary, then dependent displays. 359 // Layout from primary, then dependent displays.
187 std::set<int64_t> parents; 360 std::set<int64_t> parents;
361 std::set<int64_t> updated_displays;
188 parents.insert(primary_id); 362 parents.insert(primary_id);
189 while (parents.size()) { 363 while (parents.size()) {
190 int64_t parent_id = *parents.begin(); 364 int64_t parent_id = *parents.begin();
191 parents.erase(parent_id); 365 parents.erase(parent_id);
192 for (const DisplayPlacement& placement : placement_list) { 366 for (const DisplayPlacement& placement : placement_list) {
193 if (placement.parent_display_id == parent_id) { 367 if (placement.parent_display_id == parent_id) {
194 if (ApplyDisplayPlacement(placement, display_list, 368 if (ApplyDisplayPlacement(placement, display_list,
195 minimum_offset_overlap) && 369 minimum_offset_overlap)) {
196 updated_ids) { 370 updated_displays.insert(placement.display_id);
197 updated_ids->push_back(placement.display_id);
198 } 371 }
199 parents.insert(placement.display_id); 372 parents.insert(placement.display_id);
200 } 373 }
201 } 374 }
202 } 375 }
376
377 // Now that all the placements have been applied, we must detect and fix any
378 // overlapping displays.
379 DeIntersectDisplays(primary_id, display_list, &placement_list,
380 &updated_displays);
381
382 // The offsets might have changed and we must update them.
383 UpdateOffsets(display_list);
384
385 if (updated_ids) {
386 updated_ids->insert(updated_ids->begin(), updated_displays.begin(),
387 updated_displays.end());
388 }
203 } 389 }
204 390
205 // static 391 // static
206 bool DisplayLayout::Validate(const DisplayIdList& list, 392 bool DisplayLayout::Validate(const DisplayIdList& list,
207 const DisplayLayout& layout) { 393 const DisplayLayout& layout) {
208 // The primary display should be in the list. 394 // The primary display should be in the list.
209 DCHECK(IsIdInList(layout.primary_id, list)); 395 DCHECK(IsIdInList(layout.primary_id, list));
210 396
211 // Unified mode, or mirror mode switched from unified mode, 397 // Unified mode, or mirror mode switched from unified mode,
212 // may not have the placement yet. 398 // may not have the placement yet.
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 } 541 }
356 542
357 gfx::Insets insets = target_display->GetWorkAreaInsets(); 543 gfx::Insets insets = target_display->GetWorkAreaInsets();
358 target_display->set_bounds( 544 target_display->set_bounds(
359 gfx::Rect(new_target_origin, target_bounds.size())); 545 gfx::Rect(new_target_origin, target_bounds.size()));
360 target_display->UpdateWorkAreaFromInsets(insets); 546 target_display->UpdateWorkAreaFromInsets(insets);
361 547
362 return old_bounds != target_display->bounds(); 548 return old_bounds != target_display->bounds();
363 } 549 }
364 550
551 void DisplayLayout::UpdateOffsets(Displays* display_list) {
552 for (auto& placement : placement_list) {
553 const Display* child_display =
554 FindDisplayById(display_list, placement.display_id);
555 const Display* parent_display =
556 FindDisplayById(display_list, placement.parent_display_id);
557
558 if (!child_display || !parent_display)
559 continue;
560
561 const gfx::Rect& child_bounds = child_display->bounds();
562 const gfx::Rect& parent_bounds = parent_display->bounds();
563
564 if (placement.position == DisplayPlacement::TOP ||
565 placement.position == DisplayPlacement::BOTTOM) {
566 placement.offset = child_bounds.x() - parent_bounds.x();
567 } else {
568 placement.offset = child_bounds.y() - parent_bounds.y();
569 }
570 }
571 }
572
365 } // namespace display 573 } // namespace display
OLDNEW
« 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