|
@@ -274,7 +274,10 @@ void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
|
|
void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
|
|
void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
|
|
{
|
|
{
|
|
PushStyleColor(ImGuiCol_Text, col);
|
|
PushStyleColor(ImGuiCol_Text, col);
|
|
- TextV(fmt, args);
|
|
|
|
|
|
+ if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
|
|
|
|
+ TextEx(va_arg(args, const char*), NULL, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting
|
|
|
|
+ else
|
|
|
|
+ TextV(fmt, args);
|
|
PopStyleColor();
|
|
PopStyleColor();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -288,8 +291,12 @@ void ImGui::TextDisabled(const char* fmt, ...)
|
|
|
|
|
|
void ImGui::TextDisabledV(const char* fmt, va_list args)
|
|
void ImGui::TextDisabledV(const char* fmt, va_list args)
|
|
{
|
|
{
|
|
- PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
|
|
|
|
- TextV(fmt, args);
|
|
|
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
|
+ PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
|
|
|
|
+ if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
|
|
|
|
+ TextEx(va_arg(args, const char*), NULL, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting
|
|
|
|
+ else
|
|
|
|
+ TextV(fmt, args);
|
|
PopStyleColor();
|
|
PopStyleColor();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -303,11 +310,14 @@ void ImGui::TextWrapped(const char* fmt, ...)
|
|
|
|
|
|
void ImGui::TextWrappedV(const char* fmt, va_list args)
|
|
void ImGui::TextWrappedV(const char* fmt, va_list args)
|
|
{
|
|
{
|
|
- ImGuiWindow* window = GetCurrentWindow();
|
|
|
|
- bool need_backup = (window->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set
|
|
|
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
|
+ bool need_backup = (g.CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set
|
|
if (need_backup)
|
|
if (need_backup)
|
|
PushTextWrapPos(0.0f);
|
|
PushTextWrapPos(0.0f);
|
|
- TextV(fmt, args);
|
|
|
|
|
|
+ if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
|
|
|
|
+ TextEx(va_arg(args, const char*), NULL, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting
|
|
|
|
+ else
|
|
|
|
+ TextV(fmt, args);
|
|
if (need_backup)
|
|
if (need_backup)
|
|
PopTextWrapPos();
|
|
PopTextWrapPos();
|
|
}
|
|
}
|
|
@@ -1423,11 +1433,13 @@ static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs)
|
|
}
|
|
}
|
|
|
|
|
|
// Shrink excess width from a set of item, by removing width from the larger items first.
|
|
// Shrink excess width from a set of item, by removing width from the larger items first.
|
|
|
|
+// Set items Width to -1.0f to disable shrinking this item.
|
|
void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess)
|
|
void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess)
|
|
{
|
|
{
|
|
if (count == 1)
|
|
if (count == 1)
|
|
{
|
|
{
|
|
- items[0].Width = ImMax(items[0].Width - width_excess, 1.0f);
|
|
|
|
|
|
+ if (items[0].Width >= 0.0f)
|
|
|
|
+ items[0].Width = ImMax(items[0].Width - width_excess, 1.0f);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer);
|
|
ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer);
|
|
@@ -1436,7 +1448,9 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc
|
|
{
|
|
{
|
|
while (count_same_width < count && items[0].Width <= items[count_same_width].Width)
|
|
while (count_same_width < count && items[0].Width <= items[count_same_width].Width)
|
|
count_same_width++;
|
|
count_same_width++;
|
|
- float max_width_to_remove_per_item = (count_same_width < count) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f);
|
|
|
|
|
|
+ float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f);
|
|
|
|
+ if (max_width_to_remove_per_item <= 0.0f)
|
|
|
|
+ break;
|
|
float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item);
|
|
float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item);
|
|
for (int item_n = 0; item_n < count_same_width; item_n++)
|
|
for (int item_n = 0; item_n < count_same_width; item_n++)
|
|
items[item_n].Width -= width_to_remove_per_item;
|
|
items[item_n].Width -= width_to_remove_per_item;
|
|
@@ -3587,6 +3601,8 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im
|
|
#define STB_TEXTEDIT_K_REDO 0x20000B // keyboard input to perform redo
|
|
#define STB_TEXTEDIT_K_REDO 0x20000B // keyboard input to perform redo
|
|
#define STB_TEXTEDIT_K_WORDLEFT 0x20000C // keyboard input to move cursor left one word
|
|
#define STB_TEXTEDIT_K_WORDLEFT 0x20000C // keyboard input to move cursor left one word
|
|
#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word
|
|
#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word
|
|
|
|
+#define STB_TEXTEDIT_K_PGUP 0x20000E // keyboard input to move cursor up a page
|
|
|
|
+#define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page
|
|
#define STB_TEXTEDIT_K_SHIFT 0x400000
|
|
#define STB_TEXTEDIT_K_SHIFT 0x400000
|
|
|
|
|
|
#define STB_TEXTEDIT_IMPLEMENTATION
|
|
#define STB_TEXTEDIT_IMPLEMENTATION
|
|
@@ -3853,6 +3869,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
bool clear_active_id = false;
|
|
bool clear_active_id = false;
|
|
bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline);
|
|
bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline);
|
|
|
|
|
|
|
|
+ float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
|
|
|
|
+
|
|
const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start);
|
|
const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start);
|
|
const bool init_state = (init_make_active || user_scroll_active);
|
|
const bool init_state = (init_make_active || user_scroll_active);
|
|
if (init_state && g.ActiveId != id)
|
|
if (init_state && g.ActiveId != id)
|
|
@@ -3913,7 +3931,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);
|
|
g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);
|
|
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Home) | ((ImU64)1 << ImGuiKey_End);
|
|
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Home) | ((ImU64)1 << ImGuiKey_End);
|
|
if (is_multiline)
|
|
if (is_multiline)
|
|
- g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_PageUp) | ((ImU64)1 << ImGuiKey_PageDown); // FIXME-NAV: Page up/down actually not supported yet by widget, but claim them ahead.
|
|
|
|
|
|
+ g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_PageUp) | ((ImU64)1 << ImGuiKey_PageDown);
|
|
if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
|
|
if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
|
|
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Tab);
|
|
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Tab);
|
|
}
|
|
}
|
|
@@ -3955,7 +3973,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
ImFont* password_font = &g.InputTextPasswordFont;
|
|
ImFont* password_font = &g.InputTextPasswordFont;
|
|
password_font->FontSize = g.Font->FontSize;
|
|
password_font->FontSize = g.Font->FontSize;
|
|
password_font->Scale = g.Font->Scale;
|
|
password_font->Scale = g.Font->Scale;
|
|
- password_font->DisplayOffset = g.Font->DisplayOffset;
|
|
|
|
password_font->Ascent = g.Font->Ascent;
|
|
password_font->Ascent = g.Font->Ascent;
|
|
password_font->Descent = g.Font->Descent;
|
|
password_font->Descent = g.Font->Descent;
|
|
password_font->ContainerAtlas = g.Font->ContainerAtlas;
|
|
password_font->ContainerAtlas = g.Font->ContainerAtlas;
|
|
@@ -4053,6 +4070,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
IM_ASSERT(state != NULL);
|
|
IM_ASSERT(state != NULL);
|
|
IM_ASSERT(io.KeyMods == GetMergedKeyModFlags() && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); // We rarely do this check, but if anything let's do it here.
|
|
IM_ASSERT(io.KeyMods == GetMergedKeyModFlags() && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); // We rarely do this check, but if anything let's do it here.
|
|
|
|
|
|
|
|
+ const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1);
|
|
|
|
+ state->Stb.row_count_per_page = row_count_per_page;
|
|
|
|
+
|
|
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
|
|
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
|
|
const bool is_osx = io.ConfigMacOSXBehaviors;
|
|
const bool is_osx = io.ConfigMacOSXBehaviors;
|
|
const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiKeyModFlags_Super | ImGuiKeyModFlags_Shift));
|
|
const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiKeyModFlags_Super | ImGuiKeyModFlags_Shift));
|
|
@@ -4072,6 +4092,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
|
|
|
|
+ else if (IsKeyPressedMap(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
|
|
|
|
+ else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
|
|
else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
|
|
else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
|
|
@@ -4441,12 +4463,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
if (is_multiline)
|
|
if (is_multiline)
|
|
{
|
|
{
|
|
// Test if cursor is vertically visible
|
|
// Test if cursor is vertically visible
|
|
- float scroll_y = draw_window->Scroll.y;
|
|
|
|
- const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
|
|
|
|
if (cursor_offset.y - g.FontSize < scroll_y)
|
|
if (cursor_offset.y - g.FontSize < scroll_y)
|
|
scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
|
|
scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
|
|
else if (cursor_offset.y - inner_size.y >= scroll_y)
|
|
else if (cursor_offset.y - inner_size.y >= scroll_y)
|
|
scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
|
|
scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
|
|
|
|
+ const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
|
|
scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y);
|
|
scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y);
|
|
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
|
|
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
|
|
draw_window->Scroll.y = scroll_y;
|
|
draw_window->Scroll.y = scroll_y;
|
|
@@ -4692,7 +4713,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
|
|
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
|
|
}
|
|
}
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
- OpenPopupContextItem("context");
|
|
|
|
|
|
+ OpenPopupOnItemClick("context");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
|
|
else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
|
|
@@ -4717,7 +4738,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|
sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
|
|
sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
|
|
}
|
|
}
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
- OpenPopupContextItem("context");
|
|
|
|
|
|
+ OpenPopupOnItemClick("context");
|
|
}
|
|
}
|
|
|
|
|
|
ImGuiWindow* picker_active_window = NULL;
|
|
ImGuiWindow* picker_active_window = NULL;
|
|
@@ -4738,7 +4759,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
- OpenPopupContextItem("context");
|
|
|
|
|
|
+ OpenPopupOnItemClick("context");
|
|
|
|
|
|
if (BeginPopup("picker"))
|
|
if (BeginPopup("picker"))
|
|
{
|
|
{
|
|
@@ -4958,7 +4979,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
- OpenPopupContextItem("context");
|
|
|
|
|
|
+ OpenPopupOnItemClick("context");
|
|
}
|
|
}
|
|
else if (flags & ImGuiColorEditFlags_PickerHueBar)
|
|
else if (flags & ImGuiColorEditFlags_PickerHueBar)
|
|
{
|
|
{
|
|
@@ -4971,7 +4992,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|
value_changed = value_changed_sv = true;
|
|
value_changed = value_changed_sv = true;
|
|
}
|
|
}
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
|
- OpenPopupContextItem("context");
|
|
|
|
|
|
+ OpenPopupOnItemClick("context");
|
|
|
|
|
|
// Hue bar logic
|
|
// Hue bar logic
|
|
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
|
|
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
|
|
@@ -5891,7 +5912,8 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags
|
|
float button_size = g.FontSize;
|
|
float button_size = g.FontSize;
|
|
float button_x = ImMax(window->DC.LastItemRect.Min.x, window->DC.LastItemRect.Max.x - g.Style.FramePadding.x * 2.0f - button_size);
|
|
float button_x = ImMax(window->DC.LastItemRect.Min.x, window->DC.LastItemRect.Max.x - g.Style.FramePadding.x * 2.0f - button_size);
|
|
float button_y = window->DC.LastItemRect.Min.y;
|
|
float button_y = window->DC.LastItemRect.Min.y;
|
|
- if (CloseButton(window->GetID((void*)((intptr_t)id + 1)), ImVec2(button_x, button_y)))
|
|
|
|
|
|
+ ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id);
|
|
|
|
+ if (CloseButton(close_button_id, ImVec2(button_x, button_y)))
|
|
*p_open = false;
|
|
*p_open = false;
|
|
last_item_backup.Restore();
|
|
last_item_backup.Restore();
|
|
}
|
|
}
|
|
@@ -6811,13 +6833,22 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected,
|
|
// - TabBarTabListPopupButton() [Internal]
|
|
// - TabBarTabListPopupButton() [Internal]
|
|
//-------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
|
|
|
+struct ImGuiTabBarSection
|
|
|
|
+{
|
|
|
|
+ int TabCount; // Number of tabs in this section.
|
|
|
|
+ float Width; // Sum of width of tabs in this section (after shrinking down)
|
|
|
|
+ float Spacing; // Horizontal spacing at the end of the section.
|
|
|
|
+
|
|
|
|
+ ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); }
|
|
|
|
+};
|
|
|
|
+
|
|
namespace ImGui
|
|
namespace ImGui
|
|
{
|
|
{
|
|
static void TabBarLayout(ImGuiTabBar* tab_bar);
|
|
static void TabBarLayout(ImGuiTabBar* tab_bar);
|
|
static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label);
|
|
static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label);
|
|
static float TabBarCalcMaxTabWidth();
|
|
static float TabBarCalcMaxTabWidth();
|
|
static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
|
|
static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
|
|
- static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
|
|
|
|
|
|
+ static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImGuiTabBarSection* sections);
|
|
static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar);
|
|
static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar);
|
|
static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar);
|
|
static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar);
|
|
}
|
|
}
|
|
@@ -6828,16 +6859,28 @@ ImGuiTabBar::ImGuiTabBar()
|
|
SelectedTabId = NextSelectedTabId = VisibleTabId = 0;
|
|
SelectedTabId = NextSelectedTabId = VisibleTabId = 0;
|
|
CurrFrameVisible = PrevFrameVisible = -1;
|
|
CurrFrameVisible = PrevFrameVisible = -1;
|
|
LastTabContentHeight = 0.0f;
|
|
LastTabContentHeight = 0.0f;
|
|
- WidthAllTabs = WidthAllTabsIdeal = OffsetNextTab = 0.0f;
|
|
|
|
|
|
+ WidthAllTabs = WidthAllTabsIdeal = 0.0f;
|
|
ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f;
|
|
ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f;
|
|
|
|
+ ScrollingRectMinX = ScrollingRectMaxX = 0.0f;
|
|
Flags = ImGuiTabBarFlags_None;
|
|
Flags = ImGuiTabBarFlags_None;
|
|
ReorderRequestTabId = 0;
|
|
ReorderRequestTabId = 0;
|
|
ReorderRequestDir = 0;
|
|
ReorderRequestDir = 0;
|
|
TabsActiveCount = 0;
|
|
TabsActiveCount = 0;
|
|
- WantLayout = VisibleTabWasSubmitted = false;
|
|
|
|
|
|
+ WantLayout = VisibleTabWasSubmitted = TabsAddedNew = false;
|
|
LastTabItemIdx = -1;
|
|
LastTabItemIdx = -1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs)
|
|
|
|
+{
|
|
|
|
+ const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
|
|
|
|
+ const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;
|
|
|
|
+ const int a_section = (a->Flags & ImGuiTabItemFlags_Leading) ? 0 : (a->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
|
|
|
+ const int b_section = (b->Flags & ImGuiTabItemFlags_Leading) ? 0 : (b->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
|
|
|
+ if (a_section != b_section)
|
|
|
|
+ return a_section - b_section;
|
|
|
|
+ return (int)(a->IndexDuringLayout - b->IndexDuringLayout);
|
|
|
|
+}
|
|
|
|
+
|
|
static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs)
|
|
static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs)
|
|
{
|
|
{
|
|
const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
|
|
const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
|
|
@@ -6894,10 +6937,11 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- // When toggling ImGuiTabBarFlags_Reorderable flag, ensure tabs are ordered based on their submission order.
|
|
|
|
- if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1)
|
|
|
|
- if ((flags & ImGuiTabBarFlags_DockNode) == 0)
|
|
|
|
|
|
+ // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable
|
|
|
|
+ if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable)))
|
|
|
|
+ if (tab_bar->Tabs.Size > 1 && (flags & ImGuiTabBarFlags_DockNode) == 0)
|
|
ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
|
|
ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
|
|
|
|
+ tab_bar->TabsAddedNew = false;
|
|
|
|
|
|
// Flags
|
|
// Flags
|
|
if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
|
|
if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
|
|
@@ -6971,7 +7015,10 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
tab_bar->WantLayout = false;
|
|
tab_bar->WantLayout = false;
|
|
|
|
|
|
// Garbage collect by compacting list
|
|
// Garbage collect by compacting list
|
|
|
|
+ // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section)
|
|
int tab_dst_n = 0;
|
|
int tab_dst_n = 0;
|
|
|
|
+ bool need_sort_by_section = false;
|
|
|
|
+ ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing
|
|
for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
|
|
for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
|
|
{
|
|
{
|
|
ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
|
|
ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
|
|
@@ -6985,11 +7032,32 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
}
|
|
}
|
|
if (tab_dst_n != tab_src_n)
|
|
if (tab_dst_n != tab_src_n)
|
|
tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];
|
|
tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];
|
|
|
|
+
|
|
|
|
+ tab = &tab_bar->Tabs[tab_dst_n];
|
|
|
|
+ tab->IndexDuringLayout = (ImS8)tab_dst_n;
|
|
|
|
+
|
|
|
|
+ // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another)
|
|
|
|
+ ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1];
|
|
|
|
+ int curr_tab_section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
|
|
|
+ int prev_tab_section_n = (prev_tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (prev_tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
|
|
|
+ if (tab_dst_n > 0 && curr_tab_section_n == 0 && prev_tab_section_n != 0)
|
|
|
|
+ need_sort_by_section = true;
|
|
|
|
+ if (tab_dst_n > 0 && prev_tab_section_n == 2 && curr_tab_section_n != 2)
|
|
|
|
+ need_sort_by_section = true;
|
|
|
|
+
|
|
|
|
+ sections[curr_tab_section_n].TabCount++;
|
|
tab_dst_n++;
|
|
tab_dst_n++;
|
|
}
|
|
}
|
|
if (tab_bar->Tabs.Size != tab_dst_n)
|
|
if (tab_bar->Tabs.Size != tab_dst_n)
|
|
tab_bar->Tabs.resize(tab_dst_n);
|
|
tab_bar->Tabs.resize(tab_dst_n);
|
|
|
|
|
|
|
|
+ if (need_sort_by_section)
|
|
|
|
+ ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection);
|
|
|
|
+
|
|
|
|
+ // Calculate spacing between sections
|
|
|
|
+ sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
|
|
|
|
+ sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
|
|
|
|
+
|
|
// Setup next selected tab
|
|
// Setup next selected tab
|
|
ImGuiID scroll_track_selected_tab_id = 0;
|
|
ImGuiID scroll_track_selected_tab_id = 0;
|
|
if (tab_bar->NextSelectedTabId)
|
|
if (tab_bar->NextSelectedTabId)
|
|
@@ -7011,23 +7079,29 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
// Tab List Popup (will alter tab_bar->BarRect and therefore the available width!)
|
|
// Tab List Popup (will alter tab_bar->BarRect and therefore the available width!)
|
|
const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0;
|
|
const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0;
|
|
if (tab_list_popup_button)
|
|
if (tab_list_popup_button)
|
|
- if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Max.x!
|
|
|
|
|
|
+ if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x!
|
|
scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
|
|
scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
|
|
|
|
|
|
- // Compute ideal widths
|
|
|
|
|
|
+ // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central
|
|
|
|
+ // (whereas our tabs are stored as: leading, central, trailing)
|
|
|
|
+ int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount };
|
|
g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size);
|
|
g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size);
|
|
- float width_total_contents = 0.0f;
|
|
|
|
|
|
+
|
|
|
|
+ // Compute ideal tabs widths + store them into shrink buffer
|
|
ImGuiTabItem* most_recently_selected_tab = NULL;
|
|
ImGuiTabItem* most_recently_selected_tab = NULL;
|
|
|
|
+ int curr_section_n = -1;
|
|
bool found_selected_tab_id = false;
|
|
bool found_selected_tab_id = false;
|
|
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
|
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
|
{
|
|
{
|
|
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
|
|
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
|
|
IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);
|
|
IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);
|
|
|
|
|
|
- if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected)
|
|
|
|
|
|
+ if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button))
|
|
most_recently_selected_tab = tab;
|
|
most_recently_selected_tab = tab;
|
|
if (tab->ID == tab_bar->SelectedTabId)
|
|
if (tab->ID == tab_bar->SelectedTabId)
|
|
found_selected_tab_id = true;
|
|
found_selected_tab_id = true;
|
|
|
|
+ if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID)
|
|
|
|
+ scroll_track_selected_tab_id = tab->ID;
|
|
|
|
|
|
// Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar.
|
|
// Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar.
|
|
// Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,
|
|
// Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,
|
|
@@ -7036,56 +7110,87 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
const bool has_close_button = tab->Window ? tab->Window->HasCloseButton : ((tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0);
|
|
const bool has_close_button = tab->Window ? tab->Window->HasCloseButton : ((tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0);
|
|
tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x;
|
|
tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x;
|
|
|
|
|
|
- width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->ContentWidth;
|
|
|
|
|
|
+ int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
|
|
|
+ ImGuiTabBarSection* section = §ions[section_n];
|
|
|
|
+ section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f);
|
|
|
|
+ curr_section_n = section_n;
|
|
|
|
|
|
// Store data so we can build an array sorted by width if we need to shrink tabs down
|
|
// Store data so we can build an array sorted by width if we need to shrink tabs down
|
|
- g.ShrinkWidthBuffer[tab_n].Index = tab_n;
|
|
|
|
- g.ShrinkWidthBuffer[tab_n].Width = tab->ContentWidth;
|
|
|
|
- }
|
|
|
|
|
|
+ int shrink_buffer_index = shrink_buffer_indexes[section_n]++;
|
|
|
|
+ g.ShrinkWidthBuffer[shrink_buffer_index].Index = tab_n;
|
|
|
|
+ g.ShrinkWidthBuffer[shrink_buffer_index].Width = tab->ContentWidth;
|
|
|
|
|
|
- // Compute width
|
|
|
|
- const float initial_offset_x = 0.0f; // g.Style.ItemInnerSpacing.x;
|
|
|
|
- const float width_avail = ImMax(tab_bar->BarRect.GetWidth() - initial_offset_x, 0.0f);
|
|
|
|
- float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f;
|
|
|
|
- if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown))
|
|
|
|
- {
|
|
|
|
- // If we don't have enough room, resize down the largest tabs first
|
|
|
|
- ShrinkWidths(g.ShrinkWidthBuffer.Data, g.ShrinkWidthBuffer.Size, width_excess);
|
|
|
|
- for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
|
|
|
- tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index].Width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width);
|
|
|
|
|
|
+ IM_ASSERT(tab->ContentWidth > 0.0f);
|
|
|
|
+ tab->Width = tab->ContentWidth;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // Compute total ideal width (used for e.g. auto-resizing a window)
|
|
|
|
+ tab_bar->WidthAllTabsIdeal = 0.0f;
|
|
|
|
+ for (int section_n = 0; section_n < 3; section_n++)
|
|
|
|
+ tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing;
|
|
|
|
+
|
|
|
|
+ // Horizontal scrolling buttons
|
|
|
|
+ // (note that TabBarScrollButtons() will alter BarRect.Max.x)
|
|
|
|
+ if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll))
|
|
|
|
+ if (ImGuiTabItem* scroll_track_selected_tab = TabBarScrollingButtons(tab_bar))
|
|
|
|
+ {
|
|
|
|
+ scroll_track_selected_tab_id = scroll_track_selected_tab->ID;
|
|
|
|
+ if (!(scroll_track_selected_tab->Flags & ImGuiTabItemFlags_Button))
|
|
|
|
+ tab_bar->SelectedTabId = scroll_track_selected_tab_id;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Shrink widths if full tabs don't fit in their allocated space
|
|
|
|
+ float section_0_w = sections[0].Width + sections[0].Spacing;
|
|
|
|
+ float section_1_w = sections[1].Width + sections[1].Spacing;
|
|
|
|
+ float section_2_w = sections[2].Width + sections[2].Spacing;
|
|
|
|
+ bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth();
|
|
|
|
+ float width_excess;
|
|
|
|
+ if (central_section_is_visible)
|
|
|
|
+ width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section
|
|
else
|
|
else
|
|
|
|
+ width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section
|
|
|
|
+
|
|
|
|
+ // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore
|
|
|
|
+ if (width_excess > 0.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible))
|
|
{
|
|
{
|
|
- const float tab_max_width = TabBarCalcMaxTabWidth();
|
|
|
|
- for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
|
|
|
|
|
+ int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount);
|
|
|
|
+ int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0);
|
|
|
|
+ ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess);
|
|
|
|
+
|
|
|
|
+ // Apply shrunk values into tabs and sections
|
|
|
|
+ for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++)
|
|
{
|
|
{
|
|
- ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
|
|
|
|
- tab->Width = ImMin(tab->ContentWidth, tab_max_width);
|
|
|
|
- IM_ASSERT(tab->Width > 0.0f);
|
|
|
|
|
|
+ ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];
|
|
|
|
+ float shrinked_width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width);
|
|
|
|
+ if (shrinked_width < 0.0f)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
|
|
|
+ sections[section_n].Width -= (tab->Width - shrinked_width);
|
|
|
|
+ tab->Width = shrinked_width;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Layout all active tabs
|
|
// Layout all active tabs
|
|
- float offset_x = initial_offset_x;
|
|
|
|
- float offset_x_ideal = offset_x;
|
|
|
|
- tab_bar->OffsetNextTab = offset_x; // This is used by non-reorderable tab bar where the submission order is always honored.
|
|
|
|
- for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
|
|
|
|
|
+ int section_tab_index = 0;
|
|
|
|
+ float tab_offset = 0.0f;
|
|
|
|
+ tab_bar->WidthAllTabs = 0.0f;
|
|
|
|
+ for (int section_n = 0; section_n < 3; section_n++)
|
|
{
|
|
{
|
|
- ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
|
|
|
|
- tab->Offset = offset_x;
|
|
|
|
- if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID)
|
|
|
|
- scroll_track_selected_tab_id = tab->ID;
|
|
|
|
- offset_x += tab->Width + g.Style.ItemInnerSpacing.x;
|
|
|
|
- offset_x_ideal += tab->ContentWidth + g.Style.ItemInnerSpacing.x;
|
|
|
|
- }
|
|
|
|
- tab_bar->WidthAllTabs = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f);
|
|
|
|
- tab_bar->WidthAllTabsIdeal = ImMax(offset_x_ideal - g.Style.ItemInnerSpacing.x, 0.0f);
|
|
|
|
|
|
+ ImGuiTabBarSection* section = §ions[section_n];
|
|
|
|
+ if (section_n == 2)
|
|
|
|
+ tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset);
|
|
|
|
|
|
- // Horizontal scrolling buttons
|
|
|
|
- const bool scrolling_buttons = (tab_bar->WidthAllTabs > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll);
|
|
|
|
- if (scrolling_buttons)
|
|
|
|
- if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x!
|
|
|
|
- scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
|
|
|
|
|
|
+ for (int tab_n = 0; tab_n < section->TabCount; tab_n++)
|
|
|
|
+ {
|
|
|
|
+ ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n];
|
|
|
|
+ tab->Offset = tab_offset;
|
|
|
|
+ tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);
|
|
|
|
+ }
|
|
|
|
+ tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f);
|
|
|
|
+ tab_offset += section->Spacing;
|
|
|
|
+ section_tab_index += section->TabCount;
|
|
|
|
+ }
|
|
|
|
|
|
// If we have lost the selected tab, select the next most recently active one
|
|
// If we have lost the selected tab, select the next most recently active one
|
|
if (found_selected_tab_id == false)
|
|
if (found_selected_tab_id == false)
|
|
@@ -7104,7 +7209,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
// Update scrolling
|
|
// Update scrolling
|
|
if (scroll_track_selected_tab_id)
|
|
if (scroll_track_selected_tab_id)
|
|
if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id))
|
|
if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id))
|
|
- TabBarScrollToTab(tab_bar, scroll_track_selected_tab);
|
|
|
|
|
|
+ TabBarScrollToTab(tab_bar, scroll_track_selected_tab, sections);
|
|
tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);
|
|
tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);
|
|
tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);
|
|
tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);
|
|
if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)
|
|
if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)
|
|
@@ -7120,6 +7225,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|
{
|
|
{
|
|
tab_bar->ScrollingSpeed = 0.0f;
|
|
tab_bar->ScrollingSpeed = 0.0f;
|
|
}
|
|
}
|
|
|
|
+ tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing;
|
|
|
|
+ tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing;
|
|
|
|
|
|
// Clear name buffers
|
|
// Clear name buffers
|
|
if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
|
|
if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
|
|
@@ -7207,17 +7314,23 @@ void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)
|
|
// Called on manual closure attempt
|
|
// Called on manual closure attempt
|
|
void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
|
|
void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
|
|
{
|
|
{
|
|
- if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
|
|
|
|
|
|
+ IM_ASSERT(!(tab->Flags & ImGuiTabItemFlags_Button));
|
|
|
|
+ if (!(tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
|
|
{
|
|
{
|
|
// This will remove a frame of lag for selecting another tab on closure.
|
|
// This will remove a frame of lag for selecting another tab on closure.
|
|
// However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure
|
|
// However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure
|
|
- tab->LastFrameVisible = -1;
|
|
|
|
- tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;
|
|
|
|
|
|
+ tab->WantClose = true;
|
|
|
|
+ if (tab_bar->VisibleTabId == tab->ID)
|
|
|
|
+ {
|
|
|
|
+ tab->LastFrameVisible = -1;
|
|
|
|
+ tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
|
|
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- // Actually select before expecting closure
|
|
|
|
- tab_bar->NextSelectedTabId = tab->ID;
|
|
|
|
|
|
+ // Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup)
|
|
|
|
+ if (tab_bar->VisibleTabId != tab->ID)
|
|
|
|
+ tab_bar->NextSelectedTabId = tab->ID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -7227,23 +7340,34 @@ static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)
|
|
return ImMax(scrolling, 0.0f);
|
|
return ImMax(scrolling, 0.0f);
|
|
}
|
|
}
|
|
|
|
|
|
-static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
|
|
|
|
|
|
+static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImGuiTabBarSection* sections)
|
|
{
|
|
{
|
|
|
|
+ if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))
|
|
|
|
+ return;
|
|
|
|
+
|
|
ImGuiContext& g = *GImGui;
|
|
ImGuiContext& g = *GImGui;
|
|
float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)
|
|
float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)
|
|
int order = tab_bar->GetTabOrder(tab);
|
|
int order = tab_bar->GetTabOrder(tab);
|
|
- float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f);
|
|
|
|
- float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f);
|
|
|
|
|
|
+
|
|
|
|
+ // 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;
|
|
|
|
+
|
|
|
|
+ // 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);
|
|
|
|
+ float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f);
|
|
tab_bar->ScrollingTargetDistToVisibility = 0.0f;
|
|
tab_bar->ScrollingTargetDistToVisibility = 0.0f;
|
|
- if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= tab_bar->BarRect.GetWidth()))
|
|
|
|
|
|
+ if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width))
|
|
{
|
|
{
|
|
|
|
+ // Scroll to the left
|
|
tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f);
|
|
tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f);
|
|
tab_bar->ScrollingTarget = tab_x1;
|
|
tab_bar->ScrollingTarget = tab_x1;
|
|
}
|
|
}
|
|
- else if (tab_bar->ScrollingTarget < tab_x2 - tab_bar->BarRect.GetWidth())
|
|
|
|
|
|
+ else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width)
|
|
{
|
|
{
|
|
- tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - tab_bar->BarRect.GetWidth()) - tab_bar->ScrollingAnim, 0.0f);
|
|
|
|
- tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth();
|
|
|
|
|
|
+ // Scroll to the right
|
|
|
|
+ tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f);
|
|
|
|
+ tab_bar->ScrollingTarget = tab_x2 - scrollable_width;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -7258,7 +7382,7 @@ void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, in
|
|
bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)
|
|
bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)
|
|
{
|
|
{
|
|
ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId);
|
|
ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId);
|
|
- if (!tab1)
|
|
|
|
|
|
+ if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder))
|
|
return false;
|
|
return false;
|
|
|
|
|
|
//IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools
|
|
//IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools
|
|
@@ -7266,11 +7390,16 @@ bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)
|
|
if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size)
|
|
if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size)
|
|
return false;
|
|
return false;
|
|
|
|
|
|
|
|
+ // Reordered TabItem must share the same position flags than target
|
|
ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
|
|
ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
|
|
|
|
+ if (tab2->Flags & ImGuiTabItemFlags_NoReorder)
|
|
|
|
+ return false;
|
|
|
|
+ if ((tab1->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (tab2->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
ImGuiTabItem item_tmp = *tab1;
|
|
ImGuiTabItem item_tmp = *tab1;
|
|
*tab1 = *tab2;
|
|
*tab1 = *tab2;
|
|
*tab2 = item_tmp;
|
|
*tab2 = item_tmp;
|
|
- tab1 = tab2 = NULL;
|
|
|
|
|
|
|
|
if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
|
|
if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
|
|
MarkIniSettingsDirty();
|
|
MarkIniSettingsDirty();
|
|
@@ -7288,13 +7417,6 @@ static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
|
|
const ImVec2 backup_cursor_pos = window->DC.CursorPos;
|
|
const ImVec2 backup_cursor_pos = window->DC.CursorPos;
|
|
//window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255));
|
|
//window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255));
|
|
|
|
|
|
- const ImRect avail_bar_rect = tab_bar->BarRect;
|
|
|
|
- bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f)));
|
|
|
|
- if (want_clip_rect)
|
|
|
|
- PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true);
|
|
|
|
-
|
|
|
|
- ImGuiTabItem* tab_to_select = NULL;
|
|
|
|
-
|
|
|
|
int select_dir = 0;
|
|
int select_dir = 0;
|
|
ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
|
|
ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
|
|
arrow_col.w *= 0.5f;
|
|
arrow_col.w *= 0.5f;
|
|
@@ -7305,30 +7427,44 @@ static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
|
|
const float backup_repeat_rate = g.IO.KeyRepeatRate;
|
|
const float backup_repeat_rate = g.IO.KeyRepeatRate;
|
|
g.IO.KeyRepeatDelay = 0.250f;
|
|
g.IO.KeyRepeatDelay = 0.250f;
|
|
g.IO.KeyRepeatRate = 0.200f;
|
|
g.IO.KeyRepeatRate = 0.200f;
|
|
- window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y);
|
|
|
|
|
|
+ float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width);
|
|
|
|
+ window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y);
|
|
if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
|
|
if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
|
|
select_dir = -1;
|
|
select_dir = -1;
|
|
- window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y);
|
|
|
|
|
|
+ window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y);
|
|
if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
|
|
if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
|
|
select_dir = +1;
|
|
select_dir = +1;
|
|
PopStyleColor(2);
|
|
PopStyleColor(2);
|
|
g.IO.KeyRepeatRate = backup_repeat_rate;
|
|
g.IO.KeyRepeatRate = backup_repeat_rate;
|
|
g.IO.KeyRepeatDelay = backup_repeat_delay;
|
|
g.IO.KeyRepeatDelay = backup_repeat_delay;
|
|
|
|
|
|
- if (want_clip_rect)
|
|
|
|
- PopClipRect();
|
|
|
|
-
|
|
|
|
|
|
+ ImGuiTabItem* tab_to_scroll_to = NULL;
|
|
if (select_dir != 0)
|
|
if (select_dir != 0)
|
|
if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
|
|
if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
|
|
{
|
|
{
|
|
int selected_order = tab_bar->GetTabOrder(tab_item);
|
|
int selected_order = tab_bar->GetTabOrder(tab_item);
|
|
int target_order = selected_order + select_dir;
|
|
int target_order = selected_order + select_dir;
|
|
- tab_to_select = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; // If we are at the end of the list, still scroll to make our tab visible
|
|
|
|
|
|
+
|
|
|
|
+ // Skip tab item buttons until another tab item is found or end is reached
|
|
|
|
+ while (tab_to_scroll_to == NULL)
|
|
|
|
+ {
|
|
|
|
+ // If we are at the end of the list, still scroll to make our tab visible
|
|
|
|
+ tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order];
|
|
|
|
+
|
|
|
|
+ // Cross through buttons
|
|
|
|
+ // (even if first/last item is a button, return it so we can update the scroll)
|
|
|
|
+ if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button)
|
|
|
|
+ {
|
|
|
|
+ target_order += select_dir;
|
|
|
|
+ selected_order += select_dir;
|
|
|
|
+ tab_to_scroll_to = (target_order <= 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
window->DC.CursorPos = backup_cursor_pos;
|
|
window->DC.CursorPos = backup_cursor_pos;
|
|
tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
|
|
tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
|
|
|
|
|
|
- return tab_to_select;
|
|
|
|
|
|
+ return tab_to_scroll_to;
|
|
}
|
|
}
|
|
|
|
|
|
static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
|
|
static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
|
|
@@ -7355,6 +7491,9 @@ static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
|
|
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
|
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
|
|
{
|
|
{
|
|
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
|
|
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
|
|
|
|
+ if (tab->Flags & ImGuiTabItemFlags_Button)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
const char* tab_name = tab_bar->GetTabName(tab);
|
|
const char* tab_name = tab_bar->GetTabName(tab);
|
|
if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID))
|
|
if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID))
|
|
tab_to_select = tab;
|
|
tab_to_select = tab;
|
|
@@ -7371,6 +7510,7 @@ static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
|
|
//-------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------
|
|
// - BeginTabItem()
|
|
// - BeginTabItem()
|
|
// - EndTabItem()
|
|
// - EndTabItem()
|
|
|
|
+// - TabItemButton()
|
|
// - TabItemEx() [Internal]
|
|
// - TabItemEx() [Internal]
|
|
// - SetTabItemClosed()
|
|
// - SetTabItemClosed()
|
|
// - TabItemCalcSize() [Internal]
|
|
// - TabItemCalcSize() [Internal]
|
|
@@ -7391,6 +7531,8 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f
|
|
IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!");
|
|
IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!");
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
+ IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead!
|
|
|
|
+
|
|
bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL);
|
|
bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL);
|
|
if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
|
|
if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
|
|
{
|
|
{
|
|
@@ -7419,6 +7561,22 @@ void ImGui::EndTabItem()
|
|
window->IDStack.pop_back();
|
|
window->IDStack.pop_back();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags)
|
|
|
|
+{
|
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
|
+ ImGuiWindow* window = g.CurrentWindow;
|
|
|
|
+ if (window->SkipItems)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ ImGuiTabBar* tab_bar = g.CurrentTabBar;
|
|
|
|
+ if (tab_bar == NULL)
|
|
|
|
+ {
|
|
|
|
+ IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window)
|
|
bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window)
|
|
{
|
|
{
|
|
// Layout whole tab bar if not already done
|
|
// Layout whole tab bar if not already done
|
|
@@ -7444,6 +7602,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button));
|
|
|
|
+ IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing
|
|
|
|
+
|
|
// Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented)
|
|
// Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented)
|
|
if (flags & ImGuiTabItemFlags_NoCloseButton)
|
|
if (flags & ImGuiTabItemFlags_NoCloseButton)
|
|
p_open = NULL;
|
|
p_open = NULL;
|
|
@@ -7462,6 +7623,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
tab = &tab_bar->Tabs.back();
|
|
tab = &tab_bar->Tabs.back();
|
|
tab->ID = id;
|
|
tab->ID = id;
|
|
tab->Width = size.x;
|
|
tab->Width = size.x;
|
|
|
|
+ tab_bar->TabsAddedNew = true;
|
|
tab_is_new = true;
|
|
tab_is_new = true;
|
|
}
|
|
}
|
|
tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab);
|
|
tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab);
|
|
@@ -7471,6 +7633,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
|
|
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
|
|
const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
|
|
const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
|
|
const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);
|
|
const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);
|
|
|
|
+ const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;
|
|
tab->LastFrameVisible = g.FrameCount;
|
|
tab->LastFrameVisible = g.FrameCount;
|
|
tab->Flags = flags;
|
|
tab->Flags = flags;
|
|
tab->Window = docked_window;
|
|
tab->Window = docked_window;
|
|
@@ -7488,20 +7651,14 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Append name _with_ the zero-terminator.
|
|
tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Append name _with_ the zero-terminator.
|
|
}
|
|
}
|
|
|
|
|
|
- // If we are not reorderable, always reset offset based on submission order.
|
|
|
|
- // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!)
|
|
|
|
- if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
|
|
|
|
- {
|
|
|
|
- tab->Offset = tab_bar->OffsetNextTab;
|
|
|
|
- tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// Update selected tab
|
|
// Update selected tab
|
|
if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
|
|
if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
|
|
if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)
|
|
if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)
|
|
- tab_bar->NextSelectedTabId = id; // New tabs gets activated
|
|
|
|
|
|
+ if (!is_tab_button)
|
|
|
|
+ tab_bar->NextSelectedTabId = id; // New tabs gets activated
|
|
if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // SetSelected can only be passed on explicit tab bar
|
|
if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // SetSelected can only be passed on explicit tab bar
|
|
- tab_bar->NextSelectedTabId = id;
|
|
|
|
|
|
+ if (!is_tab_button)
|
|
|
|
+ tab_bar->NextSelectedTabId = id;
|
|
|
|
|
|
// Lock visibility
|
|
// Lock visibility
|
|
// (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!)
|
|
// (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!)
|
|
@@ -7521,6 +7678,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
|
|
PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
|
|
ItemAdd(ImRect(), id);
|
|
ItemAdd(ImRect(), id);
|
|
PopItemFlag();
|
|
PopItemFlag();
|
|
|
|
+ if (is_tab_button)
|
|
|
|
+ return false;
|
|
return tab_contents_visible;
|
|
return tab_contents_visible;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -7531,15 +7690,19 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
|
|
const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
|
|
|
|
|
|
// Layout
|
|
// Layout
|
|
|
|
+ const bool is_central_section = (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) == 0;
|
|
size.x = tab->Width;
|
|
size.x = tab->Width;
|
|
- window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
|
|
|
|
|
|
+ if (is_central_section)
|
|
|
|
+ window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
|
|
|
|
+ else
|
|
|
|
+ window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);
|
|
ImVec2 pos = window->DC.CursorPos;
|
|
ImVec2 pos = window->DC.CursorPos;
|
|
ImRect bb(pos, pos + size);
|
|
ImRect bb(pos, pos + size);
|
|
|
|
|
|
// We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)
|
|
// We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)
|
|
- bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x > tab_bar->BarRect.Max.x);
|
|
|
|
|
|
+ const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX);
|
|
if (want_clip_rect)
|
|
if (want_clip_rect)
|
|
- PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true);
|
|
|
|
|
|
+ PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true);
|
|
|
|
|
|
ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
|
|
ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
|
|
ItemSize(bb.GetSize(), style.FramePadding.y);
|
|
ItemSize(bb.GetSize(), style.FramePadding.y);
|
|
@@ -7554,12 +7717,12 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
}
|
|
}
|
|
|
|
|
|
// Click to Select a tab
|
|
// Click to Select a tab
|
|
- ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap);
|
|
|
|
|
|
+ ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowItemOverlap);
|
|
if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW))
|
|
if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW))
|
|
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
|
|
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
|
|
bool hovered, held;
|
|
bool hovered, held;
|
|
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
|
|
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
|
|
- if (pressed)
|
|
|
|
|
|
+ if (pressed && !is_tab_button)
|
|
tab_bar->NextSelectedTabId = id;
|
|
tab_bar->NextSelectedTabId = id;
|
|
hovered |= (g.HoveredId == id);
|
|
hovered |= (g.HoveredId == id);
|
|
|
|
|
|
@@ -7654,13 +7817,14 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
// Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.
|
|
// Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.
|
|
const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
|
|
const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
|
|
if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)))
|
|
if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)))
|
|
- tab_bar->NextSelectedTabId = id;
|
|
|
|
|
|
+ if (!is_tab_button)
|
|
|
|
+ tab_bar->NextSelectedTabId = id;
|
|
|
|
|
|
if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
|
|
if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
|
|
flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
|
|
flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
|
|
|
|
|
|
// Render tab label, process close button
|
|
// Render tab label, process close button
|
|
- const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0;
|
|
|
|
|
|
+ const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0;
|
|
bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible);
|
|
bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible);
|
|
if (just_closed && p_open != NULL)
|
|
if (just_closed && p_open != NULL)
|
|
{
|
|
{
|
|
@@ -7679,6 +7843,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|
if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))
|
|
if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))
|
|
SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
|
|
SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
|
|
|
|
|
|
|
|
+ IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected
|
|
|
|
+ if (is_tab_button)
|
|
|
|
+ return pressed;
|
|
return tab_contents_visible;
|
|
return tab_contents_visible;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -7727,7 +7894,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
|
|
const float width = bb.GetWidth();
|
|
const float width = bb.GetWidth();
|
|
IM_UNUSED(flags);
|
|
IM_UNUSED(flags);
|
|
IM_ASSERT(width > 0.0f);
|
|
IM_ASSERT(width > 0.0f);
|
|
- const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f));
|
|
|
|
|
|
+ const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));
|
|
const float y1 = bb.Min.y + 1.0f;
|
|
const float y1 = bb.Min.y + 1.0f;
|
|
const float y2 = bb.Max.y + ((flags & ImGuiTabItemFlags_Preview) ? 0.0f : -1.0f);
|
|
const float y2 = bb.Max.y + ((flags & ImGuiTabItemFlags_Preview) ? 0.0f : -1.0f);
|
|
draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
|
|
draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
|
|
@@ -8083,7 +8250,7 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag
|
|
float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n));
|
|
float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n));
|
|
float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
|
|
float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
|
|
column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
|
|
column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
|
|
- column->ClipRect.ClipWith(window->ClipRect);
|
|
|
|
|
|
+ column->ClipRect.ClipWithFull(window->ClipRect);
|
|
}
|
|
}
|
|
|
|
|
|
if (columns->Count > 1)
|
|
if (columns->Count > 1)
|