@qooxdoo/framework
Version:
The JS Framework for Coders
458 lines (380 loc) • 11.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:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Martin Wittemann (martinwittemann)
* Fabian Jakobs (fjakobs)
************************************************************************ */
/**
* The RepeatButton is a special button, which fires repeatedly {@link #execute}
* events, while a button is pressed on the button. The initial delay
* and the interval time can be set using the properties {@link #firstInterval}
* and {@link #interval}. The {@link #execute} events will be fired in a shorter
* amount of time if a button is hold, until the min {@link #minTimer}
* is reached. The {@link #timerDecrease} property sets the amount of milliseconds
* which will decreased after every firing.
*
* <pre class='javascript'>
* var button = new qx.ui.form.RepeatButton("Hello World");
*
* button.addListener("execute", function(e) {
* alert("Button is executed");
* }, this);
*
* this.getRoot.add(button);
* </pre>
*
* This example creates a button with the label "Hello World" and attaches an
* event listener to the {@link #execute} event.
*
* *External Documentation*
*
* <a href='http://manual.qooxdoo.org/${qxversion}/pages/widget/repeatbutton.html' target='_blank'>
* Documentation of this widget in the qooxdoo manual.</a>
*/
qx.Class.define("qx.ui.form.RepeatButton",
{
extend : qx.ui.form.Button,
/**
* @param label {String} Label to use
* @param icon {String?null} Icon to use
*/
construct : function(label, icon)
{
this.base(arguments, label, icon);
// create the timer and add the listener
this.__timer = new qx.event.AcceleratingTimer();
this.__timer.addListener("interval", this._onInterval, this);
},
events :
{
/**
* This event gets dispatched with every interval. The timer gets executed
* as long as the user holds down a button.
*/
"execute" : "qx.event.type.Event",
/**
* This event gets dispatched when the button is pressed.
*/
"press" : "qx.event.type.Event",
/**
* This event gets dispatched when the button is released.
*/
"release" : "qx.event.type.Event"
},
properties :
{
/**
* Interval used after the first run of the timer. Usually a smaller value
* than the "firstInterval" property value to get a faster reaction.
*/
interval :
{
check : "Integer",
init : 100
},
/**
* Interval used for the first run of the timer. Usually a greater value
* than the "interval" property value to a little delayed reaction at the first
* time.
*/
firstInterval :
{
check : "Integer",
init : 500
},
/** This configures the minimum value for the timer interval. */
minTimer :
{
check : "Integer",
init : 20
},
/** Decrease of the timer on each interval (for the next interval) until minTimer reached. */
timerDecrease :
{
check : "Integer",
init : 2
}
},
members :
{
__executed : null,
__timer : null,
/**
* Calling this function is like a tap from the user on the
* button with all consequences.
* <span style='color: red'>Be sure to call the {@link #release} function.</span>
*
*/
press : function()
{
// only if the button is enabled
if (this.isEnabled())
{
// if the state pressed must be applied (first call)
if (!this.hasState("pressed"))
{
// start the timer
this.__startInternalTimer();
}
// set the states
this.removeState("abandoned");
this.addState("pressed");
}
},
/**
* Calling this function is like a release from the user on the
* button with all consequences.
* Usually the {@link #release} function will be called before the call of
* this function.
*
* @param fireExecuteEvent {Boolean?true} flag which signals, if an event should be fired
*/
release : function(fireExecuteEvent)
{
// only if the button is enabled
if (!this.isEnabled()) {
return;
}
// only if the button is pressed
if (this.hasState("pressed"))
{
// if the button has not been executed
if (!this.__executed) {
this.execute();
}
}
// remove button states
this.removeState("pressed");
this.removeState("abandoned");
// stop the repeat timer and therefore the execution
this.__stopInternalTimer();
},
/*
---------------------------------------------------------------------------
PROPERTY APPLY ROUTINES
---------------------------------------------------------------------------
*/
// overridden
_applyEnabled : function(value, old)
{
this.base(arguments, value, old);
if (!value)
{
if (this.isCapturing()) {
// also release capture because out event is missing on iOS
this.releaseCapture();
}
// remove button states
this.removeState("pressed");
this.removeState("abandoned");
// stop the repeat timer and therefore the execution
this.__stopInternalTimer();
}
},
/*
---------------------------------------------------------------------------
EVENT HANDLER
---------------------------------------------------------------------------
*/
/**
* 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.__timer.start();
}
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");
this.__timer.stop();
}
},
/**
* Callback method for the "pointerdown" method.
*
* Sets the interval of the timer (value of firstInterval property) and
* starts the timer. Additionally removes the state "abandoned" and adds the
* state "pressed".
*
* @param e {qx.event.type.Pointer} pointerdown event
*/
_onPointerDown : function(e)
{
if (!e.isLeftPressed()) {
return;
}
// Activate capturing if the button get a pointerout while
// the button is pressed.
this.capture();
this.__startInternalTimer();
e.stopPropagation();
},
/**
* Callback method for the "pointerup" event.
*
* Handles the case that the user is releasing a button
* before the timer interval method got executed. This way the
* "execute" method get executed at least one time.
*
* @param e {qx.event.type.Pointer} pointerup event
*/
_onPointerUp : function(e)
{
this.releaseCapture();
if (!this.hasState("abandoned"))
{
this.addState("hovered");
if (this.hasState("pressed") && !this.__executed) {
this.execute();
}
}
this.__stopInternalTimer();
e.stopPropagation();
},
// Nothing to do, 'execute' is already fired by _onPointerUp.
_onTap : function(e) {},
/**
* Listener method for "keyup" event.
*
* Removes "abandoned" and "pressed" state (if "pressed" state is set)
* for the keys "Enter" or "Space" and stops the internal timer
* (same like pointer up).
*
* @param e {Event} Key event
*/
_onKeyUp : function(e)
{
switch(e.getKeyIdentifier())
{
case "Enter":
case "Space":
if (this.hasState("pressed"))
{
if (!this.__executed) {
this.execute();
}
this.removeState("pressed");
this.removeState("abandoned");
e.stopPropagation();
this.__stopInternalTimer();
}
}
},
/**
* Listener method for "keydown" event.
*
* Removes "abandoned" and adds "pressed" state
* for the keys "Enter" or "Space". It also starts
* the internal timer (same like pointerdown).
*
* @param e {Event} Key event
*/
_onKeyDown : function(e)
{
switch(e.getKeyIdentifier())
{
case "Enter":
case "Space":
this.removeState("abandoned");
this.addState("pressed");
e.stopPropagation();
this.__startInternalTimer();
}
},
/**
* Callback for the interval event.
*
* Stops the timer and starts it with a new interval
* (value of the "interval" property - value of the "timerDecrease" property).
* Dispatches the "execute" event.
*
* @param e {qx.event.type.Event} interval event
*/
_onInterval : function(e)
{
this.__executed = true;
this.fireEvent("execute");
},
/*
---------------------------------------------------------------------------
INTERNAL TIMER
---------------------------------------------------------------------------
*/
/**
* Starts the internal timer which causes firing of execution
* events in an interval. It also presses the button.
*
*/
__startInternalTimer : function()
{
this.fireEvent("press");
this.__executed = false;
this.__timer.set({
interval: this.getInterval(),
firstInterval: this.getFirstInterval(),
minimum: this.getMinTimer(),
decrease: this.getTimerDecrease()
}).start();
this.removeState("abandoned");
this.addState("pressed");
},
/**
* Stops the internal timer and releases the button.
*
*/
__stopInternalTimer : function()
{
this.fireEvent("release");
this.__timer.stop();
this.removeState("abandoned");
this.removeState("pressed");
}
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function() {
this._disposeObjects("__timer");
}
});