/// <summary> /// Creates the actual image that shows the checkbox graphically. /// </summary> /// <param name="checkbox">The parent object to add the image.</param> /// <param name="color">The color style for the box border.</param> /// <param name="actualSize">The actual check mark size, which will be updated if it /// is 0x0 to the default size.</param> /// <returns>The image reference to the checkmark image itself.</returns> private Image CreateCheckImage(GameObject checkbox, ColorStyleSetting color, ref Vector2 actualSize) { // Checkbox border (grr rule of only one Graphics per GO...) var checkBorder = PUIElements.CreateUI(checkbox, "CheckBorder"); var borderImg = checkBorder.AddComponent <Image>(); borderImg.sprite = PUITuning.Images.CheckBorder; borderImg.color = color.activeColor; borderImg.type = Image.Type.Sliced; // Checkbox foreground var imageChild = PUIElements.CreateUI(checkbox, "CheckMark", true, PUIAnchoring. Center, PUIAnchoring.Center); var checkImage = imageChild.AddComponent <Image>(); checkImage.sprite = PUITuning.Images.Checked; checkImage.preserveAspect = true; // Determine the checkbox size if (actualSize.x <= 0.0f || actualSize.y <= 0.0f) { var rt = imageChild.rectTransform(); actualSize.x = LayoutUtility.GetPreferredWidth(rt); actualSize.y = LayoutUtility.GetPreferredHeight(rt); } imageChild.SetUISize(CheckSize, false); return(checkImage); }
/// <summary> /// Creates a vertical scroll bar. /// </summary> /// <param name="parent">The parent component.</param> /// <returns>The scroll bar component.</returns> private Scrollbar CreateScrollVert(GameObject parent) { // Outer scrollbar var track = PUIElements.CreateUI(parent, "Scrollbar V", true, PUIAnchoring.End, PUIAnchoring.Stretch); track.AddComponent <Image>().sprite = PUITuning.Images.ScrollBorderVertical; // Scroll track var sb = track.AddComponent <Scrollbar>(); sb.interactable = true; sb.transition = Selectable.Transition.ColorTint; sb.colors = PUITuning.Colors.ScrollbarColors; sb.SetDirection(Scrollbar.Direction.BottomToTop, true); // Sliding area var area = PUIElements.CreateUI(track, "Sliding Area", false); // Handle var handle = PUIElements.CreateUI(area, "Handle", true, PUIAnchoring.Stretch, PUIAnchoring.Beginning); sb.handleRect = handle.rectTransform(); var hImg = handle.AddComponent <Image>(); hImg.sprite = PUITuning.Images.ScrollHandleVertical; sb.targetGraphic = hImg; track.SetActive(true); // Sizing track.SetUISize(new Vector2(TrackSize + 2.0f, TrackSize + 2.0f)); area.SetUISize(new Vector2(TrackSize, TrackSize)); return(sb); }
/// <summary> /// Adds a hot pink rectangle over the target matching its size, to help identify it /// better. /// </summary> /// <param name="parent">The target UI component.</param> public static void AddPinkOverlay(GameObject parent) { var child = PUIElements.CreateUI(parent, "Overlay"); var img = child.AddComponent <Image>(); img.color = new Color(1.0f, 0.0f, 1.0f, 0.2f); }
/// <summary> /// Creates a vertical scroll bar. /// </summary> /// <param name="parent">The parent component.</param> /// <returns>The scroll bar component.</returns> private Scrollbar CreateScrollVert(GameObject parent) { // Outer scrollbar var track = PUIElements.CreateUI(parent, "Scrollbar V", true, PUIAnchoring.End, PUIAnchoring.Stretch); var bg = track.AddComponent <Image>(); bg.sprite = PUITuning.Images.ScrollBorderVertical; bg.type = Image.Type.Sliced; // Scroll track var sb = track.AddComponent <Scrollbar>(); sb.interactable = true; sb.transition = Selectable.Transition.ColorTint; sb.colors = PUITuning.Colors.ScrollbarColors; sb.SetDirection(Scrollbar.Direction.BottomToTop, true); // Handle var handle = PUIElements.CreateUI(track, "Handle", true, PUIAnchoring.Stretch, PUIAnchoring.Beginning); PUIElements.SetAnchorOffsets(handle, 1.0f, 1.0f, 1.0f, 1.0f); sb.handleRect = handle.rectTransform(); var hImg = handle.AddComponent <Image>(); hImg.sprite = PUITuning.Images.ScrollHandleVertical; hImg.type = Image.Type.Sliced; sb.targetGraphic = hImg; track.SetActive(true); PUIElements.SetAnchorOffsets(track, -TrackSize, 0.0f, 2.0f, 2.0f); return(sb); }
public GameObject Build() { var textField = PUIElements.CreateUI(null, Name); // Background var style = TextStyle ?? PUITuning.Fonts.TextLightStyle; textField.AddComponent <Image>().color = style.textColor; // Text box with rectangular clipping area; put pivot in upper left var textArea = PUIElements.CreateUI(textField, "Text Area", false); textArea.rectTransform().pivot = Vector2.up; textArea.AddComponent <Image>().color = BackColor; var mask = textArea.AddComponent <RectMask2D>(); // Scrollable text var textBox = PUIElements.CreateUI(textArea, "Text", true, PUIAnchoring.Beginning, PUIAnchoring.End); // Text to display var textDisplay = textBox.AddComponent <TextMeshProUGUI>(); textDisplay.alignment = TextAlignment; textDisplay.autoSizeTextContainer = false; textDisplay.enabled = true; textDisplay.color = style.textColor; textDisplay.font = style.sdfFont; textDisplay.fontSize = style.fontSize; textDisplay.fontStyle = style.style; // Text field itself textField.SetActive(false); var textEntry = textField.AddComponent <TMP_InputField>(); textEntry.textComponent = textDisplay; textEntry.textViewport = textArea.rectTransform(); textField.SetActive(true); textEntry.text = Text ?? ""; textDisplay.text = Text ?? ""; // Events! ConfigureTextEntry(textEntry); var events = textField.AddComponent <PTextFieldEvents>(); events.OnTextChanged = OnTextChanged; events.OnValidate = OnValidate; // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { textField.AddComponent <ToolTip>().toolTip = ToolTip; } mask.enabled = true; // Lay out, even better than before var layout = textField.AddComponent <RelativeLayoutGroup>(); layout.SetTopEdge(textArea, fraction: 1.0f).SetBottomEdge(textArea, fraction: 0.0f). SetMargin(textArea, new RectOffset(1, 1, 1, 1)).OverrideSize(textArea, new Vector2(MinWidth, Math.Max(LineCount, 1) * PUIUtils.GetLineHeight(style))). LockLayout(); layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; OnRealize?.Invoke(textField); return(textField); }
public GameObject Build() { var toggle = PUIElements.CreateUI(null, Name); // Set on click event var kToggle = toggle.AddComponent <KToggle>(); var evt = OnStateChanged; if (evt != null) { kToggle.onValueChanged += (on) => { evt?.Invoke(toggle, on); } } ; kToggle.artExtension = new KToggleArtExtensions(); kToggle.soundPlayer = PUITuning.ToggleSounds; // Background image var fgImage = toggle.AddComponent <KImage>(); fgImage.color = Color.activeColor; fgImage.sprite = InactiveSprite; toggle.SetActive(false); // Toggled images var toggleImage = toggle.AddComponent <ImageToggleState>(); toggleImage.TargetImage = fgImage; toggleImage.useSprites = true; toggleImage.InactiveSprite = InactiveSprite; toggleImage.ActiveSprite = ActiveSprite; toggleImage.startingState = InitialState ? ImageToggleState.State.Active : ImageToggleState.State.Inactive; toggleImage.useStartingState = true; toggleImage.ActiveColour = Color.activeColor; toggleImage.DisabledActiveColour = Color.disabledActiveColor; toggleImage.InactiveColour = Color.inactiveColor; toggleImage.DisabledColour = Color.disabledColor; toggleImage.HoverColour = Color.hoverColor; toggleImage.DisabledHoverColor = Color.disabledhoverColor; kToggle.isOn = InitialState; toggle.SetActive(true); // Set size if (Size.x > 0.0f && Size.y > 0.0f) { toggle.SetUISize(Size, true); } else { PUIElements.AddSizeFitter(toggle, DynamicSize); } // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { toggle.AddComponent <ToolTip>().toolTip = ToolTip; } toggle.SetFlexUISize(FlexSize).SetActive(true); OnRealize?.Invoke(toggle); return(toggle); }
public GameObject Build() { var textField = PUIElements.CreateUI(null, Name); // Background var style = TextStyle ?? PUITuning.Fonts.TextLightStyle; textField.AddComponent <Image>().color = style.textColor; // Text box with rectangular clipping area var textArea = PUIElements.CreateUI(textField, "Text Area", false); textArea.AddComponent <Image>().color = BackColor; var mask = textArea.AddComponent <RectMask2D>(); // Scrollable text var textBox = PUIElements.CreateUI(textArea, "Text"); // Text to display var textDisplay = textBox.AddComponent <TextMeshProUGUI>(); textDisplay.alignment = TextAlignment; textDisplay.autoSizeTextContainer = false; textDisplay.enabled = true; textDisplay.color = style.textColor; textDisplay.font = style.sdfFont; textDisplay.fontSize = style.fontSize; textDisplay.fontStyle = style.style; textDisplay.maxVisibleLines = 1; // Text field itself textField.SetActive(false); var textEntry = textField.AddComponent <TMP_InputField>(); textEntry.textComponent = textDisplay; textEntry.textViewport = textArea.rectTransform(); textField.SetActive(true); textEntry.text = Text ?? ""; textDisplay.text = Text ?? ""; // Events! ConfigureTextEntry(textEntry); var events = textField.AddComponent <PTextFieldEvents>(); events.OnTextChanged = OnTextChanged; events.OnValidate = OnValidate; // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { textField.AddComponent <ToolTip>().toolTip = ToolTip; } mask.enabled = true; // Lay out, even better than before var tbTransform = textBox.rectTransform(); LayoutRebuilder.ForceRebuildLayoutImmediate(tbTransform); new RelativeLayout(textField).SetTopEdge(textArea, fraction: 1.0f).SetBottomEdge( textArea, fraction: 0.0f).SetMargin(textArea, new RectOffset(1, 1, 1, 1)). OverrideSize(textArea, new Vector2(MinWidth, LayoutUtility.GetPreferredHeight( tbTransform))).Execute(true); textField.SetFlexUISize(FlexSize); OnRealize?.Invoke(textField); return(textField); }
public GameObject Build() { var textField = PUIElements.CreateUI(null, Name); // Background var style = TextStyle ?? PUITuning.Fonts.TextLightStyle; textField.AddComponent <Image>().color = style.textColor; // Text box with rectangular clipping area var textArea = PUIElements.CreateUI(textField, "Text Area", false); textArea.AddComponent <Image>().color = BackColor; var mask = textArea.AddComponent <RectMask2D>(); // Scrollable text var textBox = PUIElements.CreateUI(textArea, "Text"); // Text to display var textDisplay = textBox.AddComponent <TextMeshProUGUI>(); textDisplay.alignment = TextAlignment; textDisplay.autoSizeTextContainer = false; textDisplay.enabled = true; textDisplay.color = style.textColor; textDisplay.font = style.sdfFont; textDisplay.fontSize = style.fontSize; textDisplay.fontStyle = style.style; textDisplay.maxVisibleLines = 1; // Text field itself textField.SetActive(false); var textEntry = textField.AddComponent <TMP_InputField>(); textEntry.textComponent = textDisplay; textEntry.textViewport = textArea.rectTransform(); textField.SetActive(true); textEntry.text = Text ?? ""; textDisplay.text = Text ?? ""; // Events! ConfigureTextEntry(textEntry); var events = textField.AddComponent <PTextFieldEvents>(); events.OnTextChanged = OnTextChanged; events.OnValidate = OnValidate; // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { textField.AddComponent <ToolTip>().toolTip = ToolTip; } mask.enabled = true; // Lay out - TMP_InputField does not support auto layout so we have to do a hack var rt = textBox.rectTransform(); float height = LayoutUtility.GetPreferredHeight(rt); textField.SetUISize(new Vector2(MinWidth + 2.0f, height + 2.0f), true). SetFlexUISize(FlexSize); textArea.SetUISize(new Vector2(MinWidth, height)); OnRealize?.Invoke(textField); return(textField); }
public GameObject Build() { var toggle = PUIElements.CreateUI(null, Name); // Set on click event var kToggle = toggle.AddComponent <KToggle>(); var evt = OnStateChanged; if (evt != null) { kToggle.onValueChanged += (on) => { evt?.Invoke(toggle, on); } } ; UIDetours.ART_EXTENSION.Set(kToggle, new KToggleArtExtensions()); UIDetours.SOUND_PLAYER_TOGGLE.Set(kToggle, PUITuning.ToggleSounds); // Background image var fgImage = toggle.AddComponent <Image>(); fgImage.color = Color.activeColor; fgImage.sprite = InactiveSprite; toggle.SetActive(false); // Toggled images var toggleImage = toggle.AddComponent <ImageToggleState>(); toggleImage.TargetImage = fgImage; toggleImage.useSprites = true; toggleImage.InactiveSprite = InactiveSprite; toggleImage.ActiveSprite = ActiveSprite; toggleImage.startingState = InitialState ? ImageToggleState.State.Active : ImageToggleState.State.Inactive; toggleImage.useStartingState = true; toggleImage.ActiveColour = Color.activeColor; toggleImage.DisabledActiveColour = Color.disabledActiveColor; toggleImage.InactiveColour = Color.inactiveColor; toggleImage.DisabledColour = Color.disabledColor; toggleImage.HoverColour = Color.hoverColor; toggleImage.DisabledHoverColor = Color.disabledhoverColor; UIDetours.IS_ON.Set(kToggle, InitialState); toggle.SetActive(true); // Set size if (Size.x > 0.0f && Size.y > 0.0f) { toggle.SetUISize(Size, true); } else { PUIElements.AddSizeFitter(toggle, DynamicSize); } // Add tooltip PUIElements.SetToolTip(toggle, ToolTip).SetFlexUISize(FlexSize).SetActive(true); OnRealize?.Invoke(toggle); return(toggle); }
/// <summary> /// Shared routine to spawn UI text objects. /// </summary> /// <param name="parent">The parent object for the text.</param> /// <param name="style">The text style to use.</param> /// <param name="contents">The default text.</param> /// <returns>The child text object.</returns> protected static LocText TextChildHelper(GameObject parent, TextStyleSetting style, string contents = "") { var textChild = PUIElements.CreateUI(parent, "Text"); var locText = PUIElements.AddLocText(textChild, style); // Font needs to be set before the text locText.alignment = TMPro.TextAlignmentOptions.Center; locText.text = contents; return(locText); }
public GameObject Build() { var textField = PUIElements.CreateUI(null, Name); var style = TextStyle ?? PUITuning.Fonts.TextLightStyle; // Background var border = textField.AddComponent <Image>(); border.sprite = PUITuning.Images.BoxBorderWhite; border.type = Image.Type.Sliced; border.color = style.textColor; // Text box with rectangular clipping area; put pivot in upper left var textArea = PUIElements.CreateUI(textField, "Text Area", false); textArea.AddComponent <Image>().color = BackColor; var mask = textArea.AddComponent <RectMask2D>(); // Scrollable text var textBox = PUIElements.CreateUI(textArea, "Text"); // Text to display var textDisplay = PTextField.ConfigureField(textBox.AddComponent <TextMeshProUGUI>(), style, TextAlignment); textDisplay.enableWordWrapping = true; textDisplay.raycastTarget = true; // Text field itself textField.SetActive(false); var textEntry = textField.AddComponent <TMP_InputField>(); textEntry.textComponent = textDisplay; textEntry.textViewport = textArea.rectTransform(); textEntry.text = Text ?? ""; textDisplay.text = Text ?? ""; // Events! ConfigureTextEntry(textEntry); var events = textField.AddComponent <PTextFieldEvents>(); events.OnTextChanged = OnTextChanged; events.OnValidate = OnValidate; events.TextObject = textBox; // Add tooltip PUIElements.SetToolTip(textField, ToolTip); mask.enabled = true; PUIElements.SetAnchorOffsets(textBox, new RectOffset()); textField.SetActive(true); // Lay out var layout = PUIUtils.InsetChild(textField, textArea, Vector2.one, new Vector2( MinWidth, Math.Max(LineCount, 1) * PUIUtils.GetLineHeight(style))). AddOrGet <LayoutElement>(); layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; OnRealize?.Invoke(textField); return(textField); }
public override GameObject Build() { var button = PUIElements.CreateUI(null, Name); GameObject sprite = null, text = null; // Background var bgImage = button.AddComponent <KImage>(); bgImage.colorStyleSetting = Color ?? PUITuning.Colors.ButtonPinkStyle; SetupButtonBackground(bgImage); // Set on click event var kButton = button.AddComponent <KButton>(); var evt = OnClick; if (evt != null) { kButton.onClick += () => evt?.Invoke(button); } SetupButton(kButton, bgImage); // Add foreground image since the background already has one if (Sprite != null) { var fgImage = ImageChildHelper(button, this); kButton.fgImage = fgImage; sprite = fgImage.gameObject; } // Add text if (!string.IsNullOrEmpty(Text)) { text = TextChildHelper(button, TextStyle ?? PUITuning.Fonts.UILightStyle, Text).gameObject; } // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { button.AddComponent <ToolTip>().toolTip = ToolTip; } button.SetActive(true); // Arrange the icon and text var layout = button.AddComponent <RelativeLayoutGroup>(); layout.Margin = Margin; GameObject inner; ArrangeComponent(layout, inner = WrapTextAndSprite(text, sprite), TextAlignment); if (!DynamicSize) { layout.LockLayout(); } layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; InvokeRealize(button); return(button); }
public override GameObject Build() { var button = PUIElements.CreateUI(null, Name); GameObject sprite = null, text = null; // Background var bgImage = button.AddComponent <KImage>(); var bgColorStyle = Color ?? PUITuning.Colors.ButtonPinkStyle; UIDetours.COLOR_STYLE_SETTING.Set(bgImage, bgColorStyle); SetupButtonBackground(bgImage); // Set on click event var kButton = button.AddComponent <KButton>(); var evt = OnClick; if (evt != null) { // Detouring an Event is not worth the effort kButton.onClick += () => evt?.Invoke(button); } SetupButton(kButton, bgImage); // Add foreground image since the background already has one if (Sprite != null) { var fgImage = ImageChildHelper(button, this); UIDetours.FG_IMAGE.Set(kButton, fgImage); sprite = fgImage.gameObject; } // Add text if (!string.IsNullOrEmpty(Text)) { text = TextChildHelper(button, TextStyle ?? PUITuning.Fonts.UILightStyle, Text).gameObject; } // Add tooltip PUIElements.SetToolTip(button, ToolTip).SetActive(true); // Arrange the icon and text var layout = button.AddComponent <RelativeLayoutGroup>(); layout.Margin = Margin; GameObject inner; ArrangeComponent(layout, inner = WrapTextAndSprite(text, sprite), TextAlignment); if (!DynamicSize) { layout.LockLayout(); } layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; DestroyLayoutIfPossible(button); InvokeRealize(button); return(button); }
public GameObject Build() { if (Child == null) { throw new InvalidOperationException("No child component"); } var pane = PUIElements.CreateUI(null, Name); if (BackColor.a > 0.0f) { pane.AddComponent <Image>().color = BackColor; } // Scroll pane itself var scroll = pane.AddComponent <KScrollRect>(); scroll.horizontal = ScrollHorizontal; scroll.vertical = ScrollVertical; // Viewport var viewport = PUIElements.CreateUI(pane, "Viewport", true, PUIAnchoring.Stretch, PUIAnchoring.Stretch); viewport.AddComponent <RectMask2D>().enabled = true; // BoxLayoutGroup resizes the viewport which is undesired, we only want to pass // an auto-layout request on to the children viewport.AddComponent <VerticalLayoutGroup>().childAlignment = TextAnchor.UpperLeft; scroll.viewport = viewport.rectTransform(); // Make the child; give it a separate Canvas to reduce layout rebuilds var child = Child.Build(); child.AddOrGet <Canvas>().pixelPerfect = false; child.AddOrGet <GraphicRaycaster>(); PUIElements.SetAnchors(PUIElements.SetParent(child, viewport), PUIAnchoring. Beginning, PUIAnchoring.End); scroll.content = child.rectTransform(); pane.SetActive(true); // Vertical scrollbar if (ScrollVertical) { scroll.verticalScrollbar = CreateScrollVert(pane); scroll.verticalScrollbarVisibility = AlwaysShowVertical ? ScrollRect. ScrollbarVisibility.Permanent : ScrollRect.ScrollbarVisibility.AutoHide; } if (ScrollHorizontal) { scroll.horizontalScrollbar = CreateScrollHoriz(pane); scroll.horizontalScrollbarVisibility = AlwaysShowHorizontal ? ScrollRect. ScrollbarVisibility.Permanent : ScrollRect.ScrollbarVisibility.AutoHide; } pane.SetFlexUISize(FlexSize); OnRealize?.Invoke(pane); return(pane); }
/// <summary> /// Builds the actual scroll pane object. /// </summary> /// <param name="parent">The parent of this scroll pane.</param> /// <param name="child">The child element of this scroll pane.</param> /// <returns>The realized scroll pane.</returns> internal GameObject BuildScrollPane(GameObject parent, GameObject child) { var pane = PUIElements.CreateUI(parent, Name); if (BackColor.a > 0.0f) { pane.AddComponent <Image>().color = BackColor; } pane.SetActive(false); // Scroll pane itself var scroll = pane.AddComponent <KScrollRect>(); scroll.horizontal = ScrollHorizontal; scroll.vertical = ScrollVertical; // Viewport var viewport = PUIElements.CreateUI(pane, "Viewport"); viewport.rectTransform().pivot = Vector2.up; viewport.AddComponent <RectMask2D>().enabled = true; viewport.AddComponent <ViewportLayoutGroup>(); scroll.viewport = viewport.rectTransform(); // Give the Child a separate Canvas to reduce layout rebuilds child.AddOrGet <Canvas>().pixelPerfect = false; child.AddOrGet <GraphicRaycaster>(); PUIElements.SetAnchors(child.SetParent(viewport), PUIAnchoring.Beginning, PUIAnchoring.End); scroll.content = child.rectTransform(); // Vertical scrollbar if (ScrollVertical) { scroll.verticalScrollbar = CreateScrollVert(pane); scroll.verticalScrollbarVisibility = AlwaysShowVertical ? ScrollRect. ScrollbarVisibility.Permanent : ScrollRect.ScrollbarVisibility. AutoHideAndExpandViewport; } if (ScrollHorizontal) { scroll.horizontalScrollbar = CreateScrollHoriz(pane); scroll.horizontalScrollbarVisibility = AlwaysShowHorizontal ? ScrollRect. ScrollbarVisibility.Permanent : ScrollRect.ScrollbarVisibility. AutoHideAndExpandViewport; } pane.SetActive(true); // Custom layout to pass child sizes to the scroll pane var layout = pane.AddComponent <PScrollPaneLayout>(); layout.flexibleHeight = FlexSize.y; layout.flexibleWidth = FlexSize.x; return(pane); }
/// <summary> /// Shared routine to spawn UI image objects. /// </summary> /// <param name="parent">The parent object for the image.</param> /// <param name="settings">The settings to use for displaying the image.</param> /// <returns>The child image object.</returns> protected static Image ImageChildHelper(GameObject parent, PTextComponent settings) { var imageChild = PUIElements.CreateUI(parent, "Image", true, PUIAnchoring.Beginning, PUIAnchoring.Beginning); var rt = imageChild.rectTransform(); // The pivot is important here rt.pivot = CENTER; var img = imageChild.AddComponent <Image>(); img.color = settings.SpriteTint; img.sprite = settings.Sprite; img.type = settings.SpriteMode; img.preserveAspect = settings.MaintainSpriteAspect; // Set up transform var scale = Vector3.one; float rot = 0.0f; var rotate = settings.SpriteTransform; if ((rotate & ImageTransform.FlipHorizontal) != ImageTransform.None) { scale.x = -1.0f; } if ((rotate & ImageTransform.FlipVertical) != ImageTransform.None) { scale.y = -1.0f; } if ((rotate & ImageTransform.Rotate90) != ImageTransform.None) { rot = 90.0f; } if ((rotate & ImageTransform.Rotate180) != ImageTransform.None) { rot += 180.0f; } // Update transform var transform = imageChild.rectTransform(); transform.localScale = scale; transform.Rotate(new Vector3(0.0f, 0.0f, rot)); // Limit size if needed var imageSize = settings.SpriteSize; if (imageSize.x > 0.0f && imageSize.y > 0.0f) { imageChild.SetUISize(imageSize, true); } return(img); }
/// <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); } } }
/// <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 screens = UIDetours.SIDE_SCREENS.Get(inst); var body = UIDetours.SS_CONTENT_BODY.Get(inst); string name = typeof(T).Name; if (body != null && screens != null) { // The ref normally contains a prefab which is instantiated var newScreen = new DetailsScreen.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 UIDetours.SS_CONTENT_CONTAINER.Set(controller, uiPrefab); uiPrefab.transform.parent = rootObject.transform; } newScreen.name = name; // Offset is never used UIDetours.SS_OFFSET.Set(newScreen, Vector2.zero); UIDetours.SS_PREFAB.Set(newScreen, controller); UIDetours.SS_INSTANCE.Set(newScreen, controller); InsertSideScreenContent(screens, newScreen, targetClassName, insertBefore); } } }
public override GameObject Build() { var panel = PUIElements.CreateUI(null, Name); var mapping = DictionaryPool <IUIComponent, GameObject, PRelativePanel> .Allocate(); SetImage(panel); // Realize each component and add them to the panel foreach (var pair in constraints) { var component = pair.Key; var realized = component.Build(); realized.SetParent(panel); // We were already guaranteed that there were no duplicate keys mapping[component] = realized; } // Add layout component var layout = panel.AddComponent <RelativeLayoutGroup>(); layout.Margin = Margin; foreach (var pair in constraints) { var realized = mapping[pair.Key]; var rawParams = pair.Value; var newParams = new RelativeLayoutParams(); // Copy all of the settings Resolve(newParams.TopEdge, rawParams.TopEdge, mapping); Resolve(newParams.BottomEdge, rawParams.BottomEdge, mapping); Resolve(newParams.LeftEdge, rawParams.LeftEdge, mapping); Resolve(newParams.RightEdge, rawParams.RightEdge, mapping); newParams.OverrideSize = rawParams.OverrideSize; newParams.Insets = rawParams.Insets; layout.SetRaw(realized, newParams); } if (!DynamicSize) { layout.LockLayout(); } mapping.Recycle(); // Set flex size layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; InvokeRealize(panel); return(panel); }
public override GameObject Build() { var label = PUIElements.CreateUI(null, Name); // Background if (BackColor.a > 0) { label.AddComponent <Image>().color = BackColor; } // Add foreground image if (Sprite != null) { ImageChildHelper(label, this); } // Add text if (!string.IsNullOrEmpty(Text)) { TextChildHelper(label, TextStyle ?? PUITuning.Fonts.UILightStyle, Text); } // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { label.AddComponent <ToolTip>().toolTip = ToolTip; } label.SetActive(true); // Icon and text are side by side var lp = new BoxLayoutParams() { Spacing = IconSpacing, Direction = PanelDirection.Horizontal, Margin = Margin, Alignment = TextAlignment }; if (DynamicSize) { label.AddComponent <BoxLayoutGroup>().Params = lp; } else { BoxLayoutGroup.LayoutNow(label, lp); } label.SetFlexUISize(FlexSize); InvokeRealize(label); return(label); }
public override GameObject Build() { var label = PUIElements.CreateUI(null, Name); GameObject sprite = null, text = null; // Background if (BackColor.a > 0) { label.AddComponent <Image>().color = BackColor; } // Add foreground image if (Sprite != null) { sprite = ImageChildHelper(label, this).gameObject; } // Add text if (!string.IsNullOrEmpty(Text)) { text = TextChildHelper(label, TextStyle ?? PUITuning.Fonts.UILightStyle, Text).gameObject; } // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { label.AddComponent <ToolTip>().toolTip = ToolTip; } label.SetActive(true); // Arrange the icon and text var layout = label.AddComponent <RelativeLayoutGroup>(); layout.Margin = Margin; ArrangeComponent(layout, WrapTextAndSprite(text, sprite), TextAlignment); if (!DynamicSize) { layout.LockLayout(); } layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; InvokeRealize(label); return(label); }
/// <summary> /// Shared routine to spawn UI image objects. /// </summary> /// <param name="parent">The parent object for the image.</param> /// <param name="sprite">The sprite to display.</param> /// <param name="rotate">How to rotate or flip the sprite.</param> /// <param name="imageSize">The size to which to scale the sprite.</param> /// <returns>The child image object.</returns> protected static Image ImageChildHelper(GameObject parent, Sprite sprite, ImageTransform rotate = ImageTransform.None, Vector2 imageSize = default) { var imageChild = PUIElements.CreateUI("Image"); var img = imageChild.AddComponent <Image>(); PUIElements.SetParent(imageChild, parent); img.sprite = sprite; img.preserveAspect = true; // Set up transform var scale = Vector3.one; float rot = 0.0f; if ((rotate & ImageTransform.FlipHorizontal) != ImageTransform.None) { scale.x = -1.0f; } if ((rotate & ImageTransform.FlipVertical) != ImageTransform.None) { scale.y = -1.0f; } if ((rotate & ImageTransform.Rotate90) != ImageTransform.None) { rot = 90.0f; } if ((rotate & ImageTransform.Rotate180) != ImageTransform.None) { rot += 180.0f; } // Update transform var transform = imageChild.rectTransform(); transform.localScale = scale; transform.Rotate(new Vector3(0.0f, 0.0f, rot)); // Limit size if needed if (imageSize.x > 0.0f && imageSize.y > 0.0f) { PUIElements.SetSizeImmediate(imageChild, imageSize); } return(img); }
public GameObject Build() { if (Parent == null) { throw new InvalidOperationException("Parent for dialog may not be null"); } var dialog = PUIElements.CreateUI(Parent, Name); var dComponent = dialog.AddComponent <PDialogComp>(); // Background dialog.AddComponent <Canvas>(); dialog.AddComponent <GraphicRaycaster>(); var bg = dialog.AddComponent <Image>(); bg.color = DialogBackColor; bg.sprite = PUITuning.Images.BoxBorder; bg.type = Image.Type.Sliced; // Add each component var layout = dialog.AddComponent <PGridLayoutGroup>(); layout.AddRow(new GridRowSpec()); layout.AddRow(new GridRowSpec(flex: 1.0f)); layout.AddRow(new GridRowSpec()); layout.AddColumn(new GridColumnSpec(flex: 1.0f)); layout.AddColumn(new GridColumnSpec()); LayoutTitle(layout, dComponent.DoButton); layout.AddComponent(Body.Build(), new GridComponentSpec(1, 0) { ColumnSpan = 2, Margin = new RectOffset(10, 10, 10, 10) }); CreateUserButtons(layout, dComponent.DoButton); // Configure body position SetDialogSize(dialog); // Dialog is realized dComponent.dialog = this; dComponent.sortKey = SortKey; OnRealize?.Invoke(dialog); return(dialog); }
public override GameObject Build() { if (Columns < 1) { throw new InvalidOperationException("At least one column must be defined"); } if (Rows < 1) { throw new InvalidOperationException("At least one row must be defined"); } var panel = PUIElements.CreateUI(null, Name); SetImage(panel); // Add layout component var layout = panel.AddComponent <PGridLayoutGroup>(); layout.Margin = Margin; foreach (var column in columns) { layout.AddColumn(column); } foreach (var row in rows) { layout.AddRow(row); } // Add children foreach (var child in children) { layout.AddComponent(child.Item.Build(), child); } if (!DynamicSize) { layout.LockLayout(); } layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; InvokeRealize(panel); return(panel); }
/// <summary> /// Builds a row selection prefab object for this combo box. /// </summary> /// <param name="style">The text style for the entries.</param> /// <param name="entryColor">The color for the entry backgrounds.</param> /// <returns>A template for each row in the dropdown.</returns> private GameObject BuildRowPrefab(TextStyleSetting style, ColorStyleSetting entryColor) { var im = ItemMargin; var rowPrefab = PUIElements.CreateUI(null, "RowEntry"); // Background of the entry var bgImage = rowPrefab.AddComponent <KImage>(); UIDetours.COLOR_STYLE_SETTING.Set(bgImage, entryColor); UIDetours.APPLY_COLOR_STYLE.Invoke(bgImage); // Checkmark for the front of the entry var isSelected = PUIElements.CreateUI(rowPrefab, "Selected"); var fgImage = isSelected.AddComponent <Image>(); fgImage.color = style.textColor; fgImage.preserveAspect = true; fgImage.sprite = PUITuning.Images.Checked; // Button for the entry to select it var entryButton = rowPrefab.AddComponent <KButton>(); PButton.SetupButton(entryButton, bgImage); UIDetours.FG_IMAGE.Set(entryButton, fgImage); // Tooltip for the entry rowPrefab.AddComponent <ToolTip>(); // Text for the entry var textContainer = PUIElements.CreateUI(rowPrefab, "Text"); PUIElements.AddLocText(textContainer, style).SetText(" "); // Configure the entire layout in 1 statement! (jk this is awful) var group = rowPrefab.AddComponent <RelativeLayoutGroup>(); group.AnchorYAxis(isSelected).OverrideSize(isSelected, CheckSize).SetLeftEdge( isSelected, fraction: 0.0f).SetMargin(isSelected, im).AnchorYAxis( textContainer).SetLeftEdge(textContainer, toRight: isSelected).SetRightEdge( textContainer, 1.0f).SetMargin(textContainer, new RectOffset(0, im.right, im.top, im.bottom)).LockLayout(); rowPrefab.SetActive(false); return(rowPrefab); }
public GameObject Build() { var panel = PUIElements.CreateUI(Name); if (BackColor.a > 0.0f) { panel.AddComponent <Image>().color = BackColor; } panel.layer = LayerMask.NameToLayer("UI"); // Add children foreach (var child in children) { PUIElements.SetParent(child.Build(), panel); } // Add layout component var args = new BoxLayoutParams() { Direction = Direction, Alignment = Alignment, Spacing = Spacing, Margin = Margin }; // Gotta love freezable layouts if (DynamicSize) { var lg = panel.AddComponent <BoxLayoutGroup>(); lg.Params = args; lg.flexibleWidth = FlexSize.x; lg.flexibleHeight = FlexSize.y; } else { BoxLayoutGroup.LayoutNow(panel, args).SetFlexUISize(FlexSize); } OnRealize?.Invoke(panel); return(panel); }
/// <summary> /// Creates the handle component. /// </summary> /// <param name="slider">The parent component.</param> /// <returns>The sliding handle object.</returns> private GameObject CreateHandle(GameObject slider) { // Handle var handle = PUIElements.CreateUI(slider, "Handle", true, PUIAnchoring.Center, PUIAnchoring.Center); var handleImg = handle.AddComponent <Image>(); handleImg.sprite = PUITuning.Images.SliderHandle; handleImg.preserveAspect = true; handle.SetUISize(new Vector2(HandleSize, HandleSize)); // Rotate the handle if needed (CCW) float rot = 0.0f; switch (Direction) { case Slider.Direction.TopToBottom: rot = 90.0f; break; case Slider.Direction.RightToLeft: rot = 180.0f; break; case Slider.Direction.BottomToTop: rot = 270.0f; break; default: break; } if (rot != 0.0f) { handle.transform.Rotate(new Vector3(0.0f, 0.0f, rot)); } return(handle); }
/// <summary> /// Builds a PLib UI object and adds it to an existing UI object. /// </summary> /// <param name="component">The UI object to add.</param> /// <param name="parent">The parent of the new object.</param> /// <param name="index">The sibling index to insert the element at, if provided.</param> /// <returns>The built version of the UI object.</returns> public static GameObject AddTo(this IUIComponent component, GameObject parent, int index = -2) { if (component == null) { throw new ArgumentNullException("component"); } if (parent == null) { throw new ArgumentNullException("parent"); } var child = component.Build(); PUIElements.SetParent(child, parent); if (index == -1) { child.transform.SetAsLastSibling(); } else if (index >= 0) { child.transform.SetSiblingIndex(index); } return(child); }
public GameObject Build() { var combo = PUIElements.CreateUI(null, Name); var style = TextStyle ?? PUITuning.Fonts.UILightStyle; var entryColor = EntryColor ?? PUITuning.Colors.ButtonBlueStyle; RectOffset margin = Margin, im = ItemMargin; // Background color var bgImage = combo.AddComponent <KImage>(); var backColorStyle = BackColor ?? PUITuning.Colors.ButtonBlueStyle; UIDetours.COLOR_STYLE_SETTING.Set(bgImage, backColorStyle); PButton.SetupButtonBackground(bgImage); // Need a LocText (selected item) var selection = PUIElements.CreateUI(combo, "SelectedItem"); if (MinWidth > 0) { selection.SetMinUISize(new Vector2(MinWidth, 0.0f)); } var selectedLabel = PUIElements.AddLocText(selection, style); // Vertical flow panel with the choices var contentContainer = PUIElements.CreateUI(null, "Content"); contentContainer.AddComponent <VerticalLayoutGroup>().childForceExpandWidth = true; // Scroll pane with items is laid out below everything else var pullDown = new PScrollPane("PullDown") { ScrollHorizontal = false, ScrollVertical = true, AlwaysShowVertical = true, FlexSize = Vector2.right, TrackSize = 8.0f, BackColor = entryColor. inactiveColor }.BuildScrollPane(combo, contentContainer); // Add a black border (Does not work, covered by the combo box buttons...) #if false var pdImage = pullDown.GetComponent <Image>(); pdImage.sprite = PUITuning.Images.BoxBorder; pdImage.type = Image.Type.Sliced; #endif pullDown.rectTransform().pivot = new Vector2(0.5f, 1.0f); // Initialize the drop down var comboBox = combo.AddComponent <PComboBoxComponent>(); comboBox.CheckColor = style.textColor; comboBox.ContentContainer = contentContainer.rectTransform(); comboBox.EntryPrefab = BuildRowPrefab(style, entryColor); comboBox.MaxRowsShown = MaxRowsShown; comboBox.Pulldown = pullDown; comboBox.SelectedLabel = selectedLabel; comboBox.SetItems(Content); comboBox.SetSelectedItem(InitialItem); comboBox.OnSelectionChanged = (obj, item) => OnOptionSelected?.Invoke(obj. gameObject, item as T); // Inner component with the pulldown image var image = PUIElements.CreateUI(combo, "OpenImage"); var icon = image.AddComponent <Image>(); icon.sprite = PUITuning.Images.Contract; icon.color = style.textColor; // Button component var dropButton = combo.AddComponent <KButton>(); PButton.SetupButton(dropButton, bgImage); UIDetours.FG_IMAGE.Set(dropButton, icon); dropButton.onClick += comboBox.OnClick; // Add tooltip PUIElements.SetToolTip(selection, ToolTip); combo.SetActive(true); // Button gets laid out on the right, rest of space goes to the label // Scroll pane is laid out on the bottom var layout = combo.AddComponent <RelativeLayoutGroup>(); layout.AnchorYAxis(selection).SetLeftEdge(selection, fraction: 0.0f).SetRightEdge(selection, toLeft: image).AnchorYAxis(image).SetRightEdge( image, fraction: 1.0f).SetMargin(selection, new RectOffset(margin.left, im.right, margin.top, margin.bottom)).SetMargin(image, new RectOffset(0, margin.right, margin.top, margin.bottom)).OverrideSize(image, ArrowSize). AnchorYAxis(pullDown, 0.0f).OverrideSize(pullDown, Vector2.up); layout.LockLayout(); if (DynamicSize) { layout.UnlockLayout(); } // Disable sizing on the pulldown pullDown.AddOrGet <LayoutElement>().ignoreLayout = true; // Scroll pane is hidden right away pullDown.SetActive(false); layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; OnRealize?.Invoke(combo); return(combo); }
public GameObject Build() { var textField = PUIElements.CreateUI(Name); // Background var style = TextStyle ?? PUITuning.Fonts.TextLightStyle; textField.AddComponent <Image>().color = style.textColor; // Text box with rectangular clipping area var textArea = PUIElements.CreateUI("Text Area", true, false); textArea.AddComponent <Image>().color = BackColor; PUIElements.SetParent(textArea, textField); var mask = textArea.AddComponent <RectMask2D>(); // Scrollable text var textBox = PUIElements.CreateUI("Text"); PUIElements.SetParent(textBox, textArea); // Text to display var textDisplay = textBox.AddComponent <TextMeshProUGUI>(); textDisplay.alignment = TextAlignment; textDisplay.autoSizeTextContainer = false; textDisplay.enabled = true; textDisplay.color = style.textColor; textDisplay.font = style.sdfFont; textDisplay.fontSize = style.fontSize; textDisplay.fontStyle = style.style; textDisplay.maxVisibleLines = 1; // Text field itself var onChange = OnTextChanged; textField.SetActive(false); var textEntry = textField.AddComponent <TMP_InputField>(); textEntry.textComponent = textDisplay; textEntry.textViewport = textArea.rectTransform(); textField.SetActive(true); textEntry.text = Text ?? ""; textDisplay.text = Text ?? ""; ConfigureTextEntry(textEntry).onDeselect.AddListener((text) => { onChange?.Invoke(textField, (text ?? "").TrimEnd()); }); // Add tooltip if (!string.IsNullOrEmpty(ToolTip)) { textField.AddComponent <ToolTip>().toolTip = ToolTip; } mask.enabled = true; // Lay out - TMP_InputField does not support auto layout so we have to do a hack var minSize = new Vector2(MinWidth, LayoutUtility.GetPreferredHeight(textBox. rectTransform())); textArea.SetMinUISize(minSize).SetFlexUISize(Vector2.one); var lp = new BoxLayoutParams() { Direction = PanelDirection.Horizontal, Alignment = TextAnchor.MiddleLeft, Margin = new RectOffset(1, 1, 1, 1) }; if (DynamicSize) { var layout = textField.AddComponent <BoxLayoutGroup>(); layout.Params = lp; layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; textField.SetMinUISize(minSize); } else { BoxLayoutGroup.LayoutNow(textField, lp, new Vector2(MinWidth, 0.0f)). SetFlexUISize(FlexSize); } OnRealize?.Invoke(textField); return(textField); }