• Hi Guest!

    We have posted a new VaM2 dev log on Patreon, starting a monthly cadence of written progress updates between Beta releases. Highlights include the new Gizmos System, Selection Carousel, and Modes System with Context-Specific Editing. Beta1.2 is 15 of 21 items complete.

    Read the full post on Patreon, or follow progress on the public Trello roadmap.
CustomUI

Plugins + Scripts CustomUI

Download [<1 MB]

14mhz

Well-known member
Joined
Oct 23, 2024
Messages
316
Solutions
7
Reactions
2,065
14mhz submitted a new resource:

CustomUI - This is a plugin simple development tool.

※ Reporting bugs or issues will help me improve the plugin and help others.
CustomUI
● This .cs for plugin UI implementation library for developer.
● This library doesn't aim to offer something novel, but rather simplifies and standardizes components that are commonly implemented by developers.
● Of course...

Read more about this resource...
 
Hi 14MHz, I stumbled over a minor problem I cannot solve for now. I use a CustomUI.addSliderSimple Layout with an added Default button which itself is in a tab layout which will be reloaded on clicking on the tab. This will load the current value into the slider, which is great.
C#:
public static JSONStorableFloat erectionmorph1_min_JSON = new JSONStorableFloat("Penis Length Flaccid", 2.2f, -5.0f, 10.0f, false);

//Init value, min value, max value in the GUI
value = new float[][] {
             new float[] {erectionmorph1_min_JSON.val, erectionmorph1_min_JSON.min, erectionmorph1_min_JSON.max},
            ...

BaseLabelUI ui01 = CustomUI.addLabel(this, true, titleLabel).setTextFontSize(26).setBackgroundColor(Color.clear).setTextColor(Color.white);
ui01.setTextAlign(TextAnchor.MiddleLeft);

BaseSliderSimpleUI ui02 = CustomUI.addSliderSimple(this, true, name, _val[0], _val[1], _val[2]).setLabelTextWeight(0.0f).setSliderWeight(0.75f).setCallback(callbackEvent).register();
UIDynamicSlider u = ui02.uid as UIDynamicSlider;
if (u != null)
{
       u.defaultButtonEnabled = true;
       Transform btnTransform = u.transform.Find("DefaultValueButton");

       if (btnTransform != null)
       {
              RectTransform rt = btnTransform.GetComponent<RectTransform>();
              rt.anchoredPosition = new Vector2(-40f, 13f);
        }
}

In this situation, however, the default value is always overwritten with the current value as the slider is created. Do you have any idea how to make the default stick to the static default value of the JSONStorableFloat (2.2f in this case)? Thank you in advance for any help!!
 
Hi 14MHz, I stumbled over a minor problem I cannot solve for now. I use a CustomUI.addSliderSimple Layout with an added Default button which itself is in a tab layout which will be reloaded on clicking on the tab. This will load the current value into the slider, which is great.
C#:
public static JSONStorableFloat erectionmorph1_min_JSON = new JSONStorableFloat("Penis Length Flaccid", 2.2f, -5.0f, 10.0f, false);

//Init value, min value, max value in the GUI
value = new float[][] {
             new float[] {erectionmorph1_min_JSON.val, erectionmorph1_min_JSON.min, erectionmorph1_min_JSON.max},
            ...

BaseLabelUI ui01 = CustomUI.addLabel(this, true, titleLabel).setTextFontSize(26).setBackgroundColor(Color.clear).setTextColor(Color.white);
ui01.setTextAlign(TextAnchor.MiddleLeft);

BaseSliderSimpleUI ui02 = CustomUI.addSliderSimple(this, true, name, _val[0], _val[1], _val[2]).setLabelTextWeight(0.0f).setSliderWeight(0.75f).setCallback(callbackEvent).register();
UIDynamicSlider u = ui02.uid as UIDynamicSlider;
if (u != null)
{
       u.defaultButtonEnabled = true;
       Transform btnTransform = u.transform.Find("DefaultValueButton");

       if (btnTransform != null)
       {
              RectTransform rt = btnTransform.GetComponent<RectTransform>();
              rt.anchoredPosition = new Vector2(-40f, 13f);
        }
}

In this situation, however, the default value is always overwritten with the current value as the slider is created. Do you have any idea how to make the default stick to the static default value of the JSONStorableFloat (2.2f in this case)? Thank you in advance for any help!!
Hello, RunRudolf !
Has the issue you requested been resolved?
 
Hello, RunRudolf !
Has the issue you requested been resolved?
Hi 14mhz! No, I dont think anything changed in this regard (did you change anything?).
I think its a VaM specific behaviour that the default values are set each time a slider is created. Since this happens every time the sub UI is (re)created, the default values will be overwritten with the current active value. Maybe I have to prevent the slider from being created each time the UI is called?
 
I think its a VaM specific behaviour that the default values are set each time a slider is created.
<-- In my opinion, it seems to be an issue with the implementation method.

-----

I think that in various other button-based Tab implementations, it is often done like this.
The following implementation leads to many issues because it deletes the object when a tab is selected and recreates it every time.
C#:
if select tab
    foreach alltab
       close(tab elements)
    new(tab elements) <-- Actually, it is not advisable to implement it in this way (This is not the :) right way)
As an alternative, I suggest making only the elements within the selected Tab visible. By doing so, the problems you mentioned should not arise.
C#:
if select tab
    foreach alltab
       if (t is tab)
           visiable(tab elements)
       else
           invisiable(tab elements)
The CustomTabUI I implemented is designed in this way.

In addition, in your code,
C#:
public static JSONStorableFloat erectionmorph1_min_JSON = new JSONStorableFloat("Penis Length Flaccid", 2.2f, -5.0f, 10.0f, false);

//Init value, min value, max value in the GUI
value = new float[][] {
             new float[] {erectionmorph1_min_JSON.val, erectionmorph1_min_JSON.min, erectionmorph1_min_JSON.max},
            ...

BaseLabelUI ui01 = CustomUI.addLabel(this, true, titleLabel).setTextFontSize(26).setBackgroundColor(Color.clear).setTextColor(Color.white);
ui01.setTextAlign(TextAnchor.MiddleLeft);

BaseSliderSimpleUI ui02 = CustomUI.addSliderSimple(this, true, name, _val[0], _val[1], _val[2]).setLabelTextWeight(0.0f).setSliderWeight(0.75f).setCallback(callbackEvent).register();
UIDynamicSlider u = ui02.uid as UIDynamicSlider;
if (u != null)
{
       u.defaultButtonEnabled = true;
       Transform btnTransform = u.transform.Find("DefaultValueButton");

       if (btnTransform != null)
       {
              RectTransform rt = btnTransform.GetComponent<RectTransform>();
              rt.anchoredPosition = new Vector2(-40f, 13f);
        }
}
when register() is called with name as the key, a JSONStorableFloat is generated automatically.
Apart from sharing the initial value, it is not connected to "erectionmorph1_min_JSON"
 
Last edited:
<-- In my opinion, it seems to be an issue with the implementation method.

-----

I think that in various other button-based Tab implementations, it is often done like this.
The following implementation leads to many issues because it deletes the object when a tab is selected and recreates it every time.
C#:
if select tab
    foreach alltab
       close(tab elements)
    new(tab elements) <-- Actually, it is not advisable to implement it in this way (This is not the :) right way)
As an alternative, I suggest making only the elements within the selected Tab visible. By doing so, the problems you mentioned should not arise.
C#:
if select tab
    foreach alltab
       if (t is tab)
           visiable(tab elements)
       else
           invisiable(tab elements)
The CustomTabUI I implemented is designed in this way.

In addition, in your code,
C#:
public static JSONStorableFloat erectionmorph1_min_JSON = new JSONStorableFloat("Penis Length Flaccid", 2.2f, -5.0f, 10.0f, false);

//Init value, min value, max value in the GUI
value = new float[][] {
             new float[] {erectionmorph1_min_JSON.val, erectionmorph1_min_JSON.min, erectionmorph1_min_JSON.max},
            ...

BaseLabelUI ui01 = CustomUI.addLabel(this, true, titleLabel).setTextFontSize(26).setBackgroundColor(Color.clear).setTextColor(Color.white);
ui01.setTextAlign(TextAnchor.MiddleLeft);

BaseSliderSimpleUI ui02 = CustomUI.addSliderSimple(this, true, name, _val[0], _val[1], _val[2]).setLabelTextWeight(0.0f).setSliderWeight(0.75f).setCallback(callbackEvent).register();
UIDynamicSlider u = ui02.uid as UIDynamicSlider;
if (u != null)
{
       u.defaultButtonEnabled = true;
       Transform btnTransform = u.transform.Find("DefaultValueButton");

       if (btnTransform != null)
       {
              RectTransform rt = btnTransform.GetComponent<RectTransform>();
              rt.anchoredPosition = new Vector2(-40f, 13f);
        }
}
when register() is called with name as the key, a JSONStorableFloat is generated automatically.
Apart from sharing the initial value, it is not connected to "erectionmorph1_min_JSON"
Thanks for your help! I am aware that this method is not perfect, but I cannot restructure the entire UI. I also did not succeed putting the entire layout into a container and hiding it without leaving empty boxes. So I coutinue working with the create and destroy method, but now found a way to add consistent defaultValues for your SliderSimples.

I added the following function to your CustomUI.cs to reset the DefaultValue to the initial value of the JSONStorableFloat each time the UI is created:
C#:
        public BaseSliderSimpleUI setDefaultValue(float def) //Added by RunRudolf
        {
            UIDynamicSlider u = uid as UIDynamicSlider;
            if (u != null)
            {
                u.defaultValue = def;
                u.defaultButtonEnabled = true; //Optional, if button was deactivated
            }
            return this;
        }

I then call this in a for with the .setDefaultValue() included:
C#:
for (int r = 0; r < layout.row; r++)
{
      string name2 = variable[(r * 2) + 1];
      float[] _val = value[r * 2];
      float[] _val2 = value[(r * 2) + 1];
      float[] _defVal2 = defaultValue[r + 1];
      ...

      BaseSliderSimpleUI ui03 = CustomUI.addSliderSimple(this, true, name2, _val2[0], _val2[1],   _val2[2]).setLabelTextWeight(0.0f).setSliderWeight(0.75f).setCallback(callbackEvent).setDefaultValue(_defVal2[0]);
      UIDynamicSlider u2 = ui03.uid as UIDynamicSlider;
      if (u2 != null)
       {
                u2.defaultButtonEnabled = true;
                Transform btnTransform = u2.transform.Find("DefaultValueButton");
                if (btnTransform != null)
                {
                        RectTransform rt = btnTransform.GetComponent<RectTransform>();
                        rt.anchoredPosition = new Vector2(-40f, 13f);
                 }
       }
}

The defaultValues are simply created like this:
C#:
defaultValue = new float[][] {
      new float[] {erectionmorph1_min_JSON.defaultVal},
      new float[] {erectionmorph1_max_JSON.defaultVal},
      ...

It seems to solve the problem of changing default values for me. BTW, I also left the .register(), its not needed here apparently. Thanks again for your feedback!
 
How would I show/hide certain UI elements?
Trying .uid.gameObject.SetActive(false) but that isn't hiding them for me.
 
How would I show/hide certain UI elements?
Trying .uid.gameObject.SetActive(false) but that isn't hiding them for me.
Good to hear from you, bill_prime. I'm away from my computer at the moment, so I can't be exact. SetActive normally works fine, but when it fails, in my experience it's usually due to interference from another routine. (I turned it off, but another process is turning it back on). Rather than calling it in Init() or Start(), try testing it in Update().
 
Good to hear from you, bill_prime. I'm away from my computer at the moment, so I can't be exact. SetActive normally works fine, but when it fails, in my experience it's usually due to interference from another routine. (I turned it off, but another process is turning it back on). Rather than calling it in Init() or Start(), try testing it in Update().
Okay no worries, thank you for the reply. I will do some more testing in different cases.
 
Hey @14mhz, i'm considering using your library for my next companion plugin for VAMStory, and with the demo, after adding the demo plugin on an empty. Saving the scene and reloading it.
I get this in the log:

!> Exception NullReferenceException: Object reference not set to an instance of an object CustomUI14mhz.UIUtil+<assign>c__Iterator0.MoveNext ()
UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress)

Know that I have Acid's forward log always enable to detect silent errors, so this might be a silent one ^^
 
!> Exception NullReferenceException: Object reference not set to an instance of an object CustomUI14mhz.UIUtil+<assign>c__Iterator0.MoveNext ()
UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress)
This seems to be an error I have never encountered before. -..- (maybe seems to lie in the coroutine section of my code)
Could you kindly provide me with the scene.json so that I can better understand the circumstances under which this error occurs? ^o^
 
Last edited:
This seems to be an error I have never encountered before. -..- (maybe seems to lie in the coroutine section of my code)
Could you kindly provide me with the scene.json so that I can better understand the circumstances under which this error occurs? ^o^

Sure! It's just very basic, I simply loaded the thing on an empty and edited a couple of fields.
So that you don't forget since I confirmed: it's a silent error, so you need to load Acid's Log Forward plugin to see it :)
 

Attachments

  • 1779651229.json
    6.7 KB · Views: 0
Sure! It's just very basic, I simply loaded the thing on an empty and edited a couple of fields.
So that you don't forget since I confirmed: it's a silent error, so you need to load Acid's Log Forward plugin to see it :)
Yes, it is indeed a hidden error.
I discovered it in the file located at C:\Users\Administrator\AppData\LocalLow\MeshedVR\VaM\output_log.txt
 
The bug fix has been completed, and the changes will be distributed in the next version.
Thank you @hazmhox !!!

Damn bro! you're fast! thank you very much <3

Hey I have a question out of curiosity, is it possible to do "sub tabs", or if you prefer, a main tab list and inside of each children tabs.
Not that I need them right now, but just out of curiosity to plan my plugin properly in term of UI design ^^
 
Ho btw! One idea, the lists/chooser take SetPopList which sets "choices". But list also allows displayChoices, which is super handy in some situation (I'm using it often recently). You could add them if you wanted?
 
Ho btw! One idea, the lists/chooser take SetPopList which sets "choices". But list also allows displayChoices, which is super handy in some situation (I'm using it often recently). You could add them if you wanted?
The displayChoices feature has been implemented, and will release the next version within this week.
Additionally, the Tab‑inner‑Tab feature has been implemented and will be released along with a demo. :D
14mhz.Plugin-CustomTabUI.10.gif
 

Similar threads

Back
Top Bottom