Bug 731972 - GCLI selection field should be clear when more options exist, and allow you to get them; r=dcamp

This commit is contained in:
Joe Walker 2012-07-05 21:47:45 +01:00
parent f31175531c
commit 106f3add54
5 changed files with 70 additions and 25 deletions

View File

@ -606,7 +606,6 @@ exports.lookupFormat = function(key, swaps) {
*/
define('gcli/types', ['require', 'exports', 'module' , 'gcli/argument'], function(require, exports, module) {
var types = exports;
var Argument = require('gcli/argument').Argument;
@ -669,7 +668,9 @@ var Status = {
return combined;
}
};
types.Status = Status;
exports.Status = Status;
/**
* The type.parse() method converts an Argument into a value, Conversion is
@ -718,8 +719,6 @@ function Conversion(value, arg, status, message, predictions) {
this.predictions = predictions;
}
types.Conversion = Conversion;
/**
* Ensure that all arguments that are part of this conversion know what they
* are assigned to.
@ -855,6 +854,15 @@ Conversion.prototype.constrainPredictionIndex = function(index) {
return index;
};
/**
* Constant to allow everyone to agree on the maximum number of predictions
* that should be provided. We actually display 1 less than this number.
*/
Conversion.maxPredictions = 11;
exports.Conversion = Conversion;
/**
* ArrayConversion is a special Conversion, needed because arrays are converted
* member by member rather then as a whole, which means we can track the
@ -929,7 +937,7 @@ ArrayConversion.prototype.toString = function() {
}, this).join(', ') + ' ]';
};
types.ArrayConversion = ArrayConversion;
exports.ArrayConversion = ArrayConversion;
/**
@ -1013,7 +1021,7 @@ Type.prototype.getType = function() {
return this;
};
types.Type = Type;
exports.Type = Type;
/**
* Private registry of types
@ -1021,7 +1029,7 @@ types.Type = Type;
*/
var registeredTypes = {};
types.getTypeNames = function() {
exports.getTypeNames = function() {
return Object.keys(registeredTypes);
};
@ -1034,7 +1042,7 @@ types.getTypeNames = function() {
* #getType() is called with a 'name' that matches Type.prototype.name we will
* pass the typeSpec into this constructor.
*/
types.registerType = function(type) {
exports.registerType = function(type) {
if (typeof type === 'object') {
if (type instanceof Type) {
if (!type.name) {
@ -1057,7 +1065,7 @@ types.registerType = function(type) {
}
};
types.registerTypes = function registerTypes(newTypes) {
exports.registerTypes = function registerTypes(newTypes) {
Object.keys(newTypes).forEach(function(name) {
var type = newTypes[name];
type.name = name;
@ -1068,14 +1076,14 @@ types.registerTypes = function registerTypes(newTypes) {
/**
* Remove a type from the list available to the system
*/
types.deregisterType = function(type) {
exports.deregisterType = function(type) {
delete registeredTypes[type.name];
};
/**
* Find a type, previously registered using #registerType()
*/
types.getType = function(typeSpec) {
exports.getType = function(typeSpec) {
var type;
if (typeof typeSpec === 'string') {
type = registeredTypes[typeSpec];
@ -1685,8 +1693,6 @@ function SelectionType(typeSpec) {
SelectionType.prototype = Object.create(Type.prototype);
SelectionType.prototype.maxPredictions = 10;
SelectionType.prototype.stringify = function(value) {
if (value == null) {
return '';
@ -1767,11 +1773,12 @@ SelectionType.prototype._findPredictions = function(arg) {
var predictions = [];
var lookup = this.getLookup();
var i, option;
var maxPredictions = Conversion.maxPredictions;
// If the arg has a suffix then we're kind of 'done'. Only an exact match
// will do.
if (arg.suffix.length > 0) {
for (i = 0; i < lookup.length && predictions.length < this.maxPredictions; i++) {
for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
option = lookup[i];
if (option.name === arg.text) {
this._addToPredictions(predictions, option, arg);
@ -1782,7 +1789,7 @@ SelectionType.prototype._findPredictions = function(arg) {
}
// Start with prefix matching
for (i = 0; i < lookup.length && predictions.length < this.maxPredictions; i++) {
for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
option = lookup[i];
if (option.name.indexOf(arg.text) === 0) {
this._addToPredictions(predictions, option, arg);
@ -1790,8 +1797,8 @@ SelectionType.prototype._findPredictions = function(arg) {
}
// Try infix matching if we get less half max matched
if (predictions.length < (this.maxPredictions / 2)) {
for (i = 0; i < lookup.length && predictions.length < this.maxPredictions; i++) {
if (predictions.length < (maxPredictions / 2)) {
for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
option = lookup[i];
if (option.name.indexOf(arg.text) !== -1) {
if (predictions.indexOf(option) === -1) {
@ -7863,12 +7870,14 @@ JavascriptField.DEFAULT_VALUE = '__JavascriptField.DEFAULT_VALUE';
* limitations under the License.
*/
define('gcli/ui/fields/menu', ['require', 'exports', 'module' , 'gcli/util', 'gcli/argument', 'gcli/canon', 'gcli/ui/domtemplate', 'text!gcli/ui/fields/menu.css', 'text!gcli/ui/fields/menu.html'], function(require, exports, module) {
define('gcli/ui/fields/menu', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10n', 'gcli/argument', 'gcli/types', 'gcli/canon', 'gcli/ui/domtemplate', 'text!gcli/ui/fields/menu.css', 'text!gcli/ui/fields/menu.html'], function(require, exports, module) {
var util = require('gcli/util');
var l10n = require('gcli/l10n');
var Argument = require('gcli/argument').Argument;
var Conversion = require('gcli/types').Conversion;
var canon = require('gcli/canon');
var domtemplate = require('gcli/ui/domtemplate');
@ -7918,6 +7927,11 @@ function Menu(options) {
this.onItemClick = util.createEvent('Menu.onItemClick');
}
/**
* Allow the template engine to get at localization strings
*/
Menu.prototype.l10n = l10n.propertyLookup;
/**
* Avoid memory leaks
*/
@ -7963,6 +7977,11 @@ Menu.prototype.show = function(items, match) {
return;
}
if (this.items.length >= Conversion.maxPredictions) {
this.items.splice(-1);
this.items.hasMore = true;
}
var options = this.template.cloneNode(true);
domtemplate.template(options, this, this.templateOptions);
@ -8063,13 +8082,16 @@ exports.Menu = Menu;
define("text!gcli/ui/fields/menu.css", [], "");
define("text!gcli/ui/fields/menu.html", [], "\n" +
"<table class=\"gcli-menu-template\" aria-live=\"polite\">\n" +
" <tr class=\"gcli-menu-option\" foreach=\"item in ${items}\"\n" +
" onclick=\"${onItemClickInternal}\" title=\"${item.manual}\">\n" +
" <td class=\"gcli-menu-name\">${item.name}</td>\n" +
" <td class=\"gcli-menu-desc\">${item.description}</td>\n" +
" </tr>\n" +
"</table>\n" +
"<div>\n" +
" <table class=\"gcli-menu-template\" aria-live=\"polite\">\n" +
" <tr class=\"gcli-menu-option\" foreach=\"item in ${items}\"\n" +
" onclick=\"${onItemClickInternal}\" title=\"${item.manual}\">\n" +
" <td class=\"gcli-menu-name\">${item.name}</td>\n" +
" <td class=\"gcli-menu-desc\">${item.description}</td>\n" +
" </tr>\n" +
" </table>\n" +
" <div class=\"gcli-menu-more\" if=\"${items.hasMore}\">${l10n.fieldMenuMore}</div>\n" +
"</div>\n" +
"");
/*

View File

@ -47,6 +47,11 @@ fieldArrayAdd=Add
# used to remove arguments.
fieldArrayDel=Delete
# LOCALIZATION NOTE (fieldMenuMore): When the menu has displayed all the
# matches that it should (i.e. about 10 items) then we display this to alert
# the user that more matches are available.
fieldMenuMore=More matches, keep typing
# LOCALIZATION NOTE (jstypeParseScope): The command line provides completion
# for JavaScript commands, however there are times when the scope of what
# we're completing against can't be used. This error message is displayed when

View File

@ -123,3 +123,9 @@
.gcli-menu-typed {
color: hsl(25,78%,50%);
}
.gcli-menu-more {
font-size: 80%;
text-align: right;
-moz-padding-end: 8px;
}

View File

@ -125,3 +125,9 @@
.gcli-menu-typed {
color: hsl(25,78%,50%);
}
.gcli-menu-more {
font-size: 80%;
text-align: right;
-moz-padding-end: 8px;
}

View File

@ -123,3 +123,9 @@
.gcli-menu-typed {
color: hsl(25,78%,50%);
}
.gcli-menu-more {
font-size: 80%;
text-align: right;
-moz-padding-end: 8px;
}