From b6f6b28c98c79e8b5c138b732cf3fc7fb2164d34 Mon Sep 17 00:00:00 2001 From: zbyv Date: Wed, 11 Mar 2026 20:07:02 +0100 Subject: [PATCH] Add menu stuff --- src/gui/context.cpp | 6 +++ src/gui/context.hpp | 1 + src/gui/menu.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++++ src/gui/menu.hpp | 119 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 246 insertions(+) create mode 100644 src/gui/menu.cpp create mode 100644 src/gui/menu.hpp diff --git a/src/gui/context.cpp b/src/gui/context.cpp index f8564e3..d42f424 100644 --- a/src/gui/context.cpp +++ b/src/gui/context.cpp @@ -199,6 +199,12 @@ void gui::Context::DrawText(std::string_view text, const glm::vec2& pos, uint32_ } } +void gui::Context::DrawTextAligned(std::string_view text, const glm::vec2& pos, const glm::vec2& align, uint32_t color, float scale) +{ + auto size = MeasureText(text) * scale; + DrawText(text, pos + size * align, color, scale); +} + void gui::Context::Render() { va_.SetVBOData(vertices_.data(), vertices_.size() * sizeof(vertices_[0])); diff --git a/src/gui/context.hpp b/src/gui/context.hpp index cb0b58e..bf708ce 100644 --- a/src/gui/context.hpp +++ b/src/gui/context.hpp @@ -36,6 +36,7 @@ public: glm::vec2 MeasureText(std::string_view text); void DrawText(std::string_view text, const glm::vec2& pos, uint32_t color = 0xFFFFFFFF, float scale = 1.0f); + void DrawTextAligned(std::string_view text, const glm::vec2& pos, const glm::vec2& align, uint32_t color = 0xFFFFFFFF, float scale = 1.0f); void Render(); diff --git a/src/gui/menu.cpp b/src/gui/menu.cpp new file mode 100644 index 0000000..0141f2b --- /dev/null +++ b/src/gui/menu.cpp @@ -0,0 +1,120 @@ +#include "menu.hpp" + +#include + +// Menu + +void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const +{ + // TODO: draw bg + + glm::vec2 cursor = pos; + + for (size_t i = 0; i < items_.size(); ++i) + { + items_[i]->Draw(DrawMenuItemArgs(ctx, cursor, focus_ == i)); + cursor.y += items_[i]->GetSize().y; + } +} + +void gui::Menu::Input(MenuInput in) +{ + switch (in) + { + case MI_UP: + SwitchFocus(-1); + break; + + case MI_DOWN: + SwitchFocus(1); + break; + + default: + if (!items_.empty()) + items_[focus_]->Input(in); + break; + } +} + +glm::vec2 gui::Menu::MeasureSize() const +{ + glm::vec2 size(0.0f); + + for (const auto& item : items_) + { + const auto& itemsize = item->GetSize(); + size.x = glm::max(size.x, itemsize.x); + size.y += itemsize.y; + } + + return size; +} + +void gui::Menu::SwitchFocus(int dir) +{ + if (items_.empty()) + return; + + focus_ = (focus_ + items_.size() + dir) % items_.size(); +} + +// ButtonMenuItem + +gui::ButtonMenuItem::ButtonMenuItem(std::string text, std::function click_cb) + : text_(std::move(text)), click_cb_(std::move(click_cb)) +{ + size_ = glm::vec2(500.0f, 30.0f); +} + +void gui::ButtonMenuItem::Draw(const DrawMenuItemArgs& args) const +{ + Super::Draw(args); + glm::vec2 center = args.pos + glm::vec2(0.0f, size_.y * 0.5f); + args.ctx.DrawTextAligned(text_, center, glm::vec2(0.0f, -0.5f), args.focused ? COLOR_FOCUSED : COLOR_INACTIVE); +} + +void gui::ButtonMenuItem::Input(MenuInput in) +{ + if (in == MI_ENTER && click_cb_) + click_cb_(); +} + +// SelectMenuItem + +gui::SelectMenuItem::SelectMenuItem(std::string text, std::function click_cb, + std::function switch_cb) + : ButtonMenuItem(std::move(text), std::move(click_cb)), switch_cb_(std::move(switch_cb)) +{ +} + +void gui::SelectMenuItem::Draw(const DrawMenuItemArgs& args) const +{ + Super::Draw(args); + + char buffer[128]; + size_t len = snprintf(buffer, sizeof(buffer), "< %s >", select_text_.c_str()); + + glm::vec2 center_right = args.pos + glm::vec2(size_.x, size_.y * 0.5f); + args.ctx.DrawTextAligned(std::string_view(buffer, len), center_right, glm::vec2(-1.0f, -0.5f), + args.focused ? COLOR_FOCUSED : COLOR_INACTIVE); +} + +void gui::SelectMenuItem::Input(MenuInput in) +{ + switch (in) + { + case MI_LEFT: + if (switch_cb_) + switch_cb_(-1); + break; + + case MI_RIGHT: + if (switch_cb_) + switch_cb_(1); + break; + + default: + Super::Input(in); + break; + } +} diff --git a/src/gui/menu.hpp b/src/gui/menu.hpp new file mode 100644 index 0000000..0d58d9f --- /dev/null +++ b/src/gui/menu.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include +#include + +#include "context.hpp" + +namespace gui +{ + +enum MenuInput +{ + MI_UP, + MI_DOWN, + MI_LEFT, + MI_RIGHT, + MI_BACK, + MI_ENTER, +}; + +class MenuItem; + +struct DrawMenuItemArgs +{ + Context& ctx; + glm::vec2 pos; + bool focused; + + DrawMenuItemArgs(Context& ctx, const glm::vec2& pos, bool focused) : ctx(ctx), pos(pos), focused(focused) {} +}; + +class Menu +{ +public: + Menu() = default; + + template T, typename... TArgs> + T& Add(TArgs&&... args) + { + auto item = std::make_unique(std::forward(args)...); + auto& item_ref = *item; + items_.emplace_back(std::move(item)); + return item_ref; + } + + void Draw(Context& ctx, const glm::vec2& pos) const; + void Input(MenuInput in); + + glm::vec2 MeasureSize() const; + +private: + void SwitchFocus(int dir); + +private: + std::vector> items_; + size_t focus_ = 0; + +}; + +class MenuItem +{ +public: + constexpr static uint32_t COLOR_INACTIVE = 0xFFFFFFFF; + constexpr static uint32_t COLOR_FOCUSED = 0xFF00FFFF; + + MenuItem() = default; + + virtual void Draw(const DrawMenuItemArgs& args) const {} + virtual void Input(MenuInput in) {} + + const glm::vec2& GetSize() const { return size_; } + + virtual ~MenuItem() = default; + +protected: + glm::vec2 size_; + +}; + +class ButtonMenuItem : public MenuItem +{ +public: + using Super = MenuItem; + + ButtonMenuItem(std::string text, std::function click_cb); + + virtual void Draw(const DrawMenuItemArgs& args) const override; + virtual void Input(MenuInput in) override; + + virtual ~ButtonMenuItem() = default; + +private: + std::string text_; + std::function click_cb_; +}; + +class SelectMenuItem : public ButtonMenuItem +{ +public: + using Super = ButtonMenuItem; + + SelectMenuItem(std::string text, std::function click_cb, std::function switch_cb); + + virtual void Draw(const DrawMenuItemArgs& args) const override; + virtual void Input(MenuInput in) override; + + void SetSelectionText(std::string select_text) { select_text_ = std::move(select_text); } + + virtual ~SelectMenuItem() = default; + +protected: + std::string select_text_; + std::function switch_cb_; + +}; + + + +} \ No newline at end of file