diff --git a/imgui.cpp b/imgui.cpp index 7e7dce8ac..d54db370b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1237,6 +1237,7 @@ static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt); // Misc static void UpdateSettings(); static int UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); +static void RenderWindowShadow(ImGuiWindow* window); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); @@ -6720,11 +6721,8 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Draw window shadow if (style.WindowShadowSize > 0.0f && (!(flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_Popup))) - { - float shadow_size = style.WindowShadowSize; - ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist; - window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_size, shadow_offset, GetColorU32(ImGuiCol_WindowShadow), window_rounding); - } + if (style.Colors[ImGuiCol_WindowShadow].w > 0.0f) + RenderWindowShadow(window); // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) @@ -6772,6 +6770,15 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } } +void ImGui::RenderWindowShadow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + float shadow_size = style.WindowShadowSize; + ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist; + window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_size, shadow_offset, GetColorU32(ImGuiCol_WindowShadow), window->WindowRounding); +} + // Render title text, collapse button, close button void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) { diff --git a/imgui.h b/imgui.h index f83078e1a..433328ab9 100644 --- a/imgui.h +++ b/imgui.h @@ -3123,9 +3123,11 @@ struct ImDrawList // Shadows primitives // [BETA] API - // FIXME-SHADOWS: high-level api to draw shadow without a hole? + // - Add a shadow for a rectangular object, with min-max giving the object extents, and offset shifting the shadow. Rounding parameters refer to the object itself, not the shadow. + // - In the vast majority of cases, filled shadows are unnecessary and wasteful. We still provide the primitives for consistency and flexibility. #define IMGUI_HAS_SHADOWS 1 - IMGUI_API void AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); // Add a shadow for a rectangular object, with min-max giving the object extents, and offset giving an offset to shift the shadow by. Rounding parameters refer to the object itself, not the shadow. + IMGUI_API void AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); + IMGUI_API void AddShadowRectFilled(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() // - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 17f7ea93c..d12a3bcef 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -9611,6 +9611,53 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Shadows")) + { + static float shadow_thickness = 40.0f; + static ImVec4 shadow_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + static bool shadow_filled = false; + static ImVec4 shape_color = ImVec4(0.9f, 0.9f, 0.9f, 1.0f); + static float shape_rounding = 0.0f; + ImGui::Checkbox("Shadow filled", &shadow_filled); + ImGui::SameLine(); + HelpMarker("This will fill the section behind the shape to shadow. It's often unnecessary and wasteful but provided for consistency."); + + ImGui::DragFloat("Shadow Thickness", &shadow_thickness, 1.0f, 0.0f, 100.0f, "%.02f"); + ImGui::ColorEdit4("Shadow Color", &shadow_color.x); + ImGui::ColorEdit4("Shape Color", &shape_color.x); + ImGui::DragFloat("Shape Rounding", &shape_rounding, 1.0f, 0.0f, 20.0f, "%.02f"); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + { + ImVec2 p = ImGui::GetCursorScreenPos(); + ImVec2 r1(p.x + 50.0f, p.y + 50.0f); + ImVec2 r2(p.x + 150.0f, p.y + 150.0f); + if (shadow_filled) + draw_list->AddShadowRectFilled(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color)); + else + draw_list->AddShadowRect(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color), shape_rounding); + draw_list->AddRectFilled(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding); + ImGui::Dummy(ImVec2(200.0f, 200.0f)); + } + { + // FIXME-SHADOWS: We properly need AddShadowCircle() api ? + ImVec2 p = ImGui::GetCursorScreenPos(); + float off = 10.0f; + ImVec2 r1(p.x + 50.0f + off, p.y + 50.0f + off); + ImVec2 r2(p.x + 150.0f - off, p.y + 150.0f - off); + ImVec2 c(p.x + 100.0f, p.y + 100.0f); + if (shadow_filled) + draw_list->AddShadowRectFilled(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color)); + else + draw_list->AddShadowRect(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color), 50.0f); + draw_list->AddCircleFilled(c, 50.0f, ImGui::GetColorU32(shape_color), 0); + ImGui::Dummy(ImVec2(200.0f, 200.0f)); + } + + + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("BG/FG draw lists")) { static bool draw_bg = true; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 0166217da..dd05ec529 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2169,7 +2169,7 @@ static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const static int ClipPolygonShape(ImVec2* src_points, int num_src_points, ImVec2* dest_points, int allocated_dest_points, ImVec2 clip_rect_min, ImVec2 clip_rect_max) { // Early-out with an empty result if clipping region is zero-sized - if ((clip_rect_max.x <= clip_rect_min.x) || (clip_rect_max.y <= clip_rect_min.y)) + if (clip_rect_max.x <= clip_rect_min.x || clip_rect_max.y <= clip_rect_min.y) return 0; // Early-out if there is no source geometry @@ -2312,7 +2312,7 @@ static int ClipPolygonShape(ImVec2* src_points, int num_src_points, ImVec2* dest static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const ImVec2& a_max, const ImVec2& a_min_uv, const ImVec2& a_max_uv, ImVec2* b_points, int num_b_points, ImU32 col) { // Early out without drawing anything if A is zero-size - if ((a_min.x >= a_max.x) || (a_min.y >= a_max.y)) + if (a_min.x >= a_max.x || a_min.y >= a_max.y) return; // First clip B to A @@ -2456,33 +2456,33 @@ static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const } } -void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners) +static void AddShadowRectEx(ImDrawList* draw_list, const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners, bool is_filled) { - if ((col & IM_COL32_A_MASK) == 0) - return; - ImVec2* inner_rect_points = NULL; // Points that make up the shape of the inner rectangle (used when it has rounded corners) - int num_inner_rect_points = 0; + int inner_rect_points_count = 0; // Generate a path describing the inner rectangle and copy it to our buffer const bool is_rounded = (rounding > 0.0f) && (rounding_corners != ImDrawCornerFlags_None); // Do we have rounded corners? - if (is_rounded) + if (is_rounded && !is_filled) { - _Path.Size = 0; - PathRect(p_min, p_max, rounding, rounding_corners); - num_inner_rect_points = _Path.Size; - inner_rect_points = (ImVec2*)alloca(num_inner_rect_points * sizeof(ImVec2)); //-V630 - memcpy(inner_rect_points, _Path.Data, num_inner_rect_points * sizeof(ImVec2)); - _Path.Size = 0; + IM_ASSERT(draw_list->_Path.Size == 0); + draw_list->PathRect(p_min, p_max, rounding, rounding_corners); + inner_rect_points_count = draw_list->_Path.Size; + inner_rect_points = (ImVec2*)alloca(inner_rect_points_count * sizeof(ImVec2)); //-V630 + memcpy(inner_rect_points, draw_list->_Path.Data, inner_rect_points_count * sizeof(ImVec2)); + draw_list->_Path.Size = 0; } + if (is_filled) + draw_list->PrimReserve(6 * 9, 4 * 9); + // Draw the relevant chunks of the texture (the texture is split into a 3x3 grid) for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { const int uv_index = x + (y + y + y); // y*3 formatted so as to ensure the compiler avoids an actual multiply - const ImVec4 uvs = _Data->ShadowRectUvs[uv_index]; + const ImVec4 uvs = draw_list->_Data->ShadowRectUvs[uv_index]; ImVec2 draw_min, draw_max; switch (x) @@ -2500,14 +2500,32 @@ void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float s ImVec2 uv_min(uvs.x, uvs.y); ImVec2 uv_max(uvs.z, uvs.w); - if (is_rounded) - AddSubtractedRect(this, draw_min + offset, draw_max + offset, uv_min, uv_max, inner_rect_points, num_inner_rect_points, col); // Complex path for rounded rectangles + if (is_filled) + draw_list->PrimRectUV(draw_min + offset, draw_max + offset, uv_min, uv_max, col); + else if (is_rounded) + AddSubtractedRect(draw_list, draw_min + offset, draw_max + offset, uv_min, uv_max, inner_rect_points, inner_rect_points_count, col); // Complex path for rounded rectangles else - AddSubtractedRect(this, draw_min + offset, draw_max + offset, uv_min, uv_max, p_min, p_max, col); // Simple fast path for non-rounded rectangles + AddSubtractedRect(draw_list, draw_min + offset, draw_max + offset, uv_min, uv_max, p_min, p_max, col); // Simple fast path for non-rounded rectangles } } } +void ImDrawList::AddShadowRectFilled(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + AddShadowRectEx(this, p_min, p_max, shadow_thickness, offset, col, 0.0f, ImDrawCornerFlags_None, true); +} + +void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + AddShadowRectEx(this, p_min, p_max, shadow_thickness, offset, col, rounding, rounding_corners, false); +} + //----------------------------------------------------------------------------- // [SECTION] ImDrawListSplitter diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b5d66baa0..6b3f55f22 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8685,10 +8685,14 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im } window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + + // Create window + PushStyleColor(ImGuiCol_WindowShadow, ImVec4(0, 0, 0, 0)); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint bool is_open = Begin(name, NULL, window_flags); PopStyleVar(2); + PopStyleColor(); return is_open; }