Displaying UI
Before you can display any UI, you'll need to have:
- Initialized the Framework API;
- Authored one or more StarML views and set them up to be deployed; and
- Registered the asset path of your view(s).
Once all those steps are completed, it's time to bring that UI to life.
Menus
To create a menu from a StardewUI view, use the CreateMenu
methods:
Where Mods/authorName.ModName/Views/SomeView
is the concatenation of the view's asset prefix, e.g. Mods/authorName.ModName/Views
, with the StarML file name/path, e.g. SomeView
(excluding the .sml
extension).
Once created, the menu is shown the same way as a vanilla menu, e.g. by assigning it to Game1.activeClickableMenu
, or as a child menu of another menu, or to TitleMenu.subMenu
, and so on.
Danger
The CreateMenuFromMarkup
method is only intended for testing, or for very unusual scenarios where the UI needs to be constructed completely on the fly and the combination of data bindings and structural attributes such as *switch
and *if
aren't sufficient, which should be rare.
Programmatically-constructed StarML must be parsed every time the view is created and won't benefit from hot reload or from many of StardewUI's performance optimizations.
CreateMenuFromAsset
is always the recommended entry point and should be used whenever possible.
Several complete examples are provided in the test mod:
var context = new
{
HeaderText = "Example Menu Title",
ItemData = ItemRegistry.GetData("(O)117"),
};
Game1.activeClickableMenu = viewEngine.CreateMenuFromAsset(
"Mods/focustense.StardewUITest/Views/TestView",
context);
Menu Controllers
Menus are analogous to the "window" in a web or desktop app. While the StarML document always results in a View, underneath it there is a more complex mechanism responsible for backgrounds, input handling, pausing game state, ensuring only one menu can be active, and so on. This is the the Menu itself—the implementation of IActiveClickableMenu
which also acts as the bridge between Stardew Valley and StardewUI.
Most UIs do not need to change anything about how the ViewMenu
works; it is set up so that its defaults accommodate 80-90% of usage scenarios. For those scenarios that require more precise control over aspects of the menu that cannot be controlled by either the StarML markup or a reference to the IClickableMenu
, StardewUI provides an alternative set of APIs for working with IMenuController
.
The controller allows you to:
- Change how the menu is closed: prevent closing, respond to events before/after closing, or replace the default
Close
implementation entirely for menus that need to be integrated into other frameworks; - Display the clickable Close button or "red X" found at the top-right of many Stardew Valley menus, and optionally customize its image or location;
- Change absolute position, screen dimming, gutters, and other aspects affecting the "exterior" of the menu.
The full set of controller customizations are documented in the IMenuController
reference.
Any standard menu is easy to replace with a menu controller by simply changing the API call from CreateMenuFromAsset
to CreateMenuControllerFromAsset
and referring to the controller's Menu
when ready. The previous example would be changed to:
var context = new
{
HeaderText = "Example Menu Title",
ItemData = ItemRegistry.GetData("(O)117"),
};
var controller = viewEngine.CreateMenuControllerFromAsset(
"Mods/focustense.StardewUITest/Views/TestView",
context);
controller.DimmingAmount = 0.5f; // Example customization
Game1.activeClickableMenu = controller.Menu;
HUD
The game's HUD – Heads Up Display – refers to the persistent UI that is drawn over top of the game world, such as the date/time widget, health/energy bars, and so on.
Using the API
The recommended way to display HUD-style UI is to create a Drawable from StarML. The API for this is very similar to the menu API:
var context = new SomeViewModel();
string markup =
@"<lane layout=""500px content"" orientation=""vertical"">
<label font=""dialogue"" text=""Things"" />
<label *repeat={Things} text={DisplayName} />
</lane>";
IViewDrawable drawable = viewEngine.CreateDrawableFromMarkup(markup);
drawable.Context = new SomeViewModel(...);
// Optional, only needed if the view is not fixed-size
drawable.MaxSize = new(maxWidth, maxHeight);
Danger
Avoid using the CreateDrawableFromMarkup
variant in release builds for the same reasons described in menus.
IViewDrawable
implements IDisposable
. If you are done with an instance and never going to draw it again, make sure to call its Dispose()
method. StardewUI will attempt to detect unreachable drawables and stop updating them, but this is less reliable and will take longer than a drawable that was disposed correctly, potentially degrading game performance over time.
The test mod also has a simple HUD example:
private void ToggleHud()
{
if (hudWidget is not null)
{
hudWidget.Dispose();
hudWidget = null;
}
else
{
hudWidget = viewEngine.CreateDrawableFromAsset(
"Mods/focustense.StardewUITest/Views/Example-Hud");
hudWidget.Context = new { Title = "I'm a HUD!" };
}
}
Using Views Directly
Advanced usage warning
This section is provided for disambiguation, since there are more differences between the Core Library and Framework usage for HUD-style UI than there are for menus. If you aren't already using the Core Library to handle specialized scenarios or extend the framework, ignore this section and use the drawable API instead.
When working with the core library, the IViewDrawable
abstraction is not involved; instead, IView
can be used directly with a SpriteBatch
.
A Fishing Sea uses this older method for its HUD:
Example
internal sealed class ModEntry : Mod
{
private IView? seedFishPreview;
public override void Entry(IModHelper helper)
{
seedFishPreview = new SeedFishInfoView();
helper.Events.Display.RenderedHud += Display_RenderedHud;
}
private void Display_RenderedHud(object? sender, RenderedHudEventArgs e)
{
seedFishPreview.Measure(new(500, 500));
var overlayBatch = new PropagatedSpriteBatch(
spriteBatch,
Transform.FromTranslation(new Vector2(0, 100))
);
SeedFishPreview.Draw(overlayBatch);
}
}
The above is substantially simplified from the actual mod code for brevity. There are only 3 steps to the process:
- Create an
IView
orIDrawable
instance and save it (do not recreate the view on every frame). - Each frame, call its
Measure
method, providing the maximum width/height it should be allowed to use; the actual width/height may be smaller. - After measuring—usually immediately afterward—Call the view's
Draw
method with the desired position.
Important
Unless the view is completely static (that is, neither its content nor its position ever changes), you must call Measure
on every frame. This method is responsible for layout, and changes to a view's properties, such as the text of a label, will not be reflected until the next layout. Do not be concerned about frame performance; StardewUI is very careful to limit work to the parts that changed.
Other Scenarios
These are considered advanced usage and not recommended for StardewUI beginners; to learn more, head to the respective page: