2021-05-26 13:01:50 +00:00
|
|
|
|
#include "ui_ex.h"
|
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
#include <base/system.h>
|
|
|
|
|
|
|
|
|
|
#include <base/math.h>
|
|
|
|
|
#include <engine/textrender.h>
|
|
|
|
|
|
2021-05-26 13:01:50 +00:00
|
|
|
|
#include <engine/client/input.h>
|
|
|
|
|
#include <engine/keys.h>
|
|
|
|
|
|
|
|
|
|
#include <game/client/lineinput.h>
|
|
|
|
|
#include <game/client/render.h>
|
|
|
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
2021-11-29 14:43:16 +00:00
|
|
|
|
CUIEx::CUIEx()
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-11-26 20:55:31 +00:00
|
|
|
|
m_MouseSlow = false;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-31 09:43:02 +00:00
|
|
|
|
void CUIEx::Init(CUI *pUI, IKernel *pKernel, CRenderTools *pRenderTools, IInput::CEvent *pInputEventsArray, int *pInputEventCount)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
m_pUI = pUI;
|
|
|
|
|
m_pKernel = pKernel;
|
|
|
|
|
m_pRenderTools = pRenderTools;
|
2021-05-31 09:43:02 +00:00
|
|
|
|
m_pInputEventsArray = pInputEventsArray;
|
|
|
|
|
m_pInputEventCount = pInputEventCount;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
|
|
|
|
m_pInput = Kernel()->RequestInterface<IInput>();
|
|
|
|
|
m_pGraphics = Kernel()->RequestInterface<IGraphics>();
|
|
|
|
|
m_pTextRender = Kernel()->RequestInterface<ITextRender>();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 20:55:31 +00:00
|
|
|
|
void CUIEx::ConvertMouseMove(float *pX, float *pY) const
|
|
|
|
|
{
|
|
|
|
|
UI()->ConvertMouseMove(pX, pY);
|
|
|
|
|
|
|
|
|
|
if(m_MouseSlow)
|
|
|
|
|
{
|
|
|
|
|
const float SlowMouseFactor = 0.05f;
|
|
|
|
|
*pX *= SlowMouseFactor;
|
|
|
|
|
*pY *= SlowMouseFactor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float CUIEx::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current)
|
|
|
|
|
{
|
2021-12-09 21:57:21 +00:00
|
|
|
|
Current = clamp(Current, 0.0f, 1.0f);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
2021-12-09 21:57:21 +00:00
|
|
|
|
// layout
|
2021-11-26 20:55:31 +00:00
|
|
|
|
CUIRect Rail;
|
|
|
|
|
pRect->Margin(5.0f, &Rail);
|
|
|
|
|
|
|
|
|
|
CUIRect Handle;
|
2021-12-13 16:48:12 +00:00
|
|
|
|
Rail.HSplitTop(clamp(33.0f, Rail.w, Rail.h / 3.0f), &Handle, 0);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
Handle.y = Rail.y + (Rail.h - Handle.h) * Current;
|
|
|
|
|
|
|
|
|
|
// logic
|
2021-12-09 21:57:21 +00:00
|
|
|
|
static float s_OffsetY;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
const bool InsideRail = UI()->MouseInside(&Rail);
|
|
|
|
|
const bool InsideHandle = UI()->MouseInside(&Handle);
|
|
|
|
|
bool Grabbed = false; // whether to apply the offset
|
|
|
|
|
|
|
|
|
|
if(UI()->ActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(UI()->MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
if(Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT))
|
|
|
|
|
m_MouseSlow = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UI()->SetActiveItem(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(UI()->HotItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(UI()->MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
UI()->SetActiveItem(pID);
|
|
|
|
|
s_OffsetY = UI()->MouseY() - Handle.y;
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(UI()->MouseButtonClicked(0) && !InsideHandle && InsideRail)
|
|
|
|
|
{
|
|
|
|
|
UI()->SetActiveItem(pID);
|
2021-12-09 21:57:21 +00:00
|
|
|
|
s_OffsetY = Handle.h / 2.0f;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InsideHandle)
|
2021-12-09 21:57:21 +00:00
|
|
|
|
{
|
2021-11-26 20:55:31 +00:00
|
|
|
|
UI()->SetHotItem(pID);
|
2021-12-09 21:57:21 +00:00
|
|
|
|
}
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
|
|
|
|
float ReturnValue = Current;
|
|
|
|
|
if(Grabbed)
|
|
|
|
|
{
|
|
|
|
|
const float Min = Rail.y;
|
|
|
|
|
const float Max = Rail.h - Handle.h;
|
|
|
|
|
const float Cur = UI()->MouseY() - s_OffsetY;
|
|
|
|
|
ReturnValue = clamp((Cur - Min) / Max, 0.0f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// render
|
2021-12-09 21:57:21 +00:00
|
|
|
|
RenderTools()->DrawUIRect(&Rail, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), CUI::CORNER_ALL, Rail.w / 2.0f);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
|
|
|
|
float ColorSlider;
|
|
|
|
|
if(UI()->ActiveItem() == pID)
|
|
|
|
|
ColorSlider = 0.9f;
|
2021-12-09 21:57:21 +00:00
|
|
|
|
else if(UI()->HotItem() == pID)
|
|
|
|
|
ColorSlider = 1.0f;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
else
|
2021-12-09 21:57:21 +00:00
|
|
|
|
ColorSlider = 0.8f;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
2021-12-09 21:57:21 +00:00
|
|
|
|
RenderTools()->DrawUIRect(&Handle, ColorRGBA(ColorSlider, ColorSlider, ColorSlider, 1.0f), CUI::CORNER_ALL, Handle.w / 2.0f);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float CUIEx::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current, const ColorRGBA *pColorInner)
|
|
|
|
|
{
|
2021-12-09 21:57:21 +00:00
|
|
|
|
Current = clamp(Current, 0.0f, 1.0f);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
2021-12-09 21:57:21 +00:00
|
|
|
|
// layout
|
2021-11-26 20:55:31 +00:00
|
|
|
|
CUIRect Rail;
|
|
|
|
|
if(pColorInner)
|
|
|
|
|
Rail = *pRect;
|
|
|
|
|
else
|
|
|
|
|
pRect->HMargin(5.0f, &Rail);
|
|
|
|
|
|
|
|
|
|
CUIRect Handle;
|
2021-12-13 16:48:12 +00:00
|
|
|
|
Rail.VSplitLeft(pColorInner ? 8.0f : clamp(33.0f, Rail.h, Rail.w / 3.0f), &Handle, 0);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
Handle.x += (Rail.w - Handle.w) * Current;
|
|
|
|
|
|
|
|
|
|
// logic
|
2021-12-09 21:57:21 +00:00
|
|
|
|
static float s_OffsetX;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
const bool InsideRail = UI()->MouseInside(&Rail);
|
|
|
|
|
const bool InsideHandle = UI()->MouseInside(&Handle);
|
|
|
|
|
bool Grabbed = false; // whether to apply the offset
|
|
|
|
|
|
|
|
|
|
if(UI()->ActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(UI()->MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
if(Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT))
|
|
|
|
|
m_MouseSlow = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UI()->SetActiveItem(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(UI()->HotItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(UI()->MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
UI()->SetActiveItem(pID);
|
|
|
|
|
s_OffsetX = UI()->MouseX() - Handle.x;
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(UI()->MouseButtonClicked(0) && !InsideHandle && InsideRail)
|
|
|
|
|
{
|
|
|
|
|
UI()->SetActiveItem(pID);
|
2021-12-09 21:57:21 +00:00
|
|
|
|
s_OffsetX = Handle.w / 2.0f;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InsideHandle)
|
2021-12-09 21:57:21 +00:00
|
|
|
|
{
|
2021-11-26 20:55:31 +00:00
|
|
|
|
UI()->SetHotItem(pID);
|
2021-12-09 21:57:21 +00:00
|
|
|
|
}
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
|
|
|
|
float ReturnValue = Current;
|
|
|
|
|
if(Grabbed)
|
|
|
|
|
{
|
|
|
|
|
const float Min = Rail.x;
|
|
|
|
|
const float Max = Rail.w - Handle.w;
|
|
|
|
|
const float Cur = UI()->MouseX() - s_OffsetX;
|
|
|
|
|
ReturnValue = clamp((Cur - Min) / Max, 0.0f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// render
|
|
|
|
|
if(pColorInner)
|
|
|
|
|
{
|
|
|
|
|
CUIRect Slider;
|
|
|
|
|
Handle.VMargin(-2.0f, &Slider);
|
|
|
|
|
Slider.HMargin(-3.0f, &Slider);
|
|
|
|
|
RenderTools()->DrawUIRect(&Slider, ColorRGBA(0.15f, 0.15f, 0.15f, 1.0f), CUI::CORNER_ALL, 5.0f);
|
|
|
|
|
Slider.Margin(2.0f, &Slider);
|
|
|
|
|
RenderTools()->DrawUIRect(&Slider, *pColorInner, CUI::CORNER_ALL, 3.0f);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-12-09 21:57:21 +00:00
|
|
|
|
RenderTools()->DrawUIRect(&Rail, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), CUI::CORNER_ALL, Rail.h / 2.0f);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
|
|
|
|
float ColorSlider;
|
|
|
|
|
if(UI()->ActiveItem() == pID)
|
|
|
|
|
ColorSlider = 0.9f;
|
2021-12-09 21:57:21 +00:00
|
|
|
|
else if(UI()->HotItem() == pID)
|
|
|
|
|
ColorSlider = 1.0f;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
else
|
2021-12-09 21:57:21 +00:00
|
|
|
|
ColorSlider = 0.8f;
|
2021-11-26 20:55:31 +00:00
|
|
|
|
|
2021-12-09 21:57:21 +00:00
|
|
|
|
RenderTools()->DrawUIRect(&Handle, ColorRGBA(ColorSlider, ColorSlider, ColorSlider, 1.0f), CUI::CORNER_ALL, Handle.h / 2.0f);
|
2021-11-26 20:55:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 18:46:31 +00:00
|
|
|
|
bool CUIEx::DoEditBox(const void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden, int Corners, const char *pEmptyText)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
int Inside = UI()->MouseInside(pRect);
|
|
|
|
|
bool ReturnValue = false;
|
|
|
|
|
bool UpdateOffset = false;
|
|
|
|
|
|
|
|
|
|
FontSize *= UI()->Scale();
|
|
|
|
|
|
2021-11-02 23:08:00 +00:00
|
|
|
|
auto &&SetHasSelection = [&](bool HasSelection) {
|
|
|
|
|
m_HasSelection = HasSelection;
|
|
|
|
|
m_pSelItem = m_HasSelection ? pID : nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-26 13:01:50 +00:00
|
|
|
|
if(UI()->LastActiveItem() == pID)
|
|
|
|
|
{
|
2021-11-02 23:08:00 +00:00
|
|
|
|
if(m_HasSelection && m_pSelItem != pID)
|
|
|
|
|
{
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
m_CurCursor = minimum(str_length(pStr), m_CurCursor);
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
bool IsShiftPressed = Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT);
|
2021-12-18 11:23:20 +00:00
|
|
|
|
bool IsModPressed = Input()->ModifierIsPressed();
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
2021-12-18 11:23:20 +00:00
|
|
|
|
if(!IsShiftPressed && IsModPressed && Input()->KeyPress(KEY_V))
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
const char *pText = Input()->GetClipboardText();
|
|
|
|
|
if(pText)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-10-19 11:49:31 +00:00
|
|
|
|
int OffsetL = clamp(m_CurCursor, 0, str_length(pStr));
|
2021-09-16 14:50:17 +00:00
|
|
|
|
int OffsetR = OffsetL;
|
|
|
|
|
|
|
|
|
|
if(m_HasSelection)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
int SelLeft = minimum(m_CurSelStart, m_CurSelEnd);
|
|
|
|
|
int SelRight = maximum(m_CurSelStart, m_CurSelEnd);
|
|
|
|
|
int UTF8SelLeft = -1;
|
|
|
|
|
int UTF8SelRight = -1;
|
|
|
|
|
if(TextRender()->SelectionToUTF8OffSets(pStr, SelLeft, SelRight, UTF8SelLeft, UTF8SelRight))
|
|
|
|
|
{
|
|
|
|
|
OffsetL = UTF8SelLeft;
|
|
|
|
|
OffsetR = UTF8SelRight;
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(false);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string NewStr(pStr, OffsetL);
|
|
|
|
|
|
|
|
|
|
int WrittenChars = 0;
|
|
|
|
|
|
|
|
|
|
const char *pIt = pText;
|
|
|
|
|
while(*pIt)
|
|
|
|
|
{
|
|
|
|
|
const char *pTmp = pIt;
|
|
|
|
|
int Character = str_utf8_decode(&pTmp);
|
|
|
|
|
if(Character == -1 || Character == 0)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
break;
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
|
|
|
|
if(Character == '\r' || Character == '\n')
|
|
|
|
|
{
|
|
|
|
|
NewStr.append(1, ' ');
|
|
|
|
|
++WrittenChars;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NewStr.append(pIt, (std::intptr_t)(pTmp - pIt));
|
|
|
|
|
WrittenChars += (int)(std::intptr_t)(pTmp - pIt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pIt = pTmp;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
|
|
|
|
NewStr.append(pStr + OffsetR);
|
|
|
|
|
|
|
|
|
|
str_copy(pStr, NewStr.c_str(), StrSize);
|
|
|
|
|
|
|
|
|
|
m_CurCursor = OffsetL + WrittenChars;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
ReturnValue = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 11:23:20 +00:00
|
|
|
|
if(!IsShiftPressed && IsModPressed && (Input()->KeyPress(KEY_C) || Input()->KeyPress(KEY_X)))
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(m_HasSelection)
|
|
|
|
|
{
|
|
|
|
|
int SelLeft = minimum(m_CurSelStart, m_CurSelEnd);
|
|
|
|
|
int SelRight = maximum(m_CurSelStart, m_CurSelEnd);
|
|
|
|
|
int UTF8SelLeft = -1;
|
|
|
|
|
int UTF8SelRight = -1;
|
|
|
|
|
if(TextRender()->SelectionToUTF8OffSets(pStr, SelLeft, SelRight, UTF8SelLeft, UTF8SelRight))
|
|
|
|
|
{
|
|
|
|
|
std::string NewStr(&pStr[UTF8SelLeft], UTF8SelRight - UTF8SelLeft);
|
|
|
|
|
Input()->SetClipboardText(NewStr.c_str());
|
|
|
|
|
if(Input()->KeyPress(KEY_X))
|
|
|
|
|
{
|
|
|
|
|
NewStr = std::string(pStr, UTF8SelLeft) + std::string(pStr + UTF8SelRight);
|
|
|
|
|
str_copy(pStr, NewStr.c_str(), StrSize);
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(false);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(m_CurCursor > UTF8SelLeft)
|
|
|
|
|
m_CurCursor = maximum(0, m_CurCursor - (UTF8SelRight - UTF8SelLeft));
|
|
|
|
|
else
|
|
|
|
|
m_CurCursor = UTF8SelLeft;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Input()->SetClipboardText(pStr);
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 11:23:20 +00:00
|
|
|
|
if(!IsShiftPressed && IsModPressed && Input()->KeyPress(KEY_A))
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
m_CurSelStart = 0;
|
|
|
|
|
int StrLen = str_length(pStr);
|
|
|
|
|
TextRender()->UTF8OffToDecodedOff(pStr, StrLen, m_CurSelEnd);
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(true);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
m_CurCursor = StrLen;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 11:23:20 +00:00
|
|
|
|
if(!IsShiftPressed && IsModPressed && Input()->KeyPress(KEY_U))
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
pStr[0] = '\0';
|
2021-09-16 14:50:17 +00:00
|
|
|
|
m_CurCursor = 0;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
ReturnValue = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
for(int i = 0; i < *m_pInputEventCount; i++)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
int LastCursor = m_CurCursor;
|
2021-12-03 16:46:00 +00:00
|
|
|
|
int Len, NumChars;
|
|
|
|
|
str_utf8_stats(pStr, StrSize, StrSize, &Len, &NumChars);
|
2021-12-18 11:23:20 +00:00
|
|
|
|
int32_t ManipulateChanges = CLineInput::Manipulate(m_pInputEventsArray[i], pStr, StrSize, StrSize, &Len, &m_CurCursor, &NumChars, m_HasSelection ? CLineInput::LINE_INPUT_MODIFY_DONT_DELETE : 0, IsModPressed ? KEY_LCTRL : 0);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
ReturnValue |= (ManipulateChanges & (CLineInput::LINE_INPUT_CHANGE_STRING | CLineInput::LINE_INPUT_CHANGE_CHARACTERS_DELETE)) != 0;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
// if cursor changed, reset selection
|
|
|
|
|
if(ManipulateChanges != 0)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(m_HasSelection && (ManipulateChanges & (CLineInput::LINE_INPUT_CHANGE_STRING | CLineInput::LINE_INPUT_CHANGE_CHARACTERS_DELETE)) != 0)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
int OffsetL = 0;
|
|
|
|
|
int OffsetR = 0;
|
|
|
|
|
|
|
|
|
|
bool IsReverseSel = m_CurSelStart > m_CurSelEnd;
|
|
|
|
|
|
|
|
|
|
int ExtraNew = 0;
|
|
|
|
|
int ExtraOld = 0;
|
|
|
|
|
// selection correction from added chars
|
|
|
|
|
if(IsReverseSel)
|
|
|
|
|
{
|
|
|
|
|
TextRender()->UTF8OffToDecodedOff(pStr, m_CurCursor, ExtraNew);
|
|
|
|
|
TextRender()->UTF8OffToDecodedOff(pStr, LastCursor, ExtraOld);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SelLeft = minimum(m_CurSelStart, m_CurSelEnd);
|
|
|
|
|
int SelRight = maximum(m_CurSelStart, m_CurSelEnd);
|
|
|
|
|
int UTF8SelLeft = -1;
|
|
|
|
|
int UTF8SelRight = -1;
|
|
|
|
|
if(TextRender()->SelectionToUTF8OffSets(pStr, SelLeft + (ExtraNew - ExtraOld), SelRight + (ExtraNew - ExtraOld), UTF8SelLeft, UTF8SelRight))
|
|
|
|
|
{
|
|
|
|
|
OffsetL = UTF8SelLeft;
|
|
|
|
|
OffsetR = UTF8SelRight;
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(false);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string NewStr(pStr, OffsetL);
|
|
|
|
|
|
|
|
|
|
NewStr.append(pStr + OffsetR);
|
|
|
|
|
|
|
|
|
|
str_copy(pStr, NewStr.c_str(), StrSize);
|
|
|
|
|
|
|
|
|
|
if(!IsReverseSel)
|
|
|
|
|
m_CurCursor = clamp<int>(m_CurCursor - (UTF8SelRight - UTF8SelLeft), 0, NewStr.length());
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(IsShiftPressed && (ManipulateChanges & CLineInput::LINE_INPUT_CHANGE_STRING) == 0)
|
|
|
|
|
{
|
|
|
|
|
int CursorPosDecoded = -1;
|
|
|
|
|
int LastCursorPosDecoded = -1;
|
|
|
|
|
|
|
|
|
|
if(!m_HasSelection)
|
|
|
|
|
{
|
|
|
|
|
m_CurSelStart = -1;
|
|
|
|
|
m_CurSelEnd = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(TextRender()->UTF8OffToDecodedOff(pStr, m_CurCursor, CursorPosDecoded))
|
|
|
|
|
{
|
|
|
|
|
if(TextRender()->UTF8OffToDecodedOff(pStr, LastCursor, LastCursorPosDecoded))
|
|
|
|
|
{
|
|
|
|
|
if(!m_HasSelection)
|
|
|
|
|
{
|
|
|
|
|
m_CurSelStart = LastCursorPosDecoded;
|
|
|
|
|
m_CurSelEnd = LastCursorPosDecoded;
|
|
|
|
|
}
|
|
|
|
|
m_CurSelEnd += (CursorPosDecoded - LastCursorPosDecoded);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(m_CurSelStart == m_CurSelEnd)
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(false);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
else
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(true);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(m_HasSelection && (ManipulateChanges & CLineInput::LINE_INPUT_CHANGE_CURSOR) != 0)
|
|
|
|
|
{
|
|
|
|
|
if(m_CurSelStart < m_CurSelEnd)
|
|
|
|
|
{
|
|
|
|
|
if(m_CurCursor >= LastCursor)
|
|
|
|
|
m_CurCursor = LastCursor;
|
|
|
|
|
else
|
|
|
|
|
TextRender()->DecodedOffToUTF8Off(pStr, m_CurSelStart, m_CurCursor);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(m_CurCursor <= LastCursor)
|
|
|
|
|
m_CurCursor = LastCursor;
|
|
|
|
|
else
|
|
|
|
|
TextRender()->DecodedOffToUTF8Off(pStr, m_CurSelStart, m_CurCursor);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(false);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
}
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Inside)
|
|
|
|
|
{
|
|
|
|
|
UI()->SetHotItem(pID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUIRect Textbox = *pRect;
|
|
|
|
|
RenderTools()->DrawUIRect(&Textbox, ColorRGBA(1, 1, 1, 0.5f), Corners, 3.0f);
|
|
|
|
|
Textbox.VMargin(2.0f, &Textbox);
|
|
|
|
|
Textbox.HMargin(2.0f, &Textbox);
|
|
|
|
|
|
|
|
|
|
const char *pDisplayStr = pStr;
|
|
|
|
|
char aStars[128];
|
|
|
|
|
|
|
|
|
|
if(Hidden)
|
|
|
|
|
{
|
|
|
|
|
unsigned s = str_length(pDisplayStr);
|
|
|
|
|
if(s >= sizeof(aStars))
|
|
|
|
|
s = sizeof(aStars) - 1;
|
|
|
|
|
for(unsigned int i = 0; i < s; ++i)
|
|
|
|
|
aStars[i] = '*';
|
|
|
|
|
aStars[s] = 0;
|
|
|
|
|
pDisplayStr = aStars;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char aDispEditingText[128 + IInput::INPUT_TEXT_SIZE + 2] = {0};
|
2021-09-16 14:50:17 +00:00
|
|
|
|
int DispCursorPos = m_CurCursor;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
if(UI()->LastActiveItem() == pID && Input()->GetIMEEditingTextLength() > -1)
|
|
|
|
|
{
|
|
|
|
|
int EditingTextCursor = Input()->GetEditingCursor();
|
|
|
|
|
str_copy(aDispEditingText, pDisplayStr, sizeof(aDispEditingText));
|
|
|
|
|
char aEditingText[IInput::INPUT_TEXT_SIZE + 2];
|
|
|
|
|
if(Hidden)
|
|
|
|
|
{
|
|
|
|
|
// Do not show editing text in password field
|
|
|
|
|
str_copy(aEditingText, "[*]", sizeof(aEditingText));
|
|
|
|
|
EditingTextCursor = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
str_format(aEditingText, sizeof(aEditingText), "[%s]", Input()->GetIMEEditingText());
|
|
|
|
|
}
|
|
|
|
|
int NewTextLen = str_length(aEditingText);
|
|
|
|
|
int CharsLeft = (int)sizeof(aDispEditingText) - str_length(aDispEditingText) - 1;
|
|
|
|
|
int FillCharLen = minimum(NewTextLen, CharsLeft);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
for(int i = str_length(aDispEditingText) - 1; i >= m_CurCursor; i--)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
aDispEditingText[i + FillCharLen] = aDispEditingText[i];
|
|
|
|
|
for(int i = 0; i < FillCharLen; i++)
|
2021-09-16 14:50:17 +00:00
|
|
|
|
aDispEditingText[m_CurCursor + i] = aEditingText[i];
|
|
|
|
|
DispCursorPos = m_CurCursor + EditingTextCursor + 1;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
pDisplayStr = aDispEditingText;
|
|
|
|
|
UpdateOffset = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-06 22:32:12 +00:00
|
|
|
|
bool IsEmptyText = false;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
if(pDisplayStr[0] == '\0')
|
|
|
|
|
{
|
|
|
|
|
pDisplayStr = pEmptyText;
|
2021-11-06 22:32:12 +00:00
|
|
|
|
IsEmptyText = true;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
TextRender()->TextColor(1, 1, 1, 0.75f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DispCursorPos = minimum(DispCursorPos, str_length(pDisplayStr));
|
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
bool JustGotActive = false;
|
|
|
|
|
if(UI()->ActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(!UI()->MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
UI()->SetActiveItem(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(UI()->HotItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(UI()->MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
if(UI()->LastActiveItem() != pID)
|
|
|
|
|
JustGotActive = true;
|
|
|
|
|
UI()->SetActiveItem(pID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 13:01:50 +00:00
|
|
|
|
// check if the text has to be moved
|
2021-05-31 09:43:02 +00:00
|
|
|
|
if(UI()->LastActiveItem() == pID && !JustGotActive && (UpdateOffset || *m_pInputEventCount))
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, DispCursorPos, std::numeric_limits<float>::max());
|
2021-12-03 18:46:31 +00:00
|
|
|
|
if(w - *pOffset > Textbox.w)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
// move to the left
|
|
|
|
|
float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1, std::numeric_limits<float>::max());
|
|
|
|
|
do
|
|
|
|
|
{
|
2021-12-03 18:46:31 +00:00
|
|
|
|
*pOffset += minimum(wt - *pOffset - Textbox.w, Textbox.w / 3);
|
|
|
|
|
} while(w - *pOffset > Textbox.w + 0.0001f);
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
2021-12-03 18:46:31 +00:00
|
|
|
|
else if(w - *pOffset < 0.0f)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
|
|
|
|
// move to the right
|
|
|
|
|
do
|
|
|
|
|
{
|
2021-12-03 18:46:31 +00:00
|
|
|
|
*pOffset = maximum(0.0f, *pOffset - Textbox.w / 3);
|
|
|
|
|
} while(w - *pOffset < -0.0001f);
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UI()->ClipEnable(pRect);
|
2021-12-03 18:46:31 +00:00
|
|
|
|
Textbox.x -= *pOffset;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
CTextCursor SelCursor;
|
|
|
|
|
TextRender()->SetCursor(&SelCursor, 0, 0, 16, 0);
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
bool HasMouseSel = false;
|
|
|
|
|
if(UI()->LastActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(!m_MouseIsPress && UI()->MouseButtonClicked(0))
|
|
|
|
|
{
|
|
|
|
|
m_MouseIsPress = true;
|
|
|
|
|
m_MousePressX = UI()->MouseX();
|
|
|
|
|
m_MousePressY = UI()->MouseY();
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(m_MouseIsPress)
|
|
|
|
|
{
|
|
|
|
|
m_MouseCurX = UI()->MouseX();
|
|
|
|
|
m_MouseCurY = UI()->MouseY();
|
|
|
|
|
}
|
2021-11-06 22:32:12 +00:00
|
|
|
|
HasMouseSel = m_MouseIsPress && !IsEmptyText;
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(m_MouseIsPress && UI()->MouseButtonReleased(0))
|
|
|
|
|
{
|
|
|
|
|
m_MouseIsPress = false;
|
|
|
|
|
}
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(UI()->LastActiveItem() == pID)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
int CursorPos = -1;
|
|
|
|
|
TextRender()->UTF8OffToDecodedOff(pDisplayStr, DispCursorPos, CursorPos);
|
|
|
|
|
|
|
|
|
|
SelCursor.m_CursorMode = HasMouseSel ? TEXT_CURSOR_CURSOR_MODE_CALCULATE : TEXT_CURSOR_CURSOR_MODE_SET;
|
|
|
|
|
SelCursor.m_CursorCharacter = CursorPos;
|
|
|
|
|
SelCursor.m_CalculateSelectionMode = HasMouseSel ? TEXT_CURSOR_SELECTION_MODE_CALCULATE : (m_HasSelection ? TEXT_CURSOR_SELECTION_MODE_SET : TEXT_CURSOR_SELECTION_MODE_NONE);
|
|
|
|
|
SelCursor.m_PressMouseX = m_MousePressX;
|
|
|
|
|
SelCursor.m_PressMouseY = m_MousePressY;
|
|
|
|
|
SelCursor.m_ReleaseMouseX = m_MouseCurX;
|
|
|
|
|
SelCursor.m_ReleaseMouseY = m_MouseCurY;
|
|
|
|
|
SelCursor.m_SelectionStart = m_CurSelStart;
|
|
|
|
|
SelCursor.m_SelectionEnd = m_CurSelEnd;
|
|
|
|
|
}
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2022-01-21 15:20:15 +00:00
|
|
|
|
UI()->DoLabel(&Textbox, pDisplayStr, FontSize, TEXTALIGN_LEFT, -1, 1, &SelCursor);
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
|
|
|
|
if(UI()->LastActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(SelCursor.m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE)
|
2021-05-26 13:01:50 +00:00
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
|
m_CurSelStart = SelCursor.m_SelectionStart;
|
|
|
|
|
m_CurSelEnd = SelCursor.m_SelectionEnd;
|
2021-11-02 23:08:00 +00:00
|
|
|
|
SetHasSelection(m_CurSelStart != m_CurSelEnd);
|
2021-05-26 13:01:50 +00:00
|
|
|
|
}
|
2021-09-16 14:50:17 +00:00
|
|
|
|
if(SelCursor.m_CursorMode == TEXT_CURSOR_CURSOR_MODE_CALCULATE)
|
|
|
|
|
{
|
|
|
|
|
TextRender()->DecodedOffToUTF8Off(pDisplayStr, SelCursor.m_CursorCharacter, DispCursorPos);
|
|
|
|
|
m_CurCursor = DispCursorPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-26 13:01:50 +00:00
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
|
TextRender()->TextColor(1, 1, 1, 1);
|
|
|
|
|
|
|
|
|
|
// set the ime cursor
|
|
|
|
|
if(UI()->LastActiveItem() == pID && !JustGotActive)
|
|
|
|
|
{
|
|
|
|
|
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, DispCursorPos, std::numeric_limits<float>::max());
|
|
|
|
|
Textbox.x += w;
|
2021-05-26 13:01:50 +00:00
|
|
|
|
Input()->SetEditingPosition(Textbox.x, Textbox.y + FontSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UI()->ClipDisable();
|
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
2021-12-03 18:46:31 +00:00
|
|
|
|
|
|
|
|
|
bool CUIEx::DoClearableEditBox(const void *pID, const void *pClearID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden, int Corners, const char *pEmptyText)
|
|
|
|
|
{
|
|
|
|
|
CUIRect EditBox;
|
|
|
|
|
CUIRect ClearButton;
|
|
|
|
|
pRect->VSplitRight(15.0f, &EditBox, &ClearButton);
|
|
|
|
|
bool ReturnValue = DoEditBox(pID, &EditBox, pStr, StrSize, FontSize, pOffset, Hidden, Corners & ~CUI::CORNER_R, pEmptyText);
|
|
|
|
|
|
|
|
|
|
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT);
|
|
|
|
|
RenderTools()->DrawUIRect(&ClearButton, ColorRGBA(1, 1, 1, 0.33f * UI()->ButtonColorMul(pClearID)), Corners & ~CUI::CORNER_L, 3.0f);
|
2022-01-21 15:20:15 +00:00
|
|
|
|
UI()->DoLabel(&ClearButton, "×", ClearButton.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER, -1, 0);
|
2021-12-03 18:46:31 +00:00
|
|
|
|
TextRender()->SetRenderFlags(0);
|
|
|
|
|
if(UI()->DoButtonLogic(pClearID, "×", 0, &ClearButton))
|
|
|
|
|
{
|
|
|
|
|
pStr[0] = 0;
|
|
|
|
|
UI()->SetActiveItem(pID);
|
|
|
|
|
ReturnValue = true;
|
|
|
|
|
}
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|