gale.ui package

gale.ui: a general-purpose, per-game-customizable widget toolkit (panels, labels, buttons, progress bars, checkboxes, list views, containers, text boxes, text inputs, cursors) built on top of gale.input_handler, gale.text, and gale.timer.

See docs/examples/ui.rst for a walkthrough.

Author: Alejandro Mujica (aledrums@gmail.com)

class gale.ui.Button(x, y, width, height, text, on_click=None, font=None, theme=None)[source]

Bases: Widget

A clickable rectangle with a centered label. Fires on_click both when clicked with the mouse and when it is the focused widget and on_confirm is triggered (typically bound to Enter/a controller’s “confirm” button through gale.input_handler).

Usage example:

def start_game():

button = Button(220, 200, 200, 48, “Start”, on_click=start_game)

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • text (str)

  • on_click (Callable[[], None] | None)

  • font (Font | None)

  • theme (Theme | None)

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

on_mouse_click(position, data)[source]
Parameters:
Return type:

bool

on_confirm()[source]
Return type:

bool

class gale.ui.Checkbox(x, y, size, checked=False, on_change=None, theme=None)[source]

Bases: Widget

A square toggle. Fires on_change(checked) whenever its state flips, either by a mouse click or by on_confirm while focused.

Parameters:
  • x (float)

  • y (float)

  • size (float)

  • checked (bool)

  • on_change (Callable[[bool], None] | None)

  • theme (Theme | None)

toggle()[source]
Return type:

None

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

on_mouse_click(position, data)[source]
Parameters:
Return type:

bool

on_confirm()[source]
Return type:

bool

class gale.ui.Container(x, y, width, height, children=None, theme=None, visible=True)[source]

Bases: Widget

A real scene-graph node holding any number of child widgets. Rendered/updated in add order (which is also z-order: to bring a widget to front, remove and re-add it). Mouse events hit-test children in reverse add order (top-most first) and stop at the first one that consumes the event. on_confirm/on_navigate are forwarded only to the currently focused child; an unconsumed on_navigate instead moves focus to the next focusable sibling.

Usage example:

menu = Container(40, 40, 240, 160, children=[

Panel(40, 40, 240, 160), ListView(48, 48, 224, 144, items=[(“Host”, start_hosting), (“Join”, start_joining)]),

])

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • children (Sequence[Widget] | None)

  • theme (Theme | None)

  • visible (bool)

property focusable: bool

bool(x) -> bool

Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

add_child(widget)[source]
Parameters:

widget (Widget)

Return type:

None

remove_child(widget)[source]
Parameters:

widget (Widget)

Return type:

None

update(dt)[source]
Parameters:

dt (float)

Return type:

None

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

on_mouse_motion(position)[source]
Parameters:

position (Tuple[float, float])

Return type:

None

on_mouse_click(position, data)[source]
Parameters:
Return type:

bool

on_confirm()[source]
Return type:

bool

on_navigate(direction)[source]
Parameters:

direction (Tuple[int, int])

Return type:

bool

class gale.ui.Cursor(image, hotspot=(0, 0))[source]

Bases: object

A custom cursor image, usable two ways:

  • As the OS mouse pointer: call set_as_system_cursor() once (a state’s enter() is a good place) to swap pygame’s own cursor for this image.

  • As a keyboard-navigation indicator: pass it to ListView(cursor=…) so it is rendered next to the currently selected item.

A game configures the cursors it needs through its own settings.py, the same way it already does for TEXTURES/FONTS/SOUNDS:

CURSORS = {

“pointer”: Cursor(pygame.image.load(BASE_DIR / “assets” / “graphics” / “cursor_pointer.png”), hotspot=(2, 2)),

}

Parameters:
  • image (Surface)

  • hotspot (Tuple[int, int])

set_as_system_cursor()[source]

Replace the OS mouse pointer with this cursor’s image.

Return type:

None

show()[source]
Return type:

None

hide()[source]
Return type:

None

render(surface, position)[source]

Draw this cursor’s image at position, used for the keyboard-navigation indicator use case (the OS pointer use case never calls this: pygame already draws the real cursor).

Parameters:
  • surface (Surface) – The surface to draw on.

  • position (Tuple[float, float]) – Where the hotspot should land.

Return type:

None

class gale.ui.Label(x, y, text, font=None, color=None, center=False, shadowed=False, theme=None)[source]

Bases: Widget

A piece of static (or programmatically updatable, via set_text) text, drawn with gale.text.Text.

Usage example:

label = Label(320, 40, “Rally”, center=True) label.set_text(“Rally: 3 - 2”)

Parameters:
  • x (float)

  • y (float)

  • text (str)

  • font (Font | None)

  • color (Color | None)

  • center (bool)

  • shadowed (bool)

  • theme (Theme | None)

set_text(text)[source]
Parameters:

text (str) – The new text to display.

Return type:

None

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

class gale.ui.ListView(x, y, width, height, items, font=None, cursor=None, theme=None)[source]

Bases: Widget

A vertical list of selectable items, each a (label, on_select) pair. Supports both keyboard navigation (on_navigate moves the selection with wraparound, on_confirm invokes the selected item) and mouse interaction (hovering highlights, clicking selects and invokes). This is the generalization of gale.ui’s “menu”: build a menu as Container([Panel(…), ListView(…)]).

Usage example:

menu = ListView(

40, 40, 240, 120, items=[(“Host”, start_hosting), (“Join”, start_joining), (“Quit”, quit_game)],

)

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • items (Sequence[Tuple[str, Callable[[], None]]])

  • font (Font | None)

  • cursor (Cursor | None)

  • theme (Theme | None)

row_height()[source]
Return type:

float

row_rect(index)[source]
Parameters:

index (int)

Return type:

Rect

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

on_mouse_motion(position)[source]
Parameters:

position (Tuple[float, float])

Return type:

None

on_mouse_click(position, data)[source]
Parameters:
Return type:

bool

on_confirm()[source]
Return type:

bool

on_navigate(direction)[source]
Parameters:

direction (Tuple[int, int])

Return type:

bool

class gale.ui.PaginatedTextBox(x, y, width, height, text, font=None, lines_per_page=3, button_height=28, button_width=96, previous_label='Previous', next_label='Next', theme=None, visible=True)[source]

Bases: Container

A TextBox paired with “Previous”/”Next” buttons docked under it, for content the player pages through at their own pace instead of a click/Enter-to-continue dialogue (e.g. an instructions or help screen). The buttons are kept in sync every update(): disabled on the first/last page instead of wrapping around, and this never auto-hides itself the way TextBox.advance() does on its own — close it explicitly (e.g. from a Window’s close button) if needed.

Usage example:

help_box = PaginatedTextBox(160, 90, 320, 220, long_help_text)

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • text (str)

  • font (Font | None)

  • lines_per_page (int)

  • button_height (int)

  • button_width (int)

  • previous_label (str)

  • next_label (str)

  • theme (Theme | None)

  • visible (bool)

update(dt)[source]
Parameters:

dt (float)

Return type:

None

class gale.ui.Panel(x, y, width, height, theme=None, visible=True)[source]

Bases: Widget

A plain filled, bordered rectangle. Typically used as the background chrome behind other widgets (see Container), or on its own as a HUD backdrop.

Usage example:

panel = Panel(10, 10, 200, 120)

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • theme (Theme | None)

  • visible (bool)

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

class gale.ui.ProgressBar(x, y, width, height, value=0, max_value=100, color=None, theme=None)[source]

Bases: Widget

A filled bar showing value out of max_value. value and max_value are plain public attributes (no set_value/set_max methods) so they can be driven directly by gale.timer.Timer.tween, e.g.:

Timer.tween(0.5, [(health_bar, {“value”: new_hp})])

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • value (float)

  • max_value (float)

  • color (Color | None)

  • theme (Theme | None)

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

class gale.ui.Theme(font=None, text_color=(235, 235, 235, 255), background_color=(30, 30, 34, 255), border_color=(235, 235, 235, 255), border_width=1, accent_color=(90, 200, 255, 255), hover_color=(55, 60, 75, 255), focus_color=(255, 210, 60, 255), disabled_color=(120, 120, 120, 255), padding=4)[source]

Bases: object

A bundle of style values every widget reads from, either its own (passed to its constructor) or a shared default one.

Usage example:

pygame.font.init() dark_theme = Theme(font=pygame.font.Font(None, 20)) panel = Panel(10, 10, 200, 100, theme=dark_theme)

Parameters:
  • font (Font | None)

  • text_color (Color)

  • background_color (Color)

  • border_color (Color)

  • border_width (int)

  • accent_color (Color)

  • hover_color (Color)

  • focus_color (Color)

  • disabled_color (Color)

  • padding (int)

class gale.ui.TextBox(x, y, width, height, text, font=None, lines_per_page=3, on_close=None, theme=None)[source]

Bases: Widget

A paginated block of text, such as dialogue or an in-game hint. advance() moves to the next page (on a mouse click or on_confirm), or, on the last page, hides the box and calls on_close. This is the click-or-Enter-to-continue style used by RPG dialogue, where the player doesn’t need to navigate backward.

For content the player pages through with explicit “Previous”/”Next” buttons instead (e.g. a rules or help screen), use next_page() and previous_page() directly, or wrap this in a PaginatedTextBox, which also wires up and enables/disables those buttons for you.

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • text (str)

  • font (Font | None)

  • lines_per_page (int)

  • on_close (Callable[[], None] | None)

  • theme (Theme | None)

property finished: bool
property page_count: int
property has_next_page: bool
property has_previous_page: bool
advance()[source]
Return type:

None

next_page()[source]

Move to the next page, if any, without the auto-close behavior of advance(). Meant to back a “Next” button.

Returns:

Whether there was a next page to move to.

Return type:

bool

previous_page()[source]

Move back to the previous page, if any. Meant to back a “Previous” button.

Returns:

Whether there was a previous page to move back to.

Return type:

bool

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

on_mouse_click(position, data)[source]
Parameters:
Return type:

bool

on_confirm()[source]
Return type:

bool

class gale.ui.TextInput(x, y, width, height, initial_text='', max_length=None, font=None, on_submit=None, theme=None)[source]

Bases: Widget

A single-line editable text field, for things like a player name, a chat message, or a server address/join code. Needs raw keyboard events rather than the confirm/navigate vocabulary, so its owner (typically UIManager) must call handle_key directly while this widget is focused (see wants_raw_keyboard).

Limitation: built on pygame’s KEYDOWN.unicode, so it only supports ASCII-range input, not full IME/composed-character entry. That is enough for names, chat, and join codes, but not for every language.

Usage example:

name_input = TextInput(40, 40, 240, 32, initial_text=”Player”, max_length=16)

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • initial_text (str)

  • max_length (int | None)

  • font (Font | None)

  • on_submit (Callable[[str], None] | None)

  • theme (Theme | None)

handle_key(data)[source]
Parameters:

data (KeyboardData) – The keyboard event to react to. Only KEYDOWN events (data.pressed) cause a change.

Returns:

Whether the key was consumed.

Return type:

bool

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

on_mouse_click(position, data)[source]
Parameters:
Return type:

bool

class gale.ui.UIManager(root, virtual_width, window_width, virtual_height, window_height, confirm_action='confirm', navigate_actions=None)[source]

Bases: object

Connects a widget tree (a root Container) to gale.input_handler, rescaling mouse positions from window to virtual coordinates and routing keyboard “confirm”/navigation actions and raw key events to the right widget.

Not self-registered with InputHandler.register_listener: like StateMachine/StateStack, its owner (typically a BaseState) must call on_input manually, so only the currently active UI receives input.

Usage example:

class LobbyState(BaseState):
def enter(self, **kwargs) -> None:
self.ui = UIManager(

build_lobby_menu(), virtual_width=settings.VIRTUAL_WIDTH, window_width=settings.WINDOW_WIDTH, virtual_height=settings.VIRTUAL_HEIGHT, window_height=settings.WINDOW_HEIGHT, confirm_action=”confirm”, navigate_actions={“move_up”: (0, -1), “move_down”: (0, 1)},

)

def update(self, dt: float) -> None:

self.ui.update(dt)

def render(self, surface: pygame.Surface) -> None:

self.ui.render(surface)

def on_input(self, input_id: str, input_data) -> None:

self.ui.on_input(input_id, input_data)

Parameters:
  • root (Container)

  • virtual_width (int)

  • window_width (int)

  • virtual_height (int)

  • window_height (int)

  • confirm_action (str)

  • navigate_actions (Dict[str, Tuple[int, int]] | None)

update(dt)[source]
Parameters:

dt (float)

Return type:

None

render(surface)[source]
Return type:

None

on_input(input_id, input_data)[source]
Parameters:
  • input_id (str) – The action id notified by InputHandler.

  • input_data – The associated input data.

Return type:

None

class gale.ui.Widget(x, y, width, height, theme=None, visible=True, enabled=True)[source]

Bases: object

Base class for anything gale.ui can render and route input to.

A subclass overrides update/render to draw itself, and whichever of on_mouse_motion/on_mouse_click/on_confirm/on_navigate it reacts to. on_mouse_click, on_confirm, and on_navigate return a bool meaning “did I consume this?”, so a Container knows whether to keep forwarding the same input to something else (a click behind a focused widget should not also reach whatever is under it).

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • theme (Theme | None)

  • visible (bool)

  • enabled (bool)

focusable: bool = False
property theme: Theme
property rect: Rect
contains(position)[source]
Parameters:

position (Tuple[float, float]) – A point, in the same coordinate space as x/y.

Returns:

Whether position is inside this widget’s rect.

Return type:

bool

update(dt)[source]
Parameters:

dt (float)

Return type:

None

render(surface)[source]
Parameters:

surface (Surface)

Return type:

None

on_mouse_motion(position)[source]
Parameters:

position (Tuple[float, float])

Return type:

None

on_mouse_click(position, data)[source]
Parameters:
Return type:

bool

on_confirm()[source]
Return type:

bool

on_navigate(direction)[source]
Parameters:

direction (Tuple[int, int])

Return type:

bool

class gale.ui.Window(x, y, width, height, title='', closable=True, on_close=None, close_button_size=20, children=None, theme=None, visible=True)[source]

Bases: Container

A Panel-backed Container with an optional title and an optional close (“X”) button docked in its top-right corner, the way a desktop window or an in-game modal dialog (an inventory, a pause menu, a dialogue box the player can dismiss early) is closed.

Usage example:

def on_close():

print(“closed”)

window = Window(

160, 90, 320, 220, title=”Inventory”, on_close=on_close, children=[ListView(…)],

)

Parameters:
  • x (float)

  • y (float)

  • width (float)

  • height (float)

  • title (str)

  • closable (bool)

  • on_close (Callable[[], None] | None)

  • close_button_size (int)

  • children (Sequence[Widget] | None)

  • theme (Theme | None)

  • visible (bool)

close()[source]

Hide the window and invoke on_close, if any. Safe to call directly (e.g. bound to a keyboard shortcut or another widget’s on_click) even when closable is False.

Return type:

None

gale.ui.get_default_theme()[source]
Returns:

The current default theme, creating one with every value defaulted the first time this is called.

Return type:

Theme

gale.ui.set_default_theme(theme)[source]

Replace the default theme every widget created without an explicit theme falls back to. Existing widgets read gale.ui.theme.get_default_theme() fresh on every render, so this takes effect immediately, without having to recreate them.

Parameters:

theme (Theme) – The new default theme.

Return type:

None

Submodules