diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 152d325..4429afa 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -14,6 +14,10 @@ #include #include #include +#include +#include + +#include "imgui_internal.h" #include "fonts/montserrat.c" // Data @@ -24,6 +28,8 @@ static bool g_SwapChainOccluded = false; static UINT g_ResizeWidth = 0, g_ResizeHeight = 0; static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr; +#define IM_PI 3.14159265358979323846f + // Forward declarations of helper functions bool CreateDeviceD3D(HWND hWnd); void CleanupDeviceD3D(); @@ -31,6 +37,24 @@ void CreateRenderTarget(); void CleanupRenderTarget(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +void draw_star(ImVec2 center, float outerRadius, float innerRadius, int numPoints, ImU32 color, float rotation) +{ + ImDrawList* drawList = ImGui::GetWindowDrawList(); + int totalPoints = numPoints * 2; + + for (int i = 0; i < totalPoints; i++) + { + float angle = rotation + ((float)i / totalPoints) * 2.0f * IM_PI - (IM_PI / 2.0f); + float radius = (i % 2 == 0) ? outerRadius : innerRadius; + drawList->PathLineTo(ImVec2( + center.x + cosf(angle) * radius, + center.y + sinf(angle) * radius + )); + } + + drawList->PathFillConcave(color); +} + // Main code int main(int, char**) { @@ -78,6 +102,10 @@ int main(int, char**) // Our state ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + bool test_bool = false; + float test_float = 0.f; + int test_int = 0; + // Main loop bool done = false; while (!done) @@ -133,7 +161,7 @@ int main(int, char**) auto draw = ImGui::GetWindowDrawList(); auto b_draw = ImGui::GetBackgroundDrawList(); - const int shadow_layers = 10; + /*const int shadow_layers = 10; const float shadow_size = 5.0f; for (int i = 0; i < shadow_layers; ++i) { @@ -146,13 +174,13 @@ int main(int, char**) IM_COL32(40, 46, 60, alpha), 10.0f + expand ); - } + }*/ draw->AddRectFilled(ImGui::GetWindowPos(), ImGui::GetWindowPos() + ImGui::GetWindowSize(), IM_COL32(40, 46, 60, 255), 10.f); // titlebar { const float title_h = 30.0f; - const float title_r = 10.0f; + const float title_r = 8.f; const ImU32 col_l = IM_COL32(129, 183, 255, 255); const ImU32 col_r = IM_COL32(137, 120, 255, 255); @@ -171,6 +199,89 @@ int main(int, char**) draw->AddCircleFilled(ImVec2(pos.x + title_r, pos.y + title_r), title_r, col_l, 24); draw->AddCircleFilled(ImVec2(pos.x + size.x - title_r, pos.y + title_r), title_r, col_r, 24); + + const float t = static_cast(ImGui::GetTime()); + + const int segments = 80; + const float amp = 3.5f; + const float freq = 6.0f; + const float phase_step = 0.8f; + ImU32 wave_color = IM_COL32(180, 200, 255, 140); + for (int l = 0; l < 3; ++l) + { + float y_base = pos.y + title_h * 0.55f + l * 3.5f; + draw->PathClear(); + for (int i = 0; i <= segments; ++i) + { + float u = static_cast(i) / static_cast(segments); + float x = pos.x + (size.x) * u; + float y = y_base + std::sinf(u * freq + t * 2.2f + l * phase_step) * amp; + draw->PathLineTo(ImVec2(x, y)); + } + draw->PathStroke(wave_color, false, 1.6f); + } + + static std::vector star_offsets; + static std::vector star_radii; + static std::vector star_rotations; + static std::vector star_opacities; + constexpr float kStarRadius = 7.5f; + constexpr float kStarMinDistance = 12.0f; + if (star_offsets.empty()) + { + star_offsets.reserve(16); + star_radii.reserve(16); + star_rotations.reserve(16); + star_opacities.reserve(16); + unsigned seed = 1234567u; + const char* title_text = "Mocha"; + float title_reserved = 12.0f + ImGui::CalcTextSize(title_text).x + 16.0f; + float x_min = ImClamp(title_reserved, 10.0f, size.x - 10.0f); + float x_max = size.x - 10.0f; + auto nextf = [&seed]() { + seed = seed * 1664525u + 1013904223u; + return (seed >> 8) * (1.0f / 16777216.0f); + }; + + const int star_count = 16; + float step = (x_max - x_min) / static_cast(star_count); + for (int i = 0; i < star_count; ++i) + { + float jitter = (nextf() - 0.5f) * step * 0.35f; + float x = ImClamp(x_min + (i + 0.5f) * step + jitter, x_min, x_max); + float y = 4.0f + nextf() * (title_h - 8.0f); + ImVec2 candidate(x, y); + + bool too_close = false; + for (const ImVec2& p : star_offsets) + { + if (ImLengthSqr(p - candidate) < kStarMinDistance * kStarMinDistance) + { + too_close = true; + break; + } + } + if (too_close) + continue; + + star_offsets.push_back(candidate); + star_radii.push_back(kStarRadius * (0.7f + nextf() * 0.6f)); + star_rotations.push_back(nextf() * (2.0f * IM_PI)); + star_opacities.push_back(0.55f + nextf() * 0.45f); + } + } + draw->PushClipRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + size.x, pos.y + title_h), true); + for (size_t i = 0; i < star_offsets.size(); ++i) + { + float offset = std::sinf(t * 1.5f + static_cast(i) * 0.6f) * 2.2f; + ImVec2 center = pos + star_offsets[i] + ImVec2(0.0f, offset); + float fade = star_opacities[i] * (0.85f + 0.15f * (0.5f + 0.5f * std::sinf(t * 1.5f + static_cast(i) * 0.6f))); + ImU32 star_color = ImGui::ColorConvertFloat4ToU32(ImVec4(1.0f, 1.0f, 1.0f, fade)); + draw_star(center, star_radii[i], star_radii[i] * 0.55f, 5, star_color, star_rotations[i]); + } + draw->PopClipRect(); + + ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + 2.f, ImGui::GetCursorPosY() - 2.f)); ImGui::Text("Mocha"); } @@ -213,6 +324,15 @@ int main(int, char**) ImGui::SetCursorPos(ImVec2(((size.x - text_size.x) * 0.5f), 5.f)); ImGui::Text(text); } + + // content + { + ImGui::SetCursorPos(ImVec2(10, 40)); + ImGui::Checkbox("Test Toggle", &test_bool); + + ImGui::SetCursorPos(ImVec2(10, ImGui::GetCursorPosY() + 25.f));; ImGui::SliderFloat("Test Slider (float)", &test_float, 0.f, 100.f); + ImGui::SetCursorPosX(10); ImGui::SliderInt("Test Slider (int)", &test_int, 0, 100); + } } ImGui::EndChild(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 55d264d..9c80f69 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1128,7 +1128,7 @@ bool ImGui::Checkbox(const char* label, bool* v) const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float square_sz = GetFrameHeight(); + const float square_sz = GetFrameHeight() - 2.5f; const ImVec2 pos = window->DC.CursorPos; const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); ItemSize(total_bb, style.FramePadding.y); @@ -1146,27 +1146,34 @@ bool ImGui::Checkbox(const char* label, bool* v) MarkItemEdited(id); } - const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); + const ImRect check_bb(pos + ImVec2(0.f, 2.5f), pos + ImVec2(square_sz, square_sz) + ImVec2(0.f, 2.5f)); RenderNavHighlight(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + RenderFrame(check_bb.Min, check_bb.Max, IM_COL32(55, 60, 75, 255), true, 5.f); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0; - if (mixed_value) + if (*v) { - // Undocumented tristate/mixed/indeterminate checkbox (#2644) - // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) - ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); - window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); - } - else if (*v) - { - const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); - RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); + const ImU32 grad_tl = IM_COL32(133, 151, 255, 255); + const ImU32 grad_tr = IM_COL32(133, 151, 255, 255); + const ImU32 grad_br = IM_COL32(113, 131, 235, 255); + const ImU32 grad_bl = IM_COL32(113, 131, 235, 255); + + window->DrawList->AddRectFilledMultiColor(check_bb.Min, check_bb.Max, grad_tl, grad_tr, grad_br, grad_bl); + for (int i = 0; i < 10; ++i) + { + const float t = (float)i / 9.0f; + const float r = 1.0f + t * 8.0f; + const float falloff = (1.0f - t) * (1.0f - t); // smoother fade + const int a = (int)(85.0f * falloff); + const float rounding = 5.0f + t * 3.0f; + window->DrawList->AddRect(check_bb.Min - ImVec2(r, r), check_bb.Max + ImVec2(r, r), IM_COL32(133, 151, 255, a), rounding, ImDrawFlags_None, 1.0f); + } + window->DrawList->AddRect(check_bb.Min - ImVec2(1.0f, 1.0f), check_bb.Max + ImVec2(1.0f, 1.0f), IM_COL32(133, 151, 255, 220), 5.0f, ImDrawFlags_None, 1.5f); + RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(3.0f, 3.0f), IM_COL32(255, 255, 255, 255), square_sz - 6.0f); } - ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); - if (g.LogEnabled) - LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); + + ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x + 5.f, check_bb.Min.y + style.FramePadding.y); if (label_size.x > 0.0f) RenderText(label_pos, label); @@ -3061,8 +3068,17 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const float w = CalcItemWidth(); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f) - ImVec2(0.f, 19.f)); + const ImVec2 label_pos(frame_bb.Min.x, frame_bb.Min.y - label_size.y - style.ItemInnerSpacing.y); + + ImVec2 total_min = frame_bb.Min; + ImVec2 total_max = frame_bb.Max; + if (label_size.x > 0.0f) + { + total_min.y = ImMin(total_min.y, label_pos.y); + total_max.x = ImMax(total_max.x, label_pos.x + label_size.x); + } + const ImRect total_bb(total_min, total_max); const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); @@ -3105,7 +3121,43 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + RenderFrame(frame_bb.Min, frame_bb.Max, IM_COL32(55, 60, 75, 255), true, style.FrameRounding); + + // dude come on + double v_cur = 0.0, v_min_d = 0.0, v_max_d = 1.0; + switch (data_type) + { + case ImGuiDataType_S8: v_cur = (double)*(const ImS8*)p_data; v_min_d = (double)*(const ImS8*)p_min; v_max_d = (double)*(const ImS8*)p_max; break; + case ImGuiDataType_U8: v_cur = (double)*(const ImU8*)p_data; v_min_d = (double)*(const ImU8*)p_min; v_max_d = (double)*(const ImU8*)p_max; break; + case ImGuiDataType_S16: v_cur = (double)*(const ImS16*)p_data; v_min_d = (double)*(const ImS16*)p_min; v_max_d = (double)*(const ImS16*)p_max; break; + case ImGuiDataType_U16: v_cur = (double)*(const ImU16*)p_data; v_min_d = (double)*(const ImU16*)p_min; v_max_d = (double)*(const ImU16*)p_max; break; + case ImGuiDataType_S32: v_cur = (double)*(const ImS32*)p_data; v_min_d = (double)*(const ImS32*)p_min; v_max_d = (double)*(const ImS32*)p_max; break; + case ImGuiDataType_U32: v_cur = (double)*(const ImU32*)p_data; v_min_d = (double)*(const ImU32*)p_min; v_max_d = (double)*(const ImU32*)p_max; break; + case ImGuiDataType_S64: v_cur = (double)*(const ImS64*)p_data; v_min_d = (double)*(const ImS64*)p_min; v_max_d = (double)*(const ImS64*)p_max; break; + case ImGuiDataType_U64: v_cur = (double)*(const ImU64*)p_data; v_min_d = (double)*(const ImU64*)p_min; v_max_d = (double)*(const ImU64*)p_max; break; + case ImGuiDataType_Float: v_cur = (double)*(const float*)p_data; v_min_d = (double)*(const float*)p_min; v_max_d = (double)*(const float*)p_max; break; + case ImGuiDataType_Double: v_cur = *(const double*)p_data; v_min_d = *(const double*)p_min; v_max_d = *(const double*)p_max; break; + default: break; + } + + float t = 0.0f; + if (v_max_d != v_min_d) + { + t = ImSaturate((float)((v_cur - v_min_d) / (v_max_d - v_min_d))); + t = ImFloor(t * 25.0f + 0.5f) / 25.0f; + } + + const float fill_x = ImFloor(ImLerp(frame_bb.Min.x, frame_bb.Max.x, t) * 25.0f + 0.5f) / 25.0f; + if (fill_x > frame_bb.Min.x) + { + ImDrawList* draw_list = window->DrawList; + const ImVec2 fill_max(fill_x, frame_bb.Max.y); + const int vert_start_idx = draw_list->VtxBuffer.Size; + draw_list->AddRectFilled(frame_bb.Min, fill_max, IM_COL32(255, 255, 255, 255), 25.0f); + const int vert_end_idx = draw_list->VtxBuffer.Size; + ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, frame_bb.Min, fill_max, IM_COL32(129, 183, 255, 255), IM_COL32(137, 120, 255, 255)); + } // Slider behavior ImRect grab_bb; @@ -3113,19 +3165,39 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (value_changed) MarkItemEdited(id); - // Render grab - if (grab_bb.Max.x > grab_bb.Min.x) - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); + + ImGuiStorage* storage = GetStateStorage(); + const ImGuiID value_anim_id = id ^ 0x7A9C31F2; + float value_pop_t = storage->GetFloat(value_anim_id, 0.0f); + + if (hovered) + value_pop_t = ImMin(value_pop_t + 6.0f * g.IO.DeltaTime, 1.0f); + else + value_pop_t = ImMax(value_pop_t - 8.0f * g.IO.DeltaTime, 0.0f); + + storage->SetFloat(value_anim_id, value_pop_t); + + if (value_pop_t > 0.0f) + { + const ImVec2 value_size = CalcTextSize(value_buf, NULL, true); + const float x = frame_bb.Max.x - value_size.x - style.FramePadding.x; + const float y_hidden = frame_bb.Max.y + style.FramePadding.y + value_size.y; + const float y_shown = frame_bb.Min.y - value_size.y - 3.5f; + const float pop = 1.0f - ImPow(1.0f - value_pop_t, 3.0f); + const ImVec2 value_pos(x, ImLerp(y_hidden, y_shown, pop)); + + // Clip so text inside the slider is masked, creating a "behind slider" effect. + window->DrawList->PushClipRect(window->ClipRect.Min, ImVec2(window->ClipRect.Max.x, frame_bb.Min.y), true); + RenderText(value_pos, value_buf); + window->DrawList->PopClipRect(); + } if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + RenderText(label_pos, label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0)); return value_changed;