Browse Source

Added tabs, refactored ui animator and updated docs

jamesperet 2 years ago
parent
commit
5c1dcbd82d

+ 12 - 0
Docs/ActivityMessages.md

@@ -0,0 +1,12 @@
+# ActivityMessages
+
+A subsystem that receives messages and then shows them to the player. These messages can be sticky, can fade with time, can be changed by events and can fire functions when clicked.
+
+### 🎈Back Log
+
+- [ ] Activity Message Id system
+  *Register activity messages that contain an ID and later on change the contents of the message using the same ID*
+- [ ] Activity Message Animations
+- [ ] Activity Message Task icons (incomplete, completed, failed)
+- [ ] Activity Message Icon
+  *Receive a sprite when creating the activity message and use a different prefab*

+ 7 - 0
Docs/ActivityMessages.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 3fb5afe6e0b095c4da46b9966e9bfd20
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 3 - 0
Docs/InteractionHandler.md

@@ -0,0 +1,3 @@
+# InteractionHandler
+
+The ``InteractionHandler`` is a set of scripts with interfaces and components for dealing with user interactions.

+ 7 - 0
Docs/InteractionHandler.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 912e99c5967672f44ae3ab9f6fc4deda
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 0 - 2
Docs/Tooltips.md

@@ -1,5 +1,3 @@
 # Tooltips
 
 The Tooltip system is a part of the [UI package](../Readme.md) that manages showing, hiding and managing tooltips.
-
-

+ 5 - 0
Docs/UiAnimator.md

@@ -0,0 +1,5 @@
+# UiAnimator
+
+The UiAnimator component can animate an element when it is enabled or disabled. It can manipulate position, scale and transparency for show and hide animations. 
+
+The show animation can be automatic when the gameObject is enabled. The hide animation requires a controller to tell the UiAnimator to play the hide animation and disable the gameObject. 

+ 7 - 0
Docs/UiAnimator.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 5b6fff26671052040af74ce108c26799
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 7 - 0
Docs/UiMenu.md

@@ -0,0 +1,7 @@
+# UiMenu
+
+A set of components to create menus with lists of buttons, images and tooltips. Also includes a component and helpers for dealing with interactions.
+
+### 🎈Back Log
+
+- [ ] Menu button Interaction sounds

+ 7 - 0
Docs/UiMenu.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e4fc04a92e415fe49b9d321c901ed3b0
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 22 - 12
Readme.md

@@ -1,4 +1,4 @@
-# 📦 KairoEngine.UI.v0.1.2
+# 📦 KairoEngine.UI.v0.1.4
 
 The **UI** package contains the ``UiSystem`` component and the ``UiSystemModule``. The module loads the Ui System in place which manages canvases with UI data. The system now which canvases are enabled and has a global toggle for all the game UI.
 
@@ -32,29 +32,39 @@ Find or create the ``GameConfig`` file, click add new module and select the **UI
 
 Once the ``GameConfig`` is loaded on the game start, the base UI system will be instantiated in your game. This will create or find a *UI* GameObject inside the *GameManager* GameObject and add a ``UiSystem`` component to it. The rest of the game UI can be put as a child of *GameManager/UI*.
 
-##### New Interface elements
+##### Interface elements
 
 More information on the interface elements included in the UI package can be found on each elements documentation:
 
-- [UiAnimator]() – A generic component that animates a peice of the interface.
+- [InteractionHandler](docs/InteractionHandler.md) – :heavy_check_mark:A set of scripts with interfaces and components for dealing with user interactions.
 
-- [FadeTransition]() – A subsystem that fades the screen to a color and back again. It can trigger and listen to events.
+- [UiMenu](docs/UiMenu.md) – :heavy_check_mark: A set of components to create menus with lists of buttons, images and tooltips. Also includes a component and helpers for dealing with interactions. 
 
-- [FloatingText]() – A subsystem that listens for events, manages and instantiates animated text in the world.
+- [UiAnimator](docs/UiAnimator.md) – :heavy_check_mark: A generic component that animates a peice of the interface.
 
-- [MessageBox]() – A prefab containing a window with a title, message and buttons that require imidiate action.
+- [FadeTransition]() – :heavy_check_mark: A subsystem that fades the screen to a color and back again. It can trigger and listen to events.
 
-- [ProgressBar]() – A prefab containing a bar representing the total and completed percentage of a task.
+- [FloatingText]() – :heavy_check_mark: A subsystem that listens for events, manages and instantiates animated text in the world.
 
-- [Tooltip](Docs/Tooltips.md)  – A subsystem that show tooltips when hovering the mouse over a certain object.
+- [MessageBox]() – :x: A prefab containing a window with a title, message and buttons that require imidiate action.
 
-- [ActivityMessages]() – A subsystem that receives messages and then shows them to the player. These messages can be sticky, can fade with time, can be changed by events and can fire functions when clicked.
+- [ProgressBar]() – :grey_question: A prefab containing a bar representing the total and completed percentage of a task.
 
-- [ContextMenu]() – A prefab that draws a context menu with sevaral buttons, separators and submenus.
+- [Tooltip](Docs/Tooltips.md)  – :heavy_check_mark: A subsystem that show tooltips when hovering the mouse over a certain object.
 
-- [ModalWindow]() – A prefab containing a window that is shown on top of other UI.
+- [ActivityMessages](docs/ActivityMessages.md) – :hammer: A subsystem that receives messages and then shows them to the player. These messages can be sticky, can fade with time, can be changed by events and can fire functions when clicked.
 
-- [LoadingScreen]() – A subsystem that shows a loading screen. It can trigger and listen to events.
+- [ContextMenu]() – :x: A prefab that draws a context menu with sevaral buttons, separators and submenus.
+
+- [ModalWindow]() – :x: A prefab containing a window that is shown on top of other UI.
+
+- [LoadingScreen]() – :x: A subsystem that shows a loading screen. It can trigger and listen to events.
+
+- Tabs – :x:
+
+- Encyclopedia – :x:
+
+- Credits – :x:
 
 ##### Register Ui Elements in the UiSystem
 

+ 1 - 0
Runtime/ConfigOptions/SliderConfigOptionUI.cs

@@ -55,6 +55,7 @@ namespace KairoEngine.UI.ConfigOptions
         public void OnChange()
         {
             ApplyConfig();
+            if(this.gameObject.activeInHierarchy == false) return;
             if(delayedSave != null) StopCoroutine(delayedSave);
             delayedSave = StartCoroutine(Timer.StartRealtime(2f, SaveConfig));
         }

+ 2 - 0
Runtime/InteractionHandler/ButtonData.cs

@@ -3,9 +3,11 @@ using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.UI;
 using TMPro;
+using Sirenix.OdinInspector;
 
 namespace KairoEngine.UI.InteractionHandler
 {
+    [HideMonoScript]
     public class ButtonData : MonoBehaviour
     {
         public TextMeshProUGUI headerComponent;

+ 4 - 2
Runtime/InteractionHandler/ClickHandler.cs

@@ -5,15 +5,17 @@ using Sirenix.OdinInspector;
 
 namespace KairoEngine.UI.InteractionHandler
 {
+    [HideMonoScript]
     public class ClickHandler : MonoBehaviour
     {
+        [InfoBox("Control this button dynamicly thru a parent object that implements the ClickHandler interface. Trigger the OnClick function on this script thru the Button Component.")]
         public string title = "";
-        [ShowInInspector] public IClickHandler receiver;
+        [ShowInInspector, ReadOnly, ShowIf("@receiver != null")] public IClickHandler receiver;
 
         public void OnClick()
         {
             if(receiver != null) receiver.OnClick(title);
-            else Debug.LogError("ClickHandler has no receiver!", this.gameObject);
+            //else Debug.LogError("ClickHandler has no receiver!", this.gameObject);
         }
     }
 }

+ 3 - 0
Runtime/MenuComponents/MenuController.cs

@@ -7,6 +7,9 @@ using KairoEngine.UI.InteractionHandler;
 
 namespace KairoEngine.UI
 {
+    /// <summary>
+    /// Set GameActions to be run when a UI Menu buttom has been clicked
+    /// </summary>
     [HideMonoScript]
     public class MenuController : MonoBehaviour, IClickHandler
     {

+ 38 - 7
Runtime/MenuComponents/MenuUI.cs

@@ -4,22 +4,31 @@ using UnityEngine;
 using TMPro;
 using Sirenix.OdinInspector;
 using UnityEngine.UI;
+using KairoEngine.Core;
 using KairoEngine.UI.Tooltips;
 using KairoEngine.UI.InteractionHandler;
 
 namespace KairoEngine.UI
 {
+    /// <summary>
+    /// Create and control a menu o buttons. Buttons can be pre-generated or can be created in runtime. When a button is clicked, 
+    /// the MenuUI component will execute a function with the IClickHandler interface.
+    /// </summary>
     [HideMonoScript]
     public class MenuUI : MonoBehaviour, IClickHandler, IUiSystemElement
     {
         [BoxGroup("Settings")] public string elementTitle = "New UI Element";
-        [BoxGroup("Settings")] public bool createMenuOnStart = true;
+        [BoxGroup("Settings")] public bool procedural = false;
+        [BoxGroup("Settings"), HideIf("@procedural")] public bool createMenuOnStart = true;
         [BoxGroup("Settings")] public bool visibleOnStart = true;
+        [BoxGroup("Settings")] public bool debug = false;
         [BoxGroup("Settings")] public GameObject buttonPrefab;
         [BoxGroup("Settings")] public RectTransform menuParent;
         [BoxGroup("Settings")] public Canvas menuCanvas;
+        [BoxGroup("Settings")] public UiAnimator uiAnimator;
 
-        [PropertySpace(4,4), OnCollectionChanged("UpdateButtonData")] public List<MenuButtomData> buttons = new List<MenuButtomData>();
+        [PropertySpace(4,4), OnCollectionChanged("UpdateButtonData"), ShowIf("@!procedural || debug")] 
+        public List<MenuButtomData> buttons = new List<MenuButtomData>();
 
         private bool isVisible = false;
         [HideInInspector] public IClickHandler handler;
@@ -27,7 +36,16 @@ namespace KairoEngine.UI
 
         public virtual void Start()
         {
-            if(handler == null) handler = gameObject.GetComponent<IClickHandler>();
+            if(handler == null)
+            {
+                var handlers = gameObject.GetComponents<IClickHandler>();
+                if(handlers.Length > 0) handler = handlers[handlers.Length - 1];
+            }
+            if(handler != null) 
+            {
+                if(debug) Debug.Log("Menu handler set to " + handler, this.gameObject);
+            } 
+            else Debug.LogError("Could not find IClickHandler for Menu", this.gameObject);
             if(createMenuOnStart) 
             {
                 DestroyMenu();
@@ -56,6 +74,7 @@ namespace KairoEngine.UI
                     if(buttons[i].action != "" && handler != null)
                     {
                         handler.OnClick(buttons[i].action);
+                        if(debug) Debug.Log("Clicked on " + title);
                     }
                 }
             }
@@ -63,14 +82,17 @@ namespace KairoEngine.UI
 
         public void Hide()
         {
-            menuCanvas.gameObject.SetActive(false);
+            if(uiAnimator != null) uiAnimator.Disable();
+            else menuCanvas.gameObject.SetActive(false);
             isVisible = false;
         }
 
         public void Show()
         {
-            menuCanvas.gameObject.SetActive(true);
+            if(uiAnimator != null) uiAnimator.Enable();
+            else menuCanvas.gameObject.SetActive(true);
             isVisible = true;
+
         }
 
         public bool IsVisible() => isVisible;
@@ -102,7 +124,7 @@ namespace KairoEngine.UI
             return btn;
         }
 
-        [ButtonGroup("Buttons"), Button("Create Menu")]
+        [ButtonGroup("Buttons"), Button("Create Menu"), ShowIf("@!procedural || debug")]
         public virtual void CreateMenu()
         {
             for (int i = 0; i < buttons.Count; i++)
@@ -112,7 +134,7 @@ namespace KairoEngine.UI
             }
         }
 
-        [ButtonGroup("Buttons"), Button("Destroy Menu")]
+        [ButtonGroup("Buttons"), Button("Destroy Menu"), ShowIf("@!procedural || debug")]
         public virtual void DestroyMenu()
         {
             for (int i = 0; i < buttons.Count; i++)
@@ -132,6 +154,15 @@ namespace KairoEngine.UI
                 buttons[i].useSubMenus = useSubMenus;
             }
         }
+
+        public GameObject GetButton(string action) 
+        {
+            for (int i = 0; i < buttons.Count; i++)
+            {
+                if(buttons[i].action == action) return buttons[i].button;
+            }
+            return null;
+        }
     }
 }
 

+ 8 - 0
Runtime/SFX.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8496e0ee6b286944396ee1a9f8280326
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 79 - 0
Runtime/SFX/ButtonSFX.cs

@@ -0,0 +1,79 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using KairoEngine.SFX;
+using UnityEngine.EventSystems;
+
+namespace KairoEngine.UI.SFX
+{
+    [HideMonoScript]
+    public class ButtonSFX : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
+    {
+        public SFXClip onPointerEnterSFX;
+        public SFXClip onPointerExitSFX;
+        public SFXClip onPointerDownSFX;
+        public SFXClip onPointerUpSFX;
+        public SFXClip onClickSFX;
+
+        public void OnPointerEnter(PointerEventData eventData)
+        {
+            if(MouseInputUIBlocker.BlockedByUI) return;
+            PlaySFX(onPointerEnterSFX);
+        }
+
+        public void OnPointerExit(PointerEventData eventData)
+        {
+            PlaySFX(onPointerExitSFX);
+        }
+
+        public void OnPointerDown(PointerEventData eventData)
+        {
+            if(MouseInputUIBlocker.BlockedByUI) return;
+            PlaySFX(onPointerDownSFX);
+        }
+
+        public void OnPointerUp(PointerEventData eventData)
+        {
+            PlaySFX(onPointerUpSFX);
+        }
+
+        public void OnMouseEnter()
+        {
+            if(MouseInputUIBlocker.BlockedByUI) return;
+            PlaySFX(onPointerEnterSFX);
+            
+        }
+
+        public void OnMouseExit()
+        {
+            PlaySFX(onPointerExitSFX);
+        }
+
+        public void OnMouseDown()
+        {
+            if(MouseInputUIBlocker.BlockedByUI) return;
+            PlaySFX(onPointerDownSFX);
+            
+        }
+
+        public void OnMouseUp()
+        {
+            PlaySFX(onPointerUpSFX);
+        }
+
+        public void OnClick()
+        {
+            PlaySFX(onClickSFX);
+        }
+
+        private void PlaySFX(SFXClip clip)
+        {
+            if(clip == null) return;
+            if(this.gameObject.activeInHierarchy == false) return;
+            SoundController.EmmitSound(clip, new Vector3());
+            
+        }
+    }
+
+}

+ 11 - 0
Runtime/SFX/ButtonSFX.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 449a91b8c6475094d8210735aa4a9220
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 19 - 0
Runtime/SFX/KairoEngine.UI.SFX.asmdef

@@ -0,0 +1,19 @@
+{
+    "name": "KairoEngine.UI.SFX",
+    "rootNamespace": "",
+    "references": [
+        "GUID:7e5ae6a38d1532248b4c890eca668b06",
+        "GUID:142285d3db5e7e849b02ea3a75bc2de7",
+        "GUID:d5cdde771ecbd5e47bdbe207903a3b3c",
+        "GUID:e048eeec9bdb9d30448017b829deb3f6"
+    ],
+    "includePlatforms": [],
+    "excludePlatforms": [],
+    "allowUnsafeCode": false,
+    "overrideReferences": false,
+    "precompiledReferences": [],
+    "autoReferenced": true,
+    "defineConstraints": [],
+    "versionDefines": [],
+    "noEngineReferences": false
+}

+ 7 - 0
Runtime/SFX/KairoEngine.UI.SFX.asmdef.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d8706abd305f14440b0f3ce5fc8ad23b
+AssemblyDefinitionImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 41 - 0
Runtime/SliderText.cs

@@ -0,0 +1,41 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using TMPro;
+
+
+namespace KairoEngine.UI
+{
+    public class SliderText : MonoBehaviour
+    {
+        public TextMeshProUGUI text;
+        public string suffix = "%";
+        public float multiplier = 100;
+        public Slider slider;
+        
+        public void OnEnable()
+        {
+            slider.onValueChanged.AddListener(delegate {UpdateText(); });
+        }
+
+        void OnDisable()
+        {
+            slider.onValueChanged.RemoveListener(delegate {UpdateText(); });
+        }
+
+        void Start()
+        {
+            UpdateText();
+        }
+        
+        public void UpdateText()
+        {
+            float value = slider.value * multiplier;
+            value = value < 0.6 ? 0 : Mathf.CeilToInt(value);
+            text.text = $"{(int)value}{suffix}";
+        }
+    }
+}
+
+

+ 11 - 0
Runtime/SliderText.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ff7cf84ac789d484a86e4a89a6facee4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Runtime/Tabs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3becc4bcdbdc0df448baeb9f10385de8
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 45 - 0
Runtime/Tabs/TabButton.cs

@@ -0,0 +1,45 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using Sirenix.OdinInspector;
+using KairoEngine.UI.InteractionHandler;
+
+namespace KairoEngine.UI
+{
+    [HideMonoScript]
+    public class TabButton : MonoBehaviour
+    {
+        public Button targetButton;
+        public Sprite buttonGraphic;
+        public Sprite buttonActiveGraphic;
+
+        private bool active = false;
+
+        public void SwitchState() 
+        {
+            if(active)
+            {
+                active = false;
+                targetButton.image.sprite = buttonGraphic;
+            }
+            else
+            {
+                active = true;
+                targetButton.image.sprite = buttonActiveGraphic;
+            }
+        }
+
+        public void Activate() 
+        {
+            active = true;
+            targetButton.image.sprite = buttonActiveGraphic;
+        }
+
+        public void Deactivate() 
+        {
+            active = false;
+            targetButton.image.sprite = buttonGraphic;
+        }
+    }
+}

+ 11 - 0
Runtime/Tabs/TabButton.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2656ff3061a25714c9153c6c3140d5ab
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 17 - 0
Runtime/Tabs/TabData.cs

@@ -0,0 +1,17 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.UI
+{
+    [System.Serializable]
+    public class TabData
+    {
+        [FoldoutGroup("@title")] public string id = "unique_tab__name";
+        [FoldoutGroup("@title")] public string title = "Tab Title";
+        [FoldoutGroup("@title")] public GameObject content;
+        [FoldoutGroup("@title")] public Sprite icon;
+        [FoldoutGroup("@title"), TextArea] public string description;
+    }
+}

+ 11 - 0
Runtime/Tabs/TabData.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cc41c5034a41b5c48a6877a82ea6a8d7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 112 - 0
Runtime/Tabs/TabsController.cs

@@ -0,0 +1,112 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using KairoEngine.UI.InteractionHandler;
+
+namespace KairoEngine.UI
+{
+    /// <summary>
+    /// The tabs controller creates and controls a set of Tabs. Under the hood it uses a MenuUi component to create and control
+    /// the tab buttons.
+    /// </summary>
+    [HideMonoScript, RequireComponent(typeof(MenuUI))]
+    public class TabsController : MonoBehaviour, IClickHandler
+    {
+        [BoxGroup("Settings")] public MenuUI menuUI;
+        [BoxGroup("Settings")] public int currentTab = 0;
+        [BoxGroup("Settings")] public bool createOnStart = false;
+        [BoxGroup("Settings")] public bool debug = false;
+        
+        public List<TabData> tabs = new List<TabData>();
+
+
+        private void Start()
+        {
+            if(createOnStart) 
+            {
+                DestroyTabMenu();
+                CreateTabMenu();
+            }
+        }
+
+        public void OnClick(string id)
+        {
+            TabData tab = null;
+            int tabIndex = 0;
+            for (int i = 0; i < tabs.Count; i++)
+            {
+                if(id == tabs[i].id)
+                {
+                    tab = tabs[i];
+                    tabIndex = i;
+                    break;
+                }
+            }
+            if(tab != null && (tabIndex != currentTab || tabs[tabIndex].content.activeSelf == false))
+            {
+                tabs[currentTab].content.SetActive(false);
+                tab.content.SetActive(true);
+                UpdateButton(tabs[currentTab].id, false);
+                UpdateButton(tabs[tabIndex].id, true);
+                currentTab = tabIndex;
+                if(debug) Debug.Log("Showing tab " + tabs[currentTab].id);
+                
+            }
+        }
+
+        [ButtonGroup("Buttons"), Button("Create Tab Menu")]
+        public void CreateTabMenu()
+        {
+            menuUI.DestroyMenu();
+            menuUI.buttons.Clear();
+            List<string> categories = new List<string>();
+            
+            for (int i = 0; i < tabs.Count; i++)
+            {
+                var tab = tabs[i];
+                var data = new MenuButtomData();
+                data.title = tab.title;
+                data.description = tab.description;
+                data.action = tab.id;
+                data.graphic = tab.icon;
+                data.subMenuParent = false;
+                data.showTooltip = true;
+                data.tooltipType = "";
+                menuUI.buttons.Add(data);
+            }
+            menuUI.CreateMenu();
+            OnClick(tabs[currentTab].id);
+        }
+
+        [ButtonGroup("Buttons"), Button("Destroy Tab Menu")]
+        public void DestroyTabMenu()
+        {
+            menuUI.DestroyMenu();
+            menuUI.buttons.Clear();
+            tabs[currentTab].content.SetActive(false);
+        }
+
+        public void UpdateButton(string id, bool activate)
+        {
+            var btn = menuUI.GetButton(id);
+            if(btn != null) 
+            {
+                TabButton tabButton = btn.GetComponent<TabButton>();
+                if(tabButton != null) 
+                {
+                    if(activate) tabButton.Activate();
+                    else tabButton.Deactivate();
+                }
+                else
+                {
+                    if(debug) Debug.LogError("Could not find TabButton component in button", btn);
+                }
+            }
+            else
+            {
+                if(debug) Debug.LogError("Could not find button with id " + id + " in MenuUI", this.gameObject);
+            }
+        }
+    }
+}

+ 11 - 0
Runtime/Tabs/TabsController.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9b09d82bb967ede42877f47172a7560c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 2 - 0
Runtime/Tooltips/TooltipTrigger.cs

@@ -3,9 +3,11 @@ using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.UI;
 using UnityEngine.EventSystems;
+using Sirenix.OdinInspector;
 
 namespace KairoEngine.UI.Tooltips
 {
+    [HideMonoScript]
     public class TooltipTrigger : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
     {
         private static LTDescr delay;

+ 54 - 19
Runtime/UiAnimator.cs

@@ -2,52 +2,85 @@
 using System.Collections.Generic;
 using UnityEngine;
 using Sirenix.OdinInspector;
+using KairoEngine.Core;
 
 namespace KairoEngine.UI
 {
+    [HideMonoScript]
     public class UiAnimator : MonoBehaviour
     {
-        public GameObject objectToAnimate;
+        [BoxGroup("Settings")] public GameObject objectToAnimate;
+        [BoxGroup("Settings")] public GameObject objectToDisable;
+        [BoxGroup("Settings")] public float animationTime = 0.5f;
+        [BoxGroup("Settings")] public bool animateOnEnable;
+        [BoxGroup("Settings")] public bool animateOnDisable;
         
-        public float delay = 0f;
-        public float disableDelay = 0f;
-        public bool animateOnEnable;
-        public bool animateOnDisable;
-        public bool disableObjectOnEnd = true;
+        [BoxGroup("Settings"), ShowIf("@animateOnEnable"), LabelText("Animation Delay")] public float delay = 0f;
+        [BoxGroup("Settings"), ShowIf("@animateOnDisable"), LabelText("Hide Delay")] public float disableDelay = 0f;
+        
+        
+        [BoxGroup("Settings"), ShowIf("@animateOnDisable")] public bool disableObjectOnEnd = true;
 
-        public bool animateScale = true;
-        [ShowIf(@"animateScale")] public float scaleDuration = 0.3f;
-        [ShowIf(@"animateScale")] public Vector3 sacelFrom;
-        [ShowIf(@"animateScale")] public Vector3 scaleTo;
+        [FoldoutGroup("Scale")] public bool animateScale = true;
+        [FoldoutGroup("Scale"), ShowIf(@"animateScale")] public float scaleDuration = 0.3f;
+        [FoldoutGroup("Scale"), ShowIf(@"animateScale")] public Vector3 sacelFrom;
+        [FoldoutGroup("Scale"), ShowIf(@"animateScale")] public Vector3 scaleTo;
 
-        public bool animateAlpha = true;
-        [ShowIf(@"animateAlpha")] public float alphaDuration = 0.1f;
-        [ShowIf(@"animateAlpha")] public float alphaFrom = 0f;
-        [ShowIf(@"animateAlpha")] public float alphaTo = 1f;
+        [FoldoutGroup("Alpha")] public bool animateAlpha = true;
+        [FoldoutGroup("Alpha"), ShowIf(@"animateAlpha")] public float alphaDuration = 0.1f;
+        [FoldoutGroup("Alpha"), ShowIf(@"animateAlpha")] public float alphaFrom = 0f;
+        [FoldoutGroup("Alpha"), ShowIf(@"animateAlpha")] public float alphaTo = 1f;
 
         private LTDescr _tweenObject = new LTDescr();
 
-        public void OnEnable()
+        private bool showing = true;
+        private bool cancelDisable = false;
+
+        public void OnEnable() => Enable();
+
+        public void Enable()
         {
+            if(objectToDisable != null)
+            {
+                if(objectToDisable.activeSelf == false) objectToDisable.SetActive(true);
+            }
+            if(gameObject.activeSelf == false) gameObject.SetActive(true);
             if(animateOnEnable)
             {
+                if(!showing) SwapDirection();
+                cancelDisable = true;
                 Animate();
             }
         }
 
         public void Disable()
         {
+            cancelDisable = false;
             if(animateOnDisable == false)
             {
-                if(disableObjectOnEnd) gameObject.SetActive(false);
+                if(objectToDisable != null) objectToDisable.SetActive(false);
+                else gameObject.SetActive(false);
                 return;
             }
-            SwapDirection();
+            if(showing) SwapDirection();
             Animate();
-            _tweenObject.setDelay(disableDelay).setOnComplete(() => {
+            float totalTime = (delay + animationTime + disableDelay) * 1000;
+            Timer.Execute(totalTime, () => {
                 SwapDirection();
-                if(disableObjectOnEnd) gameObject.SetActive(false);
+                if(disableObjectOnEnd && !cancelDisable) 
+                {
+                    if(objectToDisable != null) objectToDisable.SetActive(false);
+                    else gameObject.SetActive(false);
+                }
             });
+            // _tweenObject.setDelay(disableDelay).setOnComplete(() => {
+            //     SwapDirection();
+            //     if(disableObjectOnEnd && !cancelDisable) 
+            //     {
+            //         if(objectToDisable != null) objectToDisable.SetActive(false);
+            //         else gameObject.SetActive(false);
+            //     }
+            // });
         }
 
         private void Animate()
@@ -91,6 +124,8 @@ namespace KairoEngine.UI
             var alphaTemp = alphaFrom;
             alphaFrom = alphaTo;
             alphaTo = alphaTemp;
+            if(showing) showing = false;
+            else showing = true;
         }
     }
 }

+ 2 - 1
package.json

@@ -1,7 +1,7 @@
 {
     "name": "at.kairoscope.kairoengine.ui",
     "displayName" : "KairoEngine UI",
-    "version": "0.1.3",
+    "version": "0.1.4",
     "unity": "2020.3",
     "description": "User Interface library for kairoEngine",
     "repository": {
@@ -11,6 +11,7 @@
     "author": "Kairoscope",
     "dependencies": {
       "at.kairoscope.kairoengine.core":"0.1.6",
+      "at.kairoscope.kairoengine.sfx":"0.1.3",
       "at.kairoscope.thirdparty.leantween":"1.0.0",
       "at.kairoscope.thirdparty.tmpro":"1.0.0"
     }