diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp
index 7bdf3eeb7..e48a81bc1 100644
--- a/src/engine/client/input.cpp
+++ b/src/engine/client/input.cpp
@@ -109,6 +109,21 @@ void CInput::MouseModeRelative()
SDL_GetRelativeMouseState(0x0, 0x0);
}
+void CInput::NativeMousePos(int *x, int *y) const
+{
+ int nx = 0, ny = 0;
+ SDL_GetMouseState(&nx, &ny);
+
+ *x = nx;
+ *y = ny;
+}
+
+bool CInput::NativeMousePressed(int index)
+{
+ int i = SDL_GetMouseState(NULL, NULL);
+ return (i & SDL_BUTTON(index)) != 0;
+}
+
int CInput::MouseDoubleClick()
{
if(m_ReleaseDelta >= 0 && m_ReleaseDelta < (time_freq() / 3))
diff --git a/src/engine/client/input.h b/src/engine/client/input.h
index e371ec286..18de323a3 100644
--- a/src/engine/client/input.h
+++ b/src/engine/client/input.h
@@ -54,6 +54,8 @@ public:
virtual void MouseRelative(float *x, float *y);
virtual void MouseModeAbsolute();
virtual void MouseModeRelative();
+ virtual void NativeMousePos(int *x, int *y) const;
+ virtual bool NativeMousePressed(int index);
virtual int MouseDoubleClick();
virtual const char *GetClipboardText();
virtual void SetClipboardText(const char *Text);
diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp
index 30e638eaf..548c0e3fe 100644
--- a/src/engine/client/text.cpp
+++ b/src/engine/client/text.cpp
@@ -2,6 +2,8 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include
#include
+#include
+#include
#include
#include
#include
@@ -11,6 +13,8 @@
#include
#include FT_FREETYPE_H
+#include
+
// TODO: Refactor: clean this up
enum
{
@@ -28,6 +32,8 @@ struct SFontSizeChar
// width * font_size == real_size
float m_Width;
float m_Height;
+ float m_CharWidth;
+ float m_CharHeight;
float m_OffsetX;
float m_OffsetY;
float m_AdvanceX;
@@ -197,7 +203,7 @@ class CTextRender : public IEngineTextRender
unsigned int m_RenderFlags;
- std::vector m_TextContainers;
+ std::vector m_TextContainers;
std::vector m_TextContainerIndices;
int m_FirstFreeTextContainerIndex;
@@ -231,7 +237,7 @@ class CTextRender : public IEngineTextRender
void FreeTextContainer(int Index)
{
- m_TextContainers[Index].Reset();
+ m_TextContainers[Index]->Reset();
FreeTextContainerIndex(Index);
}
@@ -241,10 +247,10 @@ class CTextRender : public IEngineTextRender
{
int Size = (int)m_TextContainers.size();
for(int i = 0; i < (Index + 1) - Size; ++i)
- m_TextContainers.push_back(STextContainer());
+ m_TextContainers.push_back(new STextContainer());
}
- return m_TextContainers[Index];
+ return *m_TextContainers[Index];
}
int WordLength(const char *pText)
@@ -263,6 +269,7 @@ class CTextRender : public IEngineTextRender
ColorRGBA m_Color;
ColorRGBA m_OutlineColor;
+ ColorRGBA m_SelectionColor;
CFont *m_pDefaultFont;
FT_Library m_FTLibrary;
@@ -454,8 +461,8 @@ class CTextRender : public IEngineTextRender
{
FT_Bitmap *pBitmap;
- int x = 1;
- int y = 1;
+ int x = 0;
+ int y = 0;
unsigned int px, py;
FT_Face FtFace = pFont->m_FtFace;
@@ -502,37 +509,49 @@ class CTextRender : public IEngineTextRender
pBitmap = &FtFace->glyph->bitmap; // ignore_convention
+ unsigned int RealWidth = pBitmap->width;
+ unsigned int RealHeight = pBitmap->rows;
+
// adjust spacing
- int OutlineThickness = AdjustOutlineThicknessToFontSize(1, pSizeData->m_FontSize);
- x += OutlineThickness;
- y += OutlineThickness;
+ int OutlineThickness = 0;
+ if(RealWidth > 0)
+ {
+ OutlineThickness = AdjustOutlineThicknessToFontSize(1, pSizeData->m_FontSize);
- unsigned int Width = pBitmap->width + x * 2;
- unsigned int Height = pBitmap->rows + y * 2;
+ x += (OutlineThickness + 1);
+ y += (OutlineThickness + 1);
+ }
- // prepare glyph data
- mem_zero(ms_aGlyphData, Width * Height);
+ unsigned int Width = RealWidth + x * 2;
+ unsigned int Height = RealHeight + y * 2;
- for(py = 0; py < pBitmap->rows; py++) // ignore_convention
- for(px = 0; px < pBitmap->width; px++) // ignore_convention
- ms_aGlyphData[(py + y) * Width + px + x] = pBitmap->buffer[py * pBitmap->width + px]; // ignore_convention
-
- // upload the glyph
int X = 0;
int Y = 0;
- while(!GetCharacterSpace(pFont, 0, (int)Width, (int)Height, X, Y))
- {
- IncreaseFontTexture(pFont, 0);
- }
- UploadGlyph(pFont, 0, X, Y, (int)Width, (int)Height, ms_aGlyphData);
- Grow(ms_aGlyphData, ms_aGlyphDataOutlined, Width, Height, OutlineThickness);
-
- while(!GetCharacterSpace(pFont, 1, (int)Width, (int)Height, X, Y))
+ if(Width > 0 && Height > 0)
{
- IncreaseFontTexture(pFont, 1);
+ // prepare glyph data
+ mem_zero(ms_aGlyphData, Width * Height);
+
+ for(py = 0; py < pBitmap->rows; py++) // ignore_convention
+ for(px = 0; px < pBitmap->width; px++) // ignore_convention
+ ms_aGlyphData[(py + y) * Width + px + x] = pBitmap->buffer[py * pBitmap->width + px]; // ignore_convention
+
+ // upload the glyph
+ while(!GetCharacterSpace(pFont, 0, (int)Width, (int)Height, X, Y))
+ {
+ IncreaseFontTexture(pFont, 0);
+ }
+ UploadGlyph(pFont, 0, X, Y, (int)Width, (int)Height, ms_aGlyphData);
+
+ Grow(ms_aGlyphData, ms_aGlyphDataOutlined, Width, Height, OutlineThickness);
+
+ while(!GetCharacterSpace(pFont, 1, (int)Width, (int)Height, X, Y))
+ {
+ IncreaseFontTexture(pFont, 1);
+ }
+ UploadGlyph(pFont, 1, X, Y, (int)Width, (int)Height, ms_aGlyphDataOutlined);
}
- UploadGlyph(pFont, 1, X, Y, (int)Width, (int)Height, ms_aGlyphDataOutlined);
// set char info
{
@@ -543,6 +562,8 @@ class CTextRender : public IEngineTextRender
pFontchr->m_ID = Chr;
pFontchr->m_Height = Height;
pFontchr->m_Width = Width;
+ pFontchr->m_CharHeight = RealHeight;
+ pFontchr->m_CharWidth = RealWidth;
pFontchr->m_OffsetX = (FtFace->glyph->metrics.horiBearingX >> 6); // ignore_convention
pFontchr->m_OffsetY = -((FtFace->glyph->metrics.height >> 6) - (FtFace->glyph->metrics.horiBearingY >> 6));
pFontchr->m_AdvanceX = (FtFace->glyph->advance.x >> 6); // ignore_convention
@@ -585,8 +606,9 @@ public:
{
m_pGraphics = 0;
- m_Color = ColorRGBA(1, 1, 1, 1);
- m_OutlineColor = ColorRGBA(0, 0, 0, 0.3f);
+ m_Color = DefaultTextColor();
+ m_OutlineColor = DefaultTextOutlineColor();
+ m_SelectionColor = DefaultSelectionColor();
m_pCurFont = 0;
m_pDefaultFont = 0;
@@ -600,6 +622,13 @@ public:
virtual ~CTextRender()
{
+ for(auto *pTextCont : m_TextContainers)
+ {
+ pTextCont->Reset();
+ delete pTextCont;
+ }
+ m_TextContainers.clear();
+
for(auto &pFont : m_Fonts)
{
FT_Done_Face(pFont->m_FtFace);
@@ -772,6 +801,14 @@ public:
pCursor->m_CharCount = 0;
pCursor->m_MaxCharacterHeight = 0;
pCursor->m_LongestLineWidth = 0;
+
+ pCursor->m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_NONE;
+ pCursor->m_PressMouseX = 0;
+ pCursor->m_PressMouseY = 0;
+ pCursor->m_ReleaseMouseX = 0;
+ pCursor->m_ReleaseMouseY = 0;
+ pCursor->m_SelectionStart = 0;
+ pCursor->m_SelectionEnd = 0;
}
virtual void MoveCursor(CTextCursor *pCursor, float x, float y)
@@ -846,277 +883,32 @@ public:
}
virtual void TextOutlineColor(ColorRGBA rgb) { m_OutlineColor = rgb; };
+ virtual void TextSelectionColor(float r, float g, float b, float a)
+ {
+ m_SelectionColor.r = r;
+ m_SelectionColor.g = g;
+ m_SelectionColor.b = b;
+ m_SelectionColor.a = a;
+ }
+ virtual void TextSelectionColor(ColorRGBA rgb) { m_SelectionColor = rgb; };
+
virtual ColorRGBA GetTextColor() { return m_Color; }
virtual ColorRGBA GetTextOutlineColor() { return m_OutlineColor; }
+ virtual ColorRGBA GetTextSelectionColor() { return m_SelectionColor; }
virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length)
{
- dbg_assert(pText != NULL, "null text pointer");
-
- if(!*pText)
- return;
-
- CFont *pFont = pCursor->m_pFont;
- CFontSizeData *pSizeData = NULL;
-
- float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
- float FakeToScreenX, FakeToScreenY;
-
- int ActualSize;
- int GotNewLine = 0;
- int GotNewLineLast = 0;
- float DrawX = 0.0f, DrawY = 0.0f;
- int LineCount = 0;
- float CursorX, CursorY;
-
- float Size = pCursor->m_FontSize;
-
- // calculate the font size of the displayed glyphs
- Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
-
- FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0));
- FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0));
-
- int ActualX = (int)((pCursor->m_X * FakeToScreenX) + 0.5f);
- int ActualY = (int)((pCursor->m_Y * FakeToScreenY) + 0.5f);
- CursorX = ActualX / FakeToScreenX;
- CursorY = ActualY / FakeToScreenY;
-
- // same with size
- ActualSize = (int)(Size * FakeToScreenY);
- Size = ActualSize / FakeToScreenY;
-
- pCursor->m_AlignedFontSize = Size;
-
- // fetch pFont data
- if(!pFont)
- pFont = m_pCurFont;
-
- if(!pFont)
- return;
-
- pSizeData = pFont->GetFontSize(ActualSize);
-
- // set length
- if(Length < 0)
- Length = str_length(pText);
-
- float Scale = 1.0f / pSizeData->m_FontSize;
-
- //the outlined texture is always the same size as the current
- float UVScale = 1.0f / pFont->m_CurTextureDimensions[0];
-
- const char *pCurrent = pText;
- const char *pEnd = pCurrent + Length;
-
- if((m_RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) != 0)
+ int TextCont = CreateTextContainer(pCursor, pText, Length);
+ if(TextCont != -1)
{
- DrawX = pCursor->m_X;
- DrawY = pCursor->m_Y;
+ if((pCursor->m_Flags & TEXTFLAG_RENDER) != 0)
+ {
+ STextRenderColor TextColor = DefaultTextColor();
+ STextRenderColor TextColorOutline = DefaultTextOutlineColor();
+ RenderTextContainer(TextCont, &TextColor, &TextColorOutline);
+ }
+ DeleteTextContainer(TextCont);
}
- else
- {
- DrawX = CursorX;
- DrawY = CursorY;
- }
-
- LineCount = pCursor->m_LineCount;
-
- if(pCursor->m_Flags & TEXTFLAG_RENDER)
- {
- // make sure there are no vertices
- Graphics()->FlushVertices();
-
- if(Graphics()->IsTextBufferingEnabled())
- {
- Graphics()->TextureClear();
- Graphics()->TextQuadsBegin();
- Graphics()->SetColor(m_Color);
- }
- else
- {
- Graphics()->TextureSet(pFont->m_aTextures[1]);
- Graphics()->QuadsBegin();
- Graphics()->SetColor(m_OutlineColor.r, m_OutlineColor.g, m_OutlineColor.b, m_OutlineColor.a * m_Color.a);
- }
- }
-
- FT_UInt LastCharGlyphIndex = 0;
- size_t CharacterCounter = 0;
-
- while(pCurrent < pEnd && (pCursor->m_MaxLines < 1 || LineCount <= pCursor->m_MaxLines))
- {
- int NewLine = 0;
- const char *pBatchEnd = pEnd;
- if(pCursor->m_LineWidth > 0 && !(pCursor->m_Flags & TEXTFLAG_STOP_AT_END))
- {
- int Wlen = minimum(WordLength(pCurrent), (int)(pEnd - pCurrent));
- CTextCursor Compare = *pCursor;
- Compare.m_X = DrawX;
- Compare.m_Y = DrawY;
- Compare.m_Flags &= ~TEXTFLAG_RENDER;
- Compare.m_LineWidth = -1;
- TextEx(&Compare, pCurrent, Wlen);
-
- if(Compare.m_X - DrawX > pCursor->m_LineWidth)
- {
- // word can't be fitted in one line, cut it
- CTextCursor Cutter = *pCursor;
- Cutter.m_GlyphCount = 0;
- Cutter.m_CharCount = 0;
- Cutter.m_X = DrawX;
- Cutter.m_Y = DrawY;
- Cutter.m_Flags &= ~TEXTFLAG_RENDER;
- Cutter.m_Flags |= TEXTFLAG_STOP_AT_END;
-
- TextEx(&Cutter, pCurrent, Wlen);
- int WordGlyphs = Cutter.m_GlyphCount;
- Wlen = Cutter.m_CharCount;
- NewLine = 1;
-
- if(WordGlyphs <= 3 && GotNewLineLast == 0) // if we can't place 3 chars of the word on this line, take the next
- Wlen = 0;
- }
- else if(Compare.m_X - pCursor->m_StartX > pCursor->m_LineWidth && GotNewLineLast == 0)
- {
- NewLine = 1;
- Wlen = 0;
- }
-
- pBatchEnd = pCurrent + Wlen;
- }
-
- const char *pTmp = pCurrent;
- int NextCharacter = str_utf8_decode(&pTmp);
- while(pCurrent < pBatchEnd)
- {
- pCursor->m_CharCount += pTmp - pCurrent;
- int Character = NextCharacter;
- pCurrent = pTmp;
- NextCharacter = str_utf8_decode(&pTmp);
-
- if(Character == '\n')
- {
- ++CharacterCounter;
- LastCharGlyphIndex = 0;
-
- DrawX = pCursor->m_StartX;
- DrawY += Size;
- if((m_RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
- {
- DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
- DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
- }
- ++LineCount;
- if(pCursor->m_MaxLines > 0 && LineCount > pCursor->m_MaxLines)
- break;
- continue;
- }
-
- SFontSizeChar *pChr = GetChar(pFont, pSizeData, Character);
- if(pChr)
- {
- bool ApplyBearingX = !(((m_RenderFlags & TEXT_RENDER_FLAG_NO_X_BEARING) != 0) || (CharacterCounter == 0 && (m_RenderFlags & TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING) != 0));
- float Advance = ((((m_RenderFlags & TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH) != 0) ? (pChr->m_Width) : (pChr->m_AdvanceX + ((!ApplyBearingX) ? (-pChr->m_OffsetX) : 0.f)))) * Scale;
-
- float CharKerning = 0.f;
- if((m_RenderFlags & TEXT_RENDER_FLAG_KERNING) != 0)
- CharKerning = Kerning(pFont, LastCharGlyphIndex, pChr->m_GlyphIndex) * Scale * Size;
-
- LastCharGlyphIndex = pChr->m_GlyphIndex;
- if(pCursor->m_Flags & TEXTFLAG_STOP_AT_END && (DrawX + CharKerning) + Advance * Size - pCursor->m_StartX > pCursor->m_LineWidth)
- {
- // we hit the end of the line, no more to render or count
- pCurrent = pEnd;
- break;
- }
-
- float BearingX = (!ApplyBearingX ? 0.f : pChr->m_OffsetX) * Scale * Size;
- float CharWidth = pChr->m_Width * Scale * Size;
-
- float BearingY = 0.f;
- BearingY = (((m_RenderFlags & TEXT_RENDER_FLAG_NO_Y_BEARING) != 0) ? 0.f : (pChr->m_OffsetY * Scale * Size));
- float CharHeight = pChr->m_Height * Scale * Size;
-
- if((m_RenderFlags & TEXT_RENDER_FLAG_NO_OVERSIZE) != 0)
- {
- if(CharHeight + BearingY > Size)
- {
- BearingY = 0;
- float ScaleChar = (CharHeight + BearingY) / Size;
- CharHeight = Size;
- CharWidth /= ScaleChar;
- }
- }
-
- if(pCursor->m_Flags & TEXTFLAG_RENDER && m_Color.a != 0.f)
- {
- if(Graphics()->IsTextBufferingEnabled())
- Graphics()->QuadsSetSubset(pChr->m_aUVs[0], pChr->m_aUVs[3], pChr->m_aUVs[2], pChr->m_aUVs[1]);
- else
- Graphics()->QuadsSetSubset(pChr->m_aUVs[0] * UVScale, pChr->m_aUVs[3] * UVScale, pChr->m_aUVs[2] * UVScale, pChr->m_aUVs[1] * UVScale);
- float Y = (DrawY + Size);
-
- IGraphics::CQuadItem QuadItem((DrawX + CharKerning) + BearingX, Y - BearingY, CharWidth, -CharHeight);
- Graphics()->QuadsDrawTL(&QuadItem, 1);
- }
-
- pCursor->m_MaxCharacterHeight = maximum(pCursor->m_MaxCharacterHeight, CharHeight + BearingY);
-
- if(NextCharacter == 0 && (m_RenderFlags & TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE) != 0 && Character != ' ')
- DrawX += BearingX + CharKerning + CharWidth;
- else
- DrawX += Advance * Size + CharKerning;
- pCursor->m_GlyphCount++;
-
- ++CharacterCounter;
- }
-
- if(DrawX > pCursor->m_LongestLineWidth)
- pCursor->m_LongestLineWidth = DrawX;
- }
-
- if(NewLine)
- {
- DrawX = pCursor->m_StartX;
- DrawY += Size;
- if((m_RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
- {
- DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
- DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
- }
- GotNewLine = 1;
- GotNewLineLast = 1;
- ++LineCount;
- }
- else
- GotNewLineLast = 0;
- }
-
- if(pCursor->m_Flags & TEXTFLAG_RENDER)
- {
- if(Graphics()->IsTextBufferingEnabled())
- {
- float OutlineColor[4] = {m_OutlineColor.r, m_OutlineColor.g, m_OutlineColor.b, m_OutlineColor.a * m_Color.a};
- Graphics()->TextQuadsEnd(pFont->m_CurTextureDimensions[0], pFont->m_aTextures[0].Id(), pFont->m_aTextures[1].Id(), OutlineColor);
- }
- else
- {
- Graphics()->QuadsEndKeepVertices();
-
- Graphics()->TextureSet(pFont->m_aTextures[0]);
- Graphics()->ChangeColorOfCurrentQuadVertices(m_Color.r, m_Color.g, m_Color.b, m_Color.a);
-
- // render non outlined
- Graphics()->QuadsDrawCurrentVertices(false);
- }
- }
-
- pCursor->m_X = DrawX;
- pCursor->m_LineCount = LineCount;
-
- if(GotNewLine)
- pCursor->m_Y = DrawY;
}
virtual int CreateTextContainer(CTextCursor *pCursor, const char *pText, int Length = -1)
@@ -1130,6 +922,8 @@ public:
if(!pFont)
return -1;
+ bool IsRendered = (pCursor->m_Flags & TEXTFLAG_RENDER) != 0;
+
int ContainerIndex = GetFreeTextContainerIndex();
STextContainer &TextContainer = GetTextContainer(ContainerIndex);
TextContainer.m_pFont = pFont;
@@ -1175,7 +969,7 @@ public:
AppendTextContainer(pCursor, ContainerIndex, pText, Length);
- if(TextContainer.m_StringInfo.m_CharacterQuads.size() == 0)
+ if(TextContainer.m_StringInfo.m_CharacterQuads.size() == 0 && IsRendered)
{
FreeTextContainer(ContainerIndex);
return -1;
@@ -1183,7 +977,7 @@ public:
else
{
TextContainer.m_StringInfo.m_QuadNum = TextContainer.m_StringInfo.m_CharacterQuads.size();
- if(Graphics()->IsTextBufferingEnabled())
+ if(Graphics()->IsTextBufferingEnabled() && IsRendered)
{
if((TextContainer.m_RenderFlags & TEXT_RENDER_FLAG_NO_AUTOMATIC_QUAD_UPLOAD) == 0)
{
@@ -1268,6 +1062,76 @@ public:
FT_UInt LastCharGlyphIndex = 0;
size_t CharacterCounter = 0;
+ bool IsRendered = (pCursor->m_Flags & TEXTFLAG_RENDER) != 0;
+
+ std::vector SelectionQuads;
+ bool SelectionStarted = false;
+ bool SelectionUsedPress = false;
+ bool SelectionUsedRelease = false;
+ int SelectionStartChar = -1;
+ int SelectionEndChar = -1;
+
+ auto &&CheckSelectionStart = [&](bool CheckOuter, int CursorX, int CursorY, int &SelectionChar, bool &SelectionUsedCase, float LastCharX, float LastCharWidth, float CharX, float CharWidth, float CharY) {
+ if(!SelectionStarted && !SelectionUsedCase)
+ {
+ if((LastCharX - LastCharWidth / 2 <= CursorX &&
+ CharX + CharWidth / 2 > CursorX &&
+ CharY - Size <= CursorY &&
+ CharY > CursorY) ||
+ (CheckOuter &&
+ CharY - Size > CursorY))
+ {
+ SelectionChar = CharacterCounter;
+ SelectionStarted = !SelectionStarted;
+ SelectionUsedCase = true;
+ }
+ }
+ };
+ auto &&CheckSelectionEnd = [&](bool CheckOuter, int CursorX, int CursorY, int &SelectionChar, bool &SelectionUsedCase, float CharX, float CharWidth, float CharY) {
+ if(SelectionStarted && !SelectionUsedCase)
+ {
+ if((CharX + CharWidth / 2 > CursorX &&
+ CharY - Size <= CursorY &&
+ CharY > CursorY) ||
+ (CheckOuter &&
+ CharY <= CursorY))
+ {
+ SelectionChar = CharacterCounter;
+ SelectionStarted = !SelectionStarted;
+ SelectionUsedCase = true;
+ }
+ }
+ };
+
+ float LastSelX = DrawX;
+ float LastSelWidth = 0;
+ float LastCharX = DrawX;
+ float LastCharWidth = 0;
+
+ auto &&StartNewLine = [&]() {
+ DrawX = pCursor->m_StartX;
+ DrawY += Size;
+ if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
+ {
+ DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
+ DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
+ }
+ LastSelX = DrawX;
+ LastSelWidth = 0;
+ LastCharX = DrawX;
+ LastCharWidth = 0;
+ ++LineCount;
+ };
+
+ if(pCursor->m_CalculateSelectionMode != TEXT_CURSOR_SELECTION_MODE_NONE)
+ {
+ if(IsRendered)
+ {
+ if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
+ Graphics()->QuadContainerReset(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex);
+ }
+ }
+
while(pCurrent < pEnd && (pCursor->m_MaxLines < 1 || LineCount <= pCursor->m_MaxLines))
{
int NewLine = 0;
@@ -1276,6 +1140,7 @@ public:
{
int Wlen = minimum(WordLength((char *)pCurrent), (int)(pEnd - pCurrent));
CTextCursor Compare = *pCursor;
+ Compare.m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_NONE;
Compare.m_X = DrawX;
Compare.m_Y = DrawY;
Compare.m_Flags &= ~TEXTFLAG_RENDER;
@@ -1286,6 +1151,7 @@ public:
{
// word can't be fitted in one line, cut it
CTextCursor Cutter = *pCursor;
+ Cutter.m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_NONE;
Cutter.m_GlyphCount = 0;
Cutter.m_CharCount = 0;
Cutter.m_X = DrawX;
@@ -1315,7 +1181,7 @@ public:
while(pCurrent < pBatchEnd)
{
- TextContainer.m_CharCount += pTmp - pCurrent;
+ pCursor->m_CharCount += pTmp - pCurrent;
int Character = NextCharacter;
pCurrent = pTmp;
NextCharacter = str_utf8_decode(&pTmp);
@@ -1324,15 +1190,7 @@ public:
{
LastCharGlyphIndex = 0;
++CharacterCounter;
-
- DrawX = pCursor->m_StartX;
- DrawY += Size;
- if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
- {
- DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
- DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
- }
- ++LineCount;
+ StartNewLine();
if(pCursor->m_MaxLines > 0 && LineCount > pCursor->m_MaxLines)
break;
continue;
@@ -1342,14 +1200,16 @@ public:
if(pChr)
{
bool ApplyBearingX = !(((RenderFlags & TEXT_RENDER_FLAG_NO_X_BEARING) != 0) || (CharacterCounter == 0 && (RenderFlags & TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING) != 0));
- float Advance = ((((RenderFlags & TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH) != 0) ? (pChr->m_Width) : (pChr->m_AdvanceX + ((!ApplyBearingX) ? (-pChr->m_OffsetX) : 0.f)))) * Scale;
+ float Advance = ((((RenderFlags & TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH) != 0) ? (pChr->m_Width) : (pChr->m_AdvanceX + ((!ApplyBearingX) ? (-pChr->m_OffsetX) : 0.f)))) * Scale * Size;
+
+ float OutLineRealDiff = (pChr->m_Width - pChr->m_CharWidth) * Scale * Size;
float CharKerning = 0.f;
if((RenderFlags & TEXT_RENDER_FLAG_KERNING) != 0)
CharKerning = Kerning(TextContainer.m_pFont, LastCharGlyphIndex, pChr->m_GlyphIndex) * Scale * Size;
LastCharGlyphIndex = pChr->m_GlyphIndex;
- if(pCursor->m_Flags & TEXTFLAG_STOP_AT_END && (DrawX + CharKerning) + Advance * Size - pCursor->m_StartX > pCursor->m_LineWidth)
+ if(pCursor->m_Flags & TEXTFLAG_STOP_AT_END && (DrawX + CharKerning) + Advance - pCursor->m_StartX > pCursor->m_LineWidth)
{
// we hit the end of the line, no more to render or count
pCurrent = pEnd;
@@ -1373,16 +1233,18 @@ public:
}
}
+ float TmpY = (DrawY + Size);
+ float CharX = (DrawX + CharKerning) + BearingX;
+ float CharY = TmpY - BearingY;
+
// don't add text that isn't drawn, the color overwrite is used for that
- if(m_Color.a != 0.f)
+ if(m_Color.a != 0.f && IsRendered)
{
TextContainer.m_StringInfo.m_CharacterQuads.push_back(STextCharQuad());
STextCharQuad &TextCharQuad = TextContainer.m_StringInfo.m_CharacterQuads.back();
- float Y = (DrawY + Size);
-
- TextCharQuad.m_Vertices[0].m_X = (DrawX + CharKerning) + BearingX;
- TextCharQuad.m_Vertices[0].m_Y = Y - BearingY;
+ TextCharQuad.m_Vertices[0].m_X = CharX;
+ TextCharQuad.m_Vertices[0].m_Y = CharY;
TextCharQuad.m_Vertices[0].m_U = pChr->m_aUVs[0];
TextCharQuad.m_Vertices[0].m_V = pChr->m_aUVs[3];
TextCharQuad.m_Vertices[0].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
@@ -1390,8 +1252,8 @@ public:
TextCharQuad.m_Vertices[0].m_Color.m_B = (unsigned char)(m_Color.b * 255.f);
TextCharQuad.m_Vertices[0].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
- TextCharQuad.m_Vertices[1].m_X = (DrawX + CharKerning) + BearingX + CharWidth;
- TextCharQuad.m_Vertices[1].m_Y = Y - BearingY;
+ TextCharQuad.m_Vertices[1].m_X = CharX + CharWidth;
+ TextCharQuad.m_Vertices[1].m_Y = CharY;
TextCharQuad.m_Vertices[1].m_U = pChr->m_aUVs[2];
TextCharQuad.m_Vertices[1].m_V = pChr->m_aUVs[3];
TextCharQuad.m_Vertices[1].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
@@ -1399,8 +1261,8 @@ public:
TextCharQuad.m_Vertices[1].m_Color.m_B = (unsigned char)(m_Color.b * 255.f);
TextCharQuad.m_Vertices[1].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
- TextCharQuad.m_Vertices[2].m_X = (DrawX + CharKerning) + BearingX + CharWidth;
- TextCharQuad.m_Vertices[2].m_Y = Y - BearingY - CharHeight;
+ TextCharQuad.m_Vertices[2].m_X = CharX + CharWidth;
+ TextCharQuad.m_Vertices[2].m_Y = CharY - CharHeight;
TextCharQuad.m_Vertices[2].m_U = pChr->m_aUVs[2];
TextCharQuad.m_Vertices[2].m_V = pChr->m_aUVs[1];
TextCharQuad.m_Vertices[2].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
@@ -1408,8 +1270,8 @@ public:
TextCharQuad.m_Vertices[2].m_Color.m_B = (unsigned char)(m_Color.b * 255.f);
TextCharQuad.m_Vertices[2].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
- TextCharQuad.m_Vertices[3].m_X = (DrawX + CharKerning) + BearingX;
- TextCharQuad.m_Vertices[3].m_Y = Y - BearingY - CharHeight;
+ TextCharQuad.m_Vertices[3].m_X = CharX;
+ TextCharQuad.m_Vertices[3].m_Y = CharY - CharHeight;
TextCharQuad.m_Vertices[3].m_U = pChr->m_aUVs[0];
TextCharQuad.m_Vertices[3].m_V = pChr->m_aUVs[1];
TextCharQuad.m_Vertices[3].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
@@ -1418,14 +1280,57 @@ public:
TextCharQuad.m_Vertices[3].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
}
+ // calculate the full width from the last selection point to the end of this selection draw on screen
+ float SelWidth = (CharX + maximum(Advance, CharWidth - OutLineRealDiff / 2)) - (LastSelX + LastSelWidth);
+ float SelX = (LastSelX + LastSelWidth);
+
+ if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE)
+ {
+ if(CharacterCounter == 0)
+ {
+ CheckSelectionStart(true, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, LastCharX, LastCharWidth, CharX, CharWidth, TmpY);
+ CheckSelectionStart(true, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, LastCharX, LastCharWidth, CharX, CharWidth, TmpY);
+ }
+
+ // if selection didn't start and the mouse pos is atleast on 50% of the right side of the character start
+ CheckSelectionStart(false, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, LastCharX, LastCharWidth, CharX, CharWidth, TmpY);
+ CheckSelectionStart(false, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, LastCharX, LastCharWidth, CharX, CharWidth, TmpY);
+ CheckSelectionEnd(false, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, CharX, CharWidth, TmpY);
+ CheckSelectionEnd(false, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, CharX, CharWidth, TmpY);
+ }
+ if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_SET)
+ {
+ if((int)CharacterCounter == pCursor->m_SelectionStart)
+ {
+ SelectionStarted = !SelectionStarted;
+ SelectionUsedPress = true;
+ }
+ if((int)CharacterCounter == pCursor->m_SelectionEnd)
+ {
+ SelectionStarted = !SelectionStarted;
+ SelectionUsedRelease = true;
+ }
+ }
+
pCursor->m_MaxCharacterHeight = maximum(pCursor->m_MaxCharacterHeight, CharHeight + BearingY);
if(NextCharacter == 0 && (RenderFlags & TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE) != 0 && Character != ' ')
DrawX += BearingX + CharKerning + CharWidth;
else
- DrawX += Advance * Size + CharKerning;
+ DrawX += Advance + CharKerning;
+
pCursor->m_GlyphCount++;
++CharacterCounter;
+
+ if(SelectionStarted && IsRendered)
+ {
+ SelectionQuads.push_back(IGraphics::CQuadItem(SelX, DrawY, SelWidth, Size));
+ }
+
+ LastSelX = SelX;
+ LastSelWidth = SelWidth;
+ LastCharX = CharX;
+ LastCharWidth = CharWidth;
}
if(DrawX > pCursor->m_LongestLineWidth)
@@ -1434,22 +1339,15 @@ public:
if(NewLine)
{
- DrawX = pCursor->m_StartX;
- DrawY += Size;
- if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
- {
- DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
- DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
- }
+ StartNewLine();
GotNewLine = 1;
GotNewLineLast = 1;
- ++LineCount;
}
else
GotNewLineLast = 0;
}
- if(TextContainer.m_StringInfo.m_CharacterQuads.size() != 0)
+ if(TextContainer.m_StringInfo.m_CharacterQuads.size() != 0 && IsRendered)
{
TextContainer.m_StringInfo.m_QuadNum = TextContainer.m_StringInfo.m_CharacterQuads.size();
// setup the buffers
@@ -1466,6 +1364,40 @@ public:
}
}
+ if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE)
+ {
+ pCursor->m_SelectionStart = -1;
+ pCursor->m_SelectionEnd = -1;
+
+ if(SelectionStarted)
+ {
+ CheckSelectionEnd(true, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, std::numeric_limits::max(), 0, DrawY + Size);
+ CheckSelectionEnd(true, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, std::numeric_limits::max(), 0, DrawY + Size);
+ }
+ }
+ else if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_SET)
+ {
+ if((int)CharacterCounter == pCursor->m_SelectionEnd)
+ {
+ SelectionStarted = !SelectionStarted;
+ SelectionUsedRelease = true;
+ }
+ }
+
+ if(!SelectionQuads.empty() && SelectionUsedPress && SelectionUsedRelease)
+ {
+ Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
+ if(SelectionQuads.size() > 0)
+ {
+ if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex == -1)
+ TextContainer.m_StringInfo.m_SelectionQuadContainerIndex = Graphics()->CreateQuadContainer();
+ Graphics()->QuadContainerAddQuads(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, &SelectionQuads[0], (int)SelectionQuads.size());
+
+ pCursor->m_SelectionStart = SelectionStartChar;
+ pCursor->m_SelectionEnd = SelectionEndChar;
+ }
+ }
+
// even if no text is drawn the cursor position will be adjusted
pCursor->m_X = DrawX;
pCursor->m_LineCount = LineCount;
@@ -1490,223 +1422,6 @@ public:
AppendTextContainer(pCursor, TextContainerIndex, pText, Length);
}
- virtual void SetTextContainerSelection(int TextContainerIndex, const char *pText, int CursorPos, int SelectionStart, int SelectionEnd)
- {
- STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
-
- CFontSizeData *pSizeData = NULL;
-
- float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
- float FakeToScreenX, FakeToScreenY;
-
- int ActualSize;
- float DrawX = 0.0f, DrawY = 0.0f;
- int LineCount = 0;
-
- float Size = TextContainer.m_UnscaledFontSize;
-
- // calculate the font size of the displayed glyphs
- Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
-
- FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0));
- FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0));
-
- // same with size
- ActualSize = (int)(Size * FakeToScreenY);
- Size = ActualSize / FakeToScreenY;
-
- pSizeData = TextContainer.m_pFont->GetFontSize(TextContainer.m_FontSize);
-
- FT_Set_Pixel_Sizes(TextContainer.m_pFont->m_FtFace, 0, TextContainer.m_FontSize);
-
- // string length
- int Length = str_length(pText);
-
- float Scale = 1.0f / pSizeData->m_FontSize;
- float MaxRowHeight = (TextContainer.m_pFont->m_FtFace->size->metrics.height >> 6) * Scale * Size;
-
- const char *pCurrent = (char *)pText;
- const char *pCurrentLast = (char *)pText;
- const char *pEnd = pCurrent + Length;
-
- int RenderFlags = TextContainer.m_RenderFlags;
-
- if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) != 0)
- {
- DrawX = TextContainer.m_X;
- DrawY = TextContainer.m_Y;
- }
- else
- {
- DrawX = TextContainer.m_AlignedStartX;
- DrawY = TextContainer.m_AlignedStartY;
- }
-
- LineCount = TextContainer.m_LineCount;
-
- if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex == -1)
- TextContainer.m_StringInfo.m_SelectionQuadContainerIndex = Graphics()->CreateQuadContainer();
-
- Graphics()->QuadContainerReset(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex);
-
- std::vector SelectionQuads;
- IGraphics::CQuadItem CursorQuad;
-
- FT_UInt LastCharGlyphIndex = 0;
- size_t CharacterCounter = 0;
-
- while(pCurrent < pEnd && (TextContainer.m_MaxLines < 1 || LineCount <= TextContainer.m_MaxLines))
- {
- int NewLine = 0;
- const char *pBatchEnd = pEnd;
- if(TextContainer.m_LineWidth > 0 && !(TextContainer.m_Flags & TEXTFLAG_STOP_AT_END))
- {
- CTextCursor FakeCursor;
- SetCursor(&FakeCursor, DrawX, DrawY, TextContainer.m_UnscaledFontSize, TextContainer.m_Flags);
- FakeCursor.m_LineCount = TextContainer.m_LineCount;
- FakeCursor.m_GlyphCount = TextContainer.m_GlyphCount;
- FakeCursor.m_CharCount = TextContainer.m_CharCount;
- FakeCursor.m_MaxLines = TextContainer.m_MaxLines;
- FakeCursor.m_StartX = TextContainer.m_StartX;
- FakeCursor.m_StartY = TextContainer.m_StartY;
- FakeCursor.m_LineWidth = TextContainer.m_LineWidth;
- FakeCursor.m_pFont = TextContainer.m_pFont;
-
- int Wlen = minimum(WordLength((char *)pCurrent), (int)(pEnd - pCurrent));
- CTextCursor Compare = FakeCursor;
- Compare.m_X = DrawX;
- Compare.m_Y = DrawY;
- Compare.m_Flags &= ~TEXTFLAG_RENDER;
- Compare.m_LineWidth = -1;
- TextEx(&Compare, pCurrent, Wlen);
-
- if(Compare.m_X - DrawX > TextContainer.m_LineWidth)
- {
- // word can't be fitted in one line, cut it
- CTextCursor Cutter = FakeCursor;
- Cutter.m_GlyphCount = 0;
- Cutter.m_CharCount = 0;
- Cutter.m_X = DrawX;
- Cutter.m_Y = DrawY;
- Cutter.m_Flags &= ~TEXTFLAG_RENDER;
- Cutter.m_Flags |= TEXTFLAG_STOP_AT_END;
-
- TextEx(&Cutter, pCurrent, Wlen);
- int WordGlyphs = Cutter.m_GlyphCount;
- Wlen = Cutter.m_CharCount;
- NewLine = 1;
-
- if(WordGlyphs <= 3) // if we can't place 3 chars of the word on this line, take the next
- Wlen = 0;
- }
- else if(Compare.m_X - TextContainer.m_StartX > TextContainer.m_LineWidth)
- {
- NewLine = 1;
- Wlen = 0;
- }
-
- pBatchEnd = pCurrent + Wlen;
- }
-
- pCurrentLast = pCurrent;
- const char *pTmp = pCurrent;
- int NextCharacter = str_utf8_decode(&pTmp);
- while(pCurrent < pBatchEnd)
- {
- TextContainer.m_CharCount += pTmp - pCurrent;
- int Character = NextCharacter;
- pCurrent = pTmp;
- NextCharacter = str_utf8_decode(&pTmp);
-
- if(Character == '\n')
- {
- LastCharGlyphIndex = 0;
- ++CharacterCounter;
-
- DrawX = TextContainer.m_StartX;
- DrawY += Size;
- if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
- {
- DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
- DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
- }
- ++LineCount;
- if(TextContainer.m_MaxLines > 0 && LineCount > TextContainer.m_MaxLines)
- break;
- continue;
- }
-
- SFontSizeChar *pChr = GetChar(TextContainer.m_pFont, pSizeData, Character);
- if(pChr)
- {
- bool ApplyBearingX = !(((RenderFlags & TEXT_RENDER_FLAG_NO_X_BEARING) != 0) || (CharacterCounter == 0 && (RenderFlags & TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING) != 0));
- float Advance = ((((RenderFlags & TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH) != 0) ? (pChr->m_Width) : (pChr->m_AdvanceX + (!ApplyBearingX ? (-pChr->m_OffsetX) : 0.f)))) * Scale;
-
- float CharKerning = 0.f;
- if((RenderFlags & TEXT_RENDER_FLAG_KERNING) != 0)
- CharKerning = Kerning(TextContainer.m_pFont, LastCharGlyphIndex, pChr->m_GlyphIndex) * Scale * Size;
- LastCharGlyphIndex = pChr->m_GlyphIndex;
-
- if(TextContainer.m_Flags & TEXTFLAG_STOP_AT_END && (DrawX + CharKerning) + Advance * Size - TextContainer.m_StartX > TextContainer.m_LineWidth)
- {
- // we hit the end of the line, no more to render or count
- pCurrent = pEnd;
- break;
- }
-
- int CharBytePos = (int)((size_t)(pCurrentLast - pText));
-
- if(CharBytePos == CursorPos)
- {
- CursorQuad.Set((DrawX + CharKerning), DrawY, 2.f * Scale * Size, MaxRowHeight);
- }
-
- if(CharBytePos >= SelectionStart && CharBytePos < SelectionEnd)
- {
- SelectionQuads.push_back(IGraphics::CQuadItem((DrawX + CharKerning), DrawY, Advance * Size, MaxRowHeight));
- }
-
- float BearingX = (!ApplyBearingX ? 0.f : pChr->m_OffsetX) * Scale * Size;
- float CharWidth = pChr->m_Width * Scale * Size;
-
- if(NextCharacter == 0 && (RenderFlags & TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE) != 0)
- DrawX += BearingX + CharKerning + CharWidth;
- else
- DrawX += Advance * Size + CharKerning;
-
- TextContainer.m_GlyphCount++;
- ++CharacterCounter;
- }
- pCurrentLast = pCurrent;
- }
-
- if(NewLine)
- {
- DrawX = TextContainer.m_StartX;
- DrawY += Size;
- if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
- {
- DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
- DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
- }
- ++LineCount;
- }
- }
-
- int CharBytePos = (int)((size_t)(pCurrentLast - pText));
- if(CharBytePos == CursorPos)
- {
- CursorQuad.Set(DrawX, DrawY, 2.f * Scale * Size, MaxRowHeight);
- }
-
- Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
- Graphics()->QuadContainerAddQuads(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, &CursorQuad, 1);
- Graphics()->SetColor(0.f, 0.f, 1.f, 0.8f);
- if(SelectionQuads.size() > 0)
- Graphics()->QuadContainerAddQuads(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, &SelectionQuads[0], (int)SelectionQuads.size());
- Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
- }
-
virtual void DeleteTextContainer(int TextContainerIndex)
{
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
@@ -1714,9 +1429,9 @@ public:
{
if(TextContainer.m_StringInfo.m_QuadBufferContainerIndex != -1)
Graphics()->DeleteBufferContainer(TextContainer.m_StringInfo.m_QuadBufferContainerIndex, true);
- if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
- Graphics()->DeleteQuadContainer(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex);
}
+ if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
+ Graphics()->DeleteQuadContainer(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex);
FreeTextContainer(TextContainerIndex);
}
@@ -1741,15 +1456,17 @@ public:
if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
{
- Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
- Graphics()->RenderQuadContainer(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, 1, -1);
+ Graphics()->TextureClear();
+ Graphics()->SetColor(m_SelectionColor);
+ Graphics()->RenderQuadContainerEx(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, 0, -1, 0, 0);
+ Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
- static int64_t s_CursorRenderTime = time_get_microseconds();
+ /*static int64_t s_CursorRenderTime = time_get_microseconds();
if((time_get_microseconds() - s_CursorRenderTime) > 500000)
Graphics()->RenderQuadContainer(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, 0, 1);
if((time_get_microseconds() - s_CursorRenderTime) > 1000000)
- s_CursorRenderTime = time_get_microseconds();
+ s_CursorRenderTime = time_get_microseconds();*/
}
if(Graphics()->IsTextBufferingEnabled())
@@ -1981,11 +1698,45 @@ public:
return WidthOfText;
}
+ virtual bool SelectionToUTF8OffSets(const char *pText, int SelStart, int SelEnd, int &OffUTF8Start, int &OffUTF8End)
+ {
+ const char *pIt = pText;
+
+ OffUTF8Start = -1;
+ OffUTF8End = -1;
+
+ int CharCount = 0;
+ while(*pIt)
+ {
+ const char *pTmp = pIt;
+ int Character = str_utf8_decode(&pTmp);
+ if(Character == -1)
+ return false;
+
+ if(CharCount == SelStart)
+ OffUTF8Start = (int)((std::intptr_t)(pIt - pText));
+
+ if(CharCount == SelEnd)
+ OffUTF8End = (int)((std::intptr_t)(pIt - pText));
+
+ pIt = pTmp;
+ ++CharCount;
+ }
+
+ if(CharCount == SelStart)
+ OffUTF8Start = (int)((std::intptr_t)(pIt - pText));
+
+ if(CharCount == SelEnd)
+ OffUTF8End = (int)((std::intptr_t)(pIt - pText));
+
+ return OffUTF8Start != -1 && OffUTF8End != -1;
+ }
+
virtual void OnWindowResize()
{
bool FoundTextContainer = false;
- for(auto &TextContainer : m_TextContainers)
- if(TextContainer.m_StringInfo.m_QuadBufferContainerIndex != -1)
+ for(auto *pTextContainer : m_TextContainers)
+ if(pTextContainer->m_StringInfo.m_QuadBufferContainerIndex != -1)
FoundTextContainer = true;
if(FoundTextContainer)
{
diff --git a/src/engine/input.h b/src/engine/input.h
index 36f971195..eccf30613 100644
--- a/src/engine/input.h
+++ b/src/engine/input.h
@@ -68,6 +68,8 @@ public:
virtual void Clear() = 0;
//
+ virtual void NativeMousePos(int *mx, int *my) const = 0;
+ virtual bool NativeMousePressed(int index) = 0;
virtual void MouseModeRelative() = 0;
virtual void MouseModeAbsolute() = 0;
virtual int MouseDoubleClick() = 0;
diff --git a/src/engine/textrender.h b/src/engine/textrender.h
index 059f0fcae..79eed9683 100644
--- a/src/engine/textrender.h
+++ b/src/engine/textrender.h
@@ -35,6 +35,16 @@ enum
class CFont;
+enum ETextCursorSelectionMode
+{
+ // ignore any kind of selection
+ TEXT_CURSOR_SELECTION_MODE_NONE = 0,
+ // calculates the selection based on the mouse press and release cursor position
+ TEXT_CURSOR_SELECTION_MODE_CALCULATE,
+ // sets the selection based on the character start and end count(these values have to be decoded character offsets)
+ TEXT_CURSOR_SELECTION_MODE_SET,
+};
+
class CTextCursor
{
public:
@@ -55,6 +65,19 @@ public:
CFont *m_pFont;
float m_FontSize;
float m_AlignedFontSize;
+
+ ETextCursorSelectionMode m_CalculateSelectionMode;
+
+ // these coordinates are repsected if selection mode is set to calculate @see ETextCursorSelectionMode
+ int m_PressMouseX;
+ int m_PressMouseY;
+ int m_ReleaseMouseX;
+ int m_ReleaseMouseY;
+
+ // note m_SelectionStart can be bigger than m_SelectionEnd, depending on how the mouse cursor was dragged
+ // also note, that these are the character offsets decoded
+ int m_SelectionStart;
+ int m_SelectionEnd;
};
struct STextRenderColor
@@ -111,6 +134,7 @@ public:
ColorRGBA DefaultTextColor() { return ColorRGBA(1, 1, 1, 1); }
ColorRGBA DefaultTextOutlineColor() { return ColorRGBA(0, 0, 0, 0.3f); }
+ ColorRGBA DefaultSelectionColor() { return ColorRGBA(0, 0, 1.0f, 1.0f); }
//
virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length) = 0;
@@ -119,7 +143,6 @@ public:
// just deletes and creates text container
virtual void RecreateTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) = 0;
virtual void RecreateTextContainerSoft(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) = 0;
- virtual void SetTextContainerSelection(int TextContainerIndex, const char *pText, int CursorPos, int SelectionStart, int SelectionEnd) = 0;
virtual void DeleteTextContainer(int TextContainerIndex) = 0;
virtual void UploadTextContainer(int TextContainerIndex) = 0;
@@ -131,17 +154,22 @@ public:
virtual int AdjustFontSize(const char *pText, int TextLength, int MaxSize, int MaxWidth) = 0;
virtual int CalculateTextWidth(const char *pText, int TextLength, int FontWidth, int FontHeight) = 0;
+ virtual bool SelectionToUTF8OffSets(const char *pText, int SelStart, int SelEnd, int &OffUTF8Start, int &OffUTF8End) = 0;
+
// old foolish interface
virtual void TextColor(float r, float g, float b, float a) = 0;
virtual void TextColor(ColorRGBA rgb) = 0;
virtual void TextOutlineColor(float r, float g, float b, float a) = 0;
virtual void TextOutlineColor(ColorRGBA rgb) = 0;
+ virtual void TextSelectionColor(float r, float g, float b, float a) = 0;
+ virtual void TextSelectionColor(ColorRGBA rgb) = 0;
virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, float LineWidth) = 0;
virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int StrLength, float LineWidth, float *pAlignedHeight = NULL, float *pMaxCharacterHeightInLine = NULL) = 0;
virtual int TextLineCount(void *pFontSetV, float Size, const char *pText, float LineWidth) = 0;
virtual ColorRGBA GetTextColor() = 0;
virtual ColorRGBA GetTextOutlineColor() = 0;
+ virtual ColorRGBA GetTextSelectionColor() = 0;
virtual void OnWindowResize() = 0;
diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp
index eb8d50fa2..a6fe76e71 100644
--- a/src/game/client/components/console.cpp
+++ b/src/game/client/components/console.cpp
@@ -33,6 +33,8 @@
#include
+#include
+
#include "console.h"
CGameConsole::CInstance::CInstance(int Type)
@@ -277,9 +279,11 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
else if(Event.m_Key == KEY_PAGEUP)
{
++m_BacklogActPage;
+ m_pGameConsole->m_HasSelection = false;
}
else if(Event.m_Key == KEY_PAGEDOWN)
{
+ m_pGameConsole->m_HasSelection = false;
--m_BacklogActPage;
if(m_BacklogActPage < 0)
m_BacklogActPage = 0;
@@ -289,10 +293,12 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
else if(Event.m_Key == KEY_HOME && m_Input.GetString()[0] == '\0')
{
m_BacklogActPage = INT_MAX;
+ m_pGameConsole->m_HasSelection = false;
}
else if(Event.m_Key == KEY_END && m_Input.GetString()[0] == '\0')
{
m_BacklogActPage = 0;
+ m_pGameConsole->m_HasSelection = false;
}
else if(Event.m_Key == KEY_LSHIFT)
{
@@ -355,6 +361,8 @@ void CGameConsole::CInstance::PrintLine(const char *pLine, ColorRGBA PrintColor)
pEntry->m_PrintColor = PrintColor;
mem_copy(pEntry->m_aText, pLine, Len);
pEntry->m_aText[Len] = 0;
+ if(m_pGameConsole->m_ConsoleType == m_Type)
+ m_pGameConsole->m_NewLineCounter++;
}
CGameConsole::CGameConsole() :
@@ -650,6 +658,11 @@ void CGameConsole::OnRender()
float OffsetY = 0.0f;
float LineOffset = 1.0f;
+ bool WantsSelectionCopy = false;
+ if(Input()->KeyIsPressed(KEY_LCTRL) && Input()->KeyPress(KEY_C))
+ WantsSelectionCopy = true;
+ std::string SelectionString;
+
for(int Page = 0; Page <= pConsole->m_BacklogActPage; ++Page, OffsetY = 0.0f)
{
while(pEntry)
@@ -662,7 +675,7 @@ void CGameConsole::OnRender()
TextRender()->SetCursor(&Cursor, 0.0f, 0.0f, FontSize, 0);
Cursor.m_LineWidth = Screen.w - 10;
TextRender()->TextEx(&Cursor, pEntry->m_aText, -1);
- pEntry->m_YOffset = Cursor.m_Y + Cursor.m_FontSize + LineOffset;
+ pEntry->m_YOffset = Cursor.m_Y + Cursor.m_AlignedFontSize + LineOffset;
}
OffsetY += pEntry->m_YOffset;
@@ -670,17 +683,74 @@ void CGameConsole::OnRender()
if(y - OffsetY <= RowHeight)
break;
+ if(!m_MouseIsPress && Input()->NativeMousePressed(1))
+ {
+ m_MouseIsPress = true;
+ Input()->NativeMousePos(&m_MousePressX, &m_MousePressY);
+ m_MousePressX = (m_MousePressX / (float)Graphics()->ScreenWidth()) * Screen.w;
+ m_MousePressY = (m_MousePressY / (float)Graphics()->ScreenHeight()) * Screen.h;
+ }
+ if(m_MouseIsPress)
+ {
+ Input()->NativeMousePos(&m_MouseCurX, &m_MouseCurY);
+ m_MouseCurX = (m_MouseCurX / (float)Graphics()->ScreenWidth()) * Screen.w;
+ m_MouseCurY = (m_MouseCurY / (float)Graphics()->ScreenHeight()) * Screen.h;
+ }
+ if(m_MouseIsPress && !Input()->NativeMousePressed(1))
+ {
+ m_MouseIsPress = false;
+ }
+
// just render output from actual backlog page (render bottom up)
if(Page == pConsole->m_BacklogActPage)
{
+ if(m_NewLineCounter > 0 && (m_HasSelection || m_MouseIsPress))
+ {
+ m_MousePressY -= OffsetY;
+ if(!m_MouseIsPress)
+ m_MouseCurY -= OffsetY;
+ }
TextRender()->SetCursor(&Cursor, 0.0f, y - OffsetY, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = Screen.w - 10.0f;
+ Cursor.m_CalculateSelectionMode = (m_MouseIsPress || (m_CurSelStart != m_CurSelEnd) || m_HasSelection) ? TEXT_CURSOR_SELECTION_MODE_CALCULATE : TEXT_CURSOR_SELECTION_MODE_NONE;
+ Cursor.m_PressMouseX = m_MousePressX;
+ Cursor.m_PressMouseY = m_MousePressY;
+ Cursor.m_ReleaseMouseX = m_MouseCurX;
+ Cursor.m_ReleaseMouseY = m_MouseCurY;
TextRender()->TextEx(&Cursor, pEntry->m_aText, -1);
+ if(Cursor.m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE)
+ {
+ m_CurSelStart = minimum(Cursor.m_SelectionStart, Cursor.m_SelectionEnd);
+ m_CurSelEnd = maximum(Cursor.m_SelectionStart, Cursor.m_SelectionEnd);
+ }
+ if(m_CurSelStart != m_CurSelEnd)
+ {
+ if(WantsSelectionCopy)
+ {
+ bool HasNewLine = false;
+ if(!SelectionString.empty())
+ HasNewLine = true;
+ int OffUTF8Start = 0;
+ int OffUTF8End = 0;
+ if(TextRender()->SelectionToUTF8OffSets(pEntry->m_aText, m_CurSelStart, m_CurSelEnd, OffUTF8Start, OffUTF8End))
+ {
+ SelectionString.insert(0, (std::string(&pEntry->m_aText[OffUTF8Start], OffUTF8End - OffUTF8Start) + (HasNewLine ? "\n" : "")));
+ }
+ }
+ m_HasSelection = true;
+ }
}
pEntry = pConsole->m_Backlog.Prev(pEntry);
// reset color
TextRender()->TextColor(1, 1, 1, 1);
+ if(m_NewLineCounter > 0)
+ --m_NewLineCounter;
+ }
+
+ if(WantsSelectionCopy && !SelectionString.empty())
+ {
+ Input()->SetClipboardText(SelectionString.c_str());
}
// actual backlog page number is too high, render last available page (current checked one, render top down)
@@ -773,7 +843,8 @@ void CGameConsole::Toggle(int Type)
Input()->SetIMEState(false);
}
}
-
+ if(m_ConsoleType != Type)
+ m_HasSelection = false;
m_ConsoleType = Type;
}
diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h
index 7f332b4d1..808fa5c01 100644
--- a/src/game/client/components/console.h
+++ b/src/game/client/components/console.h
@@ -84,6 +84,16 @@ class CGameConsole : public CComponent
float m_StateChangeEnd;
float m_StateChangeDuration;
+ bool m_MouseIsPress = false;
+ int m_MousePressX = 0;
+ int m_MousePressY = 0;
+ int m_MouseCurX = 0;
+ int m_MouseCurY = 0;
+ int m_CurSelStart = 0;
+ int m_CurSelEnd = 0;
+ bool m_HasSelection = false;
+ int m_NewLineCounter = 0;
+
void Toggle(int Type);
void Dump(int Type);