Chromium Code Reviews| Index: chrome/browser/resources/ntp/apps.js |
| diff --git a/chrome/browser/resources/ntp/apps.js b/chrome/browser/resources/ntp/apps.js |
| index 4dd8bc12ce14a1589f4118fd95c06d46691860d4..f88d3fc1eacb42e3c48e380e9bf11c302ddf8909 100644 |
| --- a/chrome/browser/resources/ntp/apps.js |
| +++ b/chrome/browser/resources/ntp/apps.js |
| @@ -45,6 +45,15 @@ function getAppsCallback(data) { |
| return a.app_launch_index - b.app_launch_index; |
| }); |
| + // Determines if the web store link should be detached and place in the |
| + // top right of the screen. |
| + apps.detachWebstoreEntry = |
| + !apps.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode]; |
| + |
| + apps.data = data.apps; |
| + if (!apps.detachWebstoreEntry) |
| + apps.data.push('web-store-entry'); |
| + |
| clearClosedMenu(apps.menu); |
| data.apps.forEach(function(app) { |
| appsSectionContent.appendChild(apps.createElement(app)); |
| @@ -84,12 +93,15 @@ function getAppsCallback(data) { |
| appsPromoLink.setAttribute('ping', appsPromoPing); |
| maybeDoneLoading(); |
| - if (isDoneLoading()) { |
| - if (!apps.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode]) |
| - webStoreEntry.classList.add('loner'); |
| - else |
| - webStoreEntry.classList.remove('loner'); |
| + // Disable the animations when the app launcher is being (re)initailized. |
| + apps.layout(true); |
| + if (apps.detachWebstoreEntry) |
| + webStoreEntry.classList.add('loner'); |
| + else |
| + webStoreEntry.classList.remove('loner'); |
| + |
| + if (isDoneLoading()) { |
| updateMiniviewClipping(appsMiniview); |
| layoutSections(); |
| } |
| @@ -261,6 +273,12 @@ var apps = (function() { |
| } |
| }); |
| + // Moves the element at position |from| in array |arr| to position |to|. |
| + function arrayMove(arr, from, to) { |
| + var element = arr.splice(from, 1); |
| + arr.splice(to, 0, element[0]); |
| + } |
| + |
| return { |
| loaded: false, |
| @@ -268,6 +286,179 @@ var apps = (function() { |
| showPromo: false, |
| + detachWebstoreEntry: false, |
| + |
| + // The list of app ids, in order, of each app in the launcher. |
| + data_: null, |
| + get data() { return this.data_; }, |
| + set data(data) { |
| + var ids = []; |
|
arv (Not doing code reviews)
2011/01/22 00:52:14
this.data_ = data.map(function(app) {
return app
jstritar
2011/01/24 01:00:42
Done.
|
| + data.forEach(function(app) { |
| + ids.push(app.id); |
| + }); |
| + this.data_ = ids; |
| + this.invalidate(); |
| + }, |
| + |
| + dirty_: true, |
| + invalidate: function() { |
| + this.dirty_ = true; |
| + }, |
| + |
| + visible_: true, |
| + get visible() { |
| + return this.visible_; |
| + }, |
| + set visible(visible) { |
| + this.visible_ = visible; |
| + this.invalidate(); |
| + }, |
| + |
| + // DragAndDropDelegate |
| + |
| + dragContainer: $('apps-content'), |
| + transitionsDuration: 200, |
| + |
| + get dragItem() { return this.dragItem_; }, |
| + set dragItem(dragItem) { |
| + if (this.dragItem_ != dragItem) { |
| + this.dragItem_ = dragItem; |
| + this.invalidate(); |
| + } |
| + }, |
| + |
| + // These are the dimensions of each item in the app launcher. These need |
| + // to be in sync with the rules in apps.css. |
|
Aaron Boodman
2011/01/22 23:42:57
Is it possible to get them via offsetHeight/offset
jstritar
2011/01/24 01:00:42
Done.
|
| + get dimensions() { |
| + return { |
| + itemHeight: 136, |
| + itemWidth: 124, |
| + marginWidth: 3, |
| + marginHeight: 5, |
| + borderWidth: 0 |
| + }; |
| + }, |
| + |
| + // Gets the item under the mouse event |e|. Returns null if there is no |
| + // item or if the item is not draggable. |
| + getItem: function(e) { |
| + var item = findAncestorByClass(e.target, 'app'); |
| + |
| + // You can't drag the web store launcher. |
| + if (item.classList.contains('web-store-entry')) |
| + return null; |
| + |
| + return item; |
| + }, |
| + |
| + // Returns true if |position| is a valid place to drop an app. |
| + canDropOn: function(position) { |
| + var appCount = this.data.length; |
| + if (!this.detachWebstoreEntry) |
| + appCount--; |
| + return position >= 0 && position < appCount; |
| + }, |
| + |
| + setDragPlaceholder: function(position) { |
| + var appId = this.dragItem.querySelector('a').getAttribute('app-id'); |
| + var current = this.data.indexOf(appId); |
| + |
| + if (current == position || current < 0) |
| + return; |
| + |
| + arrayMove(this.data, current, position); |
| + this.invalidate(); |
| + this.layout(); |
| + }, |
| + |
| + saveDrag: function() { |
|
Aaron Boodman
2011/01/22 23:42:57
Method isn't called from dragdrop.js ?
jstritar
2011/01/24 01:00:42
Oops.... that could be a problem! Done.
|
| + this.invalidate(); |
| + this.layout(); |
| + |
| + // Wait until the transitions are complete before notifying the browser. |
| + // Otherwise, the apps will be re-rendered while still transitioning. |
| + setTimeout(function() { |
| + chrome.send('reorderApps', this.data.filter(function(id) { |
| + return id != 'web-store-entry'; |
| + })); |
| + }, this.transitionsDuration + 10); |
| + }, |
| + |
| + layout: function(disable_animations) { |
|
Aaron Boodman
2011/01/22 23:42:57
It sucks that it is different, but naming in JavaS
Aaron Boodman
2011/01/22 23:42:57
Style nit: I don't much care for the boolean flags
jstritar
2011/01/24 01:00:42
Done. Went with layout({disableAnimation:true}) in
jstritar
2011/01/24 01:00:42
Done.
|
| + if (!this.dirty_) |
| + return; |
| + |
| + try { |
| + if (disable_animations) |
| + this.dragContainer.setAttribute('launcher-animations', false); |
| + var d0 = Date.now(); |
| + this.applyAppRects(); |
| + this.dirty_ = false; |
| + logEvent('apps.layout: ' + (Date.now() - d0)); |
| + |
| + } finally { |
| + if (disable_animations) { |
| + // We need to re-enable animations asynchronously, so that the |
| + // animations are still disabled for this layout update. |
| + setTimeout(function() { |
| + this.dragContainer.setAttribute('launcher-animations', true); |
|
arv (Not doing code reviews)
2011/01/22 00:52:14
How about introducing a variable to dragContainer.
jstritar
2011/01/24 01:00:42
Done.
|
| + }.bind(this), 0); |
| + } |
| + } |
| + }, |
| + |
| + applyAppRects: function() { |
|
Aaron Boodman
2011/01/22 23:42:57
Kind of an odd name for this method. What about la
jstritar
2011/01/24 01:00:42
Done.
|
| + var apps = this.data; |
| + var rects = this.getAppLayoutRects(apps.length); |
| + var appsContent = $('apps-content'); |
| + var d = this.dimensions; |
| + var h = d.itemHeight + 2 * d.marginHeight + 2 * d.borderWidth; |
| + var maxRows = 0; |
| + |
| + for (var i = 0; i < apps.length; i++) { |
| + var t = appsContent.querySelector('[app-id='+apps[i]+']').parentNode; |
|
Aaron Boodman
2011/01/22 23:42:57
Avoid single variable letter names unless they are
jstritar
2011/01/24 01:00:42
Done.
|
| + |
| + // If the node is being dragged, don't try to place it in the grid. |
| + if (t == this.dragItem) |
| + continue; |
| + |
| + maxRows = Math.max(maxRows, rects[i].row); |
| + |
| + t.style.left = rects[i].left + 'px'; |
| + t.style.top = rects[i].top + 'px'; |
| + t.style.right = ''; |
|
Aaron Boodman
2011/01/22 23:42:57
It looks like maybe you were going to use 'right',
jstritar
2011/01/24 01:00:42
Done.
|
| + t.style.display = this.visible ? '' : 'none'; |
|
Aaron Boodman
2011/01/22 23:42:57
If this.visible is false, does it mean that the se
jstritar
2011/01/24 01:00:42
Done.
|
| + |
| + var innerStyle = t.firstElementChild.style; |
| + innerStyle.left = innerStyle.top = ''; |
|
Aaron Boodman
2011/01/22 23:42:57
This seems odd, when are these ever used?
jstritar
2011/01/24 01:00:42
Oops... must have brought this in from the most vi
|
| + } |
| + |
| + // Set the container's height manually now that we use absolute |
| + // positioning. |
| + var lastRect = rects[rects.length - 1]; |
| + appsContent.style.height = (++maxRows * h) + 'px'; |
| + }, |
| + |
| + getAppLayoutRects: function(appCount) { |
| + var d = this.dimensions; |
|
Aaron Boodman
2011/01/22 23:42:57
I don't think it's obvious what d stands for. How
jstritar
2011/01/24 01:00:42
Done.
|
| + var availableWidth = this.dragContainer.offsetWidth; |
| + var rtl = isRtl(); |
| + var rects = []; |
| + var w = d.itemWidth + 2 * d.borderWidth + 2 * d.marginWidth; |
| + var h = d.itemHeight + 2 * d.borderWidth + 2 * d.marginHeight; |
| + |
| + for (var i = 0; i < appCount; i++) { |
| + var row = Math.floor((w * i) / availableWidth); |
| + var top = row * h; |
| + var left = (w * i) % availableWidth; |
| + |
| + // Reflect the X axis if an RTL language is active. |
| + if (rtl) left = availableWidth - left - w; |
|
arv (Not doing code reviews)
2011/01/22 00:52:14
line break?
jstritar
2011/01/24 01:00:42
Done.
|
| + rects[i] = {left: left, top: top, row: row}; |
| + } |
| + return rects; |
| + }, |
| + |
| createElement: function(app) { |
| var div = createElement(app); |
| var a = div.firstChild; |
| @@ -344,7 +535,7 @@ var apps = (function() { |
| 'name': localStrings.getString('web_store_title'), |
| 'launch_url': localStrings.getString('web_store_url') |
| }); |
| - elm.setAttribute('app-id', 'web-store-entry'); |
| + elm.classList.add('web-store-entry'); |
| return elm; |
| }, |
| @@ -364,3 +555,6 @@ var apps = (function() { |
| } |
| }; |
| })(); |
| + |
| +// Enable drag and drop reordering of the app launcher. |
| +var appDragAndDrop = new DragAndDropController(apps); |