feat(ui): introduce anchor-based widget positioning system

Replace set_position with anchor and offset for relative positioning. Add parent pointer to widget hierarchy. Remove manual resize logic in UI managers.
This commit is contained in:
2026-07-13 14:52:35 +08:00
parent 0cbb353a2a
commit f5e53fd13d
14 changed files with 241 additions and 114 deletions

View File

@@ -13,8 +13,9 @@ public:
DebugCollector(); DebugCollector();
void report(const std::string& name, std::string_view content); void report(const std::string& name, std::string_view content);
void init_text(); void init(int width, int height);
Widget& get_widget(); Widget& get_widget();
bool handle_event(const Event& e);
private: private:
Widget m_widget; Widget m_widget;

View File

@@ -0,0 +1,15 @@
#pragma once
enum class Anchor {
TOP_LEFT,
TOP_CENTER,
TOP_RIGHT,
CENTER_LEFT,
CENTER,
CENTER_RIGHT,
BOTTOM_LEFT,
BOTTOM_CENTER,
BOTTOM_RIGHT,
};

View File

@@ -9,7 +9,7 @@
namespace Cubed { namespace Cubed {
class Button : public Widget { class Button : public Widget {
public: public:
Button(); Button(Widget* parent);
Button(const Button&) = delete; Button(const Button&) = delete;
Button(Button&&) = delete; Button(Button&&) = delete;
@@ -21,10 +21,11 @@ public:
bool handle_mouse_move_event(const MouseMoveEvent& e) override; bool handle_mouse_move_event(const MouseMoveEvent& e) override;
bool handle_mouse_button_event(const MouseButtonEvent& e) override; bool handle_mouse_button_event(const MouseButtonEvent& e) override;
void set_window_size(int width, int height) override;
Widget& set_position(const glm::vec2& pos) override;
Widget& set_position(float x, float y) override;
Button& set_scale(float scale); Button& set_scale(float scale);
Widget& set_anchor(Anchor anchor) override;
Widget& set_offset(glm::ivec2 offset) override;
glm::vec2 pos() const override;
float width() const override; float width() const override;
float height() const override; float height() const override;
float scale() const; float scale() const;
@@ -34,16 +35,17 @@ public:
} }
template <typename T, typename... Args> T& set_background(Args&&... args) { template <typename T, typename... Args> T& set_background(Args&&... args) {
auto w = std::make_unique<T>(std::forward<Args>(args)...); auto w = std::make_unique<T>(std::forward<Args>(args)..., m_parent);
T& ref = *w; T& ref = *w;
m_background = std::move(w); m_background = std::move(w);
return ref; return ref;
} }
template <typename T, typename... Args> T& set_foreground(Args&&... args) { template <typename T, typename... Args> T& set_foreground(Args&&... args) {
auto w = std::make_unique<T>(std::forward<Args>(args)...); auto w = std::make_unique<T>(std::forward<Args>(args)..., this);
T& ref = *w; T& ref = *w;
m_foreground = std::move(w); m_foreground = std::move(w);
m_foreground->set_anchor(Anchor::CENTER);
return ref; return ref;
} }
@@ -52,7 +54,5 @@ private:
std::unique_ptr<Image> m_background; std::unique_ptr<Image> m_background;
std::unique_ptr<Label> m_foreground; std::unique_ptr<Label> m_foreground;
bool m_hovered = false; bool m_hovered = false;
glm::vec2 m_min_pos;
glm::vec2 m_max_pos;
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -8,7 +8,7 @@ namespace Cubed {
class TextureManager; class TextureManager;
class Image : public Widget { class Image : public Widget {
public: public:
Image(); Image(Widget* parent);
void update(float dt) override; void update(float dt) override;
void render(Renderer& renderer) override; void render(Renderer& renderer) override;

View File

@@ -10,8 +10,8 @@ public:
Label& operator=(const Label&) = delete; Label& operator=(const Label&) = delete;
Label& operator=(Label&&) = delete; Label& operator=(Label&&) = delete;
Label(const std::string& id); Label(const std::string& id, Widget* parent);
Label(); Label(Widget* parent);
virtual ~Label() = default; virtual ~Label() = default;
Label& set_text(std::string_view text); Label& set_text(std::string_view text);

View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
#include "Cubed/input/event.hpp" #include "Cubed/input/event.hpp"
#include "glm/ext/vector_float2.hpp" #include "Cubed/ui/anchor.hpp"
#include <glm/glm.hpp>
#include <memory> #include <memory>
#include <vector> #include <vector>
namespace Cubed { namespace Cubed {
@@ -10,18 +11,19 @@ class Renderer;
class Widget { class Widget {
public: public:
Widget(const std::string& id); Widget(const std::string& id, Widget* parent);
Widget() = default; Widget(Widget* parent);
virtual ~Widget() = default; virtual ~Widget() = default;
virtual void update(float dt); virtual void update(float dt);
virtual void render(Renderer& renderer); virtual void render(Renderer& renderer);
virtual const std::string& id() const; virtual const std::string& id() const;
virtual Widget& set_position(const glm::vec2& pos); virtual Widget& set_anchor(Anchor anchor);
virtual Widget& set_position(float x, float y); virtual Widget& set_offset(glm::ivec2 offset);
virtual void set_window_size(int width, int height);
// Returns the final display size // Returns the final display size
virtual float width() const; virtual float width() const;
virtual float height() const; virtual float height() const;
virtual const glm::vec2& pos() const; virtual glm::vec2 pos() const;
virtual bool handle_key_event(const KeyEvent& e); virtual bool handle_key_event(const KeyEvent& e);
virtual bool handle_mouse_button_event(const MouseButtonEvent& e); virtual bool handle_mouse_button_event(const MouseButtonEvent& e);
@@ -39,12 +41,16 @@ public:
protected: protected:
virtual void on_update(float dt); virtual void on_update(float dt);
virtual void on_render(Renderer& renderer); virtual void on_render(Renderer& renderer);
virtual glm::vec2 compute_position() const;
Widget* m_parent = nullptr;
std::string m_id; std::string m_id;
float m_window_height = 0;
float m_window_width = 0;
// Center is at the top-left corner, position is at the top-left corner // Center is at the top-left corner, position is at the top-left corner
glm::vec2 m_pos{0.0f, 0.0f}; Anchor m_anchor = Anchor::TOP_LEFT;
glm::ivec2 m_offset{0, 0};
private: private:
Widget* m_parent = nullptr;
std::vector<std::unique_ptr<Widget>> m_children; std::vector<std::unique_ptr<Widget>> m_children;
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -5,16 +5,18 @@
namespace Cubed { namespace Cubed {
DebugCollector::DebugCollector() {} DebugCollector::DebugCollector() : m_widget(nullptr) {}
DebugCollector& DebugCollector::get() { DebugCollector& DebugCollector::get() {
static DebugCollector instance; static DebugCollector instance;
return instance; return instance;
} }
void DebugCollector::init_text() { void DebugCollector::init(int width, int height) {
constexpr int SPACE = 50;
// version_text // version_text
auto& version_text = m_widget.add_child<Label>("version"); m_widget.set_window_size(width, height);
auto& version_text = m_widget.add_child<Label>("version", &m_widget);
std::string version{"Version: " CUBED_VERSION}; std::string version{"Version: " CUBED_VERSION};
#ifdef DEBUG_MODE #ifdef DEBUG_MODE
version.append("-debug"); version.append("-debug");
@@ -24,37 +26,48 @@ void DebugCollector::init_text() {
version_text.set_color(Color::WHITE) version_text.set_color(Color::WHITE)
.set_text(version) .set_text(version)
.set_scale(0.8f) .set_scale(0.8f)
.set_position(0.0f, 100.0f); .set_anchor(Anchor::TOP_LEFT);
version_text.set_offset({0, SPACE});
m_component.try_emplace(version_text.id(), &version_text); m_component.try_emplace(version_text.id(), &version_text);
// fps // fps
auto& fps_text = m_widget.add_child<Label>("fps"); auto& fps_text = m_widget.add_child<Label>("fps", &version_text);
fps_text.set_text("FPS: 0").set_position(0.0f, 50.0f); fps_text.set_text("FPS: 0")
.set_scale(0.8f)
.set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(fps_text.id(), &fps_text); m_component.try_emplace(fps_text.id(), &fps_text);
// player_pos // player_pos
auto& player_pos_text = m_widget.add_child<Label>("player_pos"); auto& player_pos_text = m_widget.add_child<Label>("player_pos", &fps_text);
player_pos_text.set_text("x: 0.00 y: 0.00 z: 0.00") player_pos_text.set_text("x: 0.00 y: 0.00 z: 0.00")
.set_scale(0.8f) .set_scale(0.8f)
.set_position(0.0f, 150.0f); .set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(player_pos_text.id(), &player_pos_text); m_component.try_emplace(player_pos_text.id(), &player_pos_text);
// rendered_chunk // rendered_chunk
auto& rendered_chunk_text = m_widget.add_child<Label>("rendered_chunk"); auto& rendered_chunk_text =
m_widget.add_child<Label>("rendered_chunk", &player_pos_text);
rendered_chunk_text.set_text("Rendered Chunk: 0") rendered_chunk_text.set_text("Rendered Chunk: 0")
.set_scale(0.8f) .set_scale(0.8f)
.set_position(0.0f, 200.0f); .set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(rendered_chunk_text.id(), &rendered_chunk_text); m_component.try_emplace(rendered_chunk_text.id(), &rendered_chunk_text);
// rss // rss
auto& rss_text = m_widget.add_child<Label>("rss"); auto& rss_text = m_widget.add_child<Label>("rss", &rendered_chunk_text);
rss_text.set_text("RSS: 0mb").set_scale(0.8f).set_position(0.0f, 300.0f); rss_text.set_text("RSS: 0mb")
.set_scale(0.8f)
.set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(rss_text.id(), &rss_text); m_component.try_emplace(rss_text.id(), &rss_text);
// os // os
std::string os; std::string os;
auto& os_text = m_widget.add_child<Label>("os"); auto& os_text = m_widget.add_child<Label>("os", &rss_text);
os_text.set_scale(0.8f).set_position(0.0f, 250.0f); os_text.set_scale(0.8f).set_anchor(Anchor::TOP_LEFT).set_offset({0, SPACE});
if (Tools::get_os_version(os)) { if (Tools::get_os_version(os)) {
os_text.set_text("OS: " + os); os_text.set_text("OS: " + os);
Logger::info("System: {}", os); Logger::info("System: {}", os);
@@ -64,45 +77,90 @@ void DebugCollector::init_text() {
m_component.try_emplace(os_text.id(), &os_text); m_component.try_emplace(os_text.id(), &os_text);
// cpu // cpu
auto& cpu_text = m_widget.add_child<Label>("cpu"); auto& cpu_text = m_widget.add_child<Label>("cpu", &os_text);
cpu_text.set_text("CPU: " + Tools::get_cpu_info()) cpu_text.set_text("CPU: " + Tools::get_cpu_info())
.set_scale(0.7f) .set_scale(0.8f)
.set_position(0.0f, 350.0f); .set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(cpu_text.id(), &cpu_text); m_component.try_emplace(cpu_text.id(), &cpu_text);
// gpu // gpu
auto& gpu_text = m_widget.add_child<Label>("gpu"); auto& gpu_text = m_widget.add_child<Label>("gpu", &cpu_text);
gpu_text gpu_text
.set_text(std::string{"GPU: "} + .set_text(std::string{"GPU: "} +
reinterpret_cast<const char*>(glGetString(GL_RENDERER))) reinterpret_cast<const char*>(glGetString(GL_RENDERER)))
.set_scale(0.7f) .set_scale(0.7f)
.set_position(0.0f, 400.0f); .set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(gpu_text.id(), &gpu_text); m_component.try_emplace(gpu_text.id(), &gpu_text);
// opengl_version // opengl_version
auto& opengl_version_text = m_widget.add_child<Label>("opengl_version"); auto& opengl_version_text =
m_widget.add_child<Label>("opengl_version", &gpu_text);
opengl_version_text opengl_version_text
.set_text("OpenGL: " + std::to_string(GLVersion.major) + "." + .set_text("OpenGL: " + std::to_string(GLVersion.major) + "." +
std::to_string(GLVersion.minor)) std::to_string(GLVersion.minor))
.set_scale(0.7f) .set_scale(0.7f)
.set_position(0.0f, 450.0f); .set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(opengl_version_text.id(), &opengl_version_text); m_component.try_emplace(opengl_version_text.id(), &opengl_version_text);
// biome // biome
auto& biome_text = m_widget.add_child<Label>("biome"); auto& biome_text = m_widget.add_child<Label>("biome", &opengl_version_text);
biome_text.set_text("Biome: ").set_scale(0.8f).set_position(0.0f, 500.0f); biome_text.set_text("Biome: ")
.set_scale(0.8f)
.set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(biome_text.id(), &biome_text); m_component.try_emplace(biome_text.id(), &biome_text);
// speed // speed
auto& speed_text = m_widget.add_child<Label>("speed"); auto& speed_text = m_widget.add_child<Label>("speed", &biome_text);
speed_text.set_text("Speed: 0 m/s") speed_text.set_text("Speed: 0 m/s")
.set_scale(0.8f) .set_scale(0.8f)
.set_position(0.0f, 550.0f); .set_anchor(Anchor::TOP_LEFT)
.set_offset({0, SPACE});
m_component.try_emplace(speed_text.id(), &speed_text); m_component.try_emplace(speed_text.id(), &speed_text);
} }
Widget& DebugCollector::get_widget() { return m_widget; } Widget& DebugCollector::get_widget() { return m_widget; }
bool DebugCollector::handle_event(const Event& e) {
return std::visit(
Overloaded{[this](const MouseMoveEvent& e) {
if (m_widget.handle_mouse_move_event(e)) {
return true;
}
return false;
},
[this](const MouseButtonEvent& e) {
if (m_widget.handle_mouse_button_event(e)) {
return true;
}
return false;
},
[this](const MouseWheelEvent& e) {
if (m_widget.handle_mouse_wheel_event(e)) {
return true;
}
return false;
},
[this](const KeyEvent& e) {
if (m_widget.handle_key_event(e)) {
return true;
}
return false;
},
[](const TextInputEvent&) { return false; },
[this](const WindowResizeEvent& e) {
m_widget.handle_window_resize_event(e);
return false;
},
[](const FrameBufferResizeEvent&) { return false; }
},
e);
}
void DebugCollector::report(const std::string& name, std::string_view content) { void DebugCollector::report(const std::string& name, std::string_view content) {
auto t = m_component.find(name); auto t = m_component.find(name);
if (t == m_component.end()) { if (t == m_component.end()) {

View File

@@ -135,7 +135,7 @@ void Renderer::init_quad() {
void Renderer::init_text() { void Renderer::init_text() {
m_vao[4].bind(); m_vao[4].bind();
DebugCollector::get().init_text(); DebugCollector::get().init(m_window_width, m_window_height);
} }
void Renderer::begin_frame() { void Renderer::begin_frame() {
glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_FRAMEBUFFER_SRGB);
@@ -167,7 +167,7 @@ void Renderer::render_lable(const Label& label) {
Font::text_texture()->bind(0); Font::text_texture()->bind(0);
auto& data = label.data(); auto& data = label.data();
auto& pos = label.pos(); auto pos = label.pos();
auto& text_style = label.text_style(); auto& text_style = label.text_style();
auto color = color_value(text_style.color); auto color = color_value(text_style.color);
data.m_vao->bind(); data.m_vao->bind();
@@ -189,7 +189,7 @@ void Renderer::render_image(const Image& image) {
} }
const auto& shader = get_shader("image"); const auto& shader = get_shader("image");
shader.use(); shader.use();
auto& pos = image.pos(); auto pos = image.pos();
glm::mat4 model_matrix = glm::mat4 model_matrix =
glm::translate(glm::mat4(1.0f), glm::vec3(pos.x, pos.y, 0.0f)) * glm::translate(glm::mat4(1.0f), glm::vec3(pos.x, pos.y, 0.0f)) *
glm::scale(glm::mat4(1.0f), glm::scale(glm::mat4(1.0f),

View File

@@ -1,7 +1,7 @@
#include "Cubed/ui/button.hpp" #include "Cubed/ui/button.hpp"
namespace Cubed { namespace Cubed {
Button::Button() {} Button::Button(Widget* parent) : Widget(parent) {}
void Button::update(float dt) { void Button::update(float dt) {
if (m_background) { if (m_background) {
@@ -22,14 +22,14 @@ void Button::render(Renderer& renderer) {
} }
bool Button::handle_mouse_move_event(const MouseMoveEvent& e) { bool Button::handle_mouse_move_event(const MouseMoveEvent& e) {
auto pos = m_background->pos();
if (e.xpos >= m_min_pos.x && e.xpos <= m_max_pos.x) { if (e.xpos >= pos.x && e.xpos <= pos.x + width() && e.ypos >= pos.y &&
if (e.ypos >= m_min_pos.y && e.ypos <= m_max_pos.y) { e.ypos <= pos.y + height()) {
m_hovered = true; m_hovered = true;
return true; return true;
} }
}
m_hovered = false; m_hovered = false;
return false; return false;
@@ -45,26 +45,24 @@ bool Button::handle_mouse_button_event(const MouseButtonEvent& e) {
return false; return false;
} }
Widget& Button::set_position(const glm::vec2& pos) {
m_pos = pos;
m_background->set_position(pos);
m_foreground->set_position(pos.x, pos.y + m_foreground->height() / 2.0f);
m_min_pos = pos;
m_max_pos.x = m_min_pos.x + width();
m_max_pos.y = m_min_pos.y + height();
return *this;
}
Widget& Button::set_position(float x, float y) {
return set_position(glm::vec2{x, y});
}
Button& Button::set_scale(float scale) { Button& Button::set_scale(float scale) {
m_background->set_scale(scale); m_background->set_scale(scale);
m_max_pos.x = m_min_pos.x + width();
m_max_pos.y = m_min_pos.y + height();
return *this; return *this;
} }
void Button::set_window_size(int width, int height) {
m_background->set_window_size(width, height);
}
Widget& Button::set_anchor(Anchor anchor) {
m_background->set_anchor(anchor);
return *this;
}
Widget& Button::set_offset(glm::ivec2 offset) {
m_background->set_offset(offset);
return *this;
}
glm::vec2 Button::pos() const { return m_background->pos(); }
float Button::scale() const { return m_background->scale(); } float Button::scale() const { return m_background->scale(); }

View File

@@ -4,7 +4,7 @@
#include "Cubed/texture_manager.hpp" #include "Cubed/texture_manager.hpp"
namespace Cubed { namespace Cubed {
Image::Image() {} Image::Image(Widget* parent) : Widget(parent) {}
void Image::update(float dt) { (void)dt; } void Image::update(float dt) { (void)dt; }
void Image::render(Renderer& renderer) { renderer.render_image(*this); } void Image::render(Renderer& renderer) { renderer.render_image(*this); }

View File

@@ -3,8 +3,8 @@
#include "Cubed/render/renderer.hpp" #include "Cubed/render/renderer.hpp"
#include "Cubed/tools/font.hpp" #include "Cubed/tools/font.hpp"
namespace Cubed { namespace Cubed {
Label::Label(const std::string& id) : Widget(id) {} Label::Label(const std::string& id, Widget* parent) : Widget(id, parent) {}
Label::Label() {} Label::Label(Widget* parent) : Widget(parent) {}
Label& Label::set_text(std::string_view text) { Label& Label::set_text(std::string_view text) {
m_text.text = text; m_text.text = text;
update_vertices(); update_vertices();

View File

@@ -12,23 +12,21 @@ MainMenuUIManager::~MainMenuUIManager() {}
void MainMenuUIManager::init() { void MainMenuUIManager::init() {
auto start_game_button = std::make_unique<Button>(); auto start_game_button = std::make_unique<Button>(nullptr);
auto& back = start_game_button->set_background<Image>(); auto& back = start_game_button->set_background<Image>();
back.set_image("texture/ui/button001.png", back.set_image("texture/ui/button001.png",
m_scene.scene_manager().app().texture_manager()); m_scene.scene_manager().app().texture_manager());
auto& fore = start_game_button->set_foreground<Label>(); auto& fore = start_game_button->set_foreground<Label>();
fore.set_text("Start Game"); fore.set_text("Start Game");
fore.set_scale(0.6f); fore.set_scale(0.6f);
start_game_button->set_window_size(
m_scene.scene_manager().app().renderer().window_width(),
m_scene.scene_manager().app().renderer().window_height());
start_game_button->set_clicked( start_game_button->set_clicked(
[this]() { m_scene.scene_manager().request_push(SceneType::WORLD); }); [this]() { m_scene.scene_manager().request_push(SceneType::WORLD); });
start_game_button->set_scale(3.0f); start_game_button->set_scale(3.0f);
start_game_button->set_position( start_game_button->set_anchor(Anchor::CENTER);
m_scene.scene_manager().app().renderer().window_width() / 2.0f -
back.width() / 2.0f,
m_scene.scene_manager().app().renderer().window_height() / 2.0f -
back.height() / 2.0f);
m_widgets.try_emplace("start game", std::move(start_game_button)); m_widgets.try_emplace("start game", std::move(start_game_button));
} }
@@ -100,14 +98,6 @@ bool MainMenuUIManager::handle_mouse_button_event(const MouseButtonEvent& e) {
return false; return false;
} }
bool MainMenuUIManager::handle_window_resize_event(const WindowResizeEvent& e) { bool MainMenuUIManager::handle_window_resize_event(const WindowResizeEvent& e) {
auto it = m_widgets.find("start game");
if (it != m_widgets.end()) {
auto* start_game = dynamic_cast<Button*>(it->second.get());
if (start_game) {
start_game->set_position(e.width / 2 - start_game->width() / 2,
e.height / 2 - start_game->height() / 2);
}
}
for (auto& w : m_widgets) { for (auto& w : m_widgets) {
if (w.second->handle_window_resize_event(e)) { if (w.second->handle_window_resize_event(e)) {
return true; return true;

View File

@@ -1,7 +1,11 @@
#include "Cubed/ui/widget.hpp" #include "Cubed/ui/widget.hpp"
#include "Cubed/tools/log.hpp"
namespace Cubed { namespace Cubed {
Widget::Widget(const std::string& id) : m_id(id) {} Widget::Widget(const std::string& id, Widget* parent)
: m_parent(parent), m_id(id) {}
Widget::Widget(Widget* parent) : m_parent(parent) {}
void Widget::update(float dt) { void Widget::update(float dt) {
on_update(dt); on_update(dt);
@@ -21,20 +25,82 @@ void Widget::render(Renderer& renderer) {
void Widget::on_update(float) {} void Widget::on_update(float) {}
void Widget::on_render(Renderer&) {} void Widget::on_render(Renderer&) {}
Widget& Widget::set_position(const glm::vec2& pos) { glm::vec2 Widget::compute_position() const {
return set_position(pos.x, pos.y); glm::vec2 pos{0, 0};
float parent_w;
float parent_h;
if (!m_parent) {
if (m_window_height == 0 || m_window_width == 0) {
Logger::error("Window Size is 0 !");
}
parent_h = m_window_height;
parent_w = m_window_width;
} else {
parent_h = m_parent->height();
parent_w = m_parent->width();
}
const float W = width();
const float H = height();
switch (m_anchor) {
case Anchor::TOP_LEFT:
pos = {0, 0};
break;
case Anchor::TOP_CENTER:
pos = {(parent_w - W) / 2, 0};
break;
case Anchor::TOP_RIGHT:
pos = {parent_w - W, 0};
break;
case Anchor::CENTER_LEFT:
pos = {0, (parent_h - H) / 2};
break;
case Anchor::CENTER:
pos = {(parent_w - W) / 2, (parent_h - H) / 2};
break;
case Anchor::CENTER_RIGHT:
pos = {parent_w - W, (parent_h - H) / 2};
break;
case Anchor::BOTTOM_LEFT:
pos = {0, parent_h - H};
break;
case Anchor::BOTTOM_CENTER:
pos = {(parent_w - W) / 2, parent_h - H};
break;
case Anchor::BOTTOM_RIGHT:
pos = {parent_w - W, parent_h - H};
break;
}
if (m_parent) {
pos += m_parent->pos();
}
pos += m_offset;
return pos;
} }
Widget& Widget::set_position(float x, float y) { Widget& Widget::set_anchor(Anchor anchor) {
m_pos.x = x; m_anchor = anchor;
m_pos.y = y;
return *this; return *this;
} }
Widget& Widget::set_offset(glm::ivec2 offset) {
m_offset = offset;
return *this;
}
void Widget::set_window_size(int width, int height) {
m_window_height = height;
m_window_width = width;
}
float Widget::width() const { return m_window_width; }
float Widget::height() const { return m_window_height; }
float Widget::width() const { return 0.0f; } glm::vec2 Widget::pos() const { return compute_position(); }
float Widget::height() const { return 0.0f; }
const glm::vec2& Widget::pos() const { return m_pos; }
const std::string& Widget::id() const { return m_id; } const std::string& Widget::id() const { return m_id; }
bool Widget::handle_key_event(const KeyEvent& e) { bool Widget::handle_key_event(const KeyEvent& e) {
@@ -63,6 +129,9 @@ bool Widget::handle_mouse_wheel_event(const MouseWheelEvent& e) {
return false; return false;
} }
bool Widget::handle_window_resize_event(const WindowResizeEvent& e) { bool Widget::handle_window_resize_event(const WindowResizeEvent& e) {
set_window_size(e.width, e.height);
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) { for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
if ((*it)->handle_window_resize_event(e)) { if ((*it)->handle_window_resize_event(e)) {
return true; return true;

View File

@@ -11,16 +11,15 @@ WorldUIManager::WorldUIManager(WorldScene& scene) : m_scene(scene) {}
WorldUIManager::~WorldUIManager() {} WorldUIManager::~WorldUIManager() {}
void WorldUIManager::init() { void WorldUIManager::init() {
auto crosshair = std::make_unique<Image>(); auto crosshair = std::make_unique<Image>(nullptr);
crosshair->set_image("texture/ui/0.png", crosshair->set_image("texture/ui/0.png",
m_scene.scene_manager().app().texture_manager()); m_scene.scene_manager().app().texture_manager());
auto& renderer = m_scene.scene_manager().app().renderer(); auto& renderer = m_scene.scene_manager().app().renderer();
crosshair->set_window_size(renderer.window_width(),
crosshair->set_scale(3.0f).set_position( renderer.window_height());
renderer.window_width() / 2 - crosshair->width() / 2, crosshair->set_anchor(Anchor::CENTER);
renderer.window_height() / 2 - crosshair->height() / 2); crosshair->set_scale(3.0f);
m_widgets.try_emplace("crosshair", std::move(crosshair)); m_widgets.try_emplace("crosshair", std::move(crosshair));
} }
void WorldUIManager::update(float dt) { void WorldUIManager::update(float dt) {
@@ -93,15 +92,6 @@ bool WorldUIManager::handle_mouse_button_event(const MouseButtonEvent& e) {
return false; return false;
} }
bool WorldUIManager::handle_window_resize_event(const WindowResizeEvent& e) { bool WorldUIManager::handle_window_resize_event(const WindowResizeEvent& e) {
auto it = m_widgets.find("crosshair");
if (it != m_widgets.end()) {
auto* crosshair = dynamic_cast<Image*>(it->second.get());
if (crosshair) {
crosshair->set_position(e.width / 2.0f - crosshair->width() / 2.0f,
e.height / 2.0f -
crosshair->height() / 2.0f);
}
}
for (auto& w : m_widgets) { for (auto& w : m_widgets) {
if (w.second->handle_window_resize_event(e)) { if (w.second->handle_window_resize_event(e)) {
return true; return true;