Просмотр исходного кода

Moved bringToFront() to UIElement.
Added getRootElement() to UIElement.
Cleaned up menu popup code.
To optimize UI rendering, only ScrollView & LineEdit clip child UI elements by default.

Lasse Öörni 15 лет назад
Родитель
Сommit
9f91d37518

+ 34 - 2
Bin/Data/UI/TestLayout.xml

@@ -51,6 +51,36 @@
             <position value="150 270" />
             <size value="200 16" />
         </element>
+        <element type="Window" name="TestPopup2">
+            <size value="100 80" />
+            <element type="MenuItem">
+                <position value="10 10" />
+                <size value="80 16" />
+                <element type="Text">
+                    <text value="MenuItem6" />
+                    <font name="cour.ttf" size="12" />
+                    <alignment horizontal="center" vertical="center" />
+                </element>
+            </element>
+            <element type="MenuItem">
+                <position value="10 30" />
+                <size value="80 16" />
+                <element type="Text">
+                    <text value="MenuItem7" />
+                    <font name="cour.ttf" size="12" />
+                    <alignment horizontal="center" vertical="center" />
+                </element>
+            </element>
+            <element type="MenuItem">
+                <position value="10 50" />
+                <size value="80 16" />
+                <element type="Text">
+                    <text value="MenuItem8" />
+                    <font name="cour.ttf" size="12" />
+                    <alignment horizontal="center" vertical="center" />
+                </element>
+            </element>
+        </element>
         <element type="Window" name="TestPopup">
             <size value="100 80" />
             <element type="MenuItem">
@@ -74,6 +104,8 @@
             <element type="MenuItem">
                 <position value="10 50" />
                 <size value="80 16" />
+                <popup name="TestPopup2" />
+                <popupoffset value="90 0" />
                 <element type="Text">
                     <text value="MenuItem5" />
                     <font name="cour.ttf" size="12" />
@@ -84,13 +116,13 @@
         <element type="MenuItem">
             <position value="150 10" />
             <size value="80 16" />
+            <popup name="TestPopup" />
+            <popupoffset value="0 16" />
             <element type="Text">
                 <text value="MenuItem1" />
                 <font name="cour.ttf" size="12" />
                 <alignment horizontal="center" vertical="center" />
             </element>
-            <popup name="TestPopup" />
-            <popupoffset value="0 16" />
         </element>
         <element type="MenuItem">
             <position value="150 40" />

+ 3 - 1
Engine/Engine/RegisterTemplates.h

@@ -368,7 +368,6 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectBehaviour(className, asBEHAVE_FACTORY, declFactory.c_str(), asFUNCTION(ConstructUIElement<T>), asCALL_CDECL);
     engine->RegisterObjectBehaviour(className, asBEHAVE_FACTORY, declFactoryWithName.c_str(), asFUNCTION(ConstructUIElementWithName<T>), asCALL_CDECL);
     engine->RegisterObjectMethod(className, "void setStyle(const XMLElement& in)", asFUNCTION(UIElementSetStyle<T>), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod(className, "void setStyleAuto(XMLFile@+)", asFUNCTION(UIElementSetStyleAuto<T>), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "void setName(const string& in)", asMETHOD(T, setName), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setPosition(const IntVector2& in)", asMETHODPR(T, setPosition, (const IntVector2&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setPosition(int, int)", asMETHODPR(T, setPosition, (int, int), void), asCALL_THISCALL);
@@ -393,6 +392,8 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "void setFocusable(bool)", asMETHOD(T, setFocusable), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setFocus(bool)", asMETHOD(T, setFocus), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setVisible(bool)", asMETHOD(T, setVisible), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setStyleAuto(XMLFile@+)", asFUNCTION(UIElementSetStyleAuto<T>), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "void bringToFront()", asMETHOD(T, bringToFront), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void addChild(UIElement@+)", asMETHOD(T, addChild), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void removeChild(UIElement@+)", asMETHOD(T, removeChild), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void removeAllChildren()", asMETHOD(T, removeAllChildren), asCALL_THISCALL);
@@ -424,6 +425,7 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "UIElement@+ getChild(uint) const", asMETHODPR(T, getChild, (unsigned) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ getChild(const string& in, bool) const", asMETHODPR(T, getChild, (const std::string&, bool) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ getParent() const", asMETHOD(T, getParent), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "UIElement@+ getRootElement() const", asMETHOD(T, getRootElement), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "XMLElement getStyleElement(XMLFile@+) const", asMETHODPR(T, getStyleElement, (XMLFile*) const, XMLElement), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "IntVector2 screenToElement(const IntVector2& in)", asMETHOD(T, screenToElement), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "IntVector2 elementToScreen(const IntVector2& in)", asMETHOD(T, elementToScreen), asCALL_THISCALL);

+ 0 - 1
Engine/Engine/RegisterUI.cpp

@@ -273,7 +273,6 @@ static void registerUI(asIScriptEngine* engine)
     engine->RegisterObjectBehaviour("UI", asBEHAVE_RELEASE, "void f()", asMETHOD(UI, releaseRef), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void setCursor(Cursor@+)", asMETHOD(UI, setCursor), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void setFocusElement(UIElement@+)", asMETHOD(UI, setFocusElement), asCALL_THISCALL);
-    engine->RegisterObjectMethod("UI", "void bringToFront(UIElement@+)", asMETHOD(UI, bringToFront), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void clear()", asMETHOD(UI, clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@ createElement(const string& in, const string& in)", asFUNCTION(UICreateElement), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("UI", "UIElement@ loadLayout(XMLFile@+)", asFUNCTION(UILoadLayout), asCALL_CDECL_OBJLAST);

+ 0 - 1
Engine/UI/Button.cpp

@@ -35,7 +35,6 @@ Button::Button(const std::string& name) :
     mLabelOffset(IntVector2::sZero),
     mPressed(false)
 {
-    mClipChildren = true;
     mEnabled = true;
 }
 

+ 167 - 0
Engine/UI/MenuItem.cpp

@@ -0,0 +1,167 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "InputEvents.h"
+#include "MenuItem.h"
+#include "UIEvents.h"
+
+#include "DebugNew.h"
+
+MenuItem::MenuItem(const std::string& name) :
+    Button(name),
+    mPopupOffset(IntVector2::sZero),
+    mShowPopup(false)
+{
+    subscribeToEvent(EVENT_TRYFOCUS, EVENT_HANDLER(MenuItem, handleTryFocus));
+}
+
+MenuItem::~MenuItem()
+{
+}
+
+void MenuItem::setStyle(const XMLElement& element, ResourceCache* cache)
+{
+    Button::setStyle(element, cache);
+    
+    if (element.hasChildElement("popup"))
+    {
+        UIElement* root = getRootElement();
+        if (root)
+            setPopup(root->getChild(element.getChildElement("popup").getString("name"), true));
+    }
+    if (element.hasChildElement("popupoffset"))
+        setPopupOffset(element.getChildElement("popupoffset").getIntVector2("value"));
+}
+
+void MenuItem::update(float timeStep)
+{
+    // Keep pressed state if showing the popup
+    if ((!mHovering) && (!mShowPopup))
+        setPressed(false);
+}
+
+void MenuItem::onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+{
+    // Keep pressed state if showing the popup
+    setPressed(((buttons & MOUSEB_LEFT) != 0) || (mShowPopup));
+    mHovering = true;
+}
+
+void MenuItem::onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+{
+    setPressed(true);
+    // Toggle popup visibility if exists
+    showPopup(!mShowPopup);
+    
+    // Send event on each click if no popup, or whenever the popup is opened
+    if ((!mPopup) || (mShowPopup))
+    {
+        using namespace ItemSelected;
+        
+        VariantMap eventData;
+        eventData[P_ELEMENT] = (void*)this;
+        sendEvent(EVENT_ITEMSELECTED, eventData);
+    }
+}
+
+void MenuItem::setPopup(UIElement* popup)
+{
+    if (popup == this)
+        return;
+    if ((mPopup) && (!popup))
+        showPopup(false);
+    mPopup = popup;
+    // Detach from current parent (if any) to only show when it is time
+    if (mPopup)
+    {
+        UIElement* parent = mPopup->getParent();
+        parent->removeChild(mPopup);
+    }
+}
+
+void MenuItem::setPopupOffset(const IntVector2& offset)
+{
+    mPopupOffset = offset;
+}
+
+void MenuItem::setPopupOffset(int x, int y)
+{
+    mPopupOffset = IntVector2(x, y);
+}
+
+void MenuItem::showPopup(bool enable)
+{
+    if (!mPopup)
+        return;
+    
+    // Find the UI root element for showing the popup
+    UIElement* root = getRootElement();
+    if (!root)
+        return;
+    
+    if (enable)
+    {
+        mPopup->setPosition(getScreenPosition() + mPopupOffset);
+        mPopup->setVisible(true);
+        root->addChild(mPopup);
+        
+        // Set fixed high priority
+        mPopup->setBringToFront(false);
+        mPopup->setBringToBack(false);
+        mPopup->bringToFront();
+    }
+    else
+        root->removeChild(mPopup);
+    
+    mShowPopup = enable;
+}
+
+void MenuItem::handleTryFocus(StringHash eventType, VariantMap& eventData)
+{
+    if (!mShowPopup)
+        return;
+    
+    using namespace TryFocus;
+    
+    UIElement* focusElement = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
+    
+    // If global defocus, hide the popup
+    if (!focusElement)
+    {
+        showPopup(false);
+        return;
+    }
+    
+    // Otherwise see if the focused element has either the menu item or the popup in its parent chain.
+    // In that case, do not hide
+    while (focusElement)
+    {
+        if ((focusElement == this) || (focusElement == mPopup))
+            return;
+        focusElement = focusElement->getParent();
+    }
+    
+    showPopup(false);
+}
+

+ 77 - 0
Engine/UI/MenuItem.h

@@ -0,0 +1,77 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#ifndef UI_MENUITEM_H
+#define UI_MENUITEM_H
+
+#include "Button.h"
+
+//! A button that either triggers a menu selection or shows a popup element
+class MenuItem : public Button
+{
+    DEFINE_TYPE(MenuItem);
+    
+    //! Construct with name
+    MenuItem(const std::string& name = std::string());
+    //! Destruct
+    virtual ~MenuItem();
+    
+    //! Set UI element style from XML data
+    virtual void setStyle(const XMLElement& element, ResourceCache* cache);
+    //! Perform UI element update
+    virtual void update(float timeStep);
+    //! React to mouse hover
+    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    //! React to mouse click
+    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    
+    //! Set popup element to show on selection
+    void setPopup(UIElement* element);
+    //! Set popup offset when showing it
+    void setPopupOffset(const IntVector2& offset);
+    //! Set popup offset when showing it
+    void setPopupOffset(int x, int y);
+    //! Force the popup to show or hide
+    void showPopup(bool enable);
+    
+    //! Return popup element
+    UIElement* getPopup() const { return mPopup; }
+    //! Return popup offset
+    const IntVector2& getPopupOffset() const { return mPopupOffset; }
+    //! Return whether popup is open
+    bool getShowPopup() const { return mShowPopup; }
+    
+protected:
+    //! Popup element
+    SharedPtr<UIElement> mPopup;
+    //! Popup element offset
+    IntVector2 mPopupOffset;
+    //! Show popup flag
+    bool mShowPopup;
+    
+private:
+    //! Handle focus change attempt in case the popup needs to be hidden
+    void handleTryFocus(StringHash eventType, VariantMap& eventData);
+};
+
+#endif // UI_MENUITEM_H

+ 6 - 9
Engine/UI/ScrollView.cpp

@@ -51,16 +51,13 @@ void ScrollView::setStyle(const XMLElement& element, ResourceCache* cache)
     if (element.hasChildElement("viewsize"))
         setViewSize(element.getChildElement("viewsize").getIntVector2("value"));
     
-    // If sliders specified, search for them on own hierarchy level
-    if (element.hasChildElement("horizontalslider"))
+    UIElement* root = getRootElement();
+    if (root)
     {
-        if (mParent)
-            setHorizontalSlider(dynamic_cast<Slider*>(mParent->getChild(element.getChildElement("horizontalslider").getString("name"))));
-    }
-    if (element.hasChildElement("verticalslider"))
-    {
-        if (mParent)
-            setVerticalSlider(dynamic_cast<Slider*>(mParent->getChild(element.getChildElement("verticalslider").getString("name"))));
+        if (element.hasChildElement("horizontalslider"))
+            setHorizontalSlider(dynamic_cast<Slider*>(root->getChild(element.getChildElement("horizontalslider").getString("name"), true)));
+        if (element.hasChildElement("verticalslider"))
+            setVerticalSlider(dynamic_cast<Slider*>(root->getChild(element.getChildElement("verticalslider").getString("name"), true)));
     }
 }
 

+ 1 - 31
Engine/UI/UI.cpp

@@ -136,36 +136,6 @@ void UI::setFocusElement(UIElement* element)
         element->setFocus(true);
 }
 
-void UI::bringToFront(UIElement* element)
-{
-    if (!element)
-        return;
-    
-    // Follow the parent chain to the top level window. If it has BringToFront mode, bring it to front now
-    UIElement* ptr = element;
-    while ((ptr) && (ptr->getParent() != mRootElement))
-        ptr = ptr->getParent();
-    if ((!ptr) || (!ptr->getBringToFront()))
-        return;
-    
-    // Get the highest priority used by all other top level elements, decrease their priority by one,
-    // and assign that to new front element. However, take into account only active (enabled) elements
-    // and those which have the BringToBack flag set
-    int maxPriority = M_MIN_INT;
-    std::vector<UIElement*> topLevelElements = mRootElement->getChildren();
-    for (std::vector<UIElement*>::iterator i = topLevelElements.begin(); i != topLevelElements.end(); ++i)
-    {
-        UIElement* other = *i;
-        if ((other->isEnabled()) && (other->getBringToBack()) && (other != ptr))
-        {
-            int priority = other->getPriority();
-            maxPriority = max(priority, maxPriority);
-            other->setPriority(priority - 1);
-        }
-    }
-    ptr->setPriority(maxPriority);
-}
-
 void UI::clear()
 {
     mRootElement->removeAllChildren();
@@ -476,7 +446,7 @@ void UI::handleMouseButtonDown(StringHash eventType, VariantMap& eventData)
             if (button == MOUSEB_LEFT)
             {
                 setFocusElement(element);
-                bringToFront(element);
+                element->bringToFront();
             }
             
             // Handle click

+ 38 - 0
Engine/UI/UIElement.cpp

@@ -415,6 +415,34 @@ void UIElement::setStyleAuto(XMLFile* file, ResourceCache* cache)
     setStyle(element, cache);
 }
 
+void UIElement::bringToFront()
+{
+    // Follow the parent chain to the top level window. If it has BringToFront mode, bring it to front now
+    UIElement* root = getRootElement();
+    UIElement* ptr = this;
+    while ((ptr) && (ptr->getParent() != root))
+        ptr = ptr->getParent();
+    if ((!ptr) || (!ptr->getBringToFront()))
+        return;
+    
+    // Get the highest priority used by all other top level elements, decrease their priority by one,
+    // and assign that to new front element. However, take into account only active (enabled) elements
+    // and those which have the BringToBack flag set
+    int maxPriority = M_MIN_INT;
+    std::vector<UIElement*> topLevelElements = root->getChildren();
+    for (std::vector<UIElement*>::iterator i = topLevelElements.begin(); i != topLevelElements.end(); ++i)
+    {
+        UIElement* other = *i;
+        if ((other->isEnabled()) && (other->getBringToBack()) && (other != ptr))
+        {
+            int priority = other->getPriority();
+            maxPriority = max(priority, maxPriority);
+            other->setPriority(priority - 1);
+        }
+    }
+    ptr->setPriority(maxPriority);
+}
+
 void UIElement::addChild(UIElement* element)
 {
     if ((!element) || (element->mParent == this) || (mParent == element))
@@ -557,6 +585,16 @@ UIElement* UIElement::getChild(const std::string& name, bool recursive) const
     return 0;
 }
 
+UIElement* UIElement::getRootElement() const
+{
+    UIElement* root = mParent;
+    if (!root)
+        return 0;
+    while (root->getParent())
+        root = root->getParent();
+    return root;
+}
+
 XMLElement UIElement::getStyleElement(XMLFile* file) const
 {
     if (file)

+ 4 - 0
Engine/UI/UIElement.h

@@ -151,6 +151,8 @@ public:
     void setVisible(bool enable);
     //! Set style from an XML file. Find the style element automatically
     void setStyleAuto(XMLFile* file, ResourceCache* cache);
+    //! Bring UI element to front
+    void bringToFront();
     //! Add a child element
     void addChild(UIElement* element);
     //! Remove a child element
@@ -216,6 +218,8 @@ public:
     std::vector<UIElement*> getChildren(bool recursive = false) const;
     //! Return parent element
     UIElement* getParent() const { return mParent; }
+    //! Return root element
+    UIElement* getRootElement() const;
     //! Return first matching UI style element from an XML file. If not found, return empty
     XMLElement getStyleElement(XMLFile* file) const;
     

+ 0 - 1
Engine/UI/Window.cpp

@@ -39,7 +39,6 @@ Window::Window(const std::string& name) :
     mDragMode(DRAG_NONE)
 {
     mBringToFront = true;
-    mClipChildren = true;
     mEnabled = true;
     
     validateSize();

+ 3 - 3
Examples/Test/Application.cpp

@@ -204,9 +204,9 @@ void Application::init()
     cursor->setPosition(renderer->getWidth() / 2, renderer->getHeight() / 2);
     ui->setCursor(cursor);
     
-    //XMLFile* uiLayout = mCache->getResource<XMLFile>("UI/TestLayout.xml");
-    //SharedPtr<UIElement> layoutElement = ui->loadLayout(uiLayout, uiStyle);
-    //uiRoot->addChild(layoutElement);
+    XMLFile* uiLayout = mCache->getResource<XMLFile>("UI/TestLayout.xml");
+    SharedPtr<UIElement> layoutElement = ui->loadLayout(uiLayout, uiStyle);
+    uiRoot->addChild(layoutElement);
     
     mScene = mEngine->createScene();
     PhysicsWorld* world = mScene->getExtension<PhysicsWorld>();