Browse Source

Removed UI serialization hacks related to the Menu & DropDownList popup element. To avoid layout problems, now it is never added as a child temporarily.
Do not abort UI layout deserialization on encountering an unknown element.

Lasse Öörni 13 years ago
parent
commit
9401ad8063

+ 1 - 1
Bin/Data/Scripts/Editor/EditorUI.as

@@ -41,7 +41,7 @@ void CreateCursor()
     cursor.style = uiStyle;
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
     ui.cursor = cursor;
-    if (GetPlatform() == "Android")
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
         ui.cursor.visible = false;
 }
 

+ 1 - 1
Bin/Data/UI/DefaultStyle.xml

@@ -69,7 +69,7 @@
         <attribute name="Label Offset" value="-1 1" />
         <attribute name="Layout Mode" value="Horizontal" />
         <attribute name="Layout Border" value="4 1 4 1" />
-        <element type="Window" internal="true">
+        <element type="Window" internal="true" popup="true">
             <attribute name="Texture" value="Texture2D;Textures/UI.png" />
             <attribute name="Image Rect" value="48 0 64 16" />
             <attribute name="Border" value="3 3 3 3" />

+ 1 - 1
Bin/Data/UI/EditorNodeWindow.xml

@@ -47,7 +47,7 @@
             <attribute name="Name" value="NewVarDropDown" />
             <attribute name="Min Size" value="50 17" />
             <attribute name="Max Size" value="50 17" />
-            <element type="Window" internal="true">
+            <element type="Window" internal="true" popup="true">
                 <element type="ListView" internal="true">
                     <element type="BorderImage" internal="true">
                         <element internal="true">

+ 6 - 6
Bin/Data/UI/EditorSettingsDialog.xml

@@ -180,7 +180,7 @@
             <attribute name="Min Size" value="100 0" />
             <attribute name="Max Size" value="100 2147483647" />
             <attribute name="Resize Popup" value="true" />
-            <element type="Window" internal="true">
+            <element type="Window" internal="true" popup="true">
                 <element type="ListView" internal="true">
                     <element type="BorderImage" internal="true">
                         <element internal="true">
@@ -233,7 +233,7 @@
             <attribute name="Min Size" value="100 0" />
             <attribute name="Max Size" value="100 2147483647" />
             <attribute name="Resize Popup" value="true" />
-            <element type="Window" internal="true">
+            <element type="Window" internal="true" popup="true">
                 <element type="ListView" internal="true">
                     <element type="BorderImage" internal="true">
                         <element internal="true">
@@ -265,7 +265,7 @@
             <attribute name="Min Size" value="100 0" />
             <attribute name="Max Size" value="100 2147483647" />
             <attribute name="Resize Popup" value="true" />
-            <element type="Window" internal="true">
+            <element type="Window" internal="true" popup="true">
                 <element type="ListView" internal="true">
                     <element type="BorderImage" internal="true">
                         <element internal="true">
@@ -297,7 +297,7 @@
             <attribute name="Min Size" value="100 0" />
             <attribute name="Max Size" value="100 2147483647" />
             <attribute name="Resize Popup" value="true" />
-            <element type="Window" internal="true">
+            <element type="Window" internal="true" popup="true">
                 <element type="ListView" internal="true">
                     <element type="BorderImage" internal="true">
                         <element internal="true">
@@ -329,7 +329,7 @@
             <attribute name="Min Size" value="100 0" />
             <attribute name="Max Size" value="100 2147483647" />
             <attribute name="Resize Popup" value="true" />
-            <element type="Window" internal="true">
+            <element type="Window" internal="true" popup="true">
                 <element type="ListView" internal="true">
                     <element type="BorderImage" internal="true">
                         <element internal="true">
@@ -364,7 +364,7 @@
             <attribute name="Min Size" value="100 0" />
             <attribute name="Max Size" value="100 2147483647" />
             <attribute name="Resize Popup" value="true" />
-            <element type="Window" internal="true">
+            <element type="Window" internal="true" popup="true">
                 <element type="ListView" internal="true">
                     <element type="BorderImage" internal="true">
                         <element internal="true">

+ 4 - 2
Docs/Reference.dox

@@ -1016,7 +1016,7 @@ User interface elements derive from Serializable, so they can be serialized to/f
 
 The function \ref UI::LoadLayout "LoadLayout()" in UI will take an XML file and instantiate the elements defined in it. To be valid XML, there should be one root %UI element. An optional style XML file can be specified; the idea is to first read the element's style from that file, then fill in the rest from the actual layout XML file. This way the layout file can be relatively simple, as the majority of the data is already defined.
 
-See the elements' C++ code for all supported attributes, and look at the editor's user interface layouts in the Data/UI directory for examples. The serialization format is similar to scene XML serialization, with two important differences:
+See the elements' C++ code for all supported attributes, and look at the editor's user interface layouts in the Data/UI directory for examples. The serialization format is similar to scene XML serialization, with three important differences:
 
 1) The element type to instantiate, and the style to use for it can be set separately. For example the following element definition
 
@@ -1026,7 +1026,7 @@ See the elements' C++ code for all supported attributes, and look at the editor'
 
 tells to instantiate a Button element, and that it should use the style "CloseButton" defined in the style XML file.
 
-2) Internal sub-elements, for example the scroll bars of a ScrollView, need to be marked as such to avoid instantiating them as duplicates. This is done by adding the attribute internal="true" to the XML element, and is required in  both layout and style XML files. Furthermore, the elements must be listed in the order they have been added as children of the parent element (if in doubt, see the element's C++ constructor code. Omitting elements in the middle is OK.) For example:
+2) Internal child elements, for example the scroll bars of a ScrollView, need to be marked as such to avoid instantiating them as duplicates. This is done by adding the attribute internal="true" to the XML element, and is required in  both layout and style XML files. Furthermore, the elements must be listed in the order they have been added as children of the parent element (if in doubt, see the element's C++ constructor code. Omitting elements in the middle is OK.) For example:
 
 \code
 <element type="ScrollView" />
@@ -1039,6 +1039,8 @@ tells to instantiate a Button element, and that it should use the style "CloseBu
 </element>
 \endcode
 
+3) The popup element shown by Menu and DropDownList is not an actual child element. In XML serialization, it is nevertheless stored as a child element, but is marked with the attribute popup="true".
+
 Note that when %UI elements are serialized back to XML using \ref UI::SaveLayout "SaveLayout()" it is no longer possible to separate what was defined in the style XML file, and what in the actual layout file. Instead all attributes will be serialized.
 
 

+ 1 - 22
Engine/UI/DropDownList.cpp

@@ -43,10 +43,6 @@ DropDownList::DropDownList(Context* context) :
     window->SetInternal(true);
     SetPopup(window);
     
-    // Hack: parent the popup until first shown to allow loading style from XML
-    AddChild(window);
-    window->SetVisible(false);
-    
     listView_ = new ListView(context_);
     listView_->SetInternal(true);
     listView_->SetScrollBarsVisible(false, false);
@@ -78,23 +74,6 @@ void DropDownList::ApplyAttributes()
     SetSelection(selectionAttr_);
 }
 
-bool DropDownList::SaveXML(XMLElement& dest)
-{
-    // Hack: parent the popup during serialization
-    bool popupShown = popup_ && popup_->IsVisible();
-    if (popup_)
-    {
-        InsertChild(0, popup_);
-        popup_->SetVisible(false);
-    }
-    
-    bool success = UIElement::SaveXML(dest);
-    
-    ShowPopup(popupShown);
-    
-    return success;
-}
-
 void DropDownList::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor)
 {
     Button::GetBatches(batches, quads, currentScissor);
@@ -125,7 +104,7 @@ void DropDownList::OnShowPopup()
     content->UpdateLayout();
     const IntVector2& contentSize = content->GetSize();
     const IntRect& border = popup_->GetLayoutBorder();
-    popup_->SetSize(resizePopup_ ? GetWidth() : contentSize.x_ + border.left_ + border.right_, contentSize.y_ + border.top_ + 
+    popup_->SetSize(resizePopup_ ? GetWidth() : contentSize.x_ + border.left_ + border.right_, contentSize.y_ + border.top_ +
         border.bottom_);
     
     // Check if popup fits below the button. If not, show above instead

+ 0 - 2
Engine/UI/DropDownList.h

@@ -45,8 +45,6 @@ public:
     
     /// Apply attribute changes that can not be applied immediately.
     virtual void ApplyAttributes();
-    /// Save as XML data. Return true if successful.
-    virtual bool SaveXML(XMLElement& dest);
     /// Return UI rendering batches.
     virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor);
     /// React to the popup being shown.

+ 110 - 8
Engine/UI/Menu.cpp

@@ -68,21 +68,123 @@ void Menu::OnShowPopup()
 {
 }
 
+bool Menu::LoadXML(const XMLElement& source, XMLFile* styleFile)
+{
+    // Apply the style first, but only for non-internal elements
+    if (!internal_ && styleFile)
+    {
+        // Use style override if defined, otherwise type name
+        String styleName = source.GetAttribute("style");
+        if (styleName.Empty())
+            styleName = GetTypeName();
+        
+        SetStyle(styleFile, styleName);
+    }
+    
+    // Then load rest of the attributes from the source
+    if (!Serializable::LoadXML(source))
+        return false;
+    
+    unsigned nextInternalChild = 0;
+    
+    // Load child elements. Internal elements are not to be created as they already exist
+    XMLElement childElem = source.GetChild("element");
+    while (childElem)
+    {
+        bool internalElem = childElem.GetBool("internal");
+        bool popupElem = childElem.GetBool("popup");
+        String typeName = childElem.GetAttribute("type");
+        if (typeName.Empty())
+            typeName = "UIElement";
+        UIElement* child = 0;
+        
+        if (!internalElem)
+        {
+            if (!popupElem)
+                child = CreateChild(ShortStringHash(typeName));
+            else
+            {
+                // Do not add the popup element as a child even temporarily, as that can break layouts
+                SharedPtr<UIElement> popup = DynamicCast<UIElement>(context_->CreateObject(ShortStringHash(typeName)));
+                if (!popup)
+                    LOGERROR("Could not create popup element type " + ShortStringHash(typeName).ToString());
+                else
+                {
+                    child = popup;
+                    SetPopup(popup);
+                }
+            }
+        }
+        else
+        {
+            // An internal popup element should already exist
+            if (popupElem)
+                child = popup_;
+            else
+            {
+                for (unsigned i = nextInternalChild; i < children_.Size(); ++i)
+                {
+                    if (children_[i]->IsInternal() && children_[i]->GetTypeName() == typeName)
+                    {
+                        child = children_[i];
+                        nextInternalChild = i + 1;
+                        break;
+                    }
+                }
+                
+                if (!child)
+                    LOGWARNING("Could not find matching internal child element of type " + typeName + " in " + GetTypeName());
+            }
+        }
+        
+        if (child)
+        {
+            if (!child->LoadXML(childElem, styleFile))
+                return false;
+        }
+        
+        childElem = childElem.GetNext("element");
+    }
+    
+    ApplyAttributes();
+    
+    return true;
+}
+
 bool Menu::SaveXML(XMLElement& dest)
 {
-    // Hack: parent the popup during serialization
-    bool popupShown = popup_ && popup_->IsVisible();
-    if (popup_)
+    // Write type and internal flag
+    if (!dest.SetString("type", GetTypeName()))
+        return false;
+    if (internal_)
     {
-        InsertChild(0, popup_);
-        popup_->SetVisible(false);
+        if (!dest.SetBool("internal", internal_))
+            return false;
     }
     
-    bool success = UIElement::SaveXML(dest);
+    // Write attributes
+    if (!Serializable::SaveXML(dest))
+        return false;
+    
+    // Write child elements
+    for (unsigned i = 0; i < children_.Size(); ++i)
+    {
+        UIElement* element = children_[i];
+        XMLElement childElem = dest.CreateChild("element");
+        if (!element->SaveXML(childElem))
+            return false;
+    }
     
-    ShowPopup(popupShown);
+    // Save the popup element as a "virtual" child element
+    if (popup_)
+    {
+        XMLElement childElem = dest.CreateChild("element");
+        childElem.SetBool("popup", true);
+        if (!popup_->SaveXML(childElem))
+            return false;
+    }
     
-    return success;
+    return true;
 }
 
 void Menu::SetPopup(UIElement* popup)

+ 4 - 0
Engine/UI/Menu.h

@@ -33,6 +33,8 @@ class Menu : public Button
 {
     OBJECT(Menu);
     
+    using UIElement::LoadXML;
+    
 public:
     /// Construct.
     Menu(Context* context);
@@ -41,6 +43,8 @@ public:
     /// Register object factory.
     static void RegisterObject(Context* context);
     
+    /// Load from XML data with style. Return true if successful.
+    virtual bool LoadXML(const XMLElement& source, XMLFile* styleFile);
     /// Save as XML data. Return true if successful.
     virtual bool SaveXML(XMLElement& dest);
     

+ 3 - 6
Engine/UI/UIElement.cpp

@@ -237,11 +237,7 @@ bool UIElement::LoadXML(const XMLElement& source, XMLFile* styleFile)
         UIElement* child = 0;
         
         if (!internalElem)
-        {
             child = CreateChild(ShortStringHash(typeName));
-            if (!child)
-                return false;
-        }
         else
         {
             for (unsigned i = nextInternalChild; i < children_.Size(); ++i)
@@ -253,6 +249,9 @@ bool UIElement::LoadXML(const XMLElement& source, XMLFile* styleFile)
                     break;
                 }
             }
+            
+            if (!child)
+                LOGWARNING("Could not find matching internal child element of type " + typeName + " in " + GetTypeName());
         }
         
         if (child)
@@ -260,8 +259,6 @@ bool UIElement::LoadXML(const XMLElement& source, XMLFile* styleFile)
             if (!child->LoadXML(childElem, styleFile))
                 return false;
         }
-        else
-            LOGWARNING("Could not find matching internal child element of type " + typeName + " in " + GetTypeName());
         
         childElem = childElem.GetNext("element");
     }