// 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(); 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( 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(); 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(); auto itChild = child.begin(); while(itChild != child.end()) { (*itChild)->alignmentNeedsUpdating = true; ++itChild; } }, alignmentNeedsUpdating)(); }