You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
With Ellipsis, all lines that are fully or partially visible will be displayed, the drawback of that being that if the "..." are displayed at the end of a line from which the lower part is clipped, the "..." are not visible. With LineEllipsis, only lines that are fully visible vertically will be displayed. So if a line would be clipped at its lower part, the whole line will be culled, and the "..." will be displayed at the end of the last fully visible line, preventing the case where the "..." might not be seen. #jira UE-195323 #tests Added new tests in the FontOverflow functionnal test from EngineTest. Visually checked in UnrealEditor that there were no regressions. #rb Patrick.Boutot #virtualized [CL 30665613 by yohann dossantos in ue5-main branch]
317 lines
13 KiB
C++
317 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Framework/Text/SlateTextRun.h"
|
|
#include "Rendering/DrawElements.h"
|
|
#include "Fonts/FontMeasure.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Framework/Text/DefaultLayoutBlock.h"
|
|
#include "Framework/Text/ShapedTextCache.h"
|
|
#include "Framework/Text/RunUtils.h"
|
|
#include "Fonts/ShapedTextFwd.h"
|
|
|
|
TSharedRef< FSlateTextRun > FSlateTextRun::Create( const FRunInfo& InRunInfo, const TSharedRef< const FString >& InText, const FTextBlockStyle& Style )
|
|
{
|
|
return MakeShareable( new FSlateTextRun( InRunInfo, InText, Style ) );
|
|
}
|
|
|
|
TSharedRef< FSlateTextRun > FSlateTextRun::Create( const FRunInfo& InRunInfo, const TSharedRef< const FString >& InText, const FTextBlockStyle& Style, const FTextRange& InRange )
|
|
{
|
|
return MakeShareable( new FSlateTextRun( InRunInfo, InText, Style, InRange ) );
|
|
}
|
|
|
|
FTextRange FSlateTextRun::GetTextRange() const
|
|
{
|
|
return Range;
|
|
}
|
|
|
|
void FSlateTextRun::SetTextRange( const FTextRange& Value )
|
|
{
|
|
Range = Value;
|
|
}
|
|
|
|
int16 FSlateTextRun::GetBaseLine( float Scale ) const
|
|
{
|
|
const TSharedRef< FSlateFontMeasure > FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
|
|
return FontMeasure->GetBaseline( Style.Font, Scale ) - ( FMath::Min(0.0f, Style.ShadowOffset.Y) + Style.Font.OutlineSettings.OutlineSize ) * Scale;
|
|
}
|
|
|
|
int16 FSlateTextRun::GetMaxHeight( float Scale ) const
|
|
{
|
|
const TSharedRef< FSlateFontMeasure > FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
|
|
return FontMeasure->GetMaxCharacterHeight( Style.Font, Scale ) + ( FMath::Abs(Style.ShadowOffset.Y) + Style.Font.OutlineSettings.OutlineSize ) * Scale;
|
|
}
|
|
|
|
FVector2D FSlateTextRun::Measure( int32 BeginIndex, int32 EndIndex, float Scale, const FRunTextContext& TextContext ) const
|
|
{
|
|
const FVector2D ShadowOffsetToApply((EndIndex == Range.EndIndex) ? FMath::Abs(Style.ShadowOffset.X * Scale) : 0.0f, FMath::Abs(Style.ShadowOffset.Y * Scale));
|
|
|
|
// Offset the measured shaped text by the outline since the outline was not factored into the size of the text
|
|
// Need to add the outline offsetting to the beginning and the end because it surrounds both sides.
|
|
const float ScaledOutlineSize = Style.Font.OutlineSettings.OutlineSize * Scale;
|
|
const FVector2D OutlineSizeToApply((BeginIndex == Range.BeginIndex ? ScaledOutlineSize : 0) + (EndIndex == Range.EndIndex ? ScaledOutlineSize : 0), ScaledOutlineSize);
|
|
|
|
if (EndIndex - BeginIndex == 0)
|
|
{
|
|
return FVector2D(0, GetMaxHeight(Scale)) + ShadowOffsetToApply + OutlineSizeToApply;
|
|
}
|
|
|
|
// Use the full text range (rather than the run range) so that text that spans runs will still be shaped correctly
|
|
return ShapedTextCacheUtil::MeasureShapedText(TextContext.ShapedTextCache, FCachedShapedTextKey(FTextRange(0, Text->Len()), Scale, TextContext, Style.Font), FTextRange(BeginIndex, EndIndex), **Text) + ShadowOffsetToApply + OutlineSizeToApply;
|
|
}
|
|
|
|
int8 FSlateTextRun::GetKerning(int32 CurrentIndex, float Scale, const FRunTextContext& TextContext) const
|
|
{
|
|
const int32 PreviousIndex = CurrentIndex - 1;
|
|
if ( PreviousIndex < 0 || CurrentIndex == Text->Len() )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Use the full text range (rather than the run range) so that text that spans runs will still be shaped correctly
|
|
return ShapedTextCacheUtil::GetShapedGlyphKerning(TextContext.ShapedTextCache, FCachedShapedTextKey(FTextRange(0, Text->Len()), Scale, TextContext, Style.Font), PreviousIndex, **Text);
|
|
}
|
|
|
|
TSharedRef< ILayoutBlock > FSlateTextRun::CreateBlock( int32 BeginIndex, int32 EndIndex, FVector2D Size, const FLayoutBlockTextContext& TextContext, const TSharedPtr< IRunRenderer >& Renderer )
|
|
{
|
|
return FDefaultLayoutBlock::Create( SharedThis( this ), FTextRange( BeginIndex, EndIndex ), Size, TextContext, Renderer );
|
|
}
|
|
int32 FSlateTextRun::OnPaint(const FPaintArgs& PaintArgs, const FTextArgs& TextArgs, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
|
{
|
|
#if !UE_BUILD_SHIPPING
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("Slate.LogPaintedText"));
|
|
if (CVar->GetBool())
|
|
{
|
|
UE_LOG(LogSlate, Log, TEXT("FSlateTextRun: '%s'."), **Text);
|
|
}
|
|
#endif
|
|
const ESlateDrawEffect DrawEffects = bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
|
|
|
|
const TSharedRef<ILayoutBlock>& Block = TextArgs.Block;
|
|
const FTextBlockStyle& DefaultStyle = TextArgs.DefaultStyle;
|
|
const FTextLayout::FLineView& Line = TextArgs.Line;
|
|
|
|
const bool ShouldDropShadow = Style.ShadowColorAndOpacity.A > 0.f && Style.ShadowOffset.SizeSquared() > 0.f;
|
|
const FVector2D BlockLocationOffset = Block->GetLocationOffset();
|
|
const FTextRange BlockRange = Block->GetTextRange();
|
|
const FLayoutBlockTextContext BlockTextContext = Block->GetTextContext();
|
|
|
|
// The block size and offset values are pre-scaled, so we need to account for that when converting the block offsets into paint geometry
|
|
const float InverseScale = Inverse(AllottedGeometry.Scale);
|
|
|
|
// A negative shadow offset should be applied as a positive offset to the text to avoid clipping issues
|
|
const FVector2D DrawShadowOffset(
|
|
(Style.ShadowOffset.X > 0.0f) ? Style.ShadowOffset.X * AllottedGeometry.Scale : 0.0f,
|
|
(Style.ShadowOffset.Y > 0.0f) ? Style.ShadowOffset.Y * AllottedGeometry.Scale : 0.0f
|
|
);
|
|
const FVector2D DrawTextOffset(
|
|
(Style.ShadowOffset.X < 0.0f) ? -Style.ShadowOffset.X * AllottedGeometry.Scale : 0.0f,
|
|
(Style.ShadowOffset.Y < 0.0f) ? -Style.ShadowOffset.Y * AllottedGeometry.Scale : 0.0f
|
|
);
|
|
|
|
// Make sure we have up-to-date shaped text to work with
|
|
// We use the full line view range (rather than the run range) so that text that spans runs will still be shaped correctly
|
|
FShapedGlyphSequenceRef ShapedText = ShapedTextCacheUtil::GetShapedTextSubSequence(
|
|
BlockTextContext.ShapedTextCache,
|
|
FCachedShapedTextKey(Line.Range, AllottedGeometry.GetAccumulatedLayoutTransform().GetScale(), BlockTextContext, Style.Font),
|
|
BlockRange,
|
|
**Text,
|
|
BlockTextContext.TextDirection
|
|
);
|
|
|
|
FTextOverflowArgs OverflowArgs;
|
|
if ((TextArgs.OverflowPolicy == ETextOverflowPolicy::Ellipsis || TextArgs.OverflowPolicy == ETextOverflowPolicy::MultilineEllipsis) && TextArgs.OverflowDirection != ETextOverflowDirection::NoOverflow)
|
|
{
|
|
OverflowArgs.OverflowTextPtr = BlockTextContext.ShapedTextCache->FindOrAddOverflowEllipsisText(AllottedGeometry.GetAccumulatedLayoutTransform().GetScale(), BlockTextContext, Style.Font);
|
|
OverflowArgs.OverflowDirection = TextArgs.OverflowDirection;
|
|
OverflowArgs.bIsLastVisibleBlock = TextArgs.bIsLastVisibleBlock;
|
|
OverflowArgs.bIsNextBlockClipped = TextArgs.bIsNextBlockClipped;
|
|
}
|
|
|
|
// Draw the optional shadow
|
|
if (ShouldDropShadow)
|
|
{
|
|
FShapedGlyphSequenceRef ShadowShapedText = ShapedText;
|
|
if (Style.ShadowColorAndOpacity != Style.Font.OutlineSettings.OutlineColor)
|
|
{
|
|
// Copy font info for shadow to replace the outline color
|
|
FSlateFontInfo ShadowFontInfo = Style.Font;
|
|
ShadowFontInfo.OutlineSettings.OutlineColor = Style.ShadowColorAndOpacity;
|
|
ShadowFontInfo.OutlineSettings.OutlineMaterial = nullptr;
|
|
if (!ShadowFontInfo.OutlineSettings.bApplyOutlineToDropShadows)
|
|
{
|
|
ShadowFontInfo.OutlineSettings.OutlineSize = 0;
|
|
}
|
|
|
|
// Create new shaped text for drop shadow
|
|
ShadowShapedText = ShapedTextCacheUtil::GetShapedTextSubSequence(
|
|
BlockTextContext.ShapedTextCache,
|
|
FCachedShapedTextKey(Line.Range, AllottedGeometry.GetAccumulatedLayoutTransform().GetScale(), BlockTextContext, ShadowFontInfo),
|
|
BlockRange,
|
|
**Text,
|
|
BlockTextContext.TextDirection
|
|
);
|
|
}
|
|
|
|
FSlateDrawElement::MakeShapedText(
|
|
OutDrawElements,
|
|
++LayerId,
|
|
AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset() + DrawShadowOffset))),
|
|
ShadowShapedText,
|
|
DrawEffects,
|
|
InWidgetStyle.GetColorAndOpacityTint() * Style.ShadowColorAndOpacity,
|
|
InWidgetStyle.GetColorAndOpacityTint() * Style.Font.OutlineSettings.OutlineColor,
|
|
OverflowArgs
|
|
);
|
|
}
|
|
|
|
// Draw the text itself
|
|
FSlateDrawElement::MakeShapedText(
|
|
OutDrawElements,
|
|
++LayerId,
|
|
AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset() + DrawTextOffset))),
|
|
ShapedText,
|
|
DrawEffects,
|
|
InWidgetStyle.GetColorAndOpacityTint() * Style.ColorAndOpacity.GetColor(InWidgetStyle),
|
|
InWidgetStyle.GetColorAndOpacityTint() * Style.Font.OutlineSettings.OutlineColor,
|
|
OverflowArgs
|
|
);
|
|
|
|
return LayerId;
|
|
}
|
|
|
|
const TArray< TSharedRef<SWidget> >& FSlateTextRun::GetChildren()
|
|
{
|
|
static TArray< TSharedRef<SWidget> > NoChildren;
|
|
return NoChildren;
|
|
}
|
|
|
|
void FSlateTextRun::ArrangeChildren( const TSharedRef< ILayoutBlock >& Block, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const
|
|
{
|
|
// no widgets
|
|
}
|
|
|
|
int32 FSlateTextRun::GetTextIndexAt( const TSharedRef< ILayoutBlock >& Block, const FVector2D& Location, float Scale, ETextHitPoint* const OutHitPoint ) const
|
|
{
|
|
const FVector2D& BlockOffset = Block->GetLocationOffset();
|
|
const FVector2D& BlockSize = Block->GetSize();
|
|
|
|
const double Left = BlockOffset.X;
|
|
const double Top = BlockOffset.Y;
|
|
const double Right = BlockOffset.X + BlockSize.X;
|
|
const double Bottom = BlockOffset.Y + BlockSize.Y;
|
|
|
|
const bool ContainsPoint = Location.X >= Left && Location.X < Right && Location.Y >= Top && Location.Y < Bottom;
|
|
|
|
if ( !ContainsPoint )
|
|
{
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
const FTextRange BlockRange = Block->GetTextRange();
|
|
const FLayoutBlockTextContext BlockTextContext = Block->GetTextContext();
|
|
|
|
// Use the full text range (rather than the run range) so that text that spans runs will still be shaped correctly
|
|
const int32 Index = ShapedTextCacheUtil::FindCharacterIndexAtOffset(BlockTextContext.ShapedTextCache, FCachedShapedTextKey(FTextRange(0, Text->Len()), Scale, BlockTextContext, Style.Font), BlockRange, **Text, Location.X - BlockOffset.X);
|
|
if (OutHitPoint)
|
|
{
|
|
*OutHitPoint = RunUtils::CalculateTextHitPoint(Index, BlockRange, BlockTextContext.TextDirection);
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
FVector2D FSlateTextRun::GetLocationAt( const TSharedRef< ILayoutBlock >& Block, int32 Offset, float Scale ) const
|
|
{
|
|
const FVector2D& BlockOffset = Block->GetLocationOffset();
|
|
const FTextRange& BlockRange = Block->GetTextRange();
|
|
const FLayoutBlockTextContext BlockTextContext = Block->GetTextContext();
|
|
|
|
// Use the full text range (rather than the run range) so that text that spans runs will still be shaped correctly
|
|
const FTextRange RangeToMeasure = RunUtils::CalculateOffsetMeasureRange(Offset, BlockRange, BlockTextContext.TextDirection);
|
|
const FVector2D OffsetLocation = ShapedTextCacheUtil::MeasureShapedText(BlockTextContext.ShapedTextCache, FCachedShapedTextKey(FTextRange(0, Text->Len()), Scale, BlockTextContext, Style.Font), RangeToMeasure, **Text);
|
|
|
|
return BlockOffset + OffsetLocation;
|
|
}
|
|
|
|
void FSlateTextRun::Move(const TSharedRef<FString>& NewText, const FTextRange& NewRange)
|
|
{
|
|
Text = NewText;
|
|
Range = NewRange;
|
|
|
|
#if TEXT_LAYOUT_DEBUG
|
|
DebugSlice = FString( InRange.EndIndex - InRange.BeginIndex, (**Text) + InRange.BeginIndex );
|
|
#endif
|
|
}
|
|
|
|
TSharedRef<IRun> FSlateTextRun::Clone() const
|
|
{
|
|
return FSlateTextRun::Create(RunInfo, Text, Style, Range);
|
|
}
|
|
|
|
void FSlateTextRun::AppendTextTo(FString& AppendToText) const
|
|
{
|
|
AppendToText.Append(**Text + Range.BeginIndex, Range.Len());
|
|
}
|
|
|
|
void FSlateTextRun::AppendTextTo(FString& AppendToText, const FTextRange& PartialRange) const
|
|
{
|
|
check(Range.BeginIndex <= PartialRange.BeginIndex);
|
|
check(Range.EndIndex >= PartialRange.EndIndex);
|
|
|
|
AppendToText.Append(**Text + PartialRange.BeginIndex, PartialRange.Len());
|
|
}
|
|
|
|
const FRunInfo& FSlateTextRun::GetRunInfo() const
|
|
{
|
|
return RunInfo;
|
|
}
|
|
|
|
ERunAttributes FSlateTextRun::GetRunAttributes() const
|
|
{
|
|
return ERunAttributes::SupportsText;
|
|
}
|
|
|
|
FSlateTextRun::FSlateTextRun( const FRunInfo& InRunInfo, const TSharedRef< const FString >& InText, const FTextBlockStyle& InStyle )
|
|
: RunInfo( InRunInfo )
|
|
, Text( InText )
|
|
, Style( InStyle )
|
|
, Range( 0, Text->Len() )
|
|
#if TEXT_LAYOUT_DEBUG
|
|
, DebugSlice( FString( Text->Len(), **Text ) )
|
|
#endif
|
|
{
|
|
|
|
}
|
|
|
|
FSlateTextRun::FSlateTextRun( const FRunInfo& InRunInfo, const TSharedRef< const FString >& InText, const FTextBlockStyle& InStyle, const FTextRange& InRange )
|
|
: RunInfo( InRunInfo )
|
|
, Text( InText )
|
|
, Style( InStyle )
|
|
, Range( InRange )
|
|
#if TEXT_LAYOUT_DEBUG
|
|
, DebugSlice( FString( InRange.EndIndex - InRange.BeginIndex, (**Text) + InRange.BeginIndex ) )
|
|
#endif
|
|
{
|
|
|
|
}
|
|
|
|
FSlateTextRun::FSlateTextRun( const FSlateTextRun& Run )
|
|
: RunInfo( Run.RunInfo )
|
|
, Text( Run.Text )
|
|
, Style( Run.Style )
|
|
, Range( Run.Range )
|
|
#if TEXT_LAYOUT_DEBUG
|
|
, DebugSlice( Run.DebugSlice )
|
|
#endif
|
|
{
|
|
|
|
}
|
|
|
|
void FSlateTextRun::ApplyFontSizeMultiplierOnTextStyle(float FontSizeMultiplier)
|
|
{
|
|
if (FontSizeMultiplier != 0.0f)
|
|
{
|
|
Style.SetFontSize(Style.Font.Size * FontSizeMultiplier);
|
|
}
|
|
}
|