factoriax.playground.ui.panels#

Reusable drawing primitives for Factoriax menus.

All components read sizes and colors from factoriax.playground.ui.theme so the look stays consistent across menus. Each function is a stateless drawing operation. Menus own the layout and event loop.

class factoriax.playground.ui.panels.DecorationStrip(surfaces: tuple[Surface, ...], ore_surface: Surface, icon_size: int, icon_gap: int, anim_targets: tuple[int, ...], anim_step_ms: int = 700, anim_cycle_len: int = 6)#

Horizontal row of game icons with a periodic “ore on belt” animation.

Stateless: draw derives the animation phase from time_ms (typically pygame.time.get_ticks()). Build one with the factoriax_default() factory or by populating the fields manually.

draw(surf: Surface, x: int, y: int, time_ms: int) → None#

Blit every icon left-to-right at (x, y) and overlay the ore.

classmethod factoriax_default(icon_size: int, icon_gap: int = 10) → DecorationStrip#

Build the standard 8-slot strip: player, miner, 4×belt, arm, pallet.

Ore travels across the four belt slots (indices 2-5) and onto the arm (index 6). The player and miner slots stay clear.

property height: int#

Height of one icon, which is the height of the whole row.

property total_width: int#

Total width of every icon and every gap between them.

class factoriax.playground.ui.panels.InputSourceTracker#

Record which input device moved the selection last.

A menu takes the mouse hover only while mouse_active is True. That flag becomes True when the cursor moves between two calls to tick().

The caller calls mark_keyboard() for each keyboard action. A cursor that does not move therefore leaves the selection where the keyboard put it.

mark_keyboard() → None#

Record that the keyboard moved the selection.

The mouse hover then stops until the mouse moves again, so the two input devices do not fight over the selection.

tick() → None#

Read the cursor, and record whether it moved since the last call.

class factoriax.playground.ui.panels.LabelValueSection(title: str, labels: tuple[str, ...])#

A titled group of (label, value) rows for read-only / non-numeric display.

Companion to SettingSection: same visual layout, but values are arbitrary strings the caller pre-formats (key bindings, toggle state, enum-style choices). The active row gets the gold-fill highlight but no chevron decoration. There are no arrow-adjust semantics to advertise.

class factoriax.playground.ui.panels.SettingField(key: str, label: str, is_float: bool, step: float, min_value: float, max_value: float)#

One editable numeric setting in a settings menu.

clamp(value: float) → float#

Clamp value to [min_value, max_value] and cast to the type.

format(value: float) → str#

Format value for display in the right-hand column.

class factoriax.playground.ui.panels.SettingSection(title: str, fields: tuple[SettingField, ...])#

A titled group of related SettingField rows.

Used by draw_setting_sections() to render an editable settings list with elegant headings and dividers between groups.

factoriax.playground.ui.panels.draw_button(surf: Surface, rect: Rect, label: str, font: Font, *, focused: bool = False, hovered: bool = False) → None#

Draw a button with round corners, in the style of a panel.

Parameters:
  • surf – Surface to draw on.

  • rect – Area of the button.

  • label – Text at the center of the button.

  • font – Font for the label.

  • focused – True draws the button as the selected one.

  • hovered – True draws the button under the mouse.

factoriax.playground.ui.panels.draw_description(surf: Surface, rect: Rect, text: str, scroll: int, font: Font) → int#

Render wrapped scrollable text in rect. Returns the clamped scroll.

factoriax.playground.ui.panels.draw_hint_bar(surf: Surface, sw: int, y: int, text: str, font: Font) → None#

Centered hint text in theme.HINT_COLOR.

factoriax.playground.ui.panels.draw_label_value_sections(surf: Surface, rect: Rect, sections: tuple[LabelValueSection, ...] | list[LabelValueSection], values: list[str], selected_idx: int, focused: bool, font: Font, heading_font: Font) → tuple[list[Rect], int]#

Render sectioned label/value rows for bindings, toggles, choices.

The rows look the same as the rows of draw_setting_sections(). The caller gives each value as text, and the selected row gets no chevron.

Parameters:
  • surf – Surface to draw on.

  • rect – Area that holds the rows.

  • sections – Sections to draw, in order. Each one has a heading and its rows.

  • values – Value of every row, as one flat list. It follows the order of the rows of every section, one section after the other.

  • selected_idx – Index into values of the selected row.

  • focused – True draws the selected row as active.

  • font – Font for a row.

  • heading_font – Font for a heading.

factoriax.playground.ui.panels.draw_list_rows(surf: Surface, rect: Rect, rows: list[str], selected_idx: int, font: Font) → None#

Render a vertically-stacked selectable list inside rect.

factoriax.playground.ui.panels.draw_panel(surf: Surface, rect: Rect, title: str | None = None, title_font: Font | None = None, *, focused: bool = True) → None#

Draw a panel with round corners.

A title sits in a gap in the top border line.

Parameters:
  • surf – Surface to draw on.

  • rect – Area of the panel.

  • title – Text for the top border line. None draws no title.

  • title_font – Font for the title. None draws no title.

  • focused – True draws the border in the color of the active panel.

factoriax.playground.ui.panels.draw_setting_rows(surf: Surface, rect: Rect, fields: tuple[SettingField, ...] | list[SettingField], values: list[float], selected_idx: int, focused: bool, font: Font) → list[Rect]#

Render label … value rows for an editable settings list.

The selected row gets a gold fill, and chevrons around the value. The chevrons tell the user that Left and Right change this value.

Parameters:
  • surf – Surface to draw on.

  • rect – Area that holds the rows.

  • fields – Setting of each row, in order.

  • values – Value of each row, in the same order as fields.

  • selected_idx – Index of the selected row.

  • focused – True draws the selected row as active.

  • font – Font for a row.

Returns:

The rectangle of each row, so a caller can test a mouse position.

Return type:

list[pygame.Rect]

factoriax.playground.ui.panels.draw_setting_sections(surf: Surface, rect: Rect, sections: tuple[SettingSection, ...] | list[SettingSection], values: list[float], selected_idx: int, focused: bool, font: Font, heading_font: Font) → tuple[list[Rect], int]#

Render sectioned setting rows with headings and dividers.

Parameters:
  • surf – Surface to draw on.

  • rect – Area that holds the rows.

  • sections – Sections to draw, in order. Each one has a heading and its rows.

  • values – Value of every row, as one flat list. It follows the order of the rows of every section, one section after the other.

  • selected_idx – Index into values of the selected row.

  • focused – True draws the selected row as active.

  • font – Font for a row.

  • heading_font – Font for a heading.

Returns:

  • (row_rects, bottom_y), for hit-testing and for positioning content

  • below the rendered block.

factoriax.playground.ui.panels.label_value_section_layout(rect: Rect, sections: tuple[LabelValueSection, ...] | list[LabelValueSection], heading_h: int) → tuple[list[Rect], list[tuple[int, int]], int]#

Return the layout of a block of label and value sections.

Parameters:
  • rect – Area that holds the sections.

  • sections – Sections to lay out, in order.

  • heading_h – Height of one heading in pixels.

Returns:

The rectangle of each row, the position of each heading, and the row under the last section.

Return type:

tuple[list[pygame.Rect], list[tuple[int, int]], int]

factoriax.playground.ui.panels.list_row_rects(rect: Rect, n_rows: int) → list[Rect]#

Return the rectangle of each row of a list.

draw_list_rows() draws into these same rectangles. A caller that tests a mouse position therefore reads the layout from one place.

factoriax.playground.ui.panels.rgba_to_surface(rgba: ndarray) → Surface#

Convert an RGBA uint8 array of shape (H, W, 4) to a Surface.

factoriax.playground.ui.panels.setting_section_layout(rect: Rect, sections: tuple[SettingSection, ...] | list[SettingSection], heading_h: int) → tuple[list[Rect], list[tuple[int, int]], int]#

Compute per-field row rects, per-section heading positions, and bottom y.

The drawer and every caller read the layout from this one function. A caller can therefore put a button under the block, or test a mouse position, against the same coordinates.

Parameters:
  • rect – Area that holds the sections.

  • sections – Sections to lay out, in order.

  • heading_h – Height of one heading in pixels.

Returns:

The rectangle of each row, the position of each heading, and the row under the last section.

Return type:

tuple[list[pygame.Rect], list[tuple[int, int]], int]

factoriax.playground.ui.panels.wrap_text(text: str, font: Font, max_width: int) → list[str]#

Word-wrap text to fit max_width using font.

Newlines become explicit breaks. Empty paragraphs become empty lines so the line breaks of the source stay. A word longer than max_width are kept on their own line (no character-level splitting).