|
|
@@ -1,12 +1,14 @@
|
|
|
#include "BsGUIInputBox.h"
|
|
|
+#include "BsGUIManager.h"
|
|
|
#include "BsImageSprite.h"
|
|
|
#include "BsGUIWidget.h"
|
|
|
#include "BsGUISkin.h"
|
|
|
#include "BsSpriteTexture.h"
|
|
|
#include "BsTextSprite.h"
|
|
|
#include "BsGUILayoutOptions.h"
|
|
|
-#include "BsGUIKeyEvent.h"
|
|
|
+#include "BsGUIButtonEvent.h"
|
|
|
#include "BsGUIMouseEvent.h"
|
|
|
+#include "BsGUICommandEvent.h"
|
|
|
#include "CmTexture.h"
|
|
|
#include "CmCursor.h"
|
|
|
|
|
|
@@ -21,9 +23,11 @@ namespace BansheeEngine
|
|
|
}
|
|
|
|
|
|
GUIInputBox::GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
|
|
|
- :GUIElement(parent, style, layoutOptions), mNumImageRenderElements(0), mInputCursorSet(false), mDragInProgress(false)
|
|
|
+ :GUIElement(parent, style, layoutOptions), mNumImageRenderElements(0), mInputCursorSet(false), mDragInProgress(false),
|
|
|
+ mSelectionStart(0), mSelectionEnd(0), mCaretSprite(nullptr), mCaretShown(false), mSelectionShown(false), mCaretPos(0)
|
|
|
{
|
|
|
mImageSprite = cm_new<ImageSprite, PoolAlloc>();
|
|
|
+ mCaretSprite = cm_new<ImageSprite, PoolAlloc>();
|
|
|
mTextSprite = cm_new<TextSprite, PoolAlloc>();
|
|
|
|
|
|
mImageDesc.texture = mStyle->normal.texture;
|
|
|
@@ -43,6 +47,7 @@ namespace BansheeEngine
|
|
|
GUIInputBox::~GUIInputBox()
|
|
|
{
|
|
|
cm_delete<PoolAlloc>(mTextSprite);
|
|
|
+ cm_delete<PoolAlloc>(mCaretSprite);
|
|
|
cm_delete<PoolAlloc>(mImageSprite);
|
|
|
}
|
|
|
|
|
|
@@ -73,26 +78,34 @@ namespace BansheeEngine
|
|
|
UINT32 numElements = mImageSprite->getNumRenderElements();
|
|
|
numElements += mTextSprite->getNumRenderElements();
|
|
|
|
|
|
+ if(mCaretShown && GUIManager::instance().getCaretBlinkState())
|
|
|
+ numElements += mTextSprite->getNumRenderElements();
|
|
|
+
|
|
|
+ if(mSelectionShown)
|
|
|
+ {
|
|
|
+ for(auto& selectionSprite : mSelectionSprites)
|
|
|
+ {
|
|
|
+ numElements += selectionSprite->getNumRenderElements();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return numElements;
|
|
|
}
|
|
|
|
|
|
const HMaterial& GUIInputBox::getMaterial(UINT32 renderElementIdx) const
|
|
|
{
|
|
|
- if(renderElementIdx >= mNumImageRenderElements)
|
|
|
- return mTextSprite->getMaterial(mNumImageRenderElements - renderElementIdx);
|
|
|
- else
|
|
|
- return mImageSprite->getMaterial(renderElementIdx);
|
|
|
+ UINT32 localRenderElementIdx;
|
|
|
+ Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
|
|
|
+
|
|
|
+ return sprite->getMaterial(localRenderElementIdx);
|
|
|
}
|
|
|
|
|
|
UINT32 GUIInputBox::getNumQuads(UINT32 renderElementIdx) const
|
|
|
{
|
|
|
- UINT32 numQuads = 0;
|
|
|
- if(renderElementIdx >= mNumImageRenderElements)
|
|
|
- numQuads = mTextSprite->getNumQuads(mNumImageRenderElements - renderElementIdx);
|
|
|
- else
|
|
|
- numQuads = mImageSprite->getNumQuads(renderElementIdx);
|
|
|
+ UINT32 localRenderElementIdx;
|
|
|
+ Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
|
|
|
|
|
|
- return numQuads;
|
|
|
+ return sprite->getNumQuads(localRenderElementIdx);
|
|
|
}
|
|
|
|
|
|
void GUIInputBox::updateRenderElementsInternal()
|
|
|
@@ -128,6 +141,74 @@ namespace BansheeEngine
|
|
|
textDesc.vertAlign = mStyle->textVertAlign;
|
|
|
|
|
|
mTextSprite->update(textDesc);
|
|
|
+
|
|
|
+ if(mCaretShown && GUIManager::instance().getCaretBlinkState())
|
|
|
+ {
|
|
|
+ IMAGE_SPRITE_DESC mCaretDesc;
|
|
|
+ mCaretDesc.offset = getCaretPosition();
|
|
|
+ mCaretDesc.width = 1;
|
|
|
+ mCaretDesc.height = getCaretHeight();
|
|
|
+ mCaretDesc.clipRect = Rect(0, 0, textDesc.width, textDesc.height);
|
|
|
+ mCaretDesc.texture = GUIManager::instance().getCaretTexture();
|
|
|
+
|
|
|
+ mCaretSprite->update(mCaretDesc);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(mSelectionShown)
|
|
|
+ {
|
|
|
+ Vector<Rect>::type selectionRects = getSelectionRects();
|
|
|
+ // TODO - Update sprites
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Sprite* GUIInputBox::renderElemToSprite(UINT32 renderElemIdx, UINT32& localRenderElemIdx) const
|
|
|
+ {
|
|
|
+ UINT32 oldNumElements = 0;
|
|
|
+ UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
|
|
|
+ if(renderElemIdx < newNumElements)
|
|
|
+ {
|
|
|
+ localRenderElemIdx = renderElemIdx - oldNumElements;
|
|
|
+ return mTextSprite;
|
|
|
+ }
|
|
|
+
|
|
|
+ oldNumElements = newNumElements;
|
|
|
+ newNumElements += mImageSprite->getNumRenderElements();
|
|
|
+
|
|
|
+ if(renderElemIdx < newNumElements)
|
|
|
+ {
|
|
|
+ localRenderElemIdx = renderElemIdx - oldNumElements;
|
|
|
+ return mImageSprite;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(mCaretShown && GUIManager::instance().getCaretBlinkState())
|
|
|
+ {
|
|
|
+ oldNumElements = newNumElements;
|
|
|
+ newNumElements += mImageSprite->getNumRenderElements();
|
|
|
+
|
|
|
+ if(renderElemIdx < newNumElements)
|
|
|
+ {
|
|
|
+ localRenderElemIdx = renderElemIdx - oldNumElements;
|
|
|
+ return mImageSprite;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(mSelectionShown)
|
|
|
+ {
|
|
|
+ for(auto& selectionSprite : mSelectionSprites)
|
|
|
+ {
|
|
|
+ oldNumElements = newNumElements;
|
|
|
+ newNumElements += selectionSprite->getNumRenderElements();
|
|
|
+
|
|
|
+ if(renderElemIdx < newNumElements)
|
|
|
+ {
|
|
|
+ localRenderElemIdx = renderElemIdx - oldNumElements;
|
|
|
+ return selectionSprite;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ localRenderElemIdx = renderElemIdx;
|
|
|
+ return nullptr;
|
|
|
}
|
|
|
|
|
|
UINT32 GUIInputBox::_getOptimalWidth() const
|
|
|
@@ -152,19 +233,24 @@ namespace BansheeEngine
|
|
|
|
|
|
UINT32 GUIInputBox::_getRenderElementDepth(UINT32 renderElementIdx) const
|
|
|
{
|
|
|
- if(renderElementIdx >= mNumImageRenderElements)
|
|
|
+ UINT32 localRenderElementIdx;
|
|
|
+ Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
|
|
|
+
|
|
|
+ if(sprite == mImageSprite)
|
|
|
return _getDepth();
|
|
|
- else
|
|
|
+ else if(sprite == mTextSprite || sprite == mCaretSprite)
|
|
|
+ return _getDepth() + 2;
|
|
|
+ else // Selection sprites
|
|
|
return _getDepth() + 1;
|
|
|
}
|
|
|
|
|
|
void GUIInputBox::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
|
|
|
UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
|
|
|
{
|
|
|
- if(renderElementIdx >= mNumImageRenderElements)
|
|
|
- mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, mNumImageRenderElements - renderElementIdx);
|
|
|
- else
|
|
|
- mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, renderElementIdx);
|
|
|
+ UINT32 localRenderElementIdx;
|
|
|
+ Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
|
|
|
+
|
|
|
+ sprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, localRenderElementIdx);
|
|
|
}
|
|
|
|
|
|
bool GUIInputBox::mouseEvent(const GUIMouseEvent& ev)
|
|
|
@@ -198,6 +284,8 @@ namespace BansheeEngine
|
|
|
else if(ev.getType() == GUIMouseEventType::MouseDown)
|
|
|
{
|
|
|
mImageDesc.texture = mStyle->active.texture;
|
|
|
+ showCaret(getCharAtPosition(ev.getPosition()));
|
|
|
+ clearSelection();
|
|
|
markAsDirty();
|
|
|
|
|
|
return true;
|
|
|
@@ -227,11 +315,18 @@ namespace BansheeEngine
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
+ else if(ev.getType() == GUIMouseEventType::MouseDrag)
|
|
|
+ {
|
|
|
+ // TODO - Update selection
|
|
|
+ // - If mouse is over control, place selection marker there (make sure start < end)
|
|
|
+ // - Else move the selection by a certain amount of pixels depending on drag amount
|
|
|
+ markAsDirty();
|
|
|
+ }
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- bool GUIInputBox::keyEvent(const GUIKeyEvent& ev)
|
|
|
+ bool GUIInputBox::buttonEvent(const GUIButtonEvent& ev)
|
|
|
{
|
|
|
if(ev.getType() == GUIKeyEventType::KeyDown)
|
|
|
{
|
|
|
@@ -239,17 +334,40 @@ namespace BansheeEngine
|
|
|
{
|
|
|
if(mText.size() > 0)
|
|
|
{
|
|
|
- mText.erase(mText.end() - 1);
|
|
|
+ if(mSelectionShown)
|
|
|
+ {
|
|
|
+ mText.erase(mSelectionStart, mSelectionEnd);
|
|
|
+ mCaretPos = mSelectionStart;
|
|
|
+ clearSelection();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ mText.erase(mCaretPos);
|
|
|
+ }
|
|
|
+
|
|
|
markAsDirty();
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
+ // TODO - Handle newline if it's a multiline control
|
|
|
+ // TODO - left+right arrow to move the cursor
|
|
|
+ // TODO - shift + left + right arrow to select
|
|
|
+ // TODO - ctrl + a to select all
|
|
|
+ // TODO - ctrl + c, ctrl + v, ctrl + x to copy/cut/paste
|
|
|
}
|
|
|
else if(ev.getType() == GUIKeyEventType::TextInput)
|
|
|
{
|
|
|
- // TODO - How are backspace, caps and enter handled?
|
|
|
- mText += ev.getInputChar();
|
|
|
+ if(mSelectionShown)
|
|
|
+ {
|
|
|
+ mText.erase(mSelectionStart, mSelectionEnd);
|
|
|
+ mCaretPos = mSelectionStart;
|
|
|
+ clearSelection();
|
|
|
+ }
|
|
|
+
|
|
|
+ mText.insert(mText.begin() + mCaretPos, ev.getInputChar());
|
|
|
+ mCaretPos++;
|
|
|
|
|
|
markAsDirty();
|
|
|
|
|
|
@@ -259,6 +377,68 @@ namespace BansheeEngine
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ bool GUIInputBox::commandEvent(const GUICommandEvent& ev)
|
|
|
+ {
|
|
|
+ if(ev.getType() == GUICommandEventType::Redraw)
|
|
|
+ {
|
|
|
+ markAsDirty();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ void GUIInputBox::showCaret(CM::UINT32 charIdx)
|
|
|
+ {
|
|
|
+ mCaretPos = charIdx;
|
|
|
+ mCaretShown = true;
|
|
|
+ markAsDirty();
|
|
|
+ }
|
|
|
+
|
|
|
+ void GUIInputBox::clearCaret()
|
|
|
+ {
|
|
|
+ mCaretShown = false;
|
|
|
+ markAsDirty();
|
|
|
+ }
|
|
|
+
|
|
|
+ Int2 GUIInputBox::getCaretPosition() const
|
|
|
+ {
|
|
|
+ // TODO
|
|
|
+ return Int2(0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ UINT32 GUIInputBox::getCaretHeight() const
|
|
|
+ {
|
|
|
+ // TODO
|
|
|
+ return 8;
|
|
|
+ }
|
|
|
+
|
|
|
+ void GUIInputBox::showSelection(UINT32 startChar, UINT32 endChar)
|
|
|
+ {
|
|
|
+ mSelectionStart = startChar;
|
|
|
+ mSelectionEnd = endChar;
|
|
|
+ mSelectionShown = true;
|
|
|
+ markAsDirty();
|
|
|
+ }
|
|
|
+
|
|
|
+ void GUIInputBox::clearSelection()
|
|
|
+ {
|
|
|
+ mSelectionShown = false;
|
|
|
+ markAsDirty();
|
|
|
+ }
|
|
|
+
|
|
|
+ Vector<Rect>::type GUIInputBox::getSelectionRects() const
|
|
|
+ {
|
|
|
+ // TODO
|
|
|
+ return Vector<Rect>::type();
|
|
|
+ }
|
|
|
+
|
|
|
+ UINT32 GUIInputBox::getCharAtPosition(const Int2& pos) const
|
|
|
+ {
|
|
|
+ // TODO
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
void GUIInputBox::_setFocus(bool focus)
|
|
|
{
|
|
|
if(focus)
|
|
|
@@ -269,6 +449,8 @@ namespace BansheeEngine
|
|
|
else
|
|
|
{
|
|
|
mImageDesc.texture = mStyle->normal.texture;
|
|
|
+ clearCaret();
|
|
|
+ clearSelection();
|
|
|
markAsDirty();
|
|
|
}
|
|
|
}
|