Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

keyboard issue when switching between TextInput widgets on android #7698

Open
ghost opened this issue Nov 19, 2021 · 5 comments
Open

keyboard issue when switching between TextInput widgets on android #7698

ghost opened this issue Nov 19, 2021 · 5 comments
Labels
Component: input kivy/input Platform: Android Status: Needs-analysis Issue needs to be analyzed if it's real

Comments

@ghost
Copy link

ghost commented Nov 19, 2021

Software Versions

  • Python:3.7
  • OS: android 11
  • Kivy: 2.0.0
  • Kivy installation method:
    using python for android

Describe the bug
when we have more then on TextInput in same screen on android the keyboard some times not appears when switching directly from TextInput to another only work fine if I close the keyboard and focus the other one

Code and Logs and screenshots

from kivy.core.window import Window
from kivy.lang import Builder
from kivymd.app import MDApp

kv = '''
BoxLayout:
    orientation:'vertical'
    spacing:'20dp'
    padding:'20dp'

    TextInput
    TextInput
    TextInput
    TextInput
'''


class TestApp(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Window.keyboard_anim_args = {"d": 0.2, "t": "linear"}
        Window.softinput_mode = "below_target"

    def build(self):
        return Builder.load_string(kv)


TestApp().run()

Additional context

the video will show the issue on android

Screen_Recording_20211119-184400_Pydroid.3.mp4
@misl6 misl6 added Component: input kivy/input Platform: Android Status: Needs-analysis Issue needs to be analyzed if it's real labels Nov 19, 2021
@FilipeMarch
Copy link
Contributor

FilipeMarch commented May 11, 2022

Hello, I have just fixed this bug here using the keyboard_mode 'managed'. The problem happens because on_focus from each textfield is calling show_keyboard() or hide_keyboard(), but they behave like out-of-sync dancers.

When you click on TextInput 1, android keyboard appears (it executes show_keyboard())
When you click outside TextInput 1, android keyboard disappears (it executes hide_keyboard())

When you click on textfield_1 and then you click on textfield_2, you have two possibilities:

POSSIBILITY ONE:
textfield_1.focus becomes False
textfield_2.focus becomes True

OR

POSSIBILITY TWO:
textfield_2.focus becomes True
textfield_1.focus becomes False

Isn't the two possibilities the same?
No! The order is different and this makes total difference. I can't determine why for some people textfield_1 becomes unfocused before focusing on textfield_2, and for some people textfield_1 becomes unfocused after focusing on textfield_2 (causing the bug, because textfield_1 will call hide_keyboard() when unfocused, and your android keyboard will close).

The solution I found was modifying the on_touch_down function with a simple decorator that checks the touch position (touch.pos) and update self.focus accordingly. Also, I delay the show_keyboard() function in 100ms, so it will be certain that the other textfield has already been unfocused and dispatched its events.

from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.factory import Factory as F
from kivy.uix.textinput import TextInput
from kivy.core.window import Window


Window.softinput_mode = "below_target"


class TextInputFixed(TextInput):
    def on_focus(self, instance, value, *args):
        if value:
            Clock.schedule_once(self.create_keyboard, .1)
        else:
            self.hide_keyboard()

    def create_keyboard(self, *args):
        self.show_keyboard()

    def remove_focus_decorator(function):
        def wrapper(self, touch):
            if not self.collide_point(*touch.pos):
                self.focus = False
            function(self, touch)
        return wrapper

    @remove_focus_decorator
    def on_touch_down(self, touch):
        super().on_touch_down(touch)

class ScreenOne(F.Screen):
    pass


Builder.load_string("""
<ScreenOne>:
    BoxLayout:
        orientation: 'vertical'
        Button:
        Label:
            text: 'Username:'
            size_hint_y: None
            height: self.texture_size[1]
        TextInputFixed:
            pos_hint: {'center_x': .5}
            foreground_color: 1,0,0,1
            keyboard_mode: 'managed'
            on_text_validate: tf.focus = True
            multiline: False
        Label:
            text: 'Password:'
            size_hint_y: None
            height: self.texture_size[1]
        TextInputFixed:
            id: tf
            pos_hint: {'center_x': .5}
            keyboard_mode: 'managed'
        Widget:
""")

class MainApp(App):
    def build(self):
        self.screen_one = ScreenOne()
        return ScreenOne()

MainApp().run()
record-20220511-090709.mp4

@HyTurtle
Copy link

HyTurtle commented May 11, 2022

https://github.com/kivy/kivy/blob/master/kivy/uix/behaviors/focus.py#L400

Can you confirm the issue is here? Would explain why (prior to example above) it wasn't replicated. Or were you seeing the same prior to setting keyboard_mode to managed?

@FilipeMarch
Copy link
Contributor

FilipeMarch commented May 11, 2022

https://github.com/kivy/kivy/blob/master/kivy/uix/behaviors/focus.py#L400

Can you confirm the issue is here? Would explain why (prior to example above) it wasn't replicated. Or were you seeing the same prior to setting keyboard_mode to managed?

Yes, it is happening due to _on_focus being called by two TextInputs in an unorganized and non-deterministic manner. I was printing who was executing these functions first, and it was a complete mess!

Sometimes T2 would say "I will create keyboard now", and afterwards T1 would say "I will destroy keyboard now", but the order should always be 1) The keyboard disappears; 2) The keyboard appears.

But Eventloop.window.request_keyboard has a callback that executes self.focus = False (imagine what 'auto' will do when this happens), so whenever the android keyboard was requested by one textfield (the smartphone blinks, closing the previous keyboard and adding a new one instantly), the other textfield changes focus afterwards, due to Event.loop callback and it kills the android keyboard.

I have never used keyboard_mode 'manage' before and the bug was always happening on my current phone, but other users haven't reported the same issue until this week. I didn't even knew it 'manage' existed. During this night I was able to reproduce the issue both on computer and smartphone deterministically, and the code I posted prevented the bug on both.

Sorry if my explanation is not the best, but using the debugger makes it clear what is really happening there, probably there is a better fix but I will use this workaround ('manage') for now

@HyTurtle
Copy link

HyTurtle commented May 11, 2022

Not seeing where your debug prints were sort of limits the usefulness; it probably is somewhat a race condition - needing a wait for the subsequent request yea would've been how I would've interpreted what you were seeing too. Did you try with 0 there instead of .1, as in schedule it for the next clock tick. In which case probably no harm PR'ing that...

On the other hand/further thinking; if that retains the issue then presumably due to it being a property change; on two widgets both being propagated on next tick (which I believe is the case now, rather than an immediate firing) with the order being unspecified.

@FilipeMarch
Copy link
Contributor

I've tested with Clock.schedule_once(self.create_keyboard), but then the android keyboard disappears when you click on two textfields consecutively. Clock.schedule_once(self.create_keyboard, .05) still had the same problem, and Clock.schedule_once(self.create_keyboard, .075) works fine, Clock.schedule_once(self.create_keyboard, .1) seems safer and nice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: input kivy/input Platform: Android Status: Needs-analysis Issue needs to be analyzed if it's real
Projects
None yet
Development

No branches or pull requests

3 participants