diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/base/system.c | 15 | ||||
-rw-r--r-- | src/base/system.h | 9 | ||||
-rw-r--r-- | src/engine/shared/console.cpp | 61 | ||||
-rw-r--r-- | src/engine/shared/console.h | 2 | ||||
-rw-r--r-- | src/game/client/components/camera.cpp | 4 | ||||
-rw-r--r-- | src/game/client/components/menus.cpp | 7 | ||||
-rw-r--r-- | src/game/client/components/menus.h | 4 | ||||
-rw-r--r-- | src/game/client/components/menus_popups.cpp | 90 | ||||
-rw-r--r-- | src/game/client/components/skins.cpp | 59 | ||||
-rw-r--r-- | src/game/client/components/skins.h | 2 | ||||
-rw-r--r-- | src/game/client/ui.cpp | 124 | ||||
-rw-r--r-- | src/game/client/ui.h | 30 | ||||
-rw-r--r-- | src/game/editor/editor.cpp | 45 | ||||
-rw-r--r-- | src/game/editor/editor.h | 33 | ||||
-rw-r--r-- | src/game/editor/popups.cpp | 236 | ||||
-rw-r--r-- | src/test/str.cpp | 18 |
17 files changed, 320 insertions, 420 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f7e27eb48..7d68db960 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1469,7 +1469,6 @@ if(CLIENT) components/menus_demo.cpp components/menus_ingame.cpp components/menus_listbox.cpp - components/menus_popups.cpp components/menus_scrollregion.cpp components/menus_settings.cpp components/menus_start.cpp diff --git a/src/base/system.c b/src/base/system.c index 7199cb170..f77760507 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -2705,20 +2705,17 @@ void str_utf8_copy_num(char *dst, const char *src, int dst_size, int num) str_copy(dst, src, cursor < dst_size ? cursor+1 : dst_size); } -void str_utf8_stats(const char *str, int max_size, int *size, int *count) +void str_utf8_stats(const char *str, int max_size, int max_count, int *size, int *count) { *size = 0; *count = 0; - while(str[*size] && *size < max_size) + while(*size < max_size && *count < max_count) { int new_size = str_utf8_forward(str, *size); - if(new_size != *size) - { - if(new_size >= max_size) - break; - *size = new_size; - ++(*count); - } + if(new_size == *size || new_size >= max_size) + break; + *size = new_size; + ++(*count); } } diff --git a/src/base/system.h b/src/base/system.h index 3c31854f0..0a4cc4764 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -1748,19 +1748,20 @@ void str_utf8_copy_num(char *dst, const char *src, int dst_size, int num); /* Function: str_utf8_stats - Determines the byte size and utf8 character count of a string. + Determines the byte size and utf8 character count of a utf8 string. Parameters: str - Pointer to the string. max_size - Maximum number of bytes to count. + max_count - Maximum number of utf8 characters to count. size - Pointer to store size (number of non-zero bytes) of the string. count - Pointer to store count of utf8 characters of the string. Remarks: - - Assumes nothing about the encoding of the string. - It's the users responsibility to make sure the bounds are aligned. + - The string is treated as zero-terminated utf8 string. + - It's the user's responsibility to make sure the bounds are aligned. */ -void str_utf8_stats(const char *str, int max_size, int *size, int *count); +void str_utf8_stats(const char *str, int max_size, int max_count, int *size, int *count); /* Function: secure_random_init diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index 80f6176de..369087d6d 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -683,9 +683,19 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData) } } +void CConsole::TraverseChain(FCommandCallback *ppfnCallback, void **ppUserData) +{ + while(*ppfnCallback == Con_Chain) + { + CChain *pChainInfo = static_cast<CChain *>(*ppUserData); + *ppfnCallback = pChainInfo->m_pfnCallback; + *ppUserData = pChainInfo->m_pCallbackUserData; + } +} + void CConsole::Con_EvalIf(IResult *pResult, void *pUserData) { - CConsole* pConsole = static_cast<CConsole *>(pUserData); + CConsole *pConsole = static_cast<CConsole *>(pUserData); CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), pConsole->m_FlagMask); char aBuf[128]; if(!pCommand) @@ -694,16 +704,19 @@ void CConsole::Con_EvalIf(IResult *pResult, void *pUserData) pConsole->Print(OUTPUT_LEVEL_STANDARD, "console", aBuf); return; } + FCommandCallback pfnCallback = pCommand->m_pfnCallback; + void *pCallbackUserData = pCommand->m_pUserData; + pConsole->TraverseChain(&pfnCallback, &pCallbackUserData); CResult Result; - pCommand->m_pfnCallback(&Result, pCommand->m_pUserData); + pfnCallback(&Result, pCallbackUserData); bool Condition = false; - if(pCommand->m_pfnCallback == IntVariableCommand) + if(pfnCallback == IntVariableCommand) Condition = Result.m_Value == atoi(pResult->GetString(2)); else Condition = !str_comp_nocase(Result.m_aValue, pResult->GetString(2)); if(!str_comp(pResult->GetString(1), "!=")) Condition = !Condition; - else if(str_comp(pResult->GetString(1), "==") && pCommand->m_pfnCallback == StrVariableCommand) + else if(str_comp(pResult->GetString(1), "==") && pfnCallback == StrVariableCommand) pConsole->Print(OUTPUT_LEVEL_STANDARD, "console", "Error: invalid comperator for type string"); else if(!str_comp(pResult->GetString(1), ">")) Condition = Result.m_Value > atoi(pResult->GetString(2)); @@ -727,22 +740,14 @@ void CConsole::Con_EvalIf(IResult *pResult, void *pUserData) void CConsole::ConToggle(IConsole::IResult *pResult, void *pUser) { - CConsole* pConsole = static_cast<CConsole *>(pUser); + CConsole *pConsole = static_cast<CConsole *>(pUser); char aBuf[128] = {0}; CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), pConsole->m_FlagMask); if(pCommand) { FCommandCallback pfnCallback = pCommand->m_pfnCallback; void *pUserData = pCommand->m_pUserData; - - // check for chain - if(pCommand->m_pfnCallback == Con_Chain) - { - CChain *pChainInfo = static_cast<CChain *>(pCommand->m_pUserData); - pfnCallback = pChainInfo->m_pfnCallback; - pUserData = pChainInfo->m_pCallbackUserData; - } - + pConsole->TraverseChain(&pfnCallback, &pUserData); if(pfnCallback == IntVariableCommand) { CIntVariableData *pData = static_cast<CIntVariableData *>(pUserData); @@ -757,26 +762,20 @@ void CConsole::ConToggle(IConsole::IResult *pResult, void *pUser) else str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(0)); - if(aBuf[0] != 0) + if(aBuf[0]) pConsole->Print(OUTPUT_LEVEL_STANDARD, "console", aBuf); } void CConsole::ConToggleStroke(IConsole::IResult *pResult, void *pUser) { - CConsole* pConsole = static_cast<CConsole *>(pUser); + CConsole *pConsole = static_cast<CConsole *>(pUser); char aBuf[128] = {0}; CCommand *pCommand = pConsole->FindCommand(pResult->GetString(1), pConsole->m_FlagMask); if(pCommand) { FCommandCallback pfnCallback = pCommand->m_pfnCallback; - - // check for chain - if(pCommand->m_pfnCallback == Con_Chain) - { - CChain *pChainInfo = static_cast<CChain *>(pCommand->m_pUserData); - pfnCallback = pChainInfo->m_pfnCallback; - } - + void *pUserData = pCommand->m_pUserData; + pConsole->TraverseChain(&pfnCallback, &pUserData); if(pfnCallback == IntVariableCommand) { int Val = pResult->GetInteger(0)==0 ? pResult->GetInteger(3) : pResult->GetInteger(2); @@ -790,7 +789,7 @@ void CConsole::ConToggleStroke(IConsole::IResult *pResult, void *pUser) else str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(1)); - if(aBuf[0] != 0) + if(aBuf[0]) pConsole->Print(OUTPUT_LEVEL_STANDARD, "console", aBuf); } @@ -834,8 +833,16 @@ CConsole::~CConsole() { CCommand *pNext = pCommand->m_pNext; - if(pCommand->m_pfnCallback == Con_Chain) - mem_free(static_cast<CChain *>(pCommand->m_pUserData)); + FCommandCallback pfnCallback = pCommand->m_pfnCallback; + void *pUserData = pCommand->m_pUserData; + while(pfnCallback == Con_Chain) + { + CChain *pChainInfo = static_cast<CChain *>(pUserData); + pfnCallback = pChainInfo->m_pfnCallback; + pUserData = pChainInfo->m_pCallbackUserData; + mem_free(pChainInfo); + } + mem_free(pCommand); pCommand = pNext; diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h index 3ea9c429b..7976d72ed 100644 --- a/src/engine/shared/console.h +++ b/src/engine/shared/console.h @@ -54,6 +54,8 @@ class CConsole : public IConsole CCommand *m_pRecycleList; CHeap m_TempCommands; + void TraverseChain(FCommandCallback *ppfnCallback, void **ppUserData); + static void Con_Chain(IResult *pResult, void *pUserData); static void Con_Echo(IResult *pResult, void *pUserData); static void Con_Exec(IResult *pResult, void *pUserData); diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 5a85bbe15..337fa8e7b 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -138,8 +138,12 @@ void CCamera::OnRender() void CCamera::ChangePosition(int PositionNumber) { + if(m_pClient->Client()->State() == IClient::STATE_ONLINE) + return; //Do not change Main Menu Camera Positions while we are changing settings in-game + if(PositionNumber < 0 || PositionNumber > NUM_POS-1) return; + m_AnimationStartPos = m_Center; m_RotationCenter = m_Positions[PositionNumber]; m_CurrentPosition = PositionNumber; diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 4765981ca..b5edc5fe2 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -76,8 +76,6 @@ CMenus::CMenus() SetMenuPage(PAGE_START); m_MenuPageOld = PAGE_START; - m_PopupActive = false; - m_aDemoLoadingFile[0] = 0; m_DemoLoadingPopupRendered = false; @@ -1166,8 +1164,7 @@ void CMenus::RenderMenu(CUIRect Screen) } } - // do overlay popups - DoPopupMenu(); + UI()->RenderPopupMenus(); } else { @@ -1826,7 +1823,7 @@ void CMenus::OnRender() bool CMenus::CheckHotKey(int Key) const { - return !m_KeyReaderIsActive && !m_KeyReaderWasActive && !UI()->IsInputActive() && !m_PopupActive + return !m_KeyReaderIsActive && !m_KeyReaderWasActive && !UI()->IsInputActive() && !UI()->IsPopupActive() && !Input()->KeyIsPressed(KEY_LSHIFT) && !Input()->KeyIsPressed(KEY_RSHIFT) && !Input()->KeyIsPressed(KEY_LCTRL) && !Input()->KeyIsPressed(KEY_RCTRL) && !Input()->KeyIsPressed(KEY_LALT) diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index c40091acb..4ebb94796 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -278,7 +278,6 @@ private: bool m_MenuActive; vec2 m_MousePos; vec2 m_PrevMousePos; - bool m_PopupActive; int m_ActiveListBox; int m_PopupCountrySelection; bool m_SkinModified; @@ -771,9 +770,6 @@ private: void SetActive(bool Active); - void InvokePopupMenu(void *pID, int Flags, float X, float Y, float W, float H, int (*pfnFunc)(CMenus *pMenu, CUIRect Rect), void *pExtra=0); - void DoPopupMenu(); - // loading int m_LoadCurrent; int m_LoadTotal; diff --git a/src/game/client/components/menus_popups.cpp b/src/game/client/components/menus_popups.cpp deleted file mode 100644 index ef9e1042c..000000000 --- a/src/game/client/components/menus_popups.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ -/* If you are missing that file, acquire a complete release at teeworlds.com. */ - -#include <base/tl/array.h> - -#include <engine/shared/config.h> - -#include <engine/console.h> -#include <engine/graphics.h> -#include <engine/input.h> -#include <engine/keys.h> -#include <engine/storage.h> -#include <engine/serverbrowser.h> -#include <engine/textrender.h> - -#include <generated/client_data.h> -#include <generated/protocol.h> - -#include "countryflags.h" -#include "menus.h" - - -// popup menu handling -static struct -{ - CUIRect m_Rect; - void *m_pId; - int (*m_pfnFunc)(CMenus *pMenu, CUIRect Rect); - int m_IsMenu; - void *m_pExtra; -} s_Popups; - -void CMenus::InvokePopupMenu(void *pID, int Flags, float x, float y, float Width, float Height, int (*pfnFunc)(CMenus *pMenu, CUIRect Rect), void *pExtra) -{ - if(m_PopupActive) - return; - if(x + Width > UI()->Screen()->w) - x = UI()->Screen()->w - Width; - if(y + Height > UI()->Screen()->h) - y = UI()->Screen()->h - Height; - s_Popups.m_pId = pID; - s_Popups.m_IsMenu = Flags; - s_Popups.m_Rect.x = x; - s_Popups.m_Rect.y = y; - s_Popups.m_Rect.w = Width; - s_Popups.m_Rect.h = Height; - s_Popups.m_pfnFunc = pfnFunc; - s_Popups.m_pExtra = pExtra; - m_PopupActive = true; -} - -void CMenus::DoPopupMenu() -{ - if(m_PopupActive) - { - UI()->SetHotItem(&s_Popups.m_pId); - - if(UI()->CheckActiveItem(&s_Popups.m_pId)) - { - if(!UI()->MouseButton(0)) - { - if(!UI()->MouseHovered(&s_Popups.m_Rect)) - m_PopupActive = false; - UI()->SetActiveItem(0); - } - } - else if(UI()->HotItem() == &s_Popups.m_pId) - { - if(UI()->MouseButton(0)) - UI()->SetActiveItem(&s_Popups.m_pId); - } - - int Corners = CUIRect::CORNER_ALL; - if(s_Popups.m_IsMenu) - Corners = CUIRect::CORNER_R|CUIRect::CORNER_B; - - CUIRect r = s_Popups.m_Rect; - r.Draw(vec4(0.5f,0.5f,0.5f,0.75f), 3.0f, Corners); - r.Margin(1.0f, &r); - r.Draw(vec4(0,0,0,0.75f), 3.0f, Corners); - r.Margin(4.0f, &r); - - if(s_Popups.m_pfnFunc(this, r)) - m_PopupActive = false; - - if(UI()->KeyPress(KEY_ESCAPE)) - m_PopupActive = false; - } -} - diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index 9c00e1745..0251ec2ce 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -1,10 +1,8 @@ /* (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 <math.h> - #include <base/color.h> -#include <base/system.h> #include <base/math.h> +#include <base/system.h> #include <engine/graphics.h> #include <engine/storage.h> @@ -31,6 +29,11 @@ int CSkins::SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser) if(IsDir || !str_endswith(pName, ".png")) return 0; + CSkinPart Part; + str_utf8_copy_num(Part.m_aName, pName, minimum(str_length(pName) - 3, int(sizeof(Part.m_aName))), MAX_SKIN_LENGTH); + if(pSelf->FindSkinPart(pSelf->m_ScanningPart, Part.m_aName, true) != -1) + return 0; + char aBuf[IO_MAX_PATH_LENGTH]; str_format(aBuf, sizeof(aBuf), "skins/%s/%s", CSkins::ms_apSkinPartNames[pSelf->m_ScanningPart], pName); CImageInfo Info; @@ -41,11 +44,10 @@ int CSkins::SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser) return 0; } - CSkinPart Part; Part.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); Part.m_BloodColor = vec3(1.0f, 1.0f, 1.0f); - unsigned char *d = (unsigned char *)Info.m_pData; + unsigned char *pData = (unsigned char *)Info.m_pData; int Pitch = Info.m_Width*4; // dig out blood color @@ -60,11 +62,11 @@ int CSkins::SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser) for(int y = PartY; y < PartY+PartHeight; y++) for(int x = PartX; x < PartX+PartWidth; x++) { - if(d[y*Pitch+x*4+3] > 128) + if(pData[y*Pitch+x*4+3] > 128) { - aColors[0] += d[y*Pitch+x*4+0]; - aColors[1] += d[y*Pitch+x*4+1]; - aColors[2] += d[y*Pitch+x*4+2]; + aColors[0] += pData[y*Pitch+x*4+0]; + aColors[1] += pData[y*Pitch+x*4+1]; + aColors[2] += pData[y*Pitch+x*4+2]; } } @@ -77,10 +79,10 @@ int CSkins::SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser) // make the texture gray scale for(int i = 0; i < Info.m_Width*Info.m_Height; i++) { - int v = (d[i*Step]+d[i*Step+1]+d[i*Step+2])/3; - d[i*Step] = v; - d[i*Step+1] = v; - d[i*Step+2] = v; + const int Average = (pData[i*Step]+pData[i*Step+1]+pData[i*Step+2])/3; + pData[i*Step] = Average; + pData[i*Step+1] = Average; + pData[i*Step+2] = Average; } Part.m_ColorTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); @@ -92,7 +94,6 @@ int CSkins::SkinPartScan(const char *pName, int IsDir, int DirType, void *pUser) Part.m_Flags |= SKINFLAG_SPECIAL; if(DirType != IStorage::TYPE_SAVE) Part.m_Flags |= SKINFLAG_STANDARD; - str_utf8_copy_num(Part.m_aName, pName, minimum(str_length(pName) - 3, int(sizeof(Part.m_aName))), MAX_SKIN_LENGTH); if(pSelf->Config()->m_Debug) { str_format(aBuf, sizeof(aBuf), "load skin part %s", Part.m_aName); @@ -470,34 +471,34 @@ int CSkins::GetTeamColor(int UseCustomColors, int PartColor, int Team, int Part) return ColorVal; } -bool CSkins::ValidateSkinParts(char* aPartNames[NUM_SKINPARTS], int* aUseCustomColors, int* aPartColors, int GameFlags) const +bool CSkins::ValidateSkinParts(char *apPartNames[NUM_SKINPARTS], int *pUseCustomColors, int *pPartColors, int GameFlags) const { // force standard (black) eyes on team skins if(GameFlags&GAMEFLAG_TEAMS) { // TODO: adjust eye color here as well? - if(str_comp(aPartNames[SKINPART_EYES], "colorable") == 0 || str_comp(aPartNames[SKINPART_EYES], "negative") == 0) + if(str_comp(apPartNames[SKINPART_EYES], "colorable") == 0 || str_comp(apPartNames[SKINPART_EYES], "negative") == 0) { - str_copy(aPartNames[SKINPART_EYES], "standard", MAX_SKIN_ARRAY_SIZE); + str_copy(apPartNames[SKINPART_EYES], "standard", MAX_SKIN_ARRAY_SIZE); return false; } } else { - const int BodyColor = aPartColors[SKINPART_BODY]; - const int EyeColor = aPartColors[SKINPART_EYES]; + const int BodyColor = pPartColors[SKINPART_BODY]; + const int EyeColor = pPartColors[SKINPART_EYES]; vec3 BodyHsl(((BodyColor>>16)&0xff)/255.0f, ((BodyColor>>8)&0xff)/255.0f, (BodyColor&0xff)/255.0f); vec3 EyeHsl(((EyeColor>>16)&0xff)/255.0f, ((EyeColor>>8)&0xff)/255.0f, (EyeColor&0xff)/255.0f); - if(!aUseCustomColors[SKINPART_BODY]) + if(!pUseCustomColors[SKINPART_BODY]) BodyHsl = vec3(0, 0, 1); const vec3 BodyLab = RgbToLab(HslToRgb(BodyHsl)); - if(str_comp(aPartNames[SKINPART_EYES], "negative") == 0) + if(str_comp(apPartNames[SKINPART_EYES], "negative") == 0) { - if(!aUseCustomColors[SKINPART_EYES]) + if(!pUseCustomColors[SKINPART_EYES]) EyeHsl = vec3(0, 0, 1); vec3 OrgEyeHsl = EyeHsl; @@ -510,19 +511,19 @@ bool CSkins::ValidateSkinParts(char* aPartNames[NUM_SKINPARTS], int* aUseCustomC // white eye can't go to black because of our DARKEST_COLOR_LGT restriction, so switch to standard (black) eyes if(OrgEyeHsl.l < DARKEST_COLOR_LGT/255.f) - str_copy(aPartNames[SKINPART_EYES], "standard", MAX_SKIN_ARRAY_SIZE); // black + str_copy(apPartNames[SKINPART_EYES], "standard", MAX_SKIN_ARRAY_SIZE); // black else { - aUseCustomColors[SKINPART_EYES] = 1; - aPartColors[SKINPART_EYES] = (int(OrgEyeHsl.h*255) << 16) | (int(OrgEyeHsl.s*255) << 8) | (int(OrgEyeHsl.l*255)); + pUseCustomColors[SKINPART_EYES] = 1; + pPartColors[SKINPART_EYES] = (int(OrgEyeHsl.h*255) << 16) | (int(OrgEyeHsl.s*255) << 8) | (int(OrgEyeHsl.l*255)); } return false; } } - else if(str_comp(aPartNames[SKINPART_EYES], "colorable") == 0) + else if(str_comp(apPartNames[SKINPART_EYES], "colorable") == 0) { - if(!aUseCustomColors[SKINPART_EYES]) + if(!pUseCustomColors[SKINPART_EYES]) EyeHsl = vec3(0, 0, 1); vec3 OrgEyeHsl = EyeHsl; @@ -534,8 +535,8 @@ bool CSkins::ValidateSkinParts(char* aPartNames[NUM_SKINPARTS], int* aUseCustomC OrgEyeHsl.l -= 0.6f; OrgEyeHsl.l = clamp(OrgEyeHsl.l, 0.f, 1.f); - aUseCustomColors[SKINPART_EYES] = 1; - aPartColors[SKINPART_EYES] = (int(OrgEyeHsl.h*255) << 16) | (int(OrgEyeHsl.s*255) << 8) | (int(OrgEyeHsl.l*255)); + pUseCustomColors[SKINPART_EYES] = 1; + pPartColors[SKINPART_EYES] = (int(OrgEyeHsl.h*255) << 16) | (int(OrgEyeHsl.s*255) << 8) | (int(OrgEyeHsl.l*255)); return false; } diff --git a/src/game/client/components/skins.h b/src/game/client/components/skins.h index 5b79caaf4..cd96ec29a 100644 --- a/src/game/client/components/skins.h +++ b/src/game/client/components/skins.h @@ -74,7 +74,7 @@ public: int GetTeamColor(int UseCustomColors, int PartColor, int Team, int Part) const; // returns true if everything was valid and nothing changed - bool ValidateSkinParts(char *aPartNames[NUM_SKINPARTS], int *aUseCustomColors, int* aPartColors, int GameFlags) const; + bool ValidateSkinParts(char *apPartNames[NUM_SKINPARTS], int *pUseCustomColors, int *pPartColors, int GameFlags) const; void SaveSkinfile(const char *pSaveSkinName); diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 975cba877..03b764b1f 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -44,6 +44,8 @@ CUI::CUI() m_Screen.y = 0; m_NumClips = 0; + + m_NumPopupMenus = 0; } void CUI::Init(class CConfig *pConfig, class IGraphics *pGraphics, class IInput *pInput, class ITextRender *pTextRender) @@ -489,20 +491,20 @@ void CUI::DoEditBoxOption(CLineInput *pLineInput, const CUIRect *pRect, const ch float CUI::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) { - // layout - CUIRect Handle; - pRect->HSplitTop(minimum(pRect->h/8.0f, 33.0f), &Handle, 0); - Handle.y += (pRect->h-Handle.h)*Current; - Handle.VMargin(5.0f, &Handle); + Current = clamp(Current, 0.0f, 1.0f); + // layout CUIRect Rail; pRect->VMargin(5.0f, &Rail); + CUIRect Handle; + Rail.HSplitTop(clamp(33.0f, Rail.w, Rail.h / 8.0f), &Handle, 0); + Handle.y += (Rail.h - Handle.h) * Current; + // logic static float s_OffsetY; const bool InsideHandle = MouseHovered(&Handle); const bool InsideRail = MouseHovered(&Rail); - float ReturnValue = Current; bool Grabbed = false; // whether to apply the offset if(CheckActiveItem(pID)) @@ -516,14 +518,14 @@ float CUI::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) { if(MouseButton(0)) { - s_OffsetY = MouseY()-Handle.y; + s_OffsetY = MouseY() - Handle.y; SetActiveItem(pID); Grabbed = true; } } else if(MouseButtonClicked(0) && !InsideHandle && InsideRail) { - s_OffsetY = Handle.h * 0.5f; + s_OffsetY = Handle.h / 2.0f; SetActiveItem(pID); Grabbed = true; } @@ -533,45 +535,38 @@ float CUI::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) SetHotItem(pID); } + float ReturnValue = Current; if(Grabbed) { - const float Min = pRect->y; - const float Max = pRect->h-Handle.h; - const float Cur = MouseY()-s_OffsetY; - ReturnValue = clamp((Cur-Min)/Max, 0.0f, 1.0f); + const float Min = Rail.y; + const float Max = Rail.h - Handle.h; + const float Cur = MouseY() - s_OffsetY; + ReturnValue = clamp((Cur - Min) / Max, 0.0f, 1.0f); } // render - Rail.Draw(vec4(1.0f, 1.0f, 1.0f, 0.25f), Rail.w/2.0f); - - vec4 Color; - if(Grabbed) - Color = vec4(0.9f, 0.9f, 0.9f, 1.0f); - else if(InsideHandle) - Color = vec4(1.0f, 1.0f, 1.0f, 1.0f); - else - Color = vec4(0.8f, 0.8f, 0.8f, 1.0f); - Handle.Draw(Color, Handle.w/2.0f); + Rail.Draw(vec4(1.0f, 1.0f, 1.0f, 0.25f), Rail.w / 2.0f); + Handle.Draw(ScrollBarColorFunction.GetColor(Grabbed, InsideHandle), Handle.w / 2.0f); return ReturnValue; } float CUI::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) { - // layout - CUIRect Handle; - pRect->VSplitLeft(maximum(minimum(pRect->w/8.0f, 33.0f), pRect->h), &Handle, 0); - Handle.x += (pRect->w-Handle.w)*clamp(Current, 0.0f, 1.0f); - Handle.HMargin(5.0f, &Handle); + Current = clamp(Current, 0.0f, 1.0f); + // layout CUIRect Rail; pRect->HMargin(5.0f, &Rail); + CUIRect Handle; + Rail.VSplitLeft(clamp(33.0f, Rail.h, Rail.w / 8.0f), &Handle, 0); + Handle.x += (Rail.w - Handle.w) * Current; + // logic static float s_OffsetX; const bool InsideHandle = MouseHovered(&Handle); const bool InsideRail = MouseHovered(&Rail); - float ReturnValue = Current; bool Grabbed = false; // whether to apply the offset if(CheckActiveItem(pID)) @@ -585,14 +580,14 @@ float CUI::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) { if(MouseButton(0)) { - s_OffsetX = MouseX()-Handle.x; + s_OffsetX = MouseX() - Handle.x; SetActiveItem(pID); Grabbed = true; } } else if(MouseButtonClicked(0) && !InsideHandle && InsideRail) { - s_OffsetX = Handle.w * 0.5f; + s_OffsetX = Handle.w / 2.0f; SetActiveItem(pID); Grabbed = true; } @@ -602,25 +597,18 @@ float CUI::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) SetHotItem(pID); } + float ReturnValue = Current; if(Grabbed) { - const float Min = pRect->x; - const float Max = pRect->w-Handle.w; - const float Cur = MouseX()-s_OffsetX; - ReturnValue = clamp((Cur-Min)/Max, 0.0f, 1.0f); + const float Min = Rail.x; + const float Max = Rail.w - Handle.w; + const float Cur = MouseX() - s_OffsetX; + ReturnValue = clamp((Cur - Min) / Max, 0.0f, 1.0f); } // render - Rail.Draw(vec4(1.0f, 1.0f, 1.0f, 0.25f), Rail.h/2.0f); - - vec4 Color; - if(Grabbed) - Color = vec4(0.9f, 0.9f, 0.9f, 1.0f); - else if(InsideHandle) - Color = vec4(1.0f, 1.0f, 1.0f, 1.0f); - else - Color = vec4(0.8f, 0.8f, 0.8f, 1.0f); - Handle.Draw(Color, Handle.h/2.0f); + Rail.Draw(vec4(1.0f, 1.0f, 1.0f, 0.25f), Rail.h / 2.0f); + Handle.Draw(ScrollBarColorFunction.GetColor(Grabbed, InsideHandle), Handle.h / 2.0f); return ReturnValue; } @@ -724,6 +712,54 @@ float CUI::DrawClientID(float FontSize, vec2 CursorPosition, int ID, const vec4& return Width + 0.2f * FontSize; } +void CUI::DoPopupMenu(int X, int Y, int Width, int Height, void *pContext, bool (*pfnFunc)(void *pContext, CUIRect View), int Corners) +{ + dbg_assert(m_NumPopupMenus < MAX_POPUP_MENUS, "max popup menus exceeded"); + + if(X + Width > Screen()->w) + X = Screen()->w - Width; + if(Y + Height > Screen()->h) + Y = Screen()->h - Height; + + m_aPopupMenus[m_NumPopupMenus].m_Rect.x = X; + m_aPopupMenus[m_NumPopupMenus].m_Rect.y = Y; + m_aPopupMenus[m_NumPopupMenus].m_Rect.w = Width; + m_aPopupMenus[m_NumPopupMenus].m_Rect.h = Height; + m_aPopupMenus[m_NumPopupMenus].m_pContext = pContext; + m_aPopupMenus[m_NumPopupMenus].m_pfnFunc = pfnFunc; + m_aPopupMenus[m_NumPopupMenus].m_Corners = Corners; + m_NumPopupMenus++; +} + +void CUI::RenderPopupMenus() +{ + const bool MousePressed = MouseButton(0) || MouseButton(1); + static bool s_MousePressed = MousePressed; + + for(unsigned i = 0; i < m_NumPopupMenus; i++) + { + const bool Inside = MouseInside(&m_aPopupMenus[i].m_Rect); + const bool Active = i == m_NumPopupMenus - 1; + + // prevent activation of UI elements outside of active popup + if(Active) + SetHotItem(&m_aPopupMenus[i]); + + CUIRect PopupRect = m_aPopupMenus[i].m_Rect; + PopupRect.Draw(vec4(0.5f, 0.5f, 0.5f, 0.75f), 3.0f, m_aPopupMenus[i].m_Corners); + PopupRect.Margin(1.0f, &PopupRect); + PopupRect.Draw(vec4(0.0f, 0.0f, 0.0f, 0.75f), 3.0f, m_aPopupMenus[i].m_Corners); + PopupRect.Margin(4.0f, &PopupRect); + + if(m_aPopupMenus[i].m_pfnFunc(m_aPopupMenus[i].m_pContext, PopupRect)) + m_NumPopupMenus = i; // close this popup and all above it + if(Active && ((!Inside && s_MousePressed && !MousePressed) || ConsumeHotkey(HOTKEY_ESCAPE))) + m_NumPopupMenus--; // close top-most popup by clicking outside and with escape + } + + s_MousePressed = MousePressed; +} + float CUI::GetClientIDRectWidth(float FontSize) { if(!m_pConfig->m_ClShowUserId) diff --git a/src/game/client/ui.h b/src/game/client/ui.h index a1b2371dc..923f9d981 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -87,13 +87,26 @@ public: return vec4(1.0f, 1.0f, 1.0f, 0.5f); } } const LightButtonColorFunction; +static class CScrollBarColorFunction : public IButtonColorFunction +{ +public: + vec4 GetColor(bool Active, bool Hovered) const + { + if(Active) + return vec4(0.9f, 0.9f, 0.9f, 1.0f); + else if(Hovered) + return vec4(1.0f, 1.0f, 1.0f, 1.0f); + return vec4(0.8f, 0.8f, 0.8f, 1.0f); + } +} const ScrollBarColorFunction; class CUI { enum { - MAX_CLIP_NESTING_DEPTH = 16 + MAX_CLIP_NESTING_DEPTH = 16, + MAX_POPUP_MENUS = 8, }; bool m_Enabled; @@ -118,6 +131,16 @@ class CUI unsigned m_NumClips; void UpdateClipping(); + class + { + public: + CUIRect m_Rect; + int m_Corners; + void *m_pContext; + bool (*m_pfnFunc)(void *pContext, CUIRect View); // returns true to close popup + } m_aPopupMenus[MAX_POPUP_MENUS]; + unsigned m_NumPopupMenus; + class CConfig *m_pConfig; class IGraphics *m_pGraphics; class IInput *m_pInput; @@ -214,6 +237,11 @@ public: void DoScrollbarOption(const void *pID, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, const IScrollbarScale *pScale = &LinearScrollbarScale, bool Infinite = false); void DoScrollbarOptionLabeled(const void *pID, int *pOption, const CUIRect *pRect, const char *pStr, const char *apLabels[], int NumLabels, const IScrollbarScale *pScale = &LinearScrollbarScale); + // popup menu + void DoPopupMenu(int X, int Y, int Width, int Height, void *pContext, bool (*pfnFunc)(void *pContext, CUIRect View), int Corners = CUIRect::CORNER_ALL); + void RenderPopupMenus(); + bool IsPopupActive() const { return m_NumPopupMenus > 0; } + // client ID float DrawClientID(float FontSize, vec2 Position, int ID, const vec4& BgColor = vec4(1.0f, 1.0f, 1.0f, 0.5f), const vec4& TextColor = vec4(0.1f, 0.1f, 0.1f, 1.0f)); diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 9d265d5a2..9d04435e7 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -1044,8 +1044,7 @@ void CEditor::DoQuad(CQuad *q, int Index) { if(!UI()->MouseButton(1)) { - static int s_QuadPopupID = 0; - UiInvokePopupMenu(&s_QuadPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 180, PopupQuad); + UI()->DoPopupMenu(UI()->MouseX(), UI()->MouseY(), 120, 180, this, PopupQuad); m_LockMouse = false; s_Operation = OP_NONE; UI()->SetActiveItem(0); @@ -1221,8 +1220,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) { if(!UI()->MouseButton(1)) { - static int s_PointPopupID = 0; - UiInvokePopupMenu(&s_PointPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupPoint); + UI()->DoPopupMenu(UI()->MouseX(), UI()->MouseY(), 120, 150, this, PopupPoint); UI()->SetActiveItem(0); } } @@ -2234,14 +2232,13 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * ((pProps[i].m_Value >> s_aShift[2])&0xff)/255.0f, 1.0f); - static int s_ColorPicker, s_ColorPickerID; - ColorBox.Draw(Color, 0.0f, CUIRect::CORNER_NONE); + static int s_ColorPicker; if(DoButton_Editor_Common(&s_ColorPicker, 0x0, 0, &ColorBox, 0, 0x0)) { m_InitialPickerColor = RgbToHsv(vec3(Color.r, Color.g, Color.b)); m_SelectedPickerColor = m_InitialPickerColor; - UiInvokePopupMenu(&s_ColorPickerID, 0, UI()->MouseX(), UI()->MouseY(), 180, 180, PopupColorPicker); + UI()->DoPopupMenu(UI()->MouseX(), UI()->MouseY(), 180, 180, this, PopupColorPicker); } if(m_InitialPickerColor != m_SelectedPickerColor) @@ -2394,9 +2391,8 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) m_SelectedGroup = g; m_SelectedLayer = 0; - static int s_GroupPopupId = 0; if(Result == 2) - UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 220, PopupGroup); + UI()->DoPopupMenu(UI()->MouseX(), UI()->MouseY(), 145, 220, this, PopupGroup); if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick()) m_Map.m_lGroups[g]->m_Collapse ^= 1; @@ -2445,9 +2441,8 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View) { m_SelectedLayer = i; m_SelectedGroup = g; - static int s_LayerPopupID = 0; if(Result == 2) - UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 245, PopupLayer); + UI()->DoPopupMenu(UI()->MouseX(), UI()->MouseY(), 120, 245, this, PopupLayer); } LayerCur += 14.0f; @@ -2693,9 +2688,8 @@ void CEditor::RenderImagesList(CUIRect ToolBox) { m_SelectedImage = i; - static int s_PopupImageID = 0; if(Result == 2) - UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, PopupImage); + UI()->DoPopupMenu(UI()->MouseX(), UI()->MouseY(), 120, 80, this, PopupImage); } ToolBox.HSplitTop(2.0f, 0, &ToolBox); @@ -3086,8 +3080,7 @@ void CEditor::RenderFileDialog() { m_aFileDialogNewFolderName[0] = 0; m_aFileDialogErrString[0] = 0; - static int s_NewFolderPopupID = 0; - UiInvokePopupMenu(&s_NewFolderPopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupNewFolder); + UI()->DoPopupMenu(Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, this, PopupNewFolder); UI()->SetActiveItem(0); } } @@ -3102,8 +3095,7 @@ void CEditor::RenderFileDialog() str_copy(m_Map.m_MapInfoTmp.m_aVersion, m_Map.m_MapInfo.m_aVersion, sizeof(m_Map.m_MapInfoTmp.m_aVersion)); str_copy(m_Map.m_MapInfoTmp.m_aCredits, m_Map.m_MapInfo.m_aCredits, sizeof(m_Map.m_MapInfoTmp.m_aCredits)); str_copy(m_Map.m_MapInfoTmp.m_aLicense, m_Map.m_MapInfo.m_aLicense, sizeof(m_Map.m_MapInfoTmp.m_aLicense)); - static int s_MapInfoPopupID = 0; - UiInvokePopupMenu(&s_MapInfoPopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupMapInfo); + UI()->DoPopupMenu(Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, this, PopupMapInfo); UI()->SetActiveItem(0); } } @@ -3845,14 +3837,13 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) void CEditor::RenderMenubar(CUIRect MenuBar) { - CUIRect ExitButton; - static CUIRect s_File /*, view, help*/; - - MenuBar.VSplitLeft(60.0f, &s_File, &MenuBar); - if(DoButton_Menu(&s_File, "File", 0, &s_File, 0, 0)) - UiInvokePopupMenu(&s_File, 1, s_File.x, s_File.y+s_File.h-1.0f, 120, 150, PopupMenuFile, this); + CUIRect FileButton; + static int s_FileButton; + MenuBar.VSplitLeft(60.0f, &FileButton, &MenuBar); + if(DoButton_Menu(&s_FileButton, "File", 0, &FileButton, 0, 0)) + UI()->DoPopupMenu(FileButton.x, FileButton.y+FileButton.h-1.0f, 120, 150, this, PopupMenuFile, CUIRect::CORNER_R|CUIRect::CORNER_B); - CUIRect Info; + CUIRect Info, ExitButton; MenuBar.VSplitRight(20.f, &MenuBar, &ExitButton); MenuBar.VSplitLeft(40.0f, 0, &MenuBar); MenuBar.VSplitLeft(MenuBar.w*0.75f, &MenuBar, &Info); @@ -4008,14 +3999,12 @@ void CEditor::Render() if(m_PopupEventActivated) { - static int s_PopupID = 0; - UiInvokePopupMenu(&s_PopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupEvent); + UI()->DoPopupMenu(Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, this, PopupEvent); m_PopupEventActivated = false; m_PopupEventWasActivated = true; } - - UiDoPopupMenu(); + UI()->RenderPopupMenus(); if(m_GuiActive) RenderStatusbar(StatusBar); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 5271a7149..d90a86eab 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -781,26 +781,23 @@ public: void RenderGrid(CLayerGroup *pGroup); - void UiInvokePopupMenu(void *pID, int Flags, float X, float Y, float W, float H, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra=0); - void UiDoPopupMenu(); - int UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip); - static int PopupGroup(CEditor *pEditor, CUIRect View); - static int PopupLayer(CEditor *pEditor, CUIRect View); - static int PopupQuad(CEditor *pEditor, CUIRect View); - static int PopupPoint(CEditor *pEditor, CUIRect View); - static int PopupNewFolder(CEditor *pEditor, CUIRect View); - static int PopupMapInfo(CEditor *pEditor, CUIRect View); - static int PopupEvent(CEditor *pEditor, CUIRect View); - static int PopupSelectImage(CEditor *pEditor, CUIRect View); - static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View); - static int PopupImage(CEditor *pEditor, CUIRect View); - static int PopupMenuFile(CEditor *pEditor, CUIRect View); - static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View); - static int PopupSelectDoodadRuleSet(CEditor *pEditor, CUIRect View); - static int PopupDoodadAutoMap(CEditor *pEditor, CUIRect View); - static int PopupColorPicker(CEditor *pEditor, CUIRect View); + static bool PopupGroup(void *pContext, CUIRect View); + static bool PopupLayer(void *pContext, CUIRect View); + static bool PopupQuad(void *pContext, CUIRect View); + static bool PopupPoint(void *pContext, CUIRect View); + static bool PopupNewFolder(void *pContext, CUIRect View); + static bool PopupMapInfo(void *pContext, CUIRect View); + static bool PopupEvent(void *pContext, CUIRect View); + static bool PopupSelectImage(void *pContext, CUIRect View); + static bool PopupSelectGametileOp(void *pContext, CUIRect View); + static bool PopupImage(void *pContext, CUIRect View); + static bool PopupMenuFile(void *pContext, CUIRect View); + static bool PopupSelectConfigAutoMap(void *pContext, CUIRect View); + static bool PopupSelectDoodadRuleSet(void *pContext, CUIRect View); + static bool PopupDoodadAutoMap(void *pContext, CUIRect View); + static bool PopupColorPicker(void *pContext, CUIRect View); static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser); static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser); diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 98934886a..0ddd73e5c 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -13,85 +13,9 @@ #include "editor.h" - -// popup menu handling -static struct -{ - CUIRect m_Rect; - void *m_pId; - int (*m_pfnFunc)(CEditor *pEditor, CUIRect Rect); - int m_IsMenu; - void *m_pExtra; -} s_UiPopups[8]; - -static int g_UiNumPopups = 0; - -void CEditor::UiInvokePopupMenu(void *pID, int Flags, float x, float y, float Width, float Height, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra) -{ - if(x + Width > UI()->Screen()->w) - x -= Width; - if(y + Height > UI()->Screen()->h) - y -= Height; - s_UiPopups[g_UiNumPopups].m_pId = pID; - s_UiPopups[g_UiNumPopups].m_IsMenu = Flags; - s_UiPopups[g_UiNumPopups].m_Rect.x = x; - s_UiPopups[g_UiNumPopups].m_Rect.y = y; - s_UiPopups[g_UiNumPopups].m_Rect.w = Width; - s_UiPopups[g_UiNumPopups].m_Rect.h = Height; - s_UiPopups[g_UiNumPopups].m_pfnFunc = pfnFunc; - s_UiPopups[g_UiNumPopups].m_pExtra = pExtra; - g_UiNumPopups++; -} - -void CEditor::UiDoPopupMenu() -{ - for(int i = 0; i < g_UiNumPopups; i++) - { - bool Inside = UI()->MouseInside(&s_UiPopups[i].m_Rect); - UI()->SetHotItem(&s_UiPopups[i].m_pId); - - if(UI()->CheckActiveItem(&s_UiPopups[i].m_pId)) - { - if(!UI()->MouseButton(0)) - { - if(!Inside) - g_UiNumPopups--; - UI()->SetActiveItem(0); - } - } - else if(UI()->HotItem() == &s_UiPopups[i].m_pId) - { - if(UI()->MouseButton(0)) - UI()->SetActiveItem(&s_UiPopups[i].m_pId); - } - - int Corners = CUIRect::CORNER_ALL; - if(s_UiPopups[i].m_IsMenu) - Corners = CUIRect::CORNER_R|CUIRect::CORNER_B; - - CUIRect r = s_UiPopups[i].m_Rect; - r.Draw(vec4(0.5f,0.5f,0.5f,0.75f), 3.0f, Corners); - r.Margin(1.0f, &r); - r.Draw(vec4(0,0,0,0.75f), 3.0f, Corners); - r.Margin(4.0f, &r); - - if(s_UiPopups[i].m_pfnFunc(this, r)) - { - g_UiNumPopups--; - UI()->SetActiveItem(0); - } - - if(Input()->KeyPress(KEY_ESCAPE)) - { - g_UiNumPopups--; - UI()->SetActiveItem(0); - } - } -} - - -int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) +bool CEditor::PopupGroup(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; // remove group button CUIRect Button; View.HSplitBottom(12.0f, &View, &Button); @@ -104,7 +28,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) { pEditor->m_Map.DeleteGroup(pEditor->m_SelectedGroup); pEditor->m_SelectedGroup = maximum(0, pEditor->m_SelectedGroup-1); - return 1; + return true; } } else @@ -144,7 +68,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) } } - return 1; + return true; } } @@ -159,7 +83,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l); pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1; pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false; - return 1; + return true; } // new tile layer @@ -173,7 +97,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l); pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1; pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false; - return 1; + return true; } // group name @@ -247,11 +171,12 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) else if(Prop == PROP_CLIP_H) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipH = NewVal; } - return 0; + return false; } -int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) +bool CEditor::PopupLayer(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CLayer *pCurrentLayer = pEditor->GetSelectedLayer(0); bool IsGameLayer = pEditor->m_Map.m_pGameLayer == pCurrentLayer; @@ -264,7 +189,7 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) if(!IsGameLayer && pEditor->DoButton_Editor(&s_DeleteButton, "Delete layer", 0, &Button, 0, "Deletes the layer")) { pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->DeleteLayer(pEditor->m_SelectedLayer); - return 1; + return true; } // layer name @@ -333,8 +258,9 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) return pCurrentLayer->RenderProperties(&View); } -int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) +bool CEditor::PopupQuad(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CQuad *pQuad = pEditor->GetSelectedQuad(); CUIRect Button; @@ -351,7 +277,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) pLayer->m_lQuads.remove_index(pEditor->m_SelectedQuad); pEditor->m_SelectedQuad--; } - return 1; + return true; } // aspect ratio button @@ -381,7 +307,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) pQuad->m_aPoints[2].x = Left; pQuad->m_aPoints[2].y = Top+Height; pQuad->m_aPoints[3].x = Right; pQuad->m_aPoints[3].y = Top+Height; pEditor->m_Map.m_Modified = true; - return 1; + return true; } } @@ -397,7 +323,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) pQuad->m_aPoints[k].y = 1000.0f * (int(pQuad->m_aPoints[k].y) / 1000); } pEditor->m_Map.m_Modified = true; - return 1; + return true; } // square button @@ -424,7 +350,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) pQuad->m_aPoints[2].x = Left; pQuad->m_aPoints[2].y = Bottom; pQuad->m_aPoints[3].x = Right; pQuad->m_aPoints[3].y = Bottom; pEditor->m_Map.m_Modified = true; - return 1; + return true; } // center pivot button @@ -449,7 +375,7 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) pQuad->m_aPoints[4].x = Left+int((Right-Left)/2); pQuad->m_aPoints[4].y = Top+int((Bottom-Top)/2); pEditor->m_Map.m_Modified = true; - return 1; + return true; } enum @@ -523,11 +449,12 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) } if(Prop == PROP_COLOR_ENV_OFFSET) pQuad->m_ColorEnvOffset = NewVal; - return 0; + return false; } -int CEditor::PopupPoint(CEditor *pEditor, CUIRect View) +bool CEditor::PopupPoint(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CQuad *pQuad = pEditor->GetSelectedQuad(); enum @@ -615,11 +542,12 @@ int CEditor::PopupPoint(CEditor *pEditor, CUIRect View) pQuad->m_aTexcoords[v].y = f2fx(NewVal/1024.0f); } - return 0; + return false; } -int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) +bool CEditor::PopupNewFolder(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CUIRect Label, ButtonBar; // title @@ -655,7 +583,7 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) if(pEditor->Storage()->CreateFolder(aBuf, IStorage::TYPE_SAVE)) { pEditor->FilelistPopulate(IStorage::TYPE_SAVE); - return 1; + return true; } else str_copy(pEditor->m_aFileDialogErrString, "Unable to create the folder", sizeof(pEditor->m_aFileDialogErrString)); @@ -665,7 +593,7 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label); static int s_AbortButton = 0; if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0)) - return 1; + return true; } else { @@ -681,14 +609,15 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) ButtonBar.VMargin(ButtonBar.w/2.0f-55.0f, &ButtonBar); static int s_CreateButton = 0; if(pEditor->DoButton_Editor(&s_CreateButton, "Ok", 0, &ButtonBar, 0, 0)) - return 1; + return true; } - return 0; + return false; } -int CEditor::PopupMapInfo(CEditor *pEditor, CUIRect View) +bool CEditor::PopupMapInfo(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CUIRect Label, ButtonBar, Button; // title @@ -747,20 +676,21 @@ int CEditor::PopupMapInfo(CEditor *pEditor, CUIRect View) str_copy(pEditor->m_Map.m_MapInfo.m_aVersion, pEditor->m_Map.m_MapInfoTmp.m_aVersion, sizeof(pEditor->m_Map.m_MapInfo.m_aVersion)); str_copy(pEditor->m_Map.m_MapInfo.m_aCredits, pEditor->m_Map.m_MapInfoTmp.m_aCredits, sizeof(pEditor->m_Map.m_MapInfo.m_aCredits)); str_copy(pEditor->m_Map.m_MapInfo.m_aLicense, pEditor->m_Map.m_MapInfoTmp.m_aLicense, sizeof(pEditor->m_Map.m_MapInfo.m_aLicense)); - return 1; + return true; } ButtonBar.VSplitRight(30.0f, &ButtonBar, 0); ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label); static int s_AbortButton = 0; if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0)) - return 1; + return true; - return 0; + return false; } -int CEditor::PopupEvent(CEditor *pEditor, CUIRect View) +bool CEditor::PopupEvent(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CUIRect Label, ButtonBar; // title @@ -815,7 +745,7 @@ int CEditor::PopupEvent(CEditor *pEditor, CUIRect View) else if(pEditor->m_PopupEventType == POPEVENT_SAVE) pEditor->CallbackSaveMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor); pEditor->m_PopupEventWasActivated = false; - return 1; + return true; } ButtonBar.VSplitRight(30.0f, &ButtonBar, 0); ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label); @@ -823,24 +753,27 @@ int CEditor::PopupEvent(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0)) { pEditor->m_PopupEventWasActivated = false; - return 1; + return true; } - return 0; + return false; } static int g_SelectImageSelected = -100; static int g_SelectImageCurrent = -100; -int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View) +bool CEditor::PopupSelectImage(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CUIRect ButtonBar, ImageView; View.VSplitLeft(80.0f, &ButtonBar, &View); View.Margin(10.0f, &ImageView); int ShowImage = g_SelectImageCurrent; + bool ClosePopup = false; + static int s_NoImageButton; for(int i = -1; i < pEditor->m_Map.m_lImages.size(); i++) { CUIRect Button; @@ -850,15 +783,10 @@ int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View) if(pEditor->UI()->MouseInside(&Button)) ShowImage = i; - if(i == -1) - { - if(pEditor->DoButton_MenuItem(&pEditor->m_Map.m_lImages[i], "None", i==g_SelectImageCurrent, &Button)) - g_SelectImageSelected = -1; - } - else + if(pEditor->DoButton_MenuItem(i == -1 ? (void *)&s_NoImageButton : &pEditor->m_Map.m_lImages[i], i == -1 ? "None" : pEditor->m_Map.m_lImages[i]->m_aName, i == g_SelectImageCurrent, &Button)) { - if(pEditor->DoButton_MenuItem(&pEditor->m_Map.m_lImages[i], pEditor->m_Map.m_lImages[i]->m_aName, i==g_SelectImageCurrent, &Button)) - g_SelectImageSelected = i; + g_SelectImageSelected = i; + ClosePopup |= pEditor->Input()->MouseDoubleClick(); } } @@ -881,15 +809,14 @@ int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View) pEditor->Graphics()->WrapNormal(); } - return 0; + return ClosePopup; } 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); + UI()->DoPopupMenu(x, y, 400, 300, this, PopupSelectImage); } int CEditor::PopupSelectImageResult() @@ -904,8 +831,9 @@ int CEditor::PopupSelectImageResult() static int s_GametileOpSelected = -1; -int CEditor::PopupSelectGametileOp(CEditor *pEditor, CUIRect View) +bool CEditor::PopupSelectGametileOp(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; static const char *s_pButtonNames[] = { "Clear", "Collision", "Death", "Unhookable" }; static unsigned s_NumButtons = sizeof(s_pButtonNames) / sizeof(char*); CUIRect Button; @@ -918,14 +846,13 @@ int CEditor::PopupSelectGametileOp(CEditor *pEditor, CUIRect View) s_GametileOpSelected = i; } - return 0; + return false; } void CEditor::PopupSelectGametileOpInvoke(float x, float y) { - static int s_SelectGametileOpPopupId = 0; s_GametileOpSelected = -1; - UiInvokePopupMenu(&s_SelectGametileOpPopupId, 0, x, y, 120.0f, 70.0f, PopupSelectGametileOp); + UI()->DoPopupMenu(x, y, 120.0f, 70.0f, this, PopupSelectGametileOp); } int CEditor::PopupSelectGameTileOpResult() @@ -940,8 +867,9 @@ int CEditor::PopupSelectGameTileOpResult() static bool s_AutoMapProceedOrder = false; -int CEditor::PopupDoodadAutoMap(CEditor *pEditor, CUIRect View) +bool CEditor::PopupDoodadAutoMap(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CLayerTiles *pLayer = static_cast<CLayerTiles*>(pEditor->GetSelectedLayer(0)); IAutoMapper *pMapper = pEditor->m_Map.m_lImages[pLayer->m_Image]->m_pAutoMapper; CUIRect Rect; @@ -951,8 +879,8 @@ int CEditor::PopupDoodadAutoMap(CEditor *pEditor, CUIRect View) // ruleset selection static int s_ChooseDoodadRuleset = 0; if(pEditor->DoButton_Editor(&s_ChooseDoodadRuleset, pMapper->GetRuleSetName(pLayer->m_SelectedRuleSet), 0, &Rect, 0, 0)) - pEditor->UiInvokePopupMenu(&s_ChooseDoodadRuleset, 0, pEditor->UI()->MouseX(), pEditor->UI()->MouseY(), - 120.0f, 12.0f+14.0f*pMapper->RuleSetNum(), PopupSelectDoodadRuleSet); + pEditor->UI()->DoPopupMenu(pEditor->UI()->MouseX(), pEditor->UI()->MouseY(), + 120.0f, 12.0f+14.0f*pMapper->RuleSetNum(), pEditor, PopupSelectDoodadRuleSet); View.HMargin(3.f, &View); View.HSplitTop(15.0f, &Rect, &View); @@ -973,11 +901,12 @@ int CEditor::PopupDoodadAutoMap(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_Editor(&s_ButtonDoodadGenerate, "Generate", 0, &Rect, 0, 0)) s_AutoMapProceedOrder = true; - return 0; + return false; } -int CEditor::PopupSelectDoodadRuleSet(CEditor *pEditor, CUIRect View) +bool CEditor::PopupSelectDoodadRuleSet(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CLayerTiles *pLayer = static_cast<CLayerTiles*>(pEditor->GetSelectedLayer(0)); CUIRect Button; static int s_AutoMapperDoodadButtons[IAutoMapper::MAX_RULES]; @@ -990,15 +919,16 @@ int CEditor::PopupSelectDoodadRuleSet(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_Editor(&s_AutoMapperDoodadButtons[i], pMapper->GetRuleSetName(i), 0, &Button, 0, 0)) { pLayer->m_SelectedRuleSet = i; - return 1; // close the popup + return true; // close the popup } } - return 0; + return false; } -int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View) +bool CEditor::PopupSelectConfigAutoMap(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CLayerTiles *pLayer = static_cast<CLayerTiles*>(pEditor->GetSelectedLayer(0)); CUIRect Button; static int s_AutoMapperConfigButtons[IAutoMapper::MAX_RULES]; @@ -1034,21 +964,20 @@ int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View) pLayer->m_LiveAutoMap = true; } - return 0; + return false; } void CEditor::PopupSelectConfigAutoMapInvoke(float x, float y) { - static int s_AutoMapConfigSelectID = 0; CLayerTiles *pLayer = static_cast<CLayerTiles*>(GetSelectedLayer(0)); if(pLayer && pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size() && m_Map.m_lImages[pLayer->m_Image]->m_pAutoMapper->RuleSetNum()) { if(m_Map.m_lImages[pLayer->m_Image]->m_pAutoMapper->GetType() == IAutoMapper::TYPE_TILESET) - UiInvokePopupMenu(&s_AutoMapConfigSelectID, 0, x, y, 120.0f, 12.0f+14.0f*m_Map.m_lImages[pLayer->m_Image]->m_pAutoMapper->RuleSetNum()+14.0f, PopupSelectConfigAutoMap); + UI()->DoPopupMenu(x, y, 120.0f, 12.0f+14.0f*m_Map.m_lImages[pLayer->m_Image]->m_pAutoMapper->RuleSetNum()+14.0f, this, PopupSelectConfigAutoMap); else if(m_Map.m_lImages[pLayer->m_Image]->m_pAutoMapper->GetType() == IAutoMapper::TYPE_DOODADS) - UiInvokePopupMenu(&s_AutoMapConfigSelectID, 0, x, y, 120.0f, 60.0f, PopupDoodadAutoMap); + UI()->DoPopupMenu(x, y, 120.0f, 60.0f, this, PopupDoodadAutoMap); } } @@ -1064,8 +993,9 @@ bool CEditor::PopupAutoMapProceedOrder() return false; } -int CEditor::PopupColorPicker(CEditor *pEditor, CUIRect View) +bool CEditor::PopupColorPicker(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; CUIRect SVPicker, HuePicker, Palette; View.HSplitBottom(20.0f, &SVPicker, &Palette); SVPicker.VSplitRight(20.0f, &SVPicker, &HuePicker); @@ -1176,7 +1106,7 @@ int CEditor::PopupColorPicker(CEditor *pEditor, CUIRect View) pEditor->m_SelectedPickerColor = hsv; - return 0; + return false; } @@ -1189,8 +1119,9 @@ static void ModifyIndexDeleted(int *pIndex) *pIndex = *pIndex - 1; } -int CEditor::PopupImage(CEditor *pEditor, CUIRect View) +bool CEditor::PopupImage(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; static int s_ReplaceButton = 0; static int s_RemoveButton = 0; @@ -1205,7 +1136,7 @@ int CEditor::PopupImage(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embed", 0, &Slot, 0, "Embeds the image into the map file.")) { pImg->m_External = 0; - return 1; + return true; } } else @@ -1213,7 +1144,7 @@ int CEditor::PopupImage(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file.")) { pImg->m_External = 1; - return 1; + return true; } } @@ -1222,7 +1153,7 @@ int CEditor::PopupImage(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the image with a new one")) { pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Replace Image", "Replace", "mapres", "", ReplaceImage, pEditor); - return 1; + return true; } View.HSplitTop(10.0f, &Slot, &View); @@ -1233,14 +1164,15 @@ int CEditor::PopupImage(CEditor *pEditor, CUIRect View) pEditor->m_Map.m_lImages.remove_index(pEditor->m_SelectedImage); gs_ModifyIndexDeletedIndex = pEditor->m_SelectedImage; pEditor->m_Map.ModifyImageIndex(ModifyIndexDeleted); - return 1; + return true; } - return 0; + return false; } -int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) +bool CEditor::PopupMenuFile(void *pContext, CUIRect View) { + CEditor *pEditor = (CEditor *)pContext; static int s_NewMapButton = 0; static int s_SaveButton = 0; static int s_SaveAsButton = 0; @@ -1264,7 +1196,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) pEditor->Reset(); pEditor->m_aFileName[0] = 0; } - return 1; + return true; } View.HSplitTop(10.0f, &Slot, &View); @@ -1278,7 +1210,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) } else pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", pEditor->CallbackOpenMap, pEditor); - return 1; + return true; } if(pEditor->Client()->State() == IClient::STATE_ONLINE) @@ -1294,7 +1226,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) } else pEditor->LoadCurrentMap(); - return 1; + return true; } } @@ -1303,7 +1235,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_MenuItem(&s_AppendButton, "Append", 0, &Slot, 0, "Opens a map and adds everything from that map to the current one")) { pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Append map", "Append", "maps", "", pEditor->CallbackAppendMap, pEditor); - return 1; + return true; } View.HSplitTop(10.0f, &Slot, &View); @@ -1318,7 +1250,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) } else pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor); - return 1; + return true; } View.HSplitTop(2.0f, &Slot, &View); @@ -1326,7 +1258,7 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name")) { pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor); - return 1; + return true; } View.HSplitTop(10.0f, &Slot, &View); @@ -1340,8 +1272,8 @@ int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) } else pEditor->Config()->m_ClEditor = 0; - return 1; + return true; } - return 0; + return false; } diff --git a/src/test/str.cpp b/src/test/str.cpp index c8adb8471..6d066299d 100644 --- a/src/test/str.cpp +++ b/src/test/str.cpp @@ -129,31 +129,35 @@ TEST(Str, Utf8Stats) { int Size, Count; - str_utf8_stats("abc", 4, &Size, &Count); + str_utf8_stats("abc", 4, 3, &Size, &Count); EXPECT_EQ(Size, 3); EXPECT_EQ(Count, 3); - str_utf8_stats("abc", 2, &Size, &Count); + str_utf8_stats("abc", 2, 3, &Size, &Count); EXPECT_EQ(Size, 1); EXPECT_EQ(Count, 1); - str_utf8_stats("", 1, &Size, &Count); + str_utf8_stats("", 1, 0, &Size, &Count); EXPECT_EQ(Size, 0); EXPECT_EQ(Count, 0); - str_utf8_stats("abcde", 6, &Size, &Count); + str_utf8_stats("abcde", 6, 5, &Size, &Count); EXPECT_EQ(Size, 5); EXPECT_EQ(Count, 5); - str_utf8_stats("любовь", 13, &Size, &Count); + str_utf8_stats("любовь", 13, 6, &Size, &Count); EXPECT_EQ(Size, 12); EXPECT_EQ(Count, 6); - str_utf8_stats("abc愛", 7, &Size, &Count); + str_utf8_stats("abc愛", 7, 4, &Size, &Count); EXPECT_EQ(Size, 6); EXPECT_EQ(Count, 4); - str_utf8_stats("abc愛", 6, &Size, &Count); + str_utf8_stats("abc愛", 6, 4, &Size, &Count); EXPECT_EQ(Size, 3); EXPECT_EQ(Count, 3); + + str_utf8_stats("любовь", 13, 3, &Size, &Count); + EXPECT_EQ(Size, 6); + EXPECT_EQ(Count, 3); } |