/// <summary> /// Adds this side screen to the side screen list, and loads the prefabs from the /// Assignable side screen for UI consistency. /// </summary> /// <param name="existing">The existing side screens.</param> /// <param name="parent">The parent where the screens need to be added.</param> internal static void AddSideScreen(IList <DetailsScreen.SideScreenRef> existing, GameObject parent) { bool found = false; // Does it in a custom way because we need to duplicate an existing UI, not make // a new one with PLib foreach (var ssRef in existing) { if (ssRef.screenPrefab is AssignableSideScreen ss) { var newScreen = new DetailsScreen.SideScreenRef(); var ours = CreateScreen(ss); // Reparent to the details screen found = true; newScreen.name = nameof(WorkshopProfileSideScreen); newScreen.screenPrefab = ours; newScreen.screenInstance = ours; // It somehow gets a 0.8x scale var ssTransform = ours.gameObject.transform; ssTransform.SetParent(parent.transform); ssTransform.localScale = Vector3.one; existing.Insert(0, newScreen); // Must break to avoid concurrent modification exception break; } } if (!found) { PUtil.LogWarning("Unable to find Assignable side screen!"); } }
/// <summary> /// Inserts the side screen at the target location. /// </summary> /// <param name="screens">The current list of side screens.</param> /// <param name="newScreen">The screen to insert.</param> /// <param name="targetClassName">The target class name for locating the screen. If this /// class is not found, it will be added at the end regardless of insertBefore.</param> /// <param name="insertBefore">true to insert before that class, or false to insert after.</param> private static void InsertSideScreenContent(IList <SideScreenRef> screens, SideScreenRef newScreen, string targetClassName, bool insertBefore) { if (screens == null) { throw new ArgumentNullException(nameof(screens)); } if (newScreen == null) { throw new ArgumentNullException(nameof(newScreen)); } if (string.IsNullOrEmpty(targetClassName)) { // Add to end by default screens.Add(newScreen); } else { int n = screens.Count; bool found = false; for (int i = 0; i < n; i++) { var screen = screens[i]; var contents = UIDetours.SS_PREFAB.Get(screen)?.GetComponentsInChildren < SideScreenContent>(); if (contents == null || contents.Length < 1) { // Some naughty mod added a prefab with no side screen content! LogUIWarning("Could not find SideScreenContent on side screen: " + screen.name); } else if (contents[0].GetType().FullName == targetClassName) { // Once the first matching screen is found, perform insertion if (insertBefore) { screens.Insert(i, newScreen); } else if (i >= n - 1) { screens.Add(newScreen); } else { screens.Insert(i + 1, newScreen); } found = true; break; } } // Warn if no match found if (!found) { LogUIWarning("No side screen with class name {0} found!".F( targetClassName)); screens.Add(newScreen); } } }
public static void Postfix(GameObject ___sideScreenContentBody, List <DetailsScreen.SideScreenRef> ___sideScreens) { var sideScreen = Gui.Create <ComboSensorSideScreen>(parent: ___sideScreenContentBody); var sideScreenRef = new DetailsScreen.SideScreenRef(); sideScreenRef.name = typeof(ComboSensorSideScreen).Name; sideScreenRef.screenPrefab = sideScreen; sideScreenRef.screenInstance = sideScreen; ___sideScreens.Add(sideScreenRef); }
public static void Postfix() { List <DetailsScreen.SideScreenRef> sideScreens = Traverse.Create(DetailsScreen.Instance).Field("sideScreens").GetValue <List <DetailsScreen.SideScreenRef> >(); GameObject sideScreenContentBody = Traverse.Create(DetailsScreen.Instance).Field("sideScreenContentBody").GetValue <GameObject>(); TestControl controller = new TestControl(); TestSideScreen screen = controller.RootPanel.AddComponent <TestSideScreen>(); screen.gameObject.transform.parent = sideScreenContentBody.transform; DetailsScreen.SideScreenRef myRef = new DetailsScreen.SideScreenRef { name = "TestSideScreen", screenPrefab = screen, offset = new Vector2(0f, 0f), screenInstance = screen }; sideScreens.Add(myRef); }
/// <summary> /// Adds the specified side screen content to the side screen list. The side screen /// behavior should be defined in a class inherited from SideScreenContent. /// /// This method should be used in a postfix on DetailsScreen.OnPrefabInit. /// </summary> /// <typeparam name="T">The type of the controller that will determine how the side /// screen works. A new instance will be created and added as a component to the new /// side screen.</typeparam> /// <param name="targetClassName">The full name of the type of side screen to based to ordering /// around. An example of how this method can be used is: /// `AddSideScreenContentWithOrdering<MySideScreen>(typeof(CapacityControlSideScreen).FullName);` /// `typeof(TargetedSideScreen).FullName` is the suggested value of this parameter. /// Side screens from other mods can be used with their qualified names, even if no /// no reference to their type is available, but the target mod must have added their /// custom side screen to the list first.</param> /// <param name="insertBefore">Whether to insert the new screen before or after the /// target side screen in the list. Defaults to before (true). /// When inserting before the screen, if both are valid for a building then the side /// screen of type "T" will show below the one of type "fullName". When inserting after /// the screen, the reverse is true.</param> /// <param name="uiPrefab">The UI prefab to use. If null is passed, the UI should /// be created and added to the GameObject hosting the controller object in its /// constructor.</param> public static void AddSideScreenContentWithOrdering <T>(string targetClassName, bool insertBefore = true, GameObject uiPrefab = null) where T : SideScreenContent { var inst = DetailsScreen.Instance; if (inst == null) { LogUIWarning("DetailsScreen is not yet initialized, try a postfix on " + "DetailsScreen.OnPrefabInit"); } else { var trInst = Traverse.Create(inst); // These are private fields var screens = trInst.GetField <List <SideScreenRef> >("sideScreens"); var body = trInst.GetField <GameObject>("sideScreenContentBody"); string name = typeof(T).Name; if (body != null && screens != null) { // The ref normally contains a prefab which is instantiated var newScreen = new SideScreenRef(); // Mimic the basic screens var rootObject = PUIElements.CreateUI(body, name); // Preserve the border by fitting the child rootObject.AddComponent <BoxLayoutGroup>().Params = new BoxLayoutParams() { Direction = PanelDirection.Vertical, Alignment = TextAnchor. UpperCenter, Margin = new RectOffset(1, 1, 0, 1) }; var controller = rootObject.AddComponent <T>(); if (uiPrefab != null) { // Add prefab if supplied controller.ContentContainer = uiPrefab; uiPrefab.transform.parent = rootObject.transform; } newScreen.name = name; // Offset is never used newScreen.offset = Vector2.zero; newScreen.screenPrefab = controller; newScreen.screenInstance = controller; InsertSideScreenContent(screens, newScreen, targetClassName, insertBefore); } } }
public static void Postfix() { List <DetailsScreen.SideScreenRef> sideScreens = Traverse.Create(DetailsScreen.Instance).Field("sideScreens").GetValue <List <DetailsScreen.SideScreenRef> >(); GameObject sideScreenContentBody = Traverse.Create(DetailsScreen.Instance).Field("sideScreenContentBody").GetValue <GameObject>(); GridFilterableControl gridSelectControl = new GridFilterableControl(); GridFilterableSideScreen screen = gridSelectControl.RootPanel.AddComponent <GridFilterableSideScreen>(); screen.gameObject.transform.parent = sideScreenContentBody.transform; PUIUtils.DebugObjectTree(sideScreenContentBody); DetailsScreen.SideScreenRef myRef = new DetailsScreen.SideScreenRef { name = "bob", screenPrefab = screen, offset = new Vector2(0f, 0f), screenInstance = screen }; sideScreens.Add(myRef); Console.WriteLine("Postfix patch was called and added in the side screen"); }
/// <summary> /// Adds the specified side screen content to the side screen list. The side screen /// behavior should be defined in a class inherited from SideScreenContent. /// </summary> /// <typeparam name="T">The type of the controller that will determine how the side /// screen works. A new instance will be created and added as a component to the new /// side screen.</typeparam> /// <param name="uiPrefab">The UI prefab to use. If null is passed, the UI should /// be created and added to the GameObject hosting the controller object in its /// OnPrefabInit function.</param> /// <param name="inOrder">If the insertion should be performed in some order. The default of /// false will simply insert at the end of the list which will make this screen appear /// at the top of every side screen it applies to. Set to true to perform insertion /// elsewhere in the list.</param> /// <param name="insertBefore">If the insertion should be done before "insertionName" or /// after "insertionName". True means before, false means after.</param> /// <param name="insertionName">The name of the side screen that you want to choose /// the ordering of this one around. Should be from this list: /// Telepad Side Screen /// Assignable Side Screen /// Valve Side Screen /// Tree Filterable Side Screen /// Single Entity Receptacle Screen /// Planter Side Screen /// SingleSliderScreen /// DualSliderScreen /// Timed Switch Side Screen /// Threshold Switch Side Screen /// Research Side Screen /// Filter Side Screen /// Access Control Side Screen /// ActivationRangeSideScreen /// Gene Shuffler Side Screen /// Sealed Door Side Screen /// Capacity Control Side Screen /// Door Toggle Side Screen /// Suit Locker Side Screen /// Lure Side Screen /// Role Station Side Screen /// Automatable Side Screen /// SingleButtonSideScreen /// IncubatorSideScreen /// IntSliderSideScreen /// SingleCheckboxSideScreen /// CommandModuleSideScreen /// CometDetectorSideScreen /// TelescopeSideScreen /// ComplexFabricatorSideScreen /// MinionSideScreen /// MonumentSideScreen /// Logic Filter Side Screen</param> public static void AddSideScreenContent <T>(GameObject uiPrefab = null, bool inOrder = false, bool insertBefore = true, string insertionName = null) where T : SideScreenContent { var inst = DetailsScreen.Instance; if (inst == null) { LogUIWarning("DetailsScreen is not yet initialized, try a postfix on " + "DetailsScreen.OnPrefabInit"); return; } if (inOrder && insertionName == null) { LogUIWarning("When specifying \"inOrder: true\" the \"insertionName\" must be non-null"); return; } var trInst = Traverse.Create(inst); // These are private fields var ss = trInst.GetField <List <SideScreenRef> >("sideScreens"); var body = trInst.GetField <GameObject>("sideScreenContentBody"); string name = typeof(T).Name; if (ss != null && body != null) { // The ref normally contains a prefab which is instantiated var newScreen = new SideScreenRef(); // Mimic the basic screens var rootObject = new GameObject(name); PUIElements.SetParent(rootObject, body); rootObject.AddComponent <LayoutElement>(); rootObject.AddComponent <VerticalLayoutGroup>(); rootObject.AddComponent <CanvasRenderer>(); var controller = rootObject.AddComponent <T>(); if (uiPrefab != null) { // Add prefab if supplied controller.ContentContainer = uiPrefab; uiPrefab.transform.parent = rootObject.transform; } newScreen.name = name; // Never used newScreen.offset = Vector2.zero; newScreen.screenPrefab = controller; newScreen.screenInstance = controller; if (!inOrder) { ss.Add(newScreen); } else { for (var i = 0; i < ss.Count; i++) { if (ss[i].name.Equals(insertionName)) { if (insertBefore) { ss.Insert(i, newScreen); } else { if (i + 1 >= ss.Count) { ss.Add(newScreen); } else { ss.Insert(i + 1, newScreen); } } return; } } } } }