User Input
Table of contents
The BGW framework uses events to communicate user input to Components. To execute code when a specific event is fired, a function reference, or a function literal can be set in ComponentViews. If /components can be enabled for drag and drop, some additional handlers can be set. Components can be enabled for drag and drop whenever they extend DynamicComponentView.
For a more detailed introduction for Drag and Drop head here.
The full source code for this example can be found here:
Component declaration
To showcase the user input handling, the following /components are declared and wrapped inside a BoardGameApplication, to create a running example.
class UserInputExample : BoardGameApplication("User input example") {
val button: Button = Button(height = 150, width = 300, posX = 30, posY = 30).apply {
visual = ColorVisual.GREEN
}
val token: TokenView = TokenView(posX = 500, posY = 30, visual = ColorVisual.RED)
val gameScene: BoardGameScene = BoardGameScene(background = ColorVisual.LIGHT_GRAY)
}
MouseEvents
There are five types of MouseEvents. The event contains the MouseButtonType that was in action.
- onMousePressed: The user pressed a button while the mouse was above this component.
- onMouseReleased: The user released a button while the mouse was above this component.
- onMouseClicked: The user clicked above this component, i.e. pressed and released.
- onMouseEntered: The mouse entered this component. Active button is UNSPECIFIED in this case.
- onMouseExited: The mouse left this component. Active button is UNSPECIFIED in this case.
Key events
- onKeyPressed: The user pressed a key while this component had focus.
- onKeyReleased: The user released a key while this component had focus.
- onKeyTyped: The user typed a key, i.e. pressed and released or pressed and held, while this component had focus.
keyCode
will be Undefined in this case and the character field has to be used instead.
Function reference vs. function literal
There are two ways to specify code that should get executed when a specific event is fired.
The first option is assigning a function with a fitting signature. In the following example the handleMouseClicked
function is declared and gets assigned to onMouseClicked
on the button
. This is useful if the same code should be assigned on different /components, or if the reference gets removed and re-added frequently.
private fun handleMouseClicked(mouseEvent: MouseEvent) {
button.text = "someone clicked on me!"
}
init {
button.onMouseClicked = this::handleMouseClicked
}
The second option is assigning a function literal. In the following example a function literal is assigned to the onMousePressed
on the button
. This is useful if the functionality is set only once and only on one component.
button.onMousePressed = { mouseEvent ->
button.text = "pressed ${mouseEvent.button}"
}
Distinction between ComponentViews, DynamicComponentViews and UIComponentViews
There are three types of /components, that can handle user input differently.
ComponentViews have function references that can deal with mouse, key and drop events.
DynamicComponentViews are ComponentViews that can be enabled for drag and drop, so they have additional function references to deal with that.
UIComponentViews are ComponentViews that may have additional ways of dealing with user input, for example text input. Please refer to the UIComponentView doc or guide to find additional information.
Global key listeners
Global key listeners may become helpful to show menus or move playing pieces by the arrow or WASD keys. Scene-scoped listeners may be implemented on the BoardGameScene.
gameScene.onKeyPressed = { event ->
if (event.keyCode == KeyCode.ESCAPE)
exit()
}
Full example on all available methods of dealing with user input
This example uses all available fields that can be set to handle user input on ComponentViews and DynamicComponentViews.
fun main() {
UserInputExample()
}
class UserInputExample: BoardGameApplication("User input example") {
val button : Button = Button(height = 150, width = 300, posX = 30, posY = 30).apply {
visual = ColorVisual.GREEN
}
val token : TokenView = TokenView(posX = 500, posY = 30, visual = ColorVisual.RED)
val gameScene : BoardGameScene = BoardGameScene(background = ColorVisual.LIGHT_GRAY)
init {
// handling user input on ComponentView
button.onMouseClicked = this::handleMouseClicked
button.onMousePressed = { mouseEvent -> button.text = "pressed ${mouseEvent.button}" }
button.onMouseReleased = { mouseEvent -> button.text = "released ${mouseEvent.button}" }
button.onMouseEntered = { button.visual = ColorVisual.MAGENTA }
button.onMouseExited = { button.visual = ColorVisual.GREEN }
button.onKeyPressed = { keyEvent -> button.text = "pressed key: ${keyEvent.keyCode}" }
button.onKeyReleased = { keyEvent -> button.text = "released key: ${keyEvent.keyCode}" }
button.onKeyTyped = { keyEvent -> button.text = "typed key: ${keyEvent.character}" }
button.dropAcceptor = { true }
button.onDragDropped = {
it.draggedComponent.reposition(500, 30)
it.draggedComponent.rotation = 0.0
gameScene.addComponents(token)
}
button.onDragGestureEntered = { dragEvent -> button.visual = dragEvent.draggedComponent.visual }
button.onDragGestureExited = { button.visual = ColorVisual.GREEN }
// Additional function references available only to DynamicComponentViews
token.isDraggable = true
token.onDragGestureMoved = { token.rotate(5) }
token.onDragGestureStarted = { token.scale(1.2) }
token.onDragGestureEnded = { _, success -> if (success) token.resize(50, 50) }
// Global input listener
gameScene.onKeyPressed = { event ->
if (event.keyCode == KeyCode.ESCAPE)
exit()
}
showGameScene(gameScene.apply { addComponents(button, token) })
show()
}
private fun handleMouseClicked(@Suppress("UNUSED_PARAMETER") mouseEvent: MouseEvent) {
button.text = "someone clicked on me!"
}
}