Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compose/foundation/foundation/api/desktop/foundation.api
Original file line number Diff line number Diff line change
Expand Up @@ -1943,6 +1943,13 @@ public abstract interface class androidx/compose/foundation/text/contextmenu/dat
public abstract fun close ()V
}

public final class androidx/compose/foundation/text/contextmenu/internal/ComposableSingletons$DefaultTextContextMenuDropdownProvider_skikoKt {
public static final field INSTANCE Landroidx/compose/foundation/text/contextmenu/internal/ComposableSingletons$DefaultTextContextMenuDropdownProvider_skikoKt;
public fun <init> ()V
public final fun getLambda$-651812147$foundation ()Lkotlin/jvm/functions/Function5;
public final fun getLambda$1837043723$foundation ()Lkotlin/jvm/functions/Function5;
}

public final class androidx/compose/foundation/text/contextmenu/modifier/TextContextMenuModifierKt {
public static final fun appendTextContextMenuComponents (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
public static final fun filterTextContextMenuComponents (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
Expand Down
4 changes: 4 additions & 0 deletions compose/foundation/foundation/api/foundation.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,7 @@ final val androidx.compose.foundation.shape/androidx_compose_foundation_shape_Ro
final val androidx.compose.foundation.text.contextmenu.builder/androidx_compose_foundation_text_contextmenu_builder_TextContextMenuBuilderScope$stableprop // androidx.compose.foundation.text.contextmenu.builder/androidx_compose_foundation_text_contextmenu_builder_TextContextMenuBuilderScope$stableprop|#static{}androidx_compose_foundation_text_contextmenu_builder_TextContextMenuBuilderScope$stableprop[0]
final val androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuComponent$stableprop // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuComponent$stableprop|#static{}androidx_compose_foundation_text_contextmenu_data_TextContextMenuComponent$stableprop[0]
final val androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuData$stableprop // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuData$stableprop|#static{}androidx_compose_foundation_text_contextmenu_data_TextContextMenuData$stableprop[0]
final val androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuItemWithComposableLeadingIcon$stableprop // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuItemWithComposableLeadingIcon$stableprop|#static{}androidx_compose_foundation_text_contextmenu_data_TextContextMenuItemWithComposableLeadingIcon$stableprop[0]
final val androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuKeys$stableprop // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuKeys$stableprop|#static{}androidx_compose_foundation_text_contextmenu_data_TextContextMenuKeys$stableprop[0]
final val androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuSeparator$stableprop // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuSeparator$stableprop|#static{}androidx_compose_foundation_text_contextmenu_data_TextContextMenuSeparator$stableprop[0]
final val androidx.compose.foundation.text.contextmenu.modifier/androidx_compose_foundation_text_contextmenu_modifier_AddTextContextMenuDataComponentsNode$stableprop // androidx.compose.foundation.text.contextmenu.modifier/androidx_compose_foundation_text_contextmenu_modifier_AddTextContextMenuDataComponentsNode$stableprop|#static{}androidx_compose_foundation_text_contextmenu_modifier_AddTextContextMenuDataComponentsNode$stableprop[0]
Expand Down Expand Up @@ -1773,6 +1774,7 @@ final val androidx.compose.foundation/androidx_compose_foundation_BorderStroke$s
final val androidx.compose.foundation/androidx_compose_foundation_ClickableNode$stableprop // androidx.compose.foundation/androidx_compose_foundation_ClickableNode$stableprop|#static{}androidx_compose_foundation_ClickableNode$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_CombinedClickableNode_DoubleKeyClickState$stableprop // androidx.compose.foundation/androidx_compose_foundation_CombinedClickableNode_DoubleKeyClickState$stableprop|#static{}androidx_compose_foundation_CombinedClickableNode_DoubleKeyClickState$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_ComposeFoundationFlags$stableprop // androidx.compose.foundation/androidx_compose_foundation_ComposeFoundationFlags$stableprop|#static{}androidx_compose_foundation_ComposeFoundationFlags$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_ContextMenuColors$stableprop // androidx.compose.foundation/androidx_compose_foundation_ContextMenuColors$stableprop|#static{}androidx_compose_foundation_ContextMenuColors$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_FocusableNode$stableprop // androidx.compose.foundation/androidx_compose_foundation_FocusableNode$stableprop|#static{}androidx_compose_foundation_FocusableNode$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_FocusedBoundsObserverNode$stableprop // androidx.compose.foundation/androidx_compose_foundation_FocusedBoundsObserverNode$stableprop|#static{}androidx_compose_foundation_FocusedBoundsObserverNode$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_InputModeFilterIndication$stableprop // androidx.compose.foundation/androidx_compose_foundation_InputModeFilterIndication$stableprop|#static{}androidx_compose_foundation_InputModeFilterIndication$stableprop[0]
Expand Down Expand Up @@ -2119,6 +2121,7 @@ final fun androidx.compose.foundation.shape/androidx_compose_foundation_shape_Ro
final fun androidx.compose.foundation.text.contextmenu.builder/androidx_compose_foundation_text_contextmenu_builder_TextContextMenuBuilderScope$stableprop_getter(): kotlin/Int // androidx.compose.foundation.text.contextmenu.builder/androidx_compose_foundation_text_contextmenu_builder_TextContextMenuBuilderScope$stableprop_getter|androidx_compose_foundation_text_contextmenu_builder_TextContextMenuBuilderScope$stableprop_getter(){}[0]
final fun androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuComponent$stableprop_getter(): kotlin/Int // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuComponent$stableprop_getter|androidx_compose_foundation_text_contextmenu_data_TextContextMenuComponent$stableprop_getter(){}[0]
final fun androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuData$stableprop_getter(): kotlin/Int // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuData$stableprop_getter|androidx_compose_foundation_text_contextmenu_data_TextContextMenuData$stableprop_getter(){}[0]
final fun androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuItemWithComposableLeadingIcon$stableprop_getter(): kotlin/Int // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuItemWithComposableLeadingIcon$stableprop_getter|androidx_compose_foundation_text_contextmenu_data_TextContextMenuItemWithComposableLeadingIcon$stableprop_getter(){}[0]
final fun androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuKeys$stableprop_getter(): kotlin/Int // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuKeys$stableprop_getter|androidx_compose_foundation_text_contextmenu_data_TextContextMenuKeys$stableprop_getter(){}[0]
final fun androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuSeparator$stableprop_getter(): kotlin/Int // androidx.compose.foundation.text.contextmenu.data/androidx_compose_foundation_text_contextmenu_data_TextContextMenuSeparator$stableprop_getter|androidx_compose_foundation_text_contextmenu_data_TextContextMenuSeparator$stableprop_getter(){}[0]
final fun androidx.compose.foundation.text.contextmenu.modifier/androidx_compose_foundation_text_contextmenu_modifier_AddTextContextMenuDataComponentsNode$stableprop_getter(): kotlin/Int // androidx.compose.foundation.text.contextmenu.modifier/androidx_compose_foundation_text_contextmenu_modifier_AddTextContextMenuDataComponentsNode$stableprop_getter|androidx_compose_foundation_text_contextmenu_modifier_AddTextContextMenuDataComponentsNode$stableprop_getter(){}[0]
Expand Down Expand Up @@ -2275,6 +2278,7 @@ final fun androidx.compose.foundation/androidx_compose_foundation_BorderStroke$s
final fun androidx.compose.foundation/androidx_compose_foundation_ClickableNode$stableprop_getter(): kotlin/Int // androidx.compose.foundation/androidx_compose_foundation_ClickableNode$stableprop_getter|androidx_compose_foundation_ClickableNode$stableprop_getter(){}[0]
final fun androidx.compose.foundation/androidx_compose_foundation_CombinedClickableNode_DoubleKeyClickState$stableprop_getter(): kotlin/Int // androidx.compose.foundation/androidx_compose_foundation_CombinedClickableNode_DoubleKeyClickState$stableprop_getter|androidx_compose_foundation_CombinedClickableNode_DoubleKeyClickState$stableprop_getter(){}[0]
final fun androidx.compose.foundation/androidx_compose_foundation_ComposeFoundationFlags$stableprop_getter(): kotlin/Int // androidx.compose.foundation/androidx_compose_foundation_ComposeFoundationFlags$stableprop_getter|androidx_compose_foundation_ComposeFoundationFlags$stableprop_getter(){}[0]
final fun androidx.compose.foundation/androidx_compose_foundation_ContextMenuColors$stableprop_getter(): kotlin/Int // androidx.compose.foundation/androidx_compose_foundation_ContextMenuColors$stableprop_getter|androidx_compose_foundation_ContextMenuColors$stableprop_getter(){}[0]
final fun androidx.compose.foundation/androidx_compose_foundation_FocusableNode$stableprop_getter(): kotlin/Int // androidx.compose.foundation/androidx_compose_foundation_FocusableNode$stableprop_getter|androidx_compose_foundation_FocusableNode$stableprop_getter(){}[0]
final fun androidx.compose.foundation/androidx_compose_foundation_FocusedBoundsObserverNode$stableprop_getter(): kotlin/Int // androidx.compose.foundation/androidx_compose_foundation_FocusedBoundsObserverNode$stableprop_getter|androidx_compose_foundation_FocusedBoundsObserverNode$stableprop_getter(){}[0]
final fun androidx.compose.foundation/androidx_compose_foundation_InputModeFilterIndication$stableprop_getter(): kotlin/Int // androidx.compose.foundation/androidx_compose_foundation_InputModeFilterIndication$stableprop_getter|androidx_compose_foundation_InputModeFilterIndication$stableprop_getter(){}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,18 @@

package androidx.compose.foundation

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.text.JPopupTextMenu
import androidx.compose.foundation.text.contextmenu.data.TextContextMenuItemWithComposableLeadingIcon
import androidx.compose.foundation.text.contextmenu.data.TextContextMenuSession
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.derivedStateOf
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.ComposePanel
import androidx.compose.ui.awt.ComposeWindow
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.InputMode
import androidx.compose.ui.input.InputModeManager
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.nativeKeyCode
import androidx.compose.ui.input.key.type
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalInputModeManager
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupProperties
import androidx.compose.ui.window.rememberPopupPositionProviderAtPosition
import java.awt.Component
import java.awt.MouseInfo
Expand All @@ -65,10 +37,6 @@ import javax.swing.SwingUtilities
import javax.swing.event.PopupMenuEvent
import javax.swing.event.PopupMenuListener

// Design of basic representation is from Material specs:
// https://material.io/design/interaction/states.html#hover
// https://material.io/components/menus#specs

/**
* Representation of a context menu that is suitable for light themes of the application.
*/
Expand Down Expand Up @@ -104,93 +72,42 @@ class DefaultContextMenuRepresentation(
override fun Representation(state: ContextMenuState, items: () -> List<ContextMenuItem>) {
val status = state.status
if (status is ContextMenuState.Status.Open) {
var focusManager: FocusManager? by mutableStateOf(null)
var inputModeManager: InputModeManager? by mutableStateOf(null)

Popup(
properties = PopupProperties(focusable = true),
onDismissRequest = { state.status = ContextMenuState.Status.Closed },
popupPositionProvider = rememberPopupPositionProviderAtPosition(
positionPx = status.rect.center
),
onKeyEvent = {
if (it.type == KeyEventType.KeyDown) {
when (it.key.nativeKeyCode) {
java.awt.event.KeyEvent.VK_DOWN -> {
inputModeManager!!.requestInputMode(InputMode.Keyboard)
focusManager!!.moveFocus(FocusDirection.Next)
true
}
java.awt.event.KeyEvent.VK_UP -> {
inputModeManager!!.requestInputMode(InputMode.Keyboard)
focusManager!!.moveFocus(FocusDirection.Previous)
true
}
else -> false
}
} else {
false
val session = remember(state) {
object : TextContextMenuSession {
override fun close() {
state.status = ContextMenuState.Status.Closed
}
},
) {
focusManager = LocalFocusManager.current
inputModeManager = LocalInputModeManager.current
Column(
modifier = Modifier
.shadow(8.dp)
.background(backgroundColor)
.padding(vertical = 4.dp)
.width(IntrinsicSize.Max)
.verticalScroll(rememberScrollState())

) {
items().forEach { item ->
MenuItemContent(
itemHoverColor = itemHoverColor,
onClick = {
state.status = ContextMenuState.Status.Closed
item.onClick()
}
) {
BasicText(text = item.label, style = TextStyle(color = textColor))
}
}
}
val components by remember {
derivedStateOf {
items().map {
TextContextMenuItemWithComposableLeadingIcon(
key = it,
label = it.label,
enabled = true,
onClick = { it.onClick() }
)
}
}
}
}
}
}

@Composable
private fun MenuItemContent(
itemHoverColor: Color,
onClick: () -> Unit,
content: @Composable RowScope.() -> Unit
) {
var hovered by remember { mutableStateOf(false) }
Row(
modifier = Modifier
.clickable(
onClick = onClick,
)
.onHover { hovered = it }
.background(if (hovered) itemHoverColor else Color.Transparent)
.fillMaxWidth()
// Preferred min and max width used during the intrinsic measurement.
.sizeIn(
minWidth = 112.dp,
maxWidth = 280.dp,
minHeight = 32.dp
)
.padding(
PaddingValues(
horizontal = 16.dp,
vertical = 0.dp
val colors = remember(backgroundColor, textColor, itemHoverColor) {
ContextMenuColors(
backgroundColor = backgroundColor,
textColor = textColor,
iconColor = Color.Unspecified,
disabledTextColor = Color.Unspecified,
disabledIconColor = Color.Unspecified,
hoverColor = itemHoverColor,
)
),
verticalAlignment = Alignment.CenterVertically
) {
content()
}
DefaultOpenContextMenu(
session = session,
components = components,
popupPositionProvider = rememberPopupPositionProviderAtPosition(status.rect.center),
colors = colors,
)
}
}
}

Expand Down Expand Up @@ -247,16 +164,4 @@ class JPopupContextMenuRepresentation(
}
}
}
}

private fun Modifier.onHover(onHover: (Boolean) -> Unit) = pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
when (event.type) {
PointerEventType.Enter -> onHover(true)
PointerEventType.Exit -> onHover(false)
}
}
}
}
}
Loading