Source code for simvx.core.ui.command_line
"""CommandLine: a single-line text field that remembers the commands entered into it."""
from __future__ import annotations
from ..decorators import on_gui_input
from ..descriptors import Property
from ..input.controls import Key
from .widgets import TextEdit
__all__ = ["CommandLine"]
[docs]
class CommandLine(TextEdit):
"""A single-line command entry with history, the input line of a console or a REPL.
Enter submits the line through ``text_submitted``, empty or not, ends the
edit session as :class:`TextEdit` does (``editing_finished`` when the
line changed), records a non-empty line in :attr:`history` and clears the
field for the next command. Up walks back through earlier commands and Down forward again,
ending on the line that was being typed when Up was first pressed. Both
repeat while held, and the line keeps them even at either end of the
history, so an arrow typed into it never moves focus or reaches a game.
Example::
line = CommandLine(placeholder="Enter command...")
line.text_submitted.connect(run_command)
"""
max_history = Property(100, range=(1, 10000), hint="How many commands Up and Down can recall")
def __init__(self, text: str = "", placeholder: str = "", **kwargs):
super().__init__(text, placeholder, **kwargs)
self._history: list[str] = []
# Where Up and Down have walked to in the history; -1 is the line being typed.
self._recall = -1
self._draft = ""
[docs]
@property
def history(self) -> tuple[str, ...]:
"""The commands entered, oldest first: at most ``max_history`` of them."""
return tuple(self._history)
@on_gui_input(Key.ENTER)
def _submit(self, event) -> bool:
command = self.text
if command:
self._history.append(command)
del self._history[: -self.max_history]
self._recall = -1
self._draft = ""
self.text_submitted.emit(command)
self._commit()
self._show("")
self._snapshot_committed()
return True
@on_gui_input(Key.UP, repeat=True)
def _older(self, event) -> bool:
if self._history:
if self._recall == -1:
self._draft = self.text
self._recall = len(self._history) - 1
elif self._recall > 0:
self._recall -= 1
self._show(self._history[self._recall])
return True
@on_gui_input(Key.DOWN, repeat=True)
def _newer(self, event) -> bool:
if self._recall != -1:
if self._recall < len(self._history) - 1:
self._recall += 1
self._show(self._history[self._recall])
else:
self._recall = -1
self._show(self._draft)
return True
def _show(self, text: str) -> None:
"""Put *text* in the field with the caret at its end."""
if text != self.text:
self.text = text
self.text_changed.emit(text)
self.cursor_pos = len(text)
self.queue_redraw()