Merge "Reorganize selection toolbar" into androidx-master-dev
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
index 036f2c4..c338a45 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
@@ -25,6 +25,7 @@
import androidx.compose.ui.selection.Selection
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.InternalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextDelegate
@@ -60,7 +61,10 @@
style = FontStyle.Normal
)
-@OptIn(InternalTextApi::class)
+@OptIn(
+ InternalTextApi::class,
+ ExperimentalTextApi::class
+)
@RunWith(AndroidJUnit4::class)
@MediumTest
class MultiWidgetSelectionDelegateTest {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index a39fc33..8638678 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -52,6 +52,7 @@
import androidx.compose.ui.semantics.getTextLayoutResult
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.InternalTextApi
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.TextDelegate
@@ -96,6 +97,7 @@
*/
@Composable
@InternalTextApi
+@OptIn(ExperimentalTextApi::class)
fun CoreText(
text: AnnotatedString,
modifier: Modifier = Modifier,
@@ -194,7 +196,10 @@
}
}
-@OptIn(InternalTextApi::class)
+@OptIn(
+ InternalTextApi::class,
+ ExperimentalTextApi::class
+)
private class TextController(val state: TextState) {
var selectionRegistrar: SelectionRegistrar? = null
@@ -225,7 +230,7 @@
if (state.selectionRange != null) {
val newGlobalPosition = it.globalPosition
if (newGlobalPosition != state.previousGlobalPosition) {
- selectionRegistrar.onPositionChange()
+ selectionRegistrar.notifyPositionChange()
}
state.previousGlobalPosition = newGlobalPosition
}
@@ -353,7 +358,7 @@
var layoutCoordinates: LayoutCoordinates? = null
/** The latest TextLayoutResult calculated in the measure block */
var layoutResult: TextLayoutResult? = null
- /** The global position calculated during the last onPositioned callback */
+ /** The global position calculated during the last notifyPosition callback */
var previousGlobalPosition: Offset = Offset.Zero
/** The paint used to draw highlight background for selected text. */
val selectionPaint: Paint = Paint()
@@ -433,7 +438,10 @@
return Pair(placeholders, inlineComposables)
}
-@OptIn(InternalTextApi::class)
+@OptIn(
+ InternalTextApi::class,
+ ExperimentalTextApi::class
+)
@VisibleForTesting
internal fun longPressDragObserver(
state: TextState,
@@ -455,10 +463,9 @@
state.layoutCoordinates?.let {
if (!it.isAttached) return
- selectionRegistrar?.onUpdateSelection(
+ selectionRegistrar?.notifySelectionUpdateStart(
layoutCoordinates = it,
- startPosition = pxPosition,
- endPosition = pxPosition
+ startPosition = pxPosition
)
dragBeginPosition = pxPosition
@@ -466,7 +473,6 @@
}
override fun onDragStart() {
- super.onDragStart()
// selection never started
if (state.selectionRange == null) return
// Zero out the total distance that being dragged.
@@ -481,7 +487,7 @@
dragTotalDistance += dragDistance
- selectionRegistrar?.onUpdateSelection(
+ selectionRegistrar?.notifySelectionUpdate(
layoutCoordinates = it,
startPosition = dragBeginPosition,
endPosition = dragBeginPosition + dragTotalDistance
@@ -489,5 +495,13 @@
}
return dragDistance
}
+
+ override fun onStop(velocity: Offset) {
+ selectionRegistrar?.notifySelectionUpdateEnd()
+ }
+
+ override fun onCancel() {
+ selectionRegistrar?.notifySelectionUpdateEnd()
+ }
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
index eaa5cc2..f908eb0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
@@ -22,10 +22,12 @@
import androidx.compose.ui.selection.Selectable
import androidx.compose.ui.selection.Selection
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextRange
import kotlin.math.max
+@OptIn(ExperimentalTextApi::class)
internal class MultiWidgetSelectionDelegate(
private val selectionRangeUpdate: (TextRange?) -> Unit,
private val coordinatesCallback: () -> LayoutCoordinates?,
@@ -123,6 +125,7 @@
*
* @return [Selection] of the current composable, or null if the composable is not selected.
*/
+@OptIn(ExperimentalTextApi::class)
internal fun getTextSelectionInfo(
textLayoutResult: TextLayoutResult,
selectionCoordinates: Pair<Offset, Offset>,
@@ -206,6 +209,7 @@
*
* @return [Selection] of the current composable, or null if the composable is not selected.
*/
+@OptIn(ExperimentalTextApi::class)
private fun getRefinedSelectionInfo(
rawStartOffset: Int,
rawEndOffset: Int,
@@ -282,6 +286,7 @@
*
* @return an assembled object of [Selection] using the offered selection info.
*/
+@OptIn(ExperimentalTextApi::class)
private fun getAssembledSelectionInfo(
startOffset: Int,
endOffset: Int,
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
index 9ea31ce..cd6e838 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
@@ -22,6 +22,7 @@
import androidx.compose.ui.selection.Selectable
import androidx.compose.ui.selection.Selection
import androidx.compose.ui.selection.SelectionRegistrar
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.InternalTextApi
import androidx.compose.ui.text.TextDelegate
import androidx.compose.ui.text.style.ResolvedTextDirection
@@ -36,7 +37,10 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-@OptIn(InternalTextApi::class)
+@OptIn(
+ InternalTextApi::class,
+ ExperimentalTextApi::class
+)
class TextSelectionLongPressDragTest {
private val selectionRegistrar = mock<SelectionRegistrar>()
private val selectable = mock<Selectable>()
@@ -93,15 +97,14 @@
}
@Test
- fun longPressDragObserver_onLongPress_calls_getSelection_change_selection() {
+ fun longPressDragObserver_onLongPress_calls_notifySelectionInitiated() {
val position = Offset(100f, 100f)
gesture.onLongPress(position)
- verify(selectionRegistrar, times(1)).onUpdateSelection(
+ verify(selectionRegistrar, times(1)).notifySelectionUpdateStart(
layoutCoordinates = layoutCoordinates,
- startPosition = position,
- endPosition = position
+ startPosition = position
)
}
@@ -127,7 +130,7 @@
// Verify.
verify(selectionRegistrar, times(1))
- .onUpdateSelection(
+ .notifySelectionUpdate(
layoutCoordinates = layoutCoordinates,
startPosition = beginPosition2,
endPosition = beginPosition2 + dragDistance2
@@ -135,7 +138,7 @@
}
@Test
- fun longPressDragObserver_onDrag_calls_getSelection_change_selection() {
+ fun longPressDragObserver_onDrag_calls_notifySelectionDrag() {
val dragDistance = Offset(15f, 10f)
val beginPosition = Offset(30f, 20f)
gesture.onLongPress(beginPosition)
@@ -146,10 +149,35 @@
assertThat(result).isEqualTo(dragDistance)
verify(selectionRegistrar, times(1))
- .onUpdateSelection(
+ .notifySelectionUpdate(
layoutCoordinates = layoutCoordinates,
startPosition = beginPosition,
endPosition = beginPosition + dragDistance
)
}
+
+ @Test
+ fun longPressDragObserver_onStop_calls_notifySelectionEnd() {
+ val dragDistance = Offset(15f, 10f)
+ val beginPosition = Offset(30f, 20f)
+ gesture.onLongPress(beginPosition)
+ state.selectionRange = fakeInitialSelection.toTextRange()
+ gesture.onDragStart()
+ gesture.onStop(dragDistance)
+
+ verify(selectionRegistrar, times(1))
+ .notifySelectionUpdateEnd()
+ }
+
+ @Test
+ fun longPressDragObserver_onCancel_calls_notifySelectionEnd() {
+ val beginPosition = Offset(30f, 20f)
+ gesture.onLongPress(beginPosition)
+ state.selectionRange = fakeInitialSelection.toTextRange()
+ gesture.onDragStart()
+ gesture.onCancel()
+
+ verify(selectionRegistrar, times(1))
+ .notifySelectionUpdateEnd()
+ }
}
\ No newline at end of file
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 2b05879..ccac4f1 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2426,7 +2426,7 @@
package androidx.compose.ui.selection {
- public interface Selectable {
+ @androidx.compose.ui.text.ExperimentalTextApi public interface Selectable {
method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
method public long getHandlePosition-F1C5BW0(androidx.compose.ui.selection.Selection selection, boolean isStartHandle);
method public androidx.compose.ui.layout.LayoutCoordinates? getLayoutCoordinates();
@@ -2476,9 +2476,11 @@
public final class SelectionManagerKt {
}
- public interface SelectionRegistrar {
- method public void onPositionChange();
- method public void onUpdateSelection-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+ @androidx.compose.ui.text.ExperimentalTextApi public interface SelectionRegistrar {
+ method public void notifyPositionChange();
+ method public void notifySelectionUpdate-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+ method public void notifySelectionUpdateEnd();
+ method public void notifySelectionUpdateStart-YJiYy8w(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition);
method public androidx.compose.ui.selection.Selectable subscribe(androidx.compose.ui.selection.Selectable selectable);
method public void unsubscribe(androidx.compose.ui.selection.Selectable selectable);
}
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 2b05879..ccac4f1 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2426,7 +2426,7 @@
package androidx.compose.ui.selection {
- public interface Selectable {
+ @androidx.compose.ui.text.ExperimentalTextApi public interface Selectable {
method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
method public long getHandlePosition-F1C5BW0(androidx.compose.ui.selection.Selection selection, boolean isStartHandle);
method public androidx.compose.ui.layout.LayoutCoordinates? getLayoutCoordinates();
@@ -2476,9 +2476,11 @@
public final class SelectionManagerKt {
}
- public interface SelectionRegistrar {
- method public void onPositionChange();
- method public void onUpdateSelection-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+ @androidx.compose.ui.text.ExperimentalTextApi public interface SelectionRegistrar {
+ method public void notifyPositionChange();
+ method public void notifySelectionUpdate-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+ method public void notifySelectionUpdateEnd();
+ method public void notifySelectionUpdateStart-YJiYy8w(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition);
method public androidx.compose.ui.selection.Selectable subscribe(androidx.compose.ui.selection.Selectable selectable);
method public void unsubscribe(androidx.compose.ui.selection.Selectable selectable);
}
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 9061804..74fba3b 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2488,7 +2488,7 @@
package androidx.compose.ui.selection {
- public interface Selectable {
+ @androidx.compose.ui.text.ExperimentalTextApi public interface Selectable {
method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
method public long getHandlePosition-F1C5BW0(androidx.compose.ui.selection.Selection selection, boolean isStartHandle);
method public androidx.compose.ui.layout.LayoutCoordinates? getLayoutCoordinates();
@@ -2538,9 +2538,11 @@
public final class SelectionManagerKt {
}
- public interface SelectionRegistrar {
- method public void onPositionChange();
- method public void onUpdateSelection-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+ @androidx.compose.ui.text.ExperimentalTextApi public interface SelectionRegistrar {
+ method public void notifyPositionChange();
+ method public void notifySelectionUpdate-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+ method public void notifySelectionUpdateEnd();
+ method public void notifySelectionUpdateStart-YJiYy8w(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition);
method public androidx.compose.ui.selection.Selectable subscribe(androidx.compose.ui.selection.Selectable selectable);
method public void unsubscribe(androidx.compose.ui.selection.Selectable selectable);
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt
index 26d168e..b98c168 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt
@@ -20,11 +20,13 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
/**
* Provides [Selection] information for a composable to SelectionContainer. Composables who can
* be selected should subscribe to [SelectionRegistrar] using this interface.
*/
+@ExperimentalTextApi
interface Selectable {
/**
* Returns [Selection] information for a selectable composable. If no selection can be provided
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt
index 714ac17..bb80ac9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt
@@ -17,6 +17,7 @@
package androidx.compose.ui.selection
import androidx.compose.runtime.Immutable
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.style.ResolvedTextDirection
@@ -24,6 +25,7 @@
* Information about the current Selection.
*/
@Immutable
+@OptIn(ExperimentalTextApi::class)
data class Selection(
/**
* Information about the start of the selection.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt
index 3e4b606..be22667 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt
@@ -135,17 +135,12 @@
handle = null
)
}
- SelectionFloatingToolBar(manager = manager)
}
}
}
onDispose {
manager.selection = null
+ manager.hideSelectionToolbar()
}
}
-
-@Composable
-private fun SelectionFloatingToolBar(manager: SelectionManager) {
- manager.showSelectionToolbar()
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt
index b6cbf8e..ce33357 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt
@@ -31,6 +31,7 @@
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.platform.TextToolbarStatus
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.InternalTextApi
import androidx.compose.ui.text.length
import androidx.compose.ui.text.subSequence
@@ -40,7 +41,10 @@
/**
* A bridge class between user interaction to the text composables for text selection.
*/
-@OptIn(InternalTextApi::class)
+@OptIn(
+ InternalTextApi::class,
+ ExperimentalTextApi::class
+)
internal class SelectionManager(private val selectionRegistrar: SelectionRegistrarImpl) {
/**
* The current selection.
@@ -49,7 +53,6 @@
set(value) {
field = value
updateHandleOffsets()
- hideSelectionToolbar()
}
/**
@@ -123,9 +126,20 @@
init {
selectionRegistrar.onPositionChangeCallback = {
updateHandleOffsets()
+ updateSelectionToolbarPosition()
+ }
+
+ selectionRegistrar.onSelectionUpdateStartCallback = { layoutCoordinates, startPosition ->
+ updateSelection(
+ startPosition = convertToContainerCoordinates(layoutCoordinates, startPosition),
+ endPosition = convertToContainerCoordinates(layoutCoordinates, startPosition),
+ isStartHandle = true,
+ longPress = true
+ )
hideSelectionToolbar()
}
- selectionRegistrar.onUpdateSelectionCallback =
+
+ selectionRegistrar.onSelectionUpdateCallback =
{ layoutCoordinates, startPosition, endPosition ->
updateSelection(
startPosition = convertToContainerCoordinates(layoutCoordinates, startPosition),
@@ -134,6 +148,10 @@
longPress = true
)
}
+
+ selectionRegistrar.onSelectionUpdateEndCallback = {
+ showSelectionToolbar()
+ }
}
private fun updateHandleOffsets() {
@@ -265,12 +283,9 @@
}
}
- private fun hideSelectionToolbar() {
+ internal fun hideSelectionToolbar() {
if (textToolbar?.status == TextToolbarStatus.Shown) {
- val selection = selection
- if (selection == null) {
- textToolbar?.hide()
- }
+ textToolbar?.hide()
}
}
@@ -356,12 +371,14 @@
endPosition = Offset(-1f, -1f),
previousSelection = selection
)
+ hideSelectionToolbar()
if (selection != null) onSelectionChange(null)
}
fun handleDragObserver(isStartHandle: Boolean): DragObserver {
return object : DragObserver {
override fun onStart(downPosition: Offset) {
+ hideSelectionToolbar()
val selection = selection!!
// The LayoutCoordinates of the composable where the drag gesture should begin. This
// is used to convert the position of the beginning of the drag gesture from the
@@ -375,13 +392,15 @@
// The position of the character where the drag gesture should begin. This is in
// the composable coordinates.
val beginCoordinates = getAdjustedCoordinates(
- if (isStartHandle)
+ if (isStartHandle) {
selection.start.selectable.getHandlePosition(
selection = selection, isStartHandle = true
- ) else
+ )
+ } else {
selection.end.selectable.getHandlePosition(
selection = selection, isStartHandle = false
)
+ }
)
// Convert the position where drag gesture begins from composable coordinates to
@@ -434,6 +453,14 @@
)
return dragDistance
}
+
+ override fun onStop(velocity: Offset) {
+ showSelectionToolbar()
+ }
+
+ override fun onCancel() {
+ showSelectionToolbar()
+ }
}
}
@@ -468,6 +495,7 @@
return lhs?.merge(rhs) ?: rhs
}
+@OptIn(ExperimentalTextApi::class)
internal fun getCurrentSelectedText(
selectable: Selectable,
selection: Selection
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt
index 3dbff76..7e1cded 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt
@@ -19,10 +19,12 @@
import androidx.compose.runtime.ambientOf
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
/**
* An interface allowing a composable to subscribe and unsubscribe to selection changes.
*/
+@ExperimentalTextApi
interface SelectionRegistrar {
/**
* Subscribe to SelectionContainer selection changes.
@@ -38,20 +40,58 @@
* When the Global Position of a subscribed [Selectable] changes, this method
* is called.
*/
- fun onPositionChange()
+ fun notifyPositionChange()
/**
- * When selection changes, this method is called.
+ * Call this method to notify the [SelectionContainer] that the selection has been initiated.
+ * Depends on the input, [notifySelectionUpdate] may be called repeatedly after
+ * [notifySelectionUpdateStart] is called. And [notifySelectionUpdateEnd] should always be
+ * called after selection finished.
+ * For example:
+ * 1. User long pressed the text and then release. [notifySelectionUpdateStart] should be
+ * called followed by [notifySelectionUpdateEnd] being called once.
+ * 2. User long pressed the text and then drag a distance and then release.
+ * [notifySelectionUpdateStart] should be called first after the user long press, and then
+ * [notifySelectionUpdate] is called several times reporting the updates, in the end
+ * [notifySelectionUpdateEnd] is called to finish the selection.
+ *
+ * @param layoutCoordinates [LayoutCoordinates] of the [Selectable].
+ * @param startPosition coordinates of where the selection is initiated.
+ *
+ * @see notifySelectionUpdate
+ * @see notifySelectionUpdateEnd
+ */
+ fun notifySelectionUpdateStart(
+ layoutCoordinates: LayoutCoordinates,
+ startPosition: Offset
+ )
+
+ /**
+ * Call this method to notify the [SelectionContainer] that the selection has been updated.
+ * The caller of this method should make sure that [notifySelectionUpdateStart] is always
+ * called once before calling this function. And [notifySelectionUpdateEnd] is always called
+ * once after the all updates finished.
*
* @param layoutCoordinates [LayoutCoordinates] of the [Selectable].
* @param startPosition coordinates of where the selection starts.
* @param endPosition coordinates of where the selection ends.
+ *
+ * @see notifySelectionUpdateStart
+ * @see notifySelectionUpdateEnd
*/
- fun onUpdateSelection(
+ fun notifySelectionUpdate(
layoutCoordinates: LayoutCoordinates,
startPosition: Offset,
- endPosition: Offset
+ endPosition: Offset,
)
+
+ /**
+ * Call this method to notify the [SelectionContainer] that the selection update has stopped.
+ *
+ * @see notifySelectionUpdateStart
+ * @see notifySelectionUpdate
+ */
+ fun notifySelectionUpdateEnd()
}
/**
@@ -72,4 +112,5 @@
* Ambient of SelectionRegistrar. Composables that implement selection logic can use this ambient
* to get a [SelectionRegistrar] in order to subscribe and unsubscribe to [SelectionRegistrar].
*/
+@OptIn(ExperimentalTextApi::class)
val AmbientSelectionRegistrar = ambientOf<SelectionRegistrar?>()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt
index c42a8d1..dd9250d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt
@@ -18,7 +18,9 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
+@OptIn(ExperimentalTextApi::class)
internal class SelectionRegistrarImpl : SelectionRegistrar {
/**
* A flag to check if the [Selectable]s have already been sorted.
@@ -42,6 +44,21 @@
*/
internal var onPositionChangeCallback: (() -> Unit)? = null
+ /**
+ * The callback to be invoked when the selection is initiated.
+ */
+ internal var onSelectionUpdateStartCallback: ((LayoutCoordinates, Offset) -> Unit)? = null
+
+ /**
+ * The callback to be invoked when the selection is updated.
+ */
+ internal var onSelectionUpdateCallback: ((LayoutCoordinates, Offset, Offset) -> Unit)? = null
+
+ /**
+ * The callback to be invoked when selection update finished.
+ */
+ internal var onSelectionUpdateEndCallback: (() -> Unit)? = null
+
override fun subscribe(selectable: Selectable): Selectable {
_selectables.add(selectable)
sorted = false
@@ -87,27 +104,29 @@
return selectables
}
- override fun onPositionChange() {
+ override fun notifyPositionChange() {
// Set the variable sorted to be false, when the global position of a registered
// selectable changes.
sorted = false
onPositionChangeCallback?.invoke()
}
- /**
- * The callback to be invoked when the selection change was triggered.
- */
- internal var onUpdateSelectionCallback: ((LayoutCoordinates, Offset, Offset) -> Unit)? = null
+ override fun notifySelectionUpdateStart(
+ layoutCoordinates: LayoutCoordinates,
+ startPosition: Offset
+ ) {
+ onSelectionUpdateStartCallback?.invoke(layoutCoordinates, startPosition)
+ }
- override fun onUpdateSelection(
+ override fun notifySelectionUpdate(
layoutCoordinates: LayoutCoordinates,
startPosition: Offset,
endPosition: Offset
) {
- onUpdateSelectionCallback?.invoke(
- layoutCoordinates,
- startPosition,
- endPosition
- )
+ onSelectionUpdateCallback?.invoke(layoutCoordinates, startPosition, endPosition)
+ }
+
+ override fun notifySelectionUpdateEnd() {
+ onSelectionUpdateEndCallback?.invoke()
}
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt
index 8cf64c5..ea143f0 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt
@@ -25,7 +25,9 @@
import androidx.compose.ui.selection.getCurrentSelectedText
import androidx.compose.ui.selection.merge
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
+@OptIn(ExperimentalTextApi::class)
internal class DesktopSelectionManager(private val selectionRegistrar: SelectionRegistrarImpl) {
private var dragBeginPosition = Offset.Zero
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt
index ce9ba4b4..71eae61 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt
@@ -20,8 +20,10 @@
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.selection.Selectable
import androidx.compose.ui.selection.SelectionRegistrar
+import androidx.compose.ui.text.ExperimentalTextApi
// based on androidx.compose.ui.selection.SelectionRegistrarImpl
+@OptIn(ExperimentalTextApi::class)
internal class DesktopSelectionRegistrar : SelectionRegistrar {
internal var sorted: Boolean = false
@@ -71,12 +73,23 @@
return selectables
}
- override fun onPositionChange() {
+ override fun notifyPositionChange() {
sorted = false
onPositionChangeCallback?.invoke()
}
- override fun onUpdateSelection(
+ override fun notifySelectionUpdateStart(
+ layoutCoordinates: LayoutCoordinates,
+ startPosition: Offset
+ ) {
+ onUpdateSelectionCallback?.invoke(
+ layoutCoordinates,
+ startPosition,
+ startPosition
+ )
+ }
+
+ override fun notifySelectionUpdate(
layoutCoordinates: LayoutCoordinates,
startPosition: Offset,
endPosition: Offset
@@ -87,4 +100,6 @@
endPosition
)
}
+
+ override fun notifySelectionUpdateEnd() { /* do nothing */ }
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt
index ede87ef..baa520a 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt
@@ -20,7 +20,9 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
+@OptIn(ExperimentalTextApi::class)
class MockSelectable(
var getSelectionValue: Selection? = null,
var getHandlePositionValue: Offset = Offset.Zero,
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt
index 323adf5..b066747 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt
@@ -18,6 +18,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.style.ResolvedTextDirection
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.any
@@ -32,6 +33,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+@OptIn(ExperimentalTextApi::class)
@RunWith(JUnit4::class)
class SelectionManagerDragTest {
private val selectionRegistrar = SelectionRegistrarImpl()
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt
index ff70baa..18ce38e 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt
@@ -24,6 +24,7 @@
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.length
import androidx.compose.ui.text.style.ResolvedTextDirection
import androidx.compose.ui.text.subSequence
@@ -42,6 +43,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+@OptIn(ExperimentalTextApi::class)
@RunWith(JUnit4::class)
class SelectionManagerTest {
private val selectionRegistrar = spy(SelectionRegistrarImpl())
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt
index 130db56..4bf9891a 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt
@@ -18,6 +18,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
@@ -25,6 +26,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+@OptIn(ExperimentalTextApi::class)
@RunWith(JUnit4::class)
class SelectionRegistrarImplTest {
@Test
@@ -188,7 +190,7 @@
assertThat(selectionRegistrar.sorted).isTrue()
// Act.
- selectionRegistrar.onPositionChange()
+ selectionRegistrar.notifyPositionChange()
// Assert.
assertThat(selectionRegistrar.sorted).isFalse()
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt
index 5febaab..6afcab9 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.selection
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.style.ResolvedTextDirection
import com.google.common.truth.Truth.assertThat
@@ -24,6 +25,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+@OptIn(ExperimentalTextApi::class)
@RunWith(JUnit4::class)
class SelectionTest {
@Test