From a90c86e9a5d757a0ee90da7cd6686cc2cc5e5c7e Mon Sep 17 00:00:00 2001 From: Fireball Date: Fri, 5 Aug 2022 00:16:44 +0100 Subject: [PATCH 01/17] Parallax-aware zoom How this works: parallax values configure perceived distance from camera when it's moving along x and y axes. Assume that zoom is moving the camera away and scale layers accordingly, with background layers (furtherst away) changing the least. New per-ItemGroup (LayerGroup) setting allows to set the new parallax value independently from the other two. This can be used to do tricks like on Time Shop zoom correctly or make it feel like the camera is changing the field of view at the same time as moving in space. --- src/engine/shared/config_variables.h | 1 - src/game/client/components/hud.cpp | 2 +- src/game/client/components/maplayers.cpp | 7 +------ src/game/client/components/nameplates.cpp | 4 ++-- src/game/client/render.cpp | 17 +++++++++++++++-- src/game/client/render.h | 5 +++-- src/game/editor/editor.cpp | 10 ++++++---- src/game/editor/editor.h | 1 + src/game/editor/io.cpp | 4 ++++ src/game/editor/popups.cpp | 4 ++++ src/game/layers.cpp | 5 +++++ src/game/mapitems.h | 14 +++++++++++++- 12 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index e5a13a3f9..61c566805 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -322,7 +322,6 @@ MACRO_CONFIG_INT(ClShowOthers, cl_show_others, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG MACRO_CONFIG_INT(ClShowOthersAlpha, cl_show_others_alpha, 40, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show players in other teams (alpha value, 0 invisible, 100 fully visible)") MACRO_CONFIG_INT(ClOverlayEntities, cl_overlay_entities, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Overlay game tiles with a percentage of opacity") MACRO_CONFIG_INT(ClShowQuads, cl_showquads, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show quads (only interesting for mappers, or if your system has extremely bad performance)") -MACRO_CONFIG_INT(ClZoomBackgroundLayers, cl_zoom_background_layers, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom background layers") MACRO_CONFIG_COL(ClBackgroundColor, cl_background_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background color") // 0 0 128 MACRO_CONFIG_COL(ClBackgroundEntitiesColor, cl_background_entities_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background (entities) color") // 0 0 128 MACRO_CONFIG_STR(ClBackgroundEntities, cl_background_entities, 100, "", CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background (entities)") diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 7dcbf7e95..a02e11365 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -621,7 +621,7 @@ void CHud::RenderCursor() if(!m_pClient->m_Snap.m_pLocalCharacter || Client()->State() == IClient::STATE_DEMOPLAYBACK) return; - RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup()); + RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); // render cursor int CurWeapon = m_pClient->m_Snap.m_pLocalCharacter->m_Weapon % NUM_WEAPONS; diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 6c21603af..f54834742 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -1592,12 +1592,7 @@ void CMapLayers::OnRender() (int)((x1 - x0) * Graphics()->ScreenWidth()), (int)((y1 - y0) * Graphics()->ScreenHeight())); } - if((!g_Config.m_ClZoomBackgroundLayers || m_Type == TYPE_FULL_DESIGN) && !pGroup->m_ParallaxX && !pGroup->m_ParallaxY) - { - RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, 1.0f); - } - else - RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, GetCurCamera()->m_Zoom); + RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, GetCurCamera()->m_Zoom); for(int l = 0; l < pGroup->m_NumLayers; l++) { diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index 60c42a642..0519ab641 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -101,7 +101,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP // create nameplates at standard zoom float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup()); + RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); m_aNamePlates[ClientID].m_NameTextWidth = TextRender()->TextWidth(0, FontSize, pName, -1, -1.0f); @@ -126,7 +126,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP // create nameplates at standard zoom float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup()); + RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); m_aNamePlates[ClientID].m_ClanNameTextWidth = TextRender()->TextWidth(0, FontSizeClan, pClan, -1, -1.0f); diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index 825e7bbc0..6d0a1deeb 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -783,10 +783,15 @@ void CRenderTools::CalcScreenParams(float Aspect, float Zoom, float *pWidth, flo } void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, - float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints) + float ParallaxZoom, float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints) { float Width, Height; CalcScreenParams(Aspect, Zoom, &Width, &Height); + + float Scale = (ParallaxZoom * (Zoom - 1.0f) + 100.0f) / 100.0f / Zoom; + Width *= Scale; + Height *= Scale; + CenterX *= ParallaxX / 100.0f; CenterY *= ParallaxY / 100.0f; pPoints[0] = OffsetX + CenterX - Width / 2; @@ -798,7 +803,15 @@ void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float Parallax void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom) { float aPoints[4]; - MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, + MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, pGroup->GetParallaxZoom(), pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), Zoom, aPoints); Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); } + +void CRenderTools::MapScreenToInterface(float CenterX, float CenterY) +{ + float aPoints[4]; + MapScreenToWorld(CenterX, CenterY, 100.0f, 100.0f, 100.0f, + 0, 0, Graphics()->ScreenAspect(), 1.0f, aPoints); + Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); +} diff --git a/src/game/client/render.h b/src/game/client/render.h index bc63d031b..2675a4187 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -142,8 +142,9 @@ public: // helpers void CalcScreenParams(float Aspect, float Zoom, float *pWidth, float *pHeight); void MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, - float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints); - void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom = 1.0f); + float ParallaxZoom, float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints); + void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom); + void MapScreenToInterface(float CenterX, float CenterY); // DDRace diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index ac556a6ab..cf0bf744b 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -102,6 +102,7 @@ CLayerGroup::CLayerGroup() m_OffsetY = 0; m_ParallaxX = 100; m_ParallaxY = 100; + m_ParallaxZoom = 100; m_UseClipping = 0; m_ClipX = 0; @@ -133,7 +134,7 @@ void CLayerGroup::Mapping(float *pPoints) { m_pMap->m_pEditor->RenderTools()->MapScreenToWorld( m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY, - m_ParallaxX, m_ParallaxY, m_OffsetX, m_OffsetY, + m_ParallaxX, m_ParallaxY, 100.0f, m_OffsetX, m_OffsetY, m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints); pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX; @@ -2870,7 +2871,7 @@ void CEditor::DoMapEditor(CUIRect View) RenderTools()->MapScreenToWorld( m_WorldOffsetX, m_WorldOffsetY, - 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); + 100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); if(i == 0) { @@ -2912,7 +2913,7 @@ void CEditor::DoMapEditor(CUIRect View) RenderTools()->MapScreenToWorld( m_WorldOffsetX, m_WorldOffsetY, - 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); + 100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); CUIRect r; r.x = aPoints[0]; @@ -6118,7 +6119,7 @@ void CEditor::ZoomMouseTarget(float ZoomFactor) float aPoints[4]; RenderTools()->MapScreenToWorld( m_WorldOffsetX, m_WorldOffsetY, - 100.0f, 100.0f, 0.0f, 0.0f, Graphics()->ScreenAspect(), m_WorldZoom, aPoints); + 100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Graphics()->ScreenAspect(), m_WorldZoom, aPoints); float WorldWidth = aPoints[2] - aPoints[0]; float WorldHeight = aPoints[3] - aPoints[1]; @@ -6230,6 +6231,7 @@ void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture) CLayerGroup *pGroup = NewGroup(); pGroup->m_ParallaxX = 0; pGroup->m_ParallaxY = 0; + pGroup->m_ParallaxZoom = 0; CLayerQuads *pLayer = new CLayerQuads; pLayer->m_pEditor = m_pEditor; CQuad *pQuad = pLayer->NewQuad(0, 0, 1600, 1200); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 0c0ec15af..b0244ba38 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -176,6 +176,7 @@ public: int m_ParallaxX; int m_ParallaxY; + int m_ParallaxZoom; int m_UseClipping; int m_ClipX; diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 3b441fc28..5aeb82f9a 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -184,6 +184,8 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) // save group name StrToInts(GItem.m_aName, sizeof(GItem.m_aName) / sizeof(int), pGroup->m_aName); + GItem.m_ParallaxZoom = pGroup->m_ParallaxZoom; + for(const auto &pLayer : pGroup->m_vpLayers) { if(pLayer->m_Type == LAYERTYPE_TILES) @@ -607,6 +609,8 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pGItem->m_Version >= 3) IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName) / sizeof(int), pGroup->m_aName); + pGroup->m_ParallaxZoom = pGItem->GetParallaxZoom(); + for(int l = 0; l < pGItem->m_NumLayers; l++) { CLayer *pLayer = nullptr; diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 42648aab4..4b63412f8 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -320,6 +320,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) PROP_POS_Y, PROP_PARA_X, PROP_PARA_Y, + PROP_PARA_ZOOM, PROP_USE_CLIPPING, PROP_CLIP_X, PROP_CLIP_Y, @@ -334,6 +335,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) {"Pos Y", -pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Para X", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Para Y", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para Zoom", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Use Clipping", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_UseClipping, PROPTYPE_BOOL, 0, 1}, {"Clip X", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, @@ -364,6 +366,8 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX = NewVal; else if(Prop == PROP_PARA_Y) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY = NewVal; + else if(Prop == PROP_PARA_ZOOM) + pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom = NewVal; else if(Prop == PROP_POS_X) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetX = -NewVal; else if(Prop == PROP_POS_Y) diff --git a/src/game/layers.cpp b/src/game/layers.cpp index ef28343a9..d4024777a 100644 --- a/src/game/layers.cpp +++ b/src/game/layers.cpp @@ -66,6 +66,8 @@ void CLayers::Init(class IKernel *pKernel) m_pGameGroup->m_ClipH = 0; } + if(m_pGameGroup->m_Version >= 4) + m_pGameGroup->m_ParallaxZoom = 100; //break; } if(pTilemap->m_Flags & TILESLAYERFLAG_TELE) @@ -158,6 +160,9 @@ void CLayers::InitBackground(class IMap *pMap) m_pGameGroup->m_ClipW = 0; m_pGameGroup->m_ClipH = 0; } + + if(m_pGameGroup->m_Version >= 4) + m_pGameGroup->m_ParallaxZoom = 100; //We don't care about tile layers. } } diff --git a/src/game/mapitems.h b/src/game/mapitems.h index 64ac181fe..664c59461 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -278,7 +278,7 @@ struct CMapItemGroup : public CMapItemGroup_v1 { enum { - CURRENT_VERSION = 3 + CURRENT_VERSION = 4 }; int m_UseClipping; @@ -288,6 +288,18 @@ struct CMapItemGroup : public CMapItemGroup_v1 int m_ClipH; int m_aName[3]; + + // ItemGroup's perceived distance from camera when zooming. Similar to how + // Parallax{X,Y} works when camera is moving along the X and Y axes, + // this setting applies to camera moving closer or away (zooming in or out). + int m_ParallaxZoom; + int GetParallaxZoom() const + { + if(m_Version >= 4) + return m_ParallaxZoom; + else + return maximum(m_ParallaxX, m_ParallaxY); + } }; struct CMapItemLayer From 5bf7f60bf693d19e9682639f5b6ef3e191f2afec Mon Sep 17 00:00:00 2001 From: Fireball Date: Sat, 6 Aug 2022 01:31:42 +0100 Subject: [PATCH 02/17] Parallax Zoom: Add default zoom option to the editor Safe defaults for map editing: unless opted-in, Parallax Zoom will default to maximum(parallax{x,y}). --- src/game/editor/editor.cpp | 2 +- src/game/editor/editor.h | 7 +++++++ src/game/editor/io.cpp | 1 + src/game/editor/popups.cpp | 9 +++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index cf0bf744b..d502bc467 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -3345,7 +3345,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) static int s_GroupPopupId = 0; if(Result == 2) - UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 230, PopupGroup); + UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 256, PopupGroup); if(!m_Map.m_vpGroups[g]->m_vpLayers.empty() && Input()->MouseDoubleClick()) m_Map.m_vpGroups[g]->m_Collapse ^= 1; diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index b0244ba38..5361598a4 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -176,6 +176,7 @@ public: int m_ParallaxX; int m_ParallaxY; + int m_CustomParallaxZoom; int m_ParallaxZoom; int m_UseClipping; @@ -242,6 +243,12 @@ public: } }*/ + void OnEdited() + { + if(!m_CustomParallaxZoom) + m_ParallaxZoom = maximum(m_ParallaxX, m_ParallaxY); + } + void Clear() { m_vpLayers.clear(); diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 5aeb82f9a..6a466080f 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -610,6 +610,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName) / sizeof(int), pGroup->m_aName); pGroup->m_ParallaxZoom = pGItem->GetParallaxZoom(); + pGroup->m_CustomParallaxZoom = pGroup->m_ParallaxZoom != maximum(pGroup->m_ParallaxX, pGroup->m_ParallaxY); for(int l = 0; l < pGItem->m_NumLayers; l++) { diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 4b63412f8..efab59632 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -320,6 +320,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) PROP_POS_Y, PROP_PARA_X, PROP_PARA_Y, + PROP_CUSTOM_ZOOM, PROP_PARA_ZOOM, PROP_USE_CLIPPING, PROP_CLIP_X, @@ -335,6 +336,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) {"Pos Y", -pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Para X", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Para Y", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Custom Zoom", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom, PROPTYPE_BOOL, 0, 1}, {"Para Zoom", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Use Clipping", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_UseClipping, PROPTYPE_BOOL, 0, 1}, @@ -366,8 +368,13 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX = NewVal; else if(Prop == PROP_PARA_Y) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY = NewVal; + else if(Prop == PROP_CUSTOM_ZOOM) + pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom = NewVal; else if(Prop == PROP_PARA_ZOOM) + { + pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom = 1; pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom = NewVal; + } else if(Prop == PROP_POS_X) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetX = -NewVal; else if(Prop == PROP_POS_Y) @@ -382,6 +389,8 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipW = NewVal; else if(Prop == PROP_CLIP_H) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipH = NewVal; + + pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->OnEdited(); } return 0; From 54f7a9817d0f2072647317ec2f177e9ba4c21996 Mon Sep 17 00:00:00 2001 From: Fireball Date: Sat, 6 Aug 2022 02:30:37 +0100 Subject: [PATCH 03/17] Parallax Zoom: Zoom preview button in Editor Known issue: Quad Env Points are scaled incorrectly for Groups with ParallaxZoom != 100 (in Zoom mode). --- src/game/editor/editor.cpp | 16 +++++++++++++++- src/game/editor/editor.h | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index d502bc467..2c3b004ec 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -132,9 +132,11 @@ void CLayerGroup::Convert(CUIRect *pRect) void CLayerGroup::Mapping(float *pPoints) { + float ParallaxZoom = m_pMap->m_pEditor->m_PreviewZoom ? m_ParallaxZoom : 100.0f; + m_pMap->m_pEditor->RenderTools()->MapScreenToWorld( m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY, - m_ParallaxX, m_ParallaxY, 100.0f, m_OffsetX, m_OffsetY, + m_ParallaxX, m_ParallaxY, ParallaxZoom, m_OffsetX, m_OffsetY, m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints); pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX; @@ -865,6 +867,17 @@ void CEditor::DoToolbar(CUIRect ToolBar) TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top); + // zoom button + TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); + static int s_ZoomButton = 0; + // TODO: do we need a keyboard shortcut? + if(DoButton_Editor(&s_ZoomButton, "Zoom", m_PreviewZoom, &Button, 0, "Toggles preview of how layers will be zoomed in-game")) + { + m_PreviewZoom = !m_PreviewZoom; + } + + TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top); + // grid button TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); static int s_GridButton = 0; @@ -2708,6 +2721,7 @@ void CEditor::DoMapEditor(CUIRect View) m_Brush.m_OffsetY += pGroup->m_OffsetY; m_Brush.m_ParallaxX = pGroup->m_ParallaxX; m_Brush.m_ParallaxY = pGroup->m_ParallaxY; + m_Brush.m_ParallaxZoom = pGroup->m_ParallaxZoom; m_Brush.Render(); float w, h; m_Brush.GetSize(&w, &h); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 5361598a4..dbfff6033 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -781,6 +781,7 @@ public: m_GuiActive = true; m_ProofBorders = false; + m_PreviewZoom = false; m_ShowTileInfo = false; m_ShowDetail = true; @@ -954,6 +955,7 @@ public: bool m_ShowMousePointer; bool m_GuiActive; bool m_ProofBorders; + bool m_PreviewZoom; float m_MouseX = 0.0f; float m_MouseY = 0.0f; float m_MouseWorldX = 0.0f; From 4ed997f304e223fa4490423319ed7495c83984c0 Mon Sep 17 00:00:00 2001 From: Fireball Date: Sat, 6 Aug 2022 16:33:58 +0100 Subject: [PATCH 04/17] Parallax Zoom: fix QuadEnvPoint scaling (and other UI distances) --- src/game/editor/editor.cpp | 56 ++++++++++++++++++++------------------ src/game/editor/editor.h | 1 + 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 2c3b004ec..ad2a58b76 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -870,7 +870,6 @@ void CEditor::DoToolbar(CUIRect ToolBar) // zoom button TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); static int s_ZoomButton = 0; - // TODO: do we need a keyboard shortcut? if(DoButton_Editor(&s_ZoomButton, "Zoom", m_PreviewZoom, &Button, 0, "Toggles preview of how layers will be zoomed in-game")) { m_PreviewZoom = !m_PreviewZoom; @@ -1248,8 +1247,8 @@ void CEditor::DoSoundSource(CSoundSource *pSource, int Index) float CenterX = fx2f(pSource->m_Position.x); float CenterY = fx2f(pSource->m_Position.y); - float dx = (CenterX - wx) / m_WorldZoom; - float dy = (CenterY - wy) / m_WorldZoom; + float dx = (CenterX - wx) / m_MouseWScale; + float dy = (CenterY - wy) / m_MouseWScale; if(dx * dx + dy * dy < 50) UI()->SetHotItem(pID); @@ -1341,7 +1340,7 @@ void CEditor::DoSoundSource(CSoundSource *pSource, int Index) Graphics()->SetColor(0, 1, 0, 1); } - IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom); + IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale); Graphics()->QuadsDraw(&QuadItem, 1); } @@ -1369,8 +1368,8 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) float CenterX = fx2f(pQuad->m_aPoints[4].x); float CenterY = fx2f(pQuad->m_aPoints[4].y); - float dx = (CenterX - wx) / m_WorldZoom; - float dy = (CenterY - wy) / m_WorldZoom; + float dx = (CenterX - wx) / m_MouseWScale; + float dy = (CenterY - wy) / m_MouseWScale; if(dx * dx + dy * dy < 50) UI()->SetHotItem(pID); @@ -1380,7 +1379,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) if(IsQuadSelected(Index)) { Graphics()->SetColor(0, 0, 0, 1); - IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f * m_WorldZoom, 7.0f * m_WorldZoom); + IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale); Graphics()->QuadsDraw(&QuadItem, 1); } @@ -1596,7 +1595,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) else Graphics()->SetColor(0, 1, 0, 1); - IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom); + IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale); Graphics()->QuadsDraw(&QuadItem, 1); } @@ -1610,8 +1609,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) float px = fx2f(pQuad->m_aPoints[V].x); float py = fx2f(pQuad->m_aPoints[V].y); - float dx = (px - wx) / m_WorldZoom; - float dy = (py - wy) / m_WorldZoom; + float dx = (px - wx) / m_MouseWScale; + float dy = (py - wy) / m_MouseWScale; if(dx * dx + dy * dy < 50) UI()->SetHotItem(pID); @@ -1619,7 +1618,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) if(IsQuadSelected(QuadIndex) && m_SelectedPoints & (1 << V)) { Graphics()->SetColor(0, 0, 0, 1); - IGraphics::CQuadItem QuadItem(px, py, 7.0f * m_WorldZoom, 7.0f * m_WorldZoom); + IGraphics::CQuadItem QuadItem(px, py, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale); Graphics()->QuadsDraw(&QuadItem, 1); } @@ -1803,7 +1802,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) else Graphics()->SetColor(1, 0, 0, 1); - IGraphics::CQuadItem QuadItem(px, py, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom); + IGraphics::CQuadItem QuadItem(px, py, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale); Graphics()->QuadsDraw(&QuadItem, 1); } @@ -1839,7 +1838,7 @@ void CEditor::DoQuadKnife(int QuadIndex) CQuad *pQuad = &pLayer->m_vQuads[QuadIndex]; bool IgnoreGrid = Input()->KeyIsPressed(KEY_LALT) || Input()->KeyIsPressed(KEY_RALT); - float SnapRadius = 4.f * m_WorldZoom; + float SnapRadius = 4.f * m_MouseWScale; vec2 Mouse = vec2(UI()->MouseWorldX(), UI()->MouseWorldY()); vec2 Point = Mouse; @@ -2040,14 +2039,14 @@ void CEditor::DoQuadKnife(int QuadIndex) IGraphics::CQuadItem aMarkers[4]; for(int i = 0; i < m_QuadKnifeCount; i++) - aMarkers[i] = IGraphics::CQuadItem(m_aQuadKnifePoints[i].x, m_aQuadKnifePoints[i].y, 5.f * m_WorldZoom, 5.f * m_WorldZoom); + aMarkers[i] = IGraphics::CQuadItem(m_aQuadKnifePoints[i].x, m_aQuadKnifePoints[i].y, 5.f * m_MouseWScale, 5.f * m_MouseWScale); Graphics()->SetColor(0.f, 0.f, 1.f, 1.f); Graphics()->QuadsDraw(aMarkers, m_QuadKnifeCount); if(ValidPosition) { - IGraphics::CQuadItem MarkerCurrent(Point.x, Point.y, 5.f * m_WorldZoom, 5.f * m_WorldZoom); + IGraphics::CQuadItem MarkerCurrent(Point.x, Point.y, 5.f * m_MouseWScale, 5.f * m_MouseWScale); Graphics()->QuadsDraw(&MarkerCurrent, 1); } @@ -2194,8 +2193,8 @@ void CEditor::DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int PIndex) float CenterX = fx2f(pQuad->m_aPoints[4].x) + fx2f(pEnvelope->m_vPoints[PIndex].m_aValues[0]); float CenterY = fx2f(pQuad->m_aPoints[4].y) + fx2f(pEnvelope->m_vPoints[PIndex].m_aValues[1]); - float dx = (CenterX - wx) / m_WorldZoom; - float dy = (CenterY - wy) / m_WorldZoom; + float dx = (CenterX - wx) / m_MouseWScale; + float dy = (CenterY - wy) / m_MouseWScale; if(dx * dx + dy * dy < 50.0f && UI()->CheckActiveItem(nullptr)) { UI()->SetHotItem(pID); @@ -2287,7 +2286,7 @@ void CEditor::DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int PIndex) else Graphics()->SetColor(0.0f, 1.0f, 0.0f, 1.0f); - IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom); + IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale); Graphics()->QuadsDraw(&QuadItem, 1); } @@ -2798,13 +2797,13 @@ void CEditor::DoMapEditor(CUIRect View) { if(s_Operation == OP_PAN_WORLD) { - m_WorldOffsetX -= m_MouseDeltaX * m_WorldZoom; - m_WorldOffsetY -= m_MouseDeltaY * m_WorldZoom; + m_WorldOffsetX -= m_MouseDeltaX * m_MouseWScale; + m_WorldOffsetY -= m_MouseDeltaY * m_MouseWScale; } else if(s_Operation == OP_PAN_EDITOR) { - m_EditorOffsetX -= m_MouseDeltaX * m_WorldZoom; - m_EditorOffsetY -= m_MouseDeltaY * m_WorldZoom; + m_EditorOffsetX -= m_MouseDeltaX * m_MouseWScale; + m_EditorOffsetY -= m_MouseDeltaY * m_MouseWScale; } // release mouse @@ -2820,13 +2819,13 @@ void CEditor::DoMapEditor(CUIRect View) { float PanSpeed = 64.0f; if(Input()->KeyPress(KEY_A)) - m_WorldOffsetX -= PanSpeed * m_WorldZoom; + m_WorldOffsetX -= PanSpeed * m_MouseWScale; else if(Input()->KeyPress(KEY_D)) - m_WorldOffsetX += PanSpeed * m_WorldZoom; + m_WorldOffsetX += PanSpeed * m_MouseWScale; if(Input()->KeyPress(KEY_W)) - m_WorldOffsetY -= PanSpeed * m_WorldZoom; + m_WorldOffsetY -= PanSpeed * m_MouseWScale; else if(Input()->KeyPress(KEY_S)) - m_WorldOffsetY += PanSpeed * m_WorldZoom; + m_WorldOffsetY += PanSpeed * m_MouseWScale; } } else if(UI()->CheckActiveItem(s_pEditorID)) @@ -6418,6 +6417,11 @@ void CEditor::OnUpdate() float WorldWidth = aPoints[2] - aPoints[0]; float WorldHeight = aPoints[3] - aPoints[1]; + m_MouseWScale = WorldWidth / Graphics()->WindowWidth(); + // The correct thing would be to skip all UI elements for group scaled to 0, but that's a rare and not particularly important situation so we can just have this hack to avoid crashes + if(abs(m_MouseWScale) < 0.000001f) + m_MouseWScale = 1.0f; + m_MouseWorldX = aPoints[0] + WorldWidth * (s_MouseX / Graphics()->WindowWidth()); m_MouseWorldY = aPoints[1] + WorldHeight * (s_MouseY / Graphics()->WindowHeight()); m_MouseDeltaWx = m_MouseDeltaX * (WorldWidth / Graphics()->WindowWidth()); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index dbfff6033..097b36fff 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -956,6 +956,7 @@ public: bool m_GuiActive; bool m_ProofBorders; bool m_PreviewZoom; + float m_MouseWScale = 1.0f; // Mouse (i.e. UI) scale relative to the World (selected Group) float m_MouseX = 0.0f; float m_MouseY = 0.0f; float m_MouseWorldX = 0.0f; From 5d886d80f8b8452f94a20753f23b86b1a3c4df23 Mon Sep 17 00:00:00 2001 From: Fireball Date: Wed, 10 Aug 2022 02:51:03 +0100 Subject: [PATCH 05/17] Parallax Zoom: use CMapItemGroupEx to store parallax value --- src/game/client/components/maplayers.cpp | 5 ++-- src/game/client/render.cpp | 6 +++-- src/game/client/render.h | 3 ++- src/game/editor/editor.cpp | 4 +--- src/game/editor/editor.h | 3 ++- src/game/editor/io.cpp | 18 +++++++++++---- src/game/layers.cpp | 29 ++++++++++++++++++++---- src/game/layers.h | 6 +++++ src/game/mapitems.h | 14 +----------- src/game/mapitems_ex.cpp | 14 ++++++++++++ src/game/mapitems_ex.h | 20 ++++++++++++++++ src/game/mapitems_ex_types.h | 1 + 12 files changed, 93 insertions(+), 30 deletions(-) diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index f54834742..1a4e8a222 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -1561,6 +1561,7 @@ void CMapLayers::OnRender() for(int g = 0; g < m_pLayers->NumGroups(); g++) { CMapItemGroup *pGroup = m_pLayers->GetGroup(g); + CMapItemGroupEx *pGroupEx = m_pLayers->GetGroupEx(g); if(!pGroup) { @@ -1574,7 +1575,7 @@ void CMapLayers::OnRender() { // set clipping float aPoints[4]; - RenderTools()->MapScreenToGroup(Center.x, Center.y, m_pLayers->GameGroup(), GetCurCamera()->m_Zoom); + RenderTools()->MapScreenToGroup(Center.x, Center.y, m_pLayers->GameGroup(), m_pLayers->GameGroupEx(), GetCurCamera()->m_Zoom); Graphics()->GetScreen(&aPoints[0], &aPoints[1], &aPoints[2], &aPoints[3]); float x0 = (pGroup->m_ClipX - aPoints[0]) / (aPoints[2] - aPoints[0]); float y0 = (pGroup->m_ClipY - aPoints[1]) / (aPoints[3] - aPoints[1]); @@ -1592,7 +1593,7 @@ void CMapLayers::OnRender() (int)((x1 - x0) * Graphics()->ScreenWidth()), (int)((y1 - y0) * Graphics()->ScreenHeight())); } - RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, GetCurCamera()->m_Zoom); + RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, pGroupEx, GetCurCamera()->m_Zoom); for(int l = 0; l < pGroup->m_NumLayers; l++) { diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index 6d0a1deeb..c17df3785 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -15,6 +15,7 @@ #include #include +#include static float gs_SpriteWScale; static float gs_SpriteHScale; @@ -800,10 +801,11 @@ void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float Parallax pPoints[3] = pPoints[1] + Height; } -void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom) +void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, CMapItemGroupEx *pGroupEx, float Zoom) { + float ParallaxZoom = GetParallaxZoom(pGroup, pGroupEx); float aPoints[4]; - MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, pGroup->GetParallaxZoom(), + MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, ParallaxZoom, pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), Zoom, aPoints); Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); } diff --git a/src/game/client/render.h b/src/game/client/render.h index 2675a4187..94f7811d8 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -21,6 +21,7 @@ struct CDataSprite; struct CDataSprite; struct CEnvPoint; struct CMapItemGroup; +struct CMapItemGroupEx; struct CQuad; class CTeeRenderInfo @@ -143,7 +144,7 @@ public: void CalcScreenParams(float Aspect, float Zoom, float *pWidth, float *pHeight); void MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, float ParallaxZoom, float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints); - void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom); + void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, CMapItemGroupEx *pGroupEx, float Zoom); void MapScreenToInterface(float CenterX, float CenterY); // DDRace diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index ad2a58b76..b102defeb 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -6244,6 +6244,7 @@ void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture) CLayerGroup *pGroup = NewGroup(); pGroup->m_ParallaxX = 0; pGroup->m_ParallaxY = 0; + pGroup->m_CustomParallaxZoom = 0; pGroup->m_ParallaxZoom = 0; CLayerQuads *pLayer = new CLayerQuads; pLayer->m_pEditor = m_pEditor; @@ -6418,9 +6419,6 @@ void CEditor::OnUpdate() float WorldHeight = aPoints[3] - aPoints[1]; m_MouseWScale = WorldWidth / Graphics()->WindowWidth(); - // The correct thing would be to skip all UI elements for group scaled to 0, but that's a rare and not particularly important situation so we can just have this hack to avoid crashes - if(abs(m_MouseWScale) < 0.000001f) - m_MouseWScale = 1.0f; m_MouseWorldX = aPoints[0] + WorldWidth * (s_MouseX / Graphics()->WindowWidth()); m_MouseWorldY = aPoints[1] + WorldHeight * (s_MouseY / Graphics()->WindowHeight()); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 097b36fff..276dcea8e 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -246,7 +247,7 @@ public: void OnEdited() { if(!m_CustomParallaxZoom) - m_ParallaxZoom = maximum(m_ParallaxX, m_ParallaxY); + m_ParallaxZoom = GetParallaxZoomDefault(m_ParallaxX, m_ParallaxY); } void Clear() diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 6a466080f..39401f74b 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -184,7 +184,9 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) // save group name StrToInts(GItem.m_aName, sizeof(GItem.m_aName) / sizeof(int), pGroup->m_aName); - GItem.m_ParallaxZoom = pGroup->m_ParallaxZoom; + CMapItemGroupEx GItemEx; + GItemEx.m_Version = CMapItemGroupEx::CURRENT_VERSION; + GItemEx.m_ParallaxZoom = pGroup->m_ParallaxZoom; for(const auto &pLayer : pGroup->m_vpLayers) { @@ -333,7 +335,9 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) } } - df.AddItem(MAPITEMTYPE_GROUP, GroupCount++, sizeof(GItem), &GItem); + df.AddItem(MAPITEMTYPE_GROUP, GroupCount, sizeof(GItem), &GItem); + df.AddItem(MAPITEMTYPE_GROUP_EX, GroupCount, sizeof(GItemEx), &GItemEx); + GroupCount++; } // save envelopes @@ -583,9 +587,15 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag int Start, Num; DataFile.GetType(MAPITEMTYPE_GROUP, &Start, &Num); + + int StartEx, NumEx; + DataFile.GetType(MAPITEMTYPE_GROUP_EX, &StartEx, &NumEx); for(int g = 0; g < Num; g++) { CMapItemGroup *pGItem = (CMapItemGroup *)DataFile.GetItem(Start + g, nullptr, nullptr); + CMapItemGroupEx *pGItemEx = nullptr; + if(NumEx) + pGItemEx = (CMapItemGroupEx *)DataFile.GetItem(StartEx + g, nullptr, nullptr); if(pGItem->m_Version < 1 || pGItem->m_Version > CMapItemGroup::CURRENT_VERSION) continue; @@ -609,8 +619,8 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pGItem->m_Version >= 3) IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName) / sizeof(int), pGroup->m_aName); - pGroup->m_ParallaxZoom = pGItem->GetParallaxZoom(); - pGroup->m_CustomParallaxZoom = pGroup->m_ParallaxZoom != maximum(pGroup->m_ParallaxX, pGroup->m_ParallaxY); + pGroup->m_ParallaxZoom = GetParallaxZoom(pGItem, pGItemEx); + pGroup->m_CustomParallaxZoom = pGroup->m_ParallaxZoom != GetParallaxZoomDefault(pGroup->m_ParallaxX, pGroup->m_ParallaxY); for(int l = 0; l < pGItem->m_NumLayers; l++) { diff --git a/src/game/layers.cpp b/src/game/layers.cpp index d4024777a..ab384c657 100644 --- a/src/game/layers.cpp +++ b/src/game/layers.cpp @@ -3,6 +3,7 @@ #include "layers.h" #include "mapitems.h" +#include "mapitems_ex.h" #include @@ -10,9 +11,12 @@ CLayers::CLayers() { m_GroupsNum = 0; m_GroupsStart = 0; + m_GroupsExNum = 0; + m_GroupsExStart = 0; m_LayersNum = 0; m_LayersStart = 0; m_pGameGroup = 0; + m_pGameGroupEx = 0; m_pGameLayer = 0; m_pMap = 0; @@ -27,6 +31,7 @@ void CLayers::Init(class IKernel *pKernel) { m_pMap = pKernel->RequestInterface(); m_pMap->GetType(MAPITEMTYPE_GROUP, &m_GroupsStart, &m_GroupsNum); + m_pMap->GetType(MAPITEMTYPE_GROUP_EX, &m_GroupsExStart, &m_GroupsExNum); m_pMap->GetType(MAPITEMTYPE_LAYER, &m_LayersStart, &m_LayersNum); m_pTeleLayer = 0; @@ -38,6 +43,7 @@ void CLayers::Init(class IKernel *pKernel) for(int g = 0; g < NumGroups(); g++) { CMapItemGroup *pGroup = GetGroup(g); + CMapItemGroupEx *pGroupEx = GetGroupEx(g); for(int l = 0; l < pGroup->m_NumLayers; l++) { CMapItemLayer *pLayer = GetLayer(pGroup->m_StartLayer + l); @@ -50,6 +56,7 @@ void CLayers::Init(class IKernel *pKernel) { m_pGameLayer = pTilemap; m_pGameGroup = pGroup; + m_pGameGroupEx = pGroupEx; // make sure the game group has standard settings m_pGameGroup->m_OffsetX = 0; @@ -66,8 +73,9 @@ void CLayers::Init(class IKernel *pKernel) m_pGameGroup->m_ClipH = 0; } - if(m_pGameGroup->m_Version >= 4) - m_pGameGroup->m_ParallaxZoom = 100; + if(pGroupEx) + pGroupEx->m_ParallaxZoom = 100; + //break; } if(pTilemap->m_Flags & TILESLAYERFLAG_TELE) @@ -121,6 +129,7 @@ void CLayers::InitBackground(class IMap *pMap) { m_pMap = pMap; m_pMap->GetType(MAPITEMTYPE_GROUP, &m_GroupsStart, &m_GroupsNum); + m_pMap->GetType(MAPITEMTYPE_GROUP_EX, &m_GroupsExStart, &m_GroupsExNum); m_pMap->GetType(MAPITEMTYPE_LAYER, &m_LayersStart, &m_LayersNum); //following is here to prevent crash using standard map as background @@ -133,6 +142,7 @@ void CLayers::InitBackground(class IMap *pMap) for(int g = 0; g < NumGroups(); g++) { CMapItemGroup *pGroup = GetGroup(g); + CMapItemGroupEx *pGroupEx = GetGroupEx(g); for(int l = 0; l < pGroup->m_NumLayers; l++) { CMapItemLayer *pLayer = GetLayer(pGroup->m_StartLayer + l); @@ -145,6 +155,7 @@ void CLayers::InitBackground(class IMap *pMap) { m_pGameLayer = pTilemap; m_pGameGroup = pGroup; + m_pGameGroupEx = pGroupEx; // make sure the game group has standard settings m_pGameGroup->m_OffsetX = 0; @@ -161,8 +172,9 @@ void CLayers::InitBackground(class IMap *pMap) m_pGameGroup->m_ClipH = 0; } - if(m_pGameGroup->m_Version >= 4) - m_pGameGroup->m_ParallaxZoom = 100; + if(pGroupEx) + pGroupEx->m_ParallaxZoom = 100; + //We don't care about tile layers. } } @@ -211,6 +223,15 @@ CMapItemGroup *CLayers::GetGroup(int Index) const return static_cast(m_pMap->GetItem(m_GroupsStart + Index, 0, 0)); } +CMapItemGroupEx *CLayers::GetGroupEx(int Index) const +{ + // Map doesn't have GroupEx items or GroupEx indexes don't match groups for some other reason. Lets turn them off completely to be safe. + if(m_GroupsExNum != m_GroupsNum) + return nullptr; + + return static_cast(m_pMap->GetItem(m_GroupsExStart + Index, 0, 0)); +} + CMapItemLayer *CLayers::GetLayer(int Index) const { return static_cast(m_pMap->GetItem(m_LayersStart + Index, 0, 0)); diff --git a/src/game/layers.h b/src/game/layers.h index 1823dea9b..59e112267 100644 --- a/src/game/layers.h +++ b/src/game/layers.h @@ -7,6 +7,7 @@ class IKernel; class IMap; struct CMapItemGroup; +struct CMapItemGroupEx; struct CMapItemLayer; struct CMapItemLayerTilemap; @@ -14,9 +15,12 @@ class CLayers { int m_GroupsNum; int m_GroupsStart; + int m_GroupsExNum; + int m_GroupsExStart; int m_LayersNum; int m_LayersStart; CMapItemGroup *m_pGameGroup; + CMapItemGroupEx *m_pGameGroupEx; CMapItemLayerTilemap *m_pGameLayer; IMap *m_pMap; @@ -30,8 +34,10 @@ public: int NumLayers() const { return m_LayersNum; } IMap *Map() const { return m_pMap; } CMapItemGroup *GameGroup() const { return m_pGameGroup; } + CMapItemGroupEx *GameGroupEx() const { return m_pGameGroupEx; } CMapItemLayerTilemap *GameLayer() const { return m_pGameLayer; } CMapItemGroup *GetGroup(int Index) const; + CMapItemGroupEx *GetGroupEx(int Index) const; CMapItemLayer *GetLayer(int Index) const; // DDRace diff --git a/src/game/mapitems.h b/src/game/mapitems.h index 664c59461..64ac181fe 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -278,7 +278,7 @@ struct CMapItemGroup : public CMapItemGroup_v1 { enum { - CURRENT_VERSION = 4 + CURRENT_VERSION = 3 }; int m_UseClipping; @@ -288,18 +288,6 @@ struct CMapItemGroup : public CMapItemGroup_v1 int m_ClipH; int m_aName[3]; - - // ItemGroup's perceived distance from camera when zooming. Similar to how - // Parallax{X,Y} works when camera is moving along the X and Y axes, - // this setting applies to camera moving closer or away (zooming in or out). - int m_ParallaxZoom; - int GetParallaxZoom() const - { - if(m_Version >= 4) - return m_ParallaxZoom; - else - return maximum(m_ParallaxX, m_ParallaxY); - } }; struct CMapItemLayer diff --git a/src/game/mapitems_ex.cpp b/src/game/mapitems_ex.cpp index 68c29a3e2..fe35d4127 100644 --- a/src/game/mapitems_ex.cpp +++ b/src/game/mapitems_ex.cpp @@ -1,7 +1,21 @@ #include "mapitems_ex.h" +#include #include +int GetParallaxZoom(const CMapItemGroup *pGroup, const CMapItemGroupEx *pGroupEx) +{ + if(pGroupEx) + return pGroupEx->m_ParallaxZoom; + + return GetParallaxZoomDefault(pGroup->m_ParallaxX, pGroup->m_ParallaxY); +} + +int GetParallaxZoomDefault(int ParallaxX, int ParallaxY) +{ + return clamp(maximum(ParallaxX, ParallaxY), 0, 100); +} + void RegisterMapItemTypeUuids(CUuidManager *pManager) { #define UUID(id, name) pManager->RegisterName(id, name); diff --git a/src/game/mapitems_ex.h b/src/game/mapitems_ex.h index c67b36218..5486e240f 100644 --- a/src/game/mapitems_ex.h +++ b/src/game/mapitems_ex.h @@ -2,6 +2,8 @@ #define GAME_MAPITEMS_EX_H #include +#include "mapitems.h" + enum { __MAPITEMTYPE_UUID_HELPER = OFFSET_MAPITEMTYPE_UUID - 1, @@ -43,5 +45,23 @@ struct CMapItemAutoMapperConfig int m_Flags; }; +struct CMapItemGroupEx +{ + enum + { + CURRENT_VERSION = 1 + }; + + int m_Version; + + // ItemGroup's perceived distance from camera when zooming. Similar to how + // Parallax{X,Y} works when camera is moving along the X and Y axes, + // this setting applies to camera moving closer or away (zooming in or out). + int m_ParallaxZoom; +}; + +int GetParallaxZoom(const CMapItemGroup *pGroup, const CMapItemGroupEx *pGroupEx); +int GetParallaxZoomDefault(int ParallaxX, int ParallaxY); + void RegisterMapItemTypeUuids(class CUuidManager *pManager); #endif // GAME_MAPITEMS_EX_H diff --git a/src/game/mapitems_ex_types.h b/src/game/mapitems_ex_types.h index 116f9da97..304d4e5d1 100644 --- a/src/game/mapitems_ex_types.h +++ b/src/game/mapitems_ex_types.h @@ -2,3 +2,4 @@ UUID(MAPITEMTYPE_TEST, "mapitemtype-test@ddnet.tw") UUID(MAPITEMTYPE_AUTOMAPPER_CONFIG, "mapitemtype-automapper-config@ddnet.tw") +UUID(MAPITEMTYPE_GROUP_EX, "mapitemtype-group@ddnet.tw") From 991b045a11285210f9f43fb04ff531ed85b3d4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 11:36:17 +0200 Subject: [PATCH 06/17] Remove unused `CUIElement::m_ElementTime` --- src/game/client/ui.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 9c314fe4a..875a905c4 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -131,9 +131,6 @@ protected: CUI *UI() const { return m_pUI; } std::vector m_vUIRects; - // used for marquees or other user implemented things - int64_t m_ElementTime; - public: CUIElement() = default; From 9cbca642eac1ca8d1e342769813f281f57be6dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 11:40:04 +0200 Subject: [PATCH 07/17] Rename `CUIElement::Get` to `Rect` This makes it clear that the method is a getter for the rects. --- src/game/client/components/menus.h | 16 +++---- src/game/client/components/menus_browser.cpp | 46 ++++++++++---------- src/game/client/ui.h | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 7536785f3..6a9065791 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -113,14 +113,14 @@ class CMenus : public CComponent Text.HMargin(pRect->h >= 20.0f ? 2.0f : 1.0f, &Text); Text.HMargin((Text.h * FontFactor) / 2.0f, &Text); - if(!UIElement.AreRectsInit() || HintRequiresStringCheck || HintCanChangePositionOrSize || UIElement.Get(0)->m_UITextContainer == -1) + if(!UIElement.AreRectsInit() || HintRequiresStringCheck || HintCanChangePositionOrSize || UIElement.Rect(0)->m_UITextContainer == -1) { - bool NeedsRecalc = !UIElement.AreRectsInit() || UIElement.Get(0)->m_UITextContainer == -1; + bool NeedsRecalc = !UIElement.AreRectsInit() || UIElement.Rect(0)->m_UITextContainer == -1; if(HintCanChangePositionOrSize) { if(UIElement.AreRectsInit()) { - if(UIElement.Get(0)->m_X != pRect->x || UIElement.Get(0)->m_Y != pRect->y || UIElement.Get(0)->m_Width != pRect->w || UIElement.Get(0)->m_Y != pRect->h) + if(UIElement.Rect(0)->m_X != pRect->x || UIElement.Rect(0)->m_Y != pRect->y || UIElement.Rect(0)->m_Width != pRect->w || UIElement.Rect(0)->m_Y != pRect->h) { NeedsRecalc = true; } @@ -132,7 +132,7 @@ class CMenus : public CComponent if(UIElement.AreRectsInit()) { pText = GetTextLambda(); - if(str_comp(UIElement.Get(0)->m_Text.c_str(), pText) != 0) + if(str_comp(UIElement.Rect(0)->m_Text.c_str(), pText) != 0) { NeedsRecalc = true; } @@ -158,7 +158,7 @@ class CMenus : public CComponent Color.a *= UI()->ButtonColorMulDefault(); Graphics()->SetColor(Color); - CUIElement::SUIElementRect &NewRect = *UIElement.Get(i); + CUIElement::SUIElementRect &NewRect = *UIElement.Rect(i); NewRect.m_UIRectQuadContainer = Graphics()->CreateRectQuadContainer(pRect->x, pRect->y, pRect->w, pRect->h, r, Corners); NewRect.m_X = pRect->x; @@ -185,11 +185,11 @@ class CMenus : public CComponent else if(UI()->HotItem() == pID) Index = 1; Graphics()->TextureClear(); - Graphics()->RenderQuadContainer(UIElement.Get(Index)->m_UIRectQuadContainer, -1); + Graphics()->RenderQuadContainer(UIElement.Rect(Index)->m_UIRectQuadContainer, -1); ColorRGBA ColorText(TextRender()->DefaultTextColor()); ColorRGBA ColorTextOutline(TextRender()->DefaultTextOutlineColor()); - if(UIElement.Get(0)->m_UITextContainer != -1) - TextRender()->RenderTextContainer(UIElement.Get(0)->m_UITextContainer, ColorText, ColorTextOutline); + if(UIElement.Rect(0)->m_UITextContainer != -1) + TextRender()->RenderTextContainer(UIElement.Rect(0)->m_UITextContainer, ColorText, ColorTextOutline); return UI()->DoButtonLogic(pID, Checked, pRect); } diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 8c85802ae..3c67629f8 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -286,13 +286,13 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { CUIRect r = Row; r.Margin(0.5f, &r); - pItem->m_pUIElement->Get(0)->Draw(&r, ColorRGBA(1, 1, 1, 0.5f), IGraphics::CORNER_ALL, 4.0f); + pItem->m_pUIElement->Rect(0)->Draw(&r, ColorRGBA(1, 1, 1, 0.5f), IGraphics::CORNER_ALL, 4.0f); } else if(UI()->MouseHovered(&Row)) { CUIRect r = Row; r.Margin(0.5f, &r); - pItem->m_pUIElement->Get(0)->Draw(&r, ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 4.0f); + pItem->m_pUIElement->Rect(0)->Draw(&r, ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 4.0f); } if(UI()->DoButtonLogic(pItem, Selected, &Row)) @@ -328,22 +328,22 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { if(pItem->m_Flags & SERVER_FLAG_PASSWORD) { - RenderBrowserIcons(*pItem->m_pUIElement->Get(gs_OffsetColFlagLock + 0), &Button, {0.75f, 0.75f, 0.75f, 1}, TextRender()->DefaultTextOutlineColor(), "\xEF\x80\xA3", TEXTALIGN_CENTER); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFlagLock + 0), &Button, {0.75f, 0.75f, 0.75f, 1}, TextRender()->DefaultTextOutlineColor(), "\xEF\x80\xA3", TEXTALIGN_CENTER); } } else if(ID == COL_FLAG_FAV) { if(pItem->m_Favorite != TRISTATE::NONE) { - RenderBrowserIcons(*pItem->m_pUIElement->Get(gs_OffsetColFav + 0), &Button, {0.94f, 0.4f, 0.4f, 1}, TextRender()->DefaultTextOutlineColor(), "\xEF\x80\x84", TEXTALIGN_CENTER); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFav + 0), &Button, {0.94f, 0.4f, 0.4f, 1}, TextRender()->DefaultTextOutlineColor(), "\xEF\x80\x84", TEXTALIGN_CENTER); } } else if(ID == COL_FLAG_OFFICIAL) { if(pItem->m_Official && g_Config.m_UiPage != PAGE_DDNET && g_Config.m_UiPage != PAGE_KOG) { - RenderBrowserIcons(*pItem->m_pUIElement->Get(gs_OffsetColOff + 0), &Button, {0.4f, 0.7f, 0.94f, 1}, {0.0f, 0.0f, 0.0f, 1.0f}, "\xEF\x82\xA3", TEXTALIGN_CENTER); - RenderBrowserIcons(*pItem->m_pUIElement->Get(gs_OffsetColOff + 1), &Button, {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, "\xEF\x80\x8C", TEXTALIGN_CENTER, true); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColOff + 0), &Button, {0.4f, 0.7f, 0.94f, 1}, {0.0f, 0.0f, 0.0f, 1.0f}, "\xEF\x82\xA3", TEXTALIGN_CENTER); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColOff + 1), &Button, {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, "\xEF\x80\x8C", TEXTALIGN_CENTER, true); } } else if(ID == COL_NAME) @@ -356,17 +356,17 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) const char *pStr = str_utf8_find_nocase(pItem->m_aName, g_Config.m_BrFilterString); if(pStr) { - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColName + 0), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pStr - pItem->m_aName)); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 0), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pStr - pItem->m_aName)); TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColName + 1), &Button, pStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)str_length(g_Config.m_BrFilterString), &pItem->m_pUIElement->Get(gs_OffsetColName + 0)->m_Cursor); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 1), &Button, pStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)str_length(g_Config.m_BrFilterString), &pItem->m_pUIElement->Rect(gs_OffsetColName + 0)->m_Cursor); TextRender()->TextColor(1, 1, 1, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColName + 2), &Button, pStr + str_length(g_Config.m_BrFilterString), FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Get(gs_OffsetColName + 1)->m_Cursor); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 2), &Button, pStr + str_length(g_Config.m_BrFilterString), FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Rect(gs_OffsetColName + 1)->m_Cursor); } else - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColName), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); } else - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColName), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); } else if(ID == COL_MAP) { @@ -379,7 +379,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(g_Config.m_BrIndicateFinished && pItem->m_HasRank == 1) { - RenderBrowserIcons(*pItem->m_pUIElement->Get(gs_OffsetColFlagLock + 1), &Icon, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), "\xEF\x84\x9E", TEXTALIGN_CENTER); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFlagLock + 1), &Icon, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), "\xEF\x84\x9E", TEXTALIGN_CENTER); } } @@ -391,17 +391,17 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) const char *pStr = str_utf8_find_nocase(pItem->m_aMap, g_Config.m_BrFilterString); if(pStr) { - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColMap + 0), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pStr - pItem->m_aMap)); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 0), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pStr - pItem->m_aMap)); TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColMap + 1), &Button, pStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)str_length(g_Config.m_BrFilterString), &pItem->m_pUIElement->Get(gs_OffsetColMap + 0)->m_Cursor); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 1), &Button, pStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)str_length(g_Config.m_BrFilterString), &pItem->m_pUIElement->Rect(gs_OffsetColMap + 0)->m_Cursor); TextRender()->TextColor(1, 1, 1, 1); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColMap + 2), &Button, pStr + str_length(g_Config.m_BrFilterString), FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Get(gs_OffsetColMap + 1)->m_Cursor); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 2), &Button, pStr + str_length(g_Config.m_BrFilterString), FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Rect(gs_OffsetColMap + 1)->m_Cursor); } else - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColMap), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); } else - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColMap), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); } else if(ID == COL_PLAYERS) { @@ -411,14 +411,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { Button.VSplitLeft(Button.h, &Icon, &Button); Icon.Margin(2.0f, &Icon); - RenderBrowserIcons(*pItem->m_pUIElement->Get(gs_OffsetColFav + 1), &Icon, {0.94f, 0.4f, 0.4f, 1}, TextRender()->DefaultTextOutlineColor(), "\xEF\x80\x84", TEXTALIGN_LEFT); + RenderBrowserIcons(*pItem->m_pUIElement->Rect(gs_OffsetColFav + 1), &Icon, {0.94f, 0.4f, 0.4f, 1}, TextRender()->DefaultTextOutlineColor(), "\xEF\x80\x84", TEXTALIGN_LEFT); } str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumFilteredPlayers, ServerBrowser()->Max(*pItem)); if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_PLAYER)) TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); float FontSize = 12.0f; - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColPlayers), &Button, aTemp, FontSize, TEXTALIGN_RIGHT, -1, 1, false); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColPlayers), &Button, aTemp, FontSize, TEXTALIGN_RIGHT, -1, 1, false); TextRender()->TextColor(1, 1, 1, 1); } else if(ID == COL_PING) @@ -432,14 +432,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } float FontSize = 12.0f; - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColPing), &Button, aTemp, FontSize, TEXTALIGN_RIGHT, -1, 1, false); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColPing), &Button, aTemp, FontSize, TEXTALIGN_RIGHT, -1, 1, false); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } else if(ID == COL_VERSION) { const char *pVersion = pItem->m_aVersion; float FontSize = 12.0f; - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColVersion), &Button, pVersion, FontSize, TEXTALIGN_RIGHT, -1, 1, false); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColVersion), &Button, pVersion, FontSize, TEXTALIGN_RIGHT, -1, 1, false); } else if(ID == COL_GAMETYPE) { @@ -466,11 +466,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) ColorRGBA rgb = color_cast(hsl); TextRender()->TextColor(rgb); - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } else - UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(gs_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_LEFT, Button.w, 1, true); } } } diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 875a905c4..2aaae2258 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -136,7 +136,7 @@ public: void Init(CUI *pUI, int RequestedRectCount); - SUIElementRect *Get(size_t Index) + SUIElementRect *Rect(size_t Index) { return &m_vUIRects[Index]; } From f451a333611e93b9c46e1c58b031161480d1b584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 13:58:11 +0200 Subject: [PATCH 08/17] Port `CScrollRegion` from upstream with some minor refactorings Co-authored-by: LordSk --- CMakeLists.txt | 2 + src/game/client/gameclient.cpp | 2 + src/game/client/ui.cpp | 8 + src/game/client/ui.h | 15 ++ src/game/client/ui_scrollregion.cpp | 220 ++++++++++++++++++++++++++++ src/game/client/ui_scrollregion.h | 123 ++++++++++++++++ src/game/editor/editor.cpp | 2 + 7 files changed, 372 insertions(+) create mode 100644 src/game/client/ui_scrollregion.cpp create mode 100644 src/game/client/ui_scrollregion.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 95c038a30..40080509e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2103,6 +2103,8 @@ if(CLIENT) ui.h ui_rect.cpp ui_rect.h + ui_scrollregion.cpp + ui_scrollregion.h ) set_src(GAME_EDITOR GLOB src/game/editor auto_map.cpp diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 0a93d6b0d..2a8c307fd 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -374,6 +374,8 @@ void CGameClient::OnInit() void CGameClient::OnUpdate() { + CUIElementBase::Init(UI()); // update static pointer because game and editor use separate UI + // handle mouse movement float x = 0.0f, y = 0.0f; IInput::ECursorType CursorType = Input()->CursorRelative(&x, &y); diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index f16741002..8775ef35d 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -82,6 +82,13 @@ const CLinearScrollbarScale CUI::ms_LinearScrollbarScale; const CLogarithmicScrollbarScale CUI::ms_LogarithmicScrollbarScale(25); float CUI::ms_FontmodHeight = 0.8f; +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(); } + void CUI::Init(IKernel *pKernel) { m_pClient = pKernel->RequestInterface(); @@ -90,6 +97,7 @@ void CUI::Init(IKernel *pKernel) m_pTextRender = pKernel->RequestInterface(); InitInputs(m_pInput->GetEventsRaw(), m_pInput->GetEventCountRaw()); CUIRect::Init(m_pGraphics); + CUIElementBase::Init(this); } void CUI::InitInputs(IInput::CEvent *pInputEventsArray, int *pInputEventCount) diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 2aaae2258..5dbdc6949 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -158,6 +158,21 @@ struct SLabelProperties bool m_EnableWidthCheck = true; }; +class CUIElementBase +{ +private: + static CUI *s_pUI; + +public: + static void Init(CUI *pUI) { s_pUI = pUI; } + + IClient *Client() const; + IGraphics *Graphics() const; + IInput *Input() const; + ITextRender *TextRender() const; + CUI *UI() const { return s_pUI; } +}; + class CButtonContainer { }; diff --git a/src/game/client/ui_scrollregion.cpp b/src/game/client/ui_scrollregion.cpp new file mode 100644 index 000000000..91ceb588f --- /dev/null +++ b/src/game/client/ui_scrollregion.cpp @@ -0,0 +1,220 @@ +/* (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 +#include + +#include +#include + +#include "ui_scrollregion.h" + +CScrollRegion::CScrollRegion() +{ + m_ScrollY = 0.0f; + m_ContentH = 0.0f; + 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(); +} + +void CScrollRegion::Begin(CUIRect *pClipRect, vec2 *pOutOffset, CScrollRegionParams *pParams) +{ + 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; + + const float AnimationDuration = 0.5f; + + if(UI()->Enabled() && UI()->MouseHovered(&RegionRect)) + { + const bool IsPageScroll = Input()->KeyIsPressed(KEY_LALT) || Input()->KeyIsPressed(KEY_RALT); + const float ScrollUnit = IsPageScroll ? m_ClipRect.h : m_Params.m_ScrollUnit; + if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) + { + m_AnimTime = AnimationDuration; + m_AnimInitScrollY = m_ScrollY; + m_AnimTargetScrollY -= ScrollUnit; + } + else if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) + { + m_AnimTime = AnimationDuration; + m_AnimInitScrollY = m_ScrollY; + m_AnimTargetScrollY += ScrollUnit; + } + } + + 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(); + float AnimProgress = (1.0f - powf(m_AnimTime / AnimationDuration, 3.0f)); // cubic ease out + 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 + m_ContentH = maximum(ceilf(Rect.y + Rect.h - (m_ClipRect.y + m_ContentScrollOff.y)) + 1.0f, m_ContentH); + 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; +} diff --git a/src/game/client/ui_scrollregion.h b/src/game/client/ui_scrollregion.h new file mode 100644 index 000000000..b63b1f7bd --- /dev/null +++ b/src/game/client/ui_scrollregion.h @@ -0,0 +1,123 @@ +/* (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. */ +#ifndef GAME_CLIENT_UI_SCROLLREGION_H +#define GAME_CLIENT_UI_SCROLLREGION_H + +#include "ui.h" + +struct CScrollRegionParams +{ + float m_ScrollbarWidth; + float m_ScrollbarMargin; + float m_SliderMinHeight; + float m_ScrollUnit; + ColorRGBA m_ClipBgColor; + ColorRGBA m_ScrollbarBgColor; + ColorRGBA m_RailBgColor; + ColorRGBA m_SliderColor; + ColorRGBA m_SliderColorHover; + ColorRGBA m_SliderColorGrabbed; + unsigned m_Flags; + + enum + { + FLAG_CONTENT_STATIC_WIDTH = 1 << 0, + }; + + CScrollRegionParams() + { + m_ScrollbarWidth = 20.0f; + m_ScrollbarMargin = 5.0f; + m_SliderMinHeight = 25.0f; + m_ScrollUnit = 10.0f; + m_ClipBgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f); + m_ScrollbarBgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f); + m_RailBgColor = ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f); + m_SliderColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f); + m_SliderColorHover = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + m_SliderColorGrabbed = ColorRGBA(0.9f, 0.9f, 0.9f, 1.0f); + m_Flags = 0; + } + + ColorRGBA SliderColor(bool Active, bool Hovered) const + { + if(Active) + return m_SliderColorGrabbed; + else if(Hovered) + return m_SliderColorHover; + return m_SliderColor; + } +}; + +/* +Usage: + -- Initialization -- + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0, 0); + s_ScrollRegion.Begin(&ScrollRegionRect, &ScrollOffset); + Content = ScrollRegionRect; + Content.y += ScrollOffset.y; + + -- "Register" your content rects -- + CUIRect Rect; + Content.HSplitTop(SomeValue, &Rect, &Content); + s_ScrollRegion.AddRect(Rect); + + -- [Optional] Knowing if a rect is clipped -- + s_ScrollRegion.IsRectClipped(Rect); + + -- [Optional] Scroll to a rect (to the last added rect)-- + ... + s_ScrollRegion.AddRect(Rect); + s_ScrollRegion.ScrollHere(Option); + + -- [Convenience] Add rect and check for visibility at the same time + if(s_ScrollRegion.AddRect(Rect)) + // The rect is visible (not clipped) + + -- [Convenience] Add rect and scroll to it if it's selected + if(s_ScrollRegion.AddRect(Rect, ScrollToSelection && IsSelected)) + // The rect is visible (not clipped) + + -- End -- + s_ScrollRegion.End(); +*/ + +// Instances of CScrollRegion must be static, as member addresses are used as UI item IDs +class CScrollRegion : private CUIElementBase +{ +private: + float m_ScrollY; + float m_ContentH; + float m_RequestScrollY; // [0, ContentHeight] + + float m_AnimTime; + float m_AnimInitScrollY; + float m_AnimTargetScrollY; + + CUIRect m_ClipRect; + CUIRect m_RailRect; + CUIRect m_LastAddedRect; // saved for ScrollHere() + vec2 m_SliderGrabPos; // where did user grab the slider + vec2 m_ContentScrollOff; + CScrollRegionParams m_Params; + +public: + enum EScrollOption + { + SCROLLHERE_KEEP_IN_VIEW = 0, + SCROLLHERE_TOP, + SCROLLHERE_BOTTOM, + }; + + CScrollRegion(); + void Begin(CUIRect *pClipRect, vec2 *pOutOffset, CScrollRegionParams *pParams = nullptr); + void End(); + bool AddRect(const CUIRect &Rect, bool ShouldScrollHere = false); // returns true if the added rect is visible (not clipped) + void ScrollHere(EScrollOption Option = SCROLLHERE_KEEP_IN_VIEW); + bool IsRectClipped(const CUIRect &Rect) const; + bool IsScrollbarShown() const; + bool IsAnimating() const; +}; + +#endif diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 56f2b3c0a..5096ea261 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -6392,6 +6392,8 @@ void CEditor::PlaceBorderTiles() void CEditor::OnUpdate() { + CUIElementBase::Init(UI()); // update static pointer because game and editor use separate UI + if(!m_EditorWasUsedBefore) { m_EditorWasUsedBefore = true; From 7b80ad1982307f510c83dd240851f60340b6912d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 13:59:01 +0200 Subject: [PATCH 09/17] Refactor editor layer image selection popup using `CScrollRegion` The width of the popup and list are also slightly increased to better support images with longer names. --- src/game/editor/popups.cpp | 59 ++++++++++++-------------------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 755dea853..5a3863cbf 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "editor.h" // popup menu handling @@ -1247,54 +1249,29 @@ static int g_SelectImageCurrent = -100; int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View, void *pContext) { CUIRect ButtonBar, ImageView; - View.VSplitLeft(80.0f, &ButtonBar, &View); + View.VSplitLeft(150.0f, &ButtonBar, &View); View.Margin(10.0f, &ImageView); int ShowImage = g_SelectImageCurrent; - static float s_ScrollValue = 0; - float ImagesHeight = pEditor->m_Map.m_vpImages.size() * 14; - float ScrollDifference = ImagesHeight - ButtonBar.h; + const float RowHeight = 14.0f; + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollbarWidth = 10.0f; + ScrollParams.m_ScrollbarMargin = 3.0f; + ScrollParams.m_ScrollUnit = RowHeight * 5; + s_ScrollRegion.Begin(&ButtonBar, &ScrollOffset, &ScrollParams); + ButtonBar.y += ScrollOffset.y; - if(pEditor->m_Map.m_vpImages.size() > 20) // Do we need a scrollbar? - { - CUIRect Scroll; - ButtonBar.VSplitRight(20.0f, &ButtonBar, &Scroll); - s_ScrollValue = pEditor->UI()->DoScrollbarV(&s_ScrollValue, &Scroll, s_ScrollValue); - - if(pEditor->UI()->MouseInside(&Scroll) || pEditor->UI()->MouseInside(&ButtonBar)) - { - int ScrollNum = (int)((ImagesHeight - ButtonBar.h) / 14.0f) + 1; - if(ScrollNum > 0) - { - if(pEditor->Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) - s_ScrollValue = clamp(s_ScrollValue - 1.0f / ScrollNum, 0.0f, 1.0f); - if(pEditor->Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) - s_ScrollValue = clamp(s_ScrollValue + 1.0f / ScrollNum, 0.0f, 1.0f); - } - } - } - - float ImageStartAt = ScrollDifference * s_ScrollValue; - if(ImageStartAt < 0.0f) - ImageStartAt = 0.0f; - - float ImageStopAt = ImagesHeight - ScrollDifference * (1 - s_ScrollValue); - float ImageCur = 0.0f; for(int i = -1; i < (int)pEditor->m_Map.m_vpImages.size(); i++) { - if(ImageCur > ImageStopAt) - break; - if(ImageCur < ImageStartAt) - { - ImageCur += 14.0f; - continue; - } - ImageCur += 14.0f; - CUIRect Button; - ButtonBar.HSplitTop(14.0f, &Button, &ButtonBar); + ButtonBar.HSplitTop(RowHeight, &Button, &ButtonBar); + if(!s_ScrollRegion.AddRect(Button)) + continue; + Button.HSplitTop(12.0f, &Button, 0); if(pEditor->UI()->MouseInside(&Button)) ShowImage = i; @@ -1311,6 +1288,8 @@ int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View, void *pContext) } } + s_ScrollRegion.End(); + if(ShowImage >= 0 && (size_t)ShowImage < pEditor->m_Map.m_vpImages.size()) { if(ImageView.h < ImageView.w) @@ -1338,7 +1317,7 @@ void CEditor::PopupSelectImageInvoke(int Current, float x, float y) static int s_SelectImagePopupId = 0; g_SelectImageSelected = -100; g_SelectImageCurrent = Current; - UiInvokePopupMenu(&s_SelectImagePopupId, 0, x, y, 400, 300, PopupSelectImage); + UiInvokePopupMenu(&s_SelectImagePopupId, 0, x, y, 450, 300, PopupSelectImage); } int CEditor::PopupSelectImageResult() From 4cdd36aed34ed142807272684162774858dd0dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 19:01:09 +0200 Subject: [PATCH 10/17] Remove redundant `m_GuiActive` checks This condition is already checked before calling the methods. --- src/game/editor/editor.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 5096ea261..9d3cdfab8 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -3286,9 +3286,6 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) { - if(!m_GuiActive) - return; - CUIRect LayersBox = ToolBox; CUIRect Slot, Button; char aBuf[64]; @@ -3987,9 +3984,6 @@ void CEditor::SortImages() void CEditor::RenderImages(CUIRect ToolBox, CUIRect View) { - if(!m_GuiActive) - return; - static float s_ScrollValue = 0; float ImagesHeight = 30.0f + 14.0f * m_Map.m_vpImages.size() + 27.0f; float ScrollDifference = ImagesHeight - ToolBox.h; @@ -4201,9 +4195,6 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect View) void CEditor::RenderSounds(CUIRect ToolBox, CUIRect View) { - if(!m_GuiActive) - return; - static float s_ScrollValue = 0; float SoundsHeight = 30.0f + 14.0f * m_Map.m_vpSounds.size() + 27.0f; float ScrollDifference = SoundsHeight - ToolBox.h; From ad8f49fe7920e7146f74c75e67317765eec8a3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 19:02:55 +0200 Subject: [PATCH 11/17] Remove unnecessary indentation/scope --- src/game/editor/editor.cpp | 334 ++++++++++++++++++------------------- 1 file changed, 166 insertions(+), 168 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 9d3cdfab8..bde82571e 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -3331,193 +3331,191 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) float LayerCur = 0; // render layers + for(int g = 0; g < (int)m_Map.m_vpGroups.size(); g++) { - for(int g = 0; g < (int)m_Map.m_vpGroups.size(); g++) + if(LayerCur > LayerStopAt) + break; + else if(LayerCur + m_Map.m_vpGroups[g]->m_vpLayers.size() * 14.0f + 19.0f < LayerStartAt) + { + LayerCur += m_Map.m_vpGroups[g]->m_vpLayers.size() * 14.0f + 19.0f; + continue; + } + + CUIRect VisibleToggle; + if(LayerCur >= LayerStartAt) + { + LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); + Slot.VSplitLeft(12, &VisibleToggle, &Slot); + if(DoButton_Ex(&m_Map.m_vpGroups[g]->m_Visible, m_Map.m_vpGroups[g]->m_Visible ? "V" : "H", m_Map.m_vpGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", IGraphics::CORNER_L, 10.0f, 0)) + m_Map.m_vpGroups[g]->m_Visible = !m_Map.m_vpGroups[g]->m_Visible; + + str_format(aBuf, sizeof(aBuf), "#%d %s", g, m_Map.m_vpGroups[g]->m_aName); + float FontSize = 10.0f; + while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Slot.w) + FontSize--; + if(int Result = DoButton_Ex(&m_Map.m_vpGroups[g], aBuf, g == m_SelectedGroup, &Slot, + BUTTON_CONTEXT, m_Map.m_vpGroups[g]->m_Collapse ? "Select group. Shift click to select all layers. Double click to expand." : "Select group. Shift click to select all layers. Double click to collapse.", IGraphics::CORNER_R, FontSize)) + { + if(g != m_SelectedGroup) + SelectLayer(0, g); + + if((Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT)) && m_SelectedGroup == g) + { + m_vSelectedLayers.clear(); + for(size_t i = 0; i < m_Map.m_vpGroups[g]->m_vpLayers.size(); i++) + { + AddSelectedLayer(i); + } + } + + static int s_GroupPopupId = 0; + if(Result == 2) + UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 256, PopupGroup); + + if(!m_Map.m_vpGroups[g]->m_vpLayers.empty() && Input()->MouseDoubleClick()) + m_Map.m_vpGroups[g]->m_Collapse ^= 1; + } + LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); + } + LayerCur += 14.0f; + + for(int i = 0; i < (int)m_Map.m_vpGroups[g]->m_vpLayers.size(); i++) { if(LayerCur > LayerStopAt) break; - else if(LayerCur + m_Map.m_vpGroups[g]->m_vpLayers.size() * 14.0f + 19.0f < LayerStartAt) + else if(LayerCur < LayerStartAt) { - LayerCur += m_Map.m_vpGroups[g]->m_vpLayers.size() * 14.0f + 19.0f; + LayerCur += 14.0f; continue; } - CUIRect VisibleToggle; - if(LayerCur >= LayerStartAt) + if(m_Map.m_vpGroups[g]->m_Collapse) + continue; + + //visible + LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); + Slot.VSplitLeft(12.0f, nullptr, &Button); + Button.VSplitLeft(15, &VisibleToggle, &Button); + + if(DoButton_Ex(&m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible, m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible ? "V" : "H", 0, &VisibleToggle, 0, "Toggle layer visibility", IGraphics::CORNER_L, 10.0f, 0)) + m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible = !m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible; + + if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_aName[0]) + str_copy(aBuf, m_Map.m_vpGroups[g]->m_vpLayers[i]->m_aName, sizeof(aBuf)); + else { - LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); - Slot.VSplitLeft(12, &VisibleToggle, &Slot); - if(DoButton_Ex(&m_Map.m_vpGroups[g]->m_Visible, m_Map.m_vpGroups[g]->m_Visible ? "V" : "H", m_Map.m_vpGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", IGraphics::CORNER_L, 10.0f, 0)) - m_Map.m_vpGroups[g]->m_Visible = !m_Map.m_vpGroups[g]->m_Visible; - - str_format(aBuf, sizeof(aBuf), "#%d %s", g, m_Map.m_vpGroups[g]->m_aName); - float FontSize = 10.0f; - while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Slot.w) - FontSize--; - if(int Result = DoButton_Ex(&m_Map.m_vpGroups[g], aBuf, g == m_SelectedGroup, &Slot, - BUTTON_CONTEXT, m_Map.m_vpGroups[g]->m_Collapse ? "Select group. Shift click to select all layers. Double click to expand." : "Select group. Shift click to select all layers. Double click to collapse.", IGraphics::CORNER_R, FontSize)) + if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_TILES) { - if(g != m_SelectedGroup) - SelectLayer(0, g); + CLayerTiles *pTiles = (CLayerTiles *)m_Map.m_vpGroups[g]->m_vpLayers[i]; + str_copy(aBuf, pTiles->m_Image >= 0 ? m_Map.m_vpImages[pTiles->m_Image]->m_aName : "Tiles", sizeof(aBuf)); + } + else if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_QUADS) + { + CLayerQuads *pQuads = (CLayerQuads *)m_Map.m_vpGroups[g]->m_vpLayers[i]; + str_copy(aBuf, pQuads->m_Image >= 0 ? m_Map.m_vpImages[pQuads->m_Image]->m_aName : "Quads", sizeof(aBuf)); + } + else if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_SOUNDS) + { + CLayerSounds *pSounds = (CLayerSounds *)m_Map.m_vpGroups[g]->m_vpLayers[i]; + str_copy(aBuf, pSounds->m_Sound >= 0 ? m_Map.m_vpSounds[pSounds->m_Sound]->m_aName : "Sounds", sizeof(aBuf)); + } + if(str_length(aBuf) > 11) + str_format(aBuf, sizeof(aBuf), "%.8s...", aBuf); + } + float FontSize = 10.0f; + while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Button.w) + FontSize--; + int Checked = 0; + if(g == m_SelectedGroup) + { + for(const auto &Selected : m_vSelectedLayers) + { + if(Selected == i) + { + Checked = 1; + break; + } + } + } + + if(m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pGameLayer || + m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pFrontLayer || + m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pSwitchLayer || + m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pTuneLayer || + m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pSpeedupLayer || + m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pTeleLayer) + { + Checked += 6; + } + if(int Result = DoButton_Ex(m_Map.m_vpGroups[g]->m_vpLayers[i], aBuf, Checked, &Button, + BUTTON_CONTEXT, "Select layer. Shift click to select multiple.", IGraphics::CORNER_R, FontSize)) + { + static CLayerPopupContext s_LayerPopupContext = {}; + if(Result == 1) + { if((Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT)) && m_SelectedGroup == g) { - m_vSelectedLayers.clear(); - for(size_t i = 0; i < m_Map.m_vpGroups[g]->m_vpLayers.size(); i++) - { - AddSelectedLayer(i); - } - } - - static int s_GroupPopupId = 0; - if(Result == 2) - UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 256, PopupGroup); - - if(!m_Map.m_vpGroups[g]->m_vpLayers.empty() && Input()->MouseDoubleClick()) - m_Map.m_vpGroups[g]->m_Collapse ^= 1; - } - LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); - } - LayerCur += 14.0f; - - for(int i = 0; i < (int)m_Map.m_vpGroups[g]->m_vpLayers.size(); i++) - { - if(LayerCur > LayerStopAt) - break; - else if(LayerCur < LayerStartAt) - { - LayerCur += 14.0f; - continue; - } - - if(m_Map.m_vpGroups[g]->m_Collapse) - continue; - - //visible - LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); - Slot.VSplitLeft(12.0f, nullptr, &Button); - Button.VSplitLeft(15, &VisibleToggle, &Button); - - if(DoButton_Ex(&m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible, m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible ? "V" : "H", 0, &VisibleToggle, 0, "Toggle layer visibility", IGraphics::CORNER_L, 10.0f, 0)) - m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible = !m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible; - - if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_aName[0]) - str_copy(aBuf, m_Map.m_vpGroups[g]->m_vpLayers[i]->m_aName, sizeof(aBuf)); - else - { - if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_TILES) - { - CLayerTiles *pTiles = (CLayerTiles *)m_Map.m_vpGroups[g]->m_vpLayers[i]; - str_copy(aBuf, pTiles->m_Image >= 0 ? m_Map.m_vpImages[pTiles->m_Image]->m_aName : "Tiles", sizeof(aBuf)); - } - else if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_QUADS) - { - CLayerQuads *pQuads = (CLayerQuads *)m_Map.m_vpGroups[g]->m_vpLayers[i]; - str_copy(aBuf, pQuads->m_Image >= 0 ? m_Map.m_vpImages[pQuads->m_Image]->m_aName : "Quads", sizeof(aBuf)); - } - else if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_SOUNDS) - { - CLayerSounds *pSounds = (CLayerSounds *)m_Map.m_vpGroups[g]->m_vpLayers[i]; - str_copy(aBuf, pSounds->m_Sound >= 0 ? m_Map.m_vpSounds[pSounds->m_Sound]->m_aName : "Sounds", sizeof(aBuf)); - } - if(str_length(aBuf) > 11) - str_format(aBuf, sizeof(aBuf), "%.8s...", aBuf); - } - - float FontSize = 10.0f; - while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Button.w) - FontSize--; - int Checked = 0; - if(g == m_SelectedGroup) - { - for(const auto &Selected : m_vSelectedLayers) - { - if(Selected == i) - { - Checked = 1; - break; - } - } - } - - if(m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pGameLayer || - m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pFrontLayer || - m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pSwitchLayer || - m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pTuneLayer || - m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pSpeedupLayer || - m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pTeleLayer) - { - Checked += 6; - } - if(int Result = DoButton_Ex(m_Map.m_vpGroups[g]->m_vpLayers[i], aBuf, Checked, &Button, - BUTTON_CONTEXT, "Select layer. Shift click to select multiple.", IGraphics::CORNER_R, FontSize)) - { - static CLayerPopupContext s_LayerPopupContext = {}; - if(Result == 1) - { - if((Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT)) && m_SelectedGroup == g) - { - auto Position = std::find(m_vSelectedLayers.begin(), m_vSelectedLayers.end(), i); - if(Position != m_vSelectedLayers.end()) - m_vSelectedLayers.erase(Position); - else - AddSelectedLayer(i); - } - else if(!(Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT))) - { - SelectLayer(i, g); - } - } - else if(Result == 2) - { - bool IsLayerSelected = false; - - if(m_SelectedGroup == g) - { - for(const auto &Selected : m_vSelectedLayers) - { - if(Selected == i) - { - IsLayerSelected = true; - break; - } - } - } - - if(!IsLayerSelected) - { - SelectLayer(i, g); - } - - if(m_vSelectedLayers.size() > 1) - { - bool AllTile = true; - for(size_t j = 0; AllTile && j < m_vSelectedLayers.size(); j++) - { - if(m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers[m_vSelectedLayers[j]]->m_Type == LAYERTYPE_TILES) - s_LayerPopupContext.m_vpLayers.push_back((CLayerTiles *)m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers[m_vSelectedLayers[j]]); - else - AllTile = false; - } - - // Don't allow editing if all selected layers are tile layers - if(!AllTile) - s_LayerPopupContext.m_vpLayers.clear(); - } + auto Position = std::find(m_vSelectedLayers.begin(), m_vSelectedLayers.end(), i); + if(Position != m_vSelectedLayers.end()) + m_vSelectedLayers.erase(Position); else - s_LayerPopupContext.m_vpLayers.clear(); - - UiInvokePopupMenu(&s_LayerPopupContext, 0, UI()->MouseX(), UI()->MouseY(), 120, 320, PopupLayer, &s_LayerPopupContext); + AddSelectedLayer(i); + } + else if(!(Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT))) + { + SelectLayer(i, g); } } + else if(Result == 2) + { + bool IsLayerSelected = false; - LayerCur += 14.0f; - LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); + if(m_SelectedGroup == g) + { + for(const auto &Selected : m_vSelectedLayers) + { + if(Selected == i) + { + IsLayerSelected = true; + break; + } + } + } + + if(!IsLayerSelected) + { + SelectLayer(i, g); + } + + if(m_vSelectedLayers.size() > 1) + { + bool AllTile = true; + for(size_t j = 0; AllTile && j < m_vSelectedLayers.size(); j++) + { + if(m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers[m_vSelectedLayers[j]]->m_Type == LAYERTYPE_TILES) + s_LayerPopupContext.m_vpLayers.push_back((CLayerTiles *)m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers[m_vSelectedLayers[j]]); + else + AllTile = false; + } + + // Don't allow editing if all selected layers are tile layers + if(!AllTile) + s_LayerPopupContext.m_vpLayers.clear(); + } + else + s_LayerPopupContext.m_vpLayers.clear(); + + UiInvokePopupMenu(&s_LayerPopupContext, 0, UI()->MouseX(), UI()->MouseY(), 120, 320, PopupLayer, &s_LayerPopupContext); + } } - if(LayerCur > LayerStartAt && LayerCur < LayerStopAt) - LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); - LayerCur += 5.0f; + + LayerCur += 14.0f; + LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); } + if(LayerCur > LayerStartAt && LayerCur < LayerStopAt) + LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); + LayerCur += 5.0f; } if(Input()->KeyPress(KEY_DOWN) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0) From 9f01c6fe6603e702c2b59d83ad288b4a95206587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 19:36:53 +0200 Subject: [PATCH 12/17] Refactor editor layer list using `CScrollRegion` --- src/game/editor/editor.cpp | 174 +++++++++++++------------------------ src/game/editor/editor.h | 4 +- 2 files changed, 60 insertions(+), 118 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index bde82571e..5c50512df 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -3284,74 +3285,36 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * return Change; } -void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) +void CEditor::RenderLayers(CUIRect LayersBox) { - CUIRect LayersBox = ToolBox; - CUIRect Slot, Button; + const float RowHeight = 12.0f; char aBuf[64]; - float LayersHeight = 12.0f; // Height of AddGroup button - static float s_ScrollValue = 0; + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollbarWidth = 10.0f; + ScrollParams.m_ScrollbarMargin = 3.0f; + ScrollParams.m_ScrollUnit = RowHeight * 5.0f; + s_ScrollRegion.Begin(&LayersBox, &ScrollOffset, &ScrollParams); + LayersBox.y += ScrollOffset.y; - for(auto &pGroup : m_Map.m_vpGroups) - { - // Each group is 19.0f - // Each layer is 14.0f - LayersHeight += 19.0f; - if(!pGroup->m_Collapse) - LayersHeight += pGroup->m_vpLayers.size() * 14.0f; - } - - float ScrollDifference = LayersHeight - LayersBox.h; - - if(LayersHeight > LayersBox.h) // Do we even need a scrollbar? - { - CUIRect Scroll; - LayersBox.VSplitRight(20.0f, &LayersBox, &Scroll); - s_ScrollValue = UI()->DoScrollbarV(&s_ScrollValue, &Scroll, s_ScrollValue); - - if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&LayersBox)) - { - int ScrollNum = (int)((LayersHeight - LayersBox.h) / 15.0f) + 1; - if(ScrollNum > 0) - { - if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) - s_ScrollValue = clamp(s_ScrollValue - 1.0f / ScrollNum, 0.0f, 1.0f); - if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) - s_ScrollValue = clamp(s_ScrollValue + 1.0f / ScrollNum, 0.0f, 1.0f); - } - } - } - - float LayerStartAt = ScrollDifference * s_ScrollValue; - if(LayerStartAt < 0.0f) - LayerStartAt = 0.0f; - - float LayerStopAt = LayersHeight - ScrollDifference * (1 - s_ScrollValue); - float LayerCur = 0; + const bool ScrollToSelection = SelectLayerByTile(); // render layers for(int g = 0; g < (int)m_Map.m_vpGroups.size(); g++) { - if(LayerCur > LayerStopAt) - break; - else if(LayerCur + m_Map.m_vpGroups[g]->m_vpLayers.size() * 14.0f + 19.0f < LayerStartAt) + CUIRect Slot, VisibleToggle; + LayersBox.HSplitTop(RowHeight, &Slot, &LayersBox); + if(s_ScrollRegion.AddRect(Slot)) { - LayerCur += m_Map.m_vpGroups[g]->m_vpLayers.size() * 14.0f + 19.0f; - continue; - } - - CUIRect VisibleToggle; - if(LayerCur >= LayerStartAt) - { - LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); - Slot.VSplitLeft(12, &VisibleToggle, &Slot); + Slot.VSplitLeft(12.0f, &VisibleToggle, &Slot); if(DoButton_Ex(&m_Map.m_vpGroups[g]->m_Visible, m_Map.m_vpGroups[g]->m_Visible ? "V" : "H", m_Map.m_vpGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", IGraphics::CORNER_L, 10.0f, 0)) m_Map.m_vpGroups[g]->m_Visible = !m_Map.m_vpGroups[g]->m_Visible; str_format(aBuf, sizeof(aBuf), "#%d %s", g, m_Map.m_vpGroups[g]->m_aName); float FontSize = 10.0f; - while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Slot.w) + while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Slot.w && FontSize >= 7.0f) FontSize--; if(int Result = DoButton_Ex(&m_Map.m_vpGroups[g], aBuf, g == m_SelectedGroup, &Slot, BUTTON_CONTEXT, m_Map.m_vpGroups[g]->m_Collapse ? "Select group. Shift click to select all layers. Double click to expand." : "Select group. Shift click to select all layers. Double click to collapse.", IGraphics::CORNER_R, FontSize)) @@ -3375,27 +3338,37 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) if(!m_Map.m_vpGroups[g]->m_vpLayers.empty() && Input()->MouseDoubleClick()) m_Map.m_vpGroups[g]->m_Collapse ^= 1; } - LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); } - LayerCur += 14.0f; + + LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); + s_ScrollRegion.AddRect(Slot); for(int i = 0; i < (int)m_Map.m_vpGroups[g]->m_vpLayers.size(); i++) { - if(LayerCur > LayerStopAt) - break; - else if(LayerCur < LayerStartAt) - { - LayerCur += 14.0f; - continue; - } - if(m_Map.m_vpGroups[g]->m_Collapse) continue; - //visible - LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); - Slot.VSplitLeft(12.0f, nullptr, &Button); - Button.VSplitLeft(15, &VisibleToggle, &Button); + bool IsLayerSelected = false; + if(m_SelectedGroup == g) + { + for(const auto &Selected : m_vSelectedLayers) + { + if(Selected == i) + { + IsLayerSelected = true; + break; + } + } + } + + LayersBox.HSplitTop(RowHeight + 2.0f, &Slot, &LayersBox); + if(!s_ScrollRegion.AddRect(Slot, ScrollToSelection && IsLayerSelected)) + continue; + Slot.HSplitTop(RowHeight, &Slot, nullptr); + + CUIRect Button; + Slot.VSplitLeft(12.0f, nullptr, &Slot); + Slot.VSplitLeft(15.0f, &VisibleToggle, &Button); if(DoButton_Ex(&m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible, m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible ? "V" : "H", 0, &VisibleToggle, 0, "Toggle layer visibility", IGraphics::CORNER_L, 10.0f, 0)) m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible = !m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible; @@ -3424,21 +3397,10 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) } float FontSize = 10.0f; - while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Button.w) + while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Button.w && FontSize >= 7.0f) FontSize--; - int Checked = 0; - if(g == m_SelectedGroup) - { - for(const auto &Selected : m_vSelectedLayers) - { - if(Selected == i) - { - Checked = 1; - break; - } - } - } + int Checked = IsLayerSelected ? 1 : 0; if(m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pGameLayer || m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pFrontLayer || m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pSwitchLayer || @@ -3469,20 +3431,6 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) } else if(Result == 2) { - bool IsLayerSelected = false; - - if(m_SelectedGroup == g) - { - for(const auto &Selected : m_vSelectedLayers) - { - if(Selected == i) - { - IsLayerSelected = true; - break; - } - } - } - if(!IsLayerSelected) { SelectLayer(i, g); @@ -3509,13 +3457,10 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) UiInvokePopupMenu(&s_LayerPopupContext, 0, UI()->MouseX(), UI()->MouseY(), 120, 320, PopupLayer, &s_LayerPopupContext); } } - - LayerCur += 14.0f; - LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); } - if(LayerCur > LayerStartAt && LayerCur < LayerStopAt) - LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); - LayerCur += 5.0f; + + LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); + s_ScrollRegion.AddRect(Slot); } if(Input()->KeyPress(KEY_DOWN) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0) @@ -3581,22 +3526,23 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) } } - if(LayerCur <= LayerStopAt) + CUIRect AddGroupButton; + LayersBox.HSplitTop(RowHeight + 1.0f, &AddGroupButton, &LayersBox); + if(s_ScrollRegion.AddRect(AddGroupButton)) { - LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); - - static int s_NewGroupButton = 0; - if(DoButton_Editor(&s_NewGroupButton, "Add group", 0, &Slot, IGraphics::CORNER_R, "Adds a new group")) + AddGroupButton.HSplitTop(RowHeight, &AddGroupButton, 0); + static int s_AddGroupButton = 0; + if(DoButton_Editor(&s_AddGroupButton, "Add group", 0, &AddGroupButton, IGraphics::CORNER_R, "Adds a new group")) { m_Map.NewGroup(); m_SelectedGroup = m_Map.m_vpGroups.size() - 1; } } - SelectLayerByTile(s_ScrollValue); + s_ScrollRegion.End(); } -void CEditor::SelectLayerByTile(float &Scroll) +bool CEditor::SelectLayerByTile() { // ctrl+rightclick a map index to select the layer that has a tile there static bool s_CtrlClick = false; @@ -3605,18 +3551,15 @@ void CEditor::SelectLayerByTile(float &Scroll) int MatchedLayer = -1; int Matches = 0; bool IsFound = false; - int TotalLayers = 0; - int SelectedLayer = 0; if(UI()->MouseButton(1) && Input()->ModifierIsPressed()) { if(s_CtrlClick) - return; + return false; s_CtrlClick = true; for(size_t g = 0; g < m_Map.m_vpGroups.size(); g++) { for(size_t l = 0; l < m_Map.m_vpGroups[g]->m_vpLayers.size(); l++) { - TotalLayers++; if(IsFound) continue; if(m_Map.m_vpGroups[g]->m_vpLayers[l]->m_Type != LAYERTYPE_TILES) @@ -3636,7 +3579,6 @@ void CEditor::SelectLayerByTile(float &Scroll) { MatchedGroup = g; MatchedLayer = l; - SelectedLayer = TotalLayers; } if(++Matches > s_Selected) { @@ -3644,7 +3586,6 @@ void CEditor::SelectLayerByTile(float &Scroll) MatchedGroup = g; MatchedLayer = l; IsFound = true; - SelectedLayer = TotalLayers; } } } @@ -3653,12 +3594,13 @@ void CEditor::SelectLayerByTile(float &Scroll) { if(!IsFound) s_Selected = 1; - Scroll = (float)SelectedLayer / (float)TotalLayers; SelectLayer(MatchedLayer, MatchedGroup); + return true; } } else s_CtrlClick = false; + return false; } void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser) @@ -5958,7 +5900,7 @@ void CEditor::Render() } if(m_Mode == MODE_LAYERS) - RenderLayers(ToolBox, View); + RenderLayers(ToolBox); else if(m_Mode == MODE_IMAGES) RenderImages(ToolBox, View); else if(m_Mode == MODE_SOUNDS) diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index cdb9f769e..7ebdc0e0d 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -1126,8 +1126,8 @@ public: bool IsEnvelopeUsed(int EnvelopeIndex) const; + void RenderLayers(CUIRect LayersBox); void RenderImages(CUIRect Toolbox, CUIRect View); - void RenderLayers(CUIRect Toolbox, CUIRect View); void RenderSounds(CUIRect Toolbox, CUIRect View); void RenderModebar(CUIRect View); void RenderStatusbar(CUIRect View); @@ -1140,7 +1140,7 @@ public: void AddFileDialogEntry(int Index, CUIRect *pView); void SelectGameLayer(); void SortImages(); - void SelectLayerByTile(float &Scroll); + bool SelectLayerByTile(); //Tile Numbers For Explanations - TODO: Add/Improve tiles and explanations enum From 5dc6c90a509142b363193de2db64c6319e94bce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 21:30:43 +0200 Subject: [PATCH 13/17] Refactor editor image list using `CScrollRegion`, split method --- src/game/editor/editor.cpp | 281 +++++++++++++++++-------------------- src/game/editor/editor.h | 3 +- 2 files changed, 128 insertions(+), 156 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 5c50512df..f33433566 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -3922,133 +3922,20 @@ void CEditor::SortImages() } } -void CEditor::RenderImages(CUIRect ToolBox, CUIRect View) +void CEditor::RenderImagesList(CUIRect ToolBox) { - static float s_ScrollValue = 0; - float ImagesHeight = 30.0f + 14.0f * m_Map.m_vpImages.size() + 27.0f; - float ScrollDifference = ImagesHeight - ToolBox.h; + const float RowHeight = 12.0f; - if(ImagesHeight > ToolBox.h) // Do we even need a scrollbar? - { - CUIRect Scroll; - ToolBox.VSplitRight(20.0f, &ToolBox, &Scroll); - s_ScrollValue = UI()->DoScrollbarV(&s_ScrollValue, &Scroll, s_ScrollValue); - - if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox)) - { - int ScrollNum = (int)((ImagesHeight - ToolBox.h) / 14.0f) + 1; - if(ScrollNum > 0) - { - if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) - s_ScrollValue = clamp(s_ScrollValue - 1.0f / ScrollNum, 0.0f, 1.0f); - if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) - s_ScrollValue = clamp(s_ScrollValue + 1.0f / ScrollNum, 0.0f, 1.0f); - } - } - } - - float ImageStartAt = ScrollDifference * s_ScrollValue; - if(ImageStartAt < 0.0f) - ImageStartAt = 0.0f; - - float ImageStopAt = ImagesHeight - ScrollDifference * (1 - s_ScrollValue); - float ImageCur = 0.0f; - - for(int e = 0; e < 2; e++) // two passes, first embedded, then external - { - CUIRect Slot; - - if(ImageCur > ImageStopAt) - break; - else if(ImageCur >= ImageStartAt) - { - ToolBox.HSplitTop(15.0f, &Slot, &ToolBox); - if(e == 0) - UI()->DoLabel(&Slot, "Embedded", 12.0f, TEXTALIGN_CENTER); - else - UI()->DoLabel(&Slot, "External", 12.0f, TEXTALIGN_CENTER); - } - ImageCur += 15.0f; - - for(int i = 0; i < (int)m_Map.m_vpImages.size(); i++) - { - if((e && !m_Map.m_vpImages[i]->m_External) || - (!e && m_Map.m_vpImages[i]->m_External)) - { - continue; - } - - if(ImageCur > ImageStopAt) - break; - else if(ImageCur < ImageStartAt) - { - ImageCur += 14.0f; - continue; - } - ImageCur += 14.0f; - - char aBuf[128]; - str_copy(aBuf, m_Map.m_vpImages[i]->m_aName, sizeof(aBuf)); - ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); - - int Selected = m_SelectedImage == i; - - const bool ImageUsed = std::any_of(m_Map.m_vpGroups.cbegin(), m_Map.m_vpGroups.cend(), [i](const auto &pGroup) { - return std::any_of(pGroup->m_vpLayers.cbegin(), pGroup->m_vpLayers.cend(), [i](const auto &pLayer) { - if(pLayer->m_Type == LAYERTYPE_QUADS) - return static_cast(pLayer)->m_Image == i; - else if(pLayer->m_Type == LAYERTYPE_TILES) - return static_cast(pLayer)->m_Image == i; - return false; - }); - }); - - if(!ImageUsed) - Selected += 2; // Image is unused - - if(Selected < 2 && e == 1) - { - if(!IsVanillaImage(m_Map.m_vpImages[i]->m_aName)) - { - Selected += 4; // Image should be embedded - } - } - - float FontSize = 10.0f; - while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Slot.w) - FontSize--; - - if(int Result = DoButton_Ex(&m_Map.m_vpImages[i], aBuf, Selected, &Slot, - BUTTON_CONTEXT, "Select image.", 0, FontSize)) - { - m_SelectedImage = i; - - static int s_PopupImageID = 0; - if(Result == 2) - { - CEditorImage *pImg = m_Map.m_vpImages[m_SelectedImage]; - int Height; - if(pImg->m_External || IsVanillaImage(pImg->m_aName)) - Height = 60; - else - Height = 43; - UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, Height, PopupImage); - } - } - - ToolBox.HSplitTop(2.0f, nullptr, &ToolBox); - } - - // separator - ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); - ImageCur += 5.0f; - IGraphics::CLineItem LineItem(Slot.x, Slot.y + Slot.h / 2, Slot.x + Slot.w, Slot.y + Slot.h / 2); - Graphics()->TextureClear(); - Graphics()->LinesBegin(); - Graphics()->LinesDraw(&LineItem, 1); - Graphics()->LinesEnd(); - } + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollbarWidth = 10.0f; + ScrollParams.m_ScrollbarMargin = 3.0f; + ScrollParams.m_ScrollUnit = RowHeight * 5; + s_ScrollRegion.Begin(&ToolBox, &ScrollOffset, &ScrollParams); + ToolBox.y += ScrollOffset.y; + bool ScrollToSelection = false; if(Input()->KeyPress(KEY_DOWN) && m_Dialog == DIALOG_NONE) { int OldImage = m_SelectedImage; @@ -4072,6 +3959,7 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect View) } } } + ScrollToSelection = OldImage != m_SelectedImage; } if(Input()->KeyPress(KEY_UP) && m_Dialog == DIALOG_NONE) { @@ -4096,44 +3984,124 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect View) } } } + ScrollToSelection = OldImage != m_SelectedImage; } - // render image - int i = m_SelectedImage; - if(i >= 0 && (size_t)i < m_Map.m_vpImages.size()) + for(int e = 0; e < 2; e++) // two passes, first embedded, then external { - CUIRect r; - View.Margin(10.0f, &r); - if(r.h < r.w) - r.w = r.h; - else - r.h = r.w; - float Max = (float)(maximum(m_Map.m_vpImages[i]->m_Width, m_Map.m_vpImages[i]->m_Height)); - r.w *= m_Map.m_vpImages[i]->m_Width / Max; - r.h *= m_Map.m_vpImages[i]->m_Height / Max; - Graphics()->TextureSet(m_Map.m_vpImages[i]->m_Texture); - Graphics()->BlendNormal(); - Graphics()->WrapClamp(); - Graphics()->QuadsBegin(); - IGraphics::CQuadItem QuadItem(r.x, r.y, r.w, r.h); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); - Graphics()->WrapNormal(); - } - //if(ImageCur + 27.0f > ImageStopAt) - // return; + CUIRect Slot; + ToolBox.HSplitTop(RowHeight + 3.0f, &Slot, &ToolBox); + if(s_ScrollRegion.AddRect(Slot)) + UI()->DoLabel(&Slot, e == 0 ? "Embedded" : "External", 12.0f, TEXTALIGN_CENTER); - CUIRect Slot; - ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); + for(int i = 0; i < (int)m_Map.m_vpImages.size(); i++) + { + if((e && !m_Map.m_vpImages[i]->m_External) || + (!e && m_Map.m_vpImages[i]->m_External)) + { + continue; + } + + ToolBox.HSplitTop(RowHeight + 2.0f, &Slot, &ToolBox); + int Selected = m_SelectedImage == i; + if(!s_ScrollRegion.AddRect(Slot, Selected && ScrollToSelection)) + continue; + Slot.HSplitTop(RowHeight, &Slot, nullptr); + + const bool ImageUsed = std::any_of(m_Map.m_vpGroups.cbegin(), m_Map.m_vpGroups.cend(), [i](const auto &pGroup) { + return std::any_of(pGroup->m_vpLayers.cbegin(), pGroup->m_vpLayers.cend(), [i](const auto &pLayer) { + if(pLayer->m_Type == LAYERTYPE_QUADS) + return static_cast(pLayer)->m_Image == i; + else if(pLayer->m_Type == LAYERTYPE_TILES) + return static_cast(pLayer)->m_Image == i; + return false; + }); + }); + + if(!ImageUsed) + Selected += 2; // Image is unused + + if(Selected < 2 && e == 1) + { + if(!IsVanillaImage(m_Map.m_vpImages[i]->m_aName)) + { + Selected += 4; // Image should be embedded + } + } + + float FontSize = 10.0f; + while(TextRender()->TextWidth(nullptr, FontSize, m_Map.m_vpImages[i]->m_aName, -1, -1.0f) > Slot.w) + FontSize--; + + if(int Result = DoButton_Ex(&m_Map.m_vpImages[i], m_Map.m_vpImages[i]->m_aName, Selected, &Slot, + BUTTON_CONTEXT, "Select image.", IGraphics::CORNER_ALL, FontSize)) + { + m_SelectedImage = i; + + static int s_PopupImageID = 0; + if(Result == 2) + { + CEditorImage *pImg = m_Map.m_vpImages[m_SelectedImage]; + int Height; + if(pImg->m_External || IsVanillaImage(pImg->m_aName)) + Height = 60; + else + Height = 43; + UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, Height, PopupImage); + } + } + } + + // separator + ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); + if(s_ScrollRegion.AddRect(Slot)) + { + IGraphics::CLineItem LineItem(Slot.x, Slot.y + Slot.h / 2, Slot.x + Slot.w, Slot.y + Slot.h / 2); + Graphics()->TextureClear(); + Graphics()->LinesBegin(); + Graphics()->LinesDraw(&LineItem, 1); + Graphics()->LinesEnd(); + } + } // new image - static int s_NewImageButton = 0; - ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); - if(DoButton_Editor(&s_NewImageButton, "Add", 0, &Slot, 0, "Load a new image to use in the map")) - InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this); + static int s_AddImageButton = 0; + CUIRect AddImageButton; + ToolBox.HSplitTop(5.0f + RowHeight + 1.0f, &AddImageButton, &ToolBox); + if(s_ScrollRegion.AddRect(AddImageButton)) + { + AddImageButton.HSplitTop(5.0f, nullptr, &AddImageButton); + AddImageButton.HSplitTop(RowHeight, &AddImageButton, nullptr); + if(DoButton_Editor(&s_AddImageButton, "Add", 0, &AddImageButton, 0, "Load a new image to use in the map")) + InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this); + } + s_ScrollRegion.End(); } -void CEditor::RenderSounds(CUIRect ToolBox, CUIRect View) +void CEditor::RenderSelectedImage(CUIRect View) +{ + if(m_SelectedImage < 0 || (size_t)m_SelectedImage >= m_Map.m_vpImages.size()) + return; + + View.Margin(10.0f, &View); + if(View.h < View.w) + View.w = View.h; + else + View.h = View.w; + float Max = maximum(m_Map.m_vpImages[m_SelectedImage]->m_Width, m_Map.m_vpImages[m_SelectedImage]->m_Height); + View.w *= m_Map.m_vpImages[m_SelectedImage]->m_Width / Max; + View.h *= m_Map.m_vpImages[m_SelectedImage]->m_Height / Max; + Graphics()->TextureSet(m_Map.m_vpImages[m_SelectedImage]->m_Texture); + Graphics()->BlendNormal(); + Graphics()->WrapClamp(); + Graphics()->QuadsBegin(); + IGraphics::CQuadItem QuadItem(View.x, View.y, View.w, View.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + Graphics()->WrapNormal(); +} + +void CEditor::RenderSounds(CUIRect ToolBox) { static float s_ScrollValue = 0; float SoundsHeight = 30.0f + 14.0f * m_Map.m_vpSounds.size() + 27.0f; @@ -5902,7 +5870,10 @@ void CEditor::Render() if(m_Mode == MODE_LAYERS) RenderLayers(ToolBox); else if(m_Mode == MODE_IMAGES) - RenderImages(ToolBox, View); + { + RenderImagesList(ToolBox); + RenderSelectedImage(View); + } else if(m_Mode == MODE_SOUNDS) RenderSounds(ToolBox, View); } diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 7ebdc0e0d..d5cb0dcfa 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -1127,7 +1127,8 @@ public: bool IsEnvelopeUsed(int EnvelopeIndex) const; void RenderLayers(CUIRect LayersBox); - void RenderImages(CUIRect Toolbox, CUIRect View); + void RenderImagesList(CUIRect Toolbox); + void RenderSelectedImage(CUIRect View); void RenderSounds(CUIRect Toolbox, CUIRect View); void RenderModebar(CUIRect View); void RenderStatusbar(CUIRect View); From 359beffff34cdb98ffa126040acdd3e796d13fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 13 Aug 2022 21:47:52 +0200 Subject: [PATCH 14/17] Refactor editor sound list using `CScrollRegion` --- src/game/editor/editor.cpp | 126 +++++++++++++++---------------------- src/game/editor/editor.h | 2 +- 2 files changed, 50 insertions(+), 78 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index f33433566..1aec7b95a 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -4103,58 +4103,42 @@ void CEditor::RenderSelectedImage(CUIRect View) void CEditor::RenderSounds(CUIRect ToolBox) { - static float s_ScrollValue = 0; - float SoundsHeight = 30.0f + 14.0f * m_Map.m_vpSounds.size() + 27.0f; - float ScrollDifference = SoundsHeight - ToolBox.h; + const float RowHeight = 12.0f; - if(SoundsHeight > ToolBox.h) // Do we even need a scrollbar? + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollbarWidth = 10.0f; + ScrollParams.m_ScrollbarMargin = 3.0f; + ScrollParams.m_ScrollUnit = RowHeight * 5; + s_ScrollRegion.Begin(&ToolBox, &ScrollOffset, &ScrollParams); + ToolBox.y += ScrollOffset.y; + + bool ScrollToSelection = false; + if(Input()->KeyPress(KEY_DOWN) && m_Dialog == DIALOG_NONE) { - CUIRect Scroll; - ToolBox.VSplitRight(20.0f, &ToolBox, &Scroll); - s_ScrollValue = UI()->DoScrollbarV(&s_ScrollValue, &Scroll, s_ScrollValue); - - if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox)) - { - int ScrollNum = (int)((SoundsHeight - ToolBox.h) / 14.0f) + 1; - if(ScrollNum > 0) - { - if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) - s_ScrollValue = clamp(s_ScrollValue - 1.0f / ScrollNum, 0.0f, 1.0f); - if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) - s_ScrollValue = clamp(s_ScrollValue + 1.0f / ScrollNum, 0.0f, 1.0f); - } - } + m_SelectedSound = (m_SelectedSound + 1) % m_Map.m_vpSounds.size(); + ScrollToSelection = true; + } + if(Input()->KeyPress(KEY_UP) && m_Dialog == DIALOG_NONE) + { + m_SelectedSound = (m_SelectedSound + m_Map.m_vpSounds.size() - 1) % m_Map.m_vpSounds.size(); + ScrollToSelection = true; } - float SoundStartAt = ScrollDifference * s_ScrollValue; - if(SoundStartAt < 0.0f) - SoundStartAt = 0.0f; - - float SoundStopAt = SoundsHeight - ScrollDifference * (1 - s_ScrollValue); - float SoundCur = 0.0f; - CUIRect Slot; - - ToolBox.HSplitTop(15.0f, &Slot, &ToolBox); - UI()->DoLabel(&Slot, "Embedded", 12.0f, TEXTALIGN_CENTER); - SoundCur += 15.0f; + ToolBox.HSplitTop(RowHeight + 3.0f, &Slot, &ToolBox); + if(s_ScrollRegion.AddRect(Slot)) + UI()->DoLabel(&Slot, "Embedded", 12.0f, TEXTALIGN_CENTER); for(int i = 0; i < (int)m_Map.m_vpSounds.size(); i++) { - if(SoundCur > SoundStopAt) - break; - else if(SoundCur < SoundStartAt) - { - SoundCur += 14.0f; - continue; - } - SoundCur += 14.0f; - - char aBuf[128]; - str_copy(aBuf, m_Map.m_vpSounds[i]->m_aName, sizeof(aBuf)); - ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); - + ToolBox.HSplitTop(RowHeight + 2.0f, &Slot, &ToolBox); int Selected = m_SelectedSound == i; + if(!s_ScrollRegion.AddRect(Slot, Selected && ScrollToSelection)) + continue; + Slot.HSplitTop(RowHeight, &Slot, nullptr); + const bool SoundUsed = std::any_of(m_Map.m_vpGroups.cbegin(), m_Map.m_vpGroups.cend(), [i](const auto &pGroup) { return std::any_of(pGroup->m_vpLayers.cbegin(), pGroup->m_vpLayers.cend(), [i](const auto &pLayer) { if(pLayer->m_Type == LAYERTYPE_SOUNDS) @@ -4167,11 +4151,11 @@ void CEditor::RenderSounds(CUIRect ToolBox) Selected += 2; // Sound is unused float FontSize = 10.0f; - while(TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f) > Slot.w) + while(TextRender()->TextWidth(nullptr, FontSize, m_Map.m_vpSounds[i]->m_aName, -1, -1.0f) > Slot.w) FontSize--; - if(int Result = DoButton_Ex(&m_Map.m_vpSounds[i], aBuf, Selected, &Slot, - BUTTON_CONTEXT, "Select sound.", 0, FontSize)) + if(int Result = DoButton_Ex(&m_Map.m_vpSounds[i], m_Map.m_vpSounds[i]->m_aName, Selected, &Slot, + BUTTON_CONTEXT, "Select sound.", IGraphics::CORNER_ALL, FontSize)) { m_SelectedSound = i; @@ -4179,43 +4163,31 @@ void CEditor::RenderSounds(CUIRect ToolBox) if(Result == 2) UiInvokePopupMenu(&s_PopupSoundID, 0, UI()->MouseX(), UI()->MouseY(), 120, 43, PopupSound); } - - ToolBox.HSplitTop(2.0f, nullptr, &ToolBox); } // separator ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); - IGraphics::CLineItem LineItem(Slot.x, Slot.y + Slot.h / 2, Slot.x + Slot.w, Slot.y + Slot.h / 2); - Graphics()->TextureClear(); - Graphics()->LinesBegin(); - Graphics()->LinesDraw(&LineItem, 1); - Graphics()->LinesEnd(); - - if(Input()->KeyPress(KEY_DOWN) && m_Dialog == DIALOG_NONE) + if(s_ScrollRegion.AddRect(Slot)) { - m_SelectedSound = clamp(m_SelectedSound, 0, (int)m_Map.m_vpSounds.size() - 1); - if(m_SelectedSound == (int)m_Map.m_vpSounds.size() - 1) - m_SelectedSound = 0; - else - m_SelectedSound += 1; - } - if(Input()->KeyPress(KEY_UP) && m_Dialog == DIALOG_NONE) - { - m_SelectedSound = clamp(m_SelectedSound, 0, (int)m_Map.m_vpSounds.size() - 1); - if(m_SelectedSound == 0 && !m_Map.m_vpSounds.empty()) - m_SelectedSound = m_Map.m_vpSounds.size() - 1; - else - m_SelectedSound -= 1; + IGraphics::CLineItem LineItem(Slot.x, Slot.y + Slot.h / 2, Slot.x + Slot.w, Slot.y + Slot.h / 2); + Graphics()->TextureClear(); + Graphics()->LinesBegin(); + Graphics()->LinesDraw(&LineItem, 1); + Graphics()->LinesEnd(); } - //CUIRect Slot; - ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); - - // new Sound - static int s_NewSoundButton = 0; - ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); - if(DoButton_Editor(&s_NewSoundButton, "Add", 0, &Slot, 0, "Load a new sound to use in the map")) - InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_SOUND, "Add Sound", "Add", "mapres", "", AddSound, this); + // new sound + static int s_AddSoundButton = 0; + CUIRect AddSoundButton; + ToolBox.HSplitTop(5.0f + RowHeight + 1.0f, &AddSoundButton, &ToolBox); + if(s_ScrollRegion.AddRect(AddSoundButton)) + { + AddSoundButton.HSplitTop(5.0f, nullptr, &AddSoundButton); + AddSoundButton.HSplitTop(RowHeight, &AddSoundButton, nullptr); + if(DoButton_Editor(&s_AddSoundButton, "Add", 0, &AddSoundButton, 0, "Load a new sound to use in the map")) + InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_SOUND, "Add Sound", "Add", "mapres", "", AddSound, this); + } + s_ScrollRegion.End(); } static int EditorListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser) @@ -5875,7 +5847,7 @@ void CEditor::Render() RenderSelectedImage(View); } else if(m_Mode == MODE_SOUNDS) - RenderSounds(ToolBox, View); + RenderSounds(ToolBox); } UI()->MapScreen(); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index d5cb0dcfa..dead757eb 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -1129,7 +1129,7 @@ public: void RenderLayers(CUIRect LayersBox); void RenderImagesList(CUIRect Toolbox); void RenderSelectedImage(CUIRect View); - void RenderSounds(CUIRect Toolbox, CUIRect View); + void RenderSounds(CUIRect Toolbox); void RenderModebar(CUIRect View); void RenderStatusbar(CUIRect View); void RenderEnvelopeEditor(CUIRect View); From 713b6584f0392e8443a70a0547fdb898c0659b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 17 Aug 2022 19:02:18 +0200 Subject: [PATCH 15/17] Add `str_countchr` to count occurrences of a char in a string --- src/base/system.cpp | 12 ++++++++++++ src/base/system.h | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/base/system.cpp b/src/base/system.cpp index 6e848895e..0a4fe1cbf 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -2997,6 +2997,18 @@ const char *str_rchr(const char *haystack, char needle) return strrchr(haystack, needle); } +int str_countchr(const char *haystack, char needle) +{ + int count = 0; + while(*haystack) + { + if(*haystack == needle) + count++; + haystack++; + } + return count; +} + void str_hex(char *dst, int dst_size, const void *data, int data_size) { static const char hex[] = "0123456789ABCDEF"; diff --git a/src/base/system.h b/src/base/system.h index 9e40dcf6a..3aebdca83 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -1588,6 +1588,23 @@ const char *str_find(const char *haystack, const char *needle); */ const char *str_rchr(const char *haystack, char needle); +/* + Function: str_countchr + Counts the number of occurrences of a character in a string. + + Parameters: + haystack - String to count in + needle - Character to count + + Returns: + The number of characters in the haystack string matching + the needle character. + + Remarks: + - The strings are treated as zero-terminated strings. +*/ +int str_countchr(const char *haystack, char needle); + /* Function: str_hex Takes a datablock and generates a hex string of it, with spaces From 7c496dd5b4635375c19f197547c4eb9964156e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 15 Aug 2022 23:19:56 +0200 Subject: [PATCH 16/17] Make ingame server info MOTD scrollable using `CScrollRegion` --- src/game/client/components/menus_ingame.cpp | 31 ++++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 8c20c73bc..a82447be1 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "menus.h" @@ -450,14 +451,30 @@ void CMenus::RenderServerInfo(CUIRect MainView) } // motd - Motd.HSplitTop(10.0f, 0, &Motd); + const float MotdFontSize = 16.0f; + Motd.HSplitTop(10.0f, nullptr, &Motd); Motd.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - Motd.Margin(5.0f, &Motd); - y = 0.0f; - x = 5.0f; - TextRender()->Text(0, Motd.x + x, Motd.y + y, 32, Localize("MOTD"), -1.0f); - y += 32.0f + 5.0f; - TextRender()->Text(0, Motd.x + x, Motd.y + y, 16, m_pClient->m_Motd.m_aServerMotd, Motd.w); + Motd.HMargin(5.0f, &Motd); + Motd.VMargin(10.0f, &Motd); + + CUIRect MotdHeader; + Motd.HSplitTop(2.0f * MotdFontSize, &MotdHeader, &Motd); + Motd.HSplitTop(5.0f, nullptr, &Motd); + TextRender()->Text(nullptr, MotdHeader.x, MotdHeader.y, 2.0f * MotdFontSize, Localize("MOTD"), -1.0f); + + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollUnit = 5 * MotdFontSize; + s_ScrollRegion.Begin(&Motd, &ScrollOffset, &ScrollParams); + Motd.y += ScrollOffset.y; + + CUIRect MotdTextArea; + Motd.HSplitTop((str_countchr(m_pClient->m_Motd.m_aServerMotd, '\n') + 1) * MotdFontSize, &MotdTextArea, &Motd); + s_ScrollRegion.AddRect(MotdTextArea); + TextRender()->Text(nullptr, MotdTextArea.x, MotdTextArea.y, MotdFontSize, m_pClient->m_Motd.m_aServerMotd, MotdTextArea.w); + + s_ScrollRegion.End(); } bool CMenus::RenderServerControlServer(CUIRect MainView) From 638559b648dc92f1582ff96be183d816c3963f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 19 Aug 2022 16:11:40 +0200 Subject: [PATCH 17/17] Refactor scrollable controls settings using `CScrollRegion` Replace hacky usage of `UiDoListboxStart` with `CScrollRegion`. --- src/game/client/components/menus_settings.cpp | 181 ++++++++++-------- 1 file changed, 102 insertions(+), 79 deletions(-) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index aec4f9333..5b88217aa 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "binds.h" @@ -927,7 +928,7 @@ float CMenus::RenderSettingsControlsJoystick(CUIRect View) const float Spacing = 2.0f; const float BackgroundHeight = NumOptions * (ButtonHeight + Spacing) + (NumOptions == 1 ? 0 : Spacing); if(View.h < BackgroundHeight) - return BackgroundHeight; // TODO: make this less hacky by porting CScrollRegion from vanilla + return BackgroundHeight; View.HSplitTop(BackgroundHeight, &View, 0); @@ -1107,110 +1108,120 @@ void CMenus::RenderSettingsControls(CUIRect MainView) } } - // controls in a scrollable listbox - static int s_ControlsList = 0; - static int s_SelectedControl = -1; - static float s_ScrollValue = 0; - static int s_OldSelected = 0; + // scrollable controls static float s_JoystickSettingsHeight = 0.0f; // we calculate this later and don't render until enough space is available - // Hacky values: Size of 10.0f per item for smoother scrolling, 72 elements - // fits the current size of controls settings - const float PseudoItemSize = 10.0f; - UiDoListboxStart(&s_ControlsList, &MainView, PseudoItemSize, Localize("Controls"), "", 72 + (int)ceilf(s_JoystickSettingsHeight / PseudoItemSize + 0.5f), 1, s_SelectedControl, s_ScrollValue); - - CUIRect MouseSettings, MovementSettings, WeaponSettings, VotingSettings, ChatSettings, DummySettings, MiscSettings, JoystickSettings, ResetButton; - CListboxItem Item = UiDoListboxNextItem(&s_OldSelected, false, false, true); - Item.m_Rect.HSplitTop(10.0f, 0, &Item.m_Rect); - Item.m_Rect.VSplitMid(&MouseSettings, &VotingSettings); + static CScrollRegion s_ScrollRegion; + vec2 ScrollOffset(0.0f, 0.0f); + CScrollRegionParams ScrollParams; + ScrollParams.m_ScrollUnit = 120.0f; + s_ScrollRegion.Begin(&MainView, &ScrollOffset, &ScrollParams); + MainView.y += ScrollOffset.y; const float FontSize = 14.0f; const float Margin = 10.0f; const float HeaderHeight = FontSize + 5.0f + Margin; + CUIRect MouseSettings, MovementSettings, WeaponSettings, VotingSettings, ChatSettings, DummySettings, MiscSettings, JoystickSettings, ResetButton; + MainView.VSplitMid(&MouseSettings, &VotingSettings); + // mouse settings { MouseSettings.VMargin(5.0f, &MouseSettings); MouseSettings.HSplitTop(80.0f, &MouseSettings, &JoystickSettings); - MouseSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - MouseSettings.VMargin(10.0f, &MouseSettings); + if(s_ScrollRegion.AddRect(MouseSettings)) + { + MouseSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + MouseSettings.VMargin(10.0f, &MouseSettings); - TextRender()->Text(0, MouseSettings.x, MouseSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Mouse"), -1.0f); + TextRender()->Text(0, MouseSettings.x, MouseSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Mouse"), -1.0f); - MouseSettings.HSplitTop(HeaderHeight, 0, &MouseSettings); + MouseSettings.HSplitTop(HeaderHeight, 0, &MouseSettings); - CUIRect Button; - MouseSettings.HSplitTop(20.0f, &Button, &MouseSettings); - UI()->DoScrollbarOption(&g_Config.m_InpMousesens, &g_Config.m_InpMousesens, &Button, Localize("Ingame mouse sens."), 1, 500, &CUI::ms_LogarithmicScrollbarScale, CUI::SCROLLBAR_OPTION_NOCLAMPVALUE); + CUIRect Button; + MouseSettings.HSplitTop(20.0f, &Button, &MouseSettings); + UI()->DoScrollbarOption(&g_Config.m_InpMousesens, &g_Config.m_InpMousesens, &Button, Localize("Ingame mouse sens."), 1, 500, &CUI::ms_LogarithmicScrollbarScale, CUI::SCROLLBAR_OPTION_NOCLAMPVALUE); - MouseSettings.HSplitTop(2.0f, 0, &MouseSettings); + MouseSettings.HSplitTop(2.0f, 0, &MouseSettings); - MouseSettings.HSplitTop(20.0f, &Button, &MouseSettings); - UI()->DoScrollbarOption(&g_Config.m_UiMousesens, &g_Config.m_UiMousesens, &Button, Localize("UI mouse sens."), 1, 500, &CUI::ms_LogarithmicScrollbarScale, CUI::SCROLLBAR_OPTION_NOCLAMPVALUE); + MouseSettings.HSplitTop(20.0f, &Button, &MouseSettings); + UI()->DoScrollbarOption(&g_Config.m_UiMousesens, &g_Config.m_UiMousesens, &Button, Localize("UI mouse sens."), 1, 500, &CUI::ms_LogarithmicScrollbarScale, CUI::SCROLLBAR_OPTION_NOCLAMPVALUE); + } } // joystick settings { JoystickSettings.HSplitTop(Margin, 0, &JoystickSettings); - JoystickSettings.HSplitTop(s_JoystickSettingsHeight, &JoystickSettings, &MovementSettings); - JoystickSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - JoystickSettings.VMargin(Margin, &JoystickSettings); + JoystickSettings.HSplitTop(s_JoystickSettingsHeight + HeaderHeight + Margin, &JoystickSettings, &MovementSettings); + if(s_ScrollRegion.AddRect(JoystickSettings)) + { + JoystickSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + JoystickSettings.VMargin(Margin, &JoystickSettings); - TextRender()->Text(0, JoystickSettings.x, JoystickSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Controller"), -1.0f); + TextRender()->Text(0, JoystickSettings.x, JoystickSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Controller"), -1.0f); - JoystickSettings.HSplitTop(HeaderHeight, 0, &JoystickSettings); - s_JoystickSettingsHeight = RenderSettingsControlsJoystick(JoystickSettings) + HeaderHeight + Margin; // + Margin for another bottom margin + JoystickSettings.HSplitTop(HeaderHeight, 0, &JoystickSettings); + s_JoystickSettingsHeight = RenderSettingsControlsJoystick(JoystickSettings); + } } // movement settings { MovementSettings.HSplitTop(Margin, 0, &MovementSettings); MovementSettings.HSplitTop(365.0f, &MovementSettings, &WeaponSettings); - MovementSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - MovementSettings.VMargin(Margin, &MovementSettings); + if(s_ScrollRegion.AddRect(MovementSettings)) + { + MovementSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + MovementSettings.VMargin(Margin, &MovementSettings); - TextRender()->Text(0, MovementSettings.x, MovementSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Movement"), -1.0f); + TextRender()->Text(0, MovementSettings.x, MovementSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Movement"), -1.0f); - MovementSettings.HSplitTop(HeaderHeight, 0, &MovementSettings); - DoSettingsControlsButtons(0, 15, MovementSettings); + MovementSettings.HSplitTop(HeaderHeight, 0, &MovementSettings); + DoSettingsControlsButtons(0, 15, MovementSettings); + } } // weapon settings { WeaponSettings.HSplitTop(Margin, 0, &WeaponSettings); WeaponSettings.HSplitTop(190.0f, &WeaponSettings, &ResetButton); - WeaponSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - WeaponSettings.VMargin(Margin, &WeaponSettings); + if(s_ScrollRegion.AddRect(WeaponSettings)) + { + WeaponSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + WeaponSettings.VMargin(Margin, &WeaponSettings); - TextRender()->Text(0, WeaponSettings.x, WeaponSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Weapon"), -1.0f); + TextRender()->Text(0, WeaponSettings.x, WeaponSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Weapon"), -1.0f); - WeaponSettings.HSplitTop(HeaderHeight, 0, &WeaponSettings); - DoSettingsControlsButtons(15, 22, WeaponSettings); + WeaponSettings.HSplitTop(HeaderHeight, 0, &WeaponSettings); + DoSettingsControlsButtons(15, 22, WeaponSettings); + } } // defaults { ResetButton.HSplitTop(Margin, 0, &ResetButton); ResetButton.HSplitTop(40.0f, &ResetButton, 0); - ResetButton.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - ResetButton.HMargin(10.0f, &ResetButton); - ResetButton.VMargin(30.0f, &ResetButton); - ResetButton.HSplitTop(20.0f, &ResetButton, 0); - static CButtonContainer s_DefaultButton; - if(DoButton_Menu(&s_DefaultButton, Localize("Reset to defaults"), 0, &ResetButton)) + if(s_ScrollRegion.AddRect(ResetButton)) { - m_pClient->m_Binds.SetDefaults(); + ResetButton.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + ResetButton.HMargin(10.0f, &ResetButton); + ResetButton.VMargin(30.0f, &ResetButton); + static CButtonContainer s_DefaultButton; + if(DoButton_Menu(&s_DefaultButton, Localize("Reset to defaults"), 0, &ResetButton)) + { + m_pClient->m_Binds.SetDefaults(); - g_Config.m_InpMousesens = 200; - g_Config.m_UiMousesens = 200; + g_Config.m_InpMousesens = 200; + g_Config.m_UiMousesens = 200; - g_Config.m_InpControllerEnable = 0; - g_Config.m_InpControllerGUID[0] = '\0'; - g_Config.m_InpControllerAbsolute = 0; - g_Config.m_InpControllerSens = 100; - g_Config.m_InpControllerX = 0; - g_Config.m_InpControllerY = 1; - g_Config.m_InpControllerTolerance = 5; - g_Config.m_UiControllerSens = 100; + g_Config.m_InpControllerEnable = 0; + g_Config.m_InpControllerGUID[0] = '\0'; + g_Config.m_InpControllerAbsolute = 0; + g_Config.m_InpControllerSens = 100; + g_Config.m_InpControllerX = 0; + g_Config.m_InpControllerY = 1; + g_Config.m_InpControllerTolerance = 5; + g_Config.m_UiControllerSens = 100; + } } } @@ -1218,55 +1229,67 @@ void CMenus::RenderSettingsControls(CUIRect MainView) { VotingSettings.VMargin(5.0f, &VotingSettings); VotingSettings.HSplitTop(80.0f, &VotingSettings, &ChatSettings); - VotingSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - VotingSettings.VMargin(Margin, &VotingSettings); + if(s_ScrollRegion.AddRect(VotingSettings)) + { + VotingSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + VotingSettings.VMargin(Margin, &VotingSettings); - TextRender()->Text(0, VotingSettings.x, VotingSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Voting"), -1.0f); + TextRender()->Text(0, VotingSettings.x, VotingSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Voting"), -1.0f); - VotingSettings.HSplitTop(HeaderHeight, 0, &VotingSettings); - DoSettingsControlsButtons(22, 24, VotingSettings); + VotingSettings.HSplitTop(HeaderHeight, 0, &VotingSettings); + DoSettingsControlsButtons(22, 24, VotingSettings); + } } // chat settings { ChatSettings.HSplitTop(Margin, 0, &ChatSettings); ChatSettings.HSplitTop(145.0f, &ChatSettings, &DummySettings); - ChatSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - ChatSettings.VMargin(Margin, &ChatSettings); + if(s_ScrollRegion.AddRect(ChatSettings)) + { + ChatSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + ChatSettings.VMargin(Margin, &ChatSettings); - TextRender()->Text(0, ChatSettings.x, ChatSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Chat"), -1.0f); + TextRender()->Text(0, ChatSettings.x, ChatSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Chat"), -1.0f); - ChatSettings.HSplitTop(HeaderHeight, 0, &ChatSettings); - DoSettingsControlsButtons(24, 29, ChatSettings); + ChatSettings.HSplitTop(HeaderHeight, 0, &ChatSettings); + DoSettingsControlsButtons(24, 29, ChatSettings); + } } // dummy settings { DummySettings.HSplitTop(Margin, 0, &DummySettings); DummySettings.HSplitTop(100.0f, &DummySettings, &MiscSettings); - DummySettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - DummySettings.VMargin(Margin, &DummySettings); + if(s_ScrollRegion.AddRect(DummySettings)) + { + DummySettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + DummySettings.VMargin(Margin, &DummySettings); - TextRender()->Text(0, DummySettings.x, DummySettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Dummy"), -1.0f); + TextRender()->Text(0, DummySettings.x, DummySettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Dummy"), -1.0f); - DummySettings.HSplitTop(HeaderHeight, 0, &DummySettings); - DoSettingsControlsButtons(29, 32, DummySettings); + DummySettings.HSplitTop(HeaderHeight, 0, &DummySettings); + DoSettingsControlsButtons(29, 32, DummySettings); + } } // misc settings { MiscSettings.HSplitTop(Margin, 0, &MiscSettings); MiscSettings.HSplitTop(300.0f, &MiscSettings, 0); - MiscSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); - MiscSettings.VMargin(Margin, &MiscSettings); + if(s_ScrollRegion.AddRect(MiscSettings)) + { + MiscSettings.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 10.0f); + MiscSettings.VMargin(Margin, &MiscSettings); - TextRender()->Text(0, MiscSettings.x, MiscSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Miscellaneous"), -1.0f); + TextRender()->Text(0, MiscSettings.x, MiscSettings.y + (HeaderHeight - FontSize) / 2.f, FontSize, Localize("Miscellaneous"), -1.0f); - MiscSettings.HSplitTop(HeaderHeight, 0, &MiscSettings); - DoSettingsControlsButtons(32, 44, MiscSettings); + MiscSettings.HSplitTop(HeaderHeight, 0, &MiscSettings); + DoSettingsControlsButtons(32, 44, MiscSettings); + } } - UiDoListboxEnd(&s_ScrollValue, 0); + s_ScrollRegion.End(); } int CMenus::RenderDropDown(int &CurDropDownState, CUIRect *pRect, int CurSelection, const void **pIDs, const char **pStr, int PickNum, CButtonContainer *pButtonContainer, float &ScrollVal)