Commit 6b5ed297 authored by estade@chromium.org's avatar estade@chromium.org

NTP4: Add ability to drag links to the apps pages.

again, backend updates are not hooked up yet (because no backend exists)

1. allow tile pages to accept drags of non-tiles (with stub implementation of the tile's appearance)
2. refactor ugliness of dealing with multiple drag events into event handler thunks. This will also hopefully make it easier to hook up synthesized touch drag events.

BUG=none
TEST=manual

Review URL: http://codereview.chromium.org/7038051

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86184 0039d316-1c4b-4281-b951-d872f2087c98
parent 285a0088
...@@ -51,3 +51,18 @@ ...@@ -51,3 +51,18 @@
its almost home. */ its almost home. */
z-index: 100; z-index: 100;
} }
/* Link (bookmark) tiles. */
.link {
display: -webkit-box;
position: absolute;
-webkit-box-orient: vertical;
}
.link > * {
-webkit-box-flex: 1;
}
.link span {
display: block;
}
...@@ -79,6 +79,48 @@ cr.define('ntp4', function() { ...@@ -79,6 +79,48 @@ cr.define('ntp4', function() {
}, },
}; };
/**
* Creates a new Link object. This is a stub implementation for now.
* @param {Object} data The url and title.
* @constructor
* @extends {HTMLAnchorElement}
*/
function Link(data) {
var el = cr.doc.createElement('a');
el.__proto__ = Link.prototype;
el.data = data;
el.initialize();
return el;
};
Link.prototype = {
__proto__: HTMLAnchorElement.prototype,
initialize: function() {
this.className = 'link';
var thumbnailDiv = this.ownerDocument.createElement('div');
this.appendChild(thumbnailDiv);
var title = this.ownerDocument.createElement('span');
title.textContent = this.data.title;
this.appendChild(title);
},
/**
* Set the size and position of the link tile.
* @param {number} size The total size of |this|.
* @param {number} x The x-position.
* @param {number} y The y-position.
* animate.
*/
setBounds: function(size, x, y) {
this.style.width = this.style.height = size + 'px';
this.style.left = x + 'px';
this.style.top = y + 'px';
},
};
// The fraction of the app tile size that the icon uses. // The fraction of the app tile size that the icon uses.
var APP_IMG_SIZE_FRACTION = 4 / 5; var APP_IMG_SIZE_FRACTION = 4 / 5;
...@@ -124,6 +166,36 @@ cr.define('ntp4', function() { ...@@ -124,6 +166,36 @@ cr.define('ntp4', function() {
appendApp: function(appData) { appendApp: function(appData) {
this.appendTile(new App(appData)); this.appendTile(new App(appData));
}, },
/** @inheritDoc */
shouldAcceptDrag: function(tile, dataTransfer) {
return tile || (dataTransfer && dataTransfer.types.indexOf('url') != -1);
},
/** @inheritDoc */
addOutsideData: function(dataTransfer, index) {
var url = dataTransfer.getData('url');
assert(url);
if (!url)
return;
// If the dataTransfer has html data, use that html's text contents as the
// title of the new link.
var html = dataTransfer.getData('text/html');
var title;
if (html) {
// It's important that we don't attach this node to the document
// because it might contain scripts.
var node = this.ownerDocument.createElement('div');
node.innerHTML = html;
title = node.textContent;
}
if (!title)
title = url;
var data = {url: url, title: title};
this.addTileAt(new Link(data), index);
},
}; };
return { return {
......
...@@ -280,8 +280,8 @@ cr.define('ntp4', function() { ...@@ -280,8 +280,8 @@ cr.define('ntp4', function() {
}, },
/** @inheritDoc */ /** @inheritDoc */
acceptOutsideDrags: function() { shouldAcceptDrag: function(tile, dataTransfer) {
return false; return this.contains(tile);
}, },
/** @inheritDoc */ /** @inheritDoc */
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
*/ */
html { html {
font-family: segoe ui, arial, helvetica, sans-serif;
font-size: 14px;
/* It's necessary to put this here instead of in body in order to get the /* It's necessary to put this here instead of in body in order to get the
background-size of 100% to work properly */ background-size of 100% to work properly */
height: 100%; height: 100%;
...@@ -22,12 +24,6 @@ body { ...@@ -22,12 +24,6 @@ body {
-webkit-user-select: none; -webkit-user-select: none;
} }
body,
button span {
font-family: segoe ui, arial, helvetica, sans-serif;
font-size: 14px;
}
#card-slider-frame { #card-slider-frame {
/* Must match #footer height. */ /* Must match #footer height. */
bottom: 50px; bottom: 50px;
......
...@@ -60,7 +60,8 @@ body { ...@@ -60,7 +60,8 @@ body {
border-color-top: $$9; /* COLOR_NTP_SECTION_HEADER_RULE */ border-color-top: $$9; /* COLOR_NTP_SECTION_HEADER_RULE */
} }
.app span { .app span,
.link span {
color: $8; /* COLOR_NTP_TEXT */ color: $8; /* COLOR_NTP_TEXT */
} }
......
...@@ -334,12 +334,26 @@ cr.define('ntp4', function() { ...@@ -334,12 +334,26 @@ cr.define('ntp4', function() {
* @protected * @protected
*/ */
appendTile: function(tileElement) { appendTile: function(tileElement) {
this.calculateLayoutValues_(); this.addTileAt(tileElement, this.tileElements_.length);
},
/**
* Adds the given element to the tile grid.
* @param {Node} tileElement The tile object/node to insert.
* @param {number} index The location in the tile grid to insert it at.
* @protected
*/
addTileAt: function(tileElement, index) {
var wrapperDiv = new Tile(tileElement); var wrapperDiv = new Tile(tileElement);
this.tileGrid_.appendChild(wrapperDiv); if (index == this.tileElements_.length) {
this.tileGrid_.appendChild(wrapperDiv);
} else {
this.tileGrid_.insertBefore(wrapperDiv,
this.tileElements_[index]);
}
this.calculateLayoutValues_();
this.positionTile_(this.tileElements_.length - 1); this.positionTile_(index);
this.classList.remove('animating-tile-page'); this.classList.remove('animating-tile-page');
}, },
...@@ -562,12 +576,21 @@ cr.define('ntp4', function() { ...@@ -562,12 +576,21 @@ cr.define('ntp4', function() {
* The number of un-paired dragenter events that have fired on |this|. This * The number of un-paired dragenter events that have fired on |this|. This
* is incremented by |onDragEnter_| and decremented by |onDragLeave_|. This * is incremented by |onDragEnter_| and decremented by |onDragLeave_|. This
* is necessary because dragging over child widgets will fire additional * is necessary because dragging over child widgets will fire additional
* enter and leave events on |this|. * enter and leave events on |this|. A non-zero value does not necessarily
* indicate that |isCurrentDragTarget_| is true.
* @type {number} * @type {number}
* @private * @private
*/ */
dragEnters_: 0, dragEnters_: 0,
/**
* Whether the tile page is currently being dragged over with data it can
* accept.
* @type {boolean}
* @private
*/
isCurrentDragTarget_: false,
/** /**
* Handler for dragenter events fired on |tileGrid_|. * Handler for dragenter events fired on |tileGrid_|.
* @param {Event} e A MouseEvent for the drag. * @param {Event} e A MouseEvent for the drag.
...@@ -576,10 +599,57 @@ cr.define('ntp4', function() { ...@@ -576,10 +599,57 @@ cr.define('ntp4', function() {
onDragEnter_: function(e) { onDragEnter_: function(e) {
if (++this.dragEnters_ > 1) if (++this.dragEnters_ > 1)
return; return;
this.doDragEnter_(e);
},
// TODO(estade): for now we only allow tile drags. /**
if (!TilePage.currentlyDraggingTile) * Thunk for dragover events fired on |tileGrid_|.
* @param {Event} e A MouseEvent for the drag.
* @private
*/
onDragOver_: function(e) {
if (!this.isCurrentDragTarget_)
return; return;
this.doDragOver_(e);
},
/**
* Thunk for drop events fired on |tileGrid_|.
* @param {Event} e A MouseEvent for the drag.
* @private
*/
onDrop_: function(e) {
this.dragEnters_ = 0;
if (!this.isCurrentDragTarget_)
return;
this.doDrop_(e);
},
/**
* Thunk for dragleave events fired on |tileGrid_|.
* @param {Event} e A MouseEvent for the drag.
* @private
*/
onDragLeave_: function(e) {
if (--this.dragEnters_ > 0)
return;
this.isCurrentDragTarget_ = false;
this.cleanUpDrag_();
},
/**
* Performs all actions necessary when a drag enters the tile page.
* @param {Event} e A mouseover event for the drag enter.
* @private
*/
doDragEnter_: function(e) {
if (!this.shouldAcceptDrag(TilePage.currentlyDraggingTile,
e.dataTransfer)) {
return;
}
this.isCurrentDragTarget_ = true;
// Applies the mask so doppleganger tiles disappear into the fog. // Applies the mask so doppleganger tiles disappear into the fog.
this.updateMask_(); this.updateMask_();
...@@ -592,18 +662,19 @@ cr.define('ntp4', function() { ...@@ -592,18 +662,19 @@ cr.define('ntp4', function() {
}, },
/** /**
* Handler for dragover events fired on |tileGrid_|. * Performs all actions necessary when the user moves the cursor during
* @param {Event} e A MouseEvent for the drag. * a drag over the tile page.
* @param {Event} e A mouseover event for the drag over.
* @private * @private
*/ */
onDragOver_: function(e) { doDragOver_: function(e) {
e.dataTransfer.dropEffect = 'move';
var draggedTile = TilePage.currentlyDraggingTile;
if (!draggedTile)
return;
e.preventDefault(); e.preventDefault();
if (TilePage.currentlyDraggingTile)
e.dataTransfer.dropEffect = 'move';
else
e.dataTransfer.dropEffect = 'copy';
var newDragIndex = this.getWouldBeIndexForPoint_(e.clientX, e.clientY); var newDragIndex = this.getWouldBeIndexForPoint_(e.clientX, e.clientY);
if (newDragIndex < 0 || newDragIndex >= this.tileElements_.length) if (newDragIndex < 0 || newDragIndex >= this.tileElements_.length)
newDragIndex = this.dragItemIndex_; newDragIndex = this.dragItemIndex_;
...@@ -611,34 +682,29 @@ cr.define('ntp4', function() { ...@@ -611,34 +682,29 @@ cr.define('ntp4', function() {
}, },
/** /**
* Handler for drop events fired on |tileGrid_|. * Performs all actions necessary when the user completes a drop.
* @param {Event} e A MouseEvent for the drag. * @param {Event} e A mouseover event for the drag drop.
* @private * @private
*/ */
onDrop_: function(e) { doDrop_: function(e) {
this.dragEnters_ = 0;
e.stopPropagation(); e.stopPropagation();
this.isCurrentDragTarget_ = false;
var index = this.currentDropIndex_; var index = this.currentDropIndex_;
if ((index == this.dragItemIndex_) && this.withinPageDrag_) if ((index == this.dragItemIndex_) && this.withinPageDrag_)
return; return;
var adjustment = index > this.dragItemIndex_ ? 1 : 0; var adjustedIndex = this.currentDropIndex_ +
this.tileGrid_.insertBefore( (index > this.dragItemIndex_ ? 1 : 0);
TilePage.currentlyDraggingTile, if (TilePage.currentlyDraggingTile) {
this.tileElements_[this.currentDropIndex_ + adjustment]); this.tileGrid_.insertBefore(
this.cleanUpDrag_(); TilePage.currentlyDraggingTile,
}, this.tileElements_[adjustedIndex]);
} else {
/** this.addOutsideData(e.dataTransfer, adjustedIndex);
* Handler for dragleave events fired on |tileGrid_|. }
* @param {Event} e A MouseEvent for the drag.
* @private
*/
onDragLeave_: function(e) {
if (--this.dragEnters_ > 0)
return;
this.classList.remove('animating-tile-page');
this.cleanUpDrag_(); this.cleanUpDrag_();
}, },
...@@ -647,7 +713,6 @@ cr.define('ntp4', function() { ...@@ -647,7 +713,6 @@ cr.define('ntp4', function() {
* @private * @private
*/ */
cleanUpDrag_: function() { cleanUpDrag_: function() {
this.classList.remove('animating-tile-page');
for (var i = 0; i < this.tileElements_.length; i++) { for (var i = 0; i < this.tileElements_.length; i++) {
// The current drag tile will be positioned in its dragend handler. // The current drag tile will be positioned in its dragend handler.
if (this.tileElements_[i] == this.currentlyDraggingTile) if (this.tileElements_[i] == this.currentlyDraggingTile)
...@@ -686,10 +751,13 @@ cr.define('ntp4', function() { ...@@ -686,10 +751,13 @@ cr.define('ntp4', function() {
}, },
/** /**
* This is equivalent to dragEnters_, but for drags over the navigation * These are equivalent to dragEnters_ and isCurrentDragTarget_, but for
* dot. * drags over the navigation dot.
* TODO(estade): thunkify the event handlers in the same manner as the
* tile grid drag handlers.
*/ */
dotDragEnters_: 0, dotDragEnters_: 0,
dotIsCurrentDragTarget_: false,
/** /**
* A drag has entered the navigation dot. If the user hovers long enough, * A drag has entered the navigation dot. If the user hovers long enough,
...@@ -700,10 +768,12 @@ cr.define('ntp4', function() { ...@@ -700,10 +768,12 @@ cr.define('ntp4', function() {
if (++this.dotDragEnters_ > 1) if (++this.dotDragEnters_ > 1)
return; return;
if (!TilePage.currentlyDraggingTile) if (!this.shouldAcceptDrag(TilePage.currentlyDraggingTile,
return; e.dataTransfer)) {
if (!this.acceptOutsideDrags())
return; return;
}
this.dotIsCurrentDragTarget_ = true;
var self = this; var self = this;
function navPageClearTimeout() { function navPageClearTimeout() {
...@@ -721,11 +791,38 @@ cr.define('ntp4', function() { ...@@ -721,11 +791,38 @@ cr.define('ntp4', function() {
if (--this.dotDragEnters_ > 0) if (--this.dotDragEnters_ > 0)
return; return;
if (!this.dotIsCurrentDragTarget_)
return;
this.dotIsCurrentDragTarget_ = false;
if (this.dotNavTimeout) { if (this.dotNavTimeout) {
window.clearTimeout(this.dotNavTimeout); window.clearTimeout(this.dotNavTimeout);
this.dotNavTimeout = null; this.dotNavTimeout = null;
} }
}, },
/**
* Checks if a page can accept a drag with the given data.
* @param {Object} tile The drag tile, if any.
* @param {Object} dataTransfer The dataTransfer object, if the drag object
* is not a tile (e.g. it is a link).
* @return {boolean} True if this page can handle the drag.
*/
shouldAcceptDrag: function(tile, dataTransfer) {
return false;
},
/**
* Called to accept a drag drop.
* @param {Object} dataTransfer The data transfer object that holds the drop
* data.
* @param {number} index The tile index at which the drop occurred.
*/
addOutsideData: function(dataTransfer, index) {
// This should not get called unless there is a non-default
// implementation.
assert(false);
},
}; };
return { return {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment