First pass UI Alignment
This commit is contained in:
@ -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
|
||||
};
|
||||
}
|
@ -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;
|
||||
};
|
||||
}
|
@ -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,
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user