2323from ..events import Blur , Focus , Mount
2424from ..geometry import Offset , Size
2525from ..message import Message
26- from ..reactive import reactive , var
26+ from ..reactive import Reactive , reactive , var
2727from ..suggester import Suggester , SuggestionReady
2828from ..timer import Timer
2929from ..validation import ValidationResult , Validator
@@ -174,7 +174,7 @@ class Input(Widget, can_focus=True):
174174 cursor_blink = reactive (True , init = False )
175175 value = reactive ("" , layout = True , init = False )
176176 input_scroll_offset = reactive (0 )
177- cursor_position = reactive (0 )
177+ cursor_position : Reactive [ int ] = reactive (0 )
178178 view_position = reactive (0 )
179179 placeholder = reactive ("" )
180180 complete = reactive ("" )
@@ -335,8 +335,11 @@ def __init__(
335335 elif self .type == "number" :
336336 self .validators .append (Number ())
337337
338+ self ._initial_value = True
339+ """Indicates if the value has been set for the first time yet."""
338340 if value is not None :
339341 self .value = value
342+
340343 if tooltip is not None :
341344 self .tooltip = tooltip
342345
@@ -391,8 +394,8 @@ def _watch_cursor_blink(self, blink: bool) -> None:
391394 if blink :
392395 self ._blink_timer .resume ()
393396 else :
397+ self ._pause_blink_cycle ()
394398 self ._cursor_visible = True
395- self ._blink_timer .pause ()
396399
397400 @property
398401 def cursor_screen_offset (self ) -> Offset :
@@ -412,6 +415,11 @@ def _watch_value(self, value: str) -> None:
412415 )
413416 self .post_message (self .Changed (self , value , validation_result ))
414417
418+ # If this is the first time the value has been updated, set the cursor position to the end
419+ if self ._initial_value :
420+ self .cursor_position = len (self .value )
421+ self ._initial_value = False
422+
415423 def _watch_valid_empty (self ) -> None :
416424 """Repeat validation when valid_empty changes."""
417425 self ._watch_value (self .value )
@@ -506,32 +514,25 @@ def _toggle_cursor(self) -> None:
506514 """Toggle visibility of cursor."""
507515 self ._cursor_visible = not self ._cursor_visible
508516
509- def _on_mount (self , _ : Mount ) -> None :
517+ def _on_mount (self , event : Mount ) -> None :
510518 self ._blink_timer = self .set_interval (
511519 0.5 ,
512520 self ._toggle_cursor ,
513521 pause = not (self .cursor_blink and self .has_focus ),
514522 )
515523
516- def _on_blur (self , _ : Blur ) -> None :
517- assert self ._blink_timer is not None
518- self ._blink_timer .pause ()
524+ def _on_blur (self , event : Blur ) -> None :
525+ self ._pause_blink_cycle ()
519526 if "blur" in self .validate_on :
520527 self .validate (self .value )
521528
522- def _on_focus (self , _ : Focus ) -> None :
523- assert self ._blink_timer is not None
524- self .cursor_position = len (self .value )
525- if self .cursor_blink :
526- self ._blink_timer .resume ()
529+ def _on_focus (self , event : Focus ) -> None :
530+ self ._restart_blink_cycle ()
527531 self .app .cursor_position = self .cursor_screen_offset
528532 self ._suggestion = ""
529533
530534 async def _on_key (self , event : events .Key ) -> None :
531- assert self ._blink_timer is not None
532- self ._cursor_visible = True
533- if self .cursor_blink :
534- self ._blink_timer .reset ()
535+ self ._restart_blink_cycle ()
535536
536537 if event .is_printable :
537538 event .stop ()
@@ -562,11 +563,29 @@ async def _on_click(self, event: events.Click) -> None:
562563 else :
563564 self .cursor_position = len (self .value )
564565
566+ async def _on_mouse_down (self , event : events .MouseDown ) -> None :
567+ self ._pause_blink_cycle ()
568+
569+ async def _on_mouse_up (self , event : events .MouseUp ) -> None :
570+ self ._restart_blink_cycle ()
571+
565572 async def _on_suggestion_ready (self , event : SuggestionReady ) -> None :
566573 """Handle suggestion messages and set the suggestion when relevant."""
567574 if event .value == self .value :
568575 self ._suggestion = event .suggestion
569576
577+ def _restart_blink_cycle (self ) -> None :
578+ """Restart the cursor blink cycle."""
579+ self ._cursor_visible = True
580+ if self .cursor_blink and self ._blink_timer :
581+ self ._blink_timer .reset ()
582+
583+ def _pause_blink_cycle (self ) -> None :
584+ """Hide the blinking cursor and pause the blink cycle."""
585+ self ._cursor_visible = False
586+ if self .cursor_blink and self ._blink_timer :
587+ self ._blink_timer .pause ()
588+
570589 def insert_text_at_cursor (self , text : str ) -> None :
571590 """Insert new text at the cursor, move the cursor to the end of the new text.
572591
@@ -739,7 +758,8 @@ def action_delete_left_word(self) -> None:
739758 self .cursor_position = 0
740759 else :
741760 self .cursor_position = hit .start ()
742- self .value = f"{ self .value [: self .cursor_position ]} { after } "
761+ new_value = f"{ self .value [: self .cursor_position ]} { after } "
762+ self .value = new_value
743763
744764 def action_delete_left_all (self ) -> None :
745765 """Delete all characters to the left of the cursor position."""
0 commit comments