diff --git a/README.md b/README.md index 101cafc35..88c7f4e3e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Demo ---- You should be able to build the examples from sources (tested on Windows/Mac/Linux). If you don't, let me know! If you want to have a quick look at the features of ImGui, you can download Windows binaries of the demo app here. -- [imgui-demo-binaries-20151226.zip](http://www.miracleworld.net/imgui/binaries/imgui-demo-binaries-20151226.zip) (Windows binaries, ImGui 1.47 2015/12/26, 4 executables, 515 KB) +- [imgui-demo-binaries-20160410.zip](http://www.miracleworld.net/imgui/binaries/imgui-demo-binaries-20160410.zip) (Windows binaries, ImGui 1.48+ 2016/04/10, 4 executables, 534 KB) Gallery diff --git a/examples/apple_example/imguiex.xcodeproj/project.pbxproj b/examples/apple_example/imguiex.xcodeproj/project.pbxproj index 652d15b6b..5d7776deb 100644 --- a/examples/apple_example/imguiex.xcodeproj/project.pbxproj +++ b/examples/apple_example/imguiex.xcodeproj/project.pbxproj @@ -366,7 +366,7 @@ INFOPLIST_FILE = "imguiex-osx/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LIBRARY_SEARCH_PATHS = /usr/local/lib; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_BUNDLE_IDENTIFIER = "org.imgui.example.imguiex.imguiex-osx"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -389,7 +389,7 @@ INFOPLIST_FILE = "imguiex-osx/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LIBRARY_SEARCH_PATHS = /usr/local/lib; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_BUNDLE_IDENTIFIER = "org.imgui.example.imguiex.imguiex-osx"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; diff --git a/examples/directx9_example/imgui_impl_dx9.cpp b/examples/directx9_example/imgui_impl_dx9.cpp index 17f4f078c..813d58ef2 100644 --- a/examples/directx9_example/imgui_impl_dx9.cpp +++ b/examples/directx9_example/imgui_impl_dx9.cpp @@ -53,6 +53,15 @@ void ImGui_ImplDX9_RenderDrawLists(ImDrawData* draw_data) return; } + // Backup some DX9 state (not all!) + // FIXME: Backup/restore everything else + D3DRENDERSTATETYPE last_render_state_to_backup[] = { D3DRS_CULLMODE, D3DRS_LIGHTING, D3DRS_ZENABLE, D3DRS_ALPHABLENDENABLE, D3DRS_ALPHATESTENABLE, D3DRS_BLENDOP, D3DRS_SRCBLEND, D3DRS_DESTBLEND, D3DRS_SCISSORTESTENABLE }; + DWORD last_render_state_values[ARRAYSIZE(last_render_state_to_backup)] = { 0 }; + IDirect3DPixelShader9* last_ps; g_pd3dDevice->GetPixelShader( &last_ps ); + IDirect3DVertexShader9* last_vs; g_pd3dDevice->GetVertexShader( &last_vs ); + for (int n = 0; n < ARRAYSIZE(last_render_state_to_backup); n++) + g_pd3dDevice->GetRenderState(last_render_state_to_backup[n], &last_render_state_values[n]); + // Copy and convert all vertices into a single contiguous buffer CUSTOMVERTEX* vtx_dst; ImDrawIdx* idx_dst; @@ -91,8 +100,8 @@ void ImGui_ImplDX9_RenderDrawLists(ImDrawData* draw_data) g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, false ); g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, false ); g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true ); - g_pd3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD ); g_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, false ); + g_pd3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD ); g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); g_pd3dDevice->SetRenderState( D3DRS_SCISSORTESTENABLE, true ); @@ -137,6 +146,12 @@ void ImGui_ImplDX9_RenderDrawLists(ImDrawData* draw_data) } vtx_offset += cmd_list->VtxBuffer.size(); } + + // Restore some modified DX9 state (not all!) + g_pd3dDevice->SetPixelShader( last_ps ); + g_pd3dDevice->SetVertexShader( last_vs ); + for (int n = 0; n < ARRAYSIZE(last_render_state_to_backup); n++) + g_pd3dDevice->SetRenderState(last_render_state_to_backup[n], last_render_state_values[n]); } IMGUI_API LRESULT ImGui_ImplDX9_WndProcHandler(HWND, UINT msg, WPARAM wParam, LPARAM lParam) diff --git a/examples/opengl_example/imgui_impl_glfw.cpp b/examples/opengl_example/imgui_impl_glfw.cpp index fd9ce7206..f6447e151 100644 --- a/examples/opengl_example/imgui_impl_glfw.cpp +++ b/examples/opengl_example/imgui_impl_glfw.cpp @@ -1,6 +1,10 @@ // ImGui GLFW binding with OpenGL // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. +// If your context is GL3/GL3 then prefer using the code in opengl3_example. +// You *might* use this code with a GL3/GL4 context but make sure you disable the programmable pipeline by calling "glUseProgram(0)" before ImGui::Render(). +// We cannot do that from GL2 code because the function doesn't exist. + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. diff --git a/examples/opengl_example/imgui_impl_glfw.h b/examples/opengl_example/imgui_impl_glfw.h index 09778224f..07dd96f04 100644 --- a/examples/opengl_example/imgui_impl_glfw.h +++ b/examples/opengl_example/imgui_impl_glfw.h @@ -1,6 +1,10 @@ // ImGui GLFW binding with OpenGL // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. +// If your context is GL3/GL3 then prefer using the code in opengl3_example. +// You *might* use this code with a GL3/GL4 context but make sure you disable the programmable pipeline by calling "glUseProgram(0)" before ImGui::Render(). +// We cannot do that from GL2 code because the function doesn't exist. + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 21cc8f2a7..94ef6594e 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -10,7 +10,7 @@ int main(int, char**) { // Setup SDL - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) != 0) { printf("Error: %s\n", SDL_GetError()); return -1; diff --git a/examples/sdl_opengl_example/main.cpp b/examples/sdl_opengl_example/main.cpp index e9574e2fc..f42e7faf4 100644 --- a/examples/sdl_opengl_example/main.cpp +++ b/examples/sdl_opengl_example/main.cpp @@ -10,7 +10,7 @@ int main(int, char**) { // Setup SDL - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) != 0) { printf("Error: %s\n", SDL_GetError()); return -1; diff --git a/imgui.cpp b/imgui.cpp index f608f72b7..621673706 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -152,8 +152,9 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2016/04/09 (1.49) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to ColorEdit*() functions + - 2016/04/xx (1.49) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to ColorEdit*() functions replaced ColorEdit4() third parameter 'bool show_alpha=true' to 'ImGuiColorEditFlags flags=0x01' where ImGuiColorEditFlags_Alpha=0x01 for dodgy compatibility + - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). @@ -536,9 +537,10 @@ - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation). - style/opt: PopStyleVar could be optimized by having GetStyleVar returns the type, using a table mapping stylevar enum to data type. - style: global scale setting. + - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle - text: simple markup language for color change? - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. - - font: helper to add glyph redirect/replacements (e.g. redirect alternate apostrophe unicode code points to ascii one, etc.) + - font: fix AddRemapChar() to work before font has been built. - log: LogButtons() options for specifying depth and/or hiding depth slider - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope) - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard) @@ -558,9 +560,9 @@ - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438) - style editor: color child window height expressed in multiple of line height. - remote: make a system like RemoteImGui first-class citizen/project (#75) -!- demo: custom render demo pushes a clipping rectangle past parent window bounds. expose ImGui::PushClipRect() from imgui_internal.h? - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. - - examples: directx9/directx11: save/restore device state more thoroughly. + - examples: directx9: save/restore device state more thoroughly. + - examples: window minimize, maximize (#583) - optimization: use another hash function than crc32, e.g. FNV1a - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)? - optimization: turn some the various stack vectors into statically-sized arrays @@ -633,7 +635,6 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window); static void ClearSetNextWindowData(); static void CheckStacksSize(ImGuiWindow* window, bool write); static void Scrollbar(ImGuiWindow* window, bool horizontal); -static bool CloseWindowButton(bool* p_opened); static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list); static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window); @@ -1575,6 +1576,7 @@ ImGuiWindow::ImGuiWindow(const char* name) MoveID = GetID("#MOVE"); Flags = 0; + IndexWithinParent = 0; PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); @@ -2305,7 +2307,7 @@ static int ChildWindowComparer(const void* lhs, const void* rhs) return d; if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox)) return d; - return 0; + return (a->IndexWithinParent - b->IndexWithinParent); } static void AddWindowToSortedBuffer(ImVector& out_sorted_windows, ImGuiWindow* window) @@ -2339,9 +2341,13 @@ static void AddDrawListToRenderList(ImVector& out_render_list, ImDr return; } + // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. + IM_ASSERT(draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); + IM_ASSERT(draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); + IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); + // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices) // If this assert triggers because you are drawing lots of stuff manually, A) workaround by calling BeginChild()/EndChild() to put your draw commands in multiple draw lists, B) #define ImDrawIdx to a 'unsigned int' in imconfig.h and render accordingly. - IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); // Sanity check. Bug or mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8))); // Too many vertices in same ImDrawList. See comment above. out_render_list.push_back(draw_list); @@ -2363,22 +2369,11 @@ static void AddWindowToRenderList(ImVector& out_render_list, ImGuiW } } -void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_existing_clip_rect) +void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) { ImGuiWindow* window = GetCurrentWindow(); - - ImRect cr(clip_rect_min, clip_rect_max); - if (intersect_with_existing_clip_rect) - { - // Clip our argument with the current clip rect - cr.Clip(window->ClipRect); - } - cr.Max.x = ImMax(cr.Min.x, cr.Max.x); - cr.Max.y = ImMax(cr.Min.y, cr.Max.y); - - IM_ASSERT(cr.Min.x <= cr.Max.x && cr.Min.y <= cr.Max.y); - window->ClipRect = cr; - window->DrawList->PushClipRect(ImVec4(cr.Min.x, cr.Min.y, cr.Max.x, cr.Max.y)); + window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); + window->ClipRect = window->DrawList->_ClipRectStack.back(); } void ImGui::PopClipRect() @@ -3372,7 +3367,7 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; const ImVec2 content_avail = ImGui::GetContentRegionAvail(); - ImVec2 size = ImRound(size_arg); + ImVec2 size = ImFloor(size_arg); if (size.x <= 0.0f) { if (size.x == 0.0f) @@ -3696,6 +3691,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ if (first_begin_of_the_frame) { window->Active = true; + window->IndexWithinParent = 0; window->BeginCount = 0; window->DrawList->Clear(); window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); @@ -3826,7 +3822,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ // Position child window if (flags & ImGuiWindowFlags_ChildWindow) + { + window->IndexWithinParent = parent_window->DC.ChildWindows.Size; parent_window->DC.ChildWindows.push_back(window); + } if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) { window->Pos = window->PosFloat = parent_window->DC.CursorPos; @@ -4007,6 +4006,8 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ { ImRect menu_bar_rect = window->MenuBarRect(); window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, 1|2); + if (flags & ImGuiWindowFlags_ShowBorders) + window->DrawList->AddLine(menu_bar_rect.GetBL()-ImVec2(0,0), menu_bar_rect.GetBR()-ImVec2(0,0), GetColorU32(ImGuiCol_Border)); } // Scrollbars @@ -4076,7 +4077,12 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ if (!(flags & ImGuiWindowFlags_NoTitleBar)) { if (p_opened != NULL) - CloseWindowButton(p_opened); + { + const float pad = 2.0f; + const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f; + if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad)) + *p_opened = false; + } const ImVec2 text_size = CalcTextSize(name, NULL, true); if (!(flags & ImGuiWindowFlags_NoCollapse)) @@ -4113,9 +4119,9 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ const ImRect title_bar_rect = window->TitleBarRect(); const float border_size = window->BorderSize; ImRect clip_rect; - clip_rect.Min.x = title_bar_rect.Min.x + 0.5f + ImMax(border_size, window->WindowPadding.x*0.5f); - clip_rect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + 0.5f + border_size; - clip_rect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, window->WindowPadding.x*0.5f); + clip_rect.Min.x = title_bar_rect.Min.x + ImMax(border_size, (float)(int)(window->WindowPadding.x*0.5f)); + clip_rect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + border_size; + clip_rect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, (float)(int)(window->WindowPadding.x*0.5f)); clip_rect.Max.y = window->Pos.y + window->Size.y - border_size - window->ScrollbarSizes.y; PushClipRect(clip_rect.Min, clip_rect.Max, true); @@ -4188,14 +4194,14 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); if (!horizontal) - bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() - border_size : 0.0f); + bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; int window_rounding_corners; if (horizontal) window_rounding_corners = 8 | (other_scrollbar ? 0 : 4); else - window_rounding_corners = ((window->Flags & ImGuiWindowFlags_NoTitleBar) ? 2 : 0) | (other_scrollbar ? 0 : 4); + window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? 2 : 0) | (other_scrollbar ? 0 : 4); window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners); bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); @@ -5383,13 +5389,11 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) } // Upper-right button to close a window. -static bool CloseWindowButton(bool* p_opened) +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) { ImGuiWindow* window = ImGui::GetCurrentWindow(); - const ImGuiID id = window->GetID("#CLOSE"); - const float size = window->TitleBarHeight() - 4.0f; - const ImRect bb(window->Rect().GetTR() + ImVec2(-2.0f-size,2.0f), window->Rect().GetTR() + ImVec2(-2.0f,2.0f+size)); + const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); bool hovered, held; bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held); @@ -5397,18 +5401,15 @@ static bool CloseWindowButton(bool* p_opened) // Render const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); const ImVec2 center = bb.GetCenter(); - window->DrawList->AddCircleFilled(center, ImMax(2.0f,size*0.5f), col, 16); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12); - const float cross_extent = (size * 0.5f * 0.7071f) - 1.0f; + const float cross_extent = (radius * 0.7071f) - 1.0f; if (hovered) { window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), ImGui::GetColorU32(ImGuiCol_Text)); window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), ImGui::GetColorU32(ImGuiCol_Text)); } - if (p_opened != NULL && pressed) - *p_opened = false; - return pressed; } @@ -5716,65 +5717,6 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display return opened; } -void ImGui::Bullet() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiState& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); - ItemSize(bb); - if (!ItemAdd(bb, NULL)) - { - ImGui::SameLine(0, style.FramePadding.x*2); - return; - } - - // Render - const float bullet_size = g.FontSize*0.15f; - window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text)); - - // Stay on same line - ImGui::SameLine(0, style.FramePadding.x*2); -} - -// Text with a little bullet aligned to the typical tree node. -void ImGui::BulletTextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiState& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const char* text_begin = g.TempBuffer; - const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - const ImVec2 label_size = CalcTextSize(text_begin, text_end, true); - const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it - const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding - ItemSize(bb); - if (!ItemAdd(bb, NULL)) - return; - - // Render - const float bullet_size = g.FontSize*0.15f; - window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text)); - RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end); -} - -void ImGui::BulletText(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - BulletTextV(fmt, args); - va_end(args); -} - // If returning 'true' the node is open and the user is responsible for calling TreePop bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) { @@ -5896,6 +5838,65 @@ ImGuiID ImGui::GetID(const void* ptr_id) return GImGui->CurrentWindow->GetID(ptr_id); } +void ImGui::Bullet() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiState& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + { + ImGui::SameLine(0, style.FramePadding.x*2); + return; + } + + // Render + const float bullet_size = g.FontSize*0.15f; + window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text)); + + // Stay on same line + ImGui::SameLine(0, style.FramePadding.x*2); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiState& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const char* text_begin = g.TempBuffer; + const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + const ImVec2 label_size = CalcTextSize(text_begin, text_end, true); + const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it + const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + // Render + const float bullet_size = g.FontSize*0.15f; + window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text)); + RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end); +} + +void ImGui::BulletText(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + BulletTextV(fmt, args); + va_end(args); +} + static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size) { if (data_type == ImGuiDataType_Int) @@ -7792,10 +7793,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf, buf+edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); // Draw blinking cursor - ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; - if (cursor_is_visible) - draw_window->DrawList->AddLine(cursor_screen_pos + ImVec2(0.0f,-g.FontSize+0.5f), cursor_screen_pos + ImVec2(0.0f,-1.5f), GetColorU32(ImGuiCol_Text)); + ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x, cursor_screen_pos.y-1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.Max, GetColorU32(ImGuiCol_Text)); // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) if (is_editable) @@ -8421,7 +8423,7 @@ bool ImGui::BeginMenuBar() ImGui::BeginGroup(); // Save position ImGui::PushID("##menubar"); ImRect rect = window->MenuBarRect(); - PushClipRect(ImVec2(rect.Min.x+0.5f, rect.Min.y-0.5f+window->BorderSize), ImVec2(rect.Max.x+0.5f, rect.Max.y-0.5f), false); + PushClipRect(ImVec2(rect.Min.x, rect.Min.y + window->BorderSize), ImVec2(rect.Max.x, rect.Max.y), false); window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y); window->DC.LayoutType = ImGuiLayoutType_Horizontal; window->DC.MenuBarAppending = true; @@ -8970,6 +8972,7 @@ bool ImGui::IsRectVisible(const ImVec2& size) return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); } +// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) void ImGui::BeginGroup() { ImGuiWindow* window = GetCurrentWindow(); @@ -9213,7 +9216,6 @@ void ImGui::Columns(int columns_count, const char* id, bool border) { if (g.ActiveIdIsJustActivated) g.ActiveClickDeltaToCenter.x = x - g.IO.MousePos.x; - x = GetDraggedColumnOffset(i); SetColumnOffset(i, x); } @@ -9511,8 +9513,8 @@ void ImGui::ShowMetricsWindow(bool* opened) ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); - clip_rect.Round(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, ImColor(255,255,0)); - vtxs_rect.Round(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, ImColor(255,0,255)); + clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, ImColor(255,255,0)); + vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, ImColor(255,0,255)); } if (!draw_opened) continue; diff --git a/imgui.h b/imgui.h index 364182442..80e678378 100644 --- a/imgui.h +++ b/imgui.h @@ -187,14 +187,14 @@ namespace ImGui IMGUI_API void PopButtonRepeat(); // Cursor / Layout - IMGUI_API void BeginGroup(); // lock horizontal starting position. once closing a group it is seen as a single item (so you can use IsItemHovered() on a group, SameLine() between groups, etc. - IMGUI_API void EndGroup(); IMGUI_API void Separator(); // horizontal line IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally IMGUI_API void Spacing(); // add spacing IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size IMGUI_API void Indent(); // move content position toward the right by style.IndentSpacing pixels IMGUI_API void Unindent(); // move content position back to the left (cancel Indent) + IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) + IMGUI_API void EndGroup(); IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position IMGUI_API float GetCursorPosX(); // " IMGUI_API float GetCursorPosY(); // " @@ -369,6 +369,10 @@ namespace ImGui IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard IMGUI_API void LogText(const char* fmt, ...) IM_PRINTFARGS(1); // pass text data straight to log (without being displayed) + // Clipping + IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); + IMGUI_API void PopClipRect(); + // Utilities IMGUI_API bool IsItemHovered(); // was the last item hovered by mouse? IMGUI_API bool IsItemHoveredRect(); // was the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this @@ -712,7 +716,7 @@ struct ImGuiIO ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize - // Advanced/subtle behaviors + // Advanced/subtle behaviors bool WordMovementUsesAltKey; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl bool ShortcutsUseSuperKey; // = defined(__APPLE__) // OS X style: Shortcuts using Cmd/Super instead of Ctrl bool DoubleClickSelectsWord; // = defined(__APPLE__) // OS X style: Double click selects by word instead of selecting whole text @@ -885,8 +889,8 @@ struct ImGuiTextFilter const char* end() const { return e; } bool empty() const { return b == e; } char front() const { return *b; } - static bool isblank(char c) { return c == ' ' || c == '\t'; } - void trim_blanks() { while (b < e && isblank(*b)) b++; while (e > b && isblank(*(e-1))) e--; } + static bool is_blank(char c) { return c == ' ' || c == '\t'; } + void trim_blanks() { while (b < e && is_blank(*b)) b++; while (e > b && is_blank(*(e-1))) e--; } IMGUI_API void split(char separator, ImVector& out); }; @@ -1133,7 +1137,7 @@ struct ImDrawList ImDrawList() { _OwnerName = NULL; Clear(); } ~ImDrawList() { ClearFreeMemory(); } - IMGUI_API void PushClipRect(const ImVec4& clip_rect); // Scissoring. Note that the values are (x1,y1,x2,y2) and NOT (x1,y1,w,h). This is passed down to your render function but not used for CPU-side clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) + IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) IMGUI_API void PushClipRectFullScreen(); IMGUI_API void PopClipRect(); IMGUI_API void PushTextureID(const ImTextureID& texture_id); @@ -1144,6 +1148,8 @@ struct ImDrawList IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners = 0x0F, float thickness = 1.0f); // a: upper-left, b: lower-right IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners = 0x0F); // a: upper-left, b: lower-right IMGUI_API void AddRectFilledMultiColor(const ImVec2& a, const ImVec2& b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); + IMGUI_API void AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col); IMGUI_API void AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness = 1.0f); IMGUI_API void AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); IMGUI_API void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12, float thickness = 1.0f); @@ -1185,9 +1191,9 @@ struct ImDrawList IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); // Axis aligned rectangle (composed of two triangles) IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); IMGUI_API void PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col); - inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col){ _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; } inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } + inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } IMGUI_API void UpdateClipRect(); IMGUI_API void UpdateTextureID(); }; @@ -1289,15 +1295,6 @@ struct ImFontAtlas // ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). struct ImFont { - // Members: Settings - float FontSize; // // Height of characters, set during loading (don't change after loading) - float Scale; // = 1.0f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() - ImVec2 DisplayOffset; // = (0.0f,1.0f) // Offset font rendering by xx pixels - ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() - ImFontConfig* ConfigData; // // Pointer within ImFontAtlas->ConfigData - int ConfigDataCount; // - - // Members: Runtime data struct Glyph { ImWchar Codepoint; @@ -1305,29 +1302,44 @@ struct ImFont float X0, Y0, X1, Y1; float U0, V0, U1, V1; // Texture coordinates }; - float Ascent, Descent; // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] - ImFontAtlas* ContainerAtlas; // What we has been loaded into - ImVector Glyphs; + + // Members: Hot ~62/78 bytes + float FontSize; // // Height of characters, set during loading (don't change after loading) + float Scale; // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() + ImVec2 DisplayOffset; // = (0.f,1.f) // Offset font rendering by xx pixels + ImVector Glyphs; // // All glyphs. + ImVector IndexXAdvance; // // Sparse. Glyphs->XAdvance in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). + ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) - float FallbackXAdvance; // - ImVector IndexXAdvance; // Sparse. Glyphs->XAdvance directly indexable (more cache-friendly that reading from Glyphs, for CalcTextSize functions which are often bottleneck in large UI) - ImVector IndexLookup; // Sparse. Index glyphs by Unicode code-point. + float FallbackXAdvance; // == FallbackGlyph->XAdvance + ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() + + // Members: Cold ~18/26 bytes + short ConfigDataCount; // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. + ImFontConfig* ConfigData; // // Pointer within ContainerAtlas->ConfigData + ImFontAtlas* ContainerAtlas; // // What we has been loaded into + float Ascent, Descent; // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] // Methods IMGUI_API ImFont(); IMGUI_API ~ImFont(); IMGUI_API void Clear(); IMGUI_API void BuildLookupTable(); - IMGUI_API const Glyph* FindGlyph(unsigned short c) const; + IMGUI_API const Glyph* FindGlyph(ImWchar c) const; IMGUI_API void SetFallbackChar(ImWchar c); - float GetCharAdvance(unsigned short c) const { return ((int)c < IndexXAdvance.Size) ? IndexXAdvance[(int)c] : FallbackXAdvance; } - bool IsLoaded() const { return ContainerAtlas != NULL; } + float GetCharAdvance(ImWchar c) const { return ((int)c < IndexXAdvance.Size) ? IndexXAdvance[(int)c] : FallbackXAdvance; } + bool IsLoaded() const { return ContainerAtlas != NULL; } // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; - IMGUI_API void RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, ImDrawList* draw_list, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; + IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const; + IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; + + // Private + IMGUI_API void GrowIndex(int new_size); + IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. }; //---- Include imgui_user.h at the end of imgui.h diff --git a/imgui_demo.cpp b/imgui_demo.cpp index fdf2d8ea3..036364479 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -268,7 +268,7 @@ void ImGui::ShowTestWindow(bool* p_opened) if (ImGui::CollapsingHeader("Widgets")) { - if (ImGui::TreeNode("Tree")) + if (ImGui::TreeNode("Trees")) { for (int i = 0; i < 5; i++) { @@ -412,7 +412,7 @@ void ImGui::ShowTestWindow(bool* p_opened) for (int i = 0; i < 16; i++) { ImGui::PushID(i); - if (ImGui::Selectable("Me", &selected[i], 0, ImVec2(50,50))) + if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) { int x = i % 4, y = i / 4; if (x > 0) selected[i - 1] ^= 1; @@ -1862,7 +1862,7 @@ static void ShowExampleAppCustomRendering(bool* opened) points.pop_back(); } } - draw_list->PushClipRect(ImVec4(canvas_pos.x, canvas_pos.y, canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y)); // clip lines within the canvas (if we resize it, etc.) + draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y)); // clip lines within the canvas (if we resize it, etc.) for (int i = 0; i < points.Size - 1; i += 2) draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i+1].x, canvas_pos.y + points[i+1].y), 0xFF00FFFF, 2.0f); draw_list->PopClipRect(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 1d268561b..e2eea92c8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -214,20 +214,33 @@ void ImDrawList::UpdateTextureID() #undef GetCurrentClipRect #undef GetCurrentTextureId -// Scissoring. The values in clip_rect are x1, y1, x2, y2. Only apply to rendering! Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) -void ImDrawList::PushClipRect(const ImVec4& clip_rect) +// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) +void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) { - _ClipRectStack.push_back(clip_rect); + ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); + if (intersect_with_current_clip_rect && _ClipRectStack.Size) + { + ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size-1]; + if (cr.x < current.x) cr.x = current.x; + if (cr.y < current.y) cr.y = current.y; + if (cr.z > current.z) cr.z = current.z; + if (cr.w > current.w) cr.w = current.w; + } + cr.z = ImMax(cr.x, cr.z); + cr.w = ImMax(cr.y, cr.w); + cr.x = (float)(int)(cr.x + 0.5f); // Round (expecting to round down). Ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. + cr.y = (float)(int)(cr.y + 0.5f); + cr.z = (float)(int)(cr.z + 0.5f); + cr.w = (float)(int)(cr.w + 0.5f); + + _ClipRectStack.push_back(cr); UpdateClipRect(); } void ImDrawList::PushClipRectFullScreen() { - PushClipRect(GNullClipRect); - - // FIXME-OPT: This would be more correct but we're not supposed to access ImGuiState from here? - //ImGuiState& g = *GImGui; - //PushClipRect(GetVisibleRect()); + PushClipRect(ImVec2(GNullClipRect.x, GNullClipRect.y), ImVec2(GNullClipRect.z, GNullClipRect.w)); + //PushClipRect(GetVisibleRect()); // FIXME-OPT: This would be more correct but we're not supposed to access ImGuiState from here? } void ImDrawList::PopClipRect() @@ -823,6 +836,30 @@ void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& c, ImU32 PrimWriteVtx(ImVec2(a.x, c.y), uv, col_bot_left); } +void ImDrawList::AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness) +{ + if ((col >> 24) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathLineTo(d); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col) +{ + if ((col >> 24) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathLineTo(d); + PathFill(col); +} + void ImDrawList::AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness) { if ((col >> 24) == 0) @@ -894,14 +931,6 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. - // reserve vertices for worse case (over-reserving is useful and easily amortized) - const int char_count = (int)(text_end - text_begin); - const int vtx_count_max = char_count * 4; - const int idx_count_max = char_count * 6; - const int vtx_begin = VtxBuffer.Size; - const int idx_begin = IdxBuffer.Size; - PrimReserve(idx_count_max, vtx_count_max); - ImVec4 clip_rect = _ClipRectStack.back(); if (cpu_fine_clip_rect) { @@ -910,18 +939,7 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); } - font->RenderText(font_size, pos, col, clip_rect, text_begin, text_end, this, wrap_width, cpu_fine_clip_rect != NULL); - - // give back unused vertices - // FIXME-OPT: clean this up - VtxBuffer.resize((int)(_VtxWritePtr - VtxBuffer.Data)); - IdxBuffer.resize((int)(_IdxWritePtr - IdxBuffer.Data)); - int vtx_unused = vtx_count_max - (VtxBuffer.Size - vtx_begin); - int idx_unused = idx_count_max - (IdxBuffer.Size - idx_begin); - CmdBuffer.back().ElemCount -= idx_unused; - _VtxWritePtr -= vtx_unused; - _IdxWritePtr -= idx_unused; - _VtxCurrentIdx = (unsigned int)VtxBuffer.Size; + font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); } void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) @@ -1204,7 +1222,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_d ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); IM_ASSERT(font_cfg.FontData == NULL); font_cfg.FontDataOwnedByAtlas = true; - return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, font_cfg_template, glyph_ranges); + return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges); } ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) @@ -1670,20 +1688,15 @@ void ImFont::BuildLookupTable() for (int i = 0; i != Glyphs.Size; i++) max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); + IM_ASSERT(Glyphs.Size < 32*1024); IndexXAdvance.clear(); - IndexXAdvance.resize(max_codepoint + 1); IndexLookup.clear(); - IndexLookup.resize(max_codepoint + 1); - for (int i = 0; i < max_codepoint + 1; i++) - { - IndexXAdvance[i] = -1.0f; - IndexLookup[i] = -1; - } + GrowIndex(max_codepoint + 1); for (int i = 0; i < Glyphs.Size; i++) { int codepoint = (int)Glyphs[i].Codepoint; IndexXAdvance[codepoint] = Glyphs[i].XAdvance; - IndexLookup[codepoint] = i; + IndexLookup[codepoint] = (short)i; } // Create a glyph to handle TAB @@ -1697,7 +1710,7 @@ void ImFont::BuildLookupTable() tab_glyph.Codepoint = '\t'; tab_glyph.XAdvance *= 4; IndexXAdvance[(int)tab_glyph.Codepoint] = (float)tab_glyph.XAdvance; - IndexLookup[(int)tab_glyph.Codepoint] = (int)(Glyphs.Size-1); + IndexLookup[(int)tab_glyph.Codepoint] = (short)(Glyphs.Size-1); } FallbackGlyph = NULL; @@ -1714,13 +1727,43 @@ void ImFont::SetFallbackChar(ImWchar c) BuildLookupTable(); } +void ImFont::GrowIndex(int new_size) +{ + IM_ASSERT(IndexXAdvance.Size == IndexLookup.Size); + int old_size = IndexLookup.Size; + if (new_size <= old_size) + return; + IndexXAdvance.resize(new_size); + IndexLookup.resize(new_size); + for (int i = old_size; i < new_size; i++) + { + IndexXAdvance[i] = -1.0f; + IndexLookup[i] = (short)-1; + } +} + +void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) +{ + IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. + int index_size = IndexLookup.Size; + + if (dst < index_size && IndexLookup.Data[dst] == -1 && !overwrite_dst) // 'dst' already exists + return; + if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op + return; + + GrowIndex(dst + 1); + IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : -1; + IndexXAdvance[dst] = (src < index_size) ? IndexXAdvance.Data[src] : 1.0f; +} + const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const { if (c < IndexLookup.Size) { - const int i = IndexLookup[c]; + const short i = IndexLookup[c]; if (i != -1) - return &Glyphs[i]; + return &Glyphs.Data[i]; } return FallbackGlyph; } @@ -1916,7 +1959,23 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons return text_size; } -void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, ImDrawList* draw_list, float wrap_width, bool cpu_fine_clip) const +void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const +{ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. + return; + if (const Glyph* glyph = FindGlyph(c)) + { + float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; + pos.x = (float)(int)pos.x + DisplayOffset.x; + pos.y = (float)(int)pos.y + DisplayOffset.y; + ImVec2 pos_tl(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale); + ImVec2 pos_br(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale); + draw_list->PrimReserve(6, 4); + draw_list->PrimRectUV(pos_tl, pos_br, ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); + } +} + +void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const { if (!text_end) text_end = text_begin + strlen(text_begin); @@ -1934,14 +1993,22 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re const bool word_wrap_enabled = (wrap_width > 0.0f); const char* word_wrap_eol = NULL; - ImDrawVert* vtx_write = draw_list->_VtxWritePtr; - ImDrawIdx* idx_write = draw_list->_IdxWritePtr; - unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; - + // Skip non-visible lines const char* s = text_begin; if (!word_wrap_enabled && y + line_height < clip_rect.y) while (s < text_end && *s != '\n') // Fast-forward to next line s++; + + // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) + const int vtx_count_max = (int)(text_end - s) * 4; + const int idx_count_max = (int)(text_end - s) * 6; + const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max; + draw_list->PrimReserve(idx_count_max, vtx_count_max); + + ImDrawVert* vtx_write = draw_list->_VtxWritePtr; + ImDrawIdx* idx_write = draw_list->_IdxWritePtr; + unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; + while (s < text_end) { if (word_wrap_enabled) @@ -2010,11 +2077,10 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re if (c != ' ' && c != '\t') { // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w - float y1 = (float)(y + glyph->Y0 * scale); - float y2 = (float)(y + glyph->Y1 * scale); - - float x1 = (float)(x + glyph->X0 * scale); - float x2 = (float)(x + glyph->X1 * scale); + float x1 = x + glyph->X0 * scale; + float x2 = x + glyph->X1 * scale; + float y1 = y + glyph->Y0 * scale; + float y2 = y + glyph->Y1 * scale; if (x1 <= clip_rect.z && x2 >= clip_rect.x) { // Render a character @@ -2073,9 +2139,13 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re x += char_width; } + // Give back unused vertices + draw_list->VtxBuffer.resize((int)(vtx_write - draw_list->VtxBuffer.Data)); + draw_list->IdxBuffer.resize((int)(idx_write - draw_list->IdxBuffer.Data)); + draw_list->CmdBuffer[draw_list->CmdBuffer.Size-1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size); draw_list->_VtxWritePtr = vtx_write; - draw_list->_VtxCurrentIdx = vtx_current_idx; draw_list->_IdxWritePtr = idx_write; + draw_list->_VtxCurrentIdx = (unsigned int)draw_list->VtxBuffer.Size; } //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index 65d766c8d..b0acf420b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -136,7 +136,7 @@ static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } -static inline ImVec2 ImRound(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } +static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. @@ -232,7 +232,7 @@ struct IMGUI_API ImRect void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; } - void Round() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } + void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const { if (!on_edge && Contains(p)) @@ -593,8 +593,9 @@ struct IMGUI_API ImGuiDrawContext struct IMGUI_API ImGuiWindow { char* Name; - ImGuiID ID; - ImGuiWindowFlags Flags; + ImGuiID ID; // == ImHash(Name) + ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + int IndexWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. ImVec2 PosFloat; ImVec2 Pos; // Position rounded-up to nearest pixel ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) @@ -635,8 +636,8 @@ struct IMGUI_API ImGuiWindow ImGuiStorage StateStorage; float FontWindowScale; // Scale multiplier per-window ImDrawList* DrawList; - ImGuiWindow* RootWindow; - ImGuiWindow* RootNonPopupWindow; + ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. + ImGuiWindow* RootNonPopupWindow; // If we are a child widnow, this is pointing to the first non-child non-popup parent window. Else point to ourself. // Focus int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() @@ -709,11 +710,9 @@ namespace ImGui IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. - IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_existing_clip_rect = true); - IMGUI_API void PopClipRect(); - IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); + IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0); IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power);