diff --git a/fxgl-samples/src/main/java/intermediate/MouseInputFluentSample.java b/fxgl-samples/src/main/java/intermediate/MouseInputFluentSample.java new file mode 100644 index 000000000..24e6109a3 --- /dev/null +++ b/fxgl-samples/src/main/java/intermediate/MouseInputFluentSample.java @@ -0,0 +1,30 @@ +package intermediate; + +import com.almasb.fxgl.app.GameApplication; +import com.almasb.fxgl.app.GameSettings; +import javafx.scene.input.MouseButton; + +import static com.almasb.fxgl.dsl.FXGL.onBtnBuilder; + +public class MouseInputFluentSample extends GameApplication { + + @Override + protected void initSettings(GameSettings settings) { } + + @Override + protected void initInput() { + + onBtnBuilder(MouseButton.PRIMARY) + .onActionBegin(() -> System.out.println("Primary Begin")) + .onAction(() -> System.out.println("Primary Action")) + .onActionEnd(() -> System.out.println("Primary End")); + + onBtnBuilder(MouseButton.SECONDARY) + .onActionBegin(() -> System.out.println("Secondary Begin")) + .onActionEnd(() -> System.out.println("Secondary End")); + } + + public static void main(String[] args) { + launch(args); + } +} \ No newline at end of file diff --git a/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/ButtonInputBuilder.kt b/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/ButtonInputBuilder.kt new file mode 100644 index 000000000..4f5168afb --- /dev/null +++ b/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/ButtonInputBuilder.kt @@ -0,0 +1,46 @@ +package com.almasb.fxgl.dsl + +import com.almasb.fxgl.core.util.EmptyRunnable +import com.almasb.fxgl.input.Input +import com.almasb.fxgl.input.InputModifier +import com.almasb.fxgl.input.UserAction +import javafx.scene.input.MouseButton + +class ButtonInputBuilder { + + companion object { + private var counter = 0 + } + + private var onActionBegin: Runnable = EmptyRunnable + private var onAction: Runnable = EmptyRunnable + private var onActionEnd: Runnable = EmptyRunnable + + constructor(input: Input, button: MouseButton, modifier: InputModifier = InputModifier.NONE, name: String = "AUTO-${counter++}") { + input.addAction(object : UserAction(name) { + override fun onActionBegin() { + onActionBegin.run() + } + + override fun onAction() { + onAction.run() + } + + override fun onActionEnd() { + onActionEnd.run() + } + }, button, modifier) + } + + fun onActionBegin(onActionBegin: Runnable) = this.also { + it.onActionBegin = onActionBegin + } + + fun onAction(onAction: Runnable) = this.also { + it.onAction = onAction + } + + fun onActionEnd(onActionEnd: Runnable) = this.also { + it.onActionEnd = onActionEnd + } +} \ No newline at end of file diff --git a/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/FXGL.kt b/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/FXGL.kt index 28fd07100..9e7ab78b7 100644 --- a/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/FXGL.kt +++ b/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/FXGL.kt @@ -861,9 +861,26 @@ class FXGL private constructor() { companion object { * Note: for onAction() to work, [input.update()] needs to be called. */ @JvmStatic @JvmOverloads fun onKeyBuilder( - input: Input, - key: KeyCode, - modifier: InputModifier = InputModifier.NONE) = KeyInputBuilder(input, key, modifier) + input: Input, + key: KeyCode, + modifier: InputModifier = InputModifier.NONE) = KeyInputBuilder(input, key, modifier) + + @JvmStatic fun onBtnBuilder( + btn: MouseButton, + name: String) = ButtonInputBuilder(getInput(), btn, InputModifier.NONE, name) + + @JvmStatic @JvmOverloads fun onBtnBuilder( + btn: MouseButton, + modifier: InputModifier = InputModifier.NONE) = ButtonInputBuilder(getInput(), btn, modifier) + + /** + * Button builder for custom input. + * Note: for onAction() to work, [input.update()] needs to be called. + */ + @JvmStatic @JvmOverloads fun onBtnBuilder( + input: Input, + btn: MouseButton, + modifier: InputModifier = InputModifier.NONE) = ButtonInputBuilder(input, btn, modifier) } } @@ -873,4 +890,3 @@ class FXGL private constructor() { companion object { - diff --git a/fxgl/src/test/kotlin/com/almasb/fxgl/dsl/ButtonInputBuilderTest.kt b/fxgl/src/test/kotlin/com/almasb/fxgl/dsl/ButtonInputBuilderTest.kt new file mode 100644 index 000000000..5399d0cf6 --- /dev/null +++ b/fxgl/src/test/kotlin/com/almasb/fxgl/dsl/ButtonInputBuilderTest.kt @@ -0,0 +1,141 @@ +package com.almasb.fxgl.dsl + +import com.almasb.fxgl.input.Input +import javafx.scene.input.MouseButton +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class ButtonInputBuilderTest { + + private lateinit var input: Input + private val timePerFrame = 0.016 + + @BeforeEach + fun setUp() { + input = Input() + } + + @Test + fun `onActionBegin is called when mouse button is pressed`() { + var count = 0 + val expectedCount = 1 + + ButtonInputBuilder(input, MouseButton.PRIMARY) + .onActionBegin { count++ } + + input.mockButtonPress(MouseButton.PRIMARY) + input.update(timePerFrame) + + assertEquals(expectedCount, count) + } + + @Test + fun `onAction is called while mouse button is held`() { + var count = 0 + val expectedCount = 3 + + ButtonInputBuilder(input, MouseButton.PRIMARY) + .onAction { count++ } + + input.mockButtonPress(MouseButton.PRIMARY) + + input.update(timePerFrame) + input.update(timePerFrame) + input.update(timePerFrame) + + assertEquals(expectedCount, count) + } + + @Test + fun `onActionEnd is called when mouse button is released`() { + var count = 0 + val expectedCount = 1 + + ButtonInputBuilder(input, MouseButton.PRIMARY) + .onActionEnd { count++ } + + input.mockButtonPress(MouseButton.PRIMARY) + input.update(timePerFrame) + + input.mockButtonRelease(MouseButton.PRIMARY) + input.update(timePerFrame) + + assertEquals(expectedCount, count) + } + + @Test + fun `all callbacks can be chained`() { + var beginCount = 0 + var actionCount = 0 + var endCount = 0 + + val expectedBeginCount = 1 + val expectedActionCount = 2 + val expectedEndCount = 1 + + ButtonInputBuilder(input, MouseButton.PRIMARY) + .onActionBegin { beginCount++ } + .onAction { actionCount++ } + .onActionEnd { endCount++ } + + input.mockButtonPress(MouseButton.PRIMARY) + + input.update(timePerFrame) + input.update(timePerFrame) + + input.mockButtonRelease(MouseButton.PRIMARY) + input.update(timePerFrame) + + assertEquals(expectedBeginCount, beginCount) + assertEquals(expectedActionCount, actionCount) + assertEquals(expectedEndCount, endCount) + } + + @Test + fun `FXGL onBtnBuilder with custom input supports onActionBegin`() { + var count = 0 + val expectedCount = 1 + + FXGL.onBtnBuilder(input, MouseButton.PRIMARY) + .onActionBegin { count++ } + + input.mockButtonPress(MouseButton.PRIMARY) + input.update(timePerFrame) + + assertEquals(expectedCount, count) + } + + @Test + fun `FXGL onBtnBuilder with custom input supports onAction`() { + var count = 0 + val expectedCount = 2 + + FXGL.onBtnBuilder(input, MouseButton.PRIMARY) + .onAction { count++ } + + input.mockButtonPress(MouseButton.PRIMARY) + + input.update(timePerFrame) + input.update(timePerFrame) + + assertEquals(expectedCount, count) + } + + @Test + fun `FXGL onBtnBuilder with custom input supports onActionEnd`() { + var count = 0 + val expectedCount = 1 + + FXGL.onBtnBuilder(input, MouseButton.PRIMARY) + .onActionEnd { count++ } + + input.mockButtonPress(MouseButton.PRIMARY) + input.update(timePerFrame) + + input.mockButtonRelease(MouseButton.PRIMARY) + input.update(timePerFrame) + + assertEquals(expectedCount, count) + } +} \ No newline at end of file