2010-11-20 10:37:14 +00:00
|
|
|
|
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
|
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
2020-09-26 19:41:58 +00:00
|
|
|
|
#include "ui.h"
|
2022-05-27 16:14:31 +00:00
|
|
|
|
|
|
|
|
|
#include <base/math.h>
|
2022-08-07 20:03:34 +00:00
|
|
|
|
#include <base/system.h>
|
|
|
|
|
|
2022-08-10 21:00:25 +00:00
|
|
|
|
#include <engine/client.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
|
#include <engine/graphics.h>
|
2022-05-27 16:14:31 +00:00
|
|
|
|
#include <engine/input.h>
|
|
|
|
|
#include <engine/keys.h>
|
2020-09-26 19:41:58 +00:00
|
|
|
|
#include <engine/shared/config.h>
|
2022-05-27 16:14:31 +00:00
|
|
|
|
|
2022-08-07 20:03:34 +00:00
|
|
|
|
#include <game/client/lineinput.h>
|
2023-03-27 15:22:17 +00:00
|
|
|
|
#include <game/localization.h>
|
2022-08-07 20:03:34 +00:00
|
|
|
|
|
2022-03-11 16:34:48 +00:00
|
|
|
|
#include <limits>
|
2007-05-22 15:03:32 +00:00
|
|
|
|
|
2021-09-13 21:48:10 +00:00
|
|
|
|
void CUIElement::Init(CUI *pUI, int RequestedRectCount)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
2022-07-09 21:35:00 +00:00
|
|
|
|
m_pUI = pUI;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
pUI->AddUIElement(this);
|
2021-09-13 21:48:10 +00:00
|
|
|
|
if(RequestedRectCount > 0)
|
2022-07-09 21:35:00 +00:00
|
|
|
|
InitRects(RequestedRectCount);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-13 21:48:10 +00:00
|
|
|
|
void CUIElement::InitRects(int RequestedRectCount)
|
2020-11-25 12:05:53 +00:00
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
|
dbg_assert(m_vUIRects.empty(), "UI rects can only be initialized once, create another ui element instead.");
|
|
|
|
|
m_vUIRects.resize(RequestedRectCount);
|
2022-07-09 21:35:00 +00:00
|
|
|
|
for(auto &Rect : m_vUIRects)
|
|
|
|
|
Rect.m_pParent = this;
|
2021-09-13 21:48:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUIElement::SUIElementRect::SUIElementRect() { Reset(); }
|
|
|
|
|
|
|
|
|
|
void CUIElement::SUIElementRect::Reset()
|
|
|
|
|
{
|
|
|
|
|
m_UIRectQuadContainer = -1;
|
|
|
|
|
m_UITextContainer = -1;
|
|
|
|
|
m_X = -1;
|
|
|
|
|
m_Y = -1;
|
|
|
|
|
m_Width = -1;
|
|
|
|
|
m_Height = -1;
|
|
|
|
|
m_Text.clear();
|
2020-11-25 12:05:53 +00:00
|
|
|
|
mem_zero(&m_Cursor, sizeof(m_Cursor));
|
2022-07-01 04:42:36 +00:00
|
|
|
|
m_TextColor = ColorRGBA(-1, -1, -1, -1);
|
|
|
|
|
m_TextOutlineColor = ColorRGBA(-1, -1, -1, -1);
|
2021-09-13 21:48:10 +00:00
|
|
|
|
m_QuadColor = ColorRGBA(-1, -1, -1, -1);
|
2020-11-25 12:05:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-09 21:35:00 +00:00
|
|
|
|
void CUIElement::SUIElementRect::Draw(const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding)
|
|
|
|
|
{
|
|
|
|
|
bool NeedsRecreate = false;
|
2023-01-07 09:59:32 +00:00
|
|
|
|
if(m_UIRectQuadContainer == -1 || m_Width != pRect->w || m_Height != pRect->h || mem_comp(&m_QuadColor, &Color, sizeof(Color)) != 0)
|
2022-07-09 21:35:00 +00:00
|
|
|
|
{
|
2023-02-14 23:39:47 +00:00
|
|
|
|
m_pParent->UI()->Graphics()->DeleteQuadContainer(m_UIRectQuadContainer);
|
2022-07-09 21:35:00 +00:00
|
|
|
|
NeedsRecreate = true;
|
|
|
|
|
}
|
2023-01-07 09:59:32 +00:00
|
|
|
|
m_X = pRect->x;
|
|
|
|
|
m_Y = pRect->y;
|
2022-07-09 21:35:00 +00:00
|
|
|
|
if(NeedsRecreate)
|
|
|
|
|
{
|
|
|
|
|
m_Width = pRect->w;
|
|
|
|
|
m_Height = pRect->h;
|
|
|
|
|
m_QuadColor = Color;
|
|
|
|
|
|
|
|
|
|
m_pParent->UI()->Graphics()->SetColor(Color);
|
2023-01-07 09:59:32 +00:00
|
|
|
|
m_UIRectQuadContainer = m_pParent->UI()->Graphics()->CreateRectQuadContainer(0, 0, pRect->w, pRect->h, Rounding, Corners);
|
2022-07-09 21:35:00 +00:00
|
|
|
|
m_pParent->UI()->Graphics()->SetColor(1, 1, 1, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pParent->UI()->Graphics()->TextureClear();
|
2023-01-07 09:59:32 +00:00
|
|
|
|
m_pParent->UI()->Graphics()->RenderQuadContainerEx(m_UIRectQuadContainer,
|
|
|
|
|
0, -1, m_X, m_Y, 1, 1);
|
2022-07-09 21:35:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-22 15:03:32 +00:00
|
|
|
|
/********************************************************
|
2011-04-13 18:37:12 +00:00
|
|
|
|
UI
|
2007-05-22 15:03:32 +00:00
|
|
|
|
*********************************************************/
|
2009-10-27 14:38:53 +00:00
|
|
|
|
|
2022-08-07 20:03:34 +00:00
|
|
|
|
const CLinearScrollbarScale CUI::ms_LinearScrollbarScale;
|
|
|
|
|
const CLogarithmicScrollbarScale CUI::ms_LogarithmicScrollbarScale(25);
|
2021-12-03 18:49:22 +00:00
|
|
|
|
float CUI::ms_FontmodHeight = 0.8f;
|
|
|
|
|
|
2022-08-13 11:58:11 +00:00
|
|
|
|
CUI *CUIElementBase::s_pUI = nullptr;
|
|
|
|
|
|
|
|
|
|
IClient *CUIElementBase::Client() const { return s_pUI->Client(); }
|
|
|
|
|
IGraphics *CUIElementBase::Graphics() const { return s_pUI->Graphics(); }
|
|
|
|
|
IInput *CUIElementBase::Input() const { return s_pUI->Input(); }
|
|
|
|
|
ITextRender *CUIElementBase::TextRender() const { return s_pUI->TextRender(); }
|
|
|
|
|
|
2022-08-07 20:03:34 +00:00
|
|
|
|
void CUI::Init(IKernel *pKernel)
|
2021-12-03 19:17:43 +00:00
|
|
|
|
{
|
2022-08-10 21:00:25 +00:00
|
|
|
|
m_pClient = pKernel->RequestInterface<IClient>();
|
2022-08-07 20:03:34 +00:00
|
|
|
|
m_pGraphics = pKernel->RequestInterface<IGraphics>();
|
|
|
|
|
m_pInput = pKernel->RequestInterface<IInput>();
|
|
|
|
|
m_pTextRender = pKernel->RequestInterface<ITextRender>();
|
|
|
|
|
InitInputs(m_pInput->GetEventsRaw(), m_pInput->GetEventCountRaw());
|
2022-08-07 16:12:07 +00:00
|
|
|
|
CUIRect::Init(m_pGraphics);
|
2022-08-13 11:58:11 +00:00
|
|
|
|
CUIElementBase::Init(this);
|
2021-12-03 19:17:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-07 20:03:34 +00:00
|
|
|
|
void CUI::InitInputs(IInput::CEvent *pInputEventsArray, int *pInputEventCount)
|
|
|
|
|
{
|
|
|
|
|
m_pInputEventsArray = pInputEventsArray;
|
|
|
|
|
m_pInputEventCount = pInputEventCount;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
|
CUI::CUI()
|
|
|
|
|
{
|
2022-05-27 16:14:31 +00:00
|
|
|
|
m_Enabled = true;
|
|
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
|
m_pHotItem = 0;
|
|
|
|
|
m_pActiveItem = 0;
|
|
|
|
|
m_pLastActiveItem = 0;
|
2021-11-22 17:26:13 +00:00
|
|
|
|
m_pBecomingHotItem = 0;
|
2022-04-07 07:46:02 +00:00
|
|
|
|
m_pActiveTooltipItem = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
|
m_MouseX = 0;
|
|
|
|
|
m_MouseY = 0;
|
|
|
|
|
m_MouseWorldX = 0;
|
|
|
|
|
m_MouseWorldY = 0;
|
|
|
|
|
m_MouseButtons = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
m_LastMouseButtons = 0;
|
|
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
|
m_Screen.x = 0;
|
|
|
|
|
m_Screen.y = 0;
|
|
|
|
|
m_Screen.w = 848.0f;
|
|
|
|
|
m_Screen.h = 480.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-12 10:29:47 +00:00
|
|
|
|
CUI::~CUI()
|
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
|
for(CUIElement *&pEl : m_vpOwnUIElements)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
|
|
|
|
delete pEl;
|
|
|
|
|
}
|
2022-06-11 19:38:18 +00:00
|
|
|
|
m_vpOwnUIElements.clear();
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-13 21:48:10 +00:00
|
|
|
|
CUIElement *CUI::GetNewUIElement(int RequestedRectCount)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
2021-09-13 21:48:10 +00:00
|
|
|
|
CUIElement *pNewEl = new CUIElement(this, RequestedRectCount);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
|
2022-06-11 19:38:18 +00:00
|
|
|
|
m_vpOwnUIElements.push_back(pNewEl);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
|
|
|
|
|
return pNewEl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::AddUIElement(CUIElement *pElement)
|
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
|
m_vpUIElements.push_back(pElement);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::ResetUIElement(CUIElement &UIElement)
|
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
|
for(CUIElement::SUIElementRect &Rect : UIElement.m_vUIRects)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
2023-02-14 23:39:47 +00:00
|
|
|
|
Graphics()->DeleteQuadContainer(Rect.m_UIRectQuadContainer);
|
2022-07-08 16:51:19 +00:00
|
|
|
|
TextRender()->DeleteTextContainer(Rect.m_UITextContainer);
|
2021-09-13 21:48:10 +00:00
|
|
|
|
Rect.Reset();
|
|
|
|
|
}
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::OnElementsReset()
|
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
|
for(CUIElement *pEl : m_vpUIElements)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
|
|
|
|
ResetUIElement(*pEl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::OnWindowResize()
|
|
|
|
|
{
|
|
|
|
|
OnElementsReset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::OnLanguageChange()
|
|
|
|
|
{
|
|
|
|
|
OnElementsReset();
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 16:14:31 +00:00
|
|
|
|
void CUI::Update(float MouseX, float MouseY, float MouseWorldX, float MouseWorldY)
|
2007-05-22 15:03:32 +00:00
|
|
|
|
{
|
2022-05-27 16:14:31 +00:00
|
|
|
|
unsigned MouseButtons = 0;
|
|
|
|
|
if(Enabled())
|
|
|
|
|
{
|
|
|
|
|
if(Input()->KeyIsPressed(KEY_MOUSE_1))
|
|
|
|
|
MouseButtons |= 1;
|
|
|
|
|
if(Input()->KeyIsPressed(KEY_MOUSE_2))
|
|
|
|
|
MouseButtons |= 2;
|
|
|
|
|
if(Input()->KeyIsPressed(KEY_MOUSE_3))
|
|
|
|
|
MouseButtons |= 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_MouseDeltaX = MouseX - m_MouseX;
|
|
|
|
|
m_MouseDeltaY = MouseY - m_MouseY;
|
|
|
|
|
m_MouseX = MouseX;
|
|
|
|
|
m_MouseY = MouseY;
|
|
|
|
|
m_MouseWorldX = MouseWorldX;
|
|
|
|
|
m_MouseWorldY = MouseWorldY;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
m_LastMouseButtons = m_MouseButtons;
|
2022-05-27 16:14:31 +00:00
|
|
|
|
m_MouseButtons = MouseButtons;
|
2021-11-22 17:26:13 +00:00
|
|
|
|
m_pHotItem = m_pBecomingHotItem;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
if(m_pActiveItem)
|
|
|
|
|
m_pHotItem = m_pActiveItem;
|
2021-11-22 17:26:13 +00:00
|
|
|
|
m_pBecomingHotItem = 0;
|
2022-07-11 16:52:51 +00:00
|
|
|
|
if(!Enabled())
|
|
|
|
|
{
|
|
|
|
|
m_pHotItem = nullptr;
|
|
|
|
|
m_pActiveItem = nullptr;
|
|
|
|
|
}
|
2007-05-22 15:03:32 +00:00
|
|
|
|
}
|
2009-10-27 14:38:53 +00:00
|
|
|
|
|
2021-11-26 20:55:31 +00:00
|
|
|
|
bool CUI::MouseInside(const CUIRect *pRect) const
|
2007-05-22 15:03:32 +00:00
|
|
|
|
{
|
2021-11-26 20:55:31 +00:00
|
|
|
|
return pRect->Inside(m_MouseX, m_MouseY);
|
2007-05-22 15:03:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-06 20:03:24 +00:00
|
|
|
|
void CUI::ConvertMouseMove(float *pX, float *pY, IInput::ECursorType CursorType) const
|
2021-10-23 11:48:21 +00:00
|
|
|
|
{
|
2022-06-06 20:03:24 +00:00
|
|
|
|
float Factor = 1.0f;
|
|
|
|
|
switch(CursorType)
|
|
|
|
|
{
|
|
|
|
|
case IInput::CURSOR_MOUSE:
|
|
|
|
|
Factor = g_Config.m_UiMousesens / 100.0f;
|
|
|
|
|
break;
|
|
|
|
|
case IInput::CURSOR_JOYSTICK:
|
2022-06-15 11:00:27 +00:00
|
|
|
|
Factor = g_Config.m_UiControllerSens / 100.0f;
|
2022-06-06 20:03:24 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
dbg_msg("assert", "CUI::ConvertMouseMove CursorType %d", (int)CursorType);
|
|
|
|
|
dbg_break();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-08-07 20:03:34 +00:00
|
|
|
|
|
|
|
|
|
if(m_MouseSlow)
|
|
|
|
|
Factor *= 0.05f;
|
|
|
|
|
|
2022-06-06 20:03:24 +00:00
|
|
|
|
*pX *= Factor;
|
|
|
|
|
*pY *= Factor;
|
2021-10-23 11:48:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-21 11:17:09 +00:00
|
|
|
|
bool CUI::ConsumeHotkey(EHotkey Hotkey)
|
|
|
|
|
{
|
|
|
|
|
const bool Pressed = m_HotkeysPressed & Hotkey;
|
|
|
|
|
m_HotkeysPressed &= ~Hotkey;
|
|
|
|
|
return Pressed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CUI::OnInput(const IInput::CEvent &Event)
|
|
|
|
|
{
|
|
|
|
|
if(!Enabled())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(Event.m_Flags & IInput::FLAG_PRESS)
|
|
|
|
|
{
|
|
|
|
|
unsigned LastHotkeysPressed = m_HotkeysPressed;
|
|
|
|
|
if(Event.m_Key == KEY_RETURN || Event.m_Key == KEY_KP_ENTER)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_ENTER;
|
|
|
|
|
else if(Event.m_Key == KEY_ESCAPE)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_ESCAPE;
|
2022-11-14 21:50:59 +00:00
|
|
|
|
else if(Event.m_Key == KEY_TAB && !Input()->AltIsPressed())
|
2022-08-21 11:17:09 +00:00
|
|
|
|
m_HotkeysPressed |= HOTKEY_TAB;
|
|
|
|
|
else if(Event.m_Key == KEY_DELETE)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_DELETE;
|
|
|
|
|
else if(Event.m_Key == KEY_UP)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_UP;
|
|
|
|
|
else if(Event.m_Key == KEY_DOWN)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_DOWN;
|
|
|
|
|
else if(Event.m_Key == KEY_MOUSE_WHEEL_UP)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_SCROLL_UP;
|
|
|
|
|
else if(Event.m_Key == KEY_MOUSE_WHEEL_DOWN)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_SCROLL_DOWN;
|
2023-01-05 19:38:30 +00:00
|
|
|
|
else if(Event.m_Key == KEY_PAGEUP)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_PAGE_UP;
|
|
|
|
|
else if(Event.m_Key == KEY_PAGEDOWN)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_PAGE_DOWN;
|
|
|
|
|
else if(Event.m_Key == KEY_HOME)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_HOME;
|
|
|
|
|
else if(Event.m_Key == KEY_END)
|
|
|
|
|
m_HotkeysPressed |= HOTKEY_END;
|
2022-08-21 11:17:09 +00:00
|
|
|
|
return LastHotkeysPressed != m_HotkeysPressed;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 20:55:31 +00:00
|
|
|
|
float CUI::ButtonColorMul(const void *pID)
|
|
|
|
|
{
|
2022-05-27 08:55:32 +00:00
|
|
|
|
if(CheckActiveItem(pID))
|
2021-11-26 20:55:31 +00:00
|
|
|
|
return ButtonColorMulActive();
|
|
|
|
|
else if(HotItem() == pID)
|
|
|
|
|
return ButtonColorMulHot();
|
|
|
|
|
return ButtonColorMulDefault();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-07 09:50:30 +00:00
|
|
|
|
const CUIRect *CUI::Screen()
|
2007-05-22 15:03:32 +00:00
|
|
|
|
{
|
2011-04-13 18:37:12 +00:00
|
|
|
|
float Aspect = Graphics()->ScreenAspect();
|
|
|
|
|
float w, h;
|
2007-05-22 15:03:32 +00:00
|
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
|
h = 600;
|
2020-09-26 19:41:58 +00:00
|
|
|
|
w = Aspect * h;
|
2007-05-22 15:03:32 +00:00
|
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
|
m_Screen.w = w;
|
|
|
|
|
m_Screen.h = h;
|
2007-05-27 18:14:24 +00:00
|
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
|
return &m_Screen;
|
2007-05-22 15:03:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-22 19:48:55 +00:00
|
|
|
|
void CUI::MapScreen()
|
|
|
|
|
{
|
|
|
|
|
const CUIRect *pScreen = Screen();
|
|
|
|
|
Graphics()->MapScreen(pScreen->x, pScreen->y, pScreen->w, pScreen->h);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-10 22:13:19 +00:00
|
|
|
|
float CUI::PixelSize()
|
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
|
return Screen()->w / Graphics()->ScreenWidth();
|
2012-01-10 22:13:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 13:12:59 +00:00
|
|
|
|
void CUI::ClipEnable(const CUIRect *pRect)
|
2007-10-29 18:40:04 +00:00
|
|
|
|
{
|
2022-05-13 18:20:04 +00:00
|
|
|
|
if(IsClipped())
|
|
|
|
|
{
|
|
|
|
|
const CUIRect *pOldRect = ClipArea();
|
|
|
|
|
CUIRect Intersection;
|
|
|
|
|
Intersection.x = std::max(pRect->x, pOldRect->x);
|
|
|
|
|
Intersection.y = std::max(pRect->y, pOldRect->y);
|
|
|
|
|
Intersection.w = std::min(pRect->x + pRect->w, pOldRect->x + pOldRect->w) - pRect->x;
|
|
|
|
|
Intersection.h = std::min(pRect->y + pRect->h, pOldRect->y + pOldRect->h) - pRect->y;
|
2022-06-11 19:38:18 +00:00
|
|
|
|
m_vClips.push_back(Intersection);
|
2022-05-13 18:20:04 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
|
m_vClips.push_back(*pRect);
|
2022-05-13 18:20:04 +00:00
|
|
|
|
}
|
|
|
|
|
UpdateClipping();
|
2007-10-29 18:40:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-27 14:38:53 +00:00
|
|
|
|
void CUI::ClipDisable()
|
2007-10-29 18:40:04 +00:00
|
|
|
|
{
|
2022-05-13 18:20:04 +00:00
|
|
|
|
dbg_assert(IsClipped(), "no clip region");
|
2022-06-11 19:38:18 +00:00
|
|
|
|
m_vClips.pop_back();
|
2022-05-13 18:20:04 +00:00
|
|
|
|
UpdateClipping();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CUIRect *CUI::ClipArea() const
|
|
|
|
|
{
|
|
|
|
|
dbg_assert(IsClipped(), "no clip region");
|
2022-06-11 19:38:18 +00:00
|
|
|
|
return &m_vClips.back();
|
2022-05-13 18:20:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::UpdateClipping()
|
|
|
|
|
{
|
|
|
|
|
if(IsClipped())
|
|
|
|
|
{
|
|
|
|
|
const CUIRect *pRect = ClipArea();
|
|
|
|
|
const float XScale = Graphics()->ScreenWidth() / Screen()->w;
|
|
|
|
|
const float YScale = Graphics()->ScreenHeight() / Screen()->h;
|
|
|
|
|
Graphics()->ClipEnable((int)(pRect->x * XScale), (int)(pRect->y * YScale), (int)(pRect->w * XScale), (int)(pRect->h * YScale));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Graphics()->ClipDisable();
|
|
|
|
|
}
|
2007-10-29 18:40:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-12 10:29:47 +00:00
|
|
|
|
int CUI::DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect)
|
2008-01-12 12:27:55 +00:00
|
|
|
|
{
|
2011-04-13 18:37:12 +00:00
|
|
|
|
// logic
|
|
|
|
|
int ReturnValue = 0;
|
2022-05-13 18:45:57 +00:00
|
|
|
|
const bool Inside = MouseHovered(pRect);
|
2022-05-27 16:42:45 +00:00
|
|
|
|
static int s_ButtonUsed = -1;
|
2009-10-27 14:38:53 +00:00
|
|
|
|
|
2022-05-27 08:55:32 +00:00
|
|
|
|
if(CheckActiveItem(pID))
|
2009-10-27 14:38:53 +00:00
|
|
|
|
{
|
2022-05-27 16:42:45 +00:00
|
|
|
|
if(s_ButtonUsed >= 0 && !MouseButton(s_ButtonUsed))
|
2009-10-27 14:38:53 +00:00
|
|
|
|
{
|
|
|
|
|
if(Inside && Checked >= 0)
|
2022-05-13 18:28:57 +00:00
|
|
|
|
ReturnValue = 1 + s_ButtonUsed;
|
2022-05-27 09:59:14 +00:00
|
|
|
|
SetActiveItem(nullptr);
|
2022-05-27 16:42:45 +00:00
|
|
|
|
s_ButtonUsed = -1;
|
2009-10-27 14:38:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(HotItem() == pID)
|
2023-03-15 17:15:43 +00:00
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < 3; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(MouseButton(i))
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
s_ButtonUsed = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Inside && !MouseButton(0) && !MouseButton(1) && !MouseButton(2))
|
|
|
|
|
SetHotItem(pID);
|
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CUI::DoDraggableButtonLogic(const void *pID, int Checked, const CUIRect *pRect, bool *pClicked, bool *pAbrupted)
|
|
|
|
|
{
|
|
|
|
|
// logic
|
|
|
|
|
int ReturnValue = 0;
|
|
|
|
|
const bool Inside = MouseHovered(pRect);
|
|
|
|
|
static int s_ButtonUsed = -1;
|
|
|
|
|
|
|
|
|
|
if(pClicked != nullptr)
|
|
|
|
|
*pClicked = false;
|
|
|
|
|
if(pAbrupted != nullptr)
|
|
|
|
|
*pAbrupted = false;
|
|
|
|
|
|
|
|
|
|
if(CheckActiveItem(pID))
|
|
|
|
|
{
|
|
|
|
|
if(s_ButtonUsed == 0)
|
|
|
|
|
{
|
|
|
|
|
if(Checked >= 0)
|
|
|
|
|
ReturnValue = 1 + s_ButtonUsed;
|
|
|
|
|
if(!MouseButton(s_ButtonUsed))
|
|
|
|
|
{
|
|
|
|
|
if(pClicked != nullptr)
|
|
|
|
|
*pClicked = true;
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
s_ButtonUsed = -1;
|
|
|
|
|
}
|
|
|
|
|
if(MouseButton(1))
|
|
|
|
|
{
|
|
|
|
|
if(pAbrupted != nullptr)
|
|
|
|
|
*pAbrupted = true;
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
s_ButtonUsed = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(s_ButtonUsed > 0 && !MouseButton(s_ButtonUsed))
|
|
|
|
|
{
|
|
|
|
|
if(Inside && Checked >= 0)
|
|
|
|
|
ReturnValue = 1 + s_ButtonUsed;
|
|
|
|
|
if(pClicked != nullptr)
|
|
|
|
|
*pClicked = true;
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
s_ButtonUsed = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(HotItem() == pID)
|
2009-10-27 14:38:53 +00:00
|
|
|
|
{
|
2017-08-30 20:36:39 +00:00
|
|
|
|
for(int i = 0; i < 3; ++i)
|
2009-10-27 14:38:53 +00:00
|
|
|
|
{
|
2017-08-30 20:36:39 +00:00
|
|
|
|
if(MouseButton(i))
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(pID);
|
2022-05-13 18:28:57 +00:00
|
|
|
|
s_ButtonUsed = i;
|
2017-08-30 20:36:39 +00:00
|
|
|
|
}
|
2009-10-27 14:38:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
|
2022-05-27 16:42:45 +00:00
|
|
|
|
if(Inside && !MouseButton(0) && !MouseButton(1) && !MouseButton(2))
|
2009-10-27 14:38:53 +00:00
|
|
|
|
SetHotItem(pID);
|
|
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
|
return ReturnValue;
|
2009-10-27 14:38:53 +00:00
|
|
|
|
}
|
2015-08-17 12:10:08 +00:00
|
|
|
|
|
|
|
|
|
int CUI::DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY)
|
|
|
|
|
{
|
2022-05-13 19:34:46 +00:00
|
|
|
|
if(MouseHovered(pRect))
|
2015-08-17 12:10:08 +00:00
|
|
|
|
SetHotItem(pID);
|
|
|
|
|
|
2020-12-14 00:51:31 +00:00
|
|
|
|
if(HotItem() == pID && MouseButtonClicked(0))
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
|
2022-05-27 08:55:32 +00:00
|
|
|
|
if(CheckActiveItem(pID) && !MouseButton(0))
|
2022-05-27 09:59:14 +00:00
|
|
|
|
SetActiveItem(nullptr);
|
2020-12-14 00:51:31 +00:00
|
|
|
|
|
2022-05-27 08:55:32 +00:00
|
|
|
|
if(!CheckActiveItem(pID))
|
2015-08-17 12:10:08 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2023-03-18 21:06:03 +00:00
|
|
|
|
if(Input()->ShiftIsPressed())
|
|
|
|
|
m_MouseSlow = true;
|
|
|
|
|
|
2015-08-17 12:10:08 +00:00
|
|
|
|
if(pX)
|
2022-06-12 08:34:09 +00:00
|
|
|
|
*pX = clamp(m_MouseX - pRect->x, 0.0f, pRect->w);
|
2015-08-17 12:10:08 +00:00
|
|
|
|
if(pY)
|
2022-06-12 08:34:09 +00:00
|
|
|
|
*pY = clamp(m_MouseY - pRect->y, 0.0f, pRect->h);
|
2015-08-17 12:10:08 +00:00
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-10 21:00:25 +00:00
|
|
|
|
void CUI::DoSmoothScrollLogic(float *pScrollOffset, float *pScrollOffsetChange, float ViewPortSize, float TotalSize, float ScrollSpeed)
|
|
|
|
|
{
|
|
|
|
|
// instant scrolling if distance too long
|
|
|
|
|
if(absolute(*pScrollOffsetChange) > ViewPortSize)
|
|
|
|
|
{
|
|
|
|
|
*pScrollOffset += *pScrollOffsetChange;
|
|
|
|
|
*pScrollOffsetChange = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
// smooth scrolling
|
|
|
|
|
if(*pScrollOffsetChange)
|
|
|
|
|
{
|
|
|
|
|
const float Delta = *pScrollOffsetChange * clamp(Client()->RenderFrameTime() * ScrollSpeed, 0.0f, 1.0f);
|
|
|
|
|
*pScrollOffset += Delta;
|
|
|
|
|
*pScrollOffsetChange -= Delta;
|
|
|
|
|
}
|
|
|
|
|
// clamp to first item
|
|
|
|
|
if(*pScrollOffset < 0.0f)
|
|
|
|
|
{
|
|
|
|
|
*pScrollOffset = 0.0f;
|
|
|
|
|
*pScrollOffsetChange = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
// clamp to last item
|
|
|
|
|
if(TotalSize > ViewPortSize && *pScrollOffset > TotalSize - ViewPortSize)
|
|
|
|
|
{
|
|
|
|
|
*pScrollOffset = TotalSize - ViewPortSize;
|
|
|
|
|
*pScrollOffsetChange = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-09 14:10:07 +00:00
|
|
|
|
void CUI::DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps)
|
2008-01-12 12:27:55 +00:00
|
|
|
|
{
|
2023-04-09 15:28:58 +00:00
|
|
|
|
int Flags = LabelProps.m_StopAtEnd ? TEXTFLAG_STOP_AT_END : 0;
|
|
|
|
|
float TextHeight = 0.0f;
|
2023-04-09 14:10:07 +00:00
|
|
|
|
float AlignedFontSize = 0.0f;
|
|
|
|
|
float MaxCharacterHeightInLine = 0.0f;
|
|
|
|
|
float MaxTextWidth = LabelProps.m_MaxWidth != -1 ? LabelProps.m_MaxWidth : pRect->w;
|
2023-04-09 15:28:58 +00:00
|
|
|
|
float TextWidth = TextRender()->TextWidth(Size, pText, -1, LabelProps.m_MaxWidth, Flags, &TextHeight, &AlignedFontSize, &MaxCharacterHeightInLine);
|
2023-04-09 14:10:07 +00:00
|
|
|
|
while(TextWidth > MaxTextWidth + 0.001f)
|
2022-03-11 16:34:48 +00:00
|
|
|
|
{
|
|
|
|
|
if(!LabelProps.m_EnableWidthCheck)
|
|
|
|
|
break;
|
|
|
|
|
if(Size < 4.0f)
|
|
|
|
|
break;
|
|
|
|
|
Size -= 1.0f;
|
2023-04-09 15:28:58 +00:00
|
|
|
|
TextWidth = TextRender()->TextWidth(Size, pText, -1, LabelProps.m_MaxWidth, Flags, &TextHeight, &AlignedFontSize, &MaxCharacterHeightInLine);
|
2022-03-11 16:34:48 +00:00
|
|
|
|
}
|
2021-09-13 21:48:10 +00:00
|
|
|
|
|
2023-04-09 14:10:07 +00:00
|
|
|
|
float CursorX = 0.0f;
|
2022-01-21 15:20:15 +00:00
|
|
|
|
if(Align & TEXTALIGN_CENTER)
|
2011-04-13 18:37:12 +00:00
|
|
|
|
{
|
2023-04-09 14:10:07 +00:00
|
|
|
|
CursorX = pRect->x + (pRect->w - TextWidth) / 2.f;
|
2008-01-12 12:27:55 +00:00
|
|
|
|
}
|
2022-01-21 15:20:15 +00:00
|
|
|
|
else if(Align & TEXTALIGN_LEFT)
|
2018-03-21 14:54:51 +00:00
|
|
|
|
{
|
2023-04-09 14:10:07 +00:00
|
|
|
|
CursorX = pRect->x;
|
2018-03-21 14:54:51 +00:00
|
|
|
|
}
|
2022-01-21 15:20:15 +00:00
|
|
|
|
else if(Align & TEXTALIGN_RIGHT)
|
2008-01-12 12:27:55 +00:00
|
|
|
|
{
|
2023-04-09 14:10:07 +00:00
|
|
|
|
CursorX = pRect->x + pRect->w - TextWidth;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-09 15:28:58 +00:00
|
|
|
|
float CursorY = pRect->y + (pRect->h - TextHeight) / 2.f;
|
2023-04-09 14:10:07 +00:00
|
|
|
|
if(LabelProps.m_AlignVertically == 0)
|
|
|
|
|
{
|
|
|
|
|
CursorY = pRect->y + (pRect->h - AlignedFontSize) / 2.f - (AlignedFontSize - MaxCharacterHeightInLine) / 2.f;
|
2008-01-12 12:27:55 +00:00
|
|
|
|
}
|
2021-09-13 21:48:10 +00:00
|
|
|
|
|
|
|
|
|
CTextCursor Cursor;
|
2023-04-09 15:28:58 +00:00
|
|
|
|
TextRender()->SetCursor(&Cursor, CursorX, CursorY, Size, TEXTFLAG_RENDER | Flags);
|
2022-03-11 16:34:48 +00:00
|
|
|
|
Cursor.m_LineWidth = (float)LabelProps.m_MaxWidth;
|
|
|
|
|
if(LabelProps.m_pSelCursor)
|
2021-09-16 14:50:17 +00:00
|
|
|
|
{
|
2022-03-11 16:34:48 +00:00
|
|
|
|
Cursor.m_CursorMode = LabelProps.m_pSelCursor->m_CursorMode;
|
|
|
|
|
Cursor.m_CursorCharacter = LabelProps.m_pSelCursor->m_CursorCharacter;
|
|
|
|
|
Cursor.m_CalculateSelectionMode = LabelProps.m_pSelCursor->m_CalculateSelectionMode;
|
|
|
|
|
Cursor.m_PressMouseX = LabelProps.m_pSelCursor->m_PressMouseX;
|
|
|
|
|
Cursor.m_PressMouseY = LabelProps.m_pSelCursor->m_PressMouseY;
|
|
|
|
|
Cursor.m_ReleaseMouseX = LabelProps.m_pSelCursor->m_ReleaseMouseX;
|
|
|
|
|
Cursor.m_ReleaseMouseY = LabelProps.m_pSelCursor->m_ReleaseMouseY;
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
2022-03-11 16:34:48 +00:00
|
|
|
|
Cursor.m_SelectionStart = LabelProps.m_pSelCursor->m_SelectionStart;
|
|
|
|
|
Cursor.m_SelectionEnd = LabelProps.m_pSelCursor->m_SelectionEnd;
|
2021-09-16 14:50:17 +00:00
|
|
|
|
}
|
2021-09-13 21:48:10 +00:00
|
|
|
|
|
|
|
|
|
TextRender()->TextEx(&Cursor, pText, -1);
|
|
|
|
|
|
2022-03-11 16:34:48 +00:00
|
|
|
|
if(LabelProps.m_pSelCursor)
|
2021-09-16 14:50:17 +00:00
|
|
|
|
{
|
2022-03-11 16:34:48 +00:00
|
|
|
|
*LabelProps.m_pSelCursor = Cursor;
|
2021-09-16 14:50:17 +00:00
|
|
|
|
}
|
2007-10-29 18:40:04 +00:00
|
|
|
|
}
|
2010-12-14 00:20:47 +00:00
|
|
|
|
|
Mark parameters as `const` when possible
According to cppchecker's `constParameter` error:
```
src\engine\gfx\image_manipulation.cpp:7:58: style: Parameter 'pSrc' can be declared as pointer to const [constParameter]
static void Dilate(int w, int h, int BPP, unsigned char *pSrc, unsigned char *pDest, unsigned char AlphaThreshold = TW_DILATE_ALPHA_THRESHOLD)
^
src\engine\gfx\image_manipulation.cpp:58:67: style: Parameter 'pSrc' can be declared as pointer to const [constParameter]
static void CopyColorValues(int w, int h, int BPP, unsigned char *pSrc, unsigned char *pDest)
^
src\engine\shared\network_conn.cpp:241:42: style: Parameter 'Addr' can be declared as reference to const [constParameter]
void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup)
^
src\base\system.cpp:4060:71: style: Parameter 'random' can be declared as pointer to const [constParameter]
void generate_password(char *buffer, unsigned length, unsigned short *random, unsigned random_length)
^
src\engine\client\backend\vulkan\backend_vulkan.cpp:263:38: style: Parameter 'AllocatedMemory' can be declared as reference to const [constParameter]
void Free(SMemoryHeapQueueElement &AllocatedMemory)
^
src\engine\client\backend\vulkan\backend_vulkan.cpp:1708:47: style: Parameter 'ImgExtent' can be declared as reference to const [constParameter]
static size_t ImageMipLevelCount(VkExtent3D &ImgExtent)
^
src\engine\client\backend\vulkan\backend_vulkan.cpp:2801:29: style: Parameter 'Image' can be declared as reference to const [constParameter]
void ImageBarrier(VkImage &Image, size_t MipMapBase, size_t MipMapCount, size_t LayerBase, size_t LayerCount, VkFormat Format, VkImageLayout OldLayout, VkImageLayout NewLayout)
^
src\engine\client\backend\vulkan\backend_vulkan.cpp:6495:46: style: Parameter 'ExecBuffer' can be declared as reference to const [constParameter]
void Cmd_Clear(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_Clear *pCommand)
^
src\game\client\components\skins.cpp:83:72: style: Parameter 'pImg' can be declared as pointer to const [constParameter]
static void CheckMetrics(CSkin::SSkinMetricVariable &Metrics, uint8_t *pImg, int ImgWidth, int ImgX, int ImgY, int CheckWidth, int CheckHeight)
^
src\game\client\prediction\entities\character.h:106:37: style: Parameter 'pNewInput' can be declared as pointer to const [constParameter]
void SetInput(CNetObj_PlayerInput *pNewInput)
^
src\game\client\prediction\gameworld.cpp:245:106: style: Parameter 'pNotThis' can be declared as pointer to const [constParameter]
CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2 &NewPos, CCharacter *pNotThis, int CollideWith, class CCharacter *pThisOnly)
^
src\game\client\prediction\gameworld.cpp:245:151: style: Parameter 'pThisOnly' can be declared as pointer to const [constParameter]
CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2 &NewPos, CCharacter *pNotThis, int CollideWith, class CCharacter *pThisOnly)
^
src\game\client\prediction\gameworld.cpp:283:116: style: Parameter 'pNotThis' can be declared as pointer to const [constParameter]
std::list<class CCharacter *> CGameWorld::IntersectedCharacters(vec2 Pos0, vec2 Pos1, float Radius, class CEntity *pNotThis)
^
src\game\client\ui.cpp:522:180: style: Parameter 'pReadCursor' can be declared as pointer to const [constParameter]
void CUI::DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps, int StrLen, CTextCursor *pReadCursor)
^
src\game\client\ui_scrollregion.cpp:23:86: style: Parameter 'pParams' can be declared as pointer to const [constParameter]
void CScrollRegion::Begin(CUIRect *pClipRect, vec2 *pOutOffset, CScrollRegionParams *pParams)
^
src\game\server\scoreworker.h:239:29: style: Parameter 'aTimeCp' can be declared as const array [constParameter]
void Set(float Time, float aTimeCp[NUM_CHECKPOINTS])
^
src\game\server\score.cpp:135:80: style: Parameter 'aTimeCp' can be declared as const array [constParameter]
void CScore::SaveScore(int ClientID, float Time, const char *pTimestamp, float aTimeCp[NUM_CHECKPOINTS], bool NotEligible)
^
src\game\server\teeinfo.cpp:40:57: style: Parameter 'pUseCustomColors' can be declared as pointer to const [constParameter]
CTeeInfo::CTeeInfo(const char *apSkinPartNames[6], int *pUseCustomColors, int *pSkinPartColors)
^
src\game\server\teeinfo.cpp:40:80: style: Parameter 'pSkinPartColors' can be declared as pointer to const [constParameter]
CTeeInfo::CTeeInfo(const char *apSkinPartNames[6], int *pUseCustomColors, int *pSkinPartColors)
^
```
2022-11-13 14:32:53 +00:00
|
|
|
|
void CUI::DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps, int StrLen, const CTextCursor *pReadCursor)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
2023-04-09 15:28:58 +00:00
|
|
|
|
int Flags = pReadCursor ? (pReadCursor->m_Flags & ~TEXTFLAG_RENDER) : LabelProps.m_StopAtEnd ? TEXTFLAG_STOP_AT_END : 0;
|
|
|
|
|
float TextHeight = 0.0f;
|
2023-04-09 14:10:07 +00:00
|
|
|
|
float AlignedFontSize = 0.0f;
|
|
|
|
|
float MaxCharacterHeightInLine = 0.0f;
|
2022-03-11 16:34:48 +00:00
|
|
|
|
float MaxTextWidth = LabelProps.m_MaxWidth != -1 ? LabelProps.m_MaxWidth : pRect->w;
|
2023-04-09 15:28:58 +00:00
|
|
|
|
float TextWidth = TextRender()->TextWidth(Size, pText, -1, LabelProps.m_MaxWidth, Flags, &TextHeight, &AlignedFontSize, &MaxCharacterHeightInLine);
|
2023-04-09 14:10:07 +00:00
|
|
|
|
while(TextWidth > MaxTextWidth + 0.001f)
|
2022-03-11 16:34:48 +00:00
|
|
|
|
{
|
|
|
|
|
if(!LabelProps.m_EnableWidthCheck)
|
|
|
|
|
break;
|
|
|
|
|
if(Size < 4.0f)
|
|
|
|
|
break;
|
|
|
|
|
Size -= 1.0f;
|
2023-04-09 15:28:58 +00:00
|
|
|
|
TextWidth = TextRender()->TextWidth(Size, pText, -1, LabelProps.m_MaxWidth, Flags, &TextHeight, &AlignedFontSize, &MaxCharacterHeightInLine);
|
2022-03-11 16:34:48 +00:00
|
|
|
|
}
|
2020-10-12 10:29:47 +00:00
|
|
|
|
|
2023-04-09 14:10:07 +00:00
|
|
|
|
float CursorX = 0;
|
2022-01-21 15:20:15 +00:00
|
|
|
|
if(Align & TEXTALIGN_CENTER)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
2023-04-09 14:10:07 +00:00
|
|
|
|
CursorX = pRect->x + (pRect->w - TextWidth) / 2.f;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
2022-01-21 15:20:15 +00:00
|
|
|
|
else if(Align & TEXTALIGN_LEFT)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
2023-04-09 14:10:07 +00:00
|
|
|
|
CursorX = pRect->x;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
2022-01-21 15:20:15 +00:00
|
|
|
|
else if(Align & TEXTALIGN_RIGHT)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
2023-04-09 14:10:07 +00:00
|
|
|
|
CursorX = pRect->x + pRect->w - TextWidth;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-09 15:28:58 +00:00
|
|
|
|
float CursorY = pRect->y + (pRect->h - TextHeight) / 2.f;
|
2023-04-09 14:10:07 +00:00
|
|
|
|
if(LabelProps.m_AlignVertically == 0)
|
|
|
|
|
{
|
|
|
|
|
CursorY = pRect->y + (pRect->h - AlignedFontSize) / 2.f - (AlignedFontSize - MaxCharacterHeightInLine) / 2.f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CTextCursor Cursor;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
if(pReadCursor)
|
|
|
|
|
{
|
|
|
|
|
Cursor = *pReadCursor;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-04-09 15:28:58 +00:00
|
|
|
|
TextRender()->SetCursor(&Cursor, CursorX, CursorY, Size, TEXTFLAG_RENDER | Flags);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
2022-03-11 16:34:48 +00:00
|
|
|
|
Cursor.m_LineWidth = LabelProps.m_MaxWidth;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
|
2020-11-08 18:41:16 +00:00
|
|
|
|
RectEl.m_TextColor = TextRender()->GetTextColor();
|
|
|
|
|
RectEl.m_TextOutlineColor = TextRender()->GetTextOutlineColor();
|
2021-09-13 21:48:10 +00:00
|
|
|
|
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
|
|
|
|
TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor());
|
2022-07-08 16:51:19 +00:00
|
|
|
|
TextRender()->CreateTextContainer(RectEl.m_UITextContainer, &Cursor, pText, StrLen);
|
2021-09-13 21:48:10 +00:00
|
|
|
|
TextRender()->TextColor(RectEl.m_TextColor);
|
|
|
|
|
TextRender()->TextOutlineColor(RectEl.m_TextOutlineColor);
|
|
|
|
|
RectEl.m_Cursor = Cursor;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-09 14:10:07 +00:00
|
|
|
|
void CUI::DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, float MaxWidth, int AlignVertically, bool StopAtEnd, int StrLen, const CTextCursor *pReadCursor)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
|
|
|
|
bool NeedsRecreate = false;
|
2020-11-08 18:41:16 +00:00
|
|
|
|
bool ColorChanged = RectEl.m_TextColor != TextRender()->GetTextColor() || RectEl.m_TextOutlineColor != TextRender()->GetTextOutlineColor();
|
2023-04-09 14:10:07 +00:00
|
|
|
|
if(RectEl.m_UITextContainer == -1 || RectEl.m_Width != pRect->w || RectEl.m_Height != pRect->h || ColorChanged)
|
2020-10-12 10:29:47 +00:00
|
|
|
|
{
|
|
|
|
|
NeedsRecreate = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(StrLen <= -1)
|
|
|
|
|
{
|
|
|
|
|
if(str_comp(RectEl.m_Text.c_str(), pText) != 0)
|
|
|
|
|
NeedsRecreate = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(StrLen != (int)RectEl.m_Text.size() || str_comp_num(RectEl.m_Text.c_str(), pText, StrLen) != 0)
|
|
|
|
|
NeedsRecreate = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-09 14:10:07 +00:00
|
|
|
|
RectEl.m_X = pRect->x;
|
|
|
|
|
RectEl.m_Y = pRect->y;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
if(NeedsRecreate)
|
|
|
|
|
{
|
2022-07-08 16:51:19 +00:00
|
|
|
|
TextRender()->DeleteTextContainer(RectEl.m_UITextContainer);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
|
2023-04-09 14:10:07 +00:00
|
|
|
|
RectEl.m_Width = pRect->w;
|
|
|
|
|
RectEl.m_Height = pRect->h;
|
2020-10-12 10:29:47 +00:00
|
|
|
|
|
|
|
|
|
if(StrLen > 0)
|
|
|
|
|
RectEl.m_Text = std::string(pText, StrLen);
|
|
|
|
|
else if(StrLen < 0)
|
|
|
|
|
RectEl.m_Text = pText;
|
|
|
|
|
else
|
|
|
|
|
RectEl.m_Text.clear();
|
|
|
|
|
|
2021-09-13 21:48:10 +00:00
|
|
|
|
CUIRect TmpRect;
|
2023-01-07 09:59:32 +00:00
|
|
|
|
TmpRect.x = 0;
|
|
|
|
|
TmpRect.y = 0;
|
2023-04-09 14:10:07 +00:00
|
|
|
|
TmpRect.w = pRect->w;
|
|
|
|
|
TmpRect.h = pRect->h;
|
2022-03-11 16:34:48 +00:00
|
|
|
|
|
|
|
|
|
SLabelProperties Props;
|
|
|
|
|
Props.m_MaxWidth = MaxWidth;
|
|
|
|
|
Props.m_AlignVertically = AlignVertically;
|
|
|
|
|
Props.m_StopAtEnd = StopAtEnd;
|
|
|
|
|
DoLabel(RectEl, &TmpRect, pText, Size, Align, Props, StrLen, pReadCursor);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-01 04:42:36 +00:00
|
|
|
|
ColorRGBA ColorText(RectEl.m_TextColor);
|
|
|
|
|
ColorRGBA ColorTextOutline(RectEl.m_TextOutlineColor);
|
2020-10-12 10:29:47 +00:00
|
|
|
|
if(RectEl.m_UITextContainer != -1)
|
2023-01-07 09:59:32 +00:00
|
|
|
|
{
|
2023-04-09 14:10:07 +00:00
|
|
|
|
TextRender()->RenderTextContainer(RectEl.m_UITextContainer, ColorText, ColorTextOutline, pRect->x, pRect->y);
|
2023-01-07 09:59:32 +00:00
|
|
|
|
}
|
2020-10-12 10:29:47 +00:00
|
|
|
|
}
|
2021-09-13 21:48:10 +00:00
|
|
|
|
|
2022-08-07 20:03:34 +00:00
|
|
|
|
bool CUI::DoEditBox(const void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden, int Corners, const SUIExEditBoxProperties &Properties)
|
|
|
|
|
{
|
|
|
|
|
const bool Inside = MouseHovered(pRect);
|
|
|
|
|
bool ReturnValue = false;
|
|
|
|
|
bool UpdateOffset = false;
|
|
|
|
|
|
|
|
|
|
auto &&SetHasSelection = [&](bool HasSelection) {
|
|
|
|
|
m_HasSelection = HasSelection;
|
|
|
|
|
m_pSelItem = m_HasSelection ? pID : nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto &&SelectAllText = [&]() {
|
|
|
|
|
m_CurSelStart = 0;
|
|
|
|
|
int StrLen = str_length(pStr);
|
|
|
|
|
TextRender()->UTF8OffToDecodedOff(pStr, StrLen, m_CurSelEnd);
|
|
|
|
|
SetHasSelection(true);
|
|
|
|
|
m_CurCursor = StrLen;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(LastActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(m_HasSelection && m_pSelItem != pID)
|
|
|
|
|
{
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_CurCursor = minimum(str_length(pStr), m_CurCursor);
|
|
|
|
|
|
2022-11-14 21:50:59 +00:00
|
|
|
|
const bool IsShiftPressed = Input()->ShiftIsPressed();
|
|
|
|
|
const bool IsModPressed = Input()->ModifierIsPressed();
|
2022-08-07 20:03:34 +00:00
|
|
|
|
|
|
|
|
|
if(Enabled() && !IsShiftPressed && IsModPressed && Input()->KeyPress(KEY_V))
|
|
|
|
|
{
|
|
|
|
|
const char *pText = Input()->GetClipboardText();
|
|
|
|
|
if(pText)
|
|
|
|
|
{
|
|
|
|
|
int OffsetL = clamp(m_CurCursor, 0, str_length(pStr));
|
|
|
|
|
int OffsetR = OffsetL;
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
{
|
|
|
|
|
OffsetL = UTF8SelLeft;
|
|
|
|
|
OffsetR = UTF8SelRight;
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NewStr.append(pStr + OffsetR);
|
|
|
|
|
|
|
|
|
|
str_copy(pStr, NewStr.c_str(), StrSize);
|
|
|
|
|
|
|
|
|
|
m_CurCursor = OffsetL + WrittenChars;
|
|
|
|
|
ReturnValue = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Enabled() && !IsShiftPressed && IsModPressed && (Input()->KeyPress(KEY_C) || Input()->KeyPress(KEY_X)))
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
if(m_CurCursor > UTF8SelLeft)
|
|
|
|
|
m_CurCursor = maximum(0, m_CurCursor - (UTF8SelRight - UTF8SelLeft));
|
|
|
|
|
else
|
|
|
|
|
m_CurCursor = UTF8SelLeft;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Input()->SetClipboardText(pStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Properties.m_SelectText || (Enabled() && !IsShiftPressed && IsModPressed && Input()->KeyPress(KEY_A)))
|
|
|
|
|
{
|
|
|
|
|
SelectAllText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Enabled() && !IsShiftPressed && IsModPressed && Input()->KeyPress(KEY_U))
|
|
|
|
|
{
|
|
|
|
|
pStr[0] = '\0';
|
|
|
|
|
m_CurCursor = 0;
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
ReturnValue = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < *m_pInputEventCount; i++)
|
|
|
|
|
{
|
|
|
|
|
int LastCursor = m_CurCursor;
|
|
|
|
|
int Len, NumChars;
|
|
|
|
|
str_utf8_stats(pStr, StrSize, StrSize, &Len, &NumChars);
|
|
|
|
|
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);
|
|
|
|
|
ReturnValue |= (ManipulateChanges & (CLineInput::LINE_INPUT_CHANGE_STRING | CLineInput::LINE_INPUT_CHANGE_CHARACTERS_DELETE)) != 0;
|
|
|
|
|
|
|
|
|
|
// if cursor changed, reset selection
|
|
|
|
|
if(ManipulateChanges != 0)
|
|
|
|
|
{
|
|
|
|
|
if(m_HasSelection && (ManipulateChanges & (CLineInput::LINE_INPUT_CHANGE_STRING | CLineInput::LINE_INPUT_CHANGE_CHARACTERS_DELETE)) != 0)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
else
|
|
|
|
|
SetHasSelection(true);
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SetHasSelection(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Inside)
|
|
|
|
|
{
|
|
|
|
|
SetHotItem(pID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUIRect Textbox = *pRect;
|
|
|
|
|
Textbox.Draw(ColorRGBA(1, 1, 1, 0.5f), Corners, 3.0f);
|
|
|
|
|
Textbox.Margin(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};
|
|
|
|
|
int DispCursorPos = m_CurCursor;
|
|
|
|
|
if(LastActiveItem() == pID && Input()->GetIMEEditingTextLength() > -1)
|
|
|
|
|
{
|
|
|
|
|
int EditingTextCursor = Input()->GetEditingCursor();
|
|
|
|
|
str_copy(aDispEditingText, pDisplayStr);
|
|
|
|
|
char aEditingText[IInput::INPUT_TEXT_SIZE + 2];
|
|
|
|
|
if(Hidden)
|
|
|
|
|
{
|
|
|
|
|
// Do not show editing text in password field
|
|
|
|
|
str_copy(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);
|
|
|
|
|
for(int i = str_length(aDispEditingText) - 1; i >= m_CurCursor; i--)
|
|
|
|
|
aDispEditingText[i + FillCharLen] = aDispEditingText[i];
|
|
|
|
|
for(int i = 0; i < FillCharLen; i++)
|
|
|
|
|
aDispEditingText[m_CurCursor + i] = aEditingText[i];
|
|
|
|
|
DispCursorPos = m_CurCursor + EditingTextCursor + 1;
|
|
|
|
|
pDisplayStr = aDispEditingText;
|
|
|
|
|
UpdateOffset = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsEmptyText = false;
|
|
|
|
|
if(pDisplayStr[0] == '\0')
|
|
|
|
|
{
|
|
|
|
|
pDisplayStr = Properties.m_pEmptyText;
|
|
|
|
|
IsEmptyText = true;
|
|
|
|
|
TextRender()->TextColor(1, 1, 1, 0.75f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DispCursorPos = minimum(DispCursorPos, str_length(pDisplayStr));
|
|
|
|
|
|
|
|
|
|
bool JustGotActive = false;
|
|
|
|
|
if(CheckActiveItem(pID))
|
|
|
|
|
{
|
|
|
|
|
if(!MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(HotItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
if(LastActiveItem() != pID)
|
|
|
|
|
JustGotActive = true;
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if the text has to be moved
|
|
|
|
|
if(LastActiveItem() == pID && !JustGotActive && (UpdateOffset || *m_pInputEventCount))
|
|
|
|
|
{
|
2023-02-17 17:13:20 +00:00
|
|
|
|
float w = TextRender()->TextWidth(FontSize, pDisplayStr, DispCursorPos, std::numeric_limits<float>::max());
|
2022-08-07 20:03:34 +00:00
|
|
|
|
if(w - *pOffset > Textbox.w)
|
|
|
|
|
{
|
|
|
|
|
// move to the left
|
2023-02-17 17:13:20 +00:00
|
|
|
|
float wt = TextRender()->TextWidth(FontSize, pDisplayStr, -1, std::numeric_limits<float>::max());
|
2022-08-07 20:03:34 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
*pOffset += minimum(wt - *pOffset - Textbox.w, Textbox.w / 3);
|
|
|
|
|
} while(w - *pOffset > Textbox.w + 0.0001f);
|
|
|
|
|
}
|
|
|
|
|
else if(w - *pOffset < 0.0f)
|
|
|
|
|
{
|
|
|
|
|
// move to the right
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
*pOffset = maximum(0.0f, *pOffset - Textbox.w / 3);
|
|
|
|
|
} while(w - *pOffset < -0.0001f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ClipEnable(pRect);
|
|
|
|
|
Textbox.x -= *pOffset;
|
|
|
|
|
|
|
|
|
|
CTextCursor SelCursor;
|
|
|
|
|
TextRender()->SetCursor(&SelCursor, 0, 0, 16, 0);
|
|
|
|
|
|
|
|
|
|
bool HasMouseSel = false;
|
|
|
|
|
if(LastActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(!m_MouseIsPress && MouseButtonClicked(0))
|
|
|
|
|
{
|
|
|
|
|
m_MouseIsPress = true;
|
|
|
|
|
m_MousePressX = MouseX();
|
|
|
|
|
m_MousePressY = MouseY();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(m_MouseIsPress)
|
|
|
|
|
{
|
|
|
|
|
m_MouseCurX = MouseX();
|
|
|
|
|
m_MouseCurY = MouseY();
|
|
|
|
|
}
|
|
|
|
|
HasMouseSel = m_MouseIsPress && !IsEmptyText;
|
|
|
|
|
if(m_MouseIsPress && MouseButtonReleased(0))
|
|
|
|
|
{
|
|
|
|
|
m_MouseIsPress = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(LastActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SLabelProperties Props;
|
|
|
|
|
Props.m_pSelCursor = &SelCursor;
|
|
|
|
|
Props.m_EnableWidthCheck = IsEmptyText;
|
|
|
|
|
DoLabel(&Textbox, pDisplayStr, FontSize, TEXTALIGN_LEFT, Props);
|
|
|
|
|
|
|
|
|
|
if(LastActiveItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(SelCursor.m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE)
|
|
|
|
|
{
|
|
|
|
|
m_CurSelStart = SelCursor.m_SelectionStart;
|
|
|
|
|
m_CurSelEnd = SelCursor.m_SelectionEnd;
|
|
|
|
|
SetHasSelection(m_CurSelStart != m_CurSelEnd);
|
|
|
|
|
}
|
|
|
|
|
if(SelCursor.m_CursorMode == TEXT_CURSOR_CURSOR_MODE_CALCULATE)
|
|
|
|
|
{
|
|
|
|
|
TextRender()->DecodedOffToUTF8Off(pDisplayStr, SelCursor.m_CursorCharacter, DispCursorPos);
|
|
|
|
|
m_CurCursor = DispCursorPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextRender()->TextColor(1, 1, 1, 1);
|
|
|
|
|
|
|
|
|
|
// set the ime cursor
|
|
|
|
|
if(LastActiveItem() == pID && !JustGotActive)
|
|
|
|
|
{
|
2023-02-17 17:13:20 +00:00
|
|
|
|
float w = TextRender()->TextWidth(FontSize, pDisplayStr, DispCursorPos, std::numeric_limits<float>::max());
|
2022-08-07 20:03:34 +00:00
|
|
|
|
Textbox.x += w;
|
|
|
|
|
Input()->SetEditingPosition(Textbox.x, Textbox.y + FontSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClipDisable();
|
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CUI::DoClearableEditBox(const void *pID, const void *pClearID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden, int Corners, const SUIExEditBoxProperties &Properties)
|
|
|
|
|
{
|
|
|
|
|
CUIRect EditBox;
|
|
|
|
|
CUIRect ClearButton;
|
|
|
|
|
pRect->VSplitRight(15.0f, &EditBox, &ClearButton);
|
|
|
|
|
bool ReturnValue = DoEditBox(pID, &EditBox, pStr, StrSize, FontSize, pOffset, Hidden, Corners & ~IGraphics::CORNER_R, Properties);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
ClearButton.Draw(ColorRGBA(1, 1, 1, 0.33f * ButtonColorMul(pClearID)), Corners & ~IGraphics::CORNER_L, 3.0f);
|
|
|
|
|
|
|
|
|
|
SLabelProperties Props;
|
|
|
|
|
Props.m_AlignVertically = 0;
|
|
|
|
|
DoLabel(&ClearButton, "×", ClearButton.h * CUI::ms_FontmodHeight, TEXTALIGN_CENTER, Props);
|
|
|
|
|
TextRender()->SetRenderFlags(0);
|
|
|
|
|
if(DoButtonLogic(pClearID, 0, &ClearButton))
|
|
|
|
|
{
|
|
|
|
|
pStr[0] = 0;
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
ReturnValue = true;
|
|
|
|
|
}
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-27 15:22:17 +00:00
|
|
|
|
int CUI::DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, int Align)
|
|
|
|
|
{
|
|
|
|
|
pRect->Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f * ButtonColorMul(pButtonContainer)), IGraphics::CORNER_ALL, 3.0f);
|
|
|
|
|
|
|
|
|
|
CUIRect Label;
|
|
|
|
|
pRect->VMargin(2.0f, &Label);
|
|
|
|
|
SLabelProperties Props;
|
|
|
|
|
Props.m_AlignVertically = 0;
|
|
|
|
|
DoLabel(&Label, pText, 10.0f, Align, Props);
|
|
|
|
|
|
|
|
|
|
return DoButtonLogic(pButtonContainer, 0, pRect);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-07 20:03:34 +00:00
|
|
|
|
float CUI::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current)
|
|
|
|
|
{
|
|
|
|
|
Current = clamp(Current, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
// layout
|
|
|
|
|
CUIRect Rail;
|
|
|
|
|
pRect->Margin(5.0f, &Rail);
|
|
|
|
|
|
|
|
|
|
CUIRect Handle;
|
|
|
|
|
Rail.HSplitTop(clamp(33.0f, Rail.w, Rail.h / 3.0f), &Handle, 0);
|
|
|
|
|
Handle.y = Rail.y + (Rail.h - Handle.h) * Current;
|
|
|
|
|
|
|
|
|
|
// logic
|
|
|
|
|
static float s_OffsetY;
|
|
|
|
|
const bool InsideRail = MouseHovered(&Rail);
|
|
|
|
|
const bool InsideHandle = MouseHovered(&Handle);
|
|
|
|
|
bool Grabbed = false; // whether to apply the offset
|
|
|
|
|
|
|
|
|
|
if(CheckActiveItem(pID))
|
|
|
|
|
{
|
|
|
|
|
if(MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
Grabbed = true;
|
2022-11-14 21:50:59 +00:00
|
|
|
|
if(Input()->ShiftIsPressed())
|
2022-08-07 20:03:34 +00:00
|
|
|
|
m_MouseSlow = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(HotItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
s_OffsetY = MouseY() - Handle.y;
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(MouseButtonClicked(0) && !InsideHandle && InsideRail)
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
s_OffsetY = Handle.h / 2.0f;
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InsideHandle)
|
|
|
|
|
{
|
|
|
|
|
SetHotItem(pID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float ReturnValue = Current;
|
|
|
|
|
if(Grabbed)
|
|
|
|
|
{
|
|
|
|
|
const float Min = Rail.y;
|
|
|
|
|
const float Max = Rail.h - Handle.h;
|
|
|
|
|
const float Cur = MouseY() - s_OffsetY;
|
|
|
|
|
ReturnValue = clamp((Cur - Min) / Max, 0.0f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// render
|
|
|
|
|
Rail.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, Rail.w / 2.0f);
|
|
|
|
|
|
|
|
|
|
float ColorSlider;
|
|
|
|
|
if(CheckActiveItem(pID))
|
|
|
|
|
ColorSlider = 0.9f;
|
|
|
|
|
else if(HotItem() == pID)
|
|
|
|
|
ColorSlider = 1.0f;
|
|
|
|
|
else
|
|
|
|
|
ColorSlider = 0.8f;
|
|
|
|
|
|
|
|
|
|
Handle.Draw(ColorRGBA(ColorSlider, ColorSlider, ColorSlider, 1.0f), IGraphics::CORNER_ALL, Handle.w / 2.0f);
|
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float CUI::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current, const ColorRGBA *pColorInner)
|
|
|
|
|
{
|
|
|
|
|
Current = clamp(Current, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
// layout
|
|
|
|
|
CUIRect Rail;
|
|
|
|
|
if(pColorInner)
|
|
|
|
|
Rail = *pRect;
|
|
|
|
|
else
|
|
|
|
|
pRect->HMargin(5.0f, &Rail);
|
|
|
|
|
|
|
|
|
|
CUIRect Handle;
|
|
|
|
|
Rail.VSplitLeft(pColorInner ? 8.0f : clamp(33.0f, Rail.h, Rail.w / 3.0f), &Handle, 0);
|
|
|
|
|
Handle.x += (Rail.w - Handle.w) * Current;
|
|
|
|
|
|
|
|
|
|
// logic
|
|
|
|
|
static float s_OffsetX;
|
|
|
|
|
const bool InsideRail = MouseHovered(&Rail);
|
|
|
|
|
const bool InsideHandle = MouseHovered(&Handle);
|
|
|
|
|
bool Grabbed = false; // whether to apply the offset
|
|
|
|
|
|
|
|
|
|
if(CheckActiveItem(pID))
|
|
|
|
|
{
|
|
|
|
|
if(MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
Grabbed = true;
|
2022-11-14 21:50:59 +00:00
|
|
|
|
if(Input()->ShiftIsPressed())
|
2022-08-07 20:03:34 +00:00
|
|
|
|
m_MouseSlow = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(HotItem() == pID)
|
|
|
|
|
{
|
|
|
|
|
if(MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
s_OffsetX = MouseX() - Handle.x;
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(MouseButtonClicked(0) && !InsideHandle && InsideRail)
|
|
|
|
|
{
|
|
|
|
|
SetActiveItem(pID);
|
|
|
|
|
s_OffsetX = Handle.w / 2.0f;
|
|
|
|
|
Grabbed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InsideHandle)
|
|
|
|
|
{
|
|
|
|
|
SetHotItem(pID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float ReturnValue = Current;
|
|
|
|
|
if(Grabbed)
|
|
|
|
|
{
|
|
|
|
|
const float Min = Rail.x;
|
|
|
|
|
const float Max = Rail.w - Handle.w;
|
|
|
|
|
const float Cur = 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);
|
|
|
|
|
Slider.Draw(ColorRGBA(0.15f, 0.15f, 0.15f, 1.0f), IGraphics::CORNER_ALL, 5.0f);
|
|
|
|
|
Slider.Margin(2.0f, &Slider);
|
|
|
|
|
Slider.Draw(*pColorInner, IGraphics::CORNER_ALL, 3.0f);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Rail.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, Rail.h / 2.0f);
|
|
|
|
|
|
|
|
|
|
float ColorSlider;
|
|
|
|
|
if(CheckActiveItem(pID))
|
|
|
|
|
ColorSlider = 0.9f;
|
|
|
|
|
else if(HotItem() == pID)
|
|
|
|
|
ColorSlider = 1.0f;
|
|
|
|
|
else
|
|
|
|
|
ColorSlider = 0.8f;
|
|
|
|
|
|
|
|
|
|
Handle.Draw(ColorRGBA(ColorSlider, ColorSlider, ColorSlider, 1.0f), IGraphics::CORNER_ALL, Handle.h / 2.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::DoScrollbarOption(const void *pID, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, const IScrollbarScale *pScale, unsigned Flags)
|
|
|
|
|
{
|
|
|
|
|
const bool Infinite = Flags & CUI::SCROLLBAR_OPTION_INFINITE;
|
|
|
|
|
const bool NoClampValue = Flags & CUI::SCROLLBAR_OPTION_NOCLAMPVALUE;
|
|
|
|
|
dbg_assert(!(Infinite && NoClampValue), "cannot combine SCROLLBAR_OPTION_INFINITE and SCROLLBAR_OPTION_NOCLAMPVALUE");
|
|
|
|
|
|
|
|
|
|
int Value = *pOption;
|
|
|
|
|
if(Infinite)
|
|
|
|
|
{
|
|
|
|
|
Min += 1;
|
|
|
|
|
Max += 1;
|
|
|
|
|
if(Value == 0)
|
|
|
|
|
Value = Max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char aBufMax[256];
|
|
|
|
|
str_format(aBufMax, sizeof(aBufMax), "%s: %i", pStr, Max);
|
|
|
|
|
char aBuf[256];
|
|
|
|
|
if(!Infinite || Value != Max)
|
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%s: %i", pStr, Value);
|
|
|
|
|
else
|
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%s: ∞", pStr);
|
|
|
|
|
|
|
|
|
|
if(NoClampValue)
|
|
|
|
|
{
|
|
|
|
|
// clamp the value internally for the scrollbar
|
|
|
|
|
Value = clamp(Value, Min, Max);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float FontSize = pRect->h * CUI::ms_FontmodHeight * 0.8f;
|
2023-02-17 17:13:20 +00:00
|
|
|
|
float VSplitVal = 10.0f + maximum(TextRender()->TextWidth(FontSize, aBuf, -1, std::numeric_limits<float>::max()), TextRender()->TextWidth(FontSize, aBufMax, -1, std::numeric_limits<float>::max()));
|
2022-08-07 20:03:34 +00:00
|
|
|
|
|
|
|
|
|
CUIRect Label, ScrollBar;
|
|
|
|
|
pRect->VSplitLeft(VSplitVal, &Label, &ScrollBar);
|
|
|
|
|
DoLabel(&Label, aBuf, FontSize, TEXTALIGN_LEFT);
|
|
|
|
|
|
|
|
|
|
Value = pScale->ToAbsolute(DoScrollbarH(pID, &ScrollBar, pScale->ToRelative(Value, Min, Max)), Min, Max);
|
|
|
|
|
if(Infinite)
|
|
|
|
|
{
|
|
|
|
|
if(Value == Max)
|
|
|
|
|
Value = 0;
|
|
|
|
|
}
|
|
|
|
|
else if(NoClampValue)
|
|
|
|
|
{
|
|
|
|
|
if((Value == Min && *pOption < Min) || (Value == Max && *pOption > Max))
|
|
|
|
|
Value = *pOption; // use previous out of range value instead if the scrollbar is at the edge
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*pOption = Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::DoScrollbarOptionLabeled(const void *pID, int *pOption, const CUIRect *pRect, const char *pStr, const char **ppLabels, int NumLabels, const IScrollbarScale *pScale)
|
|
|
|
|
{
|
|
|
|
|
const int Max = NumLabels - 1;
|
|
|
|
|
int Value = clamp(*pOption, 0, Max);
|
|
|
|
|
|
|
|
|
|
char aBuf[256];
|
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%s: %s", pStr, ppLabels[Value]);
|
|
|
|
|
|
|
|
|
|
float FontSize = pRect->h * CUI::ms_FontmodHeight * 0.8f;
|
|
|
|
|
|
|
|
|
|
CUIRect Label, ScrollBar;
|
|
|
|
|
pRect->VSplitRight(60.0f, &Label, &ScrollBar);
|
|
|
|
|
Label.VSplitRight(10.0f, &Label, 0);
|
|
|
|
|
DoLabel(&Label, aBuf, FontSize, TEXTALIGN_LEFT);
|
|
|
|
|
|
|
|
|
|
Value = pScale->ToAbsolute(DoScrollbarH(pID, &ScrollBar, pScale->ToRelative(Value, 0, Max)), 0, Max);
|
|
|
|
|
|
|
|
|
|
if(HotItem() != pID && !CheckActiveItem(pID) && MouseHovered(pRect) && MouseButtonClicked(0))
|
|
|
|
|
Value = (Value + 1) % NumLabels;
|
|
|
|
|
|
|
|
|
|
*pOption = clamp(Value, 0, Max);
|
|
|
|
|
}
|
2023-03-27 15:22:17 +00:00
|
|
|
|
|
|
|
|
|
void CUI::DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Height, void *pContext, FPopupMenuFunction pfnFunc, int Corners)
|
|
|
|
|
{
|
|
|
|
|
constexpr float Margin = SPopupMenu::POPUP_BORDER + SPopupMenu::POPUP_MARGIN;
|
|
|
|
|
if(X + Width > Screen()->w - Margin)
|
|
|
|
|
X = maximum<float>(X - Width, Margin);
|
|
|
|
|
if(Y + Height > Screen()->h - Margin)
|
|
|
|
|
Y = maximum<float>(Y - Height, Margin);
|
|
|
|
|
|
|
|
|
|
m_vPopupMenus.emplace_back();
|
|
|
|
|
SPopupMenu *pNewMenu = &m_vPopupMenus.back();
|
|
|
|
|
pNewMenu->m_pID = pID;
|
|
|
|
|
pNewMenu->m_Rect.x = X;
|
|
|
|
|
pNewMenu->m_Rect.y = Y;
|
|
|
|
|
pNewMenu->m_Rect.w = Width;
|
|
|
|
|
pNewMenu->m_Rect.h = Height;
|
|
|
|
|
pNewMenu->m_Corners = Corners;
|
|
|
|
|
pNewMenu->m_pContext = pContext;
|
|
|
|
|
pNewMenu->m_pfnFunc = pfnFunc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::RenderPopupMenus()
|
|
|
|
|
{
|
|
|
|
|
for(size_t i = 0; i < m_vPopupMenus.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
const SPopupMenu &PopupMenu = m_vPopupMenus[i];
|
|
|
|
|
const bool Inside = MouseInside(&PopupMenu.m_Rect);
|
|
|
|
|
const bool Active = i == m_vPopupMenus.size() - 1;
|
|
|
|
|
|
|
|
|
|
if(Active)
|
|
|
|
|
SetHotItem(PopupMenu.m_pID);
|
|
|
|
|
|
|
|
|
|
if(CheckActiveItem(PopupMenu.m_pID))
|
|
|
|
|
{
|
|
|
|
|
if(!MouseButton(0))
|
|
|
|
|
{
|
|
|
|
|
if(!Inside)
|
|
|
|
|
{
|
|
|
|
|
ClosePopupMenu(PopupMenu.m_pID);
|
|
|
|
|
}
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(HotItem() == PopupMenu.m_pID)
|
|
|
|
|
{
|
|
|
|
|
if(MouseButton(0))
|
|
|
|
|
SetActiveItem(PopupMenu.m_pID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUIRect PopupRect = PopupMenu.m_Rect;
|
|
|
|
|
PopupRect.Draw(ColorRGBA(0.5f, 0.5f, 0.5f, 0.75f), PopupMenu.m_Corners, 3.0f);
|
|
|
|
|
PopupRect.Margin(SPopupMenu::POPUP_BORDER, &PopupRect);
|
|
|
|
|
PopupRect.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.75f), PopupMenu.m_Corners, 3.0f);
|
|
|
|
|
PopupRect.Margin(SPopupMenu::POPUP_MARGIN, &PopupRect);
|
|
|
|
|
|
|
|
|
|
EPopupMenuFunctionResult Result = PopupMenu.m_pfnFunc(PopupMenu.m_pContext, PopupRect, Active);
|
|
|
|
|
if(Result != POPUP_KEEP_OPEN || (Active && ConsumeHotkey(HOTKEY_ESCAPE)))
|
|
|
|
|
ClosePopupMenu(PopupMenu.m_pID, Result == POPUP_CLOSE_CURRENT_AND_DESCENDANTS);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::ClosePopupMenu(const SPopupMenuId *pID, bool IncludeDescendants)
|
|
|
|
|
{
|
|
|
|
|
auto PopupMenuToClose = std::find_if(m_vPopupMenus.begin(), m_vPopupMenus.end(), [pID](const SPopupMenu PopupMenu) { return PopupMenu.m_pID == pID; });
|
|
|
|
|
if(PopupMenuToClose != m_vPopupMenus.end())
|
|
|
|
|
{
|
|
|
|
|
if(IncludeDescendants)
|
|
|
|
|
m_vPopupMenus.erase(PopupMenuToClose, m_vPopupMenus.end());
|
|
|
|
|
else
|
|
|
|
|
m_vPopupMenus.erase(PopupMenuToClose);
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
if(m_pfnPopupMenuClosedCallback)
|
|
|
|
|
m_pfnPopupMenuClosedCallback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::ClosePopupMenus()
|
|
|
|
|
{
|
|
|
|
|
if(m_vPopupMenus.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_vPopupMenus.clear();
|
|
|
|
|
SetActiveItem(nullptr);
|
|
|
|
|
if(m_pfnPopupMenuClosedCallback)
|
|
|
|
|
m_pfnPopupMenuClosedCallback();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CUI::IsPopupOpen() const
|
|
|
|
|
{
|
|
|
|
|
return !m_vPopupMenus.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CUI::IsPopupOpen(const SPopupMenuId *pID) const
|
|
|
|
|
{
|
|
|
|
|
return std::any_of(m_vPopupMenus.begin(), m_vPopupMenus.end(), [pID](const SPopupMenu PopupMenu) { return PopupMenu.m_pID == pID; });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CUI::IsPopupHovered() const
|
|
|
|
|
{
|
|
|
|
|
return std::any_of(m_vPopupMenus.begin(), m_vPopupMenus.end(), [this](const SPopupMenu PopupMenu) { return MouseHovered(&PopupMenu.m_Rect); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::SetPopupMenuClosedCallback(FPopupMenuClosedCallback pfnCallback)
|
|
|
|
|
{
|
|
|
|
|
m_pfnPopupMenuClosedCallback = std::move(pfnCallback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::SMessagePopupContext::DefaultColor(ITextRender *pTextRender)
|
|
|
|
|
{
|
|
|
|
|
m_TextColor = pTextRender->DefaultTextColor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::SMessagePopupContext::ErrorColor()
|
|
|
|
|
{
|
|
|
|
|
m_TextColor = ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUI::EPopupMenuFunctionResult CUI::PopupMessage(void *pContext, CUIRect View, bool Active)
|
|
|
|
|
{
|
|
|
|
|
SMessagePopupContext *pMessagePopup = static_cast<SMessagePopupContext *>(pContext);
|
|
|
|
|
CUI *pUI = pMessagePopup->m_pUI;
|
|
|
|
|
|
|
|
|
|
pUI->TextRender()->TextColor(pMessagePopup->m_TextColor);
|
2023-04-09 17:57:14 +00:00
|
|
|
|
pUI->TextRender()->Text(View.x, View.y, SMessagePopupContext::POPUP_FONT_SIZE, pMessagePopup->m_aMessage, View.w);
|
2023-03-27 15:22:17 +00:00
|
|
|
|
pUI->TextRender()->TextColor(pUI->TextRender()->DefaultTextColor());
|
|
|
|
|
|
|
|
|
|
return (Active && pUI->ConsumeHotkey(HOTKEY_ENTER)) ? CUI::POPUP_CLOSE_CURRENT : CUI::POPUP_KEEP_OPEN;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-09 17:34:45 +00:00
|
|
|
|
void CUI::ShowPopupMessage(float X, float Y, SMessagePopupContext *pContext)
|
|
|
|
|
{
|
2023-04-09 18:00:21 +00:00
|
|
|
|
const float TextWidth = minimum(std::ceil(TextRender()->TextWidth(SMessagePopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f)), SMessagePopupContext::POPUP_MAX_WIDTH);
|
2023-04-09 17:34:45 +00:00
|
|
|
|
float TextHeight = 0.0f;
|
|
|
|
|
TextRender()->TextWidth(SMessagePopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, TextWidth, 0, &TextHeight);
|
|
|
|
|
pContext->m_pUI = this;
|
|
|
|
|
DoPopupMenu(pContext, X, Y, TextWidth + 10.0f, TextHeight + 10.0f, pContext, PopupMessage);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-27 15:22:17 +00:00
|
|
|
|
CUI::SConfirmPopupContext::SConfirmPopupContext()
|
|
|
|
|
{
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::SConfirmPopupContext::Reset()
|
|
|
|
|
{
|
|
|
|
|
m_Result = SConfirmPopupContext::UNSET;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::SConfirmPopupContext::YesNoButtons()
|
|
|
|
|
{
|
|
|
|
|
str_copy(m_aPositiveButtonLabel, Localize("Yes"));
|
|
|
|
|
str_copy(m_aNegativeButtonLabel, Localize("No"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::ShowPopupConfirm(float X, float Y, SConfirmPopupContext *pContext)
|
|
|
|
|
{
|
2023-04-09 18:00:21 +00:00
|
|
|
|
const float TextWidth = minimum(std::ceil(TextRender()->TextWidth(SConfirmPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f)), SConfirmPopupContext::POPUP_MAX_WIDTH);
|
2023-04-09 17:34:45 +00:00
|
|
|
|
float TextHeight = 0.0f;
|
|
|
|
|
TextRender()->TextWidth(SConfirmPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, TextWidth, 0, &TextHeight);
|
|
|
|
|
const float PopupHeight = TextHeight + SConfirmPopupContext::POPUP_BUTTON_HEIGHT + SConfirmPopupContext::POPUP_BUTTON_SPACING + 10.0f;
|
2023-03-27 15:22:17 +00:00
|
|
|
|
pContext->m_pUI = this;
|
|
|
|
|
pContext->m_Result = SConfirmPopupContext::UNSET;
|
|
|
|
|
DoPopupMenu(pContext, X, Y, TextWidth + 10.0f, PopupHeight, pContext, PopupConfirm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUI::EPopupMenuFunctionResult CUI::PopupConfirm(void *pContext, CUIRect View, bool Active)
|
|
|
|
|
{
|
|
|
|
|
SConfirmPopupContext *pConfirmPopup = static_cast<SConfirmPopupContext *>(pContext);
|
|
|
|
|
CUI *pUI = pConfirmPopup->m_pUI;
|
|
|
|
|
|
|
|
|
|
CUIRect Label, ButtonBar, CancelButton, ConfirmButton;
|
|
|
|
|
View.HSplitBottom(SConfirmPopupContext::POPUP_BUTTON_HEIGHT, &Label, &ButtonBar);
|
|
|
|
|
ButtonBar.VSplitMid(&CancelButton, &ConfirmButton, SConfirmPopupContext::POPUP_BUTTON_SPACING);
|
|
|
|
|
|
2023-04-09 17:57:14 +00:00
|
|
|
|
pUI->TextRender()->Text(Label.x, Label.y, SConfirmPopupContext::POPUP_FONT_SIZE, pConfirmPopup->m_aMessage, Label.w);
|
2023-03-27 15:22:17 +00:00
|
|
|
|
|
|
|
|
|
static CButtonContainer s_CancelButton;
|
|
|
|
|
if(pUI->DoButton_PopupMenu(&s_CancelButton, pConfirmPopup->m_aNegativeButtonLabel, &CancelButton, TEXTALIGN_CENTER))
|
|
|
|
|
{
|
|
|
|
|
pConfirmPopup->m_Result = SConfirmPopupContext::CANCELED;
|
|
|
|
|
return CUI::POPUP_CLOSE_CURRENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CButtonContainer s_ConfirmButton;
|
|
|
|
|
if(pUI->DoButton_PopupMenu(&s_ConfirmButton, pConfirmPopup->m_aPositiveButtonLabel, &ConfirmButton, TEXTALIGN_CENTER) || (Active && pUI->ConsumeHotkey(HOTKEY_ENTER)))
|
|
|
|
|
{
|
|
|
|
|
pConfirmPopup->m_Result = SConfirmPopupContext::CONFIRMED;
|
|
|
|
|
return CUI::POPUP_CLOSE_CURRENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CUI::POPUP_KEEP_OPEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUI::SSelectionPopupContext::SSelectionPopupContext()
|
|
|
|
|
{
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::SSelectionPopupContext::Reset()
|
|
|
|
|
{
|
|
|
|
|
m_pSelection = nullptr;
|
|
|
|
|
m_Entries.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUI::EPopupMenuFunctionResult CUI::PopupSelection(void *pContext, CUIRect View, bool Active)
|
|
|
|
|
{
|
|
|
|
|
SSelectionPopupContext *pSelectionPopup = static_cast<SSelectionPopupContext *>(pContext);
|
|
|
|
|
CUI *pUI = pSelectionPopup->m_pUI;
|
|
|
|
|
|
|
|
|
|
CUIRect Slot;
|
2023-04-09 17:34:45 +00:00
|
|
|
|
float TextHeight = 0.0f;
|
|
|
|
|
pUI->TextRender()->TextWidth(SSelectionPopupContext::POPUP_FONT_SIZE, pSelectionPopup->m_aMessage, -1, SSelectionPopupContext::POPUP_MAX_WIDTH, 0, &TextHeight);
|
|
|
|
|
View.HSplitTop(TextHeight, &Slot, &View);
|
2023-03-27 15:22:17 +00:00
|
|
|
|
|
2023-04-09 17:57:14 +00:00
|
|
|
|
pUI->TextRender()->Text(Slot.x, Slot.y, SSelectionPopupContext::POPUP_FONT_SIZE, pSelectionPopup->m_aMessage, Slot.w);
|
2023-03-27 15:22:17 +00:00
|
|
|
|
|
|
|
|
|
pSelectionPopup->m_vButtonContainers.resize(pSelectionPopup->m_Entries.size());
|
|
|
|
|
|
|
|
|
|
size_t Index = 0;
|
|
|
|
|
for(const auto &Entry : pSelectionPopup->m_Entries)
|
|
|
|
|
{
|
|
|
|
|
View.HSplitTop(SSelectionPopupContext::POPUP_ENTRY_SPACING, nullptr, &View);
|
|
|
|
|
View.HSplitTop(SSelectionPopupContext::POPUP_ENTRY_HEIGHT, &Slot, &View);
|
|
|
|
|
if(pUI->DoButton_PopupMenu(&pSelectionPopup->m_vButtonContainers[Index], Entry.c_str(), &Slot, TEXTALIGN_LEFT))
|
|
|
|
|
pSelectionPopup->m_pSelection = &Entry;
|
|
|
|
|
++Index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pSelectionPopup->m_pSelection == nullptr ? CUI::POPUP_KEEP_OPEN : CUI::POPUP_CLOSE_CURRENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CUI::ShowPopupSelection(float X, float Y, SSelectionPopupContext *pContext)
|
|
|
|
|
{
|
2023-04-09 17:34:45 +00:00
|
|
|
|
float TextHeight = 0.0f;
|
|
|
|
|
TextRender()->TextWidth(SSelectionPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, SSelectionPopupContext::POPUP_MAX_WIDTH, 0, &TextHeight);
|
|
|
|
|
const float PopupHeight = TextHeight + pContext->m_Entries.size() * (SSelectionPopupContext::POPUP_ENTRY_HEIGHT + SSelectionPopupContext::POPUP_ENTRY_SPACING) + 10.0f;
|
2023-03-27 15:22:17 +00:00
|
|
|
|
pContext->m_pUI = this;
|
|
|
|
|
pContext->m_pSelection = nullptr;
|
|
|
|
|
DoPopupMenu(pContext, X, Y, SSelectionPopupContext::POPUP_MAX_WIDTH + 10.0f, PopupHeight, pContext, PopupSelection);
|
|
|
|
|
}
|