ADD: Implement Basic UI Widgets and Components;

This commit is contained in:
sebastian 2026-02-10 19:40:31 +01:00
parent 0a384345bb
commit 917d096d60
14 changed files with 277 additions and 5 deletions

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />
<module classpath="CIDR" type="CPP_MODULE" version="4" />

View File

@ -17,7 +17,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />

View File

@ -117,6 +117,15 @@ add_executable(Dicewars_Siedler src/main.cpp
src/game/UILayer.h
src/engine/renderer/shaders/GUIShader.cpp
src/engine/renderer/shaders/GUIShader.h
src/engine/core/gui/uiComponent/UiComponent.cpp
src/engine/core/gui/uiComponent/UiComponent.h
src/engine/core/gui/uiComponent/Dimensions.h
src/engine/core/gui/uiComponent/UiPositioner.cpp
src/engine/core/gui/uiComponent/UiPositioner.h
src/engine/core/gui/uiMain/UiContainer.cpp
src/engine/core/gui/uiMain/UiContainer.h
src/engine/core/gui/uiComponent/UiImage.cpp
src/engine/core/gui/uiComponent/UiImage.h
)
target_include_directories(Dicewars_Siedler PRIVATE

View File

@ -0,0 +1,24 @@
//
// Created by sebastian on 10.02.26.
//
#ifndef DICEWARS_SIEDLER_DIMENSIONS_H
#define DICEWARS_SIEDLER_DIMENSIONS_H
struct Dimensions {
float x, y;
float width, height;
void set(const float x, const float y, const float width, const float height) {
this->x = x;
this->y = y;
this->width = width;
this->height = height;
}
[[nodiscard]] bool contains(const float testX, const float testY) const {
return testX >= x && testX <= x + width && testY >= y && testY <= y + height;
}
};
#endif //DICEWARS_SIEDLER_DIMENSIONS_H

View File

@ -0,0 +1,44 @@
//
// Created by sebastian on 10.02.26.
//
#include "UiComponent.h"
void UiComponent::addChild(std::unique_ptr<UiComponent> child) {
child->parent = this;
child->uiPositioner.compute(this->uiPositioner.screenSpace);
children.emplace_back(std::move(child));
}
void UiComponent::setVisible(const bool v) {
this->visible = v;
}
bool UiComponent::isVisible() const {
return visible;
}
void UiComponent::update(float delta) {
if (!visible) return;
onUpdate(delta);
for (const auto &child : children) {
child->update(delta);
}
}
void UiComponent::collectRenderData(std::vector<GUITexture> &guiTextures) {
if (!visible) return;
onCollectRenderData(guiTextures);
for (const auto &child : children) {
child->collectRenderData(guiTextures);
}
}
bool UiComponent::isMouseOver(float mouseX, float mouseY) {
if (!visible) return false;
return uiPositioner.screenSpace.contains(mouseX, mouseY);
}

View File

@ -0,0 +1,38 @@
//
// Created by sebastian on 10.02.26.
//
#ifndef DICEWARS_SIEDLER_UICOMPONENT_H
#define DICEWARS_SIEDLER_UICOMPONENT_H
#include <memory>
#include <vector>
#include "UiPositioner.h"
#include "../../../renderer/model/GUITexture.h"
class UiComponent {
public:
UiComponent() : uiPositioner(*this) {};
virtual ~UiComponent() = default;
void addChild(std::unique_ptr<UiComponent> child);
void setVisible(bool visible);
[[nodiscard]] bool isVisible() const;
void update(float delta);
void collectRenderData(std::vector<GUITexture>& guiTextures);
virtual bool isMouseOver(float mouseX, float mouseY);
UiComponent* parent = nullptr;
UiPositioner uiPositioner;
protected:
std::vector<std::unique_ptr<UiComponent>> children;
bool visible = true;
virtual void onUpdate(float /*delta*/) {}
virtual void onCollectRenderData(std::vector<GUITexture>&) {}
};
#endif //DICEWARS_SIEDLER_UICOMPONENT_H

View File

@ -0,0 +1,11 @@
//
// Created by sebastian on 10.02.26.
//
#include "UiImage.h"
void UiImage::onCollectRenderData(std::vector<GUITexture> &gui_textures) {
if (!visible) return;
Dimensions dims = uiPositioner.screenSpace;
gui_textures.emplace_back(textureID, glm::vec2(dims.x, dims.y), glm::vec2(dims.width, dims.height));
}

View File

@ -0,0 +1,25 @@
//
// Created by sebastian on 10.02.26.
//
#ifndef DICEWARS_SIEDLER_UIIMAGE_H
#define DICEWARS_SIEDLER_UIIMAGE_H
#include "UiComponent.h"
class UiImage : public UiComponent {
public:
UiImage(GLuint textureID, const glm::vec2& relativePos, const glm::vec2& relativeSize) : textureID(textureID) {
uiPositioner.setRelativePos(relativePos);
uiPositioner.setRelativeSize(relativeSize);
};
protected:
void onCollectRenderData(std::vector<GUITexture> &) override;
private:
GLuint textureID;
};
#endif //DICEWARS_SIEDLER_UIIMAGE_H

View File

@ -0,0 +1,28 @@
//
// Created by sebastian on 10.02.26.
//
#include "UiPositioner.h"
#include "UiComponent.h"
void UiPositioner::compute(const Dimensions &parent) {
screenSpace.x = parent.x + relativePos.x * parent.width;
screenSpace.y = parent.y + relativePos.y * parent.height;
screenSpace.width = relativeSize.x * parent.width;
screenSpace.height = relativeSize.y * parent.height;
}
void UiPositioner::setRelativePos(const glm::vec2 &pos) {
relativePos = pos;
if (uiComponent.parent) {
compute(uiComponent.parent->uiPositioner.screenSpace);
}
}
void UiPositioner::setRelativeSize(const glm::vec2 &size) {
relativeSize = size;
if (uiComponent.parent) {
compute(uiComponent.parent->uiPositioner.screenSpace);
}
}

View File

@ -0,0 +1,34 @@
//
// Created by sebastian on 10.02.26.
//
#ifndef DICEWARS_SIEDLER_UIPOSITIONER_H
#define DICEWARS_SIEDLER_UIPOSITIONER_H
#include "Dimensions.h"
#include <glm/vec2.hpp>
class UiComponent; // forward declaration
class UiPositioner {
public:
Dimensions screenSpace = Dimensions();
explicit UiPositioner(UiComponent& uiComponent)
: uiComponent(uiComponent),
relativePos(0.0f, 0.0f),
relativeSize(1.0f, 1.0f)
{}
void compute(const Dimensions& parentDimensions);
void setRelativePos(const glm::vec2& pos);
void setRelativeSize(const glm::vec2& size);
private:
UiComponent& uiComponent;
glm::vec2 relativePos; // [0..1] relative to parent
glm::vec2 relativeSize; // [0..1] relative to parent
};
#endif //DICEWARS_SIEDLER_UIPOSITIONER_H

View File

@ -0,0 +1,25 @@
//
// Created by sebastian on 10.02.26.
//
#include "UiContainer.h"
UiContainer::UiContainer(){
uiPositioner.screenSpace.set(0.f, 0.f, 1.f, 1.f);
}
bool UiContainer::isMouseOver(float mouseX, float mouseY) {
for (const auto &child : children) {
if (child->isMouseOver(mouseX, mouseY)) return true;
}
return false;
}
void UiContainer::onUpdate(float x) {
}
void UiContainer::onCollectRenderData(std::vector<GUITexture> &gui_textures) {
}

View File

@ -0,0 +1,19 @@
//
// Created by sebastian on 10.02.26.
//
#ifndef DICEWARS_SIEDLER_UICONTAINER_H
#define DICEWARS_SIEDLER_UICONTAINER_H
#include "../uiComponent/UiComponent.h"
class UiContainer : public UiComponent {
public:
UiContainer();
bool isMouseOver(float mouseX, float mouseY) override;
void onUpdate(float) override;
void onCollectRenderData(std::vector<GUITexture> &) override;
};
#endif //DICEWARS_SIEDLER_UICONTAINER_H

View File

@ -4,6 +4,7 @@
#include "UILayer.h"
#include "../engine/core/gui/uiComponent/UiImage.h"
#include "../engine/renderer/loader/Loader.h"
#include "../engine/renderer/model/GUITexture.h"
@ -15,13 +16,25 @@ UILayer::UILayer() {
void UILayer::onAttach() {
Layer::onAttach();
ModelTexture modelTexture = Loader::instance().loadTextureFromFile("assets/textures/texture.png");
guiTextures.emplace_back(modelTexture.getTextureID(), glm::vec2(0.5f, 0.5f), glm::vec2(0.5f));
rootContainer = std::make_unique<UiContainer>();
auto image = std::make_unique<UiImage>(
Loader::instance().loadTextureFromFile("assets/textures/texture.png").getTextureID(),
glm::vec2(0.5f, 0.5f), glm::vec2(0.5f)
);
rootContainer->addChild(std::move(image));
}
void UILayer::onUpdate() {
Layer::onUpdate();
std::vector<GUITexture> guiTextures;
if (rootContainer) {
rootContainer->collectRenderData(guiTextures);
}
printf("Found UI Textures: %lu\n", guiTextures.size());
guiRenderer->render(guiTextures);
}

View File

@ -6,6 +6,7 @@
#define UILAYER_H
#include <memory>
#include "../engine/core/gui/uiMain/UiContainer.h"
#include "../engine/layer/Layer.h"
#include "../engine/renderer/GUIRenderer.h"
@ -13,7 +14,8 @@
class UILayer : public Layer{
private:
std::unique_ptr<GUIRenderer> guiRenderer;
std::vector<GUITexture> guiTextures;
std::unique_ptr<UiContainer> rootContainer;
public:
UILayer();
void onAttach() override;