Skip to content

Commit b1aa3b0

Browse files
committed
Shadows: Demo code in Custom Rendering section. Added AddShadowRectFilled() variant. BeginMainMenuBar() disable shadows.
1 parent 256143d commit b1aa3b0

File tree

5 files changed

+101
-25
lines changed

5 files changed

+101
-25
lines changed

imgui.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,7 @@ static void UpdateMouseWheel();
882882
static void UpdateTabFocus();
883883
static void UpdateDebugToolItemPicker();
884884
static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
885+
static void RenderWindowShadow(ImGuiWindow* window);
885886
static void RenderWindowOuterBorders(ImGuiWindow* window);
886887
static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
887888
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
@@ -5358,11 +5359,8 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
53585359

53595360
// Draw window shadow
53605361
if (style.WindowShadowSize > 0.0f && (!(flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_Popup)))
5361-
{
5362-
float shadow_size = style.WindowShadowSize;
5363-
ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist;
5364-
window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_size, shadow_offset, GetColorU32(ImGuiCol_WindowShadow), window_rounding);
5365-
}
5362+
if (style.Colors[ImGuiCol_WindowShadow].w > 0.0f)
5363+
RenderWindowShadow(window);
53665364

53675365
// Title bar
53685366
if (!(flags & ImGuiWindowFlags_NoTitleBar))
@@ -5406,6 +5404,15 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
54065404
}
54075405
}
54085406

5407+
void ImGui::RenderWindowShadow(ImGuiWindow* window)
5408+
{
5409+
ImGuiContext& g = *GImGui;
5410+
ImGuiStyle& style = g.Style;
5411+
float shadow_size = style.WindowShadowSize;
5412+
ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist;
5413+
window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_size, shadow_offset, GetColorU32(ImGuiCol_WindowShadow), window->WindowRounding);
5414+
}
5415+
54095416
// Render title text, collapse button, close button
54105417
void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
54115418
{

imgui.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,9 +2375,11 @@ struct ImDrawList
23752375

23762376
// Shadows primitives
23772377
// [BETA] API
2378-
// FIXME-SHADOWS: high-level api to draw shadow without a hole?
2378+
// - 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.
2379+
// - In the vast majority of cases, filled shadows are unnecessary and wasteful. We still provide the primitives for consistency and flexibility.
23792380
#define IMGUI_HAS_SHADOWS 1
2380-
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.
2381+
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);
2382+
IMGUI_API void AddShadowRectFilled(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col);
23812383

23822384
// Stateful path API, add points then finish with PathFillConvex() or PathStroke()
23832385
inline void PathClear() { _Path.Size = 0; }

imgui_demo.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7045,6 +7045,53 @@ static void ShowExampleAppCustomRendering(bool* p_open)
70457045
ImGui::EndTabItem();
70467046
}
70477047

7048+
if (ImGui::BeginTabItem("Shadows"))
7049+
{
7050+
static float shadow_thickness = 40.0f;
7051+
static ImVec4 shadow_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
7052+
static bool shadow_filled = false;
7053+
static ImVec4 shape_color = ImVec4(0.9f, 0.9f, 0.9f, 1.0f);
7054+
static float shape_rounding = 0.0f;
7055+
ImGui::Checkbox("Shadow filled", &shadow_filled);
7056+
ImGui::SameLine();
7057+
HelpMarker("This will fill the section behind the shape to shadow. It's often unnecessary and wasteful but provided for consistency.");
7058+
7059+
ImGui::DragFloat("Shadow Thickness", &shadow_thickness, 1.0f, 0.0f, 100.0f, "%.02f");
7060+
ImGui::ColorEdit4("Shadow Color", &shadow_color.x);
7061+
ImGui::ColorEdit4("Shape Color", &shape_color.x);
7062+
ImGui::DragFloat("Shape Rounding", &shape_rounding, 1.0f, 0.0f, 20.0f, "%.02f");
7063+
7064+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
7065+
{
7066+
ImVec2 p = ImGui::GetCursorScreenPos();
7067+
ImVec2 r1(p.x + 50.0f, p.y + 50.0f);
7068+
ImVec2 r2(p.x + 150.0f, p.y + 150.0f);
7069+
if (shadow_filled)
7070+
draw_list->AddShadowRectFilled(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color));
7071+
else
7072+
draw_list->AddShadowRect(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color), shape_rounding);
7073+
draw_list->AddRectFilled(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);
7074+
ImGui::Dummy(ImVec2(200.0f, 200.0f));
7075+
}
7076+
{
7077+
// FIXME-SHADOWS: We properly need AddShadowCircle() api ?
7078+
ImVec2 p = ImGui::GetCursorScreenPos();
7079+
float off = 10.0f;
7080+
ImVec2 r1(p.x + 50.0f + off, p.y + 50.0f + off);
7081+
ImVec2 r2(p.x + 150.0f - off, p.y + 150.0f - off);
7082+
ImVec2 c(p.x + 100.0f, p.y + 100.0f);
7083+
if (shadow_filled)
7084+
draw_list->AddShadowRectFilled(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color));
7085+
else
7086+
draw_list->AddShadowRect(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color), 50.0f);
7087+
draw_list->AddCircleFilled(c, 50.0f, ImGui::GetColorU32(shape_color), 0);
7088+
ImGui::Dummy(ImVec2(200.0f, 200.0f));
7089+
}
7090+
7091+
7092+
ImGui::EndTabItem();
7093+
}
7094+
70487095
if (ImGui::BeginTabItem("BG/FG draw lists"))
70497096
{
70507097
static bool draw_bg = true;

imgui_draw.cpp

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,7 @@ static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const
15311531
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)
15321532
{
15331533
// Early-out with an empty result if clipping region is zero-sized
1534-
if ((clip_rect_max.x <= clip_rect_min.x) || (clip_rect_max.y <= clip_rect_min.y))
1534+
if (clip_rect_max.x <= clip_rect_min.x || clip_rect_max.y <= clip_rect_min.y)
15351535
return 0;
15361536

15371537
// Early-out if there is no source geometry
@@ -1674,7 +1674,7 @@ static int ClipPolygonShape(ImVec2* src_points, int num_src_points, ImVec2* dest
16741674
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)
16751675
{
16761676
// Early out without drawing anything if A is zero-size
1677-
if ((a_min.x >= a_max.x) || (a_min.y >= a_max.y))
1677+
if (a_min.x >= a_max.x || a_min.y >= a_max.y)
16781678
return;
16791679

16801680
// First clip B to A
@@ -1818,33 +1818,33 @@ static void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const
18181818
}
18191819
}
18201820

1821-
void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners)
1821+
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)
18221822
{
1823-
if ((col & IM_COL32_A_MASK) == 0)
1824-
return;
1825-
18261823
ImVec2* inner_rect_points = NULL; // Points that make up the shape of the inner rectangle (used when it has rounded corners)
1827-
int num_inner_rect_points = 0;
1824+
int inner_rect_points_count = 0;
18281825

18291826
// Generate a path describing the inner rectangle and copy it to our buffer
18301827
const bool is_rounded = (rounding > 0.0f) && (rounding_corners != ImDrawCornerFlags_None); // Do we have rounded corners?
1831-
if (is_rounded)
1828+
if (is_rounded && !is_filled)
18321829
{
1833-
_Path.Size = 0;
1834-
PathRect(p_min, p_max, rounding, rounding_corners);
1835-
num_inner_rect_points = _Path.Size;
1836-
inner_rect_points = (ImVec2*)alloca(num_inner_rect_points * sizeof(ImVec2)); //-V630
1837-
memcpy(inner_rect_points, _Path.Data, num_inner_rect_points * sizeof(ImVec2));
1838-
_Path.Size = 0;
1830+
IM_ASSERT(draw_list->_Path.Size == 0);
1831+
draw_list->PathRect(p_min, p_max, rounding, rounding_corners);
1832+
inner_rect_points_count = draw_list->_Path.Size;
1833+
inner_rect_points = (ImVec2*)alloca(inner_rect_points_count * sizeof(ImVec2)); //-V630
1834+
memcpy(inner_rect_points, draw_list->_Path.Data, inner_rect_points_count * sizeof(ImVec2));
1835+
draw_list->_Path.Size = 0;
18391836
}
18401837

1838+
if (is_filled)
1839+
draw_list->PrimReserve(6 * 9, 4 * 9);
1840+
18411841
// Draw the relevant chunks of the texture (the texture is split into a 3x3 grid)
18421842
for (int x = 0; x < 3; x++)
18431843
{
18441844
for (int y = 0; y < 3; y++)
18451845
{
18461846
const int uv_index = x + (y + y + y); // y*3 formatted so as to ensure the compiler avoids an actual multiply
1847-
const ImVec4 uvs = _Data->ShadowRectUvs[uv_index];
1847+
const ImVec4 uvs = draw_list->_Data->ShadowRectUvs[uv_index];
18481848

18491849
ImVec2 draw_min, draw_max;
18501850
switch (x)
@@ -1862,14 +1862,32 @@ void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float s
18621862

18631863
ImVec2 uv_min(uvs.x, uvs.y);
18641864
ImVec2 uv_max(uvs.z, uvs.w);
1865-
if (is_rounded)
1866-
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
1865+
if (is_filled)
1866+
draw_list->PrimRectUV(draw_min + offset, draw_max + offset, uv_min, uv_max, col);
1867+
else if (is_rounded)
1868+
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
18671869
else
1868-
AddSubtractedRect(this, draw_min + offset, draw_max + offset, uv_min, uv_max, p_min, p_max, col); // Simple fast path for non-rounded rectangles
1870+
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
18691871
}
18701872
}
18711873
}
18721874

1875+
void ImDrawList::AddShadowRectFilled(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col)
1876+
{
1877+
if ((col & IM_COL32_A_MASK) == 0)
1878+
return;
1879+
1880+
AddShadowRectEx(this, p_min, p_max, shadow_thickness, offset, col, 0.0f, ImDrawCornerFlags_None, true);
1881+
}
1882+
1883+
void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float shadow_thickness, const ImVec2& offset, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners)
1884+
{
1885+
if ((col & IM_COL32_A_MASK) == 0)
1886+
return;
1887+
1888+
AddShadowRectEx(this, p_min, p_max, shadow_thickness, offset, col, rounding, rounding_corners, false);
1889+
}
1890+
18731891

18741892
//-----------------------------------------------------------------------------
18751893
// [SECTION] ImDrawListSplitter

imgui_widgets.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6530,11 +6530,13 @@ bool ImGui::BeginMainMenuBar()
65306530
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
65316531
SetNextWindowPos(ImVec2(0.0f, 0.0f));
65326532
SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y));
6533+
PushStyleColor(ImGuiCol_WindowShadow, ImVec4(0, 0, 0, 0));
65336534
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
65346535
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
65356536
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
65366537
bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
65376538
PopStyleVar(2);
6539+
PopStyleColor();
65386540
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
65396541
if (!is_open)
65406542
{

0 commit comments

Comments
 (0)