Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Advanced search  

News:

Starsector 0.97a is out! (02/02/24); New blog post: Planet Search Overhaul (07/13/24)

Author Topic: Custom UI Guide : Necessary Basics  (Read 3574 times)

Kaysaar

  • Admiral
  • *****
  • Posts: 514
    • View Profile
Custom UI Guide : Necessary Basics
« on: November 28, 2023, 09:38:13 AM »

When i was starting my journey with starsector modding i saw, that there were no guides about making UI. Same goes with knowledge, which was scattered. This is my attempt of trying to collect knowledge i gained so when someone starts their journey with UI



The knowledge here is based only on my experience, if you think I told sth wrong, or there is a better way, than what I presented, feel free to contribute. This is meant for all.
This one's for UI that uses CustomVisualDialogDelegate, forum link here
This guide is to show the basic of how to do your full custom UI from scratch telling process one by one

Designing:
Spoiler
Before you begin coding UI you first need to design it. As obvious as it might sound this is most important step, you plan your UI to be scalable with resolution, to avoid issues
Basically you test on two resolutions which i find most notorious

1280x960
1366x768

[close]
Basics:
Spoiler
First what we need is to describe hierarchy of UI:



Main panel(CustomPanelAPI class) - > we can call it as foundations, all is bound to the main panel, we will use it to build our UI layer by layer. Of course you can directly create Tooltip (this will be discussed later what is tooltip) but in a much more complex UI this will only create a mess, that will be very hard to clean up later and for you as codder even harder to make adjustments in future.

To create such custom panel of our precious checkbox we use sth like this :

CustomPanelAPi customPanel = mainPanel.createCustomPanel(float width, float height, CustomPanelPlugin plugin);

CustomPanels are mainly used I say as “containers” of true UI.
TooltipMakerAPI is basically a class that is all about creating buttons, text and all that fancy stuff we always want in the UI.

TooltipMakerAPI customTooltip = customPanel.createUIElement(float width, float height, boolean withScroller)

Tooltips and custom panels are a little tricky as you can insert panels into tooltips and tooltips into panels.

Let’s start by going for the small component. First you insert panels into the tooltip (let’s call it tooltipA), so that you can have a more complex UI component which can use the scrollbar fairly easily. Then you can insert that tooltipA into the “container” panels so that not only can you place the container wherever you want, you can move it relatively easily.

Side note: If you were to place panel A directly inside panel B, the scroll bar no longer effects on panel A
   
In summary, tooltip is for creating whole UI stuff like buttons while panel is more of container where tooltip is placed , tooltip must be placed in container to work , but it can also hold other panels as UI components.

Important:
Panels you can only create from CustomPanelAPI, not the tooltip. So panel 2 and panel 3 must be initialized from panel 1 createCustomPanel method.
Tooltips MUST be initialized from PANELS you plan to place them in, so TOOLTIP 3  must be initialized from PANEL 3.

By making such a design you are able to create a much more advanced UI with ease.

Example of it you have right here:
[close]
Using CustomUIPanelPlugin:
Spoiler
When you initialize panel, the third argument is CustomUIPanelPlugin. Such plugins can make life easier.
For example rendering borders with UI, or making your own plugins to display images.
I personally use this plugin for both things, but I know they can be utilized much more efficiently, like for example creating your own horizontal scrollbar.

Example of UIPlugin displaying borders of panel;

Code:
Spoiler
Code
package data.kaysaar.aotd.vok.campaign.econ.globalproduction.ui.components;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.campaign.CustomUIPanelPlugin;
import com.fs.starfarer.api.graphics.SpriteAPI;
import com.fs.starfarer.api.input.InputEventAPI;
import com.fs.starfarer.api.ui.CustomPanelAPI;
import com.fs.starfarer.api.ui.PositionAPI;
import com.fs.starfarer.api.util.Misc;


import java.awt.*;
import java.util.ArrayList;
import java.util.List;


public class UILinesRenderer implements CustomUIPanelPlugin {
   ArrayList<CustomPanelAPI> panels = new ArrayList<>();
   SpriteAPI box = Global.getSettings().getSprite("rendering","GlitchSquare");
   Color boxColor = Misc.getDarkPlayerColor();
   public void setPanels(ArrayList<CustomPanelAPI> panels) {
       this.panels = panels;
   }
   public void setPanel(CustomPanelAPI panel) {
       panels.add(panel);
   }
   float widthPadding = 10f;
   public ArrayList<CustomPanelAPI> getPanels() {
       return panels;
   }


   @Override
   public void positionChanged(PositionAPI position) {


   }
   public UILinesRenderer(float widthPadding){
       this.widthPadding = widthPadding;
   }
   public void setBoxColor(Color boxColor) {
       this.boxColor = boxColor;
   }


   @Override
   public void renderBelow(float alphaMult) {






   }


   @Override
   public void render(float alphaMult) {
       for (CustomPanelAPI panel : panels) {
           if (panel != null) {
               box.setSize(panel.getPosition().getWidth()+widthPadding,1);
               box.setColor(boxColor);
               box.render(panel.getPosition().getX(),panel.getPosition().getY());
               box.render(panel.getPosition().getX(),panel.getPosition().getY()+panel.getPosition().getHeight());
               box.setSize(1,panel.getPosition().getHeight());
               box.render(panel.getPosition().getX(),panel.getPosition().getY());
               box.render(panel.getPosition().getX()+panel.getPosition().getWidth()+widthPadding,panel.getPosition().getY());
           }
       }
   }


   @Override
   public void advance(float amount) {


   }


   @Override
   public void processInput(List<InputEventAPI> events) {


   }


   @Override
   public void buttonPressed(Object buttonId) {


   }
}
[close]
Image:
Spoiler
[close]

Also as I mentioned before you can use it to much more advanced stuff, like for example rendering modular ships with weapon slots.

Code:
Spoiler
Code
package data.kaysaar.aotd.vok.misc.shipinfo;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.campaign.CustomUIPanelPlugin;
import com.fs.starfarer.api.graphics.SpriteAPI;
import com.fs.starfarer.api.input.InputEventAPI;
import com.fs.starfarer.api.loading.WeaponSpecAPI;
import com.fs.starfarer.api.ui.CustomPanelAPI;
import com.fs.starfarer.api.ui.PositionAPI;
import com.fs.starfarer.ui.P;
import com.fs.state.AppDriver;
import org.jetbrains.annotations.NotNull;


import java.awt.*;
import java.util.*;
import java.util.List;


public class ShipRenderer implements CustomUIPanelPlugin {
   LinkedHashMap<CustomPanelAPI, ShipRenderInfo.Module> partsOfShip = new LinkedHashMap<>();
   transient SpriteAPI spriteToRender = Global.getSettings().getSprite("rendering", "GlitchSquare");
   CustomPanelAPI absoultePanel = null;


   public void setAbsoultePanel(CustomPanelAPI absoultePanel) {
       this.absoultePanel = absoultePanel;
   }


   public void setPartsOfShip(HashMap<CustomPanelAPI, ShipRenderInfo.Module> partsOfShip, CustomPanelAPI test) {
       this.partsOfShip = sortPartsOfShipByRenderingOrder(partsOfShip);
       this.test = test;


   }


   public boolean canRender() {
       if (absoultePanel == null) return true;
       for (CustomPanelAPI panelAPI : partsOfShip.keySet()) {
           if (panelAPI.getPosition().getY() < absoultePanel.getPosition().getY() - 60) {
               return false;
           }
           if (panelAPI.getPosition().getY() > absoultePanel.getPosition().getY() + absoultePanel.getPosition().getHeight() + 60) {
               return false;
           }
       }
       return true;
   }


   private LinkedHashMap<CustomPanelAPI, ShipRenderInfo.Module> sortPartsOfShipByRenderingOrder(HashMap<CustomPanelAPI, ShipRenderInfo.Module> parts) {
       // Convert the entries of the HashMap to a list
       List<Map.Entry<CustomPanelAPI, ShipRenderInfo.Module>> entries = new ArrayList<>(parts.entrySet());


       // Sort the list based on the renderingOrder
       Collections.sort(entries, new Comparator<Map.Entry<CustomPanelAPI, ShipRenderInfo.Module>>() {
           @Override
           public int compare(Map.Entry<CustomPanelAPI, ShipRenderInfo.Module> e1, Map.Entry<CustomPanelAPI, ShipRenderInfo.Module> e2) {
               return Integer.compare(e1.getValue().renderingOrder, e2.getValue().renderingOrder);
           }
       });


       // Create a LinkedHashMap to preserve the sorted order
       LinkedHashMap<CustomPanelAPI, ShipRenderInfo.Module> sortedMap = new LinkedHashMap<>();
       for (Map.Entry<CustomPanelAPI, ShipRenderInfo.Module> entry : entries) {
           sortedMap.put(entry.getKey(), entry.getValue());
       }


       return sortedMap;
   }


   CustomPanelAPI test;
   public float scale = 1f;


   public void setScale(float scale) {
       this.scale = scale;
   }


   @Override
   public void positionChanged(PositionAPI position) {
       return;
   }


   @Override
   public void renderBelow(float alphaMult) {
       return;
   }


   @Override
   public void render(float alphaMult) {
//        if(test!=null){
//            spriteToRender.setColor(Color.ORANGE);
//            spriteToRender.setSize(test.getPosition().getWidth(),test.getPosition().getHeight());
//            spriteToRender.renderAtCenter(test.getPosition().getCenterX(),test.getPosition().getCenterY());
//        }
       if (!canRender()) return;
       for (Map.Entry<CustomPanelAPI, ShipRenderInfo.Module> entry : partsOfShip.entrySet()) {
           SpriteAPI sprite = Global.getSettings().getSprite(Global.getSettings().getHullSpec(entry.getValue().slotOnOriginal.id).getSpriteName());
           sprite.setAngle(entry.getValue().slotOnOriginal.angle);
           sprite.setSize((float) entry.getValue().width * scale, (float) entry.getValue().height * scale);
           sprite.renderAtCenter(entry.getKey().getPosition().getCenterX(), entry.getKey().getPosition().getCenterY());
           for (ShipRenderInfo.Slot builtInSlot : entry.getValue().built_in_slots) {
               WeaponSpecAPI weapon = Global.getSettings().getWeaponSpec(builtInSlot.id);
               String base = weapon.getTurretSpriteName();
               SpriteAPI baseSprite = Global.getSettings().getSprite(base);
               setSizeOfSpriteToScale(baseSprite);
               baseSprite.setAngle(builtInSlot.angle);
               float x = entry.getKey().getPosition().getX() + entry.getValue().center.x * scale;
               float y = entry.getKey().getPosition().getY() + entry.getValue().center.y * scale;
               renderSlotMoved(baseSprite, x + builtInSlot.locationOnShip.x * scale, y + builtInSlot.locationOnShip.y * scale);


           }
       }




   }




   private static void renderSlotMoved(SpriteAPI underSprite, float x, float y) {
       underSprite.renderAtCenter(x, y);
   }


   private void setSizeOfSpriteToScale(@NotNull SpriteAPI baseSprite) {
       baseSprite.setSize((float) baseSprite.getWidth() * scale, (float) baseSprite.getHeight() * scale);
   }


   @Override
   public void advance(float amount) {
       return;
   }


   @Override
   public void processInput(List<InputEventAPI> events) {
       return;
   }


   @Override
   public void buttonPressed(Object buttonId) {
       return;
   }
}
[close]
Image:
Spoiler
[close]
[close]
Tooltip method examples:
Spoiler
Button
Spoiler
Okay so first to create such a button we will lay out layers. First custom panel and Tooltip. Because I want this button to basically cover the entire panel I’ll write on paint


To insert button, we have 2 options

addButton
Code
tooltip.addButton("Special Project", null, base, bg, Alignment.MID, CutStyle.TOP, 150, 20, 0f);
Here with such button you have more freedom, not with coloring, but with how button outline looks like
Result
Spoiler
[close]

addAreaCheckbox
Code
tooltip.addAreaCheckbox("Name", SortingState.NON_INITIALIZED, base, bg, bright, UIData.WIDTH_OF_NAMES_ORDER, 20, 0f);

Result
Spoiler
[close]

As you might see, I have passed in this button in the second argument of my own class SortingState. This is where you can  define custom data contained in the button, which can be used later to trigger events related to this button.

Handling Buttons (advance method)
Spoiler
Code
for (ButtonAPI button : sortingButtons) {
   if (button.isChecked()) {
       if (button.getCustomData() instanceof String) {
           button.setChecked(false);
           SortingState state = mapOfButtonStates.get(button.getCustomData());
           if(state == SortingState.NON_INITIALIZED){
               state = SortingState.ASCENDING;
           }
           else if(state == SortingState.ASCENDING){
               state = SortingState.DESCENDING;
           }
           else if(state == SortingState.DESCENDING){
               state = SortingState.NON_INITIALIZED;
           }
           for (String s : mapOfButtonStates.keySet()) {
               mapOfButtonStates.put(s,SortingState.NON_INITIALIZED);
           }
           mapOfButtonStates.put((String) button.getCustomData(),state);
           currPage = 0;
           reset = true;
       }
       break;
   }
}


if (reset) {
   reset();
}

Here is an example of your own button handler.
Note here: If you want to update the UI, you need to remove it and re-create again!
This is why chopping the UI into smaller pieces comes  into play, as you just need to remove that one panel, where you had that part of the UI, which needs updating.

[close]
[close]
Images
Spoiler
With images same we can have two routes, of how we can achieve that.Either as
A: Using CustomUIPanelPlugin (like i showcased in Using CustomUIPanelPlugin part)
B:Using dedicated method in tooltip : addImage
Code
CustomPanelAPI mainPanel = this.absoultePanel;
UILinesRenderer renderer  = new UILinesRenderer(0f);// here we initalize our plugin much faster than panel
CustomPanelAPI testingPanel = Global.getSettings().createCustom(195,90,renderer);//width and size of industry image
TooltipMakerAPI tooltip = testingPanel.createUIElement(195,90,false);
String pathToImage = Global.getSettings().getIndustrySpec(Industries.HEAVYINDUSTRY).getImageName();
tooltip.addImage(pathToImage,0f);
testingPanel.addUIElement(tooltip).inTL(0,0);
renderer.setPanel(testingPanel);
mainPanel.addComponent(testingPanel).inTL(0,0);
With addImage you can also specify width and height of image. If you only specify width, the method will adjust height based on the ratio of image.


Because addimage does not return any variable, if you wanna move an image , for example repositioning to the middle you should use the tooltip.getPrev() method!

Example here
Code
WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(id);
                String base = spec.getTurretSpriteName();
                String under = spec.getTurretUnderSpriteName();
                tooltip.addImage(under,0);
                tooltip.getPrev().getPosition().inMid();
                tooltip.addImage(base,0);
                tooltip.getPrev().getPosition().inMid();
[close]
Headers:
Spoiler
To make your UI more readable, instead of a big pile of text, you can divide it with headers. Like for example Industry tooltip does !
Code example:
Code
tooltip.addSectionHeading("Produced resources", Alignment.MID, 0f);
tooltip.addPara("Construction speed bonus: %s",5f,Color.ORANGE,"50%");
tooltip.addPara("Orbital Skunk-work bonus %s",5f, Misc.getPositiveHighlightColor(),"Each built ship have 1 built-in s-mod");
tooltip.addPara("Hypershunt bonus %s",5f, Misc.getPositiveHighlightColor(),"Decrease cost of special project stages by 50%");

Result
Spoiler
[close]
[close]
Text
Spoiler
If you wanna add text you can use addPara. As you can also see  there are 5 different methods having addPara;
Code
LabelAPI addPara(String str, float pad);
LabelAPI addPara(String str, Color color, float pad);
LabelAPI addPara(String format, float pad, Color hl, String... highlights);
LabelAPI addPara(String format, float pad, Color[] hl, String ... highlights);
LabelAPI addPara(String format, float pad, Color color, Color hl, String ... highlights);

1: We use that to simply add text
2: We use that to add coloured text
3 We use that one to add text, where specific bits of text are highlighted (where %s is) on specific color
4 We use that one to add text, where specific bits of text are highlighted (where %s is) on specific colors
5. We use that one to add text which have different color and on top of where specific bits of text are highlighted (where %s is) on specific color

Here is example usage of :
Code
LabelAPI addPara(String format, float pad, Color hl, String... highlights);

Code
tooltip.addPara("Construction speed bonus: %s",5f,Color.ORANGE,"50%");
tooltip.addPara("Orbital Skunk-work bonus %s",5f, Misc.getPositiveHighlightColor(),"Each built ship have 1 built-in s-mod");
tooltip.addPara("Hypershunt bonus %s",5f, Misc.getPositiveHighlightColor(),"Decrease cost of special project stages by 50%");

Result
Spoiler
[close]

Useful things to know about LabelAPI:
I think the best way for you to learn all about LabelAPI, would be extensively using it, but as small highlight with LabelAPI you can do things like
Calculating both  text height and width ( useful when positioning)
Autosizing text width (For example if we want text to be more compact)

Custom Fonts
Spoiler
Current version of starsector (0.97 as this is written) has support for adding custom fonts!
All you need is .fnt file of your font you wanna use
Code
String filename = "graphics/fonts/";
ArrayList<Integer>fontSizes = new ArrayList<>();
fontSizes.add(12);
fontSizes.add(15);
fontSizes.add(18);
fontSizes.add(20);
fontSizes.add(24);
fontSizes.add(35);
for (Integer fontSize : fontSizes) {
   try {
       Global.getSettings().loadFont(filename+"ACES07_Regular_"+fontSize+".fnt");
   } catch (IOException e) {
       throw new RuntimeException(e);
 

Before you will use addPara method, for this font to be used you also need to do this
Code
tooltip.setParaFont("graphics/fonts/ACES07_Regular_15.fnt");

And result of such changes?
Spoiler
[close]
[close]
[close]
On Hover Tooltips:
Spoiler
Sometimes you will find yourself in a situation, that for example you would wanna display additional info about some part of the mechanic in your UI, but something as optional, like for example cargo info when you hover over the cargo icon.
For it you will use addTooltipToPrevious
Code
tooltip.setTitleOrbitronLarge();
LabelAPI labelAPI= tooltip.addPara("Technology Tree of "+ AoTDMainResearchManager.getInstance().getManagerForPlayer().getFaction().getDisplayName(), Color.ORANGE,10f);
labelAPI.getPosition().inTL(150-labelAPI.computeTextWidth(labelAPI.getText())/2,10);
tooltip.setParaFontDefault();
specialProjectButton=tooltip.addButton("Special Projects","UI_SPECIAL_PROJECTS",290,30,10f);
specialProjectButton.getPosition().inTL(5,52);
tooltip.addTooltipToPrevious(new TooltipMakerAPI.TooltipCreator() {
   @Override
   public boolean isTooltipExpandable(Object tooltipParam) {
       return true;
   }


   @Override
   public float getTooltipWidth(Object tooltipParam) {
       return 500;
   }


   @Override
   public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
       tooltip.addPara("Special projects are special type of research options, that appear once certain criteria are met.\nThese projects unlike research options in the tech tree are not guaranteed to be successful." +
               "\nCurrently special projects are inaccessible due to being WIP",10f);
   }
}, TooltipMakerAPI.TooltipLocation.RIGHT,false);

Result
Spoiler
[close]
[close]
Custom Components:
Spoiler
Other Panels:
Yes you can add whole panels to tooltip. The best way to do it avoid any troubles is to add Panels initalized with  Global.getSettings().createCustom(width,height,CustomUIPanelPlugin);
Code
CustomPanelAPI panel = Global.getSettings().createCustom(100,100,null);
mainTooltip.addCustom(panel ,5f);
Sub tooltip:
I find this to be more efficient sometimes as adding too much panels is not also a good idea (Check what you need to look for )
Code
CustomPanelAPI panel = Global.getSettings().createCustom(300,100,null);
TooltipMakerAPI tooltip = panel.createUIElement(300,100,true);
TooltipMakerAPI subTooltip = tooltip.beginSubTooltip(300);
subTooltip.addPara("My own subTooltip",0f);
tooltip.endSubTooltip();
tooltip.addCustom(subTooltip,5f);
[close]
Spacers
Spoiler
If you wanna create spaces between two Ui components in tooltip ( a gap between them), a safe bet to do it is with using spacers.
Code
tooltip.addSpacer(30f);
[close]
[close]
Using PositionAPI
Spoiler
Panels, buttons, Tooltips and all of the UI components have fields with PositionAPI. You can use it to directly retrieve coordinates of certain UI parts. This is very helpful in designing responsive UI. Where instead of hard setting coordinates of UI, you use coordinates of relative parts of UI.

Note! Using PositionAPI on components that are placed on tooltip which has not been placed yet on panel, will result in Y coordinates to be on minus. So whenever  retrieving coordinates of for example the last text you placed , multiply the Y cord by -1.
[close]
Using Stencil Mask:
Spoiler
Using Stencil Mask

To avoid blue lines rendered with openGL like on that image below:



You need to use Stencil Mask.

First before you even start using stencil you need to include this in advance method.
Code
 glClearStencil(0);
glStencilMask(0xff);

Here is function for : drawmask as we will be using that below:
Code
 void drawmask(CustomPanelAPI p) {
        GL11.glBegin(GL_QUADS);
        float x = p.getPosition().getX() - 6;
        float y = p.getPosition().getY();
        float w = p.getPosition().getWidth() + 11;
        float h = p.getPosition().getHeight();
        GL11.glVertex2f(x, y);
        GL11.glVertex2f(x + w, y);
        GL11.glVertex2f(x + w, y + h);
        GL11.glVertex2f(x, y + h);
        GL11.glEnd();
    }

Then moving on to render method section.
Code
  glClear(GL_STENCIL_BUFFER_BIT);
            glColorMask(false, false, false, false); //disable colour
            glEnable(GL_STENCIL_TEST); //enable stencil
            openGlUtilis.drawmask(panel1);
            glStencilFunc(GL_ALWAYS, 1, 0xff); // Do not test the current value in the stencil buffer, always accept any value on there for drawing
            glStencilMask(0xff);
            glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); // Make every test succeed
            openGlUtilis.drawmask(panel1);
            glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Make sure you will no longer (over)write stencil values, even if any test succeeds
            glColorMask(true, true, true, true); // Make sure we draw on the backbuffer again.
            glStencilFunc(GL_EQUAL, 1, 0xFF); // Now we will only draw pixels where the corresponding stencil buffer value equals 1

//here you have code for drawing panels for example with drawPanelBorder we mentioned , that will be located in stencil mask

         glDisable(GL_STENCIL_TEST);
[close]
What you need to look for:
Spoiler
As you go with designing UI there are few things you need to look for.
Don’t use too many Panels at once !
Spoiler
As I said, it is good to use a lot of panels, for easy work with UI, but at the same time, each panel has its own render,  and advanced method, which when you start to stack it more and more, it will generate lag.
You won’t probably exceed such a number easily, as this is when you have dozens of panels in UI.
[close]
Check those resolutions!
Spoiler
I am serious, you might think that people no longer use such small resolutions, and here you will be mistaken. A good UI is a UI that looks good on all resolutions.
[close]
Use relative positioning
Spoiler
With ever changing resolution (because not everyone is playing 1920x1080) using relative positioning can be very helpful in creating a cohesive UI.
[close]
Pre calculate width and height of tooltips and panels/b]
Spoiler
Once you place panel or tooltip, you cant resize them , you need to remove them , change parameters and then insert them back, so it has any effect!
[close]
[close]
Useful Codding Snippets
Spoiler
I will be posting here useful codding snippets, that might come in handy in future for those who wants to dive into creating UI

Drawing stencil mask based on Panel (arg scale is used for how much height should be used in stencil) and endStencil
Spoiler
Code
 public static void startStencil(CustomPanelAPI panel,float scale) {
        GL11.glClearStencil(0);
        GL11.glStencilMask(0xff);
        GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);

        GL11.glColorMask(false, false, false, false);
        GL11.glEnable(GL11.GL_STENCIL_TEST);

        GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0xff);
        GL11.glStencilMask(0xff);
        GL11.glStencilOp(GL11.GL_REPLACE, GL11.GL_REPLACE, GL11.GL_REPLACE);

        GL11.glBegin(GL11.GL_POLYGON);
        PositionAPI position = panel.getPosition();
        float x = position.getX();
        float y = position.getY();
        float width = position.getWidth();
        float height = position.getHeight();

        // Define the rectangle
        GL11.glVertex2f(x, y);
        GL11.glVertex2f(x + width, y);
        GL11.glVertex2f(x + width, y + height*scale);
        GL11.glVertex2f(x, y + height*scale);
        GL11.glEnd();

        GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
        GL11.glColorMask(true, true, true, true);

        GL11.glStencilFunc(GL11.GL_EQUAL, 1, 0xFF);
    }
    public static void endStencil() {
        GL11.glDisable(GL11.GL_STENCIL_TEST);
    }
[close]

[close]



« Last Edit: July 12, 2024, 12:39:01 AM by Kaysaar »
Logged

nathan67003

  • Commander
  • ***
  • Posts: 203
  • Excellent imagination, mediocre implementation.
    • View Profile
Re: Custom UI Guide : Necessary Basics
« Reply #1 on: November 29, 2023, 06:46:24 AM »

Praise be. More guides is always a great thing.
Logged
I have some ideas but can't sprite worth a damn and the ideas imply really involved stuff which I've no clue how to even tackle.

Alex

  • Administrator
  • Admiral
  • *****
  • Posts: 24758
    • View Profile
Re: Custom UI Guide : Necessary Basics
« Reply #2 on: January 10, 2024, 07:56:30 PM »

Stickied this, very nice work!
Logged

Kaysaar

  • Admiral
  • *****
  • Posts: 514
    • View Profile
Re: Custom UI Guide : Necessary Basics
« Reply #3 on: July 07, 2024, 12:20:14 AM »

Hello, I have revamped this guide, I wanted to do it months ago, but I did not had time.
Here it is much more info than previously, I hope this will help much more!
Logged

Kaysaar

  • Admiral
  • *****
  • Posts: 514
    • View Profile
Re: Custom UI Guide : Necessary Basics
« Reply #4 on: July 07, 2024, 12:20:47 AM »

Stickied this, very nice work!
Thank you alex, I hope you will find this updated version also as a good work !
Logged

FastLite

  • Ensign
  • *
  • Posts: 1
    • View Profile
Re: Custom UI Guide : Necessary Basics
« Reply #5 on: September 07, 2024, 08:36:59 PM »

Hi, it's a pretty useful guide but I am having trouble understanding how to create the first tooltip or get the mainPanel. I am just getting into modding, and it would help a lot.
Logged