Demo





Features

XmlLayout allows you to create fully functional user interfaces and UI elements using XML.
  • Utilise all of the functionality of UnityGUI through XML
  • Supports event handling, on any element (onClick, onMouseEnter, onMouseExit, onValueChanged, etc.)
  • Easily retrieve form data from multiple elements at once with XmlLayout.GetFormData()
  • Access and create Xml Elements dynamically at runtime
  • Includes Xml Scheme Definition (XSD) for intellisense support (autocomplete).
  • Set default values to be used by other elements (e.g. visual styles)


Example Code

Basic Concepts

Each XmlLayout object may have Xml specified manually (either in the editor or in code), or it may load Xml directly from a file.
Additionally, each XmlLayout object may have an optional XmlLayoutController (which is coded specifically for that XmlLayout) which:
  1. Performs the setup of dynamic objects and lists (such as dropdown lists)
  2. Is responsible for handling any events fired by the XmlLayout object e.g. <Button onClick='MethodName();' /> would execute the XmlLayoutController's MethodName() method (if it exists) when the button is clicked.

XmlLayout Elements

Most elements available in XmlLayout are standard Unity 4.6+ GUI elements, e.g. VerticalLayout, GridLayout, Button, InputField, etc.
However, some additional elements have been provided to provide a more complete UI solution:
  • TableLayout

    TableLayout is a LayoutGroup inspired by HTML Tables, allowing you to organise UI elements using rows and columns. As with HTML tables, cells can span multiple rows.
  • ToggleButton / ToggleButtonGroup

    ToggleButton is essentially a Toggle element, but with the appearance of a Button.

Styles

A small collection of default style values used by all of the examples.

                    

    <XmlLayout
  xmlns="XmlLayout"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">

  <!-- 
  NOTE: Now controlled by the 'Color Scheme Manager'
  <Include path="Xml/ColorSchemes/GreenYellow" />
  -->
  
  <Defaults>
    <Text color="text_Base" alignment="MiddleLeft" font="Arial" />
    <Text class="title" alignment="MiddleCenter" color="titleTextColor" fontSize="32" shadow="shadowColor" font="Arial Bold" />
    <Text class="h1" font="Arial Bold" fontSize="24" />
    <Text class="h2" font="Arial Bold" fontSize="16" color="titleTextColor" shadow="shadowColor" />
    <Text class="h3" font="Arial Bold" fontSize="12" />

    <TableLayout class="noCellBackground" cellBackgroundColor="clear" />
    
    <Row class="headerRow" dontUseTableRowBackground="true" image="Sprites/Layout/Title" color="titleRowColor" preferredHeight="64" />
    <Row class="sectionHeaderRow" dontUseTableRowBackground="true" image="Sprites/Layout/Button" color="sectionHeaderRowColor" preferredHeight="36" />
    <Row class="viewport"  dontUseTableRowBackground="true" image="Sprites/Layout/Viewport" color="viewportColor" />

    <Button font="Arial Bold" image="Sprites/Layout/Button" colors="button_Base|button_Active|button_Active|button_Disabled" textColor="buttonText" textShadow="shadowColor" />

    <Button class="disabled" colors="button_Disabled|button_Disabled|button_Disabled|button_Disabled" textColor="buttonText_Disabled" interactable="false" />        
    
    <!--<Button class="submit" colors="rgb(0.1,0.4,0)|rgb(0.1,0.4,0)|rgb(0.1,0.4,0)" textColor="rgb(0,0.5,0)" />-->

    <ToggleButton font="Arial Bold" 
                  image="Sprites/Layout/Button" 
                  colors="button_Base|button_Active|button_Active|button_Disabled"
                  textColors="text_Faded|text_Highlighted|text_Highlighted"
                  deselectedBackgroundColor="toggleButton_Deselected"                  
                  deselectedIconColor="toggleButtonIcon_Deselected"
                  selectedBackgroundColor="toggleButton_Selected"                  
                  selectedIconColor="toggleButtonIcon_Selected"   
                  textShadow="shadowColor" />

    <VerticalScrollView class="scrollView"
                    color="scrollViewBackground"
                    image="Sprites/Layout/Base"
                    movementType="Clamped"
                    scrollSensitivity="15"
                    scrollbarBackgroundColor="scrollbarBackground"
                    scrollbarColors="scrollbar_Base|scrollbar_Active|scrollbar_Active|scrollbar_Disabled"
                    scrollbarImage="Sprites/Layout/Scrollbar" />

    <HorizontalScrollView class="scrollView"
                    color="scrollViewBackground"
                    image="Sprites/Layout/Base"
                    movementType="Clamped"
                    scrollSensitivity="15"
                    scrollbarBackgroundColor="scrollbarBackground"
                    scrollbarColors="scrollbar_Base|scrollbar_Active|scrollbar_Active|scrollbar_Disabled"
                    scrollbarImage="Sprites/Layout/Scrollbar" />

    <!-- Audio 'Styles' -->
    <Button onClickSound="Audio/beep-timber" audioVolume="0.25" audioMixerGroup="Audio/XmlLayoutExampleAudioMixer|Master/Interface" />

    <Tooltip tooltipBorderColor="tooltipBorder" tooltipTextColor="tooltipText" />

    <!-- Hover Styles (new in v1.21) -->
    <Slider backgroundColor="sliderBackground" fillColor="slider_Base" handleColor="slider_Base" hoverClass="hover" navigation="Vertical" />
    <Slider class="hover" fillColor="slider_Active" handleColor="slider_Active" />    
    
    <Dropdown colors="dropdown_Base|dropdown_Active" textColor="dropdownText_Base" hoverClass="hover" />
    <Dropdown class="hover" textColor="dropdownText_Active" />

    <Toggle colors="toggle_Base|toggle_Active|toggle_Active" />

    <InputField colors="inputField_Base|inputField_Active|inputField_Active" textColor="inputFieldText_Base" hoverClass="hover" />
    <InputField class="hover" textColor="inputFieldText_Active" />
        
    <Button hoverClass="hover" />        
    <Button class="hover" textColor="buttonText_Active" />        
    
    <!-- Cursors -->
    <Button cursor="Cursors/ExampleCursor_Hover|0 0" />
    <InputField cursor="Cursors/ExampleCursor_Hover|0 0" />
    <Toggle cursor="Cursors/ExampleCursor_Hover|0 0" />
    <ToggleButton cursor="Cursors/ExampleCursor_Hover|0 0" />
    <Slider cursor="Cursors/ExampleCursor_Hover|0 0" />        
  </Defaults>  
</XmlLayout>                                
                                

Example Menu

The menu used to select an example to view, demonstrating one way of adding elements dynamically using a template.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">
  
  <Include path="Xml/Styles.xml" />   

  <TableLayout class="noCellBackground" height="450" width="256" rectAlignment="MiddleLeft" offsetXY="32 0" useGlobalCellPadding="false"  raycastTarget="1">
    <Row class="headerRow">
      <Cell>
        <Text class="title">          
          Examples
        </Text>
      </Cell>
    </Row>
    <Row class="viewport">
      <Cell dontUseTableCellBackground="true">
        <VerticalLayout padding="10" spacing="5" id="menuButtons">          
          <!-- This is a template which will be used by the XmlLayoutController to create the menu buttons dynamically -->
          <Button id="menuButtonTemplate" active="false"></Button>          
        </VerticalLayout>
      </Cell>
    </Row>    
  </TableLayout>  
</XmlLayout>                                
                                
    using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

namespace UI.Xml.Examples
{
    [ExecuteInEditMode]
    class XmlLayout_Example_ExampleMenu : XmlLayoutController
    {
        /// <summary>
        /// Populated in the editor
        /// </summary>
        public List<XmlLayout> Examples = new List<XmlLayout>();

        protected XmlLayout CurrentExample = null;

        private XmlElement menuButtonGroup;

        public void SelectExample(string name = null)
        {
            if (name == null)
            {
                CurrentExample = null;
                HideAllExamples();

                return;
            }

            var newExample = Examples.FirstOrDefault(e => e.name == name);

            if (newExample != null)
            {
                if (CurrentExample != null && newExample != CurrentExample)
                {
                    // Hide the current example, then call ShowExample(newExample) when the hide animation is complete
                    CurrentExample.Hide(() => ShowExample(newExample));
                }
                else
                {
                    ShowExample(newExample);
                }
            }
            else
            {
                // Special handling, this is a different scene
                switch (name)
                {
                    case "Drag & Drop":
                        UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Drag & Drop Example");
                        break;
                    case "Localization":
                        UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Localization Example");
                        break;
                    case "World Space":
                        UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("World Space Example");
                        break;
                }
            }
        }

        void ShowExample(XmlLayout newExample)
        {
            // Hiding all but the current example helps prevent issues where the user rapidly selects multiple examples at once
            foreach (var example in Examples)
            {
                if (example != newExample) example.Hide();
            }

            newExample.Show();

            CurrentExample = newExample;
        }

        public void HideAllExamples()
        {
            foreach (var example in Examples)
            {
                if (example.gameObject.activeInHierarchy) example.Hide();
            }
        }

        /// <summary>
        /// LayoutRebuilt is called by XmlLayout whenever the layout is finished being built
        /// (regardless of whether the rebuild was triggered automatically or manually)
        /// </summary>
        public override void LayoutRebuilt(ParseXmlResult parseResult)
        {
            if (parseResult != ParseXmlResult.Changed) return;

            // get the menu button container
            menuButtonGroup = xmlLayout.GetElementById("menuButtons");

            // get the menu button template so that we can clone it
            var menuButtonTemplate = xmlLayout.GetElementById("menuButtonTemplate");

            foreach (var example in Examples)
            {
                var name = example.name;

                AddMenuButton(name, menuButtonGroup, menuButtonTemplate);
            }

            AddMenuButton("Drag & Drop", menuButtonGroup, menuButtonTemplate);
            AddMenuButton("Localization", menuButtonGroup, menuButtonTemplate);
            AddMenuButton("World Space", menuButtonGroup, menuButtonTemplate);
        }

        void AddMenuButton(string name, XmlElement menuButtonGroup, XmlElement menuButtonTemplate)
        {
            // Create a copy of the template
            var menuButton = GameObject.Instantiate(menuButtonTemplate);
            menuButton.name = name;

            // Access the XmlElement component and initialise it for this new button
            var xmlElement = menuButton.GetComponent<XmlElement>();
            xmlElement.Initialise(xmlLayout, (RectTransform)menuButton.transform, menuButtonTemplate.tagHandler);

            // Add the xmlElement to the menuButtonGroup
            menuButtonGroup.AddChildElement(menuButton);

            // Set the necessary attributes, and Apply them
            xmlElement.SetAttribute("text", name);
            xmlElement.SetAttribute("active", "true");  // the template is inactive (so as not to be visible), so we need to activate this button
            xmlElement.SetAttribute("onClick", "SelectExample(" + name + ");"); // Call the SelectExample function (in this XmlEventReceiver)

            xmlElement.SetAttribute("tooltip", "Show the <color=\"#00FF00\">" + name + "</color> example.");
            xmlElement.SetAttribute("tooltipPosition", "Right");
            xmlElement.SetAttribute("tooltipOffset", "15");
            xmlElement.ApplyAttributes();
        }
    }
}
                                    

Demo Overlay

A simple bar at the bottom of the screen with the XmlLayout logo and the version number.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd"
           cursor="Cursors/ExampleCursor_Default|0 0" cursorClick="Cursors/ExampleCursor_Click|0 0">
  
  <Include path="Xml/Styles.xml" />
  
  <TableLayout class="noCellBackground" width="100%" height="36" rectAlignment="LowerCenter" padding="5" image="Sprites/Layout/Gradient" color="titleRowColor"  columnWidths="288 0 72" shadow="rgba(0,0,0,0.5)">
    <Row>      
      <Cell>
        <Image image="Sprites/XmlLayout" preserveAspect="1" ignoreLayout="1" height="160" width="288" rectAlignment="MiddleCenter" offsetXY="0 2" shadow="black" outline="rgba(0.2,1,0.2,0.5)" />
      </Cell>
      <Cell><!-- Empty spacer cell --></Cell>
      <Cell>
          <Text id="versionNumber" alignment="MiddleCenter" fontStyle="Bold" color="text_Alternate" shadow="black" outline="rgba(0,0,0,0.25)" tooltip="XmlLayout V1.89" tooltipPosition="Above" tooltipTextColor="tooltipText">v1.89</Text>
      </Cell>                            
    </Row>
  </TableLayout>  
</XmlLayout>                                
                                

Options Menu

An example of an options menu, using TableLayout to position elements, and demonstrating how XmlLayout.GetFormData() can be used to obtain values from multuple input elements at once.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">
  
  <Include path="Xml/Styles.xml" />
    
  <TableLayout class="noCellBackground" height="550" width="512" rectAlignment="MiddleRight" offsetXY="-32 0" showAnimation="Grow" hideAnimation="Shrink" raycastTarget="1" name="test">
    <Row class="headerRow">
      <Cell>
        <Text class="title">
          Options
        </Text>            
      </Cell>
    </Row>
    <Row class="viewport">
      <Cell dontUseTableCellBackground="true">
        <TableLayout cellPadding="7" padding="20" cellSpacing="5" cellBackgroundImage="Sprites/Outline_Sharp" cellBackgroundColor="rgba(1,1,0,0.125)">                                   
            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Video</Text>
              </Cell>
            </Row>

            <Row preferredHeight="48">
              <Cell>
                <Text class="h3">Resolution</Text>
              </Cell>
              <Cell>
                <Dropdown id="resolution" preferredHeight="32" onValueChanged="FormChanged();">
                  <Option>1920x1080</Option>
                  <Option selected="true">960x600</Option>
                  <Option>1024x768</Option>
                  <Option>800x600</Option>
                </Dropdown>
              </Cell>
            </Row>
            <Row preferredHeight="48">
              <Cell>
                <Text class="h3">Quality</Text>
              </Cell>
              <Cell>
                <Dropdown id="quality" onValueChanged="FormChanged();">                  
                </Dropdown>
              </Cell>
            </Row>
          
            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Audio</Text>
              </Cell>
            </Row>

            <Row>
              <Cell>
                <Text class="h3">Master</Text>
              </Cell>
              <Cell>
                <Slider id="masterVolume" value="0.8" onValueChanged="FormChanged();" />
              </Cell>
            </Row>
            <Row>
              <Cell>
                <Text class="h3">Music</Text>
              </Cell>
              <Cell>
                <Slider id="musicVolume" value="0.45" onValueChanged="FormChanged();" />
              </Cell>
            </Row>
            <Row>
              <Cell>
                <Text class="h3">SFX</Text>
              </Cell>
              <Cell>              
                <Slider id="sfxVolume" value="0.55" onValueChanged="FormChanged();" />
              </Cell>
            </Row>

            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Gameplay</Text>
              </Cell>
            </Row>

            <Row>
              <Cell>
                <Text class="h3">Enable Hints</Text>
              </Cell>
              <Cell>
                <Toggle id="enableHints" onValueChanged="FormChanged();"></Toggle>
              </Cell>
            </Row>

          <Row preferredHeight="44">
            <Cell>
              <Button onClick="ResetForm();">Reset</Button>
            </Cell>
            <Cell>
              <Button id="applyButton" onClick="SubmitForm();" class="disabled">Apply</Button>
            </Cell>            
          </Row>
        </TableLayout>
      </Cell>
    </Row>
  </TableLayout>      
</XmlLayout>                                
                                
    using UnityEngine;
using UnityEngine.UI;

using System;
using System.Collections.Generic;
using System.Linq;

namespace UI.Xml.Examples
{    
    [ExecuteInEditMode]
    class XmlLayout_Example_Options : XmlLayoutController
    {
        /// <summary>
        /// This is a reference to another XmlEventReceiver object, which is set up to act as a simple modal message dialog
        /// (The reference is added in the editor)
        /// </summary>
        public XmlLayout_Example_MessageDialog MessageDialog = null;

        public override void LayoutRebuilt(ParseXmlResult parseResult)
        {            
            SetFormDefaults();
        }

        void SetFormDefaults()
        {            
            // You can easily set form defaults using code by getting hold of the elements by their ids, and then setting their value as needed

            // Note: this is completely optional - Form defaults can also be set directly in the Xml itself (but this allows you to set values programmatically)
            
            // var masterVolumeSlider = xmlLayout.GetElementById<Slider>("masterVolume");
            // masterVolumeSlider.value = 0;        

            var resolutionDropdown = xmlLayout.GetElementById<Dropdown>("resolution");
            resolutionDropdown.SetOptions("1920x1080", "960x600", "1024x768", "800x600");       // SetOptions is an extension method located in the UI.Xml namespace
            resolutionDropdown.SetSelectedValue("960x600");                                     // SetSelectedValue is an extension method located in the UI.Xml namespace

            var qualityDropdown = xmlLayout.GetElementById<Dropdown>("quality");
            qualityDropdown.SetOptions(QualitySettings.names);
            qualityDropdown.value = QualitySettings.GetQualityLevel();

            // SetFormDefaults changes the values of some of the elements on the page, which in turn triggers their event handlers,
            // which results in the 'Apply' button being highlighted by FormChanged()
            ClearApplyButtonHighlight();
        }

        void FormChanged()
        {
            // A value in the form has been changed, so we're going to highlight the 'Apply' button so that the user knows that they have to click it to save their changes
            var applyButton = xmlLayout.GetElementById("applyButton");            

            applyButton.RemoveClass("disabled");                        
        }

        void ClearApplyButtonHighlight()
        {
            var applyButton = xmlLayout.GetElementById("applyButton");

            applyButton.AddClass("disabled");            
        }

        void ResetForm()
        {
            // Rebuild the Xml Elements
            xmlLayout.RebuildLayout(true);
            
            // this will also call LayoutRebuilt()                      
        }

        void SubmitForm()
        {            
            // xmlLayout.GetFormData() returns the values of all form objects in the layout with an 'id' set (as a Dictionary<string, string>)
            var formValues = xmlLayout.GetFormData();

            // As this is only an example, we're not going to actually use these values - instead, we'll just format them into a human-readable string and show the user
            // (with the exception of the Quality setting)
            string formattedFormValues = "<b>Form Values</b>:\n----------------------------------------\n";

            foreach (var formValue in formValues)
            {
                formattedFormValues += String.Format("<b>{0}</b>: <i>{1}</i>\n", FormatFieldName(formValue.Key), formValue.Value);
            }

            formattedFormValues += "\n\n";
            formattedFormValues += "For the purposes of this example, only the <i>Quality</i> setting will take effect.";

            // Show the formatted values in a message dialog (which is also an XmlLayout)
            MessageDialog.Show("Form Submitted", formattedFormValues);

            // Retrieve the index of the selected quality level from QualitySettings.names and set the new value
            var qualitySetting = QualitySettings.names.ToList().IndexOf(formValues["quality"]);
            QualitySettings.SetQualityLevel(qualitySetting);

            // The changes have now been 'applied', so we can clear the highlight
            ClearApplyButtonHighlight();
        }

        /// <summary>
        /// Convert a variable name from 'thisPattern' to 'This Pattern'
        /// </summary>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        string FormatFieldName(string fieldName)
        {
           var s = new string(fieldName.ToCharArray().SelectMany((c, i) => i > 0 && char.IsUpper(c) ? new char[] { ' ', c } : new char[] { c }).ToArray());

           s = char.ToUpper(s[0]) + s.Substring(1);

           return s;
        }        

    }
}
                                    

Message Dialog

A simple message dialog used by the Options Menu and Test it Out examples.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd"
           height="100%" 
           width="100%" 
           rectAlignment="MiddleCenter">
  
  <Include path="Xml/Styles.xml" />

  <Panel color="rgba(0,0,0,0.75)" name="Overlay" raycastTarget="1">

    <TableLayout class="noCellBackground" height="320" width="480" rectAlignment="MiddleCenter" name="Dialog" showAnimation="Grow" hideAnimation="Shrink">
      <Row class="headerRow" preferredHeight="52">
        <Cell>
          <Text id="titleText" class="title" fontSize="28">          
            Dialog Title
          </Text>
        </Cell>
      </Row>
      <Row class="viewport">
        <Cell>
          <TableLayout class="noCellBackground" cellPadding="10">
            <Row>
              <Cell columnSpan="3">
                <Panel>
                  <Text id="messageText" alignment="MiddleCenter" width="80%" fontSize="14" shadow="rgba(0,0,0,0.5)">Dialog Text</Text>
                </Panel>
              </Cell>
            </Row>
            <Row preferredHeight="48">
              <Cell></Cell>
              <Cell>
                <Button onClick="Hide();" preferredWidth="50">Close</Button>
              </Cell>
              <Cell></Cell>
            </Row>                
          </TableLayout>
        </Cell>
      </Row>    
    </TableLayout>
    
  </Panel>
</XmlLayout>                                
                                
    using UnityEngine;
using UnityEngine.UI;

using System;
using System.Collections.Generic;
using System.Collections;

namespace UI.Xml.Examples
{    
    public class XmlLayout_Example_MessageDialog : XmlLayoutController
    {
        XmlElementReference<Text> titleText;
        XmlElementReference<Text> messageText;
        
        void Awake()
        {                        
            titleText = XmlElementReference<Text>("titleText");
            messageText = XmlElementReference<Text>("messageText");            
        }

        public void Show(string title, string text)
        {
            xmlLayout.Show();

            // Because this dialog may not have been active yet at this point,
            // we need to wait a frame to make sure that the XmlLayout has finished setting up,
            // and that the titleText and messageText objects have been populated by Start()
            StartCoroutine(DelayedShow(title, text));            
        }

        protected IEnumerator DelayedShow(string title, string text)
        {            
            yield return new WaitForEndOfFrame();
            
            titleText.element.text = title;
            messageText.element.text = text;
        }        

        public void AppendText(string newText)
        {
            Show(this.titleText.element.text, messageText.element.text + "\r\n\r\n" + newText);            
        }

        public override void LayoutRebuilt(ParseXmlResult parseResult)
        {
            // start with the root XmlElement
            Localize(xmlLayout.XmlElement);
        }

        private void Localize(XmlElement element)
        {
            if (element.attributes.ContainsKey("localized"))
            {
                // localize this element using the dictionary
            }

            if (element.childElements.Count > 0)
            {
                foreach (var child in element.childElements) 
                {
                    // skip ChildXmlLayouts (and consequently their children)
                    if (child.tagType == "ChildXmlLayout") continue;

                    Localize(child);
                }                
            }
        }
    }
}
                                    

Shop Styles

A collection of styles used by the Shop and Currency Overlay examples.

                    

    <XmlLayout
  xmlns="XmlLayout"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">

  <Defaults>        
    <!-- Product Styles -->
    <Panel class="product" image="Sprites/Layout/Base" color="rgb(0.9,1,0.9)" padding="10" shadow="black" />
    <Panel class="productImageOutline" image="Sprites/Outline_With_Background" color="rgba(0.05,0.05,0.05,0.75)" />
    <Button class="productButton" colors="#CDAD00|#FFD700|#FFD700" textColor="#FFFF00" image="Sprites/Layout/Base" textOutline="black" />    
    <Panel class="productImage" color="white" height="70%" preserveAspect="1" rectAlignment="LowerCenter" />    
    <Text class="productQuantity" alignment="UpperCenter" outline="black" fontSize="16" font="Arial Bold" />    
    
    <Panel class="productBestDealBack" image="Sprites/Layout/Button" color="rgb(1,0,0,0.6)" height="20" rectAlignment="LowerCenter" offsetXY="0 -4" />
    <Text class="productBestDealText" fontSize="12" alignment="MiddleCenter" color="#FFFF00" font="Arial Bold" />
    <!-- /Product Styles -->

    <!-- Styles for Confirm Purchase Dialog -->
    <Text class="headerText" alignment="MiddleCenter" fontSize="18" shadow="black" font="Arial Bold" />    

    <Text class="confirmPurchaseText" alignment="MiddleCenter" font="Arial Bold" outline="black" fontSize="24" />
    <!-- /Styles for Confirm Purchase Dialog -->        

  </Defaults>

</XmlLayout>                                
                                

Shop

An example of an in-game shop demonstrating scroll views, TableLayout, and dynamically added elements.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">

  <Include path="Xml/Styles.xml" />
  <Include path="Xml/ShopStyles.xml" />

  <TableLayout class="noCellBackground" height="384" width="512" rectAlignment="MiddleRight" offsetXY="-32 0" useGlobalCellPadding="false" showAnimation="Grow" hideAnimation="FadeOut"  raycastTarget="1">
    <Row class="headerRow">
      <Cell>
        <Text class="title">          
          Shop
        </Text>
      </Cell>
    </Row>    
    <Row class="viewport">
      <Cell padding="15">        
        <VerticalScrollView class="scrollView">
          <TableLayout id="shopContent" cellSpacing="15" cellBackgroundColor="clear" autoCalculateHeight="1" padding="8 24 8 8">
            <Row preferredHeight="128">
              <Cell active="false">                
                
                <!-- Product Template -->
                <Panel id="productTemplate" class="product" active="false">                                      
                  <TableLayout cellBackgroundColor="clear" cellSpacing="5">
                    <Row preferredHeight="72">
                      <Cell>
                        <Panel class="productImageOutline">
                          <Panel padding="4">
                            <Text internalId="productQuantity" class="productQuantity">x1</Text>
                          </Panel>
                          <Panel type="Simple" internalId="productImage" class="productImage">
                            <Panel internalId="productBestDeal" class="productBestDealBack" active="false">
                              <Text shadow="black" class="productBestDealText">Best Deal!</Text>
                            </Panel>
                          </Panel>
                        </Panel>
                      </Cell>                      
                    </Row>
                    <Row>                      
                      <Cell>
                        <Button internalId="productBuyButton" class="productButton">$0.00</Button>                        
                      </Cell>
                    </Row>
                  </TableLayout>                  
                </Panel>                
                <!-- End of Product Template -->
                
              </Cell>              
            </Row>                                    
          </TableLayout>
        </VerticalScrollView>
      </Cell>
    </Row>    
  </TableLayout>  
</XmlLayout>                                
                                
    using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using UI.Tables;

namespace UI.Xml.Examples
{    
    class XmlLayout_Example_Shop : XmlLayoutController
    {
        public XmlLayout_Example_Shop_ConfirmDialog ConfirmPurchaseDialog = null;
        public XmlLayout_Example_CurrencyOverlay CurrencyOverlay = null;

        [SerializeField]
        public List<ExampleProduct> Products = new List<ExampleProduct>();        

        void OnEnable()
        {
            CurrencyOverlay.Show();            
        }

        void OnDisable()
        {
            CurrencyOverlay.Hide();
        }

        void OnValidate()
        {
            if (!this.gameObject.activeInHierarchy) return;

            if (Application.isPlaying)
            {
                StartCoroutine(Rebuild());
            }            
        }

        public IEnumerator Rebuild()
        {
            yield return new WaitForEndOfFrame();
            xmlLayout.RebuildLayout();
        }

        public override void LayoutRebuilt(ParseXmlResult parseResult)
        {            
            if (parseResult != ParseXmlResult.Changed || Products == null || !Products.Any()) return;

            var shopContent = xmlLayout.GetElementById<TableLayout>("shopContent");
            var itemTemplate = xmlLayout.GetElementById("productTemplate");

            var columnCount = 4;
            var column = 0;
            var rows = shopContent.Rows.ToList();
            var productCount = Products.Count;

            var rowHeight = rows.First().preferredHeight;

            TableRow row;
            if (rows.Any())
            {
                row = rows.Last();
            }
            else
            {
                row = shopContent.AddRow(0);
                row.preferredHeight = rowHeight;
            }            
            
            for(var x = 0; x < productCount; x++)
            {
                var product = Products[x];

                var item = GameObject.Instantiate(itemTemplate);                
                item.Initialise(xmlLayout, (RectTransform)item.transform, itemTemplate.tagHandler);

                item.gameObject.SetActive(true); // the template is inactive so as not to show up, so we need to activate our new object

                // Add a new cell to the row (containing our new product)
                row.AddCell(item.rectTransform);                

                HandleProduct(product, item, x);

                // increment column count
                column++;

                // move to the next row, if necessary
                if (column == columnCount && (x + 1) < productCount)
                {
                    column = 0;
                    row = shopContent.AddRow(0);
                    row.preferredHeight = rowHeight;
                }
            }            
        }

        void HandleProduct(ExampleProduct product, XmlElement item, int productId)
        {
            var image = item.GetElementByInternalId<Image>("productImage");            
            if (product.Image != null)
            {
                image.sprite = product.Image;
            }
            image.color = Color.white;

            var button = item.GetElementByInternalId<Button>("productBuyButton");         
            button.GetComponentInChildren<Text>().text = String.Format("${0}", product.Price);
            button.onClick.AddListener(new UnityEngine.Events.UnityAction(() => { PurchaseButtonClicked(productId); }));            

            var productQuantity = item.GetElementByInternalId<Text>("productQuantity");
            productQuantity.text = String.Format("x{0}", product.Quantity);

            if (product.IsBestDeal)
            {
                var ribbon = item.GetElementByInternalId<Image>("productBestDeal");
                ribbon.gameObject.SetActive(true);
            }
        }

        void PurchaseButtonClicked(int productId)
        {
            var product = Products[productId];

            ConfirmPurchaseDialog.Show(product, PurchaseConfirmed);
        }

        public void PurchaseConfirmed(ExampleProduct product)
        {
            CurrencyOverlay.AddCurrency(product);
        }        
    }    

    [Serializable]
    public class ExampleProduct
    {
        public float Price = 0;
        public string Name = "";
        public int Quantity;
        public Sprite Image;
        public bool IsBestDeal = false;
    }
}
                                    

Shop Confirm Purchase Dialog

A dialog, similar to Message Dialog which the Shop example uses to confirm purchases.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd"
           height="100%" 
           width="100%" 
           rectAlignment="MiddleCenter">

  <Include path="Xml/Styles.xml" />
  <Include path="Xml/ShopStyles.xml" />

  <Panel color="rgba(0,0,0,0.75)" name="Overlay">

    <TableLayout class="noCellBackground" height="256" width="384" rectAlignment="MiddleCenter" name="Dialog" showAnimation="Grow" hideAnimation="Shrink"  raycastTarget="1">
      <Row class="headerRow" preferredHeight="52">
        <Cell>
          <Text id="titleText" class="title" fontSize="28">          
            Confirm Purchase
          </Text>
        </Cell>
      </Row>
      <Row class="viewport">
        <Cell>
          <TableLayout class="noCellBackground" cellPadding="10" width="95%" ignoreLayout="1">
            <Row>
              <Cell columnSpan="2">                
                <TableLayout cellPadding="10" cellBackgroundColor="rgb(0.35,0.35,0.35)">
                  <Row preferredHeight="48" class="sectionHeaderRow">
                    <Cell>
                      <Text class="headerText">Product</Text>
                    </Cell>
                    <Cell>
                      <Text class="headerText">Quantity</Text>
                    </Cell>
                    <Cell>
                      <Text class="headerText">Price</Text>
                    </Cell>
                  </Row>
                  <Row>                    
                    <Cell>
                      <Image id="productImage" image="Sprites/Shop/Coin" preserveAspect="1" />
                    </Cell>
                    <Cell>
                      <Text id="productQuantity" class="confirmPurchaseText">x1</Text>
                    </Cell>
                    <Cell>
                      <Text id="productPrice" class="confirmPurchaseText">$0.00</Text>
                    </Cell>
                  </Row>
                </TableLayout>                               
              </Cell>
            </Row>
            <Row preferredHeight="48">              
              <Cell>
                <Button onClick="ConfirmPurchase();">Confirm Purchase</Button>
              </Cell>
              <Cell>
                <Button onClick="Hide();">Cancel</Button>
              </Cell>              
            </Row>                
          </TableLayout>
        </Cell>
      </Row>    
    </TableLayout>
    
  </Panel>
</XmlLayout>                                
                                
    using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using UI.Tables;

namespace UI.Xml.Examples
{    
    class XmlLayout_Example_Shop_ConfirmDialog : XmlLayoutController
    {
        ExampleProduct product = null;
        Action<ExampleProduct> callback = null;                

        public void Show(ExampleProduct product, Action<ExampleProduct> callback = null)
        {
            xmlLayout.Show();

            this.product = product;
            this.callback = callback;

            // Because this dialog may not have been active yet at this point,
            // we need to wait a frame to make sure that the XmlLayout has finished setting up            
            StartCoroutine(DelayedShow());
        }

        protected IEnumerator DelayedShow()
        {
            while(!xmlLayout.IsReady) yield return null;

            xmlLayout.GetElementById<Image>("productImage").sprite = product.Image;
            xmlLayout.GetElementById<Text>("productQuantity").text = String.Format("x{0}", product.Quantity);
            xmlLayout.GetElementById<Text>("productPrice").text = String.Format("${0}", product.Price);
        }

        void ConfirmPurchase()
        {
            callback(product);

            Hide();
        }        
    }
}
                                    

Currency Overlay

A UI element showing the resources available to the user (as 'purchased' in the Shop example.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">
  
  <Include path="Xml/Styles.xml" />

  <Defaults>
    <Text shadow="black" font="Arial Bold"></Text>
    <TableLayout class="item" columnWidths="32 0" />
    <Image preserveAspect="1" shadow="black" />
  </Defaults>

  <Panel height="32" rectAlignment="UpperCenter" image="Sprites/Layout/Gradient" color="titleRowColor" showAnimation="SlideIn_Top" hideAnimation="SlideOut_Top" animationDuration="0.5">
    <TableLayout class="noCellBackground" rectAlignment="MiddleRight" width="356" cellPadding="5">
      <Row preferredHeight="0">        
        <Cell>
          <TableLayout class="noCellBackground item">
            <Row>
              <Cell>
                <Image image="Sprites/Shop/coin" />
              </Cell>
              <Cell>                
                <Text id="0">x0</Text>
              </Cell>
            </Row>
          </TableLayout>
        </Cell>
        <Cell>
          <TableLayout class="noCellBackground item">
            <Row>
              <Cell>
                <Image image="Sprites/Shop/gemGreen" />
              </Cell>
              <Cell>
                <Text id="1">x0</Text>
              </Cell>
            </Row>
          </TableLayout>
        </Cell>
        <Cell>
          <TableLayout class="noCellBackground item">
            <Row>
              <Cell>
                <Image image="Sprites/Shop/gemBlue" />
              </Cell>
              <Cell>
                <Text id="2">x0</Text>
              </Cell>
            </Row>
          </TableLayout>
        </Cell>
        <Cell>
          <TableLayout class="noCellBackground item">
            <Row>
              <Cell>
                <Image image="Sprites/Shop/gemRed" />
              </Cell>
              <Cell>
                <Text id="3">x0</Text>
              </Cell>
            </Row>
          </TableLayout>
        </Cell>
      </Row>
    </TableLayout>
  </Panel>
</XmlLayout>                                
                                
    using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using UI.Tables;

namespace UI.Xml.Examples
{    
    class XmlLayout_Example_CurrencyOverlay : XmlLayoutController
    {
        [SerializeField]
        public List<ExampleProduct> CurrencyQuantities = new List<ExampleProduct>()
        {
            new ExampleProduct() { Name = "Coins", Quantity = 0 },
            new ExampleProduct() { Name = "Green Gems", Quantity = 0 },
            new ExampleProduct() { Name = "Blue Gems", Quantity = 0 },
            new ExampleProduct() { Name = "Red Gems", Quantity = 0 }
        };        

        public void AddCurrency(ExampleProduct productPurchased)
        {
            var currency = CurrencyQuantities.First(c => c.Name == productPurchased.Name);

            currency.Quantity += productPurchased.Quantity;

            UpdateDisplay();
        }

        public override void LayoutRebuilt(ParseXmlResult parseResult)
        {
            UpdateDisplay();
        }

        public void UpdateDisplay()
        {
            for (var x = 0; x < CurrencyQuantities.Count; x++)
            {
                var text = xmlLayout.GetElementById<Text>(x.ToString());

                text.text = String.Format("x{0}", CurrencyQuantities[x].Quantity);
            }
        }        
    }
}
                                    

Element List

An example showcasing the various elements available.

                    

    <XmlLayout xmlns="XmlLayout"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd" showAnimation="Grow" hideAnimation="FadeOut">

  <Include path="Xml/Styles.xml" />  

  <Defaults>
    <Panel color="accentVariantC" image="Sprites/Outline_Sharp" />
    <Text class="label" alignment="MiddleCenter" color="rgb(1,1,1,0.5)" />
    <Text class="h2" alignment="MiddleCenter" />    
    <Toggle textcolor="white" />

    <Button class="iconButton" image="Sprites/Layout/Base" iconColor="text_Highlighted" iconHoverColor="text_Highlighted" />
    <Button class="iconButtonWithText" iconWidth="48" padding="4 0 2 2" ignoreLayout="1" width="160" height="32" />

    <ToggleButton class="iconButton" image="Sprites/Layout/Base" />
    <ToggleButton class="iconButtonWithText" iconWidth="48" padding="4 0 2 2" ignoreLayout="1" width="160" height="32" />
  </Defaults>  

  <TableLayout class="noCellBackground" height="512" width="512" rectAlignment="MiddleRight" offsetXY="-32 0"  raycastTarget="1">
    <Row class="headerRow">
      <Cell>
        <Text class="title">
          Element List
        </Text>
      </Cell>
    </Row>
    <Row class="viewport">
      <Cell>
        <VerticalScrollView class="scrollView" color="rgba(0,0,0,0.25)">
          <TableLayout cellPadding="7" padding="20" cellSpacing="5" cellBackgroundImage="Sprites/Outline_Sharp" cellBackgroundColor="rgba(1,1,0,0.125)" autoCalculateHeight="1" columnWidths="180 0">
            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Basic Elements</Text>
              </Cell>
            </Row>

            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Text Label</Text>
              </Cell>
              <Cell>
                <Text alignment="MiddleCenter">
                  This <i>is</i> some <textcolor color="green">rich</textcolor> text.
                </Text>
              </Cell>
            </Row>

            <Row preferredHeight="64">
              <Cell>
                <Text class="h3">Image</Text>
              </Cell>
              <Cell>
                <Image image="Sprites/Shop/gemGreen" preserveAspect="1" />                
              </Cell>
            </Row>

            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Input Elements</Text>
              </Cell>
            </Row>
            
            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Input Field</Text>
              </Cell>
              <Cell>
                <InputField />
              </Cell>
            </Row>
            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Toggle</Text>
              </Cell>
              <Cell>
                <Toggle />
              </Cell>
            </Row>
            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Toggle Group</Text>
              </Cell>
              <Cell>
                <ToggleGroup>
                  <HorizontalLayout>
                    <Toggle isOn="1">A</Toggle>
                    <Toggle>B</Toggle>
                    <Toggle>C</Toggle>
                    <Toggle>D</Toggle>
                  </HorizontalLayout>
                </ToggleGroup>
              </Cell>
            </Row>            

            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Slider</Text>
              </Cell>
              <Cell>
                <Slider minValue="0" maxValue="1" value="0.5" />
              </Cell>
            </Row>

            <Row preferredHeight="48">
              <Cell>
                <Text class="h3" alignment="UpperLeft">Dropdown</Text>
              </Cell>
              <Cell>
                <Dropdown>                  
                  <Option selected="1">Option 1</Option>
                  <Option>Option 2</Option>
                  <Option>Option 3</Option>
                  <Option>Option 4</Option>
                  <Option>Option 5</Option>
                  <Option>Option 6</Option>
                  <Option>Option 7</Option>
                  <Option>Option 8</Option>
                </Dropdown>
              </Cell>
            </Row>

            <Row preferredHeight="48">
              <Cell>
                <Text class="h3">Button</Text>                
              </Cell>
              <Cell>
                <Button>Button Text</Button>
              </Cell>
            </Row>

            <Row preferredHeight="48">
              <Cell>
                <Text class="h3">Button with icon</Text>
              </Cell>
              <Cell>                
                <GridLayout cellSize="32,32" spacing="10" childAlignment="MiddleCenter">
                  <Button class="iconButton" icon="Sprites/Icons/Move" />
                  <Button class="iconButton" icon="Sprites/Icons/Cog" />
                  <Button class="iconButton" icon="Sprites/Icons/Cancel" />
                  <Button class="iconButton" icon="Sprites/Icons/Arrow_Right" />
                </GridLayout>
              </Cell>
            </Row>

            <Row preferredHeight="48">
              <Cell>
                <Text class="h3">Button with left-side icon</Text>
              </Cell>
              <Cell>
                <Button class="iconButton iconButtonWithText" icon="Sprites/Icons/Arrow_Left" textAlignment="MiddleLeft">Button Text</Button>
              </Cell>
            </Row>

            <Row preferredHeight="48">
              <Cell>
                <Text class="h3">Button with right-side icon</Text>
              </Cell>
              <Cell>
                <Button class="iconButton iconButtonWithText" icon="Sprites/Icons/Arrow_Right" iconAlignment="Right" textAlignment="MiddleRight">Button Text</Button>
              </Cell>
            </Row>

            <Row preferredHeight="48">
              <Cell>
                <Text class="h3">Toggle Button</Text>
              </Cell>
              <Cell>
                <ToggleButton class="iconButton iconButtonWithText" icon="Sprites/Icons/Arrow_Left" textAlignment="MiddleLeft">Button Text</ToggleButton>
              </Cell>
            </Row>

            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Toggle Button Group</Text>
              </Cell>
              <Cell>
                <ToggleGroup>
                  <GridLayout cellSize="32,32" spacing="10" childAlignment="MiddleCenter">
                    <ToggleButton class="iconButton" icon="Sprites/Icons/Arrow_Left" isOn="1"></ToggleButton>
                    <ToggleButton class="iconButton" icon="Sprites/Icons/Arrow_Up"></ToggleButton>
                    <ToggleButton class="iconButton" icon="Sprites/Icons/Arrow_Down"></ToggleButton>
                    <ToggleButton class="iconButton" icon="Sprites/Icons/Arrow_Right"></ToggleButton>
                  </GridLayout>
                </ToggleGroup>
              </Cell>
            </Row>

            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Misc Elements</Text>
              </Cell>
            </Row>

            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Progress Bar</Text>
              </Cell>
              <Cell>
                <ProgressBar id="progressBar" percentage="0" showPercentageText="1" fontStyle="Bold" textColor="text_Alternate" textShadow="shadowColor" fillImageColor="accent" image="Sprites/Layout/Gradient" fillImage="Sprites/Layout/Button" percentageTextFormat="0.00" />
              </Cell>
            </Row>            
                 
            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Layout Elements</Text>
              </Cell>
            </Row>

            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Panel</Text>
              </Cell>
              <Cell>
                <Panel  image="Sprites/Outline_Sharp"></Panel>
              </Cell>
            </Row>
            <Row preferredHeight="44">
              <Cell>
                <Text class="h3">Horizontal Layout</Text>
              </Cell>
              <Cell>
                <HorizontalLayout spacing="5">
                  <Panel>
                    <Text class="label">1</Text>
                  </Panel>
                  <Panel>
                    <Text class="label">2</Text>
                  </Panel>
                  <Panel>
                    <Text class="label">3</Text>
                  </Panel>
                  <Panel>
                    <Text class="label">4</Text>
                  </Panel>                  
                </HorizontalLayout>
              </Cell>
            </Row>
            <Row preferredHeight="128">
              <Cell>
                <Text class="h3" alignment="UpperLeft">Vertical Layout</Text>
              </Cell>
              <Cell>
                <VerticalLayout spacing="2">
                  <Panel>
                    <Text class="label">1</Text>
                  </Panel>
                  <Panel>
                    <Text class="label">2</Text>
                  </Panel>
                  <Panel>
                    <Text class="label">3</Text>
                  </Panel>
                  <Panel>
                    <Text class="label">4</Text>
                  </Panel>
                </VerticalLayout>
              </Cell>
            </Row>
            <Row preferredHeight="80">
              <Cell>
                <Text class="h3" alignment="UpperLeft">Grid Layout</Text>
              </Cell>
              <Cell>
                <GridLayout spacing="4" cellSize="48,32" childAlignment="MiddleCenter">
                  <Button>1</Button>
                  <Button>2</Button>
                  <Button>3</Button>
                  <Button>4</Button>
                  <Button>5</Button>
                  <Button>6</Button>
                  <Button>7</Button>
                  <Button>8</Button>
                  <Button>9</Button>
                  <Button>10</Button>                                
                </GridLayout>
              </Cell>
            </Row>

            <Row preferredHeight="128">
              <Cell>
                <Text class="h3" alignment="UpperLeft">Table Layout</Text>
              </Cell>
              <Cell>
                <TableLayout cellSpacing="1">
                  <Row class="sectionHeaderRow" preferredHeight="32">
                    <Cell>
                      <Text class="h2">C1</Text>
                    </Cell>
                    <Cell>
                      <Text class="h2">C2</Text>
                    </Cell>
                    <Cell>
                      <Text class="h2">C3</Text>
                    </Cell>
                  </Row>
                  <Row>
                    <Cell>
                      <Text class="label">1</Text>
                    </Cell>
                    <Cell>
                      <Text class="label">2</Text>
                    </Cell>
                    <Cell>
                      <Text class="label">3</Text>
                    </Cell>
                  </Row>
                  <Row>
                    <Cell>
                      <Text class="label">4</Text>
                    </Cell>
                    <Cell>
                      <Text class="label">5</Text>
                    </Cell>
                    <Cell>
                      <Text class="label">6</Text>
                    </Cell>
                  </Row>
                </TableLayout>
              </Cell>
            </Row>

            <Row class="sectionHeaderRow">
              <Cell columnSpan="2" dontUseTableCellBackground="1">
                <Text class="h2" alignment="MiddleCenter">Scroll Views</Text>
              </Cell>
            </Row>

            <Row preferredHeight="128">
              <Cell>
                <Text class="h3" alignment="UpperLeft">Vertical Scrollview</Text>
              </Cell>
              <Cell>
                <VerticalScrollView class="scrollView">
                  <VerticalLayout height="512" padding="16" spacing="16">
                    <Panel>
                      <Text class="label">1</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">2</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">3</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">4</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">5</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">6</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">7</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">8</Text>
                    </Panel>
                  </VerticalLayout>
                </VerticalScrollView>
              </Cell>
            </Row>

            <Row preferredHeight="128">
              <Cell>
                <Text class="h3" alignment="UpperLeft">Horizontal Scrollview</Text>
              </Cell>
              <Cell>
                <HorizontalScrollView class="scrollView">
                  <HorizontalLayout width="512" padding="16" spacing="16">
                    <Panel>
                      <Text class="label">1</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">2</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">3</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">4</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">5</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">6</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">7</Text>
                    </Panel>
                    <Panel>
                      <Text class="label">8</Text>
                    </Panel>
                  </HorizontalLayout>
                </HorizontalScrollView>
              </Cell>
            </Row>          
          </TableLayout>
        </VerticalScrollView>
      </Cell>
    </Row>
  </TableLayout>  
</XmlLayout>                                
                                
    using UnityEngine;
using UnityEngine.UI;

using System;
using System.Collections.Generic;
using System.Collections;

namespace UI.Xml.Examples
{    
    class XmlLayout_Example_List : XmlLayoutController
    {
        XmlElementReference<XmlLayoutProgressBar> progressBar;

        void Start()
        {
            progressBar = XmlElementReference<XmlLayoutProgressBar>("progressBar");            
        }

        void Update()
        {
            progressBar.element.percentage += Time.deltaTime * 2.5f;

            if (progressBar.element.percentage >= 100f) progressBar.element.percentage = 0.0f;
        }        
    }
}
                                    

Test it Out

An example allowing you to enter your own Xml code and try it out.

                    

    <XmlLayout xmlns="XmlLayout" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd" showAnimation="Grow" hideAnimation="Shrink">
  
  <Include path="Xml/Styles.xml" />

  <Defaults>    
    <Cell class="header" dontUseTableCellBackground="1" color="titleRowColor" image="Sprites/Layout/Button" />
    <Panel class="outlined" color="rgb(0.5,0.5,0.5)" image="Sprites/Outline_Sharp" />
    <Text class="h2" alignment="MiddleCenter" />
  </Defaults>

  <TableLayout class="noCellBackground" height="512" width="880" rectAlignment="MiddleCenter" raycastTarget="1" id="mainPanel">
    <Row class="headerRow">
      <Cell>
        <TableLayout class="noCellBackground" columnWidths="64 0">
          <Row>
            <Cell>
              <Button image="Sprites/Icons/Arrow_Left" onClick="Hide()" ignoreLayout="1" preserveAspect="1" type="Simple" width="32" height="32" />
            </Cell>
            <Cell>
              <Text class="title">
                Test it out
              </Text>
            </Cell>        
          </Row>
        </TableLayout>
      </Cell>
    </Row>
    <Row class="viewport">
      <Cell dontUseTableCellBackground="true">
        <TableLayout class="noCellBackground" padding="10" cellSpacing="2" cellPadding="10" columnWidths="480 0">
          <Row preferredHeight="48">
            <Cell class="header">
              <Text class="h2">Code</Text>
            </Cell>
            <Cell class="header">              
              <Text class="h2">Output</Text>
              <Button image="Sprites/Icons/Move" onClick="ToggleViewportSize();" ignoreLayout="true" rectAlignment="MiddleRight" width="32" height="32" offsetXY="-8 0" />              
            </Cell>
          </Row>
          <Row>
            <Cell>
              <TableLayout cellSpacing="10">
                <Row preferredHeight="32">
                  <Cell>
                    <HorizontalLayout spacing="5" padding="5">
                      <Button onClick="Empty();">Empty</Button>
                      <Button onClick="ExampleA();">Example A</Button>
                      <Button onClick="ExampleB();">Example B</Button>
                      <Button onClick="ExampleC();">Example C</Button>
                    </HorizontalLayout>
                  </Cell>                  
                </Row>
                <Row>
                  <Cell dontUseTableCellBackground="1">                    
                    <VerticalScrollView class="scrollView" color="rgb(0,0,0,0.75)" id="codeInputScrollView">
                      <TableLayout columnWidths="36 0" height="1024">
                        <Row>
                          <Cell>
                            <InputField interactable="false" lineType="MultiLineNewline" fontSize="12" font="Courier New Bold" id="lineNumbers">                                                          
                            </InputField>
                          </Cell>
                          <Cell>
                            <InputField id="codeInputField" colors="clear" textColor="white" fontStyle="Bold" fontSize="12" font="Courier New" onValueChanged="XmlChanged(value);" lineType="MultiLineNewline"></InputField>
                          </Cell>
                        </Row>
                      </TableLayout>
                    </VerticalScrollView>
                  </Cell>
                </Row>
                <Row preferredHeight="32">
                  <Cell>
                    <Button onClick="UpdateDisplay();">Update</Button>
                  </Cell>                  
                </Row>                
              </TableLayout>
            </Cell>
            <Cell>
              <Mask>
                <Panel padding="10" class="outlined" id="outputContainer">
                  <Panel id="output" name="Output">
                    <!-- Output goes here -->
                  </Panel>                
                </Panel>
              </Mask>
            </Cell>
          </Row>
        </TableLayout>
      </Cell>
    </Row>
  </TableLayout>

  <TableLayout width="100%" height="100%" id="expandedOutput" active="false">
    <Row preferredHeight="48">
      <Cell class="header">
        <Text class="h2">Output</Text>
        <Button image="Sprites/Icons/Move" onClick="ToggleViewportSize();" ignoreLayout="true" rectAlignment="MiddleRight" width="32" height="32" offsetXY="-8 0" />
      </Cell>
    </Row>
    <Row class="viewport">
      <Cell>
        <Mask>
          <HorizontalLayout id="expandedOutputPanel" childForceExpandHeight="true" childForceExpandWidth="true" />
        </Mask>
      </Cell>
    </Row>
  </TableLayout>
</XmlLayout>                                
                                
    using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using UI.Tables;

namespace UI.Xml.Examples
{
    class XmlLayout_Example_TestItOut : XmlLayoutController
    {
        public XmlLayout_Example_MessageDialog MessageDialog = null;
        public XmlLayout_Example_ExampleMenu ExampleMenu = null;

        string Xml = null;

        bool viewPortExpanded = false;

        void Start()
        {
            // Select the Empty example
            Empty();
        }

        public override void Show()
        {
            base.Show();

            ScrollToTop();
        }

        public override void Hide(Action onCompleteCallback = null)
        {
            ExampleMenu.SelectExample(null);
        }

        void UpdateCodeInputField()
        {
            // populate the code input field with the Example Xml
            var codeInputField = xmlLayout.GetElementById<InputField>("codeInputField");
            codeInputField.text = Xml.Trim();

            ScrollToTop();
        }

        void ScrollToTop()
        {
            // Scroll to the top
            var codeInputScrollView = xmlLayout.GetElementById<ScrollRect>("codeInputScrollView");
            codeInputScrollView.verticalNormalizedPosition = 1;
        }

        // Called by the codeInputField when its text changes
        void XmlChanged(string newXml)
        {
            Xml = newXml != null ? newXml.Trim() : "";
        }

        void ToggleViewportSize()
        {
            var outputPanel = xmlLayout.GetElementById("output");
            var expandedOutput = xmlLayout.GetElementById("expandedOutput");
            var mainPanel = xmlLayout.GetElementById("mainPanel");

            if (!viewPortExpanded)
            {
                expandedOutput.gameObject.SetActive(true);

                var expandedOutputPanel = xmlLayout.GetElementById("expandedOutputPanel");
                outputPanel.transform.SetParent(expandedOutputPanel.transform);

                mainPanel.gameObject.SetActive(false);
            }
            else
            {
                var outputContainer = xmlLayout.GetElementById("outputContainer");
                outputPanel.transform.SetParent(outputContainer.transform);

                expandedOutput.gameObject.SetActive(false);

                mainPanel.gameObject.SetActive(true);
            }

            viewPortExpanded = !viewPortExpanded;

            UpdateDisplay();
        }

        public override void LayoutRebuilt(ParseXmlResult parseResult)
        {
            if (!Application.isPlaying) return;

            if (parseResult == ParseXmlResult.Changed)
            {
                UpdateDisplay();
            }
        }

        public void UpdateDisplay()
        {
            // Delaying until the end of the frame allows us to be certain that XmlLayout is completely set up before we execute this code
            XmlLayoutTimer.AtEndOfFrame(_UpdateDisplay, this);
        }

        void _UpdateDisplay()
        {
            var lineNumbers = xmlLayout.GetElementById<InputField>("lineNumbers");
            lineNumbers.text = String.Join( "\r\n", Enumerable.Range(1, 100).Select(i => i.ToString().PadLeft(2, '0')).ToArray());

            var outputField = xmlLayout.GetElementById("output");
            var outputFieldXmlLayout = outputField.gameObject.GetComponent<XmlLayout>() ?? outputField.gameObject.AddComponent<XmlLayout>();

            outputField.ApplyAttributes(GetOutputFieldAttributes());
            outputFieldXmlLayout.gameObject.SetActive(true);

            outputFieldXmlLayout.Hide(() =>
            {
                outputFieldXmlLayout.gameObject.SetActive(true);

                // borrow styles from the parent
                outputFieldXmlLayout.DefaultsFiles = this.xmlLayout.DefaultsFiles;

                outputFieldXmlLayout.Xml = this.Xml;
                try
                {
                    // We're using a custom log handler here so that any log/error messsages can be displayed in our message dialog
                    var oldHandler = Debug.unityLogger.logHandler;
                    Debug.unityLogger.logHandler = new TestLogHandler(MessageDialog, oldHandler);

                    outputFieldXmlLayout.RebuildLayout(false, true);

                    Debug.unityLogger.logHandler = oldHandler;
                }
                catch (Exception e)
                {
                    MessageDialog.Show("Xml Parse Error", e.Message);
                }

                outputField.ApplyAttributes(GetOutputFieldAttributes());
                outputFieldXmlLayout.Show();
            });
        }

        private AttributeDictionary GetOutputFieldAttributes()
        {
            // Essentially what we're doing here is overriding the default attributes
            // so as to animate out Output XmlLayout (even though the Xml code doesn't specify it)
            return new AttributeDictionary()
                    {
                        {"ShowAnimation", "Grow"},
                        {"HideAnimation", "FadeOut"},
                        {"AnimationDuration", "0.2"},
                    };
        }

        void Empty()
        {
            Xml = @"
<XmlLayout>
    <Include path=""Xml/Styles.xml"" />


</XmlLayout>
            ";

            UpdateCodeInputField();
            UpdateDisplay();
        }

        void ExampleA()
        {
            Xml = @"
<XmlLayout>
    <Defaults>
        <Text alignment=""MiddleCenter""
              fontStyle=""Bold""
              fontSize=""18""
              color=""white"" />

        <Text class=""header""
              color=""#00FF00""
              fontSize=""24""
              outline=""black"" />

        <Image preserveAspect=""true"" />
    </Defaults>

    <TableLayout cellPadding=""10"" cellSpacing=""5"">
        <Row preferredHeight=""48"">
            <Cell columnSpan=""3"">
                <Text class=""header"">Gems</Text>
            </Cell>
        </Row>
        <Row>
            <Cell>
                <Image image=""Sprites/Shop/gemRed"" />
            </Cell>
            <Cell>
                <Image image=""Sprites/Shop/gemBlue"" />
            </Cell>
            <Cell>
                <Image image=""Sprites/Shop/gemGreen"" />
            </Cell>
        </Row>
        <Row preferredHeight=""48"">
            <Cell><Text>Red</Text></Cell>
            <Cell><Text>Blue</Text></Cell>
            <Cell><Text>Green</Text></Cell>
        </Row>
    </TableLayout>
</XmlLayout>
            ";

            UpdateCodeInputField();
            UpdateDisplay();
        }

        void ExampleB()
        {
            Xml = @"
<XmlLayout>
    <Include path=""Xml/Styles.xml"" />

    <VerticalLayout padding=""20"" spacing=""5"">
        <Button>Button 1</Button>
        <Button>Button 2</Button>
        <Button>Button 3</Button>
        <Button>Button 4</Button>
        <Button>Button 5</Button>
        <Button>Button 6</Button>
        <Button>Button 7</Button>
        <Button>Button 8</Button>
    </VerticalLayout>
</XmlLayout>
            ";

            UpdateCodeInputField();
            UpdateDisplay();
        }

        void ExampleC()
        {
            Xml = @"
<XmlLayout>
    <Include path=""Xml/Styles.xml"" />

    <Defaults>
        <Panel class=""cornerPanel""
               width=""100""
               height=""50""
               color=""rgba(0,0.5,0,0.5)""
               image=""Sprites/Outline_With_Background""
        />

        <Text color=""#00FF00""
              fontStyle=""Bold""
              alignment=""MiddleCenter"" />
    </Defaults>

    <Panel width=""90%""
           height=""90%""
           image=""Sprites/Outline""
           color=""rgb(0.5,0.5,0.5)"">
        <Panel class=""cornerPanel""
               rectAlignment=""UpperLeft"">
            <Text>Upper Left</Text>
        </Panel>

        <Panel class=""cornerPanel""
               rectAlignment=""UpperRight"">
            <Text>Upper Right</Text>
        </Panel>

        <Image image=""Sprites/Shop/coin""
               width=""100""
               height=""100""
               rectAlignment=""MiddleCenter""
               preserveAspect=""true""
               allowDragging=""true""
               restrictDraggingToParentBounds=""false"" />

        <Text offsetXY=""0,-48"" raycastTarget=""false"">Try dragging the coin!</Text>

        <Panel class=""cornerPanel""
               rectAlignment=""LowerLeft"">
            <Text>Lower Left</Text>
        </Panel>

        <Panel class=""cornerPanel""
               rectAlignment=""LowerRight"">
            <Text>Lower Right</Text>
        </Panel>
    </Panel>

</XmlLayout>
            ";

            UpdateCodeInputField();
            UpdateDisplay();
        }
    }

    class TestLogHandler : ILogHandler
    {
        XmlLayout_Example_MessageDialog m_MessageDialog;
        ILogHandler m_OriginalLogger;

        public TestLogHandler(XmlLayout_Example_MessageDialog messageDialog, ILogHandler originalLogger)
        {
            m_MessageDialog = messageDialog;
            m_OriginalLogger = originalLogger;
        }

        public void LogException(Exception exception, UnityEngine.Object context)
        {
            // Pass on the message to the original logger so that it can be displayed on the console
            m_OriginalLogger.LogException(exception, context);
        }

        public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args)
        {
            if (!m_MessageDialog.gameObject.activeInHierarchy)
            {
                m_MessageDialog.Show(logType.ToString(), String.Format(format, args));
            }
            else
            {
                m_MessageDialog.AppendText(String.Format(format, args));
            }

            // Pass on the message to the original logger so that it can be displayed on the console as well
            m_OriginalLogger.LogFormat(logType, context, format, args);
        }
    }
}
                                    

RTS Hud Example

An example showing the potential layout of a full-screen heads-up display.

                    

    <XmlLayout xmlns="XmlLayout"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">

  <Include path="Xml/Styles.xml" />

  <Defaults>
    <Panel class="borderOuter" padding="8" image="Sprites/Layout/Base" color="rgb(0.25,0.4,0.25)" ignoreLayout="true" rectAlignment="LowerCenter" />
    <Panel class="borderInner" image="Sprites/Layout/Indented" color="rgb(0,0.3,0)" />

    <Button class="commandButton" 
            image="Sprites/HUD/CommandButton" 
            iconColor="rgb(0.8,1,0.8)"            
            showAnimation="Grow" 
            animationDuration="0.5"
            colors="rgb(0.2,0.7,0)|rgb(0.3,1,0.3)|rgb(0.2,0.9,0)"
            padding="2" />    

    <Button class="unitIcon"
            image="Sprites/Outline"
            icon="Sprites/HUD/UnitIcon" 
            iconColor="rgb(0.4,1,0.0.4)"
            showAnimation="Grow"
            animationDuration="0.5"            
            padding="2"
            color="rgb(0,1,0)" />

    <Button class="topLeft" width="224" height="32" rectAlignment="UpperLeft" offsetXY="16 -48" />

    <Text class="resourceText" fontStyle="Bold" color="rgb(0,1,0)" horizontalOverflow="Overflow" />

    <Tooltip tooltipPosition="Above" tooltipBorderColor="rgb(0,1,0)" tooltipBackgroundColor="rgb(0,0,0)" tooltipFollowMouse="1" />
  </Defaults>

  <!-- Overlay -->
  <Panel image="Sprites/HUD/Game" color="white" type="Simple" preserveAspect="1" height="125%" width="125%" showAnimation="FadeIn" hideAnimation="FadeOut" raycastTarget="1">    
  </Panel>

  <!-- Back Button -->
  <Button class="topLeft" image="Sprites/Layout/Button" icon="Sprites/Icons/Arrow_Left" iconWidth="32" iconColor="rgb(0,1,0)" iconHoverColor="rgb(0,1,0)" padding="0 0 4 4" onClick="Hide();" showAnimation="FadeIn" hideAnimation="FadeOut">
    Back to Example List
  </Button>
  
  <!-- Top Bar -->
  <Panel height="32" rectAlignment="UpperCenter" image="Sprites/Layout/Gradient" color="rgb(0.2,0.9,0,0.5)" showAnimation="SlideIn_Top" hideAnimation="SlideOut_Top" animationDuration="0.5">
    <TableLayout class="noCellBackground" rectAlignment="MiddleRight" width="224" cellPadding="5">
      <Row preferredHeight="0">        
        <Cell tooltip="Green Minerals" tooltipPosition="Below">
          <TableLayout class="noCellBackground item">
            <Row>
              <Cell>
                <Image image="Sprites/Shop/gemGreen" preserveAspect="1" width="125%" height="125%" ignoreLayout="1"/>
              </Cell>
              <Cell>
                <Text class="resourceText">500</Text>
              </Cell>
            </Row>
          </TableLayout>
        </Cell>
        <Cell tooltip="Blue Minerals" tooltipPosition="Below">
          <TableLayout class="noCellBackground item">
            <Row>
              <Cell>
                <Image image="Sprites/Shop/gemBlue" preserveAspect="1" width="125%" height="125%" ignoreLayout="1" />
              </Cell>
              <Cell>
                <Text class="resourceText">1000</Text>
              </Cell>
            </Row>
          </TableLayout>
        </Cell>
      </Row>
    </TableLayout>
  </Panel>  

  <!-- Bottom Bar -->
  <TableLayout columnWidths="192 0 128 256" height="192" rectAlignment="LowerCenter" class="noCellBackground" showAnimation="SlideIn_Bottom" hideAnimation="SlideOut_Bottom">
    <Row>     
      <Cell>
        <!-- Minimap -->
        <Panel class="borderOuter" padding="12">
          <Panel padding="4" image="Sprites/Outline" color="rgba(0,1,0,0.75)">
            <Panel class="borderInner">
              <Image image="Sprites/HUD/Minimap" preserveAspect="1" />
            </Panel>
          </Panel>
        </Panel>
      </Cell>

      <Cell>
        <!-- Unit List -->
        <Panel class="borderOuter" image="Sprites/Layout/Title" height="168">
        </Panel>

        <Panel class="borderOuter" image="Sprites/Layout/Viewport" height="160">
          <Panel class="borderInner">

            <TableLayout cellBackgroundColor="rgba(1,1,1,0.05)" cellBackgroundImage="Sprites/Outline" cellSpacing="8" padding="8 8 8 16" cellPadding="2">
              <Row>
                <Cell>
                  <Button class="unitIcon" tooltip="Fighter" />
                </Cell>
                <Cell>
                  <Button class="unitIcon" tooltip="Fighter" />
                </Cell>
                <Cell>
                  <Button class="unitIcon" tooltip="Fighter" />
                </Cell>
                <Cell>
                  <Button class="unitIcon" tooltip="Fighter" />
                </Cell>
                <Cell></Cell>                
              </Row>

              <Row>
                <Cell></Cell>
                <Cell></Cell>
                <Cell></Cell>
                <Cell></Cell>
                <Cell></Cell>                
              </Row>
            </TableLayout>
            
          </Panel>
        </Panel>
      </Cell>

      <Cell>
        <!-- Unit Portrait -->
        <Panel class="borderOuter" height="160">
          <Panel class="borderInner" color="rgb(0.1,0.1,0.1)">
            <Panel padding="4" image="Sprites/Outline" color="rgba(0,1,0,0.75)">
              <Panel padding="8">
                <Image image="Sprites/HUD/UnitPortrait" showAnimation="FadeIn" hideAnimation="FadeOut" animationDuration="0.5" preserveAspect="1" color="rgba(1,1,1,0.5)" />
              </Panel>
            </Panel>
          </Panel>
        </Panel>
      </Cell>

      <Cell>
        <!-- Commands -->
                
        <Panel class="borderOuter" image="Sprites/Layout/Title" height="192">        
        </Panel>
        
        <Panel class="borderOuter" image="Sprites/Layout/Viewport" height="160">
          <Panel class="borderInner">
            <TableLayout cellBackgroundColor="rgba(1,1,1,0.05)" cellBackgroundImage="Sprites/Outline" cellSpacing="8" padding="8 8 4 4" cellPadding="2">
              <Row>
                <Cell>
                  <Button class="commandButton" icon="Sprites/Icons/Move" tooltip="Move" />
                </Cell>
                <Cell>
                  <Button class="commandButton" icon="Sprites/Icons/Crosshair" tooltip="Attack" />
                </Cell>
                <Cell>
                  <Button class="commandButton" icon="Sprites/Icons/Cancel" tooltip="Cancel" />
                </Cell>
                <Cell>
                  <Button class="commandButton" icon="Sprites/Icons/Anticlockwise-Rotation" tooltip="Return" />                  
                </Cell>
              </Row>
              
              <Row>
                <Cell></Cell>
                <Cell></Cell>
                <Cell></Cell>
                <Cell></Cell>
              </Row>
              
              <Row>
                <Cell>
                  <Button class="commandButton" icon="Sprites/Icons/Arrow_Left" tooltip="Previous Page" />
                </Cell>
                <Cell></Cell>
                <Cell>
                  <Button class="commandButton" icon="Sprites/Icons/Cog" tooltip="Advanced Options" />
                </Cell>
                <Cell>
                  <Button class="commandButton" icon="Sprites/Icons/Arrow_Right" tooltip="Next Page" />                  
                </Cell>
              </Row>

            </TableLayout>
          </Panel>
        </Panel>        
      </Cell>
    </Row>
    
  </TableLayout>
  
</XmlLayout>                                
                                
    using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UI.Xml;

namespace UI.Xml.Examples
{
    class XmlLayout_Example_HUD : XmlLayoutController
    {
        public XmlLayout_Example_ExampleMenu ExampleMenu = null;        

        public override void Hide(Action onCompleteCallback = null)
        {
            if (ExampleMenu != null)
            {
                ExampleMenu.SelectExample(null);
            }
            else
            {
                // This code is executed in the World Space example (which is in a different scene to the base Example Scene)
                base.Hide(() => { UnityEngine.SceneManagement.SceneManager.LoadScene("ExampleScene"); });
            }
        }
    }
}
                                    

Drag And Drop Example

An example showing a simple drag and drop interface.

                    

    <XmlLayout xmlns="XmlLayout"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="XmlLayout ../../Configuration/XmlLayout.xsd" showAnimation="SlideIn_Right" hideAnimation="SlideOut_Right">

  <Include path="Xml/Styles.xml" />
  
  <Defaults>
    <!-- By default, TableCells ignore raycasts, but that needs to be changed (elements must block raycasts to be drop receivers) -->
    <Cell class="itemCell" isDropReceiver="true" raycastTarget="1" onElementDropped="ItemDropped()" />
    <Image class="item" allowDragging="true" restrictDraggingToParentBounds="false" preserveAspect="true" />

    <Text color="white" fontSize="32" resizeTextForBestFit="true" minHeight="32" font="Fonts/Arial Bold" />    
  </Defaults>

  <Button rectAlignment="UpperRight" 
          offsetXY="-64 -64" 
          width="256" 
          height="32" 
          fontSize="16" 
          icon="Sprites/Icons/Arrow_Left" iconWidth="64" iconColor="rgb(0,1,0)" iconHoverColor="rgb(0,1,0)" padding="0 0 4 4"
          onClick="ReturnToMainExamples()">
    Back to Example List
  </Button>    
  
  <TableLayout width="800" height="384" offsetXY="0 -32" cellSpacing="32" cellBackgroundColor="clear">
    <Row preferredHeight="32">
      <Cell>
        <Text>Drag items from one cell to another</Text>
      </Cell>
    </Row>

    <Row>
      <Cell>
        <HorizontalLayout name="Container" flexibleHeight="1" spacing="16">

          <Panel name="LeftPanel" color="white" rectAlignment="MiddleLeft" image="Sprites/Layout/Base" padding="8">
            <Panel image="Sprites/Layout/Base" color="rgb(0.15,0.15,0.15)">

              <TableLayout name="leftTable">
                <Row>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell">
                    <Image class="item" image="Sprites/Shop/Coin" name="Coin" />
                  </Cell>
                  <Cell class="itemCell"></Cell>
                </Row>

                <Row>
                  <Cell class="itemCell">
                    <Image class="item" image="Sprites/Shop/gemRed" name="Red Gem" />
                  </Cell>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell"></Cell>
                </Row>

                <Row>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell">
                    <Image class="item" image="Sprites/Shop/gemBlue" name="Blue Gem" />
                  </Cell>
                </Row>
              </TableLayout>

            </Panel>
          </Panel>

          <Panel name="RightPanel" color="white" rectAlignment="MiddleRight" image="Sprites/Layout/Base" padding="8">
            <Panel image="Sprites/Layout/Base" color="rgb(0.15,0.15,0.15)">

              <TableLayout name="rightTable">
                <Row>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell"></Cell>
                </Row>

                <Row>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell">
                    <Image class="item" image="Sprites/Shop/gemGreen" name="Green Gem" />
                  </Cell>
                  <Cell class="itemCell"></Cell>
                </Row>

                <Row>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell"></Cell>
                  <Cell class="itemCell"></Cell>
                </Row>
              </TableLayout>

            </Panel>
          </Panel>

        </HorizontalLayout>
      </Cell>
    </Row>

    <Row preferredHeight="32">
      <Cell>
        <Text id="debugText"></Text>
      </Cell>
    </Row>    
  </TableLayout>  

</XmlLayout>                                
                                
    using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UI.Xml;
using UI.Tables;
using UnityEngine.UI;

class DragAndDropExampleXmlLayoutController : XmlLayoutController
{    
    void ItemDropped(XmlElement droppedItem, XmlElement cell)
    {
        if (!cell.HasClass("itemCell") || !droppedItem.HasClass("item")) return;

        droppedItem.parentElement.RemoveChildElement(droppedItem);
        
        cell.AddChildElement(droppedItem);
        
        // debug text
        var tableCell = cell.GetComponent<TableCell>();               

        var debugText = xmlLayout.GetElementById<Text>("debugText");
        debugText.text = String.Format("Item '{0}' dropped on cell '{1}' in table '{2}'", droppedItem.name, GetCellPositionString(tableCell), GetTableName(tableCell));
    }

    string GetCellPositionString(TableCell cell)
    {
        var row = cell.GetRow();
        var table = row.GetTable();

        var rowPosition = table.Rows.IndexOf(row) + 1;
        var columnPosition = row.Cells.IndexOf(cell) + 1;

        return String.Format("{0},{1}", rowPosition, columnPosition);
    }

    string GetTableName(TableCell cell)
    {
        return cell.GetRow().GetTable().name;
    }

    void ReturnToMainExamples()
    {
        xmlLayout.Hide(() => { UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("ExampleScene"); });        
    }

    void Awake()
    {
        xmlLayout.Show();
    }
}
                                    

Localization Example

An example showing localization in action.

                    

    <XmlLayout xmlns="XmlLayout"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="XmlLayout ../../Configuration/XmlLayout.xsd"
           showAnimation="SlideIn_Right" hideAnimation="SlideOut_Right">

  <Include path="Xml/Styles.xml" />

  <Button rectAlignment="UpperRight"
        offsetXY="-64 -64"
        width="256"
        height="32"
        fontSize="16"
        icon="Sprites/Icons/Arrow_Left" iconWidth="64" iconColor="rgb(0,1,0)" iconHoverColor="rgb(0,1,0)" padding="0 0 4 4"
        onClick="ReturnToMainExamples()">
    Back to Example List
  </Button>

  <ToggleGroup id="languageToggleGroup" onValueChanged="ChangeLanguage(selectedText)">
    <TableLayout class="noCellBackground" width="600" height="64" rectAlignment="UpperCenter" offsetXY="0 -120" cellPadding="8">
      <Row class="viewport">
        <Cell>
          <ToggleButton name="English">English</ToggleButton>
        </Cell>
        <Cell>
          <ToggleButton name="Spanish">Spanish</ToggleButton>
        </Cell>
        <Cell>
          <ToggleButton name="French">French</ToggleButton>
        </Cell>
        <Cell>
          <ToggleButton name="Afrikaans">Afrikaans</ToggleButton>
        </Cell>
        <Cell>
          <ToggleButton name="None">No Localization</ToggleButton>
        </Cell>
      </Row>
    </TableLayout>
  </ToggleGroup>
  

  <TableLayout class="noCellBackground" height="280" width="600" offsetXY="0 -48" useGlobalCellPadding="false">
    <Row class="headerRow">
      <Cell>
        <Text class="title">
          {LOCALIZATION_EXAMPLE_HEADER}
        </Text>
      </Cell>
    </Row>
    <Row class="viewport">
      <Cell dontUseTableCellBackground="true" padding="16">
        <TableLayout cellPadding="8" cellBackgroundColor="rgba(1,1,1,0.1)">
          <Row>
            <Cell>
              <Text class="h3">{LOCALIZATION_EXAMPLE_USERNAME_LABEL}</Text>
            </Cell>
            <Cell>
              <InputField id="username" placeholderText="{LOCALIZATION_EXAMPLE_USERNAME_PLACEHOLDER_TEXT}" />
            </Cell>
          </Row>

          <Row>
            <Cell>
              <Text class="h3">{LOCALIZATION_EXAMPLE_PASSWORD_LABEL}</Text>
            </Cell>
            <Cell>
              <InputField id="password" inputType="Password" placeholderText="{LOCALIZATION_EXAMPLE_PASSWORD_PLACEHOLDER_TEXT}" />
            </Cell>
          </Row>

          <Row>
            <Cell>
              <Button resizeTextForBestFit="1" resizeTextMaxSize="16">{LOCALIZATION_EXAMPLE_LOGIN_BUTTON}</Button>
            </Cell>
            <Cell>
              <Button resizeTextForBestFit="1" resizeTextMaxSize="16">{LOCALIZATION_EXAMPLE_REGISTER_BUTTON}</Button>
            </Cell>
          </Row>
        </TableLayout>
      </Cell>
    </Row>
  </TableLayout>

</XmlLayout>                                
                                
    using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UI.Xml;

class XmlLayout_Localization_Example : XmlLayoutController
{
    public string selectedLanguage = "English";
    XmlElementReference<XmlLayoutToggleGroup> toggleGroup = null;

    void Awake()
    {
        xmlLayout.Show();

        toggleGroup = XmlElementReference<XmlLayoutToggleGroup>("languageToggleGroup");        
    }
    
    public override void LayoutRebuilt(ParseXmlResult parseResult)
    {
        if (toggleGroup != null)
        {
            // As the layout has just been rebuilt, the toggle group will no longer have anything selected
            // so we're just going to set it again here     
            toggleGroup.element.SetSelectedValue(selectedLanguage, false);            
        }
    }

    void ChangeLanguage(string language)
    {
        this.selectedLanguage = language;

        if (language == "No Localization")
        {
            xmlLayout.SetLocalizationFile(null);
            return;
        }

        var languageFile = XmlLayoutUtilities.LoadResource<XmlLayoutLocalization>("Localization/" + language);

        if (languageFile == null)
        {
            Debug.LogWarningFormat("Warning: localization file for language '{0}' not found!", language);
            return;
        }

        xmlLayout.SetLocalizationFile(languageFile);
    }

    void ReturnToMainExamples()
    {
        xmlLayout.Hide(() => { UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("ExampleScene"); });
    }
}
                                    

Color Scheme Manager Example

An example showing one way of implementing color schemes/themes.

                    

    <XmlLayout xmlns="XmlLayout"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="XmlLayout ../../../Configuration/XmlLayout.xsd">

  <HorizontalLayout color="rgb(0.5,0.5,0.5)" padding="8" image="Sprites/Layout/Viewport">
    <Text color="white">Color Scheme:</Text>
    
    <Dropdown id="colorSchemeDropdown" preferredWidth="192" onValueChanged="ChangeColorScheme(selectedValue)">
      <Option>GreenYellow</Option>
      <Option>BlueYellow</Option>
      <Option>Grayscale</Option>
      <Option>GrayscaleAlt</Option>
    </Dropdown>
  </HorizontalLayout>

</XmlLayout>                                
                                
    using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

namespace UI.Xml
{
    [ExecuteInEditMode]
    class XmlLayout_Example_ColorSchemeManager : XmlLayoutController
    {
        // Set to the root Canvas for the scene in the inspector
        public GameObject Root = null;

        private string colorScheme
        {
            get { return PlayerPrefs.GetString("colorScheme"); }
            set { PlayerPrefs.SetString("colorScheme", value); }
        }

        private void OnEnable()
        {
            if (string.IsNullOrEmpty(colorScheme)) colorScheme = "GreenYellow";

            ChangeColorSchemeWithoutRebuild(colorScheme);
        }

        private void Start()
        {
            xmlLayout.GetElementById<Dropdown>("colorSchemeDropdown").SetSelectedValue(colorScheme);

            ChangeColorScheme(colorScheme);
        }

        public void ChangeColorSchemeWithoutRebuild(string newScheme)
        {
            colorScheme = newScheme;

            var colorSchemeFile = XmlLayoutUtilities.LoadResource<TextAsset>(string.Format("Xml/ColorSchemes/{0}", newScheme));

            if (colorSchemeFile == null)
            {
                Debug.LogErrorFormat("[XmlLayout][Example][Color Scheme Manager] Warning: unable to locate color scheme definition '{0}'", newScheme);
                return;
            }

            List<XmlLayout> xmlLayouts = Root.gameObject
                                             .GetComponentsInChildren<XmlLayout>(true)
                                             .ToList();

            foreach (var layout in xmlLayouts)
            {
                // skip this layout
                if (layout == this.xmlLayout) continue;

                if (layout.DefaultsFiles == null) layout.DefaultsFiles = new List<TextAsset>();

                layout.DefaultsFiles.Clear();
                layout.DefaultsFiles.Add(colorSchemeFile);
            }
        }

        public void ChangeColorScheme(string newScheme)
        {
            colorScheme = newScheme;

            var colorSchemeFile = XmlLayoutUtilities.LoadResource<TextAsset>(string.Format("Xml/ColorSchemes/{0}", newScheme));

            if (colorSchemeFile == null)
            {
                Debug.LogErrorFormat("[XmlLayout][Example][Color Scheme Manager] Warning: unable to locate color scheme definition '{0}'", newScheme);
                return;
            }

            List<XmlLayout> xmlLayouts = Root.gameObject
                                             .GetComponentsInChildren<XmlLayout>(true)
                                             .ToList();

            foreach(var layout in xmlLayouts)
            {
                // skip this layout
                if (layout == this.xmlLayout) continue;

                if (layout.DefaultsFiles == null) layout.DefaultsFiles = new List<TextAsset>();

                var inactive = !layout.gameObject.activeSelf;

                if (inactive)
                {
                    layout.gameObject.SetActive(true);
                }

                layout.DefaultsFiles.Clear();
                layout.DefaultsFiles.Add(colorSchemeFile);

                layout.RebuildLayout(true);

                if (inactive)
                {
                    // copy the local variable (if we use 'layout' it will reference the foreach variable which changes through each iteration)
                    var layoutTemp = layout;

                    // hide the layout again at the end of the frame
                    XmlLayoutTimer.AtEndOfFrame(() =>
                    {
                        if (layoutTemp == null) return;

                        //canvasGroup.alpha = alphaBefore;
                        layoutTemp.gameObject.SetActive(false);
                    }, layoutTemp, true);
                }
            }
        }
    }
}
                                    

Credits

Some of the images used in this example (coin, gemGreen, gemBlue, gemRed) are sourced from OpenGameArt.org and were created by RavenMore.

Additionally, some of the icons were sourced from game-icons.net:
Sbed
- cancel.png

Delapouite
- move.png
- crosshair.png
- anticlockwise-rotation.png

Lorc
- cog.png


Thanks!