using UnityEngine; using System.Collections.Generic; namespace PixelCrushers.DialogueSystem.UnityGUI { /// /// The basic GUI control, which simply contains child controls. /// public class GUIControl : MonoBehaviour { /// /// The drawing order depth. Higher numbers get drawn after lower numbers. /// public int depth = 0; /// /// If true, children are drawn in depth order; otherwise no specific order. /// public bool depthSortChildren = false; /// /// The scaled rect defining the position of the control. /// public ScaledRect scaledRect = new ScaledRect(ScaledRect.wholeScreen); /// /// Auto-size settings. These specify whether and how to auto-size to fit the contents. /// public AutoSize autoSize; /// /// Fit settings. These specify how to fit around other controls. /// public Fit fit; /// /// Keyboard/controller navigation settings. /// public Navigation navigation; /// /// The pixel rect represented by scaledRect. /// /// /// The pixel rect. /// public Rect rect { get; set; } /// /// If true, this control and its children are visible. /// public bool visible = true; /// /// Clip children to the control's bounds? /// public bool clipChildren = true; /// /// Gets or sets the offset to apply to the screen rect for this control; useful for manual /// repositioning outside the normal GUI control system. /// /// The offset. public Vector2 Offset { get; set; } /// /// The child controls. /// protected List Children { get { return children; } } /// /// When true, the control needs to update its style, size, position, etc. /// Use the Refresh() method to set this to true. /// public bool NeedToUpdateLayout { get { return needToUpdateLayout; } set { needToUpdateLayout = value; } } /// /// The size of the window most recently passed to Refresh(). Used to update /// the control's layout. /// protected Vector2 WindowSize { get { return windowSize; } set { windowSize = value; } } /// /// Gets a value indicating whether keyboard/controller navigation is enabled. /// public bool IsNavigationEnabled { get { return (navigation != null) && navigation.enabled; } } /// /// Gets the full name of the GameObject, used to focus the control when using /// keyboard/controller navigation. /// /// The full name. public string FullName { get { if (string.IsNullOrEmpty(fullName)) fullName = Tools.GetFullName(this.gameObject); return fullName; } } /// /// Gets or sets dRect, which offsets the rect when the parent window isn't clipping. /// public Vector2 dRect { get; set; } /// /// The cached full name of the GameObject. /// private string fullName = null; private List children = new List(); private bool needToUpdateLayout = true; private Vector2 windowSize = Vector2.zero; private bool navigationSelectButtonClicked = false; public virtual void Awake() { dRect = Vector2.zero; rect = scaledRect.GetPixelRect(); Offset = Vector2.zero; } /// /// Checks if the control needs to enable the first child for key/controller navigation. /// public virtual void OnEnable() { Refresh(); if (IsNavigationEnabled && navigation.focusFirstControlOnEnable) navigation.FocusFirstControl(); } /// /// Draw the control and its children. /// /// Relative mouse position within the window containing this control. public void Draw(Vector2 relativeMousePosition) { if (visible && gameObject.activeSelf) { UpdateLayout(); DrawSelf(relativeMousePosition); DrawChildren(relativeMousePosition); } } /// /// Draws the control, but not its children. /// /// Relative mouse position within the window containing this control. public virtual void DrawSelf(Vector2 relativeMousePosition) { } /// /// Draws the children, taking into account key/controller navigation if enabled. /// /// Relative mouse position. public virtual void DrawChildren(Vector2 relativeMousePosition) { if (Children.Count == 0) return; if (clipChildren) GUI.BeginGroup(rect); try { bool isNavigationEnabled = IsNavigationEnabled; if (isNavigationEnabled && (navigation.click != KeyCode.Space)) { if ((Event.current.type == EventType.KeyDown) && (Event.current.character == ' ')) return; } GUIControl clickedControl = null; bool navigationClicked = IsNavigationEnabled && (navigation.IsClicked || navigationSelectButtonClicked); Vector2 childMousePosition = new Vector2(relativeMousePosition.x - rect.x, relativeMousePosition.y - rect.y); if (isNavigationEnabled) navigation.CheckNavigationInput(childMousePosition); foreach (var child in Children) { if (IsNavigationEnabled) { GUI.SetNextControlName(child.FullName); if (navigationClicked && string.Equals(GUI.GetNameOfFocusedControl(), child.FullName)) { navigationSelectButtonClicked = false; clickedControl = child; } } child.Draw(childMousePosition); } if (isNavigationEnabled) GUI.FocusControl(navigation.FocusedControlName); if ((clickedControl != null) && (clickedControl is GUIButton)) (clickedControl as GUIButton).Click(); } finally { if (clipChildren) GUI.EndGroup(); } } /// /// If navigation is enabled, check if the selection button was pressed. /// Remember the value so we can check it in DrawChildren. /// public virtual void Update() { if (IsNavigationEnabled) { navigationSelectButtonClicked = DialogueManager.getInputButtonDown(navigation.clickButton); } } /// /// Marks a control as needing to update its layout. /// /// Window size. public virtual void Refresh(Vector2 windowSize) { NeedToUpdateLayout = true; WindowSize = windowSize; } public virtual void Refresh() { NeedToUpdateLayout = true; } /// /// Updates the layout (size, position, formatting, etc.) of the control and its children. /// public virtual void UpdateLayout() { if (NeedToUpdateLayout) { UpdateLayoutSelf(); FitSelf(); UpdateLayoutChildren(); FitChildren(); //NeedToUpdateLayout = false; } } /// /// Updates the control's layout but not its children. /// public virtual void UpdateLayoutSelf() { NeedToUpdateLayout = false; if (WindowSize.x == 0) WindowSize = new Vector2(Screen.width, Screen.height); rect = scaledRect.GetPixelRect(WindowSize); if ((Offset.x != 0) || (Offset.y != 0)) rect = new Rect(rect.x + Offset.x, rect.y + Offset.y, rect.width, rect.height); if ((dRect.x != 0) || (dRect.y != 0)) rect = new Rect(rect.x + dRect.x, rect.y + dRect.y, rect.width, rect.height); if (autoSize != null) AutoSizeSelf(); } /// /// Auto-sizes the control according to the autoSize settings. /// public virtual void AutoSizeSelf() { } /// /// Fits the control according to the fit settings. /// protected virtual void FitSelf() { if ((fit != null) && (fit.IsSpecified)) { float xMin = rect.xMin; float xMax = rect.xMax; float yMin = rect.yMin; float yMax = rect.yMax; if (fit.above != null) { yMax = fit.above.rect.yMin; if ((fit.below == null) && !fit.expandToFit) { yMin = yMax - rect.height; } } if (fit.below != null) { yMin = fit.below.rect.yMax; if ((fit.above == null) && !fit.expandToFit) { yMax = yMin + rect.height; } } if (fit.leftOf != null) { xMax = fit.leftOf.rect.xMin; if ((fit.rightOf == null) && !fit.expandToFit) { xMin = xMax - rect.width; } } if (fit.rightOf != null) { xMin = fit.rightOf.rect.xMax; if ((fit.rightOf == null) && !fit.expandToFit) { xMax = xMin + rect.width; } } rect = Rect.MinMaxRect(xMin, yMin, xMax, yMax); } } /// /// Refreshes the list of children and updates their layouts. /// private void UpdateLayoutChildren() { FindChildren(); if (depthSortChildren) SortChildren(); Vector2 childWindowSize = new Vector2(rect.width, rect.height); foreach (var child in Children) { UpdateLayoutChild(child, childWindowSize); } } private void UpdateLayoutChild(GUIControl child, Vector2 childWindowSize) { child.Refresh(childWindowSize); child.dRect = clipChildren ? Vector2.zero : new Vector2(rect.x, rect.y); child.UpdateLayout(); //---Was (replaced by dRect above): if (!clipChildren) child.rect = new Rect(child.rect.x + rect.x, child.rect.y + rect.y, child.rect.width, child.rect.height); } private void FitChildren() { for (int i = 0; i < Children.Count; i++) { Children[i].FitSelf(); } } /// /// Updates the children list with all child controls found on child objects. /// private void FindChildren() { Children.Clear(); foreach (Transform t in transform) { GUIControl[] components = t.GetComponents(); Children.AddRange(components); if (components.Length > 0) { components[0].FindChildren(); } } } private void SortChildren() { Children.Sort((x, y) => x.depth.CompareTo(y.depth)); } } }