7

How can I customize a menu for selected text to a TextField in Jetpack Compose? I mean something like this:

Example

Didn't find anything in the official documentation or on the Internet about how to do this using Jetpack Compose.

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121

1 Answers1

9

You can provide a custom TextToolbar in the LocalTextToolbar. There, in the showMenu method, you must startActionMode as you would have done in the old Android. CustomTextToolbar:

override fun showMenu(
    rect: Rect,

    onCopyRequested: (() -> Unit)?,
    onPasteRequested: (() -> Unit)?,
    onCutRequested: (() -> Unit)?,
    onSelectAllRequested: (() -> Unit)?

// Before 1.2.0 ActionCallback has to be defined like this:
// typealias ActionCallback = () -> Unit
//
//  onCopyRequested: ActionCallback?,
//  onPasteRequested: ActionCallback?,
//  onCutRequested: ActionCallback?,
//  onSelectAllRequested: ActionCallback?
) {
    println("showMenu")
    view.startActionMode(TextActionModeCallback())
}

class TextActionModeCallback(
) : ActionMode.Callback {
    override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
        println("onActionItemClicked $mode $item")
        return true
    }

    override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
        println("onActionItemClicked $mode $menu")
        return false
    }

    override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
        println("onActionItemClicked $mode $menu")
        return true
    }

    override fun onDestroyActionMode(mode: ActionMode?) {
        println("onActionItemClicked $mode")
    }
}

As a ref how to implement it, check out compose source code: AndroidTextToolbar and TextActionModeCallback

Then you can use it like this:

CompositionLocalProvider(
    LocalTextToolbar provides CustomTextToolbar(LocalView.current)
) {
    var text by remember { mutableStateOf("") }
    TextField(value = text, onValueChange = { text = it })
}

To make this work for the entire application, you need to do it at the top of the composables tree, for example in setContent.

p.s. I had to define ActionCallback, because it's internal in compose. I believe it's a bug, so I've created this issue. It was fixed in Compose 1.2.0-alpha05.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • 1
    The linked issue has been fixed with v1.2.0-alpha05 https://developer.android.com/jetpack/androidx/releases/compose-ui#1.2.0-alpha05 – Brian Mar 25 '22 at 21:26
  • I put all this in the code and when I long press on a text, hoping to get the popup menu, I do see prints of onActionItemClicked. How can I get the usual menu up? – LXJ Apr 04 '22 at 20:28
  • @LeoXJ As a ref how to implement it, check out compose source code: [AndroidTextToolbar](https://github.com/androidx/androidx/blob/3167373c96f8ab86f2d102b0e520cf185cf52b46/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidTextToolbar.android.kt#L32) and [TextActionModeCallback](https://github.com/androidx/androidx/blob/3167373c96f8ab86f2d102b0e520cf185cf52b46/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/actionmodecallback/TextActionModeCallback.android.kt#L30) – Phil Dukhov Apr 05 '22 at 01:40
  • That's almost a start from scratch? – LXJ Apr 06 '22 at 22:09
  • This kinda works, but if you have a keyboard connect this won't prevent from copy pasting. I haven't found a way to do it. :(. – Pedro Varela Aug 22 '23 at 15:38
  • @PedroVarela you can try ignoring `onValueChange` if diff is more than a single symbol - but it'll also will block keyboard suggestions from working – Phil Dukhov Aug 22 '23 at 16:07
  • @PhilDukhov if you connect a physical keyboard to your phone, or even running the emulator, a want to prevent the text in the input field from being copied, I think this is more a task of the clipboard manager, but I haven't read that. – Pedro Varela Aug 22 '23 at 20:55