Floating toolbar for text selection Jetpack Compose

Viewed 1287

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.

1 Answers

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.

Related