Totally refactored UILabel

This commit is contained in:
2023-03-05 23:42:15 -08:00
parent aeb5014a07
commit ee2dc931f2
15 changed files with 463 additions and 194 deletions

View File

@ -115,17 +115,24 @@ namespace Dawn {
return children;
}
/**
* Finds all children, and children of children, recursively that match
* the queried component.
*
* @tparam T Component Type to find.
* @return Array of pointers to matching children components.
*/
template<class T>
std::vector<T*> findChildrenDeep() {
std::vector<Transform*> itemsToCheck = this->transform.children;
std::vector<Transform*> transformsToCheck = this->transform.children;
std::vector<T*> itemsFound;
while(itemsToCheck.size() > 0) {
auto item = itemsToCheck.begin();
vectorAppend(&itemsToCheck, (*item)->children);
auto component = (*item)->item->getComponent<T>();
while(transformsToCheck.size() > 0) {
auto tras = transformsToCheck.begin();
vectorAppend(&transformsToCheck, (*tras)->children);
auto component = (*tras)->item->getComponent<T>();
if(component != nullptr) itemsFound.push_back(component);
itemsToCheck.erase(item);
transformsToCheck.erase(tras);
}
return itemsFound;

View File

@ -18,7 +18,7 @@ Camera::Camera(SceneItem *item) :
orthoBottom(0.0f),
orthoTop(1.0f),
clipNear(0.001f),
clipFar(50.0f)
clipFar(1000.0f)
{
}

View File

@ -5,6 +5,7 @@
#include "UICanvas.hpp"
#include "game/DawnGame.hpp"
#include "UIComponent.hpp"
using namespace Dawn;
@ -13,10 +14,47 @@ UICanvas * UICanvas::create(Scene *scene) {
return item->addComponent<UICanvas>();
}
UICanvas::UICanvas(SceneItem *item) : SceneItemComponent(item) {
UICanvas::UICanvas(SceneItem *item) :
SceneItemComponent(item),
camera(nullptr)
{
}
float_t UICanvas::getWidth() {
return w;
}
float_t UICanvas::getHeight() {
return h;
}
float_t UICanvas::getContentWidth() {
return this->getWidth();
}
float_t UICanvas::getContentHeight() {
return this->getHeight();
}
void UICanvas::onStart() {
if(camera == nullptr) camera = getScene()->findComponent<Camera>();
useEffectWithTeardown([&]{
if(camera == nullptr) return evtRenderResize = [&] {};
this->w = camera->getRenderTarget()->getWidth();
this->h = camera->getRenderTarget()->getHeight();
return evtRenderResize = useEvent([&](float_t w, float_t h){
this->w = w;
this->h = h;
auto comps = this->item->findChildren<UIComponent>();
auto itComps = comps.begin();
while(itComps != comps.end()) {
(*itComps)->alignmentNeedsUpdating = true;
++itComps;
}
}, camera->eventRenderTargetResized);
}, camera)();
}
void UICanvas::onDispose() {

View File

@ -9,13 +9,26 @@
#include "scene/components/display/Camera.hpp"
namespace Dawn {
class UIComponentDimensional {
public:
virtual float_t getWidth() = 0;
virtual float_t getHeight() = 0;
virtual float_t getContentWidth() = 0;
virtual float_t getContentHeight() = 0;
};
enum UIDrawType {
UI_DRAW_TYPE_WORLD_ABSOLUTE,
UI_DRAW_TYPE_WORLD_CAMERA_RELATIVE,
UI_DRAW_TYPE_CAMERA_OVERLAY
// UI_DRAW_TYPE_CAMERA_OVERLAY
};
class UICanvas : public SceneItemComponent {
class UICanvas : public SceneItemComponent, public UIComponentDimensional {
private:
std::function<void()> evtRenderResize;
float_t w = 1;
float_t h = 1;
public:
/**
* Creates a UI Canvas Scene Item Element, and attaches it to the provided
@ -27,7 +40,7 @@ namespace Dawn {
static UICanvas * create(Scene *scene);
//======================================================================//
StateProperty<Camera*> camera;
enum UIDrawType drawType = UI_DRAW_TYPE_WORLD_CAMERA_RELATIVE;
/**
@ -37,6 +50,11 @@ namespace Dawn {
*/
UICanvas(SceneItem *item);
float_t getWidth() override;
float_t getHeight() override;
float_t getContentWidth() override;
float_t getContentHeight() override;
void onStart() override;
void onDispose() override;
};

View File

@ -7,6 +7,113 @@
using namespace Dawn;
UIComponent::UIComponent(SceneItem *item) : SceneItemComponent(item) {
UIComponent::UIComponent(SceneItem *item) :
SceneItemComponent(item),
alignment(glm::vec4(0, 0, 128, 128)),
alignX(UI_COMPONENT_ALIGN_START),
alignY(UI_COMPONENT_ALIGN_START),
alignmentNeedsUpdating(true)
{
}
UIComponentDimensional * UIComponent::getParentDimensional() {
auto parent = this->transform->getParent();
if(!parent) return nullptr;
auto dimensional = parent->item->getComponent<UIComponentDimensional>();
assertNotNull(dimensional);
return dimensional;
}
void UIComponent::updateAlignment() {
if(!this->alignmentNeedsUpdating) return;
auto dimensional = this->getParentDimensional();
auto align = (glm::vec4)this->alignment;
auto translate = this->transform->getLocalPosition();
UIComponent::calculateDimensions(
this->alignX,
&translate.x,
&this->width,
dimensional->getWidth(),
this->getContentWidth(),
glm::vec2(align[0], align[2])
);
UIComponent::calculateDimensions(
this->alignY,
&translate.y,
&this->height,
dimensional->getHeight(),
this->getContentHeight(),
glm::vec2(align[1], align[3])
);
this->transform->setLocalPosition(translate);
this->alignmentNeedsUpdating = false;
this->eventAlignmentUpdated.invoke();
}
void UIComponent::calculateDimensions(
enum UIComponentAlign align,
float_t *position,
float_t *size,
float_t outerSize,
float_t innerSize,
glm::vec2 alignment
) {
assertNotNull(position);
assertNotNull(size);
switch(align) {
case UI_COMPONENT_ALIGN_STRETCH:
*position = alignment[0];
*size = outerSize + (alignment[0] + alignment[1]);
break;
case UI_COMPONENT_ALIGN_START:
*position = alignment[0];
*size = alignment[1];
break;
case UI_COMPONENT_ALIGN_MIDDLE:
*size = mathMax<float_t>(innerSize, alignment[1]);
*position = (outerSize / 2.0f) - (innerSize / 2.0f) + alignment[0];
break;
case UI_COMPONENT_ALIGN_END:
*size = alignment[0];
*position = outerSize - innerSize - alignment[1];
break;
default:
assertUnreachable();
break;
}
}
float_t UIComponent::getWidth() {
return this->width;
}
float_t UIComponent::getHeight() {
return this->height;
}
void UIComponent::onStart() {
useEffect([&]{
this->alignmentNeedsUpdating = true;
}, { &alignment, &alignX, &alignY });
useEffect([&]{
this->updateAlignment();
auto child = this->item->findChildren<UIComponent>();
auto itChild = child.begin();
while(itChild != child.end()) {
(*itChild)->alignmentNeedsUpdating = true;
++itChild;
}
}, alignmentNeedsUpdating)();
}

View File

@ -6,20 +6,71 @@
#pragma once
#include "scene/SceneItemComponent.hpp"
#include "UICanvas.hpp"
#include "display/shader/Shader.hpp"
#include "util/mathutils.hpp"
namespace Dawn {
class UIComponentRendaerable {
public:
int32_t cum;
enum UIComponentAlign {
UI_COMPONENT_ALIGN_START,
UI_COMPONENT_ALIGN_MIDDLE,
UI_COMPONENT_ALIGN_END,
UI_COMPONENT_ALIGN_STRETCH
};
class UIComponent : public SceneItemComponent {
private:
UICanvas *canvas = nullptr;
class UIComponentRenderable {
public:
virtual std::vector<struct ShaderPassItem> getPassItems(
glm::mat4 projection,
glm::mat4 view
) = 0;
};
class UIComponent : public SceneItemComponent, public UIComponentDimensional {
protected:
// Calculated (and cached) values
float_t width = 1;
float_t height = 1;
StateEvent<> eventAlignmentUpdated;
UIComponentDimensional * getParentDimensional();
void updateAlignment();
public:
StateProperty<bool_t> alignmentNeedsUpdating;
/**
* Method used to calculate alignment dimensions.
*
* @param align Alignment value enumator.
* @param position Output position floating point.
* @param size Output size floating point.
* @param outerSize Outer size (of the parent).
* @param innerSize Inner size (of this element's content).
* @param alignment Alignment settings.
*/
static void calculateDimensions(
enum UIComponentAlign align,
float_t *position,
float_t *size,
float_t outerSize,
float_t innerSize,
glm::vec2 alignment
);
//
StateProperty<UIComponentAlign> alignX;
StateProperty<UIComponentAlign> alignY;
StateProperty<glm::vec4> alignment;
UIComponent(SceneItem *item);
float_t getWidth() override;
float_t getHeight() override;
void onStart() override;
friend class UICanvas;
};
}

View File

@ -4,6 +4,7 @@
// https://opensource.org/licenses/MIT
#include "UILabel.hpp"
#include "game/DawnGame.hpp"
using namespace Dawn;
@ -11,39 +12,100 @@ UILabel::UILabel(SceneItem *item) :
UIComponent(item),
text(""),
fontSize(10.0f),
font(nullptr)
font(nullptr),
maxWidth(UI_LABEL_MAX_WIDTH_NONE)
{
}
bool_t UILabel::hasText() {
return (
this->font != nullptr &&
this->font->isReady() &&
((std::string)this->text).size() > 0
);
}
void UILabel::updateMesh() {
if(!this->needsRebuffering || !this->hasText) return;
if(((Font*)this->font) == nullptr || !this->font->isReady()) return;
if(!this->needsRebuffering) return;
if(!this->hasText()) return;
// float_t width = this->width;
// if(width == 0) width = -1;
// std::string text = this->getGame()->localeManager.getString(key);
float_t width = this->maxWidth;
assertTrue(width == 0 || width == -1 || width > 0);
if(width == 0) {
auto dimensional = this->getParentDimensional();
auto align = (glm::vec4)this->alignment;
float_t x;
UIComponent::calculateDimensions(
this->alignX,
&x,
&width,
dimensional->getWidth(),
0,
glm::vec2(align[0], align[2])
);
}
this->font->buffer(
text,
fontSize,
-1,
width,
&this->mesh,
&this->measure
);
this->needsRebuffering = false;
}
std::vector<struct ShaderPassItem> UILabel::getPassItems(
glm::mat4 proj, glm::mat4 view
) {
if(!this->hasText()) return {};
this->updateMesh();
struct ShaderPassItem item;
auto shader = &getGame()->renderManager.uiShaderProgram;
item.shaderProgram = shader;
item.colorValues[shader->paramColor] = textColor;
item.matrixValues[shader->paramProjection] = proj;
item.matrixValues[shader->paramView] = view;
item.matrixValues[shader->paramModel] = this->transform->getWorldTransform();
item.boolValues[shader->paramHasTexture] = true;
item.textureSlots[0] = this->font->getTexture();
item.textureValues[shader->paramTexture] = 0;
item.start = this->startQuad * QUAD_INDICE_COUNT;
item.count = this->quadCount == -1 ? -1 : this->quadCount * QUAD_INDICE_COUNT;
item.w = this->transform->getWorldPosition().z;
item.renderFlags = RENDER_MANAGER_RENDER_FLAG_BLEND;
item.mesh = &mesh;
return { item };
}
float_t UILabel::getContentWidth() {
if(this->maxWidth > 0) return this->maxWidth;
if(!this->hasText()) return 0;
this->updateMesh();
return this->measure.getWidth();
}
float_t UILabel::getContentHeight() {
if(!this->hasText()) return 0;
this->updateMesh();
return this->measure.getHeight();
}
void UILabel::onStart() {
UIComponent::onStart();
useEffect([&]{
hasText = ((std::string)text).size() > 0;
needsRebuffering = true;
}, text);
alignmentNeedsUpdating = true;
}, { &fontSize, &font, &text, &maxWidth });
useEffect([&]{
needsRebuffering = true;
}, fontSize);
}, alignmentNeedsUpdating);
useEffect([&]{
useEvent([&]{
needsRebuffering = true;
}, font);
}, eventAlignmentUpdated);
}

View File

@ -7,20 +7,23 @@
#include "UIComponent.hpp"
#include "display/font/Font.hpp"
#define UI_LABEL_MAX_WIDTH_NONE -1
#define UI_LABEL_MAX_WIDTH_ALIGN 0
namespace Dawn {
class UILabel : public UIComponent {
class UILabel : public UIComponent, public UIComponentRenderable {
private:
bool_t needsRebuffering = true;
bool_t hasText = false;
Mesh mesh;
bool_t hasText();
void updateMesh();
public:
StateProperty<std::string> text;
StateProperty<float_t> fontSize;
StateProperty<Font*> font;
StateProperty<float_t> maxWidth;
struct Color textColor = COLOR_MAGENTA;
struct FontMeasure measure;
int32_t startQuad = 0;
@ -28,6 +31,11 @@ namespace Dawn {
UILabel(SceneItem *item);
float_t getContentWidth() override;
float_t getContentHeight() override;
std::vector<struct ShaderPassItem> getPassItems(
glm::mat4 projection, glm::mat4 view
) override;
void onStart() override;
};
}