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)
(20 intermediate revisions by one other user not shown)
Line 3: Line 3:
In Vintage Story you currently need to formulate your GUIs in code, which requires acute spacial thinking. Hopefully someday we'll have a point&click GUI designer that takes off a lot of work. Until then, here are some of the essentials.
In Vintage Story you currently need to formulate your GUIs in code, which requires acute spacial thinking. Hopefully someday we'll have a point&click GUI designer that takes off a lot of work. Until then, here are some of the essentials.


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:
== 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 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 24: 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 30: 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