feat(ui): add Rect widget with fill, color, and parent-relative sizing

This commit is contained in:
2026-07-13 20:48:51 +08:00
parent acb0b94d7b
commit 80ba6dad89
15 changed files with 215 additions and 42 deletions

View File

@@ -68,4 +68,5 @@ target_sources(${PROJECT_NAME}
ui/main_menu_ui_manager.cpp
scene/main_menu_scene.cpp
ui/column_layout.cpp
ui/rect.cpp
)

View File

@@ -157,6 +157,26 @@ void Renderer::render_world(ClientWorld& world) {
m_world_renderer.render(world);
}
void Renderer::render_rect(const Rect& rect) {
auto& shader = get_shader("rect");
shader.use();
shader.set_loc("proj_matrix", m_ui_proj_matrix);
auto pos = rect.pos();
glm::mat4 model_matrix =
glm::translate(glm::mat4(1.0f), glm::vec3(pos.x, pos.y, 0.0f)) *
glm::scale(glm::mat4(1.0f),
glm::vec3(rect.width(), rect.height(), 1.0f));
shader.set_loc("model_matrix", model_matrix);
auto color = color_value(rect.color());
shader.set_loc("inColor",
glm::vec4(color.x, color.y, color.z, rect.alpha()));
m_vao[3].bind();
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void Renderer::render_lable(const Label& label) {
const auto& shader = get_shader("text");

View File

@@ -33,6 +33,8 @@ void ShaderManager::init() {
"shaders/player_f_shader.glsl");
register_shader("player_depth", "shaders/depth_player_shader.glsl",
"shaders/depth_player_fragment_shader.glsl");
register_shader("rect", "shaders/rect_v_shader.glsl",
"shaders/rect_f_shader.glsl");
}
void ShaderManager::register_shader(const std::string& name,

View File

@@ -9,6 +9,19 @@ void ColumnLayout::update(float dt) {
layout();
}
float ColumnLayout::width() const {
if (m_parent) {
return m_parent->width();
}
return m_window_width;
}
float ColumnLayout::height() const {
if (m_parent) {
return m_parent->height();
}
return m_window_height;
}
void ColumnLayout::set_spacing(int spacing) { m_spacing = spacing; }
void ColumnLayout::layout() {

View File

@@ -12,13 +12,21 @@ MainMenuUIManager::MainMenuUIManager(MainMenuScene& scene) : m_scene(scene) {}
MainMenuUIManager::~MainMenuUIManager() {}
void MainMenuUIManager::init() {
auto rect = std::make_unique<Rect>(nullptr);
rect->set_fill(true);
rect->set_anchor(Anchor::TOP_LEFT);
rect->set_color(Color::WHITE);
auto& renderer = m_scene.scene_manager().app().renderer();
auto layout = std::make_unique<ColumnLayout>(nullptr);
layout->set_spacing(20);
layout->set_anchor(Anchor::CENTER);
layout->set_window_size(renderer.window_width(), renderer.window_height());
rect->set_window_size(renderer.window_width(), renderer.window_height());
auto& layout = rect->add_child<ColumnLayout>();
layout.set_spacing(20);
layout.set_anchor(Anchor::CENTER);
layout.set_window_size(renderer.window_width(), renderer.window_height());
{
auto& start_game_button = layout->add_child<Button>();
auto& start_game_button = layout.add_child<Button>();
auto& back = start_game_button.set_background<Image>();
back.set_image("texture/ui/button001.png",
@@ -32,7 +40,7 @@ void MainMenuUIManager::init() {
start_game_button.set_scale(4.0f);
}
auto& exit_game = layout->add_child<Button>();
auto& exit_game = layout.add_child<Button>();
{
auto& back = exit_game.set_background<Image>();
@@ -45,22 +53,17 @@ void MainMenuUIManager::init() {
m_scene.scene_manager().app().window().should_close_window();
});
}
m_widgets.try_emplace("main menu layout", std::move(layout));
m_widgets.try_emplace("background", rect.get());
m_widgets.try_emplace("main menu layout", &layout);
m_root_widget = std::move(rect);
}
void MainMenuUIManager::update(float dt) {
for (auto& w : m_widgets) {
w.second->update(dt);
}
}
void MainMenuUIManager::update(float dt) { m_root_widget->update(dt); }
void MainMenuUIManager::render(Renderer& renderer) {
renderer.begin_render_ui();
for (auto& widget : m_widgets) {
widget.second->render(renderer);
}
m_root_widget->render(renderer);
renderer.end_render_ui();
}
@@ -101,42 +104,39 @@ bool MainMenuUIManager::handle_event(const Event& e) {
e);
}
bool MainMenuUIManager::handle_mouse_move_event(const MouseMoveEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_mouse_move_event(e)) {
return true;
}
if (m_root_widget->handle_mouse_move_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_mouse_button_event(const MouseButtonEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_mouse_button_event(e)) {
return true;
}
if (m_root_widget->handle_mouse_button_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_window_resize_event(const WindowResizeEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_window_resize_event(e)) {
return true;
}
if (m_root_widget->handle_window_resize_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_mouse_wheel_event(const MouseWheelEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_mouse_wheel_event(e)) {
return true;
}
if (m_root_widget->handle_mouse_wheel_event(e)) {
return true;
}
return false;
}
bool MainMenuUIManager::handle_key_event(const KeyEvent& e) {
for (auto& w : m_widgets) {
if (w.second->handle_key_event(e)) {
return true;
}
if (m_root_widget->handle_key_event(e)) {
return true;
}
return false;

58
src/ui/rect.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "Cubed/ui/rect.hpp"
#include "Cubed/render/renderer.hpp"
namespace Cubed {
Rect::Rect(Widget* parent)
: Widget(parent) {
};
Rect::~Rect() {}
void Rect::update(float dt) {
if (m_fill) {
if (!m_parent) {
m_width = m_window_width;
m_height = m_window_height;
} else {
m_width = m_parent->width();
m_height = m_parent->height();
}
}
Widget::update(dt);
}
void Rect::render(Renderer& renderer) {
renderer.render_rect(*this);
Widget::render(renderer);
}
float Rect::width() const { return m_width * m_scale; }
float Rect::height() const { return m_height * m_scale; }
float Rect::alpha() const { return m_alpha; }
Rect& Rect::set_width(float width) {
m_width = width;
return *this;
}
Rect& Rect::set_height(float height) {
m_height = height;
return *this;
}
Rect& Rect::set_fill(bool fill) {
m_fill = fill;
return *this;
}
Rect& Rect::set_scale(float scale) {
m_scale = scale;
return *this;
}
Rect& Rect::set_color(Color color) {
m_color = color;
return *this;
}
Rect& Rect::set_alpha(float alpha) {
m_alpha = std::clamp(alpha, 0.0f, 1.0f);
return *this;
}
Color Rect::color() const { return m_color; }
} // namespace Cubed

View File

@@ -103,8 +103,18 @@ 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 {
if (m_parent) {
return m_parent->width();
}
return m_window_width;
}
float Widget::height() const {
if (m_parent) {
return m_parent->height();
}
return m_window_height;
}
glm::vec2 Widget::pos() const { return compute_position(); }
const std::string& Widget::id() const { return m_id; }