How-To: Tabs

Goal: a tab bar (settings screen, inventory categories) with design-time tabs, runtime-registered tabs, and per-tab content widgets.

Prerequisites
A screen to host the tabs and a UCrimsonButtonBase subclass to use as the tab button (see How-To: Buttons & Action Glyphs).

1. Make the tab button

Create a Blueprint from Crimson Tab Button Base (UCrimsonTabButtonBase). It already implements ICrimsonTabButtonInterface; override the Set Tab Label Info event to receive an FCrimsonTabDescriptor (TabId, TabText, IconBrush, TabTag, TabPayload, BadgeCount) and populate your label/icon. The event re-fires whenever the badge count changes, so a badge needs no separate binding - bind an optional Text_Badge block, or handle OnTabBadgeChanged. Use TabTag to answer "which thing is this tab for?" rather than encoding meaning into TabId and parsing it back out.

Reference implementation
Open the shipped W_CrimsonButtonTab (/CrimsonUI/UI/Foundation/Buttons/) - a working, text-only tab button. Its hierarchy is AnimBoundContentBorder (Common Border) -> TextOverlay (Overlay) -> ButtonTextBlock (Common Text), with Hover_Animate / Pressed_Animate driving the border. Note it does NOT override Set Tab Label Info: the C++ SetTabLabelInfo_Implementation calls SetButtonText, which fires the Update Button Text event the graph overrides. You only need to override Set Tab Label Info yourself when your button shows something the text route cannot carry - an icon from IconBrush, or a payload off TabTag / TabPayload. Badges need no graph either way: bind an optional Text_Badge block and the C++ fills it in and collapses it at zero.

2. Make the tab list and preregister tabs

Create a Blueprint from Crimson Tab List Widget Base (UCrimsonTabListWidgetBase, a UCommonTabListWidgetBase). Fill PreregisteredTabInfoArray in Class Defaults - one FCrimsonTabDescriptor per design-time tab: TabId (unique FName), TabText, IconBrush, TabButtonType (your WBP_TabButton), and TabContentType (the UCommonUserWidget page shown while the tab is active). Then, as with any CommonUI tab list, call Set Linked Switcher (pointing at a UCommonActivatableWidgetSwitcher in your screen) before tabs are created - content widgets are instantiated into it.

Reference implementation
Open the shipped W_CrimsonHorizontalTabList (/CrimsonUI/UI/Foundation/TabbedView/) - a working tab list whose Tab Button Type is W_CrimsonButtonTab. Duplicate it and fill PreregisteredTabInfoArray with your own entries (e.g. Graphics / Audio / Controls, each pairing your tab button with its page widget).
Verify
PIE: the tab bar shows one button per preregistered entry; selecting a tab switches the linked switcher to that tab's content widget.

3. Register tabs at runtime

Node chain: Make CrimsonTabDescriptor (Tab Id = Mods, Tab Text = Mods, Tab Button Type, Tab Content Type) -> Register Dynamic Tab on the tab list. The node returns false if registration failed.

Runtime helpers (all BlueprintCallable): SetTabHiddenState(TabId, bHidden) (registers/unregisters the tab, preserving its authored position when it returns), RemoveDynamicTab(TabId), SetTabBadgeCount(TabId, Count) / GetTabBadgeCount(TabId), IsTabVisible(TabId), GetVisibleTabCount(), GetTabContentForId(TabId), IsFirstTabActive() / IsLastTabActive() (for prev/next bumper logic), GetTabDescriptor(TabId, Out) and GetAllTabDescriptors() - both cover authored and dynamic tabs alike. Bind OnTabContentCreated to touch each content widget as it is instantiated, or OnCrimsonTabSelected to receive selection with the descriptor already attached.

Verify
The dynamic tab appears after registration; hiding it with Set Tab Hidden State removes the button but keeps its registration.

See also

  • How-To: Buttons & Action Glyphs - the tab button's base class.
  • How-To: Push & Pop Screens - hosting the tabbed screen on a layer.