Navigation, Scrolling & Prompts

Page stacks with transitions, scroll controllers, virtualized lists, pan views, and input prompt glyphs.

This page covers the larger interaction systems that build on top of the core widget model: navigating between full pages, scrolling through content, and displaying context-sensitive input prompts.


HNavStack renders a stack of pages. Declare a GlobalKey<T> as a state field to keep a reference to the mounted element across rebuilds, then call navigation methods on it from anywhere in the state.

using HELIX.Widgets;
using HELIX.Widgets.Navigation;
using HELIX.Widgets.Universal;

public class MenuRoot : StatefulWidget<MenuRoot> {
  public override State<MenuRoot> CreateState() => new MenuRootState();
}

public class MenuRootState : State<MenuRoot> {
  // The key lives on the state so it survives rebuilds of the widget.
  private readonly GlobalKey<NavStackElement> _nav = new();

  public override Widget Build(BuildContext context) {
    return new HColumn {
      new HNavStack(key: _nav).Fill(),
      new HRow(gap: 8) {
        new HButton(onClick: ShowSettings) { new HText("Settings") },
        new HButton(onClick: ShowProfile)  { new HText("Profile")  }
      }
    };
  }

  private void ShowSettings() {
    _nav.Element.PushReplacement(new WidgetNavPage {
      Buildable = new SettingsPage().ToBuildable()
    });
  }

  private void ShowProfile() {
    _nav.Element.PushReplacement(new WidgetNavPage {
      Buildable = new ProfilePage().ToBuildable()
    });
  }
}

Page transitions

Transitions are represented by PageTransition implementations. The built-in options are:

TransitionBehavior
InstantPageTransitionSwaps pages with no animation
FadePageTransitionCross-fades between the old and new page
SlidePageTransitionSlides the new page in from an edge

Pass a transition to PushReplacement or Push to customize per-navigation:

_nav.Element.PushReplacement(
  new WidgetNavPage { Buildable = new SettingsPage().ToBuildable() },
  transition: new SlidePageTransition()
);

Implement PageTransition to create fully custom animated transitions.


Scaffolds

HScaffold is a higher-level page shell. Use it when a page needs to consistently manage named layout areas like body content, navigation surfaces, top bars, footers, or any persistent chrome that should not be recreated on each navigation event.


Scroll views

HScrollView creates a scrollable container for child widgets. It is the right choice for forms, settings panels, and content where all children can be mounted at once without a significant performance cost.

new HScrollView(Axis.Vertical) {
  new HColumn(gap: 8) {
    rows.Select(row => new HText(row.Title)).Spread()
  }
}.Fill();

Linking a scrollbar

Use ScrollController when another widget needs to observe or control the scroll position. Register it with AddDisposable<S> to keep its lifetime tied to the state:

private ScrollController _scroll;

public override void InitState() {
  _scroll = AddDisposable(new ScrollController());
}

public override Widget Build(BuildContext context) {
  return new HRow {
    new HScrollView(Axis.Vertical, controller: _scroll) {
      BuildContent()
    }.Expand(),
    new HSlider(_scroll)   // automatically configured as a scrollbar
  };
}

Passing _scroll to HSlider tells it to act as a scrollbar. It reads the scroll range and thumb size from the controller and forwards drag interactions back to it.


Virtualized lists

For lists of any significant length, use HListView instead of HScrollView + HColumn. HListView asks for row widgets only for the items currently visible in the scroll viewport, keeping the mounted VisualElement count small regardless of the total item count.

new HListView(
  (context, index) => new HBox {
    new HText($"Quest {index + 1:000}"),
    new HText(quests[index].Title).Body(context)
  }.Padding(12),
  count: quests.Count,
  fixedItemHeight: 56f
);

Fixed vs. dynamic height

ModeWhen to use
fixedItemHeightAll rows are the same height. Enables O(1) scroll position math.
Dynamic heightRows have variable heights. Each row is measured before layout, so only use this when necessary.

Setting fixedItemHeight whenever possible gives the best scroll performance.


Pan views

HPanView supports two-dimensional panning of a larger child surface. Unlike HScrollView, which constrains to one axis, HPanView allows free dragging in both directions. It is well-suited for:

  • Map and minimap panels
  • Node-based editors
  • Canvas-style drawing tools
  • Any content that overflows in two dimensions

Input prompts

HPrompt displays glyphs for input bindings, selecting the correct image based on the active input device and platform.

Prompt rendering is provider-based. A provider supplies the SVG or texture resources for a specific device family. Multiple providers can be registered at once so HELIX can pick the right glyph at runtime when the user switches between keyboard, mouse, or controller.

Built-in provider sets

Provider setDevices covered
Kenny Keyboard/MouseStandard keyboard and mouse prompts
Kenny XboxXbox Series controller prompts
Kenny PlayStationPlayStation controller prompts
Kenny Nintendo Switch 2Nintendo Switch 2 Joy-Con and Pro Controller prompts
Kenny Steam DeckSteam Deck button prompts
Kenny Steam ControllerSteam Controller prompts

Configuration types

TypeRole
IPromptLayerProviderInterface for a prompt image source
SvgResourcePromptLayerProviderLoads SVG layers from Unity Resources
HelixInputControllerTracks the active input device and variant
InputConfigurationRegisters the active set of prompt providers
GamepadVariantIdentifies a specific controller family
InputDeviceTypeKeyboard/mouse or gamepad

Common use cases

  • Action bars: show which button triggers an action next to its label.
  • Tutorial hints: display contextual control reminders during onboarding.
  • Rebinding screens: render the currently bound key or button for each action.
  • Context-sensitive help: update displayed glyphs when the user switches input device.

On this page