Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

@interface CMPEditMenuView : UIView

@property (readonly) BOOL isEditMenuShown;

- (void)showEditMenuAtRect:(CGRect)targetRect
copy:(void (^)(void))copyBlock
cut:(void (^)(void))cutBlock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,10 @@ internal class UIKitTextInputService(

val textToolbar: TextToolbar by lazy(LazyThreadSafetyMode.NONE) {
object : TextToolbar {
override val status: TextToolbarStatus
get() = (currentInputConnection as? ComposeTextInputConnection)?.toolbarStatus ?: TextToolbarStatus.Hidden
// Reflects whether the hosting CMPEditMenuView is alive, not the menu's literal visibility,
// TODO: https://youtrack.jetbrains.com/issue/CMP-10352
override var status: TextToolbarStatus = TextToolbarStatus.Hidden
private set

override fun showMenu(
rect: Rect,
Expand All @@ -151,42 +153,20 @@ internal class UIKitTextInputService(
onCutRequested: (() -> Unit)?,
onSelectAllRequested: (() -> Unit)?
) {
status = TextToolbarStatus.Shown
if (currentInputConnection == null) {
// TODO: https://youtrack.jetbrains.com/issue/CMP-10352
// Entry point for showing the context menu in SelectionContainer scenarios, where
// there is no active text input session. iOS requires a UIView that can become first
// responder in order to host the context menu, so we create a dedicated connection
// backed by a hidden view for this purpose.
// Note: start() is intentionally not called here — it establishes a text editing
// session (requiring a PlatformTextInputMethodRequest) which is not applicable for
// SelectionContainer.
// without the text input session backed by a hidden view for this purpose.
currentInputConnection = SelectionContainerConnection(
view = view,
coroutineScope = coroutineScope,
viewConfiguration = viewConfiguration,
focusManager = focusManager
)
currentInputConnection?.start(
object : PlatformTextInputMethodRequest {
override val value: () -> TextFieldValue get() = { TextFieldValue() }
override val state: TextEditorState = object : TextEditorState {
override val selection: TextRange get() = TextRange(0, 0)
override val composition: TextRange? get() = null
override val length: Int get() = 0
override fun get(index: Int): Char = ' '
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence = ""
override val text: String get() = ""
}
override val imeOptions: ImeOptions get() = ImeOptions.Default
override val onEditCommand: (List<EditCommand>) -> Unit get() = { _ -> }
override val onImeAction: ((ImeAction) -> Unit)? get() = null
override val textLayoutResult: () -> TextLayoutResult? get() = { null }
override val focusedRectInRoot: () -> Rect? get() = { null }
override val textFieldRectInRoot: () -> Rect? get() = { null }
override val textClippingRectInRoot: () -> Rect? get() = { null }
override val unclippedTextOffsetInRoot: () -> Offset? get() = { null }
override val editText: (block: TextEditingScope.() -> Unit) -> Unit get() = { _ -> }
}
)
currentInputConnection?.start(noopTextInputRequest())
}
(currentInputConnection as? ComposeTextInputConnection)?.showToolbarMenu(
rect = rect,
Expand All @@ -198,11 +178,11 @@ internal class UIKitTextInputService(
}

override fun hide() {
status = TextToolbarStatus.Hidden
(currentInputConnection as? ComposeTextInputConnection)?.hideToolbar()

if (currentInputConnection is SelectionContainerConnection) {
// stop() removes the view from the hierarchy and resigns first responder,
// without requiring a prior start() call.
// stop() removes the view from the hierarchy and resigns first responder.
currentInputConnection?.stop()
currentInputConnection = null
}
Expand Down Expand Up @@ -255,3 +235,30 @@ internal class UIKitTextInputService(
focusManager = { null }
}
}

/**
* A stub [PlatformTextInputMethodRequest] with no real editing state. Used to start a
* [SelectionContainerConnection] purely to attach its hosting view for the context menu —
* there is no text editing session behind it.
*/
private fun noopTextInputRequest(): PlatformTextInputMethodRequest =
object : PlatformTextInputMethodRequest {
override val value: () -> TextFieldValue get() = { TextFieldValue() }
override val state: TextEditorState = object : TextEditorState {
override val selection: TextRange get() = TextRange(0, 0)
override val composition: TextRange? get() = null
override val length: Int get() = 0
override fun get(index: Int): Char = ' '
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence = ""
override val text: String get() = ""
}
override val imeOptions: ImeOptions get() = ImeOptions.Default
override val onEditCommand: (List<EditCommand>) -> Unit get() = { _ -> }
override val onImeAction: ((ImeAction) -> Unit)? get() = null
override val textLayoutResult: () -> TextLayoutResult? get() = { null }
override val focusedRectInRoot: () -> Rect? get() = { null }
override val textFieldRectInRoot: () -> Rect? get() = { null }
override val textClippingRectInRoot: () -> Rect? get() = { null }
override val unclippedTextOffsetInRoot: () -> Offset? get() = { null }
override val editText: (block: TextEditingScope.() -> Unit) -> Unit get() = { _ -> }
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package androidx.compose.ui.text.input

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.platform.TextToolbarStatus
import androidx.compose.ui.platform.UIKitNativeTextInputContextMenuCustomAction
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.scene.ComposeSceneFocusManager
Expand Down Expand Up @@ -129,13 +128,6 @@ internal open class ComposeTextInputConnection(
)
}

val toolbarStatus: TextToolbarStatus
get() = if (textInputView.isTextMenuShown()) {
TextToolbarStatus.Shown
} else {
TextToolbarStatus.Hidden
}

fun showToolbarMenu(
rect: Rect,
onCopyRequested: (() -> Unit)?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,6 @@ internal class ComposeTextInputView(

fun hideTextMenu() = this.hideEditMenu()

fun isTextMenuShown() = isEditMenuShown

private val _tokenizer = TextInputStringTokenizer(textInput = this) {
input?.let { it.textInRange(TextRange(0, it.endOfDocument())) }
}
Expand Down
Loading