|
|
@@ -152,6 +152,7 @@
|
|
|
Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
|
|
|
Also read releases logs https://github.com/ocornut/imgui/releases for more details.
|
|
|
|
|
|
+ - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
|
|
|
- 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
|
|
|
- 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337).
|
|
|
- 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
|
|
|
@@ -552,6 +553,7 @@
|
|
|
- shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus)
|
|
|
!- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing
|
|
|
- keyboard: full keyboard navigation and focus. (#323)
|
|
|
+ - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622)
|
|
|
- focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
|
|
|
- input: rework IO system to be able to pass actual ordered/timestamped events.
|
|
|
- input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style).
|
|
|
@@ -708,7 +710,7 @@ ImGuiStyle::ImGuiStyle()
|
|
|
ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
|
|
|
ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
|
|
|
TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
|
|
|
- IndentSpacing = 22.0f; // Horizontal spacing when e.g. entering a tree node
|
|
|
+ IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
|
|
|
ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
|
|
|
ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
|
|
|
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
|
|
|
@@ -1722,29 +1724,22 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id)
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
window->DC.LastItemID = id ? *id : 0;
|
|
|
window->DC.LastItemRect = bb;
|
|
|
+ window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
|
|
|
if (IsClippedEx(bb, id, false))
|
|
|
- {
|
|
|
- window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
|
|
|
return false;
|
|
|
- }
|
|
|
|
|
|
// This is a sensible default, but widgets are free to override it after calling ItemAdd()
|
|
|
ImGuiState& g = *GImGui;
|
|
|
if (IsMouseHoveringRect(bb.Min, bb.Max))
|
|
|
{
|
|
|
- // Matching the behavior of IsHovered() but ignore if ActiveId==window->MoveID (we clicked on the window background)
|
|
|
+ // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background)
|
|
|
// So that clicking on items with no active id such as Text() still returns true with IsItemHovered()
|
|
|
window->DC.LastItemHoveredRect = true;
|
|
|
- window->DC.LastItemHoveredAndUsable = false;
|
|
|
if (g.HoveredRootWindow == window->RootWindow)
|
|
|
if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveID))
|
|
|
if (IsWindowContentHoverable(window))
|
|
|
window->DC.LastItemHoveredAndUsable = true;
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
|
|
|
- }
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
@@ -1755,14 +1750,13 @@ bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when
|
|
|
ImGuiWindow* window = GetCurrentWindowRead();
|
|
|
|
|
|
if (!bb.Overlaps(window->ClipRect))
|
|
|
- {
|
|
|
if (!id || *id != GImGui->ActiveId)
|
|
|
if (clip_even_when_logged || !g.LogEnabled)
|
|
|
return true;
|
|
|
- }
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+// NB: This is an internal helper. The user-facing IsItemHovered() is using data emitted from ItemAdd(), with a slightly different logic.
|
|
|
bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
|
|
|
{
|
|
|
ImGuiState& g = *GImGui;
|
|
|
@@ -1770,7 +1764,7 @@ bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindowRead();
|
|
|
if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow))
|
|
|
- if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && ImGui::IsMouseHoveringRect(bb.Min, bb.Max))
|
|
|
+ if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max))
|
|
|
if (IsWindowContentHoverable(g.HoveredRootWindow))
|
|
|
return true;
|
|
|
}
|
|
|
@@ -2373,6 +2367,7 @@ static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiW
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
|
|
|
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
@@ -2748,6 +2743,12 @@ void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool opened, float scale, bool
|
|
|
window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text));
|
|
|
}
|
|
|
|
|
|
+void ImGui::RenderBullet(ImVec2 pos)
|
|
|
+{
|
|
|
+ ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
|
|
|
+}
|
|
|
+
|
|
|
void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col)
|
|
|
{
|
|
|
ImGuiState& g = *GImGui;
|
|
|
@@ -3062,6 +3063,11 @@ bool ImGui::IsItemActive()
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+bool ImGui::IsItemClicked(int mouse_button)
|
|
|
+{
|
|
|
+ return IsMouseClicked(mouse_button) && IsItemHovered();
|
|
|
+}
|
|
|
+
|
|
|
bool ImGui::IsAnyItemHovered()
|
|
|
{
|
|
|
return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
|
|
|
@@ -4123,11 +4129,11 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_
|
|
|
// Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
|
|
|
const ImRect title_bar_rect = window->TitleBarRect();
|
|
|
const float border_size = window->BorderSize;
|
|
|
- ImRect clip_rect;
|
|
|
- clip_rect.Min.x = title_bar_rect.Min.x + ImMax(border_size, (float)(int)(window->WindowPadding.x*0.5f));
|
|
|
- clip_rect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + border_size;
|
|
|
- clip_rect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, (float)(int)(window->WindowPadding.x*0.5f));
|
|
|
- clip_rect.Max.y = window->Pos.y + window->Size.y - border_size - window->ScrollbarSizes.y;
|
|
|
+ ImRect clip_rect; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
|
|
|
+ clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
|
|
|
+ clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size);
|
|
|
+ clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
|
|
|
+ clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size);
|
|
|
PushClipRect(clip_rect.Min, clip_rect.Max, true);
|
|
|
|
|
|
// Clear 'accessed' flag last thing
|
|
|
@@ -4587,15 +4593,19 @@ bool ImGui::IsWindowFocused()
|
|
|
bool ImGui::IsRootWindowFocused()
|
|
|
{
|
|
|
ImGuiState& g = *GImGui;
|
|
|
- ImGuiWindow* root_window = g.CurrentWindow->RootWindow;
|
|
|
- return g.FocusedWindow == root_window;
|
|
|
+ return g.FocusedWindow == g.CurrentWindow->RootWindow;
|
|
|
}
|
|
|
|
|
|
bool ImGui::IsRootWindowOrAnyChildFocused()
|
|
|
{
|
|
|
ImGuiState& g = *GImGui;
|
|
|
- ImGuiWindow* root_window = g.CurrentWindow->RootWindow;
|
|
|
- return g.FocusedWindow && g.FocusedWindow->RootWindow == root_window;
|
|
|
+ return g.FocusedWindow && g.FocusedWindow->RootWindow == g.CurrentWindow->RootWindow;
|
|
|
+}
|
|
|
+
|
|
|
+bool ImGui::IsRootWindowOrAnyChildHovered()
|
|
|
+{
|
|
|
+ ImGuiState& g = *GImGui;
|
|
|
+ return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow);
|
|
|
}
|
|
|
|
|
|
float ImGui::GetWindowWidth()
|
|
|
@@ -5263,39 +5273,34 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
|
|
|
+ flags |= ImGuiButtonFlags_PressedOnClickRelease;
|
|
|
+
|
|
|
bool pressed = false;
|
|
|
- const bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
|
|
|
+ bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
|
|
|
if (hovered)
|
|
|
{
|
|
|
SetHoveredID(id);
|
|
|
if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
|
|
|
{
|
|
|
- if (g.IO.MouseDoubleClicked[0] && (flags & ImGuiButtonFlags_PressedOnDoubleClick))
|
|
|
+ if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) // Most common type
|
|
|
{
|
|
|
- pressed = true;
|
|
|
- }
|
|
|
- else if (g.IO.MouseClicked[0])
|
|
|
- {
|
|
|
- if (flags & ImGuiButtonFlags_PressedOnClick)
|
|
|
- {
|
|
|
- pressed = true;
|
|
|
- SetActiveID(0);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- SetActiveID(id, window);
|
|
|
- }
|
|
|
+ SetActiveID(id, window); // Hold on ID
|
|
|
FocusWindow(window);
|
|
|
}
|
|
|
- else if (g.IO.MouseReleased[0] && (flags & ImGuiButtonFlags_PressedOnRelease))
|
|
|
+ if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
|
|
|
{
|
|
|
pressed = true;
|
|
|
SetActiveID(0);
|
|
|
+ FocusWindow(window);
|
|
|
}
|
|
|
- else if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && ImGui::IsMouseClicked(0, true))
|
|
|
+ if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
|
|
|
{
|
|
|
pressed = true;
|
|
|
+ SetActiveID(0);
|
|
|
}
|
|
|
+ if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && ImGui::IsMouseClicked(0, true))
|
|
|
+ pressed = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -5308,12 +5313,16 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (hovered)
|
|
|
+ if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
|
|
|
pressed = true;
|
|
|
SetActiveID(0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
|
|
|
+ if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
|
|
|
+ hovered = pressed = held = false;
|
|
|
+
|
|
|
if (out_hovered) *out_hovered = hovered;
|
|
|
if (out_held) *out_held = held;
|
|
|
|
|
|
@@ -5594,6 +5603,9 @@ void ImGui::LogButtons()
|
|
|
|
|
|
bool ImGui::TreeNodeBehaviorIsOpened(ImGuiID id, ImGuiTreeNodeFlags flags)
|
|
|
{
|
|
|
+ if (flags & ImGuiTreeNodeFlags_AlwaysOpen)
|
|
|
+ return true;
|
|
|
+
|
|
|
// We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
|
|
|
ImGuiState& g = *GImGui;
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
@@ -5630,14 +5642,13 @@ bool ImGui::TreeNodeBehaviorIsOpened(ImGuiID id, ImGuiTreeNodeFlags flags)
|
|
|
|
|
|
// When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
|
|
|
// NB- If we are above max depth we still allow manually opened nodes to be logged.
|
|
|
- if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoExpandOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
|
|
|
+ if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
|
|
|
opened = true;
|
|
|
|
|
|
return opened;
|
|
|
}
|
|
|
|
|
|
-// FIXME: Split into CollapsingHeader(label, default_open?) and TreeNodeBehavior(label), obsolete the 4 parameters function.
|
|
|
-bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display_frame, bool default_open)
|
|
|
+bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
if (window->SkipItems)
|
|
|
@@ -5645,20 +5656,16 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display
|
|
|
|
|
|
ImGuiState& g = *GImGui;
|
|
|
const ImGuiStyle& style = g.Style;
|
|
|
+ const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
|
|
|
const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
|
|
|
|
|
|
- IM_ASSERT(str_id != NULL || label != NULL);
|
|
|
- if (str_id == NULL)
|
|
|
- str_id = label;
|
|
|
- if (label == NULL)
|
|
|
- label = str_id;
|
|
|
- const bool label_hide_text_after_double_hash = (label == str_id); // Only search and hide text after ## if we have passed label and ID separately, otherwise allow "##" within format string.
|
|
|
- const ImGuiID id = window->GetID(str_id);
|
|
|
- const ImVec2 label_size = CalcTextSize(label, NULL, label_hide_text_after_double_hash);
|
|
|
+ if (!label_end)
|
|
|
+ label_end = FindRenderedTextEnd(label);
|
|
|
+ const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
|
|
|
|
|
// We vertically grow up to current line height up the typical widget height.
|
|
|
const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it
|
|
|
- const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), label_size.y + padding.y*2);
|
|
|
+ const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
|
|
|
ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
|
|
|
if (display_frame)
|
|
|
{
|
|
|
@@ -5667,28 +5674,49 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display
|
|
|
bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
|
|
|
}
|
|
|
|
|
|
- const float collapser_width = g.FontSize + (display_frame ? padding.x*2 : padding.x);
|
|
|
+ const float text_offset_x = g.FontSize + (display_frame ? padding.x*3 : padding.x*2); // Collapser arrow width + Spacing
|
|
|
const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
|
|
|
ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
|
|
|
|
|
|
// For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
|
|
|
// (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not)
|
|
|
const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y);
|
|
|
- bool opened = TreeNodeBehaviorIsOpened(id, (default_open ? ImGuiTreeNodeFlags_DefaultOpen : 0) | (display_frame ? ImGuiTreeNodeFlags_NoAutoExpandOnLog : 0));
|
|
|
+ bool opened = TreeNodeBehaviorIsOpened(id, flags);
|
|
|
if (!ItemAdd(interact_bb, &id))
|
|
|
+ {
|
|
|
+ if (opened && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
|
|
|
+ TreePushRawID(id);
|
|
|
return opened;
|
|
|
+ }
|
|
|
|
|
|
- bool hovered, held;
|
|
|
- bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, ImGuiButtonFlags_NoKeyModifiers);
|
|
|
- if (pressed)
|
|
|
- {
|
|
|
- opened = !opened;
|
|
|
- window->DC.StateStorage->SetInt(id, opened);
|
|
|
+ // Flags that affects opening behavior:
|
|
|
+ // - 0(default) ..................... single-click anywhere to open
|
|
|
+ // - OpenOnDoubleClick .............. double-click anywhere to open
|
|
|
+ // - OpenOnArrow .................... single-click on arrow to open
|
|
|
+ // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
|
|
|
+ ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0);
|
|
|
+ if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
|
|
|
+ button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
|
|
|
+ bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
|
|
|
+ if (pressed && !(flags & ImGuiTreeNodeFlags_AlwaysOpen))
|
|
|
+ {
|
|
|
+ bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick));
|
|
|
+ if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
|
|
|
+ toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y));
|
|
|
+ if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
|
|
|
+ toggled |= g.IO.MouseDoubleClicked[0];
|
|
|
+ if (toggled)
|
|
|
+ {
|
|
|
+ opened = !opened;
|
|
|
+ window->DC.StateStorage->SetInt(id, opened);
|
|
|
+ }
|
|
|
}
|
|
|
+ if (flags & ImGuiTreeNodeFlags_AllowOverlapMode)
|
|
|
+ SetItemAllowOverlap();
|
|
|
|
|
|
// Render
|
|
|
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
|
|
|
- const ImVec2 text_pos = bb.Min + padding + ImVec2(collapser_width, text_base_offset_y);
|
|
|
+ const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, padding.y + text_base_offset_y);
|
|
|
if (display_frame)
|
|
|
{
|
|
|
// Framed type
|
|
|
@@ -5700,80 +5728,131 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display
|
|
|
const char log_prefix[] = "\n##";
|
|
|
const char log_suffix[] = "##";
|
|
|
LogRenderedText(text_pos, log_prefix, log_prefix+3);
|
|
|
- RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size);
|
|
|
+ RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
|
|
|
LogRenderedText(text_pos, log_suffix+1, log_suffix+3);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size);
|
|
|
+ RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// Unframed typed for tree nodes
|
|
|
- if (hovered)
|
|
|
+ if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
|
|
|
RenderFrame(bb.Min, bb.Max, col, false);
|
|
|
|
|
|
- RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), opened, 0.70f, false);
|
|
|
+ if (flags & ImGuiTreeNodeFlags_AlwaysOpen)
|
|
|
+ RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
|
|
|
+ else
|
|
|
+ RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), opened, 0.70f, false);
|
|
|
if (g.LogEnabled)
|
|
|
LogRenderedText(text_pos, ">");
|
|
|
- RenderText(text_pos, label, NULL, label_hide_text_after_double_hash);
|
|
|
+ RenderText(text_pos, label, label_end, false);
|
|
|
}
|
|
|
|
|
|
+ if (opened && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
|
|
|
+ TreePushRawID(id);
|
|
|
return opened;
|
|
|
}
|
|
|
|
|
|
-// If returning 'true' the node is open and the user is responsible for calling TreePop
|
|
|
-bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
|
|
|
+bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
if (window->SkipItems)
|
|
|
return false;
|
|
|
|
|
|
- ImGuiState& g = *GImGui;
|
|
|
- ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
|
|
|
- if (!str_id || !str_id[0])
|
|
|
- str_id = fmt;
|
|
|
+ return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label);
|
|
|
+}
|
|
|
|
|
|
- ImGui::PushID(str_id);
|
|
|
- const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false);
|
|
|
- ImGui::PopID();
|
|
|
+bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
|
|
|
+{
|
|
|
+ ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ if (window->SkipItems)
|
|
|
+ return false;
|
|
|
|
|
|
- if (opened)
|
|
|
- ImGui::TreePush(str_id);
|
|
|
+ if (p_open && !*p_open)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ ImGuiID id = window->GetID(label);
|
|
|
+ bool opened = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label);
|
|
|
+ if (p_open)
|
|
|
+ {
|
|
|
+ // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
|
|
|
+ ImGuiState& g = *GImGui;
|
|
|
+ float button_sz = g.FontSize * 0.5f;
|
|
|
+ if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(window->DC.LastItemRect.Max.x - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz))
|
|
|
+ *p_open = false;
|
|
|
+ }
|
|
|
|
|
|
return opened;
|
|
|
}
|
|
|
|
|
|
-bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
|
|
|
+bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
|
|
|
{
|
|
|
- va_list args;
|
|
|
- va_start(args, fmt);
|
|
|
- bool s = TreeNodeV(str_id, fmt, args);
|
|
|
- va_end(args);
|
|
|
- return s;
|
|
|
+ ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ if (window->SkipItems)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
|
|
|
}
|
|
|
|
|
|
-// If returning 'true' the node is open and the user is responsible for calling TreePop
|
|
|
-bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
|
|
|
+bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
if (window->SkipItems)
|
|
|
return false;
|
|
|
|
|
|
ImGuiState& g = *GImGui;
|
|
|
- ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
|
|
|
+ const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
|
|
|
+ return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
|
|
|
+}
|
|
|
|
|
|
- if (!ptr_id)
|
|
|
- ptr_id = fmt;
|
|
|
+bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
|
|
|
+{
|
|
|
+ ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ if (window->SkipItems)
|
|
|
+ return false;
|
|
|
|
|
|
- ImGui::PushID(ptr_id);
|
|
|
- const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false);
|
|
|
- ImGui::PopID();
|
|
|
+ ImGuiState& g = *GImGui;
|
|
|
+ const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
|
|
|
+ return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
|
|
|
+}
|
|
|
|
|
|
- if (opened)
|
|
|
- ImGui::TreePush(ptr_id);
|
|
|
+bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
|
|
|
+{
|
|
|
+ return TreeNodeExV(str_id, 0, fmt, args);
|
|
|
+}
|
|
|
+
|
|
|
+bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
|
|
|
+{
|
|
|
+ return TreeNodeExV(ptr_id, 0, fmt, args);
|
|
|
+}
|
|
|
|
|
|
+bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
|
|
|
+{
|
|
|
+ va_list args;
|
|
|
+ va_start(args, fmt);
|
|
|
+ bool opened = TreeNodeExV(str_id, flags, fmt, args);
|
|
|
+ va_end(args);
|
|
|
+ return opened;
|
|
|
+}
|
|
|
+
|
|
|
+bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
|
|
|
+{
|
|
|
+ va_list args;
|
|
|
+ va_start(args, fmt);
|
|
|
+ bool opened = TreeNodeExV(ptr_id, flags, fmt, args);
|
|
|
+ va_end(args);
|
|
|
+ return opened;
|
|
|
+}
|
|
|
+
|
|
|
+bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
|
|
|
+{
|
|
|
+ va_list args;
|
|
|
+ va_start(args, fmt);
|
|
|
+ bool opened = TreeNodeExV(str_id, 0, fmt, args);
|
|
|
+ va_end(args);
|
|
|
return opened;
|
|
|
}
|
|
|
|
|
|
@@ -5781,14 +5860,29 @@ bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
|
|
|
{
|
|
|
va_list args;
|
|
|
va_start(args, fmt);
|
|
|
- bool s = TreeNodeV(ptr_id, fmt, args);
|
|
|
+ bool opened = TreeNodeExV(ptr_id, 0, fmt, args);
|
|
|
va_end(args);
|
|
|
- return s;
|
|
|
+ return opened;
|
|
|
}
|
|
|
|
|
|
-bool ImGui::TreeNode(const char* str_label_id)
|
|
|
+bool ImGui::TreeNode(const char* label)
|
|
|
{
|
|
|
- return TreeNode(str_label_id, "%s", str_label_id);
|
|
|
+ ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ if (window->SkipItems)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+float ImGui::GetTreeNodeToLabelSpacing(ImGuiTreeNodeFlags flags)
|
|
|
+{
|
|
|
+ ImGuiState& g = *GImGui;
|
|
|
+ float off_from_start;
|
|
|
+ if (flags & ImGuiTreeNodeFlags_Framed)
|
|
|
+ off_from_start = g.FontSize + (g.Style.FramePadding.x * 3.0f) - ((float)(int)(g.CurrentWindow->WindowPadding.x*0.5f) - 1);
|
|
|
+ else
|
|
|
+ off_from_start = g.FontSize + (g.Style.FramePadding.x * 2.0f);
|
|
|
+ return off_from_start;
|
|
|
}
|
|
|
|
|
|
void ImGui::SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond)
|
|
|
@@ -5861,12 +5955,9 @@ void ImGui::Bullet()
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // Render
|
|
|
- const float bullet_size = g.FontSize*0.15f;
|
|
|
- window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text));
|
|
|
-
|
|
|
- // Stay on same line
|
|
|
- ImGui::SameLine(0, style.FramePadding.x*2);
|
|
|
+ // Render and stay on same line
|
|
|
+ RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
|
|
|
+ SameLine(0, style.FramePadding.x*2);
|
|
|
}
|
|
|
|
|
|
// Text with a little bullet aligned to the typical tree node.
|
|
|
@@ -5890,8 +5981,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
|
|
|
return;
|
|
|
|
|
|
// Render
|
|
|
- const float bullet_size = g.FontSize*0.15f;
|
|
|
- window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), bullet_size, GetColorU32(ImGuiCol_Text));
|
|
|
+ RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
|
|
|
RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end);
|
|
|
}
|
|
|
|
|
|
@@ -8222,7 +8312,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|
|
if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick;
|
|
|
if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease;
|
|
|
if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
|
|
|
- if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnDoubleClick;
|
|
|
+ if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
|
|
|
bool hovered, held;
|
|
|
bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags);
|
|
|
if (flags & ImGuiSelectableFlags_Disabled)
|
|
|
@@ -8429,7 +8519,7 @@ bool ImGui::BeginMenuBar()
|
|
|
ImGui::BeginGroup(); // Save position
|
|
|
ImGui::PushID("##menubar");
|
|
|
ImRect rect = window->MenuBarRect();
|
|
|
- PushClipRect(ImVec2(rect.Min.x, rect.Min.y + window->BorderSize), ImVec2(rect.Max.x, rect.Max.y), false);
|
|
|
+ PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false);
|
|
|
window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
|
|
|
window->DC.LayoutType = ImGuiLayoutType_Horizontal;
|
|
|
window->DC.MenuBarAppending = true;
|
|
|
@@ -8992,7 +9082,7 @@ float ImGui::GetColumnWidth(int column_index)
|
|
|
if (column_index < 0)
|
|
|
column_index = window->DC.ColumnsCurrent;
|
|
|
|
|
|
- const float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
|
|
|
+ float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
|
|
|
return w;
|
|
|
}
|
|
|
|
|
|
@@ -9002,8 +9092,8 @@ static void PushColumnClipRect(int column_index)
|
|
|
if (column_index < 0)
|
|
|
column_index = window->DC.ColumnsCurrent;
|
|
|
|
|
|
- const float x1 = window->Pos.x + ImGui::GetColumnOffset(column_index) - 1;
|
|
|
- const float x2 = window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1;
|
|
|
+ float x1 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index) - 1.0f);
|
|
|
+ float x2 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1.0f);
|
|
|
ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true);
|
|
|
}
|
|
|
|
|
|
@@ -9131,6 +9221,14 @@ void ImGui::TreePush(const void* ptr_id)
|
|
|
PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
|
|
|
}
|
|
|
|
|
|
+void ImGui::TreePushRawID(ImGuiID id)
|
|
|
+{
|
|
|
+ ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ ImGui::Indent();
|
|
|
+ window->DC.TreeDepth++;
|
|
|
+ window->IDStack.push_back(id);
|
|
|
+}
|
|
|
+
|
|
|
void ImGui::TreePop()
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
@@ -9349,8 +9447,8 @@ void ImGui::ShowMetricsWindow(bool* opened)
|
|
|
ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
|
|
|
for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
|
|
|
vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
|
|
|
- clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, ImColor(255,255,0));
|
|
|
- vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, ImColor(255,0,255));
|
|
|
+ clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
|
|
|
+ vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
|
|
|
}
|
|
|
if (!draw_opened)
|
|
|
continue;
|
|
|
@@ -9366,7 +9464,7 @@ void ImGui::ShowMetricsWindow(bool* opened)
|
|
|
}
|
|
|
ImGui::Selectable(buf, false);
|
|
|
if (ImGui::IsItemHovered())
|
|
|
- overlay_draw_list->AddPolyline(triangles_pos, 3, ImColor(255,255,0), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle
|
|
|
+ overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle
|
|
|
}
|
|
|
ImGui::TreePop();
|
|
|
}
|