main
The main screen.
Attributes⚓︎
CHAR_OFFSET
module-attribute
⚓︎
CHAR_OFFSET = math.floor(0.4 * MAX_CHARS)
Offset to keep the next characters visible.
MAX_CHARS
module-attribute
⚓︎
MAX_CHARS = math.floor(0.8 * get_terminal_size()[0])
Determine maximum characters that can fit in 80% of the terminal width.
Classes⚓︎
Main ⚓︎
Bases: Screen
The main screen for the application.
Source code in tui_typer_tutor/screens/main.py
class Main(Screen): # type: ignore[type-arg]
"""The main screen for the application."""
DEFAULT_CSS: ClassVar[str] = """
Screen {
background: #1b2b34;
}
#left-pad {
width: 10%;
}
#content {
width: 80%;
align: center middle;
}
.tutor-container {
content-align-horizontal: left;
height: 2;
}
#text-container {
color: #7e8993;
}
#typed-container .error {
color: #ec5f67;
}
#typed-container .success {
color: #99c794;
}
#typed-unknown {
color: #b69855;
}
"""
BINDINGS: ClassVar[list[Binding]] = [ # type: ignore[assignment]
Binding('ctrl+q', 'save_and_quit', 'Save and Quit'),
]
keys: Keys
metrics: SessionMetrics
width: int = 0
def action_save_and_quit(self) -> None:
"""Save and quit."""
append_csv(self.metrics.end_session(self.keys))
# TODO: Print out or display a success message on completion!
sys.exit(0)
def compose(self) -> ComposeResult: # noqa: PLR6301
"""Layout."""
yield Header()
with Horizontal():
yield Vertical(id='left-pad')
# FYI: ^^ couldn't get 'center' alignment to work
with Vertical(id='content'):
yield Horizontal(id='text-container', classes='tutor-container')
yield Horizontal(id='typed-container', classes='tutor-container')
yield Label(id='typed-unknown', classes='warning')
yield Footer()
def on_mount(self) -> None:
"""On widget mount."""
# TODO: Support more customization
seed_file = get_config().seed_file
self.keys = Keys(expected=load_seed_data(seed_text=seed_file.read_text()))
self.metrics = SessionMetrics.from_filename(filename=seed_file.name)
cont = self.query_one('#text-container', Horizontal)
for key in self.keys.expected: # FYI: Mounts all expected keys and crops
cont.mount(Label(key.text, classes='text'))
@beartype
def on_key(self, event: Key) -> None: # noqa: CAC001
"""Capture all key presses and show in the typed input."""
if event.key in {'ctrl+q', 'ctrl+backslash'}:
return # ignore bound keys
try:
on_keypress(event.key, self.keys)
except AtEndOfExpectedError:
self.action_save_and_quit()
if self.keys.last_was_delete:
if self.width:
self.width -= 1
with suppress(NoMatches):
self.query('Label.typed').last().remove()
self.query_one('#typed-unknown', Label).update('')
else:
self.width += 1
cursor_width = MAX_CHARS - CHAR_OFFSET
if self.width >= cursor_width:
self.query('Label.typed').first().remove()
self.query('Label.text').first().remove()
# Choose the class
color_class = 'success'
display_text = self.keys.typed[-1].text
if not self.keys.typed[-1].was_correct:
color_class = 'error'
# Ensure that invisible characters are displayed
display_text = display_text.strip() or '█'
typed_label = Label(display_text, classes=f'typed {color_class}')
self.query_one('#typed-container', Horizontal).mount(typed_label)
textual_unknown = event.key if display_text == UNKNOWN else ''
self.query_one('#typed-unknown', Label).update(textual_unknown)
Functions⚓︎
action_save_and_quit ⚓︎
action_save_and_quit()
Save and quit.
Source code in tui_typer_tutor/screens/main.py
def action_save_and_quit(self) -> None:
"""Save and quit."""
append_csv(self.metrics.end_session(self.keys))
# TODO: Print out or display a success message on completion!
sys.exit(0)
compose ⚓︎
compose()
Layout.
Source code in tui_typer_tutor/screens/main.py
def compose(self) -> ComposeResult: # noqa: PLR6301
"""Layout."""
yield Header()
with Horizontal():
yield Vertical(id='left-pad')
# FYI: ^^ couldn't get 'center' alignment to work
with Vertical(id='content'):
yield Horizontal(id='text-container', classes='tutor-container')
yield Horizontal(id='typed-container', classes='tutor-container')
yield Label(id='typed-unknown', classes='warning')
yield Footer()
on_key ⚓︎
on_key(event)
Capture all key presses and show in the typed input.
Source code in tui_typer_tutor/screens/main.py
@beartype
def on_key(self, event: Key) -> None: # noqa: CAC001
"""Capture all key presses and show in the typed input."""
if event.key in {'ctrl+q', 'ctrl+backslash'}:
return # ignore bound keys
try:
on_keypress(event.key, self.keys)
except AtEndOfExpectedError:
self.action_save_and_quit()
if self.keys.last_was_delete:
if self.width:
self.width -= 1
with suppress(NoMatches):
self.query('Label.typed').last().remove()
self.query_one('#typed-unknown', Label).update('')
else:
self.width += 1
cursor_width = MAX_CHARS - CHAR_OFFSET
if self.width >= cursor_width:
self.query('Label.typed').first().remove()
self.query('Label.text').first().remove()
# Choose the class
color_class = 'success'
display_text = self.keys.typed[-1].text
if not self.keys.typed[-1].was_correct:
color_class = 'error'
# Ensure that invisible characters are displayed
display_text = display_text.strip() or '█'
typed_label = Label(display_text, classes=f'typed {color_class}')
self.query_one('#typed-container', Horizontal).mount(typed_label)
textual_unknown = event.key if display_text == UNKNOWN else ''
self.query_one('#typed-unknown', Label).update(textual_unknown)
on_mount ⚓︎
on_mount()
On widget mount.
Source code in tui_typer_tutor/screens/main.py
def on_mount(self) -> None:
"""On widget mount."""
# TODO: Support more customization
seed_file = get_config().seed_file
self.keys = Keys(expected=load_seed_data(seed_text=seed_file.read_text()))
self.metrics = SessionMetrics.from_filename(filename=seed_file.name)
cont = self.query_one('#text-container', Horizontal)
for key in self.keys.expected: # FYI: Mounts all expected keys and crops
cont.mount(Label(key.text, classes='text'))