Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 * @param {Element} container Content container. | 6 * @param {Element} container Content container. |
| 7 * @param {cr.ui.ArrayDataModel} dataModel Data model. | 7 * @param {cr.ui.ArrayDataModel} dataModel Data model. |
| 8 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. | 8 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. |
| 9 * @param {MetadataCache} metadataCache Metadata cache. | 9 * @param {MetadataCache} metadataCache Metadata cache. |
| 10 * @param {function} toggleMode Function to switch to the Slide mode. | 10 * @param {function} toggleMode Function to switch to the Slide mode. |
| 11 * @constructor | 11 * @constructor |
| 12 */ | 12 */ |
| 13 function MosaicMode(container, dataModel, selectionModel, | 13 function MosaicMode(container, dataModel, selectionModel, |
| 14 metadataCache, toggleMode) { | 14 metadataCache, toggleMode) { |
| 15 this.mosaic_ = new Mosaic(container.ownerDocument, | 15 this.mosaic_ = new Mosaic(container.ownerDocument, |
| 16 dataModel, selectionModel, metadataCache); | 16 dataModel, selectionModel, metadataCache); |
| 17 container.appendChild(this.mosaic_); | 17 container.appendChild(this.mosaic_); |
| 18 | 18 |
| 19 this.toggleMode_ = toggleMode; | 19 this.toggleMode_ = toggleMode; |
| 20 this.mosaic_.addEventListener('dblclick', this.toggleMode_); | 20 this.mosaic_.addEventListener('dblclick', this.toggleMode_); |
| 21 } | 21 } |
| 22 | 22 |
| 23 /** | 23 /** |
| 24 * @return {Mosaic} The mosaic control. | |
| 25 */ | |
| 26 MosaicMode.prototype.getMosaic = function() { return this.mosaic_ }; | |
| 27 | |
| 28 /** | |
| 24 * @return {string} Mode name. | 29 * @return {string} Mode name. |
| 25 */ | 30 */ |
| 26 MosaicMode.prototype.getName = function() { return 'mosaic' }; | 31 MosaicMode.prototype.getName = function() { return 'mosaic' }; |
| 27 | 32 |
| 28 /** | 33 /** |
| 29 * Initialize the mosaic. | |
| 30 */ | |
| 31 MosaicMode.prototype.init = function() { this.mosaic_.init() }; | |
| 32 | |
| 33 /** | |
| 34 * Enter the mosaic mode. | |
| 35 */ | |
| 36 MosaicMode.prototype.enter = function() { this.mosaic_.enter() }; | |
| 37 | |
| 38 /** | |
| 39 * Execute an action (this mode has no busy state). | 34 * Execute an action (this mode has no busy state). |
| 40 * @param {function} action Action to execute. | 35 * @param {function} action Action to execute. |
| 41 */ | 36 */ |
| 42 MosaicMode.prototype.executeWhenReady = function(action) { action() }; | 37 MosaicMode.prototype.executeWhenReady = function(action) { action() }; |
| 43 | 38 |
| 44 /** | 39 /** |
| 45 * @return {boolean} Always true (no toolbar fading in this mode). | 40 * @return {boolean} Always true (no toolbar fading in this mode). |
| 46 */ | 41 */ |
| 47 MosaicMode.prototype.hasActiveTool = function() { return true }; | 42 MosaicMode.prototype.hasActiveTool = function() { return true }; |
| 48 | 43 |
| 49 /** | 44 /** |
| 50 * Keydown handler. | 45 * Keydown handler. |
| 51 * | 46 * |
| 52 * @param {Event} event Event. | 47 * @param {Event} event Event. |
| 53 * @return {boolean} True if processed. | 48 * @return {boolean} True if processed. |
| 54 */ | 49 */ |
| 55 MosaicMode.prototype.onKeyDown = function(event) { | 50 MosaicMode.prototype.onKeyDown = function(event) { |
| 56 switch (util.getKeyModifiers(event) + event.keyIdentifier) { | 51 switch (util.getKeyModifiers(event) + event.keyIdentifier) { |
| 57 case 'Enter': | 52 case 'Enter': |
| 58 this.toggleMode_(); | 53 this.toggleMode_(); |
| 59 return true; | 54 return true; |
| 60 } | 55 } |
| 61 return this.mosaic_.onKeyDown(event); | 56 return this.mosaic_.onKeyDown(event); |
| 62 }; | 57 }; |
| 63 | 58 |
| 64 /** | |
| 65 * @return {Rect} Selected tile image rectangle. Used for animated transition | |
| 66 * to/from Slide mode. | |
| 67 */ | |
| 68 MosaicMode.prototype.getSelectedTileRect = function() { | |
| 69 return this.mosaic_.getSelectedTileRect(); | |
| 70 }; | |
| 71 | |
| 72 //////////////////////////////////////////////////////////////////////////////// | 59 //////////////////////////////////////////////////////////////////////////////// |
| 73 | 60 |
| 74 /** | 61 /** |
| 75 * Mosaic control. | 62 * Mosaic control. |
| 76 * | 63 * |
| 77 * @param {Document} document Document. | 64 * @param {Document} document Document. |
| 78 * @param {cr.ui.ArrayDataModel} dataModel Data model. | 65 * @param {cr.ui.ArrayDataModel} dataModel Data model. |
| 79 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. | 66 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. |
| 80 * @param {MetadataCache} metadataCache Metadata cache. | 67 * @param {MetadataCache} metadataCache Metadata cache. |
| 81 * @return {Element} Mosaic element. | 68 * @return {Element} Mosaic element. |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 }; | 154 }; |
| 168 | 155 |
| 169 /** | 156 /** |
| 170 * @return {Mosaic.Tile} Selected tile or undefined if no selection. | 157 * @return {Mosaic.Tile} Selected tile or undefined if no selection. |
| 171 */ | 158 */ |
| 172 Mosaic.prototype.getSelectedTile = function() { | 159 Mosaic.prototype.getSelectedTile = function() { |
| 173 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; | 160 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; |
| 174 }; | 161 }; |
| 175 | 162 |
| 176 /** | 163 /** |
| 177 * @return {Rect} Selected tile's image rectangle. | 164 * @param {number} index Tile index. |
| 165 * @return {Rect} Tile's image rectangle. | |
| 178 */ | 166 */ |
| 179 Mosaic.prototype.getSelectedTileRect = function() { | 167 Mosaic.prototype.getTileRect = function(index) { |
| 180 var tile = this.getSelectedTile(); | 168 var tile = this.tiles_[index]; |
| 181 return tile && tile.getImageRect(); | 169 return tile && tile.getImageRect(); |
| 182 }; | 170 }; |
| 183 | 171 |
| 184 /** | 172 /** |
| 185 * Called when showing the mosaic after the slide mode. | 173 * @param {number} index Tile index. |
| 174 * Scroll the given tile into the viewport. | |
| 186 */ | 175 */ |
| 187 Mosaic.prototype.enter = function() { | 176 Mosaic.prototype.scrollIntoView = function(index) { |
| 188 var tile = this.getSelectedTile(); | 177 var tile = this.tiles_[index]; |
| 189 if (tile) tile.scrollIntoView(); | 178 if (tile) tile.scrollIntoView(); |
| 190 }; | 179 }; |
| 191 | 180 |
| 192 /** | 181 /** |
| 193 * Load multiple tiles. | 182 * Load multiple tiles. |
| 194 * | 183 * |
| 195 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. | 184 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. |
| 196 * @param {function} opt_callback Completion callback. | 185 * @param {function} opt_callback Completion callback. |
| 197 * @private | 186 * @private |
| 198 */ | 187 */ |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 394 * @param {Event} event Event. | 383 * @param {Event} event Event. |
| 395 * @return {boolean} True if the event has been consumed. | 384 * @return {boolean} True if the event has been consumed. |
| 396 */ | 385 */ |
| 397 Mosaic.prototype.onKeyDown = function(event) { | 386 Mosaic.prototype.onKeyDown = function(event) { |
| 398 this.selectionController_.handleKeyDown(event); | 387 this.selectionController_.handleKeyDown(event); |
| 399 if (event.defaultPrevented) // Navigating with keyboard, hide hover state. | 388 if (event.defaultPrevented) // Navigating with keyboard, hide hover state. |
| 400 this.classList.remove('hover-visible'); | 389 this.classList.remove('hover-visible'); |
| 401 return event.defaultPrevented; | 390 return event.defaultPrevented; |
| 402 }; | 391 }; |
| 403 | 392 |
| 393 /** | |
| 394 * @return {boolean} True if the mosaic zoom effect can be applied. It is | |
| 395 * too slow if there are to many images. | |
| 396 * TODO(kaznacheev): Consider unloading the images that are out of the viewport. | |
| 397 */ | |
| 398 Mosaic.prototype.canZoom = function() { | |
| 399 return this.layoutModel_.getTileCount() < 100; | |
| 400 }; | |
| 401 | |
| 402 /** | |
| 403 * Show the mosaic. | |
| 404 */ | |
| 405 Mosaic.prototype.show = function() { | |
| 406 var duration = ImageView.ZOOM_ANIMATION_DURATION; | |
| 407 if (this.canZoom()) { | |
| 408 // Fade in in parallel with the zoom effect. | |
| 409 this.setAttribute('visible', 'zooming'); | |
| 410 } else { | |
| 411 // Mosaic is not animating but the large image is. Fade in the mosaic | |
| 412 // shortly before the large image animation is done. | |
| 413 duration -= 100; | |
| 414 } | |
| 415 setTimeout(function() { | |
| 416 // Make the selection visible. | |
| 417 // If the mosaic is not animated it will start fading in now. | |
| 418 this.setAttribute('visible', 'normal'); | |
| 419 }.bind(this), duration); | |
| 420 | |
| 421 }; | |
| 422 | |
| 423 /** | |
| 424 * Hide the mosaic. | |
| 425 */ | |
| 426 Mosaic.prototype.hide = function() { | |
| 427 this.removeAttribute('visible'); | |
| 428 }; | |
| 429 | |
| 430 /** | |
| 431 * Apply or reset the zoom transform. | |
| 432 * | |
| 433 * @param {Rect} tileRect Tile rectangle. Reset the transform if null. | |
| 434 * @param {Rect} imageRect Large image rectangle. Reset the transform if null. | |
| 435 * @param {boolean} opt_instant True of the transition should be instant. | |
| 436 */ | |
| 437 Mosaic.prototype.transform = function(tileRect, imageRect, opt_instant) { | |
| 438 if (opt_instant) | |
|
dgozman
2012/09/14 15:17:38
braces must be present when else clause is present
| |
| 439 this.style.webkitTransitionDuration = '0'; | |
| 440 else | |
| 441 this.style.webkitTransitionDuration = | |
| 442 ImageView.ZOOM_ANIMATION_DURATION + 'ms'; | |
| 443 | |
| 444 if (this.canZoom() && tileRect && imageRect) { | |
| 445 var scaleX = imageRect.width / tileRect.width; | |
| 446 var scaleY = imageRect.height / tileRect.height; | |
| 447 var shiftX = (imageRect.left + imageRect.width / 2) - | |
| 448 (tileRect.left + tileRect.width / 2); | |
| 449 var shiftY = (imageRect.top + imageRect.height / 2) - | |
| 450 (tileRect.top + tileRect.height / 2); | |
| 451 this.style.webkitTransform = | |
| 452 'translate(' + shiftX * scaleX + 'px, ' + shiftY * scaleY + 'px)' + | |
| 453 'scaleX(' + scaleX + ') scaleY(' + scaleY + ')'; | |
| 454 } else { | |
| 455 this.style.webkitTransform = ''; | |
| 456 } | |
| 457 }; | |
| 458 | |
| 404 //////////////////////////////////////////////////////////////////////////////// | 459 //////////////////////////////////////////////////////////////////////////////// |
| 405 | 460 |
| 406 /** | 461 /** |
| 407 * Creates a selection controller that is to be used with grid. | 462 * Creates a selection controller that is to be used with grid. |
| 408 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to | 463 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to |
| 409 * interact with. | 464 * interact with. |
| 410 * @param {Mosaic.Layout} layoutModel The layout model to use. | 465 * @param {Mosaic.Layout} layoutModel The layout model to use. |
| 411 * @constructor | 466 * @constructor |
| 412 * @extends {!cr.ui.ListSelectionController} | 467 * @extends {!cr.ui.ListSelectionController} |
| 413 */ | 468 */ |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 445 * @param {Element} container Container element. | 500 * @param {Element} container Container element. |
| 446 * @constructor | 501 * @constructor |
| 447 */ | 502 */ |
| 448 Mosaic.Layout = function(container) { | 503 Mosaic.Layout = function(container) { |
| 449 this.container_ = container; | 504 this.container_ = container; |
| 450 this.columns_ = []; | 505 this.columns_ = []; |
| 451 this.newColumn_ = null; | 506 this.newColumn_ = null; |
| 452 }; | 507 }; |
| 453 | 508 |
| 454 /** | 509 /** |
| 455 * Blank space at the top and the bottom of the mosaic element. | 510 * Blank space at the top of the mosaic element. We do not do that is CSS |
| 511 * because we want the mosaic to occupy the entire parent div which makes | |
| 512 * animated transitions easier. | |
| 456 */ | 513 */ |
| 457 Mosaic.Layout.PADDING = 50; | 514 Mosaic.Layout.PADDING_TOP = 50; |
| 515 | |
| 516 /** | |
| 517 * Blank space at the bottom of the mosaic element. | |
| 518 */ | |
| 519 Mosaic.Layout.PADDING_BOTTOM = 70; | |
| 458 | 520 |
| 459 /** | 521 /** |
| 460 * Horizontal and vertical spacing between images. Should be kept in sync | 522 * Horizontal and vertical spacing between images. Should be kept in sync |
| 461 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) | 523 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) |
| 462 */ | 524 */ |
| 463 Mosaic.Layout.SPACING = 10; | 525 Mosaic.Layout.SPACING = 10; |
| 464 | 526 |
| 465 /** | 527 /** |
| 466 * @return {number} Total number of tiles added to the layout. | 528 * @return {number} Total number of tiles added to the layout. |
| 467 */ | 529 */ |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 481 /** | 543 /** |
| 482 * Add a tile to the layout. | 544 * Add a tile to the layout. |
| 483 * | 545 * |
| 484 * @param {Mosaic.Tile} tile The tile to be added. | 546 * @param {Mosaic.Tile} tile The tile to be added. |
| 485 * @param {boolean} isLast True if this tile is the last. | 547 * @param {boolean} isLast True if this tile is the last. |
| 486 */ | 548 */ |
| 487 Mosaic.Layout.prototype.add = function(tile, isLast) { | 549 Mosaic.Layout.prototype.add = function(tile, isLast) { |
| 488 var layoutQueue = [tile]; | 550 var layoutQueue = [tile]; |
| 489 var tilesPerRow = Mosaic.Row.TILES_PER_ROW; | 551 var tilesPerRow = Mosaic.Row.TILES_PER_ROW; |
| 490 | 552 |
| 491 var layoutHeight = this.container_.clientHeight - Mosaic.Layout.PADDING * 2; | 553 var layoutHeight = this.container_.clientHeight - |
| 554 (Mosaic.Layout.PADDING_TOP + Mosaic.Layout.PADDING_BOTTOM); | |
| 492 | 555 |
| 493 while (layoutQueue.length) { | 556 while (layoutQueue.length) { |
| 494 if (!this.newColumn_) { | 557 if (!this.newColumn_) { |
| 495 this.newColumn_ = new Mosaic.Column( | 558 this.newColumn_ = new Mosaic.Column( |
| 496 this.columns_.length, this.getTileCount(), layoutHeight, tilesPerRow); | 559 this.columns_.length, this.getTileCount(), layoutHeight, tilesPerRow); |
| 497 tilesPerRow = Mosaic.Row.TILES_PER_ROW; // Reset the default. | 560 tilesPerRow = Mosaic.Row.TILES_PER_ROW; // Reset the default. |
| 498 } | 561 } |
| 499 | 562 |
| 500 this.newColumn_.add(layoutQueue.shift()); | 563 this.newColumn_.add(layoutQueue.shift()); |
| 501 | 564 |
| 502 if (this.newColumn_.prepareLayout(isLast && !layoutQueue.length)) { | 565 if (this.newColumn_.prepareLayout(isLast && !layoutQueue.length)) { |
| 503 if (this.newColumn_.isSuboptimal()) { | 566 if (this.newColumn_.isSuboptimal()) { |
| 504 // Forget the new column, put its tiles back into the layout queue. | 567 // Forget the new column, put its tiles back into the layout queue. |
| 505 layoutQueue = this.newColumn_.getTiles().concat(layoutQueue); | 568 layoutQueue = this.newColumn_.getTiles().concat(layoutQueue); |
| 506 tilesPerRow = 1; // Force 1 tile per row for the next column. | 569 tilesPerRow = 1; // Force 1 tile per row for the next column. |
| 507 } else { | 570 } else { |
| 508 var lastColumn = this.columns_[this.columns_.length - 1]; | 571 var lastColumn = this.columns_[this.columns_.length - 1]; |
| 509 this.newColumn_.layout(lastColumn ? lastColumn.getRightEdge() : 0, | 572 this.newColumn_.layout(lastColumn ? lastColumn.getRightEdge() : 0, |
| 510 Mosaic.Layout.PADDING); | 573 Mosaic.Layout.PADDING_TOP); |
| 511 this.columns_.push(this.newColumn_); | 574 this.columns_.push(this.newColumn_); |
| 512 } | 575 } |
| 513 this.newColumn_ = null; | 576 this.newColumn_ = null; |
| 514 } | 577 } |
| 515 } | 578 } |
| 516 }; | 579 }; |
| 517 | 580 |
| 518 /** | 581 /** |
| 519 * Backtrack the layout to the beginning of the column containing a tile with | 582 * Backtrack the layout to the beginning of the column containing a tile with |
| 520 * a given index. | 583 * a given index. |
| (...skipping 609 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1130 * relative to the viewport. | 1193 * relative to the viewport. |
| 1131 */ | 1194 */ |
| 1132 Mosaic.Tile.prototype.getImageRect = function() { | 1195 Mosaic.Tile.prototype.getImageRect = function() { |
| 1133 if (this.left_ == null) // Not laid out. | 1196 if (this.left_ == null) // Not laid out. |
| 1134 return null; | 1197 return null; |
| 1135 | 1198 |
| 1136 var margin = Mosaic.Layout.SPACING / 2; | 1199 var margin = Mosaic.Layout.SPACING / 2; |
| 1137 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, | 1200 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, |
| 1138 this.width_, this.height_).inflate(-margin, -margin); | 1201 this.width_, this.height_).inflate(-margin, -margin); |
| 1139 }; | 1202 }; |
| OLD | NEW |