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

Unified Diff: chrome/browser/resources/ntp/apps.js

Issue 6297013: [NTP] Allow reordering of apps via drag and drop. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: nit Created 9 years, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/resources/ntp/apps.css ('k') | chrome/browser/resources/ntp/drag_drop_controller.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/ntp/apps.js
diff --git a/chrome/browser/resources/ntp/apps.js b/chrome/browser/resources/ntp/apps.js
index e61ebe0ea34a85798781e3f0628bddc5e0675a27..db15e58c75b188582dd499538d6ffac6b26b64a2 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({disableAnimations: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,230 @@ 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) {
+ this.data_ = data.map(function(app) {
+ return app.id;
+ });
+ 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_();
+ }
+ },
+
+ // The dimensions of each item in the app launcher. This calculates the
+ // dimensions dynamically, so it should be called after creating the DOM.
+ dimensions_: null,
+ get dimensions() {
+ if (this.dimensions_)
+ return this.dimensions_;
+
+ var app = this.dragContainer.firstChild;
+
+ var width = app.offsetWidth;
+ var height = app.offsetHeight;
+
+ // If the apps haven't properly loaded yet, don't cache the result.
+ if (app.offsetWidth == 0 || app.offsetHeight == 0)
+ return {width:0, height:0};
+
+ var style = getComputedStyle(app);
+
+ var marginWidth =
+ parseInt(style.marginLeft) + parseInt(style.marginRight);
+ var marginHeight =
+ parseInt(style.marginTop) + parseInt(style.marginBottom);
+
+ var borderWidth = parseInt(style.borderLeftWidth) +
+ parseInt(style.borderRightWidth);
+ var borderHeight = parseInt(style.borderTopWidth) +
+ parseInt(style.borderBottomWidth);
+
+ this.dimensions_ = {
+ width: width + marginWidth + borderWidth,
+ height: height + marginHeight + borderHeight
+ };
+
+ return this.dimensions_;
+ },
+
+ // 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 |coordinates| point to a valid drop location. The
+ // coordinates are relative to the drag container and the object should
+ // have the 'x' and 'y' properties set.
+ canDropOn: function(coordinates) {
+ var cols = MAX_APPS_PER_ROW[layoutMode];
+ var rows = Math.ceil(this.data.length / cols);
+
+ var bottom = rows * this.dimensions.height;
+ var right = cols * this.dimensions.width;
+
+ if (coordinates.x > right || coordinates.x < 0 ||
+ coordinates.y > bottom || coordinates.y < 0)
+ return false;
+
+ var position = this.getIndexAt_(coordinates);
+ var appCount = this.data.length;
+
+ if (!this.detachWebstoreEntry)
+ appCount--;
+
+ return position >= 0 && position < appCount;
+ },
+
+ setDragPlaceholder: function(coordinates) {
+ var position = this.getIndexAt_(coordinates);
+ 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();
+ },
+
+ getIndexAt_: function(coordinates) {
+ var x = coordinates.x;
+ var y = coordinates.y;
+
+ var w = this.dimensions.width;
+ var h = this.dimensions.height;
+
+ var availableWidth = this.dragContainer.offsetWidth;
+
+ var row = Math.floor(y / h);
+ var col = Math.floor(x / w);
+ var index = Math.floor(availableWidth / w) * row + col;
+
+ return index;
+ },
+
+ saveDrag: function() {
+ this.invalidate_();
+ this.layout();
+
+ var appIds = this.data.filter(function(id) {
+ return id != 'web-store-entry';
+ });
+
+ // 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', appIds);
+ }, this.transitionsDuration + 10);
+ },
+
+ layout: function(options) {
+ options = options || {};
+ if (!this.dirty_ && options.force != true)
+ return;
+
+ try {
+ var container = this.dragContainer;
+ if (options.disableAnimations)
+ container.setAttribute('launcher-animations', false);
+ var d0 = Date.now();
+ this.layoutImpl_();
+ this.dirty_ = false;
+ logEvent('apps.layout: ' + (Date.now() - d0));
+
+ } finally {
+ if (options.disableAnimations) {
+ // We need to re-enable animations asynchronously, so that the
+ // animations are still disabled for this layout update.
+ setTimeout(function() {
+ container.setAttribute('launcher-animations', true);
+ }, 0);
+ }
+ }
+ },
+
+ layoutImpl_: function() {
+ var apps = this.data;
+ var rects = this.getLayoutRects_(apps.length);
+ var appsContent = this.dragContainer;
+
+ if (!this.visible)
+ return;
+
+ for (var i = 0; i < apps.length; i++) {
+ var app = appsContent.querySelector('[app-id='+apps[i]+']').parentNode;
+
+ // If the node is being dragged, don't try to place it in the grid.
+ if (app == this.dragItem)
+ continue;
+
+ app.style.left = rects[i].left + 'px';
+ app.style.top = rects[i].top + 'px';
+ }
+
+ // We need to set the container's height manually because the apps use
+ // absolute positioning.
+ var rows = Math.ceil(apps.length / MAX_APPS_PER_ROW[layoutMode]);
+ appsContent.style.height = (rows * this.dimensions.height) + 'px';
+ },
+
+ getLayoutRects_: function(appCount) {
+ var availableWidth = this.dragContainer.offsetWidth;
+ var rtl = isRtl();
+ var rects = [];
+ var w = this.dimensions.width;
+ var h = this.dimensions.height;
+
+ 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;
+ rects[i] = {left: left, top: top, row: row};
+ }
+ return rects;
+ },
+
createElement: function(app) {
var div = createElement(app);
var a = div.firstChild;
@@ -344,7 +586,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 +606,6 @@ var apps = (function() {
}
};
})();
+
+// Enable drag and drop reordering of the app launcher.
+var appDragAndDrop = new DragAndDropController(apps);
« no previous file with comments | « chrome/browser/resources/ntp/apps.css ('k') | chrome/browser/resources/ntp/drag_drop_controller.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698