Add text-transform support for UIRichText and UITextSpan.

This commit is contained in:
Martín Lucas Golini
2026-05-22 00:41:30 -03:00
parent 0c742d6431
commit ba3576aa8c
5 changed files with 89 additions and 3 deletions

View File

@@ -2,6 +2,7 @@
#define EE_UI_UIRICHTEXT_HPP
#include <eepp/graphics/richtext.hpp>
#include <eepp/graphics/texttransform.hpp>
#include <eepp/ui/uihtmlwidget.hpp>
#include <eepp/ui/uilayout.hpp>
@@ -141,6 +142,10 @@ class EE_API UIRichText : public UIHTMLWidget {
void setTextSelectionEnabled( bool active );
const TextTransform::Value& getTextTransform() const;
void setTextTransform( const TextTransform::Value& textTransform );
const Color& getSelectionBackColor() const;
void setSelectionBackColor( const Color& color );
@@ -169,6 +174,7 @@ class EE_API UIRichText : public UIHTMLWidget {
mutable Float mTextIndentPxCache{ 0 };
mutable bool mTextIndentPxDirty{ true };
WhiteSpaceCollapse mWhiteSpaceCollapse{ WhiteSpaceCollapse::Collapse };
TextTransform::Value mTextTransform{ TextTransform::None };
explicit UIRichText( const std::string& tag = "richtext" );

View File

@@ -117,6 +117,7 @@ class EE_API UITextSpan : public UIRichText {
StyleStateFontShadowColor = 1 << 6,
StyleStateFontShadowOffset = 1 << 7,
StyleStateFontBackgroundColor = 1 << 8,
StyleStateTextTransform = 1 << 9,
StyleStateAll = 0xFFFFFFFF
};
@@ -130,6 +131,8 @@ class EE_API UITextSpan : public UIRichText {
bool hasFontShadowOffset() const;
bool hasFontBackgroundColor() const;
bool hasTextTransform() const;
SpanHitBoxes& getHitBoxes();
const SpanHitBoxes& getHitBoxes() const;

View File

@@ -410,6 +410,9 @@ bool UIRichText::applyProperty( const StyleSheetProperty& attribute ) {
case PropertyId::WhiteSpaceCollapse:
setWhiteSpaceCollapse( toWhiteSpaceCollapse( attribute.value() ) );
break;
case PropertyId::TextTransform:
setTextTransform( TextTransform::fromString( attribute.asString() ) );
break;
default:
return UIHTMLWidget::applyProperty( attribute );
}
@@ -461,6 +464,8 @@ std::string UIRichText::getPropertyString( const PropertyDefinition* propertyDef
return mTextIndentEq.empty() ? "0" : mTextIndentEq;
case PropertyId::WhiteSpaceCollapse:
return fromWhiteSpaceCollapse( mWhiteSpaceCollapse );
case PropertyId::TextTransform:
return TextTransform::toString( getTextTransform() );
default:
return UIHTMLWidget::getPropertyString( propertyDef, propertyIndex );
}
@@ -475,7 +480,8 @@ std::vector<PropertyId> UIRichText::getPropertiesImplemented() const {
PropertyId::TextAlign, PropertyId::SelectionColor,
PropertyId::SelectionBackColor, PropertyId::TextSelection,
PropertyId::TextDecoration, PropertyId::LineHeight,
PropertyId::TextIndent, PropertyId::WhiteSpaceCollapse };
PropertyId::TextIndent, PropertyId::WhiteSpaceCollapse,
PropertyId::TextTransform };
props.insert( props.end(), local.begin(), local.end() );
return props;
}
@@ -733,6 +739,18 @@ Float UIRichText::getTextIndentPx() const {
return mTextIndentPxCache;
}
const TextTransform::Value& UIRichText::getTextTransform() const {
return mTextTransform;
}
void UIRichText::setTextTransform( const TextTransform::Value& textTransform ) {
if ( textTransform != mTextTransform ) {
mTextTransform = textTransform;
notifyLayoutAttrChange();
notifyLayoutAttrChangeParent();
}
}
void UIRichText::loadFromXmlNode( const pugi::xml_node& node ) {
beginAttributesTransaction();
@@ -1060,11 +1078,46 @@ void UIRichText::rebuildRichText( UILayout* container, RichText& richText, Intri
richText.setMaxWidth( 0.f ); // Let it grow unbounded to query text bounds later
}
auto getEffectiveTextTransform = []( Node* node ) -> TextTransform::Value {
while ( node ) {
if ( node->isType( UI_TYPE_RICHTEXT ) || node->isType( UI_TYPE_TEXTSPAN ) ) {
auto tt = node->asType<UIRichText>()->getTextTransform();
if ( tt != TextTransform::None )
return tt;
}
node = node->getParent();
}
return TextTransform::None;
};
auto applyTextTransform = []( String& text, TextTransform::Value tt ) {
switch ( tt ) {
case TextTransform::LowerCase:
text = text.toLower();
break;
case TextTransform::UpperCase:
text = text.toUpper();
break;
case TextTransform::Capitalize:
text = text.capitalize();
break;
default:
break;
}
};
if ( container->isType( UI_TYPE_TEXTSPAN ) ) {
UITextSpan* selfSpan = container->asType<UITextSpan>();
if ( !selfSpan->getText().empty() && !selfSpan->isInline() &&
NULL != selfSpan->getFontStyleConfig().Font ) {
String::View selfText = selfSpan->getText().view();
String transformed;
auto tt = getEffectiveTextTransform( selfSpan );
if ( tt != TextTransform::None ) {
transformed = selfText;
applyTextTransform( transformed, tt );
selfText = transformed.view();
}
FontStyleConfig style = selfSpan->getFontStyleConfig();
style.BackgroundColor = Color::Transparent;
richText.addSpan( selfText, style, Rectf::Zero, Rectf::Zero, 0,
@@ -1135,6 +1188,12 @@ void UIRichText::rebuildRichText( UILayout* container, RichText& richText, Intri
if ( shouldCollapse && lastSpanEndsWithSpace && !text.empty() && text[0] == ' ' )
text = text.substr( 1 );
{
auto tt = getEffectiveTextTransform( textNode );
if ( tt != TextTransform::None )
applyTextTransform( text, tt );
}
if ( text.empty() ) {
textNode->setLayoutCharCount( 0 );
return;
@@ -1229,6 +1288,14 @@ void UIRichText::rebuildRichText( UILayout* container, RichText& richText, Intri
spanText[0] == ' ' )
spanText = spanText.substr( 1 );
String transformed;
auto tt = getEffectiveTextTransform( span );
if ( tt != TextTransform::None ) {
transformed = spanText;
applyTextTransform( transformed, tt );
spanText = transformed.view();
}
if ( !spanText.empty() ) {
richText.addInlineText( spanText, span->getFontStyleConfig(), Rectf::Zero,
Rectf::Zero, 0, {} );

View File

@@ -126,6 +126,9 @@ bool UITextSpan::applyProperty( const StyleSheetProperty& attribute ) {
case PropertyId::TextDecoration:
setTextDecoration( attribute.asTextDecoration() );
break;
case PropertyId::TextTransform:
setTextTransform( TextTransform::fromString( attribute.asString() ) );
break;
default:
return UIRichText::applyProperty( attribute );
}
@@ -163,6 +166,8 @@ std::string UITextSpan::getPropertyString( const PropertyDefinition* propertyDef
return getOutlineColor().toHexString();
case PropertyId::TextDecoration:
return Text::styleFlagToString( getTextDecoration() );
case PropertyId::TextTransform:
return TextTransform::toString( getTextTransform() );
default:
return UIRichText::getPropertyString( propertyDef, propertyIndex );
}
@@ -180,7 +185,8 @@ std::vector<PropertyId> UITextSpan::getPropertiesImplemented() const {
PropertyId::TextShadowOffset,
PropertyId::TextStrokeWidth,
PropertyId::TextStrokeColor,
PropertyId::TextDecoration };
PropertyId::TextDecoration,
PropertyId::TextTransform };
props.insert( props.end(), local.begin(), local.end() );
return props;
}
@@ -566,6 +572,10 @@ bool UITextSpan::hasFontBackgroundColor() const {
return 0 != ( mStyleState & StyleStateFontBackgroundColor );
}
bool UITextSpan::hasTextTransform() const {
return 0 != ( mStyleState & StyleStateTextTransform );
}
SpanHitBoxes& UITextSpan::getHitBoxes() {
return mHitBoxes;
}