Skip to content

Commit

Permalink
NumericStepper: add repeatDelay property to allow decrement/increment…
Browse files Browse the repository at this point in the history
… buttons to repeat while held down (closes #190)
  • Loading branch information
joshtynjala committed Jan 22, 2025
1 parent 4db4d80 commit 2275d59
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions src/feathers/controls/NumericStepper.hx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import feathers.utils.DisplayObjectFactory;
import feathers.utils.ExclusivePointer;
import feathers.utils.MathUtil;
import openfl.display.InteractiveObject;
import openfl.display.Stage;
import openfl.errors.ArgumentError;
import openfl.events.Event;
import openfl.events.FocusEvent;
import openfl.events.KeyboardEvent;
import openfl.events.MouseEvent;
import openfl.events.TimerEvent;
import openfl.events.TouchEvent;
import openfl.ui.Keyboard;
import openfl.utils.Timer;
#if air
import openfl.ui.Multitouch;
#end
Expand Down Expand Up @@ -348,6 +351,51 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
return this._snapInterval;
}

private var _repeatTimer:Timer;
private var _currentRepeatAction:() -> Void = null;

private var _repeatDelay:Float = 0.05;

/**
The time, in seconds, before actions triggered by pressing the increment
and decrement buttons are repeated. The first repeat happens after a
delay that is five times longer than the following repeats.
In the following example, the stepper's repeat delay is set to
500 milliseconds:
```haxe
stepper.repeatDelay = 0.5;
```
To disable repeat, set `repeatDelay` to `0.0` seconds.
```haxe
// disable repeat
stepper.repeatDelay = 0.0;
```
@default 0.05
@since 1.4.0
**/
public var repeatDelay(get, set):Float;

private function get_repeatDelay():Float {
return this._repeatDelay;
}

private function set_repeatDelay(value:Float):Float {
if (this._repeatDelay == value) {
return this._repeatDelay;
}
this._repeatDelay = value;
if (this._repeatDelay == 0.0) {
this.cleanupRepeatTimer();
}
return this._repeatDelay;
}

private var _editable:Bool = true;

/**
Expand Down Expand Up @@ -707,6 +755,7 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
}

override public function dispose():Void {
this.cleanupRepeatTimer();
this.destroyDecrementButton();
this.destroyIncrementButton();
this.destroyTextInput();
Expand Down Expand Up @@ -1185,6 +1234,15 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
this.textInput = null;
}

private function cleanupRepeatTimer():Void {
this._currentRepeatAction = null;
if (this._repeatTimer != null) {
this._repeatTimer.removeEventListener(TimerEvent.TIMER, numericStepper_repeatTimer_timerHandler);
this._repeatTimer.stop();
this._repeatTimer = null;
}
}

private function restrictValue(value:Float):Float {
if (this._snapInterval != 0.0 && value != this._minimum && value != this._maximum) {
value = MathUtil.roundToNearest(value, this._snapInterval);
Expand All @@ -1209,6 +1267,22 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
this.value = newValue;
}

private function startRepeatTimer(action:() -> Void):Void {
if (this._repeatDelay <= 0.0) {
this.cleanupRepeatTimer();
return;
}
this._currentRepeatAction = action;
if (this._repeatTimer == null) {
this._repeatTimer = new Timer(this._repeatDelay * 1000);
this._repeatTimer.addEventListener(TimerEvent.TIMER, numericStepper_repeatTimer_timerHandler);
} else {
this._repeatTimer.reset();
this._repeatTimer.delay = this._repeatDelay * 1000;
}
this._repeatTimer.start();
}

private function numericStepper_focusInHandler(event:FocusEvent):Void {
if (this.stage != null && this.stage.focus != null && this.textInput != null && !this.textInput.contains(this.stage.focus)) {
event.stopImmediatePropagation();
Expand Down Expand Up @@ -1265,6 +1339,10 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
return;
}
this.decrement();
if (this._repeatDelay != 0.0) {
this.startRepeatTimer(this.decrement);
this.stage.addEventListener(MouseEvent.MOUSE_UP, numericStepper_repeatTimer_stage_mouseUpHandler, false, 0, true);
}
}

private function numericStepper_decrementButton_touchBeginHandler(event:TouchEvent):Void {
Expand All @@ -1283,6 +1361,10 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
return;
}
this.decrement();
if (this._repeatDelay != 0.0) {
this.startRepeatTimer(this.decrement);
this.stage.addEventListener(TouchEvent.TOUCH_END, numericStepper_repeatTimer_stage_touchEndHandler, false, 0, true);
}
}

private function numericStepper_incrementButton_mouseDownHandler(event:MouseEvent):Void {
Expand All @@ -1296,6 +1378,10 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
return;
}
this.increment();
if (this._repeatDelay != 0.0) {
this.startRepeatTimer(this.increment);
this.stage.addEventListener(MouseEvent.MOUSE_UP, numericStepper_repeatTimer_stage_mouseUpHandler, false, 0, true);
}
}

private function numericStepper_incrementButton_touchBeginHandler(event:TouchEvent):Void {
Expand All @@ -1314,6 +1400,22 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
return;
}
this.increment();
if (this._repeatDelay != 0.0) {
this.startRepeatTimer(this.increment);
this.stage.addEventListener(TouchEvent.TOUCH_END, numericStepper_repeatTimer_stage_touchEndHandler, false, 0, true);
}
}

private function numericStepper_repeatTimer_stage_mouseUpHandler(event:MouseEvent):Void {
var stage = cast(event.currentTarget, Stage);
stage.removeEventListener(MouseEvent.MOUSE_UP, numericStepper_repeatTimer_stage_mouseUpHandler);
this.cleanupRepeatTimer();
}

private function numericStepper_repeatTimer_stage_touchEndHandler(event:TouchEvent):Void {
var stage = cast(event.currentTarget, Stage);
stage.removeEventListener(TouchEvent.TOUCH_END, numericStepper_repeatTimer_stage_touchEndHandler);
this.cleanupRepeatTimer();
}

private function numericStepper_textInput_keyDownHandler(event:KeyboardEvent):Void {
Expand Down Expand Up @@ -1344,4 +1446,11 @@ class NumericStepper extends FeathersControl implements IRange implements IStage
}
this.parseTextInputValue();
}

private function numericStepper_repeatTimer_timerHandler(event:TimerEvent):Void {
if (this._repeatTimer.currentCount < 5) {
return;
}
this._currentRepeatAction();
}
}

0 comments on commit 2275d59

Please sign in to comment.