/// <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 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> /// 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); }