mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 08:12:05 +00:00
Bug 828088 - Rework richgrid and richgriditem bindings to use css columns for down-then-across grids. Hardening downloads and tests. r=fryn
--HG-- extra : amend_source : 745c26b0287819fd4d002e9ca193c13618bbca0a
This commit is contained in:
parent
7deb49e1b6
commit
d232cab7e4
@ -328,8 +328,8 @@ TopSitesView.prototype = {
|
||||
|
||||
for (let idx=0; idx < length; idx++) {
|
||||
let isNew = !tileset.children[idx],
|
||||
item = tileset.children[idx] || document.createElement("richgriditem"),
|
||||
site = sites[idx];
|
||||
let item = isNew ? tileset.createItemElement(site.title, site.url) : tileset.children[idx];
|
||||
|
||||
this.updateTile(item, site);
|
||||
if (isNew) {
|
||||
|
@ -191,7 +191,9 @@
|
||||
if (!referrer)
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "showpage-button").setAttribute("disabled", "true");
|
||||
let file = Downloads._getLocalFile(this.getAttribute("target"));
|
||||
|
||||
if (!file) {
|
||||
throw new Error("download-done: Couldn't bind item with target: "+this.getAttribute("target"));
|
||||
}
|
||||
let mimeService = Components.classes["@mozilla.org/mime;1"].getService(Components.interfaces.nsIMIMEService);
|
||||
let mimeType;
|
||||
try {
|
||||
|
@ -3,7 +3,6 @@
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<bindings
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
@ -14,13 +13,17 @@
|
||||
extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
|
||||
<content>
|
||||
<html:div id="grid-div" anonid="grid" class="richgrid-grid">
|
||||
<html:div id="grid-div" anonid="grid" class="richgrid-grid" xbl:inherits="compact">
|
||||
<children/>
|
||||
</html:div>
|
||||
</content>
|
||||
|
||||
<implementation implements="nsIDOMXULSelectControlElement">
|
||||
<property name="_grid" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'grid');"/>
|
||||
|
||||
<property name="isBound" readonly="true" onget="return !!this._grid"/>
|
||||
<property name="isArranging" readonly="true" onget="return !!this._scheduledArrangeItemsTimerId"/>
|
||||
|
||||
<field name="controller">null</field>
|
||||
|
||||
<!-- nsIDOMXULMultiSelectControlElement (not fully implemented) -->
|
||||
@ -85,6 +88,8 @@
|
||||
<parameter name="aItem"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if(!this.isBound)
|
||||
return;
|
||||
if (this.controller)
|
||||
this.controller.handleItemClick(aItem);
|
||||
]]>
|
||||
@ -96,6 +101,8 @@
|
||||
<parameter name="aEvent"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if(!this.isBound)
|
||||
return;
|
||||
// we'll republish this as a selectionchange event on the grid
|
||||
aEvent.stopPropagation();
|
||||
this.toggleItemSelection(aItem);
|
||||
@ -180,7 +187,7 @@
|
||||
<parameter name="aSkipArrange"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let addition = this._createItemElement(aLabel, aValue);
|
||||
let addition = this.createItemElement(aLabel, aValue);
|
||||
this.appendChild(addition);
|
||||
if (!aSkipArrange)
|
||||
this.arrangeItems();
|
||||
@ -190,12 +197,14 @@
|
||||
</method>
|
||||
|
||||
<method name="clearAll">
|
||||
<parameter name="aSkipArrange"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
while (this.firstChild) {
|
||||
this.removeChild(this.firstChild);
|
||||
}
|
||||
this._grid.style.width = "0px";
|
||||
if (!aSkipArrange)
|
||||
this.arrangeItems();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
@ -208,7 +217,7 @@
|
||||
<body>
|
||||
<![CDATA[
|
||||
let existing = this.getItemAtIndex(anIndex);
|
||||
let addition = this._createItemElement(aLabel, aValue);
|
||||
let addition = this.createItemElement(aLabel, aValue);
|
||||
if (existing) {
|
||||
this.insertBefore(addition, existing);
|
||||
} else {
|
||||
@ -338,39 +347,39 @@
|
||||
<field name="_columnCount">0</field>
|
||||
<property name="columnCount" readonly="true" onget="return this._columnCount;"/>
|
||||
|
||||
<!-- define a height where we consider an item not yet rendered
|
||||
10 is the height of the empty item (padding/border etc. only) -->
|
||||
<field name="_itemHeightRenderThreshold">10</field>
|
||||
|
||||
<property name="_containerRect">
|
||||
<property name="_containerSize">
|
||||
<getter><![CDATA[
|
||||
// return the rect that represents our bounding box
|
||||
let containerNode = this.parentNode;
|
||||
|
||||
// Autocomplete is a binding within a binding, so we have to step
|
||||
// up an additional parentNode.
|
||||
let container = null;
|
||||
if (this.parentNode.id == "results-vbox" ||
|
||||
this.parentNode.id == "searches-vbox")
|
||||
container = this.parentNode.parentNode.getBoundingClientRect();
|
||||
else
|
||||
container = this.parentNode.getBoundingClientRect();
|
||||
return container;
|
||||
if (containerNode.id == "results-vbox" ||
|
||||
containerNode.id == "searches-vbox")
|
||||
containerNode = containerNode.parentNode;
|
||||
|
||||
let rect = containerNode.getBoundingClientRect();
|
||||
// return falsy if the container has no height
|
||||
return rect.height ? {
|
||||
width: rect.width,
|
||||
height: rect.height
|
||||
} : null;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<property name="_itemRect">
|
||||
<property name="_itemSize">
|
||||
<getter><![CDATA[
|
||||
// return the rect that represents an item in the grid
|
||||
// return the dimensions that represent an item in the grid
|
||||
|
||||
// TODO: when we remove the need for DOM item measurement, 0 items will not be a problem
|
||||
let item = this.itemCount ? this.getItemAtIndex(0) : null;
|
||||
if (item) {
|
||||
let gridItemRect = item.getBoundingClientRect();
|
||||
if (gridItemRect.height > this._itemHeightRenderThreshold) {
|
||||
return gridItemRect;
|
||||
}
|
||||
// grab tile/item dimensions
|
||||
this._tileSizes = this._getTileSizes();
|
||||
|
||||
let type = this.getAttribute("tiletype") || "default";
|
||||
let dims = this._tileSizes && this._tileSizes[type];
|
||||
if (!dims) {
|
||||
throw new Error("Missing tile sizes for '" + type + "' type");
|
||||
}
|
||||
return null;
|
||||
return dims;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
@ -378,13 +387,18 @@
|
||||
<property name="_canLayout" readonly="true">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
let gridItemRect = this._itemRect;
|
||||
if (!(this._grid && this._grid.style)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let gridItemSize = this._itemSize;
|
||||
|
||||
// If we don't have valid item dimensions we can't arrange yet
|
||||
if (!(gridItemRect && gridItemRect.height)) {
|
||||
if (!(gridItemSize && gridItemSize.height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let container = this._containerRect;
|
||||
let container = this._containerSize;
|
||||
// If we don't have valid container dimensions we can't arrange yet
|
||||
if (!(container && container.height)) {
|
||||
return false;
|
||||
@ -420,15 +434,14 @@
|
||||
if (this.hasAttribute("deferlayout")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._canLayout) {
|
||||
// try again later
|
||||
this._scheduleArrangeItems();
|
||||
return;
|
||||
}
|
||||
|
||||
let gridItemRect = this._itemRect;
|
||||
let container = this._containerRect;
|
||||
let itemDims = this._itemSize;
|
||||
let containerDims = this._containerSize;
|
||||
|
||||
// reset the flags
|
||||
if (this._scheduledArrangeItemsTimerId) {
|
||||
@ -437,21 +450,30 @@
|
||||
}
|
||||
this._scheduledArrangeItemsTries = 0;
|
||||
|
||||
// We favor overflowing horizontally, not vertically
|
||||
let maxRowCount = Math.floor(container.height / gridItemRect.height) - 1;
|
||||
// clear explicit width and columns before calculating from avail. height again
|
||||
let gridStyle = this._grid.style;
|
||||
gridStyle.removeProperty('min-width');
|
||||
gridStyle.removeProperty('-moz-column-count');
|
||||
|
||||
this._rowCount = this.getAttribute("rows");
|
||||
this._columnCount = this.getAttribute("columns");
|
||||
// We favor overflowing horizontally, not vertically (rows then colums)
|
||||
// rows attribute = max rows
|
||||
let maxRowCount = Math.min(this.getAttribute("rows") || Infinity, Math.floor(containerDims.height / itemDims.height));
|
||||
this._rowCount = Math.min(this.itemCount, maxRowCount);
|
||||
|
||||
if (!this._rowCount) {
|
||||
this._rowCount = Math.min(this.itemCount, maxRowCount);
|
||||
// columns attribute = min cols
|
||||
this._columnCount = this.itemCount ?
|
||||
Math.max(
|
||||
// at least 1 column when there are items
|
||||
this.getAttribute("columns") || 1,
|
||||
Math.ceil(this.itemCount / this._rowCount)
|
||||
) : this.getAttribute("columns") || 0;
|
||||
|
||||
// width is typically auto, cap max columns by truncating items collection
|
||||
// or, setting max-width style property with overflow hidden
|
||||
// '0' is an invalid value, just leave the property unset when 0 columns
|
||||
if (this._columnCount) {
|
||||
gridStyle.MozColumnCount = this._columnCount;
|
||||
}
|
||||
if (!this._columnCount){
|
||||
this._columnCount = Math.ceil(this.itemCount / this._rowCount);
|
||||
}
|
||||
|
||||
this._grid.style.width = (this._columnCount * gridItemRect.width) + "px";
|
||||
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
@ -486,6 +508,7 @@
|
||||
<![CDATA[
|
||||
if (this.controller && this.controller.gridBoundCallback != undefined)
|
||||
this.controller.gridBoundCallback();
|
||||
|
||||
// set up cross-slide gesture handling for multiple-selection grids
|
||||
if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) {
|
||||
this._xslideHandler = new CrossSlide.Handler(this, {
|
||||
@ -495,8 +518,9 @@
|
||||
this.addEventListener("touchmove", this._xslideHandler, false);
|
||||
this.addEventListener("touchend", this._xslideHandler, false);
|
||||
}
|
||||
|
||||
// XXX This event was never actually implemented (bug 223411).
|
||||
var event = document.createEvent("Events");
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("contentgenerated", true, true);
|
||||
this.dispatchEvent(event);
|
||||
]]>
|
||||
@ -511,6 +535,62 @@
|
||||
}
|
||||
]]>
|
||||
</destructor>
|
||||
|
||||
<property name="tileWidth" readonly="true" onget="return this._itemSize.width"/>
|
||||
<property name="tileHeight" readonly="true" onget="return this._itemSize.height"/>
|
||||
|
||||
<field name="_tileStyleSheetName">"tiles.css"</field>
|
||||
<method name="_getTileSizes">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Tile sizes are constants, this avoids the need to measure a rendered item before grid layout
|
||||
// The defines.inc used by the theme CSS is the single source of truth for these values
|
||||
// This method locates and parses out (just) those dimensions from the stylesheet
|
||||
|
||||
let typeSizes = this.ownerDocument.defaultView._richgridTileSizes;
|
||||
if (typeSizes && typeSizes["default"]) {
|
||||
return typeSizes;
|
||||
}
|
||||
|
||||
// cache sizes on the global window object, for reuse between bound nodes
|
||||
typeSizes = this.ownerDocument.defaultView._richgridTileSizes = {};
|
||||
|
||||
let sheets = this.ownerDocument.styleSheets;
|
||||
// the (first matching) rules that will give us tile type => width/height values
|
||||
let typeSelectors = {
|
||||
"richgriditem" : "default",
|
||||
"richgriditem[customImage]": "thumbnail",
|
||||
"richgriditem[compact]": "compact"
|
||||
};
|
||||
let rules, sheet;
|
||||
for (let i=0; (sheet=sheets[i]); i++) {
|
||||
if (sheet.href && sheet.href.endsWith( this._tileStyleSheetName )) {
|
||||
rules = sheet.cssRules;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rules) {
|
||||
// walk the stylesheet rules until we've matched all our selectors
|
||||
for (let i=0, rule;(rule=rules[i]); i++) {
|
||||
let type = rule.selectorText && typeSelectors[rule.selectorText];
|
||||
if (type) {
|
||||
let sizes = typeSizes[type] = {};
|
||||
typeSelectors[type] = null;
|
||||
delete typeSelectors[type];
|
||||
// we assume px unit for tile dimension values
|
||||
sizes.width = parseInt(rule.style.getPropertyValue("width"));
|
||||
sizes.height = parseInt(rule.style.getPropertyValue("height"));
|
||||
}
|
||||
if (!Object.keys(typeSelectors).length)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
throw new Error("Failed to find stylesheet to parse out richgriditem dimensions\n");
|
||||
}
|
||||
return typeSizes;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="_isIndexInBounds">
|
||||
<parameter name="anIndex"/>
|
||||
<body>
|
||||
@ -520,13 +600,12 @@
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_createItemElement">
|
||||
<method name="createItemElement">
|
||||
<parameter name="aLabel"/>
|
||||
<parameter name="aValue"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let item = this.ownerDocument.createElement("richgriditem");
|
||||
item.control = this;
|
||||
item.setAttribute("label", aLabel);
|
||||
if (aValue)
|
||||
item.setAttribute("value", aValue);
|
||||
@ -541,7 +620,7 @@
|
||||
if (this.suppressOnSelect || this._suppressOnSelect)
|
||||
return;
|
||||
|
||||
var event = document.createEvent("Events");
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("select", true, true);
|
||||
this.dispatchEvent(event);
|
||||
]]>
|
||||
@ -555,7 +634,7 @@
|
||||
if (this.suppressOnSelect || this._suppressOnSelect)
|
||||
return;
|
||||
|
||||
var event = document.createEvent("Events");
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("selectionchange", true, true);
|
||||
this.dispatchEvent(event);
|
||||
]]>
|
||||
@ -630,22 +709,30 @@
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="richgrid-item">
|
||||
<content>
|
||||
<xul:vbox anonid="anon-richgrid-item" class="richgrid-item-content" xbl:inherits="customImage">
|
||||
<xul:hbox class="richgrid-icon-container" xbl:inherits="customImage">
|
||||
<xul:box class="richgrid-icon-box"><xul:image anonid="anon-richgrid-item-icon" xbl:inherits="src=iconURI"/></xul:box>
|
||||
<xul:box flex="1" />
|
||||
</xul:hbox>
|
||||
<xul:description anonid="anon-richgrid-item-label" class="richgrid-item-desc" xbl:inherits="value=label" crop="end"/>
|
||||
</xul:vbox>
|
||||
<html:div anonid="anon-tile" class="tile-content" xbl:inherits="customImage">
|
||||
<html:div class="tile-start-container" xbl:inherits="customImage">
|
||||
<html:div class="tile-icon-box"><xul:image anonid="anon-tile-icon" xbl:inherits="src=iconURI"/></html:div>
|
||||
</html:div>
|
||||
<html:div anonid="anon-tile-label" class="tile-desc" xbl:inherits="xbl:text=label"/>
|
||||
</html:div>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<property name="_box" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item');"/>
|
||||
<property name="_textbox" onget="return document.getAnonymousElementByAttribute(this, 'class', 'richgrid-item-desc');"/>
|
||||
<property name="_icon" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item-icon');"/>
|
||||
<property name="_label" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item-label');"/>
|
||||
<property name="isBound" readonly="true" onget="return !!this._icon"/>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
this.refresh();
|
||||
]]>
|
||||
</constructor>
|
||||
<property name="_boundNode" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-tile').parentNode;"/>
|
||||
<property name="_contentBox" onget="return document.getAnonymousElementByAttribute(this, 'class', 'tile-content');"/>
|
||||
<property name="_textbox" onget="return document.getAnonymousElementByAttribute(this, 'class', 'tile-desc');"/>
|
||||
<property name="_top" onget="return document.getAnonymousElementByAttribute(this, 'class', 'tile-start-container');"/>
|
||||
<property name="_icon" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-tile-icon');"/>
|
||||
<property name="_label" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-tile-label');"/>
|
||||
<property name="iconSrc"
|
||||
onset="this._icon.src = val; this.setAttribute('iconURI', val);"
|
||||
onget="return this._icon.src;" />
|
||||
@ -663,16 +750,11 @@
|
||||
onget="return this.hasAttribute('pinned')"
|
||||
onset="if (val) { this.setAttribute('pinned', val) } else this.removeAttribute('pinned');"/>
|
||||
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
this.refresh();
|
||||
]]>
|
||||
</constructor>
|
||||
<method name="refresh">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Prevent an exception in case binding is not done yet.
|
||||
if (!this._icon)
|
||||
if(!this.isBound)
|
||||
return;
|
||||
|
||||
// Seed the binding properties from bound-node attribute values
|
||||
@ -694,7 +776,7 @@
|
||||
|
||||
<property name="control">
|
||||
<getter><![CDATA[
|
||||
var parent = this.parentNode;
|
||||
let parent = this.parentNode;
|
||||
while (parent) {
|
||||
if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
|
||||
return parent;
|
||||
@ -708,12 +790,10 @@
|
||||
<setter><![CDATA[
|
||||
if (val) {
|
||||
this.setAttribute("customColor", val);
|
||||
this._box.style.backgroundColor = val;
|
||||
this._textbox.style.backgroundColor = val;
|
||||
this._contentBox.style.backgroundColor = val;
|
||||
} else {
|
||||
this.removeAttribute("customColor");
|
||||
this._box.style.removeProperty("background-color");
|
||||
this._textbox.style.removeProperty("background-color");
|
||||
this._contentBox.style.removeProperty("background-color");
|
||||
}
|
||||
]]></setter>
|
||||
</property>
|
||||
@ -722,19 +802,21 @@
|
||||
<setter><![CDATA[
|
||||
if (val) {
|
||||
this.setAttribute("customImage", val);
|
||||
this._box.style.backgroundImage = val;
|
||||
this._top.style.backgroundImage = val;
|
||||
} else {
|
||||
this.removeAttribute("customImage");
|
||||
this._box.style.removeProperty("background-image");
|
||||
this._top.style.removeProperty("background-image");
|
||||
}
|
||||
]]></setter>
|
||||
</property>
|
||||
|
||||
<method name="refreshBackgroundImage">
|
||||
<body><![CDATA[
|
||||
if(!this.isBound)
|
||||
return;
|
||||
if (this.backgroundImage) {
|
||||
this._box.style.removeProperty("background-image");
|
||||
this._box.style.setProperty("background-image", this.backgroundImage);
|
||||
this._top.style.removeProperty("background-image");
|
||||
this._top.style.setProperty("background-image", this.backgroundImage);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
@ -772,12 +854,11 @@
|
||||
<handler event="contextmenu">
|
||||
<![CDATA[
|
||||
// fires for right-click, long-click and (keyboard) contextmenu input
|
||||
// TODO: handle cross-slide event when it becomes available,
|
||||
// .. using contextmenu is a stop-gap measure to allow us to
|
||||
// toggle the selected state of tiles in a grid
|
||||
this.control.handleItemContextMenu(this, event);
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
||||
|
@ -9,6 +9,7 @@
|
||||
<?xml-stylesheet href="chrome://browser/skin/forms.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/cssthrobber.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
@ -194,7 +195,7 @@
|
||||
<scrollbox id="start-scrollbox" orient="horizontal" observes="bcast_preciseInput" flex="1">
|
||||
<vbox id="start-topsites" class="meta-section">
|
||||
<label class="meta-section-title" value="&startTopSitesHeader.label;"/>
|
||||
<richgrid id="start-topsites-grid" rows="3" columns="3" seltype="multiple" flex="1"/>
|
||||
<richgrid id="start-topsites-grid" rows="3" columns="3" tiletype="thumbnail" seltype="multiple" flex="1"/>
|
||||
</vbox>
|
||||
<vbox id="start-bookmarks" class="meta-section">
|
||||
<label class="meta-section-title" value="&startBookmarksHeader.label;"/>
|
||||
@ -210,8 +211,9 @@
|
||||
</vbox>
|
||||
</scrollbox>
|
||||
</hbox>
|
||||
|
||||
<!-- snapped view -->
|
||||
<vbox id="snapped-start" class="start-page" observes="bcast_windowState">
|
||||
<hbox id="snapped-start" class="start-page" flex="1" observes="bcast_windowState">
|
||||
<scrollbox id="snapped-scrollbox" orient="vertical" flex="1">
|
||||
<vbox id="snapped-topsites">
|
||||
<label class="meta-section-title" value="&startTopSitesHeader.label;"/>
|
||||
@ -224,7 +226,7 @@
|
||||
<label id="snappedRemoteTabsLabel" class="meta-section-title" value="&snappedRemoteTabsHeader.label;"
|
||||
onclick="PanelUI.show('remotetabs-container');" inputProcessing="true"/>
|
||||
</scrollbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<!-- Autocompletion interface -->
|
||||
<box id="start-autocomplete" observes="bcast_windowState"/>
|
||||
</hbox>
|
||||
|
@ -20,8 +20,14 @@ var Downloads = {
|
||||
|
||||
_getLocalFile: function dh__getLocalFile(aFileURI) {
|
||||
// XXX it's possible that using a null char-set here is bad
|
||||
|
||||
const fileUrl = Services.io.newURI(aFileURI.spec || aFileURI, null, null).QueryInterface(Ci.nsIFileURL);
|
||||
let spec = ('string' == typeof aFileURI) ? aFileURI : aFileURI.spec;
|
||||
let fileUrl;
|
||||
try {
|
||||
fileUrl = Services.io.newURI(spec, null, null).QueryInterface(Ci.nsIFileURL);
|
||||
} catch (ex) {
|
||||
Util.dumpLn("_getLocalFile: Caught exception creating newURI from file spec: "+aFileURI.spec+": " + ex.message);
|
||||
return;
|
||||
}
|
||||
return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile);
|
||||
},
|
||||
|
||||
@ -43,13 +49,21 @@ var Downloads = {
|
||||
openDownload: function dh_openDownload(aDownload) {
|
||||
// expects xul item
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
let download = this.manager.getDownload(id)
|
||||
let download = this.manager.getDownload(id);
|
||||
let fileURI = download.target;
|
||||
let file = this._getLocalFile(fileURI);
|
||||
|
||||
if (!(fileURI && fileURI.spec)) {
|
||||
Util.dumpLn("Cant open download "+id+", fileURI is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
let file = this._getLocalFile(fileURI);
|
||||
try {
|
||||
file.launch();
|
||||
} catch (ex) { }
|
||||
file && file.launch();
|
||||
} catch (ex) {
|
||||
Util.dumpLn("Failed to open download, with id: "+id+", download target URI spec: " + fileURI.spec);
|
||||
Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
|
||||
}
|
||||
},
|
||||
|
||||
removeDownload: function dh_removeDownload(aDownload) {
|
||||
@ -66,14 +80,24 @@ var Downloads = {
|
||||
|
||||
cancelDownload: function dh_cancelDownload(aDownload) {
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
let download = this.manager.getDownload(id)
|
||||
let download = this.manager.getDownload(id);
|
||||
this.manager.cancelDownload(id);
|
||||
|
||||
let fileURI = download.target;
|
||||
let file = this._getLocalFile(fileURI);
|
||||
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
if (!(fileURI && fileURI.spec)) {
|
||||
Util.dumpLn("Cant remove download file for: "+id+", fileURI is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
let file = this._getLocalFile(fileURI);
|
||||
try {
|
||||
if (file && file.exists())
|
||||
file.remove(false);
|
||||
} catch (ex) {
|
||||
Util.dumpLn("Failed to cancel download, with id: "+id+", download target URI spec: " + fileURI.spec);
|
||||
Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
|
||||
}
|
||||
},
|
||||
|
||||
pauseDownload: function dh_pauseDownload(aDownload) {
|
||||
@ -93,7 +117,7 @@ var Downloads = {
|
||||
|
||||
showPage: function dh_showPage(aDownload) {
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
let download = this.manager.getDownload(id)
|
||||
let download = this.manager.getDownload(id);
|
||||
let uri = this._getReferrerOrSource(download);
|
||||
if (uri)
|
||||
BrowserUI.newTab(uri, Browser.selectedTab);
|
||||
@ -197,13 +221,14 @@ DownloadsView.prototype = {
|
||||
},
|
||||
|
||||
_getAttrsForDownload: function dv__getAttrsForDownload(aDownload) {
|
||||
// expects a DownloadManager download object
|
||||
// params: nsiDownload
|
||||
return {
|
||||
typeName: 'download',
|
||||
downloadId: aDownload.id,
|
||||
downloadGuid: aDownload.guid,
|
||||
name: aDownload.displayName,
|
||||
target: aDownload.target,
|
||||
// use the stringified version of the target nsIURI for the item attribute
|
||||
target: aDownload.target.spec,
|
||||
iconURI: "moz-icon://" + aDownload.displayName + "?size=64",
|
||||
date: DownloadUtils.getReadableDates(new Date())[0],
|
||||
domain: DownloadUtils.getURIHost(aDownload.source.spec)[0],
|
||||
@ -215,6 +240,8 @@ DownloadsView.prototype = {
|
||||
_updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) {
|
||||
for (let name in aAttrs)
|
||||
anItem.setAttribute(name, aAttrs[name]);
|
||||
if (anItem.refresh)
|
||||
anItem.refresh();
|
||||
},
|
||||
|
||||
_getDownloadSize: function dv__getDownloadSize (aSize) {
|
||||
@ -322,8 +349,7 @@ DownloadsView.prototype = {
|
||||
},
|
||||
|
||||
clearDownloads: function dv_clearDownloads() {
|
||||
while (this._set.itemCount > 0)
|
||||
this._set.removeItemAt(0);
|
||||
this._set.clearAll();
|
||||
},
|
||||
|
||||
addDownload: function dv_addDownload(aDownload) {
|
||||
|
@ -120,9 +120,8 @@ function test() {
|
||||
/////////////////////////////////////
|
||||
// shared test setup
|
||||
function resetDownloads(){
|
||||
var defd = Promise.defer();
|
||||
// do the reset, resolve the defd when done
|
||||
// TODO (sfoster) clear out downloads db, reset relevant state
|
||||
// clear out existing and any pending downloads in the db
|
||||
// returns a promise
|
||||
|
||||
let promisedResult = getPromisedDbResult(
|
||||
"DELETE FROM moz_downloads"
|
||||
@ -190,7 +189,7 @@ function addDownloadRow(aDataRow) {
|
||||
|
||||
function gen_addDownloadRows(aDataRows){
|
||||
if (!aDataRows.length) {
|
||||
yield;
|
||||
yield null;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -238,17 +237,17 @@ gTests.push({
|
||||
|
||||
yield resetDownloads();
|
||||
|
||||
let downloadslist = document.getElementById("downloads-list");
|
||||
let downloadsList = document.getElementById("downloads-list");
|
||||
|
||||
// wait for the richgrid to announce its readiness
|
||||
// .. fail a test if the timeout is exceeded
|
||||
let isReady = waitForEvent(downloadslist, "DownloadsReady", 2000);
|
||||
let isReady = waitForEvent(downloadsList, "DownloadsReady", 2000);
|
||||
// tickle the view to cause it to refresh itself
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
|
||||
yield isReady;
|
||||
|
||||
let count = downloadslist.children.length;
|
||||
let count = downloadsList.children.length;
|
||||
is(count, 0, "Zero items in grid view with empty downloads db");
|
||||
}
|
||||
});
|
||||
@ -272,11 +271,16 @@ gTests.push({
|
||||
{ endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED },
|
||||
{ endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL },
|
||||
{ endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY },
|
||||
{ endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY },
|
||||
{ endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY }
|
||||
];
|
||||
|
||||
yield resetDownloads();
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let downloadsList = document.getElementById("downloads-list");
|
||||
// wait for the richgrid to announce its readiness
|
||||
// .. fail a test if the timeout is exceeded
|
||||
let isReady = waitForEvent(downloadsList, "DownloadsReady", 2000);
|
||||
|
||||
// NB: beware display limits which might cause mismatch btw. rendered item and db rows
|
||||
|
||||
@ -285,12 +289,6 @@ gTests.push({
|
||||
// we're going to add stuff to the downloads db.
|
||||
yield spawn( gen_addDownloadRows( DownloadData ) );
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let downloadslist = document.getElementById("downloads-list");
|
||||
// wait for the richgrid to announce its readiness
|
||||
// .. fail a test if the timeout is exceeded
|
||||
let isReady = waitForEvent(downloadslist, "DownloadsReady", 2000);
|
||||
|
||||
// tickle the view to cause it to refresh itself
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
|
||||
@ -300,11 +298,11 @@ gTests.push({
|
||||
ok(false, "DownloadsReady event never fired");
|
||||
}
|
||||
|
||||
is(downloadslist.children.length, DownloadData.length,
|
||||
is(downloadsList.children.length, DownloadData.length,
|
||||
"There is the correct number of richlistitems");
|
||||
|
||||
for (let i = 0; i < downloadslist.children.length; i++) {
|
||||
let element = downloadslist.children[i];
|
||||
for (let i = 0; i < downloadsList.children.length; i++) {
|
||||
let element = downloadsList.children[i];
|
||||
let id = element.getAttribute("downloadId");
|
||||
let dataItem = Downloads.manager.getDownload(id); // nsIDownload object
|
||||
|
||||
@ -365,10 +363,10 @@ gTests.push({
|
||||
yield spawn( gen_addDownloadRows( DownloadData ) );
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let downloadslist = document.getElementById("downloads-list");
|
||||
let downloadsList = document.getElementById("downloads-list");
|
||||
// wait for the richgrid to announce its readiness
|
||||
// .. fail a test if the timeout is exceeded
|
||||
let isReady = waitForEvent(downloadslist, "DownloadsReady", 2000);
|
||||
let isReady = waitForEvent(downloadsList, "DownloadsReady", 2000);
|
||||
// tickle the view to cause it to refresh itself
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
|
||||
@ -395,7 +393,7 @@ gTests.push({
|
||||
is(downloadRows.length, 3, "Correct number of downloads in the db before removal");
|
||||
|
||||
// remove the first one
|
||||
let itemNode = downloadslist.children[0];
|
||||
let itemNode = downloadsList.children[0];
|
||||
let id = itemNode.getAttribute("downloadId");
|
||||
// check the file exists
|
||||
let download = Downloads.manager.getDownload( id );
|
||||
@ -421,7 +419,7 @@ gTests.push({
|
||||
|
||||
is(downloadRows.length, 2, "Correct number of downloads in the db after removal");
|
||||
|
||||
is(2, downloadslist.children.length,
|
||||
is(2, downloadsList.children.length,
|
||||
"Removing a download updates the items list");
|
||||
ok(file && file.exists(), "File still exists after download removal");
|
||||
|
||||
|
@ -7,15 +7,32 @@
|
||||
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window []>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<vbox flex="1">
|
||||
<vbox id="alayout">
|
||||
<richgrid id="grid_layout" seltype="single" flex="1">
|
||||
</richgrid>
|
||||
</vbox>
|
||||
<vbox style="height:600px">
|
||||
<hbox>
|
||||
<richgrid id="clearGrid" seltype="single" flex="1" rows="2">
|
||||
<richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
|
||||
<richgriditem value="about:blank" id="clearGrid_item2" label="2nd item"/>
|
||||
<richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
|
||||
</richgrid>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<richgrid id="emptyGrid" seltype="single" flex="1" rows="2">
|
||||
</richgrid>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<richgrid id="grid1" seltype="single" flex="1">
|
||||
<richgriditem value="about:blank" id="grid1_item1" label="First item"/>
|
||||
<richgriditem value="about:blank" id="grid1_item2" label="2nd item"/>
|
||||
</richgrid>
|
||||
</hbox>
|
||||
<hbox>
|
||||
|
@ -20,7 +20,7 @@ gTests.push({
|
||||
ok(grid, "#grid1 is found");
|
||||
is(typeof grid.clearSelection, "function", "#grid1 has the binding applied");
|
||||
|
||||
is(grid.children.length, 1, "#grid1 has a single item");
|
||||
is(grid.children.length, 2, "#grid1 has a 2 items");
|
||||
is(grid.children[0].control, grid, "#grid1 item's control points back at #grid1'");
|
||||
}
|
||||
});
|
||||
@ -62,9 +62,81 @@ gTests.push({
|
||||
desc: "arrangeItems",
|
||||
run: function() {
|
||||
// implements an arrangeItems method, with optional cols, rows signature
|
||||
let grid = doc.querySelector("#grid1");
|
||||
let container = doc.getElementById("alayout");
|
||||
let grid = doc.querySelector("#grid_layout");
|
||||
|
||||
is(typeof grid.arrangeItems, "function", "arrangeItems is a function on the grid");
|
||||
todo(false, "Test outcome of arrangeItems with cols and rows arguments");
|
||||
|
||||
ok(grid.tileHeight, "grid has truthy tileHeight value");
|
||||
ok(grid.tileWidth, "grid has truthy tileWidth value");
|
||||
|
||||
// make the container big enough for 3 rows
|
||||
container.style.height = 3 * grid.tileHeight + 20 + "px";
|
||||
|
||||
// add some items
|
||||
grid.appendItem("test title", "about:blank", true);
|
||||
grid.appendItem("test title", "about:blank", true);
|
||||
grid.appendItem("test title", "about:blank", true);
|
||||
grid.appendItem("test title", "about:blank", true);
|
||||
grid.appendItem("test title", "about:blank", true);
|
||||
|
||||
grid.arrangeItems();
|
||||
// they should all fit nicely in a 3x2 grid
|
||||
is(grid.rowCount, 3, "rowCount is calculated correctly for a given container height and tileheight");
|
||||
is(grid.columnCount, 2, "columnCount is calculated correctly for a given container maxWidth and tilewidth");
|
||||
|
||||
// squish the available height
|
||||
// should overflow (clip) a 2x2 grid
|
||||
|
||||
let under3rowsHeight = (3 * grid.tileHeight -20) + "px";
|
||||
container.style.height = under3rowsHeight;
|
||||
grid.arrangeItems();
|
||||
|
||||
is(grid.rowCount, 2, "rowCount is re-calculated correctly for a given container height");
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "clearAll",
|
||||
run: function() {
|
||||
let grid = doc.getElementById("clearGrid");
|
||||
grid.arrangeItems();
|
||||
|
||||
// grid has rows=2 so we expect at least 2 rows and 2 columns with 3 items
|
||||
is(typeof grid.clearAll, "function", "clearAll is a function on the grid");
|
||||
is(grid.itemCount, 3, "grid has 3 items initially");
|
||||
is(grid.rowCount, 2, "grid has 2 rows initially");
|
||||
is(grid.columnCount, 2, "grid has 2 cols initially");
|
||||
|
||||
let arrangeSpy = spyOnMethod(grid, "arrangeItems");
|
||||
grid.clearAll();
|
||||
|
||||
is(grid.itemCount, 0, "grid has 0 itemCount after clearAll");
|
||||
is(grid.children.length, 0, "grid has 0 children after clearAll");
|
||||
is(grid.rowCount, 0, "grid has 0 rows when empty");
|
||||
is(grid.columnCount, 0, "grid has 0 cols when empty");
|
||||
|
||||
is(arrangeSpy.callCount, 1, "arrangeItems is called once when we clearAll");
|
||||
arrangeSpy.restore();
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "empty grid",
|
||||
run: function() {
|
||||
let grid = doc.getElementById("emptyGrid");
|
||||
grid.arrangeItems();
|
||||
yield waitForCondition(() => !grid.isArranging);
|
||||
|
||||
// grid has rows=2 but 0 items
|
||||
ok(grid.isBound, "binding was applied");
|
||||
is(grid.itemCount, 0, "empty grid has 0 items");
|
||||
is(grid.rowCount, 0, "empty grid has 0 rows");
|
||||
is(grid.columnCount, 0, "empty grid has 0 cols");
|
||||
|
||||
let columnsNode = grid._grid;
|
||||
let cStyle = doc.defaultView.getComputedStyle(columnsNode);
|
||||
is(cStyle.getPropertyValue("-moz-column-count"), "auto", "empty grid has -moz-column-count: auto");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -784,6 +784,24 @@ function runTests() {
|
||||
});
|
||||
}
|
||||
|
||||
// wrap a method with a spy that records how and how many times it gets called
|
||||
// the spy is returned; use spy.restore() to put the original back
|
||||
function spyOnMethod(aObj, aMethod) {
|
||||
let origFunc = aObj[aMethod];
|
||||
let spy = function() {
|
||||
spy.calledWith = Array.slice(arguments);
|
||||
spy.callCount++;
|
||||
return (spy.returnValue = origFunc.apply(aObj, arguments));
|
||||
};
|
||||
spy.callCount = 0;
|
||||
spy.restore = function() {
|
||||
return (aObj[aMethod] = origFunc);
|
||||
};
|
||||
return (aObj[aMethod] = spy);
|
||||
}
|
||||
|
||||
// replace a method with a stub that records how and how many times it gets called
|
||||
// the stub is returned; use stub.restore() to put the original back
|
||||
function stubMethod(aObj, aMethod) {
|
||||
let origFunc = aObj[aMethod];
|
||||
let func = function() {
|
||||
|
@ -122,7 +122,7 @@
|
||||
opacity: 0;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1, 1);
|
||||
@ -168,7 +168,7 @@ documenttab[closing] > .documenttab-container {
|
||||
font-size: @metro_font_normal@;
|
||||
width: @thumbnail_width@;
|
||||
padding: 4px @metro_spacing_snormal@ 8px;
|
||||
|
||||
|
||||
background: #000;
|
||||
opacity: 0.95;
|
||||
color: #fff;
|
||||
@ -223,7 +223,7 @@ documenttab[selected] .documenttab-selection {
|
||||
}
|
||||
|
||||
.selection-overlay {
|
||||
pointer-events: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.selection-overlay:-moz-focusring {
|
||||
@ -732,13 +732,15 @@ setting[type="radio"] > vbox {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
/*tile content should be on same line in snapped view */
|
||||
#snapped-topsites-grid > richgriditem > .richgrid-item-content {
|
||||
-moz-box-orient: horizontal;
|
||||
/* startUI sections, grids */
|
||||
#start-container .meta-section {
|
||||
/* allot space for at least a single column */
|
||||
min-width: @grid_double_column_width@;
|
||||
}
|
||||
|
||||
[viewstate="snapped"] .canSnapTiles .richgrid-item-desc {
|
||||
-moz-margin-start: 8px;
|
||||
#start-topsites {
|
||||
/* allot space for 3 tile columns for the topsites grid */
|
||||
min-width: calc(3 * @grid_double_column_width@);
|
||||
}
|
||||
|
||||
/* if snapped, hide the fullscreen awesome screen, if viewstate is anything
|
||||
@ -761,9 +763,9 @@ setting[type="radio"] > vbox {
|
||||
|
||||
#start-container[viewstate="snapped"] .meta-section {
|
||||
margin: 0px;
|
||||
min-width: @grid_double_column_width@;
|
||||
}
|
||||
|
||||
|
||||
/* Browser Content Areas ----------------------------------------------------- */
|
||||
|
||||
/* Hide the browser while the start UI is visible */
|
||||
|
@ -29,8 +29,16 @@
|
||||
%define thumbnail_width 232px
|
||||
%define thumbnail_height 148px
|
||||
|
||||
%define grid_column_width 131px
|
||||
%define grid_double_column_width 262px
|
||||
%define grid_row_height 86px
|
||||
%define grid_double_row_height 172px
|
||||
|
||||
%define compactgrid_column_width 62px
|
||||
%define compactgrid_row_height 62px
|
||||
|
||||
%define tile_border_color #dbdcde
|
||||
%define tile_width 200px
|
||||
%define tile_spacing 12px
|
||||
|
||||
%define scroller_thickness 4px
|
||||
%define scroller_minimum 8px
|
||||
|
@ -15,6 +15,7 @@ chrome.jar:
|
||||
skin/config.css (config.css)
|
||||
* skin/forms.css (forms.css)
|
||||
* skin/platform.css (platform.css)
|
||||
* skin/tiles.css (tiles.css)
|
||||
skin/touchcontrols.css (touchcontrols.css)
|
||||
skin/netError.css (netError.css)
|
||||
% override chrome://global/skin/about.css chrome://browser/skin/about.css
|
||||
|
@ -447,148 +447,6 @@ notification {
|
||||
}
|
||||
|
||||
|
||||
/* Rich Grid ---------------------------------------------------------------- */
|
||||
|
||||
richgrid {
|
||||
display: -moz-box;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
||||
richgrid .meta-grid {
|
||||
display: block;
|
||||
}
|
||||
|
||||
richgriditem {
|
||||
padding: @metro_spacing_small@;
|
||||
}
|
||||
|
||||
richgriditem .richgrid-item-content {
|
||||
border: @metro_border_thin@ solid @tile_border_color@;
|
||||
box-shadow: 0 0 @metro_spacing_snormal@ rgba(0, 0, 0, 0.1);
|
||||
-moz-box-sizing: border-box;
|
||||
padding: 10px 8px 6px 8px;
|
||||
position: relative;
|
||||
}
|
||||
.richgrid-item-content {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
richgriditem[selected] .richgrid-item-content::after {
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-image: url(chrome://browser/skin/images/tile-selected-check-hdpi.png);
|
||||
background-origin: border-box;
|
||||
background-position: right 0 top 0;
|
||||
background-repeat: no-repeat;
|
||||
/* scale the image whatever the dppx */
|
||||
background-size: 35px 35px;
|
||||
border: @metro_border_xthick@ solid @selected_color@;
|
||||
}
|
||||
|
||||
richgriditem[crosssliding] {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ease the return to original position when cross-sliding */
|
||||
richgriditem:not([crosssliding]) {
|
||||
transition: transform ease-out 0.2s;
|
||||
}
|
||||
|
||||
richgriditem .richgrid-icon-container {
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
richgriditem .richgrid-icon-box {
|
||||
padding: 4px;
|
||||
background: #fff;
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
|
||||
/* tile pinned-state indication */
|
||||
richgriditem[pinned] .richgrid-item-content::before {
|
||||
pointer-events:none;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
right: 0;
|
||||
left: auto;
|
||||
top: 0;
|
||||
background-image: url(chrome://browser/skin/images/pinned-hdpi.png);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
/* scale the image whatever the dppx */
|
||||
background-size: 70px 70px;
|
||||
}
|
||||
|
||||
/* Selected _and_ pinned tiles*/
|
||||
richgriditem[selected][pinned] .richgrid-item-content::before {
|
||||
background-position: right -@metro_border_xthick@ top -@metro_border_xthick@;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
richgriditem[pinned]:-moz-locale-dir(rtl) .richgrid-item-content::before {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
richgriditem[customColor] {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
richgriditem[customImage] {
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
|
||||
richgriditem[customColor] .richgrid-icon-box {
|
||||
opacity: 0.8;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.richgrid-item-content[customImage] {
|
||||
height: 160px;
|
||||
width: 250px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
-moz-box-pack: end;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
/* hide icon if there is an image background */
|
||||
.richgrid-icon-container[customImage] {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
.richgrid-item-desc {
|
||||
width: @tile_width@;
|
||||
font-size: @metro_font_normal@;
|
||||
margin-left: 0px;
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.richgrid-item-content[customImage] > .richgrid-item-desc {
|
||||
background: hsla(0,2%,98%,.95);
|
||||
/*margin-bottom: 0px;
|
||||
margin-right: 0px;*/
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
richgriditem image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
list-style-image: url("chrome://browser/skin/images/identity-icons-generic.png");
|
||||
}
|
||||
|
||||
/* Dialogs ----------------------------------------------------------------- */
|
||||
|
||||
.modal-block,
|
||||
|
274
browser/metro/theme/tiles.css
Normal file
274
browser/metro/theme/tiles.css
Normal file
@ -0,0 +1,274 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Tile grid ------------------------------------------------------------- */
|
||||
|
||||
%filter substitution
|
||||
%include defines.inc
|
||||
|
||||
/*
|
||||
*****************************************************
|
||||
The following rules define the key tile dimensions
|
||||
They are (also) snarfed via the CSSOM as the dimensions used in the #richgrid binding
|
||||
*****************************************************
|
||||
*/
|
||||
richgriditem {
|
||||
width: @grid_double_column_width@;
|
||||
height: @grid_row_height@;
|
||||
}
|
||||
richgriditem[customImage] {
|
||||
width: @grid_double_column_width@;
|
||||
height: @grid_double_row_height@;
|
||||
}
|
||||
richgriditem[compact] {
|
||||
width: auto;
|
||||
height: @compactgrid_row_height@;
|
||||
}
|
||||
|
||||
/*
|
||||
*****************************************************
|
||||
*/
|
||||
|
||||
richgrid {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
richgrid > .richgrid-grid {
|
||||
-moz-column-width: @grid_double_column_width@; /* tile width (2x unit + gutter) */
|
||||
min-width: @grid_double_column_width@; /* min 1 column */
|
||||
min-height: @grid_double_row_height@; /* 2 rows (or 1 double rows) minimum; multiple of tile_height */
|
||||
-moz-column-fill: auto; /* do not attempt to balance content between columns */
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-count: auto;
|
||||
display: block;
|
||||
-moz-box-sizing: content-box;
|
||||
overflow-x: hidden; /* clipping will only kick in if an explicit width is set */
|
||||
transition: 100ms transform ease-out;
|
||||
}
|
||||
|
||||
richgriditem {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: @grid_double_column_width@;
|
||||
height: @grid_row_height@;
|
||||
-moz-box-sizing: border-box;
|
||||
-moz-column-gap: 0;
|
||||
overflow:hidden;
|
||||
cursor: default;
|
||||
transition: 300ms height ease-out,
|
||||
150ms opacity ease-out,
|
||||
100ms transform ease-out;
|
||||
}
|
||||
|
||||
.tile-content {
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
background-origin: padding-box;
|
||||
/* content positioning within the grid "cell"
|
||||
gives us the gutters/spacing between tiles */
|
||||
top: 2px; right: 6px; bottom: 10px; left: 6px;
|
||||
border: @metro_border_thin@ solid @tile_border_color@;
|
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1);
|
||||
transition: 150ms transform ease-out;
|
||||
}
|
||||
|
||||
.tile-start-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 20px;
|
||||
background: hsla(0,2%,98%,.95);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tile-icon-box {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
background: #fff;
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.tile-icon-box > image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
list-style-image: url("chrome://browser/skin/images/identity-icons-generic.png");
|
||||
}
|
||||
|
||||
.tile-desc {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 20px; /* the colored bar in the default tile is the background color peeking through */
|
||||
z-index: 1;
|
||||
padding: 4px 8px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
-moz-margin-start: 0;
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
richgriditem.collapsed {
|
||||
height: 0!important;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
richgriditem.collapsed > .tile-content {
|
||||
transform: scaleY(0);
|
||||
transition: 150ms transform ease-out 150ms;
|
||||
}
|
||||
|
||||
richgriditem:active {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* thumbnail variation */
|
||||
richgriditem[customImage] {
|
||||
width: @grid_double_column_width@;
|
||||
height: @grid_double_row_height@;
|
||||
-moz-box-pack: end;
|
||||
padding: 0px;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
richgriditem[customImage] .tile-desc {
|
||||
background: transparent;
|
||||
margin: 0px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
richgriditem[customImage] > .tile-content > .tile-desc {
|
||||
/* ensure thumbnail labels get their color from the parent richgriditem element */
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* put the image in place of the icon if there is an image background */
|
||||
richgriditem[customImage] > .tile-content > .tile-start-container {
|
||||
background-size: cover;
|
||||
background-position: top left;
|
||||
background-repeat: no-repeat;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 32px; /* TODO: should be some em value? */;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background-color: hsla(0,2%,98%,.95);
|
||||
}
|
||||
richgriditem[customImage] .tile-icon-box {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
/* selected tile indicator */
|
||||
richgriditem[selected] > .tile-content::after {
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background-image: url(chrome://browser/skin/images/tile-selected-check-hdpi.png);
|
||||
background-origin: border-box;
|
||||
background-position: right 0 top 0;
|
||||
background-repeat: no-repeat;
|
||||
/* scale the image whatever the dppx */
|
||||
background-size: 35px 35px;
|
||||
border: @metro_border_xthick@ solid @selected_color@;
|
||||
}
|
||||
|
||||
richgriditem[crosssliding] {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* ease the return to original position when cross-sliding */
|
||||
richgriditem:not([crosssliding]) {
|
||||
transition: transform ease-out 0.2s;
|
||||
}
|
||||
|
||||
|
||||
/* tile pinned-state indication */
|
||||
richgriditem[pinned] > .tile-content::before {
|
||||
pointer-events:none;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: auto;
|
||||
z-index: 1;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
background-image: url(chrome://browser/skin/images/pinned-hdpi.png);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
/* scale the image whatever the dppx */
|
||||
background-size: 70px 70px;
|
||||
}
|
||||
|
||||
/* Selected _and_ pinned tiles*/
|
||||
richgriditem[selected][pinned] > .tile-content::before {
|
||||
background-position: right -@metro_border_xthick@ top -@metro_border_xthick@;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
richgriditem[pinned]:-moz-locale-dir(rtl) > .tile-content::before {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
richgriditem[customColor] {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Snapped-view variation
|
||||
We use the compact, single-column grid treatment for <=320px */
|
||||
|
||||
@media (max-width: 330px) {
|
||||
|
||||
richgrid > .richgrid-grid {
|
||||
-moz-column-width: auto!important; /* let it flow */
|
||||
-moz-column-count: auto!important; /* let it flow */
|
||||
height: auto; /* let it flow */
|
||||
min-width: 280px;
|
||||
transition: 100ms transform ease-out;
|
||||
}
|
||||
|
||||
richgriditem {
|
||||
width: @grid_double_column_width@;
|
||||
overflow: hidden;
|
||||
height: @compactgrid_row_height@;
|
||||
}
|
||||
|
||||
.tile-desc {
|
||||
top: 0;
|
||||
left: 44px; /* label goes to the right of the favicon */
|
||||
right: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tile-start-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 6px;
|
||||
background: #fff;
|
||||
padding: 8px;
|
||||
}
|
||||
.tile-icon-box {
|
||||
padding: 2px;
|
||||
background: #fff;
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user