From a99dd24d6b824123614441280dd25528490e6477 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 15 Mar 2026 19:39:02 +0300 Subject: [PATCH] improvement --- examples/example_hud/gui/main_window.cpp | 88 ++++---- examples/example_hud/gui/main_window.hpp | 4 +- include/omath/hud/entity_overlay.hpp | 18 +- include/omath/hud/entity_overlay_widgets.hpp | 223 ++++++++----------- source/hud/entity_overlay.cpp | 151 ++++++------- 5 files changed, 205 insertions(+), 279 deletions(-) diff --git a/examples/example_hud/gui/main_window.cpp b/examples/example_hud/gui/main_window.cpp index 0eae408..e57758c 100644 --- a/examples/example_hud/gui/main_window.cpp +++ b/examples/example_hud/gui/main_window.cpp @@ -154,6 +154,9 @@ namespace imgui_desktop::gui 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({m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y}, std::make_shared()) @@ -164,51 +167,46 @@ namespace imgui_desktop::gui m_corner_ratio, m_box_thickness}), when(m_show_dashed_box, DashedBox{m_dash_color, m_dash_len, m_dash_gap, m_dash_thickness}), - // ── Bars ───────────────────────────────────────────────────── - when(m_show_right_bar, RightBar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, - m_bar_value, m_bar_offset}), - when(m_show_left_bar, LeftBar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, - m_bar_value, m_bar_offset}), - when(m_show_top_bar, TopBar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, - m_bar_value, m_bar_offset}), - when(m_show_bottom_bar, BottomBar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, - m_bar_value, m_bar_offset}), - when(m_show_right_dashed_bar, - RightDashedBar{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}), - when(m_show_left_dashed_bar, - LeftDashedBar{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}), - when(m_show_top_dashed_bar, - TopDashedBar{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}), - when(m_show_bottom_dashed_bar, - BottomDashedBar{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}), - - // ── Labels ─────────────────────────────────────────────────── - when(m_show_right_labels, - RightLabel{{0.f, 1.f, 0.f, 1.f}, m_label_offset, m_outlined, "Health: 100/100"}), - when(m_show_right_labels, - RightLabel{{1.f, 0.f, 0.f, 1.f}, m_label_offset, m_outlined, "Shield: 125/125"}), - when(m_show_right_labels, - RightLabel{{1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*"}), - when(m_show_left_labels, LeftLabel{omath::Color::from_rgba(255, 128, 0, 255), m_label_offset, - m_outlined, "Armor: 75"}), - when(m_show_left_labels, LeftLabel{omath::Color::from_rgba(0, 200, 255, 255), m_label_offset, - m_outlined, "Level: 42"}), - when(m_show_top_labels, TopLabel{omath::Color::from_rgba(255, 255, 0, 255), m_label_offset, - m_outlined, "*SCOPED*"}), - when(m_show_top_labels, TopLabel{omath::Color::from_rgba(255, 0, 0, 255), m_label_offset, - m_outlined, "*BLEEDING*"}), - when(m_show_centered_top, CenteredTopLabel{omath::Color::from_rgba(0, 255, 255, 255), - m_label_offset, m_outlined, "*VISIBLE*"}), - when(m_show_centered_bottom, CenteredBottomLabel{omath::Color::from_rgba(255, 255, 255, 255), - m_label_offset, m_outlined, "PlayerName"}), - when(m_show_bottom_labels, - BottomLabel{omath::Color::from_rgba(200, 200, 0, 255), m_label_offset, m_outlined, "42m"}), - - // ── Misc ───────────────────────────────────────────────────── + 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})); } diff --git a/examples/example_hud/gui/main_window.hpp b/examples/example_hud/gui/main_window.hpp index fa91a36..6572bdf 100644 --- a/examples/example_hud/gui/main_window.hpp +++ b/examples/example_hud/gui/main_window.hpp @@ -43,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; diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index 97d90b3..5de3c19 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -145,20 +145,10 @@ namespace omath::hud void dispatch(const widget::Box& w); void dispatch(const widget::CorneredBox& w); void dispatch(const widget::DashedBox& w); - void dispatch(const widget::RightBar& w); - void dispatch(const widget::LeftBar& w); - void dispatch(const widget::TopBar& w); - void dispatch(const widget::BottomBar& w); - void dispatch(const widget::RightDashedBar& w); - void dispatch(const widget::LeftDashedBar& w); - void dispatch(const widget::TopDashedBar& w); - void dispatch(const widget::BottomDashedBar& w); - void dispatch(const widget::RightLabel& w); - void dispatch(const widget::LeftLabel& w); - void dispatch(const widget::TopLabel& w); - void dispatch(const widget::BottomLabel& w); - void dispatch(const widget::CenteredTopLabel& w); - void dispatch(const widget::CenteredBottomLabel& 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& position, const Color& color, const std::string_view& text); diff --git a/include/omath/hud/entity_overlay_widgets.hpp b/include/omath/hud/entity_overlay_widgets.hpp index 92e3fdb..0cf8e47 100644 --- a/include/omath/hud/entity_overlay_widgets.hpp +++ b/include/omath/hud/entity_overlay_widgets.hpp @@ -4,12 +4,23 @@ #pragma once #include "omath/linear_algebra/vector2.hpp" #include "omath/utility/color.hpp" +#include #include -#include +#include +#include namespace omath::hud::widget { - // ── Boxes ───────────────────────────────────────────────────────────────── + // ── Overloaded helper for std::visit ────────────────────────────────────── + template + struct Overloaded : Ts... + { + using Ts::operator()...; + }; + template + Overloaded(Ts...) -> Overloaded; + + // ── Standalone widgets ──────────────────────────────────────────────────── struct Box { Color color; @@ -33,134 +44,6 @@ namespace omath::hud::widget float thickness = 1.f; }; - // ── Bars ────────────────────────────────────────────────────────────────── - struct RightBar - { - Color color; - Color outline; - Color bg; - float width; - float ratio; - float offset = 5.f; - }; - struct LeftBar - { - Color color; - Color outline; - Color bg; - float width; - float ratio; - float offset = 5.f; - }; - struct TopBar - { - Color color; - Color outline; - Color bg; - float height; - float ratio; - float offset = 5.f; - }; - struct BottomBar - { - Color color; - Color outline; - Color bg; - float height; - float ratio; - float offset = 5.f; - }; - - struct RightDashedBar - { - Color color; - Color outline; - Color bg; - float width; - float ratio; - float dash_len; - float gap_len; - float offset = 5.f; - }; - struct LeftDashedBar - { - Color color; - Color outline; - Color bg; - float width; - float ratio; - float dash_len; - float gap_len; - float offset = 5.f; - }; - struct TopDashedBar - { - Color color; - Color outline; - Color bg; - float height; - float ratio; - float dash_len; - float gap_len; - float offset = 5.f; - }; - struct BottomDashedBar - { - Color color; - Color outline; - Color bg; - float height; - float ratio; - float dash_len; - float gap_len; - float offset = 5.f; - }; - - // ── Labels ──────────────────────────────────────────────────────────────── - struct RightLabel - { - Color color; - float offset; - bool outlined; - std::string text; - }; - struct LeftLabel - { - Color color; - float offset; - bool outlined; - std::string text; - }; - struct TopLabel - { - Color color; - float offset; - bool outlined; - std::string text; - }; - struct BottomLabel - { - Color color; - float offset; - bool outlined; - std::string text; - }; - struct CenteredTopLabel - { - Color color; - float offset; - bool outlined; - std::string text; - }; - struct CenteredBottomLabel - { - Color color; - float offset; - bool outlined; - std::string text; - }; - - // ── Misc ────────────────────────────────────────────────────────────────── struct Skeleton { Color color; @@ -173,17 +56,87 @@ namespace omath::hud::widget 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 + struct Centered + { + W child; + }; + template + Centered(W) -> Centered; + + // ── Side widget variant ─────────────────────────────────────────────────── + struct None {}; ///< No-op placeholder — used by widget::when for disabled elements. + using SideWidget = std::variant>; + + // ── Side containers ─────────────────────────────────────────────────────── + // Storing std::initializer_list 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 children; RightSide(std::initializer_list c) : children(c) {} }; + struct LeftSide { std::initializer_list children; LeftSide(std::initializer_list c) : children(c) {} }; + struct TopSide { std::initializer_list children; TopSide(std::initializer_list c) : children(c) {} }; + struct BottomSide { std::initializer_list children; BottomSide(std::initializer_list 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 + requires std::constructible_from + SideWidget when(const bool condition, W widget) + { + if (condition) return SideWidget{std::move(widget)}; + return None{}; + } } // namespace omath::hud::widget namespace omath::hud { - /// Returns an engaged optional when condition is true, std::nullopt otherwise. - /// Designed for use with EntityOverlay::contents() to conditionally include widgets. + /// Top-level: returns an engaged optional when condition is true, std::nullopt otherwise. + /// Designed for use with EntityOverlay::contents() for top-level widget types. template - std::optional when(bool condition, W widget) + std::optional when(const bool condition, W widget) { - if (condition) - return std::move(widget); + if (condition) return std::move(widget); return std::nullopt; } } // namespace omath::hud diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index 0bc8ffe..05f6315 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -459,98 +459,83 @@ namespace omath::hud } // ── widget dispatch ─────────────────────────────────────────────────────── void EntityOverlay::dispatch(const widget::Box& w) - { - add_2d_box(w.color, w.fill, w.thickness); - } + { 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); - } + { 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::RightBar& w) - { - add_right_bar(w.color, w.outline, w.bg, w.width, w.ratio, w.offset); - } - - void EntityOverlay::dispatch(const widget::LeftBar& w) - { - add_left_bar(w.color, w.outline, w.bg, w.width, w.ratio, w.offset); - } - - void EntityOverlay::dispatch(const widget::TopBar& w) - { - add_top_bar(w.color, w.outline, w.bg, w.height, w.ratio, w.offset); - } - - void EntityOverlay::dispatch(const widget::BottomBar& w) - { - add_bottom_bar(w.color, w.outline, w.bg, w.height, w.ratio, w.offset); - } - - void EntityOverlay::dispatch(const widget::RightDashedBar& w) - { - add_right_dashed_bar(w.color, w.outline, w.bg, w.width, w.ratio, w.dash_len, w.gap_len, w.offset); - } - - void EntityOverlay::dispatch(const widget::LeftDashedBar& w) - { - add_left_dashed_bar(w.color, w.outline, w.bg, w.width, w.ratio, w.dash_len, w.gap_len, w.offset); - } - - void EntityOverlay::dispatch(const widget::TopDashedBar& w) - { - add_top_dashed_bar(w.color, w.outline, w.bg, w.height, w.ratio, w.dash_len, w.gap_len, w.offset); - } - - void EntityOverlay::dispatch(const widget::BottomDashedBar& w) - { - add_bottom_dashed_bar(w.color, w.outline, w.bg, w.height, w.ratio, w.dash_len, w.gap_len, w.offset); - } - - void EntityOverlay::dispatch(const widget::RightLabel& w) - { - add_right_label(w.color, w.offset, w.outlined, w.text); - } - - void EntityOverlay::dispatch(const widget::LeftLabel& w) - { - add_left_label(w.color, w.offset, w.outlined, w.text); - } - - void EntityOverlay::dispatch(const widget::TopLabel& w) - { - add_top_label(w.color, w.offset, w.outlined, w.text); - } - - void EntityOverlay::dispatch(const widget::BottomLabel& w) - { - add_bottom_label(w.color, w.offset, w.outlined, w.text); - } - - void EntityOverlay::dispatch(const widget::CenteredTopLabel& w) - { - add_centered_top_label(w.color, w.offset, w.outlined, w.text); - } - - void EntityOverlay::dispatch(const widget::CenteredBottomLabel& w) - { - add_centered_bottom_label(w.color, w.offset, w.outlined, w.text); - } + { 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); - } + { 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) { - add_snap_line(w.start, w.color, w.width); + 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& 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& 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& 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& w) + { add_centered_bottom_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, + }, child); } } // namespace omath::hud \ No newline at end of file