Search code examples
pythonkivykivy-languagecursor-positionkivymd

Move cursor to final position of string in a MDTextField


I am trying to give a date format to a MDTextField on an app I am developing with KivyMD. The format of such field such be 'dd/mm/yyyy'.

What I am trying to do is that once the first 2 numbers are written, a '/' will be written automatically and the cursor will jump to the last position, right of the '/' (e.g. '21/'). In the same manner, after writting other 2 numbers after the first '/', a second '/' will be written and the cursor will move to the end again (e.g. '21/09/').

I have managed to make both '/' appear, nevertheless, it is the cursor I am unable to place on the desired location. My code is the following:

def apply_date_format(self):
    # delete '/' if len is equal or less than 2 and final character is /  
    if len(self.ids.viajeInicio.text) =< 2 and (self.ids.viajeInicio.text).endswith('/'):
        self.ids.viajeInicio.text = (self.ids.viajeInicio.text[:-1])
    # first '/'
    elif len(self.ids.viajeInicio.text) == 2 and (self.ids.viajeInicio.text).isnumeric():
        self.ids.viajeInicio.text= self.ids.viajeInicio.text + "/"
    # second '/'
    elif len(self.ids.viajeInicio.text) == 5 and (self.ids.viajeInicio.text[3:5]).isnumeric():
        self.ids.viajeInicio.text= self.ids.viajeInicio.text + "/"
    # delete last '/' if len is <= 5 and last character is '/'
    elif len(self.ids.viajeInicio.text) > 3 and len(self.ids.viajeInicio.text) <= 5 \
             and (self.ids.viajeInicio.text).endswith('/'):
        self.ids.viajeInicio.text = (self.ids.viajeInicio.text[:-1])

The MDTextField has the id viajeInicio and the function apply_date_format is called on the on_text event. The code is the following:

MDTextField:
    id: viajeInicio
    hint_text: 'Ingresar Fecha de Inicio del Viaje'
    pos_hint: {"x":0, "top":1}
    helper_text: 'Formato de fecha: dd/mm/aaaa'
    helper_text_mode: 'on_focus'
    required: True
    on_text:
        root.apply_date_format()

How can I move the cursor position to the end of the string after the '/' are written. Moreover, is there a better way to complete the desired task?

Thanks a lot in advance


Solution

  • I think it is simpler to just extend the MDTextField class and over-ride it's insert_text() method. Something like this:

    class DateMDTextField(MDTextField):
        def insert_text(self, the_text, from_undo=False):
            if the_text == '/':
                # do not allow typed in '/'
                return
            cc, cr = self.cursor
            cur_text = self._lines[cr] + the_text  # existing text plus the to be inserted the_text
            cur_len = len(cur_text)
    
            # new_text will be inserted. The default is to just use the_text
            new_text = the_text
    
            # delete '/' if len is equal or less than 2 and final character is /
            if cur_len <= 2 and cur_text.endswith('/'):
                new_text = new_text[:-1]
            # first '/'
            elif cur_len == 2 and cur_text.isnumeric():
                new_text += '/'
            # second '/'
            elif cur_len == 5 and cur_text[3:5].isnumeric():
                new_text += '/'
            # delete last '/' if len is <= 5 and last character is '/'
            elif cur_len > 3 and cur_len <= 5 and cur_text.endswith('/'):
                new_text = new_text[:-1]
            # do not allow extra characters
            elif cur_len > 10:
                return
    
            # call the insert_text() of the MDTextField with the possibly modified text
            super(DateMDTextField, self).insert_text(new_text, from_undo=from_undo)
    

    The above code uses your logic (with a couple small additions), and since it just ends up calling the insert_text() of the MDTextField, all the cursor movement is handled for you.

    So you can just replace:

    MDTextField:
        id: viajeInicio
        hint_text: 'Ingresar Fecha de Inicio del Viaje'
        pos_hint: {"x":0, "top":1}
        helper_text: 'Formato de fecha: dd/mm/aaaa'
        helper_text_mode: 'on_focus'
        required: True
        on_text:
            root.apply_date_format()
    

    with:

    DateMDTextField:
        id: viajeInicio
        hint_text: 'Ingresar Fecha de Inicio del Viaje'
        pos_hint: {"x":0, "top":1}
        helper_text: 'Formato de fecha: dd/mm/aaaa'
        helper_text_mode: 'on_focus'
        required: True
    

    in your 'kv'. You do not need to explicitly call the insert_text() method as it is automatically called by the baseclass TextInput. And you also do not need the on_text entry.

    And you no longer need the apply_date_format() method.