First pass UI Alignment

This commit is contained in:
2023-12-19 11:43:53 -06:00
parent 0c6ac2dfbb
commit aa14955924
11 changed files with 307 additions and 70 deletions

View File

@ -60,17 +60,13 @@ struct RenderPassContext &ctx
nextBinding = 0;
// Define the root alignment
struct UIAlign rootAlignment = {
glm::vec2(0, 0),
glm::vec2(1280, 720)
};
// Get the quads for each component
auto itComponents = components.begin();
auto self = std::ref(*this);
while(itComponents != components.end()) {
auto component = *itComponents;
component->getQuads(rootAlignment, self);
component->getQuads(self);
++itComponents;
}
@ -136,4 +132,13 @@ void UICanvas::flushPass() {
nextBinding = 0;
textures.clear();
textureBindings.clear();
}
void UICanvas::addComponent(std::shared_ptr<UIComponent> component) {
components.push_back(component);
component->updateAlignment(
glm::vec2(0, 0),
glm::vec2(1280, 720),
1.0f
);
}

View File

@ -18,6 +18,7 @@ namespace Dawn {
private:
std::shared_ptr<Mesh> mesh;
UIShaderData data;
std::vector<std::shared_ptr<UIComponent>> components;
size_t quadCount = 0;
shadertexturebinding_t nextBinding = 0;
@ -41,8 +42,6 @@ namespace Dawn {
void flushPass();
public:
std::vector<std::shared_ptr<UIComponent>> components;
std::vector<std::shared_ptr<IRenderPass>> getPasses(
struct RenderPassContext &ctx
) override;
@ -63,5 +62,12 @@ namespace Dawn {
const enum UIShaderQuadStyle style,
const std::shared_ptr<Texture> texture = nullptr
);
/**
* Adds a component to the canvas.
*
* @param component The component to add.
*/
void addComponent(std::shared_ptr<UIComponent> component);
};
}

View File

@ -14,9 +14,9 @@ namespace Dawn {
class TrueTypeTexture final {
private:
FT_Face face;
uint32_t fontSize;
public:
uint32_t fontSize;
std::shared_ptr<Texture> texture;
std::unordered_map<wchar_t, struct TrueTypeCharacter> characterData;

View File

@ -4,29 +4,194 @@
// https://opensource.org/licenses/MIT
#include "UIComponent.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
UIComponent::UIComponent() {
alignUnit[0] = UIAlignmentUnit::SCALE;
alignUnit[1] = UIAlignmentUnit::SCALE;
alignUnit[2] = UIAlignmentUnit::SCALE;
alignUnit[3] = UIAlignmentUnit::SCALE;
eventAlignmentUpdated = [&](const glm::vec2 p, const glm::vec2 s) {
};
}
void UIComponent::updateAlignment(
const glm::vec2 pPos,
const glm::vec2 pSize,
const float_t canvasScale
) {
auto valueAxis = [&](
const enum UIAlignmentUnit unit,
const float_t alignment,
const float_t parentSize,
const float_t ratioSize
) {
switch(unit) {
case UIAlignmentUnit::PIXEL:
return alignment;
case UIAlignmentUnit::SCALE:
return canvasScale * alignment;
case UIAlignmentUnit::PERCENT:
return parentSize * (alignment / 100.0f);
case UIAlignmentUnit::RATIO:
return (alignment / 100.0f) * ratioSize;
default:
assertUnreachable("Invalid UIAlignmentType");
return 0.0f;
}
};
auto alignAxis = [&](
const enum UIAlignmentType type,
const enum UIAlignmentUnit unit0,
const enum UIAlignmentUnit unit1,
const float_t alignment0,
const float_t alignment1,
const float_t parentSize,
const float_t ratioSize,
float_t &selfPosition,
float_t &selfSize
) {
switch(type) {
case UIAlignmentType::START:
selfPosition = valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
);
selfSize = valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
);
break;
case UIAlignmentType::MIDDLE:
selfSize = valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
);
selfPosition = (parentSize / 2.0f) - (selfSize / 2.0f) + valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
);
break;
case UIAlignmentType::END:
selfSize = valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
);
selfPosition = parentSize - selfSize - valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
);
break;
case UIAlignmentType::STRETCH:
selfPosition = valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
);
selfSize = parentSize - (selfPosition + valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
));
break;
default:
assertUnreachable("Invalid UIAlignmentType");
}
};
bool_t heightFirst = (
alignUnit[0] == UIAlignmentUnit::RATIO ||
alignUnit[2] == UIAlignmentUnit::RATIO
);
if(heightFirst) {
// Align height first, this will define size.y which we can use as the ratio
// for the width/X axis alignment.
alignAxis(
alignY,
alignUnit[1],
alignUnit[3],
align[1],
align[3],
pSize.y,
0,
position.y,
size.y
);
alignAxis(
alignX,
alignUnit[0],
alignUnit[2],
align[0],
align[2],
pSize.x,
size.y,
position.x,
size.x
);
} else {
alignAxis(
alignX,
alignUnit[0],
alignUnit[2],
align[0],
align[2],
pSize.x,
0,
position.x,
size.x
);
alignAxis(
alignY,
alignUnit[1],
alignUnit[3],
align[1],
align[3],
pSize.y,
size.x,
position.y,
size.y
);
}
this->eventAlignmentUpdated(position, size);
}
std::vector<std::shared_ptr<UIComponent>> UIComponent::getChildren() {
return {};
}
void UIComponent::getQuads(
const struct UIAlign alignment,
UICanvas &ctx
) {
auto selfAlignment = getSelfAlignment(alignment);
this->getSelfQuads(selfAlignment, ctx);
void UIComponent::getQuads(UICanvas &ctx) {
this->getSelfQuads(ctx);
auto children = getChildren();
for(auto &c : children) {
c->getQuads(selfAlignment, ctx);
c->getQuads(ctx);
}
}
struct UIAlign UIComponent::getSelfAlignment(const struct UIAlign alignment) {
return (struct UIAlign){
alignment.position + position,
alignment.size
};
}

View File

@ -8,13 +8,25 @@
#include "component/ui/UICanvas.hpp"
namespace Dawn {
struct UIAlign {
glm::vec2 position;
glm::vec2 size;
enum class UIAlignmentType {
START,
MIDDLE,
END,
STRETCH
};
enum class UIAlignmentUnit {
PIXEL,
SCALE,
PERCENT,
RATIO
};
class UIComponent {
protected:
glm::vec2 position;
glm::vec2 size;
/**
* Virtual method overridden by the UIComponent to get the quads for the
* component.
@ -22,21 +34,34 @@ namespace Dawn {
* @param alignment The alignment of this component.
* @param ctx The canvas to add the quads to.
*/
virtual void getSelfQuads(
const struct UIAlign alignment,
UICanvas &ctx
) = 0;
virtual void getSelfQuads(UICanvas &ctx) = 0;
/**
* Allows a component to modify its self alignment.
* Updates the alignment of this component based on the parent.
*
* @param alignment Aliugnment to modify.
* @return New alignment for this component.
* @param parentPosition The position of the parent.
* @param parentSize The size of the parent.
*/
virtual struct UIAlign getSelfAlignment(const struct UIAlign alignment);
void updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,
const float_t canvasScale
);
public:
glm::vec2 position;
glm::vec4 align = glm::vec4(0, 0, 32, 32);
enum UIAlignmentType alignX = UIAlignmentType::START;
enum UIAlignmentType alignY = UIAlignmentType::START;
enum UIAlignmentUnit alignUnit[4];
std::function<
void(const glm::vec2, const glm::vec2)
> eventAlignmentUpdated;
/**
* Instantiates a new UIComponent.
*/
UIComponent();
/**
* Virtual method overridden by the UIComponent to get the children of
@ -47,12 +72,10 @@ namespace Dawn {
/**
* Method called by the UICanvas to get the quads for this component.
*
* @param alignment The alignment of this component.
* @param ctx The canvas to add the quads to.
*/
void getQuads(
const struct UIAlign alignment,
UICanvas &ctx
);
void getQuads(UICanvas &ctx);
friend class UICanvas;
};
}

View File

@ -7,24 +7,67 @@
using namespace Dawn;
void UILabel::getSelfQuads(
const struct UIAlign alignment,
UICanvas &ctx
) {
void UILabel::getSelfQuads(UICanvas &ctx) {
std::vector<struct UIShaderQuad> quads;
if(this->texture == nullptr || this->text.empty()) return;
glm::vec4 quad;
glm::vec2 pos = alignment.position;
glm::vec2 pos = glm::vec2(0, this->texture->fontSize);
bool_t lastCharWasSpace = false;
for(wchar_t c : text) {
for(size_t i = 0; i < text.size(); i++) {
wchar_t c = text[i];
auto info = texture->getCharacterData(c);
// Newline(s)
if(c == L'\n') {
pos.x = 0;
pos.y += this->texture->fontSize;
continue;
}
// Spaces
if(c == L' ') {
pos.x += info.advance.x;
lastCharWasSpace = true;
continue;
}
// Word Wrap
if(wordWrap) {
if(lastCharWasSpace) {
// Scan ahead to next space
float_t wordWidth = pos.x;// Start at current position and scan ahead.
for(size_t j = i; j < text.size(); j++) {
wchar_t c2 = text[j];
if(c2 == L' ' || c2 == L'\n') {
break;// If we hit another space, we are OK.
}
// Will this character fit on the row? If not the whole word will wrap.
auto info2 = texture->getCharacterData(c);
wordWidth += info.advance.x;
if(wordWidth > size.x) {
pos.x = 0;
pos.y += this->texture->fontSize;
break;
}
}
lastCharWasSpace = false;
}
} else if(pos.x + info.size.x > position.x + size.x) {
// Not word wrap, but instead just overflow characters.
pos.x = 0;
pos.y += this->texture->fontSize;
}
ctx.addQuad(
{
pos.x + info.offset.x,
pos.y + info.offset.y,
pos.x + info.size.x + info.offset.x,
pos.y + info.size.y + info.offset.y
position.x + pos.x + info.offset.x,
position.y + pos.y + info.offset.y,
position.x + pos.x + info.size.x + info.offset.x,
position.y + pos.y + info.size.y + info.offset.y
},
{
info.quad.x,

View File

@ -14,12 +14,11 @@ namespace Dawn {
std::wstring text = L"Hello World";
protected:
void getSelfQuads(
const struct UIAlign alignment,
UICanvas &ctx
) override;
void getSelfQuads(UICanvas &ctx) override;
public:
bool_t wordWrap = true;
/**
* Returns the font used for this label.
*

View File

@ -7,13 +7,10 @@
using namespace Dawn;
void UIRectangle::getSelfQuads(
const struct UIAlign alignment,
UICanvas &ctx
) {
void UIRectangle::getSelfQuads(UICanvas &ctx) {
std::vector<struct UIShaderQuad> quads;
ctx.addQuad(
glm::vec4(alignment.position, alignment.position + size),
glm::vec4(position, position + size),
uv,
color,
UIShaderQuadStyle::TEXTURED,

View File

@ -9,15 +9,11 @@
namespace Dawn {
class UIRectangle final : public UIComponent {
protected:
void getSelfQuads(
const struct UIAlign alignment,
UICanvas &ctx
) override;
void getSelfQuads(UICanvas &ctx) override;
public:
struct Color color = COLOR_WHITE;
std::shared_ptr<Texture> texture = nullptr;
glm::vec2 size = glm::vec2(32, 32);
glm::vec4 uv = glm::vec4(0,0,1,1);
};
}