@@ -25,22 +25,24 @@ import androidx.compose.foundation.text.selection.SelectionAdjustment
2525import androidx.compose.foundation.text.selection.SelectionRegistrar
2626import androidx.compose.foundation.text.selection.hasSelection
2727import androidx.compose.foundation.text.selection.isMouseOrTouchPad
28- import androidx.compose.runtime.getValue
29- import androidx.compose.runtime.rememberUpdatedState
3028import androidx.compose.ui.Modifier
31- import androidx.compose.ui.composed
3229import androidx.compose.ui.geometry.Offset
3330import androidx.compose.ui.hapticfeedback.HapticFeedback
3431import androidx.compose.ui.hapticfeedback.HapticFeedbackType
3532import androidx.compose.ui.input.pointer.AwaitPointerEventScope
3633import androidx.compose.ui.input.pointer.PointerEvent
3734import androidx.compose.ui.input.pointer.PointerEventPass
35+ import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
3836import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
3937import androidx.compose.ui.input.pointer.changedToUp
4038import androidx.compose.ui.input.pointer.isPrimaryPressed
4139import androidx.compose.ui.input.pointer.isShiftPressed
42- import androidx.compose.ui.input.pointer.pointerInput
4340import androidx.compose.ui.layout.LayoutCoordinates
41+ import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
42+ import androidx.compose.ui.node.DelegatingNode
43+ import androidx.compose.ui.node.ModifierNodeElement
44+ import androidx.compose.ui.node.currentValueOf
45+ import androidx.compose.ui.platform.InspectorInfo
4446import androidx.compose.ui.platform.LocalHapticFeedback
4547import androidx.compose.ui.util.fastAll
4648import androidx.compose.ui.util.fastForEach
@@ -228,29 +230,84 @@ internal actual fun SelectionRegistrar.makeSelectionModifier(
228230 return Modifier .selectionGestureInput(mouseSelectionObserver, longPressDragObserver)
229231}
230232
231- // Copied from SelectionController.kt, except CupertinoTextDragObserver parameter
232233private fun Modifier.selectionGestureInput (
233234 mouseSelectionObserver : MouseSelectionObserver ,
234235 textDragObserver : CupertinoTextDragObserver ,
235- ): Modifier = composed {
236- // TODO(https://youtrack.jetbrains.com/issue/COMPOSE-79) how we can rewrite this without `composed`?
237- val currentMouseSelectionObserver by rememberUpdatedState(mouseSelectionObserver)
238- val currentTextDragObserver by rememberUpdatedState(textDragObserver)
239- val hapticFeedback = LocalHapticFeedback .current
240- this .pointerInput(Unit ) {
241- val clicksCounter = ClicksCounter (viewConfiguration)
242- awaitEachGesture {
243- val down = awaitDown()
244- if (
245- down.isMouseOrTouchPad() &&
246- down.buttons.isPrimaryPressed &&
247- down.changes.fastAll { ! it.isConsumed }
248- ) {
249- mouseSelection(currentMouseSelectionObserver, clicksCounter, down)
250- } else if (! down.isMouseOrTouchPad()) {
251- touchSelection(currentTextDragObserver, clicksCounter, down, hapticFeedback)
236+ ): Modifier = this .then(
237+ SelectionGestureInputElement (
238+ mouseSelectionObserver = mouseSelectionObserver,
239+ textDragObserver = textDragObserver,
240+ )
241+ )
242+
243+ private class SelectionGestureInputElement (
244+ private val mouseSelectionObserver : MouseSelectionObserver ,
245+ private val textDragObserver : CupertinoTextDragObserver ,
246+ ) : ModifierNodeElement<SelectionGestureInputNode>() {
247+
248+ override fun create (): SelectionGestureInputNode = SelectionGestureInputNode (
249+ mouseSelectionObserver = mouseSelectionObserver,
250+ textDragObserver = textDragObserver,
251+ )
252+
253+ override fun update (node : SelectionGestureInputNode ) = node.update(
254+ mouseSelectionObserver = mouseSelectionObserver,
255+ textDragObserver = textDragObserver,
256+ )
257+
258+ override fun InspectorInfo.inspectableProperties () {
259+ name = " selectionGestureInput"
260+ properties[" mouseSelectionObserver" ] = mouseSelectionObserver
261+ properties[" textDragObserver" ] = textDragObserver
262+ }
263+
264+ override fun equals (other : Any? ): Boolean =
265+ other is SelectionGestureInputElement &&
266+ other != = this &&
267+ mouseSelectionObserver == other.mouseSelectionObserver &&
268+ textDragObserver == other.textDragObserver
269+
270+ override fun hashCode (): Int {
271+ var result = mouseSelectionObserver.hashCode()
272+ result = 31 * result + textDragObserver.hashCode()
273+ return result
274+ }
275+ }
276+
277+ private class SelectionGestureInputNode (
278+ private var mouseSelectionObserver : MouseSelectionObserver ,
279+ private var textDragObserver : CupertinoTextDragObserver ,
280+ ): DelegatingNode(), CompositionLocalConsumerModifierNode {
281+
282+ private val pointerInputNode = delegate(
283+ SuspendingPointerInputModifierNode (
284+ pointerInputEventHandler = {
285+ val hapticFeedback = currentValueOf(LocalHapticFeedback )
286+ val clicksCounter = ClicksCounter (viewConfiguration)
287+
288+ awaitEachGesture {
289+ val down = awaitDown()
290+
291+ if (
292+ down.isMouseOrTouchPad() &&
293+ down.buttons.isPrimaryPressed &&
294+ down.changes.fastAll { ! it.isConsumed }
295+ ) {
296+ mouseSelection(mouseSelectionObserver, clicksCounter, down)
297+ } else if (! down.isMouseOrTouchPad()) {
298+ touchSelection(textDragObserver, clicksCounter, down, hapticFeedback)
299+ }
300+ }
252301 }
253- }
302+ )
303+ )
304+
305+ fun update (
306+ mouseSelectionObserver : MouseSelectionObserver ,
307+ textDragObserver : CupertinoTextDragObserver ,
308+ ) {
309+ this .mouseSelectionObserver = mouseSelectionObserver
310+ this .textDragObserver = textDragObserver
254311 }
255312}
256313
0 commit comments