Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,17 @@ fun CustomTextMenuProvider(content: @Composable () -> Unit) {

private fun AnnotatedString.crop() = if (length <= 5) toString() else "${take(5)}..."

@OptIn(ExperimentalFoundationApi::class)
private fun swingItem(
label: String,
color: java.awt.Color,
key: Int,
onClick: () -> Unit
menuItemAction: TextContextMenu.Action
) = JMenuItem(label).apply {
icon = circleIcon(color)
accelerator = KeyStroke.getKeyStroke(key, if (hostOs.isMacOS) META_DOWN_MASK else CTRL_DOWN_MASK)
addActionListener { onClick() }
isEnabled = menuItemAction.enabled()
addActionListener { menuItemAction.action() }
}

private fun circleIcon(color: java.awt.Color) = object : Icon {
Expand Down
3 changes: 2 additions & 1 deletion compose/foundation/foundation/api/desktop/foundation.api
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ public final class androidx/compose/foundation/DarkThemeKt {

public final class androidx/compose/foundation/DefaultContextMenuRepresentation : androidx/compose/foundation/ContextMenuRepresentation {
public static final field $stable I
public synthetic fun <init> (JJJLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (JJJJILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (JJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun Representation (Landroidx/compose/foundation/ContextMenuState;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.foundation.text.contextmenu.data.TextContextMenuItemWith
import androidx.compose.foundation.text.contextmenu.data.TextContextMenuSession
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -65,42 +66,48 @@ val DarkDefaultContextMenuRepresentation = DefaultContextMenuRepresentation(
class DefaultContextMenuRepresentation(
private val backgroundColor: Color,
private val textColor: Color,
private val itemHoverColor: Color
private val itemHoverColor: Color,
private val disabledTextColor: Color = textColor.copy(alpha = 0.38f),
) : ContextMenuRepresentation {
@OptIn(ExperimentalComposeUiApi::class)
@Composable
override fun Representation(state: ContextMenuState, items: () -> List<ContextMenuItem>) {
val status = state.status
if (status is ContextMenuState.Status.Open) {
val session = remember(state) {
object : TextContextMenuSession {
override fun close() {
state.status = ContextMenuState.Status.Closed
}
if (status !is ContextMenuState.Status.Open) return

val session = remember(state) {
object : TextContextMenuSession {
override fun close() {
state.status = ContextMenuState.Status.Closed
}
}
val components by remember {
derivedStateOf {
items().map {
TextContextMenuItemWithComposableLeadingIcon(
key = it,
label = it.label,
enabled = true,
onClick = {
session.close()
it.onClick()
}
)
}
}
val components by remember {
derivedStateOf {
items().map {
TextContextMenuItemWithComposableLeadingIcon(
key = it,
label = it.label,
enabled = (it as? ContextMenuItemWithEnabledState)?.enabled() != false,
onClick = {
session.close()
it.onClick()
}
)
}
}
val colors = remember(backgroundColor, textColor, itemHoverColor) {
}

if (components.isEmpty()) {
SideEffect { session.close() }
} else {
val colors = remember(backgroundColor, textColor, itemHoverColor, disabledTextColor) {
ContextMenuColors(
backgroundColor = backgroundColor,
textColor = textColor,
iconColor = Color.Unspecified,
disabledTextColor = Color.Unspecified,
disabledIconColor = Color.Unspecified,
iconColor = textColor,
disabledTextColor = disabledTextColor,
disabledIconColor = disabledTextColor,
hoverColor = itemHoverColor,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,7 @@ open class ContextMenuItem(
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as ContextMenuItem
if (other !is ContextMenuItem) return false

if (label != other.label) return false
if (onClick != other.onClick) return false
Expand All @@ -215,6 +213,41 @@ open class ContextMenuItem(
}
}

/**
* Individual element of context menu.
*
* @param label The text to be displayed as a context menu item.
* @param enabled Whether the menu item should be enabled.
* @param onClick The action to be executed after click on the item.
*/
@ExperimentalFoundationApi
open class ContextMenuItemWithEnabledState(
label: String,
val enabled: () -> Boolean,
onClick: () -> Unit
) : ContextMenuItem(label, onClick) {

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ContextMenuItemWithEnabledState) return false

if (!super.equals(other)) return false
if (enabled != other.enabled) return false

return true
}

override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + enabled.hashCode()
return result
}

override fun toString(): String {
return "ContextMenuItemWithEnabledState(label='$label', enabled=$enabled)"
}
}

/**
* Data container contains all [ContextMenuItem]s were defined previously in the hierarchy.
* [ContextMenuRepresentation] uses it to display context menu.
Expand Down
Loading