mirror of
https://github.com/orange-cpp/omath.git
synced 2026-04-18 15:23:26 +00:00
Merge pull request #168 from orange-cpp/feature/hud_declarative
Feature/hud declarative
This commit is contained in:
@@ -68,71 +68,81 @@ namespace imgui_desktop::gui
|
||||
|
||||
if (ImGui::CollapsingHeader("Entity", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::SliderFloat("X##ent", &m_entity_x, 100.f, vp->Size.x - 100.f);
|
||||
ImGui::SliderFloat("Top Y", &m_entity_top_y, 20.f, m_entity_bottom_y - 20.f);
|
||||
ImGui::SliderFloat("Bottom Y", &m_entity_bottom_y, m_entity_top_y + 20.f, vp->Size.y - 20.f);
|
||||
ImGui::SliderFloat("X##ent", &m_entity_x, 100.f, vp->Size.x - 100.f);
|
||||
ImGui::SliderFloat("Top Y", &m_entity_top_y, 20.f, m_entity_bottom_y - 20.f);
|
||||
ImGui::SliderFloat("Bottom Y", &m_entity_bottom_y, m_entity_top_y + 20.f, vp->Size.y - 20.f);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Box", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Checkbox("Box", &m_show_box); ImGui::SameLine();
|
||||
ImGui::Checkbox("Cornered", &m_show_cornered_box); ImGui::SameLine();
|
||||
ImGui::Checkbox("Dashed", &m_show_dashed_box);
|
||||
ImGui::Checkbox("Box##chk", &m_show_box);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Cornered", &m_show_cornered_box);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Dashed", &m_show_dashed_box);
|
||||
ImGui::ColorEdit4("Color##box", reinterpret_cast<float*>(&m_box_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::ColorEdit4("Fill##box", reinterpret_cast<float*>(&m_box_fill), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Thickness", &m_box_thickness, 0.5f, 5.f);
|
||||
ImGui::SliderFloat("Corner ratio", &m_corner_ratio, 0.05f, 0.5f);
|
||||
ImGui::ColorEdit4("Fill##box", reinterpret_cast<float*>(&m_box_fill), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Thickness", &m_box_thickness, 0.5f, 5.f);
|
||||
ImGui::SliderFloat("Corner ratio", &m_corner_ratio, 0.05f, 0.5f);
|
||||
ImGui::Separator();
|
||||
ImGui::ColorEdit4("Dash color", reinterpret_cast<float*>(&m_dash_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Dash length", &m_dash_len, 2.f, 30.f);
|
||||
ImGui::SliderFloat("Dash gap", &m_dash_gap, 1.f, 20.f);
|
||||
ImGui::SliderFloat("Dash thick", &m_dash_thickness, 0.5f, 5.f);
|
||||
ImGui::ColorEdit4("Dash color", reinterpret_cast<float*>(&m_dash_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Dash length", &m_dash_len, 2.f, 30.f);
|
||||
ImGui::SliderFloat("Dash gap", &m_dash_gap, 1.f, 20.f);
|
||||
ImGui::SliderFloat("Dash thick", &m_dash_thickness, 0.5f, 5.f);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Bars", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::ColorEdit4("Color##bar", reinterpret_cast<float*>(&m_bar_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::ColorEdit4("BG##bar", reinterpret_cast<float*>(&m_bar_bg_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::ColorEdit4("Outline##bar", reinterpret_cast<float*>(&m_bar_outline_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Width##bar", &m_bar_width, 1.f, 20.f);
|
||||
ImGui::SliderFloat("Value##bar", &m_bar_value, 0.f, 1.f);
|
||||
ImGui::ColorEdit4("Color##bar", reinterpret_cast<float*>(&m_bar_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::ColorEdit4("BG##bar", reinterpret_cast<float*>(&m_bar_bg_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::ColorEdit4("Outline##bar", reinterpret_cast<float*>(&m_bar_outline_color),
|
||||
ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Width##bar", &m_bar_width, 1.f, 20.f);
|
||||
ImGui::SliderFloat("Value##bar", &m_bar_value, 0.f, 1.f);
|
||||
ImGui::SliderFloat("Offset##bar", &m_bar_offset, 1.f, 20.f);
|
||||
ImGui::Checkbox("Right##bar", &m_show_right_bar); ImGui::SameLine();
|
||||
ImGui::Checkbox("Left##bar", &m_show_left_bar);
|
||||
ImGui::Checkbox("Top##bar", &m_show_top_bar); ImGui::SameLine();
|
||||
ImGui::Checkbox("Bottom##bar", &m_show_bottom_bar);
|
||||
ImGui::Checkbox("Right dashed##bar", &m_show_right_dashed_bar); ImGui::SameLine();
|
||||
ImGui::Checkbox("Left dashed##bar", &m_show_left_dashed_bar);
|
||||
ImGui::Checkbox("Top dashed##bar", &m_show_top_dashed_bar); ImGui::SameLine();
|
||||
ImGui::Checkbox("Bot dashed##bar", &m_show_bottom_dashed_bar);
|
||||
ImGui::SliderFloat("Dash len##bar", &m_bar_dash_len, 2.f, 20.f);
|
||||
ImGui::SliderFloat("Dash gap##bar", &m_bar_dash_gap, 1.f, 15.f);
|
||||
ImGui::Checkbox("Right##bar", &m_show_right_bar);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Left##bar", &m_show_left_bar);
|
||||
ImGui::Checkbox("Top##bar", &m_show_top_bar);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Bottom##bar", &m_show_bottom_bar);
|
||||
ImGui::Checkbox("Right dashed##bar", &m_show_right_dashed_bar);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Left dashed##bar", &m_show_left_dashed_bar);
|
||||
ImGui::Checkbox("Top dashed##bar", &m_show_top_dashed_bar);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Bot dashed##bar", &m_show_bottom_dashed_bar);
|
||||
ImGui::SliderFloat("Dash len##bar", &m_bar_dash_len, 2.f, 20.f);
|
||||
ImGui::SliderFloat("Dash gap##bar", &m_bar_dash_gap, 1.f, 15.f);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Labels", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Checkbox("Outlined", &m_outlined);
|
||||
ImGui::SliderFloat("Offset##lbl", &m_label_offset, 0.f, 15.f);
|
||||
ImGui::Checkbox("Right##lbl", &m_show_right_labels); ImGui::SameLine();
|
||||
ImGui::Checkbox("Left##lbl", &m_show_left_labels);
|
||||
ImGui::Checkbox("Top##lbl", &m_show_top_labels); ImGui::SameLine();
|
||||
ImGui::Checkbox("Bottom##lbl", &m_show_bottom_labels);
|
||||
ImGui::Checkbox("Ctr top##lbl", &m_show_centered_top); ImGui::SameLine();
|
||||
ImGui::Checkbox("Ctr bot##lbl", &m_show_centered_bottom);
|
||||
ImGui::Checkbox("Right##lbl", &m_show_right_labels);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Left##lbl", &m_show_left_labels);
|
||||
ImGui::Checkbox("Top##lbl", &m_show_top_labels);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Bottom##lbl", &m_show_bottom_labels);
|
||||
ImGui::Checkbox("Ctr top##lbl", &m_show_centered_top);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Ctr bot##lbl", &m_show_centered_bottom);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Skeleton"))
|
||||
{
|
||||
ImGui::Checkbox("Show##skel", &m_show_skeleton);
|
||||
ImGui::ColorEdit4("Color##skel", reinterpret_cast<float*>(&m_skel_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Thick##skel", &m_skel_thickness, 0.5f, 5.f);
|
||||
ImGui::Checkbox("Show##skel", &m_show_skeleton);
|
||||
ImGui::ColorEdit4("Color##skel", reinterpret_cast<float*>(&m_skel_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Thick##skel", &m_skel_thickness, 0.5f, 5.f);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Snap Line"))
|
||||
{
|
||||
ImGui::Checkbox("Show##snap", &m_show_snap);
|
||||
ImGui::ColorEdit4("Color##snap", reinterpret_cast<float*>(&m_snap_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Width##snap", &m_snap_width, 0.5f, 5.f);
|
||||
ImGui::Checkbox("Show##snap", &m_show_snap);
|
||||
ImGui::ColorEdit4("Color##snap", reinterpret_cast<float*>(&m_snap_color), ImGuiColorEditFlags_NoInputs);
|
||||
ImGui::SliderFloat("Width##snap", &m_snap_width, 0.5f, 5.f);
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
@@ -141,77 +151,64 @@ namespace imgui_desktop::gui
|
||||
|
||||
void MainWindow::draw_overlay()
|
||||
{
|
||||
using namespace omath::hud::widget;
|
||||
using omath::hud::when;
|
||||
const auto* vp = ImGui::GetMainViewport();
|
||||
const Bar bar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset};
|
||||
const DashedBar dbar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width,
|
||||
m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset};
|
||||
|
||||
omath::hud::EntityOverlay ent(
|
||||
{m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y},
|
||||
std::make_shared<omath::hud::ImguiHudRenderer>());
|
||||
omath::hud::EntityOverlay({m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y},
|
||||
std::make_shared<omath::hud::ImguiHudRenderer>())
|
||||
.contents(
|
||||
// ── Boxes ────────────────────────────────────────────────────
|
||||
when(m_show_box, Box{m_box_color, m_box_fill, m_box_thickness}),
|
||||
when(m_show_cornered_box, CorneredBox{omath::Color::from_rgba(255, 0, 255, 255), m_box_fill,
|
||||
m_corner_ratio, m_box_thickness}),
|
||||
when(m_show_dashed_box, DashedBox{m_dash_color, m_dash_len, m_dash_gap, m_dash_thickness}),
|
||||
|
||||
draw_boxes(ent);
|
||||
draw_bars(ent);
|
||||
draw_labels(ent);
|
||||
|
||||
if (m_show_skeleton)
|
||||
ent.add_skeleton(m_skel_color, m_skel_thickness);
|
||||
if (m_show_snap)
|
||||
ent.add_snap_line({vp->Size.x / 2.f, vp->Size.y}, m_snap_color, m_snap_width);
|
||||
}
|
||||
|
||||
void MainWindow::draw_boxes(omath::hud::EntityOverlay& ent) const
|
||||
{
|
||||
if (m_show_box)
|
||||
ent.add_2d_box(m_box_color, m_box_fill, m_box_thickness);
|
||||
if (m_show_cornered_box)
|
||||
ent.add_cornered_2d_box(omath::Color::from_rgba(255, 0, 255, 255), m_box_fill, m_corner_ratio, m_box_thickness);
|
||||
if (m_show_dashed_box)
|
||||
ent.add_dashed_box(m_dash_color, m_dash_len, m_dash_gap, m_dash_thickness);
|
||||
}
|
||||
|
||||
void MainWindow::draw_bars(omath::hud::EntityOverlay& ent) const
|
||||
{
|
||||
if (m_show_right_bar)
|
||||
ent.add_right_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset);
|
||||
if (m_show_left_bar)
|
||||
ent.add_left_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset);
|
||||
if (m_show_top_bar)
|
||||
ent.add_top_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset);
|
||||
if (m_show_bottom_bar)
|
||||
ent.add_bottom_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset);
|
||||
|
||||
if (m_show_right_dashed_bar)
|
||||
ent.add_right_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset);
|
||||
if (m_show_left_dashed_bar)
|
||||
ent.add_left_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset);
|
||||
if (m_show_top_dashed_bar)
|
||||
ent.add_top_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset);
|
||||
if (m_show_bottom_dashed_bar)
|
||||
ent.add_bottom_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset);
|
||||
}
|
||||
|
||||
void MainWindow::draw_labels(omath::hud::EntityOverlay& ent) const
|
||||
{
|
||||
if (m_show_right_labels)
|
||||
{
|
||||
ent.add_right_label({0.f, 1.f, 0.f, 1.f}, m_label_offset, m_outlined, "Health: {}/100", 100);
|
||||
ent.add_right_label({1.f, 0.f, 0.f, 1.f}, m_label_offset, m_outlined, "Shield: {}/125", 125);
|
||||
ent.add_right_label({1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*");
|
||||
}
|
||||
if (m_show_left_labels)
|
||||
{
|
||||
ent.add_left_label(omath::Color::from_rgba(255, 128, 0, 255), m_label_offset, m_outlined, "Armor: 75");
|
||||
ent.add_left_label(omath::Color::from_rgba(0, 200, 255, 255), m_label_offset, m_outlined, "Level: 42");
|
||||
}
|
||||
if (m_show_top_labels)
|
||||
{
|
||||
ent.add_top_label(omath::Color::from_rgba(255, 255, 0, 255), m_label_offset, m_outlined, "*SCOPED*");
|
||||
ent.add_top_label(omath::Color::from_rgba(255, 0, 0, 255), m_label_offset, m_outlined, "*BLEEDING*");
|
||||
}
|
||||
if (m_show_centered_top)
|
||||
ent.add_centered_top_label(omath::Color::from_rgba(0, 255, 255, 255), m_label_offset, m_outlined, "*VISIBLE*");
|
||||
if (m_show_centered_bottom)
|
||||
ent.add_centered_bottom_label(omath::Color::from_rgba(255, 255, 255, 255), m_label_offset, m_outlined, "PlayerName");
|
||||
if (m_show_bottom_labels)
|
||||
ent.add_bottom_label(omath::Color::from_rgba(200, 200, 0, 255), m_label_offset, m_outlined, "42m");
|
||||
RightSide
|
||||
{
|
||||
when(m_show_right_bar, bar),
|
||||
when(m_show_right_dashed_bar, dbar),
|
||||
when(m_show_right_labels,
|
||||
Label{{0.f, 1.f, 0.f, 1.f}, m_label_offset, m_outlined, "Health: 100/100"}),
|
||||
when(m_show_right_labels,
|
||||
Label{{1.f, 0.f, 0.f, 1.f}, m_label_offset, m_outlined, "Shield: 125/125"}),
|
||||
when(m_show_right_labels,
|
||||
Label{{1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*"}),
|
||||
},
|
||||
LeftSide
|
||||
{
|
||||
when(m_show_left_bar, bar),
|
||||
when(m_show_left_dashed_bar, dbar),
|
||||
when(m_show_left_labels, Label{omath::Color::from_rgba(255, 128, 0, 255),
|
||||
m_label_offset, m_outlined, "Armor: 75"}),
|
||||
when(m_show_left_labels, Label{omath::Color::from_rgba(0, 200, 255, 255),
|
||||
m_label_offset, m_outlined, "Level: 42"}),
|
||||
},
|
||||
TopSide
|
||||
{
|
||||
when(m_show_top_bar, bar),
|
||||
when(m_show_top_dashed_bar, dbar),
|
||||
when(m_show_centered_top, Centered{Label{omath::Color::from_rgba(0, 255, 255, 255),
|
||||
m_label_offset, m_outlined, "*VISIBLE*"}}),
|
||||
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 255, 0, 255), m_label_offset,
|
||||
m_outlined, "*SCOPED*"}),
|
||||
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 0, 0, 255), m_label_offset,
|
||||
m_outlined, "*BLEEDING*"}),
|
||||
},
|
||||
BottomSide
|
||||
{
|
||||
when(m_show_bottom_bar, bar),
|
||||
when(m_show_bottom_dashed_bar, dbar),
|
||||
when(m_show_centered_bottom, Centered{Label{omath::Color::from_rgba(255, 255, 255, 255),
|
||||
m_label_offset, m_outlined, "PlayerName"}}),
|
||||
when(m_show_bottom_labels, Label{omath::Color::from_rgba(200, 200, 0, 255),
|
||||
m_label_offset, m_outlined, "42m"}),
|
||||
},
|
||||
when(m_show_skeleton, Skeleton{m_skel_color, m_skel_thickness}),
|
||||
when(m_show_snap, SnapLine{{vp->Size.x / 2.f, vp->Size.y}, m_snap_color, m_snap_width}));
|
||||
}
|
||||
|
||||
void MainWindow::present()
|
||||
|
||||
@@ -19,9 +19,6 @@ namespace imgui_desktop::gui
|
||||
private:
|
||||
void draw_controls();
|
||||
void draw_overlay();
|
||||
void draw_boxes(omath::hud::EntityOverlay& ent) const;
|
||||
void draw_bars(omath::hud::EntityOverlay& ent) const;
|
||||
void draw_labels(omath::hud::EntityOverlay& ent) const;
|
||||
void present();
|
||||
|
||||
GLFWwindow* m_window = nullptr;
|
||||
@@ -46,8 +43,8 @@ namespace imgui_desktop::gui
|
||||
omath::Color m_bar_bg_color{0.f, 0.f, 0.f, 0.5f};
|
||||
omath::Color m_bar_outline_color{0.f, 0.f, 0.f, 1.f};
|
||||
float m_bar_width = 4.f, m_bar_value = 0.75f, m_bar_offset = 5.f;
|
||||
bool m_show_right_bar = true, m_show_left_bar = true;
|
||||
bool m_show_top_bar = true, m_show_bottom_bar = true;
|
||||
bool m_show_right_bar = true, m_show_left_bar = true;
|
||||
bool m_show_top_bar = true, m_show_bottom_bar = true;
|
||||
bool m_show_right_dashed_bar = false, m_show_left_dashed_bar = false;
|
||||
bool m_show_top_dashed_bar = false, m_show_bottom_dashed_bar = false;
|
||||
float m_bar_dash_len = 6.f, m_bar_dash_gap = 4.f;
|
||||
|
||||
@@ -3,127 +3,161 @@
|
||||
//
|
||||
#pragma once
|
||||
#include "canvas_box.hpp"
|
||||
#include "entity_overlay_widgets.hpp"
|
||||
#include "hud_renderer_interface.hpp"
|
||||
#include "omath/linear_algebra/vector2.hpp"
|
||||
#include "omath/utility/color.hpp"
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace omath::hud
|
||||
{
|
||||
class EntityOverlay final
|
||||
{
|
||||
public:
|
||||
EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom,
|
||||
const std::shared_ptr<HudRendererInterface>& renderer);
|
||||
const std::shared_ptr<HudRendererInterface>& renderer);
|
||||
|
||||
void add_2d_box(const Color& box_color, const Color& fill_color = Color{0.f, 0.f, 0.f, 0.f},
|
||||
float thickness = 1.f) const;
|
||||
// ── Boxes ────────────────────────────────────────────────────────
|
||||
EntityOverlay& add_2d_box(const Color& box_color, const Color& fill_color = Color{0.f, 0.f, 0.f, 0.f},
|
||||
float thickness = 1.f);
|
||||
|
||||
void add_cornered_2d_box(const Color& box_color, const Color& fill_color = Color{0.f, 0.f, 0.f, 0.f},
|
||||
float corner_ratio_len = 0.2f, float thickness = 1.f) const;
|
||||
EntityOverlay& add_cornered_2d_box(const Color& box_color, const Color& fill_color = Color{0.f, 0.f, 0.f, 0.f},
|
||||
float corner_ratio_len = 0.2f, float thickness = 1.f);
|
||||
|
||||
void add_right_bar(const Color& color, const Color& outline_color, const Color& bg_color, float width,
|
||||
float ratio, float offset = 5.f);
|
||||
EntityOverlay& add_dashed_box(const Color& color, float dash_len = 8.f, float gap_len = 5.f,
|
||||
float thickness = 1.f);
|
||||
|
||||
void add_left_bar(const Color& color, const Color& outline_color, const Color& bg_color, float width,
|
||||
float ratio, float offset = 5.f);
|
||||
// ── Bars ─────────────────────────────────────────────────────────
|
||||
EntityOverlay& add_right_bar(const Color& color, const Color& outline_color, const Color& bg_color, float width,
|
||||
float ratio, float offset = 5.f);
|
||||
|
||||
EntityOverlay& add_left_bar(const Color& color, const Color& outline_color, const Color& bg_color, float width,
|
||||
float ratio, float offset = 5.f);
|
||||
|
||||
EntityOverlay& add_top_bar(const Color& color, const Color& outline_color, const Color& bg_color, float height,
|
||||
float ratio, float offset = 5.f);
|
||||
|
||||
EntityOverlay& add_bottom_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float height, float ratio, float offset = 5.f);
|
||||
|
||||
EntityOverlay& add_right_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float width, float ratio, float dash_len, float gap_len,
|
||||
float offset = 5.f);
|
||||
|
||||
EntityOverlay& add_left_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float width, float ratio, float dash_len, float gap_len, float offset = 5.f);
|
||||
|
||||
EntityOverlay& add_top_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float height, float ratio, float dash_len, float gap_len, float offset = 5.f);
|
||||
|
||||
EntityOverlay& add_bottom_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float height, float ratio, float dash_len, float gap_len,
|
||||
float offset = 5.f);
|
||||
|
||||
// ── Labels ───────────────────────────────────────────────────────
|
||||
EntityOverlay& add_right_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
||||
|
||||
EntityOverlay& add_left_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
||||
|
||||
EntityOverlay& add_top_label(const Color& color, float offset, bool outlined, std::string_view text);
|
||||
|
||||
EntityOverlay& add_bottom_label(const Color& color, float offset, bool outlined, std::string_view text);
|
||||
|
||||
EntityOverlay& add_centered_top_label(const Color& color, float offset, bool outlined,
|
||||
const std::string_view& text);
|
||||
|
||||
EntityOverlay& add_centered_bottom_label(const Color& color, float offset, bool outlined,
|
||||
const std::string_view& text);
|
||||
|
||||
template<typename... Args>
|
||||
void add_right_label(const Color& color, const float offset, const bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
EntityOverlay& add_right_label(const Color& color, float offset, bool outlined, std::format_string<Args...> fmt,
|
||||
Args&&... args)
|
||||
{
|
||||
const std::string label = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
add_right_label(color, offset, outlined, std::string_view{label});
|
||||
return add_right_label(color, offset, outlined,
|
||||
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
|
||||
}
|
||||
|
||||
void add_right_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
||||
|
||||
template<typename... Args>
|
||||
void add_top_label(const Color& color, const float offset, const bool outlined, std::format_string<Args...> fmt,
|
||||
Args&&... args)
|
||||
EntityOverlay& add_left_label(const Color& color, float offset, bool outlined, std::format_string<Args...> fmt,
|
||||
Args&&... args)
|
||||
{
|
||||
const std::string label = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
add_top_label(color, offset, outlined, std::string_view{label});
|
||||
return add_left_label(color, offset, outlined,
|
||||
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
|
||||
}
|
||||
|
||||
void add_top_label(const Color& color, float offset, bool outlined, std::string_view text);
|
||||
|
||||
void add_top_bar(const Color& color, const Color& outline_color, const Color& bg_color, float height,
|
||||
float ratio, float offset = 5.f);
|
||||
|
||||
void add_snap_line(const Vector2<float>& start_pos, const Color& color, float width);
|
||||
|
||||
void add_dashed_box(const Color& color, float dash_len = 8.f, float gap_len = 5.f,
|
||||
float thickness = 1.f) const;
|
||||
|
||||
void add_skeleton(const Color& color, float thickness = 1.f) const;
|
||||
|
||||
void add_right_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float width, float ratio, float dash_len, float gap_len, float offset = 5.f);
|
||||
|
||||
void add_left_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float width, float ratio, float dash_len, float gap_len, float offset = 5.f);
|
||||
|
||||
void add_top_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float height, float ratio, float dash_len, float gap_len, float offset = 5.f);
|
||||
|
||||
void add_bottom_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
float height, float ratio, float dash_len, float gap_len, float offset = 5.f);
|
||||
|
||||
void add_bottom_bar(const Color& color, const Color& outline_color, const Color& bg_color, float height,
|
||||
float ratio, float offset = 5.f);
|
||||
|
||||
template<typename... Args>
|
||||
void add_bottom_label(const Color& color, const float offset, const bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
EntityOverlay& add_top_label(const Color& color, float offset, bool outlined, std::format_string<Args...> fmt,
|
||||
Args&&... args)
|
||||
{
|
||||
const std::string label = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
add_bottom_label(color, offset, outlined, std::string_view{label});
|
||||
return add_top_label(color, offset, outlined,
|
||||
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
|
||||
}
|
||||
|
||||
void add_bottom_label(const Color& color, float offset, bool outlined, std::string_view text);
|
||||
|
||||
template<typename... Args>
|
||||
void add_left_label(const Color& color, const float offset, const bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
EntityOverlay& add_bottom_label(const Color& color, float offset, bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
{
|
||||
const std::string label = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
add_left_label(color, offset, outlined, std::string_view{label});
|
||||
return add_bottom_label(color, offset, outlined,
|
||||
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
|
||||
}
|
||||
|
||||
void add_left_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
||||
|
||||
template<typename... Args>
|
||||
void add_centered_bottom_label(const Color& color, const float offset, const bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
EntityOverlay& add_centered_top_label(const Color& color, float offset, bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
{
|
||||
const std::string label = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
add_centered_bottom_label(color, offset, outlined, std::string_view{label});
|
||||
return add_centered_top_label(color, offset, outlined,
|
||||
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
|
||||
}
|
||||
|
||||
void add_centered_bottom_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
||||
|
||||
template<typename... Args>
|
||||
void add_centered_top_label(const Color& color, const float offset, const bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
EntityOverlay& add_centered_bottom_label(const Color& color, float offset, bool outlined,
|
||||
std::format_string<Args...> fmt, Args&&... args)
|
||||
{
|
||||
const std::string label = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
add_centered_top_label(color, offset, outlined, std::string_view{label});
|
||||
return add_centered_bottom_label(color, offset, outlined,
|
||||
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
|
||||
}
|
||||
|
||||
void add_centered_top_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
||||
// ── Misc ─────────────────────────────────────────────────────────
|
||||
EntityOverlay& add_snap_line(const Vector2<float>& start_pos, const Color& color, float width);
|
||||
|
||||
EntityOverlay& add_skeleton(const Color& color, float thickness = 1.f);
|
||||
|
||||
// ── Declarative interface ─────────────────────────────────────────
|
||||
/// Pass any combination of widget:: descriptor structs (and std::optional<W>
|
||||
/// from when()) to render them all in declaration order.
|
||||
template<typename... Widgets>
|
||||
EntityOverlay& contents(Widgets&&... widgets)
|
||||
{
|
||||
(dispatch(std::forward<Widgets>(widgets)), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void draw_outlined_text(const Vector2<float>& position, const Color& color,
|
||||
const std::string_view& text);
|
||||
void draw_dashed_line(const Vector2<float>& from, const Vector2<float>& to, const Color& color,
|
||||
float dash_len, float gap_len, float thickness) const;
|
||||
// optional<W> dispatch — enables when() conditional widgets
|
||||
template<typename W>
|
||||
void dispatch(const std::optional<W>& w)
|
||||
{
|
||||
if (w)
|
||||
dispatch(*w);
|
||||
}
|
||||
|
||||
void dispatch(const widget::Box& w);
|
||||
void dispatch(const widget::CorneredBox& w);
|
||||
void dispatch(const widget::DashedBox& w);
|
||||
void dispatch(const widget::RightSide& w);
|
||||
void dispatch(const widget::LeftSide& w);
|
||||
void dispatch(const widget::TopSide& w);
|
||||
void dispatch(const widget::BottomSide& w);
|
||||
void dispatch(const widget::Skeleton& w);
|
||||
void dispatch(const widget::SnapLine& w);
|
||||
void draw_outlined_text(const Vector2<float>& position, const Color& color, const std::string_view& text);
|
||||
void draw_dashed_line(const Vector2<float>& from, const Vector2<float>& to, const Color& color, float dash_len,
|
||||
float gap_len, float thickness) const;
|
||||
void draw_dashed_fill(const Vector2<float>& origin, const Vector2<float>& step_dir,
|
||||
const Vector2<float>& perp_dir, float full_len, float filled_len,
|
||||
const Color& fill_color, const Color& split_color,
|
||||
float dash_len, float gap_len) const;
|
||||
const Vector2<float>& perp_dir, float full_len, float filled_len, const Color& fill_color,
|
||||
const Color& split_color, float dash_len, float gap_len) const;
|
||||
|
||||
CanvasBox m_canvas;
|
||||
Vector2<float> m_text_cursor_right;
|
||||
Vector2<float> m_text_cursor_top;
|
||||
@@ -131,4 +165,4 @@ namespace omath::hud
|
||||
Vector2<float> m_text_cursor_left;
|
||||
std::shared_ptr<HudRendererInterface> m_renderer;
|
||||
};
|
||||
} // namespace omath::hud
|
||||
} // namespace omath::hud
|
||||
|
||||
142
include/omath/hud/entity_overlay_widgets.hpp
Normal file
142
include/omath/hud/entity_overlay_widgets.hpp
Normal file
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// Created by orange on 15.03.2026.
|
||||
//
|
||||
#pragma once
|
||||
#include "omath/linear_algebra/vector2.hpp"
|
||||
#include "omath/utility/color.hpp"
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
namespace omath::hud::widget
|
||||
{
|
||||
// ── Overloaded helper for std::visit ──────────────────────────────────────
|
||||
template<typename... Ts>
|
||||
struct Overloaded : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template<typename... Ts>
|
||||
Overloaded(Ts...) -> Overloaded<Ts...>;
|
||||
|
||||
// ── Standalone widgets ────────────────────────────────────────────────────
|
||||
struct Box
|
||||
{
|
||||
Color color;
|
||||
Color fill{0.f, 0.f, 0.f, 0.f};
|
||||
float thickness = 1.f;
|
||||
};
|
||||
|
||||
struct CorneredBox
|
||||
{
|
||||
Color color;
|
||||
Color fill{0.f, 0.f, 0.f, 0.f};
|
||||
float corner_ratio = 0.2f;
|
||||
float thickness = 1.f;
|
||||
};
|
||||
|
||||
struct DashedBox
|
||||
{
|
||||
Color color;
|
||||
float dash_len = 8.f;
|
||||
float gap_len = 5.f;
|
||||
float thickness = 1.f;
|
||||
};
|
||||
|
||||
struct Skeleton
|
||||
{
|
||||
Color color;
|
||||
float thickness = 1.f;
|
||||
};
|
||||
struct SnapLine
|
||||
{
|
||||
Vector2<float> start;
|
||||
Color color;
|
||||
float width;
|
||||
};
|
||||
|
||||
// ── Side-agnostic widgets (used inside XxxSide containers) ────────────────
|
||||
|
||||
/// A filled bar. `size` is width for left/right sides, height for top/bottom.
|
||||
struct Bar
|
||||
{
|
||||
Color color;
|
||||
Color outline;
|
||||
Color bg;
|
||||
float size;
|
||||
float ratio;
|
||||
float offset = 5.f;
|
||||
};
|
||||
|
||||
/// A dashed bar. Same field semantics as Bar plus dash parameters.
|
||||
struct DashedBar
|
||||
{
|
||||
Color color;
|
||||
Color outline;
|
||||
Color bg;
|
||||
float size;
|
||||
float ratio;
|
||||
float dash_len;
|
||||
float gap_len;
|
||||
float offset = 5.f;
|
||||
};
|
||||
|
||||
struct Label
|
||||
{
|
||||
Color color;
|
||||
float offset;
|
||||
bool outlined;
|
||||
std::string_view text;
|
||||
};
|
||||
|
||||
/// Wraps a Label to request horizontal centering (only applied in TopSide / BottomSide).
|
||||
template<typename W>
|
||||
struct Centered
|
||||
{
|
||||
W child;
|
||||
};
|
||||
template<typename W>
|
||||
Centered(W) -> Centered<W>;
|
||||
|
||||
// ── Side widget variant ───────────────────────────────────────────────────
|
||||
struct None {}; ///< No-op placeholder — used by widget::when for disabled elements.
|
||||
using SideWidget = std::variant<None, Bar, DashedBar, Label, Centered<Label>>;
|
||||
|
||||
// ── Side containers ───────────────────────────────────────────────────────
|
||||
// Storing std::initializer_list<SideWidget> is safe here: the backing array
|
||||
// is a const SideWidget[] on the caller's stack whose lifetime matches the
|
||||
// temporary side-container object, which is consumed within the same
|
||||
// full-expression by EntityOverlay::dispatch. No heap allocation occurs.
|
||||
|
||||
struct RightSide { std::initializer_list<SideWidget> children; RightSide(std::initializer_list<SideWidget> c) : children(c) {} };
|
||||
struct LeftSide { std::initializer_list<SideWidget> children; LeftSide(std::initializer_list<SideWidget> c) : children(c) {} };
|
||||
struct TopSide { std::initializer_list<SideWidget> children; TopSide(std::initializer_list<SideWidget> c) : children(c) {} };
|
||||
struct BottomSide { std::initializer_list<SideWidget> children; BottomSide(std::initializer_list<SideWidget> c) : children(c) {} };
|
||||
|
||||
} // namespace omath::hud::widget
|
||||
|
||||
namespace omath::hud::widget
|
||||
{
|
||||
/// Inside XxxSide containers: returns the widget as a SideWidget when condition is true,
|
||||
/// or None{} otherwise. Preferred over hud::when for types inside the SideWidget variant.
|
||||
template<typename W>
|
||||
requires std::constructible_from<SideWidget, W>
|
||||
SideWidget when(const bool condition, W widget)
|
||||
{
|
||||
if (condition) return SideWidget{std::move(widget)};
|
||||
return None{};
|
||||
}
|
||||
} // namespace omath::hud::widget
|
||||
|
||||
namespace omath::hud
|
||||
{
|
||||
/// Top-level: returns an engaged optional<W> when condition is true, std::nullopt otherwise.
|
||||
/// Designed for use with EntityOverlay::contents() for top-level widget types.
|
||||
template<typename W>
|
||||
std::optional<W> when(const bool condition, W widget)
|
||||
{
|
||||
if (condition) return std::move(widget);
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace omath::hud
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace omath::hud
|
||||
{
|
||||
void EntityOverlay::add_2d_box(const Color& box_color, const Color& fill_color, const float thickness) const
|
||||
EntityOverlay& EntityOverlay::add_2d_box(const Color& box_color, const Color& fill_color, const float thickness)
|
||||
{
|
||||
const auto points = m_canvas.as_array();
|
||||
|
||||
@@ -13,9 +13,11 @@ namespace omath::hud
|
||||
|
||||
if (fill_color.value().w > 0.f)
|
||||
m_renderer->add_filled_polyline({points.data(), points.size()}, fill_color);
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::add_cornered_2d_box(const Color& box_color, const Color& fill_color,
|
||||
const float corner_ratio_len, const float thickness) const
|
||||
EntityOverlay& EntityOverlay::add_cornered_2d_box(const Color& box_color, const Color& fill_color,
|
||||
const float corner_ratio_len, const float thickness)
|
||||
{
|
||||
const auto corner_line_length =
|
||||
std::abs((m_canvas.top_left_corner - m_canvas.top_right_corner).x * corner_ratio_len);
|
||||
@@ -50,9 +52,11 @@ namespace omath::hud
|
||||
m_renderer->add_line(m_canvas.bottom_right_corner,
|
||||
m_canvas.bottom_right_corner - Vector2<float>{corner_line_length, 0.f}, box_color,
|
||||
thickness);
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::add_right_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float width, float ratio, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_right_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float width, float ratio, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const auto max_bar_height = std::abs(m_canvas.top_right_corner.y - m_canvas.bottom_right_corner.y);
|
||||
@@ -65,9 +69,11 @@ namespace omath::hud
|
||||
bar_start + Vector2<float>(width, -max_bar_height), outline_color);
|
||||
|
||||
m_text_cursor_right.x += offset + width;
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::add_left_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float width, float ratio, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_left_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float width, float ratio, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const auto max_bar_height = std::abs(m_canvas.top_left_corner.y - m_canvas.bottom_right_corner.y);
|
||||
@@ -80,9 +86,11 @@ namespace omath::hud
|
||||
bar_start + Vector2<float>(width, -max_bar_height), outline_color);
|
||||
|
||||
m_text_cursor_left.x -= offset + width;
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::add_right_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
EntityOverlay& EntityOverlay::add_right_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
{
|
||||
if (outlined)
|
||||
draw_outlined_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text);
|
||||
@@ -90,9 +98,11 @@ namespace omath::hud
|
||||
m_renderer->add_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text.data());
|
||||
|
||||
m_text_cursor_right.y += m_renderer->calc_text_size(text.data()).y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::add_top_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view text)
|
||||
EntityOverlay& EntityOverlay::add_top_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view text)
|
||||
{
|
||||
m_text_cursor_top.y -= m_renderer->calc_text_size(text.data()).y;
|
||||
|
||||
@@ -100,9 +110,11 @@ namespace omath::hud
|
||||
draw_outlined_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text);
|
||||
else
|
||||
m_renderer->add_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text.data());
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::add_top_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float height, float ratio, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_top_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float height, float ratio, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const auto max_bar_width = std::abs(m_canvas.top_left_corner.x - m_canvas.bottom_right_corner.x);
|
||||
@@ -114,36 +126,38 @@ namespace omath::hud
|
||||
m_renderer->add_rectangle(bar_start, bar_start + Vector2<float>(max_bar_width, -height), outline_color);
|
||||
|
||||
m_text_cursor_top.y -= offset + height;
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::add_snap_line(const Vector2<float>& start_pos, const Color& color, const float width)
|
||||
EntityOverlay& EntityOverlay::add_snap_line(const Vector2<float>& start_pos, const Color& color, const float width)
|
||||
{
|
||||
const Vector2<float> line_end =
|
||||
m_canvas.bottom_left_corner
|
||||
+ Vector2<float>{m_canvas.bottom_right_corner.x - m_canvas.bottom_left_corner.x, 0.f} / 2;
|
||||
m_renderer->add_line(start_pos, line_end, color, width);
|
||||
|
||||
return *this;
|
||||
}
|
||||
void EntityOverlay::draw_dashed_fill(const Vector2<float>& origin, const Vector2<float>& step_dir,
|
||||
const Vector2<float>& perp_dir, const float full_len,
|
||||
const float filled_len, const Color& fill_color,
|
||||
const Color& split_color, const float dash_len,
|
||||
const Vector2<float>& perp_dir, const float full_len, const float filled_len,
|
||||
const Color& fill_color, const Color& split_color, const float dash_len,
|
||||
const float gap_len) const
|
||||
{
|
||||
if (full_len <= 0.f)
|
||||
return;
|
||||
|
||||
const float step = dash_len + gap_len;
|
||||
const float n = std::floor((full_len + gap_len) / step);
|
||||
const float step = dash_len + gap_len;
|
||||
const float n = std::floor((full_len + gap_len) / step);
|
||||
if (n < 1.f)
|
||||
return;
|
||||
|
||||
const float used = n * dash_len + (n - 1.f) * gap_len;
|
||||
const float used = n * dash_len + (n - 1.f) * gap_len;
|
||||
const float offset = (full_len - used) / 2.f;
|
||||
|
||||
const auto fill_rect = [&](const Vector2<float>& a, const Vector2<float>& b, const Color& c)
|
||||
{
|
||||
m_renderer->add_filled_rectangle(
|
||||
{std::min(a.x, b.x), std::min(a.y, b.y)},
|
||||
{std::max(a.x, b.x), std::max(a.y, b.y)}, c);
|
||||
m_renderer->add_filled_rectangle({std::min(a.x, b.x), std::min(a.y, b.y)},
|
||||
{std::max(a.x, b.x), std::max(a.y, b.y)}, c);
|
||||
};
|
||||
|
||||
// Draw split lines (gaps) across the full bar first
|
||||
@@ -154,9 +168,9 @@ namespace omath::hud
|
||||
for (float i = 0.f; i < n; ++i)
|
||||
{
|
||||
const float dash_start = offset + i * step;
|
||||
const float dash_end = dash_start + dash_len;
|
||||
const float gap_start = dash_end;
|
||||
const float gap_end = dash_start + step;
|
||||
const float dash_end = dash_start + dash_len;
|
||||
const float gap_start = dash_end;
|
||||
const float gap_end = dash_start + step;
|
||||
|
||||
// Fill dash only up to filled_len
|
||||
if (dash_start < filled_len)
|
||||
@@ -181,106 +195,115 @@ namespace omath::hud
|
||||
fill_rect(origin + step_dir * trail_start, origin + step_dir * full_len + perp_dir, split_color);
|
||||
}
|
||||
|
||||
void EntityOverlay::add_right_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float width, float ratio, const float dash_len,
|
||||
const float gap_len, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_right_dashed_bar(const Color& color, const Color& outline_color,
|
||||
const Color& bg_color, const float width, float ratio,
|
||||
const float dash_len, const float gap_len, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const float height = std::abs(m_canvas.top_right_corner.y - m_canvas.bottom_right_corner.y);
|
||||
const auto bar_start = Vector2<float>{m_text_cursor_right.x + offset, m_canvas.bottom_right_corner.y};
|
||||
const float height = std::abs(m_canvas.top_right_corner.y - m_canvas.bottom_right_corner.y);
|
||||
const auto bar_start = Vector2<float>{m_text_cursor_right.x + offset, m_canvas.bottom_right_corner.y};
|
||||
|
||||
m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2<float>{width, -height}, bg_color);
|
||||
draw_dashed_fill(bar_start, {0.f, -1.f}, {width, 0.f}, height, height * ratio,
|
||||
color, outline_color, dash_len, gap_len);
|
||||
m_renderer->add_rectangle(bar_start - Vector2<float>{1.f, 0.f},
|
||||
bar_start + Vector2<float>{width, -height}, outline_color);
|
||||
draw_dashed_fill(bar_start, {0.f, -1.f}, {width, 0.f}, height, height * ratio, color, outline_color, dash_len,
|
||||
gap_len);
|
||||
m_renderer->add_rectangle(bar_start - Vector2<float>{1.f, 0.f}, bar_start + Vector2<float>{width, -height},
|
||||
outline_color);
|
||||
m_text_cursor_right.x += offset + width;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_left_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float width, float ratio, const float dash_len,
|
||||
const float gap_len, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_left_dashed_bar(const Color& color, const Color& outline_color,
|
||||
const Color& bg_color, const float width, float ratio,
|
||||
const float dash_len, const float gap_len, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const float height = std::abs(m_canvas.top_left_corner.y - m_canvas.bottom_left_corner.y);
|
||||
const auto bar_start = Vector2<float>{m_text_cursor_left.x - (offset + width), m_canvas.bottom_left_corner.y};
|
||||
const float height = std::abs(m_canvas.top_left_corner.y - m_canvas.bottom_left_corner.y);
|
||||
const auto bar_start = Vector2<float>{m_text_cursor_left.x - (offset + width), m_canvas.bottom_left_corner.y};
|
||||
|
||||
m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2<float>{width, -height}, bg_color);
|
||||
draw_dashed_fill(bar_start, {0.f, -1.f}, {width, 0.f}, height, height * ratio,
|
||||
color, outline_color, dash_len, gap_len);
|
||||
m_renderer->add_rectangle(bar_start - Vector2<float>{1.f, 0.f},
|
||||
bar_start + Vector2<float>{width, -height}, outline_color);
|
||||
draw_dashed_fill(bar_start, {0.f, -1.f}, {width, 0.f}, height, height * ratio, color, outline_color, dash_len,
|
||||
gap_len);
|
||||
m_renderer->add_rectangle(bar_start - Vector2<float>{1.f, 0.f}, bar_start + Vector2<float>{width, -height},
|
||||
outline_color);
|
||||
m_text_cursor_left.x -= offset + width;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_top_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float height, float ratio, const float dash_len,
|
||||
const float gap_len, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_top_dashed_bar(const Color& color, const Color& outline_color,
|
||||
const Color& bg_color, const float height, float ratio,
|
||||
const float dash_len, const float gap_len, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const float bar_w = std::abs(m_canvas.top_left_corner.x - m_canvas.top_right_corner.x);
|
||||
const auto bar_start = Vector2<float>{m_canvas.top_left_corner.x, m_text_cursor_top.y - offset};
|
||||
const float bar_w = std::abs(m_canvas.top_left_corner.x - m_canvas.top_right_corner.x);
|
||||
const auto bar_start = Vector2<float>{m_canvas.top_left_corner.x, m_text_cursor_top.y - offset};
|
||||
|
||||
m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2<float>{bar_w, -height}, bg_color);
|
||||
draw_dashed_fill(bar_start, {1.f, 0.f}, {0.f, -height}, bar_w, bar_w * ratio,
|
||||
color, outline_color, dash_len, gap_len);
|
||||
draw_dashed_fill(bar_start, {1.f, 0.f}, {0.f, -height}, bar_w, bar_w * ratio, color, outline_color, dash_len,
|
||||
gap_len);
|
||||
m_renderer->add_rectangle(bar_start, bar_start + Vector2<float>{bar_w, -height}, outline_color);
|
||||
m_text_cursor_top.y -= offset + height;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_bottom_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float height, float ratio, const float dash_len,
|
||||
const float gap_len, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_bottom_dashed_bar(const Color& color, const Color& outline_color,
|
||||
const Color& bg_color, const float height, float ratio,
|
||||
const float dash_len, const float gap_len, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const float bar_w = std::abs(m_canvas.bottom_left_corner.x - m_canvas.bottom_right_corner.x);
|
||||
const auto bar_start = Vector2<float>{m_canvas.bottom_left_corner.x, m_text_cursor_bottom.y + offset};
|
||||
const float bar_w = std::abs(m_canvas.bottom_left_corner.x - m_canvas.bottom_right_corner.x);
|
||||
const auto bar_start = Vector2<float>{m_canvas.bottom_left_corner.x, m_text_cursor_bottom.y + offset};
|
||||
|
||||
m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2<float>{bar_w, height}, bg_color);
|
||||
draw_dashed_fill(bar_start, {1.f, 0.f}, {0.f, height}, bar_w, bar_w * ratio,
|
||||
color, outline_color, dash_len, gap_len);
|
||||
draw_dashed_fill(bar_start, {1.f, 0.f}, {0.f, height}, bar_w, bar_w * ratio, color, outline_color, dash_len,
|
||||
gap_len);
|
||||
m_renderer->add_rectangle(bar_start, bar_start + Vector2<float>{bar_w, height}, outline_color);
|
||||
m_text_cursor_bottom.y += offset + height;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_skeleton(const Color& color, const float thickness) const
|
||||
EntityOverlay& EntityOverlay::add_skeleton(const Color& color, const float thickness)
|
||||
{
|
||||
// Maps normalized (rx in [0,1], ry in [0,1]) to canvas screen position
|
||||
const auto joint = [&](const float rx, const float ry) -> Vector2<float>
|
||||
{
|
||||
const auto top = m_canvas.top_left_corner
|
||||
+ (m_canvas.top_right_corner - m_canvas.top_left_corner) * rx;
|
||||
const auto bot = m_canvas.bottom_left_corner
|
||||
+ (m_canvas.bottom_right_corner - m_canvas.bottom_left_corner) * rx;
|
||||
const auto top = m_canvas.top_left_corner + (m_canvas.top_right_corner - m_canvas.top_left_corner) * rx;
|
||||
const auto bot =
|
||||
m_canvas.bottom_left_corner + (m_canvas.bottom_right_corner - m_canvas.bottom_left_corner) * rx;
|
||||
return top + (bot - top) * ry;
|
||||
};
|
||||
|
||||
using B = std::pair<std::pair<float,float>, std::pair<float,float>>;
|
||||
using B = std::pair<std::pair<float, float>, std::pair<float, float>>;
|
||||
static constexpr std::array<B, 15> k_bones{{
|
||||
// Spine
|
||||
{{0.50f, 0.13f}, {0.50f, 0.22f}}, // head → neck
|
||||
{{0.50f, 0.22f}, {0.50f, 0.38f}}, // neck → chest
|
||||
{{0.50f, 0.38f}, {0.50f, 0.55f}}, // chest → pelvis
|
||||
// Left arm
|
||||
{{0.50f, 0.22f}, {0.25f, 0.25f}}, // neck → L shoulder
|
||||
{{0.25f, 0.25f}, {0.13f, 0.42f}}, // L shoulder → L elbow
|
||||
{{0.13f, 0.42f}, {0.08f, 0.56f}}, // L elbow → L hand
|
||||
// Right arm
|
||||
{{0.50f, 0.22f}, {0.75f, 0.25f}}, // neck → R shoulder
|
||||
{{0.75f, 0.25f}, {0.87f, 0.42f}}, // R shoulder → R elbow
|
||||
{{0.87f, 0.42f}, {0.92f, 0.56f}}, // R elbow → R hand
|
||||
// Left leg
|
||||
{{0.50f, 0.55f}, {0.36f, 0.58f}}, // pelvis → L hip
|
||||
{{0.36f, 0.58f}, {0.32f, 0.77f}}, // L hip → L knee
|
||||
{{0.32f, 0.77f}, {0.27f, 0.97f}}, // L knee → L foot
|
||||
// Right leg
|
||||
{{0.50f, 0.55f}, {0.64f, 0.58f}}, // pelvis → R hip
|
||||
{{0.64f, 0.58f}, {0.68f, 0.77f}}, // R hip → R knee
|
||||
{{0.68f, 0.77f}, {0.73f, 0.97f}}, // R knee → R foot
|
||||
// Spine
|
||||
{{0.50f, 0.13f}, {0.50f, 0.22f}}, // head → neck
|
||||
{{0.50f, 0.22f}, {0.50f, 0.38f}}, // neck → chest
|
||||
{{0.50f, 0.38f}, {0.50f, 0.55f}}, // chest → pelvis
|
||||
// Left arm
|
||||
{{0.50f, 0.22f}, {0.25f, 0.25f}}, // neck → L shoulder
|
||||
{{0.25f, 0.25f}, {0.13f, 0.42f}}, // L shoulder → L elbow
|
||||
{{0.13f, 0.42f}, {0.08f, 0.56f}}, // L elbow → L hand
|
||||
// Right arm
|
||||
{{0.50f, 0.22f}, {0.75f, 0.25f}}, // neck → R shoulder
|
||||
{{0.75f, 0.25f}, {0.87f, 0.42f}}, // R shoulder → R elbow
|
||||
{{0.87f, 0.42f}, {0.92f, 0.56f}}, // R elbow → R hand
|
||||
// Left leg
|
||||
{{0.50f, 0.55f}, {0.36f, 0.58f}}, // pelvis → L hip
|
||||
{{0.36f, 0.58f}, {0.32f, 0.77f}}, // L hip → L knee
|
||||
{{0.32f, 0.77f}, {0.27f, 0.97f}}, // L knee → L foot
|
||||
// Right leg
|
||||
{{0.50f, 0.55f}, {0.64f, 0.58f}}, // pelvis → R hip
|
||||
{{0.64f, 0.58f}, {0.68f, 0.77f}}, // R hip → R knee
|
||||
{{0.68f, 0.77f}, {0.73f, 0.97f}}, // R knee → R foot
|
||||
}};
|
||||
|
||||
for (const auto& [a, b] : k_bones)
|
||||
m_renderer->add_line(joint(a.first, a.second), joint(b.first, b.second), color, thickness);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::draw_dashed_line(const Vector2<float>& from, const Vector2<float>& to, const Color& color,
|
||||
@@ -290,31 +313,30 @@ namespace omath::hud
|
||||
if (total <= 0.f)
|
||||
return;
|
||||
|
||||
const auto dir = (to - from).normalized();
|
||||
const auto dir = (to - from).normalized();
|
||||
const float step = dash_len + gap_len;
|
||||
|
||||
const float n_dashes = std::floor((total + gap_len) / step);
|
||||
const float n_dashes = std::floor((total + gap_len) / step);
|
||||
if (n_dashes < 1.f)
|
||||
return;
|
||||
|
||||
const float used = n_dashes * dash_len + (n_dashes - 1.f) * gap_len;
|
||||
const float used = n_dashes * dash_len + (n_dashes - 1.f) * gap_len;
|
||||
const float offset = (total - used) / 2.f;
|
||||
|
||||
for (float i = 0.f; i < n_dashes; ++i)
|
||||
{
|
||||
const float pos = offset + i * step;
|
||||
const float pos = offset + i * step;
|
||||
const auto dash_start = from + dir * pos;
|
||||
const auto dash_end = from + dir * std::min(pos + dash_len, total);
|
||||
const auto dash_end = from + dir * std::min(pos + dash_len, total);
|
||||
m_renderer->add_line(dash_start, dash_end, color, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityOverlay::add_dashed_box(const Color& color, const float dash_len, const float gap_len,
|
||||
const float thickness) const
|
||||
EntityOverlay& EntityOverlay::add_dashed_box(const Color& color, const float dash_len, const float gap_len,
|
||||
const float thickness)
|
||||
{
|
||||
const float min_edge = std::min(
|
||||
(m_canvas.top_right_corner - m_canvas.top_left_corner).length(),
|
||||
(m_canvas.bottom_right_corner - m_canvas.top_right_corner).length());
|
||||
const float min_edge = std::min((m_canvas.top_right_corner - m_canvas.top_left_corner).length(),
|
||||
(m_canvas.bottom_right_corner - m_canvas.top_right_corner).length());
|
||||
const float corner_len = std::min(dash_len, min_edge / 2.f);
|
||||
|
||||
const auto draw_edge = [&](const Vector2<float>& from, const Vector2<float>& to)
|
||||
@@ -326,10 +348,12 @@ namespace omath::hud
|
||||
m_renderer->add_line(to - dir * corner_len, to, color, thickness);
|
||||
};
|
||||
|
||||
draw_edge(m_canvas.top_left_corner, m_canvas.top_right_corner);
|
||||
draw_edge(m_canvas.top_right_corner, m_canvas.bottom_right_corner);
|
||||
draw_edge(m_canvas.bottom_right_corner,m_canvas.bottom_left_corner);
|
||||
draw_edge(m_canvas.top_left_corner, m_canvas.top_right_corner);
|
||||
draw_edge(m_canvas.top_right_corner, m_canvas.bottom_right_corner);
|
||||
draw_edge(m_canvas.bottom_right_corner, m_canvas.bottom_left_corner);
|
||||
draw_edge(m_canvas.bottom_left_corner, m_canvas.top_left_corner);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::draw_outlined_text(const Vector2<float>& position, const Color& color,
|
||||
@@ -343,8 +367,8 @@ namespace omath::hud
|
||||
m_renderer->add_text(position + outline_offset, Color{0.f, 0.f, 0.f, 1.f}, text.data());
|
||||
m_renderer->add_text(position, color, text.data());
|
||||
}
|
||||
void EntityOverlay::add_bottom_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float height, float ratio, const float offset)
|
||||
EntityOverlay& EntityOverlay::add_bottom_bar(const Color& color, const Color& outline_color, const Color& bg_color,
|
||||
const float height, float ratio, const float offset)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0.f, 1.f);
|
||||
const auto max_bar_width = std::abs(m_canvas.bottom_right_corner.x - m_canvas.bottom_left_corner.x);
|
||||
@@ -355,10 +379,12 @@ namespace omath::hud
|
||||
m_renderer->add_rectangle(bar_start, bar_start + Vector2<float>(max_bar_width, height), outline_color);
|
||||
|
||||
m_text_cursor_bottom.y += offset + height;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_bottom_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view text)
|
||||
EntityOverlay& EntityOverlay::add_bottom_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view text)
|
||||
{
|
||||
const auto text_size = m_renderer->calc_text_size(text);
|
||||
|
||||
@@ -368,10 +394,12 @@ namespace omath::hud
|
||||
m_renderer->add_text(m_text_cursor_bottom + Vector2<float>{0.f, offset}, color, text);
|
||||
|
||||
m_text_cursor_bottom.y += text_size.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_left_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
EntityOverlay& EntityOverlay::add_left_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
{
|
||||
const auto text_size = m_renderer->calc_text_size(text);
|
||||
const auto pos = m_text_cursor_left + Vector2<float>{-(offset + text_size.x), 0.f};
|
||||
@@ -382,10 +410,12 @@ namespace omath::hud
|
||||
m_renderer->add_text(pos, color, text);
|
||||
|
||||
m_text_cursor_left.y += text_size.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_centered_bottom_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
EntityOverlay& EntityOverlay::add_centered_bottom_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
{
|
||||
const auto text_size = m_renderer->calc_text_size(text);
|
||||
const auto box_center_x =
|
||||
@@ -398,10 +428,12 @@ namespace omath::hud
|
||||
m_renderer->add_text(pos, color, text);
|
||||
|
||||
m_text_cursor_bottom.y += text_size.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EntityOverlay::add_centered_top_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
EntityOverlay& EntityOverlay::add_centered_top_label(const Color& color, const float offset, const bool outlined,
|
||||
const std::string_view& text)
|
||||
{
|
||||
const auto text_size = m_renderer->calc_text_size(text);
|
||||
const auto box_center_x =
|
||||
@@ -414,6 +446,8 @@ namespace omath::hud
|
||||
draw_outlined_text(pos, color, text);
|
||||
else
|
||||
m_renderer->add_text(pos, color, text);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
EntityOverlay::EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom,
|
||||
@@ -423,4 +457,85 @@ namespace omath::hud
|
||||
m_text_cursor_left(m_canvas.top_left_corner), m_renderer(renderer)
|
||||
{
|
||||
}
|
||||
// ── widget dispatch ───────────────────────────────────────────────────────
|
||||
void EntityOverlay::dispatch(const widget::Box& w)
|
||||
{ add_2d_box(w.color, w.fill, w.thickness); }
|
||||
|
||||
void EntityOverlay::dispatch(const widget::CorneredBox& w)
|
||||
{ add_cornered_2d_box(w.color, w.fill, w.corner_ratio, w.thickness); }
|
||||
|
||||
void EntityOverlay::dispatch(const widget::DashedBox& w)
|
||||
{ add_dashed_box(w.color, w.dash_len, w.gap_len, w.thickness); }
|
||||
|
||||
void EntityOverlay::dispatch(const widget::Skeleton& w)
|
||||
{ add_skeleton(w.color, w.thickness); }
|
||||
|
||||
void EntityOverlay::dispatch(const widget::SnapLine& w)
|
||||
{ add_snap_line(w.start, w.color, w.width); }
|
||||
|
||||
// ── Side container dispatch ───────────────────────────────────────────────
|
||||
void EntityOverlay::dispatch(const widget::RightSide& s)
|
||||
{
|
||||
for (const auto& child : s.children)
|
||||
std::visit(widget::Overloaded{
|
||||
[](const widget::None&) {},
|
||||
[this](const widget::Bar& w)
|
||||
{ add_right_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.offset); },
|
||||
[this](const widget::DashedBar& w)
|
||||
{ add_right_dashed_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.dash_len, w.gap_len, w.offset); },
|
||||
[this](const widget::Label& w)
|
||||
{ add_right_label(w.color, w.offset, w.outlined, w.text); },
|
||||
[this](const widget::Centered<widget::Label>& w)
|
||||
{ add_right_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); },
|
||||
}, child);
|
||||
}
|
||||
|
||||
void EntityOverlay::dispatch(const widget::LeftSide& s)
|
||||
{
|
||||
for (const auto& child : s.children)
|
||||
std::visit(widget::Overloaded{
|
||||
[](const widget::None&) {},
|
||||
[this](const widget::Bar& w)
|
||||
{ add_left_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.offset); },
|
||||
[this](const widget::DashedBar& w)
|
||||
{ add_left_dashed_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.dash_len, w.gap_len, w.offset); },
|
||||
[this](const widget::Label& w)
|
||||
{ add_left_label(w.color, w.offset, w.outlined, w.text); },
|
||||
[this](const widget::Centered<widget::Label>& w)
|
||||
{ add_left_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); },
|
||||
}, child);
|
||||
}
|
||||
|
||||
void EntityOverlay::dispatch(const widget::TopSide& s)
|
||||
{
|
||||
for (const auto& child : s.children)
|
||||
std::visit(widget::Overloaded{
|
||||
[](const widget::None&) {},
|
||||
[this](const widget::Bar& w)
|
||||
{ add_top_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.offset); },
|
||||
[this](const widget::DashedBar& w)
|
||||
{ add_top_dashed_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.dash_len, w.gap_len, w.offset); },
|
||||
[this](const widget::Label& w)
|
||||
{ add_top_label(w.color, w.offset, w.outlined, w.text); },
|
||||
[this](const widget::Centered<widget::Label>& w)
|
||||
{ add_centered_top_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); },
|
||||
}, child);
|
||||
}
|
||||
|
||||
void EntityOverlay::dispatch(const widget::BottomSide& s)
|
||||
{
|
||||
for (const auto& child : s.children)
|
||||
std::visit(widget::Overloaded{
|
||||
[](const widget::None&) {},
|
||||
[this](const widget::Bar& w)
|
||||
{ add_bottom_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.offset); },
|
||||
[this](const widget::DashedBar& w)
|
||||
{ add_bottom_dashed_bar(w.color, w.outline, w.bg, w.size, w.ratio, w.dash_len, w.gap_len, w.offset); },
|
||||
[this](const widget::Label& w)
|
||||
{ add_bottom_label(w.color, w.offset, w.outlined, w.text); },
|
||||
[this](const widget::Centered<widget::Label>& w)
|
||||
{ add_centered_bottom_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); },
|
||||
}, child);
|
||||
}
|
||||
|
||||
} // namespace omath::hud
|
||||
Reference in New Issue
Block a user