Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |