Spec-Zone.ru › Kotlin 2

Контекстные меню

Compose Multiplatform для настольных компьютеров из коробки поддерживает контекстные меню для текста и позволяет удобно настраивать любые контекстные меню: добавлять элементы, задавать темы и изменять текст.

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

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

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

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

Контекстное меню в произвольной области

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

import androidx.compose.foundation.ContextMenuArea import androidx.compose.foundation.ContextMenuItem import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication(title = "Context menu") { ContextMenuArea(items = { listOf( ContextMenuItem("User-defined action") { // Custom action }, ContextMenuItem("Another user-defined action") { // Another custom action } ) }) { // Blue box where context menu will be available Box(modifier = Modifier.background(Color.Blue).height(100.dp).width(100.dp)) } }
Context menu: ContextMenuArea

Настройка темы

Вы можете настроить цвета контекстного меню, чтобы создать адаптивный пользовательский интерфейс, соответствующий настройкам системы, и избежать резких изменений контрастности при переключении между приложениями. Для светлой и тёмной тем по умолчанию предусмотрены две встроенные реализации: LightDefaultContextMenuRepresentation и DarkDefaultContextMenuRepresentation. Они не применяются к цветам контекстного меню автоматически, поэтому нужно задать подходящую тему с помощью LocalContextMenuRepresentation:

import androidx.compose.foundation.DarkDefaultContextMenuRepresentation import androidx.compose.foundation.LightDefaultContextMenuRepresentation import androidx.compose.foundation.LocalContextMenuRepresentation import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.TextField import androidx.compose.material.darkColors import androidx.compose.material.lightColors import androidx.compose.runtime.CompositionLocalProvider 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.window.singleWindowApplication fun main() = singleWindowApplication(title = "Dark theme") { MaterialTheme( colors = if (isSystemInDarkTheme()) darkColors() else lightColors() ) { val contextMenuRepresentation = if (isSystemInDarkTheme()) { DarkDefaultContextMenuRepresentation } else { LightDefaultContextMenuRepresentation } CompositionLocalProvider(LocalContextMenuRepresentation provides contextMenuRepresentation) { Surface(Modifier.fillMaxSize()) { Box { var value by remember { mutableStateOf("") } TextField(value, { value = it }) } } } } }
Context menu: Dark theme

Локализация элементов меню

По умолчанию контекстное меню отображается на предпочитаемом языке, заданном в системных настройках:

Context menu: Localization

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

java.util.Locale.setDefault(java.util.Locale("en"))

Контекстное меню для текста

Контекстное меню для текста по умолчанию

Compose Multiplatform для настольных компьютеров предлагает встроенные контекстные меню для TextField и выделяемых Text.

Контекстное меню текстового поля по умолчанию включает следующие действия, зависящие от положения курсора и диапазона выделения: копировать, вырезать, вставить и выделить всё. Это стандартное контекстное меню доступно по умолчанию в компоненте material TextField (androidx.compose.material.TextField или androidx.compose.material3.TextField) и компоненте foundation BasicTextField (androidx.compose.foundation.text.BasicTextField).

import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication(title = "Context menu") { val text = remember { mutableStateOf("Hello!") } TextField( value = text.value, onValueChange = { text.value = it }, label = { Text(text = "Input") } ) }
Default context menu for TextField

Подробности см. в справочнике API.

Контекстное меню простого текстового элемента включает только действие копирования. Чтобы включить контекстное меню для компонента Text, сделайте текст выделяемым, обернув его в SelectionContainer:

import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.Text import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication(title = "Context menu") { SelectionContainer { Text("Hello World!") } }
Default context menu for Text

Добавление пользовательских элементов

Чтобы добавить пользовательские действия в контекстное меню компонентов TextField и Text, задайте новые элементы с помощью ContextMenuItem и добавьте их в иерархию элементов контекстного меню через ContextMenuDataProvider. Например, в следующем фрагменте кода показано, как добавить два новых пользовательских действия в контекстные меню текстового поля и простого выделяемого текстового элемента:

import androidx.compose.foundation.ContextMenuDataProvider import androidx.compose.foundation.ContextMenuItem import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication(title = "Context menu") { val text = remember { mutableStateOf("Hello!") } Column { ContextMenuDataProvider( items = { listOf( ContextMenuItem("User-defined action") { // Custom action }, ContextMenuItem("Another user-defined action") { // Another custom action } ) } ) { TextField( value = text.value, onValueChange = { text.value = it }, label = { Text(text = "Input") } ) Spacer(Modifier.height(16.dp)) SelectionContainer { Text("Hello World!") } } } }
Context menu with custom actions

Переопределение контекстного меню для текста по умолчанию

Чтобы переопределить контекстное меню по умолчанию для текстовых полей и выделяемых текстовых элементов, переопределите интерфейс TextContextMenu. В следующем фрагменте кода мы используем исходный TextContextMenu, добавляя в конец списка ещё один элемент. Новый элемент адаптируется к выделенному тексту:

import androidx.compose.foundation.ContextMenuDataProvider import androidx.compose.foundation.ContextMenuItem import androidx.compose.foundation.ContextMenuState import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Column import androidx.compose.foundation.text.LocalTextContextMenu import androidx.compose.foundation.text.TextContextMenu import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.window.singleWindowApplication import java.net.URLEncoder import java.nio.charset.Charset fun main() = singleWindowApplication(title = "Context menu") { CustomTextMenuProvider { Column { SelectionContainer { Text("Hello, Compose!") } var text by remember { mutableStateOf("") } TextField(text, { text = it }) } } } @OptIn(ExperimentalFoundationApi::class) @Composable fun CustomTextMenuProvider(content: @Composable () -> Unit) { val textMenu = LocalTextContextMenu.current val uriHandler = LocalUriHandler.current CompositionLocalProvider( LocalTextContextMenu provides object : TextContextMenu { @Composable override fun Area( textManager: TextContextMenu.TextManager, state: ContextMenuState, content: @Composable () -> Unit ) { // Reuses original TextContextMenu and adds a new item ContextMenuDataProvider({ val shortText = textManager.selectedText.crop() if (shortText.isNotEmpty()) { val encoded = URLEncoder.encode(shortText, Charset.defaultCharset()) listOf(ContextMenuItem("Search $shortText") { uriHandler.openUri("https://google.com/search?q=$encoded") }) } else { emptyList() } }) { textMenu.Area(textManager, state, content = content) } } }, content = content ) } private fun AnnotatedString.crop() = if (length <= 5) toString() else "${take(5)}..."
Context menu: LocalTextContextMenu

Взаимодействие со Swing

Если вы встраиваете код Compose в существующее приложение Swing и хотите, чтобы контекстное меню соответствовало внешнему виду и поведению других частей приложения, можно использовать класс JPopupTextMenu. В этом классе LocalTextContextMenu использует Swing JPopupMenu для контекстных меню в компонентах Compose.

import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Column import androidx.compose.foundation.text.JPopupTextMenu import androidx.compose.foundation.text.LocalTextContextMenu import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.awt.ComposePanel import androidx.compose.ui.platform.LocalLocalization import java.awt.Color import java.awt.Component import java.awt.Dimension import java.awt.Graphics import java.awt.event.KeyEvent import java.awt.event.KeyEvent.CTRL_DOWN_MASK import java.awt.event.KeyEvent.META_DOWN_MASK import javax.swing.Icon import javax.swing.JFrame import javax.swing.JMenuItem import javax.swing.JPopupMenu import javax.swing.KeyStroke.getKeyStroke import javax.swing.SwingUtilities import org.jetbrains.skiko.hostOs fun main() = SwingUtilities.invokeLater { val panel = ComposePanel() panel.setContent { JPopupTextMenuProvider(panel) { Column { SelectionContainer { Text("Hello, World!") } var text by remember { mutableStateOf("") } TextField(text, { text = it }) } } } val window = JFrame() window.contentPane.add(panel) window.size = Dimension(800, 600) window.isVisible = true window.title = "Swing interop" } @OptIn(ExperimentalFoundationApi::class) @Composable fun JPopupTextMenuProvider(owner: Component, content: @Composable () -> Unit) { val localization = LocalLocalization.current CompositionLocalProvider( LocalTextContextMenu provides JPopupTextMenu(owner) { textManager, items -> JPopupMenu().apply { textManager.cut?.also { add( swingItem(localization.cut, Color.RED, KeyEvent.VK_X, it) ) } textManager.copy?.also { add( swingItem(localization.copy, Color.GREEN, KeyEvent.VK_C, it) ) } textManager.paste?.also { add( swingItem(localization.paste, Color.BLUE, KeyEvent.VK_V, it) ) } textManager.selectAll?.also { add(JPopupMenu.Separator()) add( swingItem(localization.selectAll, Color.BLACK, KeyEvent.VK_A, it) ) } // Adds items that can be defined via ContextMenuDataProvider in other parts of the application for (item in items) { add( JMenuItem(item.label).apply { addActionListener { item.onClick() } } ) } } }, content = content ) } private fun swingItem( label: String, color: Color, key: Int, onClick: () -> Unit ) = JMenuItem(label).apply { icon = circleIcon(color) accelerator = getKeyStroke(key, if (hostOs.isMacOS) META_DOWN_MASK else CTRL_DOWN_MASK) addActionListener { onClick() } } private fun circleIcon(color: Color) = object : Icon { override fun paintIcon(c: Component?, g: Graphics, x: Int, y: Int) { g.create().apply { this.color = color translate(8, 2) fillOval(0, 0, 16, 16) } } override fun getIconWidth() = 16 override fun getIconHeight() = 16 }
Context menu: Swing interoperability

Что дальше

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

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-context-menus.html

Spec-Zone.ru

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