Skip to content

Commit 9bbd4a7

Browse files
authored
Merge pull request #100 from /issues/97
Refactor repeatable and long press timeline to global (#97)
2 parents 12162d3 + 623ba9f commit 9bbd4a7

14 files changed

Lines changed: 517 additions & 130 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 comtel2000
3+
*
4+
* Redistribution and use in source and binary forms, with or without modification, are permitted
5+
* provided that the following conditions are met:
6+
*
7+
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
8+
* and the following disclaimer.
9+
*
10+
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
11+
* conditions and the following disclaimer in the documentation and/or other materials provided with
12+
* the distribution.
13+
*
14+
* 3. Neither the name of the comtel2000 nor the names of its contributors may be used to endorse or
15+
* promote products derived from this software without specific prior written permission.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
18+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19+
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
24+
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*******************************************************************************/
26+
27+
package org.comtel2000.keyboard.control;
28+
29+
import java.util.function.Consumer;
30+
31+
import javafx.animation.Animation.Status;
32+
import javafx.animation.KeyFrame;
33+
import javafx.animation.Timeline;
34+
import javafx.event.ActionEvent;
35+
import javafx.scene.Node;
36+
import javafx.util.Duration;
37+
38+
abstract class ButtonTimeline {
39+
40+
protected final Timeline timeline;
41+
private Node owner;
42+
protected Consumer<ActionEvent> handler;
43+
44+
protected ButtonTimeline(double delay) {
45+
timeline = !Double.isNaN(delay) && delay > 0.0d ? new Timeline(new KeyFrame(Duration.millis(delay), this::handle)) : new Timeline();
46+
}
47+
48+
ButtonTimeline withOwner(Node owner, Consumer<ActionEvent> handler) {
49+
if (!isOwner(owner)) {
50+
timeline.stop();
51+
this.owner = owner;
52+
}
53+
this.handler = handler;
54+
return this;
55+
}
56+
57+
boolean isOwner(Node node) {
58+
return owner != null && node == owner;
59+
}
60+
61+
boolean isEnabled() {
62+
return !timeline.getKeyFrames().isEmpty();
63+
}
64+
65+
void playFromStart() {
66+
timeline.playFromStart();
67+
}
68+
69+
void stop() {
70+
timeline.stop();
71+
}
72+
73+
Status getStatus() {
74+
return timeline.getStatus();
75+
}
76+
77+
double getCurrentRate() {
78+
return timeline.getCurrentRate();
79+
}
80+
81+
void handle(ActionEvent event) {
82+
if (handler != null) {
83+
handler.accept(event);
84+
}
85+
}
86+
}

fx-onscreen-keyboard/src/main/java/org/comtel2000/keyboard/control/KeyBoardPopup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public void setOnKeyboardCloseButton(EventHandler<? super Event> value) {
261261
}
262262

263263
public void setVisible(final Visibility visible, final TextInputControl textNode) {
264-
264+
keyboard.timelines().stop();
265265
if ((visible == Visibility.POS || visible == Visibility.SHOW) && textNode != null) {
266266
Map<String, String> vkProps = FXOK.getVkProperties(textNode);
267267
if (vkProps.isEmpty()) {

fx-onscreen-keyboard/src/main/java/org/comtel2000/keyboard/control/KeyButton.java

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
import org.comtel2000.keyboard.event.KeyButtonEvent;
3030

31-
import javafx.animation.Timeline;
3231
import javafx.beans.property.ObjectProperty;
3332
import javafx.beans.property.ObjectPropertyBase;
3433
import javafx.event.Event;
@@ -38,60 +37,35 @@
3837

3938
public abstract class KeyButton extends Button implements LongPressable {
4039

41-
private static double KEY_LONG_PRESS_DELAY = 400;
42-
43-
private static final double KEY_LONG_PRESS_DELAY_MIN = 100;
44-
private static final double KEY_LONG_PRESS_DELAY_MAX = 1000;
45-
46-
Timeline buttonDelay;
4740
private String keyText;
4841
private boolean movable;
4942
private boolean sticky;
5043
private int keyCode;
5144
private ObjectProperty<EventHandler<? super KeyButtonEvent>> onLongPressed;
5245
private ObjectProperty<EventHandler<? super KeyButtonEvent>> onShortPressed;
5346

54-
static {
55-
String s = System.getProperty("org.comtel2000.keyboard.longPressDelay");
56-
if (s != null && !s.isBlank()) {
57-
try {
58-
Double delay = Double.valueOf(s);
59-
KEY_LONG_PRESS_DELAY = Math.min(Math.max(delay, KEY_LONG_PRESS_DELAY_MIN),
60-
KEY_LONG_PRESS_DELAY_MAX);
61-
} catch (NumberFormatException e) {
62-
// ignore
63-
}
64-
}
65-
}
66-
67-
public KeyButton() {
68-
this(null, null, KEY_LONG_PRESS_DELAY);
47+
protected KeyButton() {
48+
this(null, null);
6949
}
7050

7151
protected KeyButton(String label) {
72-
this(label, null, KEY_LONG_PRESS_DELAY);
52+
this(label, null);
7353
}
7454

7555
protected KeyButton(Node graphic) {
76-
this(null, graphic, KEY_LONG_PRESS_DELAY);
56+
this(null, graphic);
7757
}
7858

7959
protected KeyButton(String label, Node graphic) {
80-
this(label, graphic, KEY_LONG_PRESS_DELAY);
81-
}
82-
83-
protected KeyButton(String label, double delay) {
84-
this(label, null, delay);
85-
}
86-
87-
protected KeyButton(String label, Node graphic, double delay) {
8860
super(label, graphic);
8961
getStyleClass().add("key-button");
90-
initEventListener(delay > 0 ? delay : KEY_LONG_PRESS_DELAY);
91-
9262
}
9363

94-
protected abstract void initEventListener(double delay);
64+
final void setTimelines(Timelines timelines) {
65+
initEventListener(timelines);
66+
}
67+
68+
protected abstract void initEventListener(Timelines timelines);
9569

9670
void fireLongPressed() {
9771
fireEvent(new KeyButtonEvent(this, KeyButtonEvent.LONG_PRESSED));
@@ -169,42 +143,42 @@ public String getName() {
169143
return onShortPressed;
170144
}
171145

172-
public int getKeyCode() {
146+
int getKeyCode() {
173147
return keyCode;
174148
}
175149

176-
public void setKeyCode(int keyCode) {
150+
void setKeyCode(int keyCode) {
177151
this.keyCode = keyCode;
178152
}
179153

180-
public String getKeyText() {
154+
String getKeyText() {
181155
return keyText;
182156
}
183157

184-
public void setKeyText(String keyText) {
158+
void setKeyText(String keyText) {
185159
this.keyText = keyText;
186160
}
187161

188-
public void addExtKeyCode(int keyCode, String label) {
162+
void addExtKeyCode(Timelines timelines, int keyCode, String label) {
189163
}
190164

191-
public boolean isMovable() {
165+
boolean isMovable() {
192166
return movable;
193167
}
194168

195-
public void setMovable(boolean movable) {
169+
void setMovable(boolean movable) {
196170
this.movable = movable;
197171
}
198172

199-
public boolean isRepeatable() {
173+
boolean isRepeatable() {
200174
return false;
201175
}
202176

203-
public boolean isSticky() {
177+
boolean isSticky() {
204178
return sticky;
205179
}
206180

207-
public void setSticky(boolean sticky) {
181+
void setSticky(boolean sticky) {
208182
this.sticky = sticky;
209183
}
210184

fx-onscreen-keyboard/src/main/java/org/comtel2000/keyboard/control/KeyButtonEventHandler.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 comtel2000
3+
*
4+
* Redistribution and use in source and binary forms, with or without modification, are permitted
5+
* provided that the following conditions are met:
6+
*
7+
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
8+
* and the following disclaimer.
9+
*
10+
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
11+
* conditions and the following disclaimer in the documentation and/or other materials provided with
12+
* the distribution.
13+
*
14+
* 3. Neither the name of the comtel2000 nor the names of its contributors may be used to endorse or
15+
* promote products derived from this software without specific prior written permission.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
18+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19+
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
24+
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*******************************************************************************/
26+
127
package org.comtel2000.keyboard.control;
228

329
import static org.comtel2000.keyboard.control.StandardKeyCode.ARROW_DOWN;
@@ -50,7 +76,7 @@
5076
import javafx.event.EventHandler;
5177
import javafx.util.Duration;
5278

53-
class KeyButtonEventHandler implements EventHandler<KeyButtonEvent> {
79+
class KeyButtonEventHandler implements EventHandler<KeyButtonEvent> {
5480

5581
private static final Logger logger = LoggerFactory.getLogger(KeyButtonEventHandler.class);
5682
private final KeyboardPane keyboard;
@@ -63,6 +89,7 @@ class KeyButtonEventHandler implements EventHandler<KeyButtonEvent> {
6389
public void handle(KeyButtonEvent event) {
6490
if (!event.getEventType().equals(KeyButtonEvent.SHORT_PRESSED)) {
6591
logger.warn("ignore non short pressed events");
92+
keyboard.timelines().stop();
6693
return;
6794
}
6895
event.consume();

fx-onscreen-keyboard/src/main/java/org/comtel2000/keyboard/control/KeyboardPane.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,15 @@ public class KeyboardPane extends Region implements EventHandler<KeyButtonEvent>
115115

116116
private final KeyboardLocales keyboardLocalesSupplier;
117117

118+
private final Timelines timelines;
119+
118120
public KeyboardPane() {
119121
getStyleClass().add("key-background");
120122
setFocusTraversable(false);
121123
keyButtonEventHandler = new KeyButtonEventHandler(this);
122124
layoutLocaleSwitcher = new LayoutLocaleSwitcher(this);
123125
keyboardLocalesSupplier = new KeyboardLocales(this);
126+
timelines = new Timelines();
124127
}
125128

126129
@Override
@@ -644,4 +647,7 @@ public void handle(KeyButtonEvent event) {
644647
keyButtonEventHandler.handle(event);
645648
}
646649

650+
Timelines timelines() {
651+
return timelines;
652+
}
647653
}

fx-onscreen-keyboard/src/main/java/org/comtel2000/keyboard/control/KeyboardRegion.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 comtel2000
3+
*
4+
* Redistribution and use in source and binary forms, with or without modification, are permitted
5+
* provided that the following conditions are met:
6+
*
7+
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
8+
* and the following disclaimer.
9+
*
10+
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
11+
* conditions and the following disclaimer in the documentation and/or other materials provided with
12+
* the distribution.
13+
*
14+
* 3. Neither the name of the comtel2000 nor the names of its contributors may be used to endorse or
15+
* promote products derived from this software without specific prior written permission.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
18+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19+
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
24+
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*******************************************************************************/
26+
127
package org.comtel2000.keyboard.control;
228

329
import static org.comtel2000.keyboard.xml.XmlHelper.ATTR_CODES;
@@ -125,13 +151,13 @@ private void build(URL layout) {
125151
String[] codes = code.split(",");
126152
final KeyButton button;
127153
if (codes.length > 1 || code.equals(Integer.toString(StandardKeyCode.LOCALE_SWITCH))) {
128-
button = new MultiKeyButton(this, getStylesheets());
154+
button = new MultiKeyButton(this, keyboard.getStylesheets());
129155
} else if (readBooleanAttribute(reader, ATTR_REPEATABLE, false)) {
130156
button = new RepeatableKeyButton();
131157
} else {
132158
button = new ShortPressKeyButton();
133159
}
134-
160+
button.setTimelines(keyboard.timelines());
135161
button.setFocusTraversable(false);
136162
button.setPickOnBounds(true); // Ensures the button reacts to clicks/taps on the entire area, including
137163
// label/icon (fixes #92)
@@ -166,13 +192,13 @@ private void build(URL layout) {
166192
if (codes.length > 1) {
167193
for (var i = 1; i < codes.length; i++) {
168194
int keyCode = parseInt(codes[i]);
169-
button.addExtKeyCode(keyCode, Character.toString((char) keyCode));
195+
button.addExtKeyCode(keyboard.timelines(), keyCode, Character.toString((char) keyCode));
170196
}
171197
}
172198

173199
if (button.getKeyCode() == StandardKeyCode.LOCALE_SWITCH) {
174200
for (var l : keyboard.getAvailableLocales().keySet()) {
175-
button.addExtKeyCode(StandardKeyCode.LOCALE_SWITCH, l.getLanguage().toUpperCase(Locale.ENGLISH));
201+
button.addExtKeyCode(keyboard.timelines(), StandardKeyCode.LOCALE_SWITCH, l.getLanguage().toUpperCase(Locale.ENGLISH));
176202
}
177203
}
178204

0 commit comments

Comments
 (0)