11package com.jetpackduba.gitnuro.ui.context_menu
22
33import androidx.compose.foundation.*
4+ import androidx.compose.foundation.gestures.awaitEachGesture
45import androidx.compose.foundation.layout.*
56import androidx.compose.foundation.text.TextContextMenu
67import androidx.compose.foundation.text.selection.DisableSelection
@@ -23,15 +24,13 @@ import androidx.compose.ui.graphics.painter.Painter
2324import androidx.compose.ui.input.InputMode
2425import androidx.compose.ui.input.InputModeManager
2526import androidx.compose.ui.input.key.*
26- import androidx.compose.ui.input.pointer.PointerIcon
27- import androidx.compose.ui.input.pointer.isSecondary
28- import androidx.compose.ui.input.pointer.pointerHoverIcon
29- import androidx.compose.ui.input.pointer.pointerInput
27+ import androidx.compose.ui.input.pointer.*
3028import androidx.compose.ui.layout.onGloballyPositioned
3129import androidx.compose.ui.platform.LocalFocusManager
3230import androidx.compose.ui.platform.LocalInputModeManager
3331import androidx.compose.ui.platform.LocalLocalization
3432import androidx.compose.ui.unit.*
33+ import androidx.compose.ui.util.fastAll
3534import androidx.compose.ui.window.Popup
3635import androidx.compose.ui.window.PopupPositionProvider
3736import androidx.compose.ui.window.PopupProperties
@@ -76,44 +75,43 @@ fun DropDownMenu(showIcons: Boolean = true, items: () -> List<ContextMenuElement
7675@OptIn(ExperimentalComposeUiApi ::class )
7776@Composable
7877private fun Modifier.contextMenu (enabled : Boolean , items : () -> List <ContextMenuElement >): Modifier {
79- val (mouseEvent, setMouseEvent) = remember { mutableStateOf<MouseEvent ?>(null ) }
80-
81- val modifier = this .pointerInput(enabled) {
82- awaitPointerEventScope {
83- while (true ) {
84- val lastPointerEvent = awaitFirstDownEvent()
85- val lastMouseEvent = lastPointerEvent.awtEventOrNull
86-
87- if (lastMouseEvent != null && enabled) {
88- if (lastPointerEvent.button.isSecondary) {
89- lastPointerEvent.changes.forEach {
90- it.consume()
91- }
78+ val (mouseOffset, setMouseOffset) = remember { mutableStateOf<Offset ?>(null ) }
79+ val (elementPosition, setElementPosition) = remember { mutableStateOf<Offset ?>(null ) }
80+
81+ val modifier = this
82+ .onGloballyPositioned { layoutCoordinates ->
83+ val offsetToRoot = layoutCoordinates.localToRoot(Offset .Zero )
84+ setElementPosition(offsetToRoot)
85+ }
86+ .pointerInput(items, enabled) {
87+ awaitEachGesture {
88+ val event = awaitEventFirstDown()
9289
93- val currentCheck = System .currentTimeMillis()
94- if (lastCheck != 0L && currentCheck - lastCheck < MIN_TIME_BETWEEN_POPUPS_IN_MS ) {
95- println (" Popup ignored!" )
96- } else {
97- lastCheck = currentCheck
90+ if (enabled && event.buttons.isSecondaryPressed) {
91+ println (" Secondary is pressed" )
92+ val currentCheck = System .currentTimeMillis()
9893
99- setMouseEvent(lastMouseEvent)
100- }
94+ if (lastCheck != 0L && currentCheck - lastCheck < MIN_TIME_BETWEEN_POPUPS_IN_MS ) {
95+ println (" Popup ignored!" )
96+ } else {
97+ lastCheck = currentCheck
98+ event.changes.forEach { it.consume() }
99+ setMouseOffset(event.changes[0 ].position)
101100 }
102101 }
103102 }
104103 }
105- }
106104
107- if (mouseEvent != null ) {
105+ if (mouseOffset != null && elementPosition != null ) {
108106 val contextMenuElements = items()
109107 if (contextMenuElements.isNotEmpty()) {
110108 DisableSelection {
111109 showPopup(
112110 showIcons = true ,
113- x = mouseEvent.x ,
114- y = mouseEvent.y ,
111+ x = (elementPosition.x + mouseOffset.x).toInt() ,
112+ y = (elementPosition.y + mouseOffset.y).toInt() ,
115113 contextMenuElements = contextMenuElements,
116- onDismissRequest = { setMouseEvent (null ) },
114+ onDismissRequest = { setMouseOffset (null ) },
117115 )
118116 }
119117 }
@@ -122,6 +120,17 @@ private fun Modifier.contextMenu(enabled: Boolean, items: () -> List<ContextMenu
122120 return modifier
123121}
124122
123+ private suspend fun AwaitPointerEventScope.awaitEventFirstDown (): PointerEvent {
124+ var event: PointerEvent
125+ do {
126+ event = awaitPointerEvent()
127+ } while (
128+ ! event.changes.fastAll { it.changedToDown() }
129+ )
130+ return event
131+ }
132+
133+
125134@Composable
126135private fun Modifier.dropdownMenu (showIcons : Boolean , items : () -> List <ContextMenuElement >): Modifier {
127136 val (isClicked, setIsClicked) = remember { mutableStateOf(false ) }
@@ -268,7 +277,7 @@ fun TextEntry(
268277 contextTextEntry.onClick()
269278 }
270279 .pointerHoverIcon(PointerIcon .Default )
271- .padding(horizontal = if (showIcons) 8 .dp else 12 .dp, vertical = 8 .dp),
280+ .padding(horizontal = if (showIcons) 8 .dp else 12 .dp, vertical = 8 .dp),
272281 verticalAlignment = Alignment .CenterVertically ,
273282 ) {
274283 if (showIcons) {
0 commit comments