17

I'm writing an app using Kivy framework and I stumbled upon a minor but annoying problem: I don't know how to handle Tab/Enter/Arrow keys in text fields so that pressing either of them would dispatch an event, eg. switch the focus (jump) to another TextInput or launch something like send_form()

Could anyone please shed some light on this issue?

Leri
  • 12,367
  • 7
  • 43
  • 60
minder
  • 2,059
  • 5
  • 24
  • 39
  • 2
    I don't think there's a lot of support for that kind of thing in Kivy right now, they seem to be mainly touch focused. You could submit a feature request, or consider modifying kivy's TextInput class yourself; [start here](https://github.com/kivy/kivy/blob/master/kivy/uix/textinput.py), see line 1266 (elif key == 9: # tab). Fire your own custom event in there maybe? – Daniel Kinsman Aug 21 '12 at 04:50

4 Answers4

49

Kivy 1.9 provides the ability to set write_tab: False on text inputs (see docs), causing the tab key to focus on the next focusable widget.

Kivy allows the Enter key to dispatch events by setting multiline: False and on_text_validate: root.foo().

So, to create a text input widget that has the desired Enter and Tab functionality, do as follows:

TextInput:
    write_tab: False
    multiline: False
    on_text_validate: root.foo()
mcastle
  • 2,882
  • 3
  • 25
  • 43
  • 1
    Thanks, even though it doesn't work for Android (as in `Kivy==1.10.0`). – Andre Miras Jul 20 '17 at 20:14
  • This helped a lot! Thank you! For anyone else wondering if kivymd's text inputs inherit from the kivy TextInput: the answer is yes. – tafaust Nov 14 '20 at 10:07
  • Thanks , will it also enable "shift+tab" to move in opposite direction i.e. previous focusable widget? I tried but only working for forward direction. – Neeraj Jul 11 '21 at 02:45
9

Just found this old question and figured I would contribute. I also needed tab / enter to go to the next field. I did what @tshirtman suggested. This is my custom TextInput class:

from kivy.uix.textinput import TextInput


class TabTextInput(TextInput):

    def __init__(self, *args, **kwargs):
        self.next = kwargs.pop('next', None)
        super(TabTextInput, self).__init__(*args, **kwargs)

    def set_next(self, next):
        self.next = next

    def _keyboard_on_key_down(self, window, keycode, text, modifiers):
        key, key_str = keycode
        if key in (9, 13) and self.next is not None:
            self.next.focus = True
            self.next.select_all()
        else:
            super(TabTextInput, self)._keyboard_on_key_down(window, keycode, text, modifiers)

This allows you to pass next when you instantiate the input, or alternatively call set_next on an existing input.

9 and 13 are the key codes for tab and enter.

Works well for me.

dgel
  • 16,352
  • 8
  • 58
  • 75
  • Oh, _keyboard_on_key_down method, just I need it! Thanks! – swietyy Aug 20 '13 at 21:05
  • So I have a TextInput instance with a call: on_text_validate: root.change_context(). How should I change this (in my kv file) into a TabTextInput so that my textinput field behaves in multiline mode? – Bill Bridge Mar 28 '17 at 06:23
5

As suggested by Daniel Kinsman in his comment, you could subclass TextInput, add "previous" and "next" ObjectProperties for tab support (easy to set in kv using references to other widgets), and handle the keyboard events differently. There is no out of the box support for this right now, but if you want to work on such modification drop us a feature-request or comme discuss it in #kivy on freenode.

https://github.com/kivy/kivy/blob/master/kivy/uix/textinput.py#L1188

Maybe it would be even better to add such support on widget, and add some focus logic, so tab/enter have effects on any activable widget, and some widgets like slider use right/left/up/down keys too.

So there is still a lot to do in Kivy about that, and if you are interested in helping, you can really make it happen faster, we'll help you :)

Tshirtman
  • 5,859
  • 1
  • 19
  • 26
  • I do really need keyboard support, i.e. activate (clik) a widget when hitting "F1" or "" or "Ctrl+T", move with arrows between options on Spinner etc. This would improve usability on "normal" PCs. I'll try to help :) – minder Aug 26 '12 at 08:07
  • 3
    @tshirtman, similar to this topic, in Kivy's docuementation ` `on_text_validate Fired only in multiline=False mode, when the user hits 'enter'. This will also unfocus the textinput.`. How can I keep the focus after the enter key is clicked. – securecurve Jan 28 '13 at 00:19
0

[Insufficient points to just comment, so adding this here...]

It's crucial to note that the keyboard NEXT behavior only works easily if the next field is managed by the same keyboard layout. However, an advanced app will have:

  • username (qwerty)
  • password (password)
  • ID (numeric) etc

So the approaches above really doesn't work out.

In the kv file:

    MyTextInput:
        next: idTheNextFieldBelowThis

In your MyTextInput class:

    def insert_text(self, value, from_undo=False):
        #
        # Unfortunately the TextInput write_tab behavior only works if the next field is the same exact keyboard
        # type.
        #
        if not value[-1:] == '  ':
            return super(MyTextInput, self).insert_text(value, from_undo=from_undo)
        r = super(MyTextInput, self).insert_text(value[:-1], from_undo=from_undo)
        if self.next is not None:
            self.next.focus = True
        return r