Pārlūkot izejas kodu

LineEdit copy-paste & selection by shift-cursor.

Lasse Öörni 15 gadi atpakaļ
vecāks
revīzija
6d22d95d97

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

@@ -75,6 +75,5 @@
     </element>
     <element type="Text">
         <font name="Cour.ttf" size="10" />
-        <selectioncolor value="1.0 0.5 0.5" />
     </element>
 </elements>

+ 6 - 0
Engine/Engine/RegisterUI.cpp

@@ -172,12 +172,18 @@ static void registerLineEdit(asIScriptEngine* engine)
     engine->RegisterObjectMethod("LineEdit", "void setMaxLength(uint)", asMETHOD(LineEdit, setMaxLength), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "void setEchoCharacter(uint8)", asMETHOD(LineEdit, setEchoCharacter), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "void setDefocusable(bool)", asMETHOD(LineEdit, setDefocusable), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "void setCursorMovable(bool)", asMETHOD(LineEdit, setCursorMovable), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "void setTextSelectable(bool)", asMETHOD(LineEdit, setTextSelectable), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "void setTextCopyable(bool)", asMETHOD(LineEdit, setTextCopyable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "const string& getText() const", asMETHOD(LineEdit, getText), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "uint getCursorPosition() const", asMETHOD(LineEdit, getCursorPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "float getCursorBlinkRate() const", asMETHOD(LineEdit, getCursorBlinkRate), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "uint getMaxLength() const", asMETHOD(LineEdit, getMaxLength), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "uint8 getEchoCharacter() const", asMETHOD(LineEdit, getEchoCharacter), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "bool isDefocusable() const", asMETHOD(LineEdit, isDefocusable), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "bool isCursorMovable() const", asMETHOD(LineEdit, isCursorMovable), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "bool isTextSelectable() const", asMETHOD(LineEdit, isTextSelectable), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "bool isTextCopyable() const", asMETHOD(LineEdit, isTextCopyable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "Text@+ getTextElement() const", asMETHOD(LineEdit, getTextElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "BorderImage@+ getCursorElement() const", asMETHOD(LineEdit, getCursorElement), asCALL_THISCALL);
     registerRefCasts<UIElement, LineEdit>(engine, "UIElement", "LineEdit");

+ 128 - 20
Engine/UI/LineEdit.cpp

@@ -24,6 +24,7 @@
 #include "Precompiled.h"
 #include "Input.h"
 #include "LineEdit.h"
+#include "Log.h"
 #include "Text.h"
 #include "UIEvents.h"
 
@@ -34,12 +35,15 @@ LineEdit::LineEdit(const std::string& name, const std::string& text) :
     mLastFont(0),
     mLastFontSize(0),
     mCursorPosition(0),
-    mDragStartPosition(0),
+    mDragStartPosition(M_MAX_UNSIGNED),
     mCursorBlinkRate(1.0f),
     mCursorBlinkTimer(0.0f),
     mMaxLength(0),
     mEchoCharacter(0),
     mDefocusable(true),
+    mCursorMovable(true),
+    mTextSelectable(true),
+    mTextCopyable(true),
     mDefocus(false)
 {
     mClipChildren = true;
@@ -65,6 +69,14 @@ void LineEdit::setStyle(const XMLElement& element, ResourceCache* cache)
     
     if (element.hasChildElement("maxlength"))
         setMaxLength(element.getChildElement("maxlength").getInt("value"));
+    if (element.hasChildElement("defocusable"))
+        setDefocusable(element.getChildElement("defocusable").getBool("enable"));
+    if (element.hasChildElement("cursormovable"))
+        setCursorMovable(element.getChildElement("cursormovable").getBool("enable"));
+    if (element.hasChildElement("textselectable"))
+        setTextSelectable(element.getChildElement("textselectable").getBool("enable"));
+    if (element.hasChildElement("textcopyable"))
+        setTextCopyable(element.getChildElement("textcopyable").getBool("enable"));
     
     XMLElement textElem = element.getChildElement("text");
     if (textElem)
@@ -119,7 +131,7 @@ void LineEdit::update(float timeStep)
 
 void LineEdit::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
-    if (buttons & MOUSEB_LEFT)
+    if ((buttons & MOUSEB_LEFT) && (mCursorMovable))
     {
         unsigned pos = getCharIndex(position);
         if (pos != M_MAX_UNSIGNED)
@@ -137,20 +149,18 @@ void LineEdit::onDragStart(const IntVector2& position, const IntVector2& screenP
 
 void LineEdit::onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
-    unsigned start = mDragStartPosition;
-    unsigned current = getCharIndex(position);
-    if ((start != M_MAX_UNSIGNED) && (current != M_MAX_UNSIGNED))
+    if ((mCursorMovable) && (mTextSelectable))
     {
-        if (start < current)
+        unsigned start = mDragStartPosition;
+        unsigned current = getCharIndex(position);
+        if ((start != M_MAX_UNSIGNED) && (current != M_MAX_UNSIGNED))
         {
-            mText->setSelection(start, current - start);
+            if (start < current)
+                mText->setSelection(start, current - start);
+            else
+                mText->setSelection(current, start - current);
             setCursorPosition(current);
         }
-        else
-        {
-            mText->setSelection(current, start - current);
-            setCursorPosition(start);
-        }
     }
 }
 
@@ -161,21 +171,93 @@ void LineEdit::onKey(int key, int buttons, int qualifiers)
     
     switch (key)
     {
+    case 'X':
+    case 'C':
+        if ((mTextCopyable) && (qualifiers & QUAL_CTRL))
+        {
+            unsigned start = mText->getSelectionStart();
+            unsigned length = mText->getSelectionLength();
+            
+            if (mText->getSelectionLength())
+                sClipBoard = mLine.substr(start, length);
+            
+            if (key == 'X')
+            {
+                if (start + length < mLine.length())
+                    mLine = mLine.substr(0, start) + mLine.substr(start + length);
+                else
+                    mLine = mLine.substr(0, start);
+                mText->setSelection(0, 0);
+                mCursorPosition = start;
+                changed = true;
+            }
+        }
+        break;
+        
+    case 'V':
+        if ((mTextCopyable) && (qualifiers & QUAL_CTRL))
+        {
+            if (sClipBoard.length())
+            {
+                if (mCursorPosition < mLine.length())
+                    mLine = mLine.substr(0, mCursorPosition) + sClipBoard + mLine.substr(mCursorPosition);
+                else
+                    mLine += sClipBoard;
+                mCursorPosition += sClipBoard.length();
+                changed = true;
+            }
+        }
+        break;
+        
     case KEY_LEFT:
-        mText->setSelection(0, 0);
-        if (mCursorPosition > 0)
+        if (!(qualifiers & QUAL_SHIFT))
+            mText->setSelection(0, 0);
+        if ((mCursorMovable) && (mCursorPosition > 0))
         {
-            --mCursorPosition;
+            if ((mTextSelectable) && (qualifiers & QUAL_SHIFT) && (!mText->getSelectionLength()))
+                mDragStartPosition = mCursorPosition;
+            
+            if (qualifiers & QUAL_CTRL)
+                mCursorPosition = 0;
+            else
+                --mCursorPosition;
             cursorMoved = true;
+            
+            if ((mTextSelectable) && (qualifiers & QUAL_SHIFT))
+            {
+                unsigned start = mDragStartPosition;
+                unsigned current = mCursorPosition;
+                if (start < current)
+                    mText->setSelection(start, current - start);
+                else
+                    mText->setSelection(current, start - current);
+            }
         }
         break;
         
     case KEY_RIGHT:
-        mText->setSelection(0, 0);
-        if (mCursorPosition < mLine.length())
+        if (!(qualifiers & QUAL_SHIFT))
+            mText->setSelection(0, 0);
+        if ((mCursorMovable) && (mCursorPosition < mLine.length()))
         {
-            ++mCursorPosition;
+            if ((mTextSelectable) && (qualifiers & QUAL_SHIFT) && (!mText->getSelectionLength()))
+                mDragStartPosition = mCursorPosition;
+            
+            if (qualifiers & QUAL_CTRL)
+                mCursorPosition = mLine.length();
+            else
+                ++mCursorPosition;
             cursorMoved = true;
+            
+            if ((mTextSelectable) && (qualifiers & QUAL_SHIFT))
+            {
+                unsigned start = mDragStartPosition;
+                unsigned current = mCursorPosition;
+                if (start < current)
+                    mText->setSelection(start, current - start);
+                else
+                    mText->setSelection(current, start - current);
+            }
         }
         break;
         
@@ -223,6 +305,9 @@ void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
 {
     bool changed = false;
     
+    if (qualifiers & QUAL_CTRL)
+        return;
+    
     if (c == '\b')
     {
         if ((mLine.length()) && (mCursorPosition))
@@ -242,11 +327,16 @@ void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
         VariantMap eventData;
         eventData[P_ELEMENT] = (void*)this;
         sendEvent(EVENT_TEXTFINISHED, eventData);
+        
+        mText->setSelection(0, 0);
     }
     else if (c == 27)
     {
         if (mDefocusable)
+        {
+            mText->setSelection(0, 0);
             mDefocus = true;
+        }
     }
     else if ((c >= 0x20) && ((!mMaxLength) || (mLine.length() < mMaxLength)))
     {
@@ -271,7 +361,6 @@ void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
                 mLine = mLine.substr(0, start) + charStr + mLine.substr(start + length);
             else
                 mLine = mLine.substr(0, start) + charStr;
-            mText->setSelection(0, 0);
             mCursorPosition = start + 1;
         }
         changed = true;
@@ -279,6 +368,7 @@ void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
     
     if (changed)
     {
+        mText->setSelection(0, 0);
         updateText();
         updateCursor();
         
@@ -295,12 +385,15 @@ void LineEdit::setText(const std::string& text)
 {
     mLine = text;
     mText->setText(text);
+    // If cursor is not movable, make sure it's at the text end
+    if (!mCursorMovable)
+        setCursorPosition(mLine.length());
     updateText();
 }
 
 void LineEdit::setCursorPosition(unsigned position)
 {
-    if (position > mLine.length())
+    if ((position > mLine.length()) || (!mCursorMovable))
         position = mLine.length();
     
     if (position != mCursorPosition)
@@ -331,6 +424,21 @@ void LineEdit::setDefocusable(bool enable)
     mDefocusable = enable;
 }
 
+void LineEdit::setCursorMovable(bool enable)
+{
+    mCursorMovable = enable;
+}
+
+void LineEdit::setTextSelectable(bool enable)
+{
+    mTextSelectable = enable;
+}
+
+void LineEdit::setTextCopyable(bool enable)
+{
+    mTextCopyable = enable;
+}
+
 void LineEdit::updateText()
 {
     if (!mEchoCharacter)

+ 18 - 0
Engine/UI/LineEdit.h

@@ -67,6 +67,12 @@ public:
     void setEchoCharacter(char c);
     //! Set whether can defocus with ESC, default true
     void setDefocusable(bool enable);
+    //! Set whether can move cursor with arrows or mouse, default true
+    void setCursorMovable(bool enable);
+    //! Set whether selections are allowed, default true
+    void setTextSelectable(bool enable);
+    //! Set whether copy-paste operations are allowed, default true
+    void setTextCopyable(bool enable);
     
     //! Return text
     const std::string& getText() const { return mLine; }
@@ -80,6 +86,12 @@ public:
     char getEchoCharacter() const { return mEchoCharacter; }
     //! Return whether can defocus with ESC
     bool isDefocusable() const { return mDefocusable; }
+    //! Return whether can move cursor with arrows or mouse
+    bool isCursorMovable() const { return mCursorMovable; }
+    //! Return whether selections are allowed
+    bool isTextSelectable() const { return mTextSelectable; }
+    //! Return whether copy-paste operations are allowed
+    bool isTextCopyable() const { return mTextCopyable; }
     //! Return text element
     Text* getTextElement() const { return mText; }
     //! Return cursor element
@@ -117,6 +129,12 @@ protected:
     char mEchoCharacter;
     //! ESC defocus flag
     bool mDefocusable;
+    //! Cursor movable flag
+    bool mCursorMovable;
+    //! Text selectable flag
+    bool mTextSelectable;
+    //! Copy-paste enable flag
+    bool mTextCopyable;
     //! Defocus flag (defocus on next update)
     bool mDefocus;
 };

+ 2 - 0
Engine/UI/UIElement.cpp

@@ -29,6 +29,8 @@
 
 #include "DebugNew.h"
 
+std::string UIElement::sClipBoard;
+
 UIElement::UIElement(const std::string& name) :
     mName(name),
     mParent(0),

+ 3 - 0
Engine/UI/UIElement.h

@@ -293,6 +293,9 @@ protected:
     //! Userdata
     Variant mUserData;
     
+    //! Clipboard data
+    static std::string sClipBoard;
+    
 private:
     //! Return child elements recursively
     void getChildrenRecursive(std::vector<UIElement*>& dest) const;