| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- #include "BsGUIInputCaret.h"
- #include "BsGUIManager.h"
- #include "BsImageSprite.h"
- #include "CmFont.h"
- using namespace CamelotFramework;
- namespace BansheeEngine
- {
- GUIInputCaret::GUIInputCaret(const TEXT_SPRITE_DESC& textDesc, const Int2& offset, const Int2 clipOffset)
- :GUIInputTool(textDesc, offset, clipOffset), mCaretPos(0)
- {
- mCaretSprite = cm_new<ImageSprite, PoolAlloc>();
- updateText(textDesc, offset, clipOffset);
- }
- GUIInputCaret::~GUIInputCaret()
- {
- cm_delete<PoolAlloc>(mCaretSprite);
- }
- Int2 GUIInputCaret::getSpriteOffset() const
- {
- return getCaretPosition(mTextOffset);
- }
- Rect GUIInputCaret::getSpriteClipRect() const
- {
- Int2 offset = getSpriteOffset();
- return Rect(-offset.x + mTextOffset.x - mClipOffset.x, -offset.y + mTextOffset.y - mClipOffset.y,
- mTextDesc.width + 1, mTextDesc.height); // Increase clip size by 1, so we can fit the caret in case it is fully at the end of the text
- }
- void GUIInputCaret::updateSprite()
- {
- IMAGE_SPRITE_DESC mCaretDesc;
- mCaretDesc.width = 1;
- mCaretDesc.height = getCaretHeight();
- mCaretDesc.texture = GUIManager::instance().getCaretTexture();
- mCaretSprite->update(mCaretDesc);
- }
- void GUIInputCaret::moveCaretToStart()
- {
- mCaretPos = 0;
- }
- void GUIInputCaret::moveCaretToEnd()
- {
- mCaretPos = getMaxCaretPos();
- }
- void GUIInputCaret::moveCaretLeft()
- {
- mCaretPos = (UINT32)std::max(0, (INT32)mCaretPos - 1);
- }
- void GUIInputCaret::moveCaretRight()
- {
- UINT32 maxCaretPos = getMaxCaretPos();
- mCaretPos = std::min(maxCaretPos, mCaretPos + 1);
- }
- void GUIInputCaret::moveCaretUp()
- {
- UINT32 charIdx = getCharIdxAtCaretPos();
- if(charIdx > 0)
- charIdx -= 1;
- UINT32 lineIdx = getLineForChar(charIdx);
- const GUIInputLineDesc& desc = getLineDesc(lineIdx);
- // If char is a newline, I want that to count as being on the next line because that's
- // how user sees it
- if(desc.isNewline(charIdx))
- lineIdx++;
- if(lineIdx == 0)
- {
- moveCaretToStart();
- return;
- }
- Int2 caretCoords = getCaretPosition(mTextOffset);
- caretCoords.y -= getCaretHeight();
- moveCaretToPos(caretCoords);
- }
- void GUIInputCaret::moveCaretDown()
- {
- UINT32 charIdx = getCharIdxAtCaretPos();
- if(charIdx > 0)
- charIdx -= 1;
- UINT32 lineIdx = getLineForChar(charIdx);
- const GUIInputLineDesc& desc = getLineDesc(lineIdx);
- // If char is a newline, I want that to count as being on the next line because that's
- // how user sees it
- if(desc.isNewline(charIdx))
- lineIdx++;
- if(lineIdx == (getNumLines() - 1))
- {
- moveCaretToEnd();
- return;
- }
- Int2 caretCoords = getCaretPosition(mTextOffset);
- caretCoords.y += getCaretHeight();
- moveCaretToPos(caretCoords);
- }
- void GUIInputCaret::moveCaretToPos(const CM::Int2& pos)
- {
- INT32 charIdx = getCharIdxAtPos(pos);
- if(charIdx != -1)
- {
- Rect charRect = getCharRect(charIdx);
- float xCenter = charRect.x + charRect.width * 0.5f;
- if(pos.x <= xCenter)
- moveCaretToChar(charIdx, CARET_BEFORE);
- else
- moveCaretToChar(charIdx, CARET_AFTER);
- }
- else
- {
- UINT32 numLines = getNumLines();
- if(numLines == 0)
- {
- mCaretPos = 0;
- return;
- }
- UINT32 curPos = 0;
- for(UINT32 i = 0; i < numLines; i++)
- {
- const GUIInputLineDesc& line = getLineDesc(i);
- if(pos.y >= line.getLineYStart() && pos.y < (line.getLineYStart() + (INT32)line.getLineHeight()))
- {
- mCaretPos = curPos;
- return;
- }
- UINT32 numChars = line.getEndChar(false) - line.getStartChar() + 1; // +1 For extra line start position
- curPos += numChars;
- }
- const GUIInputLineDesc& firstLine = getLineDesc(0);
- if(pos.y < firstLine.getLineYStart()) // Before first line
- mCaretPos = 0;
- else // After last line
- mCaretPos = curPos - 1;
- }
- }
- void GUIInputCaret::moveCaretToChar(UINT32 charIdx, CaretPos caretPos)
- {
- if(charIdx >= (UINT32)mTextDesc.text.size())
- {
- mCaretPos = 0;
- return;
- }
- UINT32 numLines = getNumLines();
- UINT32 curPos = 0;
- UINT32 curCharIdx = 0;
- for(UINT32 i = 0; i < numLines; i++)
- {
- const GUIInputLineDesc& lineDesc = getLineDesc(i);
-
- curPos++; // Move past line start position
- UINT32 numChars = lineDesc.getEndChar() - lineDesc.getStartChar();
- UINT32 numCaretPositions = lineDesc.getEndChar(false) - lineDesc.getStartChar();
- if(charIdx >= (curCharIdx + numChars))
- {
- curCharIdx += numChars;
- curPos += numCaretPositions;
- continue;
- }
- UINT32 diff = charIdx - curCharIdx;
- if(caretPos == CARET_BEFORE)
- curPos += diff - 1;
- else
- curPos += diff;
- break;
- }
- mCaretPos = curPos;
- }
- UINT32 GUIInputCaret::getCharIdxAtCaretPos() const
- {
- return getCharIdxAtInputIdx(mCaretPos);
- }
- Int2 GUIInputCaret::getCaretPosition(const CM::Int2& offset) const
- {
- if(mTextDesc.text.size() > 0)
- {
- UINT32 curPos = 0;
- UINT32 numLines = getNumLines();
- for(UINT32 i = 0; i < numLines; i++)
- {
- const GUIInputLineDesc& lineDesc = getLineDesc(i);
- if(mCaretPos == curPos)
- {
- // Caret is on line start
- return Int2(offset.x, lineDesc.getLineYStart());
- }
- curPos += lineDesc.getEndChar(false) - lineDesc.getStartChar() + 1; // + 1 for special line start position
- }
- UINT32 charIdx = getCharIdxAtCaretPos();
- if(charIdx > 0)
- charIdx -= 1;
- charIdx = std::min((UINT32)(mTextDesc.text.size() - 1), charIdx);
- Rect charRect = getCharRect(charIdx);
- UINT32 lineIdx = getLineForChar(charIdx);
- UINT32 yOffset = getLineDesc(lineIdx).getLineYStart();
- return Int2(charRect.x + charRect.width, yOffset);
- }
- else
- {
- return offset;
- }
- }
- UINT32 GUIInputCaret::getCaretHeight() const
- {
- UINT32 charIdx = getCharIdxAtCaretPos();
- if(charIdx > 0)
- charIdx -= 1;
- if(charIdx < (UINT32)mTextDesc.text.size())
- {
- UINT32 lineIdx = getLineForChar(charIdx);
- return getLineDesc(lineIdx).getLineHeight();
- }
- else
- {
- if(mTextDesc.font != nullptr)
- {
- UINT32 nearestSize = mTextDesc.font->getClosestAvailableSize(mTextDesc.fontSize);
- const FontData* fontData = mTextDesc.font->getFontDataForSize(nearestSize);
- if(fontData != nullptr)
- return fontData->fontDesc.lineHeight;
- }
- }
- return 0;
- }
- bool GUIInputCaret::isCaretAtNewline() const
- {
- return isNewline(mCaretPos);
- }
- UINT32 GUIInputCaret::getMaxCaretPos() const
- {
- if(mTextDesc.text.size() == 0)
- return 0;
- UINT32 numLines = getNumLines();
- UINT32 maxPos = 0;
- for(UINT32 i = 0; i < numLines; i++)
- {
- const GUIInputLineDesc& lineDesc = getLineDesc(i);
- UINT32 numChars = lineDesc.getEndChar(false) - lineDesc.getStartChar() + 1; // + 1 for special line start position
- maxPos += numChars;
- }
- return maxPos - 1;
- }
- }
|