Repository: kivy/kivy

Issue:
the textinput widget's cursor_blink property can't be set
* Python: 3.6
* OS: Windows 7
* Kivy: 1.10
* Kivy installation method: pip

According to the docs the TextInput widget's cursor_blink property is automatically set and can't be changed. (And the docs are right, 'cos I tried setting it to False.)

I have told Windows not to blink the cursor, but Kivy ignores this even though all other Windows applications respect it.

Some people cannot work with blinking cursors, so it ought to be possible to switch it off -- or at least to respect the user preferences.

See http://www.jurta.org/en/prog/noblink


Discussion:
The CodeInput widget suffers from the same problem, and doesn't seem to have a cursor_blink property.

Resolving patch:
diff --git a/kivy/uix/textinput.py b/kivy/uix/textinput.py
--- a/kivy/uix/textinput.py
+++ b/kivy/uix/textinput.py
@@ -1765,13 +1765,26 @@ def _get_text_width(self, text, tab_width, _label_cached):
         Cache_append('textinput.width', cid, width)
         return width
 
+    def on_cursor_blink(self, instance, value):
+        # trigger blink event reset to switch blinking while focused
+        self._reset_cursor_blink()
+
     def _do_blink_cursor(self, dt):
+        if not self.cursor_blink:
+            # ignore event if not triggered,
+            # stop if cursor_blink value changed right now
+            if self._do_blink_cursor_ev.is_triggered:
+                self._do_blink_cursor_ev.cancel()
+            # don't blink, make cursor visible
+            self._cursor_blink = False
+            return
+
         # Callback for blinking the cursor.
-        self.cursor_blink = not self.cursor_blink
+        self._cursor_blink = not self._cursor_blink
 
     def _reset_cursor_blink(self, *args):
         self._do_blink_cursor_ev.cancel()
-        self.cursor_blink = 0
+        self._cursor_blink = False
         self._do_blink_cursor_ev()
 
     def on_cursor(self, instance, value):
@@ -2496,6 +2509,7 @@ def _refresh_hint_text(self):
     _editable = BooleanProperty(True)
     _insert_int_pat = re.compile(u'^-?[0-9]*$')
     _insert_float_pat = re.compile(u'^-?[0-9]*\\.?[0-9]*$')
+    _cursor_blink = BooleanProperty(False)
 
     readonly = BooleanProperty(False)
     '''If True, the user will not be able to change the content of a textinput.
@@ -2556,13 +2570,18 @@ def _refresh_hint_text(self):
     and defaults to True.
     '''
 
-    cursor_blink = BooleanProperty(False)
-    '''This property is used to blink the cursor graphic. The value of
-    :attr:`cursor_blink` is automatically computed. Setting a value on it will
-    have no impact.
+    cursor_blink = BooleanProperty(True)
+    '''This property is used to set whether the graphic cursor should blink
+    or not.
+
+    .. versionchanged:: 1.10.1
+        `cursor_blink` has been refactored to enable switching the blinking
+        on/off and the previous behavior has been moved to a private
+        `_cursor_blink` property. The previous default value `False` has been
+        changed to `True`.
 
     :attr:`cursor_blink` is a :class:`~kivy.properties.BooleanProperty` and
-    defaults to False.
+    defaults to True.
     '''
 
     def _get_cursor(self):


Test patch:
diff --git a/kivy/tests/test_uix_textinput.py b/kivy/tests/test_uix_textinput.py
--- a/kivy/tests/test_uix_textinput.py
+++ b/kivy/tests/test_uix_textinput.py
@@ -7,6 +7,7 @@
 
 from kivy.tests.common import GraphicUnitTest
 from kivy.uix.textinput import TextInput
+from kivy.clock import Clock
 
 
 class TextInputTest(unittest.TestCase):
@@ -322,6 +323,36 @@ def test_cursor_movement_control(self):
 
             ti._key_up((None, None, 'ctrl_L', 1), repeat=False)
 
+    def test_cursor_blink(self):
+        ti = TextInput(cursor_blink=True)
+        ti.focus = True
+
+        # overwrite blinking event, because too long delay
+        ti._do_blink_cursor_ev = Clock.create_trigger(
+            ti._do_blink_cursor, 0.01, interval=True
+        )
+
+        self.render(ti)
+
+        # from kwargs cursor_blink == True
+        self.assertTrue(ti.cursor_blink)
+        self.assertTrue(ti._do_blink_cursor_ev.is_triggered)
+
+        # set whether to blink & check if resets
+        ti.cursor_blink = False
+        for i in range(30):
+            self.advance_frames(int(0.01 * Clock._max_fps) + 1)
+            self.assertFalse(ti._do_blink_cursor_ev.is_triggered)
+
+            # no blinking, cursor visible
+            self.assertFalse(ti._cursor_blink)
+
+        ti.cursor_blink = True
+        self.assertTrue(ti.cursor_blink)
+        for i in range(30):
+            self.advance_frames(int(0.01 * Clock._max_fps) + 1)
+            self.assertTrue(ti._do_blink_cursor_ev.is_triggered)
+
 
 if __name__ == '__main__':
     import unittest