I am (unsuccessfully) searching for how to e.g. op...
# compose-desktop
d
I am (unsuccessfully) searching for how to e.g. open a popup, if the user inside a textfield presses a keyboard shortcut like ctrl+space. Has anybody a codesnippet on how to "do" something if the user presses some kind of shortcut key combination while inside a Component? thanx (also am totally clueless on how I could open the popup below the current cursor position)
t
I think you can use
Modifier.keyEvent
to track the key presses.
message has been deleted
the
KeyEvent
has flags to identify simltns key presses.
โœ… 2
Here's one complete example to detect
Control + Space
๐Ÿ‘ 1
i
Example:
Copy code
import androidx.compose.desktop.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.plus
import androidx.compose.ui.input.key.shortcuts
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round

fun main() = Window {
    var value: TextFieldValue by remember { mutableStateOf(TextFieldValue("")) }
    var textLayoutResult: TextLayoutResult? by remember { mutableStateOf(null) }
    var isPopupShowed: Boolean by remember { mutableStateOf(false) }

    fun TextLayoutResult.currentCursor() = getCursorRect(
        value.selection.end.coerceAtMost(layoutInput.text.length)
    ).topRight.round()

    val popupCoordinates by remember {
        derivedStateOf { textLayoutResult?.currentCursor() }
    }

    Box {
        BasicTextField(
            value,
            { value = it },
            modifier = Modifier.shortcuts {
                on(Key.CtrlLeft + Key.Spacebar) {
                    isPopupShowed = !isPopupShowed
                }
            },
            onTextLayout = {
                textLayoutResult = it
            }
        )

        if (popupCoordinates != null && isPopupShowed) {
            Box(Modifier.offset { popupCoordinates!! }) {
                Box(Modifier.requiredSize(100.dp, 25.dp).background(Color.Red))
            }
        }
    }
}
1. You can use desktop-only
Modifier.shortcuts
instead of
onKeyEvent
2. There is an issue - shortcuts/onKeyEvent don't intercept keys, they will be dispatched to the BasicTextField (when we press ctrl-space we will show popup and type "space" in the field). Probably it is a bug. 3. You can't use just
TextField
for now. It doesn't have the ability to set
onTextLayout
. You have to use
BasicTextField
4. You can't use
Popup
too, because it will intercept all key events (we fill fix this in the future). But you can use simple
Box(Modifier.offset)
for now.
d
ah, cool ... (but Modifier.shortcuts would make the Component incompatible when deployed to web or android, right?)
t
@Igor Demin Wow, that's much btter.
i
Oh, right. It is news for me that we have
isCtrlPressed
in the common code, keys is not my expertise ๐Ÿ™‚. In this case, I think it is better to use
onKeyEvent
@Andrew Rudenko [JB] probably can tell more about cases when we should use
Modifier.shortcuts
a
I think we are going to deprecate
Modifier.shortcuts
There is an issue - shortcuts/onKeyEvent don't intercept keys, they will be dispatched to the BasicTextField (when we press ctrl-space we will show popup and type "space" in the field). Probably it is a bug.
you can intercept events with
Modifier.onPreviewKeyEvent
๐Ÿ‘ 1
@Dirk Hoffmann did you delete your last message? ) because I prepared an answer ๐Ÿ™‚
d
realised that
Copy code
val isCtrlSpacePressed = keyEvent.type == KeyEventType.KeyDown && keyEvent.isCtrlPressed && keyEvent.nativeKeyEvent.keyCode == NativeKeyEvent.VK_SPACE
                if(isCtrlSpacePressed) {
                    isPopupShowed = !isPopupShowed
                    true
on pressing Ctrl. space there are more than on keyEvent coming ... and only one of them makes the if true so no wonder the space is propagated ... still would be curious on your prepared answer ๐Ÿ™‚
a
On desktop there are three events are generated (by AWT): down, typed and released. In this case text field is listening for typed event. But typed events do not have "proper" keyCodes, so it's not consumed by your code. A working (and cross platform, it should work on android too) code snippet is:
Copy code
BasicTextField(
            value,
            onValueChange = { value = it },
            modifier = Modifier.onPreviewKeyEvent {
                if ((it.key == Key.Spacebar || it.utf16CodePoint == ' '.toInt()) && it.isCtrlPressed) {
                    if (it.type == KeyEventType.KeyDown) {
                        println("fancy stuff")
                    }
                    true
                } else {
                    false
                }
            }
        )
I understand that it could be confusing, but that's how it's currently working...
d
AWT ... I know ... that's the reason why the size of Spaceshuttle's Boosters are directly correlated to the size of roman horse arses ...
๐Ÿ˜„ 1
hmmm, the TAB key never "arrives" at the (Basic)TextField or is there a way to get the TAB key not be "eaten up" by something above (for Focus, I guess)