Kivy TextInput horizontal and vertical align (centering text)

Caaarlos picture Caaarlos · Nov 8, 2016 · Viewed 9.2k times · Source

How to center a text horizontally in a TextInput in Kivy?

I have the following screen: wrong screen

But I want to centralize my text like this: right screen

And this is part of my kv language:

        BoxLayout:  
            orientation: 'vertical'
            Label:
                markup: True
                text: '[b] Type something... [/b]'
                size_hint: 1, 0.6
                size: self.parent.size[0], 200
                font_size: self.size[0] * 0.1
                text_size: self.size
                halign: 'center'
                valign: 'middle'

                canvas.before:
                    Color:
                        rgb: 0, 0, 204
                    Rectangle:
                        pos: self.pos
                        size: self.size

            TextInput:
                focus: True

How can I center my TextInput's text?

Answer

Peter Badida picture Peter Badida · Nov 8, 2016

Afaik, there's no such thing as aligning in the same way as it's in Label, however, you can use padding to push the position wherever you want. Keep in mind that changing the size of text will affect the centering, therefore you'll need to recalculate on change of size (e.g. when working with multiple devices, sizes, etc).

Or there could be even a workaround, where you could make TextInput invisible, use Label to fetch touch event to trigger TextInput (which would open keyboard) and change Label's text on change of TextInput's text property. You'll lose the possibility to work with cursor this way and you'll need to handle wrapping text.

Example:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
<Test>:
    TextInput:
        text: 't'
        font_size: 60
        # left, right
        padding_x:
            [self.center[0] - self._get_text_width(max(self._lines, key=len), self.tab_width, self._label_cached) / 2.0,
            0] if self.text else [self.center[0], 0]
        # top, bottom
        padding_y: [self.height / 2.0 - (self.line_height / 2.0) * len(self._lines), 0]
''')
class Test(BoxLayout):
    pass
class TestApp(App):
    def build(self):
        return Test()
TestApp().run()

self._get_text_width(...) is obviously a method of a TextInput. It's working with the core of the widget so it might be unstable (first example I posted was buggy because of my mistake) ^^

Now if padding_x's values padd from left and right, you'll need only the left side (the difference is only in using addition and substraction in the right place), so let's do that:

  1. get the longest substring in the TextInput
  2. get its width (because it's not consistent!) via the fancy method
  3. substract from center[0] coordinate

When we've already centered the X axis, let's go to the Y. padding_y's values are top and bottom:

  1. padd down the half of the widget's height
  2. get a half of a height of a single line
  3. multiply the number by the count of lines that are in TextInput
  4. substract the number from self.height / 2.0
  5. bottom is 0, we don't care about it

Note: max() expects some arguments and if there's no text, max() will raise its voice. We'll shut it with an alternative left padding for padding_x using only the center:

<padding_x with max> if self.text else [self.center[0], 0]