Spec-Zone.ru › Kotlin 2

Обработчики событий мыши

В настольном проекте можно обрабатывать различные события мыши: щелчки, перемещения, прокрутку, а также вход и выход из области ввода.

Примеры в этом руководстве — запускаемые настольные приложения. Чтобы попробовать один из них:

  1. Создайте проект Compose Multiplatform с помощью плагина Kotlin Multiplatform для IDE или онлайн-мастера.

  2. Откройте файл точки входа настольного приложения. Например, desktopApp/src/main/kotlin/com/example/my_desktop_app/main.kt.

  3. Замените его содержимое приведённым примером.

Обработчики щелчков

Обработчики щелчков доступны как в Compose Multiplatform для Android, так и в Compose Multiplatform для настольных приложений, поэтому ваш код будет работать на обеих платформах. Например, вот как настроить простые обработчики щелчков с помощью модификаторов onClick, onDoubleClick и onLongClick:

import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.sp import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication(title = "Mouse clicks") { var count by remember { mutableIntStateOf(0) } Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) { var text by remember { mutableStateOf("Click magenta box!") } Column { @OptIn(ExperimentalFoundationApi::class) Box(modifier = Modifier .background(Color.Magenta) .fillMaxWidth(0.7f) .fillMaxHeight(0.7f) .combinedClickable( onClick = { text = "Click! ${count++}" }, onDoubleClick = { text = "Double click! ${count++}" }, onLongClick = { text = "Long click! ${count++}" } ) ) Text(text = text, fontSize = 40.sp) } } }
Mouse click listeners

Модификатор combinedClickable поддерживает только основную кнопку (левую кнопку мыши) и события касания. Если нужно обрабатывать кнопки по-разному, см. раздел Modifier.onClick.

Обработчики перемещения

Модификатор onPointerEvent является экспериментальным. Для его использования требуется явное согласие (подробности см. ниже); применять его следует только для оценки. Сведения о стабильном API см. в разделе Modifier.pointerInput.

Чтобы создать обработчик перемещения указателя, который меняет цвет фона окна в зависимости от положения указателя мыши, добавьте следующий код:

import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication(title = "Mouse move listeners") { var color by remember { mutableStateOf(Color(0, 0, 0)) } Box(modifier = Modifier .wrapContentSize(Alignment.Center) .fillMaxSize() .background(color = color) .onPointerEvent(PointerEventType.Move) { val position = it.changes.first().position color = Color(position.x.toInt() % 256, position.y.toInt() % 256, 0) } ) }
Mouse move listeners

Обработчики входа

Модификатор onPointerEvent является экспериментальным. Для его использования требуется явное согласие (подробности см. ниже); применять его следует только для оценки. Сведения о стабильном API см. в разделе Modifier.pointerInput.

Compose Multiplatform для настольных приложений поддерживает обработчики входа указателя в область ввода и выхода из неё. Например, следующий код при наведении изменит стиль шрифта строки:

import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication(title = "Mouse enter listeners") { Column( Modifier.background(Color.White), verticalArrangement = Arrangement.spacedBy(10.dp), ) { repeat(10) { index -> var active by remember { mutableStateOf(false) } Text(modifier = Modifier .fillMaxWidth() .background(color = if (active) Color.Green else Color.White) .onPointerEvent(PointerEventType.Enter) { active = true } .onPointerEvent(PointerEventType.Exit) { active = false }, fontSize = 30.sp, fontStyle = if (active) FontStyle.Italic else FontStyle.Normal, text = "Item $index", textAlign = TextAlign.Center ) } } }
Mouse enter listeners

Обработчики прокрутки

Модификатор onPointerEvent является экспериментальным. Для его использования требуется явное согласие (подробности см. ниже); применять его следует только для оценки. Сведения о стабильном API см. в разделе Modifier.pointerInput.

В следующем примере показано, как увеличивать или уменьшать отображаемое число в зависимости от направления прокрутки мыши:

import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.unit.sp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication(title = "Mouse scroll listeners") { var number by remember { mutableFloatStateOf(0f) } Box( Modifier .fillMaxSize() .onPointerEvent(PointerEventType.Scroll) { number += it.changes.first().scrollDelta.y }, contentAlignment = Alignment.Center ) { Text("Scroll to change the number: $number", fontSize = 30.sp) } }
Mouse scroll listeners

Экспериментальные обработчики onClick

Модификатор onClick является экспериментальным и поддерживается только в настольных проектах. Для его использования требуется явное согласие (подробности см. ниже); применять его следует только для оценки.

Modifier.onClick предоставляет отдельные обратные вызовы для щелчков, двойных щелчков и длительных щелчков. Он обрабатывает только щелчки, вызванные событиями указателя, и изначально не обрабатывает события click специальных возможностей.

Для каждого onClick можно настроить целевые события указателя с помощью matcher: PointerMatcher и keyboardModifiers: PointerKeyboardModifiers.() -> Boolean:

  • matcher позволяет выбрать кнопку мыши, щелчок которой должен вызывать событие.

  • keyboardModifiers позволяет фильтровать события указателя, произошедшие при нажатой указанной клавише.

Также можно объединить несколько модификаторов onClick в цепочку, чтобы обрабатывать разные щелчки с разными условиями сопоставления и модификаторами клавиатуры. В отличие от clickable, onClick не имеет значений Modifier.indication и Modifier.semantics по умолчанию и не вызывает событие щелчка при нажатии Enter. При необходимости добавьте эти модификаторы отдельно. Чтобы обеспечить правильную передачу событий, сначала объявляйте наиболее общие обработчики onClick (с наименьшим количеством условий).

import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.PointerMatcher import androidx.compose.foundation.background import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.size import androidx.compose.foundation.onClick import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerButton import androidx.compose.ui.input.pointer.isAltPressed import androidx.compose.ui.input.pointer.isShiftPressed import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalFoundationApi::class) fun main() = singleWindowApplication(title = "Mouse clicks") { Column { var topBoxText by remember { mutableStateOf("Click me\nusing LMB or LMB + Shift") } var topBoxCount by remember { mutableIntStateOf(0) } // No indication on interaction Box(modifier = Modifier .size(300.dp, 200.dp) .background(Color.LightGray) // The most generic click handler (without extra conditions) should be the first one .onClick { // Receives all left mouse button clicks except for when Shift is pressed println("Click with primary button") topBoxText = "LMB ${topBoxCount++}" }.onClick( keyboardModifiers = { isShiftPressed } // Accepts clicks only when Shift is pressed ) { // Receives all left mouse button clicks when Shift is pressed println("Click with primary button and shift pressed") topBoxCount++ topBoxText = "LMB + Shift ${topBoxCount++}" } ) { AnimatedContent( targetState = topBoxText, modifier = Modifier.align(Alignment.Center) ) { Text(text = it, textAlign = TextAlign.Center) } } var bottomBoxText by remember { mutableStateOf("Click me\nusing LMB or\nRMB + Alt") } var bottomBoxCount by remember { mutableStateOf(0) } val interactionSource = remember { MutableInteractionSource() } // With indication on interaction Box(modifier = Modifier .size(300.dp, 200.dp) .background(Color.Yellow) .onClick( enabled = true, interactionSource = interactionSource, matcher = PointerMatcher.mouse(PointerButton.Secondary), // Right mouse button keyboardModifiers = { isAltPressed }, // Accepts clicks only when Alt is pressed onLongClick = { // Optional bottomBoxText = "RMB Long Click + Alt ${bottomBoxCount++}" println("Long Click with secondary button and Alt pressed") }, onDoubleClick = { // Optional bottomBoxText = "RMB Double Click + Alt ${bottomBoxCount++}" println("Double Click with secondary button and Alt pressed") }, onClick = { bottomBoxText = "RMB Click + Alt ${bottomBoxCount++}" println("Click with secondary button and Alt pressed") } ) .onClick(interactionSource = interactionSource) { // Uses default parameters bottomBoxText = "LMB Click ${bottomBoxCount++}" println("Click with primary button (mouse left button)") } .indication(interactionSource, LocalIndication.current) ) { AnimatedContent( targetState = bottomBoxText, modifier = Modifier.align(Alignment.Center) ) { Text(text = it, textAlign = TextAlign.Center) } } } }
Modifier.onClick

Экспериментальный модификатор onDrag

Модификатор onDrag является экспериментальным и поддерживается только в настольных проектах. Для его использования требуется явное согласие (подробности см. ниже); применять его следует только для оценки.

С помощью Modifier.onDrag можно указать указатель, который будет запускать перетаскивание, через matcher: PointerMatcher. Как и для onClick, можно объединить в цепочку несколько модификаторов onDrag.

Если клавиши могут менять поведение перетаскивания, можно также проверить состояние модификаторов клавиатуры с помощью LocalWindowInfo.current.keyboardModifier. Например, перемещать элемент обычным перетаскиванием, а копировать и вставлять его при перетаскивании с нажатой клавишей Ctrl.

В следующем примере показано, как обрабатывать события перетаскивания, вызванные левой и правой кнопками мыши, а также с использованием клавиатуры:

import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.PointerMatcher import androidx.compose.foundation.background import androidx.compose.foundation.gestures.onDrag import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerButton import androidx.compose.ui.input.pointer.isCtrlPressed import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalFoundationApi::class) fun main() = singleWindowApplication(title = "Drag") { val windowInfo = LocalWindowInfo.current Column { var topBoxOffset by remember { mutableStateOf(Offset(0f, 0f)) } Box(modifier = Modifier .offset { IntOffset(topBoxOffset.x.toInt(), topBoxOffset.y.toInt()) } .size(200.dp) .background(Color.Green) .onDrag { // By default: enabled = true, matcher = PointerMatcher.Primary (left mouse button) topBoxOffset += it } ) { Text(text = "Drag with LMB", modifier = Modifier.align(Alignment.Center)) } var bottomBoxOffset by remember { mutableStateOf(Offset(0f, 0f)) } Box(modifier = Modifier .offset { IntOffset(bottomBoxOffset.x.toInt(), bottomBoxOffset.y.toInt()) } .size(200.dp) .background(Color.LightGray) .onDrag( matcher = PointerMatcher.mouse(PointerButton.Secondary), // Right mouse button onDragStart = { println("Gray Box: drag start") }, onDragEnd = { println("Gray Box: drag end") } ) { val keyboardModifiers = windowInfo.keyboardModifiers bottomBoxOffset += if (keyboardModifiers.isCtrlPressed) it * 2f else it } ) { Text( text = "Drag with RMB,\ntry with CTRL", modifier = Modifier.align(Alignment.Center) ) } } }
Modifier.onDrag

Также перетаскивание можно обрабатывать без модификатора с помощью suspend fun PointerInputScope.detectDragGestures:

import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.PointerMatcher import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalFoundationApi::class) fun main() = singleWindowApplication(title = "Drag") { var topBoxOffset by remember { mutableStateOf(Offset(0f, 0f)) } Box(modifier = Modifier .offset { IntOffset(topBoxOffset.x.toInt(), topBoxOffset.y.toInt()) } .size(200.dp) .background(Color.Green) .pointerInput(Unit) { detectDragGestures( matcher = PointerMatcher.Primary ) { topBoxOffset += it } } ) { Text(text = "Drag with LMB", modifier = Modifier.align(Alignment.Center)) } }

Доступ к необработанным событиям AWT при взаимодействии со Swing

Модификатор onPointerEvent является экспериментальным. Для его использования требуется явное согласие (подробности см. ниже); применять его следует только для оценки. Сведения о стабильном API см. в разделе Modifier.pointerInput.

Compose Multiplatform для настольных приложений использует Swing и позволяет получать доступ к необработанным событиям AWT:

import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.awt.awtEventOrNull import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication(title = "Raw AWT events") { var text by remember { mutableStateOf("") } Box( Modifier .fillMaxSize() .onPointerEvent(PointerEventType.Press) { text = it.awtEventOrNull?.locationOnScreen?.toString().orEmpty() }, contentAlignment = Alignment.Center ) { Text(text) } }
Swing interoperability

Обработка необработанных событий в общем коде с помощью pointerInput

В приведённых выше фрагментах используется функция Modifier.onPointerEvent — вспомогательная функция для подписки на определённый тип событий указателя. Это новый и более короткий вариант функции Modifier.pointerInput. Сейчас она является экспериментальной и доступна только для настольных приложений, поэтому использовать её в общем коде нельзя.

Если нужно подписаться на события в общем коде или использовать стабильный API, можно воспользоваться функцией Modifier.pointerInput:

import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Text import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication(title = "Raw events via Modifier.pointerInput") { val list = remember { mutableStateListOf<String>() } Column( Modifier .fillMaxSize() .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent() val position = event.changes.first().position // On each relayout, Compose will send a synthetic Move event, // so we skip it to avoid event spam if (event.type != PointerEventType.Move) { list.add(0, "${event.type} $position") } } } }, ) { for (item in list.take(20)) { Text(item) } } }
Raw events via Modifier.pointerInput

Что дальше

Изучите руководства по другим компонентам для настольных приложений.

21 июля 2026 г.
Строка менюСобытия клавиатуры

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform/compose-desktop-mouse-events.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API