#include "UIWidgets.h" #define IMGUI_DEFINE_MATH_OPERATORS #include #include #include #include #include #include #include namespace UIWidgets { // Automatically adds newlines to break up text longer than a specified number of characters // Manually included newlines will still be respected and reset the line length // If line is midword when it hits the limit, text should break at the last encountered space std::string WrappedText(const char* text, unsigned int charactersPerLine) { std::string newText(text); const size_t tipLength = newText.length(); int lastSpace = -1; int currentLineLength = 0; for (unsigned int currentCharacter = 0; currentCharacter < tipLength; currentCharacter++) { if (newText[currentCharacter] == '\n') { currentLineLength = 0; lastSpace = -1; continue; } else if (newText[currentCharacter] == ' ') { lastSpace = currentCharacter; } if ((currentLineLength >= charactersPerLine) && (lastSpace >= 0)) { newText[lastSpace] = '\n'; currentLineLength = currentCharacter - lastSpace - 1; lastSpace = -1; } currentLineLength++; } return newText; } std::string WrappedText(const std::string& text, unsigned int charactersPerLine) { return WrappedText(text.c_str(), charactersPerLine); } void Tooltip(const char* text) { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", WrappedText(text).c_str()); } } void PushStyleMenu(const ImVec4& color) { ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f)); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_PopupBg, UIWidgets::ColorValues.at(Colors::DarkGray)); ImGui::PushStyleColor(ImGuiCol_Border, UIWidgets::ColorValues.at(Colors::DarkGray)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 15.0f)); ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 3.0f); } void PushStyleMenu(Colors color) { PushStyleMenu(UIWidgets::ColorValues.at(color)); } void PopStyleMenu() { ImGui::PopStyleVar(2); ImGui::PopStyleColor(4); } bool BeginMenu(const char* label, Colors color) { bool dirty = false; PushStyleMenu(color); ImGui::SetNextWindowSizeConstraints(ImVec2(200.0f, 0.0f), ImVec2(FLT_MAX, FLT_MAX)); if (ImGui::BeginMenu(label)) { dirty = true; } PopStyleMenu(); return dirty; } void PushStyleMenuItem(const ImVec4& color) { ImGui::PushStyleColor(ImGuiCol_HeaderHovered, color); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0f, 15.0f)); } void PushStyleMenuItem(Colors color) { PushStyleMenuItem(ColorValues.at(color)); } void PopStyleMenuItem() { ImGui::PopStyleVar(1); ImGui::PopStyleColor(1); } bool MenuItem(const char* label, const char* shortcut, Colors color) { bool dirty = false; PushStyleMenuItem(color); if (ImGui::MenuItem(label, shortcut)) { dirty = true; } PopStyleMenuItem(); return dirty; } void PushStyleButton(const ImVec4& color) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 0.8f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x, color.y, color.z, 0.6f)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f); } void PushStyleButton(Colors color) { PushStyleButton(UIWidgets::ColorValues.at(color)); } void PopStyleButton() { ImGui::PopStyleVar(3); ImGui::PopStyleColor(4); } bool Button(const char* label, const ButtonOptions& options) { ImGui::BeginDisabled(options.disabled); PushStyleButton(options.color); bool dirty = ImGui::Button(label, options.size); PopStyleButton(); ImGui::EndDisabled(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } return dirty; } bool WindowButton(const char* label, const char* cvarName, std::shared_ptr windowPtr, const ButtonOptions& options) { ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); std::string buttonText = label; bool dirty = false; if (CVarGetInteger(cvarName, 0)) { buttonText = ICON_FA_WINDOW_CLOSE " " + buttonText; } else { buttonText = ICON_FA_EXTERNAL_LINK_SQUARE " " + buttonText; } if (Button(buttonText.c_str(), options)) { windowPtr->ToggleVisibility(); dirty = true; } ImGui::PopStyleVar(); return dirty; } void PushStyleCheckbox(const ImVec4& color) { ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 0.8f)); ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f)); ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.0f, 1.0f, 1.0f, 0.7f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f); } void PushStyleCheckbox(Colors color) { PushStyleCheckbox(UIWidgets::ColorValues.at(color)); } void PopStyleCheckbox() { ImGui::PopStyleVar(3); ImGui::PopStyleColor(5); } void RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // Hide anything after a '##' string const char* text_display_end; if (hide_text_after_hash) { text_display_end = ImGui::FindRenderedTextEnd(text, text_end); } else { if (!text_end) text_end = text + strlen(text); // FIXME-OPT text_display_end = text_end; } if (text != text_display_end) { window->DrawList->AddText(g.Font, g.FontSize, pos, ImGui::GetColorU32(ImGuiCol_Text), text, text_display_end); if (g.LogEnabled) ImGui::LogRenderedText(&pos, text, text_display_end); } } bool Checkbox(const char* _label, bool* value, const CheckboxOptions& options) { ImGuiWindow* window = ImGui::GetCurrentWindow(); if (window->SkipItems) return false; ImGui::BeginDisabled(options.disabled); bool above = options.labelPosition == LabelPosition::Above; bool lpFar = options.labelPosition == LabelPosition::Far; bool right = options.alignment == ComponentAlignment::Right; bool none = options.labelPosition == LabelPosition::None; std::string labelStr = (none ? "##" : ""); labelStr.append(_label); const char* label = labelStr.c_str(); ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); const float square_sz = ImGui::GetFrameHeight(); ImVec2 pos = window->DC.CursorPos; if (right) { float labelOffsetX = (above ? 0 : (style.ItemInnerSpacing.x * 2.0f) + square_sz); if (!lpFar) { pos.x += ImGui::GetContentRegionAvail().x - (label_size.x + labelOffsetX); } } float bbAboveX = lpFar ? ImGui::GetContentRegionAvail().x : (label_size.x + (above ? 0 : (style.ItemInnerSpacing.x * 2.0f) + square_sz)); float bbAboveY = label_size.y + (above ? square_sz : 0) + (style.FramePadding.y * 2.0f); const ImRect total_bb(pos, pos + ImVec2(bbAboveX, bbAboveY)); ImGui::ItemSize(total_bb, style.FramePadding.y); if (!ImGui::ItemAdd(total_bb, id)) { ImGui::EndDisabled(); return false; } bool hovered, held, pressed; pressed = ImGui::ButtonBehavior(total_bb, id, &hovered, &held); if (pressed) { *value = !(*value); ImGui::MarkItemEdited(id); } PushStyleCheckbox(options.color); ImVec2 checkPos = pos; ImVec2 labelPos = pos; if (options.labelPosition == LabelPosition::Above) { checkPos.y += label_size.y + (style.ItemInnerSpacing.y * 2.0f); } else { labelPos.y += (square_sz / 2) - (label_size.y / 2); } if (options.alignment == ComponentAlignment::Right) { checkPos.x = total_bb.Max.x - square_sz; } else { float labelFarOffset = ImGui::GetContentRegionAvail().x - label_size.x; float labelOffsetX = above ? 0 : (lpFar ? labelFarOffset : (style.ItemInnerSpacing.x * 2.0f) + square_sz); labelPos.x += labelOffsetX; } const ImRect check_bb(checkPos, checkPos + ImVec2(square_sz, square_sz)); ImGui::RenderNavHighlight(total_bb, id); ImGui::RenderFrame(check_bb.Min, check_bb.Max, ImGui::GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); ImU32 check_col = ImGui::GetColorU32(ImGuiCol_CheckMark); bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0; if (mixed_value) { // 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 (*value) { const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); ImGui::RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); } RenderText(labelPos, label, ImGui::FindRenderedTextEnd(label), true); PopStyleCheckbox(); ImGui::EndDisabled(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } return pressed; } bool CVarCheckbox(const char* label, const char* cvarName, const CheckboxOptions& options) { bool dirty = false; bool value = (bool)CVarGetInteger(cvarName, options.defaultValue); if (Checkbox(label, &value, options)) { CVarSetInteger(cvarName, value); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); dirty = true; } return dirty; } void PushStyleCombobox(const ImVec4& color) { ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 0.8f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 0.6f)); ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f)); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 0.8f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f)); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 0.6f)); ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(color.x, color.y, color.z, 0.6f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f)); } void PushStyleCombobox(Colors color) { PushStyleCombobox(UIWidgets::ColorValues.at(color)); } void PopStyleCombobox() { ImGui::PopStyleVar(4); ImGui::PopStyleColor(9); } void PushStyleSlider(Colors color_) { const ImVec4& color = UIWidgets::ColorValues.at(color_); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(color.x, color.y, color.z, 1.0f)); ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(1.0, 1.0, 1.0, 0.4f)); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(1.0, 1.0, 1.0, 0.5f)); ImGui::PushStyleVar(ImGuiStyleVar_GrabRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); } void PopStyleSlider() { ImGui::PopStyleVar(4); ImGui::PopStyleColor(6); } bool SliderInt(const char* label, int32_t* value, const IntSliderOptions& options) { bool dirty = false; std::string invisibleLabelStr = "##" + std::string(label); const char* invisibleLabel = invisibleLabelStr.c_str(); ImGui::PushID(label); ImGui::BeginGroup(); ImGui::BeginDisabled(options.disabled); PushStyleSlider(options.color); if (options.alignment == ComponentAlignment::Left) { if (options.labelPosition == LabelPosition::Above) { ImGui::Text(label, *value); } } else if (options.alignment == ComponentAlignment::Right) { if (options.labelPosition == LabelPosition::Above) { ImGui::NewLine(); ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x); ImGui::Text(label, *value); } } if (options.showButtons) { if (Button("-", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value > options.min) { *value -= options.step; if (*value < options.min) *value = options.min; dirty = true; } ImGui::SameLine(0, 3.0f); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("+").x + 20.0f + 3.0f)); } else { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); } if (ImGui::SliderScalar(invisibleLabel, ImGuiDataType_S32, value, &options.min, &options.max, options.format, options.flags)) { dirty = true; } if (options.showButtons) { ImGui::SameLine(0, 3.0f); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (Button("+", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value < options.max) { *value += options.step; if (*value > options.max) *value = options.max; dirty = true; } } PopStyleSlider(); ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); return dirty; } bool CVarSliderInt(const char* label, const char* cvarName, const IntSliderOptions& options) { bool dirty = false; int32_t value = CVarGetInteger(cvarName, options.defaultValue); if (SliderInt(label, &value, options)) { CVarSetInteger(cvarName, value); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); dirty = true; } return dirty; } void ClampFloat(float* value, float min, float max, float step) { int ticks = 0; float increment = 1.0f; if (step < 1.0f) { ticks++; increment = 0.1f; } if (step < 0.1f) { ticks++; increment = 0.01f; } if (step < 0.01f) { ticks++; increment = 0.001f; } if (step < 0.001f) { ticks++; increment = 0.0001f; } if (step < 0.0001f) { ticks++; increment = 0.00001f; } if (step < 0.00001f) { ticks++; increment = 0.000001f; } int factor = 1 * std::pow(10, ticks); if (*value < min) { *value = min; } else if (*value > max) { *value = max; } else { int trunc = (int)std::round(*value * factor); *value = (float)trunc / factor; } } bool SliderFloat(const char* label, float* value, const FloatSliderOptions& options) { bool dirty = false; std::string invisibleLabelStr = "##" + std::string(label); const char* invisibleLabel = invisibleLabelStr.c_str(); float valueToDisplay = options.isPercentage ? *value * 100.0f : *value; float maxToDisplay = options.isPercentage ? options.max * 100.0f : options.max; float minToDisplay = options.isPercentage ? options.min * 100.0f : options.min; ImGui::PushID(label); ImGui::BeginGroup(); ImGui::BeginDisabled(options.disabled); PushStyleSlider(options.color); if (options.alignment == ComponentAlignment::Left) { if (options.labelPosition == LabelPosition::Above) { ImGui::Text(label, valueToDisplay); } } else if (options.alignment == ComponentAlignment::Right) { if (options.labelPosition == LabelPosition::Above) { ImGui::NewLine(); ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x); ImGui::Text(label, valueToDisplay); } } if (options.showButtons) { if (Button("-", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value > options.min) { *value -= options.step; ClampFloat(value, options.min, options.max, options.step); dirty = true; } ImGui::SameLine(0, 3.0f); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("+").x + 20.0f + 3.0f)); } else { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); } if (ImGui::SliderScalar(invisibleLabel, ImGuiDataType_Float, &valueToDisplay, &minToDisplay, &maxToDisplay, options.format, options.flags)) { *value = options.isPercentage ? valueToDisplay / 100.0f : valueToDisplay; ClampFloat(value, options.min, options.max, options.step); dirty = true; } if (options.showButtons) { ImGui::SameLine(0, 3.0f); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (Button("+", ButtonOptions{ .color = options.color }.Size(Sizes::Inline)) && *value < options.max) { *value += options.step; ClampFloat(value, options.min, options.max, options.step); dirty = true; } } PopStyleSlider(); ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.disabledTooltip)) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); return dirty; } bool CVarSliderFloat(const char* label, const char* cvarName, const FloatSliderOptions& options) { bool dirty = false; float value = CVarGetFloat(cvarName, options.defaultValue); if (SliderFloat(label, &value, options)) { CVarSetFloat(cvarName, value); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); dirty = true; } return dirty; } bool CVarColorPicker(const char* label, const char* cvarName, Color_RGBA8 defaultColor) { Color_RGBA8 color = CVarGetColor(cvarName, defaultColor); ImVec4 colorVec = ImVec4(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); bool changed = false; PushStyleCombobox(Colors::Gray); if (ImGui::ColorEdit3(label, (float*)&colorVec, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoBorder)) { color.r = (uint8_t)(colorVec.x * 255.0f); color.g = (uint8_t)(colorVec.y * 255.0f); color.b = (uint8_t)(colorVec.z * 255.0f); color.a = (uint8_t)(colorVec.w * 255.0f); CVarSetColor(cvarName, color); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); changed = true; } PopStyleCombobox(); return changed; } void DrawFlagArray32(const std::string& name, uint32_t& flags) { ImGui::PushID(name.c_str()); for (int32_t flagIndex = 0; flagIndex < 32; flagIndex++) { if ((flagIndex % 8) != 0) { ImGui::SameLine(); } ImGui::PushID(flagIndex); uint32_t bitMask = 1 << flagIndex; bool flag = (flags & bitMask) != 0; std::string label = fmt::format("0x{:02X} ({})", flagIndex, flagIndex); if (UIWidgets::Checkbox(label.c_str(), &flag, CheckboxOptions{ { .tooltip = label.c_str() } }.LabelPosition(LabelPosition::None))) { if (flag) { flags |= bitMask; } else { flags &= ~bitMask; } } ImGui::PopID(); } ImGui::PopID(); } void DrawFlagArray16(const std::string& name, uint16_t& flags) { ImGui::PushID(name.c_str()); for (int16_t flagIndex = 0; flagIndex < 16; flagIndex++) { if ((flagIndex % 8) != 0) { ImGui::SameLine(); } ImGui::PushID(flagIndex); uint16_t bitMask = 1 << flagIndex; bool flag = (flags & bitMask) != 0; std::string label = fmt::format("0x{:02X} ({})", flagIndex, flagIndex); if (UIWidgets::Checkbox(label.c_str(), &flag, CheckboxOptions{ { .tooltip = label.c_str() } }.LabelPosition(LabelPosition::None))) { if (flag) { flags |= bitMask; } else { flags &= ~bitMask; } } ImGui::PopID(); } ImGui::PopID(); } void DrawFlagArray8(const std::string& name, uint8_t& flags) { ImGui::PushID(name.c_str()); for (int8_t flagIndex = 0; flagIndex < 8; flagIndex++) { if ((flagIndex % 8) != 0) { ImGui::SameLine(); } ImGui::PushID(flagIndex); uint8_t bitMask = 1 << flagIndex; bool flag = (flags & bitMask) != 0; std::string label = fmt::format("0x{:02X} ({})", flagIndex, flagIndex); if (UIWidgets::Checkbox(label.c_str(), &flag, CheckboxOptions{ { .tooltip = label.c_str() } }.LabelPosition(LabelPosition::None))) { if (flag) { flags |= bitMask; } else { flags &= ~bitMask; } } ImGui::PopID(); } ImGui::PopID(); } void DrawFlagArray8Mask(const std::string& name, uint8_t& flags) { ImGui::PushID(name.c_str()); for (int8_t flagIndex = 0; flagIndex < 8; flagIndex++) { if ((flagIndex % 8) != 0) { ImGui::SameLine(); } ImGui::PushID(flagIndex); uint8_t bitMask = 1 << flagIndex; bool flag = (flags & bitMask) != 0; std::string label = fmt::format("0x{:02X} ({})", bitMask, flagIndex); if (UIWidgets::Checkbox(label.c_str(), &flag, CheckboxOptions{ { .tooltip = label.c_str() } }.LabelPosition(LabelPosition::None))) { if (flag) { flags |= bitMask; } else { flags &= ~bitMask; } } ImGui::PopID(); } ImGui::PopID(); } } // namespace UIWidgets