Skip to content

Choice widgets

When a dialogue reaches a Choice line, Libretto spawns a UI widget that offers the player the available replies, waits for a selection (or for the choice timer to run out), and jumps to the chosen reply. This page explains how to configure which widget is used and how to build your own.

Configuration

The widget class used by default for dialogue choices is set in Project Settings ▸ Libretto, in the UI section.

Choice widget settings in Project Settings ▸ Libretto

  • Widget Choice Default Class — the choice widget spawned for a Choice that has no matching flag. If you leave it empty, choices fall back to the widget shipped with the plugin, DefaultLBRDialogueChoiceWidget_WBP. That default is a good starting point: duplicate it to use as a base or as a worked example.
  • Widget Choice Map Class — lets you pick a different widget for specific choices based on a gameplay flag present on the Choice line. The order of the entries in the map defines priority: when a line carries several flags, the first flag that appears in the map decides which widget class is used.

Available replies

A reply is considered available when all of the following hold:

  1. It has never been selected, or it has been selected but Do Once is unticked.
  2. It has no Display Condition, or the condition evaluates to true.
  3. It has no Availability Condition, or the condition evaluates to true.

Note

Display Condition and Availability Condition differ in how a failing condition is handled. A failing Display Condition removes the reply entirely — it is neither offered nor shown. A failing Availability Condition still shows the reply, but greyed out and not selectable. See the Dialogue Editor page for where to set Do Once, Default Reply, and both conditions on a Reply line.

Building a custom widget

A choice UI is made of two widgets:

  1. The choice widget itself, which must inherit from ULBRDialogueChoiceWidget.
  2. The reply button widget, which must inherit from ULBRDialogueChoiceButton.

ULBRDialogueChoiceWidget properties

Property Purpose
Dialogue The Dialogue asset the choice belongs to.
DialogueInstance The running ALBRDialogueInstance.
LineIDChoice The ID of the Choice line being displayed.
ButtonClass The ULBRDialogueChoiceButton subclass used for the reply buttons. Leave it empty to use the built-in default button, which builds a simple button-with-text on the fly.
ChoiceTimerHandle Timer handle for a timed choice — read it to know how much time is left.
ChoiceButtons The reply buttons currently spawned by this widget.

Together, Dialogue, DialogueInstance and LineIDChoice give you everything needed to configure the choice presentation.

Getting the Reply line's own ID from an IDToJump

Several methods return an IDToJump — the ID of the first line played when a reply is picked, not the ID of the Reply line itself. To recover the Reply line's ID from an IDToJump, call Dialogue->GetIndex(IDToJump) to get the line index, then Dialogue->GetParentIndex(that index); the parent index is the Reply line.

ULBRDialogueChoiceWidget methods

Methods flagged Overridable are BlueprintNativeEvents — override them in your widget Blueprint to change behaviour, and call the parent when you want to keep the default. The others are helpers you call but should not need to reimplement.

Method Overridable What it does Default behaviour
AddChoiceToView Yes Initializes and shows the widget. Adds the widget to the player screen, switches input to UI only, and shows the mouse cursor.
AddButtonChoiceToView Yes Called after each reply button is created, to attach it to the choice widget. Adds the reply button as a child of the choice widget's root panel.
ChoiceTimedOut Yes Called by the Dialogue Manager when a timed choice runs out. Must call ValidateChoice to confirm the selection. Validates the choice line itself, which resolves to GetChoiceDefaultReplyOrFirstID.
ChoiceSelected Yes Called by a reply button (via ButtonChoiceSelected) when the player picks a reply. Must call ValidateChoice. Use it to run gameplay effects and to distinguish an explicit pick from the timeout default. Validates the selected reply.
ChoiceValidated Yes The final validation step, called once a reply is confirmed. Calls RemoveWidgetChoice.
RemoveWidgetChoice Yes Tears the widget down. Switches input back to game only, hides the mouse cursor, and removes the widget from its parent.
DisplayChoice No Called by the Dialogue Manager when the choice widget is created. Sets Dialogue, DialogueInstance and LineIDChoice, then calls AddChoiceButtons.
AddChoiceButtons No Called by DisplayChoice. Iterates the choice's replies and adds a reply button for each available one (buttons whose Availability Condition is false are still added so they can be greyed out).
GetChoiceDefaultReplyOrFirstID No Returns the IDToJump of the first available Default Reply, or of the first available reply, or -1 if none is available. Walks the replies in order: returns the first available reply tagged Default Reply; otherwise the first available reply.
GetChoiceDefaultReplyID No Returns the IDToJump of the first available Default Reply, or -1. Walks the replies in order: returns the first available reply tagged Default Reply.

Validation is split into two steps

ChoiceValidated is the confirmation hook, and it delegates the actual UI teardown to the separate, overridable RemoveWidgetChoice. Override RemoveWidgetChoice if you only need to change how the widget is dismissed (input mode, cursor, animations), and ChoiceValidated if you need to run logic at the moment a reply is confirmed.

Empty choices do not soft-lock

A choice can end up with no selectable reply (for example, every Do Once reply already used on a second pass). Libretto detects this: the empty framing is normally skipped upstream, and if an empty choice widget is still reached, the dialogue is ended cleanly instead of looping. You do not need to handle this case in your widget.

ULBRDialogueChoiceButton properties

Property Purpose
Dialogue The Dialogue asset, used to look up the reply this button offers.
LineID The ID of the Reply line (its prompt text).
LineIDReply The reply's IDToJump — the first line played when this reply is selected.
ButtonChoiceSelected The event the choice widget listens to; it is bound to the widget's ChoiceSelected.

ULBRDialogueChoiceButton methods

Method Overridable What it does Default behaviour
ReplySelected No Called when the button is clicked. Broadcasts ButtonChoiceSelected with IDToJump = LineIDReply.
GetReplyText No Used to display the button's label. Returns the localized text of the corresponding Reply line.
IsReplyAvailable No Used to enable or disable the button. Returns the reply's availability.

Not blocking the character (Firewatch-style choices)

By default the choice widget takes full control: input goes UI only and the mouse cursor appears, which stops the player from moving. If you want a choice the player can pick while still walking around — the "walk-and-talk" style used in games like Firewatch — follow the pattern implemented in the shipped default widget.

In broad strokes:

  • Override AddChoiceToView so it does not switch to UI only input and does not block the character. Keep game input active and add the choice UI as an overlay.
  • Add input bindings to move focus between replies (and to confirm one) instead of relying on mouse clicks.

Flagging a non-blocking choice

A dedicated gameplay tag, Libretto.Flags.Choice.NonBlocking, marks a Choice line as non-blocking. Put it on the Choice line and map it to your walk-and-talk widget through Widget Choice Map Class. The C++ base widget does not act on this flag on its own — the non-blocking behaviour lives in your widget's overrides.