// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using System.Collections.Generic; using Doozy.Editor.EditorUI.Components.Internal; using Doozy.Editor.EditorUI.ScriptableObjects.Colors; using Doozy.Editor.EditorUI.Utils; using Doozy.Editor.Reactor.Internal; using Doozy.Runtime.Colors; using Doozy.Runtime.Reactor.Easings; using Doozy.Runtime.Reactor.Internal; using Doozy.Runtime.Reactor.Reactions; using Doozy.Runtime.UIElements; using Doozy.Runtime.UIElements.Extensions; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; namespace Doozy.Editor.EditorUI.Components { public class FluidFoldout : VisualElement, IDisposable { //VisualElement //--TabButton //--Container //----Content //--Footer public virtual void Dispose() { tabButton?.Dispose(); colorReaction?.Recycle(); animatedContainer?.Dispose(); } //SETTINGS public const int k_DefaultContentLeftPadding = 16; public const int k_DefaultContentMinimumPadding = 4; public static Ease expandReactionEase => FluidAnimatedContainer.k_ReactionEase; public static float expandReactionDuration => FluidAnimatedContainer.k_ReactionDuration; private static Color defaultContainerColor => EditorColors.Default.Background; // private FloatReaction expandReaction { get; } // private float expandTargetValue { get; set; } private ColorReaction colorReaction { get; } private bool initialized { get; set; } private Color containerColor { get; set; } private Color GetFooterColor() => tabButton.fluidElement.hasAccentColor ? tabButton.fluidElement.selectableAccentColor.normalColor.WithAlpha(EditorGUIUtility.isProSkin ? 0.3f : 0.4f) : defaultContainerColor; #region IToggle & TabButton public FluidToggleButtonTab tabButton { get; } public IToggleGroup toggleGroup { get => tabButton.toggleGroup; set => tabButton.toggleGroup = value; } public bool isOn { get => tabButton.isOn; set => tabButton.isOn = value; } public FluidFoldout SetIsOn(bool newValue, bool animateChange) { tabButton.SetIsOn(newValue, animateChange); return this; } public FluidFoldout AddToToggleGroup(IToggleGroup value) { tabButton.AddToToggleGroup(value); return this; } public FluidFoldout RemoveFromToggleGroup() { tabButton.RemoveFromToggleGroup(); return this; } public void UpdateValueFromGroup(bool newValue, bool silent = false) => tabButton.UpdateValueFromGroup(newValue, silent); #endregion private TemplateContainer templateContainer { get; } private VisualElement layoutContainer { get; } private VisualElement header { get; } private VisualElement content { get; } private VisualElement contentLayoutContainer { get; } private VisualElement footer { get; } public FluidAnimatedContainer animatedContainer { get; } public FluidFoldout(string labelText, EditorSelectableColorInfo accentColor, string tooltip = "") : this() { this .SetLabelText(labelText) .SetAccentColor(accentColor) .SetTooltip(tooltip); } public FluidFoldout(string labelText, string tooltip = "") : this() { this .SetLabelText(labelText) .SetTooltip(tooltip); } public FluidFoldout(string labelText, IEnumerable textures, EditorSelectableColorInfo accentColor = null, string tooltip = "") : this() { this .SetLabelText(labelText) .SetIcon(textures) .SetAccentColor(accentColor) .SetTooltip(tooltip); } public FluidFoldout(IEnumerable textures, EditorSelectableColorInfo accentColor, string tooltip = "") : this() { this .SetIcon(textures) .SetAccentColor(accentColor) .SetTooltip(tooltip); } public FluidFoldout(IEnumerable textures, string tooltip = "") : this() { this .SetIcon(textures) .SetTooltip(tooltip); } public FluidFoldout() { Add(templateContainer = EditorLayouts.EditorUI.FluidFoldout.CloneTree()); templateContainer .AddStyle(EditorStyles.EditorUI.FieldIcon) // .AddStyle(EditorStyles.EditorUI.FieldName) .AddStyle(EditorStyles.EditorUI.FluidFoldout); layoutContainer = templateContainer.Q("LayoutContainer"); header = layoutContainer.Q("Header"); contentLayoutContainer = layoutContainer.Q("ContentLayoutContainer"); content = layoutContainer.Q("ContentContainer"); footer = layoutContainer.Q("Footer"); content.Add(animatedContainer = new FluidAnimatedContainer(false)); tabButton = FluidToggleButtonTab.Get() .SetElementSize(ElementSize.Normal) .SetTabPosition(TabPosition.TabOnTop) .SetIcon(EditorSpriteSheets.EditorUI.Components.CarretRightToDown) .SetAnimationTrigger(IconAnimationTrigger.OnValueChanged); tabButton.SetOnValueChanged(value => schedule.Execute(ValueChanged)); tabButton.Q(nameof(FluidToggleButtonTab.layoutContainer)).RemoveClass("ToggleButton").AddClass(nameof(FluidFoldout)); tabButton.Q(nameof(FluidToggleButtonTab.buttonContainer)).RemoveClass("ToggleButton").AddClass(nameof(FluidFoldout)); tabButton.Q(nameof(FluidToggleButtonTab.icon)).AddClass(nameof(FluidFoldout)); tabButton.iconReaction.SetDuration(expandReactionDuration); header.AddChild(tabButton); colorReaction = Reaction.Get() .SetEditorHeartbeat() .SetSetter(color => footer.SetStyleBackgroundColor(color)) .SetDuration(expandReactionDuration) .SetEase(expandReactionEase); //RESET { ResetContentPadding(); ResetSize(); ResetColors(); } } /// Add elements to this foldout's content container /// Target element public FluidFoldout AddContent(VisualElement element) { animatedContainer.AddContent(element); return this; } public FluidFoldout ClearContent() { animatedContainer.ClearContent(); return this; } /// Add a new PropertyField to this foldout's content container and get a reference to it /// Property field bindingPath public PropertyField AddContentPropertyField(string bindingPath) { PropertyField field = DesignUtils.NewPropertyField(bindingPath); animatedContainer.AddContent(field); return field; } #region Value Changed -> Open / Close private void ValueChanged() { // Debug.Log($"{nameof(FluidFoldout)}.{nameof(ValueChanged)} - isOn:{isOn}"); animatedContainer.Toggle(isOn); colorReaction.PlayToValue(isOn ? GetFooterColor() : defaultContainerColor); } public FluidFoldout Open(bool animateChange = true) { tabButton.SetIsOn(true, animateChange); return this; } public FluidFoldout Close(bool animateChange = true) { tabButton.SetIsOn(false, animateChange); return this; } #endregion #region Icon /// Set custom a custom animated icon /// Icon textures public FluidFoldout SetIcon(IEnumerable textures) { tabButton .SetIcon(textures) .SetAnimationTrigger(IconAnimationTrigger.OnValueChanged); return this; } #endregion #region Label /// Set label text /// Label text public FluidFoldout SetLabelText(string labelText) { tabButton.SetLabelText(labelText); return this; } /// Clear the text and tooltip values from the button's label public FluidFoldout ClearLabelText() { tabButton.ClearLabelText(); return this; } #endregion #region Size /// Set the foldout side /// New size public FluidFoldout SetElementSize(ElementSize value) { tabButton.SetElementSize(value); return this; } /// Reset the foldout to its default value (small) public FluidFoldout ResetSize() => SetElementSize(ElementSize.Small); #endregion #region Color public FluidFoldout SetAccentColor(EditorSelectableColorInfo value) { tabButton.SetToggleAccentColor(value); return this; } public FluidFoldout ResetColors() { SetContainerColor(defaultContainerColor); tabButton.ResetColors(); tabButton.fluidElement.ResetAccentColor(); return this; } public FluidFoldout SetContainerColor(Color color) { containerColor = color; tabButton.SetContainerColorOff(containerColor.WithAlpha(1f)); contentLayoutContainer.SetStyleBorderColor(containerColor.WithAlpha(0.4f)); animatedContainer.fluidContainer.SetStyleBackgroundColor(containerColor.WithAlpha(0.2f)); footer.SetStyleBackgroundColor(containerColor.WithAlpha(1f)); return this; } #endregion #region Padding /// Set the left padding for the foldout's content /// Padding value for the left side public FluidFoldout SetContentLeftPadding(int leftPadding = k_DefaultContentLeftPadding) { animatedContainer.fluidContainer.SetStylePaddingLeft(leftPadding); return this; } /// Reset the foldout's content left padding to its default value (16) public FluidFoldout ResetContentLeftPadding() => SetContentLeftPadding(); /// Set the foldout's content padding values (left, top, right, bottom) /// Padding value for all sides public FluidFoldout SetContentPadding(int padding = k_DefaultContentMinimumPadding) { animatedContainer.fluidContainer.SetStylePadding(padding); return this; } /// Set the foldout's content padding values (left, top, right, bottom) to the minimum value (4) public FluidFoldout RemoveContentPadding() => SetContentPadding(); /// Set the foldout's content padding values (left, top, right, bottom) /// Padding value the left side /// Padding value the top side /// Padding value the right side /// Padding value the bottom side public FluidFoldout SetContentPadding(int left, int top, int right, int bottom) { animatedContainer.fluidContainer.SetStylePadding(left, top, right, bottom); return this; } /// Set the foldout's content padding values (left, top, right, bottom) /// Padding value for all sides public FluidFoldout SetContentPadding(EdgeValues edge) { animatedContainer.fluidContainer.SetStylePadding(edge); return this; } /// Reset the foldout's content padding values (left, top, right, bottom) to their default values (16, 4, 4, 4) public FluidFoldout ResetContentPadding() => SetContentPadding(k_DefaultContentLeftPadding, k_DefaultContentMinimumPadding, k_DefaultContentMinimumPadding, k_DefaultContentMinimumPadding); #endregion } }