Input
Input State vs Events
There are two ways of handling input events. One is by checking the input state, and the other is event-based.
Accessing input state
The Input
singleton contains the stage of the input values at a time.
It is updated once per frame. You can get it from the Views
or the Stage
singletons.
val input = views.input
Events
KorGE provides a high level API to handle events attaching events to a View. The events are only triggered when the associated View is attached to the stage. Views have several extension methods to attach events to them. Which method depends on each kind of event.
Mouse/Touch Events
Mouse Input State
view.addUpdater {
val xy: Point = input.mouse
val buttons: Int = input.mouseButtons // flags with the pressed buttons
}
Mouse Events
You can handle click
, over
(hover), out
, down
, downFromOutside
, up
, upOutside
, upAnywhere
, move
, moveAnywhere
, moveOutside
and exit
mouse events.
For example:
view.mouse {
click { /*...*/ }
over { /*...*/ } // hover
out { /*...*/ }
down { /*...*/ }
downFromOutside { /*...*/ }
up { /*...*/ }
upOutside { /*...*/ }
upAnywhere { /*...*/ }
move { /*...*/ }
moveAnywhere { /*...*/ }
moveOutside { /*...*/ }
exit { /*...*/ }
}
or
view.onClick { /*...*/ } // suspending block
Multi-touch Events
view.addUpdater {
val ntouches: Int = input.activeTouches.size
val touches: List<Touch> = input.activeTouches
val rawTouch0: Touch = input.touches[0]
}
Keys
Keys Input State
import com.soywiz.klock.milliseconds
import com.soywiz.klogger.Console
import com.soywiz.korev.Key
// Triggered on every frame
view.addUpdater { timespan: TimeSpan ->
val scale = timespan / 16.milliseconds
// True when the key is pressing; same as `input.keys.pressing(Key.LEFT)`
if (input.keys[Key.LEFT]) x -= 2.0 * scale
if (input.keys.pressing(Key.RIGHT)) x += 2.0 * scale
// True only in one frame when the key started to be pressed
if (input.keys.justPressed(Key.ESCAPE)) views.gameWindow.close(0)
// True once in one frame when the key released (finished pressing)
if (input.keys.justReleased(Key.ENTER)) Console.info("I'm working!")
}
Keys Events
To handle any key:
// Matches any key
view.keys {
down { e -> /*...*/ }
up { e -> /*...*/ }
}
// Or as a shortcut
view.onKeyUp { e -> /*...*/ } // suspending block
view.onKeyDown { e -> /*...*/ } // suspending block
To handle only a specific key event:
// Matches just one key
view.keys {
// Executes when the key is down. Depending on the platform this might trigger multiple events. Use justDown to trigger it only once.
down(Key.LEFT) { e -> /*...*/ }
// Executes when the key is up
up(Key.LEFT) { e -> /*...*/ }
// Executes on every frame (be aware that fps might vary)
downFrame(Key.LEFT) { e -> /*...*/ }
// Executes every 16 milliseconds, when the key is down
downFrame(Key.LEFT, 16.milliseconds) { e -> /*...*/ }
// Executes only when the key was pressed once, then it won't be triggered again until released and pressed again
justDown(Key.LEFT) { e -> /*...*/ }
// Useful for UIs, the code is executed every half a second at first, and then every 100 milliseconds doing an acceleration.
downRepeating(Key.LEFT) { e -> /*...*/ }
}
Gamepads
Gamepads Input State
view.addUpdater {
val gamepads = input.connectedGamepads
val rawGamepad0 = input.gamepads[0]
val pressedStart: Boolean = rawGamepad0[GameButton.START]
val pos: Point = rawGamepad0[GameStick.LEFT]
}
Gamepads Events
view.gamepad {
val playerId = 0
connected { playerId -> /*...*/ }
disconnected { playerId -> /*...*/ }
stick(playerId, GameStick.LEFT) { x, y -> /*...*/ }
button(playerId) { pressed, button, value -> /*...*/ }
down(playerId, GameButton.BUTTON0) { /*...*/ }
}
On stage resizing
Example:
view.onStageResized { width, height ->
// ...
}
view.onEvent(ViewsResizedEvent) {
}
Handling Drag & Drop File Events
It is possible to detect and react to drag & drop events inside KorGE.
To do so, you can handle events of the type DropFileEvent
.
For simplicity, there is a method you can call from a view to register to DropFileEvent:
val dropFileRect = solidRect(Size(width, height), Colors.RED)
.visible(false)
onDropFile {
when (it.type) {
DropFileEvent.Type.START -> dropFileRect.visible = true
DropFileEvent.Type.END -> dropFileRect.visible = false
DropFileEvent.Type.DROP -> {
launchImmediately {
it.files?.firstOrNull()?.let {
image(it.readBitmap()).size(Size(width, height))
}
}
}
}
}
Or if you want to register events directly:
onEvents(DropFileEvent.Type.START) { println("A file is being dragged into the window") }
onEvents(DropFileEvent.Type.DROP) { println("A file has been successfully dropped in the window. Files: ${it.files}") }
onEvents(DropFileEvent.Type.END) { println("The drag&drop finished either with or without drop") }
or:
onEvents(*DropFileEvent.Type.ALL) {
println("${it.type}")
}
Dragging Views
In the case you want to make a view draggable. There is a View.draggable
and View.draggableCloseable
extensions:
val solidRect = solidRect(Size(100, 100), Colors.RED)
val closeable = solidRect.draggableCloseable()
The Closeable version returns a Closeable instance allowing you to stop accepting the dragging after the close.
Configure how dragging works
It is possible to configure the dragging View mediator, like this:
For example if you want only the dragging to work on the X or the Y you can set autoMove = false
:
val closeable = solidRect.draggableCloseable(selector = solidRect, autoMove = false) { info: DraggableInfo ->
//info.view.pos = info.viewNextXY
info.view.x = info.viewNextXY.x
}