diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 442a6d4a67..da68579839 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -77,8 +77,26 @@ jobs: fetch-depth: 0 submodules: recursive - - name: Compile a universal OpenMP (macOS) - run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb + - name: Compile universal dependencies (macOS) + run: | + for formula in ./shell/apple/libomp.rb ./shell/apple/libpng-flycast.rb; do + libname=$(basename "$formula" .rb) + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew install --build-from-source --formula "$formula" || \ + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew reinstall --build-from-source --formula "$formula" + + prefix=$(brew --prefix $libname) + # Find and verify the static library + libfile=$(find "$prefix/lib" -name "*.a" | head -n1) + lipo -info "$libfile" + + # Export prefix for subsequent CMake steps + if [ -z "$CMAKE_PREFIX_PATH" ]; then + CMAKE_PREFIX_PATH="$prefix" + else + CMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH:$prefix" + fi + done + echo "CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH" >> $GITHUB_ENV if: matrix.config.name == 'apple-darwin' - uses: actions/cache@v5 diff --git a/.gitmodules b/.gitmodules index 5ea2f8046d..ef4ae2ea2b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -59,3 +59,6 @@ [submodule "core/deps/tinygettext"] path = core/deps/tinygettext url = https://github.com/flyinghead/tinygettext +[submodule "core/deps/freetype"] + path = core/deps/freetype + url = https://gitlab.freedesktop.org/freetype/freetype.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 878b5d5a56..4dc9d3521f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -888,7 +888,17 @@ if(NOT LIBRETRO) core/deps/imgui/imgui_cjk.cpp core/deps/imgui/imgui_stdlib.cpp core/deps/imgui/imgui_tables.cpp - core/deps/imgui/imgui_widgets.cpp) + core/deps/imgui/imgui_widgets.cpp + core/deps/imgui/misc/freetype/imgui_freetype.cpp) + + set(FT_DISABLE_BZIP2 ON CACHE BOOL "" FORCE) + set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "" FORCE) + set(FT_DISABLE_BROTLI ON CACHE BOOL "" FORCE) + add_subdirectory(core/deps/freetype EXCLUDE_FROM_ALL) + if(WINDOWS_STORE) + target_compile_definitions(freetype PRIVATE _WINRT_DLL) + endif() + target_link_libraries(${PROJECT_NAME} PRIVATE freetype) if(ENABLE_FC_PROFILER) target_include_directories(${PROJECT_NAME} PRIVATE core/deps/implot) diff --git a/core/deps/freetype b/core/deps/freetype new file mode 160000 index 0000000000..0a0221a134 --- /dev/null +++ b/core/deps/freetype @@ -0,0 +1 @@ +Subproject commit 0a0221a1347e2f1e07c395263540026e9a0aa7c7 diff --git a/core/deps/imgui/backends/imgui_impl_dx11.cpp b/core/deps/imgui/backends/imgui_impl_dx11.cpp index c36e1579d1..2e2a1ebb3a 100644 --- a/core/deps/imgui/backends/imgui_impl_dx11.cpp +++ b/core/deps/imgui/backends/imgui_impl_dx11.cpp @@ -2,8 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -15,12 +17,19 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-01-19: DirectX11: Added 'SamplerNearest' in ImGui_ImplDX11_RenderState. Renamed 'SamplerDefault' to 'SamplerLinear'. +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: DirectX11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. +// 2025-05-07: DirectX11: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows). +// 2025-01-06: DirectX11: Expose VertexConstantBuffer in ImGui_ImplDX11_RenderState. Reset projection matrix in ImDrawCallback_ResetRenderState handler. +// 2024-10-07: DirectX11: Changed default texture sampler to Clamp instead of Repeat/Wrap. +// 2024-10-07: DirectX11: Expose selected render state in ImGui_ImplDX11_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer. // 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled). -// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore. +// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX11_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore. // 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile(). @@ -45,7 +54,20 @@ #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +/* Flycast: Use ImTextureDX11 instead // DirectX11 data +struct ImGui_ImplDX11_Texture +{ + ID3D11Texture2D* pTexture; + ID3D11ShaderResourceView* pTextureView; +}; +*/ struct ImGui_ImplDX11_Data { ID3D11Device* pd3dDevice; @@ -57,10 +79,9 @@ struct ImGui_ImplDX11_Data ID3D11InputLayout* pInputLayout; ID3D11Buffer* pVertexConstantBuffer; ID3D11PixelShader* pPixelShader; - ID3D11SamplerState* pFontSampler; - ID3D11SamplerState* pTextureSampler; - ID3D11SamplerState* pPointSampler; - ImTextureDX11 FontTexture; + ID3D11SamplerState* pTexSamplerLinear; + ID3D11SamplerState* pTexSamplerLinearBorder; + ID3D11SamplerState* pTexSamplerNearest; ID3D11RasterizerState* pRasterizerState; ID3D11BlendState* pBlendState; ID3D11DepthStencilState* pDepthStencilState; @@ -83,40 +104,60 @@ static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData() } // Functions -static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx) +static void ImGui_ImplDX11_SetupRenderState(const ImDrawData* draw_data, ID3D11DeviceContext* device_ctx) { ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); // Setup viewport - D3D11_VIEWPORT vp; - memset(&vp, 0, sizeof(D3D11_VIEWPORT)); - vp.Width = draw_data->DisplaySize.x; - vp.Height = draw_data->DisplaySize.y; + D3D11_VIEWPORT vp = {}; + vp.Width = draw_data->DisplaySize.x * draw_data->FramebufferScale.x; + vp.Height = draw_data->DisplaySize.y * draw_data->FramebufferScale.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; - ctx->RSSetViewports(1, &vp); + device_ctx->RSSetViewports(1, &vp); + + // Setup orthographic projection matrix into our constant buffer + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + D3D11_MAPPED_SUBRESOURCE mapped_resource; + if (device_ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) == S_OK) + { + VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData; + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + float mvp[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.5f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + }; + memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); + device_ctx->Unmap(bd->pVertexConstantBuffer, 0); + } // Setup shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; - ctx->IASetInputLayout(bd->pInputLayout); - ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); - ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); - ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->VSSetShader(bd->pVertexShader, nullptr, 0); - ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); - ctx->PSSetShader(bd->pPixelShader, nullptr, 0); - ctx->GSSetShader(nullptr, nullptr, 0); - ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. - ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. - ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. - - // Setup blend state + device_ctx->IASetInputLayout(bd->pInputLayout); + device_ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); + device_ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); + device_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + device_ctx->VSSetShader(bd->pVertexShader, nullptr, 0); + device_ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); + device_ctx->PSSetShader(bd->pPixelShader, nullptr, 0); + device_ctx->GSSetShader(nullptr, nullptr, 0); + device_ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. + device_ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. + device_ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. + + // Setup render state const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; - ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); - ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); - ctx->RSSetState(bd->pRasterizerState); + device_ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); + device_ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); + device_ctx->RSSetState(bd->pRasterizerState); } // Render function @@ -127,15 +168,21 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) return; ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); - ID3D11DeviceContext* ctx = bd->pd3dDeviceContext; + ID3D11DeviceContext* device = bd->pd3dDeviceContext; + + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplDX11_UpdateTexture(tex); // Create and grow vertex/index buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; - D3D11_BUFFER_DESC desc; - memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); + D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DYNAMIC; desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; @@ -148,8 +195,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) { if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; - D3D11_BUFFER_DESC desc; - memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); + D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DYNAMIC; desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D11_BIND_INDEX_BUFFER; @@ -160,44 +206,21 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Upload vertex/index data into a single contiguous GPU buffer D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; - if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) + if (device->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) return; - if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) + if (device->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) return; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); - vtx_dst += cmd_list->VtxBuffer.Size; - idx_dst += cmd_list->IdxBuffer.Size; - } - ctx->Unmap(bd->pVB, 0); - ctx->Unmap(bd->pIB, 0); - - // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - { - D3D11_MAPPED_SUBRESOURCE mapped_resource; - if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) - return; - VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData; - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - float mvp[4][4] = - { - { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, - }; - memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); - ctx->Unmap(bd->pVertexConstantBuffer, 0); + memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtx_dst += draw_list->VtxBuffer.Size; + idx_dst += draw_list->IdxBuffer.Size; } + device->Unmap(bd->pVB, 0); + device->Unmap(bd->pIB, 0); // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) struct BACKUP_DX11_STATE @@ -226,114 +249,143 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) }; BACKUP_DX11_STATE old = {}; old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; - ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects); - ctx->RSGetViewports(&old.ViewportsCount, old.Viewports); - ctx->RSGetState(&old.RS); - ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask); - ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef); - ctx->PSGetShaderResources(0, 1, &old.PSShaderResource); - ctx->PSGetSamplers(0, 1, &old.PSSampler); + device->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects); + device->RSGetViewports(&old.ViewportsCount, old.Viewports); + device->RSGetState(&old.RS); + device->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask); + device->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef); + device->PSGetShaderResources(0, 1, &old.PSShaderResource); + device->PSGetSamplers(0, 1, &old.PSSampler); old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256; - ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount); - ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount); - ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer); - ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount); + device->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount); + device->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount); + device->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer); + device->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount); - ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology); - ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset); - ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); - ctx->IAGetInputLayout(&old.InputLayout); + device->IAGetPrimitiveTopology(&old.PrimitiveTopology); + device->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset); + device->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); + device->IAGetInputLayout(&old.InputLayout); // Setup desired DX state - ImGui_ImplDX11_SetupRenderState(draw_data, ctx); + ImGui_ImplDX11_SetupRenderState(draw_data, device); + + // Setup render state structure (for callbacks and custom texture bindings) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplDX11_RenderState render_state; + render_state.Device = bd->pd3dDevice; + render_state.DeviceContext = bd->pd3dDeviceContext; + render_state.SamplerLinear = bd->pTexSamplerLinear; + render_state.SamplerNearest = bd->pTexSamplerNearest; + render_state.VertexConstantBuffer = bd->pVertexConstantBuffer; + platform_io.Renderer_RenderState = &render_state; // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) int global_idx_offset = 0; int global_vtx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + ImVec2 clip_scale = draw_data->FramebufferScale; + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplDX11_SetupRenderState(draw_data, ctx); + ImGui_ImplDX11_SetupRenderState(draw_data, device); else - pcmd->UserCallback(cmd_list, pcmd); + pcmd->UserCallback(draw_list, pcmd); } else { // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; - ctx->RSSetScissorRects(1, &r); + device->RSSetScissorRects(1, &r); // Bind texture, Draw ImTextureDX11 *tex = (ImTextureDX11 *)pcmd->GetTexID(); - if (tex != &bd->FontTexture) - { - if (tex->pointSampling) - ctx->PSSetSamplers(0, 1, &bd->pPointSampler); - else - ctx->PSSetSamplers(0, 1, &bd->pTextureSampler); - } - else - ctx->PSSetSamplers(0, 1, &bd->pFontSampler); + if((ImTextureID)tex != ImGui::GetIO().Fonts->TexID.GetTexID()) + { + if (tex->pointSampling) + device->PSSetSamplers(0, 1, &bd->pTexSamplerNearest); + else + device->PSSetSamplers(0, 1, &bd->pTexSamplerLinearBorder); + } + else + device->PSSetSamplers(0, 1, &bd->pTexSamplerLinear); ID3D11ShaderResourceView* texture_srv = tex->shaderResourceView; - ctx->PSSetShaderResources(0, 1, &texture_srv); - ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset); + device->PSSetShaderResources(0, 1, &texture_srv); + device->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset); } } - global_idx_offset += cmd_list->IdxBuffer.Size; - global_vtx_offset += cmd_list->VtxBuffer.Size; + global_idx_offset += draw_list->IdxBuffer.Size; + global_vtx_offset += draw_list->VtxBuffer.Size; } + platform_io.Renderer_RenderState = nullptr; // Restore modified DX state - ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); - ctx->RSSetViewports(old.ViewportsCount, old.Viewports); - ctx->RSSetState(old.RS); if (old.RS) old.RS->Release(); - ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); - ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); - ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); - ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); - ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release(); + device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); + device->RSSetViewports(old.ViewportsCount, old.Viewports); + device->RSSetState(old.RS); if (old.RS) old.RS->Release(); + device->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); + device->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); + device->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); + device->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); + device->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release(); for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release(); - ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release(); - ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); - ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release(); + device->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release(); + device->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); + device->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release(); for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release(); - ctx->IASetPrimitiveTopology(old.PrimitiveTopology); - ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); - ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); - ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); + device->IASetPrimitiveTopology(old.PrimitiveTopology); + device->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); + device->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); + device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -static void ImGui_ImplDX11_CreateFontsTexture() +static void ImGui_ImplDX11_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + if (ImTextureDX11* backend_tex = (ImTextureDX11*)tex->BackendUserData) + { + IM_ASSERT(backend_tex == (ImTextureDX11*)tex->TexID); + backend_tex->shaderResourceView->Release(); + backend_tex->pTexture->Release(); + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; + } + tex->SetStatus(ImTextureStatus_Destroyed); +} - // Upload texture to graphics system +void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (tex->Status == ImTextureStatus_WantCreate) { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + unsigned int* pixels = (unsigned int*)tex->GetPixels(); + ImTextureDX11* backend_tex = IM_NEW(ImTextureDX11)(); + + // Create texture D3D11_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; + desc.Width = (UINT)tex->Width; + desc.Height = (UINT)tex->Height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -341,14 +393,12 @@ static void ImGui_ImplDX11_CreateFontsTexture() desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; - - ID3D11Texture2D* pTexture = nullptr; D3D11_SUBRESOURCE_DATA subResource; subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); - IM_ASSERT(pTexture != nullptr); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &backend_tex->pTexture); + IM_ASSERT(backend_tex->pTexture != nullptr && "Backend failed to create texture!"); // Create texture view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; @@ -357,35 +407,29 @@ static void ImGui_ImplDX11_CreateFontsTexture() srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->FontTexture.shaderResourceView); - pTexture->Release(); - } + bd->pd3dDevice->CreateShaderResourceView(backend_tex->pTexture, &srvDesc, &backend_tex->shaderResourceView); + IM_ASSERT(backend_tex->shaderResourceView != nullptr && "Backend failed to create texture!"); - // Store our identifier - io.Fonts->SetTexID((ImTextureID)&bd->FontTexture); - - // Create texture samplers - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Store identifiers + tex->SetTexID((ImTextureID)backend_tex); + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = backend_tex; + } + else if (tex->Status == ImTextureStatus_WantUpdates) { - D3D11_SAMPLER_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; - desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; - desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; - desc.MipLODBias = 0.f; - desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; - bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); - - desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; - desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; - bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTextureSampler); - - desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; - bd->pd3dDevice->CreateSamplerState(&desc, &bd->pPointSampler); + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + ImTextureDX11* backend_tex = (ImTextureDX11*)tex->BackendUserData; + IM_ASSERT(backend_tex == (ImTextureDX11*)tex->TexID); + for (ImTextureRect& r : tex->Updates) + { + D3D11_BOX box = { (UINT)r.x, (UINT)r.y, (UINT)0, (UINT)(r.x + r.w), (UINT)(r.y + r .h), (UINT)1 }; + bd->pd3dDeviceContext->UpdateSubresource(backend_tex->pTexture, 0, &box, tex->GetPixelsAt(r.x, r.y), (UINT)tex->GetPitch(), 0); + } + tex->SetStatus(ImTextureStatus_OK); } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplDX11_DestroyTexture(tex); } bool ImGui_ImplDX11_CreateDeviceObjects() @@ -393,8 +437,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); if (!bd->pd3dDevice) return false; - if (bd->pFontSampler) - ImGui_ImplDX11_InvalidateDeviceObjects(); + ImGui_ImplDX11_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) // If you would like to use this DX11 sample code but remove this dependency you can: @@ -457,7 +500,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() // Create the constant buffer { - D3D11_BUFFER_DESC desc; + D3D11_BUFFER_DESC desc = {}; desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX11); desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; @@ -537,7 +580,28 @@ bool ImGui_ImplDX11_CreateDeviceObjects() bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } - ImGui_ImplDX11_CreateFontsTexture(); + // Create texture samplers + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + { + D3D11_SAMPLER_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.MipLODBias = 0.f; + desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + desc.MinLOD = 0.f; + desc.MaxLOD = 0.f; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinear); + + desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; + desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinearBorder); + + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerNearest); + } return true; } @@ -548,15 +612,14 @@ void ImGui_ImplDX11_InvalidateDeviceObjects() if (!bd->pd3dDevice) return; - if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; } - if (bd->pTextureSampler) { bd->pTextureSampler->Release(); bd->pTextureSampler = nullptr; } - if (bd->pPointSampler) { bd->pPointSampler->Release(); bd->pPointSampler = nullptr; } - if (bd->FontTexture.shaderResourceView) { - bd->FontTexture.shaderResourceView->Release(); - bd->FontTexture.shaderResourceView = nullptr; - // We copied data->FontTexture.shaderResourceView to io.Fonts->TexID so let's clear that as well. - ImGui::GetIO().Fonts->SetTexID(0); - } + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplDX11_DestroyTexture(tex); + + if (bd->pTexSamplerLinear) { bd->pTexSamplerLinear->Release(); bd->pTexSamplerLinear = nullptr; } + if (bd->pTexSamplerLinearBorder){ bd->pTexSamplerLinearBorder->Release(); bd->pTexSamplerLinearBorder = nullptr; } + if (bd->pTexSamplerNearest) { bd->pTexSamplerNearest->Release(); bd->pTexSamplerNearest = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; } @@ -579,6 +642,10 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -606,14 +673,17 @@ void ImGui_ImplDX11_Shutdown() ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplDX11_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); } + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -622,8 +692,9 @@ void ImGui_ImplDX11_NewFrame() ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX11_Init()?"); - if (!bd->pFontSampler) - ImGui_ImplDX11_CreateDeviceObjects(); + if (!bd->pVertexShader) + if (!ImGui_ImplDX11_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplDX11_CreateDeviceObjects() failed!"); } //----------------------------------------------------------------------------- diff --git a/core/deps/imgui/backends/imgui_impl_dx11.h b/core/deps/imgui/backends/imgui_impl_dx11.h old mode 100644 new mode 100755 index be5657bd62..eca87284e1 --- a/core/deps/imgui/backends/imgui_impl_dx11.h +++ b/core/deps/imgui/backends/imgui_impl_dx11.h @@ -2,8 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -19,20 +21,38 @@ struct ID3D11Device; struct ID3D11DeviceContext; +struct ID3D11Texture2D; struct ID3D11ShaderResourceView; +struct ID3D11SamplerState; +struct ID3D11Buffer; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); -// Use if you want to reset your rendering device without losing Dear ImGui state. -IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); +// Use if you want to reset your rendering state without using ImGui_ImplDX11_RenderDrawData. IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); + +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. +IMGUI_IMPL_API void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex); + +// Selected render state exposed to applications and callbacks. +struct ImGui_ImplDX11_RenderState +{ + ID3D11Device* Device; + ID3D11DeviceContext* DeviceContext; + ID3D11SamplerState* SamplerLinear; + ID3D11SamplerState* SamplerNearest; + ID3D11Buffer* VertexConstantBuffer; +}; // ImTextureID should be a pointer to this struct struct ImTextureDX11 { + ID3D11Texture2D *pTexture; ID3D11ShaderResourceView *shaderResourceView; bool pointSampling; }; diff --git a/core/deps/imgui/backends/imgui_impl_dx9.cpp b/core/deps/imgui/backends/imgui_impl_dx9.cpp old mode 100644 new mode 100755 index 5d5edbc867..4ec639bf13 --- a/core/deps/imgui/backends/imgui_impl_dx9.cpp +++ b/core/deps/imgui/backends/imgui_impl_dx9.cpp @@ -2,8 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -15,6 +17,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-06-11: DirectX9: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. +// 2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -42,15 +47,21 @@ // DirectX #include +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + // DirectX data struct ImGui_ImplDX9_Data { LPDIRECT3DDEVICE9 pd3dDevice; LPDIRECT3DVERTEXBUFFER9 pVB; LPDIRECT3DINDEXBUFFER9 pIB; - ImTextureDX9 FontTexture; int VertexBufferSize; int IndexBufferSize; + bool HasRgbaSupport; ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } }; @@ -88,41 +99,41 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) vp.Height = (DWORD)draw_data->DisplaySize.y; vp.MinZ = 0.0f; vp.MaxZ = 1.0f; - bd->pd3dDevice->SetViewport(&vp); + + LPDIRECT3DDEVICE9 device = bd->pd3dDevice; + device->SetViewport(&vp); // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling. - bd->pd3dDevice->SetPixelShader(nullptr); - bd->pd3dDevice->SetVertexShader(nullptr); - bd->pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); - bd->pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); - bd->pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); - bd->pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); - bd->pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - bd->pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); - bd->pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - bd->pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); - bd->pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - bd->pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - bd->pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); - bd->pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); - bd->pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); - bd->pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - bd->pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); - bd->pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE); - bd->pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); - bd->pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); - bd->pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE); - bd->pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); - bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); - bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); - bd->pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - bd->pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + device->SetPixelShader(nullptr); + device->SetVertexShader(nullptr); + device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + device->SetRenderState(D3DRS_ZENABLE, FALSE); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); + device->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); + device->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); + device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + device->SetRenderState(D3DRS_FOGENABLE, FALSE); + device->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE); + device->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + device->SetRenderState(D3DRS_STENCILENABLE, FALSE); + device->SetRenderState(D3DRS_CLIPPING, TRUE); + device->SetRenderState(D3DRS_LIGHTING, FALSE); + device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); // Setup orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. @@ -140,9 +151,9 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) 0.0f, 0.0f, 0.5f, 0.0f, (L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f } } }; - bd->pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity); - bd->pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity); - bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection); + device->SetTransform(D3DTS_WORLD, &mat_identity); + device->SetTransform(D3DTS_VIEW, &mat_identity); + device->SetTransform(D3DTS_PROJECTION, &mat_projection); } } @@ -153,33 +164,65 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - // Create and grow buffers if needed ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + LPDIRECT3DDEVICE9 device = bd->pd3dDevice; + + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplDX9_UpdateTexture(tex); + + // Create and grow buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; - if (bd->pd3dDevice->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0) + if (device->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0) return; } if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) { if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; - if (bd->pd3dDevice->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0) + if (device->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0) return; } +/* Removed in Flycast + // Backup the DX9 state + IDirect3DStateBlock9* state_block = nullptr; + if (device->CreateStateBlock(D3DSBT_ALL, &state_block) < 0) + return; + if (state_block->Capture() < 0) + { + state_block->Release(); + return; + } + + // Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to) + D3DMATRIX last_world, last_view, last_projection; + device->GetTransform(D3DTS_WORLD, &last_world); + device->GetTransform(D3DTS_VIEW, &last_view); + device->GetTransform(D3DTS_PROJECTION, &last_projection); +*/ // Allocate buffers CUSTOMVERTEX* vtx_dst; ImDrawIdx* idx_dst; if (bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0) { +/* Removed in Flycast + state_block->Release(); +*/ return; } if (bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0) { bd->pVB->Unlock(); +/* Removed in Flycast + state_block->Release(); +*/ return; } @@ -187,11 +230,10 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) // FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and // 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR // 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; } - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data; - for (int i = 0; i < cmd_list->VtxBuffer.Size; i++) + const ImDrawVert* vtx_src = draw_list->VtxBuffer.Data; + for (int i = 0; i < draw_list->VtxBuffer.Size; i++) { vtx_dst->pos[0] = vtx_src->pos.x; vtx_dst->pos[1] = vtx_src->pos.y; @@ -202,14 +244,14 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) vtx_dst++; vtx_src++; } - memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); - idx_dst += cmd_list->IdxBuffer.Size; + memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + idx_dst += draw_list->IdxBuffer.Size; } bd->pVB->Unlock(); bd->pIB->Unlock(); - bd->pd3dDevice->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX)); - bd->pd3dDevice->SetIndices(bd->pIB); - bd->pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); + device->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX)); + device->SetIndices(bd->pIB); + device->SetFVF(D3DFVF_CUSTOMVERTEX); // Setup desired DX state ImGui_ImplDX9_SetupRenderState(draw_data); @@ -219,12 +261,11 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) int global_vtx_offset = 0; int global_idx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() @@ -232,7 +273,7 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) ImGui_ImplDX9_SetupRenderState(draw_data); else - pcmd->UserCallback(cmd_list, pcmd); + pcmd->UserCallback(draw_list, pcmd); } else { @@ -246,36 +287,64 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) const RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; const ImTextureDX9 *tex = (const ImTextureDX9 *)pcmd->GetTexID(); const LPDIRECT3DTEXTURE9 texture = tex->d3dTexture; - bd->pd3dDevice->SetTexture(0, texture); - if (tex != &bd->FontTexture) - { - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); - if (tex->pointSampling) - { - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); - } - else - { - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - } - } - else - { - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - } - bd->pd3dDevice->SetScissorRect(&r); - bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3); + device->SetTexture(0, texture); + if((ImTextureID)tex != ImGui::GetIO().Fonts->TexID.GetTexID()) + { + device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); + device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); + if (tex->pointSampling) + { + device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + } + else + { + device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + } + } + else + { + device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + } + device->SetScissorRect(&r); + device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)draw_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3); } } - global_idx_offset += cmd_list->IdxBuffer.Size; - global_vtx_offset += cmd_list->VtxBuffer.Size; + global_idx_offset += draw_list->IdxBuffer.Size; + global_vtx_offset += draw_list->VtxBuffer.Size; + } +/* Removed in Flycast + // Restore the DX9 transform + device->SetTransform(D3DTS_WORLD, &last_world); + device->SetTransform(D3DTS_VIEW, &last_view); + device->SetTransform(D3DTS_PROJECTION, &last_projection); + + // Restore the DX9 state + state_block->Apply(); + state_block->Release(); +*/ +} + +static bool ImGui_ImplDX9_CheckFormatSupport(LPDIRECT3DDEVICE9 pDevice, D3DFORMAT format) +{ + LPDIRECT3D9 pd3d = nullptr; + if (pDevice->GetDirect3D(&pd3d) != D3D_OK) + return false; + D3DDEVICE_CREATION_PARAMETERS param = {}; + D3DDISPLAYMODE mode = {}; + if (pDevice->GetCreationParameters(¶m) != D3D_OK || pDevice->GetDisplayMode(0, &mode) != D3D_OK) + { + pd3d->Release(); + return false; } + // Font texture should support linear filter, color blend and write to render-target + bool support = (pd3d->CheckDeviceFormat(param.AdapterOrdinal, param.DeviceType, mode.Format, D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, format)) == D3D_OK; + pd3d->Release(); + return support; } bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) @@ -289,9 +358,14 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx9"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = 4096; bd->pd3dDevice = device; bd->pd3dDevice->AddRef(); + bd->HasRgbaSupport = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8); return true; } @@ -301,76 +375,99 @@ void ImGui_ImplDX9_Shutdown() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplDX9_InvalidateDeviceObjects(); if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } -static bool ImGui_ImplDX9_CheckFormatSupport(IDirect3DDevice9* pDevice, D3DFORMAT format) +// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices) +static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, const ImU32* src, int src_pitch, ImU32* dst, int dst_pitch, int w, int h) { - IDirect3D9* pd3d = nullptr; - if (pDevice->GetDirect3D(&pd3d) != D3D_OK) - return false; - D3DDEVICE_CREATION_PARAMETERS param = {}; - D3DDISPLAYMODE mode = {}; - if (pDevice->GetCreationParameters(¶m) != D3D_OK || pDevice->GetDisplayMode(0, &mode) != D3D_OK) +#ifndef IMGUI_USE_BGRA_PACKED_COLOR + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + const bool convert_rgba_to_bgra = (!bd->HasRgbaSupport && tex_use_colors); +#else + const bool convert_rgba_to_bgra = false; + IM_UNUSED(tex_use_colors); +#endif + for (int y = 0; y < h; y++) { - pd3d->Release(); - return false; + const ImU32* src_p = (const ImU32*)(const void*)((const unsigned char*)src + src_pitch * y); + ImU32* dst_p = (ImU32*)(void*)((unsigned char*)dst + dst_pitch * y); + if (convert_rgba_to_bgra) + for (int x = w; x > 0; x--, src_p++, dst_p++) // Convert copy + *dst_p = IMGUI_COL_TO_DX9_ARGB(*src_p); + else + memcpy(dst_p, src_p, w * 4); // Raw copy } - // Font texture should support linear filter, color blend and write to render-target - bool support = (pd3d->CheckDeviceFormat(param.AdapterOrdinal, param.DeviceType, mode.Format, D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, format)) == D3D_OK; - pd3d->Release(); - return support; } -static bool ImGui_ImplDX9_CreateFontsTexture() +void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); - unsigned char* pixels; - int width, height, bytes_per_pixel; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel); - // Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices) -#ifndef IMGUI_USE_BGRA_PACKED_COLOR - const bool rgba_support = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8); - if (!rgba_support && io.Fonts->TexPixelsUseColors) + if (tex->Status == ImTextureStatus_WantCreate) { - ImU32* dst_start = (ImU32*)ImGui::MemAlloc((size_t)width * height * bytes_per_pixel); - for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + (size_t)width * height; dst < dst_end; src++, dst++) - *dst = IMGUI_COL_TO_DX9_ARGB(*src); - pixels = (unsigned char*)dst_start; - } -#else - const bool rgba_support = false; -#endif - - // Upload texture to graphics system - bd->FontTexture.d3dTexture = nullptr; - if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, rgba_support ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture.d3dTexture, nullptr) < 0) - return false; - D3DLOCKED_RECT tex_locked_rect; - if (bd->FontTexture.d3dTexture->LockRect(0, &tex_locked_rect, nullptr, 0) != D3D_OK) - return false; - for (int y = 0; y < height; y++) - memcpy((unsigned char*)tex_locked_rect.pBits + (size_t)tex_locked_rect.Pitch * y, pixels + (size_t)width * bytes_per_pixel * y, (size_t)width * bytes_per_pixel); - bd->FontTexture.d3dTexture->UnlockRect(0); + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + LPDIRECT3DTEXTURE9 dx_tex = nullptr; + HRESULT hr = bd->pd3dDevice->CreateTexture(tex->Width, tex->Height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &dx_tex, nullptr); + if (hr < 0) + { + IM_ASSERT(hr >= 0 && "Backend failed to create texture!"); + return; + } - // Store our identifier - io.Fonts->SetTexID((ImTextureID)&bd->FontTexture); + D3DLOCKED_RECT locked_rect; + if (dx_tex->LockRect(0, &locked_rect, nullptr, 0) == D3D_OK) + { + ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixels(), tex->Width * 4, (ImU32*)locked_rect.pBits, (ImU32)locked_rect.Pitch, tex->Width, tex->Height); + dx_tex->UnlockRect(0); + } -#ifndef IMGUI_USE_BGRA_PACKED_COLOR - if (!rgba_support && io.Fonts->TexPixelsUseColors) - ImGui::MemFree(pixels); -#endif + // Store identifiers + ImTextureDX9* tex_wrapper = new ImTextureDX9{dx_tex, false}; + tex->SetTexID((ImTextureID)tex_wrapper); + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantUpdates) + { + // Update selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region. + const ImTextureDX9 *tex_wrapper = (const ImTextureDX9 *)tex->TexID; + const LPDIRECT3DTEXTURE9 backend_tex = tex_wrapper->d3dTexture; + RECT update_rect = { (LONG)tex->UpdateRect.x, (LONG)tex->UpdateRect.y, (LONG)(tex->UpdateRect.x + tex->UpdateRect.w), (LONG)(tex->UpdateRect.y + tex->UpdateRect.h) }; + D3DLOCKED_RECT locked_rect; + if (backend_tex->LockRect(0, &locked_rect, &update_rect, 0) == D3D_OK) + for (ImTextureRect& r : tex->Updates) + ImGui_ImplDX9_CopyTextureRegion(tex->UseColors, (ImU32*)tex->GetPixelsAt(r.x, r.y), tex->Width * 4, + (ImU32*)locked_rect.pBits + (r.x - update_rect.left) + (r.y - update_rect.top) * (locked_rect.Pitch / 4), (int)locked_rect.Pitch, r.w, r.h); + backend_tex->UnlockRect(0); + tex->SetStatus(ImTextureStatus_OK); + } + else if (tex->Status == ImTextureStatus_WantDestroy) + { + ImTextureDX9 *tex_wrapper = (ImTextureDX9 *)tex->TexID; + if (tex_wrapper) + { + IM_ASSERT(tex_wrapper == (ImTextureDX9*)tex->TexID); + tex_wrapper->d3dTexture->Release(); + tex_wrapper->d3dTexture = nullptr; - return true; + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + } + tex->SetStatus(ImTextureStatus_Destroyed); + } } bool ImGui_ImplDX9_CreateDeviceObjects() @@ -378,8 +475,6 @@ bool ImGui_ImplDX9_CreateDeviceObjects() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); if (!bd || !bd->pd3dDevice) return false; - if (!ImGui_ImplDX9_CreateFontsTexture()) - return false; return true; } @@ -388,24 +483,23 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); if (!bd || !bd->pd3dDevice) return; + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + { + tex->SetStatus(ImTextureStatus_WantDestroy); + ImGui_ImplDX9_UpdateTexture(tex); + } if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } - if (bd->FontTexture.d3dTexture) - { - bd->FontTexture.d3dTexture->Release(); - bd->FontTexture.d3dTexture = nullptr; - // We copied bd->FontTexture to io.Fonts->TexID so let's clear that as well. - ImGui::GetIO().Fonts->SetTexID(0); - } } void ImGui_ImplDX9_NewFrame() { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX9_Init()?"); - - if (!bd->FontTexture.d3dTexture) - ImGui_ImplDX9_CreateDeviceObjects(); + IM_UNUSED(bd); } //----------------------------------------------------------------------------- diff --git a/core/deps/imgui/backends/imgui_impl_dx9.h b/core/deps/imgui/backends/imgui_impl_dx9.h old mode 100644 new mode 100755 index e8205a6e36..db6cd263b7 --- a/core/deps/imgui/backends/imgui_impl_dx9.h +++ b/core/deps/imgui/backends/imgui_impl_dx9.h @@ -2,8 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. Win32) // Implemented features: -// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -20,12 +22,13 @@ struct IDirect3DDevice9; struct IDirect3DTexture9; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device); IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame(); IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); -// Use if you want to reset your rendering device without losing Dear ImGui state. +// Use if you want to reset your rendering state without using ImGui_ImplDX9_RenderDrawData. IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects(); @@ -35,5 +38,7 @@ struct ImTextureDX9 IDirect3DTexture9 *d3dTexture; bool pointSampling; }; +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. +IMGUI_IMPL_API void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex); #endif // #ifndef IMGUI_DISABLE diff --git a/core/deps/imgui/backends/imgui_impl_opengl3.cpp b/core/deps/imgui/backends/imgui_impl_opengl3.cpp old mode 100644 new mode 100755 index 4d38f6e317..5a3f4e3f3a --- a/core/deps/imgui/backends/imgui_impl_opengl3.cpp +++ b/core/deps/imgui/backends/imgui_impl_opengl3.cpp @@ -11,9 +11,64 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-07-22: OpenGL: Add and call embedded loader shutdown during ImGui_ImplOpenGL3_Shutdown() to facilitate multiple init/shutdown cycles in same process. (#8792) +// 2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures (#8802) + restore non-WebGL/ES update path that doesn't require a CPU-side copy. +// 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL3_CreateFontsTexture() and ImGui_ImplOpenGL3_DestroyFontsTexture(). +// 2025-06-04: OpenGL: Made GLES 3.20 contexts not access GL_CONTEXT_PROFILE_MASK nor GL_PRIMITIVE_RESTART. (#8664) +// 2025-02-18: OpenGL: Lazily reinitialize embedded GL loader for when calling backend from e.g. other DLL boundaries. (#8406) +// 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. +// 2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) +// 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562) +// 2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447) +// 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink. +// 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983) +// 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445) +// 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333) +// 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375) +// 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333) +// 2023-03-23: OpenGL: Properly restoring "no shader program bound" if it was the case prior to running the rendering function. (#6267, #6220, #6224) +// 2023-03-15: OpenGL: Fixed GL loader crash when GL_VERSION returns nullptr. (#6154, #4445, #3530) +// 2023-03-06: OpenGL: Fixed restoration of a potentially deleted OpenGL program, by calling glIsProgram(). (#6220, #6224) +// 2022-11-09: OpenGL: Reverted use of glBufferSubData(), too many corruptions issues + old issues seemingly can't be reproed with Intel drivers nowadays (revert 2021-12-15 and 2022-05-23 changes). +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-09-27: OpenGL: Added ability to '#define IMGUI_IMPL_OPENGL_DEBUG'. +// 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127). +// 2022-05-13: OpenGL: Fixed state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states. +// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers. +// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions. +// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader. +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state. +// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader. +// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version. +// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) +// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater. +// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer. +// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state. +// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state. +// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x) +// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre-3.3 context which have the defines set by a loader. +// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader. +// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX. +// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix. +// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset. +// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader. +// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader. +// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders. +// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility. +// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call. +// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop. +// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early. +// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0). +// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader. +// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. // 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450). // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT). +// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN. // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". // 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. @@ -21,7 +76,7 @@ // 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. // 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. // 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. -// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a nullptr pointer. // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. @@ -37,17 +92,17 @@ // version version string //---------------------------------------- // 2.0 110 "#version 110" -// 2.1 120 -// 3.0 130 -// 3.1 140 +// 2.1 120 "#version 120" +// 3.0 130 "#version 130" +// 3.1 140 "#version 140" // 3.2 150 "#version 150" -// 3.3 330 -// 4.0 400 +// 3.3 330 "#version 330 core" +// 4.0 400 "#version 400 core" // 4.1 410 "#version 410 core" -// 4.2 420 -// 4.3 430 -// ES 2.0 100 "#version 100" -// ES 3.0 300 "#version 300 es" +// 4.2 420 "#version 410 core" +// 4.3 430 "#version 430 core" +// ES 2.0 100 "#version 100" = WebGL 1.0 +// ES 3.0 300 "#version 300 es" = WebGL 2.0 //---------------------------------------- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) @@ -55,6 +110,7 @@ #endif #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_opengl3.h" #include #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier @@ -69,76 +125,102 @@ #include "rend/gles/glcache.h" // OpenGL Data -static char g_GlslVersionString[32] = ""; -static GLuint g_FontTexture = 0; -static GLuint g_ShaderHandle = 0; -static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; -static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; -static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; +struct ImGui_ImplOpenGL3_Data +{ + GLuint GlVersion; + char GlslVersionString[32]; + GLuint ShaderHandle; + GLint AttribLocationTex; + GLint AttribLocationProjMtx; + GLuint AttribLocationVtxPos; + GLuint AttribLocationVtxUV; + GLuint AttribLocationVtxColor; + unsigned int VboHandle, ElementsHandle; + bool HasPolygonMode; + bool HasBindSampler; + bool HasClipOrigin; + ImVector TempBuffer; + + ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; +} // Functions -static bool ImGui_ImplOpenGL3_CreateDeviceObjects(); -static void ImGui_ImplOpenGL3_DestroyDeviceObjects(); +bool ImGui_ImplOpenGL3_CreateDeviceObjects(); +void ImGui_ImplOpenGL3_DestroyDeviceObjects(); -bool ImGui_ImplOpenGL3_Init() +bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { ImGuiIO& io = ImGui::GetIO(); IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + // Setup backend capabilities flags + ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_opengl3"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; - // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. - const char* glsl_version; - if (theGLContext.isGLES()) - glsl_version = "#version 100"; // OpenGL ES 2.0 - else + // Flycast: Detect version using theGLContext + if (glsl_version == nullptr) + { + if (theGLContext.isGLES()) + glsl_version = "#version 100"; // OpenGL ES 2.0 + else #if defined(__APPLE__) - glsl_version = "#version 140"; // OpenGL 3.1 + glsl_version = "#version 140"; // OpenGL 3.1 #else - glsl_version = "#version 130"; // OpenGL 3.0 + glsl_version = "#version 130"; // OpenGL 3.0 +#endif + } + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString)); + strcpy(bd->GlslVersionString, glsl_version); + strcat(bd->GlslVersionString, "\n"); + + // Detect extensions we support + bd->HasPolygonMode = !theGLContext.isGLES(); + bd->HasBindSampler = (theGLContext.getMajorVersion() >= 3 && !theGLContext.isGLES()) || (theGLContext.isGLES() && theGLContext.getMajorVersion() >= 3); +#ifdef GL_CLIP_ORIGIN + bd->HasClipOrigin = (theGLContext.getMajorVersion() >= 4 && glClipControl != NULL); #endif - IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); - strcpy(g_GlslVersionString, glsl_version); - strcat(g_GlslVersionString, "\n"); return true; } void ImGui_ImplOpenGL3_Shutdown() { + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplOpenGL3_DestroyDeviceObjects(); + + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures; + platform_io.ClearRendererHandlers(); + IM_DELETE(bd); } void ImGui_ImplOpenGL3_NewFrame() { - if (!g_FontTexture) - ImGui_ImplOpenGL3_CreateDeviceObjects(); + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?"); + + if (!bd->ShaderHandle) + if (!ImGui_ImplOpenGL3_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplOpenGL3_CreateDeviceObjects() failed!"); } -// OpenGL3 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) +static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) { - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width <= 0 || fb_height <= 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // Backup GL state - glActiveTexture(GL_TEXTURE0); - bool clip_origin_lower_left = true; -#ifdef GL_CLIP_ORIGIN - if (theGLContext.getMajorVersion() >= 4 && glClipControl != NULL) - { - GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) - if (last_clip_origin == GL_UPPER_LEFT) - clip_origin_lower_left = false; - } -#endif + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill glcache.Enable(GL_BLEND); @@ -146,13 +228,24 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glcache.Disable(GL_CULL_FACE); glcache.Disable(GL_DEPTH_TEST); - glcache.Enable(GL_SCISSOR_TEST); glcache.Disable(GL_STENCIL_TEST); + glcache.Enable(GL_SCISSOR_TEST); #ifdef GL_POLYGON_MODE - if (glPolygonMode != NULL) + if (bd->HasPolygonMode && glPolygonMode != NULL) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif + // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) +#if defined(GL_CLIP_ORIGIN) + bool clip_origin_lower_left = true; + if (bd->HasClipOrigin) + { + GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin); + if (current_clip_origin == GL_UPPER_LEFT) + clip_origin_lower_left = false; + } +#endif + // Setup viewport, orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); @@ -160,6 +253,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; float T = draw_data->DisplayPos.y; float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; +#if defined(GL_CLIP_ORIGIN) + if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left +#endif const float ortho_projection[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, @@ -167,64 +263,99 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) { 0.0f, 0.0f, -1.0f, 0.0f }, { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, }; - glcache.UseProgram(g_ShaderHandle); - glUniform1i(g_AttribLocationTex, 0); - glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); + glcache.UseProgram(bd->ShaderHandle); + glUniform1i(bd->AttribLocationTex, 0); + glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); #ifndef GLES2 - if (theGLContext.getMajorVersion() >= 3 && glBindSampler != NULL) + if (bd->HasBindSampler && glBindSampler != NULL) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. #endif - GLuint vao_handle = 0; + vertex_array_object = 0; #ifndef GLES2 if (theGLContext.getMajorVersion() >= 3) { // Recreate the VAO every time // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) - glGenVertexArrays(1, &vao_handle); - glBindVertexArray(vao_handle); + glGenVertexArrays(1, &vertex_array_object); + glBindVertexArray(vertex_array_object); } #endif - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); - glEnableVertexAttribArray(g_AttribLocationPosition); - glEnableVertexAttribArray(g_AttribLocationUV); - glEnableVertexAttribArray(g_AttribLocationColor); - glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos)); - glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv)); - glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col)); - - // Draw - ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) + glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle); + glEnableVertexAttribArray(bd->AttribLocationVtxPos); + glEnableVertexAttribArray(bd->AttribLocationVtxUV); + glEnableVertexAttribArray(bd->AttribLocationVtxColor); + glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos)); + glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv)); + glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col)); +} + +// OpenGL3 Render function. +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. +void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplOpenGL3_UpdateTexture(tex); + + // Backup GL state + glActiveTexture(GL_TEXTURE0); + + // Setup desired GL state + // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) + // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. + GLuint vertex_array_object = 0; + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)draw_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)draw_list->VtxBuffer.Data, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)draw_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)draw_list->IdxBuffer.Data, GL_STREAM_DRAW); - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) + const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != nullptr) { - // User callback (registered via ImDrawList::AddCallback) - pcmd->UserCallback(cmd_list, pcmd); + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + else + pcmd->UserCallback(draw_list, pcmd); } else { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - if (clip_origin_lower_left) - glcache.Scissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); - else - glcache.Scissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) - - // Bind texture, Draw - glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); - } + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + // Apply scissor/clipping rectangle (Y is inverted in OpenGL) + glcache.Scissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); + + // Bind texture, Draw + glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); + } } } @@ -232,42 +363,77 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #ifndef GLES2 - if (vao_handle != 0) - glDeleteVertexArrays(1, &vao_handle); + if (vertex_array_object != 0) + glDeleteVertexArrays(1, &vertex_array_object); #endif } -static bool ImGui_ImplOpenGL3_CreateFontsTexture() +static void ImGui_ImplOpenGL3_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - g_FontTexture = glcache.GenTexture(); - glcache.BindTexture(GL_TEXTURE_2D, g_FontTexture); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (theGLContext.getMajorVersion() >= 3) - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)g_FontTexture); - - return true; + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (tex->BackendUserData != (void*)bd) + return; + GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; + if (gl_tex_id != 0) + glcache.DeleteTextures(1, &gl_tex_id); + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); + tex->BackendUserData = nullptr; } -static void ImGui_ImplOpenGL3_DestroyFontsTexture() +void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) { - if (g_FontTexture) + if (tex->Status == ImTextureStatus_Destroyed) + tex->SetStatus(ImTextureStatus_WantCreate); + + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) + { + if (theGLContext.getMajorVersion() >= 3) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + + if (tex->Status == ImTextureStatus_WantCreate) + { + GLuint gl_texture_id = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, gl_texture_id); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixels()); + + tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id); + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = (void*)bd; + } + else if (tex->Status == ImTextureStatus_WantUpdates) { - ImGuiIO& io = ImGui::GetIO(); - glcache.DeleteTextures(1, &g_FontTexture); - g_FontTexture = 0; + GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID; + glcache.BindTexture(GL_TEXTURE_2D, gl_tex_id); + if (theGLContext.getMajorVersion() >= 3) + { + glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width); + for (auto& r : tex->Updates) + glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y)); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + else + { + // Fallback for GLES2 / No unpack row length: use a temporary buffer to upload + for (auto& r : tex->Updates) + { + const int src_pitch = r.w * 4; // RGBA + bd->TempBuffer.resize(r.h * src_pitch); + char* out_p = bd->TempBuffer.Data; + for (int y = 0; y < r.h; y++, out_p += src_pitch) + memcpy(out_p, (const char*)tex->GetPixelsAt(r.x, r.y + y), src_pitch); + glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, bd->TempBuffer.Data); + } + } + tex->SetStatus(ImTextureStatus_OK); } + else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplOpenGL3_DestroyTexture(tex); } // If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. @@ -291,11 +457,12 @@ static bool CheckShader(GLuint handle, const char* desc) // If you get an error please report on GitHub. You may try different GL context version or GLSL version. static bool CheckProgram(GLuint handle, const char* desc) { + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); GLint status = 0, log_length = 0; glGetProgramiv(handle, GL_LINK_STATUS, &status); glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); if ((GLboolean)status == GL_FALSE) - WARN_LOG(RENDERER, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')", desc, g_GlslVersionString); + WARN_LOG(RENDERER, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')", desc, bd->GlslVersionString); if (log_length > 0) { ImVector buf; @@ -306,11 +473,13 @@ static bool CheckProgram(GLuint handle, const char* desc) return (GLboolean)status == GL_TRUE; } -static bool ImGui_ImplOpenGL3_CreateDeviceObjects() +bool ImGui_ImplOpenGL3_CreateDeviceObjects() { + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + // Parse GLSL version string int glsl_version = 130; - sscanf(g_GlslVersionString, "#version %d", &glsl_version); + sscanf(bd->GlslVersionString, "#version %d", &glsl_version); const GLchar* vertex_shader_glsl_120 = "uniform mat4 ProjMtx;\n" @@ -341,7 +510,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() "}\n"; const GLchar* vertex_shader_glsl_300_es = - "precision mediump float;\n" + "precision highp float;\n" "layout (location = 0) in vec2 Position;\n" "layout (location = 1) in vec2 UV;\n" "layout (location = 2) in vec4 Color;\n" @@ -413,8 +582,8 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() "}\n"; // Select shaders matching our GLSL versions - const GLchar* vertex_shader = NULL; - const GLchar* fragment_shader = NULL; + const GLchar* vertex_shader = nullptr; + const GLchar* fragment_shader = nullptr; if (glsl_version < 130) { vertex_shader = vertex_shader_glsl_120; @@ -437,52 +606,57 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() } // Create shaders - const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; + const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader }; GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL); + glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr); glCompileShader(vert_handle); - CheckShader(vert_handle, "vertex shader"); + if (!CheckShader(vert_handle, "vertex shader")) + return false; - const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; + const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader }; GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL); + glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr); glCompileShader(frag_handle); - CheckShader(frag_handle, "fragment shader"); - - g_ShaderHandle = glCreateProgram(); - glAttachShader(g_ShaderHandle, vert_handle); - glAttachShader(g_ShaderHandle, frag_handle); - glLinkProgram(g_ShaderHandle); - CheckProgram(g_ShaderHandle, "shader program"); - - glDetachShader(g_ShaderHandle, vert_handle); - glDetachShader(g_ShaderHandle, frag_handle); + if (!CheckShader(frag_handle, "fragment shader")) + return false; + + // Link + bd->ShaderHandle = glCreateProgram(); + glAttachShader(bd->ShaderHandle, vert_handle); + glAttachShader(bd->ShaderHandle, frag_handle); + glLinkProgram(bd->ShaderHandle); + if (!CheckProgram(bd->ShaderHandle, "shader program")) + return false; + + glDetachShader(bd->ShaderHandle, vert_handle); + glDetachShader(bd->ShaderHandle, frag_handle); glDeleteShader(vert_handle); glDeleteShader(frag_handle); - g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); - g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); - g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); - g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); - g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); + bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture"); + bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx"); + bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position"); + bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV"); + bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color"); // Create buffers - glGenBuffers(1, &g_VboHandle); - glGenBuffers(1, &g_ElementsHandle); - - ImGui_ImplOpenGL3_CreateFontsTexture(); + glGenBuffers(1, &bd->VboHandle); + glGenBuffers(1, &bd->ElementsHandle); return true; } -static void ImGui_ImplOpenGL3_DestroyDeviceObjects() +void ImGui_ImplOpenGL3_DestroyDeviceObjects() { - if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); - if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); - g_VboHandle = g_ElementsHandle = 0; - - glcache.DeleteProgram(g_ShaderHandle); - g_ShaderHandle = 0; - - ImGui_ImplOpenGL3_DestroyFontsTexture(); + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; } + if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; } + if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; } + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplOpenGL3_DestroyTexture(tex); } + +#endif // #ifndef IMGUI_DISABLE diff --git a/core/deps/imgui/backends/imgui_impl_opengl3.h b/core/deps/imgui/backends/imgui_impl_opengl3.h old mode 100644 new mode 100755 index dc77e37f31..ffa85768d7 --- a/core/deps/imgui/backends/imgui_impl_opengl3.h +++ b/core/deps/imgui/backends/imgui_impl_opengl3.h @@ -1,35 +1,68 @@ -// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) -// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) -// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) +// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 2.x 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) +// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui +// About WebGL/ES: +// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. +// - This is done automatically on iOS, Android and Emscripten targets. +// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h. -// About OpenGL function loaders: -// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. -// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. -// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp // About GLSL version: -// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. -// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" -// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. +// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string. +// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" +// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. #pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE -// Set default OpenGL loader to be gl3w -#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ - && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ - && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ - && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) -#define IMGUI_IMPL_OPENGL_LOADER_GL3W -#endif - -IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(); +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); + +// (Optional) Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); + +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex); + +// Configuration flags to add in your imconfig file: +//#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten) +//#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android) + +// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. +#if !defined(IMGUI_IMPL_OPENGL_ES2) \ +&& !defined(IMGUI_IMPL_OPENGL_ES3) + +// Try to detect GLES on matching platforms +#if defined(__APPLE__) +#include +#endif +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) +#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" +#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__) +#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" +#else +// Otherwise imgui_impl_opengl3_loader.h will be used. +#endif + +#endif + +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/core/deps/imgui/backends/imgui_impl_vulkan.cpp b/core/deps/imgui/backends/imgui_impl_vulkan.cpp old mode 100644 new mode 100755 index 820f0aaffa..c3a3049893 --- a/core/deps/imgui/backends/imgui_impl_vulkan.cpp +++ b/core/deps/imgui/backends/imgui_impl_vulkan.cpp @@ -2,16 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. - -// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. -// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. -// To build this on 32-bit systems and support texture changes: -// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files) -// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. -// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) -// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files) +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ @@ -33,10 +27,36 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-09-26: [Helpers] *BREAKING CHANGE*: Vulkan: Helper ImGui_ImplVulkanH_DestroyWindow() does not call vkDestroySurfaceKHR(): as surface is created by caller of ImGui_ImplVulkanH_CreateOrResizeWindow(), it is more consistent that we don't destroy it. (#9163) +// 2026-01-05: [Helpers] *BREAKING CHANGE*: Vulkan: Helper for creating render pass uses ImGui_ImplVulkanH_Window::AttachmentDesc to create render pass. Removed ClearEnabled. (#9152) +// 2025-11-24: [Helpers] Vulkan: Helper for creating a swap-chain (used by examples and multi-viewports) selects VkSwapchainCreateInfoKHR's compositeAlpha based on cap.supportedCompositeAlpha. (#8784) +// 2025-10-15: Vulkan: Added IMGUI_IMPL_VULKAN_VOLK_FILENAME to configure path to volk.h header. (#9008) +// 2025-09-26: *BREAKING CHANGE*: moved some fields in ImGui_ImplVulkan_InitInfo: init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass, init_info.Subpass --> init_info.PipelineInfoMain.Subpass, init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples, init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo. +// 2025-09-26: *BREAKING CHANGE*: renamed ImGui_ImplVulkan_MainPipelineCreateInfo to ImGui_ImplVulkan_PipelineInfo. Introduced very recently so shouldn't affect many users. +// 2025-09-26: [Helpers] *BREAKING CHANGE*: Helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a VkImageUsageFlags image_usage` argument, default to VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT if 0. +// 2025-09-26: Vulkan: Added a way to customize shaders by filling ImGui_ImplVulkan_InitInfo::CustomShaderVertCreateInfo/CustomShaderFragCreateInfo. (#8585) +// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. +// 2025-09-04: Vulkan: Added ImGui_ImplVulkan_CreateMainPipeline(). (#8110, #8111) +// 2025-07-27: Vulkan: Fixed texture update corruption introduced on 2025-06-11. (#8801, #8755, #8840) +// 2025-07-07: Vulkan: Fixed texture synchronization issue introduced on 2025-06-11. (#8772) +// 2025-06-27: Vulkan: Fixed validation errors during texture upload/update by aligning upload size to 'nonCoherentAtomSize'. (#8743, #8744) +// 2025-06-11: Vulkan: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplVulkan_CreateFontsTexture() and ImGui_ImplVulkan_DestroyFontsTexture(). +// 2025-05-07: Vulkan: Fixed validation errors during window detach in multi-viewport mode. (#8600, #8176) +// 2025-05-07: Vulkan: Load dynamic rendering functions using vkGetDeviceProcAddr() + try both non-KHR and KHR versions. (#8600, #8326, #8365) +// 2025-04-07: Vulkan: Deep-copy ImGui_ImplVulkan_InitInfo::PipelineRenderingCreateInfo's pColorAttachmentFormats buffer when set, in order to reduce common user-error of specifying a pointer to data that gets out of scope. (#8282) +// 2025-02-14: *BREAKING CHANGE*: Added uint32_t api_version to ImGui_ImplVulkan_LoadFunctions(). +// 2025-02-13: Vulkan: Added ApiVersion field in ImGui_ImplVulkan_InitInfo. Default to header version if unspecified. Dynamic rendering path loads "vkCmdBeginRendering/vkCmdEndRendering" (without -KHR suffix) on API 1.3. (#8326) +// 2025-01-09: Vulkan: Added IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE to clarify how many image sampler descriptors are expected to be available in descriptor pool. (#6642) +// 2025-01-06: [Helpers] Vulkan: Added more ImGui_ImplVulkanH_XXXX helper functions to simplify our examples. +// 2024-12-11: [Helpers] Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) +// 2024-11-27: Vulkan: Make user-provided descriptor pool optional. As a convenience, when setting init_info->DescriptorPoolSize the backend will create one itself. (#8172, #4867) +// 2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap. +// 2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. +// 2024-10-07: Vulkan: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*. // 2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().) // 2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering. // 2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure. -// 2024-01-19: Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236) +// 2024-01-19: [Helpers] Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236) // 2024-01-11: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size (#3957). Fixed MinAllocationSize handing (#7189). // 2024-01-03: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous "best practice" validation layer. (#7189, #4238) // 2024-01-03: Vulkan: Stopped creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them. @@ -51,7 +71,7 @@ // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-10-04: Vulkan: Added experimental ImGui_ImplVulkan_RemoveTexture() for api symmetry. (#914, #5738). // 2022-01-20: Vulkan: Added support for ImTextureID as VkDescriptorSet. User need to call ImGui_ImplVulkan_AddTexture(). Building for 32-bit targets requires '#define ImTextureID ImU64'. (#914). -// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. +// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likelihood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. // 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. @@ -64,16 +84,16 @@ // 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount(). -// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. +// 2019-04-04: [Helpers] Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. // 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like. // 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int). // 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display. // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. +// 2018-08-25: [Helpers] Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. // 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends. // 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. // 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. +// 2018-03-03: [Helpers] Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. // 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -89,12 +109,21 @@ #ifndef IM_MAX #define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B)) #endif +#undef Status // X11 headers are leaking this. // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant #endif +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) +#endif + // Forward Declarations struct ImGui_ImplVulkan_FrameRenderBuffers; struct ImGui_ImplVulkan_WindowRenderBuffers; @@ -104,11 +133,11 @@ void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulka void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); // Vulkan prototypes for use with custom loaders -// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h +// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h) #if defined(VK_NO_PROTOTYPES) && !defined(VOLK_H_) #define IMGUI_IMPL_VULKAN_USE_LOADER static bool g_FunctionsLoaded = false; @@ -135,6 +164,7 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorPool) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \ @@ -149,6 +179,7 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorPool) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \ @@ -163,14 +194,18 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkEnumeratePhysicalDevices) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkEndCommandBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeDescriptorSets) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetDeviceQueue) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceProperties) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceQueueFamilyProperties) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ @@ -179,8 +214,10 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueWaitIdle) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetFences) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkWaitForFences) // Define function pointers #define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; @@ -211,7 +248,17 @@ struct ImGui_ImplVulkan_WindowRenderBuffers { uint32_t Index; uint32_t Count; - ImGui_ImplVulkan_FrameRenderBuffers* FrameRenderBuffers; + ImVector FrameRenderBuffers; +}; + +struct ImGui_ImplVulkan_Texture +{ + VkDeviceMemory Memory; + VkImage Image; + VkImageView ImageView; + VkDescriptorSet DescriptorSet; + + ImGui_ImplVulkan_Texture() { memset((void*)this, 0, sizeof(*this)); } }; // Vulkan data @@ -219,21 +266,20 @@ struct ImGui_ImplVulkan_Data { ImGui_ImplVulkan_InitInfo VulkanInitInfo; VkDeviceSize BufferMemoryAlignment; + VkDeviceSize NonCoherentAtomSize; VkPipelineCreateFlags PipelineCreateFlags; VkDescriptorSetLayout DescriptorSetLayout; VkPipelineLayout PipelineLayout; - VkPipeline Pipeline; + VkPipeline Pipeline; // pipeline for main render pass (created by app) VkShaderModule ShaderModuleVert; VkShaderModule ShaderModuleFrag; + VkDescriptorPool DescriptorPool; + ImVector PipelineRenderingCreateInfoColorAttachmentFormats; // Deep copy of format array - // Font data - VkSampler FontSampler; - VkDeviceMemory FontMemory; - VkImage FontImage; - VkImageView FontView; - VkDescriptorSet FontDescriptorSet; - VkCommandPool FontCommandPool; - VkCommandBuffer FontCommandBuffer; + // Texture management + VkSampler TexSamplerLinear; + VkCommandPool TexCommandPool; + VkCommandBuffer TexCommandBuffer; // Render buffers for main window ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers; @@ -242,6 +288,7 @@ struct ImGui_ImplVulkan_Data { memset((void*)this, 0, sizeof(*this)); BufferMemoryAlignment = 256; + NonCoherentAtomSize = 64; } }; @@ -394,7 +441,7 @@ static inline VkDeviceSize AlignBufferSize(VkDeviceSize size, VkDeviceSize align return (size + alignment - 1) & ~(alignment - 1); } -static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& buffer_size, size_t new_size, VkBufferUsageFlagBits usage) +static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& buffer_size, VkDeviceSize new_size, VkBufferUsageFlagBits usage) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; @@ -481,6 +528,13 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (fb_width <= 0 || fb_height <= 0) return; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplVulkan_UpdateTexture(tex); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (pipeline == VK_NULL_HANDLE) @@ -488,12 +542,12 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Allocate array to store enough vertex/index buffers ImGui_ImplVulkan_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; - if (wrb->FrameRenderBuffers == nullptr) + if (wrb->FrameRenderBuffers.Size == 0) { wrb->Index = 0; wrb->Count = v->ImageCount; - wrb->FrameRenderBuffers = (ImGui_ImplVulkan_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); - memset(wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); + wrb->FrameRenderBuffers.resize(wrb->Count); + memset((void*)wrb->FrameRenderBuffers.Data, 0, wrb->FrameRenderBuffers.size_in_bytes()); } IM_ASSERT(wrb->Count == v->ImageCount); wrb->Index = (wrb->Index + 1) % wrb->Count; @@ -502,8 +556,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (draw_data->TotalVtxCount > 0) { // Create or resize the vertex/index buffers - size_t vertex_size = AlignBufferSize(draw_data->TotalVtxCount * sizeof(ImDrawVert), bd->BufferMemoryAlignment); - size_t index_size = AlignBufferSize(draw_data->TotalIdxCount * sizeof(ImDrawIdx), bd->BufferMemoryAlignment); + VkDeviceSize vertex_size = AlignBufferSize(draw_data->TotalVtxCount * sizeof(ImDrawVert), bd->BufferMemoryAlignment); + VkDeviceSize index_size = AlignBufferSize(draw_data->TotalIdxCount * sizeof(ImDrawIdx), bd->BufferMemoryAlignment); if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size) CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size) @@ -516,13 +570,12 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm check_vk_result(err); err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)&idx_dst); check_vk_result(err); - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); - vtx_dst += cmd_list->VtxBuffer.Size; - idx_dst += cmd_list->IdxBuffer.Size; + memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtx_dst += draw_list->VtxBuffer.Size; + idx_dst += draw_list->IdxBuffer.Size; } VkMappedMemoryRange range[2] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; @@ -540,20 +593,28 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Setup desired Vulkan state ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + // Setup render state structure (for callbacks and custom texture bindings) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplVulkan_RenderState render_state; + render_state.CommandBuffer = command_buffer; + render_state.Pipeline = pipeline; + render_state.PipelineLayout = bd->PipelineLayout; + platform_io.Renderer_RenderState = &render_state; + // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) // Render command lists // (Because we merged all buffers into a single one, we maintain our own offset into them) + VkDescriptorSet last_desc_set = VK_NULL_HANDLE; int global_vtx_offset = 0; int global_idx_offset = 0; - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (const ImDrawList* draw_list : draw_data->CmdLists) { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() @@ -561,7 +622,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); else - pcmd->UserCallback(cmd_list, pcmd); + pcmd->UserCallback(draw_list, pcmd); + last_desc_set = VK_NULL_HANDLE; } else { @@ -587,21 +649,18 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Bind DescriptorSet with font or user texture VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID(); - if (sizeof(ImTextureID) < sizeof(ImU64)) - { - // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. - IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet); - desc_set = bd->FontDescriptorSet; - } - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr); + if (desc_set != last_desc_set) + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr); + last_desc_set = desc_set; // Draw vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } } - global_idx_offset += cmd_list->IdxBuffer.Size; - global_vtx_offset += cmd_list->VtxBuffer.Size; + global_idx_offset += draw_list->IdxBuffer.Size; + global_vtx_offset += draw_list->VtxBuffer.Size; } + platform_io.Renderer_RenderState = nullptr; // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. // Our last values will leak into user/application rendering IF: @@ -614,244 +673,265 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm vkCmdSetScissor(command_buffer, 0, 1, &scissor); } -bool ImGui_ImplVulkan_CreateFontsTexture() +static void ImGui_ImplVulkan_DestroyTexture(ImTextureData* tex) { - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - VkResult err; - - // Destroy existing texture (if any) - if (bd->FontView || bd->FontImage || bd->FontMemory || bd->FontDescriptorSet) + if (ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData) { - vkQueueWaitIdle(v->Queue); - ImGui_ImplVulkan_DestroyFontsTexture(); - } - - // Create command pool/buffer - if (bd->FontCommandPool == VK_NULL_HANDLE) - { - VkCommandPoolCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - info.flags = 0; - info.queueFamilyIndex = v->QueueFamily; - vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->FontCommandPool); - } - if (bd->FontCommandBuffer == VK_NULL_HANDLE) - { - VkCommandBufferAllocateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.commandPool = bd->FontCommandPool; - info.commandBufferCount = 1; - err = vkAllocateCommandBuffers(v->Device, &info, &bd->FontCommandBuffer); - check_vk_result(err); - } - - // Start command buffer - { - err = vkResetCommandPool(v->Device, bd->FontCommandPool, 0); - check_vk_result(err); - VkCommandBufferBeginInfo begin_info = {}; - begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(bd->FontCommandBuffer, &begin_info); - check_vk_result(err); + IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet); + vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator); + vkDestroyImage(v->Device, backend_tex->Image, v->Allocator); + vkFreeMemory(v->Device, backend_tex->Memory, v->Allocator); + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->BackendUserData = nullptr; } + tex->SetStatus(ImTextureStatus_Destroyed); +} - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - size_t upload_size = width * height * 4 * sizeof(char); +void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex) +{ + if (tex->Status == ImTextureStatus_OK) + return; + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err; - // Create the Image: + if (tex->Status == ImTextureStatus_WantCreate) { - VkImageCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - info.imageType = VK_IMAGE_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; - info.extent.width = width; - info.extent.height = height; - info.extent.depth = 1; - info.mipLevels = 1; - info.arrayLayers = 1; - info.samples = VK_SAMPLE_COUNT_1_BIT; - info.tiling = VK_IMAGE_TILING_OPTIMAL; - info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); - check_vk_result(err); - VkMemoryRequirements req; - vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); - VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); - alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); - check_vk_result(err); - err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); - check_vk_result(err); - } + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + ImGui_ImplVulkan_Texture* backend_tex = IM_NEW(ImGui_ImplVulkan_Texture)(); - // Create the Image View: - { - VkImageViewCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.image = bd->FontImage; - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; - info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - info.subresourceRange.levelCount = 1; - info.subresourceRange.layerCount = 1; - err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); - check_vk_result(err); - } + // Create the Image: + { + VkImageCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.extent.width = tex->Width; + info.extent.height = tex->Height; + info.extent.depth = 1; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + err = vkCreateImage(v->Device, &info, v->Allocator, &backend_tex->Image); + check_vk_result(err); + VkMemoryRequirements req; + vkGetImageMemoryRequirements(v->Device, backend_tex->Image, &req); + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &backend_tex->Memory); + check_vk_result(err); + err = vkBindImageMemory(v->Device, backend_tex->Image, backend_tex->Memory, 0); + check_vk_result(err); + } - // Create the Descriptor Set: - bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + // Create the Image View: + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.image = backend_tex->Image; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + info.subresourceRange.levelCount = 1; + info.subresourceRange.layerCount = 1; + err = vkCreateImageView(v->Device, &info, v->Allocator, &backend_tex->ImageView); + check_vk_result(err); + } - // Create the Upload Buffer: - VkDeviceMemory upload_buffer_memory; - VkBuffer upload_buffer; - { - VkBufferCreateInfo buffer_info = {}; - buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.size = upload_size; - buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &upload_buffer); - check_vk_result(err); - VkMemoryRequirements req; - vkGetBufferMemoryRequirements(v->Device, upload_buffer, &req); - bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; - VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); - alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &upload_buffer_memory); - check_vk_result(err); - err = vkBindBufferMemory(v->Device, upload_buffer, upload_buffer_memory, 0); - check_vk_result(err); - } + // Create the Descriptor Set + backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(bd->TexSamplerLinear, backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - // Upload to Buffer: - { - char* map = nullptr; - err = vkMapMemory(v->Device, upload_buffer_memory, 0, upload_size, 0, (void**)(&map)); - check_vk_result(err); - memcpy(map, pixels, upload_size); - VkMappedMemoryRange range[1] = {}; - range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range[0].memory = upload_buffer_memory; - range[0].size = upload_size; - err = vkFlushMappedMemoryRanges(v->Device, 1, range); - check_vk_result(err); - vkUnmapMemory(v->Device, upload_buffer_memory); + // Store identifiers + tex->SetTexID((ImTextureID)backend_tex->DescriptorSet); + tex->BackendUserData = backend_tex; } - // Copy to Image: + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) { - VkImageMemoryBarrier copy_barrier[1] = {}; - copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].image = bd->FontImage; - copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_barrier[0].subresourceRange.levelCount = 1; - copy_barrier[0].subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, copy_barrier); - - VkBufferImageCopy region = {}; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.layerCount = 1; - region.imageExtent.width = width; - region.imageExtent.height = height; - region.imageExtent.depth = 1; - vkCmdCopyBufferToImage(bd->FontCommandBuffer, upload_buffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - VkImageMemoryBarrier use_barrier[1] = {}; - use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - use_barrier[0].image = bd->FontImage; - use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - use_barrier[0].subresourceRange.levelCount = 1; - use_barrier[0].subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier); - } - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet); + ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData; + + // Update full texture or selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions. + // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture. + const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x; + const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y; + const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w; + const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h; + + // Create the Upload Buffer: + VkDeviceMemory upload_buffer_memory; + + VkBuffer upload_buffer; + VkDeviceSize upload_pitch = upload_w * tex->BytesPerPixel; + VkDeviceSize upload_size = AlignBufferSize(upload_h * upload_pitch, bd->NonCoherentAtomSize); + { + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = upload_size; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &upload_buffer); + check_vk_result(err); + VkMemoryRequirements req; + vkGetBufferMemoryRequirements(v->Device, upload_buffer, &req); + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &upload_buffer_memory); + check_vk_result(err); + err = vkBindBufferMemory(v->Device, upload_buffer, upload_buffer_memory, 0); + check_vk_result(err); + } - // End command buffer - VkSubmitInfo end_info = {}; - end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - end_info.commandBufferCount = 1; - end_info.pCommandBuffers = &bd->FontCommandBuffer; - err = vkEndCommandBuffer(bd->FontCommandBuffer); - check_vk_result(err); - err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE); - check_vk_result(err); + // Upload to Buffer: + { + char* map = nullptr; + err = vkMapMemory(v->Device, upload_buffer_memory, 0, upload_size, 0, (void**)(&map)); + check_vk_result(err); + for (int y = 0; y < upload_h; y++) + memcpy(map + upload_pitch * y, tex->GetPixelsAt(upload_x, upload_y + y), (size_t)upload_pitch); + VkMappedMemoryRange range[1] = {}; + range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[0].memory = upload_buffer_memory; + range[0].size = upload_size; + err = vkFlushMappedMemoryRanges(v->Device, 1, range); + check_vk_result(err); + vkUnmapMemory(v->Device, upload_buffer_memory); + } - err = vkQueueWaitIdle(v->Queue); - check_vk_result(err); + // Start command buffer + { + err = vkResetCommandPool(v->Device, bd->TexCommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(bd->TexCommandBuffer, &begin_info); + check_vk_result(err); + } - vkDestroyBuffer(v->Device, upload_buffer, v->Allocator); - vkFreeMemory(v->Device, upload_buffer_memory, v->Allocator); + // Copy to Image: + { + VkBufferMemoryBarrier upload_barrier[1] = {}; + upload_barrier[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + upload_barrier[0].srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + upload_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + upload_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + upload_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + upload_barrier[0].buffer = upload_buffer; + upload_barrier[0].offset = 0; + upload_barrier[0].size = upload_size; + + VkImageMemoryBarrier copy_barrier[1] = {}; + copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + copy_barrier[0].oldLayout = (tex->Status == ImTextureStatus_WantCreate) ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].image = backend_tex->Image; + copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_barrier[0].subresourceRange.levelCount = 1; + copy_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, upload_barrier, 1, copy_barrier); + + VkBufferImageCopy region = {}; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = upload_w; + region.imageExtent.height = upload_h; + region.imageExtent.depth = 1; + region.imageOffset.x = upload_x; + region.imageOffset.y = upload_y; + vkCmdCopyBufferToImage(bd->TexCommandBuffer, upload_buffer, backend_tex->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + VkImageMemoryBarrier use_barrier[1] = {}; + use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].image = backend_tex->Image; + use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + use_barrier[0].subresourceRange.levelCount = 1; + use_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier); + } - return true; -} + // End command buffer + { + VkSubmitInfo end_info = {}; + end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + end_info.commandBufferCount = 1; + end_info.pCommandBuffers = &bd->TexCommandBuffer; + err = vkEndCommandBuffer(bd->TexCommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE); + check_vk_result(err); + } -// You probably never need to call this, as it is called by ImGui_ImplVulkan_CreateFontsTexture() and ImGui_ImplVulkan_Shutdown(). -void ImGui_ImplVulkan_DestroyFontsTexture() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + err = vkQueueWaitIdle(v->Queue); // FIXME-OPT: Suboptimal! + check_vk_result(err); + vkDestroyBuffer(v->Device, upload_buffer, v->Allocator); + vkFreeMemory(v->Device, upload_buffer_memory, v->Allocator); - if (bd->FontDescriptorSet) - { - ImGui_ImplVulkan_RemoveTexture(bd->FontDescriptorSet); - bd->FontDescriptorSet = VK_NULL_HANDLE; + tex->SetStatus(ImTextureStatus_OK); } - if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; } - if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; } - if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; } + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames >= (int)bd->VulkanInitInfo.ImageCount) + ImGui_ImplVulkan_DestroyTexture(tex); } static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) { - // Create the shader modules ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (bd->ShaderModuleVert == VK_NULL_HANDLE) { - VkShaderModuleCreateInfo vert_info = {}; - vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vert_info.codeSize = sizeof(__glsl_shader_vert_spv); - vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; - VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert); + VkShaderModuleCreateInfo default_vert_info = {}; + default_vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + default_vert_info.codeSize = sizeof(__glsl_shader_vert_spv); + default_vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; + VkShaderModuleCreateInfo* p_vert_info = (v->CustomShaderVertCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderVertCreateInfo : &default_vert_info; + VkResult err = vkCreateShaderModule(device, p_vert_info, allocator, &bd->ShaderModuleVert); check_vk_result(err); } if (bd->ShaderModuleFrag == VK_NULL_HANDLE) { - VkShaderModuleCreateInfo frag_info = {}; - frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - frag_info.codeSize = sizeof(__glsl_shader_frag_spv); - frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; - VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag); + VkShaderModuleCreateInfo default_frag_info = {}; + default_frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + default_frag_info.codeSize = sizeof(__glsl_shader_frag_spv); + default_frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; + VkShaderModuleCreateInfo* p_frag_info = (v->CustomShaderFragCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderFragCreateInfo : &default_frag_info; + VkResult err = vkCreateShaderModule(device, p_frag_info, allocator, &bd->ShaderModuleFrag); check_vk_result(err); } } -static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) +#if !defined(IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING) && !(defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering)) +typedef void VkPipelineRenderingCreateInfoKHR; +#endif + +static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, const ImGui_ImplVulkan_PipelineInfo* info) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_CreateShaderModules(device, allocator); @@ -909,7 +989,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC VkPipelineMultisampleStateCreateInfo ms_info = {}; ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - ms_info.rasterizationSamples = (MSAASamples != 0) ? MSAASamples : VK_SAMPLE_COUNT_1_BIT; + ms_info.rasterizationSamples = (info->MSAASamples != 0) ? info->MSAASamples : VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState color_attachment[1] = {}; color_attachment[0].blendEnable = VK_TRUE; @@ -932,38 +1012,39 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state = {}; dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); + dynamic_state.dynamicStateCount = (uint32_t)IM_COUNTOF(dynamic_states); dynamic_state.pDynamicStates = dynamic_states; - VkGraphicsPipelineCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - info.flags = bd->PipelineCreateFlags; - info.stageCount = 2; - info.pStages = stage; - info.pVertexInputState = &vertex_info; - info.pInputAssemblyState = &ia_info; - info.pViewportState = &viewport_info; - info.pRasterizationState = &raster_info; - info.pMultisampleState = &ms_info; - info.pDepthStencilState = &depth_info; - info.pColorBlendState = &blend_info; - info.pDynamicState = &dynamic_state; - info.layout = bd->PipelineLayout; - info.renderPass = renderPass; - info.subpass = subpass; + VkGraphicsPipelineCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + create_info.flags = bd->PipelineCreateFlags; + create_info.stageCount = 2; + create_info.pStages = stage; + create_info.pVertexInputState = &vertex_info; + create_info.pInputAssemblyState = &ia_info; + create_info.pViewportState = &viewport_info; + create_info.pRasterizationState = &raster_info; + create_info.pMultisampleState = &ms_info; + create_info.pDepthStencilState = &depth_info; + create_info.pColorBlendState = &blend_info; + create_info.pDynamicState = &dynamic_state; + create_info.layout = bd->PipelineLayout; + create_info.renderPass = info->RenderPass; + create_info.subpass = info->Subpass; #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING if (bd->VulkanInitInfo.UseDynamicRendering) { - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo pNext must be NULL"); - info.pNext = &bd->VulkanInitInfo.PipelineRenderingCreateInfo; - info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. + IM_ASSERT(info->PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo::sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); + IM_ASSERT(info->PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo::pNext must be nullptr"); + create_info.pNext = &info->PipelineRenderingCreateInfo; + create_info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. } #endif - - VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + VkPipeline pipeline; + VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &create_info, allocator, &pipeline); check_vk_result(err); + return pipeline; } bool ImGui_ImplVulkan_CreateDeviceObjects() @@ -972,7 +1053,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; - if (!bd->FontSampler) + if (!bd->TexSamplerLinear) { // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. VkSamplerCreateInfo info = {}; @@ -980,13 +1061,13 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.magFilter = VK_FILTER_LINEAR; info.minFilter = VK_FILTER_LINEAR; info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; info.minLod = -1000; info.maxLod = 1000; info.maxAnisotropy = 1.0f; - err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->FontSampler); + err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->TexSamplerLinear); check_vk_result(err); } @@ -1004,6 +1085,21 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } + if (v->DescriptorPoolSize != 0) + { + IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE); + VkDescriptorPoolSize pool_size = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, v->DescriptorPoolSize }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = v->DescriptorPoolSize; + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &pool_size; + + err = vkCreateDescriptorPool(v->Device, &pool_info, v->Allocator, &bd->DescriptorPool); + check_vk_result(err); + } + if (!bd->PipelineLayout) { // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix @@ -1022,34 +1118,126 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, v->RenderPass, v->MSAASamples, &bd->Pipeline, v->Subpass); + // Create pipeline + bool create_main_pipeline = (v->PipelineInfoMain.RenderPass != VK_NULL_HANDLE); +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + create_main_pipeline |= (v->UseDynamicRendering && v->PipelineInfoMain.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR); +#endif + if (create_main_pipeline) + ImGui_ImplVulkan_CreateMainPipeline(&v->PipelineInfoMain); + + // Create command pool/buffer for texture upload + if (!bd->TexCommandPool) + { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.flags = 0; + info.queueFamilyIndex = v->QueueFamily; + err = vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->TexCommandPool); + check_vk_result(err); + } + if (!bd->TexCommandBuffer) + { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = bd->TexCommandPool; + info.commandBufferCount = 1; + err = vkAllocateCommandBuffers(v->Device, &info, &bd->TexCommandBuffer); + check_vk_result(err); + } return true; } +void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* pipeline_info_in) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (bd->Pipeline) + { + vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); + bd->Pipeline = VK_NULL_HANDLE; + } + ImGui_ImplVulkan_PipelineInfo* pipeline_info = &v->PipelineInfoMain; + if (pipeline_info != pipeline_info_in) + *pipeline_info = *pipeline_info_in; + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR* pipeline_rendering_create_info = &pipeline_info->PipelineRenderingCreateInfo; + if (v->UseDynamicRendering && pipeline_rendering_create_info->pColorAttachmentFormats != nullptr) + { + // Deep copy buffer to reduce error-rate for end user (#8282) + ImVector formats; + formats.resize((int)pipeline_rendering_create_info->colorAttachmentCount); + memcpy(formats.Data, pipeline_rendering_create_info->pColorAttachmentFormats, (size_t)formats.size_in_bytes()); + formats.swap(bd->PipelineRenderingCreateInfoColorAttachmentFormats); + pipeline_rendering_create_info->pColorAttachmentFormats = bd->PipelineRenderingCreateInfoColorAttachmentFormats.Data; + } +#endif + bd->Pipeline = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, pipeline_info); +} + void ImGui_ImplVulkan_DestroyDeviceObjects() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); - ImGui_ImplVulkan_DestroyFontsTexture(); - if (bd->FontCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->FontCommandPool, 1, &bd->FontCommandBuffer); bd->FontCommandBuffer = VK_NULL_HANDLE; } - if (bd->FontCommandPool) { vkDestroyCommandPool(v->Device, bd->FontCommandPool, v->Allocator); bd->FontCommandPool = VK_NULL_HANDLE; } + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplVulkan_DestroyTexture(tex); + + if (bd->TexCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->TexCommandPool, 1, &bd->TexCommandBuffer); bd->TexCommandBuffer = VK_NULL_HANDLE; } + if (bd->TexCommandPool) { vkDestroyCommandPool(v->Device, bd->TexCommandPool, v->Allocator); bd->TexCommandPool = VK_NULL_HANDLE; } + if (bd->TexSamplerLinear) { vkDestroySampler(v->Device, bd->TexSamplerLinear, v->Allocator); bd->TexSamplerLinear = VK_NULL_HANDLE; } if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } - if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; } if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } + if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; } } -bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +static void ImGui_ImplVulkan_LoadDynamicRenderingFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) +{ + IM_UNUSED(api_version); + + // Manually load those two (see #5446, #8326, #8365, #8600) + // - Try loading core (non-KHR) versions first (this will work for Vulkan 1.3+ and the device supports dynamic rendering) + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRendering", user_data)); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRendering", user_data)); + + // - Fallback to KHR versions if core not available (this will work if KHR extension is available and enabled and also the device supports dynamic rendering) + if (ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR == nullptr || ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR == nullptr) + { + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); + } +} +#endif + +// If unspecified by user, assume that ApiVersion == HeaderVersion + // We don't care about other versions than 1.3 for our checks, so don't need to make this exhaustive (e.g. with all #ifdef VK_VERSION_1_X checks) +static uint32_t ImGui_ImplVulkan_GetDefaultApiVersion() +{ +#ifdef VK_HEADER_VERSION_COMPLETE + return VK_HEADER_VERSION_COMPLETE; +#else + return VK_API_VERSION_1_0; +#endif +} + +bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) { // Load function pointers // You can use the default Vulkan loader using: - // ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); + // ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_3, [](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); // But this would be roughly equivalent to not setting VK_NO_PROTOTYPES. + if (api_version == 0) + api_version = ImGui_ImplVulkan_GetDefaultApiVersion(); + #ifdef IMGUI_IMPL_VULKAN_USE_LOADER #define IMGUI_VULKAN_FUNC_LOAD(func) \ func = reinterpret_cast(loader_func(#func, user_data)); \ @@ -1059,9 +1247,7 @@ bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const ch #undef IMGUI_VULKAN_FUNC_LOAD #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - // Manually load those two (see #5446) - ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); - ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); + ImGui_ImplVulkan_LoadDynamicRenderingFunctions(api_version, loader_func, user_data); #endif #else IM_UNUSED(loader_func); @@ -1076,12 +1262,14 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) { IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + if (info->ApiVersion == 0) + info->ApiVersion = ImGui_ImplVulkan_GetDefaultApiVersion(); + if (info->UseDynamicRendering) { #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING #ifndef IMGUI_IMPL_VULKAN_USE_LOADER - ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdBeginRenderingKHR")); - ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdEndRenderingKHR")); + ImGui_ImplVulkan_LoadDynamicRenderingFunctions(info->ApiVersion, [](const char* function_name, void* user_data) { return vkGetDeviceProcAddr((VkDevice)user_data, function_name); }, (void*)info->Device); #endif IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR != nullptr); IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR != nullptr); @@ -1099,20 +1287,30 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_vulkan"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. + // Sanity checks IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); IM_ASSERT(info->Device != VK_NULL_HANDLE); IM_ASSERT(info->Queue != VK_NULL_HANDLE); - IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE); IM_ASSERT(info->MinImageCount >= 2); IM_ASSERT(info->ImageCount >= info->MinImageCount); - if (info->UseDynamicRendering == false) - IM_ASSERT(info->RenderPass != VK_NULL_HANDLE); + if (info->DescriptorPool != VK_NULL_HANDLE) // Either DescriptorPool or DescriptorPoolSize must be set, not both! + IM_ASSERT(info->DescriptorPoolSize == 0); + else + IM_ASSERT(info->DescriptorPoolSize > 0); + if (info->UseDynamicRendering) + IM_ASSERT(info->PipelineInfoMain.RenderPass == VK_NULL_HANDLE); bd->VulkanInitInfo = *info; - ImGui_ImplVulkan_CreateDeviceObjects(); + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(info->PhysicalDevice, &properties); + bd->NonCoherentAtomSize = properties.limits.nonCoherentAtomSize; + + if (!ImGui_ImplVulkan_CreateDeviceObjects()) + IM_ASSERT(0 && "ImGui_ImplVulkan_CreateDeviceObjects() failed!"); // <- Can't be hit yet. return true; } @@ -1122,11 +1320,14 @@ void ImGui_ImplVulkan_Shutdown() ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplVulkan_DestroyDeviceObjects(); + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); + platform_io.ClearRendererHandlers(); IM_DELETE(bd); } @@ -1134,9 +1335,7 @@ void ImGui_ImplVulkan_NewFrame() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplVulkan_Init()?"); - - if (!bd->FontDescriptorSet) - ImGui_ImplVulkan_CreateFontsTexture(); + IM_UNUSED(bd); } void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) @@ -1153,19 +1352,20 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) bd->VulkanInitInfo.MinImageCount = min_image_count; } -// Register a texture +// Register a texture by creating a descriptor // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool; // Create Descriptor Set: VkDescriptorSet descriptor_set; { VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - alloc_info.descriptorPool = v->DescriptorPool; + alloc_info.descriptorPool = pool; alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &bd->DescriptorSetLayout; VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); @@ -1193,7 +1393,8 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - vkFreeDescriptorSets(v->Device, v->DescriptorPool, 1, &descriptor_set); + VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool; + vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set); } void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator) @@ -1210,15 +1411,14 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk { for (uint32_t n = 0; n < buffers->Count; n++) ImGui_ImplVulkan_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); - IM_FREE(buffers->FrameRenderBuffers); - buffers->FrameRenderBuffers = nullptr; + buffers->FrameRenderBuffers.clear(); buffers->Index = 0; buffers->Count = 0; } //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers -// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own app.) +// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.) //------------------------------------------------------------------------- // You probably do NOT need to use or care about those functions. // Those functions only exist because: @@ -1228,7 +1428,7 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk // but it is too much code to duplicate everywhere so we exceptionally expose them. // // Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). -// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. +// You may read this code to learn about Vulkan, but it is recommended you use your own custom tailored code to do equivalent work. // (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) //------------------------------------------------------------------------- @@ -1300,6 +1500,49 @@ VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_d return VK_PRESENT_MODE_FIFO_KHR; // Always available } +VkPhysicalDevice ImGui_ImplVulkanH_SelectPhysicalDevice(VkInstance instance) +{ + uint32_t gpu_count; + VkResult err = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr); + check_vk_result(err); + IM_ASSERT(gpu_count > 0); + + ImVector gpus; + gpus.resize(gpu_count); + err = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.Data); + check_vk_result(err); + + // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers + // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple + // dedicated GPUs) is out of scope of this sample. + for (VkPhysicalDevice& device : gpus) + { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(device, &properties); + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return device; + } + + // Use first GPU (Integrated) is a Discrete one is not available. + if (gpu_count > 0) + return gpus[0]; + return VK_NULL_HANDLE; +} + + +uint32_t ImGui_ImplVulkanH_SelectQueueFamilyIndex(VkPhysicalDevice physical_device) +{ + uint32_t count; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, nullptr); + ImVector queues_properties; + queues_properties.resize((int)count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, queues_properties.Data); + for (uint32_t i = 0; i < count; i++) + if (queues_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + return i; + return (uint32_t)-1; +} + void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator) { IM_ASSERT(physical_device != VK_NULL_HANDLE && device != VK_NULL_HANDLE); @@ -1363,7 +1606,7 @@ int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_m } // Also destroy old swap chain and in-flight frames data, if any. -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage) { VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; @@ -1377,10 +1620,8 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); - IM_FREE(wd->Frames); - IM_FREE(wd->FrameSemaphores); - wd->Frames = nullptr; - wd->FrameSemaphores = nullptr; + wd->Frames.clear(); + wd->FrameSemaphores.clear(); wd->ImageCount = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); @@ -1393,6 +1634,10 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // Create Swapchain { + VkSurfaceCapabilitiesKHR cap; + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); + check_vk_result(err); + VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; info.surface = wd->Surface; @@ -1400,21 +1645,22 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V info.imageFormat = wd->SurfaceFormat.format; info.imageColorSpace = wd->SurfaceFormat.colorSpace; info.imageArrayLayers = 1; - info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | image_usage; info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family - info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + info.preTransform = (cap.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : cap.currentTransform; + if (cap.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) + info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + else if (cap.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) + info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + else + IM_ASSERT(false && "No supported composite alpha mode found!"); info.presentMode = wd->PresentMode; info.clipped = VK_TRUE; info.oldSwapchain = old_swapchain; - VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); - check_vk_result(err); if (info.minImageCount < cap.minImageCount) info.minImageCount = cap.minImageCount; else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount) info.minImageCount = cap.maxImageCount; - if (cap.currentExtent.width == 0xffffffff) { info.imageExtent.width = wd->Width = w; @@ -1431,16 +1677,15 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V check_vk_result(err); VkImage backbuffers[16] = {}; IM_ASSERT(wd->ImageCount >= min_image_count); - IM_ASSERT(wd->ImageCount < IM_ARRAYSIZE(backbuffers)); + IM_ASSERT(wd->ImageCount < IM_COUNTOF(backbuffers)); err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers); check_vk_result(err); - IM_ASSERT(wd->Frames == nullptr && wd->FrameSemaphores == nullptr); wd->SemaphoreCount = wd->ImageCount + 1; - wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount); - wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->SemaphoreCount); - memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount); - memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount); + wd->Frames.resize(wd->ImageCount); + wd->FrameSemaphores.resize(wd->SemaphoreCount); + memset(wd->Frames.Data, 0, wd->Frames.size_in_bytes()); + memset(wd->FrameSemaphores.Data, 0, wd->FrameSemaphores.size_in_bytes()); for (uint32_t i = 0; i < wd->ImageCount; i++) wd->Frames[i].Backbuffer = backbuffers[i]; } @@ -1450,15 +1695,9 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // Create the Render Pass if (wd->UseDynamicRendering == false) { - VkAttachmentDescription attachment = {}; - attachment.format = wd->SurfaceFormat.format; - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentDescription attachment = wd->AttachmentDesc; + if (attachment.format == VK_FORMAT_UNDEFINED) + attachment.format = wd->SurfaceFormat.format; VkAttachmentReference color_attachment = {}; color_attachment.attachment = 0; color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -1533,16 +1772,96 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V } // Create or resize window -void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) +// - 2025/09/26: v1.92.4 added a trailing 'VkImageUsageFlags image_usage' parameter which is usually VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT. +void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count, VkImageUsageFlags image_usage) { IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); - (void)instance; - ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); + IM_ASSERT(wd->Surface != VK_NULL_HANDLE); + IM_UNUSED(instance); + + ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count, image_usage); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); + + // FIXME: to submit the command buffer, we need a queue. In the examples folder, the ImGui_ImplVulkanH_CreateOrResizeWindow function is called + // before the ImGui_ImplVulkan_Init function, so we don't have access to the queue yet. Here we have the queue_family that we can use to grab + // a queue from the device and submit the command buffer. It would be better to have access to the queue as suggested in the FIXME below. + VkCommandPool command_pool; + VkCommandPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.queueFamilyIndex = queue_family; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VkResult err = vkCreateCommandPool(device, &pool_info, allocator, &command_pool); + check_vk_result(err); + + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkFence fence; + err = vkCreateFence(device, &fence_info, allocator, &fence); + check_vk_result(err); + + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = 1; + VkCommandBuffer command_buffer; + err = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer); + check_vk_result(err); + + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(command_buffer, &begin_info); + check_vk_result(err); + + // Transition the images to the correct layout for rendering + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = wd->Frames[i].Backbuffer; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + + err = vkEndCommandBuffer(command_buffer); + check_vk_result(err); + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + VkQueue queue; + vkGetDeviceQueue(device, queue_family, 0, &queue); + err = vkQueueSubmit(queue, 1, &submit_info, fence); + check_vk_result(err); + err = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX); + check_vk_result(err); + err = vkResetFences(device, 1, &fence); + check_vk_result(err); + + err = vkResetCommandPool(device, command_pool, 0); + check_vk_result(err); + + // Destroy command buffer and fence and command pool + vkFreeCommandBuffers(device, command_pool, 1, &command_buffer); + vkDestroyCommandPool(device, command_pool, allocator); + vkDestroyFence(device, fence, allocator); + command_pool = VK_NULL_HANDLE; + command_buffer = VK_NULL_HANDLE; + fence = VK_NULL_HANDLE; + queue = VK_NULL_HANDLE; } void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) { + IM_UNUSED(instance); vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) //vkQueueWaitIdle(bd->Queue); @@ -1550,16 +1869,16 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); - IM_FREE(wd->Frames); - IM_FREE(wd->FrameSemaphores); - wd->Frames = nullptr; - wd->FrameSemaphores = nullptr; + wd->Frames.clear(); + wd->FrameSemaphores.clear(); vkDestroyPipeline(device, wd->Pipeline, allocator); vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); - vkDestroySurfaceKHR(instance, wd->Surface, allocator); - - *wd = ImGui_ImplVulkanH_Window(); + wd->RenderPass = VK_NULL_HANDLE; + wd->Swapchain = VK_NULL_HANDLE; + wd->Width = wd->Height = 0; + wd->FrameIndex = wd->ImageCount = wd->SemaphoreCount = wd->SemaphoreIndex = 0; + //vkDestroySurfaceKHR(instance, wd->Surface, allocator); // v1.92.6 (~2026-01-16): because wd->Surface is user provided we don't attempt to destroy it ourself. } void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator) diff --git a/core/deps/imgui/backends/imgui_impl_vulkan.h b/core/deps/imgui/backends/imgui_impl_vulkan.h old mode 100644 new mode 100755 index c174a6ca14..5b3baf00c5 --- a/core/deps/imgui/backends/imgui_impl_vulkan.h +++ b/core/deps/imgui/backends/imgui_impl_vulkan.h @@ -2,11 +2,10 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. - -// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. -// See imgui_impl_vulkan.cpp file for details. +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ @@ -42,9 +41,20 @@ // If you have no idea what this is, leave it alone! //#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES -// Convenience support for Volk +// [Configuration] Convenience support for Volk // (you can also technically use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().) +// (When using Volk from directory outside your include directories list you can specify full path to the volk.h header, +// for example when using Volk from VulkanSDK and using include_directories(${Vulkan_INCLUDE_DIRS})' from 'find_package(Vulkan REQUIRED)') //#define IMGUI_IMPL_VULKAN_USE_VOLK +//#define IMGUI_IMPL_VULKAN_VOLK_FILENAME +//#define IMGUI_IMPL_VULKAN_VOLK_FILENAME // Default +// Reminder: make those changes in your imconfig.h file, not here! + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#endif #if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES) #define VK_NO_PROTOTYPES @@ -55,7 +65,11 @@ // Vulkan includes #ifdef IMGUI_IMPL_VULKAN_USE_VOLK -#include +#ifdef IMGUI_IMPL_VULKAN_VOLK_FILENAME +#include IMGUI_IMPL_VULKAN_VOLK_FILENAME +#else +#include +#endif #else #include #endif @@ -63,84 +77,131 @@ #define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING #endif +// Backend uses a small number of descriptors per font atlas + as many as additional calls done to ImGui_ImplVulkan_AddTexture(). +#define IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE (8) // Minimum per atlas + +// Specify settings to create pipeline and swapchain +struct ImGui_ImplVulkan_PipelineInfo +{ + VkRenderPass RenderPass; // Ignored if using dynamic rendering + uint32_t Subpass; // + VkSampleCountFlagBits MSAASamples = {}; // 0 defaults to VK_SAMPLE_COUNT_1_BIT +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR +#endif +}; + // Initialization data, for ImGui_ImplVulkan_Init() -// - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, -// and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor. -// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure. // [Please zero-clear before use!] +// - About descriptor pool: +// - A VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, +// and must contain a pool size large enough to hold a small number of VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptors. +// - As an convenience, by setting DescriptorPoolSize > 0 the backend will create one for you. +// - About dynamic rendering: +// - When using dynamic rendering, set UseDynamicRendering=true + fill PipelineInfoMain.PipelineRenderingCreateInfo structure. struct ImGui_ImplVulkan_InitInfo { + uint32_t ApiVersion; // Fill with API version of Instance, e.g. VK_API_VERSION_1_3 or your value of VkApplicationInfo::apiVersion. May be lower than header version (VK_HEADER_VERSION_COMPLETE) VkInstance Instance; VkPhysicalDevice PhysicalDevice; VkDevice Device; uint32_t QueueFamily; VkQueue Queue; - VkDescriptorPool DescriptorPool; // See requirements in note above - VkRenderPass RenderPass; // Ignored if using dynamic rendering - uint32_t MinImageCount; // >= 2 - uint32_t ImageCount; // >= MinImageCount - VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT + VkDescriptorPool DescriptorPool; // See requirements in note above; ignored if using DescriptorPoolSize > 0 + uint32_t DescriptorPoolSize; // Optional: set to create internal descriptor pool automatically instead of using DescriptorPool. + uint32_t MinImageCount; // >= 2 + uint32_t ImageCount; // >= MinImageCount + VkPipelineCache PipelineCache; // Optional - // (Optional) - VkPipelineCache PipelineCache; - uint32_t Subpass; + // Pipeline + ImGui_ImplVulkan_PipelineInfo PipelineInfoMain; // Infos for Main Viewport (created by app/user) + //VkRenderPass RenderPass; // --> Since 2025/09/26: set 'PipelineInfoMain.RenderPass' instead + //uint32_t Subpass; // --> Since 2025/09/26: set 'PipelineInfoMain.Subpass' instead + //VkSampleCountFlagBits MSAASamples; // --> Since 2025/09/26: set 'PipelineInfoMain.MSAASamples' instead + //VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Since 2025/09/26: set 'PipelineInfoMain.PipelineRenderingCreateInfo' instead // (Optional) Dynamic Rendering - // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3. + // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3 + setup PipelineInfoMain.PipelineRenderingCreateInfo. bool UseDynamicRendering; -#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; -#endif // (Optional) Allocation, Debugging const VkAllocationCallbacks* Allocator; void (*CheckVkResultFn)(VkResult err); - VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. + VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. + + // (Optional) Customize default vertex/fragment shaders. + // - if .sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO we use specified structs, otherwise we use defaults. + // - Shader inputs/outputs need to match ours. Code/data pointed to by the structure needs to survive for whole during of backend usage. + VkShaderModuleCreateInfo CustomShaderVertCreateInfo; + VkShaderModuleCreateInfo CustomShaderFragCreateInfo; }; -// Called by user code -IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info); -IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); -IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info); +IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); +IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) + +// (Advanced) Use e.g. if you need to recreate pipeline without reinitializing the backend (see #8110, #8111) +// The main window pipeline will be created by ImGui_ImplVulkan_Init() if possible (== RenderPass xor (UseDynamicRendering && PipelineRenderingCreateInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR)) +// Else, the pipeline can be created, or re-created, using ImGui_ImplVulkan_CreateMainPipeline() before rendering. +IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* info); + +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually. +IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex); // Register a texture (VkDescriptorSet == ImTextureID) // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem // Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. -IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); -IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set); +IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); +IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set); // Optional: load Vulkan functions with a custom function loader // This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES -IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); +IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); + +// [BETA] Selected render state data shared with callbacks. +// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call. +// (Please open an issue if you feel you need access to more data) +struct ImGui_ImplVulkan_RenderState +{ + VkCommandBuffer CommandBuffer; + VkPipeline Pipeline; + VkPipelineLayout PipelineLayout; +}; //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers -// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.) //------------------------------------------------------------------------- +// Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app. +// // You probably do NOT need to use or care about those functions. +// WE DO NOT PROVIDE STRONG GUARANTEES OF BACKWARD/FORWARD COMPATIBILITY. // Those functions only exist because: // 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. // 2) the multi-viewport / platform window implementation needs them internally. -// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings, +// Generally we avoid exposing any kind of superfluous high-level helpers in the backends, // but it is too much code to duplicate everywhere so we exceptionally expose them. // -// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). -// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. -// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) +// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, +// render pass, frame buffers, etc.). You may read this code if you are curious, but +// it is recommended you use your own custom tailored code to do equivalent work. +// +// The ImGui_ImplVulkanH_XXX functions should NOT interact with any of the state used +// by the regular ImGui_ImplVulkan_XXX functions. //------------------------------------------------------------------------- struct ImGui_ImplVulkanH_Frame; struct ImGui_ImplVulkanH_Window; // Helpers -IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); -IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator); +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage); +IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator); IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); +IMGUI_IMPL_API VkPhysicalDevice ImGui_ImplVulkanH_SelectPhysicalDevice(VkInstance instance); +IMGUI_IMPL_API uint32_t ImGui_ImplVulkanH_SelectQueueFamilyIndex(VkPhysicalDevice physical_device); IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); // Helper structure to hold the data needed by one rendering frame @@ -166,30 +227,48 @@ struct ImGui_ImplVulkanH_FrameSemaphores // (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) struct ImGui_ImplVulkanH_Window { - int Width; - int Height; - VkSwapchainKHR Swapchain; - VkSurfaceKHR Surface; - VkSurfaceFormatKHR SurfaceFormat; - VkPresentModeKHR PresentMode; - VkRenderPass RenderPass; - VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo - bool UseDynamicRendering; - bool ClearEnable; - VkClearValue ClearValue; - uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) - uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) - uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR - uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) - ImGui_ImplVulkanH_Frame* Frames; - ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores; + // Input + bool UseDynamicRendering; + VkSurfaceKHR Surface; // Surface created and destroyed by caller. + VkSurfaceFormatKHR SurfaceFormat; + VkPresentModeKHR PresentMode; + VkAttachmentDescription AttachmentDesc; // RenderPass creation: main attachment description. + VkClearValue ClearValue; // RenderPass creation: clear value when using VK_ATTACHMENT_LOAD_OP_CLEAR. + + // Internal + int Width; // Generally same as passed to ImGui_ImplVulkanH_CreateOrResizeWindow() + int Height; + VkSwapchainKHR Swapchain; + VkRenderPass RenderPass; + VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo + uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) + uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) + uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR + uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) + ImVector Frames; + ImVector FrameSemaphores; ImGui_ImplVulkanH_Window() { memset((void*)this, 0, sizeof(*this)); - PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. - ClearEnable = true; + + // Parameters to create SwapChain + PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. + + // Parameters to create RenderPass + AttachmentDesc.format = VK_FORMAT_UNDEFINED; // Will automatically use wd->SurfaceFormat.format. + AttachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; + AttachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + AttachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + AttachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + AttachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + AttachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + AttachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; } }; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + #endif // #ifndef IMGUI_DISABLE diff --git a/core/deps/imgui/imconfig.h b/core/deps/imgui/imconfig.h index c6d9b8d5f8..7adab71380 100755 --- a/core/deps/imgui/imconfig.h +++ b/core/deps/imgui/imconfig.h @@ -49,7 +49,7 @@ //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). -//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert. +//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean/ProggyForever), remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert. //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Enable Test Engine / Automation features. @@ -67,7 +67,7 @@ //#define IMGUI_USE_LEGACY_CRC32_ADLER //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) -//#define IMGUI_USE_WCHAR32 +#define IMGUI_USE_WCHAR32 //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. @@ -86,7 +86,7 @@ // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). // Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience. // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. -//#define IMGUI_ENABLE_FREETYPE +#define IMGUI_ENABLE_FREETYPE //---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT) // Only works in combination with IMGUI_ENABLE_FREETYPE. diff --git a/core/deps/imgui/imgui.cpp b/core/deps/imgui/imgui.cpp old mode 100644 new mode 100755 index 3def23fb5e..83284f03f5 --- a/core/deps/imgui/imgui.cpp +++ b/core/deps/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.5 +// dear imgui, v1.92.6 // (main code and documentation) // Help: @@ -27,7 +27,7 @@ // Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there. // Since 1.92, we encourage font loading questions to also be posted in 'Issues'. -// Copyright (c) 2014-2025 Omar Cornut +// Copyright (c) 2014-2026 Omar Cornut // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. @@ -394,6 +394,41 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/01/08 (1.92.6) - Commented out legacy names obsoleted in 1.90 (Sept 2023): 'BeginChildFrame()' --> 'BeginChild()' with 'ImGuiChildFlags_FrameStyle'. 'EndChildFrame()' --> 'EndChild()'. 'ShowStackToolWindow()' --> 'ShowIDStackToolWindow()'. 'IM_OFFSETOF()' --> 'offsetof()'. + - 2026/01/07 (1.92.6) - Popups: changed compile-time 'ImGuiPopupFlags popup_flags = 1' default value to be '= 0' for BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid(), OpenPopupOnItemClick(). Default value has same meaning before and after. + - Refer to GitHub topic #9157 if you have any question. + - Before this version, those functions had a 'ImGuiPopupFlags popup_flags = 1' default value in their function signature. + Explicitly passing a literal 0 meant ImGuiPopupFlags_MouseButtonLeft. The default literal 1 meant ImGuiPopupFlags_MouseButtonRight. + This was introduced by a change on 2020/06/23 (1.77) while changing the signature from 'int mouse_button' to 'ImGuiPopupFlags popup_flags' and trying to preserve then-legacy behavior. + We have now changed this behavior to cleanup a very old API quirk, facilitate use by bindings, and to remove the last and error-prone non-zero default value. + Also because we deemed it extremely rare to use those helper functions with the Left mouse button! As using the LMB would generally be triggered via another widget, e.g. a Button() + a OpenPopup()/BeginPopup() call. + - Before: The default = 1 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 0 means ImGuiPopupFlags_MouseButtonLeft. + - After: The default = 0 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 1 also means ImGuiPopupFlags_MouseButtonRight (if legacy behavior are enabled) or will assert (if legacy behavior are disabled). + - TL;DR: if you don't want to use right mouse button for popups, always specify it explicitly using a named ImGuiPopupFlags_MouseButtonXXXX value. + Recap: + - BeginPopupContextItem("foo"); // Behavior unchanged (use Right button) + - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonLeft); // Behavior unchanged (use Left button) + - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonLeft | xxx); // Behavior unchanged (use Left button + flags) + - BeginPopupContextItem("foo", ImGuiPopupFlags_MouseButtonRight | xxx); // Behavior unchanged (use Right button + flags) + - BeginPopupContextItem("foo", 1); // Behavior unchanged (as a courtesy we legacy interpret 1 as ImGuiPopupFlags_MouseButtonRight, will assert if disabling legacy behaviors. + - BeginPopupContextItem("foo", 0); // !! Behavior changed !! Was Left button. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft. + - BeginPopupContextItem("foo", ImGuiPopupFlags_NoReopen); // !! Behavior changed !! Was Left button + flags. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft | xxx. + - 2025/12/23 (1.92.6) - Fonts: AddFontDefault() now automatically selects an embedded font between the new scalable AddFontDefaultVector() and the classic pixel-clean AddFontDefaultBitmap(). + The default selection is based on (style.FontSizeBase * FontScaleMain * FontScaleDpi) reaching a small threshold, but old codebases may not set any of them properly. As as a result, it is likely that old codebase may still default to AddFontDefaultBitmap(). + Prefer calling either based on your own logic. You can call AddFontDefaultBitmap() to ensure legacy behavior. + - 2025/12/23 (1.92.6) - Fonts: removed ImFontConfig::PixelSnapV added in 1.92 which turns out is unnecessary (and misdocumented). Post-rescale GlyphOffset is always rounded. + - 2025/12/17 (1.92.6) - Renamed helper macro IM_ARRAYSIZE() -> IM_COUNTOF(). Kept redirection/legacy name for now. + - 2025/12/11 (1.92.6) - Hashing: handling of "###" operator to reset to seed within a string identifier doesn't include the "###" characters in the output hash anymore. + - Before: GetID("Hello###World") == GetID("###World") != GetID("World") + - After: GetID("Hello###World") == GetID("###World") == GetID("World") + - This has the property of facilitating concatenating and manipulating identifiers using "###", and will allow fixing other dangling issues. + - This will invalidate hashes (stored in .ini data) for Tables and Windows that are using the "###" operators. (#713, #1698) + - 2025/11/24 (1.92.6) - Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did erroneously make a copy of the font data, essentially defeating the purpose of this flag and wasting memory. + (trivia: undetected since July 2015, this is perhaps the oldest bug in Dear ImGui history, albeit for a rarely used feature, see #9086) + HOWEVER, fixing this bug is likely to surface bugs in user code using `FontDataOwnedByAtlas = false`. + - Prior to 1.92, font data only needed to be available during the atlas->AddFontXXX() call. + - Since 1.92, font data needs to available until atlas->RemoveFont(), or more typically until a shutdown of the owning context or font atlas. + - The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed the issue altogether. - 2025/11/06 (1.92.5) - BeginChild: commented out some legacy names which were obsoleted in 1.90.0 (Nov 2023), 1.90.9 (July 2024), 1.91.1 (August 2024): - ImGuiChildFlags_Border --> ImGuiChildFlags_Borders - ImGuiWindowFlags_NavFlattened --> ImGuiChildFlags_NavFlattened (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0) @@ -452,7 +487,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2); - You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate understanding which font input is providing which glyph. - Fonts: **IMPORTANT** on Thread Safety: - - A few functions such as font->CalcTextSizeA() were, by sheer luck (== accidentally) thread-safe even thou we had never provided that guarantee. They are definitively not thread-safe anymore as new glyphs may be loaded. + - A few functions such as font->CalcTextSizeA() were, by sheer luck (== accidentally) thread-safe even though we had never provided that guarantee. They are definitively not thread-safe anymore as new glyphs may be loaded. - Fonts: ImFont::FontSize was removed and does not make sense anymore. ImFont::LegacySize is the size passed to AddFont(). - Fonts: Removed support for PushFont(NULL) which was a shortcut for "default font". - Fonts: Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'. @@ -524,6 +559,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before) - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022). - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022). + - GetKeyIndex() is removed (obsoleted March 2022). The indirection is now unnecessary. - pre-1.87 backends are not supported: - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields. - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields. @@ -1244,6 +1280,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers +#pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif // Debug options @@ -1251,7 +1288,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window // Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls. -static const float FONT_DEFAULT_SIZE = 20.0f; +static const float FONT_DEFAULT_SIZE_BASE = 20.0f; // When using Ctrl+Tab (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in @@ -1313,8 +1350,8 @@ static bool NavScoreItem(ImGuiNavItemData* result, const ImRect& nav static void NavApplyItemToResult(ImGuiNavItemData* result); static void NavProcessItem(); static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags); -static ImGuiInputSource NavCalcPreferredRefPosSource(); -static ImVec2 NavCalcPreferredRefPos(); +static ImGuiInputSource NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type); +static ImVec2 NavCalcPreferredRefPos(ImGuiWindowFlags window_type); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); @@ -1431,10 +1468,11 @@ ImGuiStyle::ImGuiStyle() ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar - ScrollbarPadding = 2.0f; // Padding of scrollbar grab within its frame (same for both axises) + ScrollbarPadding = 2.0f; // Padding of scrollbar grab within its frame (same for both axes) GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. + ImageRounding = 0.0f; // Rounding of Image() calls. ImageBorderSize = 0.0f; // Thickness of border around tabs. TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. @@ -1452,6 +1490,7 @@ ImGuiStyle::ImGuiStyle() DragDropTargetRounding = 0.0f; // Radius of the drag and drop target frame. DragDropTargetBorderSize = 2.0f; // Thickness of the drag and drop target border. DragDropTargetPadding = 3.0f; // Size to expand the drag and drop target from actual target item size. + ColorMarkerSize = 3.0f; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers. ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. @@ -1508,6 +1547,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) GrabMinSize = ImTrunc(GrabMinSize * scale_factor); GrabRounding = ImTrunc(GrabRounding * scale_factor); LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor); + ImageRounding = ImTrunc(ImageRounding * scale_factor); ImageBorderSize = ImTrunc(ImageBorderSize * scale_factor); TabRounding = ImTrunc(TabRounding * scale_factor); TabMinWidthBase = ImTrunc(TabMinWidthBase * scale_factor); @@ -1519,6 +1559,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) DragDropTargetRounding = ImTrunc(DragDropTargetRounding * scale_factor); DragDropTargetBorderSize = ImTrunc(DragDropTargetBorderSize * scale_factor); DragDropTargetPadding = ImTrunc(DragDropTargetPadding * scale_factor); + ColorMarkerSize = ImTrunc(ColorMarkerSize * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor); @@ -1528,8 +1569,8 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) ImGuiIO::ImGuiIO() { // Most fields are initialized with zero - memset(this, 0, sizeof(*this)); - IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); + memset((void*)this, 0, sizeof(*this)); + IM_STATIC_ASSERT(IM_COUNTOF(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_COUNTOF(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Settings ConfigFlags = ImGuiConfigFlags_None; @@ -1601,8 +1642,8 @@ ImGuiIO::ImGuiIO() MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseSource = ImGuiMouseSource_Mouse; - for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } + for (int i = 0; i < IM_COUNTOF(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_COUNTOF(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } AppAcceptingEvents = true; } @@ -1710,7 +1751,7 @@ void ImGuiIO::ClearInputMouse() key_data->DownDurationPrev = -1.0f; } MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++) + for (int n = 0; n < IM_COUNTOF(MouseDown); n++) { MouseDown[n] = false; MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f; @@ -1934,7 +1975,7 @@ void ImGuiIO::AddFocusEvent(bool focused) ImGuiPlatformIO::ImGuiPlatformIO() { // Most fields are initialized with zero - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); Platform_LocaleDecimalPoint = '.'; } @@ -2028,7 +2069,7 @@ bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; - return ((b1 == b2) && (b2 == b3)); + return (b1 == b2) && (b2 == b3); } void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) @@ -2359,11 +2400,8 @@ ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed) #endif } -// Zero-terminated string hash, with support for ### to reset back to seed value -// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. -// Because this syntax is rarely used we are optimizing for the common case. -// - If we reach ### in the string we discard the hash so far and reset to the seed. -// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) +// Zero-terminated string hash, with support for ### to reset back to seed value. +// e.g. "label###id" outputs the same hash as "id" (and "label" is generally displayed by the UI functions) // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) { @@ -2375,11 +2413,16 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) #endif if (data_size != 0) { - while (data_size-- != 0) + while (data_size-- > 0) { unsigned char c = *data++; if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') + { crc = seed; + data += 2; + data_size -= 2; + continue; + } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else @@ -2392,7 +2435,11 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) while (unsigned char c = *data++) { if (c == '#' && data[0] == '#' && data[1] == '#') + { crc = seed; + data += 2; + continue; + } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else @@ -2410,7 +2457,7 @@ const char* ImHashSkipUncontributingPrefix(const char* label) const char* result = label; while (unsigned char c = *label++) if (c == '#' && label[0] == '#' && label[1] == '#') - result = label - 1; + result = label + 2; return result; } @@ -2433,7 +2480,7 @@ ImFileHandle ImFileOpen(const char* filename, const char* mode) // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314). wchar_t local_temp_stack[FILENAME_MAX]; ImVector local_temp_heap; - if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack)) + if (filename_wsize + mode_wsize > IM_COUNTOF(local_temp_stack)) local_temp_heap.resize(filename_wsize + mode_wsize); wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack; wchar_t* mode_wbuf = filename_wbuf + filename_wsize; @@ -2948,7 +2995,7 @@ ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 CountGrep = 0; if (default_filter) { - ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); + ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf)); Build(); } } @@ -2957,7 +3004,7 @@ bool ImGuiTextFilter::Draw(const char* label, float width) { if (width != 0.0f) ImGui::SetNextItemWidth(width); - bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); + bool value_changed = ImGui::InputText(label, InputBuf, IM_COUNTOF(InputBuf)); if (value_changed) Build(); return value_changed; @@ -3128,7 +3175,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE static bool GetSkipItemForListClipping() { ImGuiContext& g = *GImGui; - return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); + return g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems; } static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) @@ -3185,7 +3232,7 @@ static void ImGuiListClipper_SeekCursorAndSetupPrevLine(ImGuiListClipper* clippe ImGuiListClipper::ImGuiListClipper() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); } ImGuiListClipper::~ImGuiListClipper() @@ -3353,7 +3400,8 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) if (is_nav_request) { data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, nav_off_min, nav_off_max)); - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, nav_off_min, nav_off_max)); + if (!g.NavScoringNoClipRect.IsInverted()) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, nav_off_min, nav_off_max)); } if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); @@ -3560,6 +3608,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) }, // ImGuiStyleVar_ScrollbarPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageRounding) }, // ImGuiStyleVar_ImageRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) }, // ImGuiStyleVar_ImageBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize @@ -3581,7 +3630,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = const ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx) { IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); - IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarsInfo) == ImGuiStyleVar_COUNT); + IM_STATIC_ASSERT(IM_COUNTOF(GStyleVarsInfo) == ImGuiStyleVar_COUNT); return &GStyleVarsInfo[idx]; } @@ -3589,11 +3638,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 1) - { - IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); - return; - } + IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 1, "Calling PushStyleVar() variant with wrong type!"); float* pvar = (float*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); *pvar = val; @@ -3603,11 +3648,7 @@ void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2) - { - IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); - return; - } + IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, "Calling PushStyleVar() variant with wrong type!"); ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); pvar->x = val_x; @@ -3617,11 +3658,7 @@ void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2) - { - IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); - return; - } + IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, "Calling PushStyleVar() variant with wrong type!"); ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); pvar->y = val_y; @@ -3631,11 +3668,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) { ImGuiContext& g = *GImGui; const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2) - { - IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); - return; - } + IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, "Calling PushStyleVar() variant with wrong type!"); ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); *pvar = val; @@ -3873,13 +3906,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con // We can now claim the space between pos_max.x and ellipsis_max.x const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f); - float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; - while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) - { - // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text) - text_end_ellipsis--; - text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte - } + const float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; // Render text, render ellipsis RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); @@ -3922,6 +3949,15 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) } } +void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding) +{ + if (bb.Min.x + 1 >= bb.Max.x) + return; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + RenderRectFilledInRangeH(window->DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding); +} + void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags) { ImGuiContext& g = *GImGui; @@ -3931,6 +3967,9 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl return; if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav)) return; + + // We don't early out on 'window->Flags & ImGuiWindowFlags_NoNavInputs' because it would be inconsistent with + // other code directly checking NavCursorVisible. Instead we aim for NavCursorVisible to always be false. ImGuiWindow* window = g.CurrentWindow; if (window->DC.NavHideHighlightOneFrame) return; @@ -4175,6 +4214,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac... // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this.. + ConfigNavEnableTabbing = true; ConfigNavWindowingWithGamepad = true; ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab); ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab); @@ -4313,7 +4353,7 @@ void ImGui::Initialize() TableSettingsAddSettingsHandler(); // Setup default localization table - LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS)); + LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_COUNTOF(GLocalizationEntriesEnUS)); // Setup default ImGuiPlatformIO clipboard/IME handlers. g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations @@ -4327,7 +4367,7 @@ void ImGui::Initialize() g.Viewports.push_back(viewport); g.TempBuffer.resize(1024 * 3 + 1, 0); - // Build KeysMayBeCharInput[] lookup table (1 bool per named key) + // Build KeysMayBeCharInput[] lookup table (1 bit per named key) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9) || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period @@ -4442,6 +4482,13 @@ void ImGui::Shutdown() g.Initialized = false; } +// When using multiple context it can be helpful to give name a name. +// (A) Will be visible in debugger, (B) Will be included in all IMGUI_DEBUG_LOG() calls, (C) Should be <= 15 characters long. +void ImGui::SetContextName(ImGuiContext* ctx, const char* name) +{ + ImStrncpy(ctx->ContextName, name, IM_COUNTOF(ctx->ContextName)); +} + // No specific ordering/dependency support, will see as needed ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) { @@ -4479,7 +4526,7 @@ void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL) { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); Ctx = ctx; Name = ImStrdup(name); NameBufLen = (int)ImStrlen(name) + 1; @@ -4520,13 +4567,13 @@ static void SetCurrentWindow(ImGuiWindow* window) g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking if (window) { - bool backup_skip_items = window->SkipItems; - window->SkipItems = false; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) { ImGuiViewport* viewport = window->Viewport; g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity() } + const bool backup_skip_items = window->SkipItems; + window->SkipItems = false; ImGui::UpdateCurrentFontSize(0.0f); window->SkipItems = backup_skip_items; ImGui::NavUpdateCurrentWindowIsScrollPushableX(); @@ -4767,7 +4814,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Test if another item is active (e.g. being dragged) const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) + if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && !g.ActiveIdFromShortcut) { // When ActiveId == MoveId it means that either: // - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag) @@ -5011,7 +5058,7 @@ void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr IM_UNUSED(ptr); if (entry->FrameCount != frame_count) { - info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf); + info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_COUNTOF(info->LastEntriesBuf); entry = &info->LastEntriesBuf[info->LastEntriesIdx]; entry->FrameCount = frame_count; entry->AllocCount = entry->FreeCount = 0; @@ -5030,10 +5077,11 @@ void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr } } +// A conformant backend should return NULL on failure (e.g. clipboard data is not text). const char* ImGui::GetClipboardText() { ImGuiContext& g = *GImGui; - return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : ""; + return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : NULL; } void ImGui::SetClipboardText(const char* text) @@ -5096,7 +5144,7 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw { // Create the draw list on demand, because they are not frequently used for all viewports ImGuiContext& g = *GImGui; - IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists)); + IM_ASSERT(drawlist_no < IM_COUNTOF(viewport->BgFgDrawLists)); ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no]; if (draw_list == NULL) { @@ -5164,7 +5212,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) g.MovingWindow = window; } -// This is not 100% symetric with StartMouseMovingWindow(). +// This is not 100% symmetric with StartMouseMovingWindow(). // We do NOT clear ActiveID, because: // - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired. // - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item). @@ -5245,7 +5293,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() StartMouseMovingWindow(hovered_window); //-V595 // FIXME: In principle we might be able to call StopMouseMovingWindow() below. - // Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symetrical, at the later doesn't clear ActiveId. + // Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symmetrical, at the later doesn't clear ActiveId. // Cancel moving if clicked outside of title bar if ((hovered_window->BgClickFlags & ImGuiWindowBgClickFlags_Move) == 0) // set by io.ConfigWindowsMoveFromTitleBarOnly @@ -5283,7 +5331,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() static bool IsWindowActiveAndVisible(ImGuiWindow* window) { - return (window->Active) && (!window->Hidden); + return window->Active && !window->Hidden; } // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) @@ -5319,7 +5367,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos) const bool has_open_modal = (modal_window != NULL); int mouse_earliest_down = -1; bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) { if (io.MouseClicked[i]) { @@ -5422,8 +5470,8 @@ void ImGui::NewFrame() // Calculate frame-rate for the user, as a purely luxurious feature g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; - g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); + g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_COUNTOF(g.FramerateSecPerFrame); + g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_COUNTOF(g.FramerateSecPerFrame)); g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; // Process input queue (trickle as many events as possible), turn events into writes to IO structure @@ -5685,7 +5733,7 @@ static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) return d; if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) return d; - return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); + return a->BeginOrderWithinParent - b->BeginOrderWithinParent; } static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) @@ -5732,10 +5780,10 @@ static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) { int n = builder->Layers[0]->Size; int full_size = n; - for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++) + for (int i = 1; i < IM_COUNTOF(builder->Layers); i++) full_size += builder->Layers[i]->Size; builder->Layers[0]->resize(full_size); - for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++) + for (int layer_n = 1; layer_n < IM_COUNTOF(builder->Layers); layer_n++) { ImVector* layer = builder->Layers[layer_n]; if (layer->empty()) @@ -5879,11 +5927,7 @@ void ImGui::EndFrame() // Don't process EndFrame() multiple times. if (g.FrameCountEnded == g.FrameCount) return; - if (!g.WithinFrameScope) - { - IM_ASSERT_USER_ERROR(g.WithinFrameScope, "Forgot to call ImGui::NewFrame()?"); - return; - } + IM_ASSERT_USER_ERROR_RET(g.WithinFrameScope, "Forgot to call ImGui::NewFrame()?"); CallContextHooks(&g, ImGuiContextHookType_EndFramePre); @@ -6011,7 +6055,7 @@ void ImGui::Render() if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); } - for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) + for (int n = 0; n < IM_COUNTOF(windows_to_render_top_most); n++) if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); @@ -6161,7 +6205,7 @@ bool ImGui::IsItemDeactivated() ImGuiContext& g = *GImGui; if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated) return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; - return (g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount); + return g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount; } bool ImGui::IsItemDeactivatedAfterEdit() @@ -6293,6 +6337,12 @@ ImVec2 ImGui::GetItemRectSize() return g.LastItemData.Rect.GetSize(); } +ImGuiItemFlags ImGui::GetItemFlags() +{ + ImGuiContext& g = *GImGui; + return g.LastItemData.ItemFlags; +} + // Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'. // ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details! bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) @@ -6323,10 +6373,8 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!"); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) - // child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; - //if (window_flags & ImGuiWindowFlags_NavFlattened) - // child_flags |= ImGuiChildFlags_NavFlattened; + //if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) { child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; } + //if (window_flags & ImGuiWindowFlags_NavFlattened) { child_flags |= ImGuiChildFlags_NavFlattened; } #endif if (child_flags & ImGuiChildFlags_AutoResizeX) child_flags &= ~ImGuiChildFlags_ResizeX; @@ -6577,13 +6625,13 @@ static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window) ImVec2 size_min; if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) { - size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f; - size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f; + size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE; + size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE; } else { - size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f; - size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f; + size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE; + size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE; } // Reduce artifacts with very small windows @@ -6653,7 +6701,7 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont size_desired[ImGuiAxis_Y] = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y; // Determine maximum window size - // Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction + // Child windows are laid within their parent (unless they are also popups/menus) and thus have no restriction ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f; if (window->Flags & ImGuiWindowFlags_Tooltip) @@ -6959,7 +7007,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, int* border_hove if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f) { const float NAV_RESIZE_SPEED = 600.0f; - const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y); + const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * GetScale(); g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step; g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size g.NavWindowingToggleLayer = false; @@ -7082,7 +7130,13 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } if (override_alpha) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + if (bg_col & IM_COL32_A_MASK) + { + ImRect bg_rect(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size); + ImDrawFlags bg_rounding_flags = (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom; + ImDrawList* bg_draw_list = window->DrawList; + bg_draw_list->AddRectFilled(bg_rect.Min, bg_rect.Max, bg_col, window_rounding, bg_rounding_flags); + } } // Title bar @@ -7476,11 +7530,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. bool window_title_visible_elsewhere = false; - if (g.NavWindowingListWindow != NULL && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using Ctrl+Tab + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->WasActive && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using Ctrl+Tab window_title_visible_elsewhere = true; if (flags & ImGuiWindowFlags_ChildMenu) window_title_visible_elsewhere = true; - if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) + if ((window_title_visible_elsewhere || window_just_activated_by_user) && !window_just_created && strcmp(name, window->Name) != 0) { size_t buf_len = (size_t)window->NameBufLen; window->Name = ImStrdupcpy(window->Name, &buf_len, name); @@ -7929,13 +7983,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; // Default item width. Make it proportional to window size if window manually resizes - if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) - window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f); + const bool is_resizable_window = (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)); + if (is_resizable_window) + window->DC.ItemWidthDefault = ImTrunc(window->Size.x * 0.65f); else - window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f); - window->DC.ItemWidth = window->ItemWidthDefault; - window->DC.TextWrapPos = -1.0f; // disabled + window->DC.ItemWidthDefault = ImTrunc(g.FontSize * 16.0f); + window->DC.ItemWidth = window->DC.ItemWidthDefault; window->DC.ItemWidthStack.resize(0); + window->DC.TextWrapPos = -1.0f; // Disabled window->DC.TextWrapPosStack.resize(0); if (flags & ImGuiWindowFlags_Modal) window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg)); @@ -7972,7 +8027,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) g.TooltipPreviousWindow = window; // Set default BgClickFlags - // This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overriden by user code. + // This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overridden by user code. // FIXME: The general intent is that we will later expose config options to default to enable scrolling + select scrolling mouse button. window->BgClickFlags = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->BgClickFlags : (g.IO.ConfigWindowsMoveFromTitleBarOnly ? ImGuiWindowBgClickFlags_None : ImGuiWindowBgClickFlags_Move); @@ -8155,11 +8210,7 @@ void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) void ImGui::PopItemFlag() { ImGuiContext& g = *GImGui; - if (g.ItemFlagsStack.Size <= 1) - { - IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!"); - return; - } + IM_ASSERT_USER_ERROR_RET(g.ItemFlagsStack.Size > 1, "Calling PopItemFlag() too many times!"); g.ItemFlagsStack.pop_back(); g.CurrentItemFlags = g.ItemFlagsStack.back(); } @@ -8189,11 +8240,7 @@ void ImGui::BeginDisabled(bool disabled) void ImGui::EndDisabled() { ImGuiContext& g = *GImGui; - if (g.DisabledStackSize <= 0) - { - IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!"); - return; - } + IM_ASSERT_USER_ERROR_RET(g.DisabledStackSize > 0, "Calling EndDisabled() too many times!"); g.DisabledStackSize--; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; //PopItemFlag(); @@ -8220,30 +8267,27 @@ void ImGui::BeginDisabledOverrideReenable() void ImGui::EndDisabledOverrideReenable() { ImGuiContext& g = *GImGui; - g.DisabledStackSize--; IM_ASSERT(g.DisabledStackSize > 0); + g.DisabledStackSize--; g.ItemFlagsStack.pop_back(); g.CurrentItemFlags = g.ItemFlagsStack.back(); g.Style.Alpha = g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup; } -void ImGui::PushTextWrapPos(float wrap_pos_x) +// ATTENTION THIS IS IN LEGACY LOCAL SPACE. +void ImGui::PushTextWrapPos(float wrap_local_pos_x) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); - window->DC.TextWrapPos = wrap_pos_x; + window->DC.TextWrapPos = wrap_local_pos_x; } void ImGui::PopTextWrapPos() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (window->DC.TextWrapPosStack.Size <= 0) - { - IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!"); - return; - } + IM_ASSERT_USER_ERROR_RET(window->DC.TextWrapPosStack.Size > 0, "Calling PopTextWrapPos() too many times!"); window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); window->DC.TextWrapPosStack.pop_back(); } @@ -8642,11 +8686,7 @@ void ImGui::PushFocusScope(ImGuiID id) void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; - if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack) - { - IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!"); - return; - } + IM_ASSERT_USER_ERROR_RET(g.FocusScopeStack.Size > g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack, "Calling PopFocusScope() too many times!"); g.FocusScopeStack.pop_back(); g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0; } @@ -8859,7 +8899,7 @@ void ImGui::UpdateFontsNewFrame() // Apply default font size the first time ImFont* font = ImGui::GetDefaultFont(); if (g.Style.FontSizeBase <= 0.0f) - g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE); + g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE_BASE); // Set initial font g.Font = font; @@ -8961,16 +9001,6 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) g.Style.FontSizeBase = g.FontSizeBase; - // Early out to avoid hidden window keeping bakes referenced and out of GC reach. - // However this would leave a pretty subtle and damning error surface area if g.FontBaked was mismatching. - // FIXME: perhaps g.FontSize should be updated? - if (window != NULL && window->SkipItems) - { - ImGuiTable* table = g.CurrentTable; - if (table == NULL || (table->CurrentColumn != -1 && table->Columns[table->CurrentColumn].IsSkipItems == false)) // See 8465#issuecomment-2951509561 and #8865. Ideally the SkipItems=true in tables would be amended with extra data. - return; - } - // Restoring is pretty much only used by PopFont() float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f; if (final_size == 0.0f) @@ -8979,7 +9009,7 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) // Global scale factors final_size *= g.Style.FontScaleMain; // Main global scale factor - final_size *= g.Style.FontScaleDpi; // Per-monitor/viewport DPI scale factor, automatically updated when io.ConfigDpiScaleFonts is enabled. + final_size *= g.Style.FontScaleDpi; // Per-monitor/viewport DPI scale factor (in docking branch: automatically updated when io.ConfigDpiScaleFonts is enabled). // Window scale (mostly obsolete now) if (window != NULL) @@ -9000,10 +9030,24 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX); if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)) g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity; + g.FontSize = final_size; + g.DrawListSharedData.FontSize = g.FontSize; + + // Early out to avoid hidden window keeping bakes referenced and out of GC reach. + // - However this leave a pretty subtle and damning error surface area if g.FontBaked was mismatching. + // Probably needs to be reevaluated into e.g. setting g.FontBaked = nullptr to mark it as dirty. + // - Note that 'PushFont(); Begin(); End(); PopFont()' from within any collapsed window is not compromised, because Begin() calls SetCurrentWindow()->...->UpdateCurrentSize() + if (window != NULL && window->SkipItems) + { + ImGuiTable* table = g.CurrentTable; + const bool allow_early_out = table == NULL || (table->CurrentColumn != -1 && table->Columns[table->CurrentColumn].IsSkipItems == false); // See 8465#issuecomment-2951509561 and #8865. Ideally the SkipItems=true in tables would be amended with extra data. + if (allow_early_out) + return; + } + g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size) : NULL; g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f; - g.DrawListSharedData.FontSize = g.FontSize; g.DrawListSharedData.FontScale = g.FontBakedScale; } @@ -9037,11 +9081,7 @@ void ImGui::PushFont(ImFont* font, float font_size_base) void ImGui::PopFont() { ImGuiContext& g = *GImGui; - if (g.FontStack.Size <= 0) - { - IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!"); - return; - } + IM_ASSERT_USER_ERROR_RET(g.FontStack.Size > 0, "Calling PopFont() too many times!"); ImFontStackData* font_stack_data = &g.FontStack.back(); SetCurrentFont(font_stack_data->Font, font_stack_data->FontSizeBeforeScaling, font_stack_data->FontSizeAfterScaling); g.FontStack.pop_back(); @@ -9181,11 +9221,7 @@ ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed) void ImGui::PopID() { ImGuiWindow* window = GImGui->CurrentWindow; - if (window->IDStack.Size <= 1) - { - IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!"); - return; - } + IM_ASSERT_USER_ERROR_RET(window->IDStack.Size > 1, "Calling PopID() too many times!"); window->IDStack.pop_back(); } @@ -9339,7 +9375,7 @@ static const char* const GKeyNames[] = "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY", "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names. }; -IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); +IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_COUNTOF(GKeyNames)); const char* ImGui::GetKeyName(ImGuiKey key) { @@ -9363,7 +9399,7 @@ const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord) const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (IsLRModKey(key)) key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift" - ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s", + ImFormatString(g.TempKeychordName, IM_COUNTOF(g.TempKeychordName), "%s%s%s%s%s", (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", (key_chord & ImGuiMod_Shift) ? "Shift+" : "", (key_chord & ImGuiMod_Alt) ? "Alt+" : "", @@ -9387,7 +9423,7 @@ int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, flo if (t0 >= t1) return 0; if (repeat_rate <= 0.0f) - return (t0 < repeat_delay) && (t1 >= repeat_delay); + return t0 < repeat_delay && t1 >= repeat_delay; const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); const int count = count_t1 - count_t0; @@ -9771,14 +9807,14 @@ bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id) bool ImGui::IsMouseDown(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array. } bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array. } @@ -9790,7 +9826,7 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) return false; const float t = g.IO.MouseDownDuration[button]; @@ -9812,14 +9848,14 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGui bool ImGui::IsMouseReleased(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any) } bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id) } @@ -9829,7 +9865,7 @@ bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]); return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay); } @@ -9837,21 +9873,21 @@ bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); } bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id); } int ImGui::GetMouseClickedCount(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button]; } @@ -9878,7 +9914,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (lock_threshold < 0.0f) lock_threshold = g.IO.MouseDragThreshold; return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; @@ -9887,7 +9923,7 @@ bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_thresho bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (!g.IO.MouseDown[button]) return false; return IsMouseDragPastThreshold(button, lock_threshold); @@ -9934,7 +9970,7 @@ bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) bool ImGui::IsAnyMouseDown() { ImGuiContext& g = *GImGui; - for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) + for (int n = 0; n < IM_COUNTOF(g.IO.MouseDown); n++) if (g.IO.MouseDown[n]) return true; return false; @@ -9946,7 +9982,7 @@ bool ImGui::IsAnyMouseDown() ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (lock_threshold < 0.0f) lock_threshold = g.IO.MouseDragThreshold; if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) @@ -9959,7 +9995,7 @@ ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr g.IO.MouseClickedPos[button] = g.IO.MousePos; } @@ -10102,7 +10138,7 @@ static void ImGui::UpdateMouseInputs() if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavHighlightItemUnderNav = false; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) { io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; io.MouseClickedCount[i] = 0; // Will be filled below @@ -10309,7 +10345,7 @@ void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse) static const char* GetInputSourceName(ImGuiInputSource source) { const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" }; - IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); + IM_ASSERT(IM_COUNTOF(input_source_names) == ImGuiInputSource_COUNT); if (source < 0 || source >= ImGuiInputSource_COUNT) return "Unknown"; return input_source_names[source]; @@ -10317,7 +10353,7 @@ static const char* GetInputSourceName(ImGuiInputSource source) static const char* GetMouseSourceName(ImGuiMouseSource source) { const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" }; - IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT); + IM_ASSERT(IM_COUNTOF(mouse_source_names) == ImGuiMouseSource_COUNT); if (source < 0 || source >= ImGuiMouseSource_COUNT) return "Unknown"; return mouse_source_names[source]; @@ -10325,11 +10361,12 @@ static const char* GetMouseSourceName(ImGuiMouseSource source) static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) { ImGuiContext& g = *GImGui; + char buf[5]; if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; } if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } + if (e->Type == ImGuiInputEventType_Text) { ImTextCharToUtf8(buf, e->Text.Char); IMGUI_DEBUG_LOG_IO("[io] %s: Text: '%s' (U+%08X)\n", prefix, buf, e->Text.Char); return; } if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } } #endif @@ -10458,6 +10495,8 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) #endif // Remaining events will be processed on the next frame + // FIXME-MULTITHREADING: io.AddKeyEvent() etc. calls are mostly thread-safe apart from the fact they push to this + // queue which may be resized here. Could potentially rework this to narrow down the section needing a mutex? (#5772) if (event_n == g.InputEventsQueue.Size) g.InputEventsQueue.resize(0); else @@ -10506,7 +10545,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_id == ImGuiKeyOwner_Any) - return (owner_data->LockThisFrame == false); + return owner_data->LockThisFrame == false; // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things. @@ -11424,7 +11463,7 @@ void ImGui::PushItemWidth(float item_width) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width - window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); + window->DC.ItemWidth = (item_width == 0.0f ? window->DC.ItemWidthDefault : item_width); g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth; } @@ -11950,7 +11989,7 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d"; char window_name[32]; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, g.TooltipOverrideCount); + ImFormatString(window_name, IM_COUNTOF(window_name), window_name_template, g.TooltipOverrideCount); ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; Begin(window_name, NULL, flags | extra_window_flags); // 2023-03-09: Added bool return value to the API, but currently always returning true. @@ -12138,7 +12177,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type). popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); - popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); + popup_ref.OpenPopupPos = NavCalcPreferredRefPos(ImGuiWindowFlags_Popup); popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id); @@ -12306,7 +12345,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags) char name[20]; IM_ASSERT((extra_window_flags & ImGuiWindowFlags_ChildMenu) == 0); // Use BeginPopupMenuEx() - ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame + ImFormatString(name, IM_COUNTOF(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) @@ -12326,7 +12365,7 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags ext char name[128]; IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu); - ImFormatString(name, IM_ARRAYSIZE(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth + ImFormatString(name, IM_COUNTOF(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); @@ -12389,11 +12428,7 @@ void ImGui::EndPopup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || g.BeginPopupStack.Size == 0) - { - IM_ASSERT_USER_ERROR(0, "Calling EndPopup() too many times or in wrong window!"); - return; - } + IM_ASSERT_USER_ERROR_RET((window->Flags & ImGuiWindowFlags_Popup) != 0 && g.BeginPopupStack.Size > 0, "Calling EndPopup() in wrong window!"); // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests) if (g.NavWindow == window) @@ -12407,13 +12442,26 @@ void ImGui::EndPopup() g.WithinEndChildID = backup_within_end_child_id; } +ImGuiMouseButton ImGui::GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags) +{ +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if ((flags & ImGuiPopupFlags_InvalidMask_) != 0) // 1,2 --> ImGuiMouseButton_Right, ImGuiMouseButton_Middle + return (flags & ImGuiPopupFlags_InvalidMask_); +#else + IM_ASSERT((flags & ImGuiPopupFlags_InvalidMask_) == 0); +#endif + if (flags & ImGuiPopupFlags_MouseButtonMask_) + return ((flags & ImGuiPopupFlags_MouseButtonMask_) >> ImGuiPopupFlags_MouseButtonShift_) - 1; + return ImGuiMouseButton_Right; // Default == 1 +} + // Helper to open a popup if mouse button is released over the item // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); + ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) { ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! @@ -12446,7 +12494,7 @@ bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flag return false; ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); + ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); @@ -12459,7 +12507,7 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); + ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) OpenPopupEx(id, popup_flags); @@ -12473,7 +12521,7 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flag if (!str_id) str_id = "void_context"; ImGuiID id = window->GetID(str_id); - int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); + ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) if (GetTopMostPopupModal() == NULL) OpenPopupEx(id, popup_flags); @@ -12603,9 +12651,9 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin(). IM_ASSERT(g.CurrentWindow == window); const float scale = g.Style.MouseCursorScale; - const ImVec2 ref_pos = NavCalcPreferredRefPos(); + const ImVec2 ref_pos = NavCalcPreferredRefPos(ImGuiWindowFlags_Tooltip); - if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse) + if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource(ImGuiWindowFlags_Tooltip) == ImGuiInputSource_Mouse) { ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size); if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size))) @@ -12686,7 +12734,7 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) if (flags & ImGuiFocusedFlags_ChildWindows) return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); else - return (ref_window == cur_window); + return ref_window == cur_window; } static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) @@ -12744,6 +12792,7 @@ void ImGui::BringWindowToFocusFront(ImGuiWindow* window) } // Note technically focus related but rather adjacent and close to BringWindowToFocusFront() +// FIXME-FOCUS: Could opt-in/opt-out enable modal check like in FocusWindow(). void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; @@ -12904,7 +12953,9 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind void ImGui::SetNavCursorVisible(bool visible) { ImGuiContext& g = *GImGui; - if (g.IO.ConfigNavCursorVisibleAlways) + if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + visible = false; + else if (g.IO.ConfigNavCursorVisibleAlways) visible = true; g.NavCursorVisible = visible; } @@ -12913,7 +12964,13 @@ void ImGui::SetNavCursorVisible(bool visible) void ImGui::SetNavCursorVisibleAfterMove() { ImGuiContext& g = *GImGui; - if (g.IO.ConfigNavCursorVisibleAuto) + if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + g.NavCursorVisible = false; + else if (g.NavInputSource == ImGuiInputSource_Keyboard && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) == 0) + g.NavCursorVisible = false; + else if (g.NavInputSource == ImGuiInputSource_Gamepad && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + g.NavCursorVisible = false; + else if (g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = true; g.NavHighlightItemUnderNav = g.NavMousePosDirty = true; } @@ -12977,6 +13034,8 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); + if (id == g.ActiveIdIsAlive) + g.NavIdIsAlive = true; if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) g.NavHighlightItemUnderNav = true; @@ -13077,7 +13136,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb) { if (quadrant == move_dir) { - ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); + ImFormatString(buf, IM_COUNTOF(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80)); draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200)); @@ -13088,7 +13147,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb) const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space)); if (debug_hovering || debug_tty) { - ImFormatString(buf, IM_ARRAYSIZE(buf), + ImFormatString(buf, IM_COUNTOF(buf), "d-box (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]); if (debug_hovering) @@ -13404,7 +13463,7 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wra // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it: // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest(). - if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main) + if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == window->DC.NavLayerCurrent) g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags; } @@ -13491,31 +13550,29 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) } } -static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource() +// Positioning logic altered slightly for remote activation: for Popup we want to use item rect, for Tooltip we leave things alone. (#9138) +// When calling for ImGuiWindowFlags_Popup we use LastItemData. +static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; + const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; + if ((window_type & ImGuiWindowFlags_Popup) && activated_shortcut) + return ImGuiInputSource_Keyboard; - // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag. - if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut) + if (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) return ImGuiInputSource_Mouse; else return ImGuiInputSource_Keyboard; // or Nav in general } -static ImVec2 ImGui::NavCalcPreferredRefPos() +static ImVec2 ImGui::NavCalcPreferredRefPos(ImGuiWindowFlags window_type) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; - ImGuiInputSource source = NavCalcPreferredRefPosSource(); - - const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; - - if (source != ImGuiInputSource_Mouse && !activated_shortcut && window == NULL) - source = ImGuiInputSource_Mouse; + ImGuiInputSource source = NavCalcPreferredRefPosSource(window_type); - // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag. if (source == ImGuiInputSource_Mouse) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) @@ -13527,8 +13584,9 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() else { // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; ImRect ref_rect; - if (activated_shortcut) + if (activated_shortcut && (window_type & ImGuiWindowFlags_Popup)) ref_rect = g.LastItemData.NavRect; else if (window != NULL) ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); @@ -13727,7 +13785,7 @@ static void ImGui::NavUpdate() // Update mouse position if requested // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - TeleportMousePos(NavCalcPreferredRefPos()); + TeleportMousePos(NavCalcPreferredRefPos(ImGuiWindowFlags_Popup)); // [DEBUG] g.NavScoringDebugCount = 0; @@ -13859,7 +13917,7 @@ void ImGui::NavUpdateCreateMoveRequest() IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer); g.NavInitRequest = g.NavInitRequestFromMove = true; g.NavInitResult.ID = 0; - if (g.IO.ConfigNavCursorVisibleAuto) + if (g.IO.ConfigNavCursorVisibleAuto) // NO check for _NoNavInputs here as we assume MoveRequests cannot be created. g.NavCursorVisible = true; } @@ -13924,7 +13982,7 @@ void ImGui::NavUpdateCreateTabbingRequest() ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; IM_ASSERT(g.NavMoveDir == ImGuiDir_None); - if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) + if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs) || !g.ConfigNavEnableTabbing) return; const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt; @@ -14220,9 +14278,13 @@ static void ImGui::NavUpdateCreateWrappingRequest() const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; + + // Menu layer does not maintain scrolling / content size (#9178) + ImVec2 wrap_size = (g.NavLayer == ImGuiNavLayer_Menu) ? window->Size : window->ContentSize + window->WindowPadding; + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { - bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; + bb_rel.Min.x = bb_rel.Max.x = wrap_size.x; if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row @@ -14242,7 +14304,7 @@ static void ImGui::NavUpdateCreateWrappingRequest() } if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) { - bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y; + bb_rel.Min.y = bb_rel.Max.y = wrap_size.y; if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column @@ -14482,7 +14544,7 @@ static void ImGui::NavUpdateWindowing() if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; - const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); + const float move_step = NAV_MOVE_SPEED * io.DeltaTime * GetScale(); g.NavWindowingAccumDeltaPos += nav_move_dir * move_step; g.NavHighlightItemUnderNav = true; ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos); @@ -14550,13 +14612,12 @@ void ImGui::NavUpdateWindowingOverlay() if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) return; - if (g.NavWindowingListWindow == NULL) - g.NavWindowingListWindow = FindWindowByName("##NavWindowingOverlay"); const ImGuiViewport* viewport = GetMainViewport(); SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); + g.NavWindowingListWindow = g.CurrentWindow; if (g.ContextName[0] != 0) SeparatorText(g.ContextName); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) @@ -14758,7 +14819,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s cond = ImGuiCond_Always; IM_ASSERT(type != NULL); - IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); + IM_ASSERT(ImStrlen(type) < IM_COUNTOF(payload.DataType) && "Payload type can be at most 32 characters long"); IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() @@ -14766,7 +14827,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) { // Copy payload - ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); + ImStrncpy(payload.DataType, type, IM_COUNTOF(payload.DataType)); g.DragDropPayloadBufHeap.resize(0); if (data_size > sizeof(g.DragDropPayloadBufLocal)) { @@ -15033,7 +15094,7 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* if (!text_end) text_end = FindRenderedTextEnd(text, text_end); - const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1); + const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + ImMax(g.Style.FramePadding.y, g.Style.ItemSpacing.y) + 1); if (ref_pos) g.LogLinePosY = ref_pos->y; if (log_new_line) @@ -15209,7 +15270,7 @@ void ImGui::LogButtons() const bool log_to_file = Button("Log To File"); SameLine(); const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); PushItemFlag(ImGuiItemFlags_NoTabStop, true); - SetNextItemWidth(80.0f); + SetNextItemWidth(CalcTextSize("999").x); SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); PopItemFlag(); PopID(); @@ -16037,7 +16098,7 @@ void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) if (!IsItemVisible()) return; draw_list->PushClipRect(board_min, board_max, true); - for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++) + for (int n = 0; n < IM_COUNTOF(keys_to_display); n++) { const KeyLayoutData* key_data = &keys_to_display[n]; ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y); @@ -16124,23 +16185,21 @@ void ImGui::UpdateDebugToolFlashStyleColor() DebugFlashStyleColorStop(); } -static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id) +ImU64 ImGui::DebugTextureIDToU64(ImTextureID tex_id) { - union { void* ptr; int integer; } tex_id_opaque; - memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id))); - if (sizeof(tex_id) >= sizeof(void*)) - ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr); - else - ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer); - return buf; + ImU64 v = 0; + memcpy(&v, &tex_id, ImMin(sizeof(ImU64), sizeof(ImTextureID))); + return v; } static const char* FormatTextureRefForDebugDisplay(char* buf, int buf_size, ImTextureRef tex_ref) { + char* buf_p = buf; char* buf_end = buf + buf_size; if (tex_ref._TexData != NULL) - buf += ImFormatString(buf, buf_end - buf, "#%03d: ", tex_ref._TexData->UniqueID); - return FormatTextureIDForDebugDisplay(buf, (int)(buf_end - buf), tex_ref.GetTexID()); // Calling TexRef::GetTexID() to avoid assert of cmd->GetTexID() + buf_p += ImFormatString(buf_p, buf_end - buf_p, "#%03d: ", tex_ref._TexData->UniqueID); + ImFormatString(buf_p, buf_end - buf_p, "0x%X", ImGui::DebugTextureIDToU64(tex_ref.GetTexID())); + return buf; } #ifdef IMGUI_ENABLE_FREETYPE @@ -16326,9 +16385,9 @@ void ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRe } PopStyleVar(); - char texid_desc[30]; + char texref_desc[30]; Text("Status = %s (%d), Format = %s (%d), UseColors = %d", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors); - Text("TexID = %s, BackendUserData = %p", FormatTextureRefForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), tex->GetTexRef()), tex->BackendUserData); + Text("TexRef = %s, BackendUserData = %p", FormatTextureRefForDebugDisplay(texref_desc, IM_COUNTOF(texref_desc), tex->GetTexRef()), tex->BackendUserData); TreePop(); } PopID(); @@ -16483,7 +16542,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImRect r = Funcs::GetTableRect(table, rect_n, column_n); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); + ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); @@ -16492,7 +16551,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else { ImRect r = Funcs::GetTableRect(table, rect_n, -1); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); + ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); @@ -16516,7 +16575,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { static char buf[64] = ""; SetNextItemWidth(-FLT_MIN); - InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf)); + InputText("##DebugTextEncodingBuf", buf, IM_COUNTOF(buf)); if (buf[0] != 0) DebugTextEncoding(buf); } @@ -16710,7 +16769,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount); if (SmallButton("GC now")) { g.GcCompactAll = true; } Text("Recent frames with allocations:"); - int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf); + int buf_size = IM_COUNTOF(info->LastEntriesBuf); for (int n = buf_size - 1; n >= 0; n--) { ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size]; @@ -16747,7 +16806,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else Text("Mouse pos: "); Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - int count = IM_ARRAYSIZE(io.MouseDown); + int count = IM_COUNTOF(io.MouseDown); Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); } Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); } @@ -16878,7 +16937,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow)) { char buf[32]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); + ImFormatString(buf, IM_COUNTOF(buf), "%d", window->BeginOrderWithinContext); float font_size = GetFontSize(); draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); @@ -17029,9 +17088,9 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con } char texid_desc[30]; - FormatTextureRefForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TexRef); + FormatTextureRefForDebugDisplay(texid_desc, IM_COUNTOF(texid_desc), pcmd->TexRef); char buf[300]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", + ImFormatString(buf, IM_COUNTOF(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list) @@ -17053,7 +17112,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con } // Display vertex information summary. Hover to get all triangles drawn in wire-frame - ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); + ImFormatString(buf, IM_COUNTOF(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); Selectable(buf); if (IsItemHovered() && fg_draw_list) DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false); @@ -17064,7 +17123,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con while (clipper.Step()) for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) { - char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf); + char* buf_p = buf, * buf_end = buf + IM_COUNTOF(buf); ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_i++) { @@ -17197,6 +17256,9 @@ void ImGui::DebugNodeFont(ImFont* font) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; Text("Loader: '%s'", loader->Name ? loader->Name : "N/A"); + + //if (DragFloat("ExtraSizeScale", &src->ExtraSizeScale, 0.01f, 0.10f, 2.0f)) + // ImFontAtlasFontRebuildOutput(atlas, font); #ifdef IMGUI_ENABLE_FREETYPE if (loader->Name != NULL && strcmp(loader->Name, "FreeType") == 0) { @@ -17255,7 +17317,7 @@ void ImGui::DebugNodeFont(ImFont* font) ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n]; if (baked->OwnerFont != font) continue; - PushID(baked_n); + PushID(baked->BakedId); if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : "")) { if (SmallButton("Load all")) @@ -17369,7 +17431,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. char buf[256]; char* p = buf; - const char* buf_end = buf + IM_ARRAYSIZE(buf); + const char* buf_end = buf + IM_COUNTOF(buf); const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) @@ -17527,7 +17589,7 @@ void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int wi if (window->ParentWindowInBeginStack != parent_in_begin_stack) continue; char buf[20]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext); + ImFormatString(buf, IM_COUNTOF(buf), "[%04d] Window", window->BeginOrderWithinContext); //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); DebugNodeWindow(window, buf); TreePush(buf); @@ -17558,14 +17620,23 @@ void ImGui::DebugLogV(const char* fmt, va_list args) g.DebugLogBuf.appendf("[%05d] ", g.FrameCount); g.DebugLogBuf.appendfv(fmt, args); g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size()); + + const char* str = g.DebugLogBuf.begin() + old_size; if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) - IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size); + IMGUI_DEBUG_PRINTF("%s", str); +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) + if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToDebugger) + { + ::OutputDebugStringA("[imgui] "); + ::OutputDebugStringA(str); + } +#endif #ifdef IMGUI_ENABLE_TEST_ENGINE // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically const int new_size = g.DebugLogBuf.size(); const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n'); if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine) - IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size); + IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), str); #endif } @@ -17645,6 +17716,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) if (BeginPopup("Outputs")) { CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY); + CheckboxFlags("OutputToDebugger", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToDebugger); #ifndef IMGUI_ENABLE_TEST_ENGINE BeginDisabled(); #endif @@ -17951,7 +18023,7 @@ static const char* DebugItemPathQuery_GetResultAsPath(ImGuiDebugItemPathQuery* q for (int stack_n = 0; stack_n < query->Results.Size; stack_n++) { char level_desc[256]; - DebugItemPathQuery_FormatLevelInfo(query, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc)); + DebugItemPathQuery_FormatLevelInfo(query, stack_n, false, level_desc, IM_COUNTOF(level_desc)); buf->append(stack_n == 0 ? "//" : "/"); for (const char* p = level_desc; *p != 0; ) { diff --git a/core/deps/imgui/imgui.h b/core/deps/imgui/imgui.h old mode 100644 new mode 100755 index 0e5a436d0c..01f61ecd37 --- a/core/deps/imgui/imgui.h +++ b/core/deps/imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.5 +// dear imgui, v1.92.6 // (headers) // Help: @@ -29,8 +29,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.5" -#define IMGUI_VERSION_NUM 19250 +#define IMGUI_VERSION "1.92.6" +#define IMGUI_VERSION_NUM 19261 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -95,7 +95,7 @@ Index of this file: #include #define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h #endif -#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! +#define IM_COUNTOF(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. #define IM_STRINGIFY_HELPER(_EXPR) #_EXPR #define IM_STRINGIFY(_EXPR) IM_STRINGIFY_HELPER(_EXPR) // Preprocessor idiom to stringify e.g. an integer or a macro. @@ -349,7 +349,7 @@ typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or // The identifier is valid even before the texture has been uploaded to the GPU/graphics system. // This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`. // This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering. -// - When a texture is created by user code (e.g. custom images), we directly stores the low-level ImTextureID. +// - When a texture is created by user code (e.g. custom images), we directly store the low-level ImTextureID. // Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side. // - When a texture is created by the backend, we stores a ImTextureData* which becomes an indirection // to extract the ImTextureID value during rendering, after texture upload has happened. @@ -853,20 +853,23 @@ namespace ImGui // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. - // - IMPORTANT: Notice that for OpenPopupOnItemClick() we exceptionally default flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks - IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) + IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. - // Popups: open+begin combined functions helpers + // Popups: Open+Begin popup combined functions helpers to create context menus. // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. - // - They are convenient to easily create context menus, hence the name. // - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future. - // - IMPORTANT: Notice that we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight. - IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! - IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. - IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). + // - IMPORTANT: If you ever used the left mouse button with BeginPopupContextXXX() helpers before 1.92.6: + // - Before this version, OpenPopupOnItemClick(), BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid() had 'a ImGuiPopupFlags popup_flags = 1' default value in their function signature. + // - Before: Explicitly passing a literal 0 meant ImGuiPopupFlags_MouseButtonLeft. The default = 1 meant ImGuiPopupFlags_MouseButtonRight. + // - After: The default = 0 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 1 also means ImGuiPopupFlags_MouseButtonRight (if legacy behavior are enabled) or will assert (if legacy behavior are disabled). + // - TL;DR: if you don't want to use right mouse button for popups, always specify it explicitly using a named ImGuiPopupFlags_MouseButtonXXXX value. + // - Read "API BREAKING CHANGES" 2026/01/07 (1.92.6) entry in imgui.cpp or GitHub topic #9157 for all details. + IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! + IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0);// open+begin popup when clicked on current window. + IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // open+begin popup when clicked in void (where there are no windows). // Popups: query functions // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. @@ -897,7 +900,7 @@ namespace ImGui // - 5. Call EndTable() IMGUI_API bool BeginTable(const char* str_id, int columns, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f); IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! - IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. + IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. 'min_row_height' include the minimum top and bottom padding aka CellPadding.y * 2.0f. IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. @@ -977,7 +980,7 @@ namespace ImGui // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) - // - Tooltips windows are automatically opted out of disabling. Note that IsItemHovered() by default returns false on disabled items, unless using ImGuiHoveredFlags_AllowWhenDisabled. + // - Tooltips windows are automatically opted out of disabling. Note that IsItemHovered() by default returns false on disabled items, unless using ImGuiHoveredFlags_AllowWhenDisabled. // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls) IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); @@ -1017,6 +1020,7 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item + IMGUI_API ImGuiItemFlags GetItemFlags(); // get generic flags of last item // Viewports // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -1047,19 +1051,22 @@ namespace ImGui IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); - // Inputs Utilities: Keyboard/Mouse/Gamepad + // Inputs Utilities: Raw Keyboard/Mouse/Gamepad Access + // - Consider using the Shortcut() function instead of IsKeyPressed()/IsKeyChordPressed()! Shortcut() is easier to use and better featured (can do focus routing check). // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...). - // - (legacy: before v1.87, we used ImGuiKey to carry native/user indices as defined by each backends. This was obsoleted in 1.87 (2022-02) and completely removed in 1.91.5 (2024-11). See https://github.com/ocornut/imgui/issues/4921) - // - (legacy: any use of ImGuiKey will assert when key < 512 to detect passing legacy native/user indices) + // - (legacy: before v1.87 (2022-02), we used ImGuiKey < 512 values to carry native/user indices as defined by each backends. This was obsoleted in 1.87 (2022-02) and completely removed in 1.91.5 (2024-11). See https://github.com/ocornut/imgui/issues/4921) IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. - IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate + IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? Repeat rate uses io.KeyRepeatDelay / KeyRepeatRate. IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)? IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord); // was key chord (mods + key) pressed, e.g. you can pass 'ImGuiMod_Ctrl | ImGuiKey_S' as a key-chord. This doesn't do any routing or focus check, please consider using Shortcut() function instead. IMGUI_API int GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names are provided for debugging purpose and are not meant to be saved persistently nor compared. IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call. - // Inputs Utilities: Shortcut Testing & Routing [BETA] + // Inputs Utilities: Shortcut Testing & Routing + // - Typical use is e.g.: 'if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S)) { ... }'. + // - Flags: Default route use ImGuiInputFlags_RouteFocused, but see ImGuiInputFlags_RouteGlobal and other options in ImGuiInputFlags_! + // - Flags: Use ImGuiInputFlags_Repeat to support repeat. // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. // ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments // ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments @@ -1071,8 +1078,10 @@ namespace ImGui // The whole system is order independent, so if Child1 makes its calls before Parent, results will be identical. // This is an important property as it facilitate working with foreign code or larger codebase. // - To understand the difference: - // - IsKeyChordPressed() compares mods and call IsKeyPressed() -> function has no side-effect. - // - Shortcut() submits a route, routes are resolved, if it currently can be routed it calls IsKeyChordPressed() -> function has (desirable) side-effects as it can prevents another call from getting the route. + // - IsKeyChordPressed() compares mods and call IsKeyPressed() + // -> the function has no side-effect. + // - Shortcut() submits a route, routes are resolved, if it currently can be routed it calls IsKeyChordPressed() + // -> the function has (desirable) side-effects as it can prevents another call from getting the route. // - Visualize registered routes in 'Metrics/Debugger->Inputs'. IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); IMGUI_API void SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); @@ -1122,7 +1131,9 @@ namespace ImGui IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. // Debug Utilities - // - Your main debugging friend is the ShowMetricsWindow() function, which is also accessible from Demo->Tools->Metrics Debugger + // - Your main debugging friend is the ShowMetricsWindow() function. + // - Interactive tools are all accessible from the 'Dear ImGui Demo->Tools' menu. + // - Read https://github.com/ocornut/imgui/wiki/Debug-Tools for a description of all available debug tools. IMGUI_API void DebugTextEncoding(const char* text); IMGUI_API void DebugFlashStyleColor(ImGuiCol idx); IMGUI_API void DebugStartItemPicker(); @@ -1219,7 +1230,7 @@ enum ImGuiChildFlags_ }; // Flags for ImGui::PushItemFlag() -// (Those are shared by all items) +// (Those are shared by all submitted items) enum ImGuiItemFlags_ { ImGuiItemFlags_None = 0, // (Default) @@ -1229,6 +1240,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window. ImGuiItemFlags_AllowDuplicateId = 1 << 5, // false // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set. + ImGuiItemFlags_Disabled = 1 << 6, // false // [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags(). }; // Flags for ImGui::InputText() @@ -1321,21 +1333,14 @@ enum ImGuiTreeNodeFlags_ }; // Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions. -// - To be backward compatible with older API which took an 'int mouse_button = 1' argument instead of 'ImGuiPopupFlags flags', -// we need to treat small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags. -// It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags. -// - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0. -// IMPORTANT: because the default parameter is 1 (==ImGuiPopupFlags_MouseButtonRight), if you rely on the default parameter -// and want to use another flag, you need to pass in the ImGuiPopupFlags_MouseButtonRight flag explicitly. +// - IMPORTANT: If you ever used the left mouse button with BeginPopupContextXXX() helpers before 1.92.6: Read "API BREAKING CHANGES" 2026/01/07 (1.92.6) entry in imgui.cpp or GitHub topic #9157. // - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later). enum ImGuiPopupFlags_ { ImGuiPopupFlags_None = 0, - ImGuiPopupFlags_MouseButtonLeft = 0, // For BeginPopupContext*(): open on Left Mouse release. Guaranteed to always be == 0 (same as ImGuiMouseButton_Left) - ImGuiPopupFlags_MouseButtonRight = 1, // For BeginPopupContext*(): open on Right Mouse release. Guaranteed to always be == 1 (same as ImGuiMouseButton_Right) - ImGuiPopupFlags_MouseButtonMiddle = 2, // For BeginPopupContext*(): open on Middle Mouse release. Guaranteed to always be == 2 (same as ImGuiMouseButton_Middle) - ImGuiPopupFlags_MouseButtonMask_ = 0x1F, - ImGuiPopupFlags_MouseButtonDefault_ = 1, + ImGuiPopupFlags_MouseButtonLeft = 1 << 2, // For BeginPopupContext*(): open on Left Mouse release. Only one button allowed! + ImGuiPopupFlags_MouseButtonRight = 2 << 2, // For BeginPopupContext*(): open on Right Mouse release. Only one button allowed! (default) + ImGuiPopupFlags_MouseButtonMiddle = 3 << 2, // For BeginPopupContext*(): open on Middle Mouse release. Only one button allowed! ImGuiPopupFlags_NoReopen = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation) //ImGuiPopupFlags_NoReopenAlwaysNavInit = 1 << 6, // For OpenPopup*(), BeginPopupContext*(): focus and initialize navigation even when not reopening. ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack @@ -1343,6 +1348,9 @@ enum ImGuiPopupFlags_ ImGuiPopupFlags_AnyPopupId = 1 << 10, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. ImGuiPopupFlags_AnyPopupLevel = 1 << 11, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel, + ImGuiPopupFlags_MouseButtonShift_ = 2, // [Internal] + ImGuiPopupFlags_MouseButtonMask_ = 0x0C, // [Internal] + ImGuiPopupFlags_InvalidMask_ = 0x03, // [Internal] Reserve legacy bits 0-1 to detect incorrectly passing 1 or 2 to the function. }; // Flags for ImGui::Selectable() @@ -1539,7 +1547,7 @@ enum ImGuiSortDirection : ImU8 // All our named keys are >= 512. Keys value 0 to 511 are left unused and were legacy native/opaque key values (< 1.87). // Support for legacy keys was completely removed in 1.91.5. // Read details about the 1.87+ transition : https://github.com/ocornut/imgui/issues/4921 -// Note that "Keys" related to physical keys and are not the same concept as input "Characters", the later are submitted via io.AddInputCharacter(). +// Note that "Keys" related to physical keys and are not the same concept as input "Characters", the latter are submitted via io.AddInputCharacter(). // The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps. enum ImGuiKey : int { @@ -1696,7 +1704,7 @@ enum ImGuiInputFlags_ enum ImGuiConfigFlags_ { ImGuiConfigFlags_None = 0, - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + space/enter to activate. + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + Space/Enter to activate. Note: some features such as basic Tabbing and CtrL+Tab are enabled by regardless of this flag (and may be disabled via other means, see #4828, #9218). ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad. ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct dear imgui to disable mouse inputs and interactions. ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. @@ -1830,6 +1838,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_ScrollbarPadding, // float ScrollbarPadding ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding + ImGuiStyleVar_ImageRounding, // float ImageRounding ImGuiStyleVar_ImageBorderSize, // float ImageBorderSize ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_TabBorderSize, // float TabBorderSize @@ -1872,19 +1881,20 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_NoTooltip = 1 << 6, // // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. ImGuiColorEditFlags_NoLabel = 1 << 7, // // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead. - ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. + ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target/source. ColorButton: disable drag and drop source. ImGuiColorEditFlags_NoBorder = 1 << 10, // // ColorButton: disable border (which is enforced by default) + ImGuiColorEditFlags_NoColorMarkers = 1 << 11, // // ColorEdit: disable rendering R/G/B/A color marker. May also be disabled globally by setting style.ColorMarkerSize = 0. // Alpha preview // - Prior to 1.91.8 (2025/01/21): alpha was made opaque in the preview by default using old name ImGuiColorEditFlags_AlphaPreview. // - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. // - The new flags may be combined better and allow finer controls. - ImGuiColorEditFlags_AlphaOpaque = 1 << 11, // // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. - ImGuiColorEditFlags_AlphaNoBg = 1 << 12, // // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. - ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 13, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. + ImGuiColorEditFlags_AlphaOpaque = 1 << 12, // // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. + ImGuiColorEditFlags_AlphaNoBg = 1 << 13, // // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. + ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 14, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. // User Options (right-click on widget to change some of them). - ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. + ImGuiColorEditFlags_AlphaBar = 1 << 18, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " @@ -1927,8 +1937,9 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds. ImGuiSliderFlags_ClampZeroRange = 1 << 10, // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it. ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. + ImGuiSliderFlags_ColorMarkers = 1 << 12, // DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component. ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange, - ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed. + ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from legacy API (obsoleted 2020-08) that has got miscast to this enum, and will trigger an assert if needed. }; // Identify a mouse button. @@ -2018,7 +2029,7 @@ enum ImGuiTableFlags_ ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu. ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. - ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file. + ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width, visibility and sort settings in the .ini file. ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). // Decorations ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) @@ -2134,7 +2145,7 @@ struct ImGuiTableSortSpecs int SpecsCount; // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled. bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag. - ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); } + ImGuiTableSortSpecs() { memset((void*)this, 0, sizeof(*this)); } }; // Sorting specification for one column of a table (sizeof == 12 bytes) @@ -2145,7 +2156,7 @@ struct ImGuiTableColumnSortSpecs ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) ImGuiSortDirection SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending - ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); } + ImGuiTableColumnSortSpecs() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -2268,7 +2279,7 @@ struct ImGuiStyle // - recap: ImGui::GetFontSize() == FontSizeBase * (FontScaleMain * FontScaleDpi * other_scaling_factors) float FontSizeBase; // Current base font size before external global factors are applied. Use PushFont(NULL, size) to modify. Use ImGui::GetFontSize() to obtain scaled value. float FontScaleMain; // Main global scale factor. May be set by application once, or exposed to end-user. - float FontScaleDpi; // Additional global scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI. + float FontScaleDpi; // Additional global scale factor from viewport/monitor contents scale. In docking branch: when io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI. float Alpha; // Global alpha applies to everything in Dear ImGui. float DisabledAlpha; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. @@ -2298,6 +2309,7 @@ struct ImGuiStyle float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. + float ImageRounding; // Rounding of Image() calls. float ImageBorderSize; // Thickness of border around Image() calls. float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. float TabBorderSize; // Thickness of border around tabs. @@ -2315,6 +2327,7 @@ struct ImGuiStyle float DragDropTargetRounding; // Radius of the drag and drop target frame. float DragDropTargetBorderSize; // Thickness of the drag and drop target border. float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size. + float ColorMarkerSize; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers. ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. @@ -2435,7 +2448,7 @@ struct ImGuiIO // Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL] // - Error recovery is provided as a way to facilitate: - // - Recovery after a programming error (native code or scripting language - the later tends to facilitate iterating on code while running). + // - Recovery after a programming error (native code or scripting language - the latter tends to facilitate iterating on code while running). // - Recovery after running an exception handler or any error processing which may skip code after an error has been detected. // - Error recovery is not perfect nor guaranteed! It is a feature to ease development. // You not are not supposed to rely on it in the course of a normal application run. @@ -2624,18 +2637,20 @@ struct ImGuiInputTextCallbackData ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only void* UserData; // What user passed to InputText() // Read-only + ImGuiID ID; // Widget ID // Read-only // Arguments for the different callback events // - During Resize callback, Buf will be same as your input buffer. // - However, during Completion/History/Always callback, Buf always points to our own internal data (it is not the same as your buffer)! Changes to it will be reflected into your own buffer shortly after the callback. // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. - ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] + ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; + bool EventActivated; // Input field just got activated // Read-only // [Always] + bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 - bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] int CursorPos; // // Read-write // [Completion,History,Always] int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection int SelectionEnd; // // Read-write // [Completion,History,Always] @@ -2645,9 +2660,10 @@ struct ImGuiInputTextCallbackData IMGUI_API ImGuiInputTextCallbackData(); IMGUI_API void DeleteChars(int pos, int bytes_count); IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); - void SelectAll() { SelectionStart = 0; SelectionEnd = BufTextLen; } - void ClearSelection() { SelectionStart = SelectionEnd = BufTextLen; } - bool HasSelection() const { return SelectionStart != SelectionEnd; } + void SelectAll() { SelectionStart = 0; CursorPos = SelectionEnd = BufTextLen; } + void SetSelection(int s, int e) { IM_ASSERT(s >= 0 && s <= BufTextLen); IM_ASSERT(e >= 0 && e <= BufTextLen); SelectionStart = s; CursorPos = SelectionEnd = e; } + void ClearSelection() { SelectionStart = SelectionEnd = BufTextLen; } + bool HasSelection() const { return SelectionStart != SelectionEnd; } }; // Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). @@ -2866,7 +2882,7 @@ struct ImGuiListClipper #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9] //inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6] - //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] + //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset((void*)this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] #endif }; @@ -3151,7 +3167,7 @@ struct ImDrawCmd int UserCallbackDataSize; // 4 // Size of callback user data when using storage, otherwise 0. int UserCallbackDataOffset;// 4 // [Internal] Offset of callback user data when using storage, otherwise -1. - ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed + ImDrawCmd() { memset((void*)this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) // Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used! @@ -3197,7 +3213,7 @@ struct ImDrawListSplitter int _Count; // Number of active channels (1+) ImVector _Channels; // Draw channels (not resized down so _Count might be < Channels.Size) - inline ImDrawListSplitter() { memset(this, 0, sizeof(*this)); } + inline ImDrawListSplitter() { memset((void*)this, 0, sizeof(*this)); } inline ~ImDrawListSplitter() { ClearFreeMemory(); } inline void Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame IMGUI_API void ClearFreeMemory(); @@ -3490,7 +3506,7 @@ struct ImTextureData bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame. // Functions - ImTextureData() { memset(this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; } + ImTextureData() { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; } ~ImTextureData() { DestroyPixels(); } IMGUI_API void Create(ImTextureFormat format, int w, int h); IMGUI_API void DestroyPixels(); @@ -3505,7 +3521,7 @@ struct ImTextureData // - Call SetTexID() and SetStatus() after honoring texture requests. Never modify TexID and Status directly! // - A backend may decide to destroy a texture that we did not request to destroy, which is fine (e.g. freeing resources), but we immediately set the texture back in _WantCreate mode. void SetTexID(ImTextureID tex_id) { TexID = tex_id; } - void SetStatus(ImTextureStatus status) { Status = status; if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame) Status = ImTextureStatus_WantCreate; } + void SetStatus(ImTextureStatus status) { Status = status; if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame && Pixels != nullptr) Status = ImTextureStatus_WantCreate; } }; //----------------------------------------------------------------------------- @@ -3519,16 +3535,15 @@ struct ImFontConfig char Name[40]; // // Name (strictly to ease debugging, hence limited size buffer) void* FontData; // // TTF/OTF data int FontDataSize; // // TTF/OTF data size - bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). + bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). SINCE 1.92, THE DATA NEEDS TO PERSIST FOR WHOLE DURATION OF ATLAS. // Options bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. - bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. - bool PixelSnapV; // true // Align Scaled GlyphOffset.y to pixel boundaries. + bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Prevents fractional font size from working correctly! Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, OversampleH/V will default to 1. ImS8 OversampleH; // 0 (2) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. ImS8 OversampleV; // 0 (1) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis. ImWchar EllipsisChar; // 0 // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. - float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). + float SizePixels; // // Output size in pixels for rasterizer (more or less maps to the resulting font height). const ImWchar* GlyphRanges; // NULL // *LEGACY* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). const ImWchar* GlyphExcludeRanges; // NULL // Pointer to a small user-provided list of Unicode ranges (2 value per range, values are inclusive, zero-terminated list). This is very close to GlyphRanges[] but designed to exclude ranges from a font source, when merging fonts with overlapping glyphs. Use "Input Glyphs Overlap Detection Tool" to find about your overlapping ranges. //ImVec2 GlyphExtraSpacing; // 0, 0 // (REMOVED AT IT SEEMS LARGELY OBSOLETE. PLEASE REPORT IF YOU WERE USING THIS). Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now. @@ -3538,9 +3553,10 @@ struct ImFontConfig float GlyphExtraAdvanceX; // 0 // Extra spacing (in pixels) between glyphs. Please contact us if you are using this. // FIXME-NEWATLAS: Intentionally unscaled ImU32 FontNo; // 0 // Index of font within TTF/OTF file unsigned int FontLoaderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. - //unsigned int FontBuilderFlags; // -- // [Renamed in 1.92] Ue FontLoaderFlags. + //unsigned int FontBuilderFlags; // -- // [Renamed in 1.92] Use FontLoaderFlags. float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future. float RasterizerDensity; // 1.0f // [LEGACY: this only makes sense when ImGuiBackendFlags_RendererHasTextures is not supported] DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered. + float ExtraSizeScale; // 1.0f // Extra rasterizer scale over SizePixels. // [Internal] ImFontFlags Flags; // Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates) @@ -3548,6 +3564,9 @@ struct ImFontConfig const ImFontLoader* FontLoader; // Custom font backend for this source (default source is the one stored in ImFontAtlas) void* FontLoaderData; // Font loader opaque storage (per font config) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + bool PixelSnapV; // true // [Obsoleted in 1.91.6] Align Scaled GlyphOffset.y to pixel boundaries. +#endif IMGUI_API ImFontConfig(); }; @@ -3564,7 +3583,7 @@ struct ImFontGlyph float U0, V0, U1, V1; // Texture coordinates for the current value of ImFontAtlas->TexRef. Cached equivalent of calling GetCustomRect() with PackId. int PackId; // [Internal] ImFontAtlasRectId value (FIXME: Cold data, could be moved elsewhere?) - ImFontGlyph() { memset(this, 0, sizeof(*this)); PackId = -1; } + ImFontGlyph() { memset((void*)this, 0, sizeof(*this)); PackId = -1; } }; // Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges(). @@ -3597,7 +3616,7 @@ struct ImFontAtlasRect unsigned short w, h; // Size ImVec2 uv0, uv1; // UV coordinates (in current texture) - ImFontAtlasRect() { memset(this, 0, sizeof(*this)); } + ImFontAtlasRect() { memset((void*)this, 0, sizeof(*this)); } }; // Flags for ImFontAtlas build @@ -3633,7 +3652,9 @@ struct ImFontAtlas IMGUI_API ImFontAtlas(); IMGUI_API ~ImFontAtlas(); IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); - IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); + IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); // Selects between AddFontDefaultVector() and AddFontDefaultBitmap(). + IMGUI_API ImFont* AddFontDefaultVector(const ImFontConfig* font_cfg = NULL); // Embedded scalable font. Recommended at any higher size. + IMGUI_API ImFont* AddFontDefaultBitmap(const ImFontConfig* font_cfg = NULL); // Embedded classic pixel-clean font. Recommended at Size 13px with no scaling. IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. @@ -3795,7 +3816,7 @@ struct ImFontBaked unsigned int MetricsTotalSurface:26;// 3 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) unsigned int WantDestroy:1; // 0 // // Queued for destroy unsigned int LoadNoFallback:1; // 0 // // Disable loading fallback in lower-level calls. - unsigned int LoadNoRenderOnLayout:1;// 0 // // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantagous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw. + unsigned int LoadNoRenderOnLayout:1;// 0 // // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantageous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw. int LastUsedFrame; // 4 // // Record of that time this was bounds ImGuiID BakedId; // 4 // // Unique ID for this baked storage ImFont* OwnerFont; // 4-8 // in // Parent font @@ -3838,10 +3859,10 @@ struct ImFont ImGuiID FontId; // Unique identifier for the font float LegacySize; // 4 // in // Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui::GetFontBaked() to get font baked at current bound size). ImVector Sources; // 16 // in // List of sources. Pointers within OwnerAtlas->Sources[] - ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...'). + ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...'). If you ever want to temporarily swap this for an alternative/dummy char, make sure to clear EllipsisAutoBake. ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?') - ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 16 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. - bool EllipsisAutoBake; // 1 // // Mark when the "..." glyph needs to be generated. + ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 17 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 8K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. + bool EllipsisAutoBake; // 1 // // Mark when the "..." glyph (== EllipsisChar) needs to be generated by combining multiple '.'. ImGuiStorage RemapPairs; // 16 // // Remapping pairs when using AddRemapChar(), otherwise empty. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS float Scale; // 4 // in // Legacy base font scale (~1.0f), multiplied by the per-window font scale which you can adjust with SetWindowFontScale() @@ -3924,7 +3945,7 @@ struct ImGuiViewport void* PlatformHandle; // void* to hold higher-level, platform window handle (e.g. HWND, GLFWWindow*, SDL_Window*) void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms) - ImGuiViewport() { memset(this, 0, sizeof(*this)); } + ImGuiViewport() { memset((void*)this, 0, sizeof(*this)); } // Helpers ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } @@ -3946,7 +3967,7 @@ struct ImGuiPlatformIO // Optional: Access OS clipboard // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) - const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx); + const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx); // Should return NULL on failure (e.g. clipboard data is not text). void (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text); void* Platform_ClipboardUserData; @@ -4001,7 +4022,7 @@ struct ImGuiPlatformImeData float InputLineHeight; // Line height (for IME). ImGuiID ViewportId; // ID of platform window/viewport. - ImGuiPlatformImeData() { memset(this, 0, sizeof(*this)); } + ImGuiPlatformImeData() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -4023,19 +4044,21 @@ namespace ImGui inline void PopButtonRepeat() { PopItemFlag(); } inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } inline void PopTabStop() { PopItemFlag(); } + // You do not need those functions! See #7838 on GitHub for more info. IMGUI_API ImVec2 GetContentRegionMax(); // Content boundaries max (e.g. window boundaries including scrolling, or current column boundaries). You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMin(); // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMax(); // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! // OBSOLETED in 1.90.0 (from September 2023) - inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } - inline void EndChildFrame() { EndChild(); } - //inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders - //inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders - inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + // OBSOLETED in 1.90.0 (from September 2023) + //inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders + //inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders + //inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, flags); } + //inline void EndChildFrame() { EndChild(); } + //inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } // OBSOLETED in 1.89.7 (from June 2023) //IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() _before_ item. //-- OBSOLETED in 1.89.4 (from March 2023) @@ -4145,10 +4168,12 @@ typedef ImFontAtlasRect ImFontAtlasCustomRect; //typedef ImGuiKeyChord ImGuiKeyModFlags; // == int //enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super }; -#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // OBSOLETED IN 1.90 (now using C++11 standard version) +//#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // OBSOLETED IN 1.90 (now using C++11 standard version) #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +#define IM_ARRAYSIZE IM_COUNTOF // RENAMED IN 1.92.6: IM_ARRAYSIZE -> IM_COUNTOF + // RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022) #ifdef IMGUI_DISABLE_METRICS_WINDOW #error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name. diff --git a/core/deps/imgui/imgui_demo.cpp b/core/deps/imgui/imgui_demo.cpp index a3996d961b..c0c4de946f 100755 --- a/core/deps/imgui/imgui_demo.cpp +++ b/core/deps/imgui/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.5 +// dear imgui, v1.92.6 // (demo code) // Help: @@ -141,7 +141,7 @@ Index of this file: #include // PRId64/PRIu64, not avail in some MinGW headers. #endif #ifdef __EMSCRIPTEN__ -#include // __EMSCRIPTEN_major__ etc. +#include // __EMSCRIPTEN_MAJOR__ etc. #endif // Visual Studio warnings @@ -764,7 +764,7 @@ static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent) { ExampleTreeNode* node = IM_NEW(ExampleTreeNode); - snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); + snprintf(node->Name, IM_COUNTOF(node->Name), "%s", name); node->UID = uid; node->Parent = parent; node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0; @@ -790,19 +790,19 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() int uid = 0; ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); const int root_items_multiplier = 2; - for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++) + for (int idx_L0 = 0; idx_L0 < IM_COUNTOF(root_names) * root_items_multiplier; idx_L0++) { - snprintf(name_buf, IM_ARRAYSIZE(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); + snprintf(name_buf, IM_COUNTOF(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); const int number_of_childs = (int)strlen(node_L1->Name); for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) { - snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Child %d", idx_L1); + snprintf(name_buf, IM_COUNTOF(name_buf), "Child %d", idx_L1); ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); node_L2->HasData = true; if (idx_L1 == 0) { - snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Sub-child %d", 0); + snprintf(name_buf, IM_COUNTOF(name_buf), "Sub-child %d", 0); ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); node_L3->HasData = true; } @@ -892,7 +892,7 @@ static void DemoWindowWidgetsBasic() // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize. IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); static char str0[128] = "Hello, world!"; - ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); + ImGui::InputText("input text", str0, IM_COUNTOF(str0)); ImGui::SameLine(); HelpMarker( "USER:\n" "Hold Shift or use mouse to select text.\n" @@ -907,7 +907,7 @@ static void DemoWindowWidgetsBasic() "in imgui_demo.cpp)."); static char str1[128] = ""; - ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); + ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_COUNTOF(str1)); IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); static int i0 = 123; @@ -998,7 +998,7 @@ static void DemoWindowWidgetsBasic() IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; static int item_current = 0; - ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo", &item_current, items, IM_COUNTOF(items)); ImGui::SameLine(); HelpMarker( "Using the simplified one-liner Combo API here.\n" "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API."); @@ -1010,7 +1010,7 @@ static void DemoWindowWidgetsBasic() IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; static int item_current = 1; - ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); + ImGui::ListBox("listbox", &item_current, items, IM_COUNTOF(items), 4); ImGui::SameLine(); HelpMarker( "Using the simplified one-liner ListBox API here.\n" "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API."); @@ -1096,8 +1096,9 @@ static void DemoWindowWidgetsColorAndPickers() ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf); - ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoColorMarkers", &base_flags, ImGuiColorEditFlags_NoColorMarkers); ImGui::CheckboxFlags("ImGuiColorEditFlags_HDR", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); @@ -1132,7 +1133,7 @@ static void DemoWindowWidgetsColorAndPickers() static ImVec4 saved_palette[32] = {}; if (saved_palette_init) { - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + for (int n = 0; n < IM_COUNTOF(saved_palette); n++) { ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); @@ -1165,7 +1166,7 @@ static void DemoWindowWidgetsColorAndPickers() color = backup_color; ImGui::Separator(); ImGui::Text("Palette"); - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + for (int n = 0; n < IM_COUNTOF(saved_palette); n++) { ImGui::PushID(n); if ((n % 8) != 0) @@ -1318,7 +1319,7 @@ static void DemoWindowWidgetsComboBoxes() const char* combo_preview_value = items[item_selected_idx]; if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) @@ -1344,7 +1345,7 @@ static void DemoWindowWidgetsComboBoxes() ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); filter.Draw("##Filter", -FLT_MIN); - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (filter.PassFilter(items[n])) @@ -1366,11 +1367,11 @@ static void DemoWindowWidgetsComboBoxes() // Simplified one-liner Combo() using an array of const char* // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview - ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_COUNTOF(items)); // Simplified one-liner Combo() using an accessor function static int item_current_4 = 0; - ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_COUNTOF(items)); ImGui::TreePop(); } @@ -1569,7 +1570,7 @@ static void DemoWindowWidgetsDragAndDrop() "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn" }; - for (int n = 0; n < IM_ARRAYSIZE(names); n++) + for (int n = 0; n < IM_COUNTOF(names); n++) { ImGui::PushID(n); if ((n % 3) != 0) @@ -1631,7 +1632,7 @@ static void DemoWindowWidgetsDragAndDrop() "We don't use the drag and drop api at all here! " "Instead we query when the item is held but not hovered, and order items accordingly."); static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" }; - for (int n = 0; n < IM_ARRAYSIZE(item_names); n++) + for (int n = 0; n < IM_COUNTOF(item_names); n++) { const char* item = item_names[n]; ImGui::Selectable(item); @@ -1639,7 +1640,7 @@ static void DemoWindowWidgetsDragAndDrop() if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) { int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1); - if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names)) + if (n_next >= 0 && n_next < IM_COUNTOF(item_names)) { item_names[n] = item_names[n_next]; item_names[n_next] = item; @@ -1710,9 +1711,12 @@ static void DemoWindowWidgetsDragsAndSliders() ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic."); ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround); ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)"); + ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkers", &flags, ImGuiSliderFlags_ColorMarkers); + //ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkersG", &flags, 1 << ImGuiSliderFlags_ColorMarkersIndexShift_); // Not explicitly documented but possible. // Drags static float drag_f = 0.5f; + static float drag_f4[4]; static int drag_i = 50; ImGui::Text("Underlying float value: %f", drag_f); ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags); @@ -1722,14 +1726,17 @@ static void DemoWindowWidgetsDragsAndSliders() //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags); ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); + ImGui::DragFloat4("DragFloat4 (0 -> 1)", drag_f4, 0.005f, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers. // Sliders static float slider_f = 0.5f; + static float slider_f4[4]; static int slider_i = 50; - const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround; + const ImGuiSliderFlags flags_for_sliders = (flags & ~ImGuiSliderFlags_WrapAround); ImGui::Text("Underlying float value: %f", slider_f); ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders); ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders); + ImGui::SliderFloat4("SliderFloat4 (0 -> 1)", slider_f4, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers. ImGui::TreePop(); } @@ -1873,7 +1880,7 @@ static void DemoWindowWidgetsListBoxes() if (ImGui::BeginListBox("listbox 1")) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) @@ -1894,7 +1901,7 @@ static void DemoWindowWidgetsListBoxes() ImGui::Text("Full-width:"); if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing()))) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { bool is_selected = (item_selected_idx == n); ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0; @@ -1980,8 +1987,8 @@ static void DemoWindowWidgetsPlotting() // Plot as lines and plot as histogram static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); + ImGui::PlotLines("Frame Times", arr, IM_COUNTOF(arr)); + ImGui::PlotHistogram("Histogram", arr, IM_COUNTOF(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!"); // Fill an array of contiguous float values to plot @@ -1996,7 +2003,7 @@ static void DemoWindowWidgetsPlotting() { static float phase = 0.0f; values[values_offset] = cosf(phase); - values_offset = (values_offset + 1) % IM_ARRAYSIZE(values); + values_offset = (values_offset + 1) % IM_COUNTOF(values); phase += 0.10f * values_offset; refresh_time += 1.0f / 60.0f; } @@ -2005,12 +2012,12 @@ static void DemoWindowWidgetsPlotting() // (in this example, we will display an average value) { float average = 0.0f; - for (int n = 0; n < IM_ARRAYSIZE(values); n++) + for (int n = 0; n < IM_COUNTOF(values); n++) average += values[n]; - average /= (float)IM_ARRAYSIZE(values); + average /= (float)IM_COUNTOF(values); char overlay[32]; sprintf(overlay, "avg %f", average); - ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); + ImGui::PlotLines("Lines", values, IM_COUNTOF(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); } // Use functions to generate output @@ -2045,10 +2052,12 @@ static void DemoWindowWidgetsProgressBars() if (ImGui::TreeNode("Progress Bars")) { // Animate a simple progress bar - static float progress = 0.0f, progress_dir = 1.0f; - progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; - if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } - if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } + static float progress_accum = 0.0f, progress_dir = 1.0f; + progress_accum += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; + if (progress_accum >= +1.1f) { progress_accum = +1.1f; progress_dir *= -1.0f; } + if (progress_accum <= -0.1f) { progress_accum = -0.1f; progress_dir *= -1.0f; } + + const float progress = IM_CLAMP(progress_accum, 0.0f, 1.0f); // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width, // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. @@ -2056,9 +2065,8 @@ static void DemoWindowWidgetsProgressBars() ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::Text("Progress Bar"); - float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f); char buf[32]; - sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753); + sprintf(buf, "%d/%d", (int)(progress * 1753), 1753); ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value. @@ -2088,7 +2096,7 @@ static void DemoWindowWidgetsQueryingStatuses() }; static int item_type = 4; static bool item_disabled = false; - ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names)); + ImGui::Combo("Item Type", &item_type, item_names, IM_COUNTOF(item_names), IM_COUNTOF(item_names)); ImGui::SameLine(); HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); ImGui::Checkbox("Item Disabled", &item_disabled); @@ -2105,8 +2113,8 @@ static void DemoWindowWidgetsQueryingStatuses() if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater) if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item - if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) - if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window) + if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_COUNTOF(str)); } // Testing input text (which handles tabbing) + if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_COUNTOF(str)); } // Testing input text (which uses a child window) if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) @@ -2114,8 +2122,8 @@ static void DemoWindowWidgetsQueryingStatuses() if (item_type == 11) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) if (item_type == 12) { ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node if (item_type == 13) { ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. - if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } - if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_COUNTOF(items)); } + if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_COUNTOF(items), IM_COUNTOF(items)); } bool hovered_delay_none = ImGui::IsItemHovered(); bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary); @@ -2182,7 +2190,7 @@ static void DemoWindowWidgetsQueryingStatuses() ImGui::EndDisabled(); char buf[1] = ""; - ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("unused", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_ReadOnly); ImGui::SameLine(); HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); @@ -2301,15 +2309,45 @@ static void DemoWindowWidgetsSelectables() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line"); - if (ImGui::TreeNode("Rendering more items on the same line")) + IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple items on the same line"); + if (ImGui::TreeNode("Multiple items on the same line")) { - // (1) Using SetNextItemAllowOverlap() - // (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically. - static bool selected[3] = { false, false, false }; - ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1"); - ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2"); - ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3"); + // (1) + // - Using SetNextItemAllowOverlap() + // - Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically. + { + static bool selected[3] = {}; + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3"); + } + + // (2) + // - Using ImGuiSelectableFlags_AllowOverlap is a shortcut for calling SetNextItemAllowOverlap() + // - No visible label, display contents inside the selectable bounds. + // - We don't maintain actual selection in this example to keep things simple. + ImGui::Spacing(); + { + static bool checked[5] = {}; + static int selected_n = 0; + const float color_marker_w = ImGui::CalcTextSize("x").x; + for (int n = 0; n < 5; n++) + { + ImGui::PushID(n); + ImGui::AlignTextToFramePadding(); + if (ImGui::Selectable("##selectable", selected_n == n, ImGuiSelectableFlags_AllowOverlap)) + selected_n = n; + ImGui::SameLine(0, 0); + ImGui::Checkbox("##check", &checked[n]); + ImGui::SameLine(); + ImVec4 color((n & 1) ? 1.0f : 0.2f, (n & 2) ? 1.0f : 0.2f, 0.2f, 1.0f); + ImGui::ColorButton("##color", color, ImGuiColorEditFlags_NoTooltip, ImVec2(color_marker_w, 0)); + ImGui::SameLine(); + ImGui::Text("Some label"); + ImGui::PopID(); + } + } + ImGui::TreePop(); } @@ -2360,13 +2398,14 @@ static void DemoWindowWidgetsSelectables() if (winning_state) ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f))); + const float size = ImGui::CalcTextSize("Sailor").x; for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++) { if (x > 0) ImGui::SameLine(); ImGui::PushID(y * 4 + x); - if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(50, 50))) + if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(size, size))) { // Toggle clicked cell + toggle neighbors selected[y][x] ^= 1; @@ -2389,7 +2428,9 @@ static void DemoWindowWidgetsSelectables() "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item " "basis using PushStyleVar(). You'll probably want to always keep your default situation to " "left-align otherwise it becomes difficult to layout multiple items on a same line"); + static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true }; + const float size = ImGui::CalcTextSize("(1.0,1.0)").x; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) @@ -2399,7 +2440,7 @@ static void DemoWindowWidgetsSelectables() sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y); if (x > 0) ImGui::SameLine(); ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment); - ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80)); + ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(size, size)); ImGui::PopStyleVar(); } } @@ -2530,7 +2571,7 @@ struct ExampleDualListBox { const int* a = (const int*)lhs; const int* b = (const int*)rhs; - return (*a - *b); + return *a - *b; } void SortItems(int n) { @@ -2718,7 +2759,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -2758,7 +2799,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -2820,7 +2861,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d { const ImGuiID item_id = items[n]; char label[64]; - sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains(item_id); ImGui::SetNextItemSelectionUserData(n); @@ -2846,7 +2887,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d // Init default state static ExampleDualListBox dlb; if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0) - for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++) + for (int item_id = 0; item_id < IM_COUNTOF(ExampleNames); item_id++) dlb.Items[0].push_back((ImGuiID)item_id); // Show @@ -2863,7 +2904,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d const int ITEMS_COUNT = 10000; ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); - if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter)) + if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter, ImVec2(0.0f, ImGui::GetFontSize() * 20))) { ImGui::TableSetupColumn("Object"); ImGui::TableSetupColumn("Action"); @@ -2884,13 +2925,15 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::PushID(n); char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); ImGui::TableNextColumn(); ImGui::SmallButton("hello"); + ImGui::PopID(); } } @@ -2918,7 +2961,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) { - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items)); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_COUNTOF(items)); ImGuiSelectionExternalStorage storage_wrapper; storage_wrapper.UserData = (void*)items; storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; }; @@ -2969,7 +3012,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection->Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -3258,7 +3301,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::TableNextColumn(); const int item_id = items[n]; - const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]; + const char* item_category = ExampleNames[item_id % IM_COUNTOF(ExampleNames)]; char label[64]; sprintf(label, "Object %05d: %s", item_id, item_category); @@ -3320,7 +3363,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d const int* payload_items = (int*)payload->Data; const int payload_count = (int)payload->DataSize / (int)sizeof(int); if (payload_count == 1) - ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]); + ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_COUNTOF(ExampleNames)]); else ImGui::Text("Dragging %d objects", payload_count); @@ -3348,7 +3391,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("##NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleVar(); } @@ -3446,7 +3489,7 @@ static void DemoWindowWidgetsTabs() ImGui::Text("Opened:"); const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; static bool opened[4] = { true, true, true, true }; // Persistent user state - for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + for (int n = 0; n < IM_COUNTOF(opened); n++) { ImGui::SameLine(); ImGui::Checkbox(names[n], &opened[n]); @@ -3456,7 +3499,7 @@ static void DemoWindowWidgetsTabs() // the underlying bool will be set to false when the tab is closed. if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) { - for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + for (int n = 0; n < IM_COUNTOF(opened); n++) if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None)) { ImGui::Text("This is the %s tab!", names[n]); @@ -3515,7 +3558,7 @@ static void DemoWindowWidgetsTabs() { bool open = true; char name[16]; - snprintf(name, IM_ARRAYSIZE(name), "%04d", active_tabs[n]); + snprintf(name, IM_COUNTOF(name), "%04d", active_tabs[n]); if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None)) { ImGui::Text("This is the %s tab!", name); @@ -3649,7 +3692,7 @@ static void DemoWindowWidgetsText() ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis - ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("UTF-8 input", buf, IM_COUNTOF(buf)); ImGui::TreePop(); } ImGui::TreePop(); @@ -3676,7 +3719,7 @@ static void DemoWindowWidgetsTextFilter() " \"-xxx\" hide lines containing \"xxx\""); filter.Draw(); const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; - for (int i = 0; i < IM_ARRAYSIZE(lines); i++) + for (int i = 0; i < IM_COUNTOF(lines); i++) if (filter.PassFilter(lines[i])) ImGui::BulletText("%s", lines[i]); ImGui::TreePop(); @@ -3721,7 +3764,7 @@ static void DemoWindowWidgetsTextInput() ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput); ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets."); ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); + ImGui::InputTextMultiline("##source", text, IM_COUNTOF(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } @@ -3747,13 +3790,13 @@ static void DemoWindowWidgetsTextInput() } }; - static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_ARRAYSIZE(buf1)); - static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CharsDecimal); - static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_ARRAYSIZE(buf4), ImGuiInputTextFlags_CharsUppercase); - static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_ARRAYSIZE(buf5), ImGuiInputTextFlags_CharsNoBlank); - static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_ARRAYSIZE(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters. - static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_ARRAYSIZE(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters. + static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_COUNTOF(buf1)); + static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CharsDecimal); + static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_COUNTOF(buf4), ImGuiInputTextFlags_CharsUppercase); + static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_COUNTOF(buf5), ImGuiInputTextFlags_CharsNoBlank); + static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_COUNTOF(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters. + static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_COUNTOF(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters. ImGui::TreePop(); } @@ -3761,10 +3804,10 @@ static void DemoWindowWidgetsTextInput() if (ImGui::TreeNode("Password Input")) { static char password[64] = "password123"; - ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); + ImGui::InputText("password", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password); ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputTextWithHint("password (w/ hint)", "", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); - ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password)); + ImGui::InputTextWithHint("password (w/ hint)", "", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password); + ImGui::InputText("password (clear)", password, IM_COUNTOF(password)); ImGui::TreePop(); } @@ -3809,20 +3852,20 @@ static void DemoWindowWidgetsTextInput() } }; static char buf1[64]; - ImGui::InputText("Completion", buf1, IM_ARRAYSIZE(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); + ImGui::InputText("Completion", buf1, IM_COUNTOF(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); ImGui::SameLine(); HelpMarker( "Here we append \"..\" each time Tab is pressed. " "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf2[64]; - ImGui::InputText("History", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); + ImGui::InputText("History", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); ImGui::SameLine(); HelpMarker( "Here we replace and select text each time Up/Down are pressed. " "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf3[64]; static int edit_count = 0; - ImGui::InputText("Edit", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); + ImGui::InputText("Edit", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); ImGui::SameLine(); HelpMarker( "Here we toggle the casing of the first character on every edit + count edits."); ImGui::SameLine(); ImGui::Text("(%d)", edit_count); @@ -3882,7 +3925,7 @@ static void DemoWindowWidgetsTextInput() static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp"; static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft; ImGui::CheckboxFlags("ImGuiInputTextFlags_ElideLeft", &flags, ImGuiInputTextFlags_ElideLeft); - ImGui::InputText("Path", buf1, IM_ARRAYSIZE(buf1), flags); + ImGui::InputText("Path", buf1, IM_COUNTOF(buf1), flags); ImGui::TreePop(); } @@ -3894,7 +3937,7 @@ static void DemoWindowWidgetsTextInput() ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo); - ImGui::InputText("Hello", buf1, IM_ARRAYSIZE(buf1), flags); + ImGui::InputText("Hello", buf1, IM_COUNTOF(buf1), flags); ImGui::TreePop(); } @@ -3936,7 +3979,7 @@ static void DemoWindowWidgetsTooltips() { ImGui::Text("I am a fancy tooltip"); static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::PlotLines("Curve", arr, IM_COUNTOF(arr)); ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); ImGui::EndTooltip(); } @@ -4084,6 +4127,7 @@ static void DemoWindowWidgetsTreeNodes() ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)"); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_FramePadding", &base_flags, ImGuiTreeNodeFlags_FramePadding); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent); HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags"); @@ -4571,23 +4615,21 @@ static void DemoWindowLayout() // Various static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f; - ImGui::PushItemWidth(80); + ImGui::PushItemWidth(ImGui::CalcTextSize("AAAAAAA").x); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; static int item = -1; - ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); + ImGui::Combo("Combo", &item, items, IM_COUNTOF(items)); ImGui::SameLine(); ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine(); ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine(); ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f); - ImGui::PopItemWidth(); - ImGui::PushItemWidth(80); ImGui::Text("Lists:"); static int selection[4] = { 0, 1, 2, 3 }; for (int i = 0; i < 4; i++) { if (i > 0) ImGui::SameLine(); ImGui::PushID(i); - ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::ListBox("", &selection[i], items, IM_COUNTOF(items)); ImGui::PopID(); //ImGui::SetItemTooltip("ListBox %d hovered", i); } @@ -4647,7 +4689,7 @@ static void DemoWindowLayout() // Capture the group size and create widgets using the same size ImVec2 size = ImGui::GetItemRectSize(); const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; - ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); + ImGui::PlotHistogram("##values", values, IM_COUNTOF(values), 0, NULL, 0.0f, 1.0f, size); ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y)); ImGui::SameLine(); @@ -4748,7 +4790,7 @@ static void DemoWindowLayout() // Tree // (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline) const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::Button("Button##1"); + ImGui::Button("Button##1"); // Will make line higher ImGui::SameLine(0.0f, spacing); if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone)) { @@ -4758,14 +4800,22 @@ static void DemoWindowLayout() ImGui::TreePop(); } + const float padding = (float)(int)(ImGui::GetFontSize() * 1.20f); // Large padding + ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, padding); + ImGui::Button("Button##2"); + ImGui::PopStyleVar(); + ImGui::SameLine(0.0f, spacing); + if (ImGui::TreeNodeEx("Node##2", ImGuiTreeNodeFlags_DrawLinesNone)) + ImGui::TreePop(); + // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. // Otherwise you can use SmallButton() (smaller fit). ImGui::AlignTextToFramePadding(); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add - // other contents below the node. - bool node_open = ImGui::TreeNode("Node##2"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); + // other contents "inside" the node. + bool node_open = ImGui::TreeNode("Node##3"); + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##3"); if (node_open) { // Placeholder tree data @@ -4775,13 +4825,13 @@ static void DemoWindowLayout() } // Bullet - ImGui::Button("Button##3"); + ImGui::Button("Button##4"); ImGui::SameLine(0.0f, spacing); ImGui::BulletText("Bullet text"); ImGui::AlignTextToFramePadding(); ImGui::BulletText("Node"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##5"); ImGui::Unindent(); } @@ -5007,7 +5057,7 @@ static void DemoWindowLayout() if (explicit_content_size) { ImGui::SameLine(); - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::CalcTextSize("123456").x); ImGui::DragFloat("##csx", &contents_size_x); ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE); @@ -5223,7 +5273,7 @@ static void DemoWindowPopups() if (ImGui::BeginPopup("my_select_popup")) { ImGui::SeparatorText("Aquarium"); - for (int i = 0; i < IM_ARRAYSIZE(names); i++) + for (int i = 0; i < IM_COUNTOF(names); i++) if (ImGui::Selectable(names[i])) selected_fish = i; ImGui::EndPopup(); @@ -5234,7 +5284,7 @@ static void DemoWindowPopups() ImGui::OpenPopup("my_toggle_popup"); if (ImGui::BeginPopup("my_toggle_popup")) { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) + for (int i = 0; i < IM_COUNTOF(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); if (ImGui::BeginMenu("Sub-menu")) { @@ -5250,7 +5300,7 @@ static void DemoWindowPopups() ImGui::OpenPopup("another popup"); if (ImGui::BeginPopup("another popup")) { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) + for (int i = 0; i < IM_COUNTOF(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); if (ImGui::BeginMenu("Sub-menu")) { @@ -5372,7 +5422,7 @@ static void DemoWindowPopups() if (ImGui::BeginPopupContextItem()) { ImGui::Text("Edit name:"); - ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); + ImGui::InputText("##edit", name, IM_COUNTOF(name)); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); @@ -5545,7 +5595,7 @@ struct MyItem // qsort() is instable so always return a way to differentiate items. // Your own compare function may want to avoid fallback on implicit sort specs. // e.g. a Name compare if it wasn't already part of the sort specs. - return (a->ID - b->ID); + return a->ID - b->ID; } }; const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL; @@ -5577,13 +5627,13 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) { ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." } }; int idx; - for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++) + for (idx = 0; idx < IM_COUNTOF(policies); idx++) if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_)) break; - const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; + const char* preview_text = (idx < IM_COUNTOF(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; if (ImGui::BeginCombo("Sizing Policy", preview_text)) { - for (int n = 0; n < IM_ARRAYSIZE(policies); n++) + for (int n = 0; n < IM_COUNTOF(policies); n++) if (ImGui::Selectable(policies[n].Name, idx == n)) *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value; ImGui::EndCombo(); @@ -5593,7 +5643,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); - for (int m = 0; m < IM_ARRAYSIZE(policies); m++) + for (int m = 0; m < IM_COUNTOF(policies); m++) { ImGui::Separator(); ImGui::Text("%s:", policies[m].Name); @@ -6091,7 +6141,7 @@ static void DemoWindowTables() strcpy(text_bufs[cell], "edit me"); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushID(cell); - ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell])); + ImGui::InputText("##cell", text_bufs[cell], IM_COUNTOF(text_bufs[cell])); ImGui::PopID(); } if (!show_widget_frame_bg) @@ -6204,7 +6254,7 @@ static void DemoWindowTables() case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break; case CT_Button: ImGui::Button(label); break; case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; - case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; + case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_COUNTOF(text_buf)); break; } ImGui::PopID(); } @@ -6509,7 +6559,7 @@ static void DemoWindowTables() ImGui::TableNextColumn(); ImGui::Text("A0 Row 0"); { - float rows_height = TEXT_BASE_HEIGHT * 2; + float rows_height = (TEXT_BASE_HEIGHT * 2.0f) + (ImGui::GetStyle().CellPadding.y * 2.0f); if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupColumn("B0"); @@ -6551,7 +6601,7 @@ static void DemoWindowTables() { for (int row = 0; row < 8; row++) { - float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row); + float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row + ImGui::GetStyle().CellPadding.y * 2.0f); ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height); ImGui::TableNextColumn(); ImGui::Text("min_row_height = %.2f", min_row_height); @@ -6655,9 +6705,10 @@ static void DemoWindowTables() ImGui::SameLine(); if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) { + const float rows_height = TEXT_BASE_HEIGHT * 1.5f + ImGui::GetStyle().CellPadding.y * 2.0f; for (int row = 0; row < 3; row++) { - ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f); + ImGui::TableNextRow(0, rows_height); for (int column = 0; column < 3; column++) { ImGui::TableNextColumn(); @@ -6922,7 +6973,7 @@ static void DemoWindowTables() if (ImGui::TreeNode("Angled headers")) { const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" }; - const int columns_count = IM_ARRAYSIZE(column_names); + const int columns_count = IM_COUNTOF(column_names); const int rows_count = 12; static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn; @@ -7151,7 +7202,7 @@ static void DemoWindowTables() items.resize(50, MyItem()); for (int n = 0; n < items.Size; n++) { - const int template_n = n % IM_ARRAYSIZE(template_items_names); + const int template_n = n % IM_COUNTOF(template_items_names); MyItem& item = items[n]; item.ID = n; item.Name = template_items_names[template_n]; @@ -7242,7 +7293,7 @@ static void DemoWindowTables() const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }; static int freeze_cols = 1; static int freeze_rows = 1; - static int items_count = IM_ARRAYSIZE(template_items_names) * 2; + static int items_count = IM_COUNTOF(template_items_names) * 2; static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12); static float row_min_height = 0.0f; // Auto static float inner_width_with_scroll = 0.0f; // Auto-extend @@ -7360,7 +7411,7 @@ static void DemoWindowTables() ImGui::SameLine(); HelpMarker("Specify height of the Selectable item."); ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999); - ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_ARRAYSIZE(contents_type_names)); + ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_COUNTOF(contents_type_names)); //filter.Draw("filter"); ImGui::TreePop(); } @@ -7380,7 +7431,7 @@ static void DemoWindowTables() items.resize(items_count, MyItem()); for (int n = 0; n < items_count; n++) { - const int template_n = n % IM_ARRAYSIZE(template_items_names); + const int template_n = n % IM_COUNTOF(template_items_names); MyItem& item = items[n]; item.ID = n; item.Name = template_items_names[template_n]; @@ -7782,10 +7833,10 @@ static void DemoWindowInputs() ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); ImGui::Text("Mouse down:"); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Mouse clicked count:"); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); } + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); } // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows // displaying the data for old/new backends. @@ -7865,6 +7916,11 @@ static void DemoWindowInputs() ImGui::CheckboxFlags("ImGuiInputFlags_Repeat", &route_options, ImGuiInputFlags_Repeat); ImGui::RadioButton("ImGuiInputFlags_RouteActive", &route_type, ImGuiInputFlags_RouteActive); ImGui::RadioButton("ImGuiInputFlags_RouteFocused (default)", &route_type, ImGuiInputFlags_RouteFocused); + ImGui::Indent(); + ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteFocused); + ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive##0", &route_options, ImGuiInputFlags_RouteOverActive); + ImGui::EndDisabled(); + ImGui::Unindent(); ImGui::RadioButton("ImGuiInputFlags_RouteGlobal", &route_type, ImGuiInputFlags_RouteGlobal); ImGui::Indent(); ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal); @@ -7908,7 +7964,7 @@ static void DemoWindowInputs() // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h) //char str[16] = "Press Ctrl+A"; //ImGui::Spacing(); - //ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); + //ImGui::InputText("InputTextB", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly); //ImGuiID item_id = ImGui::GetItemID(); //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused"); //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "..."); @@ -7933,7 +7989,7 @@ static void DemoWindowInputs() ImGui::Text("(in PopupF)"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h) - //ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); + //ImGui::InputText("InputTextG", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly); //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "..."); ImGui::EndPopup(); } @@ -7948,7 +8004,7 @@ static void DemoWindowInputs() if (ImGui::TreeNode("Mouse Cursors")) { const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" }; - IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT); + IM_ASSERT(IM_COUNTOF(mouse_cursors_names) == ImGuiMouseCursor_COUNT); ImGuiMouseCursor current = ImGui::GetMouseCursor(); const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A"; @@ -7978,14 +8034,14 @@ static void DemoWindowInputs() { ImGui::Text("Use Tab/Shift+Tab to cycle through keyboard editable fields."); static char buf[32] = "hello"; - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("1", buf, IM_COUNTOF(buf)); + ImGui::InputText("2", buf, IM_COUNTOF(buf)); + ImGui::InputText("3", buf, IM_COUNTOF(buf)); ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); - ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("4 (tab skip)", buf, IM_COUNTOF(buf)); ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopItemFlag(); - ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("5", buf, IM_COUNTOF(buf)); ImGui::TreePop(); } @@ -7999,16 +8055,16 @@ static void DemoWindowInputs() static char buf[128] = "click on a button to set focus"; if (focus_1) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("1", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 1; if (focus_2) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("2", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 2; ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); if (focus_3) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("3 (tab skip)", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 3; ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopItemFlag(); @@ -8090,7 +8146,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding"); ImGui::Separator(); - ImGui::Text("(c) 2014-2025 Omar Cornut"); + ImGui::Text("(c) 2014-2026 Omar Cornut"); ImGui::Text("Developed by Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); ImGui::Text("If your company uses this, please consider funding the project."); @@ -8183,8 +8239,12 @@ void ImGui::ShowAboutWindow(bool* p_open) #endif #ifdef __EMSCRIPTEN__ ImGui::Text("define: __EMSCRIPTEN__"); +#ifdef __EMSCRIPTEN_MAJOR__ + ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_MAJOR__, __EMSCRIPTEN_MINOR__, __EMSCRIPTEN_TINY__); +#else ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); #endif +#endif #ifdef NDEBUG ImGui::Text("define: NDEBUG"); #endif @@ -8266,9 +8326,9 @@ bool ImGui::ShowStyleSelector(const char* label) static int style_idx = -1; const char* style_names[] = { "Dark", "Light", "Classic" }; bool ret = false; - if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_ARRAYSIZE(style_names)) ? style_names[style_idx] : "")) + if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_COUNTOF(style_names)) ? style_names[style_idx] : "")) { - for (int n = 0; n < IM_ARRAYSIZE(style_names); n++) + for (int n = 0; n < IM_COUNTOF(style_names); n++) { if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav)) { @@ -8431,6 +8491,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1); SeparatorText("Widgets"); + SliderFloat("ColorMarkerSize", &style.ColorMarkerSize, 0.0f, 8.0f, "%.0f"); Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0"); SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); @@ -8440,6 +8501,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f"); SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); + SliderFloat("ImageRounding", &style.ImageRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, 1.0f, "%.0f"); SeparatorText("Tooltips"); @@ -8483,7 +8545,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) } LogFinish(); } - SameLine(); SetNextItemWidth(120); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); + SameLine(); SetNextItemWidth(GetFontSize() * 10); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); SameLine(); Checkbox("Only Modified Colors", &output_only_modified); static ImGuiTextFilter filter; @@ -8842,8 +8904,8 @@ struct ExampleAppConsole char buf[1024]; va_list args; va_start(args, fmt); - vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); - buf[IM_ARRAYSIZE(buf)-1] = 0; + vsnprintf(buf, IM_COUNTOF(buf), fmt, args); + buf[IM_COUNTOF(buf)-1] = 0; va_end(args); Items.push_back(Strdup(buf)); } @@ -8971,7 +9033,7 @@ struct ExampleAppConsole // Command-line bool reclaim_focus = false; ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; - if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) + if (ImGui::InputText("Input", InputBuf, IM_COUNTOF(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) { char* s = InputBuf; Strtrim(s); @@ -9295,8 +9357,8 @@ static void ShowExampleAppLog(bool* p_open) const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }; for (int n = 0; n < 5; n++) { - const char* category = categories[counter % IM_ARRAYSIZE(categories)]; - const char* word = words[counter % IM_ARRAYSIZE(words)]; + const char* category = categories[counter % IM_COUNTOF(categories)]; + const char* word = words[counter % IM_COUNTOF(words)]; log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", ImGui::GetFrameCount(), category, ImGui::GetTime(), word); counter++; @@ -9397,7 +9459,7 @@ struct ExampleAppPropertyEditor ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); - if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) + if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) Filter.Build(); ImGui::PopItemFlag(); @@ -9703,7 +9765,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); - ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc)); + ImGui::Combo("Constraint", &type, test_desc, IM_COUNTOF(test_desc)); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); ImGui::Checkbox("Auto-resize", &auto_resize); @@ -9945,7 +10007,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape - //draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th); + //draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th); draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line @@ -10395,7 +10457,7 @@ void ShowExampleAppDocuments(bool* p_open) if (ImGui::BeginPopup("Rename")) { ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30); - if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue)) + if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_COUNTOF(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue)) { ImGui::CloseCurrentPopup(); app.RenamingDoc = NULL; @@ -10515,7 +10577,7 @@ struct ExampleAsset if (delta < 0) return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; } - return ((int)a->ID - (int)b->ID); + return (int)a->ID - (int)b->ID; } }; const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL; @@ -10793,7 +10855,7 @@ struct ExampleAssetsBrowser draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color if (ShowTypeOverlay && item_data->Type != 0) { - ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)]; + ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_COUNTOF(icon_type_overlay_colors)]; draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col); } if (display_label) diff --git a/core/deps/imgui/imgui_draw.cpp b/core/deps/imgui/imgui_draw.cpp index 1577e30615..67f2458c94 100755 --- a/core/deps/imgui/imgui_draw.cpp +++ b/core/deps/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.5 +// dear imgui, v1.92.6 // (drawing and font code) /* @@ -17,10 +17,11 @@ Index of this file: // [SECTION] ImFontAtlas: backend for stb_truetype // [SECTION] ImFontAtlas: glyph ranges helpers // [SECTION] ImFontGlyphRangesBuilder -// [SECTION] ImFont +// [SECTION] ImFontBaked, ImFont // [SECTION] ImGui Internal Render Helpers // [SECTION] Decompression code // [SECTION] Default font data (ProggyClean.ttf) +// [SECTION] Default font data (ProggyForever.ttf) */ @@ -81,6 +82,7 @@ Index of this file: #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers +#pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif //------------------------------------------------------------------------- @@ -103,6 +105,7 @@ namespace IMGUI_STB_NAMESPACE #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration #pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. +#pragma warning (disable: 5262) // (stb_truetype) implicit fall-through occurs here; are you missing a break statement? #pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. #pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. #endif @@ -389,11 +392,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) ImDrawListSharedData::ImDrawListSharedData() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); InitialFringeScale = 1.0f; - for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++) + for (int i = 0; i < IM_COUNTOF(ArcFastVtx); i++) { - const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx); + const float a = ((float)i * 2 * IM_PI) / (float)IM_COUNTOF(ArcFastVtx); ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); } ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); @@ -411,7 +414,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) IM_ASSERT(max_error > 0.0f); CircleSegmentMaxError = max_error; - for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) + for (int i = 0; i < IM_COUNTOF(CircleSegmentCounts); i++) { const float radius = (float)i; CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX); @@ -421,7 +424,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) ImDrawList::ImDrawList(ImDrawListSharedData* shared_data) { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); _SetDrawListSharedData(shared_data); } @@ -444,10 +447,13 @@ void ImDrawList::_SetDrawListSharedData(ImDrawListSharedData* data) // In the majority of cases, you would want to call PushClipRect() and PushTexture() after this. void ImDrawList::_ResetForNewFrame() { - // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. + // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory to match ImDrawCmdHeader. IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0); IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == sizeof(ImVec4)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureRef)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == offsetof(ImDrawCmdHeader, ClipRect)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == offsetof(ImDrawCmdHeader, TexRef)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == offsetof(ImDrawCmdHeader, VtxOffset)); if (_Splitter._Count > 1) _Splitter.Merge(this); @@ -643,7 +649,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy - if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) + if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts)) return _Data->CircleSegmentCounts[radius_idx]; // Use cached value else return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); @@ -2411,10 +2417,11 @@ void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, in // FIXME-NEWATLAS: Oversample specification could be more dynamic. For now, favoring automatic selection. ImFontConfig::ImFontConfig() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; OversampleH = 0; // Auto == 1 or 2 depending on size OversampleV = 0; // Auto == 1 + ExtraSizeScale = 1.0f; GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; RasterizerDensity = 1.0f; @@ -2513,6 +2520,8 @@ void ImTextureData::DestroyPixels() //----------------------------------------------------------------------------- // - ImFontAtlas::AddFont() // - ImFontAtlas::AddFontDefault() +// - ImFontAtlas::AddFontDefaultBitmap() +// - ImFontAtlas::AddFontDefaultVector() // - ImFontAtlas::AddFontFromFileTTF() // - ImFontAtlas::AddFontFromMemoryTTF() // - ImFontAtlas::AddFontFromMemoryCompressedTTF() @@ -2632,7 +2641,7 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3 ImFontAtlas::ImFontAtlas() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); TexDesiredFormat = ImTextureFormat_RGBA32; TexGlyphPadding = 1; TexMinWidth = 512; @@ -2876,6 +2885,71 @@ void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFo } } +void ImFontAtlasTextureBlockResize(const ImU32* src_pixels, const int src_w, const int src_h, ImU32* dst_pixels, const int dst_w, const int dst_h) +{ +#if 0 + // Point Sampling / Nearest Neighbor + for (int y = 0; y < dst_h; y++) + { + const int src_y = ImFloor(((y + 0.5f) * src_h) / dst_h); + for (int x = 0; x < dst_w; x++) + { + const int src_x = ImFloor(((x + 0.5f) * src_w) / dst_w); + dst_pixels[y * dst_w + x] = src_pixels[src_y * src_w + src_x]; + } + } +#else + // Box sampling - Imagine projecting the new, smaller pixels onto the larger source, covering multiple pixel. + for (int y = 0; y < dst_h; y++) + { + for (int x = 0; x < dst_w; x++) + { + // We perform a weighted mean. + ImVec4 color; + float weight_sum = 0.0f; + + // Walk from upper edge to bottom edge (vertical) + const float edge_up = ((float)y * src_h) / dst_h; + const float edge_down = ((y + 1.0f) * src_h) / dst_h; + for (float frac_pos_y = edge_up; frac_pos_y < edge_down;) + { + const int src_y = (int)ImFloor(frac_pos_y); IM_ASSERT(src_y < src_h); + const float frac_y = 1.0f - (frac_pos_y - src_y); + + // Walk from left edge to right edge (horizontal) + const float edge_left = ((float)x * src_w) / dst_w; + const float edge_right = ((x + 1.0f) * src_w) / dst_w; + for (float frac_pos_x = edge_left; frac_pos_x < edge_right;) + { + const int src_x = (int)ImFloor(frac_pos_x); IM_ASSERT(src_x < src_w); + const float frac_x = 1.0f - (frac_pos_x - src_x); + + const float src_pixel_weight = frac_x * frac_y; + + const ImVec4 pixel_color = ImGui::ColorConvertU32ToFloat4(src_pixels[src_y * src_w + src_x]); + color.x += pixel_color.x * src_pixel_weight; + color.y += pixel_color.y * src_pixel_weight; + color.z += pixel_color.z * src_pixel_weight; + color.w += pixel_color.w * src_pixel_weight; + weight_sum += src_pixel_weight; + + frac_pos_x += frac_x; + } + + frac_pos_y += frac_y; + } + + color.x /= weight_sum; + color.y /= weight_sum; + color.z /= weight_sum; + color.w /= weight_sum; + + dst_pixels[y * dst_w + x] = ImGui::ColorConvertFloat4ToU32(color); + } + } +#endif +} + // Source buffer may be written to (used for in-place mods). // Post-process hooks may eventually be added here. void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data) @@ -3028,6 +3102,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) ImFontAtlasBuildInit(this); // Create new font + const bool is_first_font = (Fonts.Size == 0); ImFont* font; if (!font_cfg_in->MergeMode) { @@ -3040,8 +3115,9 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) } else { - IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. + IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back(); + ImFontAtlasFontDiscardBakes(this, font, 0); // Need to discard bakes if the font was already used, because baked->FontLoaderDatas[] will change size. (#9162) } // Add to list @@ -3052,12 +3128,6 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) font->Sources.push_back(font_cfg); ImFontAtlasBuildUpdatePointers(this); // Pointers to Sources are otherwise dangling after we called Sources.push_back(). - if (font_cfg->FontDataOwnedByAtlas == false) - { - font_cfg->FontDataOwnedByAtlas = true; - font_cfg->FontData = ImMemdup(font_cfg->FontData, (size_t)font_cfg->FontDataSize); - } - // Sanity check // We don't round cfg.SizePixels yet as relative size of merged fonts are used afterwards. if (font_cfg->GlyphExcludeRanges != NULL) @@ -3090,6 +3160,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) } ImFontAtlasFontSourceAddToFont(this, font, font_cfg); + if (is_first_font) + ImFontAtlasBuildNotifySetFont(this, NULL, font); return font; } @@ -3108,32 +3180,72 @@ static void Decode85(const unsigned char* src, unsigned char* dst) } } #ifndef IMGUI_DISABLE_DEFAULT_FONT -static const char* GetDefaultCompressedFontDataTTF(int* out_size); +static const char* GetDefaultCompressedFontDataProggyClean(int* out_size); +static const char* GetDefaultCompressedFontDataProggyForever(int* out_size); #endif -// Load embedded ProggyClean.ttf at size 13, disable oversampling -// If you want a similar font which may be better scaled, consider using ProggyVector from the same author! -ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) +// This duplicates some of the logic in UpdateFontsNewFrame() which is a bit chicken-and-eggy/tricky to extract due to variety of codepaths and possible initialization ordering. +static float GetExpectedContextFontSize(ImGuiContext* ctx) +{ + return ((ctx->Style.FontSizeBase > 0.0f) ? ctx->Style.FontSizeBase : 13.0f) * ctx->Style.FontScaleMain * ctx->Style.FontScaleDpi; +} + +// Legacy function with heuristic to select Pixel or Vector font. +// The selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold at the time of adding the default font. +// Prefer calling AddFontDefaultVector() or AddFontDefaultBitmap() based on your own logic. +ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg) +{ + if (OwnerContext == NULL || GetExpectedContextFontSize(OwnerContext) >= 15.0f) + return AddFontDefaultVector(font_cfg); + else + return AddFontDefaultBitmap(font_cfg); +} + +// Load embedded ProggyClean.ttf. Default size 13, disable oversampling. +// If you want a similar font which may be better scaled, consider using AddFontDefaultVector(). +ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) { #ifndef IMGUI_DISABLE_DEFAULT_FONT ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (!font_cfg_template) - { - font_cfg.OversampleH = font_cfg.OversampleV = 1; - font_cfg.PixelSnapH = true; - } + font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers. if (font_cfg.SizePixels <= 0.0f) - font_cfg.SizePixels = 13.0f * 1.0f; + font_cfg.SizePixels = 13.0f; // This only serves (1) as a reference for GlyphOffset.y setting and (2) as a default for pre-1.92 backend. if (font_cfg.Name[0] == '\0') - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf"); + ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyClean.ttf"); font_cfg.EllipsisChar = (ImWchar)0x0085; - font_cfg.GlyphOffset.y += 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units + font_cfg.GlyphOffset.y += 1.0f * (font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units int ttf_compressed_size = 0; - const char* ttf_compressed = GetDefaultCompressedFontDataTTF(&ttf_compressed_size); + const char* ttf_compressed = GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size); return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else - IM_ASSERT(0 && "AddFontDefault() disabled in this build."); + IM_ASSERT(0 && "Function is disabled in this build."); + IM_UNUSED(font_cfg_template); + return NULL; +#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +} + +// Load a minimal version of ProggyForever, designed to match our good old ProggyClean, but nicely scalable. +// (See build script in https://github.com/ocornut/proggyforever for details) +ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) +{ +#ifndef IMGUI_DISABLE_DEFAULT_FONT + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + if (!font_cfg_template) + font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers. + if (font_cfg.SizePixels <= 0.0f) + font_cfg.SizePixels = 13.0f; + if (font_cfg.Name[0] == '\0') + ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyForever.ttf"); + font_cfg.ExtraSizeScale *= 1.015f; // Match ProggyClean + font_cfg.GlyphOffset.y += 0.5f * (font_cfg.SizePixels / 16.0f); // Closer match ProggyClean + avoid descenders going too high (with current code). + + int ttf_compressed_size = 0; + const char* ttf_compressed = GetDefaultCompressedFontDataProggyForever(&ttf_compressed_size); + return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); +#else + IM_ASSERT(0 && "Function is disabled in this build."); IM_UNUSED(font_cfg_template); return NULL; #endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT @@ -3159,7 +3271,7 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, // Store a short copy of filename into into the font name for convenience const char* p; for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p); + ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "%s", p); } return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges); } @@ -3211,6 +3323,9 @@ void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* shared_data->Font = new_font; if (ImGuiContext* ctx = shared_data->Context) { + if (ctx->FrameCount == 0 && old_font == NULL) // While this should work either way, we save ourselves the bother / debugging confusion of running ImGui code so early when it is not needed. + continue; + if (ctx->IO.FontDefault == old_font) ctx->IO.FontDefault = new_font; if (ctx->Font == old_font) @@ -3233,7 +3348,6 @@ void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* void ImFontAtlas::RemoveFont(ImFont* font) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); - font->ClearOutputData(); ImFontAtlasFontDestroyOutput(this, font); for (ImFontConfig* src : font->Sources) @@ -3396,6 +3510,7 @@ void ImFontAtlasBuildMain(ImFontAtlas* atlas) void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v) { + // (Only used by stb_truetype builder) // Automatically disable horizontal oversampling over size 36 const float raster_size = baked->Size * baked->RasterizerDensity * src->RasterizerDensity; *out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : (raster_size > 36.0f || src->PixelSnapH) ? 1 : 2; @@ -3610,6 +3725,12 @@ void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font) } } +void ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font) +{ + ImFontAtlasFontDestroyOutput(atlas, font); + ImFontAtlasFontInitOutput(atlas, font); +} + //----------------------------------------------------------------------------------------------------------------------------- bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src) @@ -3638,7 +3759,7 @@ void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src) IM_UNUSED(atlas); // IF YOU GET A CRASH IN THE IM_FREE() CALL HERE AND USED AddFontFromMemoryTTF(): // - DUE TO LEGACY REASON AddFontFromMemoryTTF() TRANSFERS MEMORY OWNERSHIP BY DEFAULT. - // - IT WILL THEREFORE CRASH WHEN PASSED DATA WHICH MAY NOT BE FREEED BY IMGUI. + // - IT WILL THEREFORE CRASH WHEN PASSED DATA WHICH MAY NOT BE FREED BY IMGUI. // - USE `ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; io.Fonts->AddFontFromMemoryTTF(....., &cfg);` to disable passing ownership/ // WE WILL ADDRESS THIS IN A FUTURE REWORK OF THE API. if (src->FontDataOwnedByAtlas) @@ -4009,7 +4130,7 @@ static void ImFontAtlasDebugWriteTexToDisk(ImTextureData* tex, const char* descr { ImGuiContext& g = *GImGui; char buf[128]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "[%05d] Texture #%03d - %s.png", g.FrameCount, tex->UniqueID, description); + ImFormatString(buf, IM_COUNTOF(buf), "[%05d] Texture #%03d - %s.png", g.FrameCount, tex->UniqueID, description); stbi_write_png(buf, tex->Width, tex->Height, tex->BytesPerPixel, tex->Pixels, tex->GetPitch()); // tex->BytesPerPixel is technically not component, but ok for the formats we support. } #endif @@ -4238,6 +4359,10 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) ImFontAtlasUpdateDrawListsSharedData(atlas); //atlas->TexIsBuilt = true; + + // Lazily initialize char/text classifier + // FIXME: This could be practically anywhere, and should eventually be parameters to CalcTextSize/word-wrapping code, but there's no obvious spot now. + ImTextInitClassifiers(); } // Destroy builder and all cached glyphs. Do not destroy actual fonts. @@ -4544,16 +4669,16 @@ void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas) if (tex->Status == ImTextureStatus_WantCreate) IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: create %dx%d\n", tex->UniqueID, tex->Width, tex->Height); else if (tex->Status == ImTextureStatus_WantDestroy) - IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, IM_TEXTUREID_TO_U64(tex->TexID), tex->BackendUserData); + IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, ImGui::DebugTextureIDToU64(tex->TexID), tex->BackendUserData); else if (tex->Status == ImTextureStatus_WantUpdates) { - IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); + IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); for (const ImTextureRect& r : tex->Updates) { IM_UNUSED(r); IM_ASSERT(r.x >= 0 && r.y >= 0); IM_ASSERT(r.x + r.w <= tex->Width && r.y + r.h <= tex->Height); // In theory should subtract PackPadding but it's currently part of atlas and mid-frame change would wreck assert. - //IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); + //IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); } } } @@ -4583,14 +4708,14 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* IM_ASSERT(src->FontLoaderData == NULL); // Initialize helper structure for font loading and verify that the TTF/OTF data is correct - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)src->FontData, src->FontNo); + const int font_offset = stbtt_GetFontOffsetForIndex((const unsigned char*)src->FontData, src->FontNo); if (font_offset < 0) { IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_GetFontOffsetForIndex(): FontData is incorrect, or FontNo cannot be found."); return false; } - if (!stbtt_InitFont(&bd_font_data->FontInfo, (unsigned char*)src->FontData, font_offset)) + if (!stbtt_InitFont(&bd_font_data->FontInfo, (const unsigned char*)src->FontData, font_offset)) { IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize."); @@ -4602,12 +4727,10 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* if (src->MergeMode && src->SizePixels == 0.0f) src->SizePixels = ref_size; - if (src->SizePixels >= 0.0f) - bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f); - else - bd_font_data->ScaleFactor = stbtt_ScaleForMappingEmToPixels(&bd_font_data->FontInfo, 1.0f); + bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f); if (src->MergeMode && src->SizePixels != 0.0f && ref_size != 0.0f) bd_font_data->ScaleFactor *= src->SizePixels / ref_size; // FIXME-NEWATLAS: Should tidy up that a bit + bd_font_data->ScaleFactor *= src->ExtraSizeScale; return true; } @@ -4640,7 +4763,7 @@ static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig { // FIXME-NEWFONTS: reevaluate how to use sizing metrics // FIXME-NEWFONTS: make use of line gap value - float scale_for_layout = bd_font_data->ScaleFactor * baked->Size; + const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size / src->ExtraSizeScale; int unscaled_ascent, unscaled_descent, unscaled_line_gap; stbtt_GetFontVMetrics(&bd_font_data->FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); baked->Ascent = ImCeil(unscaled_ascent * scale_for_layout); @@ -4715,12 +4838,8 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; - float font_off_x = (src->GlyphOffset.x * offsets_scale); - float font_off_y = (src->GlyphOffset.y * offsets_scale); - if (src->PixelSnapH) // Snap scaled offset. This is to mitigate backward compatibility issues for GlyphOffset, but a better design would be welcome. - font_off_x = IM_ROUND(font_off_x); - if (src->PixelSnapV) - font_off_y = IM_ROUND(font_off_y); + float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset. + float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f); font_off_x += sub_x; font_off_y += sub_y + IM_ROUND(baked->Ascent); float recip_h = 1.0f / (oversample_h * rasterizer_density); @@ -4892,11 +5011,11 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() 0xFF00, 0xFFEF, // Half-width characters 0xFFFD, 0xFFFD // Invalid }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; + static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) { memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); + UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges)); } return &full_ranges[0]; } @@ -4982,11 +5101,11 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 0xFF00, 0xFFEF, // Half-width characters 0xFFFD, 0xFFFD // Invalid }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; + static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) { memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); + UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges)); } return &full_ranges[0]; } @@ -5075,12 +5194,12 @@ void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) } //----------------------------------------------------------------------------- -// [SECTION] ImFont +// [SECTION] ImFontBaked, ImFont //----------------------------------------------------------------------------- ImFontBaked::ImFontBaked() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); FallbackGlyphIndex = -1; } @@ -5097,7 +5216,7 @@ void ImFontBaked::ClearOutputData() ImFont::ImFont() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS Scale = 1.0f; #endif @@ -5112,7 +5231,7 @@ void ImFont::ClearOutputData() { if (ImFontAtlas* atlas = OwnerAtlas) ImFontAtlasFontDiscardBakes(atlas, this, 0); - FallbackChar = EllipsisChar = 0; + //FallbackChar = EllipsisChar = 0; memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); LastBaked = NULL; } @@ -5373,22 +5492,69 @@ const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_e if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0) while (text < text_end && ImCharIsBlankA(*text)) text++; - if (*text == '\n') + if (text < text_end && *text == '\n') text++; return text; } +void ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class) +{ + for (unsigned int c = codepoint_min; c < codepoint_end; c++) + ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c); +} + +void ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c) +{ + IM_ASSERT(c >= codepoint_min && c < codepoint_end); + IM_UNUSED(codepoint_end); + c -= codepoint_min; + const ImU32 shift = (c & 15) << 1; + bits[c >> 4] = (bits[c >> 4] & ~(0x03 << shift)) | (char_class << shift); +} + +void ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s) +{ + const char* s_end = s + strlen(s); + while (*s) + { + unsigned int c; + s += ImTextCharFromUtf8(&c, s, s_end); + ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c); + } +} + +#define ImTextClassifierGet(_BITS, _CHAR_OFFSET) ((_BITS[(_CHAR_OFFSET) >> 4] >> (((_CHAR_OFFSET) & 15) << 1)) & 0x03) + +// 2-bit per character +static ImU32 g_CharClassifierIsSeparator_0000_007f[128 / 16] = {}; +static ImU32 g_CharClassifierIsSeparator_3000_300f[ 16 / 16] = {}; + +void ImTextInitClassifiers() +{ + if (ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, ',') != 0) + return; + + // List of hardcoded separators: .,;!?'" + // Making this dynamic given known ranges is trivial BUT requires us to standardize where you pass them as parameters. (#3002, #8503) + ImTextClassifierClear(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Other); + ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Blank, " \t"); + ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Punct, ".,;!?\""); + + ImTextClassifierClear(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Other); + ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Blank, 0x3000); + ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3001); + ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3002); +} + // Simple word-wrapping for English, not full-featured. Please submit failing cases! // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end. -// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) +// Refer to imgui_test_suite's "drawlist_text_wordwrap_1" for tests. const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags) { // For references, possible wrap point marked with ^ // "aaa bbb, ccc,ddd. eee fff. ggg!" // ^ ^ ^ ^ ^__ ^ ^ - // List of hardcoded separators: .,;!?'" - // Skip extra blanks after a line returns (that includes not counting them in width computation) // e.g. "Hello world" --> "Hello" "World" @@ -5399,16 +5565,20 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t const float scale = size / baked->Size; float line_width = 0.0f; - float word_width = 0.0f; float blank_width = 0.0f; wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters - const char* word_end = text; - const char* prev_word_end = NULL; - bool inside_word = true; - const char* s = text; IM_ASSERT(text_end != NULL); + + int prev_type = ImWcharClass_Other; + const bool keep_blanks = (flags & ImDrawTextFlags_WrapKeepBlanks) != 0; + + // Find next wrapping point + //const char* span_begin = s; + const char* span_end = s; + float span_width = 0.0f; + while (s < text_end) { unsigned int c = (unsigned int)*s; @@ -5424,7 +5594,7 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t return s; // Direct return, skip "Wrap_width is too small to fit anything" path. if (c == '\r') { - s = next_s; + s = next_s; // Fast-skip continue; } } @@ -5434,46 +5604,56 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t if (char_width < 0.0f) char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c); - if (ImCharIsBlankW(c)) + // Classify current character + int curr_type; + if (c < 128) + curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, c); + else if (c >= 0x3000 && c < 0x3010) + curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_3000_300f, c & 15); //-V578 + else + curr_type = ImWcharClass_Other; + + if (curr_type == ImWcharClass_Blank) { - if (inside_word) + // End span: 'A ' or '. ' + if (prev_type != ImWcharClass_Blank && !keep_blanks) { - line_width += blank_width; - blank_width = 0.0f; - word_end = s; + span_end = s; + line_width += span_width; + span_width = 0.0f; } blank_width += char_width; - inside_word = false; } else { - word_width += char_width; - if (inside_word) + // End span: '.X' unless X is a digit + if (prev_type == ImWcharClass_Punct && curr_type != ImWcharClass_Punct && !(c >= '0' && c <= '9')) // FIXME: Digit checks might be removed if allow custom separators (#8503) { - word_end = next_s; + span_end = s; + line_width += span_width + blank_width; + span_width = blank_width = 0.0f; } - else + // End span: 'A ' or '. ' + else if (prev_type == ImWcharClass_Blank && keep_blanks) { - prev_word_end = word_end; - line_width += word_width + blank_width; - if ((flags & ImDrawTextFlags_WrapKeepBlanks) && line_width <= wrap_width) - prev_word_end = s; - word_width = blank_width = 0.0f; + span_end = s; + line_width += span_width + blank_width; + span_width = blank_width = 0.0f; } - - // Allow wrapping after punctuation. - inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"' && c != 0x3001 && c != 0x3002); + span_width += char_width; } - // We ignore blank width at the end of the line (they can be skipped) - if (line_width + word_width > wrap_width) + if (span_width + blank_width + line_width > wrap_width) { - // Words that cannot possibly fit within an entire line will be cut anywhere. - if (word_width < wrap_width) - s = prev_word_end ? prev_word_end : word_end; - break; + if (span_width + blank_width > wrap_width) + break; + // FIXME: Narrow wrapping e.g. "A quick brown" -> "Quic|k br|own", would require knowing if span is going to be longer than wrap_width. + //if (span_width > wrap_width && !is_blank && !was_blank) + // return s; + return span_end; } + prev_type = curr_type; s = next_s; } @@ -5625,7 +5805,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im } // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. -// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2025-2025. Use ImDrawList::AddText(). +// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList::AddText(). void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags) { // Align to be pixel perfect @@ -5839,7 +6019,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im // - RenderBullet() // - RenderCheckMark() // - RenderArrowPointingAt() -// - RenderRectFilledRangeH() +// - RenderRectFilledInRangeH() // - RenderRectFilledWithHole() //----------------------------------------------------------------------------- // Function in need of a redesign (legacy mess) @@ -5922,15 +6102,15 @@ static inline float ImAcos01(float x) } // FIXME: Cleanup and move code to ImDrawList. -void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding) +// - Before 2025-12-04: RenderRectFilledRangeH() with 'float x_start_norm, float x_end_norm` <- normalized +// - After 2025-12-04: RenderRectFilledInRangeH() with 'float x1, float x2' <- absolute coords!! +void ImGui::RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding) { - if (x_end_norm == x_start_norm) + if (fill_x0 > fill_x1) return; - if (x_start_norm > x_end_norm) - ImSwap(x_start_norm, x_end_norm); - ImVec2 p0 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y); - ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y); + ImVec2 p0 = ImVec2(fill_x0, rect.Min.y); + ImVec2 p1 = ImVec2(fill_x1, rect.Max.y); if (rounding == 0.0f) { draw_list->AddRectFilled(p0, p1, col, 0.0f); @@ -5998,6 +6178,17 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); } +ImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold) +{ + bool round_l = r_in.Min.x <= r_outer.Min.x + threshold; + bool round_r = r_in.Max.x >= r_outer.Max.x - threshold; + bool round_t = r_in.Min.y <= r_outer.Min.y + threshold; + bool round_b = r_in.Max.y >= r_outer.Max.y - threshold; + return ImDrawFlags_RoundCornersNone + | ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0) + | ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0); +} + // Helper for ColorPicker4() // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. // Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. @@ -6161,11 +6352,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i //----------------------------------------------------------------------------- // [SECTION] Default font data (ProggyClean.ttf) //----------------------------------------------------------------------------- -// ProggyClean.ttf -// Copyright (c) 2004, 2005 Tristan Grimmer -// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download) -// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php -// If you want a similar font which may be better scaled, consider using ProggyVector from the same author! +// MIT License / Copyright (c) 2004, 2005 Tristan Grimmer +// Download and more information at https://github.com/bluescan/proggyfonts //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_DEFAULT_FONT @@ -6343,11 +6531,274 @@ static const unsigned char proggy_clean_ttf_compressed_data[9583] = 239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,35,57,102,0,0,5,250,72,249,98,247, }; -static const char* GetDefaultCompressedFontDataTTF(int* out_size) +static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) { *out_size = proggy_clean_ttf_compressed_size; return (const char*)proggy_clean_ttf_compressed_data; } + +//----------------------------------------------------------------------------- +// [SECTION] Default font data (ProggyForever-Regular-minimal.ttf) +//----------------------------------------------------------------------------- +// Based on ProggyForever: https://github.com/ocornut/proggyforever +// MIT license / Copyright (c) 2026 Disco Hello, Copyright (c) 2019,2023 Tristan Grimmer +//----------------------------------------------------------------------------- + +// File: 'output/ProggyForever-Regular-minimal.ttf' (18556 bytes) +// Exported using binary_to_compressed_c.exe -u8 "output/ProggyForever-Regular-minimal.ttf" proggy_forever_minimal_ttf +static const unsigned int proggy_forever_minimal_ttf_compressed_size = 14562; +static const unsigned char proggy_forever_minimal_ttf_compressed_data[14562] = +{ + 87,188,0,0,0,0,0,0,0,0,72,124,0,4,0,0,55,0,1,0,0,0,14,0,128,0,3,0,96,70,70,84,77,176,111,174,190,0,0,72,96,130,21,40,28,71,68,69,70,0,136,0,105,130,15,32,64,130,15,44,30,79,83,47,50, + 104,97,19,194,0,0,1,104,130,15,44,96,99,109,97,112,177,173,221,139,0,0,3,80,130,19,44,114,99,118,116,32,0,33,2,121,0,0,4,196,130,31,38,4,103,97,115,112,255,255,130,89,34,0,72,56,130, + 15,56,8,103,108,121,102,239,245,108,207,0,0,6,76,0,0,62,224,104,101,97,100,44,57,58,3,130,27,32,236,130,3,33,54,104,130,16,35,4,62,0,230,130,75,32,36,130,15,39,36,104,109,116,120,24, + 22,19,130,95,33,1,200,130,19,40,136,108,111,99,97,80,9,64,114,130,95,131,15,39,130,109,97,120,112,1,7,0,131,31,32,72,130,47,44,32,110,97,109,101,10,160,159,151,0,0,69,44,130,47,44, + 68,112,111,115,116,70,77,175,253,0,0,70,112,130,15,32,197,132,235,32,1,130,9,42,224,136,151,95,15,60,245,0,11,3,232,130,55,36,0,229,175,187,66,132,7,42,178,59,232,255,225,255,68,1, + 215,2,176,130,15,34,8,0,2,130,5,131,2,130,51,39,2,131,255,71,0,0,1,184,130,31,34,226,1,215,132,73,131,25,135,3,32,4,132,17,37,192,0,90,0,5,0,131,0,33,2,0,130,44,132,19,34,64,0,46,130, + 11,38,0,0,4,1,184,1,144,131,29,35,2,138,2,187,130,17,32,140,133,7,38,1,223,0,49,1,2,0,138,0,37,128,0,0,7,16,0,136,123,33,0,88,130,0,37,0,64,0,32,32,172,133,131,34,2,175,0,131,63,33, + 3,0,130,0,35,1,133,2,6,130,6,38,32,0,1,1,184,0,33,130,9,130,161,32,0,132,3,39,0,166,0,116,255,249,0,49,130,113,36,4,0,185,0,135,130,1,38,27,0,22,0,154,0,53,130,25,40,39,0,41,0,79,0, + 50,0,45,130,17,32,49,130,11,33,46,0,131,17,36,166,0,143,0,24,132,1,34,48,255,225,130,55,34,38,0,47,130,23,44,52,0,58,0,35,0,42,0,66,0,65,0,19,130,79,32,19,130,47,38,35,0,44,0,13,0, + 38,130,21,38,9,0,37,0,13,255,250,130,121,36,5,0,33,0,104,130,47,40,104,0,37,255,242,0,124,0,48,130,95,32,59,130,3,34,37,0,40,130,5,36,62,0,139,0,100,130,57,36,133,0,20,0,62,130,55, + 32,50,130,19,40,69,0,69,0,87,0,54,0,29,130,167,32,20,130,107,36,64,0,63,0,188,130,3,36,22,0,21,0,159,130,7,36,40,0,55,0,5,130,17,34,64,0,109,130,33,34,92,0,50,130,5,42,109,0,101,0, + 24,0,115,0,109,0,131,130,121,32,48,130,39,36,143,0,114,0,83,130,77,32,19,130,157,34,18,0,73,130,49,32,5,136,3,32,2,130,85,33,52,0,133,1,33,66,0,133,1,32,17,130,125,32,35,130,209,131, + 3,36,46,0,1,0,46,132,1,32,47,130,51,32,42,130,25,32,38,130,213,135,3,34,5,0,59,130,99,32,37,132,3,34,51,0,68,132,1,32,42,130,189,33,43,0,135,1,36,24,0,12,0,61,134,1,32,26,130,131,38, + 26,0,30,0,0,0,3,134,3,32,28,130,93,130,10,35,0,108,0,3,132,9,36,28,0,4,0,80,130,16,34,16,0,16,132,35,46,126,0,133,0,171,0,210,0,211,0,255,32,172,255,255,130,25,32,32,130,17,34,161, + 0,174,130,17,32,212,131,17,45,255,227,255,221,255,194,255,192,255,95,255,191,224,19,132,67,130,36,139,2,34,1,6,0,136,22,35,1,2,0,0,66,53,9,32,0,133,0,32,1,130,142,8,148,4,5,6,7,8,9, + 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69, + 70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,0,132,133,135,137,145,149,155,160,159,161,163,162,164,166,168,167,169,170,172,171,173,174,176,178, + 177,179,181,180,185,184,186,187,0,112,100,101,105,0,118,158,110,107,0,116,106,0,134,151,0,113,0,0,103,117,132,158,38,108,122,0,165,183,127,99,132,11,38,109,123,0,0,128,131,148,132, + 11,130,4,37,182,0,190,60,0,191,130,8,34,0,0,119,130,5,47,130,138,129,139,136,141,142,143,140,50,147,0,146,153,154,152,130,18,32,111,130,3,32,120,130,3,130,2,34,33,2,121,130,5,33,42, + 0,133,1,9,155,68,0,86,0,134,0,210,1,14,1,104,1,116,1,148,1,180,1,212,1,232,2,6,2,20,2,38,2,54,2,100,2,122,2,172,2,232,3,2,3,60,3,116,3,134,3,210,4,10,4,40,4,82,4,102,4,122,4,142,4, + 204,5,36,5,60,5,118,5,158,5,184,5,208,5,228,6,24,6,44,6,68,6,94,6,118,6,134,6,160,6,182,6,224,7,14,7,64,7,106,7,182,7,200,7,238,8,0,8,28,8,52,8,72,8,96,8,114,8,130,8,148,8,166,8,180, + 8,194,9,12,9,58,9,92,9,140,9,182,9,214,10,14,10,44,10,74,10,110,10,134,10,150,10,198,10,230,11,4,11,50,11,96,11,130,11,188,11,216,11,250,12,14,12,42,12,66,12,114,12,142,12,198,12,210, + 13,10,13,62,13,126,13,152,13,198,13,238,14,34,14,72,14,90,14,198,14,232,15,32,15,106,15,134,15,200,15,214,15,244,16,16,16,56,16,114,16,128,16,182,16,212,16,230,17,2,17,22,17,60,17, + 86,17,132,17,194,18,20,18,76,18,108,18,140,18,176,18,228,19,24,19,74,19,110,19,168,19,198,19,228,20,6,20,56,20,86,20,116,20,150,20,200,20,252,21,44,21,92,21,144,21,212,22,24,22,50, + 22,112,22,148,22,186,22,226,23,28,23,56,23,90,23,160,23,246,24,76,24,164,25,18,25,124,25,226,26,94,26,150,26,198,26,248,27,44,27,114,27,144,27,174,27,208,28,2,28,74,28,136,28,174,28, + 212,28,254,29,60,29,118,29,172,29,230,30,16,30,58,30,104,30,166,30,206,30,244,31,48,31,112,0,0,0,2,0,33,0,0,1,42,2,154,0,3,0,7,0,46,177,1,0,47,60,178,7,4,0,237,50,177,6,5,220,60,178, + 3,2,130,10,34,0,177,3,131,22,32,5,131,22,39,178,7,6,1,252,60,178,1,131,23,52,51,17,33,17,39,51,17,35,33,1,9,232,199,199,2,154,253,102,33,2,88,131,83,38,166,255,254,1,18,2,35,130,81, + 61,13,0,0,54,50,22,20,6,34,38,52,55,35,3,55,51,21,198,44,32,32,44,32,85,59,14,1,83,105,31,131,11,36,122,1,4,91,91,130,135,38,116,1,70,1,68,2,7,132,135,37,0,1,35,53,51,7,130,3,8,40, + 1,67,64,64,142,65,65,1,70,192,192,192,0,2,255,248,0,3,1,191,2,3,0,3,0,31,0,0,63,1,35,7,37,35,7,51,21,35,7,35,55,132,3,34,53,51,55,130,51,35,55,51,7,51,131,3,53,254,30,92,30,1,29,104, + 31,92,106,38,59,38,92,38,58,38,96,110,31,98,112,134,11,34,90,201,115,130,0,33,54,143,130,0,35,54,115,54,144,130,0,32,0,130,85,8,37,49,255,241,1,135,2,22,0,6,0,11,0,52,0,0,55,62,1,53, + 52,38,47,1,53,6,21,20,23,30,3,21,20,7,21,35,53,46,1,130,16,8,109,30,2,23,53,46,3,53,52,54,55,53,51,21,30,1,31,1,21,38,39,21,50,249,32,41,36,37,60,72,137,22,40,46,28,141,60,36,67,15, + 15,6,21,69,37,31,44,43,22,75,65,60,25,55,15,15,43,67,3,81,7,37,31,30,32,10,73,135,12,61,44,30,4,15,28,50,33,114,15,44,43,1,14,6,6,63,4,11,22,3,162,6,15,26,42,29,53,67,9,45,42,2,11, + 5,5,62,29,4,150,0,5,130,1,36,11,1,178,1,250,130,161,52,11,0,19,0,27,0,35,0,0,1,51,1,35,36,50,54,52,38,34,6,20,65,97,7,34,2,20,22,132,17,65,111,5,53,54,50,1,74,71,254,222,71,1,16,41, + 30,30,41,29,7,84,60,60,84,59,174,130,9,34,29,41,122,130,9,51,59,84,1,249,254,21,47,27,39,27,27,39,115,56,79,56,56,79,1,26,131,11,33,27,7,131,11,8,59,56,0,0,0,3,0,4,255,246,1,179,2, + 21,0,9,0,28,0,62,0,0,55,50,55,46,1,39,6,21,20,22,3,28,2,30,5,23,62,1,53,52,38,35,34,6,1,23,35,38,39,6,35,34,38,53,52,54,55,46,2,130,5,8,101,51,50,22,21,20,14,2,7,22,23,54,53,55,20, + 206,52,37,25,110,24,65,80,41,1,2,3,6,8,12,7,37,37,31,23,24,35,1,2,67,72,13,21,60,89,72,104,52,44,22,19,15,63,60,61,51,15,32,25,24,89,58,36,61,44,36,28,131,28,54,59,48,62,1,125,1,7, + 3,7,5,10,9,13,16,9,25,40,24,21,25,32,254,143,79,15,24,49,96,66,50,74,33,25,130,17,56,41,69,55,49,20,36,34,20,17,107,69,65,72,1,108,0,1,0,185,1,70,0,255,2,8,130,189,42,0,19,35,53,51, + 254,68,68,1,71,193,130,23,8,59,135,255,193,1,49,2,62,0,17,0,0,1,14,1,20,22,23,35,46,4,52,62,2,63,1,1,49,43,55,55,43,60,4,15,38,29,24,23,32,33,11,11,2,61,60,177,161,177,60,6,21,68,70, + 103,97,102,75,61,16,16,130,50,32,0,138,63,34,19,30,4,130,220,40,15,1,35,62,1,52,38,39,195,137,57,32,60,131,73,44,2,61,6,22,68,71,103,97,101,75,60,16,16,132,73,32,0,131,63,38,27,0,69, + 1,157,1,199,132,127,8,41,7,23,7,39,21,35,53,7,39,55,39,55,23,53,51,21,55,1,157,131,131,33,128,64,129,32,131,131,32,129,64,128,1,77,71,71,50,70,142,142,70,50,134,7,49,0,1,0,22,0,74, + 1,162,1,185,0,11,0,0,19,51,21,35,130,62,130,221,37,53,51,251,166,166,62,130,2,38,1,31,59,154,154,59,154,130,39,39,154,255,129,1,30,0,107,0,130,180,32,54,65,133,5,8,37,15,1,39,54,55, + 46,1,53,52,209,45,31,21,31,30,11,11,26,55,10,17,25,107,32,22,32,61,40,32,7,7,37,48,41,1,31,21,22,131,163,54,53,0,227,1,131,1,32,0,3,0,0,37,33,53,33,1,131,254,178,1,78,228,60,132,191, + 40,166,255,255,1,18,0,105,0,7,67,209,9,67,203,5,37,105,31,44,31,31,44,132,35,38,39,255,222,1,144,2,39,131,63,49,9,1,35,1,1,144,254,221,69,1,34,2,39,253,184,2,72,0,130,21,8,73,41,255, + 247,1,143,2,14,0,10,0,21,0,27,0,0,55,50,62,3,53,52,39,7,22,19,34,14,3,21,20,23,55,38,39,50,16,35,34,16,220,17,28,30,21,14,4,190,27,57,17,28,31,20,14,4,189,26,57,178,178,178,51,7,26, + 44,78,53,40,34,230,52,1,161,8,131,10,39,39,33,229,52,58,253,234,2,130,186,41,0,1,0,79,0,0,1,104,2,7,130,91,40,0,19,39,55,51,17,51,21,33,130,5,50,81,1,106,68,105,254,233,1,105,1,103, + 81,78,254,55,61,61,1,122,130,43,32,50,130,43,37,134,2,17,0,31,0,130,43,49,62,2,51,50,30,2,21,20,6,15,1,33,21,33,53,52,55,54,67,247,5,8,58,35,34,6,7,55,1,8,29,81,37,48,72,36,17,44,59, + 147,1,1,254,173,13,1,165,37,40,58,45,26,75,24,1,159,65,5,16,27,30,48,46,21,40,78,58,146,61,46,14,12,1,165,38,80,33,33,47,27,13,0,130,94,41,0,45,255,246,1,139,2,16,0,41,130,12,32,22, + 130,93,38,35,34,38,47,1,53,30,130,108,40,54,53,52,38,43,1,53,51,50,131,8,130,101,37,15,1,53,54,51,50,131,35,8,62,1,26,112,107,85,34,78,22,23,7,25,80,41,57,65,65,49,64,64,46,56,55,45, + 33,72,20,20,74,68,78,98,51,1,22,30,109,65,84,13,7,6,69,4,12,19,51,49,46,52,58,45,38,37,43,14,7,7,64,23,74,59,50,55,130,119,34,2,0,22,130,111,45,162,2,7,0,2,0,13,0,0,37,17,3,59,1,66, + 44,6,54,19,51,1,16,179,237,88,88,67,241,208,100,181,1,18,254,238,58,123,123,73,1,66,130,46,33,0,49,130,171,32,135,130,51,37,40,0,0,19,17,33,130,47,130,143,37,30,3,21,20,14,2,137,181, + 36,62,2,53,52,46,130,15,8,68,6,7,72,1,33,221,24,41,25,48,51,38,24,35,61,67,38,43,70,14,14,7,24,76,38,21,39,39,23,23,40,44,25,28,56,14,1,2,1,4,59,127,11,10,25,38,63,40,49,72,38,18,11, + 6,5,71,4,11,19,11,25,48,33,33,48,25,12,13,7,130,157,55,41,255,248,1,143,2,8,0,9,0,37,0,0,55,50,53,52,35,34,6,21,20,22,19,65,139,5,130,119,42,53,52,62,2,51,50,22,31,1,21,38,131,26,8, + 58,54,224,101,101,41,59,59,51,31,56,49,29,91,82,87,98,36,63,79,48,24,46,11,10,31,59,69,89,36,48,119,121,57,64,63,56,1,39,20,40,68,43,90,89,114,136,73,110,63,31,8,4,3,66,25,95,98,72, + 0,130,0,38,1,0,46,0,0,1,138,130,227,32,6,130,227,8,42,53,33,21,3,35,19,46,1,91,197,78,190,1,203,59,29,254,23,1,202,0,0,3,0,39,255,246,1,145,2,17,0,15,0,31,0,52,0,0,54,50,62,2,130,243, + 38,34,14,2,20,30,1,18,132,6,32,2,132,18,35,1,23,30,1,65,210,5,35,53,52,55,38,68,239,7,8,50,199,42,32,32,18,18,33,32,40,32,33,18,18,32,72,37,29,29,16,17,29,28,36,28,30,16,16,29,33,48, + 52,104,76,77,104,99,84,95,71,70,96,45,7,20,42,63,42,19,7,7,19,130,6,43,20,1,166,6,17,37,53,37,16,6,6,16,130,6,59,17,191,16,72,52,69,78,77,70,107,33,30,91,62,69,70,61,90,0,2,0,41,0, + 0,1,143,2,16,65,43,5,43,19,34,21,20,51,50,54,53,52,38,3,34,69,108,11,65,170,5,32,22,131,26,8,44,6,215,100,100,42,59,60,51,30,57,48,29,91,82,86,99,36,64,79,47,24,46,11,11,32,59,68,89, + 35,1,216,120,120,56,64,64,56,254,217,20,39,68,44,89,90,65,44,6,35,3,4,65,24,65,44,5,55,2,0,166,0,81,1,18,1,171,0,7,0,15,0,0,18,34,38,52,54,50,22,20,6,71,199,6,38,242,44,32,32,44,32, + 76,132,5,39,1,65,31,44,31,31,44,165,132,5,32,0,130,0,36,2,0,143,255,209,134,59,32,24,140,59,35,21,20,14,2,68,137,8,37,243,45,31,31,45,31,130,67,41,22,30,31,11,10,27,55,10,17,25,135, + 74,37,32,22,33,60,41,31,68,150,10,8,37,0,1,0,24,0,84,1,160,1,176,0,6,0,0,19,37,21,13,1,21,37,24,1,135,254,200,1,56,254,121,1,30,145,62,111,112,62,145,132,123,38,24,0,152,1,159,1,114, + 72,67,5,34,37,33,53,132,1,33,1,159,130,37,32,135,131,3,35,153,59,99,59,130,39,141,79,37,5,21,5,53,45,1,131,79,32,121,130,79,36,200,1,176,145,57,131,81,132,79,42,48,0,0,1,135,2,31,0, + 34,0,42,130,121,33,50,22,130,195,44,3,29,1,35,53,52,62,4,53,52,38,35,34,132,209,34,62,4,16,65,25,6,58,226,83,82,29,42,42,29,70,21,33,38,33,22,52,43,25,45,29,22,4,5,52,2,10,34,38,61, + 132,239,8,33,2,31,68,52,28,47,33,30,33,17,41,41,22,39,28,29,23,31,16,30,33,16,23,23,8,8,34,4,14,35,26,22,254,75,65,71,9,47,255,225,255,233,1,215,2,32,0,7,0,64,0,0,54,50,71,233,5,39, + 5,23,14,4,35,34,46,3,71,98,10,46,21,35,53,6,35,34,38,52,54,51,50,22,31,1,50,132,151,8,122,6,21,20,30,2,51,50,62,3,214,60,43,43,60,43,1,5,38,2,11,38,43,71,37,63,103,67,46,20,141,105, + 105,128,15,24,21,10,55,36,43,55,79,80,55,28,49,10,11,45,84,70,81,118,39,61,75,39,35,64,40,30,14,168,56,79,57,57,79,120,46,4,11,27,21,17,33,55,74,79,42,120,163,78,67,24,35,17,7,1,198, + 19,31,93,131,94,26,13,13,28,34,57,124,104,53,87,55,30,13,20,20,13,0,0,0,2,0,5,0,0,1,178,2,7,130,9,8,38,10,0,0,55,51,3,55,19,35,39,35,7,35,19,141,158,79,45,169,77,41,193,40,77,169,192, + 1,9,61,253,250,136,136,2,6,0,3,0,38,130,47,32,146,130,47,36,8,0,17,0,39,130,49,32,50,69,22,5,36,21,17,21,22,55,131,10,42,35,23,30,4,21,20,14,2,43,1,17,69,159,6,8,61,220,65,52,62,45, + 121,71,39,84,49,42,77,12,23,34,25,18,30,50,57,31,194,177,43,62,32,15,49,58,47,52,47,43,189,1,147,156,1,2,4,76,42,33,183,3,7,20,27,48,31,38,57,32,14,2,5,25,43,46,24,42,55,130,110,47, + 0,47,255,248,1,137,2,18,0,26,0,0,37,21,6,35,67,62,7,58,23,21,38,35,34,14,2,21,20,30,1,51,50,1,136,49,79,56,86,50,24,112,104,77,51,51,77,130,65,55,15,26,66,50,77,93,73,28,42,74,95,58, + 117,151,32,71,45,33,59,74,46,62,91,59,130,233,130,183,33,1,144,130,195,34,6,0,13,131,193,33,53,52,130,172,58,19,50,17,16,35,7,17,199,125,125,84,84,201,200,160,58,202,201,254,109,1, + 204,254,253,254,254,1,130,247,34,1,0,52,130,136,32,131,130,51,8,34,11,0,0,19,33,21,33,23,51,21,35,21,33,21,33,53,1,77,254,253,1,227,228,1,4,254,178,2,6,59,153,59,188,59,0,130,0,38, + 1,0,58,0,0,1,126,130,47,32,9,132,47,34,35,31,1,130,47,40,35,58,1,67,249,1,181,182,74,130,41,35,158,1,59,241,130,34,33,0,35,130,219,32,148,130,219,32,36,130,219,38,39,35,53,51,21,14, + 4,149,224,47,62,1,1,78,1,99,170,2,7,27,32,54,30,56,85,51,130,233,33,78,50,132,233,47,14,25,66,50,27,42,11,67,141,58,223,2,7,18,13,11,143,241,37,11,7,0,1,0,42,130,108,32,141,133,191, + 57,51,17,51,21,51,53,51,17,35,53,35,21,43,75,204,75,75,204,2,6,212,212,253,250,247,246,130,39,32,66,130,39,32,117,133,39,36,19,53,33,21,35,130,43,55,5,53,55,17,67,1,50,116,116,254, + 206,115,1,203,59,59,254,112,58,1,57,1,1,145,132,231,36,65,255,249,1,118,130,47,32,13,130,231,8,33,51,17,20,6,35,7,53,51,50,54,53,17,35,158,216,66,78,164,147,48,37,140,2,6,254,158,80, + 90,1,57,48,58,1,47,130,50,39,0,1,0,19,0,0,1,165,137,139,59,55,51,7,19,35,3,7,21,19,75,233,85,212,221,90,180,57,2,6,230,230,212,254,206,1,4,57,202,130,48,34,1,0,49,130,47,32,135,130, + 47,50,5,0,0,55,17,51,17,33,21,49,75,1,10,1,2,5,254,53,58,132,31,131,79,32,164,130,31,56,12,0,0,51,17,51,23,55,51,17,7,17,3,35,3,17,20,100,99,101,100,65,107,57,106,130,130,42,254,253, + 251,1,1,197,254,239,1,19,254,130,52,34,1,0,41,130,83,32,142,65,159,5,35,51,3,51,19,130,86,130,48,38,45,3,98,186,72,95,186,130,46,55,90,1,166,253,250,1,166,254,90,0,0,2,0,35,255,250, + 1,149,2,18,0,5,0,25,130,229,63,50,16,35,34,16,18,50,62,3,52,46,3,34,14,3,20,30,2,220,184,184,184,167,34,27,30,20,13,13,20,30,27,131,8,33,14,14,130,22,50,17,253,234,2,22,254,37,8,26, + 43,78,106,78,44,26,8,8,26,44,130,8,33,43,26,130,179,38,2,0,44,255,255,1,139,132,179,32,30,131,83,71,166,5,45,43,1,21,55,50,22,21,20,6,43,1,28,1,30,130,3,8,47,49,35,17,50,206,32,46, + 22,10,10,21,42,30,93,99,85,91,85,84,108,1,1,75,174,1,10,19,31,31,17,17,31,31,18,195,252,80,70,71,89,5,21,55,48,50,29,2,6,130,90,45,0,2,0,13,255,184,1,171,2,15,0,12,0,32,131,91,39,17, + 20,7,23,21,35,39,6,143,182,39,198,184,40,84,48,82,40,58,147,187,40,14,254,245,123,67,97,44,91,27,149,193,38,2,0,38,0,0,1,145,130,191,34,9,0,24,130,99,34,35,21,22,73,125,5,35,15,1,35, + 17,73,20,5,8,45,7,23,35,39,38,213,99,68,39,34,51,53,139,3,72,179,79,83,57,44,122,79,111,97,1,205,184,1,2,2,46,45,43,47,242,218,2,5,89,69,45,69,16,229,217,1,130,184,42,1,0,42,255,246, + 1,141,2,17,0,54,130,91,44,21,46,2,35,34,6,21,20,30,7,23,30,3,73,135,16,32,39,69,75,6,8,95,30,2,23,1,109,6,21,67,35,62,61,6,14,11,25,12,31,9,33,1,23,42,48,29,101,89,43,78,18,18,7,27, + 86,45,45,66,61,69,34,48,47,24,103,87,18,40,32,27,8,1,245,71,4,13,21,41,48,11,18,15,11,10,5,7,3,5,1,4,17,33,58,39,78,74,16,9,8,73,5,16,27,46,45,46,37,14,6,17,31,50,34,72,82,5,8,9,84, + 93,5,32,9,130,143,32,175,130,235,32,7,130,233,56,53,33,21,35,17,7,17,10,1,164,172,75,1,203,59,59,254,54,1,1,203,0,1,0,37,130,187,32,147,130,35,32,24,66,163,5,32,20,74,49,5,8,50,17, + 51,17,20,14,3,34,46,3,37,76,6,19,43,32,67,48,75,6,22,38,68,95,70,38,22,7,166,1,96,254,153,26,35,34,18,54,67,1,95,254,173,33,48,53,33,22,21,32,50,44,130,75,32,13,130,111,32,171,130, + 75,8,32,6,0,0,51,3,51,27,1,51,3,174,161,73,134,134,72,161,2,6,254,58,1,198,253,250,0,1,255,249,0,0,1,190,130,35,32,12,135,35,131,38,47,35,11,1,75,81,63,58,63,83,64,57,64,81,73,72,73, + 130,47,44,99,1,157,254,98,1,158,253,250,1,184,254,72,130,55,32,255,130,55,32,185,67,123,5,8,33,19,39,51,23,55,51,7,19,35,39,7,35,184,160,80,121,123,81,165,177,80,141,141,80,1,19,243, + 195,195,243,254,237,218,218,130,139,32,5,130,47,32,178,130,47,36,8,0,0,55,3,131,47,50,3,21,35,182,176,79,135,134,80,177,75,234,1,28,228,228,254,227,233,130,34,33,0,33,130,4,32,151, + 130,39,32,9,65,35,5,60,1,33,21,5,53,1,41,1,102,254,224,1,40,254,138,1,24,1,203,59,53,254,106,58,1,56,1,147,0,130,42,41,0,104,255,189,1,80,2,73,0,7,130,12,55,39,17,51,21,35,17,51,1, + 79,162,162,231,231,2,18,1,253,226,56,2,140,0,1,0,76,151,10,41,19,1,35,1,109,1,35,70,254,222,76,150,7,130,31,138,67,52,19,51,17,35,53,51,17,7,104,231,231,163,163,2,73,253,116,56,2,30, + 1,130,90,37,0,37,1,63,1,147,65,75,5,33,19,23,131,233,45,55,252,150,82,100,101,82,150,2,6,198,141,141,198,131,139,55,255,241,255,190,1,199,255,247,0,3,0,0,5,33,53,33,1,198,254,44,1, + 212,66,56,131,27,39,0,124,1,160,1,60,2,57,131,27,131,63,39,207,108,82,109,2,57,153,153,130,27,8,175,2,0,48,255,246,1,136,1,143,0,17,0,54,0,0,55,50,62,3,53,34,35,38,14,4,21,20,22,39, + 52,62,3,23,48,60,1,46,6,35,7,53,50,54,51,21,50,30,2,21,7,35,54,39,6,35,34,38,204,32,48,23,13,2,10,21,24,45,39,32,22,13,51,118,28,50,69,81,45,2,3,7,9,15,18,26,16,122,18,58,34,38,58, + 52,29,1,61,2,2,38,99,64,79,44,26,33,49,27,17,1,2,3,10,18,28,20,37,35,72,36,51,28,16,1,1,14,7,17,10,16,10,12,7,5,2,54,2,1,12,33,65,47,241,25,27,61,63,0,0,2,0,50,255,247,1,134,2,49,0, + 9,0,30,0,0,54,50,54,53,52,38,34,6,21,20,39,62,4,67,150,5,130,127,8,57,47,1,7,35,17,51,172,112,50,51,110,52,3,1,5,18,23,42,25,71,87,88,59,32,62,16,15,7,61,68,45,82,68,67,83,83,67,68, + 212,3,8,20,16,12,102,101,98,106,29,15,15,49,2,47,0,1,0,59,130,91,32,125,130,239,35,20,0,0,1,131,84,36,22,51,50,55,21,72,197,7,8,34,23,21,38,1,13,65,72,72,65,62,49,47,78,85,110,110, + 85,78,47,49,1,89,74,76,76,74,43,65,32,106,196,106,32,65,43,139,159,32,7,133,159,133,158,38,19,51,17,35,39,14,4,130,155,8,35,53,52,54,51,50,30,2,31,1,122,200,48,104,48,200,68,62,6,3, + 8,26,27,41,20,59,88,87,70,25,42,24,17,3,4,45,150,131,158,51,1,109,253,209,49,3,7,21,15,13,106,98,101,102,12,17,17,6,7,0,130,0,34,2,0,37,130,163,32,147,130,163,37,5,0,26,0,0,19,130, + 165,35,51,52,7,20,130,168,32,54,133,169,132,95,8,42,22,29,1,32,227,47,69,224,227,62,67,42,62,46,70,82,89,109,105,79,91,90,254,218,1,89,68,48,116,165,56,79,18,21,64,29,109,95,93,110, + 97,73,48,130,83,42,1,0,40,0,3,1,144,2,36,0,19,132,247,37,29,1,51,21,35,17,130,1,33,53,51,130,77,61,59,1,21,1,40,35,31,125,125,68,122,122,65,64,108,1,237,25,37,75,57,254,217,1,39,57, + 52,78,62,55,133,147,36,50,255,70,1,134,130,147,32,9,73,49,5,77,51,8,44,51,17,20,6,43,1,53,51,50,62,2,61,1,137,254,8,60,22,23,222,99,99,53,51,51,159,61,90,81,130,136,28,42,23,10,1,5, + 18,24,42,25,71,91,91,60,48,52,28,53,142,150,82,68,65,77,1,80,254,110,86,86,58,22,36,37,18,59,2,6,16,12,10,101,100,98,101,20,29,130,162,41,0,62,0,1,1,122,2,49,0,18,74,253,5,8,45,17, + 35,53,52,38,35,34,6,29,1,35,17,51,21,54,253,53,72,68,40,37,47,56,68,68,37,1,146,78,67,255,0,250,45,48,61,59,223,2,47,218,60,0,2,0,139,130,59,38,45,2,30,0,11,0,17,130,74,8,40,35,34, + 61,1,52,59,1,50,29,1,20,7,53,51,17,35,17,1,35,56,10,10,56,9,161,161,68,1,202,10,64,9,9,64,10,127,59,254,123,1,74,130,232,34,2,0,100,130,231,32,84,130,59,34,12,0,24,130,121,130,47,32, + 20,132,221,35,54,53,17,55,138,72,39,165,174,127,112,105,29,37,59,132,72,43,1,76,58,254,98,161,59,52,50,1,100,126,133,79,39,0,1,0,41,0,2,1,142,132,131,38,0,55,23,35,39,7,21,130,183, + 53,17,55,51,210,188,81,154,51,70,70,181,80,245,243,201,45,156,2,27,254,201,161,130,118,130,47,32,133,130,179,36,51,2,8,0,5,130,117,131,164,41,35,133,173,69,104,2,7,253,250,1,72,63, + 5,46,20,255,254,1,164,1,143,0,30,0,0,1,50,22,21,130,198,36,52,35,34,6,7,138,7,48,51,21,54,51,50,23,62,1,1,68,43,52,61,52,26,26,3,132,4,52,62,62,31,42,52,26,13,48,1,143,69,53,254,234, + 1,5,84,34,37,254,238,130,6,42,33,36,254,236,1,133,18,29,49,22,27,130,128,32,1,65,111,5,33,1,147,65,111,20,32,23,65,111,9,33,64,4,65,112,12,35,1,133,48,60,66,31,5,42,42,255,247,1,141, + 1,144,0,9,0,17,67,179,11,32,18,130,171,8,40,20,32,53,52,174,92,58,59,90,59,21,166,94,254,158,44,72,80,80,70,70,80,80,1,28,102,102,205,205,102,0,2,0,50,255,78,1,134,1,136,0,67,79,6, + 38,54,53,52,34,21,20,19,131,57,8,62,6,35,34,46,2,47,1,21,35,17,51,23,62,4,176,103,48,199,107,66,89,87,71,25,41,24,18,3,3,68,61,7,1,6,20,25,41,38,82,67,148,148,67,1,16,105,99,101,103, + 12,18,17,6,6,221,2,48,49,2,8,20,16,13,152,91,35,51,17,35,53,67,171,13,38,160,104,48,200,216,62,68,66,165,5,41,70,87,89,66,23,41,26,19,4,4,134,91,44,6,253,208,221,2,8,21,15,13,103,101, + 99,105,132,99,36,0,0,1,0,69,130,4,36,115,1,142,0,19,130,7,35,50,22,31,1,71,239,5,33,29,1,130,173,8,36,7,62,1,1,12,29,52,11,11,4,16,56,31,56,71,68,68,2,17,69,1,142,15,7,8,65,5,12,20, + 60,35,245,1,133,53,27,35,130,54,46,0,69,255,254,1,115,1,151,0,38,0,0,55,30,1,130,250,34,39,46,1,80,126,9,39,46,2,35,34,21,20,22,23,79,243,7,8,78,47,1,69,53,109,71,91,83,57,14,32,65, + 44,33,62,14,15,6,19,63,33,90,36,57,70,69,88,73,31,70,20,20,88,27,14,32,35,49,15,15,53,44,22,38,36,20,11,6,6,62,4,11,17,57,25,29,8,11,46,55,57,66,12,6,6,0,0,0,1,0,87,0,6,1,97,2,4,0, + 17,130,115,41,51,21,35,34,46,3,53,17,51,21,130,9,57,21,20,216,136,125,25,33,41,24,16,68,162,162,65,58,3,15,26,50,34,1,124,118,58,204,70,132,55,40,54,255,246,1,130,1,133,0,21,130,55, + 33,53,51,80,10,5,34,55,51,19,65,74,6,8,32,54,68,86,44,62,3,68,1,64,1,3,17,23,45,27,79,73,145,244,241,102,53,39,251,254,124,42,2,7,19,13,12,74,132,67,32,29,130,128,32,155,130,67,53, + 6,0,0,1,51,3,35,3,51,19,1,84,70,148,86,147,71,120,1,133,254,123,130,3,32,186,130,38,39,0,1,255,248,255,255,1,192,130,39,33,12,0,132,39,33,39,7,131,42,48,55,51,23,1,124,67,97,63,68, + 65,65,97,68,68,53,76,60,130,47,34,123,215,215,130,5,34,208,192,192,132,55,33,0,20,130,55,32,163,130,55,35,11,0,0,37,71,43,5,57,39,51,23,55,51,1,1,162,78,122,120,78,162,149,76,110,109, + 76,203,203,155,155,204,185,141,141,77,151,5,38,68,1,137,1,134,0,31,130,12,32,18,77,244,5,44,53,51,50,62,4,53,52,53,35,34,38,53,19,130,230,59,22,51,23,3,1,134,3,12,24,47,32,195,176, + 16,23,13,8,3,1,133,71,72,1,68,29,41,137,1,130,122,56,135,48,17,47,52,36,59,12,24,22,34,17,14,4,2,78,49,1,6,229,49,50,1,1,73,132,239,32,64,130,143,32,119,130,95,8,41,14,0,0,55,51,23, + 33,53,62,3,39,35,53,33,21,2,137,237,1,254,202,15,50,105,69,1,229,1,45,238,52,52,59,17,58,122,82,1,50,60,254,235,130,54,8,32,0,1,0,63,255,178,1,120,2,84,0,41,0,0,1,21,20,7,22,29,1,20, + 22,59,1,21,34,35,34,46,3,61,1,83,197,7,130,9,33,62,3,130,22,8,61,35,34,6,1,11,72,72,38,42,29,31,13,38,54,27,14,3,38,43,51,51,43,38,3,14,27,54,38,44,29,42,38,1,215,100,95,17,17,96,99, + 34,29,62,20,26,44,30,23,86,43,32,66,32,42,87,23,30,44,26,19,62,29,130,98,53,0,188,255,200,0,252,2,63,0,3,0,0,23,35,17,51,252,64,64,55,2,117,141,135,32,19,84,60,5,36,50,51,50,30,3,134, + 142,130,119,130,9,33,14,3,69,35,5,130,141,55,55,38,172,37,42,29,30,14,37,54,27,15,2,38,44,51,51,44,38,2,15,27,54,37,130,132,41,37,72,72,1,115,100,33,29,62,19,131,129,33,87,42,130,129, + 45,43,86,24,29,44,26,20,62,29,33,100,96,17,16,130,248,42,1,0,22,0,177,1,161,1,93,0,33,130,148,34,23,14,4,131,238,39,7,14,1,15,1,39,62,4,131,119,54,55,62,1,55,1,103,58,5,5,17,21,40, + 25,25,43,30,28,31,16,20,25,3,3,143,16,62,1,66,13,21,21,45,24,20,26,35,36,21,2,3,45,21,21,13,21,22,44,24,21,26,36,35,22,3,3,45,20,130,229,32,21,130,95,45,163,0,106,0,15,0,31,0,47,0, + 0,55,50,22,130,200,36,6,43,1,34,38,130,197,34,54,59,1,157,15,43,51,104,5,7,7,5,71,4,8,8,4,222,131,9,32,70,131,4,32,221,136,9,35,105,7,5,81,131,12,150,4,42,0,0,2,0,159,255,235,1,25, + 2,9,90,167,5,33,18,34,82,231,5,59,7,51,23,7,35,53,245,50,35,35,50,35,84,46,29,1,101,1,145,35,49,35,35,49,105,196,155,155,130,51,42,63,255,147,1,121,1,242,0,5,0,28,130,177,48,17,14, + 1,21,20,19,17,54,55,21,6,7,21,35,53,46,1,90,19,6,8,62,22,23,21,38,243,50,58,146,57,39,45,51,38,78,102,100,80,38,61,35,40,49,1,38,6,73,67,134,1,25,254,215,2,25,59,21,3,100,101,6,107, + 92,95,104,4,97,98,5,20,60,25,0,1,0,40,255,255,1,144,2,16,0,27,130,89,38,51,21,33,53,51,53,35,130,3,81,218,6,32,21,71,122,5,8,39,51,21,35,202,197,254,153,87,73,73,82,79,33,52,9,10,48, + 42,50,50,136,136,59,59,59,161,50,82,88,88,10,5,5,64,30,53,63,88,50,0,131,223,38,55,0,63,1,128,1,125,130,223,35,31,0,0,54,82,67,6,8,41,54,20,7,23,7,39,6,34,39,7,39,55,38,52,55,39,55, + 23,54,50,23,55,23,7,187,65,46,46,65,46,204,22,60,34,62,31,73,31,62,34,60,22,137,10,49,148,44,61,43,43,61,67,73,30,57,35,60,19,19,60,35,57,30,137,10,75,219,10,36,24,0,0,1,7,130,160, + 33,7,21,88,161,9,32,39,130,194,8,37,39,51,23,55,1,178,120,91,115,33,148,148,75,148,148,33,115,91,119,79,135,134,2,6,194,39,53,11,39,182,182,39,11,53,39,194,228,228,130,178,41,0,2,0, + 188,255,206,0,252,2,57,83,195,5,36,23,35,17,51,53,67,9,5,40,64,64,50,1,8,92,1,7,0,130,35,59,64,255,188,1,119,2,16,0,15,0,75,0,0,37,62,1,53,52,38,39,38,39,14,1,21,20,22,23,69,167,11, + 33,53,22,85,40,5,130,28,35,46,4,53,52,89,5,5,32,54,86,84,10,133,47,8,53,4,21,20,6,1,10,21,28,48,46,25,22,22,27,48,45,25,61,25,21,69,72,25,57,16,16,58,51,36,42,37,47,8,3,28,25,39,17, + 14,38,33,25,21,68,74,24,54,14,14,51,51,37,40,36,130,22,33,29,24,131,22,42,142,12,35,15,26,44,25,14,13,12,36,132,7,63,37,18,34,23,44,66,10,5,5,57,26,32,25,20,30,26,4,2,16,14,28,23,34, + 19,28,50,16,18,34,24,43,59,131,25,33,27,26,133,25,39,15,15,28,23,33,20,28,50,133,251,42,109,1,212,1,75,2,27,0,11,0,23,73,51,13,33,43,1,73,63,9,39,1,66,58,9,9,58,9,155,131,5,37,8,1, + 213,9,52,9,136,2,48,0,0,3,255,248,0,40,1,192,1,218,0,7,0,15,0,36,65,245,9,33,18,50,92,21,5,33,22,52,130,255,35,23,21,38,35,75,119,11,8,53,142,156,111,111,156,111,95,188,133,133,188, + 134,92,78,62,57,30,43,34,47,52,52,47,46,31,36,51,61,79,105,147,105,105,147,1,34,127,179,127,127,179,152,126,66,15,38,21,48,49,49,47,18,36,17,131,179,8,41,3,0,92,0,162,1,91,2,18,0,3, + 0,18,0,53,0,0,37,35,53,51,39,34,35,34,14,4,21,20,22,51,50,54,7,52,62,2,23,60,1,46,2,88,232,5,8,122,62,6,51,50,22,29,1,35,39,6,35,34,38,1,91,247,247,55,5,8,25,24,37,19,20,8,36,22,49, + 41,201,37,58,70,33,8,15,32,22,26,50,11,12,2,18,7,17,12,17,18,9,57,73,45,8,39,54,47,59,163,43,178,1,3,6,11,18,13,24,24,61,16,33,43,14,7,4,8,10,22,14,11,11,6,6,44,1,6,2,6,2,3,1,57,64, + 159,38,45,44,0,2,0,50,0,49,1,134,1,112,0,6,0,13,0,0,1,21,7,23,21,39,53,55,133,5,50,1,134,112,112,174,7,111,111,173,173,1,112,67,93,93,66,145,28,79,132,5,35,146,0,0,4,65,59,8,56,9,0, + 27,0,35,0,43,0,0,19,22,62,2,53,52,38,43,1,23,30,1,23,35,38,39,130,9,32,21,130,224,36,50,21,20,14,1,67,78,6,65,88,7,54,175,21,36,27,15,29,21,49,86,14,25,46,52,30,17,21,24,27,47,92,103, + 34,147,65,89,10,53,1,13,1,2,6,18,15,19,20,102,5,33,72,44,26,33,103,242,74,21,32,172,65,94,11,130,172,41,0,109,1,183,1,75,1,235,0,3,130,12,130,106,38,1,74,220,220,1,183,51,66,31,5,45, + 101,1,50,1,82,2,16,0,7,0,15,0,0,18,93,253,14,56,190,59,42,42,59,41,22,98,69,69,98,69,1,94,39,55,39,39,55,138,65,91,65,65,91,133,59,40,24,255,255,1,159,1,151,0,11,131,59,92,65,11,56, + 19,33,53,33,251,164,164,62,165,165,62,164,254,121,1,135,1,36,59,115,115,59,114,254,105,87,43,5,40,115,1,133,1,69,2,173,0,24,130,143,130,245,37,7,51,21,35,53,54,85,161,5,8,47,34,6,15, + 1,53,54,51,50,22,1,66,17,62,70,152,210,77,32,42,37,28,17,42,12,13,39,51,57,58,2,94,20,33,64,61,39,38,72,29,40,36,18,25,11,6,6,41,20,130,197,32,1,130,223,38,130,1,75,2,176,0,39,131, + 79,32,6,88,180,10,91,20,23,8,51,7,22,1,75,68,55,21,49,15,14,50,42,26,48,43,31,31,31,29,36,30,27,21,44,12,12,47,44,49,63,33,30,71,1,214,37,47,7,4,4,42,18,26,25,25,27,38,22,21,18,22, + 8,130,13,38,13,42,33,28,29,7,13,130,252,39,1,0,131,1,181,1,53,2,79,123,5,41,51,7,35,235,73,120,56,2,57,131,130,26,51,0,1,0,37,255,107,1,146,1,132,0,36,0,0,37,50,54,63,1,21,133,147, + 33,14,4,130,154,36,39,21,35,17,51,66,199,5,8,63,53,55,51,16,21,20,1,120,7,12,4,3,27,29,19,26,4,3,1,4,16,22,42,27,26,47,11,60,64,45,37,43,59,3,65,49,4,2,2,52,15,26,13,13,2,7,18,14,11, + 28,26,192,2,24,241,48,53,53,38,251,254,226,24,29,130,108,42,1,0,48,255,185,1,136,2,7,0,16,76,95,7,47,16,21,6,34,47,1,17,34,38,52,54,216,175,52,70,1,130,60,8,34,69,99,99,2,6,253,182, + 2,32,253,229,5,2,1,1,1,39,85,121,85,0,0,1,0,159,0,198,1,25,1,63,0,7,0,0,66,114,7,39,195,50,35,35,50,35,1,63,130,4,33,35,50,131,35,36,143,255,119,1,41,130,43,130,95,35,33,22,21,20,65, + 92,8,52,39,48,58,1,1,0,40,91,14,31,8,8,24,27,91,73,21,22,41,37,59,130,173,34,46,11,94,132,151,40,114,1,132,1,70,2,167,0,10,131,151,37,21,35,53,51,53,7,130,239,56,250,75,204,76,83,85, + 51,1,171,38,38,212,14,40,13,0,3,0,83,0,162,1,101,2,16,130,9,37,13,0,21,0,0,37,130,41,8,47,2,34,6,21,20,22,50,54,53,52,22,32,53,52,54,50,22,21,1,86,249,249,88,69,46,45,71,45,56,254, + 239,73,127,73,163,43,1,25,47,54,54,49,49,54,54,197,143,71,130,0,130,114,67,163,15,33,55,21,130,112,35,39,53,51,23,132,7,48,223,173,111,111,166,174,174,112,112,222,28,145,66,93,93,67, + 146,132,6,41,0,4,0,19,255,162,1,165,2,64,130,127,36,6,0,17,0,28,130,242,38,23,5,39,23,51,53,55,70,111,8,33,55,3,130,72,32,51,131,14,8,44,53,51,1,155,9,254,120,9,198,102,51,43,43,50, + 144,134,187,82,87,48,75,203,75,1,47,37,91,37,203,144,43,188,37,65,65,42,183,1,86,14,39,13,252,39,39,130,81,137,91,34,28,0,39,133,89,32,5,66,236,15,34,39,54,51,130,224,34,20,6,1,143, + 100,34,1,73,86,66,254,7,42,41,13,12,1,39,51,57,59,28,254,252,138,113,52,190,77,39,39,71,30,39,36,19,25,12,6,6,42,19,51,27,23,45,1,226,134,123,34,4,0,18,132,215,32,70,134,215,32,56, + 133,125,34,23,51,39,138,215,33,39,20,67,43,10,67,42,26,132,243,34,202,102,1,130,244,8,32,49,145,134,45,72,55,21,48,13,13,41,46,36,42,38,34,33,30,31,37,33,25,24,45,10,10,46,44,50,62, + 33,30,72,65,11,13,8,68,168,36,47,7,4,4,41,18,29,47,28,37,23,21,20,20,8,4,4,42,12,41,32,29,29,8,14,0,0,2,0,73,255,247,1,111,2,27,0,30,0,38,0,0,55,50,54,63,1,21,14,2,35,34,38,53,52,62, + 4,61,1,51,21,20,14,3,21,20,22,73,68,7,8,56,235,29,66,18,19,7,24,69,34,82,78,19,29,34,29,19,71,26,37,38,26,47,71,44,32,32,44,32,48,24,11,12,65,5,12,21,65,54,24,43,30,30,24,31,17,69, + 61,30,48,34,31,39,21,31,36,1,129,91,1,6,45,3,0,5,0,0,1,179,2,131,0,3,0,6,0,130,128,36,1,35,39,51,3,90,85,9,37,1,9,56,84,68,52,90,90,9,46,2,38,92,254,61,1,10,61,253,250,135,135,2,6, + 0,130,53,143,63,34,7,35,55,139,63,36,59,84,56,72,106,132,63,39,40,193,41,77,169,2,130,92,140,64,36,3,0,5,255,255,132,127,32,6,79,77,5,32,19,130,63,34,51,23,35,138,66,38,220,61,51,78, + 69,78,51,90,223,5,133,68,33,101,62,130,69,32,151,138,134,131,135,33,255,254,130,71,61,123,0,20,0,23,0,31,0,0,19,34,6,21,35,52,54,51,50,22,51,50,54,53,55,51,20,6,35,34,38,138,85,49, + 169,12,12,47,36,34,26,62,15,11,13,1,46,34,36,28,58,45,138,164,45,81,19,16,33,43,34,17,9,8,25,51,35,254,109,139,103,32,4,136,175,38,2,0,10,0,22,0,34,91,131,12,81,53,11,72,74,11,138, + 187,32,147,72,84,9,33,9,190,136,89,32,56,72,93,13,32,0,136,207,38,149,0,2,0,14,0,30,132,101,52,39,20,22,23,51,62,1,53,52,38,34,6,23,19,35,39,35,7,35,19,38,68,11,5,32,20,130,97,52,56, + 18,16,44,16,19,33,47,34,112,159,77,40,193,41,77,159,47,60,84,60,130,98,55,110,17,26,6,6,26,17,21,31,31,101,254,25,135,135,1,231,28,51,40,55,55,40,51,130,89,38,2,255,255,1,182,2,7,130, + 109,32,19,130,97,40,17,35,3,55,21,51,21,35,53,131,88,8,40,37,7,35,23,55,7,235,37,75,186,128,202,127,37,68,150,1,23,1,122,1,111,1,190,1,13,254,244,55,188,59,134,133,2,5,2,61,153,1,60, + 0,78,87,5,38,119,1,137,2,16,0,40,130,12,91,224,8,49,55,21,6,7,22,21,20,6,35,34,39,53,22,51,50,53,52,39,88,3,6,8,66,23,21,38,1,8,38,57,32,14,25,66,50,77,51,37,56,33,45,38,45,25,24,31, + 46,26,54,81,48,23,112,104,77,51,51,1,213,32,60,74,46,62,91,59,46,73,21,5,36,32,31,30,9,45,11,31,22,32,3,42,74,93,56,117,151,32,71,45,130,187,38,52,0,3,1,132,2,135,71,103,5,8,41,55, + 33,21,33,17,33,21,35,31,1,21,35,19,35,39,51,127,1,4,254,178,1,71,253,1,241,242,142,57,84,68,63,59,2,6,59,153,1,59,1,48,93,130,175,36,2,0,52,0,4,150,59,34,7,35,55,138,59,36,173,84,56, + 72,64,135,59,33,140,93,130,60,135,59,32,136,130,119,34,18,0,0,140,119,66,185,5,138,62,38,86,61,52,78,70,78,52,136,65,33,110,62,131,66,33,0,3,130,127,32,0,130,127,32,133,130,67,34,23, + 0,35,74,129,25,32,3,134,213,35,23,51,21,35,74,141,11,33,9,58,137,222,39,2,63,8,52,9,9,52,8,133,5,33,253,252,132,173,42,59,0,2,0,66,255,254,1,118,2,131,65,31,5,8,49,1,35,17,23,21,33, + 53,51,17,35,53,33,39,35,39,51,1,117,115,115,254,206,116,116,1,50,106,56,84,68,1,202,254,111,1,57,59,1,144,59,33,92,0,0,0,2,0,66,0,3,130,59,65,91,6,130,59,32,51,132,59,32,39,130,59, + 130,227,56,1,117,116,116,254,206,115,115,1,50,60,84,56,72,1,207,254,112,59,57,1,145,1,59,125,65,31,5,133,59,65,31,6,143,59,34,51,23,35,137,62,32,148,65,30,5,137,65,32,95,65,31,7,137, + 187,65,31,28,34,23,35,17,131,153,32,55,131,213,65,31,12,32,188,135,161,33,2,61,67,88,11,32,115,132,170,34,1,145,59,131,227,45,17,0,4,1,167,2,11,0,18,0,37,0,0,55,92,29,5,8,79,43,1,21, + 51,21,35,21,50,19,50,30,3,20,14,3,43,1,53,35,53,51,53,50,200,19,36,42,30,20,20,30,42,36,19,62,99,99,62,1,31,56,61,45,29,29,45,61,56,30,139,46,46,138,62,9,27,42,75,98,75,42,27,8,162, + 53,188,1,205,12,35,55,95,125,96,54,35,11,245,53,220,131,103,42,42,0,0,1,142,2,130,0,20,0,30,68,149,22,36,23,51,17,35,3,130,2,33,51,19,68,148,16,41,140,72,95,186,71,4,98,186,2,88,68, + 145,10,32,82,92,232,5,37,2,6,254,90,0,3,92,235,6,38,135,0,5,0,9,0,29,92,237,7,35,55,35,39,51,92,241,16,37,238,57,84,68,3,33,92,245,5,34,29,28,34,133,8,42,31,2,17,253,234,2,22,25,93, + 253,175,92,247,19,135,95,32,136,130,95,32,12,92,157,5,36,16,35,34,16,55,66,235,5,32,2,93,84,15,32,184,65,210,5,32,77,150,101,37,88,62,93,93,254,11,151,103,42,251,1,149,2,129,0,20,0, + 26,0,46,65,41,14,33,55,53,69,191,5,33,23,50,93,202,16,38,169,11,13,47,36,35,25,69,199,6,39,35,29,58,34,184,184,184,168,145,127,32,87,65,64,10,32,70,131,241,44,254,37,8,26,44,78,105, + 78,44,27,7,7,27,132,8,36,26,0,0,0,4,65,79,6,131,239,36,17,0,29,0,49,65,81,7,32,37,87,16,10,69,218,11,65,101,16,33,1,30,69,225,10,32,19,145,136,32,17,131,124,32,49,66,150,11,33,253, + 244,65,17,17,8,32,1,0,46,0,65,1,137,1,137,0,11,0,0,1,7,23,7,39,7,39,55,39,55,23,55,1,137,129,129,45,128,129,45,130,5,38,129,128,1,94,121,121,42,135,2,47,0,3,0,1,255,234,1,183,2,34, + 0,10,0,21,0,39,103,247,10,46,39,20,23,55,38,35,34,14,3,37,7,22,21,16,35,80,146,5,51,53,16,51,50,23,55,220,17,27,30,20,13,7,184,26,49,7,185,26,59,132,12,40,1,70,65,30,184,83,46,54,36, + 133,6,32,54,131,157,54,53,56,42,248,58,208,56,42,249,58,8,26,44,78,205,88,65,105,254,245,56,72,25,130,7,48,1,11,56,72,0,0,2,0,46,255,246,1,138,2,131,0,17,130,123,40,0,1,51,17,20,35, + 34,53,17,130,6,8,44,22,51,50,62,2,53,3,35,39,51,1,62,75,170,176,75,39,61,30,40,19,7,43,57,84,69,2,6,254,160,176,189,1,83,254,160,67,52,18,33,36,25,1,135,92,161,71,34,7,35,55,139,71, + 35,12,84,57,72,144,71,35,227,92,92,0,130,0,139,147,33,24,0,146,147,66,196,5,139,78,32,98,66,191,5,139,81,33,17,34,130,153,33,198,62,130,82,34,3,0,47,136,227,34,29,0,41,146,81,32,19, + 66,36,22,44,1,62,76,171,176,75,40,61,30,39,19,7,5,66,30,10,144,103,32,158,72,7,15,32,2,72,111,6,35,130,0,8,0,86,29,5,38,21,35,55,3,51,23,19,130,187,44,1,98,80,177,75,1,177,79,135,90, + 84,57,73,130,74,39,227,233,234,1,28,229,1,97,130,170,41,0,2,0,42,255,255,1,142,2,6,130,55,32,21,99,245,10,8,41,55,50,22,21,20,43,1,21,35,17,51,21,50,210,49,61,63,47,93,103,83,95,178, + 103,75,75,103,186,43,48,47,42,180,240,75,74,150,127,2,6,92,0,130,222,130,67,32,242,130,67,8,127,28,0,48,0,0,19,20,30,3,21,20,14,1,38,39,53,22,51,50,54,53,52,46,3,53,52,62,2,63,1,38, + 54,46,2,35,34,6,21,17,35,17,52,54,51,50,21,34,6,234,34,48,48,33,51,78,88,38,49,51,41,45,33,47,47,33,24,35,34,12,12,1,3,10,14,38,27,46,41,68,73,82,155,47,72,1,50,20,33,27,32,53,35,43, + 57,20,5,16,56,21,36,29,24,37,25,27,45,30,28,44,24,15,2,2,1,15,23,22,16,48,50,254,123,1,133,77,74,152,44,130,138,41,0,3,0,38,255,245,1,146,2,69,130,9,34,18,0,61,130,156,49,35,39,51, + 19,50,62,2,39,48,42,1,35,14,1,21,20,22,55,130,3,50,31,1,35,38,39,14,1,35,34,38,53,52,54,59,1,60,2,46,5,81,15,9,8,99,1,12,57,118,72,24,36,52,26,10,1,36,47,11,60,52,51,225,10,5,5,68, + 9,7,21,76,38,65,80,91,91,92,2,6,8,15,20,30,18,38,71,16,17,3,25,11,24,17,24,26,13,168,1,194,131,253,230,29,47,50,26,2,32,44,38,36,183,116,25,55,15,15,22,37,32,37,64,60,62,70,1,15,9, + 18,13,16,11,10,5,18,10,9,63,1,9,3,8,3,4,2,149,171,35,7,35,55,3,131,171,33,42,2,167,170,37,84,118,57,103,80,37,131,170,33,1,35,167,171,34,2,69,131,176,172,65,87,9,39,6,0,21,0,64,0,0, + 19,130,171,34,51,23,35,132,174,65,90,41,39,220,66,52,90,56,90,52,97,65,92,45,38,2,26,88,131,131,254,105,65,94,20,33,63,69,65,94,20,135,175,46,51,0,24,0,39,0,82,0,0,1,34,46,2,35,34, + 75,234,5,42,30,1,51,50,54,61,1,51,20,14,2,174,191,53,1,13,21,31,16,23,11,26,45,36,37,21,34,30,13,12,13,46,5,13,30,101,173,205,50,1,209,17,20,17,54,43,55,27,26,26,13,14,15,29,33,21, + 254,90,148,217,66,56,24,32,4,66,227,6,40,29,0,11,0,23,0,38,0,81,74,25,25,32,19,66,77,45,32,63,68,115,10,32,6,66,85,46,33,1,214,68,147,11,33,254,85,181,211,52,114,0,7,0,15,0,58,0,73, + 0,0,18,34,6,20,22,50,54,52,6,34,88,7,5,32,19,67,183,31,67,58,12,44,240,46,33,33,46,33,14,84,60,60,84,60,63,67,191,33,32,194,139,237,46,2,70,31,44,31,31,44,118,56,80,56,56,80,254,249, + 67,204,32,33,254,155,136,236,67,215,5,55,5,255,245,1,179,1,143,0,8,0,19,0,89,0,0,1,34,6,23,51,54,39,46,1,130,161,33,1,39,85,233,5,40,37,21,35,28,2,30,5,51,22,79,184,7,34,46,2,47,96, + 42,8,32,59,113,200,6,83,206,5,8,164,30,2,31,1,62,4,51,50,22,23,30,1,21,1,56,31,31,3,121,1,2,3,27,217,26,26,14,1,28,53,44,37,1,75,182,2,4,6,10,14,21,12,34,52,9,10,5,15,48,25,25,42,22, + 16,2,3,1,4,16,21,36,22,57,60,70,68,50,42,30,19,46,13,14,44,65,19,30,18,12,2,2,1,3,14,19,36,22,58,48,8,2,1,1,90,54,64,23,22,35,38,254,209,18,59,59,32,38,35,31,136,1,1,28,10,28,13,23, + 11,14,6,1,17,9,10,60,3,10,16,11,17,16,6,5,2,7,19,15,12,60,56,57,65,28,50,39,14,8,7,58,24,9,13,13,4,5,2,6,15,11,10,59,76,18,42,12,130,248,42,1,0,59,255,119,1,125,1,142,0,37,132,243, + 42,21,20,22,51,50,55,21,14,1,15,1,77,223,14,130,222,34,54,51,50,98,88,10,35,14,43,15,15,77,222,8,33,82,106,98,99,5,56,88,74,76,76,74,43,65,11,15,2,2,36,32,31,30,8,46,12,32,22,31,3, + 105,96,98,98,114,5,41,0,3,0,37,255,247,1,147,2,69,130,9,34,9,0,30,69,235,5,32,23,98,25,22,42,21,23,32,1,19,57,118,72,55,46,70,98,30,12,40,92,89,1,254,217,1,194,131,236,98,33,17,145, + 95,35,7,35,55,7,154,95,36,93,119,56,102,49,147,95,34,2,69,131,147,96,33,0,0,138,195,36,6,0,12,0,33,69,87,8,154,102,39,225,67,52,90,57,90,52,64,148,104,36,26,88,131,131,105,145,202, + 33,0,4,65,43,6,68,51,5,34,29,0,50,68,51,25,65,65,27,44,69,57,9,9,57,9,155,58,9,9,58,9,39,147,128,68,5,13,32,125,146,137,8,58,0,0,2,0,51,255,252,1,117,2,59,0,13,0,17,0,0,37,51,21,34, + 35,34,38,61,1,35,53,51,21,20,17,35,39,51,1,37,80,24,71,54,65,90,158,57,119,73,51,54,78,63,201,50,251,86,1,132,131,0,130,0,34,2,0,68,130,59,32,116,149,59,48,19,7,35,55,1,37,79,23,72, + 53,66,90,158,106,119,57,103,135,60,34,2,7,131,130,61,135,59,36,69,0,6,0,20,65,105,8,32,19,140,126,39,186,66,52,90,56,90,51,40,134,68,70,121,5,32,113,134,131,130,127,32,3,134,127,38, + 26,0,11,0,23,0,37,65,69,25,141,86,33,1,44,65,56,5,132,5,32,130,134,92,33,1,212,69,49,12,32,95,135,101,54,2,0,42,255,244,1,142,2,27,0,14,0,47,0,0,55,50,54,53,52,38,47,1,90,34,6,38,19, + 20,30,6,21,20,6,72,186,5,8,96,23,46,1,47,1,7,39,55,39,51,23,55,23,7,22,220,45,60,20,10,10,18,50,43,59,60,145,20,6,19,8,13,6,5,89,88,88,89,107,100,5,27,10,11,103,12,88,67,81,47,106, + 13,93,36,42,70,71,38,67,15,14,9,69,73,73,69,1,89,1,27,10,29,19,33,29,38,19,86,107,107,94,93,102,8,8,31,12,11,31,34,27,69,49,32,34,28,34,65,115,5,32,62,130,231,44,122,2,32,0,18,0,43, + 0,0,19,50,22,21,100,121,12,34,23,54,55,71,88,17,54,35,34,46,2,253,53,72,68,39,38,47,56,68,64,4,37,3,26,45,36,37,20,35,71,51,5,40,14,29,21,21,31,17,22,1,145,100,165,9,51,1,132,47,60, + 99,54,44,54,27,26,26,14,13,15,29,33,21,17,20,17,130,124,40,3,0,43,255,244,1,141,2,69,86,227,7,36,1,35,39,51,18,102,253,8,99,73,7,37,1,15,56,119,73,5,99,78,9,37,1,194,131,253,229,71, + 99,82,9,35,204,204,102,0,130,65,143,75,35,7,35,55,2,145,75,36,86,118,57,103,96,137,75,34,2,69,131,145,76,137,151,36,6,0,16,0,24,66,77,8,145,78,72,242,6,32,112,138,80,37,26,88,131,131, + 254,104,143,158,136,159,32,32,130,79,34,34,0,42,72,179,24,145,99,53,1,15,20,32,16,22,12,25,46,36,38,20,35,29,14,12,13,46,5,14,30,118,137,113,36,1,190,17,20,17,65,92,11,33,254,108,142, + 125,32,4,65,103,6,66,215,5,34,33,0,41,66,217,25,146,126,93,1,10,33,9,11,138,118,66,224,13,32,86,142,115,32,3,130,231,36,49,1,159,1,136,130,249,42,19,0,35,0,0,37,33,53,33,39,35,96,108, + 13,32,3,142,15,48,1,159,254,121,1,135,161,70,4,7,7,4,70,5,6,6,5,136,9,37,192,59,56,6,5,63,131,18,130,4,33,254,255,130,11,131,35,130,11,32,0,130,0,44,3,0,12,255,222,1,172,1,166,0,7, + 0,15,131,107,39,55,50,54,53,52,39,7,22,78,242,6,37,6,37,7,22,21,20,78,241,7,8,65,52,54,51,50,23,55,22,222,47,57,10,169,27,56,9,168,27,46,45,59,1,54,61,33,178,75,44,57,34,62,29,94,83, + 71,46,55,34,43,72,80,42,32,189,37,152,42,31,189,33,70,121,68,52,81,204,40,63,26,70,50,81,102,102,39,63,27,67,55,5,51,61,255,246,1,123,2,59,0,3,0,26,0,0,1,35,39,51,23,51,17,105,15,6, + 38,61,1,51,21,20,22,51,130,133,8,41,1,13,57,119,73,144,68,63,6,2,5,20,23,38,21,77,61,67,38,43,44,53,1,184,131,182,254,124,57,2,10,23,18,14,72,82,244,240,52,52,59,50,130,84,33,2,0,141, + 83,34,7,35,55,148,83,36,83,119,56,102,44,143,83,34,2,59,131,147,84,135,83,36,69,0,6,0,29,66,201,8,32,23,145,169,32,55,66,203,6,32,92,139,171,41,42,45,53,2,2,26,88,131,131,61,143,174, + 32,235,130,175,32,3,134,175,95,67,5,32,46,66,85,25,148,197,35,63,58,8,8,95,89,6,33,9,128,140,205,130,117,95,106,13,32,80,143,125,42,0,0,2,0,26,255,107,1,158,2,59,80,107,7,34,2,7,14, + 99,148,6,8,49,55,3,51,27,1,7,35,55,1,86,72,140,39,14,32,36,25,18,54,46,30,32,22,160,72,123,105,119,56,102,1,133,254,175,98,35,44,18,5,54,46,54,1,127,254,208,1,230,131,131,130,203,34, + 2,0,50,130,79,39,134,2,32,0,9,0,23,0,107,131,10,33,19,50,66,59,5,53,21,35,17,51,21,54,174,89,55,57,86,56,99,80,91,171,67,32,68,68,31,42,66,245,11,37,65,202,2,180,208,61,130,75,32,3, + 134,155,32,17,130,155,38,29,0,41,0,0,1,51,142,157,80,34,23,144,177,32,100,71,147,10,144,185,32,118,80,40,14,42,1,0,30,255,249,1,153,2,20,0,43,130,115,40,34,7,51,7,35,6,21,20,21,130, + 6,35,30,3,51,50,107,75,6,38,39,35,55,51,38,52,55,130,5,8,76,62,1,51,50,23,21,46,1,1,44,110,20,178,18,163,1,132,17,111,6,27,38,37,23,20,61,27,47,61,88,103,14,65,18,44,2,2,62,18,47,14, + 103,83,65,48,28,61,1,220,144,36,30,4,17,15,37,44,60,29,10,19,24,71,28,104,95,37,29,9,28,36,95,105,29,72,24,21,130,246,130,2,36,14,0,174,0,1,130,7,131,2,34,1,0,4,134,11,32,1,130,7,32, + 10,134,11,32,2,130,7,32,16,134,11,36,3,0,29,0,78,134,11,131,43,32,112,134,11,36,5,0,9,0,134,134,11,32,6,130,7,44,148,0,3,0,1,4,9,0,0,0,2,0,0,134,11,32,1,130,11,32,6,134,11,32,2,130, + 11,32,12,134,11,36,3,0,58,0,18,134,11,32,4,130,23,32,108,134,11,32,5,130,21,32,114,134,11,32,6,130,23,35,144,0,46,0,143,2,39,70,0,111,0,110,0,116,0,131,7,40,114,0,103,0,101,0,32,0, + 50,130,36,32,48,130,7,32,58,130,3,32,46,134,7,36,49,0,49,0,45,130,25,131,3,32,48,130,7,51,54,0,0,70,111,110,116,70,111,114,103,101,32,50,46,48,32,58,32,46,130,3,39,49,49,45,50,45,50, + 48,50,130,30,133,107,32,86,130,81,36,114,0,115,0,105,132,103,32,32,130,89,40,0,86,101,114,115,105,111,110,32,136,140,33,2,0,139,0,65,75,7,137,19,32,192,130,10,131,251,60,3,0,4,0,5, + 0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,130,235,8,50,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,0,32,0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41, + 0,42,0,43,0,44,130,211,36,46,0,47,0,48,130,221,9,48,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75, + 0,76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0,92,0,93,0,94,1,2,0,96,0,97,1,3,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0, + 138,1,4,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170,0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0,207, + 0,204,0,205,0,206,0,233,0,102,0,211,0,209,0,175,0,103,0,240,0,145,0,214,0,212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0,114,0,115, + 0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0,161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,1,5,11,118,101,114,116,105,99,97,108,98,97,114,7,117,110,105, + 48,48,56,53,9,111,130,20,42,115,99,111,114,101,4,69,117,114,111,0,132,0,38,1,255,255,0,2,0,1,130,11,32,12,130,3,32,22,130,3,131,13,32,98,130,183,34,1,0,4,132,13,130,4,130,2,32,1,130, + 3,36,0,229,13,183,147,132,7,42,175,187,66,0,0,0,0,229,178,59,232,5,250,48,120,202,241, +}; + +static const char* GetDefaultCompressedFontDataProggyForever(int* out_size) +{ + *out_size = proggy_forever_minimal_ttf_compressed_size; + return (const char*)proggy_forever_minimal_ttf_compressed_data; +} + #endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT #endif // #ifndef IMGUI_DISABLE diff --git a/core/deps/imgui/imgui_internal.h b/core/deps/imgui/imgui_internal.h old mode 100644 new mode 100755 index 02535c3e72..e464e40cb1 --- a/core/deps/imgui/imgui_internal.h +++ b/core/deps/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.5 +// dear imgui, v1.92.6 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. @@ -60,7 +60,7 @@ Index of this file: #include // INT_MIN, INT_MAX // Enable SSE intrinsics if available -#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE) +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE) && !defined(_M_ARM64) && !defined(_M_ARM64EC) #define IMGUI_ENABLE_SSE #include #if (defined __AVX__ || defined __SSE4_2__) @@ -106,6 +106,7 @@ Index of this file: #pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif // In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h @@ -276,11 +277,9 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 -#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // ImTrunc() is not inlined in MSVC debug builds -#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) -#endif +#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! ImTrunc() is not inlined in MSVC debug builds +#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! +//#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) // Hint for branch prediction #if (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L)) @@ -335,7 +334,6 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_PRIu64 "llu" #define IM_PRIX64 "llX" #endif -#define IM_TEXTUREID_TO_U64(_TEXID) ((ImU64)(intptr_t)(_TEXID)) //----------------------------------------------------------------------------- // [SECTION] Generic helpers @@ -442,6 +440,16 @@ IMGUI_API ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max IMGUI_API const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0); IMGUI_API const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line +// Character classification for word-wrapping logic +enum ImWcharClass +{ + ImWcharClass_Blank, ImWcharClass_Punct, ImWcharClass_Other +}; +IMGUI_API void ImTextInitClassifiers(); +IMGUI_API void ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class); +IMGUI_API void ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c); +IMGUI_API void ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s); + // Helpers: File System #ifdef IMGUI_DISABLE_FILE_FUNCTIONS #define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS @@ -518,7 +526,7 @@ inline ImVec2 ImTrunc(const ImVec2& v) { return inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } inline float ImTrunc64(float f) { return (float)(ImS64)(f); } -inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } +inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } // FIXME: Positive values only. inline int ImModPositive(int a, int b) { return (a + b) % b; } inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } @@ -602,7 +610,6 @@ struct IMGUI_API ImRect void TranslateY(float dy) { Min.y += dy; Max.y += dy; } void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. - void Floor() { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); } bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } const ImVec4& AsVec4() const { return *(const ImVec4*)&Min.x; } @@ -636,15 +643,15 @@ typedef ImU32* ImBitArrayPtr; // Name for use in structs template struct ImBitArray { - ImU32 Storage[(BITCOUNT + 31) >> 5]; + ImU32 Data[(BITCOUNT + 31) >> 5]; ImBitArray() { ClearAllBits(); } - void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } - void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } - bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); } - void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); } - void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); } - void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) - bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); } + void ClearAllBits() { memset(Data, 0, sizeof(Data)); } + void SetAllBits() { memset(Data, 255, sizeof(Data)); } + bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); } + void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Data, n); } + void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Data, n); } + void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Data, n, n2); } // Works on range [n..n2) + bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); } }; // Helper: ImBitVector @@ -701,7 +708,7 @@ struct ImSpanAllocator int Offsets[CHUNKS]; int Sizes[CHUNKS]; - ImSpanAllocator() { memset(this, 0, sizeof(*this)); } + ImSpanAllocator() { memset((void*)this, 0, sizeof(*this)); } inline void Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; } inline int GetArenaSizeInBytes() { return CurrOff; } inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; } @@ -712,10 +719,10 @@ struct ImSpanAllocator }; // Helper: ImStableVector<> -// Allocating chunks of BLOCK_SIZE items. Objects pointers are never invalidated when growing, only by clear(). +// Allocating chunks of BLOCKSIZE items. Objects pointers are never invalidated when growing, only by clear(). // Important: does not destruct anything! // Implemented only the minimum set of functions we need for it. -template +template struct ImStableVector { int Size = 0; @@ -729,19 +736,19 @@ struct ImStableVector inline void resize(int new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; } inline void reserve(int new_cap) { - new_cap = IM_MEMALIGN(new_cap, BLOCK_SIZE); - int old_count = Capacity / BLOCK_SIZE; - int new_count = new_cap / BLOCK_SIZE; + new_cap = IM_MEMALIGN(new_cap, BLOCKSIZE); + int old_count = Capacity / BLOCKSIZE; + int new_count = new_cap / BLOCKSIZE; if (new_count <= old_count) return; Blocks.resize(new_count); for (int n = old_count; n < new_count; n++) - Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCK_SIZE); + Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCKSIZE); Capacity = new_cap; } - inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; } - inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; } - inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCK_SIZE); void* ptr = &Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; } + inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; } + inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; } + inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCKSIZE); void* ptr = &Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; } }; // Helper: ImPool<> @@ -882,7 +889,7 @@ struct ImDrawDataBuilder ImVector* Layers[2]; // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData. ImVector LayerData1; - ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } + ImDrawDataBuilder() { memset((void*)this, 0, sizeof(*this)); } }; struct ImFontStackData @@ -956,7 +963,6 @@ enum ImGuiDataTypePrivate_ enum ImGuiItemFlagsPrivate_ { // Controlled by user - ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals. DO NOT mix direct use of this with BeginDisabled(). See BeginDisabled()/EndDisabled() for full disable feature, and github #211). ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() @@ -1158,7 +1164,7 @@ struct IMGUI_API ImGuiComboPreviewData float BackupPrevLineTextBaseOffset; ImGuiLayoutType BackupLayout; - ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); } + ImGuiComboPreviewData() { memset((void*)this, 0, sizeof(*this)); } }; // Stacked storage data for BeginGroup()/EndGroup() @@ -1192,7 +1198,7 @@ struct IMGUI_API ImGuiMenuColumns ImU16 OffsetMark; ImU16 Widths[4]; // Width of: Icon, Label, Shortcut, Mark (accumulators for current frame) - ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); } + ImGuiMenuColumns() { memset((void*)this, 0, sizeof(*this)); } void Update(float spacing, bool window_reappearing); float DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark); void CalcNextTotalWidth(bool update_offsets); @@ -1204,7 +1210,7 @@ struct IMGUI_API ImGuiInputTextDeactivatedState ImGuiID ID; // widget id owning the text state (which just got deactivated) ImVector TextA; // text buffer - ImGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); } + ImGuiInputTextDeactivatedState() { memset((void*)this, 0, sizeof(*this)); } void ClearFreeMemory() { ID = 0; TextA.clear(); } }; @@ -1228,7 +1234,7 @@ struct IMGUI_API ImGuiInputTextState ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. ImGuiID ID; // widget id owning the text state int TextLen; // UTF-8 length of the string in TextA (in bytes) - const char* TextSrc; // == TextA.Data unless read-only, in which case == buf passed to InputText(). Field only set and valid _inside_ the call InputText() call. + const char* TextSrc; // == TextA.Data unless read-only, in which case == buf passed to InputText(). For _ReadOnly fields, pointer will be null outside the InputText() call. ImVector TextA; // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1). ImVector TextToRevertTo; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered) ImVector CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack @@ -1262,6 +1268,7 @@ struct IMGUI_API ImGuiInputTextState int GetCursorPos() const; int GetSelectionStart() const; int GetSelectionEnd() const; + void SetSelection(int start, int end); void SelectAll(); // Reload user buf (WIP #2890) @@ -1329,18 +1336,19 @@ struct ImGuiNextWindowData ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) ImGuiWindowRefreshFlags RefreshFlagsVal; - ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } + ImGuiNextWindowData() { memset((void*)this, 0, sizeof(*this)); } inline void ClearFlags() { HasFlags = ImGuiNextWindowDataFlags_None; } }; enum ImGuiNextItemDataFlags_ { - ImGuiNextItemDataFlags_None = 0, - ImGuiNextItemDataFlags_HasWidth = 1 << 0, - ImGuiNextItemDataFlags_HasOpen = 1 << 1, - ImGuiNextItemDataFlags_HasShortcut = 1 << 2, - ImGuiNextItemDataFlags_HasRefVal = 1 << 3, - ImGuiNextItemDataFlags_HasStorageID = 1 << 4, + ImGuiNextItemDataFlags_None = 0, + ImGuiNextItemDataFlags_HasWidth = 1 << 0, + ImGuiNextItemDataFlags_HasOpen = 1 << 1, + ImGuiNextItemDataFlags_HasShortcut = 1 << 2, + ImGuiNextItemDataFlags_HasRefVal = 1 << 3, + ImGuiNextItemDataFlags_HasStorageID = 1 << 4, + ImGuiNextItemDataFlags_HasColorMarker = 1 << 5, }; struct ImGuiNextItemData @@ -1358,8 +1366,9 @@ struct ImGuiNextItemData ImU8 OpenCond; // Set by SetNextItemOpen() ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal ImGuiID StorageId; // Set by SetNextItemStorageID() + ImU32 ColorMarker; // Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags_ColorMarkers. - ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; } + ImGuiNextItemData() { memset((void*)this, 0, sizeof(*this)); SelectionUserData = -1; } inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! }; @@ -1376,7 +1385,7 @@ struct ImGuiLastItemData ImRect ClipRect; // Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasClipRect) is set.. ImGuiKeyChord Shortcut; // Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasShortcut) is set.. - ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } + ImGuiLastItemData() { memset((void*)this, 0, sizeof(*this)); } }; // Store data emitted by TreeNode() for usage by TreePop() @@ -1409,7 +1418,7 @@ struct IMGUI_API ImGuiErrorRecoveryState short SizeOfBeginPopupStack; short SizeOfDisabledStack; - ImGuiErrorRecoveryState() { memset(this, 0, sizeof(*this)); } + ImGuiErrorRecoveryState() { memset((void*)this, 0, sizeof(*this)); } }; // Data saved for each window pushed into the stack @@ -1470,7 +1479,7 @@ struct ImGuiPopupData ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup - ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; } + ImGuiPopupData() { memset((void*)this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; } }; //----------------------------------------------------------------------------- @@ -1548,7 +1557,7 @@ struct ImGuiInputEvent }; bool AddedByTestEngine; - ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } + ImGuiInputEvent() { memset((void*)this, 0, sizeof(*this)); } }; // Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior. @@ -1580,7 +1589,7 @@ struct ImGuiKeyRoutingTable ImVector EntriesNext; // Double-buffer to avoid reallocation (could use a shared buffer) ImGuiKeyRoutingTable() { Clear(); } - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); } + void Clear() { for (int n = 0; n < IM_COUNTOF(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); } }; // This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features) @@ -1663,7 +1672,7 @@ struct ImGuiListClipperData int ItemsFrozen; ImVector Ranges; - ImGuiListClipperData() { memset(this, 0, sizeof(*this)); } + ImGuiListClipperData() { memset((void*)this, 0, sizeof(*this)); } void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } }; @@ -1798,7 +1807,7 @@ struct IMGUI_API ImGuiTypingSelectState float LastRequestTime = 0.0f; bool SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing. - ImGuiTypingSelectState() { memset(this, 0, sizeof(*this)); } + ImGuiTypingSelectState() { memset((void*)this, 0, sizeof(*this)); } void Clear() { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging }; @@ -1834,7 +1843,7 @@ struct ImGuiOldColumnData ImGuiOldColumnFlags Flags; // Not exposed ImRect ClipRect; - ImGuiOldColumnData() { memset(this, 0, sizeof(*this)); } + ImGuiOldColumnData() { memset((void*)this, 0, sizeof(*this)); } }; struct ImGuiOldColumns @@ -1855,7 +1864,7 @@ struct ImGuiOldColumns ImVector Columns; ImDrawListSplitter Splitter; - ImGuiOldColumns() { memset(this, 0, sizeof(*this)); } + ImGuiOldColumns() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -1883,7 +1892,7 @@ struct ImGuiBoxSelectState ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) ImRect BoxSelectRectCurr; - ImGuiBoxSelectState() { memset(this, 0, sizeof(*this)); } + ImGuiBoxSelectState() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -1994,7 +2003,7 @@ struct ImGuiWindowSettings bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) bool WantDelete; // Set to invalidate/delete the settings entry - ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); } + ImGuiWindowSettings() { memset((void*)this, 0, sizeof(*this)); } char* GetName() { return (char*)(this + 1); } }; @@ -2010,7 +2019,7 @@ struct ImGuiSettingsHandler void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' void* UserData; - ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } + ImGuiSettingsHandler() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -2049,7 +2058,9 @@ struct ImGuiLocEntry // - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling. // - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling. #ifndef IM_ASSERT_USER_ERROR -#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0) // Recoverable User Error +#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } } while (0) // Recoverable User Error +#define IM_ASSERT_USER_ERROR_RET(_EXPR,_MSG) do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } return; } } while (0) // Recoverable User Error +#define IM_ASSERT_USER_ERROR_RETV(_EXPR,_RETV,_MSG) do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } return _RETV; } } while (0) // Recoverable User Error #endif // The error callback is currently not public, as it is expected that only advanced users will rely on it. @@ -2079,7 +2090,8 @@ enum ImGuiDebugLogFlags_ ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY - ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine + ImGuiDebugLogFlags_OutputToDebugger = 1 << 21, // Also send output to Debugger Console [Windows only] + ImGuiDebugLogFlags_OutputToTestEngine = 1 << 22, // Also send output to Dear ImGui Test Engine }; struct ImGuiDebugAllocEntry @@ -2096,7 +2108,7 @@ struct ImGuiDebugAllocInfo ImS16 LastEntriesIdx; // Current index in buffer ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations - ImGuiDebugAllocInfo() { memset(this, 0, sizeof(*this)); } + ImGuiDebugAllocInfo() { memset((void*)this, 0, sizeof(*this)); } }; struct ImGuiMetricsConfig @@ -2125,7 +2137,7 @@ struct ImGuiStackLevelInfo ImS8 DataType; // ImGuiDataType int DescOffset; // -1 or offset into parent's ResultsPathsBuf - ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); DataType = -1; DescOffset = -1; } + ImGuiStackLevelInfo() { memset((void*)this, 0, sizeof(*this)); DataType = -1; DescOffset = -1; } }; struct ImGuiDebugItemPathQuery @@ -2138,7 +2150,7 @@ struct ImGuiDebugItemPathQuery ImGuiTextBuffer ResultsDescBuf; ImGuiTextBuffer ResultPathBuf; - ImGuiDebugItemPathQuery() { memset(this, 0, sizeof(*this)); } + ImGuiDebugItemPathQuery() { memset((void*)this, 0, sizeof(*this)); } }; // State for ID Stack tool queries @@ -2149,7 +2161,7 @@ struct ImGuiIDStackTool int LastActiveFrame; float CopyToClipboardLastTime; - ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; } + ImGuiIDStackTool() { memset((void*)this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; } }; //----------------------------------------------------------------------------- @@ -2167,7 +2179,7 @@ struct ImGuiContextHook ImGuiContextHookCallback Callback; void* UserData; - ImGuiContextHook() { memset(this, 0, sizeof(*this)); } + ImGuiContextHook() { memset((void*)this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -2340,7 +2352,7 @@ struct ImGuiContext ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. - ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted + ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted. Unset/invalid if inverted. int NavScoringDebugCount; // Metrics for debugging int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id int NavTabbingCounter; // >0 when counting items for tabbing @@ -2357,10 +2369,15 @@ struct ImGuiContext bool NavJustMovedToIsTabbing; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags. bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData. - // Navigation: Windowing (Ctrl+Tab for list, or Menu button + keys or directional pads to move/resize) + // Navigation: extra config options (will be made public eventually) + // - Tabbing (Tab, Shift+Tab) and Windowing (Ctrl+Tab, Ctrl+Shift+Tab) are enabled REGARDLESS of ImGuiConfigFlags_NavEnableKeyboard being set. + // - Ctrl+Tab is reconfigurable because it is the only shortcut that may be polled when no window are focused. It also doesn't work e.g. Web platforms. + bool ConfigNavEnableTabbing; // = true. Enable tabbing (Tab, Shift+Tab). PLEASE LET ME KNOW IF YOU USE THIS. bool ConfigNavWindowingWithGamepad; // = true. Enable Ctrl+Tab by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer. - ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828) + ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). Set to 0 to disable. For reconfiguration (see #4828) ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X) + + // Navigation: Windowing (Ctrl+Tab for list, or Menu button + keys or directional pads to move/resize) ImGuiWindow* NavWindowingTarget; // Target window when doing Ctrl+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it. ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the Ctrl+Tab contents @@ -2562,6 +2579,8 @@ struct ImGuiContext // [SECTION] ImGuiWindowTempData, ImGuiWindow //----------------------------------------------------------------------------- +#define IMGUI_WINDOW_HARD_MIN_SIZE 4.0f + // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. // (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..) // (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin) @@ -2612,6 +2631,7 @@ struct IMGUI_API ImGuiWindowTempData // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). + float ItemWidthDefault; float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) @@ -2704,7 +2724,6 @@ struct IMGUI_API ImGuiWindow int LastFrameActive; // Last frame number the window was Active. float LastTimeActive; // Last timestamp the window was Active (using float as we don't need high precision there) - float ItemWidthDefault; ImGuiStorage StateStorage; ImVector ColumnsStorage; float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale() @@ -2780,7 +2799,7 @@ struct ImGuiTabItem ImGuiTabItemFlags Flags; int LastFrameVisible; int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance - float Offset; // Position relative to beginning of tab + float Offset; // Position relative to beginning of tab bar float Width; // Width currently displayed float ContentWidth; // Width of label + padding, stored during BeginTabItem() call (misnamed as "Content" would normally imply width of label only) float RequestedWidth; // Width optionally requested by caller, -1.0f is unused @@ -2789,7 +2808,7 @@ struct ImGuiTabItem ImS16 IndexDuringLayout; // Index only used during TabBarLayout(). Tabs gets reordered so 'Tabs[n].IndexDuringLayout == n' but may mismatch during additions. bool WantClose; // Marked as closed by SetTabItemClosed() - ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } + ImGuiTabItem() { memset((void*)this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } }; // Storage for a tab bar (sizeof() 160 bytes) @@ -2801,6 +2820,7 @@ struct IMGUI_API ImGuiTabBar ImGuiID ID; // Zero for tab-bars used by docking ImGuiID SelectedTabId; // Selected tab/window ImGuiID NextSelectedTabId; // Next selected tab/window. Will also trigger a scrolling animation + ImGuiID NextScrollToTabId; ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for Ctrl+Tab preview) int CurrFrameVisible; int PrevFrameVisible; @@ -2893,7 +2913,7 @@ struct ImGuiTableColumn ImGuiTableColumn() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); StretchWeight = WidthRequest = -1.0f; NameOffset = -1; DisplayOrder = IndexWithinEnabledSet = -1; @@ -3054,7 +3074,7 @@ struct IMGUI_API ImGuiTable bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis - ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } + ImGuiTable() { memset((void*)this, 0, sizeof(*this)); LastFrameActive = -1; } ~ImGuiTable() { IM_FREE(RawData); } }; @@ -3083,7 +3103,7 @@ struct IMGUI_API ImGuiTableTempData float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() - ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } + ImGuiTableTempData() { memset((void*)this, 0, sizeof(*this)); LastTimeActive = -1.0f; } }; // sizeof() ~ 16 @@ -3120,7 +3140,7 @@ struct ImGuiTableSettings ImGuiTableColumnIdx ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) - ImGuiTableSettings() { memset(this, 0, sizeof(*this)); } + ImGuiTableSettings() { memset((void*)this, 0, sizeof(*this)); } ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } }; @@ -3138,6 +3158,7 @@ namespace ImGui // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. IMGUI_API ImGuiIO& GetIO(ImGuiContext* ctx); IMGUI_API ImGuiPlatformIO& GetPlatformIO(ImGuiContext* ctx); + inline float GetScale() { ImGuiContext& g = *GImGui; return g.Style._MainScale; } // FIXME-DPI: I don't want to formalize this just yet. Because reasons. Please don't use. inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); @@ -3196,6 +3217,12 @@ namespace ImGui IMGUI_API void Initialize(); IMGUI_API void Shutdown(); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). + // Context name & generic context hooks + IMGUI_API void SetContextName(ImGuiContext* ctx, const char* name); + IMGUI_API ImGuiID AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook); + IMGUI_API void RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_to_remove); + IMGUI_API void CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType type); + // NewFrame IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); IMGUI_API void UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos); @@ -3205,12 +3232,8 @@ namespace ImGui IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); - // Generic context hooks - IMGUI_API ImGuiID AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); - IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove); - IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); - // Viewports + inline ImGuiViewport* GetWindowViewport() { return GetMainViewport(); } // For code consistency. This is public API in docking branch. IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); @@ -3248,7 +3271,6 @@ namespace ImGui // Basic Accessors inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } - inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.ItemFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); @@ -3304,6 +3326,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindBlockingModal(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); + IMGUI_API ImGuiMouseButton GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags); // Tooltips IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); @@ -3511,6 +3534,7 @@ namespace ImGui IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; } + IMGUI_API void TableFixDisplayOrder(ImGuiTable* table); IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column); @@ -3526,6 +3550,7 @@ namespace ImGui IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); + IMGUI_API void TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); @@ -3574,6 +3599,7 @@ namespace ImGui IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); + IMGUI_API void RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -3587,8 +3613,9 @@ namespace ImGui IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); - IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); + IMGUI_API void RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding); IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding); + IMGUI_API ImDrawFlags CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold); // Widgets: Text IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); @@ -3653,7 +3680,7 @@ namespace ImGui IMGUI_API void InputTextDeactivateHook(ImGuiID id); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); - inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } + inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return g.ActiveId == id && g.TempInputId == id; } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active IMGUI_API void SetNextItemRefVal(ImGuiDataType data_type, void* p_data); inline bool IsItemActiveAsInputText() { ImGuiContext& g = *GImGui; return g.ActiveId != 0 && g.ActiveId == g.LastItemData.ID && g.InputTextState.ID == g.LastItemData.ID; } // This may be useful to apply workaround that a based on distinguish whenever an item is active as a text input field. @@ -3662,6 +3689,7 @@ namespace ImGui IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); + inline void SetNextItemColorMarker(ImU32 col) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasColorMarker; g.NextItemData.ColorMarker = col; } // Plot IMGUI_API int PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg); @@ -3699,6 +3727,7 @@ namespace ImGui IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location); IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location); IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); + IMGUI_API ImU64 DebugTextureIDToU64(ImTextureID tex_id); IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label); @@ -3762,14 +3791,14 @@ struct ImFontLoader // FIXME: At this point the two other types of buffers may be managed by core to be consistent? size_t FontBakedSrcLoaderDataSize; - ImFontLoader() { memset(this, 0, sizeof(*this)); } + ImFontLoader() { memset((void*)this, 0, sizeof(*this)); } }; #ifdef IMGUI_ENABLE_STB_TRUETYPE IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype(); #endif #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are not actually compatible but we provide this as a compile-time error report helper. +typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92.0] The types are not actually compatible but we provide this as a compile-time error report helper. #endif //----------------------------------------------------------------------------- @@ -3859,7 +3888,7 @@ struct ImFontAtlasBuilder ImFontAtlasRectId PackIdMouseCursors; // White pixel + mouse cursors. Also happen to be fallback in case of packing failure. ImFontAtlasRectId PackIdLinesTexData; - ImFontAtlasBuilder() { memset(this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; } + ImFontAtlasBuilder() { memset((void*)this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; } }; IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); @@ -3888,6 +3917,7 @@ IMGUI_API void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, I IMGUI_API void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src); IMGUI_API bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed IMGUI_API void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font); +IMGUI_API void ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font); IMGUI_API void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames); IMGUI_API ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density); @@ -3913,6 +3943,7 @@ IMGUI_API void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atla IMGUI_API void ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h); +IMGUI_API void ImFontAtlasTextureBlockResize(const ImU32* src_pixels, const int src_w, const int src_h, ImU32* dst_pixels, const int dst_w, const int dst_h); IMGUI_API void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data); IMGUI_API void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor); IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col); diff --git a/core/deps/imgui/imgui_tables.cpp b/core/deps/imgui/imgui_tables.cpp index 41fdf6a4c9..8df32585b6 100755 --- a/core/deps/imgui/imgui_tables.cpp +++ b/core/deps/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.5 +// dear imgui, v1.92.6 // (tables and columns code) /* @@ -240,6 +240,7 @@ Index of this file: #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*' #pragma GCC diagnostic ignored "-Wstrict-overflow" #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif //----------------------------------------------------------------------------- @@ -335,7 +336,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[]. const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const ImVec2 avail_size = GetContentRegionAvail(); - const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f)); + const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, IMGUI_WINDOW_HARD_MIN_SIZE), use_child_window ? ImMax(avail_size.y, IMGUI_WINDOW_HARD_MIN_SIZE) : 0.0f)); const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows! if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) @@ -564,9 +565,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const int old_columns_count = table->Columns.size(); if (old_columns_count != 0 && old_columns_count != columns_count) { - // Attempt to preserve width on column count change (#4046) + // Attempt to preserve width and other settings on column count/specs change (#4046) old_columns_to_preserve = table->Columns.Data; - old_columns_raw_data = table->RawData; + old_columns_raw_data = table->RawData; // Free at end of function table->RawData = NULL; } if (table->RawData == NULL) @@ -592,7 +593,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG ImGuiTableColumn* column = &table->Columns[n]; if (old_columns_to_preserve && n < old_columns_count) { - // FIXME: We don't attempt to preserve column order in this path. *column = old_columns_to_preserve[n]; } else @@ -602,8 +602,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG column->WidthAuto = width_auto; column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; + column->DisplayOrder = (ImGuiTableColumnIdx)n; } - column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; + table->DisplayOrderToIndex[n] = column->DisplayOrder; } } if (old_columns_raw_data) @@ -698,7 +699,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) } // Handle reordering request - // Note: we don't clear ReorderColumn after handling the request. + // Note: we don't clear ReorderColumn after handling the request (FIXME: clarify why or add a test). if (table->InstanceCurrent == 0) { if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1) @@ -710,24 +711,12 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) // In the configuration below, moving C to the right of E will lead to: // ... C [D] E ---> ... [D] E C (Column name/index) // ... 2 3 4 ... 2 3 4 (Display order) - const int reorder_dir = table->ReorderColumnDir; - IM_ASSERT(reorder_dir == -1 || reorder_dir == +1); + IM_ASSERT(table->ReorderColumnDir == -1 || table->ReorderColumnDir == +1); IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable); ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn]; - ImGuiTableColumn* dst_column = &table->Columns[(reorder_dir == -1) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn]; - IM_UNUSED(dst_column); - const int src_order = src_column->DisplayOrder; - const int dst_order = dst_column->DisplayOrder; - src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order; - for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir) - table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir; - IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir); - - // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former. - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; + ImGuiTableColumn* dst_column = &table->Columns[(table->ReorderColumnDir < 0) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn]; + TableSetColumnDisplayOrder(table, table->ReorderColumn, dst_column->DisplayOrder); table->ReorderColumnDir = 0; - table->IsSettingsDirty = true; } } @@ -741,6 +730,31 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) } } +// Note that TableSetupScrollFreeze() enforce a display order range for frozen columns. +// So reordering a column across the frozen column barrier is illegal and will be undone. +void ImGui::TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order) +{ + IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); + IM_ASSERT(dst_order >= 0 && dst_order < table->ColumnsCount); + + ImGuiTableColumn* src_column = &table->Columns[column_n]; + const int src_order = src_column->DisplayOrder; + if (src_order == dst_order) + return; + const int reorder_dir = (dst_order < src_order) ? -1 : +1; + + src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order; + for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir) + table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir; + //IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir); + + // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former. + // FIXME-OPT: If this is called multiple times we'd effectively have a O(N^2) thing going on. + for (int n = 0; n < table->ColumnsCount; n++) + table->DisplayOrderToIndex[table->Columns[n].DisplayOrder] = (ImGuiTableColumnIdx)n; + table->IsSettingsDirty = true; +} + // Adjust flags: default width mode + stretch columns are not allowed when auto extending static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in) { @@ -1348,11 +1362,7 @@ void ImGui::EndTable() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "EndTable() call should only be done while in BeginTable() scope!"); - return; - } + IM_ASSERT_USER_ERROR_RET(table != NULL, "EndTable() call should only be done while in BeginTable() scope!"); // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border) @@ -1383,7 +1393,7 @@ void ImGui::EndTable() inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; - const float inner_content_max_y = table->RowPosY2; + const float inner_content_max_y = ImCeil(table->RowPosY2); // Rounding final position is important as we currently don't round row height ('Demo->Tables->Outer Size' demo uses non-integer heights) IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); if (inner_window != outer_window) inner_window->DC.CursorMaxPos.y = inner_content_max_y; @@ -1601,18 +1611,10 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); - return; - } - IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!"); + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); + IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!"); + IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); - if (table->DeclColumnsCount >= table->ColumnsCount) - { - IM_ASSERT_USER_ERROR(table->DeclColumnsCount < table->ColumnsCount, "Called TableSetupColumn() too many times!"); - return; - } ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount]; table->DeclColumnsCount++; @@ -1620,7 +1622,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa. // Give a grace to users of ImGuiTableFlags_ScrollX. if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0) - IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column."); + IM_ASSERT_USER_ERROR_RET(init_width_or_weight <= 0.0f, "TableSetupColumn(): can only specify width/weight if sizing policy is set explicitly in either Table or Column."); // When passing a width automatically enforce WidthFixed policy // (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable) @@ -1662,12 +1664,8 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); - return; - } - IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!"); + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); + IM_ASSERT(table->IsLayoutLocked == false && "TableSetupColumn(): need to call before first row!"); IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit @@ -1743,11 +1741,7 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); - return; - } + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above if (column_n < 0) column_n = table->CurrentColumn; @@ -1825,12 +1819,8 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(target != ImGuiTableBgTarget_None); - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); - return; - } if (color == IM_COL32_DISABLE) color = 0; @@ -2128,11 +2118,7 @@ bool ImGui::TableSetColumnIndex(int column_n) { if (table->CurrentColumn != -1) TableEndCell(table); - if ((column_n >= 0 && column_n < table->ColumnsCount) == false) - { - IM_ASSERT_USER_ERROR(column_n >= 0 && column_n < table->ColumnsCount, "TableSetColumnIndex() invalid column index!"); - return false; - } + IM_ASSERT_USER_ERROR_RETV(column_n >= 0 && column_n < table->ColumnsCount, false, "TableSetColumnIndex() invalid column index!"); TableBeginCell(table, column_n); } @@ -2458,6 +2444,11 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) // - TableDrawBorders() [Internal] //------------------------------------------------------------------------- + +// FIXME: This could be abstracted and merged with PushColumnsBackground(), by creating a generic struct +// with storage for backup cliprect + backup channel + storage for splitter pointer, new clip rect. +// This would slightly simplify caller code. + // Bg2 is used by Selectable (and possibly other widgets) to render to the background. // Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect. void ImGui::TablePushBackgroundChannel() @@ -2619,7 +2610,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels); g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5); memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5); - for (int n = 0; n < IM_ARRAYSIZE(merge_groups); n++) + for (int n = 0; n < IM_COUNTOF(merge_groups); n++) merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n)); ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4)); @@ -2676,7 +2667,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) // [DEBUG] Display merge groups #if 0 if (g.IO.KeyShift) - for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) + for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++) { MergeGroup* merge_group = &merge_groups[merge_group_n]; if (merge_group->ChannelsCount == 0) @@ -2704,7 +2695,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS); //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect; ImRect host_rect = table->HostClipRect; - for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) + for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++) { if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount) { @@ -3113,11 +3104,7 @@ void ImGui::TableHeadersRow() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); - return; - } + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); // Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make // it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this. @@ -3162,12 +3149,7 @@ void ImGui::TableHeader(const char* label) return; ImGuiTable* table = g.CurrentTable; - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); - return; - } - + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(table->CurrentColumn != -1); const int column_n = table->CurrentColumn; ImGuiTableColumn* column = &table->Columns[column_n]; @@ -3197,7 +3179,7 @@ void ImGui::TableHeader(const char* label) sort_arrow = true; if (column->SortOrder > 0) { - ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); + ImFormatString(sort_order_suf, IM_COUNTOF(sort_order_suf), "%d", column->SortOrder + 1); w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x; } } @@ -3342,11 +3324,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label ImGuiTable* table = g.CurrentTable; ImGuiWindow* window = g.CurrentWindow; ImDrawList* draw_list = window->DrawList; - if (table == NULL) - { - IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); - return; - } + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); IM_ASSERT(table->CurrentRow == -1 && "Must be first row"); if (max_label_width == 0.0f) @@ -3787,7 +3765,6 @@ void ImGui::TableLoadSettings(ImGuiTable* table) // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); - ImU64 display_order_mask = 0; for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) { int column_n = column_settings->Index; @@ -3804,24 +3781,51 @@ void ImGui::TableLoadSettings(ImGuiTable* table) } if (settings->SaveFlags & ImGuiTableFlags_Reorderable) column->DisplayOrder = column_settings->DisplayOrder; - display_order_mask |= (ImU64)1 << column->DisplayOrder; if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1) column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1; column->SortOrder = column_settings->SortOrder; column->SortDirection = column_settings->SortDirection; } - // Validate and fix invalid display order data - const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1; - if (display_order_mask != expected_display_order_mask) - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n; - - // Rebuild index + // Fix display order and build index + if (settings->SaveFlags & ImGuiTableFlags_Reorderable) + TableFixDisplayOrder(table); for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; } +struct ImGuiTableFixDisplayOrderColumnData +{ + ImGuiTableColumnIdx Idx; + ImGuiTable* Table; // This is unfortunate but we don't have userdata in qsort api. +}; + +// Sort by DisplayOrder and then Index +static int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void* rhs) +{ + const ImGuiTable* table = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Table; + const ImGuiTableColumnIdx lhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Idx; + const ImGuiTableColumnIdx rhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)rhs)->Idx; + const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder); + return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1; +} + +// Fix invalid display order data: compact values (0,1,3 -> 0,1,2); preserve relative order (0,3,1 -> 0,2,1); deduplicate (0,4,1,1 -> 0,3,1,2) +void ImGui::TableFixDisplayOrder(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + g.TempBuffer.reserve((int)(sizeof(ImGuiTableFixDisplayOrderColumnData) * table->ColumnsCount)); // FIXME: Maybe wrap those two lines as a helper. + ImGuiTableFixDisplayOrderColumnData* fdo_columns = (ImGuiTableFixDisplayOrderColumnData*)(void*)g.TempBuffer.Data; + for (int n = 0; n < table->ColumnsCount; n++) + { + fdo_columns[n].Idx = (ImGuiTableColumnIdx)n; + fdo_columns[n].Table = table; + } + ImQsort(fdo_columns, (size_t)table->ColumnsCount, sizeof(ImGuiTableFixDisplayOrderColumnData), TableFixDisplayOrderComparer); + for (int n = 0; n < table->ColumnsCount; n++) + table->Columns[fdo_columns[n].Idx].DisplayOrder = (ImGuiTableColumnIdx)n; +} + static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; @@ -4063,7 +4067,7 @@ void ImGui::DebugNodeTable(ImGuiTable* table) ImGuiTableColumn* column = &table->Columns[n]; const char* name = TableGetColumnName(table, n); char buf[512]; - ImFormatString(buf, IM_ARRAYSIZE(buf), + ImFormatString(buf, IM_COUNTOF(buf), "Column %d order %d '%s': offset %+.2f to %+.2f%s\n" "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n" diff --git a/core/deps/imgui/imgui_widgets.cpp b/core/deps/imgui/imgui_widgets.cpp old mode 100644 new mode 100755 index 68d741f977..90f63ee323 --- a/core/deps/imgui/imgui_widgets.cpp +++ b/core/deps/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.5 +// dear imgui, v1.92.6 // (widgets code) /* @@ -93,6 +93,7 @@ Index of this file: #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers +#pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result #endif //------------------------------------------------------------------------- @@ -135,7 +136,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); //------------------------------------------------------------------------- // For InputTextEx() -static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); +static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* state, unsigned int* p_char, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0); //------------------------------------------------------------------------- @@ -862,11 +863,10 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiBut if (window->SkipItems) return false; - // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. - IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); + // Ensure zero-size fits to contents + ImVec2 size = CalcItemSize(ImVec2(size_arg.x != 0.0f ? size_arg.x : -FLT_MIN, size_arg.y != 0.0f ? size_arg.y : -FLT_MIN), 0.0f, 0.0f); const ImGuiID id = window->GetID(str_id); - ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); ItemSize(size); if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav)) @@ -1008,20 +1008,7 @@ void ImGui::Scrollbar(ImGuiAxis axis) // Calculate scrollbar bounding box ImRect bb = GetWindowScrollbarRect(window, axis); - ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone; - if (axis == ImGuiAxis_X) - { - rounding_corners |= ImDrawFlags_RoundCornersBottomLeft; - if (!window->ScrollbarY) - rounding_corners |= ImDrawFlags_RoundCornersBottomRight; - } - else - { - if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) - rounding_corners |= ImDrawFlags_RoundCornersTopRight; - if (!window->ScrollbarX) - rounding_corners |= ImDrawFlags_RoundCornersBottomRight; - } + ImDrawFlags rounding_corners = CalcRoundingFlagsForRectInRect(bb, window->Rect(), g.Style.WindowBorderSize); float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; ImS64 scroll = (ImS64)window->Scroll[axis]; @@ -1063,6 +1050,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight(); + if (scrollbar_size_v < 1.0f) + return false; // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. @@ -1155,11 +1144,15 @@ void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const Im return; // Render - if (g.Style.ImageBorderSize > 0.0f) - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, ImDrawFlags_None, g.Style.ImageBorderSize); + float rounding = g.Style.ImageRounding; if (bg_col.w > 0.0f) - window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); - window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col), rounding); + if (rounding > 0.0f) + window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), rounding); + else + window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + if (g.Style.ImageBorderSize > 0.0f) + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, ImDrawFlags_None, g.Style.ImageBorderSize); } void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1) @@ -1199,10 +1192,14 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_ // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavCursor(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); + RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); - window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + float image_rounding = ImMax(g.Style.FrameRounding - ImMax(padding.x, padding.y), g.Style.ImageRounding); + if (image_rounding > 0.0f) + window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), image_rounding); + else + window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); return pressed; } @@ -1464,7 +1461,10 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over // Render RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding); + float fill_x0 = ImLerp(bb.Min.x, bb.Max.x, fill_n0); + float fill_x1 = ImLerp(bb.Min.x, bb.Max.x, fill_n1); + if (fill_x0 < fill_x1) + RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_x0, fill_x1, style.FrameRounding); // Default displaying the fraction as percentage string, but user can override it // Don't display text for indeterminate bars by default @@ -1473,14 +1473,14 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over { if (!overlay) { - ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); + ImFormatString(overlay_buf, IM_COUNTOF(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); overlay = overlay_buf; } ImVec2 overlay_size = CalcTextSize(overlay, NULL); if (overlay_size.x > 0.0f) { - float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x; + float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : fill_x1 + style.ItemSpacing.x; RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); } } @@ -1741,7 +1741,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end const float separator_thickness = style.SeparatorTextBorderSize; const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); - const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f)); + const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); ItemSize(min_size, text_baseline_y); if (!ItemAdd(bb, id)) return; @@ -1858,7 +1858,7 @@ static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; if (int d = (int)(b->Width - a->Width)) return d; - return (b->Index - a->Index); + return b->Index - a->Index; } // Shrink excess width from a set of item, by removing width from the larger items first. @@ -2040,7 +2040,7 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags // This is essentially a specialized version of BeginPopupEx() char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth + ImFormatString(name, IM_COUNTOF(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth // Set position given a custom constraint (peak into expected window size so we can position it) // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? @@ -2075,8 +2075,12 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags void ImGui::EndCombo() { ImGuiContext& g = *GImGui; - EndPopup(); g.BeginComboDepth--; + char name[16]; + ImFormatString(name, IM_COUNTOF(name), "##Combo_%02d", g.BeginComboDepth); // FIXME: Move those to helpers? + if (strcmp(g.CurrentWindow->Name, name) != 0) + IM_ASSERT_USER_ERROR_RET(0, "Calling EndCombo() in wrong window!"); + EndPopup(); } // Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements @@ -2261,6 +2265,11 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void* // - RoundScalarWithFormat<>() //------------------------------------------------------------------------- +static const ImU32 GDefaultRgbaColorMarkers[4] = +{ + IM_COL32(240,20,20,255), IM_COL32(20,240,20,255), IM_COL32(20,20,240,255), IM_COL32(140,140,140,255) +}; + static const ImGuiDataTypeInfo GDataTypeInfo[] = { { sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 @@ -2281,7 +2290,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = { sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool { 0, "char*","%s", "%s" }, // ImGuiDataType_String }; -IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); +IM_STATIC_ASSERT(IM_COUNTOF(GDataTypeInfo) == ImGuiDataType_COUNT); const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) { @@ -2390,7 +2399,7 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) format = type_info->ScanFmt; else - format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized)); + format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_COUNTOF(format_sanitized)); // Small types need a 32-bit buffer to receive the result from scanf() int v32 = 0; @@ -2481,7 +2490,7 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision) static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; if (decimal_precision < 0) return FLT_MIN; - return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); + return (decimal_precision < IM_COUNTOF(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); } template @@ -2495,12 +2504,12 @@ TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, // Sanitize format char fmt_sanitized[32]; - ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); + ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_COUNTOF(fmt_sanitized)); fmt_start = fmt_sanitized; // Format value with our rounding, and read back char v_str[64]; - ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); + ImFormatString(v_str, IM_COUNTOF(v_str), fmt_start, v); const char* p = v_str; while (*p == ' ') p++; @@ -2709,6 +2718,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); @@ -2777,7 +2787,10 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); + if (color_marker != 0 && style.ColorMarkerSize > 0.0f) + RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); + RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Drag behavior const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags); @@ -2786,7 +2799,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); @@ -2815,6 +2828,8 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); + if (flags & ImGuiSliderFlags_ColorMarkers) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags); PopID(); PopItemWidth(); @@ -3313,6 +3328,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); @@ -3363,7 +3379,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); + if (color_marker != 0 && style.ColorMarkerSize > 0.0f) + RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); + RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Slider behavior ImRect grab_bb; @@ -3377,7 +3396,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); @@ -3407,6 +3426,8 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); + if (flags & ImGuiSliderFlags_ColorMarkers) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags); PopID(); PopItemWidth(); @@ -3528,7 +3549,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // For the vertical slider we allow centered text to overlap the frame padding char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); @@ -3728,16 +3749,16 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); char fmt_buf[32]; char data_buf[32]; - format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); + format = ImParseFormatTrimDecorations(format, fmt_buf, IM_COUNTOF(fmt_buf)); if (format[0] == 0) format = type_info->PrintFmt; - DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); + DataTypeFormatString(data_buf, IM_COUNTOF(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData. bool value_changed = false; - if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) + if (TempInputText(bb, id, label, data_buf, IM_COUNTOF(data_buf), flags)) { // Backup old value size_t data_type_size = type_info->Size; @@ -3790,7 +3811,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0) buf[0] = 0; else - DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); + DataTypeFormatString(buf, IM_COUNTOF(buf), data_type, p_data, format); // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited. // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. @@ -3800,7 +3821,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data bool value_changed = false; if (p_step == NULL) { - if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) + if (InputText(label, buf, IM_COUNTOF(buf), flags)) value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); } else @@ -3810,7 +3831,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() PushID(label); SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); - if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view + if (InputText("", buf, IM_COUNTOF(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); @@ -4256,7 +4277,7 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st // We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators. ImGuiInputTextState::ImGuiInputTextState() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); Stb = IM_NEW(ImStbTexteditState); memset(Stb, 0, sizeof(*Stb)); } @@ -4297,6 +4318,7 @@ void ImGuiInputTextState::ClearSelection() { Stb->select_start int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; } int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } +void ImGuiInputTextState::SetSelection(int start, int end) { Stb->select_start = start; Stb->cursor = Stb->select_end = end; } float ImGuiInputTextState::GetPreferredOffsetX() const { return Stb->has_preferred_x ? Stb->preferred_x : -1; } void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; } void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } @@ -4305,7 +4327,7 @@ void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { WantReloadUserBuf ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); } // Public API to manipulate UTF-8 text from within a callback. @@ -4401,9 +4423,10 @@ void ImGui::PopPasswordFont() } // Return false to discard a character. -static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) +static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* state, unsigned int* p_char, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) { unsigned int c = *p_char; + ImGuiInputTextFlags flags = state->Flags; // Filter non-printable (NB: isprint is unreliable! see #2467) bool apply_named_filters = true; @@ -4493,9 +4516,11 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im ImGuiContext& g = *GImGui; ImGuiInputTextCallbackData callback_data; callback_data.Ctx = &g; + callback_data.ID = state->ID; + callback_data.Flags = flags; callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; callback_data.EventChar = (ImWchar)c; - callback_data.Flags = flags; + callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = user_data; if (callback(&callback_data) != 0) return false; @@ -4852,6 +4877,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); + if (input_requested_by_nav) + SetNavCursorVisibleAfterMove(); } if (g.ActiveId == id) { @@ -5010,7 +5037,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id)) { unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data)) state->OnCharPressed(c); } // FIXME: Implement Shift+Tab @@ -5033,7 +5060,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ unsigned int c = (unsigned int)io.InputQueueCharacters[n]; if (c == '\t') // Skip Tab, see above. continue; - if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data)) state->OnCharPressed(c); } @@ -5119,7 +5146,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Insert new line unsigned int c = '\n'; - if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data)) state->OnCharPressed(c); } } @@ -5188,7 +5215,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ unsigned int c; int in_len = ImTextCharFromUtf8(&c, s, clipboard_end); s += in_len; - if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true)) + if (!InputTextFilterCharacter(&g, state, &c, callback, callback_user_data, true)) continue; char c_utf8[5]; ImTextCharToUtf8(c_utf8, c); @@ -5287,8 +5314,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { ImGuiInputTextCallbackData callback_data; callback_data.Ctx = &g; - callback_data.EventFlag = event_flag; + callback_data.ID = id; callback_data.Flags = flags; + callback_data.EventFlag = event_flag; + callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = callback_user_data; // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925 @@ -5364,8 +5393,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { ImGuiInputTextCallbackData callback_data; callback_data.Ctx = &g; - callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.ID = id; callback_data.Flags = flags; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.Buf = buf; callback_data.BufTextLen = apply_new_text_length; callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); @@ -5395,7 +5426,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; - ImVec2 text_size(0.0f, 0.0f); ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size if (is_multiline) clip_rect.ClipWith(draw_window->ClipRect); @@ -5453,7 +5483,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ line_visible_n1 = ImMin(line_visible_n1, line_count); // Store text height (we don't need width) - text_size = ImVec2(inner_size.x, line_count * g.FontSize); + float text_size_y = line_count * g.FontSize; //GetForegroundDrawList()->AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255)); // Calculate blinking cursor position @@ -5513,7 +5543,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } if (new_scroll_y != scroll_y) { - const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); + const float scroll_max_y = ImMax((text_size_y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y); draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag draw_window->Scroll.y = scroll_y; @@ -5606,7 +5636,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline) { // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)... - Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); + Dummy(ImVec2(0.0f, text_size_y + style.FramePadding.y)); g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); @@ -5621,7 +5651,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ g.LastItemData.StatusFlags = item_data_backup.StatusFlags; } } - if (state) + if (state && is_readonly) state->TextSrc = NULL; // Log as text @@ -5805,8 +5835,10 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { // RGB/HSV 0..255 Sliders const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1); + const float w_per_component = IM_TRUNC(w_items / components); + const bool draw_color_marker = (flags & (ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_NoColorMarkers)) == 0; - const bool hide_prefix = (IM_TRUNC(w_items / components) <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); + const bool hide_prefix = draw_color_marker || (w_per_component <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; static const char* fmt_table_int[3][4] = { @@ -5821,6 +5853,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA }; const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; + const ImGuiSliderFlags drag_flags = draw_color_marker ? ImGuiSliderFlags_ColorMarkers : ImGuiSliderFlags_None; float prev_split = 0.0f; for (int n = 0; n < components; n++) @@ -5830,16 +5863,18 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag float next_split = IM_TRUNC(w_items * (n + 1) / components); SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); prev_split = next_split; + if (draw_color_marker) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[n]); // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. if (flags & ImGuiColorEditFlags_Float) { - value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n], drag_flags); value_changed_as_float |= value_changed; } else { - value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n], drag_flags); } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); @@ -5850,11 +5885,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // RGB Hexadecimal Input char buf[64]; if (alpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); else - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); SetNextItemWidth(w_inputs); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase)) + if (InputText("##Text", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_CharsUppercase)) { value_changed = true; char* p = buf; @@ -6462,6 +6497,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl } // Initialize/override default color options +// FIXME: Could be moved to a simple IO field. void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; @@ -6548,18 +6584,18 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) { int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); char buf[64]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + ImFormatString(buf, IM_COUNTOF(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); if (Selectable(buf)) SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); + ImFormatString(buf, IM_COUNTOF(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); if (Selectable(buf)) SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X", cr, cg, cb); if (Selectable(buf)) SetClipboardText(buf); if (!(flags & ImGuiColorEditFlags_NoAlpha)) { - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); if (Selectable(buf)) SetClipboardText(buf); } @@ -6800,7 +6836,6 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1) window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth); } -// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop. bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); @@ -6809,26 +6844,28 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; + + // When not framed, we vertically increase height up to typical framed widget height const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; - const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); + const bool use_frame_padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)); + const ImVec2 padding = use_frame_padding ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); if (!label_end) label_end = FindRenderedTextEnd(label); const ImVec2 label_size = CalcTextSize(label, label_end, false); const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing - const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float text_offset_y = use_frame_padding ? ImMax(style.FramePadding.y, window->DC.CurrLineTextBaseOffset) : window->DC.CurrLineTextBaseOffset; // Latch before ItemSize changes it const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow - // We vertically grow up to current line height up the typical widget height. - const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); + const float frame_height = label_size.y + padding.y * 2; const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL); const bool span_all_columns_label = (flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) != 0 && (g.CurrentTable != NULL); ImRect frame_bb; frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; - frame_bb.Min.y = window->DC.CursorPos.y; + frame_bb.Min.y = window->DC.CursorPos.y + (text_offset_y - padding.y); frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanLabelWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x; - frame_bb.Max.y = window->DC.CursorPos.y + frame_height; + frame_bb.Max.y = window->DC.CursorPos.y + (text_offset_y - padding.y) + frame_height; if (display_frame) { const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits @@ -7517,7 +7554,7 @@ ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags f } // Append to buffer - const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1; + const int buffer_max_len = IM_COUNTOF(data->SearchBuffer) - 1; int buffer_len = (int)ImStrlen(data->SearchBuffer); bool select_request = false; for (ImWchar w : g.IO.InputQueueCharacters) @@ -8917,7 +8954,7 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) if (float_format) { char fmt[64]; - ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); + ImFormatString(fmt, IM_COUNTOF(fmt), "%%s: %s", float_format); Text(fmt, prefix, v); } else @@ -8956,7 +8993,7 @@ void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) { ImU16 offset = 0; bool want_spacing = false; - for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) + for (int i = 0; i < IM_COUNTOF(Widths); i++) { ImU16 width = Widths[i]; if (want_spacing && width > 0) @@ -9053,6 +9090,10 @@ void ImGui::EndMenuBar() NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat } } + else + { + NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_WrapX); + } PopClipRect(); PopID(); @@ -9138,11 +9179,7 @@ bool ImGui::BeginMainMenuBar() void ImGui::EndMainMenuBar() { ImGuiContext& g = *GImGui; - if (!g.CurrentWindow->DC.MenuBarAppending) - { - IM_ASSERT_USER_ERROR(0, "Calling EndMainMenuBar() not from a menu-bar!"); // Not technically testing that it is the main menu bar - return; - } + IM_ASSERT_USER_ERROR_RET(g.CurrentWindow->DC.MenuBarAppending, "Calling EndMainMenuBar() not from a menu-bar!"); // Not technically testing that it is the main menu bar EndMenuBar(); g.CurrentWindow->Flags |= ImGuiWindowFlags_NoSavedSettings; // Restore _NoSavedSettings (#8356) @@ -9222,7 +9259,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. - ImVec2 popup_pos, pos = window->DC.CursorPos; + ImVec2 popup_pos; + ImVec2 pos = window->DC.CursorPos; PushID(label); if (!enabled) BeginDisabled(); @@ -9236,34 +9274,34 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Menu inside a horizontal menu bar // Selectable extend their highlight by half ItemSpacing in each direction. // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); float w = label_size.x; - ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); LogSetNextTextDecoration("[", "]"); RenderText(text_pos, label); PopStyleVar(); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), text_pos.y - style.FramePadding.y + window->MenuBarHeight); } else { // Menu inside a regular/vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.) - popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + ImVec2 text_pos(window->DC.CursorPos.x, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); LogSetNextTextDecoration("", ">"); - RenderText(text_pos, label); + RenderText(ImVec2(text_pos.x + offsets->OffsetLabel, text_pos.y), label); if (icon_w > 0.0f) - RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); - RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); + RenderText(ImVec2(text_pos.x + offsets->OffsetIcon, text_pos.y), icon); + RenderArrow(window->DrawList, ImVec2(text_pos.x + offsets->OffsetMark + extra_w + g.FontSize * 0.30f, text_pos.y), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); + popup_pos = ImVec2(pos.x, text_pos.y - style.WindowPadding.y); } if (!enabled) EndDisabled(); @@ -9401,7 +9439,8 @@ void ImGui::EndMenu() // Nav: When a left move request our menu failed, close ourselves. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginMenu()/EndMenu() calls + IM_ASSERT_USER_ERROR_RET((window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu), "Calling EndMenu() in wrong window!"); + ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert. if (window->BeginCount == window->BeginCountPreviousFrame) if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet()) @@ -9464,21 +9503,22 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); + ImVec2 text_pos(pos.x, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) { - RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + RenderText(text_pos + ImVec2(offsets->OffsetLabel, 0.0f), label); if (icon_w > 0.0f) - RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + RenderText(text_pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); if (shortcut_w > 0.0f) { PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); LogSetNextTextDecoration("(", ")"); - RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); + RenderText(text_pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); PopStyleColor(); } if (selected) - RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); + RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } } IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); @@ -9539,7 +9579,7 @@ struct ImGuiTabBarSection float WidthAfterShrinkMinWidth; float Spacing; // Horizontal spacing at the end of the section. - ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); } + ImGuiTabBarSection() { memset((void*)this, 0, sizeof(*this)); } }; namespace ImGui @@ -9555,7 +9595,7 @@ namespace ImGui ImGuiTabBar::ImGuiTabBar() { - memset(this, 0, sizeof(*this)); + memset((void*)this, 0, sizeof(*this)); CurrFrameVisible = PrevFrameVisible = -1; LastTabItemIdx = -1; } @@ -9697,11 +9737,7 @@ void ImGui::EndTabBar() return; ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!"); - return; - } + IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!"); // Fallback in case no TabItem have been submitted if (tab_bar->WantLayout) @@ -9790,11 +9826,17 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection); // Calculate spacing between sections - sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; - sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; + const float tab_spacing = g.Style.ItemInnerSpacing.x; + sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? tab_spacing : 0.0f; + sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? tab_spacing : 0.0f; // Setup next selected tab ImGuiID scroll_to_tab_id = 0; + if (tab_bar->NextScrollToTabId) + { + scroll_to_tab_id = tab_bar->NextScrollToTabId; + tab_bar->NextScrollToTabId = 0; + } if (tab_bar->NextSelectedTabId) { tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; @@ -9852,8 +9894,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; - section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); - section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); + section->Width += tab->ContentWidth + (section_n == curr_section_n ? tab_spacing : 0.0f); + section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? tab_spacing : 0.0f); curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down @@ -10158,6 +10200,7 @@ void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* s if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) return; + const float tab_spacing = g.Style.ItemInnerSpacing.x; const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); @@ -10176,8 +10219,8 @@ void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* s dst_idx = i; // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. - const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; - const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; + const float x1 = bar_offset + dst_tab->Offset - tab_spacing; + const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + tab_spacing; //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) break; @@ -10340,11 +10383,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f return false; ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); - return false; - } + IM_ASSERT_USER_ERROR_RETV(tab_bar != NULL, false, "Needs to be called between BeginTabBar() and EndTabBar()!"); IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); @@ -10364,11 +10403,7 @@ void ImGui::EndTabItem() return; ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); - return; - } + IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); IM_ASSERT(tab_bar->LastTabItemIdx >= 0); ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) @@ -10383,11 +10418,7 @@ bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) return false; ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); - return false; - } + IM_ASSERT_USER_ERROR_RETV(tab_bar != NULL, false, "Needs to be called between BeginTabBar() and EndTabBar()!"); return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); } @@ -10399,11 +10430,7 @@ void ImGui::TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float return; ImGuiTabBar* tab_bar = g.CurrentTabBar; - if (tab_bar == NULL) - { - IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); - return; - } + IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); SetNextItemWidth(width); TabItemEx(tab_bar, str_id, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder | ImGuiTabItemFlags_Invisible, NULL); } diff --git a/core/deps/imgui/misc/freetype/imgui_freetype.cpp b/core/deps/imgui/misc/freetype/imgui_freetype.cpp new file mode 100755 index 0000000000..12da967ede --- /dev/null +++ b/core/deps/imgui/misc/freetype/imgui_freetype.cpp @@ -0,0 +1,845 @@ +// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder) +// (code) + +// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype +// Original code by @vuhdo (Aleksei Skriabin) in 2017, with improvements by @mikesart. +// Maintained since 2019 by @ocornut. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2025/06/11: refactored for the new ImFontLoader architecture, and ImGuiBackendFlags_RendererHasTextures support. +// 2024/10/17: added plutosvg support for SVG Fonts (seems faster/better than lunasvg). Enable by using '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG'. (#7927) +// 2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics. +// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'. (#6591) +// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly. +// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns nullptr. +// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs. +// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format. +// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+). +// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas(). +// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails. +// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!) +// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions(). +// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding. +// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX. +// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) +// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member. +// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement). +// 2017/09/26: fixes for imgui internal changes. +// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply. +// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks. + +// About Gamma Correct Blending: +// - FreeType assumes blending in linear space rather than gamma space. +// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph +// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output. +// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking). + +// FIXME: cfg.OversampleH, OversampleV are not supported, but generally not necessary with this rasterizer because Hinting makes everything look better. + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_freetype.h" +#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*, +#include +#ifdef _MSC_VER +#pragma push_macro("generic") +#undef generic +#define generic __generic +#endif +#include +#include FT_FREETYPE_H // +#include FT_MODULE_H // +#include FT_GLYPH_H // +#include FT_SIZES_H // +#include FT_SYNTHESIS_H // +#include FT_TRUETYPE_TABLES_H // +#include FT_TRUETYPE_TAGS_H // +#ifdef _MSC_VER +#pragma pop_macro("generic") +#endif + +// Handle LunaSVG and PlutoSVG +#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) +#error "Cannot enable both IMGUI_ENABLE_FREETYPE_LUNASVG and IMGUI_ENABLE_FREETYPE_PLUTOSVG" +#endif +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +#include FT_OTSVG_H // +#include FT_BBOX_H // +#include +#endif +#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG +#include +#endif +#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined (IMGUI_ENABLE_FREETYPE_PLUTOSVG) +#if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) +#error IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12 +#endif +#endif + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wsubobject-linkage" // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace +#endif +#endif + +//------------------------------------------------------------------------- +// Data +//------------------------------------------------------------------------- + +// Default memory allocators +static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); } +static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); } + +// Current memory allocators +static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc; +static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc; +static void* GImGuiFreeTypeAllocatorUserData = nullptr; + +// Lunasvg support +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state); +static void ImGuiLunasvgPortFree(FT_Pointer* state); +static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state); +static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state); +#endif + +//------------------------------------------------------------------------- +// Code +//------------------------------------------------------------------------- + +#define FT_CEIL(X) (((X + 63) & -64) / 64) // From SDL_ttf: Handy routines for converting from fixed point +#define FT_SCALEFACTOR 64.0f // For converting from/to 26.6 factionals + +// Glyph metrics: +// -------------- +// +// xmin xmax +// | | +// |<-------- width -------->| +// | | +// | +-------------------------+----------------- ymax +// | | ggggggggg ggggg | ^ ^ +// | | g:::::::::ggg::::g | | | +// | | g:::::::::::::::::g | | | +// | | g::::::ggggg::::::gg | | | +// | | g:::::g g:::::g | | | +// offsetX -|-------->| g:::::g g:::::g | offsetY | +// | | g:::::g g:::::g | | | +// | | g::::::g g:::::g | | | +// | | g:::::::ggggg:::::g | | | +// | | g::::::::::::::::g | | height +// | | gg::::::::::::::g | | | +// baseline ---*---------|---- gggggggg::::::g-----*-------- | +// / | | g:::::g | | +// origin | | gggggg g:::::g | | +// | | g:::::gg gg:::::g | | +// | | g::::::ggg:::::::g | | +// | | gg:::::::::::::g | | +// | | ggg::::::ggg | | +// | | gggggg | v +// | +-------------------------+----------------- ymin +// | | +// |------------- advanceX ----------->| + +// Stored in ImFontAtlas::FontLoaderData. ALLOCATED BY US. +struct ImGui_ImplFreeType_Data +{ + FT_Library Library; + FT_MemoryRec_ MemoryManager; + ImGui_ImplFreeType_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Stored in ImFontConfig::FontLoaderData. ALLOCATED BY US. +struct ImGui_ImplFreeType_FontSrcData +{ + // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime. + bool InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_user_flags); + void CloseFont(); + ImGui_ImplFreeType_FontSrcData() { memset((void*)this, 0, sizeof(*this)); } + ~ImGui_ImplFreeType_FontSrcData() { CloseFont(); } + + // Members + FT_Face FtFace; + ImGuiFreeTypeLoaderFlags UserFlags; // = ImFontConfig::FontLoaderFlags + FT_Int32 LoadFlags; + ImFontBaked* BakedLastActivated; +}; + +// Stored in ImFontBaked::FontLoaderDatas: pointer to SourcesCount instances of this. ALLOCATED BY CORE. +struct ImGui_ImplFreeType_FontSrcBakedData +{ + FT_Size FtSize; // This represent a FT_Face with a given size. + ImGui_ImplFreeType_FontSrcBakedData() { memset((void*)this, 0, sizeof(*this)); } +}; + +bool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_font_loader_flags) +{ + FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)src->FontData, (FT_Long)src->FontDataSize, (FT_Long)src->FontNo, &FtFace); + if (error != 0) + return false; + + // Reject COLRv1 fonts as they require a dedicated rendering engine (not supported) + if (FT_IS_SFNT(FtFace)) + { + FT_ULong len = 0; + if (FT_Load_Sfnt_Table(FtFace, TTAG_COLR, 0, nullptr, &len) == 0 && len >= 2) + { + FT_Byte header[2]; + FT_ULong headerLen = sizeof(header); + if (FT_Load_Sfnt_Table(FtFace, TTAG_COLR, 0, header, &headerLen) == 0 && headerLen >= 2) + { + int version = (header[0] << 8) | header[1]; + if (version == 1) + { + FT_Done_Face(FtFace); + FtFace = nullptr; + return false; + } + } + } + } + + + error = FT_Select_Charmap(FtFace, FT_ENCODING_UNICODE); + if (error != 0) + return false; + + // Convert to FreeType flags (NB: Bold and Oblique are processed separately) + UserFlags = (ImGuiFreeTypeLoaderFlags)(src->FontLoaderFlags | extra_font_loader_flags); + + LoadFlags = 0; + if ((UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) == 0) + LoadFlags |= FT_LOAD_NO_BITMAP; + if (UserFlags & ImGuiFreeTypeLoaderFlags_NoHinting) + LoadFlags |= FT_LOAD_NO_HINTING; + if (UserFlags & ImGuiFreeTypeLoaderFlags_NoAutoHint) + LoadFlags |= FT_LOAD_NO_AUTOHINT; + if (UserFlags & ImGuiFreeTypeLoaderFlags_ForceAutoHint) + LoadFlags |= FT_LOAD_FORCE_AUTOHINT; + if (UserFlags & ImGuiFreeTypeLoaderFlags_LightHinting) + LoadFlags |= FT_LOAD_TARGET_LIGHT; + else if (UserFlags & ImGuiFreeTypeLoaderFlags_MonoHinting) + LoadFlags |= FT_LOAD_TARGET_MONO; + else + LoadFlags |= FT_LOAD_TARGET_NORMAL; + + if (UserFlags & ImGuiFreeTypeLoaderFlags_LoadColor) + LoadFlags |= FT_LOAD_COLOR; + + return true; +} + +void ImGui_ImplFreeType_FontSrcData::CloseFont() +{ + if (FtFace) + { + FT_Done_Face(FtFace); + FtFace = nullptr; + } +} + +static const FT_Glyph_Metrics* ImGui_ImplFreeType_LoadGlyph(ImGui_ImplFreeType_FontSrcData* src_data, uint32_t codepoint) +{ + uint32_t glyph_index = FT_Get_Char_Index(src_data->FtFace, codepoint); + if (glyph_index == 0) + return nullptr; + + // If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts. + // - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076 + // - https://github.com/ocornut/imgui/issues/4567 + // - https://github.com/ocornut/imgui/issues/4566 + // You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version. + FT_Error error = FT_Load_Glyph(src_data->FtFace, glyph_index, src_data->LoadFlags); + if (error) + return nullptr; + + // Need an outline for this to work + FT_GlyphSlot slot = src_data->FtFace->glyph; +#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) + IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG); +#else +#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) + IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font"); +#endif + IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP); +#endif // IMGUI_ENABLE_FREETYPE_LUNASVG + + // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting) + if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bold) + FT_GlyphSlot_Embolden(slot); + if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Oblique) + { + FT_GlyphSlot_Oblique(slot); + //FT_BBox bbox; + //FT_Outline_Get_BBox(&slot->outline, &bbox); + //slot->metrics.width = bbox.xMax - bbox.xMin; + //slot->metrics.height = bbox.yMax - bbox.yMin; + } + + return &slot->metrics; +} + +static void ImGui_ImplFreeType_BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch) +{ + IM_ASSERT(ft_bitmap != nullptr); + const uint32_t w = ft_bitmap->width; + const uint32_t h = ft_bitmap->rows; + const uint8_t* src = ft_bitmap->buffer; + const uint32_t src_pitch = ft_bitmap->pitch; + + switch (ft_bitmap->pixel_mode) + { + case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel. + { + for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) + for (uint32_t x = 0; x < w; x++) + dst[x] = IM_COL32(255, 255, 255, src[x]); + break; + } + case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB. + { + for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) + { + uint8_t bits = 0; + const uint8_t* bits_ptr = src; + for (uint32_t x = 0; x < w; x++, bits <<= 1) + { + if ((x & 7) == 0) + bits = *bits_ptr++; + dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? 255 : 0); + } + } + break; + } + case FT_PIXEL_MODE_BGRA: + { + // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good. +#define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u) + for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) + for (uint32_t x = 0; x < w; x++) + { + uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3]; + dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a); + } +#undef DE_MULTIPLY + break; + } + default: + IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!"); + } +} + +// FreeType memory allocation callbacks +static void* FreeType_Alloc(FT_Memory /*memory*/, long size) +{ + return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData); +} + +static void FreeType_Free(FT_Memory /*memory*/, void* block) +{ + GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); +} + +static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block) +{ + // Implement realloc() as we don't ask user to provide it. + if (block == nullptr) + return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); + + if (new_size == 0) + { + GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); + return nullptr; + } + + if (new_size > cur_size) + { + void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); + memcpy(new_block, block, (size_t)cur_size); + GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); + return new_block; + } + + return block; +} + +static bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) +{ + IM_ASSERT(atlas->FontLoaderData == nullptr); + ImGui_ImplFreeType_Data* bd = IM_NEW(ImGui_ImplFreeType_Data)(); + + // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html + bd->MemoryManager.user = nullptr; + bd->MemoryManager.alloc = &FreeType_Alloc; + bd->MemoryManager.free = &FreeType_Free; + bd->MemoryManager.realloc = &FreeType_Realloc; + + // https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library + FT_Error error = FT_New_Library(&bd->MemoryManager, &bd->Library); + if (error != 0) + { + IM_DELETE(bd); + return false; + } + + // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator. + FT_Add_Default_Modules(bd->Library); + +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG + // Install svg hooks for FreeType + // https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks + // https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts + SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot }; + FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", &hooks); +#endif // IMGUI_ENABLE_FREETYPE_LUNASVG +#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG + // With plutosvg, use provided hooks + FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", plutosvg_ft_svg_hooks()); +#endif // IMGUI_ENABLE_FREETYPE_PLUTOSVG + + // Store our data + atlas->FontLoaderData = (void*)bd; + + return true; +} + +static void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas) +{ + ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; + IM_ASSERT(bd != nullptr); + FT_Done_Library(bd->Library); + IM_DELETE(bd); + atlas->FontLoaderData = nullptr; +} + +static bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) +{ + ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; + ImGui_ImplFreeType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplFreeType_FontSrcData); + IM_ASSERT(src->FontLoaderData == nullptr); + src->FontLoaderData = bd_font_data; + + if (!bd_font_data->InitFont(bd->Library, src, (ImGuiFreeTypeLoaderFlags)atlas->FontLoaderFlags)) + { + IM_DELETE(bd_font_data); + src->FontLoaderData = nullptr; + return false; + } + + return true; +} + +static void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) +{ + IM_UNUSED(atlas); + ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; + IM_DELETE(bd_font_data); + src->FontLoaderData = nullptr; +} + +static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) +{ + IM_UNUSED(atlas); + float size = baked->Size; + if (src->MergeMode && src->SizePixels != 0.0f) + size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels); + size *= src->ExtraSizeScale; + + ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; + bd_font_data->BakedLastActivated = baked; + + // We use one FT_Size per (source + baked) combination. + ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; + IM_ASSERT(bd_baked_data != nullptr); + IM_PLACEMENT_NEW(bd_baked_data) ImGui_ImplFreeType_FontSrcBakedData(); + + FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize); + FT_Activate_Size(bd_baked_data->FtSize); + const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; + if (((bd_font_data->FtFace->face_flags & FT_FACE_FLAG_FIXED_SIZES) != 0) && ((bd_font_data->FtFace->face_flags & FT_FACE_FLAG_SCALABLE) == 0) && ((bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) != 0)) + { + IM_ASSERT(bd_font_data->FtFace->num_fixed_sizes > 0); + + // Loop over sizes and pick the closest, larger (or better equal) size. + int best_index = 0; + float best_height = bd_font_data->FtFace->available_sizes[best_index].y_ppem / FT_SCALEFACTOR; + for (int i = 1; i < bd_font_data->FtFace->num_fixed_sizes; i++) + { + const float cur_height = bd_font_data->FtFace->available_sizes[i].y_ppem / FT_SCALEFACTOR; + // FIXME: is this overkill? + // FIXME: is-float-close with epsilon param would be nice, maybe + if (ImFabs(cur_height - size) < 0.001f) + { + best_index = i; + best_height = cur_height; + break; + } + else if (cur_height < size) + { + if (best_height < cur_height) + { + best_index = i; + best_height = cur_height; + } + } + else + { + if (best_height < size && best_height < cur_height) + { + best_index = i; + best_height = cur_height; + } + } + } + FT_Select_Size(bd_font_data->FtFace, best_index); + } + else + { + // Vuhdo 2017: "I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height' + // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me. + // FT_Set_Pixel_Sizes() doesn't seem to get us the same result." + // (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL) + + FT_Size_RequestRec req; + req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM; + req.width = 0; + req.height = (uint32_t)(size * FT_SCALEFACTOR * rasterizer_density); + req.horiResolution = 0; + req.vertResolution = 0; + FT_Request_Size(bd_font_data->FtFace, &req); + } + + // Output + if (src->MergeMode == false) + { + // Read metrics + FT_Size_Metrics metrics = bd_baked_data->FtSize->metrics; + const float scale = 1.0f / (rasterizer_density * src->ExtraSizeScale); + baked->Ascent = (float)FT_CEIL(metrics.ascender) * scale; // The pixel extents above the baseline in pixels (typically positive). + baked->Descent = (float)FT_CEIL(metrics.descender) * scale; // The extents below the baseline in pixels (typically negative). + //LineSpacing = (float)FT_CEIL(metrics.height) * scale; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate. + //LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * scale; // The spacing in pixels between one row's descent and the next row's ascent. + //MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * scale; // This field gives the maximum horizontal cursor advance for all glyphs in the font. + } + return true; +} + +static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) +{ + IM_UNUSED(atlas); + IM_UNUSED(baked); + IM_UNUSED(src); + ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; + IM_ASSERT(bd_baked_data != nullptr); + FT_Done_Size(bd_baked_data->FtSize); + bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE() +} + +static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) +{ + ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; + uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); + if (glyph_index == 0) + return false; + + if (bd_font_data->BakedLastActivated != baked) // <-- could use id + { + // Activate current size + ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; + FT_Activate_Size(bd_baked_data->FtSize); + bd_font_data->BakedLastActivated = baked; + } + + const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, codepoint); + if (metrics == nullptr) + return false; + + FT_Face face = bd_font_data->FtFace; + FT_GlyphSlot slot = face->glyph; + const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; + float bitmap_x_scale = 1.0f; + float bitmap_y_scale = 1.0f; + if (((face->face_flags & FT_FACE_FLAG_FIXED_SIZES) != 0) && ((face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) && ((bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) != 0)) + { + // FIXME: what if this just means invisible? + IM_ASSERT(bd_font_data->FtFace->size->metrics.x_ppem > 0); + IM_ASSERT(bd_font_data->FtFace->size->metrics.y_ppem > 0); + + // Scale fixed size bitmap to target size + bitmap_x_scale = baked->Size / bd_font_data->FtFace->size->metrics.x_ppem; + bitmap_y_scale = baked->Size / bd_font_data->FtFace->size->metrics.y_ppem; + } + + // Load metrics only mode + const float advance_x = ((slot->advance.x / FT_SCALEFACTOR) * bitmap_x_scale) / rasterizer_density; + if (out_advance_x != NULL) + { + IM_ASSERT(out_glyph == NULL); + *out_advance_x = advance_x; + return true; + } + + // Render glyph into a bitmap (currently held by FreeType) + FT_Render_Mode render_mode = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL; + FT_Error error = FT_Render_Glyph(slot, render_mode); + const FT_Bitmap* ft_bitmap = &slot->bitmap; + if (error != 0 || ft_bitmap == nullptr) + return false; + + const int bitmap_w = (int)ft_bitmap->width; + const int bitmap_h = (int)ft_bitmap->rows; + const bool is_visible = (bitmap_w != 0 && bitmap_h != 0); + + // Prepare glyph + out_glyph->Codepoint = codepoint; + out_glyph->AdvanceX = advance_x; + + // Pack and retrieve position inside texture atlas + if (is_visible) + { + const int w = (int)ImCeil(bitmap_w * bitmap_x_scale); + const int h = (int)ImCeil(bitmap_h * bitmap_y_scale); + const bool rescaling = h != bitmap_h || w != bitmap_w; + + ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h); + if (pack_id == ImFontAtlasRectId_Invalid) + { + // Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?) + IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && "Out of texture memory."); + return false; + } + ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id); + + // Render pixels to our temporary buffer, while making sure we have space for an extra copy used during rescaling. + atlas->Builder->TempBuffer.resize(((rescaling ? bitmap_w * bitmap_h : 0) + w * h) * 4); + uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data; + // Blit (and convert) into the first bitmap_w * bitmap_h * 4 bytes. + ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, bitmap_w); + + if (rescaling) + { + uint32_t* dst_buffer = temp_buffer + bitmap_w * bitmap_h; + + // Perform rescale, from temp_buffer (bitmap_w * bitmap_h) to dst_buffer (w * h) + ImFontAtlasTextureBlockResize(temp_buffer, bitmap_w, bitmap_h, dst_buffer, w, h); + + // Redirect to rescaled part of the buffer + temp_buffer = dst_buffer; + } + + const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; + const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; + float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset. + float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f) + baked->Ascent; + float recip_h = 1.0f / rasterizer_density; + float recip_v = 1.0f / rasterizer_density; + + // Register glyph + float glyph_off_x = ImCeil((float)face->glyph->bitmap_left * bitmap_x_scale); + float glyph_off_y = ImCeil((float)-face->glyph->bitmap_top * bitmap_y_scale); + out_glyph->X0 = glyph_off_x * recip_h + font_off_x; + out_glyph->Y0 = glyph_off_y * recip_v + font_off_y; + out_glyph->X1 = (glyph_off_x + w) * recip_h + font_off_x; + out_glyph->Y1 = (glyph_off_y + h) * recip_v + font_off_y; + out_glyph->Visible = true; + out_glyph->Colored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA); + out_glyph->PackId = pack_id; + ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, (const unsigned char*)temp_buffer, ImTextureFormat_RGBA32, w * 4); + } + + return true; +} + +static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) +{ + IM_UNUSED(atlas); + ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; + int glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); + return glyph_index != 0; +} + +const ImFontLoader* ImGuiFreeType::GetFontLoader() +{ + static ImFontLoader loader; + loader.Name = "FreeType"; + loader.LoaderInit = ImGui_ImplFreeType_LoaderInit; + loader.LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown; + loader.FontSrcInit = ImGui_ImplFreeType_FontSrcInit; + loader.FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy; + loader.FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph; + loader.FontBakedInit = ImGui_ImplFreeType_FontBakedInit; + loader.FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy; + loader.FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph; + loader.FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData); + return &loader; +} + +void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) +{ + GImGuiFreeTypeAllocFunc = alloc_func; + GImGuiFreeTypeFreeFunc = free_func; + GImGuiFreeTypeAllocatorUserData = user_data; +} + +bool ImGuiFreeType::DebugEditFontLoaderFlags(unsigned int* p_font_loader_flags) +{ + bool edited = false; + edited |= ImGui::CheckboxFlags("NoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoHinting); + edited |= ImGui::CheckboxFlags("NoAutoHint", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoAutoHint); + edited |= ImGui::CheckboxFlags("ForceAutoHint",p_font_loader_flags, ImGuiFreeTypeLoaderFlags_ForceAutoHint); + edited |= ImGui::CheckboxFlags("LightHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LightHinting); + edited |= ImGui::CheckboxFlags("MonoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_MonoHinting); + edited |= ImGui::CheckboxFlags("Bold", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bold); + edited |= ImGui::CheckboxFlags("Oblique", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Oblique); + edited |= ImGui::CheckboxFlags("Monochrome", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Monochrome); + edited |= ImGui::CheckboxFlags("LoadColor", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LoadColor); + edited |= ImGui::CheckboxFlags("Bitmap", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bitmap); + return edited; +} + +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c +// The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT) +struct LunasvgPortState +{ + FT_Error err = FT_Err_Ok; + lunasvg::Matrix matrix; + std::unique_ptr svg = nullptr; +}; + +static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state) +{ + *_state = IM_NEW(LunasvgPortState)(); + return FT_Err_Ok; +} + +static void ImGuiLunasvgPortFree(FT_Pointer* _state) +{ + IM_DELETE(*(LunasvgPortState**)_state); +} + +static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state) +{ + LunasvgPortState* state = *(LunasvgPortState**)_state; + + // If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error. + if (state->err != FT_Err_Ok) + return state->err; + + // rows is height, pitch (or stride) equals to width * sizeof(int32) + lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch); +#if LUNASVG_VERSION_MAJOR >= 3 + state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated +#else + state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value + state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated +#endif + state->err = FT_Err_Ok; + return state->err; +} + +static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state) +{ + FT_SVG_Document document = (FT_SVG_Document)slot->other; + LunasvgPortState* state = *(LunasvgPortState**)_state; + FT_Size_Metrics& metrics = document->metrics; + + // This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender(). + // If it's the latter, don't do anything because it's // already done in the former. + if (cache) + return state->err; + + state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length); + if (state->svg == nullptr) + { + state->err = FT_Err_Invalid_SVG_Document; + return state->err; + } + +#if LUNASVG_VERSION_MAJOR >= 3 + lunasvg::Box box = state->svg->boundingBox(); +#else + lunasvg::Box box = state->svg->box(); +#endif + double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h); + double xx = (double)document->transform.xx / (1 << 16); + double xy = -(double)document->transform.xy / (1 << 16); + double yx = -(double)document->transform.yx / (1 << 16); + double yy = (double)document->transform.yy / (1 << 16); + double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem; + double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem; + +#if LUNASVG_VERSION_MAJOR >= 3 + // Scale, transform and pre-translate the matrix for the rendering step + state->matrix = lunasvg::Matrix::translated(-box.x, -box.y); + state->matrix.multiply(lunasvg::Matrix(xx, xy, yx, yy, x0, y0)); + state->matrix.scale(scale, scale); + + // Apply updated transformation to the bounding box + box.transform(state->matrix); +#else + // Scale and transform, we don't translate the svg yet + state->matrix.identity(); + state->matrix.scale(scale, scale); + state->matrix.transform(xx, xy, yx, yy, x0, y0); + state->svg->setMatrix(state->matrix); + + // Pre-translate the matrix for the rendering step + state->matrix.translate(-box.x, -box.y); + + // Get the box again after the transformation + box = state->svg->box(); +#endif + + // Calculate the bitmap size + slot->bitmap_left = FT_Int(box.x); + slot->bitmap_top = FT_Int(-box.y); + slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h)); + slot->bitmap.width = (unsigned int)(ImCeil((float)box.w)); + slot->bitmap.pitch = slot->bitmap.width * 4; + slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + + // Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box. + double metrics_width = box.w; + double metrics_height = box.h; + double horiBearingX = box.x; + double horiBearingY = -box.y; + double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0; + double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0; + slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); // Using IM_ROUND() assume width and height are positive + slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0)); + slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64); + slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64); + slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64); + slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64); + + if (slot->metrics.vertAdvance == 0) + slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0); + + state->err = FT_Err_Ok; + return state->err; +} + +#endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG + +//----------------------------------------------------------------------------- + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/core/deps/imgui/misc/freetype/imgui_freetype.h b/core/deps/imgui/misc/freetype/imgui_freetype.h new file mode 100755 index 0000000000..446464425c --- /dev/null +++ b/core/deps/imgui/misc/freetype/imgui_freetype.h @@ -0,0 +1,83 @@ +// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder) +// (headers) + +#pragma once +#include "imgui.h" // IMGUI_API +#ifndef IMGUI_DISABLE + +// Usage: +// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support +// for imgui_freetype in imgui. It is equivalent to selecting the default loader with: +// io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) + +// Optional support for OpenType SVG fonts: +// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927. +// - Add '#define IMGUI_ENABLE_FREETYPE_LUNASVG' to use lunasvg (not provided). See #6591. + +// Forward declarations +struct ImFontAtlas; +struct ImFontLoader; + +// Hinting greatly impacts visuals (and glyph sizes). +// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter. +// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h +// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way. +// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer. +// You can set those flags globally in ImFontAtlas::FontLoaderFlags +// You can set those flags on a per font basis in ImFontConfig::FontLoaderFlags +typedef unsigned int ImGuiFreeTypeLoaderFlags; +enum ImGuiFreeTypeLoaderFlags_ +{ + ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes. + ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter. + ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter. + ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text. + ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. + ImGuiFreeTypeLoaderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font? + ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? + ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results! + ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs + ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9, // Enable FreeType bitmap glyphs + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiFreeTypeBuilderFlags_NoHinting = ImGuiFreeTypeLoaderFlags_NoHinting, + ImGuiFreeTypeBuilderFlags_NoAutoHint = ImGuiFreeTypeLoaderFlags_NoAutoHint, + ImGuiFreeTypeBuilderFlags_ForceAutoHint = ImGuiFreeTypeLoaderFlags_ForceAutoHint, + ImGuiFreeTypeBuilderFlags_LightHinting = ImGuiFreeTypeLoaderFlags_LightHinting, + ImGuiFreeTypeBuilderFlags_MonoHinting = ImGuiFreeTypeLoaderFlags_MonoHinting, + ImGuiFreeTypeBuilderFlags_Bold = ImGuiFreeTypeLoaderFlags_Bold, + ImGuiFreeTypeBuilderFlags_Oblique = ImGuiFreeTypeLoaderFlags_Oblique, + ImGuiFreeTypeBuilderFlags_Monochrome = ImGuiFreeTypeLoaderFlags_Monochrome, + ImGuiFreeTypeBuilderFlags_LoadColor = ImGuiFreeTypeLoaderFlags_LoadColor, + ImGuiFreeTypeBuilderFlags_Bitmap = ImGuiFreeTypeLoaderFlags_Bitmap, +#endif +}; + +// Obsolete names (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +typedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_; +#endif + +namespace ImGuiFreeType +{ +// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'. +// If you need to dynamically select between multiple builders: +// - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())' +// - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data. +IMGUI_API const ImFontLoader* GetFontLoader(); + +// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE() +// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired. +IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr); + +// Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source) +IMGUI_API bool DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags); + +// Obsolete names (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +//IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection. +//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE' +#endif +} + +#endif // #ifndef IMGUI_DISABLE diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp index f99bdb791c..740b235f77 100644 --- a/core/rend/vulkan/vulkan_context.cpp +++ b/core/rend/vulkan/vulkan_context.cpp @@ -329,7 +329,7 @@ void VulkanContext::InitImgui() initInfo.Queue = (VkQueue)graphicsQueue; initInfo.PipelineCache = (VkPipelineCache)*pipelineCache; initInfo.DescriptorPool = (VkDescriptorPool)*descriptorPool; - initInfo.RenderPass = (VkRenderPass)*renderPass; + initInfo.PipelineInfoMain.RenderPass = (VkRenderPass)*renderPass; initInfo.MinImageCount = 2; initInfo.ImageCount = GetSwapChainSize(); #ifdef VK_DEBUG @@ -337,7 +337,7 @@ void VulkanContext::InitImgui() #endif #if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 - ImGui_ImplVulkan_LoadFunctions([](const char *function_name, void *) { + ImGui_ImplVulkan_LoadFunctions(0, [](const char *function_name, void *) { return VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr((VkInstance) *contextInstance->instance, function_name); }); #endif diff --git a/core/ui/gui.cpp b/core/ui/gui.cpp index 70a71a9f1b..82942591c5 100644 --- a/core/ui/gui.cpp +++ b/core/ui/gui.cpp @@ -40,6 +40,7 @@ #include "oslib/resources.h" #include "achievements/achievements.h" #include "gui_achievements.h" +#include "imgui/misc/freetype/imgui_freetype.h" #include "IconsFontAwesome6.h" #include #include "hw/pvr/Renderer_if.h" @@ -93,7 +94,7 @@ static Chat chat; static std::recursive_mutex guiMutex; using LockGuard = std::lock_guard; -ImFont *largeFont; +ImFont *boldFont; static Toast toast; static ThreadRunner uiThreadRunner; @@ -199,16 +200,131 @@ static ImGuiKey keycodeToImGuiKey(u8 keycode) } } -static bool addFont(const char *path, float size, ImFontConfig& fontConfig, const ImWchar *glyphRanges) { - ImFont *font = ImGui::GetIO().Fonts->AddFontFromFileTTF(path, size, &fontConfig, glyphRanges); - return font != nullptr; +namespace FontFace +{ + enum class Noto : ImU32 { Thin = 0x0, Light = 0x20000, DemiLight = 0x30000, Regular = 0x40000, Medium = 0x50000, Bold = 0x60000, Black = 0x70000 }; + enum class PingFang : ImU32 { Ultralight = 15, Thin = 12, Light = 9, Regular = 0, Medium = 3, Semibold = 6 }; + enum class GothicNeo : ImU32 { UltraLight = 12, Thin = 10, Light = 8, Regular = 0, Medium = 2, SemiBold = 4, ExtraBold = 14, Heavy = 16 }; + enum class Gulim : ImU32 { Round = 0, RoundMono = 1, Sharp = 2, SharpMono = 3 }; + enum class MSHei : ImU32 { Legacy = 0, UI = 1}; +}; + +struct FontNo { + ImU32 value = 0; + + constexpr FontNo() = default; + constexpr FontNo(ImU32 v) : value(v) {} + + template ::value>> + constexpr FontNo(E e) : value(static_cast(e)) {} +}; + +struct FontEntry { + const char* lang; // "ja", "ko", "zh", "zh_HK", "zh_TW", "zh_CN", "cjk" or nullptr for "generic" + std::vector paths; + FontNo fontNo{}; + float size = 1.0f; + float offsetY = 0.0f; +}; + +static void registerFont(std::vector& target, const FontEntry& entry) +{ + std::string locale = i18n::getCurrentLocale(); + + auto bucketRank = [&](const char* l) -> int + { + if (!l) return 3; // generic last + if (strcmp(l, "cjk") == 0) return 0; // cjk first + if (locale.rfind(l, 0) == 0) return 1; // user locale + return 2; // remaining langs + }; + + const int rank = bucketRank(entry.lang); + auto it = target.begin(); + for (; it != target.end(); ++it) + { + if (bucketRank(it->lang) > rank) + break; + } + target.insert(it, entry); } -static void addFont(const char *path[], float size, ImFontConfig& fontConfig, const ImWchar *glyphRanges) +static void registerFont(std::initializer_list*> targets, const FontEntry& entry) { - while (*path != nullptr) - if (addFont(*path++, size, fontConfig, glyphRanges)) - break; + for (auto* target : targets) + registerFont(*target, entry); +} + +static void loadFonts(const std::vector& entries, const ImFontConfig& cfg) +{ + std::unordered_set satisfiedLangs; + satisfiedLangs.reserve(8); + + bool cjkLoaded = false; + + for (const FontEntry& e : entries) + { + const char* lang = e.lang ? e.lang : "generic"; + + // Skip specific CJK langs if a generic CJK font already loaded + if (cjkLoaded && (!strcmp(lang, "ja") || !strcmp(lang, "ko") || !strncmp(lang, "zh", 2))) + continue; + + // Break-per-langTag + if (satisfiedLangs.count(lang)) + continue; + + bool loaded = false; + + for (const std::string& path : e.paths) + { + ImFontConfig entryCfg = cfg; + entryCfg.FontNo = e.fontNo.value; + entryCfg.GlyphOffset.y = e.size * e.offsetY; + static ImWchar faRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; + entryCfg.GlyphExcludeRanges = faRanges; + + ImFont* font = ImGui::GetIO().Fonts->AddFontFromFileTTF(path.c_str(), e.size, &entryCfg, nullptr); + if (font) + { + loaded = true; + break; + } + } + + if (!loaded) + continue; + + // Mark this lang as satisfied + satisfiedLangs.emplace(lang); + + // Traditional Chinese group: TW and HK satisfy each other + if (strcmp(lang, "zh_HK") == 0 || strcmp(lang, "zh_TW") == 0) + { + satisfiedLangs.emplace("zh_HK"); + satisfiedLangs.emplace("zh_TW"); + } + + // Umbrella rule: zh satisfies all specific Chinese locales + if (strcmp(lang, "zh") == 0) + { + satisfiedLangs.emplace("zh_HK"); + satisfiedLangs.emplace("zh_TW"); + satisfiedLangs.emplace("zh_CN"); + } + + // Special rule: cjk satisfies all CJK languages + if (strcmp(lang, "cjk") == 0) + { + cjkLoaded = true; + satisfiedLangs.emplace("ja"); + satisfiedLangs.emplace("ko"); + satisfiedLangs.emplace("zh"); + satisfiedLangs.emplace("zh_HK"); + satisfiedLangs.emplace("zh_TW"); + satisfiedLangs.emplace("zh_CN"); + } + } } void gui_initFonts() @@ -245,221 +361,453 @@ void gui_initFonts() if (settings.display.uiScale > 1) ImGui::GetStyle().ScaleAllSizes(settings.display.uiScale); - static const ImWchar ranges[] = - { - 0x0020, 0xFFFF, // All chars - 0, - }; - ImGuiIO& io = ImGui::GetIO(); io.Fonts->Clear(); - + io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()); + // Regular font + ImGuiStyle& style = ImGui::GetStyle(); const float fontSize = uiScaled(17.f); + style.FontSizeBase = fontSize; + size_t dataSize; std::unique_ptr data = resource::load("fonts/Roboto-Medium.ttf", dataSize); verify(data != nullptr); - ImFont *regularFont = io.Fonts->AddFontFromMemoryTTF(data.release(), dataSize, fontSize, nullptr, ranges); - ImFontConfig fontConfig; - fontConfig.MergeMode = true; - fontConfig.DstFont = regularFont; - // Font Awesome symbols (added to default font) - data = resource::load("fonts/" FONT_ICON_FILE_NAME_FAS, dataSize); + ImFont *regularFont = io.Fonts->AddFontFromMemoryTTF(data.release(), (int)dataSize, fontSize, nullptr, nullptr); + ImFontConfig fontConfig; + fontConfig.MergeMode = true; + fontConfig.DstFont = regularFont; + + // Bold font + data = resource::load("fonts/Roboto-Bold.ttf", dataSize); verify(data != nullptr); - fontConfig.FontNo = 0; - static ImWchar faRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; - io.Fonts->AddFontFromMemoryTTF(data.release(), dataSize, fontSize, &fontConfig, faRanges); - - // Large font - const float largeFontSize = uiScaled(21.f); - data = resource::load("fonts/Roboto-Regular.ttf", dataSize); - verify(data != nullptr); - largeFont = io.Fonts->AddFontFromMemoryTTF(data.release(), dataSize, largeFontSize, nullptr, ranges); - ImFontConfig largeFontConfig; - largeFontConfig.MergeMode = true; - largeFontConfig.DstFont = largeFont; - + boldFont = io.Fonts->AddFontFromMemoryTTF(data.release(), (int)dataSize, fontSize, nullptr, nullptr); + ImFontConfig boldFontConfig; + boldFontConfig.MergeMode = true; + boldFontConfig.DstFont = boldFont; + + std::vector fonts; + std::vector boldFonts; + fontConfig.Flags |= ImFontFlags_NoLoadError; + boldFontConfig.Flags |= ImFontFlags_NoLoadError; + + ImFontConfig emojiConfig = fontConfig; + emojiConfig.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor | ImGuiFreeTypeBuilderFlags_Bitmap; + ImFontConfig emojiBoldConfig = boldFontConfig; + emojiBoldConfig.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor | ImGuiFreeTypeBuilderFlags_Bitmap; + #ifdef _WIN32 - u32 cp = GetACP(); - std::string fontDir = std::string(nowide::getenv("SYSTEMROOT")) + "\\Fonts\\"; - switch (cp) - { - case 932: // Japanese - { - fontConfig.FontNo = 2; // UIGothic - largeFontConfig.FontNo = 2; - ImFont* font = io.Fonts->AddFontFromFileTTF((fontDir + "msgothic.ttc").c_str(), fontSize, &fontConfig, io.Fonts->GetGlyphRangesJapanese()); - io.Fonts->AddFontFromFileTTF((fontDir + "msgothic.ttc").c_str(), largeFontSize, &largeFontConfig, io.Fonts->GetGlyphRangesJapanese()); - fontConfig.FontNo = 2; // Meiryo UI - largeFontConfig.FontNo = 2; - if (font == nullptr) { - io.Fonts->AddFontFromFileTTF((fontDir + "Meiryo.ttc").c_str(), fontSize, &fontConfig, io.Fonts->GetGlyphRangesJapanese()); - io.Fonts->AddFontFromFileTTF((fontDir + "Meiryo.ttc").c_str(), largeFontSize, &largeFontConfig, io.Fonts->GetGlyphRangesJapanese()); - } - } - break; - case 949: // Korean - { - ImFont* font = io.Fonts->AddFontFromFileTTF((fontDir + "Malgun.ttf").c_str(), fontSize, &fontConfig, io.Fonts->GetGlyphRangesKorean()); - io.Fonts->AddFontFromFileTTF((fontDir + "Malgun.ttf").c_str(), largeFontSize, &largeFontConfig, io.Fonts->GetGlyphRangesKorean()); - if (font == nullptr) - { - fontConfig.FontNo = 2; // Dotum - io.Fonts->AddFontFromFileTTF((fontDir + "Gulim.ttc").c_str(), fontSize, &fontConfig, io.Fonts->GetGlyphRangesKorean()); - largeFontConfig.FontNo = 2; // Dotum - io.Fonts->AddFontFromFileTTF((fontDir + "Gulim.ttc").c_str(), largeFontSize, &largeFontConfig, io.Fonts->GetGlyphRangesKorean()); - } - } - break; - case 950: // Traditional Chinese - { - fontConfig.FontNo = 1; // Microsoft JhengHei UI Regular - ImFont* font = io.Fonts->AddFontFromFileTTF((fontDir + "Msjh.ttc").c_str(), fontSize, &fontConfig, GetGlyphRangesChineseTraditionalOfficial()); - largeFontConfig.FontNo = 1; - io.Fonts->AddFontFromFileTTF((fontDir + "Msjh.ttc").c_str(), largeFontSize, &largeFontConfig, GetGlyphRangesChineseTraditionalOfficial()); - if (font == nullptr) - { - fontConfig.FontNo = 0; - io.Fonts->AddFontFromFileTTF((fontDir + "MSJH.ttf").c_str(), fontSize, &fontConfig, GetGlyphRangesChineseTraditionalOfficial()); - largeFontConfig.FontNo = 0; - io.Fonts->AddFontFromFileTTF((fontDir + "MSJH.ttf").c_str(), largeFontSize, &largeFontConfig, GetGlyphRangesChineseTraditionalOfficial()); - } - } - break; - case 936: // Simplified Chinese - io.Fonts->AddFontFromFileTTF((fontDir + "Simsun.ttc").c_str(), fontSize, &fontConfig, GetGlyphRangesChineseSimplifiedOfficial()); - io.Fonts->AddFontFromFileTTF((fontDir + "Simsun.ttc").c_str(), largeFontSize, &largeFontConfig, GetGlyphRangesChineseSimplifiedOfficial()); - break; - default: - break; - } -#elif defined(__APPLE__) && !defined(TARGET_IPHONE) - std::string fontDir = std::string("/System/Library/Fonts/"); - std::string locale = i18n::getCurrentLocale(); - - if (locale.find("ja") == 0) // Japanese - { - io.Fonts->AddFontFromFileTTF((fontDir + "ヒラギノ角ゴシック W4.ttc").c_str(), fontSize, &fontConfig, io.Fonts->GetGlyphRangesJapanese()); - io.Fonts->AddFontFromFileTTF((fontDir + "ヒラギノ角ゴシック W4.ttc").c_str(), largeFontSize, &largeFontConfig, io.Fonts->GetGlyphRangesJapanese()); - } - else if (locale.find("ko") == 0) // Korean - { - io.Fonts->AddFontFromFileTTF((fontDir + "AppleSDGothicNeo.ttc").c_str(), fontSize, &fontConfig, io.Fonts->GetGlyphRangesKorean()); - io.Fonts->AddFontFromFileTTF((fontDir + "AppleSDGothicNeo.ttc").c_str(), largeFontSize, &largeFontConfig, io.Fonts->GetGlyphRangesKorean()); - } - else if (locale.find("zh-Hant") == 0) // Traditional Chinese - { - io.Fonts->AddFontFromFileTTF((fontDir + "PingFang.ttc").c_str(), fontSize, &fontConfig, GetGlyphRangesChineseTraditionalOfficial()); - io.Fonts->AddFontFromFileTTF((fontDir + "PingFang.ttc").c_str(), largeFontSize, &largeFontConfig, GetGlyphRangesChineseTraditionalOfficial()); - } - else if (locale.find("zh-Hans") == 0) // Simplified Chinese - { - io.Fonts->AddFontFromFileTTF((fontDir + "PingFang.ttc").c_str(), fontSize, &fontConfig, GetGlyphRangesChineseSimplifiedOfficial()); - io.Fonts->AddFontFromFileTTF((fontDir + "PingFang.ttc").c_str(), largeFontSize, &largeFontConfig, GetGlyphRangesChineseSimplifiedOfficial()); - } -#elif defined(__ANDROID__) - { - const ImWchar *glyphRanges = nullptr; - std::string locale = i18n::getCurrentLocale(); - if (locale.find("ja") == 0) // Japanese - glyphRanges = io.Fonts->GetGlyphRangesJapanese(); - else if (locale.find("ko") == 0) // Korean - glyphRanges = io.Fonts->GetGlyphRangesKorean(); - else if (locale.find("zh_TW") == 0 - || locale.find("zh_HK") == 0) // Traditional Chinese - glyphRanges = GetGlyphRangesChineseTraditionalOfficial(); - else if (locale.find("zh_CN") == 0) // Simplified Chinese - glyphRanges = GetGlyphRangesChineseSimplifiedOfficial(); - - if (glyphRanges != nullptr) { - io.Fonts->AddFontFromFileTTF("/system/fonts/NotoSansCJK-Regular.ttc", fontSize, &fontConfig, glyphRanges); - io.Fonts->AddFontFromFileTTF("/system/fonts/NotoSansCJK-Regular.ttc", largeFontSize, &largeFontConfig, glyphRanges); - } - } - -#elif defined(__linux__) - std::string locale = i18n::getCurrentLocale(); - if (locale.find("ja_") == 0) // Japanese + // Windows + std::string fontDir = std::string(nowide::getenv("SYSTEMROOT")) + "\\Fonts\\"; + + ImGui::GetIO().Fonts->AddFontFromFileTTF((fontDir + "seguiemj.ttf").c_str(), fontSize, &emojiConfig); + ImGui::GetIO().Fonts->AddFontFromFileTTF((fontDir + "seguiemj.ttf").c_str(), fontSize, &emojiBoldConfig); + + // registerFont(fonts, { .lang="ja", .paths={fontDir + "NotoSansJP-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); + // registerFont(boldFonts, { .lang="ja", .paths={fontDir + "NotoSansJP-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); + // registerFont({&fonts, &boldFonts}, { .lang="ja", .paths={fontDir + "BIZ-UDGothicB.ttc"}, .size=fontSize * 0.85f }); + { + FontEntry entry; entry.lang = "ja"; entry.paths = { fontDir + "NotoSansJP-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; + registerFont(fonts, entry); + } + { + FontEntry entry; entry.lang = "ja"; entry.paths = { fontDir + "NotoSansJP-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; + registerFont(boldFonts, entry); + } { - const char *fonts[] = { - "/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf", - "/usr/share/fonts/ipa-pgothic-fonts/ipagp.ttf", // redhat - "/usr/share/fonts/truetype/takao-gothic/TakaoPGothic.ttf", - "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", - "/usr/share/fonts/adobe-source-han-sans-jp-fonts/SourceHanSansJP-Regular.otf", // redhat - "/usr/share/fonts/vl-gothic-fonts/VL-Gothic-Regular.ttf", // redhat - "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", // redhat - nullptr - }; - const char *largeFonts[] = { - "/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf", - "/usr/share/fonts/ipa-pgothic-fonts/ipagp.ttf", // redhat - "/usr/share/fonts/truetype/takao-gothic/TakaoPGothic.ttf", - "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", - "/usr/share/fonts/adobe-source-han-sans-jp-fonts/SourceHanSansJP-Bold.otf", // redhat - "/usr/share/fonts/vl-gothic-fonts/VL-Gothic-Regular.ttf", // redhat - "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc", - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Bold.ttc", // redhat - nullptr - }; - addFont(fonts, fontSize, fontConfig, io.Fonts->GetGlyphRangesJapanese()); - addFont(largeFonts, largeFontSize, largeFontConfig, io.Fonts->GetGlyphRangesJapanese()); + FontEntry entry; entry.lang = "ja"; entry.paths = { fontDir + "BIZ-UDGothicB.ttc" }; entry.size = fontSize * 0.85f; + registerFont({ &fonts, &boldFonts }, entry); } - else if (locale.find("ko_") == 0) // Korean + + // registerFont(fonts, { .lang="ko", .paths={fontDir + "NotoSansKR-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); + // registerFont(boldFonts, { .lang="ko", .paths={fontDir + "NotoSansKR-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); + // registerFont({&fonts, &boldFonts}, { .lang="ko", .paths={fontDir + "malgunbd.ttf"}, .size=fontSize * 1.124891f }); + // registerFont({&fonts, &boldFonts}, { .lang="ko", .paths={fontDir + "Gulim.ttc"}, .fontNo=FontFace::Gulim::Sharp, .size=fontSize * 0.9f }); { - const char *fonts[] = { - "/usr/share/fonts/truetype/unfonts-core/UnDotum.ttf", - "/usr/share/fonts-droid-fallback/truetype/DroidSansFallback.ttf", - "/usr/share/fonts/baekmuk-dotum-fonts/dotum.ttf", // redhat - "/usr/share/fonts/adobe-source-han-sans-kr-fonts/SourceHanSansKR-Regular.otf", // redhat - "/usr/share/fonts/naver-nanum-gothic-coding-fonts/NanumGothic_Coding.ttf", // redhat - "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", // redhat - nullptr - }; - const char *largeFonts[] = { - "/usr/share/fonts/truetype/unfonts-core/UnDotumBold.ttf", - "/usr/share/fonts-droid-fallback/truetype/DroidSansFallback.ttf", - "/usr/share/fonts/adobe-source-han-sans-kr-fonts/SourceHanSansKR-Bold.otf", // redhat - "/usr/share/fonts/naver-nanum-gothic-coding-fonts/NanumGothic_Coding_Bold.ttf", // redhat - "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc", - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Bold.ttc", // redhat - nullptr - }; - addFont(fonts, fontSize, fontConfig, io.Fonts->GetGlyphRangesKorean()); - addFont(largeFonts, largeFontSize, largeFontConfig, io.Fonts->GetGlyphRangesKorean()); + FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "NotoSansKR-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; + registerFont(fonts, entry); } - else if (locale.find("zh_") == 0) // Chinese { - const ImWchar *glyphRanges = GetGlyphRangesChineseSimplifiedOfficial(); - if (locale.find("zh_TW") == 0 || locale.find("zh_HK") == 0) - glyphRanges = GetGlyphRangesChineseTraditionalOfficial(); - const char *fonts[] = { - "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", - "/usr/share/fonts/wqy-zenhei-fonts/wqy-zenhei.ttc", // redhat - "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", - "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", // redhat - }; - const char *largeFonts[] = { - "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", - "/usr/share/fonts/wqy-zenhei-fonts/wqy-zenhei.ttc", // redhat - "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", - "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc", - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Bold.ttc", // redhat - }; - addFont(fonts, fontSize, fontConfig, glyphRanges); - addFont(largeFonts, largeFontSize, largeFontConfig, glyphRanges); + FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "NotoSansKR-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; + registerFont(boldFonts, entry); } + { + FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "malgunbd.ttf" }; entry.size = fontSize * 1.124891f; + registerFont({ &fonts, &boldFonts }, entry); + } + { + FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "Gulim.ttc" }; entry.fontNo = FontFace::Gulim::Sharp; entry.size = fontSize * 0.9f; + registerFont({ &fonts, &boldFonts }, entry); + } + + // registerFont(fonts, { .lang="zh_HK", .paths={fontDir + "NotoSansHK-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); + // registerFont(boldFonts, { .lang="zh_HK", .paths={fontDir + "NotoSansHK-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); + // registerFont(fonts, { .lang="zh_TW", .paths={fontDir + "NotoSansTC-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); + // registerFont(boldFonts, { .lang="zh_TW", .paths={fontDir + "NotoSansTC-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); + // registerFont(fonts, { .lang="zh_CN", .paths={fontDir + "NotoSansSC-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); + // registerFont(boldFonts, { .lang="zh_CN", .paths={fontDir + "NotoSansSC-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); + { + FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "NotoSansHK-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; + registerFont(fonts, entry); + } + { + FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "NotoSansHK-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; + registerFont(boldFonts, entry); + } + { + FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "NotoSansTC-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; + registerFont(fonts, entry); + } + { + FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "NotoSansTC-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; + registerFont(boldFonts, entry); + } + { + FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "NotoSansSC-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; + registerFont(fonts, entry); + } + { + FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "NotoSansSC-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; + registerFont(boldFonts, entry); + } + + // registerFont({&fonts, &boldFonts}, { .lang="zh_HK", .paths={fontDir + "msjhbd.ttc"}, .fontNo=FontFace::MSHei::UI, .size=fontSize * 1.13000f, .offsetY=0.015f }); + // registerFont({&fonts, &boldFonts}, { .lang="zh_TW", .paths={fontDir + "msjhbd.ttc"}, .fontNo=FontFace::MSHei::UI, .size=fontSize * 1.13000f, .offsetY=0.015f }); + // registerFont({&fonts, &boldFonts}, { .lang="zh_CN", .paths={fontDir + "msyhbd.ttc"}, .fontNo=FontFace::MSHei::UI, .size=fontSize * 1.13000f, .offsetY=0.015f }); + { + FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "msjhbd.ttc" }; entry.fontNo = FontFace::MSHei::UI; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; + registerFont({ &fonts, &boldFonts }, entry); + } + { + FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "msjhbd.ttc" }; entry.fontNo = FontFace::MSHei::UI; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; + registerFont({ &fonts, &boldFonts }, entry); + } + { + FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "msyhbd.ttc" }; entry.fontNo = FontFace::MSHei::UI; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; + registerFont({ &fonts, &boldFonts }, entry); + } + + // Windows 7 + // registerFont({&fonts, &boldFonts}, { .lang="zh_HK", .paths={fontDir + "msjhbd.ttf"}, .size=fontSize * 1.13000f, .offsetY=0.015f }); + // registerFont({&fonts, &boldFonts}, { .lang="zh_TW", .paths={fontDir + "msjhbd.ttf"}, .size=fontSize * 1.13000f, .offsetY=0.015f }); + // registerFont({&fonts, &boldFonts}, { .lang="zh_CN", .paths={fontDir + "msyhbd.ttf"}, .size=fontSize * 1.13000f, .offsetY=0.015f }); + { + FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "msjhbd.ttf" }; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; + registerFont({ &fonts, &boldFonts }, entry); + } + { + FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "msjhbd.ttf" }; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; + registerFont({ &fonts, &boldFonts }, entry); + } + { + FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "msyhbd.ttf" }; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; + registerFont({ &fonts, &boldFonts }, entry); + } + +#elif defined(TARGET_OS_MAC) + emojiConfig.GlyphOffset = { 0.0f, fontSize * (0.2f) }; + emojiBoldConfig.GlyphOffset = emojiConfig.GlyphOffset; + ImGui::GetIO().Fonts->AddFontFromFileTTF("/System/Library/Fonts/Apple Color Emoji.ttc", fontSize, &emojiConfig); + ImGui::GetIO().Fonts->AddFontFromFileTTF("/System/Library/Fonts/Apple Color Emoji.ttc", fontSize, &emojiBoldConfig); + + registerFont(fonts, { .lang = "ja", .paths = { "/System/Library/Fonts/ヒラギノ角ゴシック W5.ttc" }, .size=fontSize * 0.85f } ); + registerFont(boldFonts, { .lang = "ja", .paths = { "/System/Library/Fonts/ヒラギノ角ゴシック W7.ttc" }, .size=fontSize * 0.85f } ); + + registerFont({&fonts, &boldFonts}, { .lang="ko", .paths = { "/System/Library/Fonts/AppleSDGothicNeo.ttc" }, .fontNo=FontFace::GothicNeo::SemiBold, .size=fontSize * 1.05f }); + + registerFont(fonts, { .lang="zh", .paths = { "/System/Library/Fonts/PingFang.ttc" }, .fontNo=FontFace::PingFang::Medium, .size=fontSize * 1.185f, .offsetY=-0.03f }); + registerFont(boldFonts, { .lang="zh", .paths = { "/System/Library/Fonts/PingFang.ttc" }, .fontNo=FontFace::PingFang::Semibold, .size=fontSize * 1.185f, .offsetY=-0.03f }); + +#elif defined(__ANDROID__) + ImGui::GetIO().Fonts->AddFontFromFileTTF("/system/fonts/NotoColorEmoji.ttf", fontSize, &emojiConfig); + ImGui::GetIO().Fonts->AddFontFromFileTTF("/system/fonts/NotoColorEmoji.ttf", fontSize, &emojiBoldConfig); + + registerFont(fonts, { .lang="cjk", .paths={ + "/system/fonts/NotoSansCJK-Medium.ttc", + "/system/fonts/NotoSansCJK-Regular.ttc" + }, .size=fontSize * 1.05f }); + registerFont(boldFonts, { .lang="cjk", .paths={ + "/system/fonts/NotoSansCJK-Bold.ttc", + "/system/fonts/NotoSansCJK-Medium.ttc", + "/system/fonts/NotoSansCJK-Regular.ttc" + }, .size=fontSize * 1.05f }); + +#elif defined(__linux__) + for (const char* path : { + "/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf", + "/usr/share/fonts/noto/NotoColorEmoji.ttf", + "/usr/share/fonts/google-noto-emoji/NotoColorEmoji.ttf", + "/usr/local/share/fonts/NotoColorEmoji.ttf", + "/usr/share/fonts/noto-emoji/NotoColorEmoji.ttf", + "/usr/share/fonts/TTF/NotoColorEmoji.ttf" + }) { + if (ImGui::GetIO().Fonts->AddFontFromFileTTF(path, fontSize, &emojiConfig)) + { + ImGui::GetIO().Fonts->AddFontFromFileTTF(path, fontSize, &emojiBoldConfig); + break; + } + } + registerFont(fonts, { .lang="cjk", .paths={ + "/usr/share/fonts/opentype/noto/NotoSansCJK-Medium.ttc", + "/usr/share/fonts/noto-cjk/NotoSansCJK-Medium.ttc", + "/usr/share/fonts/truetype/noto/NotoSansCJK-Medium.ttc", + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Medium.ttc", + "/usr/share/fonts/TTF/NotoSansCJK-Medium.ttc", + "/usr/local/share/fonts/NotoSansCJK-Medium.ttc", + "/usr/local/share/fonts/noto/NotoSansCJK-Medium.ttc", + "/usr/local/share/fonts/noto-cjk/NotoSansCJK-Medium.ttc", + "/usr/share/fonts/opentype/noto-cjk/NotoSansCJK-Medium.ttc", + "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/TTF/NotoSansCJK-Regular.ttc", + "/usr/local/share/fonts/NotoSansCJK-Regular.ttc", + "/usr/local/share/fonts/noto/NotoSansCJK-Regular.ttc", + "/usr/local/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto-cjk/NotoSansCJK-Regular.ttc" + }, .size=fontSize * 1.05f }); + registerFont(boldFonts, { .lang="cjk", .paths={ + "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc", + "/usr/share/fonts/noto-cjk/NotoSansCJK-Bold.ttc", + "/usr/share/fonts/truetype/noto/NotoSansCJK-Bold.ttc", + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Bold.ttc", + "/usr/share/fonts/TTF/NotoSansCJK-Bold.ttc", + "/usr/local/share/fonts/NotoSansCJK-Bold.ttc", + "/usr/local/share/fonts/noto/NotoSansCJK-Bold.ttc", + "/usr/local/share/fonts/noto-cjk/NotoSansCJK-Bold.ttc", + "/usr/share/fonts/opentype/noto-cjk/NotoSansCJK-Bold.ttc" + }, .size=fontSize * 1.05f }); + registerFont({&fonts, &boldFonts}, { .lang="cjk", .paths={ + "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", + "/usr/share/fonts/droid/DroidSansFallbackFull.ttf", + "/usr/share/fonts/truetype/DroidSansFallbackFull.ttf", + "/usr/share/fonts/TTF/DroidSansFallbackFull.ttf", + "/usr/local/share/fonts/DroidSansFallbackFull.ttf", + "/usr/local/share/fonts/droid/DroidSansFallbackFull.ttf" + }, .size=fontSize * 1.2f }); + + registerFont(fonts, { .lang="ja", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-jp/SourceHanSansJP-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans-jp-fonts/SourceHanSansJP-Medium.otf", + "/usr/share/fonts/opentype/source-han-sans-jp/SourceHanSansJP-Medium.otf", + "/usr/share/fonts/source-han-sans-jp/SourceHanSansJP-Medium.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansJP-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansJP-Medium.otf", + "/usr/local/share/fonts/SourceHanSansJP-Medium.otf", + "/usr/local/share/fonts/adobe-source-han-sans-jp/SourceHanSansJP-Medium.otf", + "/usr/local/share/fonts/source-han-sans-jp/SourceHanSansJP-Medium.otf" + }, .size=fontSize * 1.05f }); + registerFont(boldFonts, { .lang="ja", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-jp/SourceHanSansJP-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans-jp-fonts/SourceHanSansJP-Bold.otf", + "/usr/share/fonts/opentype/source-han-sans-jp/SourceHanSansJP-Bold.otf", + "/usr/share/fonts/source-han-sans-jp/SourceHanSansJP-Bold.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansJP-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansJP-Bold.otf", + "/usr/local/share/fonts/SourceHanSansJP-Bold.otf", + "/usr/local/share/fonts/adobe-source-han-sans-jp/SourceHanSansJP-Bold.otf", + "/usr/local/share/fonts/source-han-sans-jp/SourceHanSansJP-Bold.otf" + }, .size=fontSize * 1.05f }); + registerFont({&fonts, &boldFonts}, { .lang="ja", .paths={ + "/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf", + "/usr/share/fonts/ipa-pgothic-fonts/ipagp.ttf", + "/usr/share/fonts/truetype/ipafont-gothic/ipagp.ttf", + "/usr/share/fonts/truetype/ipa-pgothic/ipagp.ttf", + "/usr/share/fonts/TTF/ipagp.ttf", + "/usr/local/share/fonts/ipagp.ttf", + "/usr/local/share/fonts/ipafont-gothic/ipagp.ttf", + "/usr/local/share/fonts/ipa-pgothic/ipagp.ttf" + }, .size=fontSize * 0.9f }); + registerFont({&fonts, &boldFonts}, { .lang="ja", .paths={ + "/usr/share/fonts/vl-gothic-fonts/VL-Gothic-Regular.ttf", + "/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf", + "/usr/share/fonts/vlgothic/VL-Gothic-Regular.ttf", + "/usr/share/fonts/truetype/vl-gothic/VL-Gothic-Regular.ttf", + "/usr/share/fonts/TTF/VL-Gothic-Regular.ttf", + "/usr/local/share/fonts/VL-Gothic-Regular.ttf", + "/usr/local/share/fonts/vl-gothic/VL-Gothic-Regular.ttf", + "/usr/local/share/fonts/vlgothic/VL-Gothic-Regular.ttf" + }, .size=fontSize * 1.15f }); + registerFont({&fonts, &boldFonts}, { .lang="ja", .paths={ + "/usr/share/fonts/truetype/takao-gothic/TakaoPGothic.ttf", + "/usr/share/fonts/takao-gothic/TakaoPGothic.ttf", + "/usr/share/fonts/truetype/takao/TakaoPGothic.ttf", + "/usr/share/fonts/TTF/TakaoPGothic.ttf", + "/usr/local/share/fonts/TakaoPGothic.ttf", + "/usr/local/share/fonts/takao-gothic/TakaoPGothic.ttf", + "/usr/local/share/fonts/takao/TakaoPGothic.ttf" + }, .size=fontSize }); + + registerFont(fonts, { .lang="ko", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-kr/SourceHanSansKR-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans-kr-fonts/SourceHanSansKR-Medium.otf", + "/usr/share/fonts/opentype/source-han-sans-kr/SourceHanSansKR-Medium.otf", + "/usr/share/fonts/source-han-sans-kr/SourceHanSansKR-Medium.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansKR-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansKR-Medium.otf", + "/usr/local/share/fonts/SourceHanSansKR-Medium.otf", + "/usr/local/share/fonts/adobe-source-han-sans-kr/SourceHanSansKR-Medium.otf", + "/usr/local/share/fonts/source-han-sans-kr/SourceHanSansKR-Medium.otf" + }, .size=fontSize * 1.05f }); + registerFont(boldFonts, { .lang="ko", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-kr/SourceHanSansKR-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans-kr-fonts/SourceHanSansKR-Bold.otf", + "/usr/share/fonts/opentype/source-han-sans-kr/SourceHanSansKR-Bold.otf", + "/usr/share/fonts/source-han-sans-kr/SourceHanSansKR-Bold.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansKR-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansKR-Bold.otf", + "/usr/local/share/fonts/SourceHanSansKR-Bold.otf", + "/usr/local/share/fonts/adobe-source-han-sans-kr/SourceHanSansKR-Bold.otf", + "/usr/local/share/fonts/source-han-sans-kr/SourceHanSansKR-Bold.otf" + }, .size=fontSize * 1.05f }); + + registerFont(fonts, { .lang="ko", .paths={ + "/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf", + "/usr/share/fonts/nanum/NanumGothicBold.ttf", + "/usr/share/fonts/truetype/nanum-gothic/NanumGothicBold.ttf", + "/usr/share/fonts/opentype/nanum/NanumGothicBold.ttf", + "/usr/share/fonts/TTF/NanumGothicBold.ttf", + "/usr/local/share/fonts/NanumGothicBold.ttf", + "/usr/local/share/fonts/nanum/NanumGothicBold.ttf", + "/usr/local/share/fonts/nanum-gothic/NanumGothicBold.ttf" + }, .size=fontSize * 0.8f }); + registerFont(boldFonts, { .lang="ko", .paths={ + "/usr/share/fonts/truetype/nanum/NanumGothicExtraBold.ttf", + "/usr/share/fonts/nanum/NanumGothicExtraBold.ttf", + "/usr/share/fonts/truetype/nanum-gothic/NanumGothicExtraBold.ttf", + "/usr/share/fonts/opentype/nanum/NanumGothicExtraBold.ttf", + "/usr/share/fonts/TTF/NanumGothicExtraBold.ttf", + "/usr/local/share/fonts/NanumGothicExtraBold.ttf", + "/usr/local/share/fonts/nanum/NanumGothicExtraBold.ttf", + "/usr/local/share/fonts/nanum-gothic/NanumGothicExtraBold.ttf" + }, .size=fontSize * 0.8f }); + registerFont({&fonts, &boldFonts}, { .lang="ko", .paths={ + "/usr/share/fonts/naver-nanum-gothic-coding-fonts/NanumGothic_Coding_Bold.ttf", + "/usr/share/fonts/truetype/nanum/NanumGothicCoding-Bold.ttf", + "/usr/share/fonts/nanum/NanumGothicCoding-Bold.ttf", + "/usr/share/fonts/truetype/nanum-coding/NanumGothicCoding-Bold.ttf", + "/usr/share/fonts/TTF/NanumGothicCoding-Bold.ttf", + "/usr/local/share/fonts/NanumGothicCoding-Bold.ttf", + "/usr/local/share/fonts/nanum/NanumGothicCoding-Bold.ttf", + "/usr/local/share/fonts/nanum-coding/NanumGothicCoding-Bold.ttf" + }, .size=fontSize * 0.8f }); + registerFont({&fonts, &boldFonts}, { .lang="ko", .paths={ + "/usr/share/fonts/truetype/unfonts-core/UnDotumBold.ttf", + "/usr/share/fonts/unfonts-core/UnDotumBold.ttf", + "/usr/share/fonts/truetype/unfonts/UnDotumBold.ttf", + "/usr/share/fonts/TTF/UnDotumBold.ttf", + "/usr/local/share/fonts/UnDotumBold.ttf", + "/usr/local/share/fonts/unfonts-core/UnDotumBold.ttf", + "/usr/local/share/fonts/unfonts/UnDotumBold.ttf" + }, .size=fontSize, .offsetY=-0.1f }); + registerFont({&fonts, &boldFonts}, { .lang="ko", .paths={ + "/usr/share/fonts/baekmuk-dotum-fonts/dotum.ttf", + "/usr/share/fonts/truetype/baekmuk/dotum.ttf", + "/usr/share/fonts/baekmuk/dotum.ttf", + "/usr/share/fonts/truetype/dotum/dotum.ttf", + "/usr/share/fonts/TTF/dotum.ttf", + "/usr/local/share/fonts/dotum.ttf", + "/usr/local/share/fonts/baekmuk/dotum.ttf", + "/usr/local/share/fonts/baekmuk-dotum-fonts/dotum.ttf" + }, .size=fontSize * 1.18f, .offsetY=-0.02f }); + + registerFont(fonts, { .lang="zh_HK", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-hk/SourceHanSansHK-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans-hk-fonts/SourceHanSansHK-Medium.otf", + "/usr/share/fonts/opentype/source-han-sans-hk/SourceHanSansHK-Medium.otf", + "/usr/share/fonts/source-han-sans-hk/SourceHanSansHK-Medium.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansHK-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansHK-Medium.otf", + "/usr/local/share/fonts/SourceHanSansHK-Medium.otf", + "/usr/local/share/fonts/adobe-source-han-sans-hk/SourceHanSansHK-Medium.otf", + "/usr/local/share/fonts/source-han-sans-hk/SourceHanSansHK-Medium.otf" + }, .size=fontSize * 1.05f }); + registerFont(boldFonts, { .lang="zh_HK", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-hk/SourceHanSansHK-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans-hk-fonts/SourceHanSansHK-Bold.otf", + "/usr/share/fonts/opentype/source-han-sans-hk/SourceHanSansHK-Bold.otf", + "/usr/share/fonts/source-han-sans-hk/SourceHanSansHK-Bold.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansHK-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansHK-Bold.otf", + "/usr/local/share/fonts/SourceHanSansHK-Bold.otf", + "/usr/local/share/fonts/adobe-source-han-sans-hk/SourceHanSansHK-Bold.otf", + "/usr/local/share/fonts/source-han-sans-hk/SourceHanSansHK-Bold.otf" + }, .size=fontSize * 1.05f }); + registerFont(fonts, { .lang="zh_TW", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-tw/SourceHanSansTW-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans-tw-fonts/SourceHanSansTW-Medium.otf", + "/usr/share/fonts/opentype/source-han-sans-tw/SourceHanSansTW-Medium.otf", + "/usr/share/fonts/source-han-sans-tw/SourceHanSansTW-Medium.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansTW-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansTW-Medium.otf", + "/usr/local/share/fonts/SourceHanSansTW-Medium.otf", + "/usr/local/share/fonts/adobe-source-han-sans-tw/SourceHanSansTW-Medium.otf", + "/usr/local/share/fonts/source-han-sans-tw/SourceHanSansTW-Medium.otf" + }, .size=fontSize * 1.05f }); + registerFont(boldFonts, { .lang="zh_TW", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-tw/SourceHanSansTW-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans-tw-fonts/SourceHanSansTW-Bold.otf", + "/usr/share/fonts/opentype/source-han-sans-tw/SourceHanSansTW-Bold.otf", + "/usr/share/fonts/source-han-sans-tw/SourceHanSansTW-Bold.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansTW-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansTW-Bold.otf", + "/usr/local/share/fonts/SourceHanSansTW-Bold.otf", + "/usr/local/share/fonts/adobe-source-han-sans-tw/SourceHanSansTW-Bold.otf", + "/usr/local/share/fonts/source-han-sans-tw/SourceHanSansTW-Bold.otf" + }, .size=fontSize * 1.05f }); + registerFont(fonts, { .lang="zh_CN", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-sc/SourceHanSansSC-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans-sc-fonts/SourceHanSansSC-Medium.otf", + "/usr/share/fonts/opentype/source-han-sans-sc/SourceHanSansSC-Medium.otf", + "/usr/share/fonts/source-han-sans-sc/SourceHanSansSC-Medium.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansSC-Medium.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansSC-Medium.otf", + "/usr/local/share/fonts/SourceHanSansSC-Medium.otf", + "/usr/local/share/fonts/adobe-source-han-sans-sc/SourceHanSansSC-Medium.otf", + "/usr/local/share/fonts/source-han-sans-sc/SourceHanSansSC-Medium.otf" + }, .size=fontSize * 1.05f }); + registerFont(boldFonts, { .lang="zh_CN", .paths={ + "/usr/share/fonts/opentype/adobe-source-han-sans-sc/SourceHanSansSC-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans-sc-fonts/SourceHanSansSC-Bold.otf", + "/usr/share/fonts/opentype/source-han-sans-sc/SourceHanSansSC-Bold.otf", + "/usr/share/fonts/source-han-sans-sc/SourceHanSansSC-Bold.otf", + "/usr/share/fonts/opentype/adobe-source-han-sans/SourceHanSansSC-Bold.otf", + "/usr/share/fonts/adobe-source-han-sans/SourceHanSansSC-Bold.otf", + "/usr/local/share/fonts/SourceHanSansSC-Bold.otf", + "/usr/local/share/fonts/adobe-source-han-sans-sc/SourceHanSansSC-Bold.otf", + "/usr/local/share/fonts/source-han-sans-sc/SourceHanSansSC-Bold.otf" + }, .size=fontSize * 1.05f }); + registerFont({&fonts, &boldFonts}, { .lang="zh", .paths={ + "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", + "/usr/share/fonts/wqy-zenhei-fonts/wqy-zenhei.ttc", + "/usr/share/fonts/wqy/wqy-zenhei.ttc", + "/usr/share/fonts/TTF/wqy-zenhei.ttc", + "/usr/local/share/fonts/wqy-zenhei.ttc", + "/usr/local/share/fonts/wqy/wqy-zenhei.ttc" + }, .size=fontSize, .offsetY=-0.05f }); // TODO BSD, iOS, ... #endif + + loadFonts(fonts, fontConfig); + loadFonts(boldFonts, boldFontConfig); + + // Font Awesome symbols + data = resource::load("fonts/" FONT_ICON_FILE_NAME_FAS, dataSize); + verify(data != nullptr); + + ImFontConfig faFontConfig = fontConfig; + faFontConfig.FontDataOwnedByAtlas = false; + io.Fonts->AddFontFromMemoryTTF(data.get(), (int)dataSize, fontSize, &faFontConfig); + boldFontConfig.FontDataOwnedByAtlas = true; + io.Fonts->AddFontFromMemoryTTF(data.release(), (int)dataSize, fontSize, &boldFontConfig); + NOTICE_LOG(RENDERER, "Screen DPI is %.0f, size %d x %d. Scaling by %.2f", settings.display.dpi, settings.display.width, settings.display.height, settings.display.uiScale); vgamepad::applyUiScale(); } -void gui_keyboard_input(u16 wc) +void gui_keyboard_input(u32 wc) { ImGuiIO& io = ImGui::GetIO(); if (io.WantCaptureKeyboard) @@ -764,7 +1112,7 @@ static void gui_display_commands() if (!lowHeight) { ImGui::BeginChild("game_info", ScaledVec2(0, 100.f), ImGuiChildFlags_Borders, ImGuiWindowFlags_None); - ImGui::PushFont(largeFont); + ImGui::PushFont(NULL, uiLargeFontSize()); ImGui::Text("%s", art.name.c_str()); ImGui::PopFont(); { @@ -1206,11 +1554,11 @@ static void gui_display_content() { const char *label = T("Your game list is empty"); // center horizontally - const float w = largeFont->CalcTextSizeA(largeFont->LegacySize, FLT_MAX, -1.f, label).x + ImGui::GetStyle().FramePadding.x * 2; + const float w = ImGui::GetFont()->CalcTextSizeA(uiLargeFontSize(), FLT_MAX, -1.f, label).x + ImGui::GetStyle().FramePadding.x * 2; ImGui::SameLine((ImGui::GetContentRegionMax().x - w) / 2); if (ImGui::BeginChild("empty", ImVec2(0, 0), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_NavFlattened)) { - ImGui::PushFont(largeFont); + ImGui::PushFont(NULL, uiLargeFontSize()); ImGui::NewLine(); ImGui::Text("%s", label); ImguiStyleVar _(ImGuiStyleVar_FramePadding, ScaledVec2(20, 8)); @@ -1621,7 +1969,7 @@ void gui_draw_osd() const float maxW = uiScaled(640.f); ImDrawList *dl = ImGui::GetForegroundDrawList(); const ScaledVec2 padding(5.f, 5.f); - const ImVec2 size = largeFont->CalcTextSizeA(largeFont->LegacySize, FLT_MAX, maxW, &message.front(), &message.back() + 1) + const ImVec2 size = ImGui::GetFont()->CalcTextSizeA(uiLargeFontSize(), FLT_MAX, maxW, &message.front(), &message.back() + 1) + padding * 2.f; ImVec2 pos(insetLeft, ImGui::GetIO().DisplaySize.y - size.y); constexpr float alpha = 0.7f; @@ -1629,7 +1977,7 @@ void gui_draw_osd() dl->AddRectFilled(pos, pos + size, bg_col, 0.f); pos += padding; const ImU32 col = alphaOverride(0x0000FFFF, alpha); - dl->AddText(largeFont, largeFont->LegacySize, pos, col, &message.front(), &message.back() + 1, maxW); + dl->AddText(NULL, uiLargeFontSize(), pos, col, &message.front(), &message.back() + 1, maxW); } } diff --git a/core/ui/gui.h b/core/ui/gui.h index 3a568f4780..64ca92cb02 100644 --- a/core/ui/gui.h +++ b/core/ui/gui.h @@ -34,7 +34,7 @@ void gui_term(); void gui_cancel_load(); void gui_refresh_files(); void gui_cheats(); -void gui_keyboard_input(u16 wc); +void gui_keyboard_input(u32 wc); void gui_keyboard_inputUTF8(const std::string& s); void gui_keyboard_key(u8 keyCode, bool pressed); bool gui_keyboard_captured(); diff --git a/core/ui/gui_achievements.cpp b/core/ui/gui_achievements.cpp index 3faca92649..fca53a9c1e 100644 --- a/core/ui/gui_achievements.cpp +++ b/core/ui/gui_achievements.cpp @@ -29,7 +29,7 @@ #include using namespace i18n; -extern ImFont *largeFont; +extern ImFont *boldFont; extern int insetLeft; namespace achievements @@ -224,8 +224,9 @@ bool Notification::draw() { if (text[i].empty()) continue; - ImFont *font = i == 0 ? largeFont : regularFont; - textSize[i] = font->CalcTextSizeA(font->LegacySize, FLT_MAX, maxW, text[i].c_str()); + ImFont *font = i == 0 ? boldFont : regularFont; + float fontSize = i == 0 ? uiLargeFontSize() : regularFont->LegacySize; + textSize[i] = ImGui::GetFont()->CalcTextSizeA(fontSize, FLT_MAX, maxW, text[i].c_str()); totalSize.x = std::max(totalSize.x, textSize[i].x); totalSize.y += textSize[i].y; } @@ -264,9 +265,10 @@ bool Notification::draw() { if (text[i].empty()) continue; - ImFont *font = i == 0 ? largeFont : regularFont; + ImFont *font = i == 0 ? boldFont : regularFont; + float fontSize = i == 0 ? uiLargeFontSize() : regularFont->LegacySize; const ImU32 col = alphaOverride(i == 0 ? 0xffffff : 0x00ffff, alpha); - dl->AddText(font, font->LegacySize, pos, col, &text[i].front(), &text[i].back() + 1, maxW); + dl->AddText(font, fontSize, pos, col, &text[i].front(), &text[i].back() + 1, maxW); pos.y += textSize[i].y + vspacing; } } @@ -290,7 +292,7 @@ void achievementList() tex.draw(ScaledVec2(80.f, 80.f)); ImGui::SameLine(); ImGui::BeginChild("game_info", ImVec2(w, uiScaled(80.f)), ImGuiChildFlags_None, ImGuiWindowFlags_None); - ImGui::PushFont(largeFont); + ImGui::PushFont(NULL, uiLargeFontSize()); ImGui::Text("%s", game.title.c_str()); ImGui::PopFont(); std::string str = strprintf(T("You have unlocked %d of %d achievements and %d of %d points."), @@ -326,7 +328,7 @@ void achievementList() else if (category == Tnop("Unlocked") || category == Tnop("Recently Unlocked")) ImGui::Text(ICON_FA_LOCK_OPEN); ImGui::SameLine(); - ImGui::PushFont(largeFont); + ImGui::PushFont(NULL, uiLargeFontSize()); ImGui::Text("%s", T(category.c_str())); ImGui::PopFont(); ImGui::Unindent(uiScaled(10)); @@ -336,7 +338,7 @@ void achievementList() tex.draw(ScaledVec2(80.f, 80.f)); ImGui::SameLine(); ImGui::BeginChild(ImGui::GetID("ach_item"), ImVec2(0, 0), ImGuiChildFlags_AutoResizeY, ImGuiWindowFlags_None); - ImGui::PushFont(largeFont); + ImGui::PushFont(NULL, uiLargeFontSize()); ImGui::Text("%s", ach.title.c_str()); ImGui::PopFont(); diff --git a/core/ui/gui_util.cpp b/core/ui/gui_util.cpp index 7135c9d658..3665ce330b 100644 --- a/core/ui/gui_util.cpp +++ b/core/ui/gui_util.cpp @@ -45,7 +45,7 @@ static std::vector folderFiles; bool subfolders_read; extern int insetLeft, insetRight, insetTop, insetBottom; -extern ImFont *largeFont; +extern ImFont *boldFont; void error_popup(); namespace hostfs @@ -109,7 +109,7 @@ void select_file_popup(const char *prompt, StringCallback callback, ImguiStyleVar _(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.f, 0.5f)); // Left ImguiStyleVar _1(ImGuiStyleVar_DisabledAlpha, 1.0f); ImGui::BeginDisabled(); - ImGui::PushFont(largeFont); + ImGui::PushFont(NULL, uiLargeFontSize()); ImGui::ButtonEx(prompt, ImVec2(-1, 0)); ImGui::PopFont(); ImGui::EndDisabled(); @@ -224,255 +224,6 @@ void scrollWhenDraggingOnVoid(ImGuiMouseButton mouse_button) } } -static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges) -{ - for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2) - { - out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]); - base_codepoint += accumulative_offsets[n]; - } - out_ranges[0] = 0; -} - -const ImWchar* GetGlyphRangesChineseSimplifiedOfficial() -{ - // Store all official characters for Simplified Chinese. - // Sourced from https://en.wikipedia.org/wiki/Table_of_General_Standard_Chinese_Characters - // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) - static const short accumulative_offsets_from_0x4E00[] = - { - 0,1,2,4,1,1,1,1,2,1,1,1,1,2,1,1,1,2,1,1,1,1,1,1,4,2,1,2,3,1,2,3,2,2,4,1,1,1,2,1,4,1,2,3,1,2,1,1,1,1,1,2,1,1,2,2,1,3,1,1,1,1,1,5,3,7,1,2,5,6, - 8,2,1,2,1,1,1,1,2,1,1,1,1,3,2,1,4,2,1,2,1,1,1,1,1,2,1,1,1,4,1,2,1,2,1,1,5,1,1,1,1,1,1,1,1,2,1,1,2,1,3,2,1,1,1,1,1,1,4,1,1,2,2,1,1,3,2,1,1, - 4,2,1,2,1,1,4,2,2,2,7,1,1,1,2,1,1,1,1,6,1,1,1,1,3,1,1,2,1,1,1,1,1,3,1,2,2,1,2,2,2,2,2,2,1,1,3,2,3,7,1,1,1,1,2,2,1,1,1,1,1,1,1,1,1,1,1,3,1, - 1,4,3,3,1,3,1,2,2,2,1,1,1,1,1,2,1,1,1,4,1,2,2,2,2,3,3,1,3,2,3,3,2,1,1,1,1,1,2,2,1,5,1,4,6,4,1,1,5,3,1,1,1,1,6,1,1,1,2,1,1,1,2,2,3,2,1,1,1, - 2,1,1,2,2,2,1,2,2,4,14,1,2,3,1,1,1,3,1,1,2,2,1,2,4,1,1,1,1,1,2,1,1,6,6,1,1,2,3,2,4,1,4,2,1,4,2,5,2,5,4,7,1,3,2,4,1,4,3,1,1,3,2,3,1,4,5,3, - 14,2,2,1,1,3,6,8,1,12,7,1,5,2,4,12,1,5,1,1,2,3,2,6,11,1,4,7,15,5,13,1,11,1,1,2,1,1,1,2,1,2,2,4,3,1,1,4,2,6,3,3,3,1,1,1,2,1,2,1,1,1,1,1,1, - 2,1,1,3,1,4,3,1,3,1,2,3,2,1,2,2,2,1,4,2,2,1,7,2,1,1,1,1,1,2,1,1,4,1,1,3,4,2,1,2,2,1,3,2,2,5,3,2,3,1,3,7,2,2,1,3,3,2,1,1,1,1,1,2,1,1,2,3,1, - 1,2,3,1,3,1,4,1,1,1,1,1,2,3,4,4,1,2,1,1,1,2,3,3,1,1,1,1,1,2,2,1,1,1,1,2,5,1,1,1,3,1,3,1,1,6,2,1,2,4,2,2,1,5,3,11,2,2,1,2,4,8,3,8,2,1,1,1, - 1,1,1,5,1,1,1,1,1,4,1,1,9,2,1,4,4,2,2,2,3,2,2,2,2,2,5,1,4,12,10,4,1,1,5,1,2,5,2,1,5,1,1,2,3,1,3,1,2,3,4,4,11,1,1,1,2,1,2,2,2,2,1,1,1,4,1, - 2,1,1,2,1,1,3,2,2,1,1,1,1,1,1,2,1,4,1,2,1,1,1,2,1,1,2,1,2,5,3,2,1,1,3,2,1,1,8,1,2,2,3,2,3,1,2,1,2,1,5,13,3,1,2,1,4,1,1,1,1,1,1,1,4,3,1,1, - 1,1,1,1,1,4,1,3,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,2,1,1,4,2,1,2,2,1,1,2,1,1,1,1,1,1,1,2,1,1,4,2,1,1,1,1,2,3,1,1,1,2,1,1,1,1,2,1,2,1, - 3,1,2,1,1,1,2,3,3,1,1,2,1,5,2,1,1,1,1,1,1,1,2,1,6,1,1,2,2,9,1,1,2,1,1,1,3,1,1,3,2,2,2,1,2,2,1,1,1,1,1,2,2,1,1,3,1,1,2,4,2,1,1,1,1,1,1,1,1, - 1,3,2,2,1,4,2,1,2,2,1,1,1,1,1,2,1,1,1,3,1,1,1,1,1,1,1,1,1,2,2,1,3,1,1,3,3,1,1,1,1,1,3,1,3,1,1,7,2,1,2,2,5,1,2,6,1,1,3,7,2,3,2,1,1,3,3,2,2, - 1,1,1,2,2,2,5,2,1,2,2,1,2,3,1,6,5,1,6,5,3,1,1,1,3,1,1,1,1,2,4,1,1,1,1,3,1,2,2,1,1,1,1,2,1,1,1,1,1,4,2,3,4,1,3,1,2,5,3,10,2,2,2,2,2,2,1,6, - 1,4,3,1,3,1,1,1,1,2,6,1,1,1,2,2,1,1,1,2,2,1,1,3,3,1,2,2,6,1,2,1,7,1,3,2,1,9,2,1,3,1,2,2,1,3,5,1,4,1,2,2,3,2,4,1,2,5,5,1,1,6,3,1,1,3,6,2,4, - 1,1,1,1,5,5,5,1,8,1,1,8,1,4,7,9,10,2,8,5,14,10,6,1,3,1,1,1,1,2,7,2,3,1,3,1,2,2,1,3,1,1,4,1,2,2,1,1,2,4,9,3,3,3,1,5,1,1,1,1,1,1,1,1,2,1,6, - 1,1,3,2,2,5,2,1,1,1,1,1,1,1,1,1,5,3,1,1,1,1,1,1,1,3,1,1,2,1,1,1,1,1,2,1,3,4,4,1,1,5,1,1,2,2,3,1,1,1,1,3,1,2,4,1,1,3,1,1,1,1,1,1,2,1,2,2,2, - 1,1,2,1,2,1,3,2,4,1,3,1,3,1,4,1,2,1,3,2,1,2,1,1,1,3,2,1,4,6,1,2,2,5,1,3,1,1,2,1,5,1,3,1,2,2,1,1,1,2,1,4,4,5,2,1,3,3,1,2,6,2,3,7,1,1,6,1,1, - 6,1,4,3,4,5,1,7,6,1,5,13,2,1,2,2,3,1,7,2,1,2,3,1,1,4,1,2,2,3,2,1,19,5,4,12,4,15,7,1,2,2,3,3,2,1,11,3,6,2,1,4,1,1,3,1,2,3,5,1,2,2,1,1,1,1, - 1,1,2,3,3,1,1,1,2,5,1,2,3,1,1,2,3,1,2,2,1,1,1,1,1,2,6,1,1,3,8,6,1,2,2,1,4,4,1,1,1,2,1,1,2,3,5,1,3,1,1,1,5,5,1,1,2,1,1,1,1,2,1,1,3,7,2,3,8, - 2,2,1,5,1,1,1,1,3,1,2,2,1,1,5,1,1,3,4,2,3,5,3,2,2,2,1,1,2,1,1,1,1,1,1,3,5,2,5,4,3,1,3,2,4,8,1,2,1,1,6,4,6,3,1,2,1,8,3,2,4,2,2,2,3,3,1,8,1, - 1,1,1,3,1,1,3,3,2,2,12,1,3,4,1,3,12,3,4,1,1,2,3,6,3,1,2,5,3,6,2,1,1,2,2,2,4,1,2,3,3,1,1,2,4,12,12,13,1,4,10,7,8,3,8,1,5,11,1,2,1,1,1,1,1, - 1,1,1,1,1,2,3,1,1,1,1,3,1,2,4,1,2,2,5,3,4,2,1,1,2,1,1,2,1,3,4,2,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,3,1,1,4,3,1,1,1,2,1,4,1,1,2,1,2,1,1,1,5,4, - 2,1,10,1,1,2,3,1,3,6,2,8,1,1,1,1,3,2,3,2,3,1,5,2,3,1,1,2,2,2,1,5,2,1,2,1,2,2,5,3,4,1,1,1,1,1,1,1,1,1,1,1,2,3,1,1,1,3,1,1,1,4,4,5,2,1,2,2, - 1,9,2,8,1,2,2,1,2,1,6,2,2,1,3,1,3,2,1,1,1,1,1,1,2,2,2,1,5,1,2,1,1,4,2,2,2,1,5,2,2,1,1,1,7,7,5,1,1,2,6,2,1,1,1,1,1,2,3,3,1,10,4,1,1,1,1,1, - 2,1,5,2,4,2,2,4,1,3,1,2,3,2,1,2,4,1,6,2,7,1,1,2,4,2,3,1,1,2,8,2,2,1,2,12,2,1,3,3,2,14,3,8,5,1,6,4,2,7,12,5,14,2,2,4,16,1,3,1,3,1,1,1,1,2, - 3,1,2,1,1,1,3,6,1,3,1,1,2,1,2,4,3,1,1,3,1,1,2,1,1,1,1,1,4,5,1,1,5,1,3,6,1,3,1,1,5,2,1,7,8,1,1,5,3,3,1,8,8,1,1,2,2,2,1,1,1,2,5,2,1,3,1,4,1, - 1,2,1,1,1,1,2,1,2,2,1,1,4,1,1,1,6,4,2,2,1,1,1,1,3,2,8,3,1,1,6,1,1,3,3,2,13,2,12,1,3,5,1,1,1,1,1,2,1,1,2,1,4,2,2,2,2,1,3,4,1,2,3,1,1,1,1,4, - 2,2,5,2,1,1,2,4,18,1,2,1,1,1,5,2,3,2,2,1,2,1,2,1,3,1,2,4,2,2,1,4,1,1,2,1,2,1,1,1,1,5,2,3,2,1,1,2,1,5,7,2,3,1,7,2,5,1,6,2,1,3,3,1,2,1,1,1, - 4,1,1,1,4,1,2,1,3,3,3,1,2,1,3,2,2,3,2,1,2,1,1,1,1,1,1,1,1,1,4,3,1,1,3,2,1,1,3,2,1,1,3,1,4,1,1,1,1,1,1,4,6,6,1,3,2,1,1,8,2,3,2,1,1,1,4,1,3, - 5,1,1,3,1,1,1,1,1,1,2,1,3,3,2,1,1,1,1,1,2,4,1,2,2,1,3,1,5,2,2,4,1,2,2,1,2,1,3,2,3,1,1,2,1,1,1,2,4,3,1,9,1,1,3,1,3,3,3,1,3,1,1,1,1,1,2,1,6, - 1,1,1,2,1,1,1,1,1,3,1,2,3,1,6,1,2,3,2,1,4,1,1,1,4,1,5,5,1,3,1,2,1,4,2,18,7,2,2,2,2,3,4,8,5,3,2,1,4,1,3,5,2,20,3,6,1,4,14,1,1,3,1,10,3,4,4, - 6,2,1,6,1,2,18,15,10,7,2,2,1,1,1,1,1,1,1,1,3,1,1,1,2,1,4,2,1,1,1,1,5,2,1,1,5,1,3,6,1,1,1,1,1,1,2,1,1,1,1,1,1,2,1,3,1,1,1,4,3,3,5,3,1,2,1, - 1,1,1,1,1,1,3,3,3,1,2,1,1,1,1,3,1,5,1,7,1,1,1,1,1,1,1,2,1,4,1,1,1,2,1,3,3,1,5,4,4,2,1,1,2,3,1,1,1,1,1,1,1,1,2,1,1,2,2,1,1,2,1,1,1,2,1,3,3, - 1,1,1,1,1,1,1,3,1,1,1,2,2,1,2,1,5,1,1,1,2,1,5,1,1,5,3,2,3,4,1,2,1,1,1,1,2,1,1,1,2,2,1,4,3,7,1,3,5,1,2,1,3,2,1,1,1,1,1,5,9,1,2,1,1,4,2,4,1, - 1,7,1,3,1,2,3,2,5,1,1,1,2,2,1,1,2,4,2,6,2,2,1,2,2,1,1,1,2,1,1,2,3,1,3,1,2,2,2,6,2,3,4,2,1,2,3,1,10,1,2,6,1,3,6,1,2,2,5,2,1,1,1,3,6,1,3,1, - 2,1,1,7,1,2,2,1,5,4,2,1,7,6,3,4,3,1,1,1,1,2,5,3,2,4,3,3,9,2,4,7,4,1,4,5,2,1,2,10,1,3,1,3,5,6,5,3,1,1,2,5,2,1,2,2,4,2,3,8,1,2,2,6,6,4,2,2, - 25,1,9,9,6,13,6,3,1,7,2,1,2,2,1,1,6,3,1,3,3,2,3,1,1,3,2,2,1,4,1,3,3,2,1,4,2,2,3,16,4,1,4,1,2,4,2,2,1,1,2,1,1,3,1,2,2,2,2,2,1,6,3,3,1,4,2, - 1,1,1,5,1,2,1,1,2,4,1,7,2,2,3,1,1,1,1,3,1,1,1,1,1,3,1,1,1,1,2,1,4,1,2,1,1,2,1,1,1,1,2,2,1,3,1,1,1,4,4,2,1,1,2,1,1,2,1,2,1,2,2,2,2,1,1,1,4, - 2,1,1,3,2,2,5,1,1,3,1,2,1,1,1,1,1,2,1,4,1,2,1,2,2,2,2,1,3,1,1,1,3,2,1,3,4,4,2,1,2,5,5,4,1,1,6,10,1,6,4,2,1,1,3,5,1,9,4,13,2,1,1,9,3,7,2,1, - 1,3,2,3,2,1,1,8,1,2,2,2,1,3,1,1,2,4,2,2,7,2,2,1,1,1,2,2,2,1,1,3,2,3,3,2,1,1,2,2,1,3,2,1,1,1,2,2,1,3,3,2,1,1,1,4,2,1,1,1,3,2,1,2,1,2,2,4,1, - 2,1,2,3,1,2,2,2,2,3,5,2,1,2,2,1,1,4,1,2,2,1,2,1,2,2,1,2,1,2,1,1,1,1,8,2,1,4,2,5,1,1,1,1,1,2,2,1,1,2,1,1,2,2,3,4,3,3,1,1,2,1,3,6,2,1,5,2,1, - 1,1,1,1,2,1,1,1,1,1,3,1,6,2,2,8,1,8,1,1,3,1,1,2,1,1,1,2,1,1,1,1,1,1,2,1,1,2,2,2,1,2,1,1,11,1,1,1,1,1,1,1,1,1,2,4,3,2,2,1,2,8,2,2,1,6,3,4, - 4,9,2,1,3,1,1,5,2,1,3,1,1,7,1,1,1,1,1,1,7,2,2,3,2,1,2,3,2,6,3,1,4,2,1,1,2,2,1,4,4,1,1,1,1,3,1,4,1,5,2,1,2,1,1,1,12,1,4,6,3,3,4,4,1,4,2,3, - 16,2,3,2,1,3,1,2,3,3,3,1,1,3,1,8,1,1,3,6,1,1,1,1,1,1,1,4,4,3,1,1,5,1,11,1,3,2,3,1,3,3,4,6,2,7,2,2,2,4,6,1,1,3,1,13,4,1,11,2,11,13,1,7,2,7, - 2,5,2,4,8,1,6,3,9,1,7,1,2,3,3,4,1,11,8,3,4,4,10,2,1,6,7,2,9,2,1,26,60,30,1,1,1,1,3,11,6,1,1,1,3,5,2,1,1,2,3,13,9,1,1,1,1,1,3,15,2,1,5,1,1, - 1,2,1,2,1,1,2,5,1,3,4,1,6,2,7,9,1,1,2,8,2,1,3,6,1,1,2,1,2,1,1,1,1,1,2,2,6,9,1,4,4,2,4,4,8,1,1,6,2,1,1,2,1,1,1,2,1,1,1,4,2,1,2,2,1,1,1,1,4, - 1,1,2,2,4,6,1,1,1,1,4,1,1,1,1,1,5,4,1,2,4,1,1,1,1,1,1,3,3,1,1,1,1,2,3,2,2,2,3,4,1,3,1,1,1,1,1,1,1,1,3,3,1,3,1,4,1,1,1,4,2,2,1,1,1,1,1,1,1, - 1,2,1,3,2,5,1,1,1,1,1,1,1,3,1,1,1,2,2,1,2,4,3,1,1,1,1,2,1,1,1,2,2,1,1,1,2,3,2,1,3,1,1,1,2,2,2,1,2,1,1,1,1,3,3,3,1,3,1,2,3,1,1,4,1,1,1,1,3, - 4,1,2,2,1,2,1,2,1,3,1,1,1,1,3,1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,4,1,1,1,2,1,1,1,2,2,1,3,1,2,1,1,1,1,2,2,3,1,4,6,2,1,3,1,3, - 1,1,2,1,2,1,1,3,3,2,1,1,1,1,1,1,1,2,1,1,1,1,1,3,1,3,2,1,3,7,1,4,1,1,1,4,1,3,2,5,2,1,3,1,1,1,1,1,3,2,5,1,2,1,2,2,1,3,2,2,1,9,5,2,1,1,2,1,3, - 3,3,3,2,1,1,2,1,1,4,2,2,2,1,2,2,4,2,2,7,1,5,4,1,3,2,1,2,2,3,1,1,2,4,8,3,4,2,10,1,2,2,2,1,1,2,4,2,1,8,2,2,2,1,1,2,3,1,1,3,5,2,1,2,1,1,1,2, - 1,1,2,4,1,1,3,1,2,2,2,2,2,2,1,1,2,1,2,4,1,1,1,1,2,1,1,1,1,1,1,1,9,5,9,4,2,1,2,4,4,1,1,1,10,4,2,3,1,1,2,2,2,2,1,2,1,2,2,1,2,8,1,4,2,2,7,2, - 4,2,1,3,4,3,4,1,4,2,1,3,2,2,1,1,4,2,4,1,3,1,1,13,1,5,4,2,3,3,6,1,2,3,3,1,3,2,7,2,6,1,12,2,1,8,5,1,29,1,4,3,6,1,8,14,4,4,3,12,4,3,15,13,2, - 2,1,5,1,2,4,2,1,1,5,1,3,1,2,2,4,2,1,1,2,1,3,1,2,4,8,1,1,1,1,2,2,4,1,1,2,1,1,3,1,1,1,5,2,10,4,1,2,1,1,2,1,4,2,1,1,1,2,1,1,2,7,1,2,1,1,2,9, - 3,1,2,4,3,2,1,1,1,1,1,2,2,8,9,1,1,5,11,2,2,5,1,1,2,5,9,2,1,5,2,1,1,6,4,1,1,4,2,3,7,3,3,5,5,4,1,2,1,3,1,5,3,4,9,4,2,8,7,1,3,1,5,1,5,6,5,2, - 7,11,13,1,13,6,3,2,9,2,2,4,1,4,1,1,1,1,2,2,2,2,1,5,1,4,1,5,4,3,1,1,2,2,2,1,2,1,1,1,2,5,1,3,3,4,1,1,3,1,1,1,3,3,3,1,2,2,3,13,9,4,3,1,4,2,1, - 1,1,8,1,1,1,4,1,4,1,2,2,5,2,4,1,2,1,7,1,3,1,1,1,1,1,1,1,1,3,1,2,1,1,5,2,1,3,3,4,7,1,1,4,1,1,1,3,1,3,4,1,1,1,2,1,1,1,3,3,2,1,4,1,14,3,2,5, - 9,12,1,2,5,10,5,1,3,2,2,3,3,1,1,2,1,2,1,1,1,3,1,1,1,1,2,1,1,3,2,2,1,1,1,1,1,1,3,1,2,1,1,1,3,1,2,3,2,1,1,1,1,1,1,2,1,1,1,3,1,3,2,2,1,2,2,1, - 2,1,1,2,1,1,2,3,2,3,2,1,1,1,3,3,3,1,1,1,1,1,1,1,4,1,1,4,6,1,4,2,1,2,1,1,2,2,1,1,1,1,1,1,2,1,1,1,1,6,4,1,1,1,1,1,1,11,2,1,1,1,1,2,1,1,1,1, - 1,1,3,5,1,4,1,3,1,2,3,1,7,2,1,2,3,1,1,3,3,2,2,6,4,2,1,1,5,2,1,1,1,2,2,3,1,8,6,18,4,2,4,2,2,2,1,1,2,8,1,5,2,1,4,4,5,9,2,2,2,4,1,2,2,3,2,4, - 1,2,1,1,1,1,1,2,1,1,1,1,2,2,1,2,1,3,2,5,3,3,1,2,1,5,2,3,1,1,1,8,1,1,4,2,3,3,2,4,1,6,2,2,3,7,2,1,1,3,2,1,2,1,3,2,1,1,1,1,1,1,6,1,1,1,1,1,1, - 1,1,1,1,3,1,3,1,1,4,1,1,1,2,1,1,1,3,5,1,1,1,3,3,3,4,1,1,2,1,1,2,1,5,1,3,5,3,3,1,1,2,2,1,4,2,4,5,2,1,1,2,4,1,2,2,1,1,3,1,1,2,3,3,1,1,3,4,2, - 1,1,3,9,1,7,2,1,5,2,5,8,4,9,3,2,1,1,3,2,2,1,1,3,3,3,2,2,1,3,2,3,6,2,7,1,3,1,2,11,3,3,1,2,1,1,3,1,1,1,1,1,2,2,1,1,3,4,6,1,8,1,2,1,2,3,1,1, - 3,2,3,3,3,1,1,1,1,2,12,1,5,1,2,4,2,1,3,1,2,6,1,1,1,2,2,4,1,2,1,3,7,2,1,9,1,6,1,1,2,1,2,3,1,13,4,1,1,1,4,1,6,1,1,1,3,1,13,1,2,3,2,2,1,1,1, - 1,3,3,2,6,2,2,14,10,4,1,2,4,1,2,2,2,2,1,1,1,2,3,3,2,3,1,2,1,1,1,1,2,2,3,3,1,4,1,2,2,1,1,2,2,1,2,1,3,2,2,4,1,1,1,2,4,1,2,1,1,1,1,1,2,2,1,4, - 2,3,2,1,1,2,2,2,1,1,1,2,1,3,13,1,1,1,1,14,3,4,4,1,1,3,1,1,1,2,2,4,1,2,1,1,3,2,2,2,2,1,8,1,1,1,1,2,3,5,3,4,4,1,1,4,1,4,1,4,3,5,7,4,6,2,3,2, - 2,6,4,7,7,11,22,1,5,2,2,2,1,2,3,1,1,1,1,1,3,1,1,3,2,1,1,3,1,1,1,1,1,1,2,3,2,1,5,2,3,5,1,2,2,2,2,1,3,1,5,1,4,3,2,4,2,1,9,3,12,6,1,1,2,1,2, - 1,2,3,3,2,2,4,1,3,3,7,4,1,2,1,2,2,1,1,2,1,8,3,2,1,2,2,1,3,5,1,1,1,3,3,3,2,1,4,2,3,16,4,2,2,1,1,2,5,2,11,6,2,3,3,17,4,2,1,1,1,1,5,1,1,2,1, - 1,3,2,3,1,3,1,3,1,1,1,4,1,2,1,3,2,1,2,4,1,6,5,7,12,6,5,2,1,5,1,1,3,2,1,5,2,2,10,1,3,2,4,1,2,2,2,1,4,2,3,1,4,2,3,2,4,1,1,2,2,1,1,2,1,2,2,5, - 2,2,2,2,5,2,2,2,1,3,1,1,1,2,2,2,2,1,1,1,3,2,2,1,1,8,3,1,3,1,1,2,2,1,2,2,5,8,3,3,1,1,1,1,5,5,1,4,1,1,1,1,1,1,1,4,3,4,9,5,1,5,5,2,6,1,3,4,4, - 1,4,4,1,2,6,5,2,5,4,2,2,1,3,6,1,7,2,1,7,3,6,8,7,1,1,12,24,14,1,7,1,1,12,8,1,3,2,1,4,1,1,1,3,2,1,5,4,3,1,1,6,3,1,1,1,2,4,2,1,2,2,1,5,3,1,1, - 2,3,3,1,8,7,6,6,15,22,2,5,4,4,45,18,9,47,8,114,2,25,6,3,3,59,25,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,8,2,1,8,3,1,3,1,2,1,2,5,2,1,6,1,1,4,3,1,2,2,3,4,1,3, - 9,2,2,3,2,1,1,1,4,3,1,1,2,3,3,8,1,1,1,6,1,3,1,2,1,1,1,1,2,3,2,2,2,6,1,3,1,1,1,4,1,1,4,1,3,5,1,1,1,2,4,4,1,2,2,1,2,1,1,1,1,4,1,1,2,1,1,1,2, - 1,1,1,1,1,3,4,2,2,1,1,1,1,1,1,6,1,2,2,1,1,1,3,2,2,3,1,3,4,1,1,1,5,2,4,2,15,1,7,14,4,1,2,1,2,2,1,7,3,2,2,1,2,2,1,1,1,2,1,4,1,1,2,1,1,2,1,2, - 3,1,2,2,1,1,1,1,1,1,1,1,2,2,4,1,1,8,1,2,1,1,1,1,1,2,1,2,1,1,2,1,1,1,1,1,1,2,1,1,1,1,1,2,2,2,2,1,5,4,3,1,3,1,1,1,1,1,1,1,2,2,2,4,1,10,3,3, - 2,1,4,2,6,1,7,2,2,1,1,4,1,1,1,1,1,3,1,1,6,5,2,1,1,4,1,1,1,1,3,5,1,1,1,1,1,1,1,2,6,2,5,2,7,1,2,1,1,9,2,11,7,1,5,2,1,3,4,2,5,11,7,4,3,2,1,6, - 1,7,1,2,2,1,1,2,1,1,6,1,3,2,2,7,1,2,1,1,2,1,2,5,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,3,6,1,2,2,2,3,1,9,2,5,9,6,1,1,2,1,1,6,3,1,1,3,1,1,4,2,1,2,1, - 1,2,1,5,1,1,3,1,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,2,1,3,1,1,3,1,1,3,3,3,1,1,1,1,1,1,1,1,2,1,1,1,1,2,1,3,1,2,1,1,1,2,1,1,1,1,4,4,2,3,3,2,2, - 3,2,1,1,1,1,1,1,2,1,2,1,2,1,2,2,1,1,2,3,1,1,1,10,1,3,1,1,2,2,2,1,1,1,2,1,1,2,1,3,1,2,1,2,1,2,6,1,1,1,1,1,2,3,1,1,1,2,1,1,1,2,1,1,1,1,1,1, - 1,1,1,1,1,1,8,1,3,1,1,8,1,3,5,4,1,5,1,2,1,1,1,2,8,1,1,1,5,1,1,1,1,2,1,1,1,1,2,1,2,1,1,1,3,2,2,1,2,1,2,5,2,2,4,1,2,1,1,4,4,1,6,1,1,7,3,1,4, - 2,1,2,5,1,1,1,1,2,7,2,2,1,6,1,1,1,1,1,1,8,2,5,1,3,1,9,8,3,5,1,2,1,1,1,5,2,6,2,1,1,3,1,2,1,1,1,2,2,8,2,3,1,1,2,1,2,2,7,2,3,3,2,7,7,2,1,2,4, - 1,1,1,2,2,2,1,2,3,2,1,3,2,1,1,2,3,7,2,1,2,1,2,1,6,4,12,3,1,3,5,2,2,5,2,4,3,5,2,10,1,11,1,1,1,1,1,1,6,5,1,1,12,1,2,5,6,4,8,2,2,5,1,1,3,3,3, - 1,2,8,12,1,6,2,4,2,4,1,1,3,4,1,1,6,2,8,6,1,3,2,7,3,1,4,2,2,1,18,4,6,12,5,2,15,2,15,4,18,1,1,1,1,1,1,6,4,4,2,7,1,2,3,6,1,1,1,1,1,1,1,1,1,1, - 1,2,2,4,1,1,1,6,2,7,1,6,1,3,1,1,1,2,3,1,1,1,2,2,4,6,3,1,2,1,2,1,1,3,1,1,1,3,4,1,2,3,1,5,5,4,1,2,1,1,1,1,4,1,5,2,2,1,4,1,1,1,3,1,2,2,1,2,2, - 1,2,2,2,3,1,1,2,4,5,3,3,3,4,3,1,8,1,1,3,2,5,4,1,1,7,3,1,1,9,2,2,2,2,5,1,1,1,4,1,2,3,3,2,10,8,1,3,5,3,1,1,2,2,2,2,5,3,3,6,4,1,4,2,1,9,5,5, - 5,1,3,11,5,5,7,1,8,2,1,11,1,16,7,3,4,3,1,1,7,1,1,4,2,3,2,7,1,1,1,2,3,1,2,1,2,2,2,5,6,1,1,2,1,2,1,1,2,3,2,5,4,1,5,6,2,6,1,2,2,2,6,5,5,1,3,1, - 2,1,5,4,2,1,3,1,2,4,3,1,1,1,3,8,1,2,1,4,1,3,2,4,8,6,2,1,2,4,1,1,4,2,4,5,1,4,1,4,2,11,3,17,5,2,2,1,7,5,16,4,2,2,3,59,1,1,1,1,1,1,1,1,1,1,1, - 2,1,1,1,1,4,4,2,2,1,4,2,1,5,2,2,2,2,12,1,4,3,19,36,10,23,26,16,1,9,116,95,6,53,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - ,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,3,2,5,1,2,3,9,5,7,1,6,2,1,3,5,5,1,1,8,3,1,3, - 1,2,8,4,133,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,2,1,3,1,2,3,3,1,1,1,1,10,3,1,5,1,6,3,11,4,12,2,2,1,1,3,2,2,2,1,3,1,1,2,5,1,2,1,1,1,5,1,3,1,2,1,1,4,1,4,2,2,3,2,2,2,1,1,1,1,1, - 1,2,8,4,1,2,3,3,2,9,1,1,3,1,3,3,1,2,2,1,2,4,1,3,1,3,3,1,1,2,1,2,1,1,1,1,5,1,1,7,1,2,6,4,3,3,1,2,1,2,2,2,6,1,1,1,2,4,2,8,1,4,8,2,13,1,3,3,8, - 172,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,4,5,1,2,5,1,8,4,1,1,2, - 1,2,1,2,1,6,2,1,2,1,1,3,1,2,1,1,1,1,3,2,1,1,2,1,1,1,2,1,2,3,4,1,1,1,3,3,1,1,1,1,1,1,3,1,1,2,2,1,1,1,2,2,1,3,1,2,1,1,1,1,1,4,7,1,1,5,1,1,2, - 3,1,2,3,1,2,2,1,6,2,1,1,1,1,4,1,3,7,1,2,3,5,1,6,1,4,4,2,1,2,1,5,3,6,2,2,2,1,1,2,2,3,1,1,1,3,2,2,2,2,1,1,1,1,1,1,1,1,2,1,1,1,2,1,1,2,2,1,1, - 2,1,2,4,1,1,1,2,4,3,1,1,1,4,1,2,2,1,1,2,1,2,2,5,4,5,1,1,1,2,1,1,1,7,5,6,1,1,1,2,2,2,2,1,8,4,2,8,9,1,2,1,3,1,1,1,1,1,1,1,2,5,3,3,1,3,1,1,1, - 1,1,3,1,2,2,1,1,1,1,2,1,1,1,1,1,1,3,1,1,6,2,2,2,1,1,3,1,1,8,1,7,6,2,3,1,1,5,1,5,4,9,2,1,2,1,1,1,1,1,11,152,26,32,24,2,118,44,37,59,12,104, - 45,27,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1, - 1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1, - 1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,105,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,2,1,1,1,1,1,1,2,3,2,9,4,3,1,1,1,1,1,5,1,1,2,1,2,2,1,1,1,1,1,2,1,1,1,2,1,3,1,6,3,1,2,1,2,3,1,1,1,2,6,2,1,1,1,12,2,1,2,3,2,2,1,4,3,1,1,3,11, - 2,7,3,3,3,1,2,1,1,2,1,3,1,1,1,2,1,2,1,1,1,3,3,11,8,1,1,5,2,2,3,1,2,5,2,1,3,1,1,1,1,1,4,1,1,4,3,6,2,10,2,3,3,2,6,1,5,20,1,3,3,2,3,2,1,1,3,4, - 3,4,3,1,2,2,2,2,1,2,2,4,6,2,4,1,2,4,8,1,2,4,1,3,1,1,1,1,3,1,1,14,36,1,1,1,1,1,1,1,6,2,1,127,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,39,1,1,1,1,1,1,1,2,1,1,5,1,8,1,37,3,30,38,1,16,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,2,2,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,4,1,7,2,1,196,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,8,1,5,1,1,2,2,4,1,1,1,2,6,1,2,3,2,5,9,1,4,5,2,2,10,2,2,6,5,7,3,1,5,7,4,12,3,4,1,4,1,5,1,1,1,1, - 1,1,1,1,2,2,2,2,3,296,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,315,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,10,1,1,2,5,1,3,6,1,1,3,7, - 2,7,18,1,2,2,1,6,3,2,4,1,1,2,3,4,3,1,1,2,1,1,1,3,2,2,1,5,10,2,1,2,13,2,1,2,2,1,4,2,7,2,7,2,1,3,2,6,2,2,2,1,3,6,2,7,1,46,1,1,1,1,1,1,1,1,1, - 1,1,1,1,13,1,1,4,1,2,43,1,1 - }; - static ImWchar base_ranges[] = // not zero-terminated - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x2000, 0x206F, // General Punctuation - 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters - }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; - if (!full_ranges[0]) - { - memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); - } - return &full_ranges[0]; -} - -const ImWchar* GetGlyphRangesChineseTraditionalOfficial() -{ - // Store all official characters for Traditional Chinese. - // Sourced from https://https://en.wikipedia.org/wiki/List_of_Graphemes_of_Commonly-Used_Chinese_Characters - // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) - static const short accumulative_offsets_from_0x4E00[] = - { - 0,1,2,5,1,1,1,2,3,1,3,1,1,2,1,5,1,3,4,5,2,5,6,1,2,8,2,6,2,1,1,3,1,3,2,1,4,1,1,10,10,11,4,4,2,3,1,2,3,1,2,1,4,2,3,1,2,3,1,1,2,3,1,1,1,12,6,1, - 2,1,2,1,3,1,2,7,1,1,1,1,1,5,1,4,1,1,11,2,1,3,5,1,1,2,2,8,1,3,2,1,1,4,4,22,1,4,2,2,2,2,1,6,3,1,1,5,1,1,1,1,3,1,2,2,2,1,1,1,2,3,6,3,3,1,3,2,6, - 4,4,3,2,3,2,2,7,5,2,17,1,6,1,9,3,1,1,6,4,1,1,1,6,1,2,3,1,1,1,1,13,1,4,5,2,4,2,6,3,2,1,1,2,2,1,2,2,2,1,1,5,2,2,2,1,2,1,1,1,2,9,6,7,4,2,3,2,1, - 6,5,2,9,15,1,1,1,6,3,5,8,4,5,2,1,9,10,1,5,1,2,2,4,3,7,10,2,4,1,4,9,2,2,7,3,4,4,2,5,2,2,4,1,7,2,2,4,7,2,9,8,5,5,4,1,2,1,1,1,1,1,1,2,1,1,4,1,2, - 1,2,5,6,3,2,1,1,2,1,1,1,3,4,1,1,1,4,4,9,1,3,4,1,3,11,2,2,1,7,4,6,1,6,9,5,1,1,15,1,3,1,15,1,2,3,2,1,1,3,3,1,2,3,1,1,2,4,3,1,5,6,7,4,1,1,1,3,2, - 6,1,1,2,1,6,2,4,3,1,1,1,1,6,2,5,1,1,12,1,5,3,2,1,2,6,2,4,4,1,1,4,4,10,4,1,3,6,1,1,1,18,3,2,4,2,9,3,2,1,1,2,2,1,1,3,2,15,2,3,3,3,1,1,5,1,2,5, - 2,1,5,1,1,2,4,3,1,2,7,5,2,8,5,1,1,1,2,2,2,1,1,1,7,1,1,1,3,3,2,2,2,1,5,1,5,2,2,1,2,2,2,1,1,2,4,5,22,5,6,8,5,9,8,5,1,1,1,2,7,2,1,2,2,4,3,1,1,1, - 1,2,1,1,1,1,1,1,1,1,1,1,1,2,3,4,5,2,1,2,2,1,2,1,1,1,1,1,1,1,9,2,1,1,1,6,1,2,2,2,1,3,2,2,3,1,2,1,2,2,2,1,3,2,2,4,2,18,6,9,2,2,1,1,1,3,1,1,3,4, - 2,5,1,2,2,2,3,1,4,12,1,1,1,1,1,1,3,2,2,5,3,2,3,1,1,2,3,1,1,5,19,4,1,2,1,1,3,1,4,8,2,5,5,1,2,7,4,19,5,2,1,2,2,4,1,6,3,2,1,2,4,5,6,1,6,2,1,2,2, - 1,1,1,4,17,1,2,2,2,1,1,3,2,1,1,9,4,2,2,1,2,8,3,2,2,3,1,1,8,10,1,1,2,5,3,2,7,2,3,2,2,1,1,3,7,8,6,1,2,6,2,1,4,7,2,1,4,4,10,5,1,1,2,2,2,3,2,4, - 15,5,4,2,9,2,1,3,1,1,1,1,3,2,3,4,1,7,5,2,7,1,6,16,3,6,6,2,1,5,4,1,1,6,1,1,2,5,9,1,3,2,4,6,6,1,9,9,5,3,2,5,1,3,2,7,9,4,1,2,1,3,11,2,7,3,3,1, - 1,1,1,16,3,2,3,1,13,5,6,1,8,21,2,1,11,21,8,3,6,11,1,23,2,1,8,3,1,27,3,6,5,1,1,3,1,21,2,5,3,3,1,2,4,4,9,10,4,5,2,3,2,5,9,9,2,1,9,6,5,11,3,4, - 2,10,2,2,3,1,5,1,5,5,2,1,3,10,1,3,18,6,1,3,1,2,3,3,2,1,2,2,1,1,2,1,3,6,1,6,6,3,1,1,5,1,1,1,3,1,2,1,2,6,2,5,2,1,4,5,1,2,2,1,4,5,1,1,6,3,5,1,3, - 3,4,1,5,1,1,3,6,1,4,6,2,3,8,4,1,2,3,1,1,1,1,4,2,2,7,2,1,2,2,2,15,4,2,2,9,5,2,5,3,1,3,1,3,2,4,13,6,4,1,5,3,1,16,8,4,4,13,8,19,8,1,15,8,1,10,1, - 3,1,7,3,10,1,1,9,2,3,3,12,5,1,14,2,1,17,7,6,6,4,4,8,16,1,2,1,1,2,1,2,2,1,2,4,1,5,2,5,1,2,2,3,2,3,2,3,1,2,2,1,1,2,1,3,8,1,1,1,1,1,6,1,1,1,1,8, - 2,3,1,1,1,3,6,3,2,1,1,1,5,4,2,1,11,1,2,1,2,1,1,1,1,1,2,1,2,7,1,2,2,7,3,1,2,1,1,1,1,2,1,1,2,5,4,10,8,5,6,1,1,1,2,2,1,1,1,4,1,2,3,2,1,1,1,1,4, - 7,4,2,2,1,7,3,2,8,19,4,1,3,13,8,2,6,2,4,1,33,15,1,1,3,2,1,6,5,2,9,1,7,3,3,2,1,2,2,7,7,35,4,25,27,3,22,29,2,1,16,5,2,2,7,1,3,1,3,1,1,1,3,3,3, - 1,1,1,3,6,1,4,1,3,6,5,4,1,2,2,1,2,8,6,2,6,3,1,1,5,3,5,7,8,1,2,4,4,3,1,8,7,1,1,2,2,1,1,1,1,1,1,9,8,6,1,1,3,2,4,6,1,4,2,8,1,1,1,6,3,1,6,1,1,9, - 3,4,3,2,1,2,1,9,7,3,1,3,5,2,3,4,2,5,2,2,1,1,2,1,3,4,7,1,2,4,4,4,2,5,10,2,4,2,9,2,4,5,2,1,4,1,1,1,1,3,1,6,2,3,3,1,1,4,2,1,2,1,1,4,1,1,5,1,1,5, - 3,7,1,1,2,2,7,2,2,4,6,2,7,1,9,1,1,1,4,3,11,6,4,7,1,2,15,1,3,2,1,1,7,3,1,4,2,1,2,5,2,4,14,3,7,3,5,4,9,1,1,1,3,1,1,1,1,1,2,1,15,5,1,4,3,1,7, - 2,4,5,1,3,3,2,10,2,1,1,2,3,1,1,8,1,5,6,4,3,2,2,3,1,1,5,10,1,2,1,2,3,1,1,5,1,7,1,5,1,6,5,1,1,3,1,3,4,13,10,1,5,3,1,3,1,1,1,7,3,2,3,2,3,1,4,1, - 3,3,2,3,3,1,5,2,4,8,4,2,1,3,6,10,3,2,2,1,6,4,8,4,5,1,1,1,3,22,3,12,3,1,1,1,2,2,3,1,8,2,2,1,1,2,1,1,3,1,4,1,4,2,1,8,4,2,2,1,1,3,7,1,1,1,6,1,1, - 2,2,1,4,1,1,4,3,8,10,1,1,4,3,3,3,2,1,1,4,5,1,7,1,1,2,1,1,1,16,3,1,5,4,4,3,1,2,3,2,2,1,1,1,2,1,1,3,2,1,1,2,1,1,1,1,1,1,16,1,1,1,2,2,1,3,5,1,1, - 1,2,6,1,1,8,5,18,2,1,4,11,3,1,4,4,3,5,1,1,5,18,1,1,2,6,1,4,1,4,5,3,1,4,1,3,3,3,4,2,1,2,5,1,1,1,2,2,1,1,1,2,20,6,3,4,2,1,2,4,4,1,6,2,6,1,3,1, - 6,25,2,4,1,2,1,5,2,12,2,1,9,4,3,1,19,1,2,4,7,8,2,6,9,1,1,13,1,8,2,1,2,5,4,6,1,4,2,1,1,1,2,2,9,1,3,2,1,2,1,2,3,1,2,1,4,2,4,2,6,1,5,1,5,4,1,1, - 2,6,1,3,2,6,9,5,4,1,3,1,6,1,6,1,1,3,7,3,2,3,1,6,3,5,2,2,5,1,1,1,4,1,4,1,3,6,6,2,1,2,1,10,1,4,9,1,6,2,3,3,2,3,1,2,4,1,3,1,7,2,3,1,4,4,6,1,2,1, - 7,1,11,3,1,2,1,3,1,4,9,8,4,1,5,2,1,4,1,11,1,5,2,1,5,2,19,1,6,3,3,11,3,7,2,2,6,1,1,4,2,4,13,1,1,8,5,1,11,6,3,3,6,5,13,3,13,3,4,3,6,6,4,2,1,1, - 3,1,1,3,2,1,1,3,5,1,2,2,7,1,2,4,2,2,7,1,1,2,1,1,1,1,3,3,1,8,9,3,5,1,1,1,5,6,2,1,14,2,1,1,2,2,2,7,1,8,2,6,1,5,2,5,1,18,2,5,2,12,11,1,1,2,1,6, - 2,9,4,3,3,2,2,1,1,5,5,20,4,10,8,1,14,1,3,1,3,2,1,2,1,2,4,4,1,2,1,34,9,2,2,2,1,13,4,6,2,3,5,1,5,2,1,3,2,15,5,2,2,5,3,2,1,2,5,1,3,4,7,4,3,4,1, - 11,8,1,4,30,26,9,1,6,4,2,8,3,2,8,2,2,9,4,14,1,6,1,10,2,5,1,6,3,4,6,10,1,1,3,3,21,11,10,4,1,8,7,2,6,5,1,2,2,18,3,1,1,3,7,3,4,13,1,6,2,1,9,21, - 4,16,8,6,10,3,4,5,3,7,1,5,11,7,1,2,8,22,9,6,12,10,1,2,15,8,3,1,9,2,3,2,2,15,2,1,1,1,1,1,1,3,8,5,1,1,2,4,4,3,3,1,12,2,12,10,1,3,3,2,3,2,3,1,5, - 1,5,2,2,3,1,1,3,4,16,4,14,13,5,1,1,2,3,5,4,4,1,2,1,1,3,3,1,3,2,4,6,2,1,1,14,5,2,6,1,1,1,1,7,2,6,2,8,3,1,3,2,2,3,4,4,2,4,3,2,16,3,3,2,5,1,2, - 1,1,1,1,2,3,1,4,1,2,7,2,2,4,6,1,1,2,3,8,1,2,24,12,4,3,7,4,1,7,1,4,5,2,1,3,24,1,12,3,1,2,2,6,3,1,15,1,1,3,2,5,2,25,1,3,3,3,4,8,1,1,1,4,1,5,1, - 6,1,1,4,3,3,4,2,1,3,3,2,2,2,2,1,1,10,20,1,1,2,3,1,2,1,1,1,3,3,1,2,3,2,4,2,4,5,7,3,7,2,2,3,12,7,1,26,7,6,2,4,1,5,3,5,5,7,4,2,6,2,1,2,4,1,5,2, - 1,24,3,3,2,10,1,2,1,4,9,4,1,1,11,2,1,6,1,1,1,5,1,1,5,6,1,18,3,4,3,9,2,7,1,2,8,2,2,4,4,2,2,6,9,10,3,3,10,2,6,7,1,1,1,21,3,4,1,1,3,7,1,3,2,9,4, - 8,3,2,4,2,4,5,1,2,2,9,8,14,14,5,7,11,8,5,6,2,4,1,13,4,4,3,4,18,1,1,1,1,4,5,2,14,2,5,9,1,6,5,21,4,12,1,15,1,7,5,10,6,19,3,2,11,3,2,6,1,1,1, - 1,1,3,2,15,7,6,10,5,6,9,4,5,8,5,5,1,4,1,5,2,2,3,4,3,3,6,1,1,5,1,1,6,7,11,7,3,11,13,2,2,1,3,2,3,1,2,1,1,1,2,1,2,6,1,1,4,6,4,2,1,2,2,2,1,1,1,5, - 2,6,3,2,5,4,3,1,3,14,4,5,7,5,5,3,17,2,2,10,2,7,2,7,1,8,14,1,1,3,1,25,3,2,1,9,4,11,2,1,7,1,5,1,9,2,7,17,8,2,3,2,1,2,1,5,4,3,2,2,11,9,10,2,7,1, - 4,4,5,10,3,19,13,1,16,5,2,1,3,1,24,3,1,2,2,9,1,2,4,5,2,20,4,1,1,1,2,1,4,1,5,1,5,1,19,8,17,7,3,1,9,13,13,5,13,4,2,1,3,16,1,13,8,9,3,2,2,3,3,3, - 1,2,1,2,1,1,3,1,1,1,4,1,20,3,5,5,1,2,1,5,3,1,1,3,1,5,6,2,14,1,3,1,3,1,2,8,1,3,7,1,6,7,1,2,4,3,1,1,7,2,3,10,1,3,1,2,3,4,1,13,1,2,1,6,5,1,1,8, - 2,2,3,3,12,1,1,3,3,2,11,4,10,4,6,6,4,2,9,1,3,4,3,2,3,1,6,3,1,1,1,4,2,2,1,1,6,5,3,4,20,2,4,6,5,3,1,3,2,2,3,1,4,2,7,1,2,1,2,2,1,1,2,3,4,1,3,2, - 4,1,3,2,3,8,2,20,1,8,1,13,1,1,2,2,2,2,15,12,1,2,3,2,2,1,2,3,1,13,4,1,1,5,3,5,2,3,13,1,1,5,2,3,2,1,3,3,8,2,2,8,12,4,3,1,1,6,1,2,4,4,1,1,4,10, - 5,10,1,7,9,8,2,6,3,2,2,4,11,26,14,1,3,13,13,3,2,3,6,1,7,8,2,7,3,7,2,2,3,4,5,1,5,5,5,14,4,3,5,3,3,7,13,11,13,4,1,1,14,4,2,1,5,1,1,1,7,5,1,1,3, - 3,1,1,1,5,3,5,13,5,2,12,1,1,23,1,3,4,3,9,3,1,1,1,1,8,2,6,1,13,4,1,2,1,2,18,5,5,3,3,2,1,6,6,2,3,1,14,3,6,4,1,1,2,1,7,5,1,1,9,10,1,7,2,9,2,3, - 1,5,2,3,4,10,3,1,1,1,7,1,9,1,4,1,11,10,1,2,1,2,1,14,6,1,3,2,8,2,7,3,1,3,2,7,11,8,2,3,2,6,2,4,26,3,2,2,2,1,2,2,2,10,21,2,20,4,5,1,2,6,4,12,4, - 3,12,1,3,2,1,2,16,8,3,5,14,7,9,6,17,3,2,4,3,1,12,1,5,1,1,4,9,1,18,1,4,8,2,4,1,22,9,3,2,8,6,12,2,2,5,3,1,11,1,11,1,3,3,2,1,1,2,5,8,12,3,2,2, - 2,1,2,2,1,1,2,3,4,1,2,2,1,1,1,4,1,1,9,3,1,1,3,6,3,4,2,1,1,2,10,5,9,3,1,4,2,6,3,1,7,8,8,6,2,2,9,2,2,2,9,2,3,1,1,2,1,3,2,1,2,1,1,8,3,8,6,2,3,1, - 2,3,5,1,3,3,5,7,13,9,4,6,8,8,3,3,1,5,6,1,3,2,2,1,14,1,5,4,3,8,1,1,2,6,6,3,1,5,10,3,4,3,6,154,2,2,3,7,4,8,4,1,10,10,1,4,2,2,3,2,2,12,3,2,2,2, - 6,6,4,5,1,4,1,6,3,4,2,1,4,2,2,4,7,2,4,2,1,10,1,1,8,7,1,2,6,1,1,1,4,1,2,2,1,6,1,3,2,3,2,1,1,3,23,3,7,2,7,4,12,2,2,4,17,1,1,1,1,3,1,6,1,1,5,1, - 1,1,2,2,1,7,3,2,2,1,2,4,1,3,4,1,1,4,2,1,2,6,9,9,2,8,4,1,3,3,3,1,8,3,1,2,1,4,5,5,3,1,2,2,12,13,6,2,4,2,8,5,8,5,3,2,1,3,1,16,1,5,3,2,1,2,5,1,1, - 5,1,8,2,5,11,1,1,1,3,8,1,10,7,3,1,1,1,2,1,3,3,4,2,9,2,5,4,2,2,1,2,3,6,1,6,1,1,2,2,2,3,2,1,1,1,2,1,3,2,2,7,1,2,1,3,6,2,1,1,9,1,1,2,14,17,1, - 13,8,1,2,1,4,8,13,2,5,7,4,2,6,7,1,4,2,6,2,2,20,1,1,1,3,4,3,1,4,2,1,1,10,16,1,1,1,1,4,14,20,6,1,2,1,1,2,1,7,3,6,1,5,1,2,2,35,1,3,1,13,1,4,4, - 1,3,2,6,2,2,9,18,4,4,5,2,16,4,9,6,1,1,1,2,4,5,6,1,6,1,1,1,1,30,5,4,3,4,1,12,14,4,6,2,3,3,2,1,5,4,2,11,14,9,3,2,20,6,4,3,1,4,2,3,2,6,2,25,2, - 35,2,1,3,3,7,2,9,1,16,6,6,1,15,15,1,1,3,17,6,3,10,3,8,10,3,1,5,1,6,19,4,2,6,8,7,1,4,1,15,1,1,11,1,3,1,6,9,19,11,7,15,2,4,1,6,1,2,1,8,4,4,2,8, - 17,2,7,16,1,5,1,5,2,5,10,6,1,4,9,5,2,4,5,9,12,2,3,2,2,1,4,1,11,2,3,2,2,6,3,15,7,4,13,9,2,2,7,6,2,12,2,2,10,1,10,17,1,3,9,8,7,1,5,6,2,3,6,1, - 26,3,3,9,10,8,2,5,1,10,2,1,1,14,5,3,2,5,12,1,6,4,2,2,2,4,1,1,5,3,5,7,10,1,6,3,2,5,4,8,13,6,16,1,8,5,5,1,3,1,3,1,2,2,7,11,1,4,1,4,5,5,9,2,1,7, - 5,6,5,1,7,6,3,8,1,18,9,1,4,6,5,3,13,2,2,3,5,4,4,3,16,4,2,6,8,3,3,18,17,17,4,8,2,1,5,2,2,2,1,4,2,1,1,1,3,2,2,4,2,5,3,4,3,6,1,6,5,10,4,1,6,3, - 2,2,1,3,3,2,1,1,1,2,1,1,1,6,3,9,2,5,1,4,2,2,3,6,2,1,2,1,1,1,2,2,2,4,2,10,3,3,2,3,2,2,2,5,4,6,10,1,4,2,1,3,6,1,2,4,2,1,1,2,6,4,9,2,2,2,3,8,4, - 13,8,8,3,2,1,9,2,2,10,6,3,1,4,3,6,3,10,8,1,3,4,157,8,2,5,2,1,3,2,2,4,1,5,7,1,8,1,1,13,1,8,7,3,1,6,10,1,2,1,1,5,1,1,1,1,1,3,3,1,1,2,1,1,3,1, - 1,2,1,1,1,1,1,1,2,1,2,7,1,1,9,2,2,2,1,1,2,4,2,1,7,6,2,1,8,3,2,1,2,2,4,3,5,73,2,1,4,2,3,3,1,3,10,4,5,11,4,6,4,5,11,1,10,8,5,1,2,3,9,1,2,2,2, - 7,2,5,4,7,2,19,1,13,2,3,7,1,1,6,3,1,4,9,2,4,1,1,1,14,6,4,1,2,6,6,4,4,2,5,1,3,2,2,1,3,4,16,9,1,1,1,5,8,6,10,1,1,1,5,2,7,4,25,3,1,2,5,6,1,8,1, - 1,1,6,1,1,2,7,3,1,3,2,9,3,3,1,3,2,1,4,4,1,6,11,2,58,1,3,4,3,2,5,1,1,1,1,1,16,2,1,9,3,3,14,2,1,1,4,1,2,3,4,3,1,1,1,3,3,1,2,2,1,7,2,1,4,1,1,1, - 2,1,1,2,1,1,1,2,1,11,3,1,3,3,4,2,3,1,5,3,1,2,1,1,1,1,2,1,1,3,1,3,2,2,3,5,1,4,1,4,2,1,2,1,2,2,1,2,1,1,1,2,3,3,5,1,1,4,13,1,3,2,2,7,4,3,9,9,4, - 19,7,5,8,8,5,7,9,7,14,6,3,1,24,1,1,1,1,5,5,12,2,4,1,2,9,2,1,11,4,2,2,7,10,2,5,8,1,14,6,1,6,2,2,1,1,1,1,1,1,6,1,4,1,6,3,1,14,20,4,1,2,4,1,9, - 5,17,3,1,6,2,11,6,6,4,7,2,28,5,14,3,2,4,16,7,4,2,2,1,5,4,13,18,6,3,11,4,8,12,8,2,6,2,4,2,3,4,4,3,20,1,2,14,10,7,9,9,4,8,10,2,12,12,5,16,5,9, - 5,1,1,4,3,2,1,27,6,21,22,4,1,2,3,2,10,13,1,14,3,13,2,10,1,1,1,248,9,2,1,6,2,4,2,1,1,1,4,9,2,1,1,3,1,4,4,1,9,11,2,2,1,1,4,4,2,6,5,1,58,5,9,4, - 3,1,9,4,1,4,7,1,1,3,11,1,5,1,1,1,6,6,2,1,2,1,1,1,5,8,1,4,1,2,1,6,1,3,1,2,1,11,1,2,7,3,4,3,5,1,3,1,1,1,2,2,1,1,8,1,3,2,1,2,4,1,5,2,5,3,4,1,2, - 2,5,4,2,1,2,4,1,1,2,2,3,6,2,9,3,8,6,1,4,1,4,2,4,10,4,5,1,2,2,1,1,4,2,1,4,7,2,6,9,6,2,2,9,8,3,3,7,20,2,3,5,1,7,9,17,6,2,1,5,4,2,1,1,2,1,2,4, - 4,1,1,1,4,1,9,3,9,3,7,1,1,2,11,6,1,1,1,9,3,3,9,4,4,1,1,55,7,2,2,3,4,2,8,23,4,3,5,2,1,3,2,3,2,8,1,1,5,2,4,1,2,4,2,1,5,3,3,3,7,13,8,1,1,6,12, - 1,10,2,56,3,12,3,4,1,1,3,2,1,13,15,1,1,3,4,2,2,2,3,11,4,14,2,13,8,3,18,5,7,7,2,3,2,16,2,3,1,4,3,3,5,62,7,1,7,1,4,19,3,1,1,4,14,7,1,1,12,8,3, - 7,13,8,3,2,1,9,11,5,1,2,1,10,5,4,2,21,8,26,26,3,27,1,30,21,16,6,18,8,4,10,3,22,2,1,32,1,109,4,10,1,2,16,3,12,6,8,3,2,19,4,18,12,3,1,9,2,6, - 23,38,5,14,17,4,14,20,1,32,4,87,4,3,1,2,12,7,1,4,6,2,6,4,1,10,7,1,1,1,5,10,1,1,2,3,4,3,1,1,1,2,8,7,5,3,16,1,6,5,2,4,7,12,8,7,3,12,1,7,13,2, - 2,3,4,2,6,5,22,3,4,8, - }; - static ImWchar base_ranges[] = // not zero-terminated - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x2000, 0x206F, // General Punctuation - 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters - }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; - if (!full_ranges[0]) - { - memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); - } - return &full_ranges[0]; -} - // Helper to display a little (?) mark which shows a tooltip when hovered. void ShowHelpMarker(const char* desc) { @@ -949,7 +700,7 @@ bool Toast::draw() const float maxW = std::min(uiScaled(640.f), displaySize.x); ImFont *regularFont = ImGui::GetFont(); const ImVec2 titleSize = title.empty() ? ImVec2() - : largeFont->CalcTextSizeA(largeFont->LegacySize, FLT_MAX, maxW, &title.front(), &title.back() + 1); + : ImGui::GetFont()->CalcTextSizeA(uiLargeFontSize(), FLT_MAX, maxW, &title.front(), &title.back() + 1); const ImVec2 msgSize = message.empty() ? ImVec2() : regularFont->CalcTextSizeA(regularFont->LegacySize, FLT_MAX, maxW, &message.front(), &message.back() + 1); const ScaledVec2 padding(5.f, 4.f); @@ -971,7 +722,7 @@ bool Toast::draw() if (!title.empty()) { const ImU32 col = alphaOverride(ImGui::GetColorU32(ImGuiCol_Text), alpha); - dl->AddText(largeFont, largeFont->LegacySize, pos, col, &title.front(), &title.back() + 1, maxW); + dl->AddText(NULL, uiLargeFontSize(), pos, col, &title.front(), &title.back() + 1, maxW); pos.y += spacing.y + titleSize.y; } if (!message.empty()) diff --git a/core/ui/gui_util.h b/core/ui/gui_util.h index 19d8f5e22f..1d9a99aca5 100644 --- a/core/ui/gui_util.h +++ b/core/ui/gui_util.h @@ -117,6 +117,11 @@ static inline float uiScaled(float f) { return f * settings.display.uiScale; } +static inline float uiLargeFontSize() +{ + return uiScaled(22.f); +} + struct ScaledVec2 : public ImVec2 { ScaledVec2() diff --git a/fonts/Roboto-Bold.ttf.zip b/fonts/Roboto-Bold.ttf.zip new file mode 100644 index 0000000000..1bf52b5c22 Binary files /dev/null and b/fonts/Roboto-Bold.ttf.zip differ diff --git a/fonts/Roboto-Regular.ttf.zip b/fonts/Roboto-Regular.ttf.zip deleted file mode 100644 index 6bc0533e88..0000000000 Binary files a/fonts/Roboto-Regular.ttf.zip and /dev/null differ diff --git a/resources/resources.cmake b/resources/resources.cmake index ec67a46407..9bf4830a74 100644 --- a/resources/resources.cmake +++ b/resources/resources.cmake @@ -34,7 +34,7 @@ cmrc_add_resources(flycast-resources if(NOT LIBRETRO) cmrc_add_resources(flycast-resources fonts/Roboto-Medium.ttf.zip - fonts/Roboto-Regular.ttf.zip + fonts/Roboto-Bold.ttf.zip fonts/fa-solid-900.ttf.zip) if(ANDROID OR IOS) cmrc_add_resources(flycast-resources diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java index 72bd5f74bd..603c2039b2 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java @@ -199,13 +199,14 @@ public boolean sendKeyEvent(KeyEvent event) { @Override public boolean commitText(CharSequence text, int newCursorPosition) { InputDeviceManager devManager = InputDeviceManager.getInstance(); - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - if (c == '\n') { + for (int i = 0; i < text.length(); ) { + int cp = Character.codePointAt(text, i); + if (cp == '\n') { activity.hideTextInput(); return true; } - devManager.keyboardText(c); + devManager.keyboardText(cp); + i += Character.charCount(cp); } return super.commitText(text, newCursorPosition); } @@ -254,7 +255,7 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { InputDeviceManager devManager = InputDeviceManager.getInstance(); if (event.getAction() == KeyEvent.ACTION_DOWN) { if (!event.isCtrlPressed() && (event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE)) - ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); + ic.commitText(new String(Character.toChars(event.getUnicodeChar())), 1); else devManager.keyboardEvent(event.getKeyCode(), true); return true; diff --git a/shell/android-studio/flycast/src/main/jni/src/android_input.cpp b/shell/android-studio/flycast/src/main/jni/src/android_input.cpp index 12256db395..ccf270a5d5 100644 --- a/shell/android-studio/flycast/src/main/jni/src/android_input.cpp +++ b/shell/android-studio/flycast/src/main/jni/src/android_input.cpp @@ -189,7 +189,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDevi extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardText(JNIEnv *env, jobject obj, jint c) { - gui_keyboard_input((u16)c); + gui_keyboard_input((u32)c); } static std::map, jint> previous_axis_values; diff --git a/shell/android-studio/flycast/src/main/jni/src/jni_util.h b/shell/android-studio/flycast/src/main/jni/src/jni_util.h index eb712a0e85..ec79f1590c 100644 --- a/shell/android-studio/flycast/src/main/jni/src/jni_util.h +++ b/shell/android-studio/flycast/src/main/jni/src/jni_util.h @@ -189,10 +189,42 @@ class String : public Object { if (object == nullptr) return ""; - const char *s = env()->GetStringUTFChars((jstring)object, 0); - std::string str(s); - env()->ReleaseStringUTFChars((jstring)object, s); - return str; + jstring jstr = (jstring)object; + jsize length = env()->GetStringLength(jstr); + const jchar* chars = env()->GetStringChars(jstr, nullptr); + if (chars == nullptr) + return ""; + + std::string result; + result.reserve(length); // Minimum possible length + for (jsize i = 0; i < length; ++i) { + uint32_t cp = chars[i]; + if (cp >= 0xD800 && cp <= 0xDBFF && i + 1 < length) { + uint32_t low = chars[i + 1]; + if (low >= 0xDC00 && low <= 0xDFFF) { + cp = 0x10000 + ((cp - 0xD800) << 10) + (low - 0xDC00); + i++; + } + } + if (cp <= 0x7F) { + result += (char)cp; + } else if (cp <= 0x7FF) { + result += (char)(0xC0 | (cp >> 6)); + result += (char)(0x80 | (cp & 0x3F)); + } else if (cp <= 0xFFFF) { + result += (char)(0xE0 | (cp >> 12)); + result += (char)(0x80 | ((cp >> 6) & 0x3F)); + result += (char)(0x80 | (cp & 0x3F)); + } else { + result += (char)(0xF0 | (cp >> 18)); + result += (char)(0x80 | ((cp >> 12) & 0x3F)); + result += (char)(0x80 | ((cp >> 6) & 0x3F)); + result += (char)(0x80 | (cp & 0x3F)); + } + } + + env()->ReleaseStringChars(jstr, chars); + return result; } size_t size() const { diff --git a/shell/apple/libpng-flycast.rb b/shell/apple/libpng-flycast.rb new file mode 100644 index 0000000000..92cb456573 --- /dev/null +++ b/shell/apple/libpng-flycast.rb @@ -0,0 +1,42 @@ +class LibpngFlycast < Formula + desc "Library for manipulating PNG images" + homepage "http://www.libpng.org/pub/png/libpng.html" + url "https://downloads.sourceforge.net/project/libpng/libpng16/1.6.55/libpng-1.6.55.tar.gz" + sha256 "4b0abab6d219e95690ebe4db7fc9aa95f4006c83baaa022373c0c8442271283d" + license "libpng-2.0" + + depends_on "cmake" => :build + uses_from_macos "zlib" + + # Keg-only to avoid conflict with standard libpng and fix CI resolution + keg_only "it is a universal static build for Flycast" + + def install + # Build universal binary #Flycast + ENV.permit_arch_flags + args = std_cmake_args + [ + "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64", + "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15", + "-DPNG_SHARED=OFF", + "-DPNG_TESTS=OFF", + "-DPNG_ARM_NEON=off", + "-DPNG_FRAMEWORK=OFF" + ] + + system "cmake", "-S", ".", "-B", "build", *args + system "cmake", "--build", "build" + system "cmake", "--install", "build" + end + + test do + (testpath/"test.c").write <<~C + #include + int main() { + fprintf(stderr, "libpng version: %s\\n", PNG_LIBPNG_VER_STRING); + return 0; + } + C + system ENV.cc, "test.c", "-I#{include}", "-L#{lib}", "-lpng", "-o", "test" + system "./test" + end +end