Browse Source

Support for different UI cursor shapes.

Lasse Öörni 15 years ago
parent
commit
c30b162267

BIN
Bin/Data/Textures/UI.png


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

@@ -17,10 +17,7 @@
         <hoveroffset value="0 16" />
     </element>
     <element type="Cursor">
-        <size value="12 24" />
-        <texture name="Textures/UI.png" />
-        <imagerect value="0 0 12 24" />
-        <hotspot value="0 0" />
+        <shape name="normal" texture="Textures/UI.png" imagerect="0 0 12 24" hotspot="0 0" />
     </element>
     <element type="DropDownList">
         <texture name="Textures/UI.png" />

+ 12 - 3
Engine/Engine/RegisterUI.cpp

@@ -100,10 +100,19 @@ static void registerBorderImage(asIScriptEngine* engine)
 
 static void registerCursor(asIScriptEngine* engine)
 {
+    engine->RegisterEnum("CursorShape");
+    engine->RegisterEnumValue("CursorShape", "CS_NORMAL", CS_NORMAL);
+    engine->RegisterEnumValue("CursorShape", "CS_RESIZEVERTICAL", CS_RESIZEVERTICAL);
+    engine->RegisterEnumValue("CursorShape", "CS_RESIZEDIAGONAL_TOPRIGHT", CS_RESIZEDIAGONAL_TOPRIGHT);
+    engine->RegisterEnumValue("CursorShape", "CS_RESIZEHORIZONTAL", CS_RESIZEHORIZONTAL);
+    engine->RegisterEnumValue("CursorShape", "CS_RESIZEDIAGONAL_TOPLEFT", CS_RESIZEDIAGONAL_TOPLEFT);
+    engine->RegisterEnumValue("CursorShape", "CS_ACCEPTDROP", CS_ACCEPTDROP);
+    engine->RegisterEnumValue("CursorShape", "CS_REJECTDROP", CS_REJECTDROP);
+    
     registerBorderImage<Cursor>(engine, "Cursor");
-    engine->RegisterObjectMethod("Cursor", "void setHotspot(const IntVector2& in)", asMETHODPR(Cursor, setHotspot, (const IntVector2&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Cursor", "void setHotspot(int, int)", asMETHODPR(Cursor, setHotspot, (int, int), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Cursor", "const IntVector2& getHotspot() const", asMETHOD(Cursor, getHotspot), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Cursor", "void defineShape(CursorShape, Texture@+, const IntRect& in, const IntVector2& in)", asMETHOD(Cursor, defineShape), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Cursor", "void setShape(CursorShape)", asMETHOD(Cursor, setShape), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Cursor", "CursorShape getShape() const", asMETHOD(Cursor, getShape), asCALL_THISCALL);
     registerRefCasts<UIElement, Cursor>(engine, "UIElement", "Cursor");
 }
 

+ 59 - 12
Engine/UI/Cursor.cpp

@@ -23,15 +23,36 @@
 
 #include "Precompiled.h"
 #include "Cursor.h"
+#include "ResourceCache.h"
+#include "StringUtils.h"
+#include "Texture2D.h"
 
 #include "DebugNew.h"
 
+static std::string shapeNames[] =
+{
+    "normal",
+    "resizevertical",
+    "resizediagonal_topright",
+    "resizehorizontal",
+    "resizediagonal_topleft",
+    "acceptdrop",
+    "rejectdrop"
+};
+
 Cursor::Cursor(const std::string& name) :
     BorderImage(name),
-    mHotspot(IntVector2::sZero)
+    mShape(CS_NORMAL)
 {
     // Show on top of all other UI elements
     mPriority = M_MAX_INT;
+    
+    for (unsigned i = 0; i < CS_MAX_SHAPES; ++i)
+    {
+        CursorShapeData& data = mShapeData[i];
+        data.mImageRect = IntRect::sZero;
+        data.mHotSpot = IntVector2::sZero;
+    }
 }
 
 Cursor::~Cursor()
@@ -40,25 +61,51 @@ Cursor::~Cursor()
 
 void Cursor::setStyle(const XMLElement& element, ResourceCache* cache)
 {
-    BorderImage::setStyle(element, cache);
+    UIElement::setStyle(element, cache);
     
-    if (element.hasChildElement("hotspot"))
-        setHotspot(element.getChildElement("hotspot").getIntVector2("value"));
+    XMLElement shapeElem = element.getChildElement("shape");
+    while (shapeElem)
+    {
+        CursorShape shape = (CursorShape)getIndexFromStringList(shapeElem.getStringLower("name"), shapeNames, 7, 0);
+        defineShape(shape, cache->getResource<Texture2D>(shapeElem.getString("texture")), shapeElem.getIntRect("imagerect"),
+            shapeElem.getIntVector2("hotspot"));
+        shapeElem = shapeElem.getNextElement("shape");
+    }
 }
 
-IntVector2 Cursor::getScreenPosition()
+void Cursor::defineShape(CursorShape shape, Texture* texture, const IntRect& imageRect, const IntVector2& hotSpot)
 {
-    IntVector2 pos = UIElement::getScreenPosition();
-    pos -= mHotspot;
-    return pos;
+    CursorShapeData& data = mShapeData[shape];
+    data.mTexture = texture;
+    data.mImageRect = imageRect;
+    data.mHotSpot = hotSpot;
+    
+    // Reset current shape if it was edited
+    if (shape == mShape)
+        setShape(mShape);
 }
 
-void Cursor::setHotspot(const IntVector2& hotspot)
+void Cursor::setShape(CursorShape shape)
 {
-    mHotspot = hotspot;
+    mShape = shape;
+    
+    CursorShapeData& data = mShapeData[mShape];
+    mTexture = data.mTexture;
+    mImageRect = data.mImageRect;
+    setSize(data.mImageRect.mRight - data.mImageRect.mLeft, data.mImageRect.mBottom - data.mImageRect.mTop);
 }
 
-void Cursor::setHotspot(int x, int y)
+void Cursor::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
 {
-    setHotspot(IntVector2(x, y));
+    unsigned initialSize = quads.size();
+    const IntVector2& offset = mShapeData[mShape].mHotSpot;
+    
+    BorderImage::getBatches(batches, quads, currentScissor);
+    for (unsigned i = initialSize; i < quads.size(); ++i)
+    {
+        quads[i].mLeft -= offset.mX;
+        quads[i].mTop -= offset.mY;
+        quads[i].mRight -= offset.mX;
+        quads[i].mBottom -= offset.mY;
+    }
 }

+ 31 - 10
Engine/UI/Cursor.h

@@ -26,6 +26,25 @@
 
 #include "BorderImage.h"
 
+enum CursorShape
+{
+    CS_NORMAL = 0,
+    CS_RESIZEVERTICAL,
+    CS_RESIZEDIAGONAL_TOPRIGHT,
+    CS_RESIZEHORIZONTAL,
+    CS_RESIZEDIAGONAL_TOPLEFT,
+    CS_ACCEPTDROP,
+    CS_REJECTDROP,
+    CS_MAX_SHAPES
+};
+
+struct CursorShapeData
+{
+    SharedPtr<Texture> mTexture;
+    IntRect mImageRect;
+    IntVector2 mHotSpot;
+};
+
 //! An image with hotspot coordinates
 class Cursor : public BorderImage
 {
@@ -39,20 +58,22 @@ public:
     
     //! Set UI element style from XML data
     virtual void setStyle(const XMLElement& element, ResourceCache* cache);
-    //! Return UI element screen position
-    virtual IntVector2 getScreenPosition();
+    //! Return UI rendering batches
+    virtual void getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor);
     
-    //! Set hotspot coordinates
-    void setHotspot(const IntVector2& hotspot);
-    //! Set hotspot coordinates
-    void setHotspot(int x, int y);
+    //! Define a shape
+    void defineShape(CursorShape shape, Texture* texture, const IntRect& imageRect, const IntVector2& hotSpot);
+    //! Set shape
+    void setShape(CursorShape shape);
     
-    //! Return hotspot coordinates
-    const IntVector2& getHotspot() const { return mHotspot; }
+    //! Get current shape
+    CursorShape getShape() const { return mShape; }
     
 protected:
-    //! Hotspot coordinates
-    IntVector2 mHotspot;
+    //! Current shape index
+    CursorShape mShape;
+    //! Shape datas
+    CursorShapeData mShapeData[CS_MAX_SHAPES];
 };
 
 #endif // UI_CURSOR_H

+ 72 - 72
Engine/UI/UIElement.cpp

@@ -218,78 +218,6 @@ void UIElement::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& q
     mHovering = false;
 }
 
-IntVector2 UIElement::getScreenPosition()
-{
-    if (mScreenPositionDirty)
-    {
-        IntVector2 pos = mPosition;
-        const UIElement* parent = mParent;
-        const UIElement* current = this;
-        
-        while (parent)
-        {
-            switch (current->mHorizontalAlignment)
-            {
-            case HA_LEFT:
-                pos.mX += parent->mPosition.mX;
-                break;
-                
-            case HA_CENTER:
-                pos.mX += parent->mPosition.mX + parent->mSize.mX / 2 - current->mSize.mX / 2;
-                break;
-                
-            case HA_RIGHT:
-                pos.mX += parent->mPosition.mX + parent->mSize.mX - current->mSize.mX;
-                break;
-            }
-            switch (current->mVerticalAlignment)
-            {
-            case VA_TOP:
-                pos.mY += parent->mPosition.mY;
-                break;
-                
-            case VA_CENTER:
-                pos.mY += parent->mPosition.mY + parent->mSize.mY / 2 - current->mSize.mY / 2;
-                break;
-                
-            case VA_BOTTOM:
-                pos.mY += parent->mPosition.mY + parent->mSize.mY - current->mSize.mY;
-                break;
-            }
-            
-            pos += parent->mChildOffset;
-            
-            current = parent;
-            parent = parent->mParent;
-        }
-        
-        mScreenPosition = pos;
-        mScreenPositionDirty = false;
-    }
-    
-    return mScreenPosition;
-}
-
-float UIElement::getDerivedOpacity()
-{
-    if (mDerivedOpacityDirty)
-    {
-        float opacity = mOpacity;
-        const UIElement* parent = mParent;
-        
-        while (parent)
-        {
-            opacity *= parent->mOpacity;
-            parent = parent->mParent;
-        }
-        
-        mDerivedOpacity = opacity;
-        mDerivedOpacityDirty = false;
-    }
-    
-    return mDerivedOpacity;
-}
-
 void UIElement::onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
     mHovering = true;
@@ -818,6 +746,78 @@ void UIElement::removeAllChildren()
     }
 }
 
+IntVector2 UIElement::getScreenPosition()
+{
+    if (mScreenPositionDirty)
+    {
+        IntVector2 pos = mPosition;
+        const UIElement* parent = mParent;
+        const UIElement* current = this;
+        
+        while (parent)
+        {
+            switch (current->mHorizontalAlignment)
+            {
+            case HA_LEFT:
+                pos.mX += parent->mPosition.mX;
+                break;
+                
+            case HA_CENTER:
+                pos.mX += parent->mPosition.mX + parent->mSize.mX / 2 - current->mSize.mX / 2;
+                break;
+                
+            case HA_RIGHT:
+                pos.mX += parent->mPosition.mX + parent->mSize.mX - current->mSize.mX;
+                break;
+            }
+            switch (current->mVerticalAlignment)
+            {
+            case VA_TOP:
+                pos.mY += parent->mPosition.mY;
+                break;
+                
+            case VA_CENTER:
+                pos.mY += parent->mPosition.mY + parent->mSize.mY / 2 - current->mSize.mY / 2;
+                break;
+                
+            case VA_BOTTOM:
+                pos.mY += parent->mPosition.mY + parent->mSize.mY - current->mSize.mY;
+                break;
+            }
+            
+            pos += parent->mChildOffset;
+            
+            current = parent;
+            parent = parent->mParent;
+        }
+        
+        mScreenPosition = pos;
+        mScreenPositionDirty = false;
+    }
+    
+    return mScreenPosition;
+}
+
+float UIElement::getDerivedOpacity()
+{
+    if (mDerivedOpacityDirty)
+    {
+        float opacity = mOpacity;
+        const UIElement* parent = mParent;
+        
+        while (parent)
+        {
+            opacity *= parent->mOpacity;
+            parent = parent->mParent;
+        }
+        
+        mDerivedOpacity = opacity;
+        mDerivedOpacityDirty = false;
+    }
+    
+    return mDerivedOpacity;
+}
+
 std::vector<UIElement*> UIElement::getChildren(bool recursive) const
 {
     if (!recursive)

+ 4 - 4
Engine/UI/UIElement.h

@@ -114,10 +114,6 @@ public:
     virtual void update(float timeStep);
     //! Return UI rendering batches
     virtual void getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor);
-    //! Return UI element screen position
-    virtual IntVector2 getScreenPosition();
-    //! Return UI element opacity
-    virtual float getDerivedOpacity();
     
     //! React to mouse hover
     virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
@@ -247,6 +243,8 @@ public:
     const std::string& getName() const { return mName; }
     //! Return position
     const IntVector2& getPosition() const { return mPosition; }
+    //! Return screen position
+    IntVector2 getScreenPosition();
     //! Return size
     const IntVector2& getSize() const { return mSize; }
     //! Return width
@@ -279,6 +277,8 @@ public:
     int getPriority() const { return mPriority; }
     //! Return opacity
     float getOpacity() const { return mOpacity; }
+    //! Return derived opacity (affected by parent elements)
+    float getDerivedOpacity();
     //! Return whether should be brought to front when focused
     bool getBringToFront() const { return mBringToFront; }
     //! Return whether should be put to background when another element is focused