MIL-008: Choose level
Review record RC-041 (Go, autonomous mode, not independent). Task: MIL-008#1 Task: MIL-008#2 Task: MIL-008#3 Task: MIL-008#4 Task: MIL-008#5 Task: MIL-008#6 Task: MIL-008#7 Task: MIL-008#8
This commit is contained in:
@@ -20,8 +20,6 @@ from flash_card.constants import (
|
||||
LANGUAGE_FONT,
|
||||
LANGUAGE_TEXT_Y,
|
||||
RIGHT_IMAGE_NAME,
|
||||
WINDOW_PADDING,
|
||||
WINDOW_TITLE,
|
||||
WORD_FONT,
|
||||
WORD_TEXT_Y,
|
||||
WRONG_IMAGE_NAME,
|
||||
@@ -30,6 +28,7 @@ from flash_card.presentation.features.flash_card.flash_card_view_model import (
|
||||
FlashCardViewModel,
|
||||
Side,
|
||||
)
|
||||
from flash_card.presentation.shared.window import configure_window
|
||||
|
||||
|
||||
def _load_image(root: tk.Tk, name: str) -> tk.PhotoImage:
|
||||
@@ -60,8 +59,7 @@ class FlashCardView:
|
||||
self._root = root
|
||||
## @brief The data source of the texts.
|
||||
self._view_model = view_model
|
||||
root.title(WINDOW_TITLE)
|
||||
root.config(padx=WINDOW_PADDING, pady=WINDOW_PADDING, bg=BACKGROUND_COLOR)
|
||||
configure_window(root)
|
||||
|
||||
# Kept as attributes: Tkinter drops an image nothing refers to.
|
||||
## @brief Card front image, kept so Tk does not drop it.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""The level feature: its view and view model."""
|
||||
@@ -0,0 +1,97 @@
|
||||
"""View of the level screen.
|
||||
|
||||
@file level_view.py
|
||||
@brief Builds the level screen of the wireframe: a title and one button per level.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from collections.abc import Callable
|
||||
|
||||
from flash_card.constants import (
|
||||
BACKGROUND_COLOR,
|
||||
CARD_WIDTH,
|
||||
LEVEL_BUTTON_COLOR,
|
||||
LEVEL_BUTTON_FONT,
|
||||
LEVEL_BUTTON_WIDTH,
|
||||
LEVEL_SCREEN_HEIGHT,
|
||||
LEVEL_TITLE_FONT,
|
||||
)
|
||||
from flash_card.domain.value_objects.level import Level
|
||||
from flash_card.presentation.features.level.level_view_model import (
|
||||
LevelChoice,
|
||||
LevelViewModel,
|
||||
)
|
||||
|
||||
|
||||
class LevelView:
|
||||
"""The level screen: a title and a button for each level."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
root: tk.Tk,
|
||||
view_model: LevelViewModel,
|
||||
on_choose: Callable[[Level], None],
|
||||
) -> None:
|
||||
"""Build the screen on a root.
|
||||
|
||||
@param root The Tk root window.
|
||||
@param view_model The data to show.
|
||||
@param on_choose Called with the level the user picked.
|
||||
"""
|
||||
## @brief Called with the level the user picked.
|
||||
self._on_choose = on_choose
|
||||
## @brief Frame that holds the whole screen.
|
||||
self.frame = tk.Frame(
|
||||
root, bg=BACKGROUND_COLOR, width=CARD_WIDTH, height=LEVEL_SCREEN_HEIGHT
|
||||
)
|
||||
self.frame.grid(row=0, column=0)
|
||||
self.frame.grid_propagate(False)
|
||||
self.frame.grid_columnconfigure(0, weight=1)
|
||||
for row in range(len(view_model.choices) + 1):
|
||||
self.frame.grid_rowconfigure(row, weight=1)
|
||||
## @brief The title text.
|
||||
self.title_label = tk.Label(
|
||||
self.frame,
|
||||
text=view_model.title,
|
||||
font=LEVEL_TITLE_FONT,
|
||||
bg=BACKGROUND_COLOR,
|
||||
)
|
||||
self.title_label.grid(row=0, column=0)
|
||||
## @brief One button per choice, in display order.
|
||||
self.buttons = tuple(
|
||||
self._build_button(row, choice)
|
||||
for row, choice in enumerate(view_model.choices, start=1)
|
||||
)
|
||||
|
||||
def choose(self, level: Level) -> None:
|
||||
"""Forward the chosen level.
|
||||
|
||||
@param level The level the user picked.
|
||||
@return Nothing.
|
||||
"""
|
||||
self._on_choose(level)
|
||||
|
||||
def destroy(self) -> None:
|
||||
"""Remove the screen from the window.
|
||||
|
||||
@return Nothing.
|
||||
"""
|
||||
self.frame.destroy()
|
||||
|
||||
def _build_button(self, row: int, choice: LevelChoice) -> tk.Button:
|
||||
"""Create the button of one choice.
|
||||
|
||||
@param row Grid row of the button.
|
||||
@param choice The choice the button stands for.
|
||||
@return The button, placed in the frame.
|
||||
"""
|
||||
button = tk.Button(
|
||||
self.frame,
|
||||
text=choice.label,
|
||||
font=LEVEL_BUTTON_FONT,
|
||||
width=LEVEL_BUTTON_WIDTH,
|
||||
bg=LEVEL_BUTTON_COLOR,
|
||||
command=lambda: self.choose(choice.level),
|
||||
)
|
||||
button.grid(row=row, column=0)
|
||||
return button
|
||||
@@ -0,0 +1,53 @@
|
||||
"""View model of the level screen.
|
||||
|
||||
@file level_view_model.py
|
||||
@brief Exposes the title and the level choices; free of any Tkinter import.
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
|
||||
from flash_card.constants import LEVEL_CHOICES, LEVEL_TITLE
|
||||
from flash_card.domain.value_objects.level import Level
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class LevelChoice:
|
||||
"""One choice on the level screen."""
|
||||
|
||||
## @brief Text of the choice.
|
||||
label: str
|
||||
## @brief The level the choice stands for.
|
||||
level: Level
|
||||
|
||||
|
||||
class LevelViewModel:
|
||||
"""Data the level view shows."""
|
||||
|
||||
def __init__(self, choices: Sequence[tuple[str, int | None]] | None = None) -> None:
|
||||
"""Create the view model.
|
||||
|
||||
@param choices Label and word limit of each choice, or None for the
|
||||
choices of the program.
|
||||
"""
|
||||
source = LEVEL_CHOICES if choices is None else choices
|
||||
## @brief The choices in display order.
|
||||
self._choices = tuple(
|
||||
LevelChoice(label, Level(limit)) for label, limit in source
|
||||
)
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
"""Return the title of the screen.
|
||||
|
||||
@return The title text.
|
||||
"""
|
||||
return LEVEL_TITLE
|
||||
|
||||
@property
|
||||
def choices(self) -> tuple[LevelChoice, ...]:
|
||||
"""Return the choices.
|
||||
|
||||
@return The choices in display order.
|
||||
"""
|
||||
return self._choices
|
||||
Reference in New Issue
Block a user