Skip to main content Skip to docs navigation

Document not reviewed yet, might be outdated. Please, let us know if you find something invalid here.
On this page

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
}
Was this article useful?