Search Unity

[RELEASED] XmlLayout: Xml-driven UI Framework

Discussion in 'Assets and Asset Store' started by DaceZA, May 3, 2016.

  1. drpelz

    drpelz

    Joined:
    Dec 7, 2017
    Posts:
    69
    Is there a controlChildSizeWidth/Height-property available? I just need it because childForceExpandWidth/Height do not always work in my case...

    Also xmlLayout.RebuildLayout(true) doesn't work in some cases.:( In my case it didn't work so I had to make an element inactive and active to force a layout-rebuild...:confused:
     
  2. DaceZA

    DaceZA

    Joined:
    Dec 19, 2014
    Posts:
    174
    Hi there,

    Yes, you can control the Unity 'childControlWidth / childControlHeight' properties for layout groups with the matching attributes (in versions of Unity that support it, which is, I think, off the top of my head, Unity 2019.2 and newer).

    For example:


    Code (CSharp):
    1.   <VerticalLayout childControlHeight="1" childControlWidth="1" childForceExpandHeight="0" childForceExpandWidth="0"  childAlignment="MiddleCenter">
    2.  
    3.     <Button text="Test 1" preferredHeight="32" preferredWidth="128" />
    4.     <Button text="Test 2" preferredHeight="64" preferredWidth="256" />
    5.     <Button text="Test 3" preferredHeight="32" preferredWidth="128" />
    6.     <Button text="Test 4" preferredHeight="64" preferredWidth="256" />
    7.     <Button text="Test 5" preferredHeight="32" preferredWidth="128" />
    8.     <Button text="Test 6" preferredHeight="64" preferredWidth="256" />  
    9.  
    10.   </VerticalLayout>
    Results in:

    ChildControlDimensions.jpg


    There are very few reasons why xmlLayout.RebuildLayout(true); wouldn't rebuild:
    a) The gameobject is not active (XmlLayout will never rebuild if it is not active)
    b) XmlLayout's 'Awake' method hasn't been called yet - this is called by Unity almost immediately after the object becomes active for the first time in the scene
    c) A rebuild is already in progress (XmlLayout won't start another rebuild if another is already in progress)

    You can see the code for this in XmlLayout.cs, lines 290 to 295 (with 'rebuildInProgress' being set back to false on line 332 in a 'finally' block so even if an exception occurs, it will still be reset)

    While enabling and disabling the GameObject may be working in this case to trigger a rebuild, it isn't reliable as normally XmlLayout will not rebuild a layout when it becomes active unless it hasn't already been built before - if rebuildLayout(true) isn't working for you, I'd first check to see if the above conditions are being met, and alternatively, you might want to try the following approach:

    XmlLayoutTimer.AtEndOfFrame(() => RebuildLayout(true), this);


    ^ This will call RebuildLayout(true) as soon as the current frame has finished rendering, which can be useful as it allows for other code to execute first (for example, the Awake() method if you've just activated the gameObject for the first time). I've found that, in Unity, briefly delaying execution of code is often a useful way of getting around restrictions that might otherwise prevent your code from working the way you want it to - XmlLayout uses its timer class quite a lot throughout the project - and if AtEndOfFrame() isn't sufficient, there's always 'DelayedCall' as well which allows you to have the code you want executed after a specific amount of time)

    I hope this helps!
     
  3. drpelz

    drpelz

    Joined:
    Dec 7, 2017
    Posts:
    69
    Wow! Very detailed answer!:) I will check this out and tell if XmlLayoutTimer.AtEndOfFrame(() => RebuildLayout(true), this); works or not.

    Now I found a workaround for childControlHeight / childControlWidth so currently I don't need them. But hey - thank you very much for the hint! Really great support! Much appreciated!:D
     
    Last edited: Jun 8, 2020
  4. drpelz

    drpelz

    Joined:
    Dec 7, 2017
    Posts:
    69
    I get weird graphical glitches when I implement
    Code (CSharp):
    1. XmlLayoutTimer.AtEndOfFrame(() => RebuildLayout(true), this);
    into my code. It doesn't work!

    When I use
    Code (CSharp):
    1. xmlLayout.RebuildLayout(true);
    it almost works!
    In my code there are four toggle boxes. When I click on them the content changes. This works so far. But the graphics of the toggle-boxes do not update accordingly to their content. There seems to be a bug in xmllayout which prevents to update the isOn-attribute of the toggle boxes after clicking on them...:confused:

    page 2 displayed correctly


    page 2 showing incorrect toggle button
     
    Last edited: Jun 8, 2020
  5. DaceZA

    DaceZA

    Joined:
    Dec 19, 2014
    Posts:
    174
    Hi there,

    Sorry for taking so long to get back to you on this - I didn't get a notification e-mail from Unity for this post (sometimes, rarely, they don't seem to send one, or my spam filter blocks it or something, not sure which - sorry).

    Are you using a toggle group for those toggle boxes?

    I tested toggling on and off both toggles and toggle groups using code and they seem to work correctly - while you can use attributes to handle this behaviour, it's probably best if you use the element properties/methods at runtime. As a test, I modified the 'Element List' example to use the following code (after adding ids to both the toggle element and the toggle group):

    Code (CSharp):
    1.     class XmlLayout_Example_List : XmlLayoutController
    2.     {
    3.         XmlElementReference<XmlLayoutProgressBar> progressBar;
    4.  
    5.         XmlElementReference<XmlLayoutToggleComponent> toggle;
    6.         XmlElementReference<XmlLayoutToggleGroup> toggleGroup;
    7.         float timeSinceLastUpdate = 0f;
    8.  
    9.         void Start()
    10.         {
    11.             progressBar = XmlElementReference<XmlLayoutProgressBar>("progressBar");
    12.  
    13.             toggle = XmlElementReference<XmlLayoutToggleComponent>("toggle");
    14.             toggleGroup = XmlElementReference<XmlLayoutToggleGroup>("toggleGroup");
    15.         }
    16.  
    17.         void Update()
    18.         {
    19.             progressBar.element.percentage += Time.deltaTime * 2.5f;
    20.             if (progressBar.element.percentage >= 100f) progressBar.element.percentage = 0.0f;
    21.  
    22.  
    23.             timeSinceLastUpdate += Time.deltaTime;
    24.             if (timeSinceLastUpdate > 1f)
    25.             {
    26.                 timeSinceLastUpdate = 0f;
    27.                 // Toggle the value
    28.                 toggle.element.isOn = !toggle.element.isOn;
    29.                 int lastSelectedIndex = toggleGroup.element.GetSelectedValue();
    30.                 int nextIndex = lastSelectedIndex < 3 ? (lastSelectedIndex + 1) : 0;
    31.                 // Update the selected value of the toggle group
    32.                 toggleGroup.element.SetSelectedValue(nextIndex);
    33.             }
    34.         }
    35.     }
    Basically, once a second, this test code alternates the toggle value, and moves the selection through the toggle group. In my tests, it worked fine.

    While you can use attributes to control this sort of behaviour, it's probably best to use the methods provided by XmlLayout (such as SetSelectedValue()) for toggle groups.

    I hope this helps, and sorry again for taking so long to respond.
     
  6. numarian

    numarian

    Joined:
    Feb 17, 2018
    Posts:
    7
    Hi DaceZA! First of all, great tool, thanks for making it, I still learning it but it's been great so far.

    I have a question. Is it possible to make a custom tag extends or inherit attributes from another tag? I'm trying to make a Tabs element, and I want it to contain a HorizontalLayoutGroup or VerticalLayoutGroup component in the root element, and be able to set the attributes of that component. For example, I'd like to have something like this:

    Code (Boo):
    1. <Tabs orientation="Horizontal" childForceExpandHeight="true" spacing="10" childAlignment="MiddleCenter">
    2.   <Tab>Graphics</Tab>
    3.   <Tab>Audio</Tab>
    4.   <Tab>Gameplay</Tab>
    5. </Tabs>
    I haven't been able to use attributes in the tag like I wanted. I could define them manually in the property attributes and set them in the Layout Group component, but I would like to know if there is a simpler way.

    Thanks!
     
    Last edited: Sep 6, 2020
  7. DaceZA

    DaceZA

    Joined:
    Dec 19, 2014
    Posts:
    174
    Hi there!

    Thanks, I'm really glad you like XmlLayout!

    Yes, you can have tags which inherit from a parent - in fact, quite a few of XmlLayout's own tags work this way, including the layout groups.

    To inherit from another tag, simply create your new tag code as a child class of the tag you wish to inherit from (instead of as a child class of ElementTagHandler), and then all you need to do is define what is different in your new tag compared to its base tag(s) by overriding methods and properties as necessary. In your case, for example, something like the following would work fine:

    Code (CSharp):
    1. public class TabsElementTagHandler  : LayoutBaseTagHandler
    2. {
    3.      // etc.
    4. }
    You could also extend the Horizontal or Vertical Layout tags directly, but in truth, they have no extra code over LayoutBaseTagHandler - for these elements, XmlLayout is just passing the attribute values directly to the Layout component, so no special handling was necessary.

    A good example of a tag which inherits from a parent in XmlLayout would be the ToggleButton element - this element inherits most of its behaviour from the standard 'Button' element, but overrides the ApplyAttributes() method to provide some additional functionality. One minor note: the ToggleButton is a built-in XmlLayout tag, so the attribute names/etc. were defined manually in the XmlLayout autocomplete file, whereas your custom tag(s) will need to define them as usual. You can extend the base attributes property from your parent element like so:

    Code (CSharp):
    1. public override Dictionary<string, string> attributes
    2. {
    3.      get
    4.      {
    5.           var _attributes = base.attributes;
    6.           _attributes.Add("newAttribute", "xs:string");
    7.           _attributes.Add("newAttribute1", "xs:string");
    8.           _attributes.Add("newAttribute2", "xs:string");
    9.  
    10.           return _attributes;
    11.      }
    12. }
    13.  
    14. // The same approach goes for things like ApplyAttributes():
    15.  
    16. public override void ApplyAttributes(AttributeDictionary attributesToApply)
    17. {
    18.      // apply the attributes as per the parent class
    19.      base.ApplyAttributes(attributesToApply);
    20.  
    21.     // now handle the new attributes
    22. }

    You can find the code for all the built-in tags in the UI/XmlLayout/Tags directory.

    Good luck, I hope this helps!

    PS: If you're looking for a tabbed paging system, I also sell an asset called 'PagedRect' which provides this functionality, and it is compatible with XmlLayout - you can find it here if you're interested: https://assetstore.unity.com/packag...paging-galleries-and-menus-for-unity-ui-54552
     
  8. drpelz

    drpelz

    Joined:
    Dec 7, 2017
    Posts:
    69
    Hi,

    just wanted to know how do you setup a CanvasGroup in xmllayout?
     
  9. DaceZA

    DaceZA

    Joined:
    Dec 19, 2014
    Posts:
    174
    Hi there,

    In code, you can access any element's CanvasGroup component via the .CanvasGroup property.

    In Xml, the CanvasGroup 'Alpha' property is exposed as the 'opacity' attribute. The other properties (BlocksRaycasts / IgnoreParentGroups) do not have attributes defined for them, but if you need them to be accessible in Xml, it is fairly easy to add a custom attribute to do so, e.g.

    Code (CSharp):
    1. using UI.Xml;
    2.  
    3. public class BlocksRaycastsAttribute: CustomXmlAttribute
    4. {
    5.     public override bool UsesApplyMethod { get { return true; } }
    6.  
    7.     public override void Apply(XmlElement xmlElement, string value, AttributeDictionary elementAttributes)
    8.     {
    9.         xmlElement.CanvasGroup.blocksRaycasts = value.ToBoolean();
    10.     }
    11.  
    12.     public override string ValueDataType
    13.     {
    14.         get
    15.         {
    16.             return "xs:boolean";
    17.         }
    18.     }
    19.  
    20.     public override string DefaultValue
    21.     {
    22.         get
    23.         {
    24.             return "1";
    25.         }
    26.     }
    27. }
    28.  
    29.  
    I hope this helps!
     
  10. drpelz

    drpelz

    Joined:
    Dec 7, 2017
    Posts:
    69

    Hi,
    sometimes I get weird graphical glitches in the inspector when using xmllayout (Unity version 2020.1.17f1). It makes the text unreadable - check it out: