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:
WidgetA 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)
- on_mouse_click(position, data)[source]
- Parameters:
position (Tuple[float, float])
data (MouseClickData)
- Return type:
bool
- class gale.ui.Checkbox(x, y, size, checked=False, on_change=None, theme=None)[source]
Bases:
WidgetA 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)
- on_mouse_click(position, data)[source]
- Parameters:
position (Tuple[float, float])
data (MouseClickData)
- Return type:
bool
- class gale.ui.Container(x, y, width, height, children=None, theme=None, visible=True)[source]
Bases:
WidgetA 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:
- 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.
- on_mouse_click(position, data)[source]
- Parameters:
position (Tuple[float, float])
data (MouseClickData)
- Return type:
bool
- Parameters:
direction (Tuple[int, int])
- Return type:
bool
- class gale.ui.Cursor(image, hotspot=(0, 0))[source]
Bases:
objectA 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
- 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:
WidgetA 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)
- class gale.ui.ListView(x, y, width, height, items, font=None, cursor=None, theme=None)[source]
Bases:
WidgetA 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:
- on_mouse_click(position, data)[source]
- Parameters:
position (Tuple[float, float])
data (MouseClickData)
- Return type:
bool
- 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:
ContainerA 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)
- class gale.ui.Panel(x, y, width, height, theme=None, visible=True)[source]
Bases:
WidgetA 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)
- class gale.ui.ProgressBar(x, y, width, height, value=0, max_value=100, color=None, theme=None)[source]
Bases:
WidgetA 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)
- 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:
objectA 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:
WidgetA 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
- 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
- on_mouse_click(position, data)[source]
- Parameters:
position (Tuple[float, float])
data (MouseClickData)
- 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:
WidgetA 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
- on_mouse_click(position, data)[source]
- Parameters:
position (Tuple[float, float])
data (MouseClickData)
- Return type:
bool
- class gale.ui.UIManager(root, virtual_width, window_width, virtual_height, window_height, confirm_action='confirm', navigate_actions=None)[source]
Bases:
objectConnects 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)
- class gale.ui.Widget(x, y, width, height, theme=None, visible=True, enabled=True)[source]
Bases:
objectBase 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 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
- on_mouse_click(position, data)[source]
- Parameters:
position (Tuple[float, float])
data (MouseClickData)
- Return type:
bool
- 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:
ContainerA 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:
- 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:
- 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
- gale.ui.button module
- gale.ui.checkbox module
- gale.ui.container module
- gale.ui.cursor module
- gale.ui.label module
- gale.ui.list_view module
- gale.ui.manager module
- gale.ui.panel module
- gale.ui.progress_bar module
- gale.ui.text_box module
- gale.ui.text_input module
- gale.ui.theme module
- gale.ui.widget module
WidgetWidget.focusableWidget.xWidget.yWidget.widthWidget.heightWidget.visibleWidget.enabledWidget.focusedWidget.hoveredWidget.parentWidget.wants_raw_keyboardWidget.themeWidget.rectWidget.contains()Widget.update()Widget.render()Widget.on_mouse_motion()Widget.on_mouse_click()Widget.on_confirm()Widget.on_navigate()
- gale.ui.window module