Stabilize BasicTextField(state)
All surface level APIs related to BasicTextField2 are also stabilized including InputTransformation, OutputTransformation, Decorator, TextFieldLineLimits. However, some of the inner properties and methods remains Experimental due to low level of confidence at this time.
Test: ./gradlew :compose:foundation:foundation:cAT
Relnote: "`BasicTextField(state)`, `TextFieldState`, `InputTransformation`, `OutputTransformation`, `TextFieldLineLimits`, `TextFieldDecorator` are graduated to stable."
Change-Id: I9582b7fab87b79a08f617122ed7bd1d2c5b61b9a
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 1c5e44e..3c97fd7 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -1526,11 +1526,8 @@
method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicSecureTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.text.input.ImeActionHandler? onSubmit, optional int imeAction, optional int textObfuscationMode, optional int keyboardType, optional boolean enabled, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.text.input.TextFieldDecorator? decorator, optional androidx.compose.foundation.ScrollState scrollState);
}
- public final class BasicTextField2Kt {
- method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.input.TextFieldDecorator? decorator, optional androidx.compose.foundation.ScrollState scrollState);
- }
-
public final class BasicTextFieldKt {
+ method @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.input.TextFieldDecorator? decorator, optional androidx.compose.foundation.ScrollState scrollState);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
method @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
@@ -1641,11 +1638,11 @@
method public boolean onImeAction(int action);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface InputTransformation {
- method public default void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ @androidx.compose.runtime.Stable public fun interface InputTransformation {
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public default void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public default androidx.compose.foundation.text.KeyboardOptions? getKeyboardOptions();
method public void transformInput(androidx.compose.foundation.text.input.TextFieldCharSequence originalValue, androidx.compose.foundation.text.input.TextFieldBuffer valueWithChanges);
- property public default androidx.compose.foundation.text.KeyboardOptions? keyboardOptions;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public default androidx.compose.foundation.text.KeyboardOptions? keyboardOptions;
field public static final androidx.compose.foundation.text.input.InputTransformation.Companion Companion;
}
@@ -1662,36 +1659,36 @@
method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public static androidx.compose.foundation.text.input.InputTransformation? thenOrNull(androidx.compose.foundation.text.input.InputTransformation?, androidx.compose.foundation.text.input.InputTransformation? next);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface OutputTransformation {
- method public void transformOutput(androidx.compose.foundation.text.input.TextFieldBuffer);
+ @androidx.compose.runtime.Stable public fun interface OutputTransformation {
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void transformOutput(androidx.compose.foundation.text.input.TextFieldBuffer);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class TextFieldBuffer implements java.lang.Appendable {
+ public final class TextFieldBuffer implements java.lang.Appendable {
method public Appendable append(char char);
method public Appendable append(CharSequence? text);
method public Appendable append(CharSequence? text, int start, int end);
- method public CharSequence asCharSequence();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public CharSequence asCharSequence();
method public char charAt(int index);
method public androidx.compose.foundation.text.input.TextFieldBuffer.ChangeList getChanges();
method public int getCodepointLength();
method public int getLength();
- method public long getSelectionInChars();
- method public long getSelectionInCodepoints();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public long getSelectionInChars();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public long getSelectionInCodepoints();
method public boolean hasSelection();
- method public void placeCursorAfterCharAt(int index);
- method public void placeCursorAfterCodepointAt(int index);
- method public void placeCursorBeforeCharAt(int index);
- method public void placeCursorBeforeCodepointAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorAfterCharAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorAfterCodepointAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorBeforeCharAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorBeforeCodepointAt(int index);
method public void replace(int start, int end, CharSequence text);
- method public void revertAllChanges();
- method public void selectCharsIn(long range);
- method public void selectCodepointsIn(long range);
- property public final androidx.compose.foundation.text.input.TextFieldBuffer.ChangeList changes;
- property public final int codepointLength;
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void revertAllChanges();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void selectCharsIn(long range);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void selectCodepointsIn(long range);
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final androidx.compose.foundation.text.input.TextFieldBuffer.ChangeList changes;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final int codepointLength;
property public final boolean hasSelection;
property public final int length;
- property public final long selectionInChars;
- property public final long selectionInCodepoints;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final long selectionInChars;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final long selectionInCodepoints;
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static interface TextFieldBuffer.ChangeList {
@@ -1710,7 +1707,7 @@
method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static void selectAll(androidx.compose.foundation.text.input.TextFieldBuffer);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface TextFieldCharSequence extends java.lang.CharSequence {
+ public sealed interface TextFieldCharSequence extends java.lang.CharSequence {
method public boolean contentEquals(CharSequence other);
method public boolean equals(Object? other);
method public androidx.compose.ui.text.TextRange? getCompositionInChars();
@@ -1721,14 +1718,14 @@
}
public final class TextFieldCharSequenceKt {
- method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.text.input.TextFieldCharSequence TextFieldCharSequence(optional String text, optional long selection);
+ method public static androidx.compose.foundation.text.input.TextFieldCharSequence TextFieldCharSequence(optional String text, optional long selection);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public fun interface TextFieldDecorator {
+ public fun interface TextFieldDecorator {
method @androidx.compose.runtime.Composable public void Decoration(kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public sealed interface TextFieldLineLimits {
+ @androidx.compose.runtime.Stable public sealed interface TextFieldLineLimits {
field public static final androidx.compose.foundation.text.input.TextFieldLineLimits.Companion Companion;
}
@@ -1749,7 +1746,7 @@
field public static final androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine INSTANCE;
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class TextFieldState {
+ @androidx.compose.runtime.Stable public final class TextFieldState {
ctor public TextFieldState(optional String initialText, optional long initialSelectionInChars);
method public inline void edit(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.input.TextFieldBuffer,kotlin.Unit> block);
method public androidx.compose.foundation.text.input.TextFieldCharSequence getText();
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 8967c13..f4d58b1 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -1528,11 +1528,8 @@
method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicSecureTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.text.input.ImeActionHandler? onSubmit, optional int imeAction, optional int textObfuscationMode, optional int keyboardType, optional boolean enabled, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.text.input.TextFieldDecorator? decorator, optional androidx.compose.foundation.ScrollState scrollState);
}
- public final class BasicTextField2Kt {
- method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.input.TextFieldDecorator? decorator, optional androidx.compose.foundation.ScrollState scrollState);
- }
-
public final class BasicTextFieldKt {
+ method @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.input.TextFieldDecorator? decorator, optional androidx.compose.foundation.ScrollState scrollState);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
method @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
method @Deprecated @androidx.compose.runtime.Composable public static void BasicTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
@@ -1643,11 +1640,11 @@
method public boolean onImeAction(int action);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface InputTransformation {
- method public default void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ @androidx.compose.runtime.Stable public fun interface InputTransformation {
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public default void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public default androidx.compose.foundation.text.KeyboardOptions? getKeyboardOptions();
method public void transformInput(androidx.compose.foundation.text.input.TextFieldCharSequence originalValue, androidx.compose.foundation.text.input.TextFieldBuffer valueWithChanges);
- property public default androidx.compose.foundation.text.KeyboardOptions? keyboardOptions;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public default androidx.compose.foundation.text.KeyboardOptions? keyboardOptions;
field public static final androidx.compose.foundation.text.input.InputTransformation.Companion Companion;
}
@@ -1664,36 +1661,36 @@
method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public static androidx.compose.foundation.text.input.InputTransformation? thenOrNull(androidx.compose.foundation.text.input.InputTransformation?, androidx.compose.foundation.text.input.InputTransformation? next);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface OutputTransformation {
- method public void transformOutput(androidx.compose.foundation.text.input.TextFieldBuffer);
+ @androidx.compose.runtime.Stable public fun interface OutputTransformation {
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void transformOutput(androidx.compose.foundation.text.input.TextFieldBuffer);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class TextFieldBuffer implements java.lang.Appendable {
+ public final class TextFieldBuffer implements java.lang.Appendable {
method public Appendable append(char char);
method public Appendable append(CharSequence? text);
method public Appendable append(CharSequence? text, int start, int end);
- method public CharSequence asCharSequence();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public CharSequence asCharSequence();
method public char charAt(int index);
method public androidx.compose.foundation.text.input.TextFieldBuffer.ChangeList getChanges();
method public int getCodepointLength();
method public int getLength();
- method public long getSelectionInChars();
- method public long getSelectionInCodepoints();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public long getSelectionInChars();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public long getSelectionInCodepoints();
method public boolean hasSelection();
- method public void placeCursorAfterCharAt(int index);
- method public void placeCursorAfterCodepointAt(int index);
- method public void placeCursorBeforeCharAt(int index);
- method public void placeCursorBeforeCodepointAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorAfterCharAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorAfterCodepointAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorBeforeCharAt(int index);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void placeCursorBeforeCodepointAt(int index);
method public void replace(int start, int end, CharSequence text);
- method public void revertAllChanges();
- method public void selectCharsIn(long range);
- method public void selectCodepointsIn(long range);
- property public final androidx.compose.foundation.text.input.TextFieldBuffer.ChangeList changes;
- property public final int codepointLength;
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void revertAllChanges();
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void selectCharsIn(long range);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public void selectCodepointsIn(long range);
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final androidx.compose.foundation.text.input.TextFieldBuffer.ChangeList changes;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final int codepointLength;
property public final boolean hasSelection;
property public final int length;
- property public final long selectionInChars;
- property public final long selectionInCodepoints;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final long selectionInChars;
+ property @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final long selectionInCodepoints;
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static interface TextFieldBuffer.ChangeList {
@@ -1712,7 +1709,7 @@
method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static void selectAll(androidx.compose.foundation.text.input.TextFieldBuffer);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface TextFieldCharSequence extends java.lang.CharSequence {
+ public sealed interface TextFieldCharSequence extends java.lang.CharSequence {
method public boolean contentEquals(CharSequence other);
method public boolean equals(Object? other);
method public androidx.compose.ui.text.TextRange? getCompositionInChars();
@@ -1723,14 +1720,14 @@
}
public final class TextFieldCharSequenceKt {
- method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.text.input.TextFieldCharSequence TextFieldCharSequence(optional String text, optional long selection);
+ method public static androidx.compose.foundation.text.input.TextFieldCharSequence TextFieldCharSequence(optional String text, optional long selection);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public fun interface TextFieldDecorator {
+ public fun interface TextFieldDecorator {
method @androidx.compose.runtime.Composable public void Decoration(kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField);
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public sealed interface TextFieldLineLimits {
+ @androidx.compose.runtime.Stable public sealed interface TextFieldLineLimits {
field public static final androidx.compose.foundation.text.input.TextFieldLineLimits.Companion Companion;
}
@@ -1751,7 +1748,7 @@
field public static final androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine INSTANCE;
}
- @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class TextFieldState {
+ @androidx.compose.runtime.Stable public final class TextFieldState {
ctor public TextFieldState(optional String initialText, optional long initialSelectionInChars);
method @kotlin.PublishedApi internal void commitEdit(androidx.compose.foundation.text.input.TextFieldBuffer newValue);
method public inline void edit(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.input.TextFieldBuffer,kotlin.Unit> block);
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
index b0446d9..058feba 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
@@ -16,24 +16,476 @@
package androidx.compose.foundation.text
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.interaction.collectIsHoveredAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.relocation.bringIntoViewRequester
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldDecorator
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.foundation.text.input.internal.CodepointTransformation
+import androidx.compose.foundation.text.input.internal.SingleLineCodepointTransformation
+import androidx.compose.foundation.text.input.internal.TextFieldCoreModifier
+import androidx.compose.foundation.text.input.internal.TextFieldDecoratorModifier
+import androidx.compose.foundation.text.input.internal.TextFieldTextLayoutModifier
+import androidx.compose.foundation.text.input.internal.TextLayoutState
+import androidx.compose.foundation.text.input.internal.TransformedTextFieldState
+import androidx.compose.foundation.text.input.internal.selection.TextFieldSelectionState
+import androidx.compose.foundation.text.selection.SelectionHandle
+import androidx.compose.foundation.text.selection.SelectionHandleAnchor
+import androidx.compose.foundation.text.selection.SelectionHandleInfo
+import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.input.pointer.pointerHoverIcon
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalTextToolbar
+import androidx.compose.ui.platform.LocalWindowInfo
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+
+/**
+ * Basic text composable that provides an interactive box that accepts text input through software
+ * or hardware keyboard, but provides no decorations like hint or placeholder.
+ *
+ * All the editing state of this composable is hoisted through [state]. Whenever the contents of
+ * this composable change via user input or semantics, [TextFieldState.text] gets updated.
+ * Similarly, all the programmatic updates made to [state] also reflect on this composable.
+ *
+ * If you want to add decorations to your text field, such as icon or similar, and increase the
+ * hit target area, use the decorator:
+ * @sample androidx.compose.foundation.samples.BasicTextFieldDecoratorSample
+ *
+ * In order to filter (e.g. only allow digits, limit the number of characters), or change (e.g.
+ * convert every character to uppercase) the input received from the user, use an
+ * [InputTransformation].
+ * @sample androidx.compose.foundation.samples.BasicTextFieldCustomInputTransformationSample
+ *
+ * Limiting the height of the [BasicTextField] in terms of line count and choosing a scroll
+ * direction can be achieved by using [TextFieldLineLimits].
+ *
+ * Scroll state of the composable is also hoisted to enable observation and manipulation of the
+ * scroll behavior by the developer, e.g. bringing a searched keyword into view by scrolling to its
+ * position without focusing, or changing selection.
+ *
+ * It's also possible to internally wrap around an existing TextFieldState and expose a more
+ * lightweight state hoisting mechanism through a value that dictates the content of the TextField
+ * and an onValueChange callback that communicates the changes to this value.
+ * @sample androidx.compose.foundation.samples.BasicTextFieldWithValueOnValueChangeSample
+ *
+ * @param state [TextFieldState] object that holds the internal editing state of [BasicTextField].
+ * @param modifier optional [Modifier] for this text field.
+ * @param enabled controls the enabled state of the [BasicTextField]. When `false`, the text
+ * field will be neither editable nor focusable, the input of the text field will not be selectable.
+ * @param readOnly controls the editable state of the [BasicTextField]. When `true`, the text
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit.
+ * @param inputTransformation Optional [InputTransformation] that will be used to transform changes
+ * to the [TextFieldState] made by the user. The transformation will be applied to changes made by
+ * hardware and software keyboard events, pasting or dropping text, accessibility services, and
+ * tests. The transformation will _not_ be applied when changing the [state] programmatically, or
+ * when the transformation is changed. If the transformation is changed on an existing text field,
+ * it will be applied to the next user edit. the transformation will not immediately affect the
+ * current [state].
+ * @param textStyle Typographic and graphic style configuration for text content that's displayed
+ * in the editor.
+ * @param keyboardOptions Software keyboard options that contain configurations such as
+ * [KeyboardType] and [ImeAction].
+ * @param keyboardActions When the input service emits an IME action, the corresponding callback
+ * is called. Note that this IME action may be different from what you specified in
+ * [KeyboardOptions.imeAction].
+ * @param lineLimits Whether the text field should be [SingleLine], scroll horizontally, and
+ * ignore newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all
+ * newline characters ('\n') within the text will be replaced with regular whitespace (' '),
+ * ensuring that the contents of the text field are presented in a single line.
+ * @param onTextLayout Callback that is executed when the text layout becomes queryable. The
+ * callback receives a function that returns a [TextLayoutResult] if the layout can be calculated,
+ * or null if it cannot. The function reads the layout result from a snapshot state object, and will
+ * invalidate its caller when the layout result changes. A [TextLayoutResult] object contains
+ * paragraph information, size of the text, baselines and other details. The callback can be used to
+ * add additional decoration or functionality to the text. For example, to draw a cursor or
+ * selection around the text. [Density] scope is the one that was used while creating the given text
+ * layout.
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this TextField. You can create and pass in your own remembered [MutableInteractionSource]
+ * if you want to observe [Interaction]s and customize the appearance / behavior of this TextField
+ * for different [Interaction]s.
+ * @param cursorBrush [Brush] to paint cursor with. If [SolidColor] with [Color.Unspecified]
+ * provided, then no cursor will be drawn.
+ * @param outputTransformation An [OutputTransformation] that transforms how the contents of the
+ * text field are presented.
+ * @param decorator Allows to add decorations around text field, such as icon, placeholder, helper
+ * messages or similar, and automatically increase the hit target area of the text field.
+ * @param scrollState Scroll state that manages either horizontal or vertical scroll of TextField.
+ * If [lineLimits] is [SingleLine], this text field is treated as single line with horizontal
+ * scroll behavior. In other cases the text field becomes vertically scrollable.
+ */
+// This takes a composable lambda, but it is not primarily a container.
+@Suppress("ComposableLambdaParameterPosition")
+@Composable
+fun BasicTextField(
+ state: TextFieldState,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ readOnly: Boolean = false,
+ inputTransformation: InputTransformation? = null,
+ textStyle: TextStyle = TextStyle.Default,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+ keyboardActions: KeyboardActions = KeyboardActions.Default,
+ lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+ onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
+ interactionSource: MutableInteractionSource? = null,
+ cursorBrush: Brush = SolidColor(Color.Black),
+ outputTransformation: OutputTransformation? = null,
+ decorator: TextFieldDecorator? = null,
+ scrollState: ScrollState = rememberScrollState(),
+ // Last parameter must not be a function unless it's intended to be commonly used as a trailing
+ // lambda.
+) {
+ BasicTextField(
+ state = state,
+ modifier = modifier,
+ enabled = enabled,
+ readOnly = readOnly,
+ inputTransformation = inputTransformation,
+ textStyle = textStyle,
+ keyboardOptions = keyboardOptions,
+ keyboardActions = keyboardActions,
+ lineLimits = lineLimits,
+ onTextLayout = onTextLayout,
+ interactionSource = interactionSource,
+ cursorBrush = cursorBrush,
+ codepointTransformation = null,
+ outputTransformation = outputTransformation,
+ decorator = decorator,
+ scrollState = scrollState,
+ )
+}
+
+/**
+ * Internal core text field that accepts a [CodepointTransformation].
+ *
+ * @param codepointTransformation Visual transformation interface that provides a 1-to-1 mapping of
+ * codepoints.
+ */
+// This takes a composable lambda, but it is not primarily a container.
+@OptIn(ExperimentalFoundationApi::class)
+@Suppress("ComposableLambdaParameterPosition")
+@Composable
+internal fun BasicTextField(
+ state: TextFieldState,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ readOnly: Boolean = false,
+ inputTransformation: InputTransformation? = null,
+ textStyle: TextStyle = TextStyle.Default,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+ keyboardActions: KeyboardActions = KeyboardActions.Default,
+ lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+ onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
+ interactionSource: MutableInteractionSource? = null,
+ cursorBrush: Brush = SolidColor(Color.Black),
+ codepointTransformation: CodepointTransformation? = null,
+ outputTransformation: OutputTransformation? = null,
+ decorator: TextFieldDecorator? = null,
+ scrollState: ScrollState = rememberScrollState(),
+ // Last parameter must not be a function unless it's intended to be commonly used as a trailing
+ // lambda.
+) {
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+ val windowInfo = LocalWindowInfo.current
+ val singleLine = lineLimits == SingleLine
+ // We're using this to communicate focus state to cursor for now.
+ @Suppress("NAME_SHADOWING")
+ val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+ val orientation = if (singleLine) Orientation.Horizontal else Orientation.Vertical
+ val isFocused = interactionSource.collectIsFocusedAsState().value
+ val isDragHovered = interactionSource.collectIsHoveredAsState().value
+ val isWindowFocused = windowInfo.isWindowFocused
+
+ val transformedState = remember(
+ state,
+ inputTransformation,
+ codepointTransformation,
+ outputTransformation
+ ) {
+ // First prefer provided codepointTransformation if not null, e.g. BasicSecureTextField
+ // would send PasswordTransformation. Second, apply a SingleLineCodepointTransformation if
+ // text field is configured to be single line. Else, don't apply any visual transformation.
+ val appliedCodepointTransformation = codepointTransformation
+ ?: SingleLineCodepointTransformation.takeIf { singleLine }
+ TransformedTextFieldState(
+ textFieldState = state,
+ inputTransformation = inputTransformation,
+ codepointTransformation = appliedCodepointTransformation,
+ outputTransformation = outputTransformation
+ )
+ }
+
+ // Invalidate textLayoutState if TextFieldState itself has changed, since TextLayoutState
+ // would be carrying an invalid TextFieldState in its nonMeasureInputs.
+ val textLayoutState = remember(transformedState) { TextLayoutState() }
+
+ val textFieldSelectionState = remember(transformedState) {
+ TextFieldSelectionState(
+ textFieldState = transformedState,
+ textLayoutState = textLayoutState,
+ density = density,
+ enabled = enabled,
+ readOnly = readOnly,
+ isFocused = isFocused && isWindowFocused
+ )
+ }
+ val currentHapticFeedback = LocalHapticFeedback.current
+ val currentClipboardManager = LocalClipboardManager.current
+ val currentTextToolbar = LocalTextToolbar.current
+ SideEffect {
+ // These properties are not backed by snapshot state, so they can't be updated directly in
+ // composition.
+ textFieldSelectionState.update(
+ hapticFeedBack = currentHapticFeedback,
+ clipboardManager = currentClipboardManager,
+ textToolbar = currentTextToolbar,
+ density = density,
+ enabled = enabled,
+ readOnly = readOnly,
+ )
+ }
+
+ DisposableEffect(textFieldSelectionState) {
+ onDispose {
+ textFieldSelectionState.dispose()
+ }
+ }
+
+ val decorationModifiers = modifier
+ .then(
+ // semantics + some focus + input session + touch to focus
+ TextFieldDecoratorModifier(
+ textFieldState = transformedState,
+ textLayoutState = textLayoutState,
+ textFieldSelectionState = textFieldSelectionState,
+ filter = inputTransformation,
+ enabled = enabled,
+ readOnly = readOnly,
+ keyboardOptions = keyboardOptions,
+ keyboardActions = keyboardActions,
+ singleLine = singleLine,
+ interactionSource = interactionSource
+ )
+ )
+ .focusable(interactionSource = interactionSource, enabled = enabled)
+ .scrollable(
+ state = scrollState,
+ orientation = orientation,
+ // Disable scrolling when textField is disabled, there is no where to scroll, and
+ // another dragging gesture is taking place
+ enabled = enabled &&
+ scrollState.maxValue > 0 &&
+ textFieldSelectionState.draggingHandle == null,
+ reverseDirection = ScrollableDefaults.reverseDirection(
+ layoutDirection = layoutDirection,
+ orientation = orientation,
+ reverseScrolling = false
+ ),
+ interactionSource = interactionSource,
+ )
+ .pointerHoverIcon(textPointerIcon)
+
+ Box(decorationModifiers, propagateMinConstraints = true) {
+ val nonNullDecorator = decorator ?: DefaultTextFieldDecorator
+ nonNullDecorator.Decoration {
+ val minLines: Int
+ val maxLines: Int
+ if (lineLimits is MultiLine) {
+ minLines = lineLimits.minHeightInLines
+ maxLines = lineLimits.maxHeightInLines
+ } else {
+ minLines = 1
+ maxLines = 1
+ }
+
+ Box(
+ propagateMinConstraints = true,
+ modifier = Modifier
+ .heightIn(min = textLayoutState.minHeightForSingleLineField)
+ .heightInLines(
+ textStyle = textStyle,
+ minLines = minLines,
+ maxLines = maxLines
+ )
+ .textFieldMinSize(textStyle)
+ .clipToBounds()
+ .then(
+ TextFieldCoreModifier(
+ isFocused = isFocused && isWindowFocused,
+ isDragHovered = isDragHovered,
+ textLayoutState = textLayoutState,
+ textFieldState = transformedState,
+ textFieldSelectionState = textFieldSelectionState,
+ cursorBrush = cursorBrush,
+ writeable = enabled && !readOnly,
+ scrollState = scrollState,
+ orientation = orientation
+ )
+ )
+ ) {
+ Box(
+ modifier = Modifier
+ .bringIntoViewRequester(textLayoutState.bringIntoViewRequester)
+ .then(
+ TextFieldTextLayoutModifier(
+ textLayoutState = textLayoutState,
+ textFieldState = transformedState,
+ textStyle = textStyle,
+ singleLine = singleLine,
+ onTextLayout = onTextLayout
+ )
+ )
+ )
+
+ if (enabled && isFocused &&
+ isWindowFocused && textFieldSelectionState.isInTouchMode
+ ) {
+ TextFieldSelectionHandles(
+ selectionState = textFieldSelectionState
+ )
+ if (!readOnly) {
+ TextFieldCursorHandle(
+ selectionState = textFieldSelectionState
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+internal fun TextFieldCursorHandle(selectionState: TextFieldSelectionState) {
+ val cursorHandleState = selectionState.cursorHandle
+ if (cursorHandleState.visible) {
+ CursorHandle(
+ handlePosition = cursorHandleState.position,
+ modifier = Modifier
+ .semantics {
+ this[SelectionHandleInfoKey] = SelectionHandleInfo(
+ handle = Handle.Cursor,
+ position = cursorHandleState.position,
+ anchor = SelectionHandleAnchor.Middle,
+ visible = true,
+ )
+ }
+ .pointerInput(selectionState) {
+ with(selectionState) { cursorHandleGestures() }
+ },
+ minTouchTargetSize = MinTouchTargetSizeForHandles,
+ )
+ }
+}
+
+@Composable
+internal fun TextFieldSelectionHandles(
+ selectionState: TextFieldSelectionState
+) {
+ // Does not recompose if only position of the handle changes.
+ val startHandleState by remember {
+ derivedStateOf {
+ selectionState.getSelectionHandleState(isStartHandle = true, includePosition = false)
+ }
+ }
+ if (startHandleState.visible) {
+ SelectionHandle(
+ offsetProvider = {
+ selectionState
+ .getSelectionHandleState(isStartHandle = true, includePosition = true)
+ .position
+ },
+ isStartHandle = true,
+ direction = startHandleState.direction,
+ handlesCrossed = startHandleState.handlesCrossed,
+ modifier = Modifier.pointerInput(selectionState) {
+ with(selectionState) { selectionHandleGestures(true) }
+ },
+ minTouchTargetSize = MinTouchTargetSizeForHandles,
+ )
+ }
+
+ // Does not recompose if only position of the handle changes.
+ val endHandleState by remember {
+ derivedStateOf {
+ selectionState.getSelectionHandleState(isStartHandle = false, includePosition = false)
+ }
+ }
+ if (endHandleState.visible) {
+ SelectionHandle(
+ offsetProvider = {
+ selectionState
+ .getSelectionHandleState(isStartHandle = false, includePosition = true)
+ .position
+ },
+ isStartHandle = false,
+ direction = endHandleState.direction,
+ handlesCrossed = endHandleState.handlesCrossed,
+ modifier = Modifier.pointerInput(selectionState) {
+ with(selectionState) { selectionHandleGestures(false) }
+ },
+ minTouchTargetSize = MinTouchTargetSizeForHandles,
+ )
+ }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private val DefaultTextFieldDecorator = TextFieldDecorator { it() }
+
+/**
+ * Defines a minimum touch target area size for Selection and Cursor handles.
+ *
+ * Although BasicTextField is not part of Material spec, this accessibility feature is important
+ * enough to be included at foundation layer, and also TextField cannot change selection handles
+ * provided by BasicTextField to somehow achieve this accessibility requirement.
+ *
+ * This value is adopted from Android platform's TextView implementation.
+ */
+private val MinTouchTargetSizeForHandles = DpSize(40.dp, 40.dp)
/**
* Basic composable that enables users to edit text via hardware or software keyboard, but
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField2.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField2.kt
deleted file mode 100644
index 8284212..0000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField2.kt
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.text
-
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.ScrollState
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.ScrollableDefaults
-import androidx.compose.foundation.gestures.scrollable
-import androidx.compose.foundation.interaction.Interaction
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.interaction.collectIsFocusedAsState
-import androidx.compose.foundation.interaction.collectIsHoveredAsState
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.relocation.bringIntoViewRequester
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.input.InputTransformation
-import androidx.compose.foundation.text.input.OutputTransformation
-import androidx.compose.foundation.text.input.TextFieldDecorator
-import androidx.compose.foundation.text.input.TextFieldLineLimits
-import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
-import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
-import androidx.compose.foundation.text.input.TextFieldState
-import androidx.compose.foundation.text.input.internal.CodepointTransformation
-import androidx.compose.foundation.text.input.internal.SingleLineCodepointTransformation
-import androidx.compose.foundation.text.input.internal.TextFieldCoreModifier
-import androidx.compose.foundation.text.input.internal.TextFieldDecoratorModifier
-import androidx.compose.foundation.text.input.internal.TextFieldTextLayoutModifier
-import androidx.compose.foundation.text.input.internal.TextLayoutState
-import androidx.compose.foundation.text.input.internal.TransformedTextFieldState
-import androidx.compose.foundation.text.input.internal.selection.TextFieldSelectionState
-import androidx.compose.foundation.text.selection.SelectionHandle
-import androidx.compose.foundation.text.selection.SelectionHandleAnchor
-import androidx.compose.foundation.text.selection.SelectionHandleInfo
-import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clipToBounds
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.input.pointer.pointerHoverIcon
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.platform.LocalClipboardManager
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalHapticFeedback
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.platform.LocalTextToolbar
-import androidx.compose.ui.platform.LocalWindowInfo
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.DpSize
-import androidx.compose.ui.unit.dp
-
-/**
- * Basic text composable that provides an interactive box that accepts text input through software
- * or hardware keyboard, but provides no decorations like hint or placeholder.
- *
- * All the editing state of this composable is hoisted through [state]. Whenever the contents of
- * this composable change via user input or semantics, [TextFieldState.text] gets updated.
- * Similarly, all the programmatic updates made to [state] also reflect on this composable.
- *
- * If you want to add decorations to your text field, such as icon or similar, and increase the
- * hit target area, use the decorator:
- * @sample androidx.compose.foundation.samples.BasicTextFieldDecoratorSample
- *
- * In order to filter (e.g. only allow digits, limit the number of characters), or change (e.g.
- * convert every character to uppercase) the input received from the user, use an
- * [InputTransformation].
- * @sample androidx.compose.foundation.samples.BasicTextFieldCustomInputTransformationSample
- *
- * Limiting the height of the [BasicTextField] in terms of line count and choosing a scroll
- * direction can be achieved by using [TextFieldLineLimits].
- *
- * Scroll state of the composable is also hoisted to enable observation and manipulation of the
- * scroll behavior by the developer, e.g. bringing a searched keyword into view by scrolling to its
- * position without focusing, or changing selection.
- *
- * It's also possible to internally wrap around an existing TextFieldState and expose a more
- * lightweight state hoisting mechanism through a value that dictates the content of the TextField
- * and an onValueChange callback that communicates the changes to this value.
- * @sample androidx.compose.foundation.samples.BasicTextFieldWithValueOnValueChangeSample
- *
- * @param state [TextFieldState] object that holds the internal editing state of [BasicTextField].
- * @param modifier optional [Modifier] for this text field.
- * @param enabled controls the enabled state of the [BasicTextField]. When `false`, the text
- * field will be neither editable nor focusable, the input of the text field will not be selectable.
- * @param readOnly controls the editable state of the [BasicTextField]. When `true`, the text
- * field can not be modified, however, a user can focus it and copy text from it. Read-only text
- * fields are usually used to display pre-filled forms that user can not edit.
- * @param inputTransformation Optional [InputTransformation] that will be used to transform changes
- * to the [TextFieldState] made by the user. The transformation will be applied to changes made by
- * hardware and software keyboard events, pasting or dropping text, accessibility services, and
- * tests. The transformation will _not_ be applied when changing the [state] programmatically, or
- * when the transformation is changed. If the transformation is changed on an existing text field,
- * it will be applied to the next user edit. the transformation will not immediately affect the
- * current [state].
- * @param textStyle Typographic and graphic style configuration for text content that's displayed
- * in the editor.
- * @param keyboardOptions Software keyboard options that contain configurations such as
- * [KeyboardType] and [ImeAction].
- * @param keyboardActions When the input service emits an IME action, the corresponding callback
- * is called. Note that this IME action may be different from what you specified in
- * [KeyboardOptions.imeAction].
- * @param lineLimits Whether the text field should be [SingleLine], scroll horizontally, and
- * ignore newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all
- * newline characters ('\n') within the text will be replaced with regular whitespace (' '),
- * ensuring that the contents of the text field are presented in a single line.
- * @param onTextLayout Callback that is executed when the text layout becomes queryable. The
- * callback receives a function that returns a [TextLayoutResult] if the layout can be calculated,
- * or null if it cannot. The function reads the layout result from a snapshot state object, and will
- * invalidate its caller when the layout result changes. A [TextLayoutResult] object contains
- * paragraph information, size of the text, baselines and other details. The callback can be used to
- * add additional decoration or functionality to the text. For example, to draw a cursor or
- * selection around the text. [Density] scope is the one that was used while creating the given text
- * layout.
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this TextField. You can create and pass in your own remembered [MutableInteractionSource]
- * if you want to observe [Interaction]s and customize the appearance / behavior of this TextField
- * for different [Interaction]s.
- * @param cursorBrush [Brush] to paint cursor with. If [SolidColor] with [Color.Unspecified]
- * provided, then no cursor will be drawn.
- * @param outputTransformation An [OutputTransformation] that transforms how the contents of the
- * text field are presented.
- * @param decorator Allows to add decorations around text field, such as icon, placeholder, helper
- * messages or similar, and automatically increase the hit target area of the text field.
- * @param scrollState Scroll state that manages either horizontal or vertical scroll of TextField.
- * If [lineLimits] is [SingleLine], this text field is treated as single line with horizontal
- * scroll behavior. In other cases the text field becomes vertically scrollable.
- */
-@ExperimentalFoundationApi
-// This takes a composable lambda, but it is not primarily a container.
-@Suppress("ComposableLambdaParameterPosition")
-@Composable
-fun BasicTextField(
- state: TextFieldState,
- modifier: Modifier = Modifier,
- enabled: Boolean = true,
- readOnly: Boolean = false,
- inputTransformation: InputTransformation? = null,
- textStyle: TextStyle = TextStyle.Default,
- keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
- keyboardActions: KeyboardActions = KeyboardActions.Default,
- lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
- onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
- interactionSource: MutableInteractionSource? = null,
- cursorBrush: Brush = SolidColor(Color.Black),
- outputTransformation: OutputTransformation? = null,
- decorator: TextFieldDecorator? = null,
- scrollState: ScrollState = rememberScrollState(),
- // Last parameter must not be a function unless it's intended to be commonly used as a trailing
- // lambda.
-) {
- BasicTextField(
- state = state,
- modifier = modifier,
- enabled = enabled,
- readOnly = readOnly,
- inputTransformation = inputTransformation,
- textStyle = textStyle,
- keyboardOptions = keyboardOptions,
- keyboardActions = keyboardActions,
- lineLimits = lineLimits,
- onTextLayout = onTextLayout,
- interactionSource = interactionSource,
- cursorBrush = cursorBrush,
- codepointTransformation = null,
- outputTransformation = outputTransformation,
- decorator = decorator,
- scrollState = scrollState,
- )
-}
-
-/**
- * Internal core text field that accepts a [CodepointTransformation].
- *
- * @param codepointTransformation Visual transformation interface that provides a 1-to-1 mapping of
- * codepoints.
- */
-@OptIn(ExperimentalFoundationApi::class)
-// This takes a composable lambda, but it is not primarily a container.
-@Suppress("ComposableLambdaParameterPosition")
-@Composable
-internal fun BasicTextField(
- state: TextFieldState,
- modifier: Modifier = Modifier,
- enabled: Boolean = true,
- readOnly: Boolean = false,
- inputTransformation: InputTransformation? = null,
- textStyle: TextStyle = TextStyle.Default,
- keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
- keyboardActions: KeyboardActions = KeyboardActions.Default,
- lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
- onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
- interactionSource: MutableInteractionSource? = null,
- cursorBrush: Brush = SolidColor(Color.Black),
- codepointTransformation: CodepointTransformation? = null,
- outputTransformation: OutputTransformation? = null,
- decorator: TextFieldDecorator? = null,
- scrollState: ScrollState = rememberScrollState(),
- // Last parameter must not be a function unless it's intended to be commonly used as a trailing
- // lambda.
-) {
- val density = LocalDensity.current
- val layoutDirection = LocalLayoutDirection.current
- val windowInfo = LocalWindowInfo.current
- val singleLine = lineLimits == SingleLine
- // We're using this to communicate focus state to cursor for now.
- @Suppress("NAME_SHADOWING")
- val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
- val orientation = if (singleLine) Orientation.Horizontal else Orientation.Vertical
- val isFocused = interactionSource.collectIsFocusedAsState().value
- val isDragHovered = interactionSource.collectIsHoveredAsState().value
- val isWindowFocused = windowInfo.isWindowFocused
-
- val transformedState = remember(
- state,
- inputTransformation,
- codepointTransformation,
- outputTransformation
- ) {
- // First prefer provided codepointTransformation if not null, e.g. BasicSecureTextField
- // would send PasswordTransformation. Second, apply a SingleLineCodepointTransformation if
- // text field is configured to be single line. Else, don't apply any visual transformation.
- val appliedCodepointTransformation = codepointTransformation
- ?: SingleLineCodepointTransformation.takeIf { singleLine }
- TransformedTextFieldState(
- textFieldState = state,
- inputTransformation = inputTransformation,
- codepointTransformation = appliedCodepointTransformation,
- outputTransformation = outputTransformation
- )
- }
-
- // Invalidate textLayoutState if TextFieldState itself has changed, since TextLayoutState
- // would be carrying an invalid TextFieldState in its nonMeasureInputs.
- val textLayoutState = remember(transformedState) { TextLayoutState() }
-
- val textFieldSelectionState = remember(transformedState) {
- TextFieldSelectionState(
- textFieldState = transformedState,
- textLayoutState = textLayoutState,
- density = density,
- enabled = enabled,
- readOnly = readOnly,
- isFocused = isFocused && isWindowFocused
- )
- }
- val currentHapticFeedback = LocalHapticFeedback.current
- val currentClipboardManager = LocalClipboardManager.current
- val currentTextToolbar = LocalTextToolbar.current
- SideEffect {
- // These properties are not backed by snapshot state, so they can't be updated directly in
- // composition.
- textFieldSelectionState.update(
- hapticFeedBack = currentHapticFeedback,
- clipboardManager = currentClipboardManager,
- textToolbar = currentTextToolbar,
- density = density,
- enabled = enabled,
- readOnly = readOnly,
- )
- }
-
- DisposableEffect(textFieldSelectionState) {
- onDispose {
- textFieldSelectionState.dispose()
- }
- }
-
- val decorationModifiers = modifier
- .then(
- // semantics + some focus + input session + touch to focus
- TextFieldDecoratorModifier(
- textFieldState = transformedState,
- textLayoutState = textLayoutState,
- textFieldSelectionState = textFieldSelectionState,
- filter = inputTransformation,
- enabled = enabled,
- readOnly = readOnly,
- keyboardOptions = keyboardOptions,
- keyboardActions = keyboardActions,
- singleLine = singleLine,
- interactionSource = interactionSource
- )
- )
- .focusable(interactionSource = interactionSource, enabled = enabled)
- .scrollable(
- state = scrollState,
- orientation = orientation,
- // Disable scrolling when textField is disabled, there is no where to scroll, and
- // another dragging gesture is taking place
- enabled = enabled &&
- scrollState.maxValue > 0 &&
- textFieldSelectionState.draggingHandle == null,
- reverseDirection = ScrollableDefaults.reverseDirection(
- layoutDirection = layoutDirection,
- orientation = orientation,
- reverseScrolling = false
- ),
- interactionSource = interactionSource,
- )
- .pointerHoverIcon(textPointerIcon)
-
- Box(decorationModifiers, propagateMinConstraints = true) {
- val nonNullDecorator = decorator ?: DefaultTextFieldDecorator
- nonNullDecorator.Decoration {
- val minLines: Int
- val maxLines: Int
- if (lineLimits is MultiLine) {
- minLines = lineLimits.minHeightInLines
- maxLines = lineLimits.maxHeightInLines
- } else {
- minLines = 1
- maxLines = 1
- }
-
- Box(
- propagateMinConstraints = true,
- modifier = Modifier
- .heightIn(min = textLayoutState.minHeightForSingleLineField)
- .heightInLines(
- textStyle = textStyle,
- minLines = minLines,
- maxLines = maxLines
- )
- .textFieldMinSize(textStyle)
- .clipToBounds()
- .then(
- TextFieldCoreModifier(
- isFocused = isFocused && isWindowFocused,
- isDragHovered = isDragHovered,
- textLayoutState = textLayoutState,
- textFieldState = transformedState,
- textFieldSelectionState = textFieldSelectionState,
- cursorBrush = cursorBrush,
- writeable = enabled && !readOnly,
- scrollState = scrollState,
- orientation = orientation
- )
- )
- ) {
- Box(
- modifier = Modifier
- .bringIntoViewRequester(textLayoutState.bringIntoViewRequester)
- .then(
- TextFieldTextLayoutModifier(
- textLayoutState = textLayoutState,
- textFieldState = transformedState,
- textStyle = textStyle,
- singleLine = singleLine,
- onTextLayout = onTextLayout
- )
- )
- )
-
- if (enabled && isFocused &&
- isWindowFocused && textFieldSelectionState.isInTouchMode
- ) {
- TextFieldSelectionHandles(
- selectionState = textFieldSelectionState
- )
- if (!readOnly) {
- TextFieldCursorHandle(
- selectionState = textFieldSelectionState
- )
- }
- }
- }
- }
- }
-}
-
-@Composable
-internal fun TextFieldCursorHandle(selectionState: TextFieldSelectionState) {
- val cursorHandleState = selectionState.cursorHandle
- if (cursorHandleState.visible) {
- CursorHandle(
- handlePosition = cursorHandleState.position,
- modifier = Modifier
- .semantics {
- this[SelectionHandleInfoKey] = SelectionHandleInfo(
- handle = Handle.Cursor,
- position = cursorHandleState.position,
- anchor = SelectionHandleAnchor.Middle,
- visible = true,
- )
- }
- .pointerInput(selectionState) {
- with(selectionState) { cursorHandleGestures() }
- },
- minTouchTargetSize = MinTouchTargetSizeForHandles,
- )
- }
-}
-
-@Composable
-internal fun TextFieldSelectionHandles(
- selectionState: TextFieldSelectionState
-) {
- // Does not recompose if only position of the handle changes.
- val startHandleState by remember {
- derivedStateOf {
- selectionState.getSelectionHandleState(isStartHandle = true, includePosition = false)
- }
- }
- if (startHandleState.visible) {
- SelectionHandle(
- offsetProvider = {
- selectionState
- .getSelectionHandleState(isStartHandle = true, includePosition = true)
- .position
- },
- isStartHandle = true,
- direction = startHandleState.direction,
- handlesCrossed = startHandleState.handlesCrossed,
- modifier = Modifier.pointerInput(selectionState) {
- with(selectionState) { selectionHandleGestures(true) }
- },
- minTouchTargetSize = MinTouchTargetSizeForHandles,
- )
- }
-
- // Does not recompose if only position of the handle changes.
- val endHandleState by remember {
- derivedStateOf {
- selectionState.getSelectionHandleState(isStartHandle = false, includePosition = false)
- }
- }
- if (endHandleState.visible) {
- SelectionHandle(
- offsetProvider = {
- selectionState
- .getSelectionHandleState(isStartHandle = false, includePosition = true)
- .position
- },
- isStartHandle = false,
- direction = endHandleState.direction,
- handlesCrossed = endHandleState.handlesCrossed,
- modifier = Modifier.pointerInput(selectionState) {
- with(selectionState) { selectionHandleGestures(false) }
- },
- minTouchTargetSize = MinTouchTargetSizeForHandles,
- )
- }
-}
-
-@OptIn(ExperimentalFoundationApi::class)
-private val DefaultTextFieldDecorator = TextFieldDecorator { it() }
-
-/**
- * Defines a minimum touch target area size for Selection and Cursor handles.
- *
- * Although BasicTextField is not part of Material spec, this accessibility feature is important
- * enough to be included at foundation layer, and also TextField cannot change selection handles
- * provided by BasicTextField to somehow achieve this accessibility requirement.
- *
- * This value is adopted from Android platform's TextView implementation.
- */
-private val MinTouchTargetSizeForHandles = DpSize(40.dp, 40.dp)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt
index 4ae6be7..de2bfad 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt
@@ -42,7 +42,6 @@
*
* @sample androidx.compose.foundation.samples.BasicTextFieldCustomInputTransformationSample
*/
-@ExperimentalFoundationApi
@Stable
fun interface InputTransformation {
@@ -50,12 +49,14 @@
* Optional [KeyboardOptions] that will be used as the default keyboard options for configuring
* the IME. The options passed directly to the text field composable will always override this.
*/
+ @ExperimentalFoundationApi
val keyboardOptions: KeyboardOptions? get() = null
/**
* Optional semantics configuration that can update certain characteristics of the applied
* TextField, e.g. [SemanticsPropertyReceiver.maxTextLength].
*/
+ @ExperimentalFoundationApi
fun SemanticsPropertyReceiver.applySemantics() = Unit
/**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/OutputTransformation.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/OutputTransformation.kt
index 2144234..1bc7a665 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/OutputTransformation.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/OutputTransformation.kt
@@ -24,7 +24,6 @@
* A function ([transformOutput]) that transforms the text presented to a user by a
* [BasicTextField].
*/
-@ExperimentalFoundationApi
@Stable
fun interface OutputTransformation {
@@ -36,5 +35,6 @@
* Note that the contents of the [TextFieldState] remain completely unchanged. This is a one-way
* transformation that only affects what is presented to the user.
*/
+ @ExperimentalFoundationApi
fun TextFieldBuffer.transformOutput()
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
index 1c0c6f7..cf4d506 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
@@ -45,7 +45,7 @@
* To get one of these, and for usage samples, see [TextFieldState.edit]. Every change to the buffer
* is tracked in a [ChangeList] which you can access via the [changes] property.
*/
-@ExperimentalFoundationApi
+@OptIn(ExperimentalFoundationApi::class)
class TextFieldBuffer internal constructor(
initialValue: TextFieldCharSequence,
initialChanges: ChangeTracker? = null,
@@ -75,6 +75,7 @@
/**
* The number of codepoints in the text field. This will be equal to or less than [length].
*/
+ @ExperimentalFoundationApi
val codepointLength: Int get() = Character.codePointCount(buffer, 0, length)
/**
@@ -85,6 +86,7 @@
* @sample androidx.compose.foundation.samples.BasicTextFieldChangeIterationSample
* @sample androidx.compose.foundation.samples.BasicTextFieldChangeReverseIterationSample
*/
+ @ExperimentalFoundationApi
val changes: ChangeList get() = changeTracker ?: EmptyChangeList
/**
@@ -102,6 +104,9 @@
*
* @see selectionInCodepoints
*/
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @ExperimentalFoundationApi
+ @get:ExperimentalFoundationApi
var selectionInChars: TextRange = initialValue.selectionInChars
private set
@@ -110,6 +115,9 @@
*
* @see selectionInChars
*/
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @ExperimentalFoundationApi
+ @get:ExperimentalFoundationApi
val selectionInCodepoints: TextRange
get() = charsToCodepoints(selectionInChars)
@@ -252,6 +260,7 @@
* Returns a [CharSequence] backed by this buffer. Any subsequent changes to this buffer will
* be visible in the returned sequence as well.
*/
+ @ExperimentalFoundationApi
fun asCharSequence(): CharSequence = buffer
private fun clearChangeList() {
@@ -264,6 +273,7 @@
* After calling this method, this object will be in the same state it was when it was initially
* created, and [changes] will be empty.
*/
+ @ExperimentalFoundationApi
fun revertAllChanges() {
replace(0, length, sourceValue.toString())
selectionInChars = sourceValue.selectionInChars
@@ -285,6 +295,7 @@
* @see placeCursorBeforeCharAt
* @see placeCursorAfterCodepointAt
*/
+ @ExperimentalFoundationApi
fun placeCursorBeforeCodepointAt(index: Int) {
requireValidIndex(index, startExclusive = true, endExclusive = false, inCodepoints = true)
val charIndex = codepointIndexToCharIndex(index)
@@ -307,6 +318,7 @@
* @see placeCursorBeforeCodepointAt
* @see placeCursorAfterCharAt
*/
+ @ExperimentalFoundationApi
fun placeCursorBeforeCharAt(index: Int) {
requireValidIndex(index, startExclusive = true, endExclusive = false, inCodepoints = false)
selectionInChars = TextRange(index)
@@ -326,6 +338,7 @@
* @see placeCursorAfterCharAt
* @see placeCursorBeforeCodepointAt
*/
+ @ExperimentalFoundationApi
fun placeCursorAfterCodepointAt(index: Int) {
requireValidIndex(index, startExclusive = false, endExclusive = true, inCodepoints = true)
val charIndex = codepointIndexToCharIndex((index + 1).coerceAtMost(codepointLength))
@@ -347,6 +360,7 @@
* @see placeCursorAfterCodepointAt
* @see placeCursorBeforeCharAt
*/
+ @ExperimentalFoundationApi
fun placeCursorAfterCharAt(index: Int) {
requireValidIndex(index, startExclusive = false, endExclusive = true, inCodepoints = false)
selectionInChars = TextRange((index + 1).coerceAtMost(length))
@@ -368,6 +382,7 @@
*
* @see selectCharsIn
*/
+ @ExperimentalFoundationApi
fun selectCodepointsIn(range: TextRange) {
requireValidRange(range, inCodepoints = true)
selectionInChars = codepointsToChars(range)
@@ -389,6 +404,7 @@
*
* @see selectCodepointsIn
*/
+ @ExperimentalFoundationApi
fun selectCharsIn(range: TextRange) {
requireValidRange(range, inCodepoints = false)
selectionInChars = range
@@ -591,8 +607,8 @@
* avoid having to allocate something to hold them. If the [CharSequence]s are identical, the
* callback is not invoked.
*
- * E.g. given `a="abcde"` and `b="abbbdefe"`, the middle diff for `a` is `"ab[cd]e"` and for `b` is
- * `ab[bbdef]e`, so reports `aMiddle=TextRange(2, 4)` and `bMiddle=TextRange(2, 7)`.
+ * E.g. given `a="abcde"` and `b="abbbdefe"`, the middle diff for `a` is `"ab|cd|e"` and for `b` is
+ * `ab|bbdef|e`, so reports `aMiddle=TextRange(2, 4)` and `bMiddle=TextRange(2, 7)`.
*/
internal inline fun findCommonPrefixAndSuffix(
a: CharSequence,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
index d7cae25..1752151 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
@@ -33,12 +33,12 @@
*
* @see TextFieldBuffer
*/
-@ExperimentalFoundationApi
sealed interface TextFieldCharSequence : CharSequence {
/**
* The selection range. If the selection is collapsed, it represents cursor
* location. When selection range is out of bounds, it is constrained with the text length.
*/
+ @ExperimentalFoundationApi
val selectionInChars: TextRange
/**
@@ -51,6 +51,7 @@
*
* Composition can only be set by the system.
*/
+ @ExperimentalFoundationApi
val compositionInChars: TextRange?
/**
@@ -64,13 +65,11 @@
abstract override fun hashCode(): Int
}
-@ExperimentalFoundationApi
fun TextFieldCharSequence(
text: String = "",
selection: TextRange = TextRange.Zero
): TextFieldCharSequence = TextFieldCharSequenceWrapper(text, selection, composition = null)
-@OptIn(ExperimentalFoundationApi::class)
internal fun TextFieldCharSequence(
text: CharSequence,
selection: TextRange,
@@ -81,7 +80,6 @@
* Copies the contents of this sequence from [[sourceStartIndex], [sourceEndIndex]) into
* [destination] starting at [destinationOffset].
*/
-@OptIn(ExperimentalFoundationApi::class)
internal fun TextFieldCharSequence.toCharArray(
destination: CharArray,
destinationOffset: Int,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldDecorator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldDecorator.kt
index fb79323..9b87b72 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldDecorator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldDecorator.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable
/**
@@ -26,7 +25,6 @@
*
* @sample androidx.compose.foundation.samples.BasicTextFieldDecoratorSample
*/
-@ExperimentalFoundationApi
fun interface TextFieldDecorator {
/**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldLineLimits.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldLineLimits.kt
index 6a688a1..8ae39ef 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldLineLimits.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldLineLimits.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
@@ -30,7 +29,6 @@
* @see SingleLine
* @see MultiLine
*/
-@ExperimentalFoundationApi
@Stable
sealed interface TextFieldLineLimits {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
index 42d9a49..0a31ffd5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
@@ -58,7 +58,6 @@
*
* @sample androidx.compose.foundation.samples.BasicTextFieldStateCompleteSample
*/
-@ExperimentalFoundationApi
@Stable
class TextFieldState internal constructor(
initialText: String,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt
index fcad583..bdd023a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt
@@ -28,7 +28,6 @@
* codepoint before text is rendered. Visual transformation is useful when the underlying source
* of input needs to remain but rendered content should look different, e.g. password obscuring.
*/
-@ExperimentalFoundationApi
@Stable
internal fun interface CodepointTransformation {