UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

477 lines (383 loc) 11.9 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Sebastian Werner (wpbasti) * Andreas Ecker (ecker) * Martin Wittemann (martinwittemann) * Christian Hagendorn (chris_schmidt) ************************************************************************ */ /** * A list of items. Displays an automatically scrolling list for all * added {@link qx.ui.form.ListItem} instances. Supports various * selection options: single, multi, ... */ qx.Class.define("qx.ui.form.List", { extend : qx.ui.core.scroll.AbstractScrollArea, implement : [ qx.ui.core.IMultiSelection, qx.ui.form.IForm, qx.ui.form.IField, qx.ui.form.IModelSelection ], include : [ qx.ui.core.MRemoteChildrenHandling, qx.ui.core.MMultiSelectionHandling, qx.ui.form.MForm, qx.ui.form.MModelSelection ], /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ /** * @param horizontal {Boolean?false} Whether the list should be horizontal. */ construct : function(horizontal) { this.base(arguments); // Create content this.__content = this._createListItemContainer(); // Used to fire item add/remove events this.__content.addListener("addChildWidget", this._onAddChild, this); this.__content.addListener("removeChildWidget", this._onRemoveChild, this); // Add to scrollpane this.getChildControl("pane").add(this.__content); // Apply orientation if (horizontal) { this.setOrientation("horizontal"); } else { this.initOrientation(); } // Add keypress listener this.addListener("keypress", this._onKeyPress); this.addListener("keyinput", this._onKeyInput); // initialize the search string this.__pressedString = ""; }, /* ***************************************************************************** EVENTS ***************************************************************************** */ events : { /** * This event is fired after a list item was added to the list. The * {@link qx.event.type.Data#getData} method of the event returns the * added item. */ addItem : "qx.event.type.Data", /** * This event is fired after a list item has been removed from the list. * The {@link qx.event.type.Data#getData} method of the event returns the * removed item. */ removeItem : "qx.event.type.Data" }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties : { // overridden appearance : { refine : true, init : "list" }, // overridden focusable : { refine : true, init : true }, // overridden width : { refine : true, init : 100 }, // overridden height : { refine : true, init : 200 }, /** * Whether the list should be rendered horizontal or vertical. */ orientation : { check : ["horizontal", "vertical"], init : "vertical", apply : "_applyOrientation" }, /** Spacing between the items */ spacing : { check : "Integer", init : 0, apply : "_applySpacing", themeable : true }, /** Controls whether the inline-find feature is activated or not */ enableInlineFind : { check : "Boolean", init : true } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members : { __pressedString : null, __lastKeyPress : null, /** @type {qx.ui.core.Widget} The children container */ __content : null, /** @type {Class} Pointer to the selection manager to use */ SELECTION_MANAGER : qx.ui.core.selection.ScrollArea, /* --------------------------------------------------------------------------- WIDGET API --------------------------------------------------------------------------- */ // overridden getChildrenContainer : function() { return this.__content; }, /** * Handle child widget adds on the content pane * * @param e {qx.event.type.Data} the event instance */ _onAddChild : function(e) { this.fireDataEvent("addItem", e.getData()); }, /** * Handle child widget removes on the content pane * * @param e {qx.event.type.Data} the event instance */ _onRemoveChild : function(e) { this.fireDataEvent("removeItem", e.getData()); }, /* --------------------------------------------------------------------------- PUBLIC API --------------------------------------------------------------------------- */ /** * Used to route external <code>keypress</code> events to the list * handling (in fact the manager of the list) * * @param e {qx.event.type.KeySequence} KeyPress event */ handleKeyPress : function(e) { if (!this._onKeyPress(e)) { this._getManager().handleKeyPress(e); } }, /* --------------------------------------------------------------------------- PROTECTED API --------------------------------------------------------------------------- */ /** * This container holds the list item widgets. * * @return {qx.ui.container.Composite} Container for the list item widgets */ _createListItemContainer : function() { return new qx.ui.container.Composite; }, /* --------------------------------------------------------------------------- PROPERTY APPLY ROUTINES --------------------------------------------------------------------------- */ // property apply _applyOrientation : function(value, old) { var content = this.__content; // save old layout for disposal var oldLayout = content.getLayout(); // Create new layout var horizontal = value === "horizontal"; var layout = horizontal ? new qx.ui.layout.HBox() : new qx.ui.layout.VBox(); // Configure content content.setLayout(layout); content.setAllowGrowX(!horizontal); content.setAllowGrowY(horizontal); // Configure spacing this._applySpacing(this.getSpacing()); // dispose old layout if(oldLayout) { oldLayout.dispose(); } }, // property apply _applySpacing : function(value, old) { this.__content.getLayout().setSpacing(value); }, /* --------------------------------------------------------------------------- EVENT HANDLER --------------------------------------------------------------------------- */ /** * Event listener for <code>keypress</code> events. * * @param e {qx.event.type.KeySequence} KeyPress event * @return {Boolean} Whether the event was processed */ _onKeyPress : function(e) { // Execute action on press <ENTER> if (e.getKeyIdentifier() == "Enter" && !e.isAltPressed()) { var items = this.getSelection(); for (var i=0; i<items.length; i++) { items[i].fireEvent("action"); } return true; } return false; }, /* --------------------------------------------------------------------------- FIND SUPPORT --------------------------------------------------------------------------- */ /** * Handles the inline find - if enabled * * @param e {qx.event.type.KeyInput} key input event */ _onKeyInput : function(e) { // do nothing if the find is disabled if (!this.getEnableInlineFind()) { return; } // Only useful in single or one selection mode var mode = this.getSelectionMode(); if (!(mode === "single" || mode === "one")) { return; } // Reset string after a second of non pressed key if (((new Date).valueOf() - this.__lastKeyPress) > 1000) { this.__pressedString = ""; } // Combine keys the user pressed to a string this.__pressedString += e.getChar(); // Find matching item var matchedItem = this.findItemByLabelFuzzy(this.__pressedString); // if an item was found, select it if (matchedItem) { this.setSelection([matchedItem]); } // Store timestamp this.__lastKeyPress = (new Date).valueOf(); }, /** * Takes the given string and tries to find a ListItem * which starts with this string. The search is not case sensitive and the * first found ListItem will be returned. If there could not be found any * qualifying list item, null will be returned. * * @param search {String} The text with which the label of the ListItem should start with * @return {qx.ui.form.ListItem} The found ListItem or null */ findItemByLabelFuzzy : function(search) { // lower case search text search = search.toLowerCase(); // get all items of the list var items = this.getChildren(); // go threw all items for (var i=0, l=items.length; i<l; i++) { // get the label of the current item var currentLabel = items[i].getLabel(); // if the label fits with the search text (ignore case, begins with) if (currentLabel && currentLabel.toLowerCase().indexOf(search) == 0) { // just return the first found element return items[i]; } } // if no element was found, return null return null; }, /** * Find an item by its {@link qx.ui.basic.Atom#getLabel}. * * @param search {String} A label or any item * @param ignoreCase {Boolean?true} description * @return {qx.ui.form.ListItem} The found ListItem or null */ findItem : function(search, ignoreCase) { // lowercase search if (ignoreCase !== false) { search = search.toLowerCase(); }; // get all items of the list var items = this.getChildren(); var item; // go through all items for (var i=0, l=items.length; i<l; i++) { item = items[i]; // get the content of the label; text content when rich var label; if (item.isRich()) { var control = item.getChildControl("label", true); if (control) { var labelNode = control.getContentElement().getDomElement(); if (labelNode) { label = qx.bom.element.Attribute.get(labelNode, "text"); } } } else { label = item.getLabel(); } if (label != null) { if (label.translate) { label = label.translate(); } if (ignoreCase !== false) { label = label.toLowerCase(); } if (label.toString() == search.toString()) { return item; } } } return null; } }, /* ***************************************************************************** DESTRUCTOR ***************************************************************************** */ destruct : function() { this._disposeObjects("__content"); } });