2022-08-13 11:58:11 +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. */
|
|
|
|
#include <base/system.h>
|
|
|
|
#include <base/vmath.h>
|
|
|
|
|
|
|
|
#include <engine/client.h>
|
|
|
|
#include <engine/keys.h>
|
2023-01-10 17:18:59 +00:00
|
|
|
#include <engine/shared/config.h>
|
2022-08-13 11:58:11 +00:00
|
|
|
|
|
|
|
#include "ui_scrollregion.h"
|
|
|
|
|
|
|
|
CScrollRegion::CScrollRegion()
|
|
|
|
{
|
|
|
|
m_ScrollY = 0.0f;
|
|
|
|
m_ContentH = 0.0f;
|
2023-02-26 11:12:08 +00:00
|
|
|
m_AnimTimeMax = 0.0f;
|
2022-08-13 11:58:11 +00:00
|
|
|
m_AnimTime = 0.0f;
|
|
|
|
m_AnimInitScrollY = 0.0f;
|
|
|
|
m_AnimTargetScrollY = 0.0f;
|
|
|
|
m_RequestScrollY = -1.0f;
|
|
|
|
m_ContentScrollOff = vec2(0.0f, 0.0f);
|
|
|
|
m_Params = CScrollRegionParams();
|
|
|
|
}
|
|
|
|
|
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 CScrollRegion::Begin(CUIRect *pClipRect, vec2 *pOutOffset, const CScrollRegionParams *pParams)
|
2022-08-13 11:58:11 +00:00
|
|
|
{
|
|
|
|
if(pParams)
|
|
|
|
m_Params = *pParams;
|
|
|
|
|
|
|
|
const bool ContentOverflows = m_ContentH > pClipRect->h;
|
|
|
|
const bool ForceShowScrollbar = m_Params.m_Flags & CScrollRegionParams::FLAG_CONTENT_STATIC_WIDTH;
|
|
|
|
|
|
|
|
CUIRect ScrollBarBg;
|
|
|
|
bool HasScrollBar = ContentOverflows || ForceShowScrollbar;
|
|
|
|
CUIRect *pModifyRect = HasScrollBar ? pClipRect : nullptr;
|
|
|
|
pClipRect->VSplitRight(m_Params.m_ScrollbarWidth, pModifyRect, &ScrollBarBg);
|
|
|
|
ScrollBarBg.Margin(m_Params.m_ScrollbarMargin, &m_RailRect);
|
|
|
|
|
|
|
|
// only show scrollbar if required
|
|
|
|
if(HasScrollBar)
|
|
|
|
{
|
|
|
|
if(m_Params.m_ScrollbarBgColor.a > 0.0f)
|
|
|
|
ScrollBarBg.Draw(m_Params.m_ScrollbarBgColor, IGraphics::CORNER_R, 4.0f);
|
|
|
|
if(m_Params.m_RailBgColor.a > 0.0f)
|
|
|
|
m_RailRect.Draw(m_Params.m_RailBgColor, IGraphics::CORNER_ALL, m_RailRect.w / 2.0f);
|
|
|
|
}
|
|
|
|
if(!ContentOverflows)
|
|
|
|
m_ContentScrollOff.y = 0.0f;
|
|
|
|
|
|
|
|
if(m_Params.m_ClipBgColor.a > 0.0f)
|
|
|
|
pClipRect->Draw(m_Params.m_ClipBgColor, HasScrollBar ? IGraphics::CORNER_L : IGraphics::CORNER_ALL, 4.0f);
|
|
|
|
|
|
|
|
UI()->ClipEnable(pClipRect);
|
|
|
|
|
|
|
|
m_ClipRect = *pClipRect;
|
|
|
|
m_ContentH = 0.0f;
|
|
|
|
*pOutOffset = m_ContentScrollOff;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CScrollRegion::End()
|
|
|
|
{
|
|
|
|
UI()->ClipDisable();
|
|
|
|
|
|
|
|
// only show scrollbar if content overflows
|
|
|
|
if(m_ContentH <= m_ClipRect.h)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// scroll wheel
|
|
|
|
CUIRect RegionRect = m_ClipRect;
|
|
|
|
RegionRect.w += m_Params.m_ScrollbarWidth;
|
|
|
|
|
|
|
|
if(UI()->Enabled() && UI()->MouseHovered(&RegionRect))
|
|
|
|
{
|
2023-02-26 11:12:08 +00:00
|
|
|
float ScrollDirection = 0.0f;
|
2022-08-21 11:17:09 +00:00
|
|
|
if(UI()->ConsumeHotkey(CUI::HOTKEY_SCROLL_UP))
|
2023-02-26 11:12:08 +00:00
|
|
|
ScrollDirection = -1.0f;
|
2022-08-21 11:17:09 +00:00
|
|
|
else if(UI()->ConsumeHotkey(CUI::HOTKEY_SCROLL_DOWN))
|
2023-02-26 11:12:08 +00:00
|
|
|
ScrollDirection = 1.0f;
|
|
|
|
|
|
|
|
if(ScrollDirection != 0.0f)
|
2022-08-13 11:58:11 +00:00
|
|
|
{
|
2023-02-26 11:12:08 +00:00
|
|
|
const bool IsPageScroll = Input()->AltIsPressed();
|
|
|
|
const float ScrollUnit = IsPageScroll ? m_ClipRect.h : m_Params.m_ScrollUnit;
|
|
|
|
|
|
|
|
m_AnimTimeMax = g_Config.m_UiSmoothScrollTime / 1000.0f;
|
|
|
|
m_AnimTime = m_AnimTimeMax;
|
2022-08-13 11:58:11 +00:00
|
|
|
m_AnimInitScrollY = m_ScrollY;
|
2023-02-26 11:12:08 +00:00
|
|
|
m_AnimTargetScrollY += ScrollDirection * ScrollUnit;
|
2022-08-13 11:58:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const float SliderHeight = maximum(m_Params.m_SliderMinHeight, m_ClipRect.h / m_ContentH * m_RailRect.h);
|
|
|
|
|
|
|
|
CUIRect Slider = m_RailRect;
|
|
|
|
Slider.h = SliderHeight;
|
|
|
|
|
|
|
|
const float MaxSlider = m_RailRect.h - SliderHeight;
|
|
|
|
const float MaxScroll = m_ContentH - m_ClipRect.h;
|
|
|
|
|
|
|
|
if(m_RequestScrollY >= 0.0f)
|
|
|
|
{
|
|
|
|
m_AnimTargetScrollY = m_RequestScrollY;
|
|
|
|
m_AnimTime = 0.0f;
|
|
|
|
m_RequestScrollY = -1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_AnimTargetScrollY = clamp(m_AnimTargetScrollY, 0.0f, MaxScroll);
|
|
|
|
|
|
|
|
if(absolute(m_AnimInitScrollY - m_AnimTargetScrollY) < 0.5f)
|
|
|
|
m_AnimTime = 0.0f;
|
|
|
|
|
|
|
|
if(m_AnimTime > 0.0f)
|
|
|
|
{
|
|
|
|
m_AnimTime -= Client()->RenderFrameTime();
|
2023-02-28 20:31:40 +00:00
|
|
|
float AnimProgress = (1.0f - std::pow(m_AnimTime / m_AnimTimeMax, 3.0f)); // cubic ease out
|
2022-08-13 11:58:11 +00:00
|
|
|
m_ScrollY = m_AnimInitScrollY + (m_AnimTargetScrollY - m_AnimInitScrollY) * AnimProgress;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ScrollY = m_AnimTargetScrollY;
|
|
|
|
}
|
|
|
|
|
|
|
|
Slider.y += m_ScrollY / MaxScroll * MaxSlider;
|
|
|
|
|
|
|
|
bool Hovered = false;
|
|
|
|
bool Grabbed = false;
|
|
|
|
const void *pID = &m_ScrollY;
|
|
|
|
const bool InsideSlider = UI()->MouseHovered(&Slider);
|
|
|
|
const bool InsideRail = UI()->MouseHovered(&m_RailRect);
|
|
|
|
|
|
|
|
if(UI()->CheckActiveItem(pID) && UI()->MouseButton(0))
|
|
|
|
{
|
|
|
|
float MouseY = UI()->MouseY();
|
|
|
|
m_ScrollY += (MouseY - (Slider.y + m_SliderGrabPos.y)) / MaxSlider * MaxScroll;
|
|
|
|
m_SliderGrabPos.y = clamp(m_SliderGrabPos.y, 0.0f, SliderHeight);
|
|
|
|
m_AnimTargetScrollY = m_ScrollY;
|
|
|
|
m_AnimTime = 0.0f;
|
|
|
|
Grabbed = true;
|
|
|
|
}
|
|
|
|
else if(InsideSlider)
|
|
|
|
{
|
|
|
|
UI()->SetHotItem(pID);
|
|
|
|
|
|
|
|
if(!UI()->CheckActiveItem(pID) && UI()->MouseButtonClicked(0))
|
|
|
|
{
|
|
|
|
UI()->SetActiveItem(pID);
|
|
|
|
m_SliderGrabPos.y = UI()->MouseY() - Slider.y;
|
|
|
|
m_AnimTargetScrollY = m_ScrollY;
|
|
|
|
m_AnimTime = 0.0f;
|
|
|
|
}
|
|
|
|
Hovered = true;
|
|
|
|
}
|
|
|
|
else if(InsideRail && UI()->MouseButtonClicked(0))
|
|
|
|
{
|
|
|
|
m_ScrollY += (UI()->MouseY() - (Slider.y + Slider.h / 2.0f)) / MaxSlider * MaxScroll;
|
|
|
|
UI()->SetActiveItem(pID);
|
|
|
|
m_SliderGrabPos.y = Slider.h / 2.0f;
|
|
|
|
m_AnimTargetScrollY = m_ScrollY;
|
|
|
|
m_AnimTime = 0.0f;
|
|
|
|
Hovered = true;
|
|
|
|
}
|
|
|
|
else if(UI()->CheckActiveItem(pID) && !UI()->MouseButton(0))
|
|
|
|
{
|
|
|
|
UI()->SetActiveItem(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ScrollY = clamp(m_ScrollY, 0.0f, MaxScroll);
|
|
|
|
m_ContentScrollOff.y = -m_ScrollY;
|
|
|
|
|
|
|
|
Slider.Draw(m_Params.SliderColor(Grabbed, Hovered), IGraphics::CORNER_ALL, Slider.w / 2.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CScrollRegion::AddRect(const CUIRect &Rect, bool ShouldScrollHere)
|
|
|
|
{
|
|
|
|
m_LastAddedRect = Rect;
|
|
|
|
// Round up and add 1 to fix pixel clipping at the end of the scrolling area
|
2023-02-28 20:31:40 +00:00
|
|
|
m_ContentH = maximum(std::ceil(Rect.y + Rect.h - (m_ClipRect.y + m_ContentScrollOff.y)) + 1.0f, m_ContentH);
|
2022-08-13 11:58:11 +00:00
|
|
|
if(ShouldScrollHere)
|
|
|
|
ScrollHere();
|
|
|
|
return !IsRectClipped(Rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CScrollRegion::ScrollHere(EScrollOption Option)
|
|
|
|
{
|
|
|
|
const float MinHeight = minimum(m_ClipRect.h, m_LastAddedRect.h);
|
|
|
|
const float TopScroll = m_LastAddedRect.y - (m_ClipRect.y + m_ContentScrollOff.y);
|
|
|
|
|
|
|
|
switch(Option)
|
|
|
|
{
|
|
|
|
case SCROLLHERE_TOP:
|
|
|
|
m_RequestScrollY = TopScroll;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SCROLLHERE_BOTTOM:
|
|
|
|
m_RequestScrollY = TopScroll - (m_ClipRect.h - MinHeight);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SCROLLHERE_KEEP_IN_VIEW:
|
|
|
|
default:
|
|
|
|
const float DeltaY = m_LastAddedRect.y - m_ClipRect.y;
|
|
|
|
if(DeltaY < 0)
|
|
|
|
m_RequestScrollY = TopScroll;
|
|
|
|
else if(DeltaY > (m_ClipRect.h - MinHeight))
|
|
|
|
m_RequestScrollY = TopScroll - (m_ClipRect.h - MinHeight);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CScrollRegion::IsRectClipped(const CUIRect &Rect) const
|
|
|
|
{
|
|
|
|
return (m_ClipRect.x > (Rect.x + Rect.w) || (m_ClipRect.x + m_ClipRect.w) < Rect.x || m_ClipRect.y > (Rect.y + Rect.h) || (m_ClipRect.y + m_ClipRect.h) < Rect.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CScrollRegion::IsScrollbarShown() const
|
|
|
|
{
|
|
|
|
return m_ContentH > m_ClipRect.h;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CScrollRegion::IsAnimating() const
|
|
|
|
{
|
|
|
|
return m_AnimTime > 0.0f;
|
|
|
|
}
|