KorGE Input
Table of contents:
- Accessing input state
- Event-based High-level API
- Handling RAW events via Components
- Stage event dispatching
- APIs
- Handling Drag & Drop File Events
- Dragging Views
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
Mouse
view.addUpdater {
val xy: Point = input.mouse
val buttons: Int = input.mouseButtons // flags with the pressed buttons
}
Multi-touch
view.addUpdater {
val ntouches: Int = input.activeTouches.size
val touches: List<Touch> = input.activeTouches
val rawTouch0: Touch = input.touches[0]
}
Keys
import com.soywiz.klock.milliseconds
import com.soywiz.klogger.Console
import com.soywiz.korev.Key
view.addUpdater { timespan: TimeSpan ->
val scale = timespan / 16.milliseconds
if (input.keys[Key.LEFT]) x -= 2.0 * scale // Same as `input.keys.pressing(Key.LEFT)`
if (input.keys.pressing(Key.RIGHT)) x += 2.0 * scale
if (input.keys.justPressed(Key.ESCAPE)) views.gameWindow.close(0)
if (input.keys.justReleased(Key.ENTER)) Console.info("I'm working!")
}
Gamepads
view.addUpdater {
val gamepads = input.connectedGamepads
val rawGamepad0 = input.gamepads[0]
val pressedStart: Boolean = rawGamepad0[GameButton.START]
val pos: Point = rawGamepad0[GameStick.LEFT]
}
Event-based High-level API
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.
Mouse/Touch 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
Keys Events
For example:
// Matches any key
view.keys {
down { e -> /*...*/ }
up { e -> /*...*/ }
}
// 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 -> /*...*/ }
}
view.onKeyDown { e -> /*...*/ } // suspending block
Gamepad 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) { /*...*/ }
}
Handling RAW events via Components
You can handle RAW input by creating components that handle events and attaching to a view.
// Executes once per frame
interface UpdateComponent : Component {
fun update(ms: Double)
}
// New version of UpdateComponent
interface UpdateComponentV2 : UpdateComponent {
override fun update(dt: HRTimeSpan)
}
// Handling all the Events
interface EventComponent : Component {
fun onEvent(event: Event)
}
// Handling just Input Events
interface TouchComponent : Component {
fun onTouchEvent(views: Views, e: TouchEvent)
}
interface MouseComponent : Component {
fun onMouseEvent(views: Views, event: MouseEvent)
}
interface KeyComponent : Component {
fun onKeyEvent(views: Views, event: KeyEvent)
}
interface GamepadComponent : Component {
fun onGamepadEvent(views: Views, event: GamePadButtonEvent)
fun onGamepadEvent(views: Views, event: GamePadStickEvent)
fun onGamepadEvent(views: Views, event: GamePadConnectionEvent)
}
Stage event dispatching
The stage singleton receives raw events.
You can call stage.addEventListener
.
NOTE: Remember to remove the event listener once finished with it. Since you are adding it to the root node, it wonβt be collected automatically.
Example:
stage.addEventListener<ReshapeEvent> { e ->
}
Available event types:
MouseEvent
TouchEvent
ReshapeEvent
KeyEvent
GamePadConnectionEvent
GamePadUpdateEvent
GamePadButtonEvent
GamePadStickEvent
APIs
Mouse
// Configuring MouseEvents
val View.mouse: MouseEvents
inline fun <T> View.mouse(callback: MouseEvents.() -> T): T = mouse.run(callback)
// Shortcuts
inline fun <T : View?> T.onClick(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onOver(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onOut(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onDown(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onDownFromOutside(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onUp(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onUpOutside(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onUpAnywhere(noinline handler: suspend (MouseEvents) -> Unit): T
inline fun <T : View?> T.onMove(noinline handler: suspend (MouseEvents) -> Unit): T
class MouseEvents(override val view: View) : MouseComponent, UpdateComponentWithViews {
val click = Signal<MouseEvents>()
val over = Signal<MouseEvents>()
val out = Signal<MouseEvents>()
val down = Signal<MouseEvents>()
val downFromOutside = Signal<MouseEvents>()
val up = Signal<MouseEvents>()
val upOutside = Signal<MouseEvents>()
val upAnywhere = Signal<MouseEvents>()
val move = Signal<MouseEvents>()
val moveAnywhere = Signal<MouseEvents>()
val moveOutside = Signal<MouseEvents>()
val exit = Signal<MouseEvents>()
val startedPos = Point()
val lastPos = Point()
val currentPos = Point()
val hitTest: View?
var downPos = Point()
var upPos = Point()
var clickedCount = 0
val isOver: Boolean
}
Keys
// Configuring KeysEvents
val View.keys: KeysEvents
inline fun <T> View.keys(callback: KeysEvents.() -> T): T
// Shortcuts
inline fun <T : View?> T?.onKeyDown(noinline handler: suspend (KeyEvent) -> Unit)
inline fun <T : View?> T?.onKeyUp(noinline handler: suspend (KeyEvent) -> Unit)
inline fun <T : View?> T?.onKeyTyped(noinline handler: suspend (KeyEvent) -> Unit)
class KeysEvents(override val view: View) : KeyComponent {
val onKeyDown = AsyncSignal<KeyEvent>()
val onKeyUp = AsyncSignal<KeyEvent>()
val onKeyTyped = AsyncSignal<KeyEvent>()
// Handlers for a specific Key
fun down(key: Key, callback: (key: Key) -> Unit): Closeable
fun up(key: Key, callback: (key: Key) -> Unit): Closeable
fun typed(key: Key, callback: (key: Key) -> Unit): Closeable
// Handlers for any keys
fun down(callback: (key: Key) -> Unit): Closeable
fun up(callback: (key: Key) -> Unit): Closeable
fun typed(callback: (key: Key) -> Unit): Closeable
}
Gamepad
// Configuring GamePadEvents
val View.gamepad: GamePadEvents
inline fun <T> View.gamepad(callback: GamePadEvents.() -> T): T
class GamePadEvents(override val view: View) : GamepadComponent {
val stick = Signal<GamePadStickEvent>()
val button = Signal<GamePadButtonEvent>()
val connection = Signal<GamePadConnectionEvent>()
fun stick(playerId: Int, stick: GameStick, callback: (x: Double, y: Double) -> Unit)
fun down(playerId: Int, button: GameButton, callback: () -> Unit)
fun up(playerId: Int, button: GameButton, callback: () -> Unit)
fun connected(playerId: Int, callback: () -> Unit)
fun disconnected(playerId: Int, callback: () -> Unit)
override fun onGamepadEvent(views: Views, event: GamePadButtonEvent)
override fun onGamepadEvent(views: Views, event: GamePadStickEvent)
override fun onGamepadEvent(views: Views, event: GamePadConnectionEvent)
}
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
}