diff --git a/examples/.gitignore b/examples/.gitignore index 428ea446..ddfa5ead 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -31,9 +31,10 @@ xcuserdata ## Unix executables example_glfw_opengl2/example_glfw_opengl2 example_glfw_opengl3/example_glfw_opengl3 +example_glut_opengl2/example_glut_opengl2 +example_null/example_null example_sdl_opengl2/example_sdl_opengl2 example_sdl_opengl3/example_sdl_opengl3 -example_glut_opengl2/example_glut_opengl2 ## Dear ImGui Ini files imgui.ini diff --git a/examples/example_null/Makefile b/examples/example_null/Makefile new file mode 100644 index 00000000..012758fe --- /dev/null +++ b/examples/example_null/Makefile @@ -0,0 +1,59 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# + +EXE = example_null +SOURCES = main.cpp +SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp ../../imgui_widgets.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) + +CXXFLAGS = -I../ -I../../ +CXXFLAGS += -g -Wall -Wformat +LIBS = + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(findstring MINGW,$(UNAME_S)),MINGW) + ECHO_MESSAGE = "MinGW" + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:../%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:../../%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:../libs/gl3w/GL/%.c +# %.o:../libs/glad/src/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_null/main.cpp b/examples/example_null/main.cpp index 90521f9b..81af0999 100644 --- a/examples/example_null/main.cpp +++ b/examples/example_null/main.cpp @@ -1,4 +1,5 @@ -// dear imgui: null/dummy example application (compile and link imgui with no inputs, no outputs) +// dear imgui: null/dummy example application (compile and link imgui with NO INPUTS, NO OUTPUTS) +// This is useful to test building, but you cannot interact with anything here! #include "imgui.h" #include @@ -13,7 +14,7 @@ int main(int, char**) int tex_w, tex_h; io.Fonts->GetTexDataAsRGBA32(&tex_pixels, &tex_w, &tex_h); - for (int n = 0; n < 50; n++) + for (int n = 0; n < 20; n++) { printf("NewFrame() %d\n", n); io.DisplaySize = ImVec2(1920, 1080); diff --git a/imgui.cpp b/imgui.cpp index 03dc2e27..704ffc1d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4671,6 +4671,12 @@ bool ImGui::IsItemClicked(int mouse_button) return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); } +bool ImGui::IsItemToggledSelection() +{ + ImGuiContext& g = *GImGui; + return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; +} + bool ImGui::IsAnyItemHovered() { ImGuiContext& g = *GImGui; @@ -8668,7 +8674,7 @@ static void ImGui::NavUpdateMoveResult() { // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) g.NavJustMovedToId = result->ID; - g.NavJustMovedToSelectScopeId = result->SelectScopeId; + g.NavJustMovedToMultiSelectScopeId = result->SelectScopeId; } SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); g.NavMoveFromClampedRefRect = false; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index fcc37768..7cf67b7d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -128,6 +128,7 @@ static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleMenuFile(); // Helper to display a little (?) mark which shows a tooltip when hovered. +// In your own code you may want to display an actual icon if you are using a merged icon fonts (see misc/fonts/README.txt) static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); @@ -154,12 +155,13 @@ static void ShowDockingDisabledMessage() // Helper to display basic user controls. void ImGui::ShowUserGuide() { + ImGuiIO& io = ImGui::GetIO(); ImGui::BulletText("Double-click on title bar to collapse window."); ImGui::BulletText("Click and drag on lower right corner to resize window\n(double-click to auto fit window to its contents)."); ImGui::BulletText("Click and drag on any empty space to move window."); ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); - if (ImGui::GetIO().FontAllowUserScaling) + if (io.FontAllowUserScaling) ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); ImGui::BulletText("Mouse Wheel to scroll."); ImGui::BulletText("While editing text:\n"); @@ -271,7 +273,7 @@ void ImGui::ShowDemoWindow(bool* p_open) //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // Use 2/3 of the space for widgets and 1/3 for labels (default) ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // Use fixed width for labels (by passing a negative value), the rest goes to widgets. We choose a width proportional to our font size. - // Menu + // Menu Bar if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Menu")) @@ -392,7 +394,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::TreeNode("Backend Flags")) { HelpMarker("Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities."); - ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying the back-end flags. + ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying actual back-end flags. ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasGamepad); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors); ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasSetMousePos); @@ -631,7 +633,7 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Advanced, with Selectable nodes")) { - HelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); + HelpMarker("This is a more typical looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); static bool align_label_with_current_x_position = false; ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); ImGui::Text("Hello!"); @@ -644,10 +646,12 @@ static void ShowDemoWindowWidgets() for (int i = 0; i < 6; i++) { // Disable the default open on single-click behavior and pass in Selected flag according to our selection state. - ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0); + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + if (selection_mask & (1 << i)) + node_flags |= ImGuiTreeNodeFlags_Selected; if (i < 3) { - // Node + // Items 0..2 are Tree Node bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); if (ImGui::IsItemClicked()) node_clicked = i; @@ -659,7 +663,9 @@ static void ShowDemoWindowWidgets() } else { - // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). + // Items 3..5 are Tree Leaves + // The only reason we use TreeNode at all is to allow selection of the leaf. + // Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); if (ImGui::IsItemClicked()) @@ -685,7 +691,7 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Collapsing Headers")) { static bool closable_group = true; - ImGui::Checkbox("Enable extra group", &closable_group); + ImGui::Checkbox("Show 2nd header", &closable_group); if (ImGui::CollapsingHeader("Header")) { ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); @@ -789,7 +795,7 @@ static void ShowDemoWindowWidgets() ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); ImVec2 pos = ImGui::GetCursorScreenPos(); - ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImVec4(1.0f,1.0f,1.0f,1.0f), ImVec4(1.0f,1.0f,1.0f,0.5f)); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -801,7 +807,7 @@ static void ShowDemoWindowWidgets() ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); - ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128)); + ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f)); ImGui::EndTooltip(); } ImGui::TextWrapped("And now some textured buttons.."); @@ -810,7 +816,7 @@ static void ShowDemoWindowWidgets() { ImGui::PushID(i); int frame_padding = -1 + i; // -1 = uses default padding - if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImColor(0,0,0,255))) + if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImVec4(0.0f,0.0f,0.0f,1.0f))) pressed_count += 1; ImGui::PopID(); ImGui::SameLine(); @@ -1041,7 +1047,8 @@ static void ShowDemoWindowWidgets() return 0; } - // Tip: Because ImGui:: is a namespace you can add your own function into the namespace from your own source files. + // Tip: Because ImGui:: is a namespace you would typicall add your own function into the namespace in your own source files. + // For example, you may add a function called ImGui::InputText(const char* label, MyString* my_str). static bool MyInputTextMultiline(const char* label, ImVector* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0) { IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); @@ -2146,7 +2153,8 @@ static void ShowDemoWindowLayout() ImGui::PopID(); } } - float scroll_x = ImGui::GetScrollX(), scroll_max_x = ImGui::GetScrollMaxX(); + float scroll_x = ImGui::GetScrollX(); + float scroll_max_x = ImGui::GetScrollMaxX(); ImGui::EndChild(); ImGui::PopStyleVar(2); float scroll_x_delta = 0.0f; @@ -3141,7 +3149,9 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) } if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) { - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), ImColor(255, 255, 255, 255), ImColor(255, 255, 255, 128)); + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); ImGui::TreePop(); } @@ -3177,7 +3187,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() //----------------------------------------------------------------------------- -// Demonstrate creating a fullscreen menu bar and populating it. +// Demonstrate creating a "main" fullscreen menu bar and populating it. +// Note the difference between BeginMainMenuBar() and BeginMenuBar(): +// - BeginMenuBar() = menu-bar inside current window we Begin()-ed into (the window needs the ImGuiWindowFlags_MenuBar flag) +// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it. static void ShowExampleAppMainMenuBar() { if (ImGui::BeginMainMenuBar()) @@ -3201,6 +3214,7 @@ static void ShowExampleAppMainMenuBar() } } +// Note that shortcuts are currently provided for display only (future version will add flags to BeginMenu to process shortcuts) static void ShowExampleMenuFile() { ImGui::MenuItem("(dummy menu)", NULL, false, false); @@ -3734,7 +3748,7 @@ static void ShowExampleAppLog(bool* p_open) static ExampleAppLog log; // For the demo: add a debug button _BEFORE_ the normal log window contents - // We take advantage of the fact that multiple calls to Begin()/End() are appending to the same window. + // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window. // Most of the contents of the window will be added by the log.Draw() call. ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); ImGui::Begin("Example: Log", p_open); @@ -3752,6 +3766,7 @@ static void ShowExampleAppLog(bool* p_open) } ImGui::End(); + // Actually call in the regular Log helper (which will Begin() into the same window as we just did) log.Draw("Example: Log", p_open); } @@ -4040,7 +4055,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowViewport(viewport->ID); } - ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background + ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background if (ImGui::Begin("Example: Simple overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) { ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); @@ -4132,27 +4147,27 @@ static void ShowExampleAppCustomRendering(bool* p_open) { // First line uses a thickness of 1.0, second line uses the configurable thickness float th = (n == 0) ? 1.0f : thickness; - draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6, th); x += sz + spacing; // Hexagon - draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 20, th); x += sz + spacing; // Circle - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 0.0f, ImDrawCornerFlags_All, th); x += sz + spacing; + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6, th); x += sz + spacing; // Hexagon + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 20, th); x += sz + spacing; // Circle + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 0.0f, ImDrawCornerFlags_All, th); x += sz + spacing; draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_All, th); x += sz + spacing; draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight, th); x += sz + spacing; draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32, th); x += sz + spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col32, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, th); x += sz + spacing; // Diagonal line + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col32, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, th); x += sz + spacing; // Diagonal line draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col32, th); x = p.x + 4; y += sz + spacing; } - draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6); x += sz + spacing; // Hexagon - draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 32); x += sz + spacing; // Circle + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6); x += sz + spacing; // Hexagon + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 32); x += sz + spacing; // Circle draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32); x += sz + spacing; draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f); x += sz + spacing; draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight); x += sz + spacing; draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32); x += sz + spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col32); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col32); x += spacing + spacing; // Vertical line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col32); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col32); x += spacing + spacing; // Vertical line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col32); x += sz; // Pixel (faster than AddLine) draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); ImGui::Dummy(ImVec2((sz + spacing) * 9.5f, (sz + spacing) * 3)); @@ -4220,7 +4235,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImVec2 window_size = ImGui::GetWindowSize(); ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f); if (draw_bg) - ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 32, 10); + ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 32, 10+4); if (draw_fg) ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 32, 10); ImGui::EndTabItem(); diff --git a/imgui_internal.h b/imgui_internal.h index 2d48982b..b077bc65 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -320,7 +320,8 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_NoKeyModifiers = 1 << 10, // disable interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveID = 1 << 11, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) ImGuiButtonFlags_PressedOnDragDropHold = 1 << 12, // press when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) - ImGuiButtonFlags_NoNavFocus = 1 << 13 // don't override navigation focus when activated + ImGuiButtonFlags_NoNavFocus = 1 << 13, // don't override navigation focus when activated + ImGuiButtonFlags_NoHoveredOnNav = 1 << 14 // don't report as hovered when navigated on }; enum ImGuiSliderFlags_ @@ -382,7 +383,8 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0, ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, - ImGuiItemStatusFlags_Edited = 1 << 2 // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) + ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) + ImGuiItemStatusFlags_ToggledSelection = 1 << 3 // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues. #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui-test only] @@ -1008,7 +1010,7 @@ struct ImGuiContext ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 ImGuiID NavJustTabbedId; // Just tabbed to this id. ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). - ImGuiID NavJustMovedToSelectScopeId; // Just navigated to this select scope id (result of a successfully MoveRequest). + ImGuiID NavJustMovedToMultiSelectScopeId; // Just navigated to this select scope id (result of a successfully MoveRequest). ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. @@ -1180,7 +1182,7 @@ struct ImGuiContext NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavJustMovedToSelectScopeId = NavNextActivateId = 0; + NavJustTabbedId = NavJustMovedToId = NavJustMovedToMultiSelectScopeId = NavNextActivateId = 0; NavInputSource = ImGuiInputSource_None; NavScoringRectScreen = ImRect(); NavScoringCount = 0; @@ -1628,6 +1630,7 @@ namespace ImGui IMGUI_API void PushMultiItemsWidths(int components, float width_full = 0.0f); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); + IMGUI_API bool IsItemToggledSelection(); // was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7143c71a..d75b4184 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -495,7 +495,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Gamepad/Keyboard navigation // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) - hovered = true; + if (!(flags & ImGuiButtonFlags_NoHoveredOnNav)) + hovered = true; if (g.NavActivateDownId == id) { @@ -5083,6 +5084,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; + const bool was_selected = selected; + bool hovered, held; bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); bool toggled = false; @@ -5119,6 +5122,10 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) SetItemAllowOverlap(); + // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. + if (selected != was_selected) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); @@ -5306,15 +5313,15 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) bb.Max.x += window_padding.x; - // Selectables are tightly packed together, we extend the box to cover spacing between selectable. - float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); - float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); - float spacing_R = style.ItemSpacing.x - spacing_L; - float spacing_D = style.ItemSpacing.y - spacing_U; + // Selectables are tightly packed together so we extend the box to cover spacing between selectable. + const float spacing_x = style.ItemSpacing.x; + const float spacing_y = style.ItemSpacing.y; + const float spacing_L = (float)(int)(spacing_x * 0.50f); + const float spacing_U = (float)(int)(spacing_y * 0.50f); bb.Min.x -= spacing_L; bb.Min.y -= spacing_U; - bb.Max.x += spacing_R; - bb.Max.y += spacing_D; + bb.Max.x += (spacing_x - spacing_L); + bb.Max.y += (spacing_y - spacing_U); bool item_add; if (flags & ImGuiSelectableFlags_Disabled) @@ -5347,6 +5354,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_Disabled) selected = false; + const bool was_selected = selected; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) @@ -5362,6 +5370,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_AllowItemOverlap) SetItemAllowOverlap(); + // In this branch, Selectable() cannot toggle the selection so this will never trigger. + if (selected != was_selected) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + // Render if (hovered || selected) {