Dawn/src/dawn/scene/components/ui/UIComponent.cpp

198 lines
4.7 KiB
C++

// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "UIComponent.hpp"
using namespace Dawn;
UIComponent::UIComponent(SceneItem *item) :
SceneItemComponent(item),
alignment(glm::vec4(0, 0, 128, 128)),
alignUnitLeft(UI_COMPONENT_ALIGN_UNIT_SCALE),
alignUnitTop(UI_COMPONENT_ALIGN_UNIT_SCALE),
alignUnitRight(UI_COMPONENT_ALIGN_UNIT_SCALE),
alignUnitBottom(UI_COMPONENT_ALIGN_UNIT_SCALE),
alignX(UI_COMPONENT_ALIGN_START),
alignY(UI_COMPONENT_ALIGN_START),
alignmentNeedsUpdating(true)
{
}
UIComponentDimensional * UIComponent::getParentDimensional() {
auto parent = this->transform->getParent();
if(parent == nullptr) return nullptr;
auto dimensional = parent->item->getComponent<UIComponentDimensional>();
assertNotNull(dimensional);
return dimensional;
}
void UIComponent::updateAlignment() {
if(!this->alignmentNeedsUpdating) return;
auto align = (glm::vec4)this->alignment;
auto dimensional = this->getParentDimensional();
auto translate = this->transform->getLocalPosition();
assertNotNull(dimensional);
float_t parentWidth, parentHeight;
parentWidth = dimensional->getWidth();
parentHeight = dimensional->getHeight();
UIComponent::calculateDimensions(
this->alignX,
this->alignUnitLeft,
this->alignUnitRight,
&translate.x,
&this->width,
parentWidth,
this->getContentWidth(),
glm::vec2(align[0], align[2])
);
UIComponent::calculateDimensions(
this->alignY,
this->alignUnitTop,
this->alignUnitBottom,
&translate.y,
&this->height,
parentHeight,
this->getContentHeight(),
glm::vec2(align[1], align[3])
);
this->transform->setLocalPosition(translate);
this->alignmentNeedsUpdating = false;
this->eventAlignmentUpdated.invoke();
}
float_t UIComponent::calculateAlignmentValue(
float_t alignmentValue,
float_t parentSize,
enum UIComponentAlignUnit unit
) {
if(unit == UI_COMPONENT_ALIGN_UNIT_SCALE) return alignmentValue;
return (alignmentValue / 100.0f) * parentSize;
}
void UIComponent::calculateDimensions(
enum UIComponentAlign align,
enum UIComponentAlignUnit unitStart,
enum UIComponentAlignUnit unitEnd,
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: {
// For stretch:
// Alignment 0 becomes START offset, Alignment 1 becomes END Offset
*position = UIComponent::calculateAlignmentValue(
alignment[0],
outerSize,
unitStart
);
*size = outerSize - (*position + UIComponent::calculateAlignmentValue(
alignment[1],
outerSize,
unitEnd
));
break;
}
case UI_COMPONENT_ALIGN_START: {
// For start:
// Alignment 0 becomes START offset, Alignment 1 becomes SIZE
*position = UIComponent::calculateAlignmentValue(
alignment[0],
outerSize,
unitStart
);
*size = UIComponent::calculateAlignmentValue(
alignment[1],
outerSize,
unitEnd
);
break;
}
case UI_COMPONENT_ALIGN_MIDDLE: {
*size = mathMax<float_t>(
innerSize,
UIComponent::calculateAlignmentValue(
alignment[1],
outerSize,
unitEnd
)
);
*position = (outerSize / 2.0f) - (*size / 2.0f) + UIComponent::calculateAlignmentValue(
alignment[0],
outerSize,
unitStart
);
break;
}
case UI_COMPONENT_ALIGN_END: {
*size = UIComponent::calculateAlignmentValue(
alignment[0],
outerSize,
unitStart
);
*position = outerSize - *size - UIComponent::calculateAlignmentValue(
alignment[1],
outerSize,
unitEnd
);
break;
}
default:
assertUnreachable();
break;
}
}
UICanvas * UIComponent::getCanvas() {
// TODO: Cache this on first hit.
auto parent = this->transform->getParent();
while(parent != nullptr) {
auto canvas = parent->item->getComponent<UICanvas>();
if(canvas != nullptr) return canvas;
parent = parent->getParent();
}
return nullptr;
}
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)();
}