Browse Source

Added hover background color & selection color to Text.
Added qualifier key support to Input & UI.

Lasse Öörni 15 years ago
parent
commit
ee5a087570

+ 1 - 1
Bin/Data/Scripts/GraphicsTest.as

@@ -363,7 +363,7 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
         float speedMultiplier = 1.0f;
         float speedMultiplier = 1.0f;
         if (input.getKeyDown(KEY_SHIFT))
         if (input.getKeyDown(KEY_SHIFT))
             speedMultiplier = 5.0f;
             speedMultiplier = 5.0f;
-        if (input.getKeyDown(KEY_CONTROL))
+        if (input.getKeyDown(KEY_CTRL))
             speedMultiplier = 0.1f;
             speedMultiplier = 0.1f;
 
 
         if (input.getKeyDown('W'))
         if (input.getKeyDown('W'))

+ 1 - 1
Bin/Data/Scripts/NinjaSnowWar.as

@@ -345,7 +345,7 @@ void updateControls()
             playerControls.set(CTRL_LEFT, true);
             playerControls.set(CTRL_LEFT, true);
         if (input.getKeyDown('D'))
         if (input.getKeyDown('D'))
             playerControls.set(CTRL_RIGHT, true);
             playerControls.set(CTRL_RIGHT, true);
-        if (input.getKeyDown(KEY_CONTROL))
+        if (input.getKeyDown(KEY_CTRL))
             playerControls.set(CTRL_FIRE, true);
             playerControls.set(CTRL_FIRE, true);
         if (input.getKeyDown(' '))
         if (input.getKeyDown(' '))
             playerControls.set(CTRL_JUMP, true);
             playerControls.set(CTRL_JUMP, true);

+ 1 - 1
Engine/Engine/Console.cpp

@@ -39,7 +39,7 @@
 
 
 Console::Console(Engine* engine) :
 Console::Console(Engine* engine) :
     mEngine(engine),
     mEngine(engine),
-    mFontSize(1)
+    mFontSize(DEFAULT_FONT_SIZE)
 {
 {
     LOGINFO("Console created");
     LOGINFO("Console created");
     
     

+ 8 - 3
Engine/Engine/RegisterInput.cpp

@@ -33,12 +33,14 @@ static void registerKeyCodes(asIScriptEngine* engine)
     engine->RegisterGlobalProperty("const int MOUSEB_LEFT", (void*)&MOUSEB_LEFT);
     engine->RegisterGlobalProperty("const int MOUSEB_LEFT", (void*)&MOUSEB_LEFT);
     engine->RegisterGlobalProperty("const int MOUSEB_RIGHT", (void*)&MOUSEB_RIGHT);
     engine->RegisterGlobalProperty("const int MOUSEB_RIGHT", (void*)&MOUSEB_RIGHT);
     engine->RegisterGlobalProperty("const int MOUSEB_MIDDLE", (void*)&MOUSEB_MIDDLE);
     engine->RegisterGlobalProperty("const int MOUSEB_MIDDLE", (void*)&MOUSEB_MIDDLE);
+    engine->RegisterGlobalProperty("const int QUAL_SHIFT", (void*)&QUAL_SHIFT);
+    engine->RegisterGlobalProperty("const int QUAL_CTRL", (void*)&QUAL_CTRL);
     engine->RegisterGlobalProperty("const int KEY_BACK", (void*)&KEY_BACK);
     engine->RegisterGlobalProperty("const int KEY_BACK", (void*)&KEY_BACK);
     engine->RegisterGlobalProperty("const int KEY_TAB", (void*)&KEY_TAB);
     engine->RegisterGlobalProperty("const int KEY_TAB", (void*)&KEY_TAB);
     engine->RegisterGlobalProperty("const int KEY_CLEAR", (void*)&KEY_CLEAR);
     engine->RegisterGlobalProperty("const int KEY_CLEAR", (void*)&KEY_CLEAR);
     engine->RegisterGlobalProperty("const int KEY_RETURN", (void*)&KEY_RETURN);
     engine->RegisterGlobalProperty("const int KEY_RETURN", (void*)&KEY_RETURN);
     engine->RegisterGlobalProperty("const int KEY_SHIFT", (void*)&KEY_SHIFT);
     engine->RegisterGlobalProperty("const int KEY_SHIFT", (void*)&KEY_SHIFT);
-    engine->RegisterGlobalProperty("const int KEY_CONTROL", (void*)&KEY_CONTROL);
+    engine->RegisterGlobalProperty("const int KEY_CTRL", (void*)&KEY_CTRL);
     engine->RegisterGlobalProperty("const int KEY_MENU", (void*)&KEY_MENU);
     engine->RegisterGlobalProperty("const int KEY_MENU", (void*)&KEY_MENU);
     engine->RegisterGlobalProperty("const int KEY_PAUSE", (void*)&KEY_PAUSE);
     engine->RegisterGlobalProperty("const int KEY_PAUSE", (void*)&KEY_PAUSE);
     engine->RegisterGlobalProperty("const int KEY_CAPITAL", (void*)&KEY_CAPITAL);
     engine->RegisterGlobalProperty("const int KEY_CAPITAL", (void*)&KEY_CAPITAL);
@@ -111,8 +113,8 @@ static void registerKeyCodes(asIScriptEngine* engine)
     engine->RegisterGlobalProperty("const int KEY_SCROLL", (void*)&KEY_SCROLL);
     engine->RegisterGlobalProperty("const int KEY_SCROLL", (void*)&KEY_SCROLL);
     engine->RegisterGlobalProperty("const int KEY_LSHIFT", (void*)&KEY_LSHIFT);
     engine->RegisterGlobalProperty("const int KEY_LSHIFT", (void*)&KEY_LSHIFT);
     engine->RegisterGlobalProperty("const int KEY_RSHIFT", (void*)&KEY_RSHIFT);
     engine->RegisterGlobalProperty("const int KEY_RSHIFT", (void*)&KEY_RSHIFT);
-    engine->RegisterGlobalProperty("const int KEY_LCONTROL", (void*)&KEY_LCONTROL);
-    engine->RegisterGlobalProperty("const int KEY_RCONTROL", (void*)&KEY_RCONTROL);
+    engine->RegisterGlobalProperty("const int KEY_LCTRL", (void*)&KEY_LCTRL);
+    engine->RegisterGlobalProperty("const int KEY_RCTRL", (void*)&KEY_RCTRL);
     engine->RegisterGlobalProperty("const int KEY_LMENU", (void*)&KEY_LMENU);
     engine->RegisterGlobalProperty("const int KEY_LMENU", (void*)&KEY_LMENU);
     engine->RegisterGlobalProperty("const int KEY_RMENU", (void*)&KEY_RMENU);
     engine->RegisterGlobalProperty("const int KEY_RMENU", (void*)&KEY_RMENU);
     engine->RegisterGlobalProperty("const int KEY_BROWSER_BACK", (void*)&KEY_BROWSER_BACK);
     engine->RegisterGlobalProperty("const int KEY_BROWSER_BACK", (void*)&KEY_BROWSER_BACK);
@@ -199,6 +201,9 @@ static void registerInput(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Input", "bool getKeyPress(int) const", asMETHOD(Input, getKeyPress), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getKeyPress(int) const", asMETHOD(Input, getKeyPress), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getMouseButtonDown(int) const", asMETHOD(Input, getMouseButtonDown), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getMouseButtonDown(int) const", asMETHOD(Input, getMouseButtonDown), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getMouseButtonPress(int) const", asMETHOD(Input, getMouseButtonPress), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getMouseButtonPress(int) const", asMETHOD(Input, getMouseButtonPress), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "bool getQualifierDown(int) const", asMETHOD(Input, getQualifierDown), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "bool getQualifierPress(int) const", asMETHOD(Input, getQualifierPress), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "int getQualifiers() const", asMETHOD(Input, getQualifiers), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveX() const", asMETHOD(Input, getMouseMoveX), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveX() const", asMETHOD(Input, getMouseMoveX), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveY() const", asMETHOD(Input, getMouseMoveY), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveY() const", asMETHOD(Input, getMouseMoveY), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveWheel() const", asMETHOD(Input, getMouseMoveWheel), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveWheel() const", asMETHOD(Input, getMouseMoveWheel), asCALL_THISCALL);

+ 9 - 2
Engine/Engine/RegisterUI.cpp

@@ -144,13 +144,20 @@ static void registerText(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Text", "void setMaxWidth(int)", asMETHOD(Text, setMaxWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "void setMaxWidth(int)", asMETHOD(Text, setMaxWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "void setText(const string& in)", asMETHOD(Text, setText), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "void setText(const string& in)", asMETHOD(Text, setText), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "void setTextAlignment(HorizontalAlignment)", asMETHOD(Text, setTextAlignment), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "void setTextAlignment(HorizontalAlignment)", asMETHOD(Text, setTextAlignment), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Text", "void setTextSpacing(float)", asMETHOD(Text, setTextSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "void setRowSpacing(float)", asMETHOD(Text, setRowSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "void setSelection(uint, uint)", asMETHOD(Text, setSelection), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "void setSelectionColor(const Color& in)", asMETHOD(Text, setSelectionColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "void setHoverColor(const Color& in)", asMETHOD(Text, setHoverColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "Font@+ getFont() const", asMETHOD(Text, getFont), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "Font@+ getFont() const", asMETHOD(Text, getFont), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "int getFontSize() const", asMETHOD(Text, getFontSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "int getFontSize() const", asMETHOD(Text, getFontSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "int getMaxWidth() const", asMETHOD(Text, getMaxWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "int getMaxWidth() const", asMETHOD(Text, getMaxWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "const string& getText() const", asMETHOD(Text, getText), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "const string& getText() const", asMETHOD(Text, getText), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "HorizontalAlignment getTextAlignment() const", asMETHOD(Text, getTextAlignment), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "HorizontalAlignment getTextAlignment() const", asMETHOD(Text, getTextAlignment), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Text", "float getTextSpacing() const", asMETHOD(Text, getTextSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "float getRowSpacing() const", asMETHOD(Text, getRowSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "uint getSelectionStart() const", asMETHOD(Text, getSelectionStart), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "uint getSelectionLength() const", asMETHOD(Text, getSelectionLength), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "const Color& getSelectionColor() const", asMETHOD(Text, getSelectionColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text", "const Color& getHoverColor() const", asMETHOD(Text, getHoverColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "uint getNumRows() const", asMETHOD(Text, getNumRows), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "uint getNumRows() const", asMETHOD(Text, getNumRows), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "int getRowHeight() const", asMETHOD(Text, getRowHeight), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text", "int getRowHeight() const", asMETHOD(Text, getRowHeight), asCALL_THISCALL);
     registerRefCasts<UIElement, Text>(engine, "UIElement", "Text");
     registerRefCasts<UIElement, Text>(engine, "UIElement", "Text");

+ 34 - 0
Engine/Input/Input.cpp

@@ -106,6 +106,7 @@ void Input::update()
         eventData[P_X] = mMouseMoveX;
         eventData[P_X] = mMouseMoveX;
         eventData[P_Y] = mMouseMoveY;
         eventData[P_Y] = mMouseMoveY;
         eventData[P_BUTTONS] = mMouseButtonDown;
         eventData[P_BUTTONS] = mMouseButtonDown;
+        eventData[P_QUALIFIERS] = getQualifiers();
         sendEvent(EVENT_MOUSEMOVE, eventData);
         sendEvent(EVENT_MOUSEMOVE, eventData);
     }
     }
     if (mMouseMoveWheel)
     if (mMouseMoveWheel)
@@ -115,6 +116,7 @@ void Input::update()
         VariantMap eventData;
         VariantMap eventData;
         eventData[P_WHEEL] = mMouseMoveWheel;
         eventData[P_WHEEL] = mMouseMoveWheel;
         eventData[P_BUTTONS] = mMouseButtonDown;
         eventData[P_BUTTONS] = mMouseButtonDown;
+        eventData[P_QUALIFIERS] = getQualifiers();
         sendEvent(EVENT_MOUSEWHEEL, eventData);
         sendEvent(EVENT_MOUSEWHEEL, eventData);
     }
     }
     
     
@@ -156,6 +158,34 @@ bool Input::getMouseButtonPress(int button) const
     return (mMouseButtonPress & button) != 0;
     return (mMouseButtonPress & button) != 0;
 }
 }
 
 
+bool Input::getQualifierDown(int qualifier) const
+{
+    if (qualifier == QUAL_SHIFT)
+        return mKeyDown[KEY_SHIFT] != 0;
+    if (qualifier == QUAL_CTRL)
+        return mKeyDown[KEY_CTRL] != 0;
+    return false;
+}
+
+bool Input::getQualifierPress(int qualifier) const
+{
+    if (qualifier == QUAL_SHIFT)
+        return mKeyPress[KEY_SHIFT] != 0;
+    if (qualifier == QUAL_CTRL)
+        return mKeyPress[KEY_CTRL] != 0;
+    return false;
+}
+
+int Input::getQualifiers() const
+{
+    int ret = 0;
+    if (mKeyDown[KEY_SHIFT] != 0)
+        ret |= QUAL_SHIFT;
+    if (mKeyDown[KEY_CTRL] != 0)
+        ret |= QUAL_CTRL;
+    return ret;
+}
+
 void Input::handleWindowMessage(StringHash eventType, VariantMap& eventData)
 void Input::handleWindowMessage(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace WindowMessage;
     using namespace WindowMessage;
@@ -262,6 +292,8 @@ void Input::handleWindowMessage(StringHash eventType, VariantMap& eventData)
             
             
             VariantMap keyEventData;
             VariantMap keyEventData;
             keyEventData[P_CHAR] = wParam;
             keyEventData[P_CHAR] = wParam;
+            eventData[P_BUTTONS] = mMouseButtonDown;
+            eventData[P_QUALIFIERS] = getQualifiers();
             sendEvent(EVENT_CHAR, keyEventData);
             sendEvent(EVENT_CHAR, keyEventData);
         }
         }
         mSuppressNextChar = false;
         mSuppressNextChar = false;
@@ -353,6 +385,7 @@ void Input::mouseButtonChange(int button, bool newState)
     VariantMap eventData;
     VariantMap eventData;
     eventData[P_BUTTON] = button;
     eventData[P_BUTTON] = button;
     eventData[P_BUTTONS] = mMouseButtonDown;
     eventData[P_BUTTONS] = mMouseButtonDown;
+    eventData[P_QUALIFIERS] = getQualifiers();
     sendEvent(newState ? EVENT_MOUSEBUTTONDOWN : EVENT_MOUSEBUTTONUP, eventData);
     sendEvent(newState ? EVENT_MOUSEBUTTONDOWN : EVENT_MOUSEBUTTONUP, eventData);
 }
 }
 
 
@@ -371,5 +404,6 @@ void Input::keyChange(int key, bool newState)
     VariantMap eventData;
     VariantMap eventData;
     eventData[P_KEY] = key;
     eventData[P_KEY] = key;
     eventData[P_BUTTONS] = mMouseButtonDown;
     eventData[P_BUTTONS] = mMouseButtonDown;
+    eventData[P_QUALIFIERS] = getQualifiers();
     sendEvent(newState ? EVENT_KEYDOWN : EVENT_KEYUP, eventData);
     sendEvent(newState ? EVENT_KEYDOWN : EVENT_KEYUP, eventData);
 }
 }

+ 6 - 0
Engine/Input/Input.h

@@ -56,6 +56,12 @@ public:
     bool getMouseButtonDown(int button) const;
     bool getMouseButtonDown(int button) const;
     //! Check if a mouse button has been pressed on this frame
     //! Check if a mouse button has been pressed on this frame
     bool getMouseButtonPress(int button) const;
     bool getMouseButtonPress(int button) const;
+    //! Check if a qualifier key is held down
+    bool getQualifierDown(int qualifier) const;
+    //! Check if a qualifier key has been pressed on this frame
+    bool getQualifierPress(int qualifier) const;
+    //! Return the currently held down qualifiers
+    int getQualifiers() const;
     //! Return horizontal mouse movement since last frame
     //! Return horizontal mouse movement since last frame
     int getMouseMoveX() const { return mMouseMoveX; }
     int getMouseMoveX() const { return mMouseMoveX; }
     //! Return vertical mouse movement since last frame
     //! Return vertical mouse movement since last frame

+ 14 - 3
Engine/Input/InputEvents.h

@@ -31,6 +31,7 @@ DEFINE_EVENT(EVENT_MOUSEBUTTONDOWN, MouseButtonDown)
 {
 {
     EVENT_PARAM(P_BUTTON, Button);              // int
     EVENT_PARAM(P_BUTTON, Button);              // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 }
 
 
 //! Mouse button released
 //! Mouse button released
@@ -38,6 +39,7 @@ DEFINE_EVENT(EVENT_MOUSEBUTTONUP, MouseButtonUp)
 {
 {
     EVENT_PARAM(P_BUTTON, Button);              // int
     EVENT_PARAM(P_BUTTON, Button);              // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 }
 
 
 //! Mouse moved
 //! Mouse moved
@@ -46,6 +48,7 @@ DEFINE_EVENT(EVENT_MOUSEMOVE, MouseMove)
     EVENT_PARAM(P_X, X);                        // int
     EVENT_PARAM(P_X, X);                        // int
     EVENT_PARAM(P_Y, Y);                        // int
     EVENT_PARAM(P_Y, Y);                        // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 }
 
 
 //! Mouse wheel moved
 //! Mouse wheel moved
@@ -53,6 +56,7 @@ DEFINE_EVENT(EVENT_MOUSEWHEEL, MouseWheel)
 {
 {
     EVENT_PARAM(P_WHEEL, Wheel);                // int
     EVENT_PARAM(P_WHEEL, Wheel);                // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 }
 
 
 //! Key pressed
 //! Key pressed
@@ -60,6 +64,7 @@ DEFINE_EVENT(EVENT_KEYDOWN, KeyDown)
 {
 {
     EVENT_PARAM(P_KEY, Key);                    // int
     EVENT_PARAM(P_KEY, Key);                    // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 }
 
 
 //! Key released
 //! Key released
@@ -67,24 +72,30 @@ DEFINE_EVENT(EVENT_KEYUP, KeyUp)
 {
 {
     EVENT_PARAM(P_KEY, Key);                    // int
     EVENT_PARAM(P_KEY, Key);                    // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
     EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 }
 
 
 //! Character typed on the keyboard
 //! Character typed on the keyboard
 DEFINE_EVENT(EVENT_CHAR, Char)
 DEFINE_EVENT(EVENT_CHAR, Char)
 {
 {
     EVENT_PARAM(P_CHAR, Char);                  // int
     EVENT_PARAM(P_CHAR, Char);                  // int
+    EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 }
 
 
 static const int MOUSEB_LEFT = 1;
 static const int MOUSEB_LEFT = 1;
 static const int MOUSEB_RIGHT = 2;
 static const int MOUSEB_RIGHT = 2;
 static const int MOUSEB_MIDDLE = 4;
 static const int MOUSEB_MIDDLE = 4;
 
 
+static const int QUAL_SHIFT = 1;
+static const int QUAL_CTRL = 2;
+
 static const int KEY_BACK = 0x08;
 static const int KEY_BACK = 0x08;
 static const int KEY_TAB = 0x09;
 static const int KEY_TAB = 0x09;
 static const int KEY_CLEAR = 0x0c; 
 static const int KEY_CLEAR = 0x0c; 
 static const int KEY_RETURN = 0x0d;
 static const int KEY_RETURN = 0x0d;
 static const int KEY_SHIFT = 0x10;
 static const int KEY_SHIFT = 0x10;
-static const int KEY_CONTROL = 0x11;
+static const int KEY_CTRL = 0x11;
 static const int KEY_MENU = 0x12;
 static const int KEY_MENU = 0x12;
 static const int KEY_PAUSE = 0x13;
 static const int KEY_PAUSE = 0x13;
 static const int KEY_CAPITAL = 0x14;
 static const int KEY_CAPITAL = 0x14;
@@ -157,8 +168,8 @@ static const int KEY_NUMLOCK = 0x90;
 static const int KEY_SCROLL = 0x91;
 static const int KEY_SCROLL = 0x91;
 static const int KEY_LSHIFT = 0xa0;
 static const int KEY_LSHIFT = 0xa0;
 static const int KEY_RSHIFT = 0xa1;
 static const int KEY_RSHIFT = 0xa1;
-static const int KEY_LCONTROL = 0xa2;
-static const int KEY_RCONTROL = 0xa3;
+static const int KEY_LCTRL = 0xa2;
+static const int KEY_RCTRL = 0xa3;
 static const int KEY_LMENU = 0xa4;
 static const int KEY_LMENU = 0xa4;
 static const int KEY_RMENU = 0xa5;
 static const int KEY_RMENU = 0xa5;
 static const int KEY_BROWSER_BACK = 0xa6;
 static const int KEY_BROWSER_BACK = 0xa6;

+ 2 - 2
Engine/UI/Button.cpp

@@ -70,13 +70,13 @@ void Button::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quad
     BorderImage::getBatches(batches, quads, currentScissor);
     BorderImage::getBatches(batches, quads, currentScissor);
 }
 }
 
 
-void Button::onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void Button::onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     setPressed((buttons & MOUSEB_LEFT) != 0);
     setPressed((buttons & MOUSEB_LEFT) != 0);
     mHovering = true;
     mHovering = true;
 }
 }
 
 
-void Button::onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void Button::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     if (buttons & MOUSEB_LEFT)
     if (buttons & MOUSEB_LEFT)
     {
     {

+ 2 - 2
Engine/UI/Button.h

@@ -44,9 +44,9 @@ public:
     //! Return UI rendering batches
     //! Return UI rendering batches
     virtual void getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor);
     virtual void getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor);
     //! React to mouse hover
     //! React to mouse hover
-    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse click
     //! React to mouse click
-    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     
     
     //! Set inactive image rectangle
     //! Set inactive image rectangle
     void setInactiveRect(const IntRect& rect);
     void setInactiveRect(const IntRect& rect);

+ 1 - 1
Engine/UI/CheckBox.cpp

@@ -61,7 +61,7 @@ void CheckBox::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& qu
     BorderImage::getBatches(batches, quads, currentScissor);
     BorderImage::getBatches(batches, quads, currentScissor);
 }
 }
 
 
-void CheckBox::onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void CheckBox::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     if (buttons & MOUSEB_LEFT)
     if (buttons & MOUSEB_LEFT)
     {
     {

+ 1 - 1
Engine/UI/CheckBox.h

@@ -42,7 +42,7 @@ public:
     //! Return UI rendering batches
     //! Return UI rendering batches
     virtual void getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor);
     virtual void getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor);
     //! React to mouse click
     //! React to mouse click
-    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     
     
     //! Set checked state
     //! Set checked state
     void setChecked(bool enable);
     void setChecked(bool enable);

+ 5 - 3
Engine/UI/LineEdit.cpp

@@ -72,6 +72,8 @@ void LineEdit::setStyle(const XMLElement& element, ResourceCache* cache)
     }
     }
     if (element.hasChildElement("maxlength"))
     if (element.hasChildElement("maxlength"))
         setMaxLength(element.getChildElement("maxlength").getInt("value"));
         setMaxLength(element.getChildElement("maxlength").getInt("value"));
+    if (element.hasChildElement("cursorposition"))
+        setCursorPosition(element.getChildElement("cursorposition").getInt("value"));
     
     
     XMLElement textElem = element.getChildElement("text");
     XMLElement textElem = element.getChildElement("text");
     if (textElem)
     if (textElem)
@@ -112,7 +114,7 @@ void LineEdit::update(float timeStep)
     }
     }
 }
 }
 
 
-void LineEdit::onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void LineEdit::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     if (buttons & MOUSEB_LEFT)
     if (buttons & MOUSEB_LEFT)
     {
     {
@@ -129,7 +131,7 @@ void LineEdit::onClick(const IntVector2& position, const IntVector2& screenPosit
     }
     }
 }
 }
 
 
-void LineEdit::onKey(int key)
+void LineEdit::onKey(int key, int buttons, int qualifiers)
 {
 {
     bool changed = false;
     bool changed = false;
     bool cursorMoved = false;
     bool cursorMoved = false;
@@ -180,7 +182,7 @@ void LineEdit::onKey(int key)
         updateCursor();
         updateCursor();
 }
 }
 
 
-void LineEdit::onChar(unsigned char c)
+void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
 {
 {
     bool changed = false;
     bool changed = false;
     
     

+ 3 - 3
Engine/UI/LineEdit.h

@@ -45,11 +45,11 @@ public:
     virtual void update(float timeStep);
     virtual void update(float timeStep);
     
     
     //! React to mouse click
     //! React to mouse click
-    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to a key press
     //! React to a key press
-    virtual void onKey(int key);
+    virtual void onKey(int key, int buttons, int qualifiers);
     //! React to a key press translated to a character
     //! React to a key press translated to a character
-    virtual void onChar(unsigned char c);
+    virtual void onChar(unsigned char c, int buttons, int qualifiers);
     
     
     //! Set text
     //! Set text
     void setText(const std::string& text);
     void setText(const std::string& text);

+ 2 - 2
Engine/UI/MenuItem.cpp

@@ -63,14 +63,14 @@ void MenuItem::update(float timeStep)
         setPressed(false);
         setPressed(false);
 }
 }
 
 
-void MenuItem::onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void MenuItem::onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     // Keep pressed state if showing the popup
     // Keep pressed state if showing the popup
     setPressed(((buttons & MOUSEB_LEFT) != 0) || (mShowPopup));
     setPressed(((buttons & MOUSEB_LEFT) != 0) || (mShowPopup));
     mHovering = true;
     mHovering = true;
 }
 }
 
 
-void MenuItem::onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void MenuItem::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     setPressed(true);
     setPressed(true);
     // Toggle popup visibility if exists
     // Toggle popup visibility if exists

+ 2 - 2
Engine/UI/MenuItem.h

@@ -41,9 +41,9 @@ class MenuItem : public Button
     //! Perform UI element update
     //! Perform UI element update
     virtual void update(float timeStep);
     virtual void update(float timeStep);
     //! React to mouse hover
     //! React to mouse hover
-    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse click
     //! React to mouse click
-    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     
     
     //! Set popup element to show on selection
     //! Set popup element to show on selection
     void setPopup(UIElement* element);
     void setPopup(UIElement* element);

+ 3 - 3
Engine/UI/Slider.cpp

@@ -78,20 +78,20 @@ void Slider::update(float timeStep)
     mSlider->setHovering(mHovering);
     mSlider->setHovering(mHovering);
 }
 }
 
 
-void Slider::onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void Slider::onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     // Show hover effect if inside the slider button
     // Show hover effect if inside the slider button
     mHovering = mSlider->isInside(screenPosition, true);
     mHovering = mSlider->isInside(screenPosition, true);
 }
 }
 
 
-void Slider::onDragStart(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void Slider::onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     mOriginalPosition = position;
     mOriginalPosition = position;
     mOriginalSliderPosition = mSlider->getPosition();
     mOriginalSliderPosition = mSlider->getPosition();
     mDragSlider = mSlider->isInside(screenPosition, true);
     mDragSlider = mSlider->isInside(screenPosition, true);
 }
 }
 
 
-void Slider::onDragMove(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void Slider::onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     if (!mDragSlider)
     if (!mDragSlider)
         return;
         return;

+ 3 - 3
Engine/UI/Slider.h

@@ -42,11 +42,11 @@ public:
     //! Perform UI element update
     //! Perform UI element update
     virtual void update(float timeStep);
     virtual void update(float timeStep);
     //! React to mouse hover
     //! React to mouse hover
-    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag start
     //! React to mouse drag start
-    virtual void onDragStart(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag motion
     //! React to mouse drag motion
-    virtual void onDragMove(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag end
     //! React to mouse drag end
     virtual void onDragEnd(const IntVector2& position, const IntVector2& screenPosition);
     virtual void onDragEnd(const IntVector2& position, const IntVector2& screenPosition);
     
     

+ 160 - 42
Engine/UI/Text.cpp

@@ -38,7 +38,11 @@ Text::Text(const std::string& name, const std::string& text) :
     mMaxWidth(0),
     mMaxWidth(0),
     mText(text),
     mText(text),
     mTextAlignment(HA_LEFT),
     mTextAlignment(HA_LEFT),
-    mTextSpacing(1.0f),
+    mRowSpacing(1.0f),
+    mSelectionStart(0),
+    mSelectionLength(0),
+    mSelectionColor(Color(0.0f, 0.0f, 0.0f, 0.0f)),
+    mHoverColor(Color(0.0f, 0.0f, 0.0f, 0.0f)),
     mRowHeight(0)
     mRowHeight(0)
 {
 {
 }
 }
@@ -64,8 +68,6 @@ void Text::setStyle(const XMLElement& element, ResourceCache* cache)
         replaceInPlace(text, "\\n", "\n");
         replaceInPlace(text, "\\n", "\n");
         setText(text);
         setText(text);
     }
     }
-    if (element.hasChildElement("textspacing"))
-        setTextSpacing(element.getChildElement("textspacing").getFloat("value"));
     if (element.hasChildElement("textalignment"))
     if (element.hasChildElement("textalignment"))
     {
     {
         std::string horiz = element.getChildElement("textalignment").getStringLower("value");
         std::string horiz = element.getChildElement("textalignment").getStringLower("value");
@@ -76,6 +78,17 @@ void Text::setStyle(const XMLElement& element, ResourceCache* cache)
         if (horiz == "right")
         if (horiz == "right")
             setTextAlignment(HA_RIGHT);
             setTextAlignment(HA_RIGHT);
     }
     }
+    if (element.hasChildElement("rowspacing"))
+        setRowSpacing(element.getChildElement("rowspacing").getFloat("value"));
+    if (element.hasChildElement("selection"))
+    {
+        XMLElement selectionElem = element.getChildElement("selection");
+        setSelection(selectionElem.getInt("start"), selectionElem.getInt("length"));
+    }
+    if (element.hasChildElement("selectioncolor"))
+        setSelectionColor(element.getChildElement("selectioncolor").getColor("value"));
+    if (element.hasChildElement("hovercolor"))
+        setHoverColor(element.getChildElement("hovercolor").getColor("value"));
 }
 }
 
 
 bool Text::setFont(Font* font, int size)
 bool Text::setFont(Font* font, int size)
@@ -86,80 +99,163 @@ bool Text::setFont(Font* font, int size)
         return false;
         return false;
     }
     }
     
     
-    mFont = font;
-    mFontSize = max(size, 1);
-    calculateTextSize();
+    if ((font != mFont) || (size != mFontSize))
+    {
+        mFont = font;
+        mFontSize = max(size, 1);
+        updateText();
+    }
+    
     return true;
     return true;
 }
 }
 
 
 void Text::setMaxWidth(int maxWidth)
 void Text::setMaxWidth(int maxWidth)
 {
 {
-    mMaxWidth = max(maxWidth, 0);
-    calculateTextSize();
+    if (maxWidth != mMaxWidth)
+    {
+        mMaxWidth = max(maxWidth, 0);
+        updateText();
+    }
 }
 }
 
 
 void Text::setText(const std::string& text)
 void Text::setText(const std::string& text)
 {
 {
     mText = text;
     mText = text;
-    calculateTextSize();
+    
+    validateSelection();
+    updateText();
 }
 }
 
 
 void Text::setTextAlignment(HorizontalAlignment align)
 void Text::setTextAlignment(HorizontalAlignment align)
 {
 {
-    mTextAlignment = align;
+    if (align != mTextAlignment)
+    {
+        mTextAlignment = align;
+        updateText();
+    }
+}
+
+void Text::setRowSpacing(float spacing)
+{
+    if (spacing != mRowSpacing)
+    {
+        mRowSpacing = max(spacing, 0.5f);
+        updateText();
+    }
+}
+
+void Text::setSelection(unsigned start, unsigned length)
+{
+    mSelectionStart = start;
+    mSelectionLength = length;
+    validateSelection();
 }
 }
 
 
-void Text::setTextSpacing(float spacing)
+void Text::setSelectionColor(const Color& color)
 {
 {
-    mTextSpacing = max(spacing, 0.5f);
+    mSelectionColor = color;
+}
+
+void Text::setHoverColor(const Color& color)
+{
+    mHoverColor = color;
 }
 }
 
 
 void Text::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
 void Text::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
 {
 {
-    if (!mFont)
+    // Hovering batch
+    if (((mHovering) || (mSelected)) && (mHoverColor.mA > 0.0f))
     {
     {
-        mHovering = false;
-        return;
+        UIBatch batch;
+        batch.begin(&quads);
+        batch.mBlendMode = BLEND_ALPHA;
+        batch.mScissor = currentScissor;
+        batch.mTexture = 0;
+        batch.addQuad(*this, 0, 0, getWidth(), getHeight(), 0, 0, 0, 0, mHoverColor);
+        UIBatch::addOrMerge(batch, batches);
     }
     }
     
     
-    const FontFace* face = mFont->getFace(mFontSize);
-    
-    UIBatch batch;
-    batch.begin(&quads);
-    batch.mBlendMode = BLEND_ALPHA;
-    batch.mScissor = currentScissor;
-    batch.mTexture = face->mTexture;
-    
-    unsigned rowIndex = 0;
-    int x = getRowStartPosition(rowIndex);
-    int y = 0;
-    
-    for (unsigned i = 0; i < mPrintText.length(); ++i)
+    // Selection batch
+    if ((mSelectionLength) && (mCharSizes.size() >= mSelectionStart + mSelectionLength) && (mSelectionColor.mA > 0.0f))
     {
     {
-        unsigned char c = (unsigned char)mPrintText[i];
+        UIBatch batch;
+        batch.begin(&quads);
+        batch.mBlendMode = BLEND_ALPHA;
+        batch.mScissor = currentScissor;
+        batch.mTexture = 0;
         
         
-        if (c != '\n')
+        IntVector2 currentStart = mCharPositions[mSelectionStart];
+        IntVector2 currentEnd = currentStart;
+        for (unsigned i = mSelectionStart; i < mSelectionStart + mSelectionLength; ++i)
         {
         {
-            const FontGlyph& glyph = face->mGlyphs[face->mGlyphIndex[c]];
-            if (c != ' ')
-                batch.addQuad(*this, x + glyph.mOffsetX, y + glyph.mOffsetY, glyph.mWidth, glyph.mHeight, glyph.mX, glyph.mY);
-            x += glyph.mAdvanceX;
+            // Check if row changes, and start a new quad in that case
+            if ((mCharSizes[i].mX) && (mCharSizes[i].mY))
+            {
+                if (mCharPositions[i].mY != currentStart.mY)
+                {
+                    batch.addQuad(*this, currentStart.mX, currentStart.mY, currentEnd.mX - currentStart.mX, currentEnd.mY - currentStart.mY,
+                        0, 0, 0, 0, mSelectionColor);
+                    currentStart = mCharPositions[i];
+                    currentEnd = currentStart + mCharSizes[i];
+                }
+                else
+                {
+                    currentEnd.mX += mCharSizes[i].mX;
+                    currentEnd.mY = max(currentStart.mY + mCharSizes[i].mY, currentEnd.mY);
+                }
+            }
         }
         }
-        else
+        if (currentEnd != currentStart)
         {
         {
-            rowIndex++;
-            x = getRowStartPosition(rowIndex);
-            y += mRowHeight;
+            batch.addQuad(*this, currentStart.mX, currentStart.mY, currentEnd.mX - currentStart.mX, currentEnd.mY - currentStart.mY,
+                0, 0, 0, 0, mSelectionColor);
         }
         }
+        
+        UIBatch::addOrMerge(batch, batches);
     }
     }
     
     
-    UIBatch::addOrMerge(batch, batches);
+    // Text batch
+    if (mFont)
+    {
+        const FontFace* face = mFont->getFace(mFontSize);
+        
+        UIBatch batch;
+        batch.begin(&quads);
+        batch.mBlendMode = BLEND_ALPHA;
+        batch.mScissor = currentScissor;
+        batch.mTexture = face->mTexture;
+        
+        unsigned rowIndex = 0;
+        int x = getRowStartPosition(rowIndex);
+        int y = 0;
+        
+        for (unsigned i = 0; i < mPrintText.length(); ++i)
+        {
+            unsigned char c = (unsigned char)mPrintText[i];
+            
+            if (c != '\n')
+            {
+                const FontGlyph& glyph = face->mGlyphs[face->mGlyphIndex[c]];
+                if (c != ' ')
+                    batch.addQuad(*this, x + glyph.mOffsetX, y + glyph.mOffsetY, glyph.mWidth, glyph.mHeight, glyph.mX, glyph.mY);
+                x += glyph.mAdvanceX;
+            }
+            else
+            {
+                rowIndex++;
+                x = getRowStartPosition(rowIndex);
+                y += mRowHeight;
+            }
+        }
+        
+        UIBatch::addOrMerge(batch, batches);
+    }
     
     
     // Reset hovering for next frame
     // Reset hovering for next frame
     mHovering = false;
     mHovering = false;
 }
 }
 
 
-void Text::calculateTextSize()
+void Text::updateText()
 {
 {
     int width = 0;
     int width = 0;
     int height = 0;
     int height = 0;
@@ -175,7 +271,7 @@ void Text::calculateTextSize()
         const FontFace* face = mFont->getFace(mFontSize);
         const FontFace* face = mFont->getFace(mFontSize);
         mRowHeight = face->mRowHeight;
         mRowHeight = face->mRowHeight;
         int rowWidth = 0;
         int rowWidth = 0;
-        int rowHeight = (int)(mTextSpacing * mRowHeight);
+        int rowHeight = (int)(mRowSpacing * mRowHeight);
         
         
         // First see if the text must be split up
         // First see if the text must be split up
         if (!mMaxWidth)
         if (!mMaxWidth)
@@ -291,8 +387,10 @@ void Text::calculateTextSize()
         if (!height)
         if (!height)
             height = rowHeight;
             height = rowHeight;
         
         
-        // Store position of each character
+        // Store position & size of each character
         mCharPositions.resize(mText.length() + 1);
         mCharPositions.resize(mText.length() + 1);
+        mCharSizes.resize(mText.length());
+        
         unsigned rowIndex = 0;
         unsigned rowIndex = 0;
         int x = getRowStartPosition(rowIndex);
         int x = getRowStartPosition(rowIndex);
         int y = 0;
         int y = 0;
@@ -303,10 +401,12 @@ void Text::calculateTextSize()
             if (c != '\n')
             if (c != '\n')
             {
             {
                 const FontGlyph& glyph = face->mGlyphs[face->mGlyphIndex[c]];
                 const FontGlyph& glyph = face->mGlyphs[face->mGlyphIndex[c]];
+                mCharSizes[printToText[i]] = IntVector2(glyph.mAdvanceX, mRowHeight);
                 x += glyph.mAdvanceX;
                 x += glyph.mAdvanceX;
             }
             }
             else
             else
             {
             {
+                mCharSizes[printToText[i]] = IntVector2::sZero;
                 rowIndex++;
                 rowIndex++;
                 x = getRowStartPosition(rowIndex);
                 x = getRowStartPosition(rowIndex);
                 y += rowHeight;
                 y += rowHeight;
@@ -322,6 +422,24 @@ void Text::calculateTextSize()
     setSize(width, height);
     setSize(width, height);
 }
 }
 
 
+void Text::validateSelection()
+{
+    unsigned textLength = mText.length();
+    
+    if (textLength)
+    {
+        if (mSelectionStart >= textLength)
+            mSelectionStart = textLength - 1;
+        if (mSelectionStart + mSelectionLength > textLength)
+            mSelectionLength = textLength - mSelectionStart;
+    }
+    else
+    {
+        mSelectionStart = 0;
+        mSelectionLength = 0;
+    }
+}
+
 int Text::getRowStartPosition(unsigned rowIndex) const
 int Text::getRowStartPosition(unsigned rowIndex) const
 {
 {
     int rowWidth = 0;
     int rowWidth = 0;

+ 33 - 5
Engine/UI/Text.h

@@ -55,7 +55,13 @@ public:
     //! Set row alignment
     //! Set row alignment
     void setTextAlignment(HorizontalAlignment align);
     void setTextAlignment(HorizontalAlignment align);
     //! Set row spacing, 1.0 for original font spacing
     //! Set row spacing, 1.0 for original font spacing
-    void setTextSpacing(float spacing);
+    void setRowSpacing(float spacing);
+    //! Set selection
+    void setSelection(unsigned start, unsigned length);
+    //! Set selection background color. Color with 0 alpha (default) disables
+    void setSelectionColor(const Color& color);
+    //! Set hover background color. Color with 0 alpha (default) disables
+    void setHoverColor(const Color& color);
     
     
     //! Return font
     //! Return font
     Font* getFont() const { return mFont; }
     Font* getFont() const { return mFont; }
@@ -68,7 +74,15 @@ public:
     //! Return row alignment
     //! Return row alignment
     HorizontalAlignment getTextAlignment() const { return mTextAlignment; }
     HorizontalAlignment getTextAlignment() const { return mTextAlignment; }
     //! Return row spacing
     //! Return row spacing
-    float getTextSpacing() const { return mTextSpacing; }
+    float getRowSpacing() const { return mRowSpacing; }
+    //! Return selection start
+    unsigned getSelectionStart() const { return mSelectionStart; }
+    //! Return selection length
+    unsigned getSelectionLength() const { return mSelectionLength; }
+    //! Return selection background color
+    const Color& getSelectionColor() const { return mSelectionColor; }
+    //! Return hover background color
+    const Color& getHoverColor() const { return mHoverColor; }
     //! Return row height
     //! Return row height
     int getRowHeight() const { return mRowHeight; }
     int getRowHeight() const { return mRowHeight; }
     //! Return number of rows
     //! Return number of rows
@@ -77,10 +91,14 @@ public:
     const std::vector<int>& getRowWidths() const { return mRowWidths; }
     const std::vector<int>& getRowWidths() const { return mRowWidths; }
     //! Return position of each character
     //! Return position of each character
     const std::vector<IntVector2>& getCharPositions() const { return mCharPositions; }
     const std::vector<IntVector2>& getCharPositions() const { return mCharPositions; }
+    //! Return size of each character
+    const std::vector<IntVector2>& getCharSizes() const { return mCharSizes; }
     
     
 protected:
 protected:
-    //! Calculate text size
-    void calculateTextSize();
+    //! Update text when text, font or spacing changed
+    void updateText();
+    //! Validate text selection to be within the text
+    void validateSelection();
     //! Return row start X position
     //! Return row start X position
     int getRowStartPosition(unsigned rowIndex) const;
     int getRowStartPosition(unsigned rowIndex) const;
     
     
@@ -97,13 +115,23 @@ protected:
     //! Row alignment
     //! Row alignment
     HorizontalAlignment mTextAlignment;
     HorizontalAlignment mTextAlignment;
     //! Row spacing
     //! Row spacing
-    float mTextSpacing;
+    float mRowSpacing;
+    //! Selection start
+    unsigned mSelectionStart;
+    //! Selection length
+    unsigned mSelectionLength;
+    //! Selection background color
+    Color mSelectionColor;
+    //! Hover background color
+    Color mHoverColor;
     //! Row height
     //! Row height
     int mRowHeight;
     int mRowHeight;
     //! Row widths
     //! Row widths
     std::vector<int> mRowWidths;
     std::vector<int> mRowWidths;
     //! Positions of each character
     //! Positions of each character
     std::vector<IntVector2> mCharPositions;
     std::vector<IntVector2> mCharPositions;
+    //! Sizes of each character
+    std::vector<IntVector2> mCharSizes;
 };
 };
 
 
 #endif // UI_STATICTEXT_H
 #endif // UI_STATICTEXT_H

+ 17 - 7
Engine/UI/UI.cpp

@@ -54,7 +54,8 @@ UI::UI(Renderer* renderer, ResourceCache* cache) :
     mCache(cache),
     mCache(cache),
     mMouseDrag(false),
     mMouseDrag(false),
     mMouseDragElement(0),
     mMouseDragElement(0),
-    mMouseButtons(0)
+    mMouseButtons(0),
+    mQualifiers(0)
 {
 {
     if (!mRenderer)
     if (!mRenderer)
         EXCEPTION("Null renderer for UI");
         EXCEPTION("Null renderer for UI");
@@ -159,7 +160,7 @@ void UI::update(float timeStep)
         {
         {
             // If a drag is going on, transmit hover only to the element being dragged
             // If a drag is going on, transmit hover only to the element being dragged
             if ((!mMouseDragElement) || (mMouseDragElement == element))
             if ((!mMouseDragElement) || (mMouseDragElement == element))
-                element->onHover(element->screenToElement(pos), pos, mMouseButtons);
+                element->onHover(element->screenToElement(pos), pos, mMouseButtons, mQualifiers);
         }
         }
     }
     }
     
     
@@ -404,6 +405,7 @@ void UI::handleMouseMove(StringHash eventType, VariantMap& eventData)
     using namespace MouseMove;
     using namespace MouseMove;
     
     
     mMouseButtons = eventData[P_BUTTONS].getInt();
     mMouseButtons = eventData[P_BUTTONS].getInt();
+    mQualifiers = eventData[P_QUALIFIERS].getInt();
     
     
     if ((mCursor) && (mCursor->isVisible()))
     if ((mCursor) && (mCursor->isVisible()))
     {
     {
@@ -419,7 +421,7 @@ void UI::handleMouseMove(StringHash eventType, VariantMap& eventData)
         {
         {
             UIElement* element = verifyElement(mMouseDragElement);
             UIElement* element = verifyElement(mMouseDragElement);
             if ((element) && (element->isEnabled()) && (element->isVisible()))
             if ((element) && (element->isEnabled()) && (element->isVisible()))
-                element->onDragMove(element->screenToElement(pos), pos, mMouseButtons);
+                element->onDragMove(element->screenToElement(pos), pos, mMouseButtons, mQualifiers);
             else
             else
             {
             {
                 mMouseDrag = false;
                 mMouseDrag = false;
@@ -434,6 +436,7 @@ void UI::handleMouseButtonDown(StringHash eventType, VariantMap& eventData)
     using namespace MouseButtonDown;
     using namespace MouseButtonDown;
     
     
     mMouseButtons = eventData[P_BUTTONS].getInt();
     mMouseButtons = eventData[P_BUTTONS].getInt();
+    mQualifiers = eventData[P_QUALIFIERS].getInt();
     int button = eventData[P_BUTTON].getInt();
     int button = eventData[P_BUTTON].getInt();
     
     
     if ((mCursor) && (mCursor->isVisible()))
     if ((mCursor) && (mCursor->isVisible()))
@@ -450,14 +453,14 @@ void UI::handleMouseButtonDown(StringHash eventType, VariantMap& eventData)
             }
             }
             
             
             // Handle click
             // Handle click
-            element->onClick(element->screenToElement(pos), pos, mMouseButtons);
+            element->onClick(element->screenToElement(pos), pos, mMouseButtons, mQualifiers);
             
             
             // Handle start of drag
             // Handle start of drag
             if (!mMouseDrag)
             if (!mMouseDrag)
             {
             {
                 mMouseDrag = true;
                 mMouseDrag = true;
                 mMouseDragElement = element;
                 mMouseDragElement = element;
-                element->onDragStart(element->screenToElement(pos), pos, mMouseButtons);
+                element->onDragStart(element->screenToElement(pos), pos, mMouseButtons, mQualifiers);
             }
             }
         }
         }
         else
         else
@@ -473,6 +476,7 @@ void UI::handleMouseButtonUp(StringHash eventType, VariantMap& eventData)
     using namespace MouseButtonUp;
     using namespace MouseButtonUp;
     
     
     mMouseButtons = eventData[P_BUTTONS].getInt();
     mMouseButtons = eventData[P_BUTTONS].getInt();
+    mQualifiers = eventData[P_QUALIFIERS].getInt();
     
     
     if ((mCursor) && (mCursor->isVisible()))
     if ((mCursor) && (mCursor->isVisible()))
     {
     {
@@ -494,18 +498,24 @@ void UI::handleKeyDown(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace KeyDown;
     using namespace KeyDown;
     
     
+    mMouseButtons = eventData[P_BUTTONS].getInt();
+    mQualifiers = eventData[P_QUALIFIERS].getInt();
+    
     UIElement* element = getFocusElement();
     UIElement* element = getFocusElement();
     if (element)
     if (element)
-        element->onKey(eventData[P_KEY].getInt());
+        element->onKey(eventData[P_KEY].getInt(), mMouseButtons, mQualifiers);
 }
 }
 
 
 void UI::handleChar(StringHash eventType, VariantMap& eventData)
 void UI::handleChar(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace Char;
     using namespace Char;
     
     
+    mMouseButtons = eventData[P_BUTTONS].getInt();
+    mQualifiers = eventData[P_QUALIFIERS].getInt();
+    
     UIElement* element = getFocusElement();
     UIElement* element = getFocusElement();
     if (element)
     if (element)
-        element->onChar(eventData[P_CHAR].getInt());
+        element->onChar(eventData[P_CHAR].getInt(), mMouseButtons, mQualifiers);
 }
 }
 
 
 void UI::loadLayout(UIElement* current, const XMLElement& elem, XMLFile* styleFile)
 void UI::loadLayout(UIElement* current, const XMLElement& elem, XMLFile* styleFile)

+ 3 - 1
Engine/UI/UI.h

@@ -133,7 +133,9 @@ private:
     //! Mouse drag UI element
     //! Mouse drag UI element
     UIElement* mMouseDragElement;
     UIElement* mMouseDragElement;
     //! Mouse buttons held down
     //! Mouse buttons held down
-    unsigned mMouseButtons;
+    int mMouseButtons;
+    //! Qualifier keys held down
+    int mQualifiers;
 };
 };
 
 
 #endif // UI_UI_H
 #endif // UI_UI_H

+ 37 - 1
Engine/UI/UIBatch.cpp

@@ -82,7 +82,8 @@ void UIBatch::addQuad(UIElement& element, int x, int y, int width, int height, i
     mQuadCount++;
     mQuadCount++;
 }
 }
 
 
-void UIBatch::addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth, int texHeight)
+void UIBatch::addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth,
+    int texHeight)
 {
 {
     if (!mQuads)
     if (!mQuads)
         return;
         return;
@@ -124,6 +125,38 @@ void UIBatch::addQuad(UIElement& element, int x, int y, int width, int height, i
     mQuadCount++;
     mQuadCount++;
 }
 }
 
 
+void UIBatch::addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth,
+    int texHeight, const Color& color)
+{
+    if (!mQuads)
+        return;
+    
+    static UIQuad quad;
+    const IntVector2& screenPos = element.getScreenPosition();
+    
+    quad.mLeft = x + screenPos.mX;
+    quad.mTop = y + screenPos.mY;
+    quad.mRight = quad.mLeft + width;
+    quad.mBottom = quad.mTop + height;
+    quad.mLeftUV = texOffsetX;
+    quad.mTopUV = texOffsetY;
+    quad.mRightUV = texOffsetX + texWidth;
+    quad.mBottomUV = texOffsetY + texHeight;
+    
+    Color derivedColor(color.mR, color.mG, color.mB, color.mA * element.getDerivedOpacity());
+    // If alpha is 0, nothing will be rendered, so exit without adding the quad
+    if (derivedColor.mA <= 0.0f)
+        return;
+    unsigned uintColor = getD3DColor(derivedColor);
+    quad.mTopLeftColor = uintColor;
+    quad.mTopRightColor = uintColor;
+    quad.mBottomLeftColor = uintColor;
+    quad.mBottomRightColor = uintColor;
+    
+    mQuads->push_back(quad);
+    mQuadCount++;
+}
+
 bool UIBatch::merge(const UIBatch& batch)
 bool UIBatch::merge(const UIBatch& batch)
 {
 {
     if ((batch.mBlendMode != mBlendMode) ||
     if ((batch.mBlendMode != mBlendMode) ||
@@ -241,6 +274,9 @@ void UIBatch::draw(Renderer* renderer, VertexShader* vs, PixelShader* ps) const
 
 
 void UIBatch::addOrMerge(const UIBatch& batch, std::vector<UIBatch>& batches)
 void UIBatch::addOrMerge(const UIBatch& batch, std::vector<UIBatch>& batches)
 {
 {
+    if (!batch.mQuadCount)
+        return;
+    
     if (!batches.size())
     if (!batches.size())
         batches.push_back(batch);
         batches.push_back(batch);
     else
     else

+ 3 - 0
Engine/UI/UIBatch.h

@@ -84,6 +84,9 @@ public:
     void addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY);
     void addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY);
     //! Add a quad with scaled texture
     //! Add a quad with scaled texture
     void addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth, int texHeight);
     void addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth, int texHeight);
+    //! Add a quad with custom color
+    void addQuad(UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth, int texHeight,
+        const Color& color);
     //! Merge with another batch
     //! Merge with another batch
     bool merge(const UIBatch& batch);
     bool merge(const UIBatch& batch);
     //! Draw
     //! Draw

+ 10 - 8
Engine/UI/UIElement.cpp

@@ -139,6 +139,8 @@ void UIElement::setStyle(const XMLElement& element, ResourceCache* cache)
         setEnabled(element.getChildElement("enabled").getBool("enable"));
         setEnabled(element.getChildElement("enabled").getBool("enable"));
     if (element.hasChildElement("focusable"))
     if (element.hasChildElement("focusable"))
         setFocusable(element.getChildElement("focusable").getBool("enable"));
         setFocusable(element.getChildElement("focusable").getBool("enable"));
+    if (element.hasChildElement("selected"))
+        setSelected(element.getChildElement("selected").getBool("enable"));
     if (element.hasChildElement("visible"))
     if (element.hasChildElement("visible"))
         setVisible(element.getChildElement("visible").getBool("enable"));
         setVisible(element.getChildElement("visible").getBool("enable"));
 }
 }
@@ -225,32 +227,32 @@ float UIElement::getDerivedOpacity()
     return mDerivedOpacity;
     return mDerivedOpacity;
 }
 }
 
 
-void UIElement::onKey(int key)
+void UIElement::onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
+    mHovering = true;
 }
 }
 
 
-void UIElement::onChar(unsigned char c)
+void UIElement::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
 }
 }
 
 
-void UIElement::onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void UIElement::onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
-    mHovering = true;
 }
 }
 
 
-void UIElement::onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void UIElement::onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
 }
 }
 
 
-void UIElement::onDragStart(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void UIElement::onDragEnd(const IntVector2& position, const IntVector2& screenPosition)
 {
 {
 }
 }
 
 
-void UIElement::onDragEnd(const IntVector2& position, const IntVector2& screenPosition)
+void UIElement::onKey(int key, int buttons, int qualifiers)
 {
 {
 }
 }
 
 
-void UIElement::onDragMove(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void UIElement::onChar(unsigned char c, int buttons, int qualifiers)
 {
 {
 }
 }
 
 

+ 6 - 6
Engine/UI/UIElement.h

@@ -89,19 +89,19 @@ public:
     virtual float getDerivedOpacity();
     virtual float getDerivedOpacity();
     
     
     //! React to mouse hover
     //! React to mouse hover
-    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse click
     //! React to mouse click
-    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag start
     //! React to mouse drag start
-    virtual void onDragStart(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag motion
     //! React to mouse drag motion
-    virtual void onDragMove(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag end
     //! React to mouse drag end
     virtual void onDragEnd(const IntVector2& position, const IntVector2& screenPosition);
     virtual void onDragEnd(const IntVector2& position, const IntVector2& screenPosition);
     //! React to a key press
     //! React to a key press
-    virtual void onKey(int key);
+    virtual void onKey(int key, int buttons, int qualifiers);
     //! React to a key press translated to a character
     //! React to a key press translated to a character
-    virtual void onChar(unsigned char c);
+    virtual void onChar(unsigned char c, int buttons, int qualifiers);
     
     
     //! Set name
     //! Set name
     void setName(const std::string& name);
     void setName(const std::string& name);

+ 2 - 2
Engine/UI/Window.cpp

@@ -64,7 +64,7 @@ void Window::setStyle(const XMLElement& element, ResourceCache* cache)
         setResizable(element.getChildElement("resizable").getBool("enable"));
         setResizable(element.getChildElement("resizable").getBool("enable"));
 }
 }
 
 
-void Window::onDragStart(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void Window::onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     if ((buttons != MOUSEB_LEFT) || (!checkAlignment()))
     if ((buttons != MOUSEB_LEFT) || (!checkAlignment()))
     {
     {
@@ -120,7 +120,7 @@ void Window::onDragStart(const IntVector2& position, const IntVector2& screenPos
     }
     }
 }
 }
 
 
-void Window::onDragMove(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons)
+void Window::onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     if (mDragMode == DRAG_NONE)
     if (mDragMode == DRAG_NONE)
         return;
         return;

+ 2 - 2
Engine/UI/Window.h

@@ -55,9 +55,9 @@ public:
     //! Set UI element style from XML data
     //! Set UI element style from XML data
     virtual void setStyle(const XMLElement& element, ResourceCache* cache);
     virtual void setStyle(const XMLElement& element, ResourceCache* cache);
     //! React to mouse drag start
     //! React to mouse drag start
-    virtual void onDragStart(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag motion
     //! React to mouse drag motion
-    virtual void onDragMove(const IntVector2& position, const IntVector2& screenPosition, unsigned buttons);
+    virtual void onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse drag end
     //! React to mouse drag end
     virtual void onDragEnd(const IntVector2& position, const IntVector2& screenPosition);
     virtual void onDragEnd(const IntVector2& position, const IntVector2& screenPosition);
     
     

+ 1 - 1
Examples/NinjaSnowWar/Game.cpp

@@ -828,7 +828,7 @@ void Game::getControls()
             if (input->getKeyDown('S')) mControls.set(CTRL_DOWN);
             if (input->getKeyDown('S')) mControls.set(CTRL_DOWN);
             if (input->getKeyDown('A')) mControls.set(CTRL_LEFT);
             if (input->getKeyDown('A')) mControls.set(CTRL_LEFT);
             if (input->getKeyDown('D')) mControls.set(CTRL_RIGHT);
             if (input->getKeyDown('D')) mControls.set(CTRL_RIGHT);
-            if (input->getKeyDown(KEY_CONTROL)) mControls.set(CTRL_FIRE);
+            if (input->getKeyDown(KEY_CTRL)) mControls.set(CTRL_FIRE);
             if (input->getKeyDown(' ')) mControls.set(CTRL_JUMP);
             if (input->getKeyDown(' ')) mControls.set(CTRL_JUMP);
             if (input->getMouseButtonDown(MOUSEB_LEFT)) mControls.set(CTRL_FIRE);
             if (input->getMouseButtonDown(MOUSEB_LEFT)) mControls.set(CTRL_FIRE);
             if (input->getMouseButtonDown(MOUSEB_RIGHT)) mControls.set(CTRL_JUMP);
             if (input->getMouseButtonDown(MOUSEB_RIGHT)) mControls.set(CTRL_JUMP);