// Copyright (c) 2023 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "UIAlignableElement.hpp" using namespace Dawn; UIAlignableElement::UIAlignableElement() { 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 UIAlignableElement::updateSelfAlignment( 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, const float_t contentSize ) { if(alignment == UI_ALIGN_SIZE_AUTO) return contentSize; 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, const float_t contentSize, float_t &outPosition, float_t &outSize ) { switch(type) { case UIAlignmentType::START: outPosition = valueAxis( unit0, alignment0, parentSize, ratioSize, contentSize ); outSize = valueAxis( unit1, alignment1, parentSize, ratioSize, contentSize ); break; case UIAlignmentType::MIDDLE: outSize = valueAxis( unit1, alignment1, parentSize, ratioSize, contentSize ); outPosition = (parentSize / 2.0f) - (contentSize / 2.0f) + valueAxis( unit0, alignment0, parentSize, ratioSize, contentSize ); break; case UIAlignmentType::END: outSize = valueAxis( unit0, alignment0, parentSize, ratioSize, contentSize ); outPosition = parentSize - outSize - valueAxis( unit1, alignment1, parentSize, ratioSize, contentSize ); break; case UIAlignmentType::STRETCH: outPosition = valueAxis( unit0, alignment0, parentSize, ratioSize, contentSize ); outSize = parentSize - (outPosition + valueAxis( unit1, alignment1, parentSize, ratioSize, contentSize )); 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, this->getContentHeight(), position.y, size.y ); alignAxis( alignX, alignUnit[0], alignUnit[2], align[0], align[2], pSize.x, size.y, this->getContentWidth(), position.x, size.x ); } else { alignAxis( alignX, alignUnit[0], alignUnit[2], align[0], align[2], pSize.x, 0, this->getContentWidth(), position.x, size.x ); alignAxis( alignY, alignUnit[1], alignUnit[3], align[1], align[3], pSize.y, size.x, this->getContentHeight(), position.y, size.y ); } this->position += pPos; this->eventAlignmentUpdated(position, size); } bool_t UIAlignableElement::hasExplicitWidth() { if(size.x == 0.0f) return false; if( (alignX == UIAlignmentType::STRETCH) || (alignX == UIAlignmentType::END) ) { return align[0] != UI_ALIGN_SIZE_AUTO; } return align[2] != UI_ALIGN_SIZE_AUTO; } bool_t UIAlignableElement::hasExplicitHeight() { if(size.y == 0.0f) return false; if( (alignY == UIAlignmentType::STRETCH) || (alignY == UIAlignmentType::END) ) { return align[1] != UI_ALIGN_SIZE_AUTO; } return align[3] != UI_ALIGN_SIZE_AUTO; } float_t UIAlignableElement::getWidth() { if(hasExplicitWidth()) return size.x; return getContentWidth(); } float_t UIAlignableElement::getHeight() { if(hasExplicitHeight()) return size.y; return getContentHeight(); } void UIAlignableElement::updateAlignment( const glm::vec2 pPos, const glm::vec2 pSize, const float_t canvasScale ) { this->updateSelfAlignment(pPos, pSize, canvasScale); // Now update children alignment auto children = getChildren(); for(auto &c : children) { c->updateAlignment(this->position, this->size, canvasScale); } }