Browse Source

Added MenuItem UI element.
Added TryFocus UI event, sent whenever the focus element is attempted to be changed.
Added setBringToBack() & getCombinedScreenRect() functions to UIElement.
Do not transmit hover to other UI elements during mouse drag.
Minor UI code cleanup.
Documentation update.

Lasse Öörni 15 years ago
parent
commit
8c780caa0e

+ 7 - 0
Bin/Data/UI/DefaultStyle.xml

@@ -38,6 +38,13 @@
             <imagerect value="112 0 116 16" />
             <imagerect value="112 0 116 16" />
         </cursor>
         </cursor>
     </element>
     </element>
+    <element type="MenuItem">
+        <texture name="Textures/UI.png" />
+        <inactiverect value="98 2 110 14" />
+        <pressedrect value="96 0 112 16" />
+        <border value="2 2 2 2" />
+        <hoveroffset value="0 16" />
+    </element>
     <element type="ScrollView">
     <element type="ScrollView">
         <size value="16 16" />
         <size value="16 16" />
         <texture name="Textures/UI.png" />
         <texture name="Textures/UI.png" />

+ 50 - 0
Bin/Data/UI/TestLayout.xml

@@ -51,5 +51,55 @@
             <position value="150 270" />
             <position value="150 270" />
             <size value="200 16" />
             <size value="200 16" />
         </element>
         </element>
+        <element type="Window" name="TestPopup">
+            <size value="100 80" />
+            <element type="MenuItem">
+                <position value="10 10" />
+                <size value="80 16" />
+                <element type="Text">
+                    <text value="MenuItem3" />
+                    <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="MenuItem4" />
+                    <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="MenuItem5" />
+                    <font name="cour.ttf" size="12" />
+                    <alignment horizontal="center" vertical="center" />
+                </element>
+            </element>
+        </element>
+        <element type="MenuItem">
+            <position value="150 10" />
+            <size value="80 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" />
+            <size value="80 16" />
+            <element type="Text">
+                <text value="MenuItem2" />
+                <font name="cour.ttf" size="12" />
+                <alignment horizontal="center" vertical="center" />
+            </element>
+        </element>
     </element>
     </element>
 </layout>
 </layout>

+ 2 - 2
Engine/Engine/Engine.cpp

@@ -98,8 +98,8 @@ Engine::Engine(const std::string& logFileName, bool headless) :
         "ControlsUpdate", "ControlsPlayback", "MouseButtonDown", "MouseButtonUp", "MouseMove", "KeyDown", "KeyUp", "Char",
         "ControlsUpdate", "ControlsPlayback", "MouseButtonDown", "MouseButtonUp", "MouseMove", "KeyDown", "KeyUp", "Char",
         "PeerConnected", "NetworkPacket", "PeerDisconnected", "PhysicsPreStep", "PhysicsPostStep", "PhysicsCollision",
         "PeerConnected", "NetworkPacket", "PeerDisconnected", "PhysicsPreStep", "PhysicsPostStep", "PhysicsCollision",
         "EntityCollision", "WindowMessage", "WindowResized", "BeginFrame", "EndFrame", "SceneUpdate", "ScenePostUpdate",
         "EntityCollision", "WindowMessage", "WindowResized", "BeginFrame", "EndFrame", "SceneUpdate", "ScenePostUpdate",
-        "AsyncLoadProgress", "AsyncLoadFinished", "Focused", "Defocused", "Pressed", "Toggled", "SliderChanged", "ViewChanged",
-        "TextChanged", "TextFinished", ""
+        "AsyncLoadProgress", "AsyncLoadFinished", "TryFocus", "Focused", "Defocused", "Pressed", "Toggled", "SliderChanged",
+        "ViewChanged", "TextChanged", "TextFinished", "ItemSelected", ""
     };
     };
     
     
     for (unsigned i = 0; inbuiltEvents[i].length(); ++i)
     for (unsigned i = 0; inbuiltEvents[i].length(); ++i)

+ 18 - 0
Engine/Engine/RegisterTemplates.h

@@ -387,6 +387,7 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "void setPriority(int)", asMETHOD(T, setPriority), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setPriority(int)", asMETHOD(T, setPriority), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setOpacity(float)", asMETHOD(T, setOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setOpacity(float)", asMETHOD(T, setOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setBringToFront(bool)", asMETHOD(T, setBringToFront), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setBringToFront(bool)", asMETHOD(T, setBringToFront), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setBringToBack(bool)", asMETHOD(T, setBringToBack), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setClipChildren(bool)", asMETHOD(T, setClipChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setClipChildren(bool)", asMETHOD(T, setClipChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setEnabled(bool)", asMETHOD(T, setEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setEnabled(bool)", asMETHOD(T, setEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setFocusable(bool)", asMETHOD(T, setFocusable), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void setFocusable(bool)", asMETHOD(T, setFocusable), asCALL_THISCALL);
@@ -411,6 +412,7 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "int getPriority() const", asMETHOD(T, getPriority), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "int getPriority() const", asMETHOD(T, getPriority), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "float getOpacity() const", asMETHOD(T, getOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "float getOpacity() const", asMETHOD(T, getOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool getBringToFront() const", asMETHOD(T, getBringToFront), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool getBringToFront() const", asMETHOD(T, getBringToFront), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "bool getBringToBack() const", asMETHOD(T, getBringToBack), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool getClipChildren() const", asMETHOD(T, getClipChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool getClipChildren() const", asMETHOD(T, getClipChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isEnabled() const", asMETHOD(T, isEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isEnabled() const", asMETHOD(T, isEnabled), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isFocusable() const", asMETHOD(T, isFocusable), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isFocusable() const", asMETHOD(T, isFocusable), asCALL_THISCALL);
@@ -427,6 +429,7 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "IntVector2 elementToScreen(const IntVector2& in)", asMETHOD(T, elementToScreen), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "IntVector2 elementToScreen(const IntVector2& in)", asMETHOD(T, elementToScreen), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInside(IntVector2, bool)", asMETHOD(T, isInside), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInside(IntVector2, bool)", asMETHOD(T, isInside), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInsideCombined(IntVector2, bool)", asMETHOD(T, isInsideCombined), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInsideCombined(IntVector2, bool)", asMETHOD(T, isInsideCombined), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "IntRect getCombinedScreenRect()", asMETHOD(T, getCombinedScreenRect), asCALL_THISCALL);
 }
 }
 
 
 //! Template function for registering a class derived from BorderImage
 //! Template function for registering a class derived from BorderImage
@@ -447,4 +450,19 @@ template <class T> void registerBorderImage(asIScriptEngine* engine, const char*
     engine->RegisterObjectMethod(className, "const IntVector2& getHoverOffset() const", asMETHOD(T, getHoverOffset), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const IntVector2& getHoverOffset() const", asMETHOD(T, getHoverOffset), asCALL_THISCALL);
 }
 }
 
 
+//! Template function for registering a class derived from Button
+template <class T> void registerButton(asIScriptEngine* engine, const char* className)
+{
+    registerBorderImage<T>(engine, className);
+    engine->RegisterObjectMethod(className, "void setInactiveRect(const IntRect& in)", asMETHODPR(T, setInactiveRect, (const IntRect&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setInactiveRect(int, int, int, int)", asMETHODPR(T, setInactiveRect, (int, int, int, int), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setPressedRect(const IntRect& in)", asMETHODPR(T, setPressedRect, (const IntRect&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setPressedRect(int, int, int, int)", asMETHODPR(T, setPressedRect, (int, int, int, int), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setLabelOffset(const IntVector2& in)", asMETHODPR(T, setLabelOffset, (const IntVector2&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void setLabelOffset(int, int)", asMETHODPR(T, setLabelOffset, (int, int), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "const IntRect& getInactiveRect() const", asMETHOD(T, getInactiveRect), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "const IntRect& getPressedRect() const", asMETHOD(T, getPressedRect), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "const IntVector2& getLabelOffset() const", asMETHOD(T, getLabelOffset), asCALL_THISCALL);
+}
+
 #endif // ENGINE_REGISTERTEMPLATES_H
 #endif // ENGINE_REGISTERTEMPLATES_H

+ 16 - 10
Engine/Engine/RegisterUI.cpp

@@ -28,6 +28,7 @@
 #include "Engine.h"
 #include "Engine.h"
 #include "Font.h"
 #include "Font.h"
 #include "LineEdit.h"
 #include "LineEdit.h"
+#include "MenuItem.h"
 #include "RegisterTemplates.h"
 #include "RegisterTemplates.h"
 #include "ScrollView.h"
 #include "ScrollView.h"
 #include "Slider.h"
 #include "Slider.h"
@@ -89,16 +90,7 @@ static void registerCursor(asIScriptEngine* engine)
 
 
 static void registerButton(asIScriptEngine* engine)
 static void registerButton(asIScriptEngine* engine)
 {
 {
-    registerBorderImage<Button>(engine, "Button");
-    engine->RegisterObjectMethod("Button", "void setInactiveRect(const IntRect& in)", asMETHODPR(Button, setInactiveRect, (const IntRect&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "void setInactiveRect(int, int, int, int)", asMETHODPR(Button, setInactiveRect, (int, int, int, int), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "void setPressedRect(const IntRect& in)", asMETHODPR(Button, setPressedRect, (const IntRect&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "void setPressedRect(int, int, int, int)", asMETHODPR(Button, setPressedRect, (int, int, int, int), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "void setLabelOffset(const IntVector2& in)", asMETHODPR(Button, setLabelOffset, (const IntVector2&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "void setLabelOffset(int, int)", asMETHODPR(Button, setLabelOffset, (int, int), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "const IntRect& getInactiveRect() const", asMETHOD(Button, getInactiveRect), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "const IntRect& getPressedRect() const", asMETHOD(Button, getPressedRect), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Button", "const IntVector2& getLabelOffset() const", asMETHOD(Button, getLabelOffset), asCALL_THISCALL);
+    registerButton<Button>(engine, "Button");
     registerRefCasts<UIElement, Button>(engine, "UIElement", "Button");
     registerRefCasts<UIElement, Button>(engine, "UIElement", "Button");
 }
 }
 
 
@@ -180,6 +172,19 @@ static void registerLineEdit(asIScriptEngine* engine)
     registerRefCasts<UIElement, LineEdit>(engine, "UIElement", "LineEdit");
     registerRefCasts<UIElement, LineEdit>(engine, "UIElement", "LineEdit");
 }
 }
 
 
+static void registerMenuItem(asIScriptEngine* engine)
+{
+    registerButton<MenuItem>(engine, "MenuItem");
+    engine->RegisterObjectMethod("MenuItem", "void setPopup(UIElement@+)", asMETHOD(MenuItem, setPopup), asCALL_THISCALL);
+    engine->RegisterObjectMethod("MenuItem", "void setPopupOffset(const IntVector2& in)", asMETHODPR(MenuItem, setPopupOffset, (const IntVector2&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("MenuItem", "void setPopupOffset(int, int)", asMETHODPR(MenuItem, setPopupOffset, (int, int), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("MenuItem", "void showPopup(bool)", asMETHOD(MenuItem, showPopup), asCALL_THISCALL);
+    engine->RegisterObjectMethod("MenuItem", "UIElement@+ getPopup() const", asMETHOD(MenuItem, getPopup), asCALL_THISCALL);
+    engine->RegisterObjectMethod("MenuItem", "const IntVector2& getPopupOffset() const", asMETHOD(MenuItem, getPopupOffset), asCALL_THISCALL);
+    engine->RegisterObjectMethod("MenuItem", "bool getShowPopup() const", asMETHOD(MenuItem, getShowPopup), asCALL_THISCALL);
+    registerRefCasts<UIElement, MenuItem>(engine, "UIElement", "MenuItem");
+}
+
 static void registerWindow(asIScriptEngine* engine)
 static void registerWindow(asIScriptEngine* engine)
 {
 {
     registerBorderImage<Window>(engine, "Window");
     registerBorderImage<Window>(engine, "Window");
@@ -300,6 +305,7 @@ void registerUILibrary(asIScriptEngine* engine)
     registerScrollView(engine);
     registerScrollView(engine);
     registerText(engine);
     registerText(engine);
     registerLineEdit(engine);
     registerLineEdit(engine);
+    registerMenuItem(engine);
     registerWindow(engine);
     registerWindow(engine);
     registerUI(engine);
     registerUI(engine);
 }
 }

+ 3 - 0
Engine/UI/BaseUIElementFactory.cpp

@@ -27,6 +27,7 @@
 #include "CheckBox.h"
 #include "CheckBox.h"
 #include "Cursor.h"
 #include "Cursor.h"
 #include "LineEdit.h"
 #include "LineEdit.h"
+#include "MenuItem.h"
 #include "ScrollView.h"
 #include "ScrollView.h"
 #include "Slider.h"
 #include "Slider.h"
 #include "Text.h"
 #include "Text.h"
@@ -44,6 +45,8 @@ UIElement* BaseUIElementFactory::createElement(ShortStringHash type, const std::
         return new Cursor(name);
         return new Cursor(name);
     if (type == LineEdit::getTypeStatic())
     if (type == LineEdit::getTypeStatic())
         return new LineEdit(std::string(), name);
         return new LineEdit(std::string(), name);
+    if (type == MenuItem::getTypeStatic())
+        return new MenuItem(name);
     if (type == ScrollView::getTypeStatic())
     if (type == ScrollView::getTypeStatic())
         return new ScrollView(name);
         return new ScrollView(name);
     if (type == Slider::getTypeStatic())
     if (type == Slider::getTypeStatic())

+ 2 - 2
Engine/UI/ScrollView.cpp

@@ -123,7 +123,7 @@ void ScrollView::updateViewFromSliders()
     
     
     IntVector2 oldPosition = mViewPosition;
     IntVector2 oldPosition = mViewPosition;
     IntVector2 newPosition = mViewPosition;
     IntVector2 newPosition = mViewPosition;
-    IntVector2 size = getSize();
+    const IntVector2& size = getSize();
     
     
     if (mHorizontalSlider)
     if (mHorizontalSlider)
         newPosition.mX = (int)(mHorizontalSlider->getValue() * (float)size.mX);
         newPosition.mX = (int)(mHorizontalSlider->getValue() * (float)size.mX);
@@ -146,7 +146,7 @@ void ScrollView::updateViewFromSliders()
 
 
 void ScrollView::updateSliders()
 void ScrollView::updateSliders()
 {
 {
-    IntVector2 size = getSize();
+    const IntVector2& size = getSize();
     
     
     if ((mHorizontalSlider) && (size.mX > 0) && (mViewSize.mX > 0))
     if ((mHorizontalSlider) && (size.mX > 0) && (mViewSize.mX > 0))
     {
     {

+ 17 - 6
Engine/UI/UI.cpp

@@ -37,6 +37,7 @@
 #include "StringUtils.h"
 #include "StringUtils.h"
 #include "Texture2D.h"
 #include "Texture2D.h"
 #include "UI.h"
 #include "UI.h"
+#include "UIEvents.h"
 #include "VertexShader.h"
 #include "VertexShader.h"
 
 
 #include <algorithm>
 #include <algorithm>
@@ -100,7 +101,7 @@ void UI::setCursor(Cursor* cursor)
         mCursor = cursor;
         mCursor = cursor;
         
         
         IntVector2 pos = mCursor->getPosition();
         IntVector2 pos = mCursor->getPosition();
-        IntVector2 rootSize = mRootElement->getSize();
+        const IntVector2& rootSize = mRootElement->getSize();
         pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
         pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
         pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
         pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
         mCursor->setPosition(pos);
         mCursor->setPosition(pos);
@@ -109,6 +110,12 @@ void UI::setCursor(Cursor* cursor)
 
 
 void UI::setFocusElement(UIElement* element)
 void UI::setFocusElement(UIElement* element)
 {
 {
+    using namespace TryFocus;
+    
+    VariantMap eventData;
+    eventData[P_ELEMENT] = (void*)element;
+    sendEvent(EVENT_TRYFOCUS, eventData);
+    
     if (element)
     if (element)
     {
     {
         if ((element->hasFocus()) || (!element->isFocusable()))
         if ((element->hasFocus()) || (!element->isFocusable()))
@@ -143,13 +150,13 @@ void UI::bringToFront(UIElement* element)
     
     
     // Get the highest priority used by all other top level elements, decrease their priority by one,
     // 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 assign that to new front element. However, take into account only active (enabled) elements
-    // so that any noninteractive overlays are left alone
+    // and those which have the BringToBack flag set
     int maxPriority = M_MIN_INT;
     int maxPriority = M_MIN_INT;
     std::vector<UIElement*> topLevelElements = mRootElement->getChildren();
     std::vector<UIElement*> topLevelElements = mRootElement->getChildren();
     for (std::vector<UIElement*>::iterator i = topLevelElements.begin(); i != topLevelElements.end(); ++i)
     for (std::vector<UIElement*>::iterator i = topLevelElements.begin(); i != topLevelElements.end(); ++i)
     {
     {
         UIElement* other = *i;
         UIElement* other = *i;
-        if ((other->isEnabled()) && (other != ptr))
+        if ((other->isEnabled()) && (other->getBringToBack()) && (other != ptr))
         {
         {
             int priority = other->getPriority();
             int priority = other->getPriority();
             maxPriority = max(priority, maxPriority);
             maxPriority = max(priority, maxPriority);
@@ -179,7 +186,11 @@ void UI::update(float timeStep)
         IntVector2 pos = mCursor->getPosition();
         IntVector2 pos = mCursor->getPosition();
         UIElement* element = getElementAt(pos);
         UIElement* element = getElementAt(pos);
         if (element)
         if (element)
-            element->onHover(element->screenToElement(pos), pos, mMouseButtons);
+        {
+            // If a drag is going on, transmit hover only to the element being dragged
+            if ((!mMouseDragElement) || (mMouseDragElement == element))
+                element->onHover(element->screenToElement(pos), pos, mMouseButtons);
+        }
     }
     }
     
     
     {
     {
@@ -193,7 +204,7 @@ void UI::update(float timeStep)
         
         
         mBatches.clear();
         mBatches.clear();
         mQuads.clear();
         mQuads.clear();
-        IntVector2 rootSize = mRootElement->getSize();
+        const IntVector2& rootSize = mRootElement->getSize();
         getBatches(mRootElement, IntRect(0, 0, rootSize.mX, rootSize.mY));
         getBatches(mRootElement, IntRect(0, 0, rootSize.mX, rootSize.mY));
     }
     }
 }
 }
@@ -429,7 +440,7 @@ void UI::handleMouseMove(StringHash eventType, VariantMap& eventData)
         IntVector2 pos = mCursor->getPosition();
         IntVector2 pos = mCursor->getPosition();
         pos.mX += eventData[P_X].getInt();
         pos.mX += eventData[P_X].getInt();
         pos.mY += eventData[P_Y].getInt();
         pos.mY += eventData[P_Y].getInt();
-        IntVector2 rootSize = mRootElement->getSize();
+        const IntVector2& rootSize = mRootElement->getSize();
         pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
         pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
         pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
         pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
         mCursor->setPosition(pos);
         mCursor->setPosition(pos);

+ 16 - 2
Engine/UI/UIElement.cpp

@@ -37,6 +37,7 @@ UIElement::UIElement(const std::string& name) :
     mPriority(0),
     mPriority(0),
     mOpacity(1.0f),
     mOpacity(1.0f),
     mBringToFront(false),
     mBringToFront(false),
+    mBringToBack(true),
     mClipChildren(false),
     mClipChildren(false),
     mEnabled(false),
     mEnabled(false),
     mFocusable(false),
     mFocusable(false),
@@ -131,6 +132,8 @@ void UIElement::setStyle(const XMLElement& element, ResourceCache* cache)
         setHoverColor(element.getChildElement("hovercolor").getColor("value"));
         setHoverColor(element.getChildElement("hovercolor").getColor("value"));
     if (element.hasChildElement("bringtofront"))
     if (element.hasChildElement("bringtofront"))
         setBringToFront(element.getChildElement("bringtofront").getBool("enable"));
         setBringToFront(element.getChildElement("bringtofront").getBool("enable"));
+    if (element.hasChildElement("bringtoback"))
+        setBringToBack(element.getChildElement("bringtoback").getBool("enable"));
     if (element.hasChildElement("clipchildren"))
     if (element.hasChildElement("clipchildren"))
         setClipChildren(element.getChildElement("clipchildren").getBool("enable"));
         setClipChildren(element.getChildElement("clipchildren").getBool("enable"));
     if (element.hasChildElement("enabled"))
     if (element.hasChildElement("enabled"))
@@ -364,6 +367,11 @@ void UIElement::setBringToFront(bool enable)
     mBringToFront = enable;
     mBringToFront = enable;
 }
 }
 
 
+void UIElement::setBringToBack(bool enable)
+{
+    mBringToBack = enable;
+}
+
 void UIElement::setClipChildren(bool enable)
 void UIElement::setClipChildren(bool enable)
 {
 {
     mClipChildren = enable;
     mClipChildren = enable;
@@ -592,6 +600,13 @@ bool UIElement::isInsideCombined(IntVector2 position, bool isScreen)
     if (!isScreen)
     if (!isScreen)
         position = elementToScreen(position);
         position = elementToScreen(position);
     
     
+    IntRect combined = getCombinedScreenRect();
+    return (position.mX >= combined.mLeft) && (position.mY >= combined.mTop) && (position.mX < combined.mRight) &&
+        (position.mY < combined.mBottom);
+}
+
+IntRect UIElement::getCombinedScreenRect()
+{
     IntVector2 screenPosition(getScreenPosition());
     IntVector2 screenPosition(getScreenPosition());
     IntRect combined(screenPosition.mX, screenPosition.mY, screenPosition.mX + mSize.mX, screenPosition.mY + mSize.mY);
     IntRect combined(screenPosition.mX, screenPosition.mY, screenPosition.mX + mSize.mX, screenPosition.mY + mSize.mY);
     
     
@@ -609,8 +624,7 @@ bool UIElement::isInsideCombined(IntVector2 position, bool isScreen)
             combined.mBottom = childPos.mY + childSize.mY;
             combined.mBottom = childPos.mY + childSize.mY;
     }
     }
     
     
-    return (position.mX >= combined.mLeft) && (position.mY >= combined.mTop) && (position.mX < combined.mRight) &&
-        (position.mY < combined.mBottom);
+    return combined;
 }
 }
 
 
 void UIElement::setHovering(bool enable)
 void UIElement::setHovering(bool enable)

+ 8 - 0
Engine/UI/UIElement.h

@@ -137,6 +137,8 @@ public:
     void setOpacity(float opacity);
     void setOpacity(float opacity);
     //! Set whether should be brought to front when focused
     //! Set whether should be brought to front when focused
     void setBringToFront(bool enable);
     void setBringToFront(bool enable);
+    //! Set whether should be put to background when another element is focused
+    void setBringToBack(bool enable);
     //! Set whether should clip child elements
     //! Set whether should clip child elements
     void setClipChildren(bool enable);
     void setClipChildren(bool enable);
     //! Set whether reacts to input
     //! Set whether reacts to input
@@ -188,6 +190,8 @@ public:
     float getOpacity() const { return mOpacity; }
     float getOpacity() const { return mOpacity; }
     //! Return whether should be brought to front when focused
     //! Return whether should be brought to front when focused
     bool getBringToFront() const { return mBringToFront; }
     bool getBringToFront() const { return mBringToFront; }
+    //! Return whether should be put to background when another element is focused
+    bool getBringToBack() const { return mBringToBack; }
     //! Return whether should clip child elements
     //! Return whether should clip child elements
     bool getClipChildren() const { return mClipChildren; }
     bool getClipChildren() const { return mClipChildren; }
     //! Return whether reacts to input
     //! Return whether reacts to input
@@ -223,6 +227,8 @@ public:
     bool isInside(IntVector2 position, bool isScreen);
     bool isInside(IntVector2 position, bool isScreen);
     //! Return whether a point (either in element or screen coordinates) is inside the combined rect of element and its children
     //! Return whether a point (either in element or screen coordinates) is inside the combined rect of element and its children
     bool isInsideCombined(IntVector2 position, bool isScreen);
     bool isInsideCombined(IntVector2 position, bool isScreen);
+    //! Return combined screen coordinate rect of element and its children
+    IntRect UIElement::getCombinedScreenRect();
     
     
     //! Set hovering state
     //! Set hovering state
     void setHovering(bool enable);
     void setHovering(bool enable);
@@ -254,6 +260,8 @@ protected:
     int mPriority;
     int mPriority;
     //! Bring to front when focused flag
     //! Bring to front when focused flag
     bool mBringToFront;
     bool mBringToFront;
+    //! Bring to back when defocused flag
+    bool mBringToBack;
     //! Clip children flag
     //! Clip children flag
     bool mClipChildren;
     bool mClipChildren;
     //! Reacts to input flag
     //! Reacts to input flag

+ 12 - 0
Engine/UI/UIEvents.h

@@ -26,6 +26,12 @@
 
 
 #include "Event.h"
 #include "Event.h"
 
 
+//! Trying to focus an UI element. Sent before checking for success. Also sent with 0 pointer for global defocus
+DEFINE_EVENT(EVENT_TRYFOCUS, TryFocus)
+{
+    EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
+}
+
 //! UI element focused
 //! UI element focused
 DEFINE_EVENT(EVENT_FOCUSED, Focused)
 DEFINE_EVENT(EVENT_FOCUSED, Focused)
 {
 {
@@ -79,4 +85,10 @@ DEFINE_EVENT(EVENT_TEXTFINISHED, TextFinished)
     EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
     EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
 }
 }
 
 
+//! Menu item selected
+DEFINE_EVENT(EVENT_ITEMSELECTED, ItemSelected)
+{
+    EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
+}
+
 #endif // UI_UIEVENTS_H
 #endif // UI_UIEVENTS_H

+ 2 - 8
Engine/UI/Window.cpp

@@ -249,14 +249,8 @@ void Window::validatePosition()
     IntVector2 position = getPosition();
     IntVector2 position = getPosition();
     IntVector2 halfSize = getSize() / 2;
     IntVector2 halfSize = getSize() / 2;
     
     
-    if (position.mX < -halfSize.mX)
-        position.mX = -halfSize.mX;
-    if (position.mX > parentSize.mX - halfSize.mX)
-        position.mX = parentSize.mX - halfSize.mX;
-    if (position.mY < -halfSize.mY)
-        position.mY = -halfSize.mY;
-    if (position.mY > parentSize.mY - halfSize.mY)
-        position.mY = parentSize.mY - halfSize.mY;
+    position.mX = clamp(position.mX, -halfSize.mX, parentSize.mX - halfSize.mX);
+    position.mY = clamp(position.mY, -halfSize.mY, parentSize.mY - halfSize.mY);
     
     
     setPosition(position);
     setPosition(position);
 }
 }