Broken auto sizing

This commit is contained in:
2023-12-22 23:29:03 -06:00
parent 92f8b17db6
commit 9822b1a57f
26 changed files with 512 additions and 168 deletions

View File

@ -6,11 +6,11 @@
target_sources(${DAWN_TARGET_NAME}
PRIVATE
UIAlignableElement.cpp
UISubAlignableElement.cpp
UIElement.cpp
UIRectangle.cpp
UILabel.cpp
UIMenu.cpp
)
# Subdirs
add_subdirectory(container)
add_subdirectory(container)
add_subdirectory(elements)

View File

@ -26,8 +26,11 @@ void UIAlignableElement::updateSelfAlignment(
const enum UIAlignmentUnit unit,
const float_t alignment,
const float_t parentSize,
const float_t ratioSize
const float_t ratioSize,
const float_t selfSize
) {
if(alignment == UI_ALIGN_AUTO) return selfSize;
switch(unit) {
case UIAlignmentUnit::PIXEL:
return alignment;
@ -55,67 +58,76 @@ void UIAlignableElement::updateSelfAlignment(
const float_t alignment1,
const float_t parentSize,
const float_t ratioSize,
float_t &selfPosition,
float_t &selfSize
const float_t selfSize,
float_t &outPosition,
float_t &outSize
) {
switch(type) {
case UIAlignmentType::START:
selfPosition = valueAxis(
outPosition = valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
ratioSize,
selfSize
);
selfSize = valueAxis(
outSize = valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
ratioSize,
selfSize
);
break;
case UIAlignmentType::MIDDLE:
selfSize = valueAxis(
outSize = valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
ratioSize,
selfSize
);
selfPosition = (parentSize / 2.0f) - (selfSize / 2.0f) + valueAxis(
outPosition = (parentSize / 2.0f) - (selfSize / 2.0f) + valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
ratioSize,
selfSize
);
break;
case UIAlignmentType::END:
selfSize = valueAxis(
outSize = valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
ratioSize,
selfSize
);
selfPosition = parentSize - selfSize - valueAxis(
outPosition = parentSize - outSize - valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
ratioSize,
selfSize
);
break;
case UIAlignmentType::STRETCH:
selfPosition = valueAxis(
outPosition = valueAxis(
unit0,
alignment0,
parentSize,
ratioSize
ratioSize,
selfSize
);
selfSize = parentSize - (selfPosition + valueAxis(
outSize = parentSize - (outPosition + valueAxis(
unit1,
alignment1,
parentSize,
ratioSize
ratioSize,
selfSize
));
break;
@ -140,6 +152,7 @@ void UIAlignableElement::updateSelfAlignment(
align[3],
pSize.y,
0,
this->getSelfHeight(),
position.y,
size.y
);
@ -151,6 +164,7 @@ void UIAlignableElement::updateSelfAlignment(
align[2],
pSize.x,
size.y,
this->getSelfWidth(),
position.x,
size.x
);
@ -163,6 +177,7 @@ void UIAlignableElement::updateSelfAlignment(
align[2],
pSize.x,
0,
this->getSelfWidth(),
position.x,
size.x
);
@ -174,6 +189,7 @@ void UIAlignableElement::updateSelfAlignment(
align[3],
pSize.y,
size.x,
this->getSelfHeight(),
position.y,
size.y
);
@ -183,6 +199,14 @@ void UIAlignableElement::updateSelfAlignment(
this->eventAlignmentUpdated(position, size);
}
float_t UIAlignableElement::getSelfWidth() {
return size.x;
}
float_t UIAlignableElement::getSelfHeight() {
return size.y;
}
void UIAlignableElement::updateAlignment(
const glm::vec2 pPos,
const glm::vec2 pSize,

View File

@ -6,6 +6,8 @@
#pragma once
#include "ui/UIElement.hpp"
#define UI_ALIGN_AUTO -1
namespace Dawn {
enum class UIAlignmentType {
START,
@ -41,9 +43,9 @@ namespace Dawn {
public:
// Primary alignment controls
glm::vec4 align = glm::vec4(0, 0, 0, 0);
enum UIAlignmentType alignX = UIAlignmentType::STRETCH;
enum UIAlignmentType alignY = UIAlignmentType::STRETCH;
glm::vec4 align = glm::vec4(0, 0, UI_ALIGN_AUTO, UI_ALIGN_AUTO);
enum UIAlignmentType alignX = UIAlignmentType::START;
enum UIAlignmentType alignY = UIAlignmentType::START;
enum UIAlignmentUnit alignUnit[4];
std::function<
@ -55,6 +57,8 @@ namespace Dawn {
*/
UIAlignableElement();
float_t getSelfWidth() override;
float_t getSelfHeight() override;
void updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,

View File

@ -16,6 +16,22 @@ void UIElement::getSelfQuads(UICanvas &ctx) {
//Do nothing
}
float_t UIElement::getSelfWidth() {
return 0.0f;
}
float_t UIElement::getSelfHeight() {
return 0.0f;
}
float_t UIElement::getWidth() {
return this->getSelfWidth();
}
float_t UIElement::getHeight() {
return this->getSelfHeight();
}
void UIElement::getQuads(UICanvas &ctx) {
this->getSelfQuads(ctx);

View File

@ -20,6 +20,36 @@ namespace Dawn {
virtual void getSelfQuads(UICanvas &ctx);
public:
/**
* Overrided method by the UI Element that requests the minimum
* width of the content.
*
* @return The minimum width of the content.
*/
virtual float_t getContentWidth();
/**
* Overrided method by the UI Element that requests the minimum
* height of the content.
*
* @return The minimum height of the content.
*/
virtual float_t getContentHeight();
/**
* Returns the width of this component.
*
* @return The width of this component.
*/
virtual float_t getWidth();
/**
* Returns the height of this component.
*
* @return The height of this component.
*/
virtual float_t getHeight();
/**
* Virtual method overridden by the UIElement to get the children of
* this component.

View File

@ -7,10 +7,6 @@
using namespace Dawn;
std::vector<std::shared_ptr<UIElement>> UIMenu::getChildren() {
return children;
}
void UIMenu::setPosition(int32_t x, int32_t y) {
assertTrue(x >= 0, "X position must be greater than or equal to 0.");
assertTrue(y >= 0, "Y position must be greater than or equal to 0.");

View File

@ -16,9 +16,6 @@ namespace Dawn {
public:
Event<int32_t, int32_t> eventPositionChanged;
std::vector<std::shared_ptr<UIElement>> children;
std::vector<std::shared_ptr<UIElement>> getChildren() override;
/**
* Sets the position of this menu.

View File

@ -0,0 +1,68 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "UISubAlignableElement.hpp"
using namespace Dawn;
void UISubAlignableElement::updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,
const float_t canvasScale
) {
UIAlignableElement::updateAlignment(parentPosition, parentSize, canvasScale);
switch(this->subAlignX) {
case UISubAlignment::START:
this->subAlignedPosition.x = this->position.x;
break;
case UISubAlignment::MIDDLE:
this->subAlignedPosition.x = (
this->position.x +
(this->size.x / 2.0f) -
(this->getSelfWidth() / 2.0f)
);
break;
case UISubAlignment::END:
this->subAlignedPosition.x = (
this->position.x +
this->size.x -
this->getSelfWidth()
);
break;
default:
assertUnreachable("Unknown UISubAlignment!");
}
switch(this->subAlignY) {
case UISubAlignment::START:
this->subAlignedPosition.y = this->position.y;
break;
case UISubAlignment::MIDDLE:
this->subAlignedPosition.y = (
this->position.y +
(this->size.y / 2.0f) -
(this->getSelfHeight() / 2.0f)
);
break;
case UISubAlignment::END:
this->subAlignedPosition.y = (
this->position.y +
this->size.y -
this->getSelfHeight()
);
break;
default:
assertUnreachable("Unknown UISubAlignment!");
}
this->eventSubAlignmentUpdated.emit(this->subAlignedPosition);
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "ui/UIAlignableElement.hpp"
namespace Dawn {
enum class UISubAlignment {
START,
MIDDLE,
END
};
class UISubAlignableElement : public UIAlignableElement {
protected:
glm::vec2 subAlignedPosition;
public:
Event<glm::vec2> eventSubAlignmentUpdated;
enum UISubAlignment subAlignX = UISubAlignment::START;
enum UISubAlignment subAlignY = UISubAlignment::START;
void updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,
const float_t canvasScale
) override;
};
}

View File

@ -5,6 +5,8 @@
target_sources(${DAWN_TARGET_NAME}
PRIVATE
UIGroupContainer.cpp
UIColumnContainer.cpp
UIContainer.cpp
UIPaddingContainer.cpp
UIRowContainer.cpp
)

View File

@ -0,0 +1,36 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "assert/assert.hpp"
#include "UIColumnContainer.hpp"
using namespace Dawn;
void UIColumnContainer::updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,
const float_t canvasScale
) {
this->updateSelfAlignment(parentPosition, parentSize, canvasScale);
// Now we have our dimensions, divide evenly
auto children = this->getChildren();
float_t x = 0.0f;
float_t xPiece = this->size.x / (float_t)children.size();
// Update all children
for(auto &child : children) {
child->updateAlignment(
this->position + glm::vec2(x, 0),
glm::vec2(
xPiece,
this->size.y
),
canvasScale
);
x += xPiece;
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "ui/container/UIContainer.hpp"
namespace Dawn {
class UIColumnContainer final : public UIContainer {
public:
void updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,
const float_t canvasScale
) override;
};
}

View File

@ -0,0 +1,47 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "assert/assert.hpp"
#include "UIContainer.hpp"
using namespace Dawn;
std::vector<std::shared_ptr<UIElement>> UIContainer::getChildren() {
return this->children;
}
float_t UIContainer::getSelfWidth() {
float_t width = 0;
auto children = this->getChildren();
for(auto child : children) {
width = Math::max(width, child->getWidth());
}
return width;
}
float_t UIContainer::getSelfHeight() {
float_t height = 0;
auto children = this->getChildren();
for(auto child : children) {
height = Math::max(height, child->getHeight());
}
return height;
}
void UIContainer::appendChild(std::shared_ptr<UIElement> child) {
assertNotNull(child, "Cannot append a null child!");
this->children.push_back(child);
}
void UIContainer::removeChild(std::shared_ptr<UIElement> child) {
assertNotNull(child, "Cannot remove a null child!");
auto it = std::find(this->children.begin(), this->children.end(), child);
if(it == this->children.end()) return;
this->children.erase(it);
}
void UIContainer::clearChildren() {
this->children.clear();
}

View File

@ -8,7 +8,32 @@
namespace Dawn {
class UIContainer : public UIAlignableElement {
private:
std::vector<std::shared_ptr<UIElement>> children;
public:
std::vector<std::shared_ptr<UIElement>> getChildren() override;
float_t getSelfWidth() override;
float_t getSelfHeight() override;
/**
* Appends a child to this container.
*
* @param child Child to append.
*/
void appendChild(std::shared_ptr<UIElement> child);
/**
* Removes a child from this container.
*
* @param child Child to remove.
*/
void removeChild(std::shared_ptr<UIElement> child);
/**
* Removes all children from this container.
*/
void clearChildren();
};
}

View File

@ -1,28 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "UIGroupContainer.hpp"
using namespace Dawn;
std::vector<std::shared_ptr<UIElement>> UIGroupContainer::getChildren() {
return this->children;
}
void UIGroupContainer::appendChild(std::shared_ptr<UIElement> child) {
assertNotNull(child, "Cannot append a null child!");
this->children.push_back(child);
}
void UIGroupContainer::removeChild(std::shared_ptr<UIElement> child) {
assertNotNull(child, "Cannot remove a null child!");
auto it = std::find(this->children.begin(), this->children.end(), child);
if(it == this->children.end()) return;
this->children.erase(it);
}
void UIGroupContainer::clearChildren() {
this->children.clear();
}

View File

@ -1,36 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "ui/container/UIContainer.hpp"
namespace Dawn {
class UIGroupContainer : public UIContainer {
private:
std::vector<std::shared_ptr<UIElement>> children;
public:
std::vector<std::shared_ptr<UIElement>> getChildren() override;
/**
* Appends a child to this container.
*
* @param child Child to append.
*/
void appendChild(std::shared_ptr<UIElement> child);
/**
* Removes a child from this container.
*
* @param child Child to remove.
*/
void removeChild(std::shared_ptr<UIElement> child);
/**
* Removes all children from this container.
*/
void clearChildren();
};
}

View File

@ -0,0 +1,22 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "UIPaddingContainer.hpp"
using namespace Dawn;
void UIPaddingContainer::updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,
const float_t canvasScale
) {
glm::vec2 childPosition = parentPosition + glm::vec2(padding.x, padding.y);
glm::vec2 childSize = parentSize - glm::vec2(padding.x + padding.z, padding.y + padding.w);
auto children = getChildren();
for(auto &child : children) {
child->updateAlignment(childPosition, childSize, canvasScale);
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "ui/container/UIContainer.hpp"
namespace Dawn {
class UIPaddingContainer final : public UIContainer {
public:
glm::vec4 padding = { 0, 0, 0, 0 };
void updateAlignment(
const glm::vec2 parentPosition,
const glm::vec2 parentSize,
const float_t canvasScale
) override;
};
}

View File

@ -3,29 +3,24 @@
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "assert/assert.hpp"
#include "UIRowContainer.hpp"
using namespace Dawn;
std::vector<std::shared_ptr<UIElement>> UIRowContainer::getChildren() {
return this->children;
float_t UIRowContainer::getSelfWidth() {
float_t width = 0.0f;
for(auto &child : this->getChildren()) {
width += child->getWidth();
}
return width;
}
void UIRowContainer::appendChild(std::shared_ptr<UIElement> child) {
assertNotNull(child, "Cannot append a null child!");
this->children.push_back(child);
}
void UIRowContainer::removeChild(std::shared_ptr<UIElement> child) {
assertNotNull(child, "Cannot remove a null child!");
auto it = std::find(this->children.begin(), this->children.end(), child);
if(it == this->children.end()) return;
this->children.erase(it);
}
void UIRowContainer::clearChildren() {
this->children.clear();
float_t UIRowContainer::getSelfHeight() {
float_t height = 0.0f;
for(auto &child : this->getChildren()) {
height = std::max(height, child->getHeight());
}
return height;
}
void UIRowContainer::updateAlignment(
@ -42,7 +37,7 @@ void UIRowContainer::updateAlignment(
float_t yPiece = this->size.y / (float_t)children.size();
// Update all children
for(auto &child : this->children) {
for(auto &child : children) {
child->updateAlignment(
this->position + glm::vec2(0, y),
glm::vec2(

View File

@ -8,30 +8,9 @@
namespace Dawn {
class UIRowContainer final : public UIContainer {
private:
std::vector<std::shared_ptr<UIElement>> children;
public:
std::vector<std::shared_ptr<UIElement>> getChildren() override;
/**
* Appends a child to this container.
*
* @param child Child to append.
*/
void appendChild(std::shared_ptr<UIElement> child);
/**
* Removes a child from this container.
*
* @param child Child to remove.
*/
void removeChild(std::shared_ptr<UIElement> child);
/**
* Removes all children from this container.
*/
void clearChildren();
float_t getSelfWidth() override;
float_t getSelfHeight() override;
void updateAlignment(
const glm::vec2 parentPosition,

View File

@ -0,0 +1,10 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
UIRectangle.cpp
UILabel.cpp
)

View File

@ -56,7 +56,7 @@ void UILabel::getSelfQuads(UICanvas &ctx) {
lastCharWasSpace = false;
}
} else if(pos.x + info.size.x > position.x + size.x) {
} else if(pos.x + info.size.x > subAlignedPosition.x + size.x) {
// Not word wrap, but instead just overflow characters.
pos.x = 0;
pos.y += this->texture->fontSize;
@ -64,10 +64,10 @@ void UILabel::getSelfQuads(UICanvas &ctx) {
ctx.addQuad(
{
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
subAlignedPosition.x + pos.x + info.offset.x,
subAlignedPosition.y + pos.y + info.offset.y,
subAlignedPosition.x + pos.x + info.size.x + info.offset.x,
subAlignedPosition.y + pos.y + info.size.y + info.offset.y
},
{
info.quad.x,
@ -83,6 +83,78 @@ void UILabel::getSelfQuads(UICanvas &ctx) {
}
}
float_t UILabel::getContentWidth() {
if(this->texture == nullptr || this->text.empty()) return 0.0f;
float_t lineWidth = 0.0f;
float_t width = 0.0f;
for(wchar_t c : text) {
if(c == L'\n') {
width = Math::max<float_t>(width, lineWidth);
lineWidth = 0.0f;
continue;
}
auto info = texture->getCharacterData(c);
lineWidth += info.advance.x;
if(lineWidth >= size.x) return size.x;
}
return Math::max<float_t>(width, lineWidth);
}
float_t UILabel::getContentHeight() {
if(this->texture == nullptr || this->text.empty()) return 0.0f;
float_t height = this->texture->fontSize;
float_t lineWidth = 0.0f;
bool_t lastCharWasSpace = false;
for(wchar_t c : text) {
if(c == L'\n') {
height += this->texture->fontSize;
continue;
}
auto info = texture->getCharacterData(c);
if(c == L' ') {
lineWidth += info.advance.x;
lastCharWasSpace = true;
continue;
}
if(wordWrap) {
if(lastCharWasSpace) {
// Scan ahead to next space
float_t wordWidth = lineWidth;// Start at current position and scan ahead.
for(size_t j = 0; 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) {
height += this->texture->fontSize;
lineWidth = 0.0f;
break;
}
}
lastCharWasSpace = false;
}
} else if(lineWidth + info.size.x > size.x) {
height += this->texture->fontSize;
lineWidth = 0.0f;
}
}
return height;
}
std::shared_ptr<TrueTypeTexture> UILabel::getFont() {
return this->texture;
}

View File

@ -4,11 +4,11 @@
// https://opensource.org/licenses/MIT
#pragma once
#include "ui/UIAlignableElement.hpp"
#include "ui/UISubAlignableElement.hpp"
#include "display/font/TrueTypeTexture.hpp"
namespace Dawn {
class UILabel final : public UIAlignableElement {
class UILabel final : public UISubAlignableElement {
private:
std::shared_ptr<TrueTypeTexture> texture = nullptr;
std::wstring text = L"Hello World";
@ -20,6 +20,9 @@ namespace Dawn {
bool_t wordWrap = true;
struct Color color = COLOR_WHITE;
float_t getContentWidth() override;
float_t getContentHeight() override;
/**
* Returns the font used for this label.
*

View File

@ -12,11 +12,11 @@
#include "display/mesh/QuadMesh.hpp"
#include "component/ui/UICanvas.hpp"
#include "ui/UIRectangle.hpp"
#include "ui/UILabel.hpp"
#include "ui/elements/UIRectangle.hpp"
#include "ui/elements/UILabel.hpp"
#include "ui/UIMenu.hpp"
#include "ui/container/UIRowContainer.hpp"
#include "ui/container/UIGroupContainer.hpp"
#include "ui/container/UIPaddingContainer.hpp"
#include <ft2build.h>
#include FT_FREETYPE_H
@ -58,40 +58,52 @@ void Dawn::helloWorldScene(Scene &s) {
auto uiCanvasItem = s.createSceneItem();
auto uiCanvas = uiCanvasItem->addComponent<UICanvas>();
auto container = std::make_shared<UIGroupContainer>();
container->align = { 32, 32, 300, 250 };
auto container = std::make_shared<UIContainer>();
container->align = { 32, 32, UI_ALIGN_AUTO, UI_ALIGN_AUTO };
container->alignX = UIAlignmentType::START;
container->alignY = UIAlignmentType::START;
uiCanvas->addElement(container);
auto whatever = std::make_shared<UIContainer>();
whatever->align = { 0, 0, 32, 32 };
whatever->alignX = UIAlignmentType::START;
whatever->alignY = UIAlignmentType::START;
container->appendChild(whatever);
auto rect = std::make_shared<UIRectangle>();
rect->color = COLOR_MAGENTA;
rect->align = { 0, 0, 0, 0 };
rect->alignX = UIAlignmentType::STRETCH;
rect->alignY = UIAlignmentType::STRETCH;
// rectPink->align = { 0, 0, 32, 32 };
container->appendChild(rect);
return;
auto menu = std::make_shared<UIMenu>();
menu->setSize(1, 4);
container->appendChild(menu);
auto rowContainer = std::make_shared<UIRowContainer>();
menu->children.push_back(rowContainer);
container->appendChild(rowContainer);
auto label0 = std::make_shared<UILabel>();
label0->setText(L"New Game");
label0->setFont(texture);
rowContainer->appendChild(label0);
std::vector<std::wstring> labels = {
L"New Game",
L"Load Game",
L"Options",
L"Exit"
};
auto label1 = std::make_shared<UILabel>();
label1->setText(L"Load Game");
label1->setFont(texture);
rowContainer->appendChild(label1);
for(auto &label : labels) {
auto padding = std::make_shared<UIPaddingContainer>();
padding->padding = { 8, 8, 8, 8 };
auto label2 = std::make_shared<UILabel>();
label2->setText(L"Options");
label2->setFont(texture);
rowContainer->appendChild(label2);
auto label3 = std::make_shared<UILabel>();
label3->setText(L"Exit");
label3->setFont(texture);
rowContainer->appendChild(label3);
auto labelElement = std::make_shared<UILabel>();
labelElement->setText(label);
labelElement->setFont(texture);
// labelElement->subAlignX = UISubAlignment::MIDDLE;
// labelElement->subAlignY = UISubAlignment::MIDDLE;
padding->appendChild(labelElement);
rowContainer->appendChild(padding);
}
}