Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All Popup overloads call dismiss on Esc key by default #712

Merged
merged 8 commits into from
Jul 27, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,6 @@ class DefaultContextMenuRepresentation(
onKeyEvent = {
if (it.type == KeyEventType.KeyDown) {
when (it.key.nativeKeyCode) {
java.awt.event.KeyEvent.VK_ESCAPE -> {
state.status = ContextMenuState.Status.Closed
true
}
java.awt.event.KeyEvent.VK_DOWN -> {
inputModeManager!!.requestInputMode(InputMode.Keyboard)
focusManager!!.moveFocus(FocusDirection.Next)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,14 +368,6 @@ object PopupAlertDialogProvider : AlertDialogProvider {
},
focusable = true,
onDismissRequest = onDismissRequest,
onKeyEvent = {
if (it.type == KeyEventType.KeyDown && it.awtEventOrNull?.keyCode == KeyEvent.VK_ESCAPE) {
onDismissRequest()
true
} else {
false
}
},
) {
val scrimColor = Color.Black.copy(alpha = 0.32f) //todo configure scrim color in function arguments
Box(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ private fun OpenDropdownMenu(
onDismissRequest = onDismissRequest,
popupPositionProvider = popupPositionProvider,
onKeyEvent = {
handlePopupOnKeyEvent(it, onDismissRequest, focusManager!!, inputModeManager!!)
handlePopupOnKeyEvent(it, focusManager!!, inputModeManager!!)
},
) {
focusManager = LocalFocusManager.current
Expand Down Expand Up @@ -363,14 +363,10 @@ fun DropdownMenuItem(
@OptIn(ExperimentalComposeUiApi::class)
private fun handlePopupOnKeyEvent(
keyEvent: androidx.compose.ui.input.key.KeyEvent,
onDismissRequest: () -> Unit,
focusManager: FocusManager,
inputModeManager: InputModeManager
): Boolean {
return if (keyEvent.type == KeyEventType.KeyDown && keyEvent.awtEventOrNull?.keyCode == KeyEvent.VK_ESCAPE) {
onDismissRequest()
true
} else if (keyEvent.type == KeyEventType.KeyDown) {
return if (keyEvent.type == KeyEventType.KeyDown) {
when {
keyEvent.isDirectionDown -> {
inputModeManager.requestInputMode(InputMode.Keyboard)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ fun DropdownMenu(
onDismissRequest = onDismissRequest,
popupPositionProvider = popupPositionProvider,
onKeyEvent = {
handlePopupOnKeyEvent(it, onDismissRequest, focusManager!!, inputModeManager!!)
handlePopupOnKeyEvent(it, focusManager!!, inputModeManager!!)
},
) {
focusManager = LocalFocusManager.current
Expand Down Expand Up @@ -195,21 +195,17 @@ fun DropdownMenuItem(
@ExperimentalComposeUiApi
private fun handlePopupOnKeyEvent(
keyEvent: KeyEvent,
onDismissRequest: () -> Unit,
focusManager: FocusManager,
inputModeManager: InputModeManager
): Boolean {
return if (keyEvent.type == KeyEventType.KeyDown && keyEvent.key == Key.Escape) {
onDismissRequest()
true
} else if (keyEvent.type == KeyEventType.KeyDown) {
when {
keyEvent.key == Key.DirectionDown -> {
return if (keyEvent.type == KeyEventType.KeyDown) {
when (keyEvent.key) {
Key.DirectionDown -> {
inputModeManager.requestInputMode(InputMode.Keyboard)
focusManager.moveFocus(FocusDirection.Next)
true
}
keyEvent.key == Key.DirectionUp -> {
Key.DirectionUp -> {
inputModeManager.requestInputMode(InputMode.Keyboard)
focusManager.moveFocus(FocusDirection.Previous)
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fun Modifier.onPreviewKeyEvent(
onPreviewKeyEvent: (KeyEvent) -> Boolean
): Modifier = this then KeyInputElement(onKeyEvent = null, onPreKeyEvent = onPreviewKeyEvent)

private data class KeyInputElement(
internal data class KeyInputElement(
val onKeyEvent: ((KeyEvent) -> Boolean)?,
val onPreKeyEvent: ((KeyEvent) -> Boolean)?
) : ModifierNodeElement<KeyInputNode>() {
Expand All @@ -73,7 +73,7 @@ private data class KeyInputElement(
}
}

private class KeyInputNode(
internal class KeyInputNode(
var onEvent: ((KeyEvent) -> Boolean)?,
var onPreEvent: ((KeyEvent) -> Boolean)?
) : KeyInputModifierNode, Modifier.Node() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,17 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.keyEvent
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.test.isPopup
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performKeyPress
import androidx.compose.ui.test.runSkikoComposeUiTest
import androidx.compose.ui.unit.dp
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
Expand Down Expand Up @@ -147,4 +156,100 @@ class DesktopPopupTest {

rule.waitForIdle()
}

@Test
fun dismissPopupByEscWithBackPressProperty() {
var onDismissRequestCallCount = 0
rule.setContent {
Popup(
onDismissRequest = { onDismissRequestCallCount++ },
properties = PopupProperties(
focusable = true,
dismissOnBackPress = true
)
) {
Box(Modifier)
}
}

rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyDown))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(1)
rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyUp))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(1)
}

@Test
fun doNotDismissPopupByEscWithoutBackPressProperty() {
var onDismissRequestCallCount = 0
rule.setContent {
Popup(
onDismissRequest = { onDismissRequestCallCount++ },
properties = PopupProperties(
focusable = true,
dismissOnBackPress = false
)
) {
Box(Modifier)
}
}

rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyDown))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(0)
rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyUp))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(0)
}

@Test
fun dismissPopupByEscOnNotConsumedKeyEvent() {
var onDismissRequestCallCount = 0
rule.setContent {
Popup(
focusable = true,
onDismissRequest = { onDismissRequestCallCount++ },
onKeyEvent = { false }
) {
Box(Modifier)
}
}

rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyDown))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(1)
rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyUp))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(1)
}

@Test
fun doNotDismissPopupByEscOnConsumedKeyEvent() {
var onDismissRequestCallCount = 0
rule.setContent {
Popup(
focusable = true,
onDismissRequest = { onDismissRequestCallCount++ },
onKeyEvent = { true }
) {
Box(Modifier)
}
}

rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyDown))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(0)
rule.onNode(isPopup())
.performKeyPress(keyEvent(Key.Escape, KeyEventType.KeyUp))
rule.waitForIdle()
assertThat(onDismissRequestCallCount).isEqualTo(0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyInputElement
import androidx.compose.ui.input.key.NativeKeyEvent
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.node.RootForTest
Expand Down Expand Up @@ -390,8 +393,7 @@ class ComposeScene internal constructor(
initDensity = density,
coroutineContext = recomposer.effectCoroutineContext,
bounds = IntSize(constraints.maxWidth, constraints.maxHeight).toIntRect(),
onPreviewKeyEvent = onPreviewKeyEvent,
onKeyEvent = onKeyEvent,
modifier = KeyInputElement(onKeyEvent = onKeyEvent, onPreKeyEvent = onPreviewKeyEvent)
)
attach(mainOwner)
composition = mainOwner.setContent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ internal class SkiaBasedOwner(
bounds: IntRect = IntRect.Zero,
val focusable: Boolean = true,
val onOutsidePointerEvent: ((PointerInputEvent) -> Unit)? = null,
private val onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
private val onKeyEvent: (KeyEvent) -> Boolean = { false },
modifier: Modifier = Modifier,
) : Owner, RootForTest, SkiaRootForTest, PositionCalculator {
override val windowInfo: WindowInfo get() = platform.windowInfo

Expand All @@ -102,8 +101,6 @@ internal class SkiaBasedOwner(

override val sharedDrawScope = LayoutNodeDrawScope()

private val semanticsModifier = EmptySemanticsElement

// TODO(https://github.com/JetBrains/compose-multiplatform/issues/2944)
// Check if ComposePanel/SwingPanel focus interop work correctly with new features of
// the focus system (it works with the old features like moveFocus/clearFocus)
Expand Down Expand Up @@ -136,11 +133,10 @@ internal class SkiaBasedOwner(
override val root = LayoutNode().also {
it.layoutDirection = layoutDirection
it.measurePolicy = RootMeasurePolicy
it.modifier = semanticsModifier
it.modifier = EmptySemanticsElement
.then(focusOwner.modifier)
.then(keyInputModifier)
.onPreviewKeyEvent(onPreviewKeyEvent)
.onKeyEvent(onKeyEvent)
.then(modifier)
igordmn marked this conversation as resolved.
Show resolved Hide resolved
}

override val coroutineContext: CoroutineContext = coroutineContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.input.pointer.PointerButton
import androidx.compose.ui.input.pointer.PointerEventType
Expand Down Expand Up @@ -64,6 +66,21 @@ actual fun Dialog(
properties: DialogProperties,
content: @Composable () -> Unit
) {
var modifier = Modifier
.semantics { dialog() }
.drawBehind {
drawRect(Color.Black.copy(alpha = 0.4f))
}
if (properties.dismissOnBackPress) {
modifier = modifier.onKeyEvent { event: KeyEvent ->
if (event.isDismissRequest()) {
onDismissRequest()
true
} else {
false
}
}
}
val onOutsidePointerEvent = if (properties.dismissOnClickOutside) {
{ event: PointerInputEvent ->
if (event.isDismissRequest()) {
Expand All @@ -76,22 +93,8 @@ actual fun Dialog(
PopupLayout(
popupPositionProvider = WindowCenterPositionProvider,
focusable = true,
modifier = Modifier
.drawBehind {
drawRect(Color.Black.copy(alpha = 0.4f))
}
.semantics { dialog() },
modifier = modifier,
onOutsidePointerEvent = onOutsidePointerEvent,
onKeyEvent = {
if (properties.dismissOnBackPress &&
it.type == KeyEventType.KeyDown && it.key == Key.Escape
) {
onDismissRequest()
true
} else {
false
}
},
content = content
)
}
Expand All @@ -111,3 +114,6 @@ private fun PointerInputEvent.isMainAction() =

private fun PointerInputEvent.isDismissRequest() =
eventType == PointerEventType.Release && isMainAction()

private fun KeyEvent.isDismissRequest() =
type == KeyEventType.KeyDown && key == Key.Escape
Loading