Website may be up and down over next few months. I'm currently doing a complete overhaul of everything. Going back to simple individual .htm pages, new overall site theme, sanitizing and cleaning up html of all pages and blog posts, attempting to implement a new tooling and publishing system etc etc.

The XAML. Use null to define a separator.

<Menu Name="MainMenu" Grid.Row="0" IsMainMenu="True" ItemsSource="{Binding Model}">
    <Menu.Resources>
        <ControlTemplate x:Key="MenuSeparatorTemplate">
            <Separator />
        </ControlTemplate>
    </Menu.Resources>
    <Menu.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Command" Value="{Binding Command}" />
            <Setter Property="CommandParameter" Value="{Binding CommandParameter}" />
            <Setter Property="Header" Value="{Binding Header}" />
            <Setter Property="ItemsSource" Value="{Binding Children}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding }" Value="{x:Null}">
                    <Setter Property="Template" Value="{StaticResource MenuSeparatorTemplate}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>

/// <summary>
/// Determines whether an enum has been marked Obsolete.
/// </summary>
/// <param name="value">The enum value to check against.</param>
/// <returns>
///   <c>true</c> if marked obsolete; otherwise, <c>false</c>.
/// </returns>
public static bool IsEnumObsolete(this Enum value)
{
    var fi = value.GetType().GetField(value.ToString());
    var attributes = (ObsoleteAttribute[])fi.GetCustomAttributes(typeof(ObsoleteAttribute), false);
    return attributes.Length > 0;
}


 

/// <summary>Removes any items in a list that match a criteria</summary>
/// <param name="list">The list.</param>
/// <param name="predicate">The callback used to determine weather or not the item in the list should be removed.</param>
/// <exception cref="ArgumentNullException"><paramref name="predicate"/> is <see langword="null"/></exception>
public static void RemoveAny(this IList list,  Predicate<object> predicate )
{
    if (predicate == null)
    {
        throw new ArgumentNullException("predicate");
    }

    for (var i = list.Count - 1; i >= 0; i--)
    {
        var item = list[i];
        if (predicate(item))
        {
            list.RemoveAt(i);
        }
    }
}

Provided below are two extension methods for determining if two unity Rect types intersect one another.

/// <summary>
/// Returns a third Rect structure that represents the intersection of two other Rect structures. 
/// If there is no intersection, an empty Rect is returned.
/// </summary>
/// <param name="a">
/// A rectangle to intersect.   
/// </param>
/// <param name="b">
/// B rectangle to intersect.  
/// </param>
/// <returns>
/// A Rect that represents the intersection of a and b.
/// </returns>
public static Rect Intersect(this Rect a, Rect b)
{
    float x = Math.Max((sbyte)a.x, (sbyte)b.x);
    var num2 = Math.Min(a.x + a.width, b.x + b.width);
    float y = Math.Max((sbyte)a.y, (sbyte)b.y);
    var num4 = Math.Min(a.y + a.height, b.y + b.height);
    if ((num2 >= x) && (num4 >= y))
    {
        return new Rect(x, y, num2 - x, num4 - y);
    }

    return new Rect();
}

/// <summary>
/// Determines if this rectangle intersects with rect.
/// </summary>
/// <param name="source">The source rectangle from which the intersection will be tested.</param>
/// <param name="rect">
/// The rectangle to test.
/// </param>
/// <returns>
/// This method returns true if there is any intersection, otherwise false.
/// </returns>
public static bool Intersects(this Rect source, Rect rect)
{
    return !((source.x > rect.xMax) || (source.xMax< rect.x) || (source.y > rect.yMax) || (source.yMax < rect.y));
}

The DrawRectangle & DrawLine helper methods allow you to draw a lines and rectangles inside OnGUI methods.

/// <summary>
/// Draws a rectangle.
/// </summary>
/// <param name="rect">A <see cref="Rect"/> type that defines the bounds of the rectangle to draw.</param>
public static void DrawRectangle(Rect rect)
{
    DrawRectangle(rect, GUI.contentColor, 1f);
}

/// <summary>
/// Draws a rectangle.
/// </summary>
/// <param name="rect">
/// A <see cref="Rect"/> type that defines the bounds of the rectangle to draw.
/// </param>
/// <param name="color">
/// The color of the rectangle.
/// </param>
public static void DrawRectangle(Rect rect, Color color)
{
    DrawRectangle(rect, color, 1);
}

/// <summary>
/// Draws a rectangle.
/// </summary>
/// <param name="rect">
/// A <see cref="Rect"/> type that defines the bounds of the rectangle to draw.
/// </param>
/// <param name="thickness">
/// The line thickness of the rectangle.
/// </param>
public static void DrawRectangle(Rect rect, float thickness)
{
    DrawRectangle(rect, GUI.contentColor, thickness);
}

/// <summary>
/// Draws a rectangle.
/// </summary>
/// <param name="rect">
/// A <see cref="Rect"/> type that defines the bounds of the rectangle to draw.
/// </param>
/// <param name="color">
/// The color of the rectangle.
/// </param>
/// <param name="thickness">
/// The line thickness of the rectangle.
/// </param>
public static void DrawRectangle(Rect rect, Color color, float thickness)
{
    DrawHLine(new Vector2(rect.x, rect.y), rect.width, color, thickness);
    DrawHLine(new Vector2(rect.x, rect.y + rect.height - 1), rect.width, color, thickness);
    DrawVLine(new Vector2(rect.x, rect.y), rect.height, color, thickness);
    DrawVLine(new Vector2(rect.x + rect.width - 1, rect.y), rect.height, color, thickness);
}

/// <summary>
/// Draws a line.
/// </summary>
/// <param name="pointA">The start point of the line.</param>
/// <param name="pointB">The end point of the line.</param>
public static void DrawLine(Vector2 pointA, Vector2 pointB)
{
    DrawLine(pointA, pointB, GUI.contentColor, 1.0f);
}

/// <summary>
/// Draws a line.
/// </summary>
/// <param name="pointA">The start point of the line.</param>
/// <param name="pointB">The end point of the line.</param>
/// <param name="color">The color to use.</param>
public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color)
{
    DrawLine(pointA, pointB, color, 1.0f);
}

/// <summary>
/// Draws a line.
/// </summary>
/// <param name="pointA">The start point of the line.</param>
/// <param name="pointB">The end point of the line.</param>
/// <param name="thickness">THe thickness of the line.</param>
public static void DrawLine(Vector2 pointA, Vector2 pointB, float thickness)
{
    DrawLine(pointA, pointB, GUI.contentColor, thickness);
}

/// <summary>
/// Draws a line.
/// </summary>
/// <param name="pointA">The start point of the line.</param>
/// <param name="pointB">The end point of the line.</param>
/// <param name="color">The color to use.</param>
/// <param name="thickness">THe thickness of the line.</param>
public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float thickness)
{
    // Save the current GUI matrix, since we're going to make changes to it.
    var matrix = GUI.matrix;

    // Store current GUI color, so we can switch it back later,
    // and set the GUI color to the color parameter
    var savedColor = GUI.color;
    GUI.color = color;

    // Determine the angle of the line.
    var angle = Vector2.Angle(pointB - pointA, Vector2.right);

    // Vector3.Angle always returns a positive number.
    // If pointB is above pointA, then angle needs to be negative.
    if (pointA.y > pointB.y)
    {
        angle = -angle;
    }

    // Use ScaleAroundPivot to adjust the size of the line.
    // We could do this when we draw the texture, but by scaling it here we can use
    //  non-integer values for the thickness and length (such as sub 1 pixel widths).
    // Note that the pivot point is at +.5 from pointA.y, this is so that the thickness of the line
    //  is centered on the origin at pointA.
    GUIUtility.ScaleAroundPivot(new Vector2((pointB - pointA).magnitude, thickness), new Vector2(pointA.x, pointA.y + 0.5f));

    // Set the rotation for the line.
    //  The angle was calculated with pointA as the origin.
    GUIUtility.RotateAroundPivot(angle, pointA);

    // Finally, draw the actual line.
    // We're really only drawing a 1x1 texture from pointA.
    // The matrix operations done with ScaleAroundPivot and RotateAroundPivot will make this
    //  render with the proper thickness, length, and angle.
    GUI.DrawTexture(new Rect(pointA.x, pointA.y, 1, 1), Texture2D.whiteTexture);

    // We're done.  Restore the GUI matrix and GUI color to whatever they were before.
    GUI.matrix = matrix;
    GUI.color = savedColor;
}

/// <summary>
/// Draws a horizontal line.
/// </summary>
/// <param name="pointA">
/// The start point of the line.
/// </param>
/// <param name="length">
/// The length of the line.
/// </param>
/// <param name="color">
/// The color to use.
/// </param>
/// <param name="thickness">
/// THe thickness of the line.
/// </param>
/// <remarks>
/// The line will be drawn from <see cref="pointA"/> moving to the right. To draw to the left use a negative <see cref="length"/> value.
/// </remarks>
public static void DrawHLine(Vector2 pointA, float length, Color color, float thickness)
{
    // Store current GUI color, so we can switch it back later,
    // and set the GUI color to the color parameter
    var savedColor = GUI.color;
    GUI.color = color;

    // Finally, draw the actual line.
    // We're really only drawing a 1x1 texture from pointA.
    // The matrix operations done with ScaleAroundPivot and RotateAroundPivot will make this
    //  render with the proper thickness, length, and angle.
    GUI.DrawTexture(new Rect(pointA.x, pointA.y, length - 1, thickness), Texture2D.whiteTexture, ScaleMode.StretchToFill);

    // We're done.  Restore the GUI matrix and GUI color to whatever they were before.
    GUI.color = savedColor;
}

/// <summary>
/// Draws a vertical line.
/// </summary>
/// <param name="pointA">
/// The start point of the line.
/// </param>
/// <param name="length">
/// The length of the line.
/// </param>
/// <param name="color">
/// The color to use.
/// </param>
/// <param name="thickness">
/// THe thickness of the line.
/// </param>
/// <remarks>
/// The line will be drawn from <see cref="pointA"/> moving up. To draw down use a negative <see cref="length"/> value.
/// </remarks>
public static void DrawVLine(Vector2 pointA, float length, Color color, float thickness)
{
    // Store current GUI color, so we can switch it back later,
    // and set the GUI color to the color parameter
    var savedColor = GUI.color;
    GUI.color = color;

    // Finally, draw the actual line.
    // We're really only drawing a 1x1 texture from pointA.
    // The matrix operations done with ScaleAroundPivot and RotateAroundPivot will make this
    //  render with the proper thickness, length, and angle.
    GUI.DrawTexture(new Rect(pointA.x, pointA.y, thickness, length - 1), Texture2D.whiteTexture, ScaleMode.StretchToFill);

    // We're done.  Restore the GUI matrix and GUI color to whatever they were before.
    GUI.color = savedColor;
}

The FillRectangle helper methods allow you to draw a filled rectangles inside OnGUI methods.

        /// <summary>
        /// Draws a filled rectangle.
        /// </summary>
        /// <param name="rect">
        /// A <see cref="Rect"/> type that defines the bounds of the rectangle to draw.
        /// </param>
        public static void FillRectangle(Rect rect)
        {
            FillRectangle(rect, GUI.color);
        }

        /// <summary>
        /// Draws a filled rectangle.
        /// </summary>
        /// <param name="rect">
        /// A <see cref="Rect"/> type that defines the bounds of the rectangle to draw.
        /// </param>
        /// <param name="color">
        /// The color of the rectangle.
        /// </param>
        public static void FillRectangle(Rect rect, Color color)
        {
            // Store current GUI color, so we can switch it back later,
            // and set the GUI color to the color parameter
            var savedColor = GUI.color;
            GUI.color = color;

            // Finally, draw the actual rectangle.
            GUI.DrawTexture(rect, Texture2D.whiteTexture, ScaleMode.StretchToFill);

            // We're done.  Restore the GUI matrix and GUI color to whatever they were before.
            GUI.color = savedColor;
        }

        /// <summary>Gets the value associated with the specified key and casts the value to the desired type.</summary>
        /// <returns>true if the <see cref="T:System.Collections.Generic.Dictionary`2" /> contains an element with the specified key; otherwise, false.</returns>
        /// <param name="dictionary">The dictionary to retrieve the value from.</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value" /> parameter. This parameter is passed uninitialized.</param>
        public static bool TryGetValueCast<T, V, C>(this IDictionary<T, V> dictionary, T name, out C value)
        {
            return TryGetValueCast(dictionary, name, out value, default(C));
        }

        /// <summary>Gets the value associated with the specified key and casts the value to the desired type.</summary>
        /// <returns>true if the <see cref="T:System.Collections.Generic.Dictionary`2" /> contains an element with the specified key; otherwise, false.</returns>
        /// <param name="dictionary">The dictionary to retrieve the value from.</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value" /> parameter. This parameter is passed uninitialized.</param>
        /// <param name="defaultValue">The default value to return if unable to fetch using key.</param>
        public static bool TryGetValueCast<T, V, C>(this IDictionary<T, V> dictionary, T key, out C value, C defaultValue)
        {
            if (dictionary == null)
            {
                throw new ArgumentNullException("dictionary");
            }

            try
            {
                value = (C)Convert.ChangeType(dictionary[key], typeof(C));
            }
            catch
            {
                value = defaultValue;
                return false;
            }

            return true;
        }

I needed to add tab key support to a GUI.TextArea and soon discovered it was not quite as easy as I had originally thought. I have provided two code examples below. The first example is simplified and the second example is more complex that wraps TextArea controls inside of parent control.

With these examples you can type text in a TextArea/TextField and press the tab key to insert 4 spaces, or press Shift+Tab to move the line 4 spaces to the left if the area is clear.

I have also provided a third advanced example from my UIControls library to give an example of a real word usage scenario. This third example synchronizes my TextBox control with the unity’s TextEditor. My TextBox control has similar properties as Winforms TextBox.

Simplified example

using System; 
using UnityEditor;
using UnityEngine;

public class TextAreaTabSupport : EditorWindow
{
    private int lastKBFocus = -1;
    private string textA = string.Empty;
    private string textB = string.Empty;
    private string textC = string.Empty;

    [MenuItem("Test/Text Area Tab Support")]
    public static void ShowWindow()
    {
        GetWindow<TextAreaTabSupport>().Show();
    }

    public void OnGUI()
    {
        var current = Event.current;
        GUI.SetNextControlName("testa");

        if (GUI.GetNameOfFocusedControl() == "testa" && this.lastKBFocus == GUIUtility.keyboardControl)
        {
            if (current.type == EventType.KeyDown || current.type == EventType.KeyUp)
            {
                if (current.isKey && (current.keyCode == KeyCode.Tab || current.character == '\t'))
                {
                    if (current.type == EventType.KeyUp)
                    {
                        var te = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);

                        if (!current.shift)
                        {
                            for (var i = 0; i < 4; i++)
                            {
                                te.Insert(' ');
                            }
                        }
                        else
                        {
                            var min = Math.Min(te.cursorIndex, te.selectIndex);
                            var index = min;
                            var temp = te.text;
                            for (var i = 1; i < 5; i++)
                            {
                                if ((min - i) < 0 || temp[min - i] != ' ')
                                {
                                    break;
                                }

                                index = min - i;
                            }

                            if (index < min)
                            {
                                te.selectIndex = index;
                                te.cursorIndex = min;
                                te.ReplaceSelection(string.Empty);
                            }
                        }

                        this.textA = te.text;
                    }

                    current.Use();
                }
            }
        }

        this.textA = GUI.TextArea(new Rect(0, 40, 100, 100), this.textA);

        if (GUI.GetNameOfFocusedControl() == "testa" && current.type == EventType.KeyDown || current.type == EventType.KeyUp)
        {
            this.lastKBFocus = GUIUtility.keyboardControl;
        }

        GUI.SetNextControlName("testb");
        this.textB = GUI.TextArea(new Rect(110, 40, 100, 100), this.textB);

        GUI.SetNextControlName("testc");
        this.textC = GUI.TextField(new Rect(220, 40, 100, 30), this.textC);

        if (GUI.Button(new Rect(10, 110, 50, 25), "Click"))
        {

        }
    }
}

And a more complex example

using System;
using UnityEditor;
using UnityEngine;

public class TextAreaTabSupport : EditorWindow
{
    private Vector2 scroll;
    private int lastKBFocus = -1;
    private string textA = string.Empty;
    private string textB = string.Empty;
    private string textC = string.Empty;

    [MenuItem("Test/Text Area Tab Support")]
    public static void ShowWindow()
    {
        GetWindow<TextAreaTabSupport>().Show();
    }

    public void OnGUI()
    {
        var current = Event.current;

        GUI.SetNextControlName("scroller");
        using (var scroll = new GUI.ScrollViewScope(new Rect(Vector2.zero, new Vector2(330, 150)), this.scroll, new Rect(Vector2.zero, new Vector2(330, 150))))
        {
            this.scroll = scroll.scrollPosition;

            if (GUI.GetNameOfFocusedControl() == "testa" && this.lastKBFocus == GUIUtility.keyboardControl)
            {
                if (current.type == EventType.KeyDown || current.type == EventType.KeyUp)
                {
                    if (current.isKey && (current.keyCode == KeyCode.Tab || current.character == '\t'))
                    {
                        if (current.type == EventType.KeyUp)
                        {
                            var te = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);

                            if (!current.shift)
                            {
                                for (var i = 0; i < 4; i++)
                                {
                                    te.Insert(' ');
                                }
                            }
                            else
                            {
                                var min = Math.Min(te.cursorIndex, te.selectIndex);
                                var index = min;
                                var temp = te.text;
                                for (var i = 1; i < 5; i++)
                                {
                                    if ((min - i) < 0 || temp[min - i] != ' ')
                                    {
                                        break;
                                    }

                                    index = min - i;
                                }

                                if (index < min)
                                {
                                    te.selectIndex = index;
                                    te.cursorIndex = min;
                                    te.ReplaceSelection(string.Empty);
                                }
                            }

                            this.textA = te.text;
                        }

                        current.Use();
                    }
                }
            }

            using (new GUI.GroupScope(new Rect(0, 0, 110, 110)))
            {
                GUI.SetNextControlName("testa");
                this.textA = GUI.TextArea(new Rect(0, 4, 100, 100), this.textA);
            }

            if (this.lastKBFocus != GUIUtility.keyboardControl && (current.type == EventType.KeyDown || current.type == EventType.KeyUp))
            {
                this.lastKBFocus = GUIUtility.keyboardControl;
            }

            GUI.SetNextControlName("testb");
            this.textB = GUI.TextArea(new Rect(110, 40, 100, 100), this.textB);

            GUI.SetNextControlName("testc");
            this.textC = GUI.TextField(new Rect(220, 40, 100, 30), this.textC);

            if (GUI.Button(new Rect(10, 110, 50, 25), "Click"))
            {

            }
        }
    }
}

Advanced example

namespace Codefarts.UIControls.Renderers
{
#if UNITY_5
    using System;                      

    using UnityEngine;

    /// <summary>
    /// Provides a renderer implementation for the <see cref="TextBox"/> control.
    /// </summary>
    [ControlRenderer(typeof(TextBox))]
    public class TextBoxRenderer : BaseRenderer
    {
        /// <summary>
        /// Implemented by inheritors to draw the actual control.
        /// </summary>
        /// <param name="args">The rendering argument information.</param>
        /// <exception cref="System.ArgumentNullException">control</exception>
        public override void DrawControl(ControlRenderingArgs args)
        {
            var textBox = (TextBox)args.Control;

            // unity gui does not like null strings
            var text = textBox.Text == null ? string.Empty : textBox.Text;

            var maxLength = textBox.MaxLength == 0 ? int.MaxValue : textBox.MaxLength;

            var rect = new Rect(textBox.Location + args.Offset, textBox.Size);
            var hsbVisibility = textBox.HorizontalScrollBarVisibility;
            var vsbVisibility = textBox.VerticalScrollBarVisibility;
            var alwaysShowHorizontal = hsbVisibility == ScrollBarVisibility.Visible;
            var alwaysShowVertical = vsbVisibility == ScrollBarVisibility.Visible;

            KeyCode keyCode;
            bool isDown;
            bool isUp;
            string controlName;
            this.GetKeyInfoAndSetControlName(textBox, out keyCode, out isDown, out isUp, out controlName, false);

            // get style and sync it up
            var style = textBox.GetStyle(Control.ControlStyle, true, GUI.skin.textArea);
            if (textBox.Font != null)
            {
                style.SetFontStyle(textBox.Font);
            }

            var current = Event.current;
            int lastKBFocus;
            textBox.Properties.TryGetValueCast(ControlDrawingHelpers.LastKeyboardControlID, out lastKBFocus, -1);
            var selectionStart = textBox.SelectionStart;
            var selectionLength = textBox.SelectionLength;

            if (GUI.GetNameOfFocusedControl() == controlName && lastKBFocus == GUIUtility.keyboardControl)
            {
                var te = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);

                // sync textbox & texteditor selections
                this.SyncTextBoxSelection(TextBox.TextBoxSelectionStartChanged, te, textBox);
                this.SyncTextBoxSelection(TextBox.TextBoxSelectionLengthChanged, te, textBox);

                // process tab key if nessary
                text = this.HandleTabKeyPress(ref selectionStart, te, current, textBox, text);

                selectionLength = Math.Abs(te.cursorIndex - te.selectIndex);
            }

            // draw the text area/field control
            textBox.Text = this.DrawActualTextControl(textBox, text, rect, alwaysShowHorizontal, alwaysShowVertical, style, maxLength, controlName);

            // check if we need to record last keyboard control id
            if (GUI.GetNameOfFocusedControl() == controlName && lastKBFocus != GUIUtility.keyboardControl)
            {
                textBox.Properties[ControlDrawingHelpers.LastKeyboardControlID] = GUIUtility.keyboardControl;
            }

            // check is text selection changed and sync if nessary
            if (textBox.SelectionStart != selectionStart)
            {
                textBox.SelectionStart = selectionStart;
            }
            if (textBox.SelectionLength != selectionLength)
            {
                textBox.SelectionLength = selectionLength;
            }

            // Handle key events
            this.HandleKeyEventsAfterControlDrawn(controlName, keyCode, isDown, textBox, isUp);

            // handle mouse enter & leave events
            this.HandleMouseEvents(textBox);
        }

        private string HandleTabKeyPress(ref int selectionStart, TextEditor te, Event current, TextBox textBox, string text)
        {
            selectionStart = Math.Min(te.cursorIndex, te.selectIndex);
            if (current.type == EventType.KeyDown || current.type == EventType.KeyUp)
            {
                if (current.isKey && (current.keyCode == KeyCode.Tab || current.character == '\t'))
                {
                    // consume the tab key event before drawing the control
                    if (current.type == EventType.KeyUp && textBox.AcceptsTab)
                    {
                        if (!current.shift)
                        {
                            for (var i = 0; i < 4; i++)
                            {
                                te.Insert(' ');
                            }
                        }
                        else
                        {
                            var min = selectionStart;
                            var index = min;
                            var temp = te.text;
                            for (var i = 1; i < 5; i++)
                            {
                                if ((min - i) < 0 || temp[min - i] != ' ')
                                {
                                    break;
                                }

                                index = min - i;
                            }

                            if (index < min)
                            {
                                te.selectIndex = index;
                                te.cursorIndex = min;
                                te.ReplaceSelection(string.Empty);
                            }
                        }

                        selectionStart = Math.Min(te.cursorIndex, te.selectIndex);

                        text = te.text;
                    }

                    current.Use();
                }
            }

            return text;
        }

        private void SyncTextBoxSelection(string name, TextEditor editor, TextBox tb)
        {
            var props = tb.Properties;
            if (props != null)
            {
                bool changed;
                if (props.TryGetValueCast(name, out changed, false) && changed)
                {
                    props[name] = false;
                    this.SetTextEditorSelection(editor, tb.SelectionStart, tb.SelectionLength);
                }
            }
        }

        private void SetTextEditorSelection(TextEditor editor, int start, int length)
        {
            if (editor.cursorIndex < editor.selectIndex)
            {
                editor.cursorIndex = start;
                editor.selectIndex = start + length;
            }
            else
            {
                editor.selectIndex = start;
                editor.cursorIndex = start + length;
            }
        }

        protected virtual string DrawActualTextControl(TextBox textBox, string text, Rect rect, bool alwaysShowHorizontal, bool alwaysShowVertical,
            GUIStyle style, int maxLength, string controlName)
        {
            var scrollPosition = new Vector2(-textBox.HorizontalOffset, -textBox.VerticalOffset);
            if (textBox.AcceptsReturn)
            {
                var textSize = GUI.skin.textArea.CalcSize(new GUIContent(text));
                var viewRect = new Rect(Vector2.zero, textSize);
                viewRect.width = Math.Max(textSize.x, rect.width);
                viewRect.height = Math.Max(textSize.y, rect.height);

                var drawHorizScroll = viewRect.width > rect.width || alwaysShowHorizontal;
                var drawVertScroll = viewRect.height > rect.height || alwaysShowVertical;

                var horizRect = new Rect(
                    0,
                    rect.height - GUI.skin.horizontalScrollbar.fixedHeight,
                    rect.width - (drawVertScroll ? GUI.skin.verticalScrollbar.fixedWidth : 0),
                    GUI.skin.horizontalScrollbar.fixedHeight);
                var vertRect = new Rect(
                    rect.width - GUI.skin.verticalScrollbar.fixedWidth,
                    0,
                    GUI.skin.verticalScrollbar.fixedWidth,
                    rect.height - (drawHorizScroll ? GUI.skin.horizontalScrollbar.fixedHeight : 0));
                horizRect.position += rect.position;
                vertRect.position += rect.position;

                scrollPosition.x = drawHorizScroll ? scrollPosition.x : 0;
                scrollPosition.y = drawVertScroll ? scrollPosition.y : 0;

                viewRect.position = scrollPosition;
                var grpRect = new Rect(
                    rect.x,
                    rect.y,
                    rect.width - (drawVertScroll ? GUI.skin.verticalScrollbar.fixedWidth : 0),
                    rect.height - (drawHorizScroll ? GUI.skin.horizontalScrollbar.fixedHeight : 0));

                using (new GUI.GroupScope(grpRect))
                {
                    // draw background
                    var brush = textBox.Background;
                    if (brush != null)
                    {
                        BrushExtensions.Draw(brush, new Rect(Vector2.zero, grpRect.size));
                    }

                    GUI.SetNextControlName(controlName);
                    // as of unity v5.3 there is a bug that prevent me from specifying a maxlength
                    //BUG: see details here -> https://fogbugz.unity3d.com/default.asp?768436_vikdrmh7ernh03ls
                    text = GUI.TextArea(viewRect, text, style);
                }

                if (drawHorizScroll)
                {
                    textBox.HorizontalOffset = GUI.HorizontalScrollbar(
                        horizRect,
                        textBox.HorizontalOffset,
                        Math.Min(viewRect.width, rect.width),
                        0,
                        viewRect.width + (drawVertScroll ? GUI.skin.verticalScrollbar.fixedWidth : 0));
                }

                if (drawVertScroll)
                {
                    textBox.VerticalOffset = GUI.VerticalScrollbar(
                        vertRect,
                        textBox.VerticalOffset,
                        Math.Min(viewRect.height, rect.height),
                        0,
                        viewRect.height + (drawHorizScroll ? GUI.skin.horizontalScrollbar.fixedHeight : 0));
                }
            }
            else
            {
                // draw background
                var brush = textBox.Background;
                if (brush != null)
                {
                    BrushExtensions.Draw(brush, rect);
                }
                GUI.SetNextControlName(controlName);
                text = GUI.TextField(rect, text, maxLength, style);
            }

            return text;
        }
    }
#endif      
}

The class below provides a progress model for reporting the progress of an action.

/// <summary>
/// Provides a progress class for reporting progress data.
/// </summary>
/// <typeparam name="T">The type used for the result data.</typeparam>
public class ProgressModel<T>
{
    /// <summary>
    /// Gets the results queue.
    /// </summary>
    public Queue<T> Results { get; private set; }

    /// <summary>
    /// Gets the message queue.
    /// </summary>
    public Queue<string> Messages { get; private set; }

    /// <summary>
    /// Gets or sets a value indicating whether this instance is completed.
    /// </summary>
    public bool IsComplete { get; set; }

    /// <summary>
    /// Flags the response model as canceled.
    /// </summary>
    public void Cancel()
    {
        this.IsCanceled = true;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ProgressModel{T}"/> class.
    /// </summary>
    public ProgressModel()
    {
        this.Results = new Queue<T>();
        this.Messages = new Queue<string>();
    }

    /// <summary>
    /// Gets a value indicating whether this instance is canceled.
    /// </summary>
    public bool IsCanceled { get; private set; }

    /// <summary>
    /// Gets or sets the progress.
    /// </summary>
    public float Progress { get; set; }

    /// <summary>
    /// Gets or sets the information that may have been thrown.
    /// </summary>
    public Exception Exception { get; set; }

    /// <summary>
    /// Gets a value indicating whether this instance is faulted.
    /// </summary>
    public bool IsFaulted
    {
        get
        {
            return this.Exception != null;
        }
    }

    /// <summary>
    /// Gets or sets the title.
    /// </summary>
    public string Title { get; set; }   
}

The code below provides a handy helper method for drawing GUI textures via GUI.DrawTextureWithTexCoords.

/// 
/// Draws a image image.
/// 
/// The destination image.
/// The x position in the destination image.
/// The y position in the destination image.
/// The destination width of the drawn image.
/// The destination height of the drawn image.
/// If set to true the image will be drawn fliped horizontally.
/// If set to true the image will be drawen flipped vertically.
/// If set to true the image will be tiled across the destination area.
/// image    
/// If width, height, sourceWidth or sourceHeight are less then 1.
public static void Draw(Texture2D image, float x, float y, float width, float height, bool flipHorizontally, bool flipVertically, bool tile)
{
    Draw(image, x, y, width, height, 0, 0, image.width, image.height, false, false, false);
}

/// 
/// Draws a image image.
/// 
/// The destination image.
/// The x position in the destination image.
/// The y position in the destination image.
/// The destination width of the drawn image.
/// The destination height of the drawn image.
/// The x position in the source image.
/// The y position in the source image.
/// The source width.
/// The source height.
/// image    
/// If width, height, sourceWidth or sourceHeight are less then 1.
public static void Draw(Texture2D image, float x, float y, float width, float height, float sourceX, float sourceY, float sourceWidth, float sourceHeight)
{
    Draw(image, x, y, width, height, sourceX, sourceY, sourceWidth, sourceHeight, false, false, false);
}

/// 
/// Draws a image image.
/// 
/// The destination image.
/// The x position in the destination image.
/// The y position in the destination image.
/// The destination width of the drawn image.
/// The destination height of the drawn image.
/// The x position in the source image.
/// The y position in the source image.
/// The source width.
/// The source height.
/// If set to true the image will be drawn fliped horizontally.
/// If set to true the image will be drawen flipped vertically.
/// If set to true the image will be tiled across the destination area.
/// image    
/// If width, height, sourceWidth or sourceHeight are less then 1.
/// This method has issues with Clamped textures.
public static void Draw(Texture2D image, float x, float y, float width, float height, float sourceX, float sourceY, float sourceWidth, float sourceHeight,
    bool flipHorizontally, bool flipVertically, bool tile)
{
    // perform input validation
    if (image == null)
    {
        throw new ArgumentNullException("image");
    }

    if (sourceWidth < float.Epsilon)
    {
        throw new ArgumentOutOfRangeException("sourceWidth");
    }

    if (sourceHeight < float.Epsilon)
    {
        throw new ArgumentOutOfRangeException("sourceHeight");
    }

    if (width < float.Epsilon)
    {
        return; 
    }

    if (height < float.Epsilon)
    {
        return; 
    }

    var imgWidth = (float)image.width;
    var imgHeight = (float)image.height;
    var srcWidth = sourceWidth / imgWidth;
    var srcHeight = sourceHeight / imgHeight;
    var position = new Rect(x, y + height, width, -height);
    if (tile)
    {
        srcWidth = width / imgWidth;
        srcHeight = height / imgHeight;
    }

    srcWidth = flipHorizontally ? -srcWidth : srcWidth;
    srcHeight = !flipVertically ? -srcHeight : srcHeight;

    var texCoords = new Rect(sourceX / imgWidth, imgHeight - (sourceY / imgHeight), srcWidth, srcHeight);
    
    GUI.DrawTextureWithTexCoords(position, image, texCoords, true);
}

Created by: X

Just another personal website in this crazy online world

Name of author Dean Lunz (aka Created by: X)
Computer programming nerd, and tech geek.
About Me -- Resume