From 9ac94ff001faa789718abe815a96af5ab4c09a77 Mon Sep 17 00:00:00 2001 From: PanForPancakes <87640847+PanForPancakes@users.noreply.github.com> Date: Sun, 19 Mar 2023 00:56:25 +0400 Subject: [PATCH 01/10] ColorPicker: Fixed shading of S/V triangle in Hue Wheel mode. (#6254, #5200) Amend f6460970 --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fb2eb7279..507cfe87b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,7 @@ Other changes: be desirable to take advantage of this trick. (#4714) - Drag, Sliders: Fixed parsing of text input when '+' or '#' format flags are used in the format string. (#6259) [@idbrii] +- ColorEdit: Fixed shading of S/V triangle in Hue Wheel mode. (#5200, #6254) [@jamesthomasgriffin] - IO: Fixed support for calling io.AddXXXX functions fron inactive context (wrongly advertised as supported in 1.89.4). (#6199, #6256, #5856) [@cfillion] - Backends: OpenGL3: Fixed GL loader crash when GL_VERSION returns NULL. (#6154, #4445, #3530) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 97cf4df89..cbca21748 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5624,13 +5624,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); ImVec2 uv_white = GetFontTexUvWhitePixel(); - draw_list->PrimReserve(6, 6); + draw_list->PrimReserve(3, 3); draw_list->PrimVtx(tra, uv_white, hue_color32); - draw_list->PrimVtx(trb, uv_white, hue_color32); - draw_list->PrimVtx(trc, uv_white, col_white); - draw_list->PrimVtx(tra, uv_white, 0); draw_list->PrimVtx(trb, uv_white, col_black); - draw_list->PrimVtx(trc, uv_white, 0); + draw_list->PrimVtx(trc, uv_white, col_white); draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f); sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); } From c98bad042daae0f30e72a93f428eaff1214bab5b Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Mar 2023 17:38:32 +0100 Subject: [PATCH 02/10] ColorEdit, RadioButton, Windows: Using adaptative tesselation for preview circles. --- docs/CHANGELOG.txt | 2 ++ docs/FAQ.md | 2 +- imgui_draw.cpp | 1 + imgui_widgets.cpp | 22 ++++++++++++---------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 507cfe87b..3b93af202 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,8 @@ Other changes: - Drag, Sliders: Fixed parsing of text input when '+' or '#' format flags are used in the format string. (#6259) [@idbrii] - ColorEdit: Fixed shading of S/V triangle in Hue Wheel mode. (#5200, #6254) [@jamesthomasgriffin] +- Rendering: Using adaptative tesselation for: RadioButton, ColorEdit preview circles, + Windows Close and Collapse Buttons. - IO: Fixed support for calling io.AddXXXX functions fron inactive context (wrongly advertised as supported in 1.89.4). (#6199, #6256, #5856) [@cfillion] - Backends: OpenGL3: Fixed GL loader crash when GL_VERSION returns NULL. (#6154, #4445, #3530) diff --git a/docs/FAQ.md b/docs/FAQ.md index 82348b384..5b4f217aa 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -480,7 +480,7 @@ ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImVec2 p = ImGui::GetCursorScreenPos(); // Draw a red circle -draw_list->AddCircleFilled(ImVec2(p.x + 50, p.y + 50), 30.0f, IM_COL32(255, 0, 0, 255), 16); +draw_list->AddCircleFilled(ImVec2(p.x + 50, p.y + 50), 30.0f, IM_COL32(255, 0, 0, 255)); // Draw a 3 pixel thick yellow line draw_list->AddLine(ImVec2(p.x, p.y), ImVec2(p.x + 100.0f, p.y + 100.0f), IM_COL32(255, 255, 0, 255), 3.0f); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 8bfe7a5aa..5bf034604 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3790,6 +3790,7 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) { + // FIXME-OPT: This should be baked in font. draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cbca21748..eb9c590d1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -827,7 +827,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); ImVec2 center = bb.GetCenter(); if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col, 12); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col); float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; ImU32 cross_col = GetColorU32(ImGuiCol_Text); @@ -852,7 +852,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter()/*+ ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, bg_col, 12); + window->DrawList->AddCircleFilled(bb.GetCenter()/*+ ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, bg_col); RenderArrow(window->DrawList, bb.Min + g.Style.FramePadding, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold @@ -1233,17 +1233,18 @@ bool ImGui::RadioButton(const char* label, bool active) MarkItemEdited(id); RenderNavHighlight(total_bb, id); - window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); + const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius); + window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment); if (active) { const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); - window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16); + window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark)); } if (style.FrameBorderSize > 0.0f) { - window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); - window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); + window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize); + window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize); } ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); @@ -5614,7 +5615,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl float sin_hue_angle = ImSin(H * 2.0f * IM_PI); ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f); float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; - int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); + int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others. draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments); @@ -5650,9 +5651,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; - draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, 12); + int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others. + draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments); // Render alpha bar if (alpha_bar) From 89d09070e3fd0b5ea73a12f3592c3bbffe15ac61 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 22 Mar 2023 15:42:14 +0100 Subject: [PATCH 03/10] Nav: Made Ctrl+Tab/Ctrl+Shift+Tab windowing register ownership to held modifier. (#4828, #3255, #5641) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 20 ++++++++++++++++++-- imgui.h | 2 +- imgui_internal.h | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3b93af202..fa8f50b2b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Other changes: be desirable to take advantage of this trick. (#4714) - Drag, Sliders: Fixed parsing of text input when '+' or '#' format flags are used in the format string. (#6259) [@idbrii] +- Nav: Made Ctrl+Tab/Ctrl+Shift+Tab windowing register ownership to held modifier so + it doesn't interfere with other code when remapping those actions. (#4828, #3255, #5641) - ColorEdit: Fixed shading of S/V triangle in Hue Wheel mode. (#5200, #6254) [@jamesthomasgriffin] - Rendering: Using adaptative tesselation for: RadioButton, ColorEdit preview circles, Windows Close and Collapse Buttons. diff --git a/imgui.cpp b/imgui.cpp index 43d2d9dac..6b214909f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8862,6 +8862,17 @@ void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease); } +// Rarely used helper +void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +{ + if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); } + if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); } + if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); } + if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); } + if (key_chord & ImGuiMod_Shortcut) { SetKeyOwner(ImGuiMod_Shortcut, owner_id, flags); } + if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); } +} + // This is more or less equivalent to: // if (IsItemHovered() || IsItemActive()) // SetKeyOwner(key, GetItemID()); @@ -11782,10 +11793,11 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL+Tab or Square+L/R window selection + const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing"); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); - const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); + const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); + const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, 0, ImGuiInputFlags_None); const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard! if (start_windowing_with_gamepad || start_windowing_with_keyboard) @@ -11796,6 +11808,10 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f); g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; + + // Register ownership of our mods. Using ImGuiInputFlags_RouteGlobalHigh in the Shortcut() calls instead would probably be correct but may have more side-effects. + if (keyboard_next_window || keyboard_prev_window) + SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id); } // Gamepad update diff --git a/imgui.h b/imgui.h index 6c05effc9..92f448df1 100644 --- a/imgui.h +++ b/imgui.h @@ -23,7 +23,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345') #define IMGUI_VERSION "1.89.5 WIP" -#define IMGUI_VERSION_NUM 18943 +#define IMGUI_VERSION_NUM 18944 #define IMGUI_HAS_TABLE /* diff --git a/imgui_internal.h b/imgui_internal.h index 714544f34..16a30fdb7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2933,6 +2933,7 @@ namespace ImGui // Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved. IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(ctx, key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } From 821814b45022f9fc0a82cc386b9321ef917c4061 Mon Sep 17 00:00:00 2001 From: AJ Weeks Date: Wed, 22 Mar 2023 20:27:17 +0100 Subject: [PATCH 04/10] InputText: Reworked prev/next-word behavior . Include period as delimiter and tweak prev/next words logic. (#6067) --- docs/CHANGELOG.txt | 3 +++ imgui.h | 2 +- imgui_widgets.cpp | 32 ++++++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fa8f50b2b..f1101936b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,9 @@ Other changes: retaining underlying data. While we don't really want to encourage user not retaining underlying data, in the absence of a "late commit" behavior/flag we understand it may be desirable to take advantage of this trick. (#4714) +- InputText: Reworked prev/next-word behavior to more closely match Visual Studio + text editor. Include '.' as a delimiter and alter varying subtle behavior with how + blanks and separators are treated when skipping words. (#6067) [@ajweeks] - Drag, Sliders: Fixed parsing of text input when '+' or '#' format flags are used in the format string. (#6259) [@idbrii] - Nav: Made Ctrl+Tab/Ctrl+Shift+Tab windowing register ownership to held modifier so diff --git a/imgui.h b/imgui.h index 92f448df1..72947b3da 100644 --- a/imgui.h +++ b/imgui.h @@ -23,7 +23,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345') #define IMGUI_VERSION "1.89.5 WIP" -#define IMGUI_VERSION_NUM 18944 +#define IMGUI_VERSION_NUM 18945 #define IMGUI_HAS_TABLE /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index eb9c590d1..b1e3fb41e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3700,10 +3700,34 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } -// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } -static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } -static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } +static bool is_separator(unsigned int c) +{ + return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r' || c=='.' || c=='!'; +} + +static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) +{ + // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. + if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) + return 0; + + bool prev_white = ImCharIsBlankW(obj->TextW[idx - 1]); + bool prev_separ = is_separator(obj->TextW[idx - 1]); + bool curr_white = ImCharIsBlankW(obj->TextW[idx]); + bool curr_separ = is_separator(obj->TextW[idx]); + return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); +} +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) +{ + if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) + return 0; + + bool prev_white = ImCharIsBlankW(obj->TextW[idx]); + bool prev_separ = is_separator(obj->TextW[idx]); + bool curr_white = ImCharIsBlankW(obj->TextW[idx - 1]); + bool curr_separ = is_separator(obj->TextW[idx - 1]); + return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); +} static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } From 7dcf129b3b2968250753a06e98c2d4b522a80fee Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 23 Mar 2023 13:27:29 +0100 Subject: [PATCH 05/10] Backends: OpenGL3: Properly restoring "no shader program bound". (#6267, #6220, #6224) --- backends/imgui_impl_opengl3.cpp | 3 ++- docs/CHANGELOG.txt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 7391bb088..1ddeeeaf7 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 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 NULL. (#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). @@ -579,7 +580,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Restore modified GL state // This "glIsProgram()" check is required because if the program is "pending deletion" at the time of binding backup, it will have been deleted by now and will cause an OpenGL error. See #6220. - if (glIsProgram(last_program)) glUseProgram(last_program); + if (last_program == 0 || glIsProgram(last_program)) glUseProgram(last_program); glBindTexture(GL_TEXTURE_2D, last_texture); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER if (bd->GlVersion >= 330) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f1101936b..45fe314c5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -57,6 +57,8 @@ Other changes: - IO: Fixed support for calling io.AddXXXX functions fron inactive context (wrongly advertised as supported in 1.89.4). (#6199, #6256, #5856) [@cfillion] - Backends: OpenGL3: Fixed GL loader crash when GL_VERSION returns NULL. (#6154, #4445, #3530) +- Backends: OpenGL3: Properly restoring "no shader program bound" if it was the case prior to + running the rendering function. (#6267, #6220, #6224) [@BrunoLevy] - Backends: GLFW: Fixed key modifiers handling on secondary viewports. (#6248, #6034) [@aiekick] - Examples: Windows: Added 'misc/debuggers/imgui.natstepfilter' file to all Visual Studio projects, now that VS 2022 17.6 Preview 2 support adding Debug Step Filter spec files into projects. From 8a6911b8943748d639a99d5b07116961a645546b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 23 Mar 2023 18:17:45 +0100 Subject: [PATCH 06/10] Backends: Fixes zealous warnings (#5760) --- backends/imgui_impl_opengl2.cpp | 11 +++++++++++ backends/imgui_impl_opengl3.cpp | 7 +++++-- backends/imgui_impl_sdl2.cpp | 10 ++++++++++ backends/imgui_impl_sdl3.cpp | 11 +++++++++++ backends/imgui_impl_sdlrenderer.cpp | 10 ++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index eb26307ec..e833cf38d 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -44,6 +44,13 @@ #include // intptr_t #endif +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#endif + // Include OpenGL header (without an OpenGL loader) requires a bit of fiddling #if defined(_WIN32) && !defined(APIENTRY) #define APIENTRY __stdcall // It is customary to use APIENTRY for OpenGL function pointer declarations on all platforms. Additionally, the Windows OpenGL header needs APIENTRY. @@ -285,3 +292,7 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects() { ImGui_ImplOpenGL2_DestroyFontsTexture(); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 1ddeeeaf7..7bf26e666 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -107,17 +107,20 @@ #include #endif -// Clang warnings with -Weverything +// 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 #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) #endif #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' -#pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types +#pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) #endif // GL includes diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 86be801cb..478c283d5 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -72,6 +72,12 @@ #include "imgui.h" #include "imgui_impl_sdl2.h" +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#endif + // SDL #include #include @@ -632,3 +638,7 @@ void ImGui_ImplSDL2_NewFrame() // Update game controllers (if enabled and available) ImGui_ImplSDL2_UpdateGamepads(); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 9362f7ed2..c9e6e953a 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -24,6 +24,12 @@ #include "imgui.h" #include "imgui_impl_sdl3.h" +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#endif + // SDL #include #include @@ -565,3 +571,8 @@ void ImGui_ImplSDL3_NewFrame() // Update game controllers (if enabled and available) ImGui_ImplSDL3_UpdateGamepads(); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index b786ebd28..998a53739 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -31,6 +31,12 @@ #include // intptr_t #endif +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + // SDL #include #if !SDL_VERSION_ATLEAST(2,0,17) @@ -248,3 +254,7 @@ void ImGui_ImplSDLRenderer_DestroyDeviceObjects() { ImGui_ImplSDLRenderer_DestroyFontsTexture(); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif From f65bcf481ab34cd07d3909aab1479f409fa79f2f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 23 Mar 2023 19:03:17 +0100 Subject: [PATCH 07/10] Docs: added details and link to wiki from imgui_stdlib to increase discoverability. --- misc/cpp/README.txt | 2 +- misc/cpp/imgui_stdlib.cpp | 3 +++ misc/cpp/imgui_stdlib.h | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/misc/cpp/README.txt b/misc/cpp/README.txt index 429159029..17f0a3cd3 100644 --- a/misc/cpp/README.txt +++ b/misc/cpp/README.txt @@ -9,5 +9,5 @@ imgui_scoped.h Try by merging: https://github.com/ocornut/imgui/pull/2197 Discuss at: https://github.com/ocornut/imgui/issues/2096 -See more C++ related extension on Wiki +See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness diff --git a/misc/cpp/imgui_stdlib.cpp b/misc/cpp/imgui_stdlib.cpp index dd6bd8a52..c9060e886 100644 --- a/misc/cpp/imgui_stdlib.cpp +++ b/misc/cpp/imgui_stdlib.cpp @@ -4,6 +4,9 @@ // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string +// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: +// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness + #include "imgui.h" #include "imgui_stdlib.h" diff --git a/misc/cpp/imgui_stdlib.h b/misc/cpp/imgui_stdlib.h index 61afc098e..6429b3466 100644 --- a/misc/cpp/imgui_stdlib.h +++ b/misc/cpp/imgui_stdlib.h @@ -4,6 +4,9 @@ // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string +// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: +// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness + #pragma once #include From a38e3c222f5af5fb5fbaea136d5f84d599ef1bfb Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 Mar 2023 12:42:51 +0200 Subject: [PATCH 08/10] Fixed ImVec2 operator[] violating aliasing rules causing issue with Intel C++ compiler. (#6272) Note that this is not BayesBug's exact intended solution, so issues would be my responsibility ;) Amended. --- docs/CHANGELOG.txt | 2 ++ imgui.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 45fe314c5..a079dba9a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,6 +54,8 @@ Other changes: - ColorEdit: Fixed shading of S/V triangle in Hue Wheel mode. (#5200, #6254) [@jamesthomasgriffin] - Rendering: Using adaptative tesselation for: RadioButton, ColorEdit preview circles, Windows Close and Collapse Buttons. +- Misc: Fixed ImVec2 operator[] violating aliasing rules causing issue with Intel C++ + compiler. (#6272) [@BayesBug] - IO: Fixed support for calling io.AddXXXX functions fron inactive context (wrongly advertised as supported in 1.89.4). (#6199, #6256, #5856) [@cfillion] - Backends: OpenGL3: Fixed GL loader crash when GL_VERSION returns NULL. (#6154, #4445, #3530) diff --git a/imgui.h b/imgui.h index 72947b3da..351826ffe 100644 --- a/imgui.h +++ b/imgui.h @@ -255,8 +255,8 @@ struct ImVec2 float x, y; constexpr ImVec2() : x(0.0f), y(0.0f) { } constexpr ImVec2(float _x, float _y) : x(_x), y(_y) { } - float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. - float& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((float*)(char*)this)[idx]; } // We very rarely use this [] operator, so the assert overhead is fine. + float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return ((const float*)(const char*)this)[idx]; } #ifdef IM_VEC2_CLASS_EXTRA IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif From 84fd0c7ff4779606a28b7e3eb2c0c44ef79edb53 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 Mar 2023 17:06:44 +0200 Subject: [PATCH 09/10] Inputs, IO: record MouseWheelRequestAxisSwap information. Apply in UpdateMouseWheel() before legacy ctrl+wheel. --- imgui.cpp | 19 ++++++++++--------- imgui.h | 3 ++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6b214909f..0c85aa48c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8436,6 +8436,13 @@ static void ImGui::UpdateMouseInputs() ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; + // Mouse Wheel swapping flag + // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead + // - We avoid doing it on OSX as it the OS input layer handles this already. + // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature. + // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source. + io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors; + // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) if (IsMousePosValid(&io.MousePos)) io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); @@ -8596,15 +8603,9 @@ void ImGui::UpdateMouseWheel() return; // Mouse wheel scrolling - // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead - // - We avoid doing it on OSX as it the OS input layer handles this already. - // - However this means when running on OSX over Emcripten, Shift+WheelY will incur two swappings (1 in OS, 1 here), cancelling the feature. - const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; - if (swap_axis) - { - wheel.x = wheel.y; - wheel.y = 0.0f; - } + // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs() + if (g.IO.MouseWheelRequestAxisSwap) + wheel = ImVec2(wheel.y, 0.0f); // Maintain a rough average of moving magnitude on both axises // FIXME: should by based on wall clock time rather than frame-counter diff --git a/imgui.h b/imgui.h index 351826ffe..a90a4dc95 100644 --- a/imgui.h +++ b/imgui.h @@ -23,7 +23,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345') #define IMGUI_VERSION "1.89.5 WIP" -#define IMGUI_VERSION_NUM 18945 +#define IMGUI_VERSION_NUM 18946 #define IMGUI_HAS_TABLE /* @@ -2054,6 +2054,7 @@ struct ImGuiIO bool MouseReleased[5]; // Mouse button went from Down to !Down bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. + bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) From 5f301914a0bbca018fcbab7065480e1ab2c38e23 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 Mar 2023 17:09:16 +0200 Subject: [PATCH 10/10] TabBar: Tab-bars with ImGuiTabBarFlags_FittingPolicyScroll can be scrolled with horizontal mouse-wheel (or Shift + WheelY). (#2702) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- imgui_internal.h | 1 + imgui_widgets.cpp | 25 ++++++++++++++++++++++--- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a079dba9a..4f041d3f2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,8 @@ Other changes: - Nav: Made Ctrl+Tab/Ctrl+Shift+Tab windowing register ownership to held modifier so it doesn't interfere with other code when remapping those actions. (#4828, #3255, #5641) - ColorEdit: Fixed shading of S/V triangle in Hue Wheel mode. (#5200, #6254) [@jamesthomasgriffin] +- TabBar: Tab-bars with ImGuiTabBarFlags_FittingPolicyScroll can be scrolled with + horizontal mouse-wheel (or Shift + WheelY). (#2702) - Rendering: Using adaptative tesselation for: RadioButton, ColorEdit preview circles, Windows Close and Collapse Buttons. - Misc: Fixed ImVec2 operator[] violating aliasing rules causing issue with Intel C++ diff --git a/imgui.cpp b/imgui.cpp index 0c85aa48c..dbab378e6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3867,7 +3867,7 @@ void ImGui::MarkItemEdited(ImGuiID id) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } -static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) +bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) { // An active popup disable hovering on other windows (apart from its own children) // FIXME-OPT: This could be cached/stored within the window. diff --git a/imgui_internal.h b/imgui_internal.h index 16a30fdb7..bc9dd2337 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2828,6 +2828,7 @@ namespace ImGui inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); + IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b1e3fb41e..eb28738dc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7618,6 +7618,12 @@ void ImGui::EndTabBar() g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); } +// Scrolling happens only in the central section (leading/trailing sections are not scrolling) +static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections) +{ + return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; +} + // This is called only once a frame before by the first call to ItemTab() // The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) @@ -7820,9 +7826,23 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->VisibleTabId = tab_bar->SelectedTabId; tab_bar->VisibleTabWasSubmitted = false; - // Update scrolling + // Apply request requests if (scroll_to_tab_id != 0) TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); + else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) + { + const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH; + const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX; + if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f) + { + const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f; + tab_bar->ScrollingTargetDistToVisibility = 0.0f; + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step); + } + SetKeyOwner(wheel_key, tab_bar->ID); + } + + // Update scrolling tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) @@ -7959,8 +7979,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui int order = TabBarGetTabOrder(tab_bar, tab); // Scrolling happens only in the central section (leading/trailing sections are not scrolling) - // FIXME: This is all confusing. - float scrollable_width = tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; + float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections); // We make all tabs positions all relative Sections[0].Width to make code simpler float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f);