void UTextRenderComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	if (!TextLastUpdate.IdenticalTo(Text))
	{
		// The pointer used by the bound text has changed, however the text may still be the same - check that now
		if (!TextLastUpdate.IsDisplayStringEqualTo(Text))
		{
			// The source text has changed, so we need to update our render data
			MarkRenderStateDirty();	
		}

		// Update this even if the text is lexically identical, as it will update the pointer compared by IdenticalTo for the next Tick
		TextLastUpdate = FTextSnapshot(Text);
	}
}
UTextRenderComponent::UTextRenderComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Structure to hold one-time initialization
	struct FConstructorStatics
	{
		ConstructorHelpers::FObjectFinderOptional<UFont> Font;
		ConstructorHelpers::FObjectFinderOptional<UMaterial> TextMaterial;
		FConstructorStatics()
			: Font(TEXT("/Engine/EngineFonts/RobotoDistanceField"))
			, TextMaterial(TEXT("/Engine/EngineMaterials/DefaultTextMaterialOpaque"))
		{
		}
	};
	static FConstructorStatics ConstructorStatics;

	PrimaryComponentTick.bCanEverTick = true;
	bTickInEditor = true;

	Text = LOCTEXT("DefaultText", "Text");
	TextLastUpdate = FTextSnapshot(Text);

	Font = ConstructorStatics.Font.Get();
	TextMaterial = ConstructorStatics.TextMaterial.Get();

	SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
	TextRenderColor = FColor::White;
	XScale = 1;
	YScale = 1;
	HorizSpacingAdjust = 0;
	HorizontalAlignment = EHTA_Left;
	VerticalAlignment = EVRTA_TextBottom;

	bGenerateOverlapEvents = false;

	if( Font )
	{
		Font->ConditionalPostLoad();
		WorldSize = Font->GetMaxCharHeight();
		InvDefaultSize = 1.0f / WorldSize;
	}
	else
	{
		WorldSize = 30.0f;
		InvDefaultSize = 1.0f / 30.0f;
	}
}
void FTextFormatData::ConditionalCompile_NoLock()
{
	// IdenticalTo compares our pointer against the static empty instance, rather than checking if our text is actually empty
	// This is what we want to happen since a text using the static empty instance will never become non-empty, but an empty string might (due to a culture change, or in-editor change)
	bool bRequiresCompile = SourceType == ESourceType::Text && !SourceText.IdenticalTo(FText::GetEmpty());

	if (bRequiresCompile)
	{
		if (!CompiledTextSnapshot.IdenticalTo(SourceText))
		{
			if (!CompiledTextSnapshot.IsDisplayStringEqualTo(SourceText))
			{
				bRequiresCompile = true;
			}
			CompiledTextSnapshot = FTextSnapshot(SourceText); // Update this even if the text is lexically identical, as it will update the pointer compared by IdenticalTo for the next ConditionalCompile
		}
	}

	if (bRequiresCompile)
	{
		Compile_NoLock();
	}
}
void FTextFormatData::Compile_NoLock()
{
	LexedExpression.Reset();
	if (SourceType == ESourceType::Text)
	{
		SourceExpression = SourceText.ToString();
		CompiledTextSnapshot = FTextSnapshot(SourceText);
	}
	CompiledExpressionType = FTextFormat::EExpressionType::Simple;
	BaseFormatStringLength = 0;
	FormatArgumentEstimateMultiplier = 1;

	TValueOrError<TArray<FExpressionToken>, FExpressionError> Result = ExpressionParser::Lex(*SourceExpression, FTextFormatter::Get().GetTextFormatDefinitions());
	bool bValidExpression = Result.IsValid();
	if (bValidExpression)
	{
		LexedExpression = Result.StealValue();

		// Quickly make sure the tokens are valid (argument modifiers may only follow an argument token)
		for (int32 TokenIndex = 0; TokenIndex < LexedExpression.Num(); ++TokenIndex)
		{
			const FExpressionToken& Token = LexedExpression[TokenIndex];

			if (const auto* Literal = Token.Node.Cast<TextFormatTokens::FStringLiteral>())
			{
				BaseFormatStringLength += Literal->StringLen;
			}
			else if (auto* Escaped = Token.Node.Cast<TextFormatTokens::FEscapedCharacter>())
			{
				BaseFormatStringLength += 1;
			}
			else if (const auto* ArgumentToken = Token.Node.Cast<TextFormatTokens::FArgumentTokenSpecifier>())
			{
				CompiledExpressionType = FTextFormat::EExpressionType::Complex;

				if (LexedExpression.IsValidIndex(TokenIndex + 1))
				{
					const FExpressionToken& NextToken = LexedExpression[TokenIndex + 1];

					// Peek to see if the next token is an argument modifier
					if (const auto* ArgumentModifierToken = NextToken.Node.Cast<TextFormatTokens::FArgumentModifierTokenSpecifier>())
					{
						int32 ArgModLength = 0;
						bool ArgModUsesFormatArgs = false;
						ArgumentModifierToken->TextFormatArgumentModifier->EstimateLength(ArgModLength, ArgModUsesFormatArgs);

						BaseFormatStringLength += ArgModLength;
						FormatArgumentEstimateMultiplier += (ArgModUsesFormatArgs) ? 1 : 0;

						++TokenIndex; // walk over the argument token so that the next iteration will skip over the argument modifier
						continue;
					}
				}
			}
			else if (Token.Node.Cast<TextFormatTokens::FArgumentModifierTokenSpecifier>())
			{
				// Unexpected argument modifier token!
				const FText ErrorSourceText = FText::FromString(Token.Context.GetString());
				Result = MakeError(FExpressionError(FText::Format(LOCTEXT("UnexpectedArgumentModifierToken", "Unexpected 'argument modifier' token: {0} (token started at index {1})"), ErrorSourceText, Token.Context.GetCharacterIndex())));
				bValidExpression = false;
				break;
			}
		}
	}
	
	if (!bValidExpression)
	{
		LexedExpression.Reset();
		CompiledExpressionType = FTextFormat::EExpressionType::Invalid;
		UE_LOG(LogTextFormatter, Warning, TEXT("Failed to compile text format string '%s': %s"), *SourceExpression, *Result.GetError().Text.ToString());
	}
}
void UTextRenderComponent::K2_SetText(const FText& Value)
{
	Text = Value;
	TextLastUpdate = FTextSnapshot(Text); // update this immediately as we're calling MarkRenderStateDirty ourselves
	MarkRenderStateDirty();
}