Bug 897113 - add noContext/contextmenu toggle for richgrid, disable selection for snapped view and awesomebar results. New selectNone richgrid method. r=rsilveira

This commit is contained in:
Sam Foster 2013-10-22 16:32:42 -07:00
parent 36abe7a6d4
commit 728f420e0b
6 changed files with 138 additions and 43 deletions

View File

@ -91,6 +91,18 @@
</body>
</method>
<method name="selectNone">
<body>
<![CDATA[
let selectedCount = this.selectedItems.length;
this.clearSelection();
if (selectedCount && "single" != this.getAttribute("seltype")) {
this._fireEvent("selectionchange");
}
]]>
</body>
</method>
<method name="handleItemClick">
<parameter name="aItem"/>
<parameter name="aEvent"/>
@ -116,7 +128,7 @@
<parameter name="aEvent"/>
<body>
<![CDATA[
if (!this.isBound || this.suppressOnSelect)
if (!this.isBound || this.noContext)
return;
// we'll republish this as a selectionchange event on the grid
aEvent.stopPropagation();
@ -192,7 +204,7 @@
let selected = this.getItemAtIndex(val);
this.selectItem(selected);
} else {
this.clearSelection();
this.selectNone();
}
]]>
</setter>
@ -610,6 +622,9 @@
<property name="suppressOnSelect"
onget="return this.getAttribute('suppressonselect') == 'true';"
onset="this.setAttribute('suppressonselect', val);"/>
<property name="noContext"
onget="return this.hasAttribute('nocontext');"
onset="if (val) this.setAttribute('nocontext', true); else this.removeAttribute('nocontext');"/>
<property name="crossSlideBoundary"
onget="return this.hasAttribute('crossslideboundary')? this.getAttribute('crossslideboundary') : Infinity;"/>
@ -627,7 +642,7 @@
this.controller.gridBoundCallback();
// set up cross-slide gesture handling for multiple-selection grids
if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) {
if ("undefined" !== typeof CrossSlide && !this.noContext) {
this._xslideHandler = new CrossSlide.Handler(this, {
REARRANGESTART: this.crossSlideBoundary
});
@ -836,7 +851,7 @@
// which directs us to do something to the selected tiles
switch (event.action) {
case "clear":
this.clearSelection();
this.selectNone();
break;
default:
if (this.controller && this.controller.doActionOnSelectedTiles) {
@ -896,7 +911,7 @@
</handler>
<handler event="MozCrossSlideSelect">
<![CDATA[
if (this.suppressOnSelect)
if (this.noContext)
return;
this.toggleItemSelection(event.target);
]]>

View File

@ -505,13 +505,13 @@
<xul:label class="meta-section-title"
value="&autocompleteResultsHeader.label;"/>
<richgrid anonid="results" rows="3" flex="1"
seltype="single" deferlayout="true"/>
seltype="single" nocontext="true" deferlayout="true"/>
</xul:vbox>
<xul:vbox class="meta-section" flex="1">
<xul:label anonid="searches-header" class="meta-section-title"/>
<richgrid anonid="searches" rows="3" flex="1"
seltype="single" deferlayout="true"/>
seltype="single" nocontext="true" deferlayout="true"/>
</xul:vbox>
</content>
@ -519,7 +519,6 @@
<constructor>
<![CDATA[
this.hidden = true;
Services.obs.addObserver(this, "browser-search-engine-modified", false);
this._results.controller = this;

View File

@ -114,15 +114,54 @@ gTests.push({
yield restoreViewstate();
}
});
gTests.push({
desc: "Test tile selection is cleared and disabled",
setUp: function() {
BookmarksTestHelper.setup();
HistoryTestHelper.setup();
showStartUI();
},
run: function() {
// minimal event mocking to trigger context-click handlers
function makeMockEvent(item) {
return {
stopPropagation: function() {},
target: item
};
}
let startWin = Browser.selectedBrowser.contentWindow;
// make sure the bookmarks grid is showing
startWin.StartUI.onNarrowTitleClick("start-bookmarks");
let bookmarksGrid = startWin.document.querySelector("#start-bookmarks-grid");
// sanity check
ok(bookmarksGrid, "matched bookmarks grid");
ok(bookmarksGrid.children[0], "bookmarks grid has items");
// select a tile (balancing implementation leakage with test simplicity)
let mockEvent = makeMockEvent(bookmarksGrid.children[0]);
bookmarksGrid.handleItemContextMenu(bookmarksGrid.children[0], mockEvent);
// check tile was selected
is(bookmarksGrid.selectedItems.length, 1, "Tile got selected in landscape view");
// switch to snapped view
yield setSnappedViewstate();
is(bookmarksGrid.selectedItems.length, 0, "grid items selection cleared in snapped view");
// attempt to select a tile in snapped view
mockEvent = makeMockEvent(bookmarksGrid.children[0]);
bookmarksGrid.handleItemContextMenu(bookmarksGrid.children[0], mockEvent);
is(bookmarksGrid.selectedItems.length, 0, "no grid item selections possible in snapped view");
},
tearDown: function() {
BookmarksTestHelper.restore();
HistoryTestHelper.restore();
yield restoreViewstate();
}
});
gTests.push({
desc: "Navbar contextual buttons are not shown in snapped",
setUp: setUpSnapped,
run: function() {
let toolbarContextual = document.getElementById("toolbar-contextual");
let visibility = getComputedStyle(toolbarContextual).getPropertyValue("visibility");
ok(visibility === "collapse" || visibility === "hidden", "Contextual buttons not shown in navbar");
},
tearDown: restoreViewstate

View File

@ -8,13 +8,10 @@
<?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 id="alayout">
<richgrid id="grid_layout" seltype="single" flex="1">
<richgrid id="grid_layout" seltype="single" nocontext="true" flex="1">
</richgrid>
</vbox>
<vbox>
@ -22,7 +19,7 @@
</vbox>
<vbox style="height:600px">
<hbox>
<richgrid id="clearGrid" seltype="single" flex="1" rows="2">
<richgrid id="clearGrid" seltype="single" nocontext="true" 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"/>

View File

@ -355,13 +355,6 @@ gTests.push({
ok(grid.items[1].getAttribute("selected"), "Item selected attribute is truthy after grid.selectItem");
ok(grid.selectedItems.length, "There are selectedItems after grid.selectItem");
// clearSelection
grid.selectItem(grid.items[0]);
grid.selectItem(grid.items[1]);
grid.clearSelection();
is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection");
// select events
// in seltype=single mode, select is like the default action for the tile
// (think <a>, not <select multiple>)
@ -369,9 +362,18 @@ gTests.push({
handleEvent: function(aEvent) {}
};
let handlerStub = stubMethod(handler, "handleEvent");
grid.items[1].selected = true;
doc.defaultView.addEventListener("select", handler, false);
info("select listener added");
// clearSelection
grid.clearSelection();
is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection");
is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event");
info("calling selectItem, currently it is:" + grid.items[0].selected);
// Note: A richgrid in seltype=single mode fires "select" events from selectItem
grid.selectItem(grid.items[0]);
@ -412,14 +414,6 @@ gTests.push({
grid.toggleItemSelection(grid.items[1]);
is(grid.selectedItems.length, 0, "Nothing selected when we toggleItemSelection again");
// clearSelection
grid.items[0].selected=true;
grid.items[1].selected=true;
is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection");
grid.clearSelection();
is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we clearSelection");
// selectionchange events
// in seltype=multiple mode, we track selected state on all items
// (think <select multiple> not <a>)
@ -430,6 +424,15 @@ gTests.push({
doc.defaultView.addEventListener("selectionchange", handler, false);
info("selectionchange listener added");
// clearSelection
grid.items[0].selected=true;
grid.items[1].selected=true;
is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection");
grid.clearSelection();
is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we clearSelection");
is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event");
info("calling toggleItemSelection, currently it is:" + grid.items[0].selected);
// Note: A richgrid in seltype=single mode fires "select" events from selectItem
grid.toggleItemSelection(grid.items[0]);
@ -444,6 +447,39 @@ gTests.push({
}
});
gTests.push({
desc: "selectNone",
run: function() {
let grid = doc.querySelector("#grid-select2");
is(typeof grid.selectNone, "function", "selectNone is a function on the grid");
is(grid.itemCount, 2, "2 items initially");
// selectNone should fire a selectionchange event
let handler = {
handleEvent: function(aEvent) {}
};
let handlerStub = stubMethod(handler, "handleEvent");
doc.defaultView.addEventListener("selectionchange", handler, false);
info("selectionchange listener added");
grid.items[0].selected=true;
grid.items[1].selected=true;
is(grid.selectedItems.length, 2, "Both items are selected before calling selectNone");
grid.selectNone();
is(grid.selectedItems.length, 0, "Nothing selected when we selectNone");
ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we selectNone");
is(handlerStub.callCount, 1, "selectionchange event handler was called when we selectNone");
is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
is(handlerStub.calledWith[0].target, grid, "selectionchange event had the originating grid as the target");
handlerStub.restore();
doc.defaultView.removeEventListener("selectionchange", handler, false);
}
});
function gridSlotsSetup() {
let grid = this.grid = doc.createElement("richgrid");
grid.setAttribute("minSlots", 6);

View File

@ -41,19 +41,28 @@ View.prototype = {
},
_adjustDOMforViewState: function _adjustDOMforViewState(aState) {
if (this._set) {
if (undefined == aState)
aState = this._set.getAttribute("viewstate");
this._set.setAttribute("suppressonselect", (aState == "snapped"));
if (aState == "portrait") {
this._set.setAttribute("vertical", true);
} else {
this._set.removeAttribute("vertical");
}
this._set.arrangeItems();
let grid = this._set;
if (!grid) {
return;
}
if (!aState) {
aState = grid.getAttribute("viewstate");
}
switch (aState) {
case "snapped":
grid.setAttribute("nocontext", true);
grid.selectNone();
break;
case "portrait":
grid.removeAttribute("nocontext");
grid.setAttribute("vertical", true);
break;
default:
grid.removeAttribute("nocontext");
grid.removeAttribute("vertical");
}
if ("arrangeItems" in grid) {
grid.arrangeItems();
}
},