Repository: kivy/kivy

Issue:
Textinput issue with unfocus / android
Here is a traceback i have by navigating between textinput and return key (escape):

```
I/python  (14720): [INFO              ] [Base        ] Leaving application in progress...
I/python  (14720):  Traceback (most recent call last):
I/python  (14720):    File "main.py", line 14, in <module>
I/python  (14720):    File "kivy/app.py", line 824, in run
I/python  (14720):    File "kivy/base.py", line 484, in runTouchApp
I/python  (14720):    File "kivy/core/window/window_pygame.py", line 381, in mainloop
I/python  (14720):    File "kivy/core/window/window_pygame.py", line 285, in _mainloop
I/python  (14720):    File "kivy/base.py", line 327, in idle
I/python  (14720):    File "kivy/base.py", line 312, in dispatch_input
I/python  (14720):    File "kivy/base.py", line 278, in post_dispatch_input
I/python  (14720):    File "kivy/_event.pyx", line 701, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6967)
I/python  (14720):    File "kivy/uix/behaviors.py", line 140, in on_touch_up
I/python  (14720):    File "kivy/_event.pyx", line 697, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6926)
I/python  (14720):    File "kivy/_event.pyx", line 1164, in kivy._event.EventObservers.dispatch (kivy/_event.c:11984)
I/python  (14720):    File "kivy/_event.pyx", line 1088, in kivy._event.EventObservers._dispatch (kivy/_event.c:11559)
I/python  (14720):    File "kivy/uix/textinput.py", line 1112, in _handle_released
I/python  (14720):    File "kivy/uix/textinput.py", line 1258, in _show_cut_copy_paste
I/python  (14720):  AttributeError: 'NoneType' object has no attribute 'size'
```



Discussion:
@akshayaurora @matham ?


Resolving patch:
diff --git a/kivy/uix/behaviors.py b/kivy/uix/behaviors.py
--- a/kivy/uix/behaviors.py
+++ b/kivy/uix/behaviors.py
@@ -494,7 +494,6 @@ class FocusButton(FocusBehavior, Button):
         future version.
     '''
 
-    _focus_win = None
     _requested_keyboard = False
     _keyboard = ObjectProperty(None, allownone=True)
     _keyboards = {}
@@ -756,17 +755,10 @@ def _on_focused(self, instance, value, *largs):
 
     def _ensure_keyboard(self):
         if self._keyboard is None:
-            win = self._focus_win
-            if not win:
-                self._focus_win = win = EventLoop.window
-            if not win:
-                Logger.warning('FocusBehavior: '
-                'Cannot focus the element, unable to get root window')
-                return
             self._requested_keyboard = True
             keyboard = self._keyboard =\
-                win.request_keyboard(self._keyboard_released, self,
-                                     input_type=self.input_type)
+                EventLoop.window.request_keyboard(
+                    self._keyboard_released, self, input_type=self.input_type)
             keyboards = FocusBehavior._keyboards
             if keyboard not in keyboards:
                 keyboards[keyboard] = None
diff --git a/kivy/uix/textinput.py b/kivy/uix/textinput.py
--- a/kivy/uix/textinput.py
+++ b/kivy/uix/textinput.py
@@ -377,7 +377,6 @@ class TextInput(FocusBehavior, Widget):
 
     def __init__(self, **kwargs):
         self.is_focusable = kwargs.get('is_focusable', True)
-        self._win = None
         self._cursor_blink_time = Clock.get_time()
         self._cursor = [0, 0]
         self._selection = False
@@ -582,7 +581,7 @@ def insert_text(self, substring, from_undo=False):
             if not substring:
                 return
 
-        self._hide_handles(self._win)
+        self._hide_handles(EventLoop.window)
 
         if not from_undo and self.multiline and self.auto_indent \
                 and substring == u'\n':
@@ -868,7 +867,7 @@ def delete_selection(self, from_undo=False):
         '''
         if self.readonly:
             return
-        self._hide_handles(self._win)
+        self._hide_handles(EventLoop.window)
         scrl_x = self.scroll_x
         scrl_y = self.scroll_y
         cc, cr = self.cursor
@@ -936,9 +935,8 @@ def _update_selection(self, finished=False):
     #
     def long_touch(self, dt):
         if self._selection_to == self._selection_from:
-            self._show_cut_copy_paste(self._long_touch_pos,
-                                      self._win,
-                                      mode='paste')
+            self._show_cut_copy_paste(
+                self._long_touch_pos, EventLoop.window, mode='paste')
 
     def on_double_tap(self):
         '''This event is dispatched when a double tap happens
@@ -1025,15 +1023,7 @@ def on_touch_down(self, touch):
         if self._touch_count == 4:
             self.dispatch('on_quad_touch')
 
-        win = self._win
-        if not win:
-            self._win = win = EventLoop.window
-        if not win:
-            Logger.warning('Textinput: '
-                           'Cannot show bubble, unable to get root window')
-            return True
-
-        self._hide_cut_copy_paste(self._win)
+        self._hide_cut_copy_paste(EventLoop.window)
         # schedule long touch for paste
         self._long_touch_pos = touch.pos
         Clock.schedule_once(self.long_touch, .5)
@@ -1076,7 +1066,7 @@ def on_touch_up(self, touch):
             self._selection_to = self.cursor_index()
             self._update_selection(True)
             # show Bubble
-            win = self._win
+            win = EventLoop.window
             if self._selection_to != self._selection_from:
                 self._show_cut_copy_paste(touch.pos, win)
             elif self.use_handles:
@@ -1109,7 +1099,8 @@ def _handle_released(self, instance):
 
         self._update_selection()
         self._show_cut_copy_paste(
-            (instance.center_x, instance.top + self.line_height), self._win)
+            (instance.center_x, instance.top + self.line_height),
+            EventLoop.window)
 
     def _handle_move(self, instance, touch):
         if touch.grab_current != instance:
@@ -1180,7 +1171,6 @@ def _position_handles(self, *args, **kwargs):
         handle_right.y = y - handle_right.height
 
     def _hide_handles(self, win=None):
-        win = win or self._win
         if win is None:
             return
         self.remove_widget(self._handle_right)
@@ -1191,7 +1181,7 @@ def _show_handles(self, dt):
         if not self.use_handles or not self.text:
             return
 
-        win = self._win
+        win = EventLoop.window
 
         handle_right = self._handle_right
         handle_left = self._handle_left
@@ -1239,7 +1229,8 @@ def _show_cut_copy_paste(self, pos, win, parent_changed=False,
             self._bubble = bubble = TextInputCutCopyPaste(textinput=self)
             self.bind(parent=partial(self._show_cut_copy_paste,
                                      pos, win, True))
-            self._win.bind(size=lambda *args: self._hide_cut_copy_paste(win))
+            EventLoop.window.bind(
+                size=lambda *args: self._hide_cut_copy_paste(win))
             self.bind(cursor_pos=lambda *args: self._hide_cut_copy_paste(win))
         else:
             self.remove_widget(bubble)
@@ -1318,12 +1309,7 @@ def _reload_remove_observer(wr):
     def _on_textinput_focused(self, instance, value, *largs):
         self.focus = value
 
-        win = self._win
-        if not win:
-            self._win = win = EventLoop.window
-        if not win:
-            Logger.warning('Textinput: unable to get root window')
-            return
+        win = EventLoop.window
         self.cancel_selection()
         self._hide_cut_copy_paste(win)
 
@@ -1337,7 +1323,6 @@ def _on_textinput_focused(self, instance, value, *largs):
         else:
             Clock.unschedule(self._do_blink_cursor)
             self._hide_handles(win)
-            self._win = None
 
     def _ensure_clipboard(self):
         global Clipboard
@@ -1930,7 +1915,7 @@ def keyboard_on_key_down(self, window, keycode, text, modifiers):
         # Keycodes on OSX:
         ctrl, cmd = 64, 1024
         key, key_str = keycode
-        win = self._win
+        win = EventLoop.window
 
         # This allows *either* ctrl *or* cmd, but not both.
         is_shortcut = (modifiers == ['ctrl'] or (


Test patch: