Bug 625465 - Items in list view should launch description view with a single click on an add-on entry. r=dtownsend, a=beltzner

This commit is contained in:
Blair McBride 2011-02-07 16:53:53 +13:00
Родитель 2cc45c59f5
Коммит 1f572a8a6b
14 изменённых файлов: 278 добавлений и 38 удалений

Просмотреть файл

@ -54,6 +54,10 @@ xhtml|link {
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#sorters");
}
.list {
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addons-richlistbox");
}
.addon[status="installed"] {
-moz-box-orient: vertical;
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-generic");

Просмотреть файл

@ -521,6 +521,61 @@
</binding>
<binding id="addons-richlistbox" extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
<implementation>
<field name="mLastMoveTime">null</field>
<field name="mLastMousePos">null</field>
</implementation>
<handlers>
<!-- Automatically select the item under the mouse cursor -->
<handler event="mousemove"><![CDATA[
// Save latest mouse position incase we want to scroll and keep the item
// under the mouse selected (the scroll event doesn't include this information).
this.mLastMousePos = { x: event.clientX,
y: event.clientY };
// Updating every mousemove can kill performance, so make sure
// at least 30ms has passed.
var now = Date.now();
if ((!this.mLastMoveTime) || (now - this.mLastMoveTime > 30)) {
var item = event.target;
while (item && item.localName != "richlistitem")
item = item == this ? null : item.parentNode;
if (!item)
return;
var index = this.getIndexOfItem(item);
if (index != this.selectedIndex)
this.selectedIndex = index;
this.mLastMoveTime = now;
}
]]></handler>
<!-- Automatically select the item under the mouse cursor when scrolling -->
<handler event="scroll"><![CDATA[
if (!this.mLastMousePos)
return;
var item = document.elementFromPoint(this.mLastMousePos.x,
this.mLastMousePos.y);
while (item && item.localName != "richlistitem")
item = item == this ? null : item.parentNode;
if (!item)
return;
var index = this.getIndexOfItem(item);
if (index != this.selectedIndex)
this.selectedIndex = index;
]]></handler>
</handlers>
</binding>
<!-- Install status - Displays the status of an install/upgrade. -->
<binding id="install-status">
<content>
@ -845,8 +900,7 @@
<xul:vbox flex="1">
<xul:hbox align="center" class="description-container">
<xul:label flex="1" anonid="description" class="description" crop="end"/>
<xul:button anonid="details-btn" class="details button-link"
label="&addon.details.label;"
<xul:button anonid="details-btn" class="details"
tooltiptext="&addon.details.tooltip;"
oncommand="document.getBindingParent(this).showInDetailView();"/>
<xul:spacer flex="5000"/> <!-- Necessary to make the description crop -->
@ -1627,20 +1681,33 @@
<handlers>
<handler event="click" button="0"><![CDATA[
switch (event.detail) {
case 1:
// Prevent double-click where the UI changes on the first click
this._lastClickTarget = event.originalTarget;
break;
case 2:
if (event.originalTarget.localName != 'button' &&
!event.originalTarget.classList.contains('text-link') &&
event.originalTarget == this._lastClickTarget) {
this.showInDetailView();
}
break;
if (event.originalTarget.localName != "button" &&
event.originalTarget.localName != "checkbox" &&
!event.originalTarget.classList.contains("text-link")) {
this.showInDetailView();
}
]]></handler>
<!-- CSS's :active pseudo-class will match if a button is clicked,
so we need to track it manually via mousedown. -->
<handler event="mousedown" button="0"><![CDATA[
if (event.originalTarget.localName != "button" &&
event.originalTarget.localName != "checkbox" &&
!event.originalTarget.classList.contains("text-link")) {
this.setAttribute("mousedown", true);
}
]]></handler>
<handler event="mouseup" button="0"><![CDATA[
this.removeAttribute("mousedown");
]]></handler>
<handler event="mouseout"><![CDATA[
var el = event.relatedTarget;
while (el != null && el != this.parentNode) {
if (el == this)
return;
el = el.parentNode;
}
this.removeAttribute("mousedown");
]]></handler>
</handlers>
</binding>

Просмотреть файл

@ -70,6 +70,7 @@ _MAIN_TEST_FILES = \
browser_bug608316.js \
browser_bug610764.js \
browser_bug618502.js \
browser_bug625465.js \
browser_details.js \
browser_discovery.js \
browser_dragdrop.js \

Просмотреть файл

@ -3,7 +3,8 @@
*/
/**
* Tests that double-click does not go to detail view if the target is a link or button.
* Tests that clicking does not go to detail view if the target is a link or button,
* but clicking anywhere else does.
*/
function test() {
@ -40,7 +41,7 @@ function is_in_detail(aManager, view) {
is(doc.getElementById("view-port").selectedPanel.id, "detail-view", "Should be on the right view");
}
// Check that double-click does something.
// Check that clicking on the addon item does something.
add_test(function() {
open_manager("addons://list/extension", function(aManager) {
info("Part 1");
@ -48,8 +49,7 @@ add_test(function() {
var addon = get_addon_element(aManager, "test1@tests.mozilla.org");
addon.parentNode.ensureElementIsVisible(addon);
EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 1 }, aManager);
EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 2 }, aManager);
EventUtils.synthesizeMouseAtCenter(addon, { }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
@ -110,7 +110,6 @@ add_test(function() {
var rect = target.getBoundingClientRect();
var addonRect = addon.getBoundingClientRect();
EventUtils.synthesizeMouse(target, rect.width / 2, rect.height / 2, { clickCount: 1 }, aManager);
EventUtils.synthesizeMouse(addon,
rect.left - addonRect.left + rect.width / 2,
rect.top - addonRect.top + rect.height / 2,

Просмотреть файл

@ -128,7 +128,7 @@ add_test(function() {
}, false);
info("Opening context menu on enabled extension item");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
@ -147,7 +147,7 @@ add_test(function() {
}, false);
info("Opening context menu on newly disabled extension item");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
@ -166,7 +166,7 @@ add_test(function() {
}, false);
info("Opening context menu on newly enabled extension item");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
@ -183,7 +183,7 @@ add_test(function() {
}, false);
info("Opening context menu on disabled extension item");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
@ -203,7 +203,7 @@ add_test(function() {
}, false);
info("Opening context menu on enabled theme item");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
});
@ -222,7 +222,7 @@ add_test(function() {
}, false);
info("Opening context menu on disabled theme item");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
@ -242,7 +242,7 @@ add_test(function() {
info("Opening context menu on enabled extension, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
});
@ -263,7 +263,7 @@ add_test(function() {
info("Opening context menu on disabled extension, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
});
@ -284,7 +284,7 @@ add_test(function() {
info("Opening context menu on enabled theme, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
});
@ -305,7 +305,7 @@ add_test(function() {
info("Opening context menu on disabled theme, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
});
@ -325,7 +325,7 @@ add_test(function() {
info("Opening context menu with single menu item on enabled theme, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
});
@ -359,7 +359,7 @@ add_test(function() {
}, false);
info("Opening context menu on remote extension item");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
@ -389,7 +389,7 @@ add_test(function() {
info("Opening context menu on remote extension, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
el.parentNode.selectedItem = el;
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
});
});

Просмотреть файл

@ -0,0 +1,101 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Bug 625465 - Items in list view should launch description view with a single click on an add-on entry
var gManagerWindow;
var gList;
function test() {
waitForExplicitFinish();
var gProvider = new MockProvider();
for (let i = 1; i <= 30; i++) {
gProvider.createAddons([{
id: "test" + i + "@tests.mozilla.org",
name: "Test add-on " + i,
description: "foo"
}]);
}
open_manager("addons://list/extension", function(aManager) {
gManagerWindow = aManager;
gList = gManagerWindow.document.getElementById("addon-list");
run_next_test();
});
}
function end_test() {
close_manager(gManagerWindow, finish);
}
function get_item_content_pos(aItem) {
return {
x: (aItem.boxObject.x - gList.boxObject.x) + 10,
y: (aItem.boxObject.y - gList.boxObject.y - gList._scrollbox.scrollTop) + 10
};
}
function get_item_index(aItem) {
for (let i = 0; i < gList.childElementCount; i++) {
if (gList.childNodes[i] == aItem)
return i;
}
return -1;
}
function mouseover_item(aItemNum, aCallback) {
var item = get_addon_element(gManagerWindow, "test" + aItemNum + "@tests.mozilla.org");
var itemIndex = get_item_index(item);
gList.ensureElementIsVisible(item);
if (aItemNum == 1)
is(gList.selectedIndex, -1, "Should not initially have an item selected");
info("Moving mouse over item: " + item.value);
var pos = get_item_content_pos(item);
EventUtils.synthesizeMouse(gList, pos.x, pos.y, {type: "mousemove"}, gManagerWindow);
executeSoon(function() {
is(gManagerWindow.gViewController.currentViewId, "addons://list/extension", "Should still be in list view");
is(gList.selectedIndex, itemIndex, "Correct item should be selected");
aCallback();
});
}
add_test(function() {
var i = 1;
function test_next_item() {
if (i < 10) {
mouseover_item(i, function() {
i++
test_next_item();
});
} else {
run_next_test();
}
}
test_next_item();
});
add_test(function() {
gList.selectedIndex = 1;
var item = gList.selectedItem;
gList.ensureElementIsVisible(item);
var pos = get_item_content_pos(item);
EventUtils.synthesizeMouse(gList, pos.x, pos.y, {type: "mousemove"}, gManagerWindow);
executeSoon(function() {
var scrollDelta = item.boxObject.height * 2;
info("Scrolling by " + scrollDelta + "px (2 items)");
EventUtils.synthesizeMouseScroll(gList, pos.x, pos.y, {type: "MozMousePixelScroll", delta: scrollDelta}, gManagerWindow);
setTimeout(function() {
var item = gList.selectedItem;
var itemIndex = get_item_index(item);
is(itemIndex, 3, "Correct item should be selected");
run_next_test();
}, 100);
});
});

Просмотреть файл

@ -594,7 +594,8 @@ add_test(function() {
getService(Ci.nsIFocusManager);
let addon = items["Test add-on 6"];
EventUtils.synthesizeMouseAtCenter(addon, { }, gManagerWindow);
addon.parentNode.selectedItem = addon;
addon.focus();
is(fm.focusedElement, addon.parentNode, "Focus should have moved to the list");
EventUtils.synthesizeKey("VK_TAB", { }, gManagerWindow);

Просмотреть файл

@ -414,9 +414,15 @@
}
.details {
-moz-appearance: none;
border: none;
cursor: pointer;
padding: 0;
margin: 0;
-moz-margin-start: 10px;
background: transparent;
min-width: 13px;
-moz-margin-start: 6px;
list-style-image: url("moz-icon://stock/gtk-go-forward?size=16");
}
.icon-container {

Двоичные данные
toolkit/themes/pinstripe/mozapps/extensions/detail-btn.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Просмотреть файл

@ -440,9 +440,24 @@
}
.details {
-moz-appearance: none;
border: none;
cursor: pointer;
padding: 3px 0 0 0;
margin: 0;
-moz-margin-start: 10px;
background: transparent;
min-width: 13px;
-moz-margin-start: 6px;
list-style-image: url("chrome://mozapps/skin/extensions/detail-btn.png");
-moz-image-region: rect(0px, 13px, 13px, 0px);
}
.details:hover {
-moz-image-region: rect(0px, 26px, 13px, 13px);
}
.details:active {
-moz-image-region: rect(0px, 39px, 13px, 26px);
}
.icon-container {
@ -627,7 +642,7 @@
}
.addon[selected] {
background-color: rgba(105, 125, 149, 0.39);
background-color: rgba(255, 255, 255, 0.45);
color: black;
}
@ -639,6 +654,11 @@
color: #3F3F3F;
}
.addon[mousedown] {
background-color: rgba(255, 255, 255, 0.3);
box-shadow: inset 1px 1px 4px rgba(0, 0, 0, 0.15);
}
/*** search view ***/

Просмотреть файл

@ -35,6 +35,7 @@ toolkit.jar:
skin/classic/mozapps/extensions/alerticon-info-positive.png (extensions/alerticon-info-positive.png)
skin/classic/mozapps/extensions/alerticon-info-negative.png (extensions/alerticon-info-negative.png)
skin/classic/mozapps/extensions/background-texture.png (extensions/background-texture.png)
skin/classic/mozapps/extensions/detail-btn.png (extensions/detail-btn.png)
skin/classic/mozapps/extensions/about.css (extensions/about.css)
* skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
skin/classic/mozapps/extensions/extensions.svg (extensions/extensions.svg)

Двоичные данные
toolkit/themes/winstripe/mozapps/extensions/detail-btn.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Просмотреть файл

@ -513,9 +513,28 @@
}
.details {
-moz-appearance: none;
border: none;
cursor: pointer;
padding: 3px 0 0 0;
margin: 0;
-moz-margin-start: 10px;
background: transparent;
min-width: 13px;
-moz-margin-start: 6px;
list-style-image: url("chrome://mozapps/skin/extensions/detail-btn.png");
-moz-image-region: rect(0px, 13px, 13px, 0px);
}
.details:hover {
-moz-image-region: rect(0px, 26px, 13px, 13px);
}
.details:active {
-moz-image-region: rect(0px, 39px, 13px, 26px);
}
.details:-moz-focusring > .button-box {
border-color: transparent;
}
.icon-container {
@ -679,7 +698,7 @@
}
.addon[selected] {
background-color: rgba(148, 172, 204, 0.39);
background-color: rgba(255, 255, 255, 0.65);
color: black;
}
@ -691,6 +710,25 @@
color: #3F3F3F;
}
.addon[mousedown] {
background-color: rgba(255, 255, 255, 0.5);
box-shadow: inset 1px 1px 4px rgba(0, 0, 0, 0.20);
border-top-width: 1px;
border-bottom-width: 0px;
padding-top: 6px;
padding-bottom: 6px;
}
.view-pane:not(#search-view) .addon[mousedown]:first-of-type,
#search-view .addon[first][mousedown] {
border-top-width: 0px;
}
.view-pane:not(#search-view) .addon[mousedown]:last-of-type,
#search-view .addon[last][mousedown] {
border-bottom-width: 1px;
}
/*** item - uninstalled ***/

Просмотреть файл

@ -42,6 +42,7 @@ toolkit.jar:
skin/classic/mozapps/extensions/alerticon-info-positive.png (extensions/alerticon-info-positive.png)
skin/classic/mozapps/extensions/alerticon-info-negative.png (extensions/alerticon-info-negative.png)
skin/classic/mozapps/extensions/background-texture.png (extensions/background-texture.png)
skin/classic/mozapps/extensions/detail-btn.png (extensions/detail-btn.png)
skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/mozapps/handling/handling.css (handling/handling.css)
skin/classic/mozapps/passwordmgr/key.png (passwordmgr/key.png)
@ -117,6 +118,7 @@ toolkit.jar:
skin/classic/aero/mozapps/extensions/alerticon-info-positive.png (extensions/alerticon-info-positive.png)
skin/classic/aero/mozapps/extensions/alerticon-info-negative.png (extensions/alerticon-info-negative.png)
skin/classic/aero/mozapps/extensions/background-texture.png (extensions/background-texture.png)
skin/classic/aero/mozapps/extensions/detail-btn.png (extensions/detail-btn.png)
skin/classic/aero/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/aero/mozapps/handling/handling.css (handling/handling.css)
skin/classic/aero/mozapps/passwordmgr/key.png (passwordmgr/key-aero.png)