@qooxdoo/framework
Version:
The JS Framework for Coders
471 lines (388 loc) • 12.3 kB
JavaScript
/* ************************************************************************
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:
* Martin Wittemann (martinwittemann)
* Sebastian Werner (wpbasti)
* Jonathan Weiß (jonathan_rass)
* Christian Hagendorn (chris_schmidt)
************************************************************************ */
/**
* A form widget which allows a single selection. Looks somewhat like
* a normal button, but opens a list of items to select when tapping on it.
*
* Keep in mind that the SelectBox widget has always a selected item (due to the
* single selection mode). Right after adding the first item a <code>changeSelection</code>
* event is fired.
*
* <pre class='javascript'>
* var selectBox = new qx.ui.form.SelectBox();
*
* selectBox.addListener("changeSelection", function(e) {
* // ...
* });
*
* // now the 'changeSelection' event is fired
* selectBox.add(new qx.ui.form.ListItem("Item 1"));
* </pre>
*
* @childControl spacer {qx.ui.core.Spacer} flexible spacer widget
* @childControl atom {qx.ui.basic.Atom} shows the text and icon of the content
* @childControl arrow {qx.ui.basic.Image} shows the arrow to open the popup
*/
qx.Class.define("qx.ui.form.SelectBox",
{
extend : qx.ui.form.AbstractSelectBox,
implement : [
qx.ui.core.ISingleSelection,
qx.ui.form.IModelSelection,
qx.ui.form.IField
],
include : [qx.ui.core.MSingleSelectionHandling, qx.ui.form.MModelSelection],
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
construct : function()
{
this.base(arguments);
this._createChildControl("atom");
this._createChildControl("spacer");
this._createChildControl("arrow");
// Register listener
this.addListener("pointerover", this._onPointerOver, this);
this.addListener("pointerout", this._onPointerOut, this);
this.addListener("tap", this._onTap, this);
this.addListener("keyinput", this._onKeyInput, this);
this.addListener("changeSelection", this.__onChangeSelection, this);
},
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
properties :
{
// overridden
appearance :
{
refine : true,
init : "selectbox"
},
rich: {
init: false,
check: "Boolean",
apply: "_applyRich"
}
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members :
{
/** @type {qx.ui.form.ListItem} instance */
__preSelectedItem : null,
/*
---------------------------------------------------------------------------
WIDGET API
---------------------------------------------------------------------------
*/
_applyRich: function(value, oldValue) {
this.getChildControl("atom").setRich(value);
},
// overridden
_defaultFormat: function(item) {
if (item) {
if (typeof item.isRich == "function" && item.isRich()) {
this.setRich(true);
}
return item.getLabel();
}
return null;
},
// overridden
_createChildControlImpl : function(id, hash)
{
var control;
switch(id)
{
case "spacer":
control = new qx.ui.core.Spacer();
this._add(control, {flex: 1});
break;
case "atom":
control = new qx.ui.basic.Atom(" ");
control.setCenter(false);
control.setAnonymous(true);
this._add(control, {flex:1});
break;
case "arrow":
control = new qx.ui.basic.Image();
control.setAnonymous(true);
this._add(control);
break;
}
return control || this.base(arguments, id);
},
// overridden
/**
* @lint ignoreReferenceField(_forwardStates)
*/
_forwardStates : {
focused : true
},
/*
---------------------------------------------------------------------------
HELPER METHODS FOR SELECTION API
---------------------------------------------------------------------------
*/
/**
* Returns the list items for the selection.
*
* @return {qx.ui.form.ListItem[]} List items to select.
*/
_getItems : function() {
return this.getChildrenContainer().getChildren();
},
/**
* Returns if the selection could be empty or not.
*
* @return {Boolean} <code>true</code> If selection could be empty,
* <code>false</code> otherwise.
*/
_isAllowEmptySelection: function() {
return this.getChildrenContainer().getSelectionMode() !== "one";
},
/**
* Event handler for <code>changeSelection</code>.
*
* @param e {qx.event.type.Data} Data event.
*/
__onChangeSelection : function(e)
{
var listItem = e.getData()[0];
var list = this.getChildControl("list");
if (list.getSelection()[0] != listItem) {
if(listItem) {
list.setSelection([listItem]);
} else {
list.resetSelection();
}
}
this.__updateIcon();
this.__updateLabel();
},
/**
* Sets the icon inside the list to match the selected ListItem.
*/
__updateIcon : function()
{
var listItem = this.getChildControl("list").getSelection()[0];
var atom = this.getChildControl("atom");
var icon = listItem ? listItem.getIcon() : "";
icon == null ? atom.resetIcon() : atom.setIcon(icon);
},
/**
* Sets the label inside the list to match the selected ListItem.
*/
__updateLabel : function()
{
var listItem = this.getChildControl("list").getSelection()[0];
var atom = this.getChildControl("atom");
var label = listItem ? listItem.getLabel() : "";
var format = this.getFormat();
if (format != null && listItem) {
label = format.call(this, listItem);
}
// check for translation
if (label && label.translate) {
label = label.translate();
}
label == null ? atom.resetLabel() : atom.setLabel(label);
},
/*
---------------------------------------------------------------------------
EVENT LISTENERS
---------------------------------------------------------------------------
*/
/**
* Listener method for "pointerover" event
* <ul>
* <li>Adds state "hovered"</li>
* <li>Removes "abandoned" and adds "pressed" state (if "abandoned" state is set)</li>
* </ul>
*
* @param e {qx.event.type.Pointer} Pointer event
*/
_onPointerOver : function(e)
{
if (!this.isEnabled() || e.getTarget() !== this) {
return;
}
if (this.hasState("abandoned"))
{
this.removeState("abandoned");
this.addState("pressed");
}
this.addState("hovered");
},
/**
* Listener method for "pointerout" event
* <ul>
* <li>Removes "hovered" state</li>
* <li>Adds "abandoned" and removes "pressed" state (if "pressed" state is set)</li>
* </ul>
*
* @param e {qx.event.type.Pointer} Pointer event
*/
_onPointerOut : function(e)
{
if (!this.isEnabled() || e.getTarget() !== this) {
return;
}
this.removeState("hovered");
if (this.hasState("pressed"))
{
this.removeState("pressed");
this.addState("abandoned");
}
},
/**
* Toggles the popup's visibility.
*
* @param e {qx.event.type.Pointer} Pointer event
*/
_onTap : function(e) {
this.toggle();
},
// overridden
_onKeyPress : function(e)
{
var iden = e.getKeyIdentifier();
if(iden == "Enter" || iden == "Space")
{
// Apply pre-selected item (translate quick selection to real selection)
if (this.__preSelectedItem)
{
this.setSelection([this.__preSelectedItem]);
this.__preSelectedItem = null;
}
this.toggle();
}
else
{
this.base(arguments, e);
}
},
/**
* Forwards key event to list widget.
*
* @param e {qx.event.type.KeyInput} Key event
*/
_onKeyInput : function(e)
{
// clone the event and re-calibrate the event
var clone = e.clone();
clone.setTarget(this._list);
clone.setBubbles(false);
// forward it to the list
this.getChildControl("list").dispatchEvent(clone);
},
// overridden
_onListPointerDown : function(e)
{
// Apply pre-selected item (translate quick selection to real selection)
if (this.__preSelectedItem)
{
this.setSelection([this.__preSelectedItem]);
this.__preSelectedItem = null;
}
},
// overridden
_onListChangeSelection : function(e)
{
var current = e.getData();
var old = e.getOldData();
// Remove old listeners for icon and label changes.
if (old && old.length > 0)
{
old[0].removeListener("changeIcon", this.__updateIcon, this);
old[0].removeListener("changeLabel", this.__updateLabel, this);
}
if (current.length > 0)
{
// Ignore quick context (e.g. pointerover)
// and configure the new value when closing the popup afterwards
var popup = this.getChildControl("popup");
var list = this.getChildControl("list");
var context = list.getSelectionContext();
if (popup.isVisible() && (context == "quick" || context == "key"))
{
this.__preSelectedItem = current[0];
}
else
{
this.setSelection([current[0]]);
this.__preSelectedItem = null;
}
// Add listeners for icon and label changes
current[0].addListener("changeIcon", this.__updateIcon, this);
current[0].addListener("changeLabel", this.__updateLabel, this);
}
else
{
this.resetSelection();
}
},
// overridden
_onPopupChangeVisibility : function(e)
{
this.base(arguments, e);
// Synchronize the current selection to the list selection
// when the popup is closed. The list selection may be invalid
// because of the quick selection handling which is not
// directly applied to the selectbox
var popup = this.getChildControl("popup");
if (!popup.isVisible())
{
var list = this.getChildControl("list");
// check if the list has any children before selecting
if (list.hasChildren()) {
list.setSelection(this.getSelection());
}
} else {
// ensure that the list is never bigger that the max list height and
// the available space in the viewport
var distance = popup.getLayoutLocation(this);
var viewPortHeight = qx.bom.Viewport.getHeight();
// distance to the bottom and top borders of the viewport
var toTop = distance.top;
var toBottom = viewPortHeight - distance.bottom;
var availableHeigth = toTop > toBottom ? toTop : toBottom;
var maxListHeight = this.getMaxListHeight();
var list = this.getChildControl("list");
if (maxListHeight == null || maxListHeight > availableHeigth) {
list.setMaxHeight(availableHeigth);
} else if (maxListHeight < availableHeigth) {
list.setMaxHeight(maxListHeight);
}
}
}
},
/*
*****************************************************************************
DESTRUCT
*****************************************************************************
*/
destruct : function() {
this.__preSelectedItem = null;
}
});