From 98322889832a880ce48bbc1c81f6ad6c5c405530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 6 Sep 2024 21:07:56 +0200 Subject: [PATCH 01/11] Adopt changes to `AndroidManifest.xml` from SDL sample project Specify `android:installLocation="auto"` so the app can be installed on and move to the external storage. Specify optional features which the app may use (touchscreen, game controller, external mouse). Specify `android:preferMinimalPostProcessing="true"` so lower latency HDMI mode is enabled when available. Specify `android:hardwareAccelerated="true"` for consistency (it is already the default setting). Specify same `android:configChanges` and `android:alwaysRetainTaskState` values as SDL to avoid potential bugs due to inconsistency with what the `SDLActivity` expects. See https://github.com/libsdl-org/SDL/blob/f5ed158d1f59d0d3bfe65f6c3603c5082c560797/android-project/app/src/main/AndroidManifest.xml --- scripts/android/files/AndroidManifest.xml | 38 +++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/scripts/android/files/AndroidManifest.xml b/scripts/android/files/AndroidManifest.xml index fcf771bec25..3106bd9a394 100644 --- a/scripts/android/files/AndroidManifest.xml +++ b/scripts/android/files/AndroidManifest.xml @@ -1,15 +1,40 @@ - + + + + + + + + + + + + + + + @@ -25,17 +50,24 @@ android:isGame="true" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:hardwareAccelerated="true"> + + + + From c89509bc4b65f810d1d2b89ab2adfed4d611a08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 26 Aug 2024 20:49:40 +0200 Subject: [PATCH 02/11] Add icon button to open console in bottom right of start menu Add a button with the "terminal" icon in the bottom right of the start menu to open the local console to ensure that the local console is usable also when no physical keyboard (with F-keys) is available. --- src/engine/textrender.h | 1 + src/game/client/components/console.h | 3 +-- src/game/client/components/menus_start.cpp | 26 +++++++++++++++++----- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 06f950ded38..3b0ca218ec2 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -93,6 +93,7 @@ MAYBE_UNUSED static const char *FONT_ICON_EARTH_AMERICAS = "\xEF\x95\xBD"; MAYBE_UNUSED static const char *FONT_ICON_NETWORK_WIRED = "\xEF\x9B\xBF"; MAYBE_UNUSED static const char *FONT_ICON_LIST_UL = "\xEF\x83\x8A"; MAYBE_UNUSED static const char *FONT_ICON_INFO = "\xEF\x84\xA9"; +MAYBE_UNUSED static const char *FONT_ICON_TERMINAL = "\xEF\x84\xA0"; MAYBE_UNUSED static const char *FONT_ICON_SLASH = "\xEF\x9C\x95"; MAYBE_UNUSED static const char *FONT_ICON_PLAY = "\xEF\x81\x8B"; diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h index 4dbdc4767ca..e8ab47900f4 100644 --- a/src/game/client/components/console.h +++ b/src/game/client/components/console.h @@ -160,8 +160,6 @@ class CGameConsole : public CComponent static const ColorRGBA ms_SearchHighlightColor; static const ColorRGBA ms_SearchSelectedColor; - void Toggle(int Type); - static void PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser); static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData); static void ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData); @@ -196,6 +194,7 @@ class CGameConsole : public CComponent virtual bool OnInput(const IInput::CEvent &Event) override; void Prompt(char (&aPrompt)[32]); + void Toggle(int Type); bool IsClosed() { return m_ConsoleState == CONSOLE_CLOSED; } }; #endif diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index 31f3439063a..de7520404b9 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -17,6 +17,8 @@ #include "menus.h" +using namespace FontIcons; + void CMenus::RenderStartMenu(CUIRect MainView) { GameClient()->m_MenuBackground.ChangePosition(CMenuBackground::POS_START); @@ -186,13 +188,27 @@ void CMenus::RenderStartMenu(CUIRect MainView) } // render version - CUIRect VersionUpdate, CurVersion; - MainView.HSplitBottom(20.0f, nullptr, &VersionUpdate); - VersionUpdate.VSplitRight(50.0f, &CurVersion, nullptr); - VersionUpdate.VMargin(VMargin, &VersionUpdate); - + CUIRect CurVersion, ConsoleButton; + MainView.HSplitBottom(45.0f, nullptr, &CurVersion); + CurVersion.VSplitRight(40.0f, &CurVersion, nullptr); + CurVersion.HSplitTop(20.0f, &ConsoleButton, &CurVersion); + CurVersion.HSplitTop(5.0f, nullptr, &CurVersion); + ConsoleButton.VSplitRight(40.0f, nullptr, &ConsoleButton); Ui()->DoLabel(&CurVersion, GAME_RELEASE_VERSION, 14.0f, TEXTALIGN_MR); + static CButtonContainer s_ConsoleButton; + TextRender()->SetFontPreset(EFontPreset::ICON_FONT); + TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); + if(DoButton_Menu(&s_ConsoleButton, FONT_ICON_TERMINAL, 0, &ConsoleButton, nullptr, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.1f))) + { + GameClient()->m_GameConsole.Toggle(CGameConsole::CONSOLETYPE_LOCAL); + } + TextRender()->SetRenderFlags(0); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); + + CUIRect VersionUpdate; + MainView.HSplitBottom(20.0f, nullptr, &VersionUpdate); + VersionUpdate.VMargin(VMargin, &VersionUpdate); #if defined(CONF_AUTOUPDATE) CUIRect UpdateButton; VersionUpdate.VSplitRight(100.0f, &VersionUpdate, &UpdateButton); From 9e0ba8a91f0b16d7bd92406c232028dc0f04fd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 7 Sep 2024 16:57:27 +0200 Subject: [PATCH 03/11] Fix heap-use-after-free in `CVideo::Stop` The `delete ms_pCurrentVideo` deletes the current video instance (`this`) so the subsequent write to `m_Stopped` was invalid. Closes #8899. --- src/engine/client/video.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/engine/client/video.cpp b/src/engine/client/video.cpp index 85d7cc9ee7a..e4ac9cdfc64 100644 --- a/src/engine/client/video.cpp +++ b/src/engine/client/video.cpp @@ -283,6 +283,7 @@ void CVideo::Pause(bool Pause) void CVideo::Stop() { dbg_assert(!m_Stopped, "Already stopped"); + m_Stopped = true; m_pGraphics->WaitForIdle(); @@ -341,8 +342,6 @@ void CVideo::Stop() pSound->PauseAudioDevice(); delete ms_pCurrentVideo; pSound->UnpauseAudioDevice(); - - m_Stopped = true; } void CVideo::NextVideoFrameThread() From 3595b70170244b97bf1f719653d35fbba2288e9d Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sat, 7 Sep 2024 18:31:06 +0200 Subject: [PATCH 04/11] Add another row to do the copying for presented images in when pitch is bigger than width --- src/engine/client/backend/vulkan/backend_vulkan.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/client/backend/vulkan/backend_vulkan.cpp b/src/engine/client/backend/vulkan/backend_vulkan.cpp index 8c36efa4ae8..1e59e3fc24e 100644 --- a/src/engine/client/backend/vulkan/backend_vulkan.cpp +++ b/src/engine/client/backend/vulkan/backend_vulkan.cpp @@ -1508,8 +1508,9 @@ class CCommandProcessorFragment_Vulkan : public CCommandProcessorFragment_GLBase vkInvalidateMappedMemoryRanges(m_VKDevice, 1, &MemRange); size_t RealFullImageSize = maximum(ImageTotalSize, (size_t)(Height * m_GetPresentedImgDataHelperMappedLayoutPitch)); - if(vDstData.size() < RealFullImageSize) - vDstData.resize(RealFullImageSize); + size_t ExtraRowSize = Width * 4; + if(vDstData.size() < RealFullImageSize + ExtraRowSize) + vDstData.resize(RealFullImageSize + ExtraRowSize); mem_copy(vDstData.data(), pResImageData, RealFullImageSize); @@ -1520,7 +1521,8 @@ class CCommandProcessorFragment_Vulkan : public CCommandProcessorFragment_GLBase { size_t OffsetImagePacked = (Y * Width * 4); size_t OffsetImageUnpacked = (Y * m_GetPresentedImgDataHelperMappedLayoutPitch); - mem_copy(vDstData.data() + OffsetImagePacked, vDstData.data() + OffsetImageUnpacked, Width * 4); + mem_copy(vDstData.data() + RealFullImageSize, vDstData.data() + OffsetImageUnpacked, Width * 4); + mem_copy(vDstData.data() + OffsetImagePacked, vDstData.data() + RealFullImageSize, Width * 4); } } From 8e45d0a71a568a4df6ac75598d680851ae81517a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 7 Sep 2024 18:28:04 +0200 Subject: [PATCH 05/11] Fix redefinition of `CONF_ARCH_STRING` for ARM architectures The macro `__ARM_ARCH` is defined both for 32-bit and 64-bit ARM so it cannot be used to identify ARM64. Now `__ARM_ARCH_ISA_A64` is used instead, which should only be defined for ARM64. This caused a warning due to the macro `CONF_ARCH_STRING` being redefined when compiling for Android. Furthermore, support for detecting big-endian ARM64 with the `__ARM_BIG_ENDIAN` macro is added. See https://developer.arm.com/documentation/dui0774/g/chr1383660321827 --- src/base/detect.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/base/detect.h b/src/base/detect.h index 40882acb8a3..28b9982cb74 100644 --- a/src/base/detect.h +++ b/src/base/detect.h @@ -163,19 +163,19 @@ #define CONF_ARCH_ARM 1 #define CONF_ARCH_STRING "arm" #define CONF_ARCH_ENDIAN_BIG 1 -#endif - -#if defined(__ARMEL__) +#elif defined(__ARMEL__) #define CONF_ARCH_ARM 1 #define CONF_ARCH_STRING "arm" #define CONF_ARCH_ENDIAN_LITTLE 1 -#endif - -#if defined(__aarch64__) || defined(__arm64__) || defined(__ARM_ARCH) +#elif defined(__aarch64__) || defined(__arm64__) || defined(__ARM_ARCH_ISA_A64) #define CONF_ARCH_ARM64 1 #define CONF_ARCH_STRING "arm64" +#if defined(__ARM_BIG_ENDIAN) +#define CONF_ARCH_ENDIAN_BIG 1 +#else #define CONF_ARCH_ENDIAN_LITTLE 1 #endif +#endif #ifndef CONF_FAMILY_STRING #define CONF_FAMILY_STRING "unknown" From d536bceed6a6f5e30b3a973c793cfe284640bf7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 8 Sep 2024 17:29:32 +0200 Subject: [PATCH 06/11] Fix minimum Android API version and linking errors The minimum supported API version must be specified when building the native libraries, otherwise this may cause linking errors when launching the app. The minimum API level is increased to 24 (Android 7.0, covering 97.2% of usages) because: - Vulkan is only available from API 24+ on ARM64 and x64. - curl only compiles with API 23+. - The NDK version we use supports only API 21+. Ensure that the C++/Linker flags are set when building Android libraries, which was causing errors due to `-fPIC` not being set for all libraries. --- scripts/android/cmake_android.sh | 3 ++- scripts/android/files/build.gradle | 2 +- scripts/compile_libs/cmake_lib_compile.sh | 2 ++ scripts/compile_libs/gen_libs.sh | 11 ++++++----- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/android/cmake_android.sh b/scripts/android/cmake_android.sh index 2ceeecb4702..643d016a579 100755 --- a/scripts/android/cmake_android.sh +++ b/scripts/android/cmake_android.sh @@ -10,7 +10,8 @@ ANDROID_NDK_VERSION="${ANDROID_NDK_VERSION:2}" # ANDROID_NDK_HOME must be exported for cargo-ndk export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION" -ANDROID_API_LEVEL=34 +# ANDROID_API_LEVEL must specify the _minimum_ supported SDK version, otherwise this will cause linking errors at launch +ANDROID_API_LEVEL=24 ANDROID_SUB_BUILD_DIR=build_arch COLOR_RED="\e[1;31m" diff --git a/scripts/android/files/build.gradle b/scripts/android/files/build.gradle index e0090e7d0c4..cc1a5390423 100644 --- a/scripts/android/files/build.gradle +++ b/scripts/android/files/build.gradle @@ -21,7 +21,7 @@ android { defaultConfig { applicationId "org.ddnet.client" namespace("org.ddnet.client") - minSdkVersion 19 + minSdkVersion 24 targetSdkVersion 34 versionCode TW_VERSION_CODE versionName "TW_VERSION_NAME" diff --git a/scripts/compile_libs/cmake_lib_compile.sh b/scripts/compile_libs/cmake_lib_compile.sh index 775584e1136..72c3ee741d8 100755 --- a/scripts/compile_libs/cmake_lib_compile.sh +++ b/scripts/compile_libs/cmake_lib_compile.sh @@ -29,6 +29,8 @@ function compile_source_android() { -DCMAKE_SYSTEM_NAME=Android \ -DCMAKE_SYSTEM_VERSION="$1" \ -DCMAKE_ANDROID_ARCH_ABI="${3}" \ + -DCMAKE_C_FLAGS="$COMPILEFLAGS" -DCMAKE_CXX_FLAGS="$COMPILEFLAGS" -DCMAKE_CXX_FLAGS_RELEASE="$COMPILEFLAGS" -DCMAKE_C_FLAGS_RELEASE="$COMPILEFLAGS" \ + -DCMAKE_SHARED_LINKER_FLAGS="$LINKFLAGS" -DCMAKE_SHARED_LINKER_FLAGS_RELEASE="$LINKFLAGS" \ -B"$2" \ -DBUILD_SHARED_LIBS=OFF \ -DHIDAPI_SKIP_LIBUSB=TRUE \ diff --git a/scripts/compile_libs/gen_libs.sh b/scripts/compile_libs/gen_libs.sh index e9a6436bcc6..9ba0a460baf 100755 --- a/scripts/compile_libs/gen_libs.sh +++ b/scripts/compile_libs/gen_libs.sh @@ -46,7 +46,8 @@ fi mkdir -p "$1" cd "$1" || exit 1 -_ANDROID_ABI_LEVEL=34 +# ANDROID_API_LEVEL must specify the _minimum_ supported SDK version, otherwise this will cause linking errors at launch +ANDROID_API_LEVEL=24 function build_cmake_lib() { if [ ! -d "${1}" ]; then @@ -59,7 +60,7 @@ function build_cmake_lib() { ( cd "${1}" || exit 1 cp "${CURDIR}"/scripts/compile_libs/cmake_lib_compile.sh cmake_lib_compile.sh - ./cmake_lib_compile.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" + ./cmake_lib_compile.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" ) } @@ -74,7 +75,7 @@ cd compile_libs || exit 1 ( cd openssl || exit 1 cp "${CURDIR}"/scripts/compile_libs/make_lib_openssl.sh make_lib_openssl.sh - ./make_lib_openssl.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" + ./make_lib_openssl.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" ) ) @@ -97,7 +98,7 @@ build_cmake_lib opus https://github.com/xiph/opus ./autogen.sh fi cp "${CURDIR}"/scripts/compile_libs/make_lib_opusfile.sh make_lib_opusfile.sh - ./make_lib_opusfile.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" + ./make_lib_opusfile.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" ) # SQLite, just download and built by hand @@ -109,7 +110,7 @@ fi ( cd sqlite3 || exit 1 cp "${CURDIR}"/scripts/compile_libs/make_lib_sqlite3.sh make_lib_sqlite3.sh - ./make_lib_sqlite3.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" + ./make_lib_sqlite3.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS" ) cd .. From 58ce5985d4505f40c1569a5a0bba932b6d8b0d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 9 Sep 2024 17:17:17 +0200 Subject: [PATCH 07/11] Use operator `!=` instead of `mem_comp` for colors --- src/engine/shared/console.cpp | 2 +- src/game/client/ui.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index 3e18b23d691..42bd71d8ae4 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -341,7 +341,7 @@ void CConsole::Print(int Level, const char *pFrom, const char *pStr, ColorRGBA P { LEVEL LogLevel = IConsole::ToLogLevel(Level); // if console colors are not enabled or if the color is pure white, use default terminal color - if(g_Config.m_ConsoleEnableColors && mem_comp(&PrintColor, &gs_ConsoleDefaultColor, sizeof(ColorRGBA)) != 0) + if(g_Config.m_ConsoleEnableColors && PrintColor != gs_ConsoleDefaultColor) { log_log_color(LogLevel, ColorToLogColor(PrintColor), pFrom, "%s", pStr); } diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index d5e5536a3f6..3a0a611cdf4 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -57,7 +57,7 @@ void CUIElement::SUIElementRect::Reset() void CUIElement::SUIElementRect::Draw(const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding) { bool NeedsRecreate = false; - if(m_UIRectQuadContainer == -1 || m_Width != pRect->w || m_Height != pRect->h || mem_comp(&m_QuadColor, &Color, sizeof(Color)) != 0) + if(m_UIRectQuadContainer == -1 || m_Width != pRect->w || m_Height != pRect->h || m_QuadColor != Color) { m_pParent->Ui()->Graphics()->DeleteQuadContainer(m_UIRectQuadContainer); NeedsRecreate = true; From 32e924063440c11ba586d6a39347b5cf8ff39f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 10 Sep 2024 21:11:55 +0200 Subject: [PATCH 08/11] Fix editor crash when saving maps with RGB mapres Convert mapres to RGBA immediately when loading them, so the image data is always in RGBA format internally, instead of only converting when the map is saved (which was erroneously removed in #8670). This means the `cl_editor_dilate` setting will now also be applied to converted RGB images. --- src/engine/client/graphics_threaded.cpp | 2 +- src/engine/graphics.h | 2 + src/game/editor/editor.cpp | 51 ++++++++++++++++++------- src/game/editor/mapitems/map_io.cpp | 9 +++++ 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 63ea4d10403..21e5687febb 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -300,7 +300,7 @@ void CGraphics_Threaded::UnloadTexture(CTextureHandle *pIndex) FreeTextureIndex(pIndex); } -static bool ConvertToRGBA(uint8_t *pDest, const CImageInfo &SrcImage) +bool ConvertToRGBA(uint8_t *pDest, const CImageInfo &SrcImage) { if(SrcImage.m_Format == CImageInfo::FORMAT_RGBA) { diff --git a/src/engine/graphics.h b/src/engine/graphics.h index 184e907e87b..2528c0d792a 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -124,6 +124,8 @@ class CImageInfo } }; +bool ConvertToRGBA(uint8_t *pDest, const CImageInfo &SrcImage); + /* Structure: CVideoMode */ diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 86c525d0a93..78538e45b66 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -4384,7 +4384,7 @@ bool CEditor::ReplaceImage(const char *pFileName, int StorageType, bool CheckDup } } - CEditorImage ImgInfo(this); + CImageInfo ImgInfo; if(!Graphics()->LoadPng(ImgInfo, pFileName, StorageType)) { ShowFileDialogError("Failed to load image from file '%s'.", pFileName); @@ -4394,21 +4394,33 @@ bool CEditor::ReplaceImage(const char *pFileName, int StorageType, bool CheckDup std::shared_ptr pImg = m_Map.m_vpImages[m_SelectedImage]; Graphics()->UnloadTexture(&(pImg->m_Texture)); pImg->Free(); - *pImg = ImgInfo; + pImg->m_Width = ImgInfo.m_Width; + pImg->m_Height = ImgInfo.m_Height; + pImg->m_Format = ImgInfo.m_Format; + pImg->m_pData = ImgInfo.m_pData; str_copy(pImg->m_aName, aBuf); pImg->m_External = IsVanillaImage(pImg->m_aName); - if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA) + if(!pImg->m_External && pImg->m_Format != CImageInfo::FORMAT_RGBA) { - DilateImage(ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height); + uint8_t *pRgbaData = static_cast(malloc((size_t)pImg->m_Width * pImg->m_Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA))); + ConvertToRGBA(pRgbaData, *pImg); + free(pImg->m_pData); + pImg->m_pData = pRgbaData; + pImg->m_Format = CImageInfo::FORMAT_RGBA; + } + + if(!pImg->m_External && g_Config.m_ClEditorDilate == 1) + { + DilateImage(pImg->m_pData, pImg->m_Width, pImg->m_Height); } pImg->m_AutoMapper.Load(pImg->m_aName); int TextureLoadFlag = Graphics()->Uses2DTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; - if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0) + if(pImg->m_Width % 16 != 0 || pImg->m_Height % 16 != 0) TextureLoadFlag = 0; - pImg->m_Texture = Graphics()->LoadTextureRaw(ImgInfo, TextureLoadFlag, pFileName); - ImgInfo.m_pData = nullptr; + pImg->m_Texture = Graphics()->LoadTextureRaw(*pImg, TextureLoadFlag, pFileName); + SortImages(); for(size_t i = 0; i < m_Map.m_vpImages.size(); ++i) { @@ -4447,7 +4459,7 @@ bool CEditor::AddImage(const char *pFileName, int StorageType, void *pUser) return false; } - CEditorImage ImgInfo(pEditor); + CImageInfo ImgInfo; if(!pEditor->Graphics()->LoadPng(ImgInfo, pFileName, StorageType)) { pEditor->ShowFileDialogError("Failed to load image from file '%s'.", pFileName); @@ -4455,19 +4467,30 @@ bool CEditor::AddImage(const char *pFileName, int StorageType, void *pUser) } std::shared_ptr pImg = std::make_shared(pEditor); - *pImg = ImgInfo; + pImg->m_Width = ImgInfo.m_Width; + pImg->m_Height = ImgInfo.m_Height; + pImg->m_Format = ImgInfo.m_Format; + pImg->m_pData = ImgInfo.m_pData; pImg->m_External = IsVanillaImage(aBuf); - if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA) + if(pImg->m_Format != CImageInfo::FORMAT_RGBA) + { + uint8_t *pRgbaData = static_cast(malloc((size_t)pImg->m_Width * pImg->m_Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA))); + ConvertToRGBA(pRgbaData, *pImg); + free(pImg->m_pData); + pImg->m_pData = pRgbaData; + pImg->m_Format = CImageInfo::FORMAT_RGBA; + } + + if(!pImg->m_External && g_Config.m_ClEditorDilate == 1) { - DilateImage(ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height); + DilateImage(pImg->m_pData, pImg->m_Width, pImg->m_Height); } int TextureLoadFlag = pEditor->Graphics()->Uses2DTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; - if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0) + if(pImg->m_Width % 16 != 0 || pImg->m_Height % 16 != 0) TextureLoadFlag = 0; - pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(ImgInfo, TextureLoadFlag, pFileName); - ImgInfo.m_pData = nullptr; + pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(*pImg, TextureLoadFlag, pFileName); str_copy(pImg->m_aName, aBuf); pImg->m_AutoMapper.Load(pImg->m_aName); pEditor->m_Map.m_vpImages.push_back(pImg); diff --git a/src/game/editor/mapitems/map_io.cpp b/src/game/editor/mapitems/map_io.cpp index a3346b1a259..a834a3d990d 100644 --- a/src/game/editor/mapitems/map_io.cpp +++ b/src/game/editor/mapitems/map_io.cpp @@ -509,6 +509,15 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio pImg->m_Height = ImgInfo.m_Height; pImg->m_Format = ImgInfo.m_Format; pImg->m_pData = ImgInfo.m_pData; + if(pImg->m_Format != CImageInfo::FORMAT_RGBA) + { + uint8_t *pRgbaData = static_cast(malloc((size_t)pImg->m_Width * pImg->m_Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA))); + ConvertToRGBA(pRgbaData, *pImg); + free(pImg->m_pData); + pImg->m_pData = pRgbaData; + pImg->m_Format = CImageInfo::FORMAT_RGBA; + } + int TextureLoadFlag = m_pEditor->Graphics()->Uses2DTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE; if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0) TextureLoadFlag = 0; From 0ad1c08c229f86eca807c61653ea0fd74ba9fe8c Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Wed, 11 Sep 2024 17:58:38 +0200 Subject: [PATCH 09/11] Fixes a bug where econ was exposed publicly when ec_bindaddr was set to localhost Also implements the original intention of 85f5e9c that is to disable econ if ec_binaddr is invalid. --- src/engine/shared/econ.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/engine/shared/econ.cpp b/src/engine/shared/econ.cpp index 354e9ededb0..52305395abc 100644 --- a/src/engine/shared/econ.cpp +++ b/src/engine/shared/econ.cpp @@ -70,18 +70,18 @@ void CEcon::Init(CConfig *pConfig, IConsole *pConsole, CNetBan *pNetBan) } NETADDR BindAddr; - if(g_Config.m_EcBindaddr[0] == '\0') + if(g_Config.m_EcBindaddr[0] && net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) == 0) { - mem_zero(&BindAddr, sizeof(BindAddr)); + // got bindaddr + BindAddr.port = g_Config.m_EcPort; } - else if(net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) != 0) + else { char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "The configured bindaddr '%s' cannot be resolved.", g_Config.m_Bindaddr); + str_format(aBuf, sizeof(aBuf), "The configured bindaddr '%s' cannot be resolved.", g_Config.m_EcBindaddr); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "econ", aBuf); + return; } - BindAddr.type = NETTYPE_ALL; - BindAddr.port = g_Config.m_EcPort; if(m_NetConsole.Open(BindAddr, pNetBan)) { From 128ffd2313bbb38401e55c07ad73f08d24c091df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 11 Sep 2024 19:34:04 +0200 Subject: [PATCH 10/11] Initialize client networking before graphics Avoid Vulkan crash if the backend is destroyed immediately after being created. Slightly decreases time of initial black screen before loading menu is rendered. --- src/engine/client/client.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 108bfcaf17e..b814d09594e 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -2929,6 +2929,24 @@ void CClient::Run() g_UuidManager.DebugDump(); } +#ifndef CONF_WEBASM + char aNetworkError[256]; + if(!InitNetworkClient(aNetworkError, sizeof(aNetworkError))) + { + log_error("client", "%s", aNetworkError); + ShowMessageBox("Network Error", aNetworkError); + return; + } +#endif + + if(!m_Http.Init(std::chrono::seconds{1})) + { + const char *pErrorMessage = "Failed to initialize the HTTP client."; + log_error("client", "%s", pErrorMessage); + ShowMessageBox("HTTP Error", pErrorMessage); + return; + } + // init graphics m_pGraphics = CreateEngineGraphicsThreaded(); Kernel()->RegisterInterface(m_pGraphics); // IEngineGraphics @@ -2952,24 +2970,6 @@ void CClient::Run() CVideo::Init(); #endif -#ifndef CONF_WEBASM - char aNetworkError[256]; - if(!InitNetworkClient(aNetworkError, sizeof(aNetworkError))) - { - log_error("client", "%s", aNetworkError); - ShowMessageBox("Network Error", aNetworkError); - return; - } -#endif - - if(!m_Http.Init(std::chrono::seconds{1})) - { - const char *pErrorMessage = "Failed to initialize the HTTP client."; - log_error("client", "%s", pErrorMessage); - ShowMessageBox("HTTP Error", pErrorMessage); - return; - } - // init text render m_pTextRender = Kernel()->RequestInterface(); m_pTextRender->Init(); From d3f0c2a1566afaee16ad870451dca63b9966d2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 9 Sep 2024 22:37:40 +0200 Subject: [PATCH 11/11] Extract `CUi::DoEditBox_Search` function for quick search Reduce duplicate and inconsistent code for rendering quick search for the demo browser, ingame vote list, player flag, skin, skin 0.7 and asset search. The quick search and exclude in the server browser are not refactored, as they have additional labels and different alignment, which would make a general function complicated. --- src/game/client/components/menus_demo.cpp | 16 +----- src/game/client/components/menus_ingame.cpp | 19 ++----- src/game/client/components/menus_settings.cpp | 56 ++++--------------- .../client/components/menus_settings7.cpp | 20 +------ .../components/menus_settings_assets.cpp | 30 +++------- src/game/client/ui.cpp | 19 +++++++ src/game/client/ui.h | 18 ++++++ 7 files changed, 64 insertions(+), 114 deletions(-) diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 2b816a6df46..9da978a1f34 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -1397,22 +1397,10 @@ void CMenus::RenderDemoBrowserButtons(CUIRect ButtonsView, bool WasListboxItemAc // quick search { - SetIconMode(true); - CUIRect DemoSearch, SearchIcon; + CUIRect DemoSearch; ButtonBarTop.VSplitLeft(ButtonBarBottom.h * 21.0f, &DemoSearch, &ButtonBarTop); ButtonBarTop.VSplitLeft(ButtonBarTop.h / 2.0f, nullptr, &ButtonBarTop); - DemoSearch.VSplitLeft(TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS), &SearchIcon, &DemoSearch); - DemoSearch.VSplitLeft(5.0f, nullptr, &DemoSearch); - Ui()->DoLabel(&SearchIcon, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML); - SetIconMode(false); - m_DemoSearchInput.SetEmptyText(Localize("Search")); - - if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) - { - Ui()->SetActiveItem(&m_DemoSearchInput); - m_DemoSearchInput.SelectAll(); - } - if(Ui()->DoClearableEditBox(&m_DemoSearchInput, &DemoSearch, 12.0f)) + if(Ui()->DoEditBox_Search(&m_DemoSearchInput, &DemoSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed())) { RefreshFilteredDemos(); DemolistOnUpdate(false); diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index e1f3e2b53b2..ab511e76889 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -683,26 +683,15 @@ void CMenus::RenderServerControl(CUIRect MainView) // render quick search CUIRect QuickSearch; - Bottom.VSplitLeft(5.0f, 0, &Bottom); + Bottom.VSplitLeft(5.0f, nullptr, &Bottom); Bottom.VSplitLeft(250.0f, &QuickSearch, &Bottom); - TextRender()->SetFontPreset(EFontPreset::ICON_FONT); - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); - - Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML); - float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f); - TextRender()->SetRenderFlags(0); - TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - QuickSearch.VSplitLeft(SearchWidth, 0, &QuickSearch); - QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch); - - if(m_ControlPageOpening || (Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())) + if(m_ControlPageOpening) { - Ui()->SetActiveItem(&m_FilterInput); m_ControlPageOpening = false; + Ui()->SetActiveItem(&m_FilterInput); m_FilterInput.SelectAll(); } - m_FilterInput.SetEmptyText(Localize("Search")); - Ui()->DoClearableEditBox(&m_FilterInput, &QuickSearch, 14.0f); + Ui()->DoEditBox_Search(&m_FilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()); // call vote Bottom.VSplitRight(10.0f, &Bottom, 0); diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 53ab36fed86..d7143ab39a2 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -257,7 +257,7 @@ void CMenus::SetNeedSendInfo() void CMenus::RenderSettingsPlayer(CUIRect MainView) { - CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo, QuickSearch, QuickSearchClearButton; + CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo, QuickSearch; MainView.HSplitTop(20.0f, &TabBar, &MainView); TabBar.VSplitMid(&TabBar, &ChangeInfo, 20.f); TabBar.VSplitMid(&PlayerTab, &DummyTab); @@ -340,7 +340,10 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) } MainView.HSplitTop(10.0f, nullptr, &MainView); - MainView.HSplitBottom(25.0f, &MainView, &QuickSearch); + MainView.HSplitBottom(20.0f, &MainView, &QuickSearch); + MainView.HSplitBottom(5.0f, &MainView, nullptr); + QuickSearch.VSplitLeft(220.0f, &QuickSearch, nullptr); + int OldSelected = -1; static CListBox s_ListBox; s_ListBox.DoStart(48.0f, vpFilteredFlags.size(), 10, 3, OldSelected, &MainView); @@ -378,30 +381,7 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) SetNeedSendInfo(); } - // render quick search - QuickSearch.VSplitLeft(240.0f, &QuickSearch, nullptr); - QuickSearch.HSplitTop(5.0f, nullptr, &QuickSearch); - - TextRender()->SetFontPreset(EFontPreset::ICON_FONT); - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); - Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML); - TextRender()->SetRenderFlags(0); - TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - - float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f); - QuickSearch.VSplitLeft(SearchWidth - 1.5f, nullptr, &QuickSearch); - QuickSearch.VSplitLeft(5.0f, nullptr, &QuickSearch); - QuickSearch.VSplitLeft(QuickSearch.w - 10.0f, &QuickSearch, &QuickSearchClearButton); - - TextRender()->SetRenderFlags(0); - TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) - { - Ui()->SetActiveItem(&s_FlagFilterInput); - s_FlagFilterInput.SelectAll(); - } - s_FlagFilterInput.SetEmptyText(Localize("Search")); - Ui()->DoClearableEditBox(&s_FlagFilterInput, &QuickSearch, 14.0f); + Ui()->DoEditBox_Search(&s_FlagFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()); } struct CUISkin @@ -770,8 +750,8 @@ void CMenus::RenderSettingsTee(CUIRect MainView) CUIRect QuickSearch, DatabaseButton, DirectoryButton, RefreshButton; MainView.HSplitBottom(20.0f, &MainView, &QuickSearch); MainView.HSplitBottom(5.0f, &MainView, nullptr); - QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DatabaseButton); - QuickSearch.VSplitRight(10.0f, &QuickSearch, nullptr); + QuickSearch.VSplitLeft(220.0f, &QuickSearch, &DatabaseButton); + DatabaseButton.VSplitLeft(10.0f, nullptr, &DatabaseButton); DatabaseButton.VSplitLeft(150.0f, &DatabaseButton, &DirectoryButton); DirectoryButton.VSplitRight(175.0f, nullptr, &DirectoryButton); DirectoryButton.VSplitRight(25.0f, &DirectoryButton, &RefreshButton); @@ -904,24 +884,10 @@ void CMenus::RenderSettingsTee(CUIRect MainView) SetNeedSendInfo(); } - // Quick search + static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString)); + if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed())) { - TextRender()->SetFontPreset(EFontPreset::ICON_FONT); - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); - Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML); - float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f); - TextRender()->SetRenderFlags(0); - TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch); - static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString)); - if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) - { - Ui()->SetActiveItem(&s_SkinFilterInput); - s_SkinFilterInput.SelectAll(); - } - s_SkinFilterInput.SetEmptyText(Localize("Search")); - if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f)) - m_SkinListNeedsUpdate = true; + m_SkinListNeedsUpdate = true; } static CButtonContainer s_SkinDatabaseButton; diff --git a/src/game/client/components/menus_settings7.cpp b/src/game/client/components/menus_settings7.cpp index 5d73efdc8db..a560c5be335 100644 --- a/src/game/client/components/menus_settings7.cpp +++ b/src/game/client/components/menus_settings7.cpp @@ -282,24 +282,10 @@ void CMenus::RenderSettingsTee7(CUIRect MainView) } } - // Quick search + static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString)); + if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed())) { - TextRender()->SetFontPreset(EFontPreset::ICON_FONT); - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); - Ui()->DoLabel(&QuickSearch, FontIcons::FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML); - float SearchWidth = TextRender()->TextWidth(14.0f, FontIcons::FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f); - TextRender()->SetRenderFlags(0); - TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch); - static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString)); - if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) - { - Ui()->SetActiveItem(&s_SkinFilterInput); - s_SkinFilterInput.SelectAll(); - } - s_SkinFilterInput.SetEmptyText(Localize("Search")); - if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f)) - m_SkinListNeedsUpdate = true; + m_SkinListNeedsUpdate = true; } static CButtonContainer s_DirectoryButton; diff --git a/src/game/client/components/menus_settings_assets.cpp b/src/game/client/components/menus_settings_assets.cpp index a8a0e807eaa..9d022bb8416 100644 --- a/src/game/client/components/menus_settings_assets.cpp +++ b/src/game/client/components/menus_settings_assets.cpp @@ -352,7 +352,7 @@ int InitSearchList(std::vector &vpSearchList, std::vector void CMenus::RenderSettingsCustom(CUIRect MainView) { - CUIRect TabBar, CustomList, QuickSearch, QuickSearchClearButton, DirectoryButton, ReloadButton; + CUIRect TabBar, CustomList, QuickSearch, DirectoryButton, ReloadButton; MainView.HSplitTop(20.0f, &TabBar, &MainView); const float TabWidth = TabBar.w / NUMBER_OF_ASSETS_TABS; @@ -599,29 +599,13 @@ void CMenus::RenderSettingsCustom(CUIRect MainView) } } - // render quick search + // Quick search + MainView.HSplitBottom(ms_ButtonHeight, &MainView, &QuickSearch); + QuickSearch.VSplitLeft(220.0f, &QuickSearch, &DirectoryButton); + QuickSearch.HSplitTop(5.0f, nullptr, &QuickSearch); + if(Ui()->DoEditBox_Search(&s_aFilterInputs[s_CurCustomTab], &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed())) { - MainView.HSplitBottom(ms_ButtonHeight, &MainView, &QuickSearch); - QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DirectoryButton); - QuickSearch.HSplitTop(5.0f, 0, &QuickSearch); - TextRender()->SetFontPreset(EFontPreset::ICON_FONT); - TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); - - Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML); - float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f); - TextRender()->SetRenderFlags(0); - TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); - QuickSearch.VSplitLeft(SearchWidth, 0, &QuickSearch); - QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch); - QuickSearch.VSplitLeft(QuickSearch.w - 10.0f, &QuickSearch, &QuickSearchClearButton); - if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()) - { - Ui()->SetActiveItem(&s_aFilterInputs[s_CurCustomTab]); - s_aFilterInputs[s_CurCustomTab].SelectAll(); - } - s_aFilterInputs[s_CurCustomTab].SetEmptyText(Localize("Search")); - if(Ui()->DoClearableEditBox(&s_aFilterInputs[s_CurCustomTab], &QuickSearch, 14.0f)) - gs_aInitCustomList[s_CurCustomTab] = true; + gs_aInitCustomList[s_CurCustomTab] = true; } DirectoryButton.HSplitTop(5.0f, 0, &DirectoryButton); diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 3a0a611cdf4..e0c859ac04b 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -1004,6 +1004,25 @@ bool CUi::DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float return ReturnValue; } +bool CUi::DoEditBox_Search(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, bool HotkeyEnabled) +{ + CUIRect QuickSearch = *pRect; + TextRender()->SetFontPreset(EFontPreset::ICON_FONT); + TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); + DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, FontSize, TEXTALIGN_ML); + const float SearchWidth = TextRender()->TextWidth(FontSize, FONT_ICON_MAGNIFYING_GLASS); + TextRender()->SetRenderFlags(0); + TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); + QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch); + if(HotkeyEnabled && Input()->ModifierIsPressed() && Input()->KeyPress(KEY_F)) + { + SetActiveItem(pLineInput); + pLineInput->SelectAll(); + } + pLineInput->SetEmptyText(Localize("Search")); + return DoClearableEditBox(pLineInput, &QuickSearch, FontSize); +} + int CUi::DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pId, const std::function &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props) { CUIRect Text = *pRect, DropDownIcon; diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 6cc7129f211..8cf21bd308a 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -603,6 +603,24 @@ class CUi */ bool DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const std::vector &vColorSplits = {}); + /** + * Creates an input field with a search icon and a clear [x] button attached to it. + * The input will have default text "Search" and the hotkey Ctrl+F can be used to activate the input. + * + * @see DoEditBox + * + * @param pLineInput This pointer will be stored and written to on next user input. + * So you can not pass in a pointer that goes out of scope such as a local variable. + * Pass in either a member variable of the current class or a static variable. + * For example ```static CLineInputBuffered s_MyInput;``` + * @param pRect the UI rect it will attach to + * @param FontSize Size of the font (`10.0f`, `12.0f` and `14.0f` are commonly used here) + * @param HotkeyEnabled Whether the hotkey to enable this editbox is currently enabled. + * + * @return true if the value of the input field changed since the last call. + */ + bool DoEditBox_Search(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, bool HotkeyEnabled); + int DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pId, const std::function &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props = {}); // only used for popup menus int DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, float Size, int Align, float Padding = 0.0f, bool TransparentInactive = false, bool Enabled = true);