Modding:GUIs: Difference between revisions

From Vintage Story Wiki
m
VeryGoodDog moved page GUIs to Modding:GUIs
No edit summary
m (VeryGoodDog moved page GUIs to Modding:GUIs)
(19 intermediate revisions by one other user not shown)
Line 4: Line 4:


== Starting out ==
== Starting out ==
You should encapsulate every GUI Dialog into its own class, albeit a dialog may consist of multiple windows - the vanilla character dialog for example has two - one for the players wearables and one for the player stats. There's a couple of helper classes to make your live a bit easier:
You should encapsulate every GUI Dialog into its own class, albeit a dialog may consist of multiple windows - the vanilla character dialog for example has two - one for the players wearables and one for the player stats. There's a couple of base classes to make your live a bit easier.


* To create a GUI that's bound to a block entity, inherit from <code>GuiDialogBlockEntity</code>
===Block Entity GUI===
* To create a HUD element which is not interactable, inherit from <code>HudElement</code>
 
* For any other uses, inerhit from the general purpose class <code>GuiDialog</code>
To create a GUI that's bound to a block entity, inherit from <code>GuiDialogBlockEntity</code>. In your block entity code you can then create and open that gui e.g. upon player interaction (example: [https://github.com/anegostudios/vssurvivalmod/blob/master/BlockEntity/BEQuern.cs#L544 Quern Block Entity], [https://github.com/anegostudios/vssurvivalmod/blob/master/Gui/GuiDialogBlockEntityQuern.cs Quern Block Dialog])
 
===HUD===
 
To create a GUI element which is not interactable, inherit from <code>HudElement</code>.
 
===General Purpose GUI===
 
For any other uses, inerhit from the general purpose class <code>GuiDialog</code>, from which HudElement and GuiDialogBlockEntity also inherit from. You can override <code>ToggleKeyCombinationCode</code> to something like "yourAweseomeHotkeyCode" and use <code>capi.Input.RegisterHotKey</code> + <code>capi.Input.SetHotKeyHandler</code> to have your own keyboard key mapped to opening/closing your GUI (example: [https://github.com/anegostudios/vsessentialsmod/blob/master/Systems/WorldMap/WorldMapManager.cs#L105 World Map])


== GUI Basics ==
== GUI Basics ==
Line 25: Line 33:
* <code>ChildBounds</code>: The child boundaries this element contains
* <code>ChildBounds</code>: The child boundaries this element contains
* <code>Alignment</code>: The alignment of the element. If set to <code>None</code> the FixedX/Y Position is used. For any other value the FixedX/Y values are ignored. For example when you used <code>RightTop</code> the element will always be at the right top most corner of its parent bounds. If you use <code>RightFixed</code> the element will be right aligned, but its Y-Position is determined by <code>FixedY</code>
* <code>Alignment</code>: The alignment of the element. If set to <code>None</code> the FixedX/Y Position is used. For any other value the FixedX/Y values are ignored. For example when you used <code>RightTop</code> the element will always be at the right top most corner of its parent bounds. If you use <code>RightFixed</code> the element will be right aligned, but its Y-Position is determined by <code>FixedY</code>
* <code>HorizontalSizing</code>/<code>VerticalSizing</code>: The sizing method to be used, can be either <code>Fixed</code> (default), <code>Percentual</code> or <code>FitToChildren</code>


===GuiComposer===
===GuiComposer===
Line 31: Line 40:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
// Auto-sized dialog at the center of the screen
private void SetupDialog()
ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);
{
 
    // Auto-sized dialog at the center of the screen
// Just a simple 300x300 pixel box
    ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);
ElementBounds textBounds = ElementBounds.Fixed(0,0,300,300)


SingleComposer = capi.Gui.CreateCompo("myAwesomeDialog", dialogBounds)  
    // Just a simple 300x300 pixel box
  .AddStaticText("This is a piece of text at the center of your screen - Enjoy!", CairoFont.WhiteDetailText(), dialogBounds)
    ElementBounds textBounds = ElementBounds.Fixed(0, 0, 300, 300);
  .Compose()
       
;
    SingleComposer = capi.Gui.CreateCompo("myAwesomeDialog", dialogBounds)
        .AddStaticText("This is a piece of text at the center of your screen - Enjoy!", CairoFont.WhiteDetailText(), textBounds)
        .Compose()
    ;
}
</syntaxhighlight>
</syntaxhighlight>


Some explanations:
Some explanations:
* <code>ElementStdBounds</code>: Contains a bunch of often used element bounds configurations. See also [[https://github.com/anegostudios/vsapi/blob/master/Client/UI/ElementStdBounds.cs|ElementStdBounds]] on github.
* <code>ElementStdBounds</code>: Contains a bunch of often used element bounds configurations. See also [https://github.com/anegostudios/vsapi/blob/master/Client/UI/ElementStdBounds.cs ElementStdBounds] on Github.
* <code>ElementBounds.Fixed(0, 0, 300, 300)</code>: Create a new bounds instance with fixedX/Y at 0/0 and a fixed widt/height of 300/300 pixels.
* <code>ElementBounds.Fixed(0, 0, 300, 300)</code>: Create a new bounds instance with fixedX/Y at 0/0 and a fixed widt/height of 300/300 pixels.
* <code>CairoFont.WhiteDetailText()</code>: Create a new font configuration based on a often used size and color, in this case white with font size 14
* <code>CairoFont.WhiteDetailText()</code>: Create a new font configuration based on a often used size and color, in this case white with font size 14
* <code>SingleComposer</code>: This property is defined in the <code>GuiDialog</code> class. Its a getter/setter to the <code>Composers</code> dictionary. It contains all the "windows" of this dialog which are then rendered / handled by this GuiDialog instance
* <code>SingleComposer</code>: This property is defined in the <code>GuiDialog</code> class. Its a getter/setter to the <code>Composers</code> dictionary. It contains all the "windows" of this dialog which are then rendered / handled by this GuiDialog instance
==Fancying it up==
This is of course the absolute minimum example that will only show some text. Let's add a title bar and a dialog background:
<syntaxhighlight lang="c#">
private void SetupDialog()
{
    // Auto-sized dialog at the center of the screen
    ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);
    // Just a simple 300x100 pixel box with 40 pixels top spacing for the title bar
    ElementBounds textBounds = ElementBounds.Fixed(0, 40, 300, 100);
    // Background boundaries. Again, just make it fit it's child elements, then add the text as a child element
    ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding);
    bgBounds.BothSizing = ElementSizing.FitToChildren;
    bgBounds.WithChildren(textBounds);
    SingleComposer = capi.Gui.CreateCompo("myAwesomeDialog", dialogBounds)
        .AddShadedDialogBG(bgBounds)
        .AddDialogTitleBar("Heck yeah!", OnTitleBarCloseClicked)
        .AddStaticText("This is a piece of text at the center of your screen - Enjoy!", CairoFont.WhiteDetailText(), textBounds)
        .Compose()
    ;
}
private void OnTitleBarCloseClicked()
{
    TryClose();
}
</syntaxhighlight>
This covers some of the most basic parts. There is a grand amount of various pre-built UI elements that each come with their own argument list that are in dire need of Documentation. Here's an overview of some of the more commonly used ones
====Dialog / Graphics====
* <code>.AddShadedDialogBG</code>: Draws a pretty background and dialog border
* <code>.AddDialogTitleBar</code>: Draws a title bar with a close button and a button to move around the dialog
* <code>.AddInset</code>: Adds a darkened section with a inset border around it
====Text====
* <code>.AddStaticText</code>: Add a static snippet of text
* <code>.AddDynamicText</code>: Add a static snippet of text that can be set to other texts without the need to redraw the whole dialog
* <code>.AddRichtext</code>: Same as <code>.AddDynamicText</code> but allows use of [[VTML]] - a minimalist version of HTML code
* <code>.AddHoverText</code>: When the mouse cursor moves over the element boundaries, will show supplied text as a tooltip
====UI Control/Input====
* <code>.AddButton</code>: Adds a clickable button
* <code>.AddDropDown</code>: Adds a drop down element
* <code>.AddHorizontalTabs</code>: Adds horizontally aligned tabs, like the ingame chat window has
* <code>.AddVerticalScrollbar</code>: Adds a vertical scrollbar
* <code>.AddTextInput</code>: Adds an single line editable text field
* <code>.AddNumberInput</code>: Adds an editable text field built for entering numbers
* <code>.AddTextArea</code>: Adds multiple line editable text field
====Tables/Grids/Inventory====
* <code>.AddItemSlotGrid</code>: Create a grid displaying the contents of supplied inventory (example: [https://github.com/anegostudios/vsessentialsmod/blob/master/Gui/GuiDialogCarcassContents.cs GuiDialogCarcassContents])
* <code>.AddSkillItemGrid</code>: Create a grid of custom elements (example: [https://github.com/anegostudios/vssurvivalmod/blob/master/Gui/GuiDialogBlockEntityRecipeSelector.cs GuiDialogBlockEntityRecipeSelector])
====Other====
* <code>.AddIf</code>/<code>.EndIf</code>: Can be used to conditionally add certain elements (but you can also just split up your creation code for more fine grained control)
* <code>.BeginClip</code>/<code>.EndClip</code>: Used in combination with a scrollbar to cut away oversized content, such as in the creative inventory
* <code>.AddStaticCustomDraw</code>: Lets you define your own drawing code to be added to the GUI
== Examples ==
A simple standard dialog, triggered by the keyboard key 'U'
<syntaxhighlight lang="c#">
public class GuiDialogAnnoyingText : GuiDialog
{
    public override string ToggleKeyCombinationCode => "annoyingtextgui";
    public GuiDialogAnnoyingText(ICoreClientAPI capi) : base(capi)
    {
        SetupDialog();
    }
    private void SetupDialog()
    {
        // Auto-sized dialog at the center of the screen
        ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);
        // Just a simple 300x300 pixel box
        ElementBounds textBounds = ElementBounds.Fixed(0, 40, 300, 100);
        // Background boundaries. Again, just make it fit it's child elements, then add the text as a child element
        ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding);
        bgBounds.BothSizing = ElementSizing.FitToChildren;
        bgBounds.WithChildren(textBounds);
        // Lastly, create the dialog
        SingleComposer = capi.Gui.CreateCompo("myAwesomeDialog", dialogBounds)
            .AddShadedDialogBG(bgBounds)
            .AddDialogTitleBar("Heck yeah!", OnTitleBarCloseClicked)
            .AddStaticText("This is a piece of text at the center of your screen - Enjoy!", CairoFont.WhiteDetailText(), textBounds)
            .Compose()
        ;
    }
    private void OnTitleBarCloseClicked()
    {
        TryClose();
    }
}
public class AnnoyingTextSystem : ModSystem
{
    ICoreClientAPI capi;
    GuiDialog dialog;
    public override bool ShouldLoad(EnumAppSide forSide)
    {
        return forSide == EnumAppSide.Client;
    }
       
    public override void StartClientSide(ICoreClientAPI api)
    {
        base.StartClientSide(api);
        dialog = new GuiDialogAnnoyingText(api);
        capi = api;
        capi.Input.RegisterHotKey("annoyingtextgui", "Annoys you with annoyingly centered text", GlKeys.U, HotkeyType.GUIOrOtherControls);
        capi.Input.SetHotKeyHandler("annoyingtextgui", ToggleGui);
    }
    private bool ToggleGui(KeyCombination comb)
    {
        if (dialog.IsOpened()) dialog.TryClose();
        else dialog.TryOpen();
        return true;
    }
}
</syntaxhighlight>


=== TBD ===
=== TBD ===
More to come here in the future.
More to come here in the future.
Confirmedusers, Bureaucrats, editor, Administrators
1,522

edits