Skip to content

Commit

Permalink
Pass PointerEventType to ComposeSceneLayer.onOutsidePointerEvent (#…
Browse files Browse the repository at this point in the history
…1057)

## Proposed Changes

- Changes _internal_ API of `ComposeSceneLayer`
- Dismiss `Popup`s only on `Press` event type
- iOS platform binding will be fixed in the next PR by @dima-avdeev-jb 

## Testing

Test: Run reproduction snipped from the issue

## Issues Fixed

Fixes JetBrains/compose-multiplatform#4080
  • Loading branch information
MatkovIvan committed Feb 6, 2024
1 parent c2ed06a commit 64c8cff
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.runtime.CompositionContext
import androidx.compose.ui.awt.toAwtColor
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.scene.skia.SkiaLayerComponent
import androidx.compose.ui.scene.skia.SwingSkiaLayerComponent
import androidx.compose.ui.unit.Density
Expand All @@ -35,7 +36,6 @@ import java.awt.Rectangle
import java.awt.event.MouseEvent
import java.awt.event.MouseListener
import javax.swing.JLayeredPane
import javax.swing.RootPaneContainer
import javax.swing.SwingUtilities
import kotlin.math.ceil
import kotlin.math.floor
Expand Down Expand Up @@ -90,7 +90,7 @@ internal class SwingComposeSceneLayer(
}

private var _mediator: ComposeSceneMediator? = null
private var outsidePointerCallback: ((Boolean) -> Unit)? = null
private var outsidePointerCallback: ((eventType: PointerEventType) -> Unit)? = null

override var density: Density = density
set(value) {
Expand Down Expand Up @@ -171,7 +171,7 @@ internal class SwingComposeSceneLayer(
}

override fun setOutsidePointerEventListener(
onOutsidePointerEvent: ((dismissRequest: Boolean) -> Unit)?
onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)?
) {
outsidePointerCallback = onOutsidePointerEvent
}
Expand Down Expand Up @@ -213,8 +213,15 @@ internal class SwingComposeSceneLayer(

private fun onMouseEvent(event: MouseEvent) {
// TODO: Filter/consume based on [focused] flag
val dismissRequest = event.button == MouseEvent.BUTTON1 && event.id == MouseEvent.MOUSE_RELEASED
outsidePointerCallback?.invoke(dismissRequest)
if (!event.isMainAction()) {
return
}
val eventType = when (event.id) {
MouseEvent.MOUSE_PRESSED -> PointerEventType.Press
MouseEvent.MOUSE_RELEASED -> PointerEventType.Release
else -> return
}
outsidePointerCallback?.invoke(eventType)
}
}

Expand All @@ -229,3 +236,6 @@ private fun IntRect.toAwtRectangle(density: Density): Rectangle {
left, top, width, height
)
}

private fun MouseEvent.isMainAction() =
button == MouseEvent.BUTTON1
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.runtime.CompositionContext
import androidx.compose.ui.awt.toAwtColor
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.platform.PlatformWindowContext
import androidx.compose.ui.scene.skia.SkiaLayerComponent
import androidx.compose.ui.scene.skia.WindowSkiaLayerComponent
Expand Down Expand Up @@ -152,7 +153,9 @@ internal class WindowComposeSceneLayer(
)
}

override fun setOutsidePointerEventListener(onOutsidePointerEvent: ((dismissRequest: Boolean) -> Unit)?) {
override fun setOutsidePointerEventListener(
onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)?
) {
// TODO
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.compose.runtime.rememberCompositionContext
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
Expand Down Expand Up @@ -128,13 +129,11 @@ interface ComposeSceneLayer {
* the [boundsInWindow] should be entirely handled by this layer, without activating this event.
*
* @param onOutsidePointerEvent The callback function that is invoked when a pointer event
* occurs outside. It accepts a boolean parameter to denote if the event is intended to close
* this layer. When the parameter is true, it typically signifies that it's the primary (left)
* mouse button or single pointer that executed a full click (press and release) outside
* of [boundsInWindow], and false in all other cases.
* occurs outside. It's called only on the primary (left) mouse button or single pointer
* gesture that started outside of [boundsInWindow].
*/
fun setOutsidePointerEventListener(
onOutsidePointerEvent: ((dismissRequest: Boolean) -> Unit)? = null,
onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)? = null,
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ private class MultiLayerComposeSceneImpl(
inputHandler = inputHandler,
)
private var composition: Composition? = null
private var outsidePointerCallback: ((Boolean) -> Unit)? = null
private var outsidePointerCallback: ((eventType: PointerEventType) -> Unit)? = null
private var isClosed = false

override var density: Density by owner::density
Expand Down Expand Up @@ -571,7 +571,7 @@ private class MultiLayerComposeSceneImpl(
}

override fun setOutsidePointerEventListener(
onOutsidePointerEvent: ((Boolean) -> Unit)?,
onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)?,
) {
outsidePointerCallback = onOutsidePointerEvent
}
Expand All @@ -597,7 +597,10 @@ private class MultiLayerComposeSceneImpl(
}

fun onOutsidePointerEvent(event: PointerInputEvent) {
outsidePointerCallback?.invoke(event.isDismissRequest())
if (!event.isMainAction()) {
return
}
outsidePointerCallback?.invoke(event.eventType)
}
}
}
Expand All @@ -608,9 +611,6 @@ private fun PointerInputEvent.isMainAction() =
button == PointerButton.Primary ||
button == null && pointers.size == 1

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

private class CopiedList<T>(
private val populate: (MutableList<T>) -> Unit
) : MutableList<T> by mutableListOf() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ 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.type
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.InsetsConfig
import androidx.compose.ui.platform.LocalWindowInfo
Expand Down Expand Up @@ -146,8 +147,8 @@ actual fun Dialog(
null
}
val onOutsidePointerEvent = if (properties.dismissOnClickOutside) {
{ isDismissRequest: Boolean ->
if (isDismissRequest) {
{ eventType: PointerEventType ->
if (eventType == PointerEventType.Release) {
onDismissRequest()
}
}
Expand All @@ -169,7 +170,7 @@ private fun DialogLayout(
modifier: Modifier = Modifier,
onPreviewKeyEvent: ((KeyEvent) -> Boolean)? = null,
onKeyEvent: ((KeyEvent) -> Boolean)? = null,
onOutsidePointerEvent: ((Boolean) -> Unit)? = null,
onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)? = null,
content: @Composable () -> Unit
) {
val platformInsets = properties.insetsConfig.safeInsets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ 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.type
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow
Expand Down Expand Up @@ -403,7 +404,11 @@ fun Popup(
onKeyEvent
}
val onOutsidePointerEvent = if (properties.dismissOnClickOutside && onDismissRequest != null) {
{ _: Boolean -> onDismissRequest() }
{ eventType: PointerEventType ->
if (eventType == PointerEventType.Press) {
onDismissRequest()
}
}
} else {
null
}
Expand All @@ -425,7 +430,7 @@ private fun PopupLayout(
modifier: Modifier,
onPreviewKeyEvent: ((KeyEvent) -> Boolean)? = null,
onKeyEvent: ((KeyEvent) -> Boolean)? = null,
onOutsidePointerEvent: ((Boolean) -> Unit)? = null,
onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)? = null,
content: @Composable () -> Unit
) {
val platformInsets = properties.insetsConfig.safeInsets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.runtime.CompositionContext
import androidx.compose.runtime.CompositionLocalContext
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.platform.PlatformWindowContext
import androidx.compose.ui.uikit.ComposeUIViewControllerConfiguration
Expand Down Expand Up @@ -56,7 +57,7 @@ internal class UIViewComposeSceneLayer(
) : ComposeSceneLayer {

override var focusable: Boolean = focusStack != null
private var onOutsidePointerEvent: ((dismissRequest: Boolean) -> Unit)? = null
private var onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)? = null
private val rootView = composeContainer.view.window ?: composeContainer.view
private val backgroundView: UIView = object : UIView(
frame = CGRectZero.readValue()
Expand All @@ -73,7 +74,8 @@ internal class UIViewComposeSceneLayer(
//TODO pass invoke(true) on touch up event only when this touch event begins outside of layer bounds.
// Also it should be only one touch event (not multitouch with 2 and more touches).
// In other cases pass invoke(false)
onOutsidePointerEvent?.invoke(true)
onOutsidePointerEvent?.invoke(PointerEventType.Press)
onOutsidePointerEvent?.invoke(PointerEventType.Release)
return focusable
}
}
Expand Down Expand Up @@ -154,7 +156,7 @@ internal class UIViewComposeSceneLayer(
}

override fun setOutsidePointerEventListener(
onOutsidePointerEvent: ((dismissRequest: Boolean) -> Unit)?
onOutsidePointerEvent: ((eventType: PointerEventType) -> Unit)?
) {
this.onOutsidePointerEvent = onOutsidePointerEvent
}
Expand Down

0 comments on commit 64c8cff

Please sign in to comment.