Skip to content

キーボードイベント

Compose Multiplatform for Desktop では、キーボードイベントを2つの異なるスコープでイベントハンドラーを設定することで管理できます。

  • フォーカスされた要素に基づくイベントハンドラー。
  • ウィンドウスコープのイベントハンドラー。

フォーカスされたコンポーネントでのイベント

このアプローチは、キーボードのキーを押すと、現在フォーカスされているコンポーネントのイベントハンドラーがトリガーされることを意味します。

典型的なシナリオは、TextField のようなアクティブなコントロールにキーボードハンドラーを定義することです。キーイベントがデフォルトのアクションをトリガーする前にインターセプトするには、onKeyEventonPreviewKeyEvent の両方のモディファイアを使用できます。 onKeyEvent モディファイアを使用すると個々のキーストロークを処理でき、onPreviewKeyEvent はショートカットを定義するのに適しています。

次のサンプルは、 を押しながら、どのキーが押されたかに応じて異なるアクションを実行する TextField のインタラクションを示しています。

kotlin
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.isCtrlPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.window.singleWindowApplication

fun main() = singleWindowApplication (title = "Key events") {
    MaterialTheme {
        var consumedText by remember { mutableStateOf(0) }
        var text by remember { mutableStateOf("") }
        Column(Modifier.fillMaxSize(), Arrangement.spacedBy(5.dp)) {
            Text("Consumed text: $consumedText")
            TextField(
                value = text,
                onValueChange = { text = it },
                modifier = Modifier.onPreviewKeyEvent {
                    when {
                        (it.isCtrlPressed && it.key == Key.Minus && it.type == KeyEventType.KeyUp) -> {
                            consumedText -= text.length
                            text = ""
                            true
                        }
                        (it.isCtrlPressed && it.key == Key.Equals && it.type == KeyEventType.KeyUp) -> {
                            consumedText += text.length
                            text = ""
                            true
                        }
                        else -> false
                    }
                }
            )
        }
    }
}
Keyboard events in a focused component

ウィンドウスコープでのイベント

現在のウィンドウ内で常にアクティブなキーボードイベントハンドラーを定義するには、WindowsingleWindowApplicationDialog 関数で利用可能な onPreviewKeyEvent および onKeyEvent パラメーターを使用します。 これらは、イベントが消費されなかった場合のディスパッチ方法が異なります。onPreviewKeyEvent はイベントを最初の子にディスパッチし、onKeyEvent はイベントをコンポーザブルの親にディスパッチします。通常、onPreviewKeyEvent はイベントのインターセプトに推奨されます。これは、画面全体のキーボードショートカットさえも実装できるためです。

次のサンプルは、Escape キーを押してポップアップダイアログを閉じたり、 ショートカットを押してウィンドウの内容を変更したりするなど、ウィンドウのインタラクションを示しています。

kotlin
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.isCtrlPressed
import androidx.compose.ui.input.key.isShiftPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogWindow
import androidx.compose.ui.window.singleWindowApplication

private var cleared by mutableStateOf(false)

fun main() = singleWindowApplication(
    title = "Keyboard events",
    onKeyEvent = {
        if (
            it.isCtrlPressed &&
            it.isShiftPressed &&
            it.key == Key.C &&
            it.type == KeyEventType.KeyDown
        ) {
            cleared = true
            true
        } else {
            false
        }
    }
) {
    MaterialTheme {
        if (cleared) {
            Text("The App was cleared!")
        } else {
            App()
        }
    }
}

@Composable
fun App() {
    var isDialogOpen by remember { mutableStateOf(false) }

    if (isDialogOpen) {
        DialogWindow(onCloseRequest = { isDialogOpen = false },
            title = "Dialog",
            onPreviewKeyEvent = {
                if (it.key == Key.Escape && it.type == KeyEventType.KeyDown) {
                    isDialogOpen = false
                    true
                } else {
                    false
                }
            }) {
            Text("Hello!")
        }
    }

    Column(Modifier.fillMaxSize(), Arrangement.spacedBy(5.dp)) {
        Button(
            modifier = Modifier.padding(4.dp),
            onClick = { isDialogOpen = true }
        ) {
            Text("Open dialog")
        }
    }
}
Keyboard events in a window scope

次のステップ