#include "../headers/includes.h" #include #include // https://discord.gg/jxqVWub6Dk struct color_picker_state { bool init_val; bool open; float h, s, v; float a; float display_h, display_s, display_v; float display_a; float popup_alpha; float hover_alpha; c_vec4 last_color; }; static float color_unit(float value) { return ImClamp(value, 0.f, 1.f); } static int color_byte(float value) { return static_cast(color_unit(value) * 255.f + 0.5f); } static c_vec4 clamped_color(c_vec4 color) { color.x = color_unit(color.x); color.y = color_unit(color.y); color.z = color_unit(color.z); color.w = color_unit(color.w); return color; } static bool color_changed_externally(const c_vec4& a, const c_vec4& b) { return ImFabs(a.x - b.x) > 0.001f || ImFabs(a.y - b.y) > 0.001f || ImFabs(a.z - b.z) > 0.001f || ImFabs(a.w - b.w) > 0.001f; } static void sync_color_picker_state(color_picker_state* state, const c_vec4& color) { const float previous_h = state->h; const bool had_value = state->init_val; ImGui::ColorConvertRGBtoHSV(color.x, color.y, color.z, state->h, state->s, state->v); if (had_value && state->s <= 0.0001f) state->h = previous_h; state->a = color.w; state->display_h = state->h; state->display_s = state->s; state->display_v = state->v; state->display_a = state->a; state->last_color = color; state->init_val = true; } static c_vec4 color_from_picker_state(const color_picker_state* state) { c_vec4 result; ImGui::ColorConvertHSVtoRGB(state->h, state->s, state->v, result.x, result.y, result.z); result.w = state->a; return clamped_color(result); } static void format_color_hex(char* buffer, int buffer_size, const c_vec4& color, bool include_alpha) { if (include_alpha) { ImFormatString(buffer, buffer_size, "#%02X%02X%02X%02X", color_byte(color.x), color_byte(color.y), color_byte(color.z), color_byte(color.w)); return; } ImFormatString(buffer, buffer_size, "#%02X%02X%02X", color_byte(color.x), color_byte(color.y), color_byte(color.z)); } static void draw_alpha_base(ImDrawList* draw_list, const c_rect& rect, float rounding) { draw->rect_filled(draw_list, rect.Min, rect.Max, draw->get_clr(clr->widget), rounding); draw->rect_filled_multi_color(draw_list, rect.Min, rect.Max, draw->get_clr(clr->white, 0.035f), draw->get_clr(clr->black, 0.045f), draw->get_clr(clr->black, 0.045f), draw->get_clr(clr->white, 0.035f), rounding); } static void draw_color_swatch(ImDrawList* draw_list, const c_rect& rect, const c_vec4& color, float rounding) { draw_alpha_base(draw_list, rect, rounding); draw->rect_filled(draw_list, rect.Min, rect.Max, draw->get_clr(color), rounding); } static void draw_hue_bar(ImDrawList* draw_list, const c_rect& rect, float rounding) { const int segment_count = 6; for (int i = 0; i < segment_count; i++) { const float y0 = rect.Min.y + rect.GetHeight() * (static_cast(i) / static_cast(segment_count)); const float y1 = rect.Min.y + rect.GetHeight() * (static_cast(i + 1) / static_cast(segment_count)); const c_vec4 top = ImColor::HSV(static_cast(i) / static_cast(segment_count), 1.f, 1.f).Value; const c_vec4 bottom = ImColor::HSV(static_cast(i + 1) / static_cast(segment_count), 1.f, 1.f).Value; const draw_flags flags = i == 0 ? draw_flags_round_corners_top : i == segment_count - 1 ? draw_flags_round_corners_bottom : draw_flags_round_corners_none; const float segment_rounding = i == 0 || i == segment_count - 1 ? rounding : 0.f; draw->rect_filled_multi_color(draw_list, c_vec2(rect.Min.x, y0), c_vec2(rect.Max.x, y1), draw->get_clr(top), draw->get_clr(top), draw->get_clr(bottom), draw->get_clr(bottom), segment_rounding, flags); } } static void draw_color_bar_handle(ImDrawList* draw_list, const c_rect& rect) { draw->shadow_rect(draw_list, rect.Min, rect.Max, draw->get_clr(clr->black, 0.22f), s_(5), c_vec2(0, 0), 0, s_(999)); draw->rect_filled(draw_list, rect.Min, rect.Max, draw->get_clr(clr->white, 0.96f), s_(999)); draw->rect_filled(draw_list, rect.Min + s_(2, 2), rect.Max - s_(2, 2), draw->get_clr(clr->black, 0.10f), s_(999)); } static bool color_rect_button(const c_rect& rect, c_id id, bool* held) { if (!gui->item_add(rect, id)) return false; bool hovered = false; return gui->button_behavior(rect, id, &hovered, held); } bool c_widgets::color_picker(std::string name, c_vec4* color) { if (!color) return false; c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); color_picker_state* state = gui->anim_container(id); *color = clamped_color(*color); if (!state->init_val || color_changed_externally(*color, state->last_color)) sync_color_picker_state(state, *color); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(50)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(0, 8), rect.Max - s_(0, 8)); c_rect swatch = c_rect(c_vec2(inner.Max.x - s_(44), inner.GetCenter().y - s_(11)), c_vec2(inner.Max.x, inner.GetCenter().y + s_(11))); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool held = false; bool pressed = gui->button_behavior(rect, id, &hovered, &held); if (pressed) state->open = !state->open; gui->easing(state->hover_alpha, hovered || state->open ? 1.f : 0.f, 10.f, static_easing); char hex[16]; format_color_hex(hex, IM_ARRAYSIZE(hex), *color, color->w < 0.995f); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), inner.Min, inner.Max - s_(64, 0), draw->get_clr(hovered || state->open ? clr->white.Value : clr->text.Value), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), inner.Min + s_(0, 17), inner.Max - s_(64, 0), draw->get_clr(clr->text), hex, 0, 0, { 0, 0 }); draw->shadow_rect(window->DrawList, swatch.Min + s_(2, 3), swatch.Max - s_(2, 0), draw->get_clr(clr->black, 0.24f * state->hover_alpha), s_(9), c_vec2(0, 0), 0, s_(8)); draw->rect_filled(window->DrawList, swatch.Min - s_(2, 2), swatch.Max + s_(2, 2), draw->get_clr(clr->widget), s_(999)); draw_color_swatch(window->DrawList, swatch, *color, s_(999)); if (gui->content_avail().y > 0) { draw->line(window->DrawList, rect.GetBL(), rect.GetBR(), draw->get_clr(clr->border)); } gui->easing(state->popup_alpha, state->open ? 1.f : 0.f, 11.f, dynamic_easing); bool changed = false; if (state->open || state->popup_alpha > 0.f) { const float popup_width = ImClamp(rect.GetWidth(), s_(220), s_(286)); const c_vec2 popup_size = c_vec2(popup_width, s_(208)); const c_vec2 popup_pos = gui->adjust_window_pos(rect.GetBL() + s_(0, 6) - s_(0, 8) * (1.f - state->popup_alpha), popup_size); const std::string popup_name = name + "##color_popup"; gui->push_var(style_var_alpha, state->popup_alpha); gui->push_var(style_var_window_padding, s_(0, 0)); gui->push_var(style_var_item_spacing, s_(0, 0)); gui->set_next_window_pos(popup_pos); gui->set_next_window_size(popup_size); window_flags popup_flags = window_flags_no_decoration | window_flags_no_scrollbar | window_flags_no_scroll_with_mouse | window_flags_no_move | window_flags_no_background | window_flags_no_focus_on_appearing; if (!state->open) popup_flags |= window_flags_no_inputs; gui->begin(popup_name, nullptr, popup_flags); { if (state->open) gui->set_window_focus(); c_window* popup = gui->get_window(); const c_rect popup_rect = popup->Rect(); const c_vec4 live_color = color_from_picker_state(state); draw->shadow_rect(popup->DrawList, popup_rect.Min, popup_rect.Max, draw->get_clr(clr->black, 0.24f), s_(16), s_(0, 7), 0, s_(16)); draw->rect_filled(popup->DrawList, popup_rect.Min, popup_rect.Max, draw->get_clr(clr->child), s_(16)); c_rect header = c_rect(popup_rect.Min + s_(10, 8), popup_rect.Max - s_(10, 0)); header.Max.y = header.Min.y + s_(28); const c_rect preview = c_rect(header.Min, header.Min + s_(32, 22)); char live_hex[16]; char rgba[64]; format_color_hex(live_hex, IM_ARRAYSIZE(live_hex), live_color, live_color.w < 0.995f); ImFormatString(rgba, IM_ARRAYSIZE(rgba), "R %d G %d B %d A %d%%", color_byte(live_color.x), color_byte(live_color.y), color_byte(live_color.z), static_cast(color_unit(live_color.w) * 100.f + 0.5f)); draw_color_swatch(popup->DrawList, preview, live_color, s_(999)); draw->text_clipped(popup->DrawList, font->get(inter_semibold, 11), header.Min + s_(41, 0), header.Max, draw->get_clr(clr->white), live_hex, 0, 0, { 0, 0 }); draw->text_clipped(popup->DrawList, font->get(inter_medium, 10), header.Min + s_(41, 14), header.Max, draw->get_clr(clr->text), rgba, 0, 0, { 0, 0 }); const float pad = s_(10); const float gap = s_(8); const float hue_width = s_(14); const float picker_height = s_(94); const c_rect sv_rect = c_rect(popup_rect.Min + c_vec2(pad, s_(43)), c_vec2(popup_rect.Max.x - pad - hue_width - gap, popup_rect.Min.y + s_(43) + picker_height)); const c_rect hue_rect = c_rect(c_vec2(sv_rect.Max.x + gap, sv_rect.Min.y), c_vec2(sv_rect.Max.x + gap + hue_width, sv_rect.Max.y)); const c_rect alpha_rect = c_rect(c_vec2(sv_rect.Min.x, sv_rect.Max.y + s_(10)), c_vec2(hue_rect.Max.x, sv_rect.Max.y + s_(23))); const c_vec4 hue_color = ImColor::HSV(state->h, 1.f, 1.f).Value; const float sv_rounding = s_(12); const c_rect sv_fill = sv_rect; draw->rect_filled_multi_color(popup->DrawList, sv_fill.Min, sv_fill.Max, draw->get_clr(clr->white), draw->get_clr(hue_color), draw->get_clr(clr->black), draw->get_clr(clr->black), sv_rounding); bool sv_held = false; const bool sv_pressed = color_rect_button(sv_rect, popup->GetID("sv"), &sv_held); if (sv_held || sv_pressed) { state->s = color_unit((gui->mouse_pos().x - sv_rect.Min.x) / sv_rect.GetWidth()); state->v = 1.f - color_unit((gui->mouse_pos().y - sv_rect.Min.y) / sv_rect.GetHeight()); changed = true; } draw_hue_bar(popup->DrawList, hue_rect, s_(999)); bool hue_held = false; const bool hue_pressed = color_rect_button(hue_rect, popup->GetID("hue"), &hue_held); if (hue_held || hue_pressed) { state->h = color_unit((gui->mouse_pos().y - hue_rect.Min.y) / hue_rect.GetHeight()); changed = true; } draw_alpha_base(popup->DrawList, alpha_rect, s_(999)); draw->rect_filled_multi_color(popup->DrawList, alpha_rect.Min, alpha_rect.Max, draw->get_clr(c_vec4(live_color.x, live_color.y, live_color.z, 0.f)), draw->get_clr(c_vec4(live_color.x, live_color.y, live_color.z, 1.f)), draw->get_clr(c_vec4(live_color.x, live_color.y, live_color.z, 1.f)), draw->get_clr(c_vec4(live_color.x, live_color.y, live_color.z, 0.f)), s_(999)); bool alpha_held = false; const bool alpha_pressed = color_rect_button(alpha_rect, popup->GetID("alpha"), &alpha_held); if (alpha_held || alpha_pressed) { state->a = color_unit((gui->mouse_pos().x - alpha_rect.Min.x) / alpha_rect.GetWidth()); changed = true; } static const c_vec4 presets[] = { c_vec4(1.00f, 0.26f, 0.32f, 1.f), c_vec4(1.00f, 0.58f, 0.22f, 1.f), c_vec4(1.00f, 0.88f, 0.25f, 1.f), c_vec4(0.34f, 0.86f, 0.55f, 1.f), c_vec4(0.26f, 0.76f, 0.94f, 1.f), c_vec4(0.42f, 0.55f, 1.00f, 1.f), c_vec4(0.72f, 0.44f, 1.00f, 1.f), c_vec4(1.00f, 1.00f, 1.00f, 1.f), }; const int preset_count = IM_ARRAYSIZE(presets); const float chip_gap = s_(5); const float chip_height = s_(13); const float chip_width = (alpha_rect.GetWidth() - chip_gap * static_cast(preset_count - 1)) / static_cast(preset_count); const float chip_y = alpha_rect.Max.y + s_(13); for (int i = 0; i < preset_count; i++) { const c_rect chip = c_rect(c_vec2(alpha_rect.Min.x + (chip_width + chip_gap) * static_cast(i), chip_y), c_vec2(alpha_rect.Min.x + (chip_width + chip_gap) * static_cast(i) + chip_width, chip_y + chip_height)); const c_id chip_id = popup->GetID(1000 + i); const bool selected = ImFabs(live_color.x - presets[i].x) + ImFabs(live_color.y - presets[i].y) + ImFabs(live_color.z - presets[i].z) < 0.10f; bool chip_held = false; const bool chip_pressed = color_rect_button(chip, chip_id, &chip_held); draw_color_swatch(popup->DrawList, chip, presets[i], s_(999)); if (selected || chip_held) { draw->rect_filled(popup->DrawList, chip.Min - s_(2, 2), chip.Max + s_(2, 2), draw->get_clr(chip_held ? clr->white.Value : clr->accent.Value, 0.18f), s_(999)); draw_color_swatch(popup->DrawList, chip, presets[i], s_(999)); } if (chip_pressed) { ImGui::ColorConvertRGBtoHSV(presets[i].x, presets[i].y, presets[i].z, state->h, state->s, state->v); changed = true; } } gui->easing(state->display_s, state->s, 28.f, dynamic_easing); gui->easing(state->display_v, state->v, 28.f, dynamic_easing); gui->easing(state->display_h, state->h, 28.f, dynamic_easing); gui->easing(state->display_a, state->a, 28.f, dynamic_easing); const float sv_cursor_radius = s_(5.5f); const float sv_cursor_inset = sv_cursor_radius + s_(1); const c_vec2 raw_sv_cursor = c_vec2(sv_rect.Min.x + sv_rect.GetWidth() * state->display_s, sv_rect.Min.y + sv_rect.GetHeight() * (1.f - state->display_v)); const c_vec2 sv_cursor = c_vec2( ImClamp(raw_sv_cursor.x, sv_fill.Min.x + sv_cursor_inset, sv_fill.Max.x - sv_cursor_inset), ImClamp(raw_sv_cursor.y, sv_fill.Min.y + sv_cursor_inset, sv_fill.Max.y - sv_cursor_inset)); draw->shadow_circle(popup->DrawList, sv_cursor, s_(6), draw->get_clr(clr->black, 0.30f), s_(4), c_vec2(0, 0)); draw->circle_filled(popup->DrawList, sv_cursor, sv_cursor_radius, draw->get_clr(clr->white, 0.98f), 32); draw->circle_filled(popup->DrawList, sv_cursor, s_(3.1f), draw->get_clr(clr->black, 0.13f), 32); const float hue_y = hue_rect.Min.y + hue_rect.GetHeight() * state->display_h; draw_color_bar_handle(popup->DrawList, c_rect(c_vec2(hue_rect.Min.x - s_(3), hue_y - s_(4)), c_vec2(hue_rect.Max.x + s_(3), hue_y + s_(4)))); const float alpha_x = alpha_rect.Min.x + alpha_rect.GetWidth() * state->display_a; draw_color_bar_handle(popup->DrawList, c_rect(c_vec2(alpha_x - s_(4), alpha_rect.Min.y - s_(3)), c_vec2(alpha_x + s_(4), alpha_rect.Max.y + s_(3)))); if (state->open && !popup_rect.Contains(gui->mouse_pos()) && !rect.Contains(gui->mouse_pos()) && gui->mouse_clicked(0)) state->open = false; } gui->end(); gui->pop_var(3); } if (changed) { *color = color_from_picker_state(state); state->last_color = *color; } return changed; } struct slider_state { float normalized; float hover_alpha; c_vec4 text; c_vec4 value; c_vec4 track; }; custom_slider_t c_widgets::custom_slider(std::string name, float* callback, float vmin, float vmax, float width) { custom_slider_t data; c_window* window = gui->get_window(); if (window->SkipItems) return data; c_id id = window->GetID(name.data()); slider_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(width, s_(12)); c_rect rect = c_rect(pos, pos + size); data.rect = rect; gui->item_size(rect); if (!gui->item_add(rect, id)) return data; bool held, pressed = gui->button_behavior(rect, id, nullptr, &held); data.held = held; const float normalized = ImSaturate((gui->mouse_pos().x - rect.Min.x) / rect.GetWidth()); if (held) { *callback = vmin + normalized * (vmax - vmin); } gui->easing(state->normalized, ImSaturate((*callback - vmin) / (vmax - vmin)), 18.f, dynamic_easing); float x = rect.GetWidth() * state->normalized; x = ImClamp(x, s_(12), rect.GetWidth()); data.active = c_rect(rect.Min, rect.Min + c_vec2(x, rect.GetHeight())); data.circle_pos = c_vec2(rect.Min.x + x - s_(6), rect.GetCenter().y); return data; } bool c_widgets::slider(std::string name, std::string description, float* callback, float vmin, float vmax, std::string format) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); slider_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(68)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(0, 10), rect.Max - s_(0, 10)); c_rect button = c_rect(inner.GetBL() - s_(0, 12), inner.GetBR()); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool held, pressed = gui->button_behavior(button, id, nullptr, &held); const float normalized = ImSaturate((gui->mouse_pos().x - button.Min.x) / button.GetWidth()); if (held) { *callback = vmin + normalized * (vmax - vmin); } gui->easing(state->normalized, ImSaturate((*callback - vmin) / (vmax - vmin)), 18.f, dynamic_easing); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), inner.Min, inner.Max, draw->get_clr(clr->white), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), inner.Min + s_(0, 17), inner.Max, draw->get_clr(clr->text), description.data(), 0, 0, { 0, 0 }); char value_buf[64]; gui->get_fmt(value_buf, callback, format); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), inner.Min, inner.Max, draw->get_clr(clr->white), value_buf, 0, 0, { 1, 0 }); draw->rect_filled(window->DrawList, button.Min, button.Max, draw->get_clr(clr->widget), s_(999)); { float x = button.GetWidth() * state->normalized; x = ImClamp(x, s_(12), button.GetWidth()); draw->rect_filled(window->DrawList, button.Min, button.Min + c_vec2(x, button.GetHeight()), draw->get_clr(clr->accent), s_(999)); draw->circle_filled(window->DrawList, c_vec2(button.Min.x + x - s_(6), button.GetCenter().y), s_(4), draw->get_clr(clr->black), s_(999)); } if (gui->content_avail().y > 0) { draw->line(window->DrawList, rect.GetBL(), rect.GetBR(), draw->get_clr(clr->border)); } return held; } struct selectable_state { float alpha; c_vec4 text; }; bool selectable_ex(std::string name, bool is_selected) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); selectable_state* state = gui->anim_container< selectable_state>(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(29)); c_rect rect = c_rect(pos, pos + size); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool pressed = gui->button_behavior(rect, id, &hovered, nullptr); gui->easing(state->text, is_selected ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); gui->easing(state->alpha, is_selected ? 1.f : 0.f, 7.f, static_easing); if (gui->content_avail().y > 0) { draw->line(window->DrawList, rect.GetBL() + s_(10, 0), rect.GetBR() - s_(10, 0), draw->get_clr(clr->border)); } //draw->rect_filled(window->DrawList, rect.Min, rect.Max, draw->get_clr(clr->cirlce, state->alpha), s_(2)); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), rect.Min + s_(10, 0), rect.Max, draw->get_clr(state->text), name.data(), 0, 0, { 0, 0.5 }); return pressed; } struct dropdown_state { bool open; float alpha; float hover_alpha; c_vec4 background; c_vec4 text; c_vec4 arrow; }; static void draw_dropdown_chevron(ImDrawList* draw_list, c_vec2 center, bool open, ImU32 color) { const c_rect icon_rect(center - s_(8, 8), center + s_(8, 8)); ImFont* chevron_font = font->get_file("C:\\Windows\\Fonts\\segmdl2.ttf", 11.f, true); if (chevron_font) { draw->text_clipped(draw_list, chevron_font, icon_rect.Min, icon_rect.Max, color, open ? "\xEE\x9C\x8E" : "\xEE\x9C\x8D", 0, 0, { 0.5f, 0.5f }); return; } draw->text_clipped(draw_list, font->get(inter_semibold, 12), icon_rect.Min, icon_rect.Max, color, open ? "^" : "v", 0, 0, { 0.5f, 0.5f }); } bool dropdown_ex(std::string name, std::string description, std::string preview) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); dropdown_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(84)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(0, 10), rect.Max - s_(0, 10)); c_rect button = c_rect(inner.GetBL() - s_(0, 29), inner.GetBR()); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool held = false; bool pressed = gui->button_behavior(button, id, &hovered, &held); if (pressed) state->open = true; draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), inner.Min, inner.Max, draw->get_clr(clr->white), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), inner.Min + s_(0, 17), inner.Max, draw->get_clr(clr->text), description.data(), 0, 0, { 0, 0 }); draw->rect_filled(window->DrawList, button.Min, button.Max, draw->get_clr(clr->widget), s_(8)); draw->line(window->DrawList, c_vec2(button.Max.x - s_(28), button.Min.y + s_(8)), c_vec2(button.Max.x - s_(28), button.Max.y - s_(8)), draw->get_clr(clr->border, 0.9f)); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), button.Min + s_(10, 0), button.Max - s_(28, 0), draw->get_clr(clr->white), preview.data(), 0, 0, { 0, 0.5 }); const c_vec2 arrow_center(button.Max.x - s_(14), button.GetCenter().y); const ImU32 arrow_color = state->open ? draw->get_clr(clr->accent) : draw->get_clr((hovered || held) ? clr->white.Value : clr->text.Value, 0.96f); draw_dropdown_chevron(window->DrawList, arrow_center, state->open, arrow_color); if (gui->content_avail().y > 0) { draw->line(window->DrawList, rect.GetBL(), rect.GetBR(), draw->get_clr(clr->border)); } gui->easing(state->alpha, state->open ? 1.f : 0.f, 11.f, dynamic_easing); if (state->alpha > 0) { gui->push_var(style_var_alpha, state->alpha); gui->push_var(style_var_window_padding, s_(0, 0)); gui->push_var(style_var_item_spacing, s_(0, 0)); gui->set_next_window_pos(button.Min + s_(0, 10) * (1.f - state->alpha) + c_vec2(0, button.GetHeight() + s_(10))); gui->set_next_window_size(c_vec2(button.GetWidth(), 0)); window_flags popup_flags = window_flags_always_auto_resize | window_flags_no_decoration | window_flags_no_scrollbar | window_flags_no_scroll_with_mouse | window_flags_no_move | window_flags_no_background; if (!state->open) popup_flags |= window_flags_no_inputs; gui->begin(name, nullptr, popup_flags); { if (state->open) gui->set_window_focus(); window = gui->get_window(); rect = window->Rect(); draw->rect_filled(window->DrawList, rect.Min, rect.Max, draw->get_clr(clr->widget), s_(8)); if (state->open && !rect.Contains(gui->mouse_pos()) && gui->mouse_clicked(0)) state->open = false; } return true; } return false; } void end_dropdown_ex() { gui->end(); gui->pop_var(3); } bool c_widgets::dropdown(std::string name, std::string description, int* callback, std::vector variants) { bool changed = false; if (dropdown_ex(name, description, variants[*callback])) { for (int i = 0; i < variants.size(); i++) { if (selectable_ex(variants[i], i == *callback)) { *callback = i; changed = true; } } end_dropdown_ex(); } return changed; } struct profile_dropdown_state { bool open; float alpha; c_vec4 background; c_vec4 text; c_vec4 status; c_vec4 arrow; }; struct profile_dropdown_row_state { float hover_alpha; c_vec4 text; }; bool profile_dropdown_row(std::string icon, std::string label, bool active) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(label.data()); profile_dropdown_row_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(24)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(9, 4), rect.Max - s_(9, 4)); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool pressed = gui->button_behavior(rect, id, &hovered, nullptr); gui->easing(state->hover_alpha, hovered || active ? 1.f : 0.f, 10.f, static_easing); gui->easing(state->text, active ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); if (state->hover_alpha > 0.f) { const bool active_row = active && !hovered; draw->rect_filled(window->DrawList, rect.Min, rect.Max, active_row ? draw->get_clr(clr->accent.Value, state->hover_alpha * 0.16f) : draw->get_clr(clr->widget, state->hover_alpha), s_(7)); } draw->text_clipped(window->DrawList, font->get(icon_font, 14), inner.Min, inner.Min + s_(16, 16), draw->get_clr(active ? clr->accent.Value : state->text), icon.data(), 0, 0, { 0.5f, 0.5f }); draw->text_clipped(window->DrawList, font->get(inter_semibold, 11), inner.Min + s_(20, 0), inner.Max, draw->get_clr(active ? clr->white.Value : state->text), label.data(), 0, 0, { 0, 0.5f }); return pressed; } void c_widgets::brand_header(std::string name, std::string subtitle) { c_window* window = gui->get_window(); if (window->SkipItems) return; c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(50)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(10, 8), rect.Max - s_(10, 8)); c_rect mark = c_rect(inner.Min, inner.Min + s_(34, 34)); gui->item_size(rect); if (!gui->item_add(rect, window->GetID("brand_header"))) return; const bool glass = var->gui.sidebar_glass; draw->rect_filled(window->DrawList, rect.Min, rect.Max, draw->get_clr(clr->child, glass ? 0.24f : 1.f), s_(12)); if (glass) draw->rect(window->DrawList, rect.Min + s_(0.5f, 0.5f), rect.Max - s_(0.5f, 0.5f), draw->get_clr(clr->border, 0.36f), s_(12)); draw->shadow_rect(window->DrawList, mark.Min + s_(2, 2), mark.Max - s_(2, 2), draw->get_clr(clr->accent, glass ? 0.14f : 0.2f), s_(14), c_vec2(0, 0), 0, s_(10)); draw->rect_filled(window->DrawList, mark.Min, mark.Max, draw->get_clr(clr->widget, glass ? 0.48f : 1.f), s_(10)); draw->rect_filled(window->DrawList, mark.Min + s_(3, 3), mark.Max - s_(3, 3), draw->get_clr(clr->accent, 0.12f), s_(8)); const c_vec2 center = mark.GetCenter(); draw->circle_filled(window->DrawList, center, s_(12), draw->get_clr(clr->accent, 0.08f), s_(999)); draw->line(window->DrawList, center + s_(0, -13), center + s_(0, 12), draw->get_clr(clr->accent), s_(2)); draw->circle_filled(window->DrawList, center + s_(0, -3), s_(5), draw->get_clr(clr->accent), s_(999)); draw->circle_filled(window->DrawList, center + s_(0, -3), s_(2), draw->get_clr(clr->black, 0.38f), s_(999)); draw->line(window->DrawList, center + s_(0, 3), center + s_(-8, 13), draw->get_clr(clr->accent, 0.85f), s_(1.5f)); draw->line(window->DrawList, center + s_(0, 3), center + s_(8, 13), draw->get_clr(clr->accent, 0.85f), s_(1.5f)); draw->rect_filled(window->DrawList, mark.Min + s_(6, 23), mark.Min + s_(9, 29), draw->get_clr(clr->white, 0.18f), s_(1)); draw->rect_filled(window->DrawList, mark.Min + s_(11, 20), mark.Min + s_(14, 29), draw->get_clr(clr->white, 0.18f), s_(1)); draw->rect_filled(window->DrawList, mark.Min + s_(20, 21), mark.Min + s_(23, 29), draw->get_clr(clr->white, 0.18f), s_(1)); draw->rect_filled(window->DrawList, mark.Min + s_(25, 24), mark.Min + s_(28, 29), draw->get_clr(clr->white, 0.18f), s_(1)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 13), inner.Min + s_(44, 2), inner.Max, draw->get_clr(clr->white), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), inner.Min + s_(44, 18), inner.Max, draw->get_clr(clr->text), subtitle.data(), 0, 0, { 0, 0 }); } bool c_widgets::profile_dropdown(std::string name, std::string status) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); profile_dropdown_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(58)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(10, 9), rect.Max - s_(10, 9)); c_rect avatar = c_rect(inner.Min, inner.Min + s_(40, 40)); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool pressed = gui->button_behavior(rect, id, &hovered, nullptr); if (pressed) state->open = !state->open; gui->easing(state->alpha, state->open ? 1.f : 0.f, 11.f, dynamic_easing); gui->easing(state->background, (hovered || state->open) ? clr->widget.Value : clr->child.Value, 18.f, dynamic_easing); gui->easing(state->text, (hovered || state->open) ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); gui->easing(state->status, state->open ? clr->accent.Value : clr->text.Value, 18.f, dynamic_easing); gui->easing(state->arrow, state->open ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); draw->rect_filled(window->DrawList, rect.Min, rect.Max, draw->get_clr(state->background), s_(12)); draw->circle_filled(window->DrawList, avatar.GetCenter(), avatar.GetWidth() * 0.5f, draw->get_clr(clr->accent, 0.22f), s_(999)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 16), avatar.Min, avatar.Max, draw->get_clr(clr->white), "D", 0, 0, { 0.5f, 0.5f }); draw->circle_filled(window->DrawList, avatar.Max - s_(5, 5), s_(7), draw->get_clr(clr->layout), s_(999)); draw->circle_filled(window->DrawList, avatar.Max - s_(5, 5), s_(4), draw->get_clr(clr->accent), s_(999)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), inner.Min + s_(50, 4), inner.Max - s_(18, 0), draw->get_clr(clr->white), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), inner.Min + s_(50, 20), inner.Max - s_(18, 0), draw->get_clr(state->status), status.data(), 0, 0, { 0, 0 }); draw_dropdown_chevron(window->DrawList, c_vec2(rect.Max.x - s_(18), rect.GetCenter().y), state->open, draw->get_clr(state->arrow)); if (state->alpha > 0.f) { const c_vec2 popup_size = c_vec2(rect.GetWidth(), s_(104)); const c_vec2 popup_pos = rect.GetTL() - c_vec2(0, popup_size.y + s_(5) * state->alpha); gui->push_var(style_var_alpha, state->alpha); gui->push_var(style_var_window_padding, s_(5, 5)); gui->push_var(style_var_item_spacing, s_(0, 1)); gui->set_next_window_pos(popup_pos); gui->set_next_window_size(popup_size); window_flags popup_flags = window_flags_no_decoration | window_flags_no_scrollbar | window_flags_no_scroll_with_mouse | window_flags_no_move | window_flags_no_background | window_flags_no_focus_on_appearing; if (!state->open) popup_flags |= window_flags_no_inputs; gui->begin("profile_dropdown_popup", nullptr, popup_flags); { if (state->open) gui->set_window_focus(); c_window* popup = gui->get_window(); c_rect popup_rect = popup->Rect(); draw->shadow_rect(popup->DrawList, popup_rect.Min, popup_rect.Max, draw->get_clr(clr->black, 0.26f), s_(18), s_(0, 8), 0, s_(12)); draw->rect_filled(popup->DrawList, popup_rect.Min, popup_rect.Max, draw->get_clr(clr->child), s_(12)); draw->rect(popup->DrawList, popup_rect.Min, popup_rect.Max, draw->get_clr(clr->border), s_(12)); draw->text_clipped(popup->DrawList, font->get(inter_semibold, 11), popup_rect.Min + s_(9, 5), popup_rect.Max - s_(9, 0), draw->get_clr(clr->white), "Quick settings", 0, 0, { 0, 0 }); gui->dummy(s_(0, 18)); if (profile_dropdown_row("C", "Settings", true)) state->open = false; if (profile_dropdown_row("F", "Appearance", false)) state->open = false; if (profile_dropdown_row("B", "Tools", false)) state->open = false; if (state->open && !popup_rect.Contains(gui->mouse_pos()) && !rect.Contains(gui->mouse_pos()) && gui->mouse_clicked(0)) state->open = false; } gui->end(); gui->pop_var(3); } return pressed; } struct primary_button_state { c_vec4 background; c_vec4 text; float shadow; float radius; float circle_alpha; float icon_spacing; bool clicked; }; static void draw_active_icon(ImDrawList* draw_list, c_vec2 center, ImU32 color) { draw->circle(draw_list, center, s_(7), color, s_(24), s_(1.3f)); // https://discord.gg/jxqVWub6Dk draw->line(draw_list, center + s_(-3, 0), center + s_(-1, 3), color, s_(1.5f)); draw->line(draw_list, center + s_(-1, 3), center + s_(4, -3), color, s_(1.5f)); } bool c_widgets::primary_button(std::string name, std::string icon) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); primary_button_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(42)); c_rect rect = c_rect(pos, pos + size); const bool has_icon = !icon.empty(); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool held = false; bool pressed = gui->button_behavior(rect, id, &hovered, &held); if (pressed) state->clicked = true; gui->easing(state->background, held ? clr->white.Value : clr->accent.Value, 18.f, dynamic_easing); gui->easing(state->text, held ? clr->black.Value : clr->black.Value, 18.f, dynamic_easing); gui->easing(state->shadow, hovered || held ? 1.f : 0.45f, 10.f, static_easing); gui->easing(state->icon_spacing, hovered || held ? 14.f : 7.f, 18.f, dynamic_easing); state->radius = ImClamp(state->radius + gui->fixed_speed(rect.GetWidth() * 7.f) * (state->clicked ? 1.f : -1.f), 0.f, rect.GetWidth() * 0.5f + s_(8)); state->circle_alpha = ImClamp(state->circle_alpha + gui->fixed_speed(3.5f) * (state->radius > rect.GetWidth() * 0.5f - s_(1) ? 1.f : -1.f), 0.f, 1.f); if (state->circle_alpha > 0.95f) { state->radius = 0.f; state->circle_alpha = 0.f; state->clicked = false; } draw->shadow_rect(window->DrawList, rect.Min + s_(8, 8), rect.Max - s_(8, 2), draw->get_clr(clr->accent, 0.22f * state->shadow), s_(18), c_vec2(0, 0), 0, s_(11)); draw->rect_filled(window->DrawList, rect.Min, rect.Max, draw->get_clr(state->background), s_(11)); draw->push_clip_rect(window->DrawList, rect.Min, rect.Max, true); draw->circle_filled(window->DrawList, rect.GetCenter(), state->radius, draw->get_clr(clr->black, 0.28f * (1.f - state->circle_alpha)), s_(48)); draw->pop_clip_rect(window->DrawList); if (has_icon) { const bool active_icon = icon == "active"; c_vec2 icon_size = active_icon ? s_(14, 14) : gui->text_size(font->get(icon_font, 15), icon.data()); c_vec2 name_size = gui->text_size(font->get(inter_semibold, 12), name.data()); float total_width = icon_size.x + s_(state->icon_spacing) + name_size.x; c_vec2 start = c_vec2(rect.GetCenter().x - total_width * 0.5f, rect.Min.y); if (active_icon) draw_active_icon(window->DrawList, c_vec2(start.x + icon_size.x * 0.5f, rect.GetCenter().y), draw->get_clr(state->text)); else draw->text_clipped(window->DrawList, font->get(icon_font, 15), start, start + c_vec2(icon_size.x, rect.GetHeight()), draw->get_clr(state->text), icon.data(), 0, 0, { 0, 0.5f }); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), start + c_vec2(icon_size.x + s_(state->icon_spacing), 0), rect.Max, draw->get_clr(state->text), name.data(), 0, 0, { 0, 0.5f }); } else { draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), rect.Min, rect.Max, draw->get_clr(state->text), name.data(), 0, 0, { 0.5f, 0.5f }); } return pressed; } struct action_button_state { c_vec4 background; c_vec4 text; c_vec4 icon; float accent_alpha; float arrow_offset; }; static void draw_action_icon(ImDrawList* draw_list, std::string icon, c_vec2 center, ImU32 color) { const float thickness = s_(1.4f); if (icon == "copy") { draw->rect(draw_list, center + s_(-5, -4), center + s_(3, 4), color, s_(2), 0, thickness); draw->rect(draw_list, center + s_(-2, -7), center + s_(6, 1), color, s_(2), 0, thickness); return; } if (icon == "back") { draw->line(draw_list, center + s_(-5, 0), center + s_(6, 0), color, thickness); draw->line(draw_list, center + s_(-5, 0), center + s_(0, -5), color, thickness); draw->line(draw_list, center + s_(-5, 0), center + s_(0, 5), color, thickness); return; } if (icon == "reset") { draw->circle(draw_list, center, s_(6), color, s_(28), thickness); draw->triangle_filled(draw_list, center + s_(2, -8), center + s_(8, -6), center + s_(5, -2), color); return; } if (!icon.empty()) { draw->text_clipped(draw_list, font->get(icon_font, 15), center - s_(8, 8), center + s_(8, 8), color, icon.data(), 0, 0, { 0.5f, 0.5f }); } } bool c_widgets::action_button(std::string name, std::string icon) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); action_button_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(44)); c_rect rect = c_rect(pos, pos + size); c_rect button = c_rect(rect.Min + s_(0, 3), rect.Max - s_(0, 3)); c_rect icon_rect = c_rect(button.Min + s_(9, 8), button.Min + s_(31, 30)); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool held = false; bool pressed = gui->button_behavior(button, id, &hovered, &held); gui->easing(state->background, held ? clr->cirlce.Value : clr->widget.Value, 18.f, dynamic_easing); gui->easing(state->text, hovered || held ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); gui->easing(state->icon, hovered || held ? clr->accent.Value : clr->text.Value, 18.f, dynamic_easing); gui->easing(state->arrow_offset, hovered || held ? 2.f : 0.f, 18.f, dynamic_easing); state->accent_alpha = ImClamp(state->accent_alpha + gui->fixed_speed(9.f) * (hovered || held ? 1.f : -1.f), 0.f, 1.f); draw->rect_filled(window->DrawList, button.Min, button.Max, draw->get_clr(state->background), s_(8)); draw->rect(window->DrawList, button.Min, button.Max, draw->get_clr(clr->border, 0.9f), s_(8)); draw->rect_filled(window->DrawList, button.Min, button.Min + c_vec2(s_(3), button.GetHeight()), draw->get_clr(clr->accent, state->accent_alpha), s_(8)); draw->rect_filled(window->DrawList, icon_rect.Min, icon_rect.Max, draw->get_clr(clr->accent, 0.08f + state->accent_alpha * 0.12f), s_(6)); draw_action_icon(window->DrawList, icon, icon_rect.GetCenter(), draw->get_clr(state->icon)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), button.Min + s_(40, 0), button.Max - s_(28, 0), draw->get_clr(state->text), name.data(), 0, 0, { 0, 0.5f }); const c_vec2 arrow = c_vec2(button.Max.x - s_(17) + s_(state->arrow_offset), button.GetCenter().y); draw->line(window->DrawList, arrow + s_(-2, -4), arrow + s_(2, 0), draw->get_clr(state->icon), s_(1.3f)); draw->line(window->DrawList, arrow + s_(2, 0), arrow + s_(-2, 4), draw->get_clr(state->icon), s_(1.3f)); return pressed; } struct card_button_state { c_vec4 text; c_vec4 background; c_vec4 button_text; }; bool c_widgets::card_button(std::string name, std::string description, std::string button_text) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); card_button_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(82)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(elements->padding), rect.Max - s_(elements->padding)); c_rect button = c_rect(inner.GetBL() - s_(0, 27), inner.GetBR()); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool held = false; bool pressed = gui->button_behavior(button, id, &hovered, &held); gui->easing(state->text, hovered || held ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); gui->easing(state->background, hovered || held ? clr->accent.Value : clr->widget.Value, 18.f, dynamic_easing); gui->easing(state->button_text, hovered || held ? clr->black.Value : clr->white.Value, 18.f, dynamic_easing); draw->rect_filled(window->DrawList, rect.Min, rect.Max, draw->get_clr(clr->child), s_(2)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), inner.Min, inner.Max, draw->get_clr(state->text), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), inner.Min + s_(0, 16), inner.Max, draw->get_clr(clr->text), description.data(), 0, 0, { 0, 0 }); draw->rect_filled(window->DrawList, button.Min, button.Max, draw->get_clr(state->background), s_(2)); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), button.Min, button.Max, draw->get_clr(state->button_text), button_text.data(), 0, 0, { 0.5, 0.5 }); return pressed; } struct checkbox_state { c_vec4 text; c_vec4 background; c_vec4 circle; float pos; float hover_alpha; float glow_alpha; float tooltip_alpha; }; static c_vec4 title_status_color(title_status_icon status) { c_vec4 color = c_col(75, 220, 145).Value; if (status == title_status_danger) color = c_col(245, 70, 86).Value; else if (status == title_status_warning) color = c_col(255, 190, 75).Value; return color; } static const char* title_status_label(title_status_icon status) { if (status == title_status_danger) return "DANGER"; if (status == title_status_warning) return "WARNING"; if (status == title_status_safe) return "SAFE"; return ""; } static const char* title_status_hint(title_status_icon status) { if (status == title_status_danger) return "High signal item. Use only when this detail is worth the extra exposure."; if (status == title_status_warning) return "Medium signal item. Useful, but it can add visual noise fast."; if (status == title_status_safe) return "Low signal item. Stable for normal use and clean defaults."; return ""; } static bool draw_title_status_icon(ImDrawList* draw_list, const c_vec2& center, title_status_icon status, c_rect* out_rect) { if (status == title_status_none) return false; const c_vec4 color = title_status_color(status); const c_rect icon_rect(center - s_(6, 8), center + s_(6, 8)); if (out_rect) *out_rect = icon_rect; draw->text_clipped(draw_list, font->get(inter_semibold, 18), icon_rect.Min, icon_rect.Max, draw->get_clr(color), "*", 0, 0, { 0.5f, 0.5f }); return icon_rect.Contains(gui->mouse_pos()); } static void draw_title_status_tooltip(const c_rect& anchor, title_status_icon status, const std::string& title, const std::string& description, float alpha) { if (status == title_status_none || alpha <= 0.01f) return; const c_vec4 color = title_status_color(status); const char* label = title_status_label(status); const char* hint = title_status_hint(status); ImDrawList* draw_list = ImGui::GetForegroundDrawList(); const float compact = 0.90f; const c_vec2 size = s_(286.f * compact, 104.f * compact); c_vec2 pos = gui->adjust_window_pos(anchor.Max + s_(12.f * compact, -16.f * compact) - s_(0, 6.f * compact) * (1.f - alpha), size); const c_rect rect(pos, pos + size); const float rounding = s_(10.f * compact); draw->shadow_rect(draw_list, rect.Min, rect.Max, draw->get_clr(clr->black, 0.28f * alpha), s_(18.f * compact), s_(0, 8.f * compact), 0, rounding); draw->rect_filled(draw_list, rect.Min, rect.Max, draw->get_clr(clr->child, 0.98f * alpha), rounding); draw->rect_filled_multi_color(draw_list, rect.Min, rect.Max, draw->get_clr(clr->white, 0.035f * alpha), draw->get_clr(color, 0.055f * alpha), draw->get_clr(clr->black, 0.02f * alpha), draw->get_clr(clr->black, 0.02f * alpha), rounding); draw->rect(draw_list, rect.Min, rect.Max, draw->get_clr(color, 0.30f * alpha), rounding, 0, s_(1)); draw->rect_filled(draw_list, rect.Min + s_(0, 11.f * compact), rect.Min + s_(3.f * compact, 46.f * compact), draw->get_clr(color, alpha), s_(999)); const c_rect star_rect(rect.Min + s_(13.f * compact, 10.f * compact), rect.Min + s_(29.f * compact, 30.f * compact)); draw->text_clipped(draw_list, font->get(inter_semibold, 18), star_rect.Min, star_rect.Max, draw->get_clr(color, alpha), "*", 0, 0, { 0.5f, 0.5f }); draw->text_clipped(draw_list, font->get(inter_semibold, 10), rect.Min + s_(34.f * compact, 11.f * compact), rect.Min + s_(160.f * compact, 27.f * compact), draw->get_clr(color, alpha), label, 0, 0, { 0, 0.5f }); const c_rect tag(rect.Max - s_(69.f * compact, 93.f * compact), rect.Max - s_(12.f * compact, 75.f * compact)); draw->rect_filled(draw_list, tag.Min, tag.Max, draw->get_clr(color, 0.12f * alpha), s_(999)); draw->rect(draw_list, tag.Min, tag.Max, draw->get_clr(color, 0.38f * alpha), s_(999), 0, s_(1)); draw->text_clipped(draw_list, font->get(inter_semibold, 10), tag.Min, tag.Max, draw->get_clr(color, alpha), "STATUS", 0, 0, { 0.5f, 0.5f }); draw->text_clipped(draw_list, font->get(inter_semibold, 12), rect.Min + s_(13.f * compact, 36.f * compact), rect.Max - s_(13.f * compact, 0), draw->get_clr(clr->white, alpha), title.data(), 0, 0, { 0, 0 }); draw->text_clipped(draw_list, font->get(inter_medium, 10), rect.Min + s_(13.f * compact, 54.f * compact), rect.Max - s_(13.f * compact, 0), draw->get_clr(clr->text, alpha), description.data(), 0, 0, { 0, 0 }); draw->line(draw_list, rect.Min + s_(13.f * compact, 75.f * compact), rect.Max - s_(13.f * compact, 29.f * compact), draw->get_clr(clr->border, 0.92f * alpha)); draw->text_clipped(draw_list, font->get(inter_medium, 10), rect.Min + s_(13.f * compact, 82.f * compact), rect.Max - s_(13.f * compact, 8.f * compact), draw->get_clr(clr->text, 0.92f * alpha), hint, 0, 0, { 0, 0 }); } struct keybind_widget_state { bool listening; int listen_started_frame; float bind_hover_alpha; float listen_alpha; c_vec4 bind_background; c_vec4 title; }; static bool keybind_is_ctrl(ImGuiKey key) { return key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl; } static bool keybind_is_shift(ImGuiKey key) { return key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift; } static bool keybind_is_alt(ImGuiKey key) { return key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt; } static bool keybind_is_super(ImGuiKey key) { return key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper; } static bool keybind_is_mouse_key(ImGuiKey key) { return key >= ImGuiKey_MouseLeft && key <= ImGuiKey_MouseX2; } static bool keybind_is_wheel_key(ImGuiKey key) { return key == ImGuiKey_MouseWheelX || key == ImGuiKey_MouseWheelY; } static void keybind_assign(keybind_t* bind, ImGuiKey key); static void keybind_clear(keybind_t* bind); static bool keybind_is_modifier(ImGuiKey key) { return keybind_is_ctrl(key) || keybind_is_shift(key) || keybind_is_alt(key) || keybind_is_super(key); } static std::array g_keybind_previous_vk_down{}; static bool keybind_vk_down(int vk) { return vk > 0 && vk < static_cast(g_keybind_previous_vk_down.size()) && (GetAsyncKeyState(vk) & 0x8000) != 0; } static bool keybind_vk_pressed(int vk) { if (vk <= 0 || vk >= static_cast(g_keybind_previous_vk_down.size())) return false; const bool down = keybind_vk_down(vk); const bool pressed = down && !g_keybind_previous_vk_down[vk]; g_keybind_previous_vk_down[vk] = down; return pressed; } static void keybind_prime_physical_input() { for (int vk = 0; vk < static_cast(g_keybind_previous_vk_down.size()); vk++) g_keybind_previous_vk_down[vk] = keybind_vk_down(vk); } static ImGuiKey keybind_key_from_vk(int vk) { if (vk >= '0' && vk <= '9') return static_cast(ImGuiKey_0 + (vk - '0')); if (vk >= 'A' && vk <= 'Z') return static_cast(ImGuiKey_A + (vk - 'A')); if (vk >= VK_F1 && vk <= VK_F24) return static_cast(ImGuiKey_F1 + (vk - VK_F1)); if (vk >= VK_NUMPAD0 && vk <= VK_NUMPAD9) return static_cast(ImGuiKey_Keypad0 + (vk - VK_NUMPAD0)); switch (vk) { case VK_TAB: return ImGuiKey_Tab; case VK_LEFT: return ImGuiKey_LeftArrow; case VK_RIGHT: return ImGuiKey_RightArrow; case VK_UP: return ImGuiKey_UpArrow; case VK_DOWN: return ImGuiKey_DownArrow; case VK_PRIOR: return ImGuiKey_PageUp; case VK_NEXT: return ImGuiKey_PageDown; case VK_HOME: return ImGuiKey_Home; case VK_END: return ImGuiKey_End; case VK_INSERT: return ImGuiKey_Insert; case VK_DELETE: return ImGuiKey_Delete; case VK_BACK: return ImGuiKey_Backspace; case VK_SPACE: return ImGuiKey_Space; case VK_RETURN: return ImGuiKey_Enter; case VK_ESCAPE: return ImGuiKey_Escape; case VK_LCONTROL: return ImGuiKey_LeftCtrl; case VK_RCONTROL: return ImGuiKey_RightCtrl; case VK_LSHIFT: return ImGuiKey_LeftShift; case VK_RSHIFT: return ImGuiKey_RightShift; case VK_LMENU: return ImGuiKey_LeftAlt; case VK_RMENU: return ImGuiKey_RightAlt; case VK_LWIN: return ImGuiKey_LeftSuper; case VK_RWIN: return ImGuiKey_RightSuper; case VK_APPS: return ImGuiKey_Menu; case VK_OEM_7: return ImGuiKey_Apostrophe; case VK_OEM_COMMA: return ImGuiKey_Comma; case VK_OEM_MINUS: return ImGuiKey_Minus; case VK_OEM_PERIOD: return ImGuiKey_Period; case VK_OEM_2: return ImGuiKey_Slash; case VK_OEM_1: return ImGuiKey_Semicolon; case VK_OEM_PLUS: return ImGuiKey_Equal; case VK_OEM_4: return ImGuiKey_LeftBracket; case VK_OEM_5: return ImGuiKey_Backslash; case VK_OEM_6: return ImGuiKey_RightBracket; case VK_OEM_3: return ImGuiKey_GraveAccent; case VK_CAPITAL: return ImGuiKey_CapsLock; case VK_SCROLL: return ImGuiKey_ScrollLock; case VK_NUMLOCK: return ImGuiKey_NumLock; case VK_SNAPSHOT: return ImGuiKey_PrintScreen; case VK_PAUSE: return ImGuiKey_Pause; case VK_DECIMAL: return ImGuiKey_KeypadDecimal; case VK_DIVIDE: return ImGuiKey_KeypadDivide; case VK_MULTIPLY: return ImGuiKey_KeypadMultiply; case VK_SUBTRACT: return ImGuiKey_KeypadSubtract; case VK_ADD: return ImGuiKey_KeypadAdd; default: return ImGuiKey_None; } } static bool keybind_capture_physical_input(keybind_t* bind) { if (keybind_vk_pressed(VK_ESCAPE)) { keybind_clear(bind); return true; } struct mouse_key_map { int vk; ImGuiKey key; }; static const mouse_key_map mouse_keys[] = { { VK_LBUTTON, ImGuiKey_MouseLeft }, { VK_RBUTTON, ImGuiKey_MouseRight }, { VK_MBUTTON, ImGuiKey_MouseMiddle }, { VK_XBUTTON1, ImGuiKey_MouseX1 }, { VK_XBUTTON2, ImGuiKey_MouseX2 }, }; for (const mouse_key_map& mouse_key : mouse_keys) { if (keybind_vk_pressed(mouse_key.vk)) { keybind_assign(bind, mouse_key.key); return true; } } for (int vk = 1; vk < static_cast(g_keybind_previous_vk_down.size()); vk++) { const ImGuiKey key = keybind_key_from_vk(vk); if (key == ImGuiKey_None || keybind_is_modifier(key)) continue; if (keybind_vk_pressed(vk)) { keybind_assign(bind, key); return true; } } return false; } static std::string keybind_display_name(const keybind_t& bind) { if (bind.key == ImGuiKey_None) return "Unbound"; const ImGuiKey key = static_cast(bind.key); const char* key_name = ImGui::GetKeyName(key); std::string display; if (bind.ctrl) display += "Ctrl + "; if (bind.shift) display += "Shift + "; if (bind.alt) display += "Alt + "; if (bind.super) display += "Super + "; if (key_name && key_name[0] != '\0') display += key_name; else display += "Key " + std::to_string(bind.key); return display; } static void keybind_assign(keybind_t* bind, ImGuiKey key) { ImGuiIO& io = ImGui::GetIO(); bind->key = key; bind->ctrl = (io.KeyCtrl || keybind_vk_down(VK_CONTROL)) && !keybind_is_ctrl(key); bind->shift = (io.KeyShift || keybind_vk_down(VK_SHIFT)) && !keybind_is_shift(key); bind->alt = (io.KeyAlt || keybind_vk_down(VK_MENU)) && !keybind_is_alt(key); bind->super = (io.KeySuper || keybind_vk_down(VK_LWIN) || keybind_vk_down(VK_RWIN)) && !keybind_is_super(key); } static void keybind_clear(keybind_t* bind) { bind->key = ImGuiKey_None; bind->ctrl = false; bind->shift = false; bind->alt = false; bind->super = false; bind->capturing = false; } static bool keybind_capture_input(keybind_t* bind) { if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { keybind_clear(bind); return true; } for (int button = 0; button < ImGuiMouseButton_COUNT; button++) { if (ImGui::IsMouseClicked(button, false)) { keybind_assign(bind, static_cast(ImGuiKey_MouseLeft + button)); return true; } } for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++) { const ImGuiKey imgui_key = static_cast(key); if (keybind_is_mouse_key(imgui_key) || keybind_is_wheel_key(imgui_key) || keybind_is_modifier(imgui_key)) continue; if (ImGui::IsKeyPressed(imgui_key, false)) { keybind_assign(bind, imgui_key); return true; } } return keybind_capture_physical_input(bind); } bool c_widgets::keybind(std::string name, std::string description, keybind_t* bind) { if (!bind) return false; c_window* window = gui->get_window(); if (window->SkipItems) return false; bind->mode = ImClamp(bind->mode, static_cast(keybind_mode_hold), static_cast(keybind_mode_always)); c_id id = window->GetID(name.data()); keybind_widget_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(45)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(0, 9), rect.Max - s_(0, 9)); const float bind_width = ImClamp(inner.GetWidth() * 0.30f, s_(82), s_(108)); const c_rect bind_button = c_rect(c_vec2(inner.Max.x - bind_width, inner.GetCenter().y - s_(12)), c_vec2(inner.Max.x, inner.GetCenter().y + s_(12))); const c_rect text_rect = c_rect(inner.Min, c_vec2(bind_button.Min.x - s_(14), inner.Max.y)); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool row_hovered = false; bool row_held = false; const bool bind_pressed = gui->button_behavior(rect, id, &row_hovered, &row_held); const bool bind_hovered = row_hovered && bind_button.Contains(gui->mouse_pos()); const bool bind_held = row_held && bind_button.Contains(gui->mouse_pos()); bool changed = false; if (bind_pressed) { state->listening = true; bind->capturing = true; state->listen_started_frame = ImGui::GetFrameCount(); keybind_prime_physical_input(); } if (state->listening && ImGui::GetFrameCount() > state->listen_started_frame && keybind_capture_input(bind)) { state->listening = false; bind->capturing = false; changed = true; } bind->capturing = state->listening; gui->easing(state->bind_hover_alpha, bind_hovered || bind_held || state->listening ? 1.f : 0.f, 12.f, dynamic_easing); gui->easing(state->listen_alpha, state->listening ? 1.f : 0.f, 14.f, dynamic_easing); gui->easing(state->bind_background, state->listening ? clr->accent.Value : clr->widget.Value, 18.f, dynamic_easing); gui->easing(state->title, state->listening ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); const float pulse = state->listening ? 0.5f + 0.5f * ImSin(static_cast(ImGui::GetTime()) * 5.2f) : 0.f; draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), text_rect.Min, text_rect.Max, draw->get_clr(state->title), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), text_rect.Min + s_(0, 16), text_rect.Max, draw->get_clr(clr->text), description.data(), 0, 0, { 0, 0 }); if (state->listening) draw->shadow_rect(window->DrawList, bind_button.Min + s_(3, 4), bind_button.Max - s_(3, 1), draw->get_clr(clr->accent, 0.16f + pulse * 0.06f), s_(12), c_vec2(0, 0), 0, s_(7)); draw->rect_filled(window->DrawList, bind_button.Min, bind_button.Max, draw->get_clr(state->bind_background), s_(7)); draw->rect_filled_multi_color(window->DrawList, bind_button.Min, bind_button.Max, draw->get_clr(clr->white, state->listening ? 0.12f : 0.018f * state->bind_hover_alpha), draw->get_clr(clr->white, state->listening ? 0.04f : 0.012f), draw->get_clr(clr->black, 0.05f), draw->get_clr(clr->black, 0.07f), s_(7)); draw->rect(window->DrawList, bind_button.Min, bind_button.Max, draw->get_clr(state->listening ? clr->accent.Value : clr->border.Value, state->listening ? 0.80f : 0.92f), s_(7)); const std::string bind_text = state->listening ? "Press key" : keybind_display_name(*bind); draw->text_clipped(window->DrawList, font->get(inter_semibold, 10), bind_button.Min + s_(9, 0), bind_button.Max - s_(9, 0), draw->get_clr(state->listening ? clr->black.Value : (bind_hovered ? clr->white.Value : clr->text.Value)), bind_text.data(), 0, 0, { 0.5f, 0.5f }); if (gui->content_avail().y > 0) { draw->line(window->DrawList, rect.GetBL(), rect.GetBR(), draw->get_clr(clr->border)); } return changed; } bool c_widgets::checkbox(std::string name, std::string description, bool* callback, title_status_icon status) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); checkbox_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(gui->content_avail().x, s_(40)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(0, 6), rect.Max - s_(0, 6)); c_vec2 switch_size = s_(36.3f, 20.9f); c_rect button = c_rect(c_vec2(inner.Max.x - switch_size.x, inner.GetCenter().y - switch_size.y * 0.5f), c_vec2(inner.Max.x, inner.GetCenter().y + switch_size.y * 0.5f)); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool held = false; bool pressed = gui->button_behavior(inner, id, &hovered, &held); if (pressed) { *callback = !*callback; notify->add_notify(*callback ? "Function enabled" : "Function disabled", name, *callback ? success : warning); } const c_vec4 off_track = clr->widget.Value; const c_vec4 on_track = c_col(38, 37, 50).Value; const c_vec4 on_knob = clr->accent.Value; const c_vec4 off_knob = c_col(96, 88, 142).Value; const c_vec4 on_knob_inner = c_col(28, 28, 33).Value; const c_vec4 off_knob_inner = c_col(31, 31, 39).Value; gui->easing(state->pos, *callback ? 1.f : 0.f, 20.f, dynamic_easing); gui->easing(state->hover_alpha, hovered || held ? 1.f : 0.f, 14.f, dynamic_easing); gui->easing(state->glow_alpha, *callback ? 1.f : 0.f, 18.f, dynamic_easing); gui->easing(state->text, *callback ? clr->white.Value : c_vec4(clr->text.Value.x * 0.82f, clr->text.Value.y * 0.82f, clr->text.Value.z * 0.82f, clr->text.Value.w), 18.f, dynamic_easing); gui->easing(state->background, *callback ? on_track : off_track, 18.f, dynamic_easing); gui->easing(state->circle, *callback ? on_knob : off_knob, 18.f, dynamic_easing); const c_vec2 title_min = inner.Min; c_rect status_rect; bool status_hovered = false; draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), title_min, c_vec2(button.Min.x - s_(12), inner.Max.y), draw->get_clr(state->text), name.data(), 0, 0, { 0, 0.5f }); if (status != title_status_none) { const c_vec2 title_size = gui->text_size(font->get(inter_semibold, 12), name.data()); const float max_x = button.Min.x - s_(14); const float icon_x = ImMin(title_min.x + title_size.x + s_(11), max_x); status_hovered = draw_title_status_icon(window->DrawList, c_vec2(icon_x, inner.GetCenter().y), status, &status_rect); } gui->easing(state->tooltip_alpha, status_hovered ? 1.f : 0.f, 18.f, dynamic_easing); draw_title_status_tooltip(status_rect, status, name, description, state->tooltip_alpha); const float switch_rounding = s_(999); const c_vec2 knob_center = c_vec2(ImLerp(button.Min.x + s_(10.45f), button.Max.x - s_(10.45f), state->pos), button.GetCenter().y); const float marker_x = ImLerp(button.Max.x - s_(9.98f), button.Min.x + s_(9.98f), state->pos); const c_rect marker(c_vec2(marker_x - s_(1.35f), button.GetCenter().y - s_(4.35f)), c_vec2(marker_x + s_(1.35f), button.GetCenter().y + s_(4.35f))); draw->rect_filled(window->DrawList, button.Min, button.Max, draw->get_clr(state->background), switch_rounding); const ImU32 marker_col = draw->get_clr(*callback ? clr->accent.Value : c_col(132, 113, 198).Value, *callback ? 0.92f : 0.58f); window->DrawList->AddRectFilled(marker.Min, marker.Max, marker_col, marker.GetWidth() * 0.5f, ImDrawFlags_RoundCornersAll); draw->circle_filled(window->DrawList, knob_center, s_(7.0f), draw->get_clr(state->circle), 32); draw->circle_filled(window->DrawList, knob_center, s_(3.65f), draw->get_clr(*callback ? on_knob_inner : off_knob_inner, *callback ? 1.0f : 0.95f), 32); if (gui->content_avail().y > 0) { const float y = IM_FLOOR(rect.Max.y - 0.5f) + 0.5f; window->DrawList->AddLine(c_vec2(rect.Min.x, y), c_vec2(rect.Max.x, y), draw->get_clr(c_col(35, 35, 44).Value, 0.72f), 1.0f); } return pressed; } bool c_widgets::child(std::string name) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); gui->begin_content(name, c_vec2(elements->child_width, 0), s_(10, 0), s_(0, 0), window_flags_no_scrollbar | window_flags_no_scroll_with_mouse | window_flags_no_move, child_flags_always_auto_resize | child_flags_auto_resize_y); { window = gui->get_window(); draw->rect_filled(window->DrawList, window->Rect().Min, window->Rect().Max, draw->get_clr(clr->child), s_(10)); } return true; } void c_widgets::end_child() { gui->end_content(); } struct tab_button_state { float width; c_vec4 icon; c_vec4 background; float alpha; }; c_vec4 g_pill_selected_rect = c_vec4(0, 0, 0, 0); c_vec4 g_sidebar_selected_rect = c_vec4(0, 0, 0, 0); static void append_utf8(std::string& output, unsigned int codepoint) { if (codepoint <= 0x7F) { output.push_back(static_cast(codepoint)); return; } if (codepoint <= 0x7FF) { output.push_back(static_cast(0xC0 | (codepoint >> 6))); output.push_back(static_cast(0x80 | (codepoint & 0x3F))); return; } if (codepoint <= 0xFFFF) { output.push_back(static_cast(0xE0 | (codepoint >> 12))); output.push_back(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); output.push_back(static_cast(0x80 | (codepoint & 0x3F))); return; } output.push_back(static_cast(0xF0 | (codepoint >> 18))); output.push_back(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); output.push_back(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); output.push_back(static_cast(0x80 | (codepoint & 0x3F))); } static unsigned int tab_icon_codepoint(const std::string& icon) { if (icon == "player") return 0xFEA0; // fi-rr-user if (icon == "shapes") return 0xF71C; // fi-rr-grid if (icon == "world") return 0xFF21; // fi-rr-world if (icon == "polish") return 0xF8E1; // fi-rr-magic-wand if (icon == "combat") return 0xFD32; // fi-rr-sword if (icon == "aim") return 0xFD5F; // fi-rr-target if (icon == "loot") return 0xFE19; // fi-rr-treasure-chest if (icon == "movement") return 0xFB7C; // fi-rr-running if (icon == "visuals") return 0xF5F8; // fi-rr-eye if (icon == "hud") return 0xF87D; // fi-rr-layout-fluid if (icon == "profile") return 0xFEA0; // fi-rr-user if (icon == "loadout") return 0xF1E7; // fi-rr-backpack if (icon == "match") return 0xF309; // fi-rr-bullseye if (icon == "squad") return 0xFEA6; // fi-rr-users if (icon == "stats") return 0xF3A2; // fi-rr-chart-histogram return 0; } static std::string tab_icon_glyph(const std::string& icon) { const unsigned int codepoint = tab_icon_codepoint(icon); if (!codepoint) return icon; std::string glyph; append_utf8(glyph, codepoint); return glyph; } static void draw_tab_icon(ImDrawList* draw_list, const c_rect& rect, const std::string& icon, const c_vec4& color, float size, ImFont* fallback_font) { const unsigned int codepoint = tab_icon_codepoint(icon); const std::string glyph = codepoint ? tab_icon_glyph(icon) : icon; ImFont* icon_font = codepoint ? font->get_file(flaticon_uicons_regular_rounded_path, size, true) : fallback_font; if (!icon_font) icon_font = fallback_font; draw->text_clipped(draw_list, icon_font, rect.Min, rect.Max, draw->get_clr(color), glyph.c_str(), 0, 0, { 0.5, 0.5 }); } bool c_widgets::tab_button(std::string name, std::string icon, int tab) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); tab_button_state* state = gui->anim_container(id); bool is_selected = tab == var->gui.tab_stored; const float tab_height = 32.f; const float icon_slot = 32.f; float target_width = ImMax(gui->content_avail().x, s_(tab_height)); if (state->width <= 0.f) state->width = target_width; gui->easing(state->width, target_width, 18.f, dynamic_easing); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(round(state->width), s_(tab_height)); c_rect rect = c_rect(pos, pos + size); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool pressed = gui->button_behavior(rect, id, &hovered, nullptr); if (pressed) { var->gui.tab_stored = tab; } if (is_selected) g_sidebar_selected_rect = c_vec4(rect.Min.x, rect.Min.y, rect.Max.x, rect.Max.y); gui->easing(state->alpha, (is_selected || hovered) ? 1.f : 0.58f, 18.f, dynamic_easing); gui->easing(state->icon, (is_selected || hovered) ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); c_vec4 icon_color = state->icon; icon_color.w *= state->alpha; draw_tab_icon(window->DrawList, c_rect(rect.Min, rect.Min + s_(icon_slot, tab_height)), icon, icon_color, 12.5f, font->get(icon_font, 14)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), rect.Min + s_(icon_slot, 0), rect.Max - s_(11, 0), draw->get_clr(clr->white, state->alpha), name.data(), 0, 0, { 0, 0.5 }); return pressed; } bool c_widgets::sub_tab_button(std::string name, std::string icon, int tab, float width) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); tab_button_state* state = gui->anim_container(id); bool is_selected = tab == var->gui.sub_tab_stored; c_vec2 name_size = gui->text_size(font->get(inter_semibold, 11), name.data()); const float sub_tab_height = 32.f; const float icon_slot = 32.f; const float target_width = width > 0.f ? width : (s_(42.f) + name_size.x); if (state->width <= 0.f) state->width = target_width; gui->easing(state->width, target_width, 18.f, dynamic_easing); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(round(state->width), s_(sub_tab_height)); c_rect rect = c_rect(pos, pos + size); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool hovered = false; bool pressed = gui->button_behavior(rect, id, &hovered, nullptr); if (pressed) { var->gui.sub_tab_stored = tab; } if (is_selected) g_pill_selected_rect = c_vec4(rect.Min.x, rect.Min.y, rect.Max.x, rect.Max.y); gui->easing(state->alpha, (is_selected || hovered) ? 1.f : 0.58f, 18.f, dynamic_easing); gui->easing(state->icon, (is_selected || hovered) ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); c_vec4 icon_color = state->icon; icon_color.w *= state->alpha; draw_tab_icon(window->DrawList, c_rect(rect.Min, rect.Min + s_(icon_slot, sub_tab_height)), icon, icon_color, 12.f, font->get(icon_font, 13)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 11), rect.Min + s_(icon_slot, 0), rect.Max - s_(10, 0), draw->get_clr(clr->white, state->alpha), name.data(), 0, 0, { 0, 0.5 }); return pressed; } struct category_button_state { c_vec4 text; c_vec4 background; c_vec4 button_text; }; bool c_widgets::category_button(std::string name, std::string description, bool* callback) { c_window* window = gui->get_window(); if (window->SkipItems) return false; c_id id = window->GetID(name.data()); category_button_state* state = gui->anim_container(id); c_vec2 pos = window->DC.CursorPos; c_vec2 size = c_vec2(elements->tab_window_width, s_(82)); c_rect rect = c_rect(pos, pos + size); c_rect inner = c_rect(rect.Min + s_(elements->padding), rect.Max - s_(elements->padding)); c_rect button = c_rect(inner.GetBL() - s_(0, 27), inner.GetBR()); gui->item_size(rect); if (!gui->item_add(rect, id)) return false; bool pressed = gui->button_behavior(button, id, nullptr, nullptr); if (pressed) { *callback = !*callback; } gui->easing(state->text, *callback ? clr->white.Value : clr->text.Value, 18.f, dynamic_easing); gui->easing(state->background, *callback ? clr->accent.Value : clr->widget.Value, 18.f, dynamic_easing); gui->easing(state->button_text, *callback ? clr->black.Value : clr->white.Value, 18.f, dynamic_easing); draw->rect_filled(window->DrawList, rect.Min, rect.Max, draw->get_clr(clr->child), s_(10)); draw->text_clipped(window->DrawList, font->get(inter_semibold, 12), inner.Min, inner.Max, draw->get_clr(state->text), name.data(), 0, 0, { 0, 0 }); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), inner.Min + s_(0, 16), inner.Max, draw->get_clr(clr->text), description.data(), 0, 0, { 0, 0 }); draw->rect_filled(window->DrawList, button.Min, button.Max, draw->get_clr(state->background), s_(8)); draw->text_clipped(window->DrawList, font->get(inter_medium, 11), button.Min, button.Max, draw->get_clr(state->button_text), *callback ? "Enabled" : "Disabled", 0, 0, {0.5, 0.5}); return pressed; }