private void RotationField(SerializedProperty rotationProp, RectTransformData data)
        {
            Rect controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[0]);

            controlRect.x     += 10;
            controlRect.width -= 10;

            EditorGUI.PrefixLabel(controlRect, -1, Styles.transformRotationContent);
            float lblW  = EditorGUIUtility.labelWidth;
            int   ident = EditorGUI.indentLevel;
            Rect  rectX = this.GetColumnRect(controlRect, 0);
            Rect  rectY = this.GetColumnRect(controlRect, 1);
            Rect  rectZ = this.GetColumnRect(controlRect, 2);

            EditorGUIUtility.labelWidth = 13f;
            EditorGUI.indentLevel       = 0;

            EditorGUI.BeginProperty(controlRect, GUIContent.none, rotationProp);
            Vector3 euler = data.Rotation.eulerAngles;

            this.FloatField(rectX, () => data.Rotation.eulerAngles.x, (val) => data.Rotation.eulerAngles = new Vector3(val, euler.y, euler.z), DrivenTransformProperties.Rotation, Styles.X);

            this.FloatField(rectY, () => data.Rotation.eulerAngles.y, (val) => data.Rotation.eulerAngles = new Vector3(euler.x, val, euler.z), DrivenTransformProperties.Rotation, Styles.Y);

            this.FloatField(rectZ, () => data.Rotation.eulerAngles.z, (val) => data.Rotation.eulerAngles = new Vector3(euler.x, euler.y, val), DrivenTransformProperties.Rotation, Styles.Z);

            EditorGUI.EndProperty();

            EditorGUIUtility.labelWidth = lblW;
            EditorGUI.indentLevel       = ident;
        }
        private void ScaleField(SerializedProperty scaleProp, RectTransformData data)
        {
            Rect controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[0]);

            controlRect.x     += 10;
            controlRect.width -= 10;

            EditorGUI.PrefixLabel(controlRect, -1, Styles.transformScaleContent);
            float lblW  = EditorGUIUtility.labelWidth;
            int   ident = EditorGUI.indentLevel;
            Rect  rectX = this.GetColumnRect(controlRect, 0);
            Rect  rectY = this.GetColumnRect(controlRect, 1);
            Rect  rectZ = this.GetColumnRect(controlRect, 2);

            EditorGUIUtility.labelWidth = 13f;
            EditorGUI.indentLevel       = 0;

            EditorGUI.BeginProperty(rectX, Styles.X, scaleProp.FindPropertyRelative("x"));
            this.FloatField(rectX, () => data.Scale.x, (val) => data.Scale.x = val, DrivenTransformProperties.ScaleX, Styles.X);
            EditorGUI.EndProperty();

            EditorGUI.BeginProperty(rectX, Styles.Y, scaleProp.FindPropertyRelative("y"));
            this.FloatField(rectY, () => data.Scale.y, (val) => data.Scale.y = val, DrivenTransformProperties.ScaleY, Styles.Y);
            EditorGUI.EndProperty();


            EditorGUI.BeginProperty(rectX, Styles.Z, scaleProp.FindPropertyRelative("z"));
            this.FloatField(rectZ, () => data.Scale.z, (val) => data.Scale.z = val, DrivenTransformProperties.ScaleZ, Styles.Z);
            EditorGUI.EndProperty();

            EditorGUIUtility.labelWidth = lblW;
            EditorGUI.indentLevel       = ident;
        }
 public ScrollbarData(Scrollbar sb)
 {
     direction      = sb.direction;
     handleRect     = new RectTransformData(sb.handleRect);
     numberOfSteps  = sb.numberOfSteps;
     onValueChanged = sb.onValueChanged;
     size           = sb.size;
     value          = sb.value;
 }
    public BackgroundData(GameObject background)
    {
        name  = background.name;
        rtd   = new RectTransformData(background.GetComponent <RectTransform>());
        image = new ImageData(background.GetComponent <Image>());
        //Fill the connection arrays
        PageElementEventTrigger peet = background.GetComponent <PageElementEventTrigger>();

        connections = XMLSerializationManager.setElementIndexes(peet);
    }
 /// <summary>
 /// Creates a <c>RectPosition</c> from an anchor on a sibling rectangle.
 /// </summary>
 /// <param name="selfAnchor">The normalized point on a rect to anchor to the sibling.</param>
 /// <param name="sibling">The sibling rectangle to anchor to.</param>
 /// <param name="siblingAnchor">The normalized point on the sibling to anchor to.</param>
 /// <param name="offset">The offset in pixels of the <c>selfAnchor</c> to the sibling anchor point.</param>
 /// <returns></returns>
 public static AnchoredPosition FromSiblingAnchor(
     Vector2 selfAnchor,
     RectTransformData sibling,
     Vector2 siblingAnchor,
     Vector2 offset
     ) => new AnchoredPosition(
     ParentPointFromChild(sibling, siblingAnchor),
     selfAnchor,
     sibling.anchoredPosition + offset
     );
        /// <summary>
        /// Get a translated <c>RectTransformData</c> based on the fields in this struct.
        /// </summary>
        /// <param name="rt">The <c>RectTransformData</c> to translate.</param>
        /// <returns></returns>
        public RectTransformData GetRepositioned(RectTransformData rt)
        {
            var del = this.ParentAnchor - ParentPointFromChild(rt, this.ChildAnchor);

            rt.pivot            = this.ChildAnchor;
            rt.anchorMin       += del;
            rt.anchorMax       += del;
            rt.anchoredPosition = this.Offset;
            return(rt);
        }
    public ButtonData(GameObject button)
    {
        name  = button.name;
        rtd   = new RectTransformData(button.GetComponent <RectTransform>());
        image = new ImageData(button.GetComponent <Image>());
        etd   = new EventTriggerData(button.GetComponent <EventTrigger>());
        text  = new TextData(button.GetComponentInChildren <Text>());
        PageElementEventTrigger peet = button.GetComponent <PageElementEventTrigger>();

        connections = XMLSerializationManager.setElementIndexes(peet);
    }
示例#8
0
 void SaveTransformData()
 {
     clippingPanelData = new RectTransformData(ClippingPlane.rectTransform);
     nextPageClipData  = new RectTransformData(NextPageClip.rectTransform);
     shadowData        = new RectTransformData(Shadow.rectTransform);
     shadowLTRData     = new RectTransformData(ShadowLTR.rectTransform);
     leftData          = new RectTransformData(Left.rectTransform);
     leftNextData      = new RectTransformData(LeftNext.rectTransform);
     rightData         = new RectTransformData(Right.rectTransform);
     rightNextData     = new RectTransformData(RightNext.rectTransform);
     isSaveData        = true;
 }
示例#9
0
    public static void CopyFromRectTransformDimensions(this RectTransform rtx, RectTransformData rtd)
    {
        // NOTE: We skip setting the parent because expensive in Unity

        rtx.anchoredPosition = rtd.anchoredPosition;
        rtx.anchorMax        = rtd.anchorMax;
        rtx.anchorMin        = rtd.anchorMin;
        rtx.localRotation    = rtd.localRotation;
        rtx.localScale       = rtd.localScale;
        rtx.pivot            = rtd.pivot;
        rtx.sizeDelta        = rtd.sizeDelta;
        rtx.SetAnchoredPosition3DZ(0.0f);
    }
示例#10
0
        private RectTransformData GetCurrentRectTransform(RectTransform rectTransform)
        {
            RectTransformData rData = new RectTransformData();

            rData.Rotation  = rectTransform.rotation;
            rData.Position  = rectTransform.localPosition;
            rData.Scale     = rectTransform.localScale;
            rData.AnchorMin = rectTransform.anchorMin;
            rData.AnchorMax = rectTransform.anchorMax;
            rData.SizeDelta = rectTransform.sizeDelta;
            rData.Pivot     = rectTransform.pivot;

            return(rData);
        }
        private void SmartPivotField(SerializedProperty pivotProp, RectTransformData data)
        {
            Rect controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[0]);

            controlRect.x     += 10;
            controlRect.width -= 10;

            this.Vector2Field(controlRect,
                              () => data.Pivot.x, (float val) => data.Pivot.x = val,
                              () => data.Pivot.y, (float val) => data.Pivot.y = val,
                              DrivenTransformProperties.PivotX, DrivenTransformProperties.PivotY,
                              pivotProp.FindPropertyRelative("x"), pivotProp.FindPropertyRelative("y"),
                              Styles.pivotContent);
        }
示例#12
0
        /// <summary>
        /// Creates the control canvas group to hold the buttons at the bottom of the menu.
        /// </summary>
        /// <param name="style">The rect describing the size and position of the control pane.</param>
        /// <returns></returns>
        public MenuBuilder CreateControlPane(RectTransformData style)
        {
            var control = new GameObject("Control");

            GameObject.DontDestroyOnLoad(control);
            control.transform.SetParent(this.MenuObject.transform, false);
            // RectTransform
            style.Apply(control.AddComponent <RectTransform>());
            // CanvasGroup
            this.Screen.controls = control.AddComponent <CanvasGroup>();
            // Canvas Renderer
            control.AddComponent <CanvasRenderer>();
            // ZeroAlphaOnStart
            control.AddComponent <ZeroAlphaOnStart>();

            return(this);
        }
    public ScrollAreaData(GameObject scrollArea)
    {
        name = scrollArea.name;

        //SA fields
        rtd_SA   = new RectTransformData(scrollArea.GetComponent <RectTransform>());
        image_SA = new ImageData(scrollArea.GetComponent <Image>());

        //TB fields
        GameObject textBox = scrollArea.transform.GetChild(0).gameObject;

        rtd_TB = new RectTransformData(textBox.GetComponent <RectTransform>());
        srd_TB = new ScrollRectData(textBox.GetComponent <ScrollRect>());

        //SB fields
        GameObject scrollbar = textBox.transform.GetChild(0).gameObject;

        rtd_SB   = new RectTransformData(scrollbar.GetComponent <RectTransform>());
        image_SB = new ImageData(scrollbar.GetComponent <Image>());
        sb_SB    = new ScrollbarData(scrollbar.GetComponent <Scrollbar>());

        //SlA fields
        GameObject slidingArea = scrollbar.transform.GetChild(0).gameObject;

        rtd_SlA = new RectTransformData(slidingArea.GetComponent <RectTransform>());

        //Handle fields
        GameObject handle = slidingArea.transform.GetChild(0).gameObject;

        rtd_H   = new RectTransformData(handle.GetComponent <RectTransform>());
        image_H = new ImageData(handle.GetComponent <Image>());

        //TextFields
        GameObject text = textBox.transform.GetChild(1).gameObject;

        rtd_T  = new RectTransformData(text.GetComponent <RectTransform>());
        text_T = new TextData(text.GetComponent <Text>());

        //EventTrigger fields
        PageElementEventTrigger peet = scrollArea.GetComponent <PageElementEventTrigger>();

        connections = XMLSerializationManager.setElementIndexes(peet);
    }
        private void SmartAnchorFields(SerializedProperty prop, RectTransformData data)
        {
            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * (float)((!anchorExpands[data] ? 1 : 3)));

            controlRect.x     += 10;
            controlRect.width -= 10;

            controlRect.height = EditorGUIUtility.singleLineHeight;
            EditorGUI.BeginChangeCheck();
            this.anchorExpands[data] = EditorGUI.Foldout(controlRect, this.anchorExpands[data], Styles.anchorsContent);

            if (EditorGUI.EndChangeCheck())
            {
                EditorPrefs.SetBool("RectTransformEditor.showAnchorProperties", this.anchorExpands[data]);
            }

            if (!this.anchorExpands[data])
            {
                return;
            }

            EditorGUI.indentLevel = EditorGUI.indentLevel + 1;

            controlRect.y = controlRect.y + EditorGUIUtility.singleLineHeight;
            this.Vector2Field(controlRect,
                              () => data.AnchorMin.x, (float val) => data.AnchorMin.x = val,
                              () => data.AnchorMin.y, (float val) => data.AnchorMin.y = val,
                              DrivenTransformProperties.AnchorMinX, DrivenTransformProperties.AnchorMinY,
                              prop.FindPropertyRelative("AnchorMin").FindPropertyRelative("x"), prop.FindPropertyRelative("AnchorMin").FindPropertyRelative("y"),
                              Styles.anchorMinContent);

            controlRect.y = controlRect.y + EditorGUIUtility.singleLineHeight;
            this.Vector2Field(controlRect,
                              () => data.AnchorMax.x, (float val) => data.AnchorMax.x = val,
                              () => data.AnchorMax.y, (float val) => data.AnchorMax.y = val,
                              DrivenTransformProperties.AnchorMaxX, DrivenTransformProperties.AnchorMaxY,
                              prop.FindPropertyRelative("AnchorMax").FindPropertyRelative("x"), prop.FindPropertyRelative("AnchorMax").FindPropertyRelative("y"),
                              Styles.anchorMaxContent);

            EditorGUI.indentLevel = EditorGUI.indentLevel - 1;
        }
 /// <summary>
 /// Create a MenuBuilder with the default size and position data, but no content or controls.
 /// </summary>
 /// <param name="title">The title to give the menu screen.</param>
 /// <returns>The MenuBuilder object.</returns>
 public static MenuBuilder CreateMenuBuilder(string title)
 {
     return(new MenuBuilder(title)
            .CreateTitle(title, MenuTitleStyle.vanillaStyle)
            .CreateContentPane(RectTransformData.FromSizeAndPos(
                                   new RelVector2(new Vector2(1920f, 903f)),
                                   new AnchoredPosition(
                                       new Vector2(0.5f, 0.5f),
                                       new Vector2(0.5f, 0.5f),
                                       new Vector2(0f, -60f)
                                       )
                                   ))
            .CreateControlPane(RectTransformData.FromSizeAndPos(
                                   new RelVector2(new Vector2(1920f, 259f)),
                                   new AnchoredPosition(
                                       new Vector2(0.5f, 0.5f),
                                       new Vector2(0.5f, 0.5f),
                                       new Vector2(0f, -502f)
                                       )
                                   ))
            .SetDefaultNavGraph(new ChainedNavGraph()));
 }
 // Due to the lifecycle of the UIManager object, The `EditMenus` event has to be used to create custom menus.
 // This event is called every time a UIManager is created,
 // and will also call the added action if the UIManager has already started.
 internal void InitMenuCreation()
 {
     Patch.UIManager.BeforeHideDynamicMenu += ToggleMods;
     Patch.UIManager.EditMenus             += () =>
     {
         ModScreens = new Dictionary <IMod, MenuScreen>();
         var builder = new MenuBuilder("ModListMenu");
         this.screen = builder.Screen;
         builder.CreateTitle("Mods", MenuTitleStyle.vanillaStyle)
         .SetDefaultNavGraph(new ChainedNavGraph())
         .CreateContentPane(RectTransformData.FromSizeAndPos(
                                new RelVector2(new Vector2(1920f, 903f)),
                                new AnchoredPosition(
                                    new Vector2(0.5f, 0.5f),
                                    new Vector2(0.5f, 0.5f),
                                    new Vector2(0f, -60f)
                                    )
                                ))
         .CreateControlPane(RectTransformData.FromSizeAndPos(
                                new RelVector2(new Vector2(1920f, 259f)),
                                new AnchoredPosition(
                                    new Vector2(0.5f, 0.5f),
                                    new Vector2(0.5f, 0.5f),
                                    new Vector2(0f, -502f)
                                    )
                                ))
         .AddContent(
             new NullContentLayout(),
             c => c.AddScrollPaneContent(
                 new ScrollbarConfig
         {
             CancelAction = _ => this.ApplyChanges(),
             Navigation   = new Navigation {
                 mode = Navigation.Mode.Explicit
             },
             Position = new AnchoredPosition
             {
                 ChildAnchor  = new Vector2(0f, 1f),
                 ParentAnchor = new Vector2(1f, 1f),
                 Offset       = new Vector2(-310f, 0f)
             },
             SelectionPadding = _ => (-60, 0)
         },
                 new RelLength(0f),
                 RegularGridLayout.CreateVerticalLayout(105f),
                 c =>
         {
             foreach (var modInst in ModLoader.ModInstances)
             {
                 if (modInst.Error != null)
                 {
                     continue;
                 }
                 ModToggleDelegates?toggleDels = null;
                 if (modInst.Mod is ITogglableMod itmod)
                 {
                     try
                     {
                         if (
                             modInst.Mod is not(
                                 IMenuMod {
                             ToggleButtonInsideMenu: true
                         } or
                                 ICustomMenuMod {
                             ToggleButtonInsideMenu: true
                         }
                                 )
                             )
                         {
                             var rt       = c.ContentObject.GetComponent <RectTransform>();
                             rt.sizeDelta = new Vector2(0f, rt.sizeDelta.y + 105f);
                             c.AddHorizontalOption(
                                 modInst.Name,
                                 new HorizontalOptionConfig
                             {
                                 ApplySetting = (self, ind) =>
                                 {
                                     changedMods[modInst] = ind == 1;
                                 },
                                 CancelAction = _ => this.ApplyChanges(),
                                 Label        = modInst.Name,
                                 Options      = new string[] {
                                     Lang.Get("MOH_OFF", "MainMenu"),
                                     Lang.Get("MOH_ON", "MainMenu")
                                 },
                                 RefreshSetting = (self, apply) => self.optionList.SetOptionTo(
                                     modInst.Enabled ? 1 : 0
                                     ),
                                 Style       = HorizontalOptionStyle.VanillaStyle,
                                 Description = new DescriptionInfo
                                 {
                                     Text = $"v{modInst.Mod.GetVersion()}"
                                 }
                             },
                                 out var opt
                                 );
                             opt.menuSetting.RefreshValueFromGameSettings();
                         }
                         else
                         {
                             toggleDels = new ModToggleDelegates
                             {
                                 SetModEnabled = enabled =>
                                 {
                                     changedMods[modInst] = enabled;
                                 },
                                 GetModEnabled = () => modInst.Enabled,
                                             #pragma warning disable CS0618
                                 // Kept for backwards compatability.
                                 ApplyChange = () => {  }
                                             #pragma warning restore CS0618
                             };
                         }
                     }
                     catch (Exception e)
                     {
                         Logger.APILogger.LogError(e);
                     }
                 }
        /// <summary>
        /// Creates a menu button.
        /// </summary>
        /// <param name="content">The <c>ContentArea</c> to put the button in.</param>
        /// <param name="name">The name of the button game object.</param>
        /// <param name="config">The configuration options for the menu button.</param>
        /// <param name="button">The <c>MenuButton</c> component on the created menu button.</param>
        /// <returns></returns>
        public static ContentArea AddMenuButton(
            this ContentArea content,
            string name,
            MenuButtonConfig config,
            out MenuButton button
            )
        {
            var style = config.Style ?? MenuButtonStyle.VanillaStyle;

            // Option object
            var option = new GameObject($"{name}");

            GameObject.DontDestroyOnLoad(option);
            option.transform.SetParent(content.ContentObject.transform, false);
            // CanvasRenderer
            option.AddComponent <CanvasRenderer>();
            // RectTransform
            var optionRt = option.AddComponent <RectTransform>();

            new RelVector2(new RelLength(0f, 1f), style.Height)
            .GetBaseTransformData()
            .Apply(optionRt);
            content.Layout.ModifyNext(optionRt);
            // MenuButton
            var menuButton = (Patch.MenuButton)option.AddComponent <MenuButton>();

            menuButton.buttonType   = (MenuButton.MenuButtonType)Patch.MenuButton.MenuButtonType.CustomSubmit;
            menuButton.submitAction = config.SubmitAction;
            menuButton.cancelAction = (CancelAction)Patch.CancelAction.CustomCancelAction;
            menuButton.proceed      = config.Proceed;
            ((Patch.MenuSelectable)(MenuSelectable) menuButton).customCancelAction = config.CancelAction;
            content.NavGraph.AddNavigationNode(menuButton);

            // Label object
            var label = new GameObject("Label");

            GameObject.DontDestroyOnLoad(label);
            label.transform.SetParent(option.transform, false);
            // CanvasRenderer
            label.AddComponent <CanvasRenderer>();
            // RectTransform
            var labelRt = label.AddComponent <RectTransform>();

            labelRt.sizeDelta        = new Vector2(0f, 0f);
            labelRt.pivot            = new Vector2(0.5f, 0.5f);
            labelRt.anchorMin        = new Vector2(0f, 0f);
            labelRt.anchorMax        = new Vector2(1f, 1f);
            labelRt.anchoredPosition = new Vector2(0f, 0f);
            // Text
            var labelText = label.AddComponent <Text>();

            labelText.font               = MenuResources.TrajanBold;
            labelText.fontSize           = style.TextSize;
            labelText.resizeTextMaxSize  = style.TextSize;
            labelText.alignment          = TextAnchor.MiddleCenter;
            labelText.text               = config.Label;
            labelText.supportRichText    = true;
            labelText.verticalOverflow   = VerticalWrapMode.Overflow;
            labelText.horizontalOverflow = HorizontalWrapMode.Overflow;
            // FixVerticalAlign
            label.AddComponent <FixVerticalAlign>();
            // ContentSizeFitter
            var labelCsf = label.AddComponent <ContentSizeFitter>();

            labelCsf.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;

            // LeftCursor object
            var cursorL = new GameObject("CursorLeft");

            GameObject.DontDestroyOnLoad(cursorL);
            cursorL.transform.SetParent(label.transform, false);
            // CanvasRenderer
            cursorL.AddComponent <CanvasRenderer>();
            // RectTransform
            var cursorLRt = cursorL.AddComponent <RectTransform>();

            cursorLRt.sizeDelta        = new Vector2(164f, 119f);
            cursorLRt.pivot            = new Vector2(0.5f, 0.5f);
            cursorLRt.anchorMin        = new Vector2(0f, 0.5f);
            cursorLRt.anchorMax        = new Vector2(0f, 0.5f);
            cursorLRt.anchoredPosition = new Vector2(-65f, 0f);
            cursorLRt.localScale       = new Vector3(0.4f, 0.4f, 0.4f);
            // Animator
            var cursorLAnimator = cursorL.AddComponent <Animator>();

            cursorLAnimator.runtimeAnimatorController = MenuResources.MenuCursorAnimator;
            cursorLAnimator.updateMode      = AnimatorUpdateMode.UnscaledTime;
            cursorLAnimator.applyRootMotion = false;
            // Image
            cursorL.AddComponent <Image>();
            // Post Component Config
            menuButton.leftCursor = cursorLAnimator;

            // RightCursor object
            var cursorR = new GameObject("CursorRight");

            GameObject.DontDestroyOnLoad(cursorR);
            cursorR.transform.SetParent(label.transform, false);
            // CanvasRenderer
            cursorR.AddComponent <CanvasRenderer>();
            // RectTransform
            var cursorRRt = cursorR.AddComponent <RectTransform>();

            cursorRRt.sizeDelta        = new Vector2(164f, 119f);
            cursorRRt.pivot            = new Vector2(0.5f, 0.5f);
            cursorRRt.anchorMin        = new Vector2(1f, 0.5f);
            cursorRRt.anchorMax        = new Vector2(1f, 0.5f);
            cursorRRt.anchoredPosition = new Vector2(65f, 0f);
            cursorRRt.localScale       = new Vector3(-0.4f, 0.4f, 0.4f);
            // Animator
            var cursorRAnimator = cursorR.AddComponent <Animator>();

            cursorRAnimator.runtimeAnimatorController = MenuResources.MenuCursorAnimator;
            cursorRAnimator.updateMode      = AnimatorUpdateMode.UnscaledTime;
            cursorRAnimator.applyRootMotion = false;
            // Image
            cursorR.AddComponent <Image>();
            // Post Component Config
            menuButton.rightCursor = cursorRAnimator;

            // FlashEffect object
            var flash = new GameObject("FlashEffect");

            GameObject.DontDestroyOnLoad(flash);
            flash.transform.SetParent(label.transform, false);
            // CanvasRenderer
            flash.AddComponent <CanvasRenderer>();
            // RectTransform
            var flashRt = flash.AddComponent <RectTransform>();

            flashRt.sizeDelta        = new Vector2(0f, 0f);
            flashRt.anchorMin        = new Vector2(0f, 0f);
            flashRt.anchorMax        = new Vector2(1f, 1f);
            flashRt.anchoredPosition = new Vector2(0f, 0f);
            flashRt.pivot            = new Vector2(0.5f, 0.5f);
            // Image
            flash.AddComponent <Image>();
            // Animator
            var flashAnimator = flash.AddComponent <Animator>();

            flashAnimator.runtimeAnimatorController = MenuResources.MenuButtonFlashAnimator;
            flashAnimator.updateMode      = AnimatorUpdateMode.UnscaledTime;
            flashAnimator.applyRootMotion = false;
            // Post Component Config
            menuButton.flashEffect = flashAnimator;

            // Description
            if (config.Description is DescriptionInfo descInfo)
            {
                var descStyle = descInfo.Style ?? DescriptionStyle.MenuButtonSingleLineVanillaStyle;

                var description = new GameObject("Description");
                GameObject.DontDestroyOnLoad(description);
                description.transform.SetParent(option.transform, false);
                // CanvasRenderer
                description.AddComponent <CanvasRenderer>();
                // RectTransform
                var rt = description.AddComponent <RectTransform>();
                RectTransformData.FromSizeAndPos(
                    descStyle.Size,
                    new AnchoredPosition(new Vector2(0.5f, 0), new Vector2(0.5f, 1))
                    ).Apply(rt);
                // Animator
                var anim = description.AddComponent <Animator>();
                anim.runtimeAnimatorController = MenuResources.TextHideShowAnimator;
                anim.updateMode      = AnimatorUpdateMode.UnscaledTime;
                anim.applyRootMotion = false;
                // Text
                var descText = description.AddComponent <Text>();
                descText.font               = MenuResources.Perpetua;
                descText.fontSize           = descStyle.TextSize;
                descText.resizeTextMaxSize  = descStyle.TextSize;
                descText.alignment          = descStyle.TextAnchor;
                descText.text               = descInfo.Text;
                descText.supportRichText    = true;
                descText.verticalOverflow   = VerticalWrapMode.Overflow;
                descText.horizontalOverflow = HorizontalWrapMode.Wrap;
                // Post Component Config
                menuButton.descriptionText = anim;
            }

            button = menuButton;
            return(content);
        }
        /// <summary>
        /// Creates a horizontal option.
        /// </summary>
        /// <param name="content">The <c>ContentArea</c> to put the option in.</param>
        /// <param name="name">The name of the option game object.</param>
        /// <param name="config">The configuration options for the horizontal option.</param>
        /// <param name="horizontalOption">The <c>MenuOptionHorizontal</c> component on the created horizontal option.</param>
        /// <returns></returns>
        public static ContentArea AddHorizontalOption(
            this ContentArea content,
            string name,
            HorizontalOptionConfig config,
            out MenuOptionHorizontal horizontalOption
            )
        {
            var style = config.Style ?? HorizontalOptionStyle.VanillaStyle;

            // Option object
            var option = new GameObject($"{name}");

            GameObject.DontDestroyOnLoad(option);
            option.transform.SetParent(content.ContentObject.transform, false);
            // CanvasRenderer
            option.AddComponent <CanvasRenderer>();
            // RectTransform
            var optionRt = option.AddComponent <RectTransform>();

            style.Size.GetBaseTransformData().Apply(optionRt);
            content.Layout.ModifyNext(optionRt);
            // MenuOptionHorizontal
            var menuOptionHorizontal = option.AddComponent <MenuOptionHorizontal>();

            menuOptionHorizontal.optionList     = config.Options;
            menuOptionHorizontal.applySettingOn = MenuOptionHorizontal.ApplyOnType.Scroll;
            menuOptionHorizontal.cancelAction   = (CancelAction)Patch.CancelAction.CustomCancelAction;
            ((Patch.MenuSelectable)(MenuSelectable) menuOptionHorizontal).customCancelAction = config.CancelAction;
            content.NavGraph.AddNavigationNode(menuOptionHorizontal);
            // MenuSetting
            var menuSetting = (Patch.MenuSetting)option.AddComponent <MenuSetting>();

            menuSetting.settingType          = (MenuSetting.MenuSettingType)Patch.MenuSetting.MenuSettingType.CustomSetting;
            menuSetting.customApplySetting   = config.ApplySetting;
            menuSetting.customRefreshSetting = config.RefreshSetting;
            menuSetting.optionList           = menuOptionHorizontal;
            // Post Component Config
            menuOptionHorizontal.menuSetting = menuSetting;

            // Label object
            var label = new GameObject("Label");

            GameObject.DontDestroyOnLoad(label);
            label.transform.SetParent(option.transform, false);
            // CanvasRenderer
            label.AddComponent <CanvasRenderer>();
            // RectTransform
            var labelRt = label.AddComponent <RectTransform>();

            // the RectTransform that TC uses is utter garbage and imo this make far more sense
            labelRt.sizeDelta        = new Vector2(0f, 0f); // this makes sense if you think about it
            labelRt.pivot            = new Vector2(0.5f, 0.5f);
            labelRt.anchorMin        = new Vector2(0f, 0f);
            labelRt.anchorMax        = new Vector2(1f, 1f);
            labelRt.anchoredPosition = new Vector2(0f, 0f);
            // Text
            var labelText = label.AddComponent <Text>();

            labelText.font               = MenuResources.TrajanBold;
            labelText.fontSize           = style.LabelTextSize;
            labelText.resizeTextMaxSize  = style.LabelTextSize;
            labelText.alignment          = TextAnchor.MiddleLeft;
            labelText.text               = config.Label;
            labelText.supportRichText    = true;
            labelText.verticalOverflow   = VerticalWrapMode.Overflow;
            labelText.horizontalOverflow = HorizontalWrapMode.Overflow;
            // FixVerticalAlign
            label.AddComponent <FixVerticalAlign>();

            // Text object
            var optionText = new GameObject("Text");

            GameObject.DontDestroyOnLoad(optionText);
            optionText.transform.SetParent(option.transform, false);
            // CanvasRenderer
            optionText.AddComponent <CanvasRenderer>();
            // RectTransform
            var optionTextRt = optionText.AddComponent <RectTransform>();

            optionTextRt.sizeDelta        = new Vector2(0f, 0f); // this makes sense if you think about it
            optionTextRt.pivot            = new Vector2(0.5f, 0.5f);
            optionTextRt.anchorMin        = new Vector2(0f, 0f);
            optionTextRt.anchorMax        = new Vector2(1f, 1f);
            optionTextRt.anchoredPosition = new Vector2(0f, 0f);
            // Text
            var optionTextText = optionText.AddComponent <Text>();

            optionTextText.font               = MenuResources.TrajanRegular;
            optionTextText.fontSize           = style.ValueTextSize;
            optionTextText.resizeTextMaxSize  = style.ValueTextSize;
            optionTextText.alignment          = TextAnchor.MiddleRight;
            optionTextText.text               = config.Label;
            optionTextText.supportRichText    = true;
            optionTextText.verticalOverflow   = VerticalWrapMode.Overflow;
            optionTextText.horizontalOverflow = HorizontalWrapMode.Overflow;
            // FixVerticalAlign
            optionText.AddComponent <FixVerticalAlign>();
            // Post Component Config
            menuOptionHorizontal.optionText = optionTextText;

            // LeftCursor object
            var cursorL = new GameObject("CursorLeft");

            GameObject.DontDestroyOnLoad(cursorL);
            cursorL.transform.SetParent(option.transform, false);
            // CanvasRenderer
            cursorL.AddComponent <CanvasRenderer>();
            // RectTransform
            var cursorLRt = cursorL.AddComponent <RectTransform>();

            cursorLRt.sizeDelta        = new Vector2(164f, 119f);
            cursorLRt.pivot            = new Vector2(0.5f, 0.5f);
            cursorLRt.anchorMin        = new Vector2(0f, 0.5f);
            cursorLRt.anchorMax        = new Vector2(0f, 0.5f);
            cursorLRt.anchoredPosition = new Vector2(-70f, 0f);
            cursorLRt.localScale       = new Vector3(0.4f, 0.4f, 0.4f);
            // Animator
            var cursorLAnimator = cursorL.AddComponent <Animator>();

            cursorLAnimator.runtimeAnimatorController = MenuResources.MenuCursorAnimator;
            cursorLAnimator.updateMode      = AnimatorUpdateMode.UnscaledTime;
            cursorLAnimator.applyRootMotion = false;
            // Image
            cursorL.AddComponent <Image>();
            // Post Component Config
            menuOptionHorizontal.leftCursor = cursorLAnimator;

            // RightCursor object
            var cursorR = new GameObject("CursorRight");

            GameObject.DontDestroyOnLoad(cursorR);
            cursorR.transform.SetParent(option.transform, false);
            // CanvasRenderer
            cursorR.AddComponent <CanvasRenderer>();
            // RectTransform
            var cursorRRt = cursorR.AddComponent <RectTransform>();

            cursorRRt.sizeDelta        = new Vector2(164f, 119f);
            cursorRRt.pivot            = new Vector2(0.5f, 0.5f);
            cursorRRt.anchorMin        = new Vector2(1f, 0.5f);
            cursorRRt.anchorMax        = new Vector2(1f, 0.5f);
            cursorRRt.anchoredPosition = new Vector2(70f, 0f);
            cursorRRt.localScale       = new Vector3(-0.4f, 0.4f, 0.4f);
            // Animator
            var cursorRAnimator = cursorR.AddComponent <Animator>();

            cursorRAnimator.runtimeAnimatorController = MenuResources.MenuCursorAnimator;
            cursorRAnimator.updateMode      = AnimatorUpdateMode.UnscaledTime;
            cursorRAnimator.applyRootMotion = false;
            // Image
            cursorR.AddComponent <Image>();
            // Post Component Config
            menuOptionHorizontal.rightCursor = cursorRAnimator;

            // Description
            if (config.Description is DescriptionInfo descInfo)
            {
                var descStyle = descInfo.Style ?? DescriptionStyle.HorizOptionSingleLineVanillaStyle;

                var description = new GameObject("Description");
                GameObject.DontDestroyOnLoad(description);
                description.transform.SetParent(option.transform, false);
                // CanvasRenderer
                description.AddComponent <CanvasRenderer>();
                // RectTransform
                var rt = description.AddComponent <RectTransform>();
                RectTransformData.FromSizeAndPos(
                    descStyle.Size,
                    new AnchoredPosition(new Vector2(0, 0), new Vector2(0, 1), new Vector2(60, 0))
                    ).Apply(rt);
                // Animator
                var anim = description.AddComponent <Animator>();
                anim.runtimeAnimatorController = MenuResources.TextHideShowAnimator;
                anim.updateMode      = AnimatorUpdateMode.UnscaledTime;
                anim.applyRootMotion = false;
                // Text
                var descText = description.AddComponent <Text>();
                descText.font               = MenuResources.Perpetua;
                descText.fontSize           = descStyle.TextSize;
                descText.resizeTextMaxSize  = descStyle.TextSize;
                descText.alignment          = descStyle.TextAnchor;
                descText.text               = descInfo.Text;
                descText.supportRichText    = true;
                descText.verticalOverflow   = VerticalWrapMode.Overflow;
                descText.horizontalOverflow = HorizontalWrapMode.Wrap;
                // Post Component Config
                menuOptionHorizontal.descriptionText = anim;
            }

            horizontalOption = menuOptionHorizontal;
            return(content);
        }
        /// <summary>
        /// Creates a scrollable window.<br/>
        /// The scrolling content will be the same width as the parent.
        /// </summary>
        /// <param name="content">The <c>ContentArea</c> to put the scrollable window in.</param>
        /// <param name="config">The configuration options for the scrollbar.</param>
        /// <param name="contentHeight">The height of the scroll window.</param>
        /// <param name="layout">The layout to apply to the added content.</param>
        /// <param name="action">The action that will get called to add the content.</param>
        /// <param name="scrollContent">The created scrollable window game object.</param>
        /// <param name="scroll">The <c>Scrollbar</c> component on the created scrollbar.</param>
        /// <returns></returns>
        public static ContentArea AddScrollPaneContent(
            this ContentArea content,
            ScrollbarConfig config,
            RelLength contentHeight,
            IContentLayout layout,
            Action <ContentArea> action,
            out GameObject scrollContent,
            out Scrollbar scroll
            )
        {
            // Scrollbar
            content.AddScrollbar(config, out scroll);

            // ScrollMask
            var scrollMask = new GameObject("ScrollMask");

            GameObject.DontDestroyOnLoad(scrollMask);
            scrollMask.transform.SetParent(content.ContentObject.transform, false);
            // RectTransform
            var scrollMaskRt = scrollMask.AddComponent <RectTransform>();

            scrollMaskRt.sizeDelta        = new Vector2(0f, 0f);
            scrollMaskRt.pivot            = new Vector2(0.5f, 0.5f);
            scrollMaskRt.anchorMin        = new Vector2(0f, 0f);
            scrollMaskRt.anchorMax        = new Vector2(1f, 1f);
            scrollMaskRt.anchoredPosition = new Vector2(0f, 0f);
            // CanvasRenderer
            scrollMask.AddComponent <CanvasRenderer>();
            // Mask
            var mask = scrollMask.AddComponent <Mask>();

            mask.showMaskGraphic = false;
            // Image
            var maskImage = scrollMask.AddComponent <Image>();

            maskImage.raycastTarget = false;

            // Scrolling Pane
            var scrollPane = new GameObject("ScrollingPane");

            GameObject.DontDestroyOnLoad(scrollPane);
            scrollPane.transform.SetParent(scrollMask.transform, false);
            // RectTransform
            var scrollPaneRt = scrollPane.AddComponent <RectTransform>();

            RectTransformData.FromSizeAndPos(
                new RelVector2(new RelLength(0f, 1f), contentHeight),
                new AnchoredPosition(new Vector2(0.5f, 1f), new Vector2(0.5f, 1f))
                ).Apply(scrollPaneRt);
            // CanvasRenderer
            scrollPane.AddComponent <CanvasRenderer>();

            action(new ContentArea(
                       scrollPane,
                       layout,
                       new ScrollMovingNavGraph
            {
                Inner               = content.NavGraph,
                Scrollbar           = scroll,
                ScrollPaneTransform = scrollPaneRt,
                SelectionPadding    = config.SelectionPadding ?? (_ => (0, 0))
            }
 /// <summary>
 /// Gets a normalized point on the parent from a normalized point on the child.
 /// </summary>
 /// <param name="child">The child rectangle.</param>
 /// <param name="childPoint">A normalized point on the child.</param>
 /// <returns></returns>
 public static Vector2 ParentPointFromChild(
     RectTransformData child,
     Vector2 childPoint
     ) => child.anchorMin + (child.anchorMax - child.anchorMin) * childPoint;
        void DrawTransformData(string configName, SerializedProperty prop)
        {
            RectTransformData data = prop.GetValue <RectTransformData>();
            bool isCurrent         = locator.CurrentTransformData == data;

            //SerializedProperty localPosition = prop.FindPropertyRelative("LocalPosition");
            //SerializedProperty anchoredPosition = prop.FindPropertyRelative("AnchoredPosition");
            //SerializedProperty sizeDelta = prop.FindPropertyRelative("SizeDelta");
            //SerializedProperty anchorMin = prop.FindPropertyRelative("AnchorMin");
            //SerializedProperty anchorMax = prop.FindPropertyRelative("AnchorMax");
            SerializedProperty pivot    = prop.FindPropertyRelative("Pivot");
            SerializedProperty rotation = prop.FindPropertyRelative("Rotation");
            SerializedProperty scale    = prop.FindPropertyRelative("Scale");

            if (!(anchorExpands.ContainsKey(data)))
            {
                anchorExpands.Add(data, true);
            }

            Rect bounds = EditorGUILayout.BeginVertical("box");


            bool canEdit = !(isCurrent) || !(autoPullFromTransform) || autoPushToTransform;

            EditorGUI.BeginDisabledGroup(isCurrent && autoPullFromTransform);
            if (GUI.Button(new Rect(bounds.position + new Vector2(10, 10), new Vector2(40, 40)), "↓"))
            {
                Undo.RecordObject(locator, "Pull From Rect Transform");
                data.PullFromTransform(locator.transform as RectTransform);
            }
            EditorGUI.EndDisabledGroup();

            EditorGUI.BeginDisabledGroup(!(canEdit) || (isCurrent && autoPushToTransform));
            if (GUI.Button(new Rect(bounds.position + new Vector2(60, 20), new Vector2(40, 40)), "↑"))
            {
                Undo.RecordObject(locator.transform, "Push To Rect Transform");

                data.PushToTransform(locator.transform as RectTransform);
                pauseAutoPushOnce = true;
            }
            EditorGUI.EndDisabledGroup();


            if (!canEdit)
            {
                EditorGUI.BeginDisabledGroup(true);
            }

            SmartPositionAndSizeFields(prop, data);
            SmartAnchorFields(prop, data);
            SmartPivotField(pivot, data);

            EditorGUILayout.Space();

            RotationField(rotation, data);
            ScaleField(scale, data);
            base.serializedObject.ApplyModifiedProperties();


            if (!canEdit)
            {
                EditorGUI.EndDisabledGroup();
            }

            EditorGUILayout.EndVertical();
        }
        private void SmartPositionAndSizeFields(SerializedProperty prop, RectTransformData data)
        {
            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 4f);

            controlRect.height = EditorGUIUtility.singleLineHeight * 2f;


            bool equalAnchorX = data.AnchorMin.x == data.AnchorMax.x;
            bool equalAnchorY = data.AnchorMin.y == data.AnchorMax.y;

            // POS X
            Rect columnRect = this.GetColumnRect(controlRect, 0);

            if (equalAnchorX)
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("AnchoredPosition").FindPropertyRelative("x"));
                this.FloatFieldLabelAbove(columnRect, () => data.AnchoredPosition.x, (float val) => data.AnchoredPosition = new Vector2(val, data.AnchoredPosition.y), DrivenTransformProperties.AnchoredPositionX, new GUIContent("Pos X"));

                //this.SetFadingBasedOnControlID(ref data.ChangingPosX, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
            }
            else
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("AnchoredPosition").FindPropertyRelative("x"));
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("SizeDelta").FindPropertyRelative("x"));

                this.FloatFieldLabelAbove(columnRect, () => data.OffsetMin.x,
                                          (float val) => data.OffsetMin = new Vector2(val, data.OffsetMin.y),
                                          DrivenTransformProperties.None, new GUIContent("Left"));

                //this.SetFadingBasedOnControlID(ref data.ChangingLeft, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
                EditorGUI.EndProperty();
            }

            // POS Y
            columnRect = this.GetColumnRect(controlRect, 1);
            if (equalAnchorY)
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("AnchoredPosition").FindPropertyRelative("y"));
                this.FloatFieldLabelAbove(columnRect, () => data.AnchoredPosition.y, (float val) => data.AnchoredPosition = new Vector2(data.AnchoredPosition.x, val), DrivenTransformProperties.AnchoredPositionY, new GUIContent("Pos Y"));

                //this.SetFadingBasedOnControlID(ref data.ChangingPosY, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
            }
            else
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("AnchoredPosition").FindPropertyRelative("y"));
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("SizeDelta").FindPropertyRelative("y"));
                this.FloatFieldLabelAbove(columnRect, () => - data.OffsetMax.y, (float val) => data.OffsetMax = new Vector2(data.OffsetMax.x, -val), DrivenTransformProperties.None, new GUIContent("Top"));

                //this.SetFadingBasedOnControlID(ref data.ChangingTop, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
                EditorGUI.EndProperty();
            }

            // POS Z
            columnRect = this.GetColumnRect(controlRect, 2);
            EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("LocalPosition.z"));
            this.FloatFieldLabelAbove(columnRect, () => data.LocalPosition.z, (float val) => data.LocalPosition = new Vector3(data.LocalPosition.x, data.LocalPosition.y, val), DrivenTransformProperties.AnchoredPositionZ, Styles.transformPositionZContent);
            EditorGUI.EndProperty();
            controlRect.y = controlRect.y + EditorGUIUtility.singleLineHeight * 2f;

            // Size Delta Width
            columnRect = this.GetColumnRect(controlRect, 0);
            if (equalAnchorX)
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("SizeDelta").FindPropertyRelative("x"));
                this.FloatFieldLabelAbove(columnRect, () => data.SizeDelta.x, (float val) => data.SizeDelta = new Vector2(val, data.SizeDelta.y), DrivenTransformProperties.SizeDeltaX, (equalAnchorX ? new GUIContent("Width") : new GUIContent("W Delta")));

                //this.SetFadingBasedOnControlID(ref data.ChangingWidth, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
            }
            else
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("AnchoredPosition").FindPropertyRelative("x"));
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("SizeDelta").FindPropertyRelative("x"));
                this.FloatFieldLabelAbove(columnRect, () => - data.OffsetMax.x, (float val) => data.OffsetMax = new Vector2(-val, data.OffsetMax.y), DrivenTransformProperties.None, new GUIContent("Right"));

                //this.SetFadingBasedOnControlID(ref data.ChangingRight, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
                EditorGUI.EndProperty();
            }

            // Size Delta Height
            columnRect = this.GetColumnRect(controlRect, 1);
            if (equalAnchorY)
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("SizeDelta").FindPropertyRelative("y"));
                this.FloatFieldLabelAbove(columnRect, () => data.SizeDelta.y, (float val) => data.SizeDelta = new Vector2(data.SizeDelta.x, val), DrivenTransformProperties.SizeDeltaY, (equalAnchorY ? new GUIContent("Height") : new GUIContent("H Delta")));

                //this.SetFadingBasedOnControlID(ref data.ChangingHeight, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
            }
            else
            {
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("AnchoredPosition").FindPropertyRelative("y"));
                EditorGUI.BeginProperty(columnRect, null, prop.FindPropertyRelative("SizeDelta").FindPropertyRelative("y"));
                this.FloatFieldLabelAbove(columnRect, () => data.OffsetMin.y, (float val) => data.OffsetMin = new Vector2(data.OffsetMin.x, val), DrivenTransformProperties.None, new GUIContent("Bottom"));

                //this.SetFadingBasedOnControlID(ref data.ChangingBottom, EditorGUIUtility.s_LastControlID);
                EditorGUI.EndProperty();
                EditorGUI.EndProperty();
            }

            columnRect        = controlRect;
            columnRect.height = EditorGUIUtility.singleLineHeight;
            columnRect.y      = columnRect.y + EditorGUIUtility.singleLineHeight;
            columnRect.yMin   = columnRect.yMin - 2f;
            columnRect.xMin   = columnRect.xMax - 26f;
            columnRect.x      = columnRect.x - columnRect.width;
            //this.BlueprintButton(columnRect);
            //columnRect.x = columnRect.x + columnRect.width;
            //this.RawButton(columnRect);
        }