bacommon.docui package

Declarative UI system.

A high level way to build UIs that lives as a layer on top of engine apis such as bauiv1. UIs can easily be serialized to json data and be provided by webservers or other local or remote sources.

class bacommon.docui.DocUIRequest[source]

Bases: IOMultiType[DocUIRequestTypeID]

A request for some UI.

classmethod get_type(type_id: DocUIRequestTypeID) → type[DocUIRequest][source]

Return the subclass for each of our type-ids.

classmethod get_type_id() → DocUIRequestTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → DocUIRequest[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.DocUIRequestTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

UNKNOWN = 'u'
V1 = 'v1'
V2 = 'v2'
class bacommon.docui.DocUIResponse[source]

Bases: IOMultiType[DocUIResponseTypeID]

A UI provied in response to a DocUIRequest.

classmethod get_type(type_id: DocUIResponseTypeID) → type[DocUIResponse][source]

Return the subclass for each of our type-ids.

classmethod get_type_id() → DocUIResponseTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → DocUIResponse[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.DocUIResponseTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

UNKNOWN = 'u'
V1 = 'v1'
V2 = 'v2'
class bacommon.docui.DocUIWebRequest(doc_ui_request: DocUIRequest, locale: Locale, engine_build_number: int)[source]

Bases: object

Complete data sent for doc-ui http requests.

doc_ui_request: DocUIRequest

The wrapped doc-ui request.

engine_build_number: int

Engine build number. In some cases it may make sense to adjust responses depending on available engine features.

locale: Locale

The current locale of the client. doc-ui generally deals in raw strings and expects localization to happen on the server.

class bacommon.docui.DocUIWebResponse(error: str | None = None, doc_ui_response: DocUIResponse | None = None)[source]

Bases: object

Complete data returned for doc-ui http requests.

doc_ui_response: DocUIResponse | None = None

doc-ui response. Either this or error should be set; not both.

error: str | None = None

Human readable error string (if an error occurs). Either this or doc_ui_response should be set; not both.

class bacommon.docui.UnknownDocUIRequest[source]

Bases: DocUIRequest

Fallback type for unrecognized UI types.

Will show the client a ‘cannot display this UI’ placeholder request.

classmethod get_type_id() → DocUIRequestTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.UnknownDocUIResponse[source]

Bases: DocUIResponse

Fallback type for unrecognized UI types.

Will show the client a ‘cannot display this UI’ placeholder response.

classmethod get_type_id() → DocUIResponseTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.WrapParams(min_lines: int = 1, max_lines: int | None = None, max_chars_per_line: int | None = None)[source]

Bases: object

Constraints for splitting a text value into lines client-side.

Mirrors the engine’s simple line splitter (babase.split_text_into_lines()): text is broken only at valid line-break opportunities, using the fewest lines that keep every line within max_chars_per_line (when provided) while staying between min_lines and max_lines (None means unlimited), with line lengths balanced within that count. So max_chars_per_line alone gives basic wrapping and min_lines alone gives an exact line count. Constraints are best-effort. Characters are counted in Latin-width units: East Asian wide characters (CJK, kana, Hangul, full-width forms) count as two, and the engine further scales the budget per locale by how wide its script renders (Cyrillic, Greek and Tamil run wider, Arabic and Hindi narrower), so a max_chars_per_line means about the same width in every language.

Sizing a max_chars_per_line to a width: allow about 9.3 text units of width per character at text scale 1.0 (measured across every locale, 2026-10-01). For example, a doc-ui row footnote (scale 0.7) in a small-layout column (534 units of text width) fills well at 534 / (9.3 * 0.7), about 82. Aim a little wide rather than narrow: text scales down to fit its max width, so a slightly long line costs less than a visibly short one.

Default to pinning an exact line count: set min_lines to the layout’s designed count and leave max_chars_per_line unset. A max_chars_per_line-driven wrap yields a per-locale varying line count (translation lengths differ), which reads as broken in layouts designed around a specific count — and every legacy-converted string is such a layout, since the legacy pipeline hand-baked newlines at fixed counts (see D21 in the strings-asset-migration initiative). Reserve max_chars_per_line for surfaces explicitly designed to tolerate a variable number of lines.

Per decision D-t these are definition-time presentation hints: a string definition carries them optionally, they ride each locale blob, and evaluation applies them automatically. They are locale-invariant, and width-driven layout consumers may ignore them (they are a fallback presentation default).

max_chars_per_line: int | None = None
max_lines: int | None = None
min_lines: int = 1

Subpackages

Submodules

bacommon.docui.presets module

Canned doc-ui styling built from plain v2 rows and buttons.

Higher-level looks that recur across pages (a stack of grouped section buttons, say) live here so their spacing numbers have one owner. Everything returned is ordinary bacommon.docui.v2 data, so callers can still adjust what they get back, and nothing here touches the wire format.

class bacommon.docui.presets.SectionButtonSize(*values)[source]

Bases: Enum

Sizes of section_button().

LARGE = 'large'

The standard full-width button leading to another page.

MEDIUM = 'medium'

A lone button under some other content.

bacommon.docui.presets.button_stack(groups: Sequence[Sequence[bacommon.docui.v2.Button]], *, group_spacing: float = 27.0, first_group_spacing: float | None = None) → list[bacommon.docui.v2.Row][source]

Return rows showing buttons one per row, grouped.

Each button gets a FILL row of its own, so it spans the column exactly as control rows do (its own width is ignored). Buttons within a group sit tight together; groups are separated by group_spacing (the first by first_group_spacing if given, for when the stack follows something that wants a different gap). For several buttons side by side, use such a row with several buttons directly.

bacommon.docui.presets.section_button(label: LangStrSpec, action: bacommon.docui.v2.Action, *, size: SectionButtonSize = SectionButtonSize.LARGE) → bacommon.docui.v2.Button[source]

Return a button leading to a page section (or doing one thing).

In a FILL row (one or several side by side) its width only matters relative to its neighbors’; its style and height are what count.

bacommon.docui.v1 module

Version 1 doc-ui types.

class bacommon.docui.v1.Action[source]

Bases: IOMultiType[ActionTypeID]

Top level class for our multitype.

classmethod get_type(type_id: ActionTypeID) → type[Action][source]

Return the subclass for each of our type-ids.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → Action[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.v1.ActionTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

BROWSE = 'b'
LOCAL = 'l'
REPLACE = 'r'
UNKNOWN = 'u'
class bacommon.docui.v1.Browse(request: Request, default_sound: bool = True, immediate_local_action: str | None = None, immediate_local_action_args: dict | None = None)[source]

Bases: Action

Browse to a new page in a new window.

default_sound: bool = True

Plays a swish.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

immediate_local_action: str | None = None

Local action to run immediately when the button is pressed. Will be handled by bauiv1lib.docui.DocUIController.local_action().

immediate_local_action_args: dict | None = None
request: Request
class bacommon.docui.v1.Button(label: str | None = None, action: Action | None = None, size: tuple[float, float] | None = None, color: tuple[float, float, float, float] | None = None, label_color: tuple[float, float, float, float] | None = None, label_flatness: float | None = None, label_scale: float | None = None, label_is_lstr: bool = False, label_is_langstr: bool = False, label_wrap: WrapParams | None = None, texture: str | None = None, scale: float = 1.0, padding_left: float = 0.0, padding_top: float = 0.0, padding_right: float = 0.0, padding_bottom: float = 0.0, decorations: list[Decoration] | None = None, style: ButtonStyle = ButtonStyle.SQUARE, default: bool = False, selected: bool = False, icon: str | None = None, icon_scale: float | None = None, icon_color: tuple[float, float, float, float] | None = None, depth_range: tuple[float, float] | None = None, widget_id: str | None = None, debug: bool = False)[source]

Bases: object

A button in our doc-ui.

Note that size, padding, and all decorations are scaled consistently with ‘scale’.

action: Action | None = None
color: tuple[float, float, float, float] | None = None
debug: bool = False

Draw bounds of the button.

decorations: list[Decoration] | None = None
default: bool = False
depth_range: tuple[float, float] | None = None
icon: str | None = None
icon_color: tuple[float, float, float, float] | None = None
icon_scale: float | None = None
label: str | None = None

Note that doc-ui accepts only raw str values for text; use babase.Lstr.evaluate() or whatnot for multi-language support.

label_color: tuple[float, float, float, float] | None = None
label_flatness: float | None = None
label_is_langstr: bool = False

The label field holds a language-string (canonical resource-form wire JSON) to be evaluated natively at display and re-evaluated on language changes. Set by the client-side v2 transcode; mutually exclusive with label_is_lstr.

label_is_lstr: bool = False
label_scale: float | None = None
label_wrap: WrapParams | None = None

Line-wrap constraints applied to the label at widget-creation time. Set only by the client-local v2→v1 transcode; v1 producers must never send it (older clients can’t parse unknown fields — bake newlines into the label instead).

padding_bottom: float = 0.0
padding_left: float = 0.0
padding_right: float = 0.0
padding_top: float = 0.0
scale: float = 1.0
selected: bool = False
size: tuple[float, float] | None = None
style: ButtonStyle = 'q'
texture: str | None = None
widget_id: str | None = None

Custom widget id. Will be prefixed with window id, but must be unique within the window.

class bacommon.docui.v1.ButtonRow(buttons: list[Button], header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, title: str | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, title_is_lstr: bool = False, title_is_langstr: bool = False, title_wrap: WrapParams | None = None, subtitle: str | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, subtitle_is_lstr: bool = False, subtitle_is_langstr: bool = False, subtitle_wrap: WrapParams | None = None, button_spacing: float = 15.0, padding_left: float = 10.0, padding_right: float = 10.0, padding_top: float = 10.0, padding_bottom: float = 10.0, spacing_top: float = 0.0, spacing_bottom: float = 0.0, center_content: bool = False, center_title: bool = False, simple_culling_h: float = 100.0, debug: bool = False)[source]

Bases: Row

A row consisting of buttons.

button_spacing: float = 15.0

Spacing between all buttons in the row.

buttons: list[Button]
center_content: bool = False
center_title: bool = False
debug: bool = False

Draw bounds of the overall row and individual button columns (including padding). The UI will scroll to keep these areas visible in their entirety when changing selection via directional controls, so try to make sure all decorations for a button are within these bounds.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0
header_scale: float = 1.0
padding_bottom: float = 10.0

Padding on the bottom of the row’s horizonally-scrollable area.

padding_left: float = 10.0

Padding on the left of the row’s horizonally-scrollable area.

padding_right: float = 10.0

Padding on the right of the row’s horizonally-scrollable area.

padding_top: float = 10.0

Padding on the top of the row’s horizonally-scrollable area.

simple_culling_h: float = 100.0

If things disappear when scrolling left/right, turn this up.

spacing_bottom: float = 0.0

Extra space below the row’s horizontally-scrollable area.

spacing_top: float = 0.0

Extra space above the row’s horizontally-scrollable area.

subtitle: str | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_is_langstr: bool = False

The subtitle field holds a language-string (canonical resource-form wire JSON) to be evaluated natively at display and re-evaluated on language changes. Set by the client-side v2 transcode; mutually exclusive with subtitle_is_lstr.

subtitle_is_lstr: bool = False
subtitle_shadow: float | None = None
subtitle_wrap: WrapParams | None = None

Line-wrap constraints applied to the subtitle at widget-creation time. Set only by the client-local v2→v1 transcode; v1 producers must never send it (older clients can’t parse unknown fields — bake newlines into the subtitle instead).

title: str | None = None

Note that doc-ui accepts only raw str values for text; use babase.Lstr.evaluate() or whatnot for multi-language support.

title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_is_langstr: bool = False

The title field holds a language-string (canonical resource-form wire JSON) to be evaluated natively at display and re-evaluated on language changes. Set by the client-side v2 transcode; mutually exclusive with title_is_lstr.

title_is_lstr: bool = False
title_shadow: float | None = None
title_wrap: WrapParams | None = None

Line-wrap constraints applied to the title at widget-creation time. Set only by the client-local v2→v1 transcode; v1 producers must never send it (older clients can’t parse unknown fields — bake newlines into the title instead).

class bacommon.docui.v1.ButtonStyle(*values)[source]

Bases: Enum

Styles a button can be.

BACK = 'b'
BACK_SMALL = 'bs'
LARGE = 'l'
LARGER = 'xl'
MEDIUM = 'm'
SMALL = 's'
SQUARE = 'q'
SQUARE_WIDE = 'w'
TAB = 't'
class bacommon.docui.v1.Decoration[source]

Bases: IOMultiType[DecorationTypeID]

Top level class for our multitype.

classmethod get_type(type_id: DecorationTypeID) → type[Decoration][source]

Return a specific subclass given a type-id.

Should be overridden by child classes. Generally, users of the class should call get_type_cached() instead of this, as it is more efficient.

classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → Decoration[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.v1.DecorationTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

DISPLAY_ITEM = 'd'
IMAGE = 'i'
TEXT = 't'
UNKNOWN = 'u'
class bacommon.docui.v1.HAlign(*values)[source]

Bases: Enum

Horizontal alignment.

CENTER = 'c'
LEFT = 'l'
RIGHT = 'r'
class bacommon.docui.v1.Image(texture: str, position: tuple[float, float], size: tuple[float, float], color: tuple[float, float, float, float] | None = None, h_align: HAlign = HAlign.CENTER, v_align: VAlign = VAlign.CENTER, tint_texture: str | None = None, tint_color: tuple[float, float, float] | None = None, tint2_color: tuple[float, float, float] | None = None, mask_texture: str | None = None, mesh_opaque: str | None = None, mesh_transparent: str | None = None, highlight: bool = True, depth_range: tuple[float, float] | None = None)[source]

Bases: Decoration

Image decoration.

color: tuple[float, float, float, float] | None = None
depth_range: tuple[float, float] | None = None
classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

h_align: HAlign = 'c'
highlight: bool = True
mask_texture: str | None = None
mesh_opaque: str | None = None
mesh_transparent: str | None = None
position: tuple[float, float]
size: tuple[float, float]
texture: str
tint2_color: tuple[float, float, float] | None = None
tint_color: tuple[float, float, float] | None = None
tint_texture: str | None = None
v_align: VAlign = 'c'
class bacommon.docui.v1.Local(close_window: bool = False, default_sound: bool = True, immediate_local_action: str | None = None, immediate_local_action_args: dict | None = None)[source]

Bases: Action

Perform only local actions; no new requests or page changes.

close_window: bool = False
default_sound: bool = True

Plays a swish if closing the window or a click if triggered by a button press.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

immediate_local_action: str | None = None

Local action to run immediately when the button is pressed. Will be handled by bauiv1lib.docui.DocUIController.local_action().

immediate_local_action_args: dict | None = None
class bacommon.docui.v1.Page(title: str, rows: list[Row], center_vertically: bool = False, row_spacing: float = 10.0, simple_culling_v: float = 100.0, title_is_lstr: bool = False, title_is_langstr: bool = False, title_wrap: WrapParams | None = None, padding_bottom: float = 0.0, padding_left: float = 0.0, padding_top: float = 0.0, padding_right: float = 0.0)[source]

Bases: object

Doc-UI page version 1.

center_vertically: bool = False

If True, content smaller than the available height will be centered vertically. This can look natural for certain types of content such as confirmation dialogs.

padding_bottom: float = 0.0
padding_left: float = 0.0
padding_right: float = 0.0
padding_top: float = 0.0
row_spacing: float = 10.0
rows: list[Row]
simple_culling_v: float = 100.0

If things disappear when scrolling up and down, turn this up.

title: str

Note that doc-ui accepts only raw str values for text; use babase.Lstr.evaluate() or whatnot for multi-language support.

title_is_langstr: bool = False

The title field holds a language-string (canonical resource-form wire JSON) to be evaluated natively at display and re-evaluated on language changes. Set by the client-side v2 transcode; mutually exclusive with title_is_lstr.

title_is_lstr: bool = False

Whether the title is a json dict representing an Lstr. Generally doc-ui translation should be handled server-side, but this can allow client-side translation.

title_wrap: WrapParams | None = None

Line-wrap constraints applied to the title at widget-creation time. Set only by the client-local v2→v1 transcode; v1 producers must never send it (older clients can’t parse unknown fields — bake newlines into the title instead).

class bacommon.docui.v1.Replace(request: Request, default_sound: bool = True, immediate_local_action: str | None = None, immediate_local_action_args: dict | None = None)[source]

Bases: Action

Replace current page with a new one.

Should be used to effectively ‘modify’ existing UIs by replacing them with something slightly different. Things like scroll position and selection will be carried across to the new layout when possible to make for a seamless transition.

default_sound: bool = True

Plays a click if triggered by a button press.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

immediate_local_action: str | None = None

Local action to run immediately when the button is pressed. Will be handled by bauiv1lib.docui.DocUIController.local_action().

immediate_local_action_args: dict | None = None
request: Request
class bacommon.docui.v1.Request(path: str, method: RequestMethod = RequestMethod.GET, args: dict = <factory>)[source]

Bases: DocUIRequest

Full request to doc-ui.

args: dict
classmethod get_type_id() → DocUIRequestTypeID[source]

Return the type-id for this subclass.

method: RequestMethod = 'g'
path: str
class bacommon.docui.v1.RequestMethod(*values)[source]

Bases: Enum

Typeof of requests that can be made to doc-ui servers.

GET = 'g'

Fetch some resource. This can be retried and its results can optionally be cached for some amount of time.

POST = 'p'

Change some resource. This cannot be implicitly retried (at least without deduplication), nor can it be cached.

UNKNOWN = 'u'

An unknown request method. This can appear if a newer client is requesting some method from an older server that is not known to the server.

class bacommon.docui.v1.Response(page: Page, status: ResponseStatus = ResponseStatus.SUCCESS, local_action: str | None = None, local_action_args: dict | None = None, timed_action: Action | None = None, timed_action_delay: float = 0.0, minimum_engine_build: int | None = None, shared_state_id: str | None = None)[source]

Bases: DocUIResponse

Full docui response.

classmethod get_type_id() → DocUIResponseTypeID[source]

Return the type-id for this subclass.

local_action: str | None = None

Local action to run after this response is initially received. Will be handled by bauiv1lib.docui.DocUIController.local_action(). Note that these actions will not re-run if the page is automatically refreshed later (due to window resizing, back navigation, etc).

local_action_args: dict | None = None
minimum_engine_build: int | None = None

If provided, error on builds older than this (can be used to gate functionality without bumping entire docui version).

page: Page
shared_state_id: str | None = None

The client maintains some persistent state (such as widget selection) for all pages viewed. The default index for these states is the path of the request. If a server returns a significant variety of responses for a single path, however, (based on args, etc) then it may make sense for the server to provide explicit state ids for those different variations.

status: ResponseStatus = 0
timed_action: Action | None = None

New overall action to have the client schedule after this response is received. Useful for redirecting to other pages or closing the doc-ui window.

timed_action_delay: float = 0.0
class bacommon.docui.v1.ResponseStatus(*values)[source]

Bases: Enum

The overall result of a request.

COMMUNICATION_ERROR = 2

Something went wrong talking to the server. A ‘Retry’ button may be appropriate to show here (for GET requests at least).

NOT_SIGNED_IN_ERROR = 3

This requires the user to be signed in, and they aint.

SUCCESS = 0
UNKNOWN_ERROR = 1

Something went wrong. That’s all we know.

class bacommon.docui.v1.Row[source]

Bases: IOMultiType[RowTypeID]

Top level class for our multitype.

classmethod get_type(type_id: RowTypeID) → type[Row][source]

Return the subclass for each of our type-ids.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → Row[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.v1.RowTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

BUTTON_ROW = 'b'
UNKNOWN = 'u'
class bacommon.docui.v1.Text(text: str, position: tuple[float, float], size: tuple[float, float], scale: float = 1.0, h_align: HAlign = HAlign.CENTER, v_align: VAlign = VAlign.CENTER, color: tuple[float, float, float, float] | None = None, flatness: float | None = None, shadow: float | None = None, is_lstr: bool = False, is_langstr: bool = False, wrap: WrapParams | None = None, highlight: bool = True, depth_range: tuple[float, float] | None = None, debug: bool = False)[source]

Bases: Decoration

Text decoration.

color: tuple[float, float, float, float] | None = None
debug: bool = False

Show max-width/height bounds; useful during development.

depth_range: tuple[float, float] | None = None
flatness: float | None = None
classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

h_align: HAlign = 'c'
highlight: bool = True
is_langstr: bool = False

The text field holds a language-string (canonical resource-form wire JSON) to be evaluated natively at display and re-evaluated on language changes. Set by the client-side v2 transcode; mutually exclusive with is_lstr.

is_lstr: bool = False
position: tuple[float, float]
scale: float = 1.0
shadow: float | None = None
size: tuple[float, float]

Note that this effectively is max-width and max-height.

text: str

Note that doc-ui accepts only raw str values for text; use babase.Lstr.evaluate() or whatnot for multi-language support.

v_align: VAlign = 'c'
wrap: WrapParams | None = None

Line-wrap constraints applied to the text at widget-creation time. Set only by the client-local v2→v1 transcode; v1 producers must never send it (older clients can’t parse unknown fields — bake newlines into the string instead).

class bacommon.docui.v1.UnknownAction[source]

Bases: Action

Action type we don’t recognize.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.v1.UnknownDecoration[source]

Bases: Decoration

An unknown decoration.

In practice these should never show up since the master-server generates these on the fly for the client and so should not send clients one they can’t digest.

classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.v1.UnknownRow[source]

Bases: Row

A row type we don’t have.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.v1.VAlign(*values)[source]

Bases: Enum

Vertical alignment.

BOTTOM = 'b'
CENTER = 'c'
TOP = 't'

bacommon.docui.v2 module

Version 2 doc-ui types – language-agnostic (l-string) text.

Where v1 carries pre-localized raw str text (optionally a JSON-encoded legacy babase.Lstr via *_is_lstr flags) and expects the server to localize, v2 text is always a language-agnostic LangStrSpec. The server ships one response to every client regardless of language; the client resolves the referenced asset-packages in its own locale and decodes the strings at render time.

See docs/initiatives/docui-v2-lstrings.md (ballistica-internal). This is the milestone-1 slice: a minimal but real subset of the v1 element set, with text typed as LangStrSpec (the name-based form – subs are flat for now). Non-text fields mirror v1’s names/keys so client render code can stay close to v1prep.

class bacommon.docui.v2.Action[source]

Bases: IOMultiType[ActionTypeID]

Something that happens when a button is pressed.

classmethod get_type(type_id: ActionTypeID) → type[Action][source]

Return a specific subclass given a type-id.

Should be overridden by child classes. Generally, users of the class should call get_type_cached() instead of this, as it is more efficient.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → Action[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.v2.ActionTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

BROWSE = 'b'
LOCAL = 'l'
MENU = 'm'
REPLACE = 'r'
UNKNOWN = 'u'
type bacommon.docui.v2.AnyControlRow = AnyInputRow | ButtonControlRow

Union of the control-row types: rows holding a single control, label at the left and control at the right. That is the input rows plus ButtonControlRow, which holds no value.

type bacommon.docui.v2.AnyInputRow = CheckboxRow | TextInputRow | ChoiceRow | ColorRow | SliderRow | NumberRow

Union of the input-row types: the control rows holding a value, bound to a page-state key (see Page.state).

class bacommon.docui.v2.Browse(request: Request, default_sound: bool = True, sets: dict | None = None, state: dict | None = None, layout: WindowLayout = WindowLayout.LARGE)[source]

Bases: Action

Browse to a new page in a new window.

default_sound: bool = True

Plays a swish.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

layout: WindowLayout = 'l'

The new window’s layout. Note that the wire default is LARGE (an omitted value has always meant LARGE, and must keep doing so for older clients and servers), but routes’ own default is WIDE (see bacommon.docui.routes.DocUIRoute.browse()).

request: Request
sets: dict | None = None

Values to assign into the page’s state before the request goes out (see Page.state).

state: dict | None = None

A complete state to send instead of the page’s own; for handing state to a different page.

class bacommon.docui.v2.Button(label: LangStrSpec | int | None = None, action: Action | None = None, size: tuple[float, float] | None = None, color: tuple[float, float, float, float] | None = None, label_color: tuple[float, float, float, float] | None = None, label_scale: float | None = None, label_flatness: float | None = None, texture: TextureSpec | int | None = None, scale: float = 1.0, padding_left: float = 0.0, padding_top: float = 0.0, padding_right: float = 0.0, padding_bottom: float = 0.0, decorations: list[Decoration] | None = None, style: ButtonStyle = ButtonStyle.SQUARE, default: bool = False, selected: bool = False, disabled: bool = False, icon: TextureSpec | int | None = None, icon_scale: float | None = None, icon_color: tuple[float, float, float, float] | None = None, depth_range: tuple[float, float] | None = None, depiction: Depiction | None = None, depiction_h_align: HAlign = HAlign.CENTER, depiction_v_align: VAlign = VAlign.CENTER, depiction_hit_area: bool = False, widget_id: str | None = None, anim_id: str | None = None, debug: bool = False)[source]

Bases: object

A button in our doc-ui.

label is a language-agnostic LangStrSpec. Size, padding, and all decorations scale consistently with scale.

action: Action | None = None
anim_id: str | None = None

Lets client-effects animate this button (see bacommon.clienteffect.KeyframeAnimation); give its decorations the same id to have them move with it.

color: tuple[float, float, float, float] | None = None
debug: bool = False

Draw bounds of the button.

decorations: list[Decoration] | None = None
default: bool = False
depiction: Depiction | None = None

A depiction drawn as the button’s body, in place of its texture or style’s look, filling the button’s box (its size, at its scale). One with a shape of its own is fitted inside the box by depiction_h_align / depiction_v_align. The label, icon and decorations still draw over it, and it brightens, pulses and greys out with the button. To show a depiction elsewhere on a button, use a Depiction decoration. Clients predating this field show the button’s usual look.

depiction_h_align: HAlign = 'c'
depiction_hit_area: bool = False

With a depiction, have mouse and touch land on the button only where the depiction actually draws (an icon hugging one end of a wide button, a short name in a box sized for long ones) rather than anywhere in its box. The client grows that area to a minimum size so small depictions stay easy to tap; keyboard and controller selection are unaffected. Clients predating this field take presses anywhere in the box.

depiction_v_align: VAlign = 'c'
depth_range: tuple[float, float] | None = None
disabled: bool = False

Drawn greyed out and not activatable (a press plays an error sound instead of running action). Still selectable, so navigation around it is unaffected. Clients predating this field show it as a normal button.

icon: TextureSpec | int | None = None
icon_color: tuple[float, float, float, float] | None = None
icon_scale: float | None = None
label: LangStrSpec | int | None = None
label_color: tuple[float, float, float, float] | None = None
label_flatness: float | None = None
label_scale: float | None = None
padding_bottom: float = 0.0
padding_left: float = 0.0
padding_right: float = 0.0
padding_top: float = 0.0
scale: float = 1.0
selected: bool = False
size: tuple[float, float] | None = None
style: ButtonStyle = 'q'
texture: TextureSpec | int | None = None
widget_id: str | None = None

Custom widget id. Prefixed with the window id; unique within window.

class bacommon.docui.v2.ButtonControlRow(button: Button, label: LangStrSpec | int | None = None, disabled: bool = False, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, padding_left: float = 0.0, padding_right: float = 0.0, padding_top: float = 4.0, padding_bottom: float = 4.0, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, debug: bool = False)[source]

Bases: Row

A control row whose control is a single button.

Label at the left edge of the row; button at the right, drawn as it would be in a ButtonRow. Laid out like the other control rows, so it sits naturally in a column of them, but unlike them it holds no value: the button shows whatever the page draws on it (an icon or preview of a current selection, say) and does whatever its action says (browse to a page for picking a new one, say).

The button can be any size; the row grows to fit it, its label stays centered beside it (squished to fit the space the button leaves), and its title and footnote keep clear of it.

button: Button

The button. Its default and selected flags work as they do in a ButtonRow.

debug: bool = False

Draw bounds of the row.

disabled: bool = False

The whole row shown dimmed, with its button disabled (as by Button.disabled, which on its own leaves the label alone).

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
label: LangStrSpec | int | None = None
padding_bottom: float = 4.0
padding_left: float = 0.0

Extra inset for the label, which otherwise starts where row titles do.

padding_right: float = 0.0

Extra inset for the button, which otherwise ends where the last button of a right-aligned row does.

padding_top: float = 4.0
spacing_bottom: float = 0.0

See ButtonRow.spacing_bottom.

spacing_footnote: float = 0.0

See ButtonRow.spacing_footnote.

spacing_title: float = 0.0

See ButtonRow.spacing_title.

spacing_top: float = 0.0

See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None

Section title shown above the row (with an optional subtitle), styled and placed exactly as a ButtonRow’s.

title_align: HAlign | None = None
title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.ButtonRow(buttons: list[Button], layout: ButtonRowLayout = ButtonRowLayout.SCROLL, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, footnote: LangStrSpec | int | None = None, button_spacing: float = 15.0, padding_left: float | None = None, padding_right: float | None = None, padding_top: float | None = None, padding_bottom: float | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, center_content: bool = False, center_title: bool = False, content_align: HAlign | None = None, content_offset: float = 0.0, title_align: HAlign | None = None, simple_culling_h: float = 100.0, show_scrollbar: bool = True, debug: bool = False)[source]

Bases: Row

A row consisting of buttons.

title/subtitle are LangStrSpec. How the buttons use the row’s width is up to layout.

button_spacing: float = 15.0

Spacing between all buttons in the row.

buttons: list[Button]
center_content: bool = False
center_title: bool = False
content_align: HAlign | None = None

Horizontal alignment of buttons when they don’t fill the row’s width (a SCROLL row with more than that simply scrolls; a FIXED one shrinks to fit; FILL rows always fill it). Overrides center_content when set. Builds older than this field ignore it (and thus left-align unless center_content is also set).

content_offset: float = 0.0

Shifts a FIXED row’s buttons sideways as a group (positive is right). They lay out exactly as they otherwise would – same order, spacing and shrink-to-fit – and then the whole group moves this far, but never past the row’s edges (it slides back in instead). Centered content thus centers on the row’s center plus this offset: the same point header and footer decorations at that x are placed from, which is the way to line a button up with band art regardless of the layout’s width or insets. Ignored by SCROLL and FILL rows. Builds older than this field ignore it.

debug: bool = False

Draw bounds of the row and its button columns.

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

The header band’s mirror image – the same thing, below the row (below its footnote too). Every row type has one.

footer_scale: float = 1.0

Scales the footer band’s height and everything in it.

footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

get_padding() → tuple[float, float, float, float][source]

Our (left, right, top, bottom) padding, defaults applied.

A scrolling row’s bottom default leaves room for the strip its scroll bar fades in over; the others need none.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

Height of an optional band above the row (above its title too) holding free-form, non-interactive decorations; 0 for none. Each of the three decoration lists is positioned relative to a point on the band’s vertical midline – its left edge (inset like row titles), its center, and its right edge. Every row type has one.

header_scale: float = 1.0

Scales the header band’s height and everything in it.

layout: ButtonRowLayout = 's'

How the buttons use the row’s width. Builds older than this field always scroll.

padding_bottom: float | None = None
padding_left: float | None = None

Space around the buttons; None for the layout’s default (see get_padding()). Left/right are from the column’s edges (SCROLL and FIXED) or from where control rows’ contents start/end (FILL).

padding_right: float | None = None
padding_top: float | None = None
show_scrollbar: bool = True

Whether the row’s horizontal scroll bar is drawn (and grabbable by the mouse). Off, the row still scrolls every other way (drag, wheel, keys, page buttons) and keeps the same layout; only the bar goes. Builds older than this field always show it. (SCROLL only.)

simple_culling_h: float = 100.0

If things disappear when scrolling left/right, turn this up (SCROLL only).

spacing_bottom: float = 0.0

Extra space below the whole row (outside its footnote and footer). Every row type has one.

spacing_footnote: float = 0.0

Extra space between the row’s content and its footnote (no effect without one); the footnote’s counterpart to spacing_title. Builds older than this field ignore it. Every row type has one.

spacing_title: float = 0.0

Extra space between the title/subtitle and the row’s content (no effect without either). Titles normally hug their content; pushing one away makes it read as a heading for a group of rows rather than a label for this one. Builds older than this field ignore it. Every row type has one.

spacing_top: float = 0.0

Extra space above the whole row (outside its header and title). May be negative to pull rows together. Every row type has one.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None
title_align: HAlign | None = None

Horizontal alignment of the title and subtitle. Overrides center_title when set; same older-build caveat as content_align.

title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.ButtonRowLayout(*values)[source]

Bases: Enum

How a ButtonRow handles the row’s width.

ButtonRow.layout carries an enum_fallback (SCROLL), so a layout added here later scrolls on builds predating it instead of failing the whole response.

FILL = 'f'

Buttons sharing the row’s full width, spanning exactly what a control row’s contents do (from where control rows’ labels start to where their controls end), so the row lines up with control rows around it. Each button’s width is its share of that, in proportion to its own size width (equal sizes for equal buttons); everything else about each button, height included, is its own. Like FIXED, placed straight on the page.

FIXED = 'x'

Buttons at their own sizes, placed straight on the page (so nothing about them is clipped), aligned in the row like a scrolling row’s are. If they don’t all fit, they all shrink together until they do.

SCROLL = 's'

Buttons at their own sizes in a strip that scrolls sideways when they don’t all fit. The strip clips what it holds (vertically too, so a button growing as it’s pressed can lose its top and bottom edges); its scroll bar fades in over the bottom of its padding.

class bacommon.docui.v2.ButtonStyle(*values)[source]

Bases: Enum

Styles a button can be.

Button.style carries an enum_fallback (SQUARE), so a style added here later draws as a square button on builds predating it instead of failing the whole response.

BACK = 'b'
BACK_SMALL = 'bs'
LARGE = 'l'
LARGER = 'xl'
MEDIUM = 'm'
SMALL = 's'
SQUARE = 'q'
SQUARE_WIDE = 'w'
TAB = 't'
class bacommon.docui.v2.CheckboxRow(name: str, label: LangStrSpec | int | None = None, on_change: Action | None = None, disabled: bool = False, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, padding_left: float = 0.0, padding_right: float = 0.0, padding_top: float = 4.0, padding_bottom: float = 4.0, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, debug: bool = False)[source]

Bases: Row

A row consisting of a single checkbox.

Label at the left edge of the row; box at the right. Its value lives in the page’s state (see Page.state) under name, which must not start with an underscore and must hold a bool.

debug: bool = False

Draw bounds of the row.

disabled: bool = False

Shown dimmed and not toggleable (a press plays an error sound). Still selectable, so navigation around it is unaffected. Clients predating this field show it as a normal row.

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
label: LangStrSpec | int | None = None
name: str

Key in the page’s state holding our value.

on_change: Action | None = None

Fired when the value changes, after the new value has been written to the page’s state. Without one, a changed value simply goes out with whatever request the page fires next. Actions opening new windows are not allowed here.

padding_bottom: float = 4.0
padding_left: float = 0.0

Extra inset for the label, which otherwise starts where row titles do.

padding_right: float = 0.0

Extra inset for the box, which otherwise ends where the last button of a right-aligned row does.

padding_top: float = 4.0
spacing_bottom: float = 0.0

See ButtonRow.spacing_bottom.

spacing_footnote: float = 0.0

See ButtonRow.spacing_footnote.

spacing_title: float = 0.0

See ButtonRow.spacing_title.

spacing_top: float = 0.0

See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None

Section title shown above the row (with an optional subtitle), styled and placed exactly as a ButtonRow’s.

title_align: HAlign | None = None
title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.Choice(value: str | None, label: LangStrSpec | int, disabled: bool = False)[source]

Bases: object

One option in a ChoiceRow.

disabled: bool = False

Shown in the menu but greyed out and not pickable (an option this device can’t use, say). Clients predating this field show it as a normal choice.

label: LangStrSpec | int
value: str | None

The value stored in the page’s state when this is chosen. None is a legitimate choice value (an optional’s ‘nothing’ option) – it lands in state as a null, distinct from the key being absent.

class bacommon.docui.v2.ChoiceRow(name: str, choices: list[Choice], label: LangStrSpec | int | None = None, on_change: Action | None = None, disabled: bool = False, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, padding_left: float = 0.0, padding_right: float = 0.0, padding_top: float = 4.0, padding_bottom: float = 4.0, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, debug: bool = False)[source]

Bases: Row

A row consisting of a single pick-one-of-these control.

Label at the left edge of the row; a popup-menu button showing the current choice at the right. Its value lives in the page’s state (see Page.state) under name, which must not start with an underscore and must hold the value of one of choices.

choices: list[Choice]
debug: bool = False

Draw bounds of the row.

disabled: bool = False

The whole row shown dimmed with its menu unopenable (a press plays an error sound instead). Still selectable, so navigation around it is unaffected. Distinct from Choice.disabled, which greys individual options in a working menu. Clients predating this field show it as a normal row.

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
label: LangStrSpec | int | None = None
name: str

Key in the page’s state holding our value.

on_change: Action | None = None

Fired when a choice is picked, after the new value has been written to the page’s state. Without one, a changed value simply goes out with whatever request the page fires next. Actions opening new windows are not allowed here.

padding_bottom: float = 4.0
padding_left: float = 0.0

Extra inset for the label, which otherwise starts where row titles do.

padding_right: float = 0.0

Extra inset for the button, which otherwise ends where the last button of a right-aligned row does.

padding_top: float = 4.0
spacing_bottom: float = 0.0

See ButtonRow.spacing_bottom.

spacing_footnote: float = 0.0

See ButtonRow.spacing_footnote.

spacing_title: float = 0.0

See ButtonRow.spacing_title.

spacing_top: float = 0.0

See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None

Section title shown above the row (with an optional subtitle), styled and placed exactly as a ButtonRow’s.

title_align: HAlign | None = None
title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.ColorRow(name: str, label: LangStrSpec | int | None = None, on_change: Action | None = None, disabled: bool = False, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, padding_left: float = 0.0, padding_right: float = 0.0, padding_top: float = 4.0, padding_bottom: float = 4.0, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, debug: bool = False)[source]

Bases: Row

A row consisting of a single color-picking control.

Label at the left edge of the row; a swatch button showing the current color at the right, which opens a color picker. Its value lives in the page’s state (see Page.state) under name, which must not start with an underscore and must hold an [r, g, b] list of floats in the 0-1 range.

debug: bool = False

Draw bounds of the row.

disabled: bool = False

Shown dimmed (the swatch greyed like any disabled button) with its picker unopenable (a press plays an error sound). Still selectable, so navigation around it is unaffected. Clients predating this field show it as a normal row.

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
label: LangStrSpec | int | None = None
name: str

Key in the page’s state holding our value.

on_change: Action | None = None

Fired when the picker closes with a changed color, after the new value has been written to the page’s state. (Not on every intermediate change; the exact-color picker streams those.) Without one, a changed value simply goes out with whatever request the page fires next. Actions opening new windows are not allowed here.

padding_bottom: float = 4.0
padding_left: float = 0.0

Extra inset for the label, which otherwise starts where row titles do.

padding_right: float = 0.0

Extra inset for the swatch, which otherwise ends where the last button of a right-aligned row does.

padding_top: float = 4.0
spacing_bottom: float = 0.0

See ButtonRow.spacing_bottom.

spacing_footnote: float = 0.0

See ButtonRow.spacing_footnote.

spacing_title: float = 0.0

See ButtonRow.spacing_title.

spacing_top: float = 0.0

See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None

Section title shown above the row (with an optional subtitle), styled and placed exactly as a ButtonRow’s.

title_align: HAlign | None = None
title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.Decoration[source]

Bases: IOMultiType[DecorationTypeID]

Top level class for our decoration multitype.

classmethod get_type(type_id: DecorationTypeID) → type[Decoration][source]

Return a specific subclass given a type-id.

Should be overridden by child classes. Generally, users of the class should call get_type_cached() instead of this, as it is more efficient.

classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → Decoration[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.v2.DecorationTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

DEPICTION = 'd'
IMAGE = 'i'
TEXT = 't'
UNKNOWN = 'u'
class bacommon.docui.v2.Depiction(depiction: Depiction, position: tuple[float, float], size: tuple[float, float], h_align: HAlign = HAlign.CENTER, v_align: VAlign = VAlign.CENTER, highlight: bool = True, depth_range: tuple[float, float] | None = None, debug: bool = False, anim_id: str | None = None)[source]

Bases: Decoration

A bacommon.depiction.Depiction, drawn in a box.

position is the box’s center and size its size. A depiction with a shape of its own (a square icon, an image’s aspect) is fitted inside the box and placed by h_align/v_align; one without (a name) fills the box.

Depictions are self-sufficient: whatever packages one references contribute nothing to the page’s own Response.packages. Art that isn’t local yet shows as a standin, never a blocked page.

anim_id: str | None = None

Lets client-effects animate this decoration (see bacommon.clienteffect.KeyframeAnimation). Everything in a page sharing an id animates together.

debug: bool = False
depiction: Depiction
depth_range: tuple[float, float] | None = None
classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

h_align: HAlign = 'c'
highlight: bool = True

Whether to follow the button this decorates – brightening as it’s hovered, pressed, or selected, and drawing faded and greyed while it’s disabled.

position: tuple[float, float]
size: tuple[float, float]
v_align: VAlign = 'c'
class bacommon.docui.v2.HAlign(*values)[source]

Bases: Enum

Horizontal alignment.

Fields of this type carry an enum_fallback (each field’s own default; LEFT for the optional row alignments), so a value added here later decodes as that on builds predating it instead of failing the whole response.

CENTER = 'c'
LEFT = 'l'
RIGHT = 'r'
class bacommon.docui.v2.Image(texture: TextureSpec | int, position: tuple[float, float], size: tuple[float, float], color: tuple[float, float, float, float] | None = None, h_align: HAlign = HAlign.CENTER, v_align: VAlign = VAlign.CENTER, tint_texture: TextureSpec | int | None = None, tint_color: tuple[float, float, float] | None = None, tint2_color: tuple[float, float, float] | None = None, mask_texture: TextureSpec | int | None = None, mesh_opaque: MeshSpec | int | None = None, mesh_transparent: MeshSpec | int | None = None, highlight: bool = True, depth_range: tuple[float, float] | None = None, tint3_color: tuple[float, float, float] | None = None, nine_patch: ImageNinePatch | None = None, debug: bool = False, anim_id: str | None = None)[source]

Bases: Decoration

Image decoration. Textures/meshes are language-independent refs.

Unlike text, image assets need no per-locale decode; each ref (TextureSpec / MeshSpec) is resolved by the client and rendered directly.

anim_id: str | None = None

Lets client-effects animate this decoration (see bacommon.clienteffect.KeyframeAnimation). Everything in a page sharing an id animates together.

color: tuple[float, float, float, float] | None = None
debug: bool = False

Show this image’s bounds; useful during development. Worth having separately from the art because a texture with a transparent margin gives no clue where its box really is.

depth_range: tuple[float, float] | None = None
classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

h_align: HAlign = 'c'
highlight: bool = True
mask_texture: TextureSpec | int | None = None
mesh_opaque: MeshSpec | int | None = None
mesh_transparent: MeshSpec | int | None = None
nine_patch: ImageNinePatch | None = None

Draw as a 9-patch (see ImageNinePatch). Clients before this field ignore it and stretch the whole texture over the box.

position: tuple[float, float]
size: tuple[float, float]
texture: TextureSpec | int

The image’s texture. An int is the indexed form – a flat index into the textures domain of Response.packages (see bacommon.assetspec._index); the client swaps it for a TextureSpec while resolving, so everything downstream of resolve sees only specs. Old clients are served the spec form.

tint2_color: tuple[float, float, float] | None = None
tint3_color: tuple[float, float, float] | None = None

Tint through the tint texture’s blue channel (as tint_color is red and tint2_color green). Clients before this field ignore it.

tint_color: tuple[float, float, float] | None = None
tint_texture: TextureSpec | int | None = None
v_align: VAlign = 'c'
class bacommon.docui.v2.ImageNinePatch(insets: tuple[float, float, float, float], borders: tuple[float, float, float, float], tile_h: bool = False, tile_v: bool = False)[source]

Bases: object

Draws an Image as a 9-patch filling its box exactly.

The texture splits into corners, edges and a middle; corners keep their drawn size, edges and middle fill what’s between. Tint and mask textures share the texture’s layout.

borders: tuple[float, float, float, float]

How big those edges draw, in the image’s own units (as its size), same order. A pair too big for the box shrinks to fit.

insets: tuple[float, float, float, float]

Where the texture splits, as fractions of its width/height from the left, bottom, right and top. 0.5 on each side of an axis makes that axis’s middle a single texel line.

tile_h: bool = False

Repeat the horizontal (tile_h) or vertical (tile_v) middle at the corners’ scale – fitted to a whole number of copies – rather than stretching it. Its art must tile seamlessly.

tile_v: bool = False
class bacommon.docui.v2.Local(close_window: bool = False, default_sound: bool = True, immediate_local_action: str | None = None, immediate_local_action_args: dict | None = None, sets: dict | None = None, return_sets: dict | None = None)[source]

Bases: Action

Perform only local actions; no new requests or page changes.

close_window: bool = False
default_sound: bool = True

Plays a swish if closing the window, else a click.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

immediate_local_action: str | None = None

Local action to run immediately when the button is pressed. Will be handled by bauiv1lib.docui.DocUIController.local_action().

immediate_local_action_args: dict | None = None
return_sets: dict | None = None

With close_window, values to assign into the state of the page being returned to, which then refreshes with them; how a picker opened in a window of its own hands back what was picked. Carries the state’s type id (_t), and is applied only if the returned-to page’s state is of that type. Build it with bacommon.docui.routes.DocUIState.assign_on_return().

sets: dict | None = None

Values to assign into the page’s state (see Page.state).

class bacommon.docui.v2.Menu(items: list[MenuItem], default_sound: bool = True)[source]

Bases: Action

Pop up a menu of items at the button; picking one runs its action.

For buttons offering several things to do (a ‘…’ button, say). Picking a value is a ChoiceRow’s job instead. The button is drawn like any other; its label or icon is what says it opens a menu.

Only buttons can open menus; one arriving anywhere else an action can go (an input row’s on_change, a timed action, a menu item) is ignored. Clients predating this type see an unknown action.

default_sound: bool = True

Plays a swish.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

items: list[MenuItem]
class bacommon.docui.v2.MenuItem(label: LangStrSpec | int, action: Action | None = None, disabled: bool = False)[source]

Bases: object

One entry in a Menu.

action: Action | None = None

What picking this item does; anything a button’s action can be except another Menu (menus don’t nest). The menu makes its own sound when something is picked, so the action’s default_sound is not played.

disabled: bool = False

Shown in the menu but greyed out and not pickable.

label: LangStrSpec | int
class bacommon.docui.v2.NumberRow(name: str, min_value: float, max_value: float, increment: float, label: LangStrSpec | int | None = None, as_percent: bool = False, decimals: int = 0, on_change: Action | None = None, disabled: bool = False, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, padding_left: float = 0.0, padding_right: float = 0.0, padding_top: float = 4.0, padding_bottom: float = 4.0, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, debug: bool = False)[source]

Bases: Row

A row editing a number with a ‘-’ and a ‘+’ button.

Label at the left, then at the row’s right the current value as text followed by the two buttons. Each press steps the value by increment within min_value/max_value (holding a button repeats). Its value lives in the page’s state (see Page.state) under name, which must not start with an underscore and must hold a float between min_value and max_value.

For a value with many steps, SliderRow is usually the better fit; this suits short ranges where each step is a distinct setting.

as_percent: bool = False

Show the value as a whole percentage (0.5 -> 50%) rather than to decimals places.

debug: bool = False

Draw bounds of the row.

decimals: int = 0
disabled: bool = False

Shown dimmed and not adjustable (both buttons disabled). Still selectable, so navigation around it is unaffected. Clients predating this field show it as a normal adjustable row.

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
increment: float
label: LangStrSpec | int | None = None
max_value: float
min_value: float
name: str

Key in the page’s state holding our value.

on_change: Action | None = None

Fired on each press that changes the value, after it has been written to the page’s state. Without one, a changed value simply goes out with whatever request the page fires next. Actions opening new windows are not allowed here.

padding_bottom: float = 4.0
padding_left: float = 0.0

Extra inset for the label, which otherwise starts where row titles do.

padding_right: float = 0.0

Extra inset for the ‘+’ button, which otherwise ends where the last button of a right-aligned row does.

padding_top: float = 4.0
spacing_bottom: float = 0.0

See ButtonRow.spacing_bottom.

spacing_footnote: float = 0.0

See ButtonRow.spacing_footnote.

spacing_title: float = 0.0

See ButtonRow.spacing_title.

spacing_top: float = 0.0

See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None

Section title shown above the row (with an optional subtitle), styled and placed exactly as a ButtonRow’s.

title_align: HAlign | None = None
title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.Page(title: LangStrSpec | int, rows: list[Row], state: dict | None = None, viewer: Depiction | None = None, center_vertically: bool = False, show_scrollbar: bool = True, row_spacing: float = 10.0, simple_culling_v: float = 100.0, padding_bottom: float = 0.0, padding_left: float = 0.0, padding_top: float = 0.0, padding_right: float = 0.0)[source]

Bases: object

Doc-UI page version 2.

title is a language-agnostic LangStrSpec.

center_vertically: bool = False

Center content vertically when it’s smaller than the available height.

padding_bottom: float = 0.0
padding_left: float = 0.0
padding_right: float = 0.0
padding_top: float = 0.0
row_spacing: float = 10.0
rows: list[Row]
show_scrollbar: bool = True

Whether the page’s vertical scroll bar is drawn (and grabbable by the mouse). Off, the page still scrolls every other way (drag, wheel, keys) and keeps the same layout; only the bar goes. Builds older than this field always show it.

simple_culling_v: float = 100.0

If things disappear when scrolling up/down, turn this up.

state: dict | None = None

Values belonging to the page as a whole – what input rows show and edit, plus anything else the page wants handed back. The client sends the current values along with every request fired from the page (see Request.state), and whatever page comes back replaces them with its own.

A flat dict of json values. Keys starting with an underscore are reserved; _t optionally names the type of state this is so that state reaching a page expecting some other type can be recognized as such and ignored.

title: LangStrSpec | int
viewer: Depiction | None = None

What the window’s viewer pane shows, for windows at the WindowLayout.VIEWER layout (ignored elsewhere): usually a bacommon.depiction.CharacterViewerDepiction. The client keeps what the pane shows running across pages, so a page re-sent with a changed depiction (an editor’s draft, say) updates the picture in place. Without one the pane is left blank.

class bacommon.docui.v2.Replace(request: Request, default_sound: bool = True, sets: dict | None = None, state: dict | None = None)[source]

Bases: Action

Replace the current page with a new one (seamless transition).

default_sound: bool = True

Plays a click if triggered by a button press.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

request: Request
sets: dict | None = None

Values to assign into the page’s state before the request goes out (see Page.state).

state: dict | None = None

A complete state to send instead of the page’s own; for handing state to a different page.

class bacommon.docui.v2.Request(path: str, method: RequestMethod = RequestMethod.GET, args: dict = <factory>, state: dict | None = None, trigger: str | None = None)[source]

Bases: DocUIRequest

Full request to doc-ui (v2).

args: dict
classmethod get_type_id() → DocUIRequestTypeID[source]

Return the type-id for this subclass.

method: RequestMethod = 'g'
path: str
state: dict | None = None

Current state of the page this request was fired from (see Page.state), if it had any. Clients fill this in; page authors never set it directly.

trigger: str | None = None

State key of the input whose value change fired this request, if that is what fired it.

class bacommon.docui.v2.RequestMethod(*values)[source]

Bases: Enum

Type of requests that can be made to doc-ui servers.

GET = 'g'

Fetch some resource. Retriable; results optionally cacheable.

POST = 'p'

Change some resource. Not implicitly retriable, not cacheable.

UNKNOWN = 'u'

An unknown request method (newer client -> older server).

class bacommon.docui.v2.Response(page: Page, status: ResponseStatus = ResponseStatus.SUCCESS, for_build: int | None = None, packages: list[ApverNum] = <factory>, asset_index_digest: str | None = None, langstr_index_digest: str | None = None, local_action: str | None = None, local_action_args: dict | None = None, timed_action: Action | None = None, timed_action_delay: float = 0.0, minimum_engine_build: int | None = None, shared_state_id: str | None = None)[source]

Bases: DocUIResponse

Full docui response (v2).

asset_index_digest: str | None = None

Digest of the exact asset-index domain the producer indexed against, from domain_digest(). The two ends build that domain from different sources, and a disagreement is invisible on its own – an index that is wrong but still in range simply names a different asset, so the page renders with the wrong art and nothing logs. A consumer whose own digest differs must refuse to de-index rather than trust it; leaving the integers in place makes the failure loud and names the packages involved. Set only when asset refs were indexed.

for_build: int | None = None

The engine build this response was built for, as a sanity check: responses can be tailored per-build (client-effect forms etc.), so a consumer seeing a mismatch with its own build should treat the response as stale (e.g. toss cached data) rather than use it.

classmethod get_type_id() → DocUIResponseTypeID[source]

Return the type-id for this subclass.

langstr_index_digest: str | None = None

The same guard for folded language-string references, from domain_digest(). Separate from the asset digest so a mismatch says which of the two domains drifted. Set only when string refs were folded.

local_action: str | None = None

Local action to run after this response is initially received (not re-run on automatic page refreshes). Will be handled by bauiv1lib.docui.DocUIController.local_action().

local_action_args: dict | None = None
minimum_engine_build: int | None = None

If provided, error on builds older than this.

packages: list[ApverNum]

Asset-package-versions the response’s integer-indexed language-strings resolve against (position = package index). Present only on wire responses finalized to the indexed form by the server; its presence declares the page + contained client effects fully indexed (consumers may flag resource-form leaks), and it doubles as the client’s resolve/pre-warm manifest. Locally-authored responses never carry it (indexing is a wire compression; local pages stay in the authored resource form).

page: Page
shared_state_id: str | None = None

Explicit shared-state id (defaults to the request path client-side).

status: ResponseStatus = 0

(A status this build doesn’t know decodes as ResponseStatus.UNKNOWN_ERROR, so statuses can be added without older clients failing to decode the response. Builds before 23021 lack this fallback: a server must not send them ResponseStatus.NEED_UPDATE_ERROR or anything newer.)

timed_action: Action | None = None

New overall action to have the client schedule after this response is received. Useful for redirecting to other pages or closing the doc-ui window.

timed_action_delay: float = 0.0
class bacommon.docui.v2.ResponseStatus(*values)[source]

Bases: Enum

The overall result of a request.

COMMUNICATION_ERROR = 2

Something went wrong talking to the server. A ‘Retry’ may be apt.

NEED_UPDATE_ERROR = 4

The client is too old for this; it needs an update. Set by a server declining a request on those grounds, so clients can show their standard update prompt (and composite pages can say so in their own words) without reading the page’s text. Response.minimum_engine_build may name the build when the server knows it.

NOT_SIGNED_IN_ERROR = 3

This requires the user to be signed in, and they aint.

SUCCESS = 0
UNKNOWN_ERROR = 1

Something went wrong. That’s all we know.

class bacommon.docui.v2.Row[source]

Bases: IOMultiType[RowTypeID]

Top level class for our row multitype.

classmethod get_type(type_id: RowTypeID) → type[Row][source]

Return a specific subclass given a type-id.

Should be overridden by child classes. Generally, users of the class should call get_type_cached() instead of this, as it is more efficient.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

classmethod get_type_id_storage_name() → str[source]

Return the key used to store type id in serialized data.

The default is a short obscure value so that it is unlikely to conflict with members of individual type attrs, but in some cases one might prefer to serialize it to something simpler like ‘type’ by overriding this call. One just needs to make sure that no encompassed types serialize anything to that same name themself (dataclassio will error if they do).

classmethod get_unknown_type_fallback() → Row[source]

Return a fallback object in cases of unrecognized types.

This can allow newer data to remain readable in older environments. Use caution with this option, however, as it effectively modifies data.

class bacommon.docui.v2.RowTypeID(*values)[source]

Bases: Enum

Type ID for each of our subclasses.

BUTTON_CONTROL_ROW = 'bc'
BUTTON_ROW = 'b'
CHECKBOX_ROW = 'c'
CHOICE_ROW = 'h'
COLOR_ROW = 'k'
NUMBER_ROW = 'n'
SECTION = 'sc'
SLIDER_ROW = 's'
TEXT_INPUT_ROW = 't'
UNKNOWN = 'u'
class bacommon.docui.v2.Section(rows: list[Row], backing: SectionBacking | None = None, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, footnote_color: tuple[float, float, float, float] | None = None, footnote_flatness: float | None = None, footnote_shadow: float | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, debug: bool = False)[source]

Bases: Row

A group of rows, with an optional heading, note and backing.

Its rows lay out exactly as they would directly in the page; the section adds around them a heading (header band, title, subtitle) above, a note (footnote, footer band) below, and optionally a backing (see SectionBacking), which turns it into a card with a layout of its own. With none of those it just stands its rows a little apart from what comes before and after, so the grouping reads at a glance.

The heading is drawn like a row’s own titles but a bit larger, so it reads as heading the group rather than labeling one row; the note likewise. Neither is selectable: selecting the section’s first selectable row scrolls its heading into view too, and its last its note.

Sections don’t nest; a section among a section’s rows is ignored (the client logs an error). Builds older than this row type skip a section entirely, rows included.

title/subtitle/footnote are LangStrSpec.

backing: SectionBacking | None = None

Make the section a card (see SectionBacking).

debug: bool = False

Outline the section’s heading and note (as rows’ debug does). Its rows have their own debug flags.

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

The note under the section’s rows (drawn like a subtitle).

footnote_color: tuple[float, float, float, float] | None = None

The footnote’s color/flatness/shadow (drawn like a subtitle).

footnote_flatness: float | None = None
footnote_shadow: float | None = None
classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
rows: list[Row]

The rows in the section.

spacing_bottom: float = 0.0
spacing_top: float = 0.0

Extra space above the section (outside its header band) and below it (outside its footer band), on top of the standing room it keeps from its neighbors; either may be negative. See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None
title_align: HAlign | None = None

See ButtonRow.title_align; the footnote follows it too. Left by default.

title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.SectionBacking(texture: TextureSpec | int | None = None, color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0), h_pin: tuple[float, float] = (0.0, 0.0), v_pin: tuple[float, float] = (0.0, 0.0), max_width: float | None = None, content_inset: float = 0.0, padding_top: float = 0.0, padding_bottom: float = 0.0)[source]

Bases: object

A backing for a Section, and the layout that goes with it.

A backed section is a card: a rect of max_width at most (else the column’s full width), centered in the column, from the top of the section’s heading (else its first row) to the bottom of its note (else its last row) plus padding_top/padding_bottom. Its button rows clip at the rect’s left and right edges, and its content lays out inside it as a page’s does in its column: button rows’ buttons content_inset in from those edges, text (heading and note text, row titles, labels) and control and fill rows a few units further in to line up with buttons’ visible edges, and centered things centered on the card. All of that holds whatever width the section gets.

The rect is drawn as texture (tinted by color) or, with no texture, a flat color fill; an alpha of 0 draws nothing, which leaves just the layout.

A texture’s visible shape usually sits inside soft or shadowed margins; the pins say where. h_pin (left, right) is how far in from each side of the image, as a fraction of its width (0 to 0.5), the shape’s edge falls: the image is stretched so those points land on the rect’s edges. v_pin (top, bottom) does the same vertically. Pins are a property of the texture alone, so one calibration holds at every width. They don’t apply to flat fills.

color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0)
content_inset: float = 0.0

How far in from the card’s edges its button rows’ buttons sit (both sides; a page’s sit 28 in from its column’s).

h_pin: tuple[float, float] = (0.0, 0.0)
max_width: float | None = None

Widest the card gets; None to take the column’s full width.

padding_bottom: float = 0.0
padding_top: float = 0.0

Room inside the card above its first content and below its last.

texture: TextureSpec | int | None = None
v_pin: tuple[float, float] = (0.0, 0.0)
class bacommon.docui.v2.SliderRow(name: str, min_value: float, max_value: float, increment: float, label: LangStrSpec | int | None = None, as_percent: bool = False, decimals: int = 2, on_change: Action | None = None, on_drag: Local | None = None, drag_interval: float = 0.25, drag_delay: float = 0.0, disabled: bool = False, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, padding_left: float = 0.0, padding_right: float = 0.0, padding_top: float = 4.0, padding_bottom: float = 4.0, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, debug: bool = False)[source]

Bases: Row

A row consisting of a single slider control.

Label at the left (right-aligned toward the control), the current value as text, then the slider itself at the row’s right. Its value lives in the page’s state (see Page.state) under name, which must not start with an underscore and must hold a float between min_value and max_value.

as_percent: bool = False

Show the value as a whole percentage (0.5 -> 50%) rather than to decimals places.

debug: bool = False

Draw bounds of the row.

decimals: int = 2
disabled: bool = False

Shown dimmed and not adjustable. Still selectable, so navigation around it is unaffected. Clients predating this field show it as a normal adjustable row.

drag_delay: float = 0.0
drag_interval: float = 0.25
footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
increment: float
label: LangStrSpec | int | None = None
max_value: float
min_value: float
name: str

Key in the page’s state holding our value.

on_change: Action | None = None

Fired when a value is settled on – the nub released after a drag, or a run of key/controller steps going quiet (half a second after the last step, or at once on deselection) – after the value has been written to the page’s state. Without one, a changed value simply goes out with whatever request the page fires next. Actions opening new windows are not allowed here.

on_drag: Local | None = None

Fired while the nub is being dragged (key/controller steps count as dragging), at most every drag_interval seconds and not before drag_delay into the drag, with the page’s state already holding the value so far; also once for the settled value if the drag’s last apply missed it. Local only (typed so): whatever needs to track the value live – a volume being set – happens client side, with no request until the value settles.

padding_bottom: float = 4.0
padding_left: float = 0.0

Extra inset for the label, which otherwise starts where row titles do.

padding_right: float = 0.0

Extra inset for the slider, which otherwise ends where the last button of a right-aligned row does.

padding_top: float = 4.0
spacing_bottom: float = 0.0

See ButtonRow.spacing_bottom.

spacing_footnote: float = 0.0

See ButtonRow.spacing_footnote.

spacing_title: float = 0.0

See ButtonRow.spacing_title.

spacing_top: float = 0.0

See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None

Section title shown above the row (with an optional subtitle), styled and placed exactly as a ButtonRow’s.

title_align: HAlign | None = None
title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.Text(text: LangStrSpec | int, position: tuple[float, float], size: tuple[float, float], scale: float = 1.0, h_align: HAlign = HAlign.CENTER, v_align: VAlign = VAlign.CENTER, color: tuple[float, float, float, float] | None = None, flatness: float | None = None, shadow: float | None = None, highlight: bool = True, depth_range: tuple[float, float] | None = None, debug: bool = False, image_left: TextImage | None = None, image_right: TextImage | None = None, anim_id: str | None = None)[source]

Bases: Decoration

Text decoration.

text is a language-agnostic LangStrSpec.

With image_left or image_right set, the text and its images are measured, shrunk to fit size (never grown), and aligned as a single unit.

anim_id: str | None = None

Lets client-effects animate this decoration (see bacommon.clienteffect.KeyframeAnimation). Everything in a page sharing an id animates together.

color: tuple[float, float, float, float] | None = None
debug: bool = False

Show max-width/height bounds; useful during development.

depth_range: tuple[float, float] | None = None
flatness: float | None = None
classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

h_align: HAlign = 'c'
highlight: bool = True
image_left: TextImage | None = None

An image fixed to the left end of the text.

image_right: TextImage | None = None

An image fixed to the right end of the text.

position: tuple[float, float]
scale: float = 1.0
shadow: float | None = None
size: tuple[float, float]

Effectively max-width and max-height.

text: LangStrSpec | int

The text. An int is the indexed form – a flat index into the string domain of Response.packages (see bacommon.langstr._flatindex); the client unfolds it into the two-integer form the native decoder consumes while resolving. Strings carrying substitutions never fold.

v_align: VAlign = 'c'
class bacommon.docui.v2.TextImage(texture: TextureSpec | int, size: tuple[float, float], offset: tuple[float, float] = (0.0, 0.0), color: tuple[float, float, float, float] | None = None, insets: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0))[source]

Bases: object

An image fixed to one end of a Text decoration.

For things like a price: a count with its currency icon beside it, measured, fitted, and aligned as one unit. The producer cannot do that itself because it cannot know the text’s rendered width; the client measures it while laying out the text.

Sizes and offsets are in text units – they are multiplied by the text’s effective scale – so the image grows and shrinks with its text. The image’s layout box (its size less its insets) sits butted against its end of the text and vertically centered on the line; spacing from the text therefore comes from the insets, not from anything the producer adds.

Meant for single-line text; on multi-line text the images center against the whole block.

color: tuple[float, float, float, float] | None = None

Color and opacity. Deliberately separate from the text’s color: a coin should not turn green because its count is.

insets: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0)

The image’s layout box, as fractions of its size trimmed from each edge, in (left, bottom, right, top) order. Layout uses only this box – for butting against the text, centering on the line, and measuring the unit – while the full image still draws. Typically trims most of the art’s transparent padding, leaving in whatever spacing the art wants beside text – the way a font glyph carries its own side bearings. A negative inset extends the box past the image instead (like a CSS margin), for art with less padding than the spacing it wants. Fractions rather than units because this is a fact of the art, true at any size.

offset: tuple[float, float] = (0.0, 0.0)

A purely visual shift in text units, positive being right/up. It moves where the image draws and nothing else, like CSS relative positioning; measuring, fitting, and alignment ignore it. For small optical corrections – spacing belongs in insets.

size: tuple[float, float]

Size in text units.

texture: TextureSpec | int

The image’s texture. An int is the indexed form; see Image.texture.

class bacommon.docui.v2.TextInputRow(name: str, label: LangStrSpec | int | None = None, description: LangStrSpec | int | None = None, max_chars: int = 64, on_change: Action | None = None, on_submit: Action | None = None, disabled: bool = False, title: LangStrSpec | int | None = None, title_color: tuple[float, float, float, float] | None = None, title_flatness: float | None = None, title_shadow: float | None = None, subtitle: LangStrSpec | int | None = None, subtitle_color: tuple[float, float, float, float] | None = None, subtitle_flatness: float | None = None, subtitle_shadow: float | None = None, title_align: HAlign | None = None, footnote: LangStrSpec | int | None = None, padding_left: float = 0.0, padding_right: float = 0.0, padding_top: float = 4.0, padding_bottom: float = 4.0, header_height: float = 0.0, header_scale: float = 1.0, header_decorations_left: list[Decoration] | None = None, header_decorations_center: list[Decoration] | None = None, header_decorations_right: list[Decoration] | None = None, footer_height: float = 0.0, footer_scale: float = 1.0, footer_decorations_left: list[Decoration] | None = None, footer_decorations_center: list[Decoration] | None = None, footer_decorations_right: list[Decoration] | None = None, spacing_top: float = 0.0, spacing_bottom: float = 0.0, spacing_title: float = 0.0, spacing_footnote: float = 0.0, debug: bool = False)[source]

Bases: Row

A row consisting of a single editable line of text.

Label at the left edge of the row, taking up to half of its width (and squished to fit if it wants more); the editable text gets the rest. Its value lives in the page’s state (see Page.state) under name, which must not start with an underscore and must hold a str.

debug: bool = False

Draw bounds of the row.

description: LangStrSpec | int | None = None

What is being edited, for places that ask for text by way of a separate dialog (on-screen keyboards and such). The label gets used when this is not provided.

disabled: bool = False

Shown dimmed and not editable. Still selectable, so navigation around it is unaffected. Clients predating this field show it as a normal editable row.

footer_decorations_center: list[Decoration] | None = None
footer_decorations_left: list[Decoration] | None = None
footer_decorations_right: list[Decoration] | None = None
footer_height: float = 0.0

See ButtonRow.footer_height.

footer_scale: float = 1.0
footnote: LangStrSpec | int | None = None

Small explanatory text drawn below the row’s content, aligned like its title; the row grows to make room for it.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

header_decorations_center: list[Decoration] | None = None
header_decorations_left: list[Decoration] | None = None
header_decorations_right: list[Decoration] | None = None
header_height: float = 0.0

See ButtonRow.header_height.

header_scale: float = 1.0
label: LangStrSpec | int | None = None
max_chars: int = 64
name: str

Key in the page’s state holding our value.

on_change: Action | None = None

Fired when an edit is applied (a text-entry dialog closing with a value, inline editing ending, the clear button; NOT per character) with a changed value, after that value has been written to the page’s state. Without one, a changed value simply goes out with whatever request the page fires next. Actions opening new windows are not allowed here.

on_submit: Action | None = None

Fired when the user submits the field (return/enter while editing inline, or a text-entry dialog’s submit), after the value has been applied to the page’s state. For forms where typing and hitting return should do what the page’s main button does. Any action is allowed.

padding_bottom: float = 4.0
padding_left: float = 0.0

Extra inset for the label, which otherwise starts where row titles do.

padding_right: float = 0.0

Extra inset for the text box, which otherwise ends where the last button of a right-aligned row does.

padding_top: float = 4.0
spacing_bottom: float = 0.0

See ButtonRow.spacing_bottom.

spacing_footnote: float = 0.0

See ButtonRow.spacing_footnote.

spacing_title: float = 0.0

See ButtonRow.spacing_title.

spacing_top: float = 0.0

See ButtonRow.spacing_top.

subtitle: LangStrSpec | int | None = None
subtitle_color: tuple[float, float, float, float] | None = None
subtitle_flatness: float | None = None
subtitle_shadow: float | None = None
title: LangStrSpec | int | None = None

Section title shown above the row (with an optional subtitle), styled and placed exactly as a ButtonRow’s.

title_align: HAlign | None = None
title_color: tuple[float, float, float, float] | None = None
title_flatness: float | None = None
title_shadow: float | None = None
class bacommon.docui.v2.UnknownAction[source]

Bases: Action

Action type we don’t recognize.

classmethod get_type_id() → ActionTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.v2.UnknownDecoration[source]

Bases: Decoration

An unknown decoration (should never reach a client in practice).

classmethod get_type_id() → DecorationTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.v2.UnknownRow[source]

Bases: Row

A row type we don’t have.

classmethod get_type_id() → RowTypeID[source]

Return the type-id for this subclass.

class bacommon.docui.v2.VAlign(*values)[source]

Bases: Enum

Vertical alignment.

Fields of this type carry an enum_fallback (CENTER), so a value added here later decodes as that on builds predating it instead of failing the whole response.

BOTTOM = 'b'
CENTER = 'c'
TOP = 't'
class bacommon.docui.v2.WindowLayout(*values)[source]

Bases: Enum

The overall shape of a doc-ui window.

Chosen by whatever opens the window (see Browse.layout), since the window exists before its page arrives; pages replacing each other within a window share its layout. The client owns each layout’s actual geometry per ui-scale.

LARGE = 'l'

The standard full-size window.

SMALL = 's'

A narrower, shorter window for simple list-style pages (settings and the like). At small ui-scale, where windows fill the screen, content is instead laid out in a centered column of limited width.

SMALL_TALL = 'st'

A small layout’s width, with a height between a small and a small-taller layout’s. The same as a small layout at small ui-scale.

SMALL_TALLER = 'str'

A small layout’s width, but taller; for long list-style pages (options pages and the like). The same as a small layout at small ui-scale.

VIEWER = 'v'

A small layout’s column at the right of a full-size window, the rest to its left reserved for a viewer pane (a live character view, say).

WIDE = 'w'

A squat window for horizontally laid out content designed to fill the screen elegantly (a row of big buttons, say). Its pages get the same width and height at every ui-scale – small ui-scale’s on the narrowest screen – so content sized to fit never scrolls and leaves little empty backing. Fills the screen at small ui-scale (full width, unlike a small layout).

WIDER = 'wr'

A wide layout’s height, but wider at medium/large ui-scale; for content expected to be wider than the screen (a long row of items, say), where showing as much as possible means less scrolling. The same as a wide layout at small ui-scale.

bacommon.docui.v2.all_rows(rows: list[Row]) → Iterator[Row][source]

Yield rows depth-first: each row, and a section’s rows after it.

For code that visits every row wherever it sits (string and action walkers, validation). Sections don’t nest, but this simply recurses if one does.

bacommon.docui.v2.is_control_row(row: Row) → TypeIs[AnyControlRow][source]

Is this row one of the control-row types (AnyControlRow)?

Everything that lays control rows out alike keys off this rather than listing them; it narrows the type in both branches.

bacommon.docui.v2.is_input_row(row: Row) → TypeIs[AnyInputRow][source]

Is this row one of the input-row types (AnyInputRow)?

Everything concerned with the values rows hold keys off this rather than listing them; it narrows the type in both branches.

bacommon.docui.walk module

One traversal of a doc-ui page’s language-strings and asset refs.

Warning

This is an internal api and subject to change at any time. Do not use it in mod code.

Several things need to visit every string slot or every asset reference in a page: the client resolves the packages they name before rendering, the server rewrites them to their indexed wire forms, and so on. Each of those used to walk the page itself.

That went wrong the same way three times. Every walk matched decorations with an isinstance chain and no final else, so a decoration type a given walk had not been taught about contributed nothing and the walk returned a plausible answer – silently short of the truth. Adding a (since-retired) frame decoration missed three of the four places that needed it.

So the traversal lives here once, and it dispatches on DecorationTypeID with assert_never on the end. A new decoration type is now a type error in a single file rather than a silent omission in an unknown number of them.

Callbacks may return a replacement value (or None to leave a slot alone), which is what lets one traversal serve both the read-only consumers and the ones that rewrite in place.