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

Side by Side Diff: chrome/browser/resources/settings/device_page/layout_behavior.js

Issue 2090953007: MD Settings: Display: Reparent children of dragged displays (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@issue_547080_display_settings8b_collide
Patch Set: . Created 4 years, 6 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | 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 /** 5 /**
6 * @fileoverview Behavior for handling display layout, specifically 6 * @fileoverview Behavior for handling display layout, specifically
7 * edge snapping and collisions. 7 * edge snapping and collisions.
8 */ 8 */
9 9
10 /** @polymerBehavior */ 10 /** @polymerBehavior */
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 * bounds for the display. 73 * bounds for the display.
74 * @return {!chrome.system.display.Bounds} 74 * @return {!chrome.system.display.Bounds}
75 */ 75 */
76 updateDisplayBounds(id, newBounds) { 76 updateDisplayBounds(id, newBounds) {
77 this.dragLayoutId = id; 77 this.dragLayoutId = id;
78 78
79 // Find the closest parent. 79 // Find the closest parent.
80 var closestId = this.findClosest_(id, newBounds); 80 var closestId = this.findClosest_(id, newBounds);
81 81
82 // Find the closest edge. 82 // Find the closest edge.
83 var layoutPosition = this.getlayoutPositionForBounds_(newBounds, closestId); 83 var closestBounds = this.getCalculatedDisplayBounds(closestId);
84 var layoutPosition =
85 this.getLayoutPositionForBounds_(newBounds, closestBounds);
84 86
85 // Snap to the closet edge 87 // Snap to the closet edge
86 this.snapBounds_(closestId, layoutPosition, newBounds); 88 var snapPos = this.snapBounds_(newBounds, closestId, layoutPosition);
89 newBounds.left = snapPos.x;
90 newBounds.top = snapPos.y;
87 91
88 // Calculate the new bounds and delta. 92 // Calculate the new bounds and delta.
89 var oldBounds = this.dragBounds_ || this.getCalculatedDisplayBounds(id); 93 var oldBounds = this.dragBounds_ || this.getCalculatedDisplayBounds(id);
90 var deltaPos = { 94 var deltaPos = {
91 x: newBounds.left - oldBounds.left, 95 x: newBounds.left - oldBounds.left,
92 y: newBounds.top - oldBounds.top 96 y: newBounds.top - oldBounds.top
93 }; 97 };
94 98
95 // Check for collisions. 99 // Check for collisions.
96 this.collideAndModifyDelta_(id, oldBounds, deltaPos); 100 this.collideAndModifyDelta_(id, oldBounds, deltaPos);
(...skipping 15 matching lines...) Expand all
112 }, 116 },
113 117
114 /** 118 /**
115 * Called when dragging ends. Sends the updated layout to chrome. 119 * Called when dragging ends. Sends the updated layout to chrome.
116 * @param {string} id 120 * @param {string} id
117 */ 121 */
118 finishUpdateDisplayBounds(id) { 122 finishUpdateDisplayBounds(id) {
119 this.highlightEdge_('', undefined); // Remove any highlights. 123 this.highlightEdge_('', undefined); // Remove any highlights.
120 if (id != this.dragLayoutId || !this.dragBounds_) 124 if (id != this.dragLayoutId || !this.dragBounds_)
121 return; 125 return;
126
122 var layout = this.displayLayoutMap_.get(id); 127 var layout = this.displayLayoutMap_.get(id);
123 if (!layout) 128
124 return; 129 var orphanIds;
125 // Note: This updates layout in this.displayLayoutMap_ which is also the 130 if (!layout || layout.parentId == '') {
126 // entry in this.layouts. 131 // Primary display. Set the calculated position to |dragBounds_|.
127 this.updateOffsetAndPosition_( 132 this.setCalculatedDisplayBounds_(id, this.dragBounds_);
128 this.dragBounds_, this.draglayoutPosition_, layout); 133
134 // We cannot re-parent the primary display, so instead make all other
135 // displays orphans and clear their calculated bounds.
136 orphanIds = this.findChildren_(id, true /* recurse */);
137 for (let o in orphanIds)
138 this.calculatedBoundsMap_.delete(o);
139
140 // Re-parent |dragParentId_|. It will be forced to parent to the dragged
141 // display since it is the only non-orphan.
142 this.reparentOrphan_(this.dragParentId_, orphanIds);
143 orphanIds.splice(orphanIds.indexOf(this.dragParentId_), 1);
144 } else {
145 // All immediate children of |layout| will need to be re-parented.
146 orphanIds = this.findChildren_(id, false /* do not recurse */);
147 for (let o in orphanIds)
148 this.calculatedBoundsMap_.delete(o);
149
150 // When re-parenting to a descendant, also parent any immediate child to
151 // drag display's current parent.
152 var topLayout = this.displayLayoutMap_.get(this.dragParentId_);
153 while (topLayout && topLayout.parentId != '') {
154 if (topLayout.parentId == id) {
155 topLayout.parentId = layout.parentId;
156 break;
157 }
158 topLayout = this.displayLayoutMap_.get(topLayout.parentId);
159 }
160
161 // Re-parent the dragged display
michaelpg 2016/06/29 20:16:50 nit: period
stevenjb 2016/06/29 23:25:30 Done.
162 layout.parentId = this.dragParentId_;
163 this.updateOffsetAndPosition_(
164 this.dragBounds_, this.draglayoutPosition_, layout);
165 }
166
167 // Update any orphaned children. This may cause the dragged display to
168 // be re-attached if it was attached to a child.
169 this.updateOrphans_(orphanIds);
129 170
130 // Send the updated layouts. 171 // Send the updated layouts.
131 chrome.system.display.setDisplayLayout(this.layouts, function() { 172 chrome.system.display.setDisplayLayout(this.layouts, function() {
132 if (chrome.runtime.lastError) { 173 if (chrome.runtime.lastError) {
133 console.error( 174 console.error(
134 'setDisplayLayout Error: ' + chrome.runtime.lastError.message); 175 'setDisplayLayout Error: ' + chrome.runtime.lastError.message);
135 } 176 }
136 }); 177 });
137 }, 178 },
138 179
(...skipping 14 matching lines...) Expand all
153 */ 194 */
154 setCalculatedDisplayBounds_: function(displayId, bounds) { 195 setCalculatedDisplayBounds_: function(displayId, bounds) {
155 assert(bounds); 196 assert(bounds);
156 this.calculatedBoundsMap_.set( 197 this.calculatedBoundsMap_.set(
157 displayId, 198 displayId,
158 /** @type {!chrome.system.display.Bounds} */ ( 199 /** @type {!chrome.system.display.Bounds} */ (
159 Object.assign({}, bounds))); 200 Object.assign({}, bounds)));
160 }, 201 },
161 202
162 /** 203 /**
204 * Re-parent all entries in |orphanIds| and any children.
michaelpg 2016/06/29 20:16:51 nit: indicative ("Re-parents") here & below
stevenjb 2016/06/29 23:25:29 Done.
205 * @param {Array<string>} orphanIds The list of ids affected by the move.
michaelpg 2016/06/29 20:16:51 nit: !Array<string>
stevenjb 2016/06/29 23:25:29 Done.
206 * @private
207 */
208 updateOrphans_: function(orphanIds) {
209 var orphans = orphanIds.slice();
210 for (let orphan of orphanIds) {
211 var newOrphans = this.findChildren_(orphan, true /* recurse */);
212 // If the dragged display was re-parented to one of its children,
213 // there may be duplicates so merge the lists.
214 for (let o of newOrphans) {
215 if (orphans.indexOf(o) == -1)
michaelpg 2016/06/29 20:16:51 !orphans.includes(o) also, consider using a Set i
stevenjb 2016/06/29 23:25:30 Done. (Order matters, see findChildren).
216 orphans.push(o);
217 }
218 }
219
220 // Remove each orphan from the list as it is re-parented so that
221 // subsequent orphans can be parented to it.
222 while (orphans.length) {
223 var orphanId = orphans.shift();
224 this.reparentOrphan_(orphanId, orphans);
225 }
226 },
227
228 /**
229 * Re-parent the orphan to a layout that is not a member of
230 * |otherOrphanIds|.
231 * @param {string} orphanId The id of the orphan to re-parent.
232 * @param {Array<string>} otherOrphanIds The list of ids of other orphans
233 * to ignore when re-parenting.
234 * @private
235 */
236 reparentOrphan_: function(orphanId, otherOrphanIds) {
237 var layout = this.displayLayoutMap_.get(orphanId);
michaelpg 2016/06/29 20:16:51 opt nit: var layout = assert(...);
stevenjb 2016/06/29 23:25:30 I'm not a fan of that pattern outside of tests.
Dan Beam 2016/06/29 23:35:10 why?
stevenjb 2016/06/29 23:38:33 In C++ I've seen bugs where someone puts code with
michaelpg 2016/07/02 00:53:27 Acknowledged.
238 assert(layout);
239 if (orphanId == this.dragId_ && layout.parentId != '') {
240 this.setCalculatedDisplayBounds_(orphanId, this.dragBounds_);
241 return;
242 }
243 var bounds = this.getCalculatedDisplayBounds(orphanId);
244
245 // Find the closest parent.
246 var newParentId = this.findClosest_(orphanId, bounds, otherOrphanIds);
247 assert(newParentId != '');
248 layout.parentId = newParentId;
249
250 // Find the closest edge.
251 var parentBounds = this.getCalculatedDisplayBounds(newParentId);
252 var layoutPosition = this.getLayoutPositionForBounds_(bounds, parentBounds);
253
254 // Move from the nearest corner to the desired locaiton and get the delta.
michaelpg 2016/06/29 20:16:51 location
stevenjb 2016/06/29 23:25:30 Done.
255 var cornerBounds = this.getCornerBounds_(bounds, parentBounds);
256 var desiredPos = this.snapBounds_(bounds, newParentId, layoutPosition);
257 var deltaPos = {
258 x: desiredPos.x - cornerBounds.left,
259 y: desiredPos.y - cornerBounds.top
260 };
261
262 // Check for collisions.
263 this.collideAndModifyDelta_(orphanId, cornerBounds, deltaPos);
264 var desiredBounds = {
265 left: cornerBounds.left + deltaPos.x,
266 top: cornerBounds.top + deltaPos.y,
267 width: bounds.width,
268 height: bounds.height
269 };
270
271 this.updateOffsetAndPosition_(desiredBounds, layoutPosition, layout);
272 },
273
274 /**
275 * @param {string} parentId
276 * @param {boolean} recurse Include descendants of children.
277 * @return {!Array<string>}
278 * @private
279 */
280 findChildren_: function(parentId, recurse) {
281 var children = [];
282 for (let childId of this.displayLayoutMap_.keys()) {
283 if (childId == parentId)
284 continue;
285 if (this.displayLayoutMap_.get(childId).parentId == parentId) {
286 // Insert immediate children at the front of the array.
287 children.unshift(childId);
288 if (recurse) {
289 // Descendants get added to the end of the list.
290 children = children.concat(this.findChildren_(childId, true));
291 }
292 }
293 }
294 return children;
295 },
296
297 /**
163 * Recursively calculate the absolute bounds of a display. 298 * Recursively calculate the absolute bounds of a display.
164 * Caches the display bounds so that parent bounds are only calculated once. 299 * Caches the display bounds so that parent bounds are only calculated once.
165 * @param {string} id 300 * @param {string} id
166 * @param {number} width 301 * @param {number} width
167 * @param {number} height 302 * @param {number} height
168 * @private 303 * @private
169 */ 304 */
170 calculateBounds_: function(id, width, height) { 305 calculateBounds_: function(id, width, height) {
171 var left, top; 306 var left, top;
172 var layout = this.displayLayoutMap_.get(id); 307 var layout = this.displayLayoutMap_.get(id);
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 closestId = otherId; 387 closestId = otherId;
253 closestDelta2 = delta2; 388 closestDelta2 = delta2;
254 } 389 }
255 } 390 }
256 return closestId; 391 return closestId;
257 }, 392 },
258 393
259 /** 394 /**
260 * Calculates the LayoutPosition for |bounds| relative to |parentId|. 395 * Calculates the LayoutPosition for |bounds| relative to |parentId|.
261 * @param {!chrome.system.display.Bounds} bounds 396 * @param {!chrome.system.display.Bounds} bounds
262 * @param {string} parentId 397 * @param {!chrome.system.display.Bounds} parentBounds
263 * @return {!chrome.system.display.LayoutPosition} 398 * @return {!chrome.system.display.LayoutPosition}
264 */ 399 */
265 getlayoutPositionForBounds_: function(bounds, parentId) { 400 getLayoutPositionForBounds_: function(bounds, parentBounds) {
266 // Translate bounds from top-left to center. 401 // Translate bounds from top-left to center.
267 var x = bounds.left + bounds.width / 2; 402 var x = bounds.left + bounds.width / 2;
268 var y = bounds.top + bounds.height / 2; 403 var y = bounds.top + bounds.height / 2;
269 404
270 // Determine the distance from the new bounds to both of the near edges. 405 // Determine the distance from the new bounds to both of the near edges.
271 var parentBounds = this.getCalculatedDisplayBounds(parentId);
272 var left = parentBounds.left; 406 var left = parentBounds.left;
273 var top = parentBounds.top; 407 var top = parentBounds.top;
274 var width = parentBounds.width; 408 var width = parentBounds.width;
275 var height = parentBounds.height; 409 var height = parentBounds.height;
276 410
277 // Signed deltas to the center of the div. 411 // Signed deltas to the center.
278 var dx = x - (left + width / 2); 412 var dx = x - (left + width / 2);
279 var dy = y - (top + height / 2); 413 var dy = y - (top + height / 2);
280 414
281 // Unsigned distance to each edge. 415 // Unsigned distance to each edge.
282 var distx = Math.abs(dx) - width / 2; 416 var distx = Math.abs(dx) - width / 2;
283 var disty = Math.abs(dy) - height / 2; 417 var disty = Math.abs(dy) - height / 2;
284 418
285 if (distx > disty) { 419 if (distx > disty) {
286 if (dx < 0) 420 if (dx < 0)
287 return chrome.system.display.LayoutPosition.LEFT; 421 return chrome.system.display.LayoutPosition.LEFT;
288 else 422 else
289 return chrome.system.display.LayoutPosition.RIGHT; 423 return chrome.system.display.LayoutPosition.RIGHT;
290 } else { 424 } else {
291 if (dy < 0) 425 if (dy < 0)
292 return chrome.system.display.LayoutPosition.TOP; 426 return chrome.system.display.LayoutPosition.TOP;
293 else 427 else
294 return chrome.system.display.LayoutPosition.BOTTOM; 428 return chrome.system.display.LayoutPosition.BOTTOM;
295 } 429 }
296 }, 430 },
297 431
298 /** 432 /**
299 * Modifes |bounds| to the position closest to it along the edge of |parentId| 433 * Modifes |bounds| to the position closest to it along the edge of |parentId|
300 * specified by |layoutPosition|. 434 * specified by |layoutPosition|.
435 * @param {!chrome.system.display.Bounds} bounds
301 * @param {string} parentId 436 * @param {string} parentId
302 * @param {!chrome.system.display.LayoutPosition} layoutPosition 437 * @param {!chrome.system.display.LayoutPosition} layoutPosition
303 * @param {!chrome.system.display.Bounds} bounds 438 * @return {!{x: number, y: number}}
304 */ 439 */
305 snapBounds_: function(parentId, layoutPosition, bounds) { 440 snapBounds_: function(bounds, parentId, layoutPosition) {
306 var parentBounds = this.getCalculatedDisplayBounds(parentId); 441 var parentBounds = this.getCalculatedDisplayBounds(parentId);
307 442
308 var x; 443 var x;
309 if (layoutPosition == chrome.system.display.LayoutPosition.LEFT) { 444 if (layoutPosition == chrome.system.display.LayoutPosition.LEFT) {
310 x = parentBounds.left - bounds.width; 445 x = parentBounds.left - bounds.width;
311 } else if (layoutPosition == chrome.system.display.LayoutPosition.RIGHT) { 446 } else if (layoutPosition == chrome.system.display.LayoutPosition.RIGHT) {
312 x = parentBounds.left + parentBounds.width; 447 x = parentBounds.left + parentBounds.width;
313 } else { 448 } else {
314 x = this.snapToX_(bounds, parentBounds); 449 x = this.snapToX_(bounds, parentBounds);
315 } 450 }
316 451
317 var y; 452 var y;
318 if (layoutPosition == chrome.system.display.LayoutPosition.TOP) { 453 if (layoutPosition == chrome.system.display.LayoutPosition.TOP) {
319 y = parentBounds.top - bounds.height; 454 y = parentBounds.top - bounds.height;
320 } else if (layoutPosition == chrome.system.display.LayoutPosition.BOTTOM) { 455 } else if (layoutPosition == chrome.system.display.LayoutPosition.BOTTOM) {
321 y = parentBounds.top + parentBounds.height; 456 y = parentBounds.top + parentBounds.height;
322 } else { 457 } else {
323 y = this.snapToY_(bounds, parentBounds); 458 y = this.snapToY_(bounds, parentBounds);
324 } 459 }
325 460
326 bounds.left = x; 461 return {x: x, y: y};
327 bounds.top = y;
328 }, 462 },
329 463
330 /** 464 /**
331 * Snaps a horizontal value, see SnapToEdge. 465 * Snaps a horizontal value, see SnapToEdge.
332 * @param {!chrome.system.display.Bounds} newBounds 466 * @param {!chrome.system.display.Bounds} newBounds
333 * @param {!chrome.system.display.Bounds} parentBounds 467 * @param {!chrome.system.display.Bounds} parentBounds
334 * @param {number=} opt_snapDistance Provide to override the snap distance. 468 * @param {number=} opt_snapDistance Provide to override the snap distance.
335 * 0 means snap from any distance. 469 * 0 means snap from any distance.
336 * @return {number} 470 * @return {number}
337 */ 471 */
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 /** @const */ var MIN_OFFSET_OVERLAP = 50; 633 /** @const */ var MIN_OFFSET_OVERLAP = 50;
500 minOffset += MIN_OFFSET_OVERLAP; 634 minOffset += MIN_OFFSET_OVERLAP;
501 maxOffset -= MIN_OFFSET_OVERLAP; 635 maxOffset -= MIN_OFFSET_OVERLAP;
502 layout.offset = Math.max(minOffset, Math.min(offset, maxOffset)); 636 layout.offset = Math.max(minOffset, Math.min(offset, maxOffset));
503 637
504 // Update the calculated bounds to match the new offset. 638 // Update the calculated bounds to match the new offset.
505 this.calculateBounds_(layout.id, bounds.width, bounds.height); 639 this.calculateBounds_(layout.id, bounds.width, bounds.height);
506 }, 640 },
507 641
508 /** 642 /**
643 * Return the position of the corner of the div closest to |pos|.
michaelpg 2016/06/29 20:16:51 nits: Return => Returns, position => bounds, pos =
stevenjb 2016/06/29 23:25:30 Rewrote.
644 * @param {!chrome.system.display.Bounds} bounds
645 * @param {!chrome.system.display.Bounds} parentBounds
646 * @return {!chrome.system.display.Bounds}
647 * @private
648 */
649 getCornerBounds_: function(bounds, parentBounds) {
650 var x;
651 if (bounds.left > parentBounds.left + parentBounds.width / 2)
652 x = parentBounds.left + parentBounds.width;
653 else
654 x = parentBounds.left - bounds.width;
655 var y;
656 if (bounds.top > parentBounds.top + parentBounds.height / 2)
657 y = parentBounds.top + parentBounds.height;
658 else
659 y = parentBounds.top - bounds.height;
660 return {
661 left: x,
662 top: y,
663 width: bounds.width,
664 height: bounds.height,
665 };
666 },
667
668 /**
509 * Highlights the edge of the div associated with |id| based on 669 * Highlights the edge of the div associated with |id| based on
510 * |layoutPosition| and removes any other highlights. If |layoutPosition| is 670 * |layoutPosition| and removes any other highlights. If |layoutPosition| is
511 * undefined, removes all highlights. 671 * undefined, removes all highlights.
512 * @param {string} id 672 * @param {string} id
513 * @param {chrome.system.display.LayoutPosition|undefined} layoutPosition 673 * @param {chrome.system.display.LayoutPosition|undefined} layoutPosition
514 * @private 674 * @private
515 */ 675 */
516 highlightEdge_: function(id, layoutPosition) { 676 highlightEdge_: function(id, layoutPosition) {
517 for (let layout of this.layouts) { 677 for (let layout of this.layouts) {
518 var highlight = (layout.id == id) ? layoutPosition : undefined; 678 var highlight = (layout.id == id) ? layoutPosition : undefined;
519 var div = this.$$('#_' + layout.id); 679 var div = this.$$('#_' + layout.id);
520 div.classList.toggle( 680 div.classList.toggle(
521 'highlight-right', 681 'highlight-right',
522 highlight == chrome.system.display.LayoutPosition.RIGHT); 682 highlight == chrome.system.display.LayoutPosition.RIGHT);
523 div.classList.toggle( 683 div.classList.toggle(
524 'highlight-left', 684 'highlight-left',
525 highlight == chrome.system.display.LayoutPosition.LEFT); 685 highlight == chrome.system.display.LayoutPosition.LEFT);
526 div.classList.toggle( 686 div.classList.toggle(
527 'highlight-top', 687 'highlight-top',
528 highlight == chrome.system.display.LayoutPosition.TOP); 688 highlight == chrome.system.display.LayoutPosition.TOP);
529 div.classList.toggle( 689 div.classList.toggle(
530 'highlight-bottom', 690 'highlight-bottom',
531 highlight == chrome.system.display.LayoutPosition.BOTTOM); 691 highlight == chrome.system.display.LayoutPosition.BOTTOM);
532 } 692 }
533 }, 693 },
534 }; 694 };
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698