int32 FCinematicShotSection::OnPaintSection(FSequencerSectionPainter& InPainter) const
{
	static const FSlateBrush* FilmBorder = FEditorStyle::GetBrush("Sequencer.Section.FilmBorder");

	InPainter.LayerId = InPainter.PaintSectionBackground();

	FVector2D SectionSize = InPainter.SectionGeometry.GetLocalSize();

	FSlateDrawElement::MakeBox(
		InPainter.DrawElements,
		InPainter.LayerId++,
		InPainter.SectionGeometry.ToPaintGeometry(FVector2D(SectionSize.X-2.f, 7.f), FSlateLayoutTransform(FVector2D(1.f, 4.f))),
		FilmBorder,
		InPainter.SectionClippingRect.InsetBy(FMargin(1.f)),
		InPainter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect
	);

	FSlateDrawElement::MakeBox(
		InPainter.DrawElements,
		InPainter.LayerId++,
		InPainter.SectionGeometry.ToPaintGeometry(FVector2D(SectionSize.X-2.f, 7.f), FSlateLayoutTransform(FVector2D(1.f, SectionSize.Y - 11.f))),
		FilmBorder,
		InPainter.SectionClippingRect.InsetBy(FMargin(1.f)),
		InPainter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect
	);

	if (SequenceInstance.IsValid())
	{
		return FThumbnailSection::OnPaintSection(InPainter);
	}

	return InPainter.LayerId;
}
Beispiel #2
0
FPaintGeometry FGeometry::ToInflatedPaintGeometry( const FVector2D& InflateAmount ) const
{
	// This essentially adds (or subtracts) a border around the widget. We scale the size then offset by the border amount.
	// Note this is not scaling child widgets, so the scale is not changing.
	FVector2D NewSize = Size + InflateAmount * 2;
	return ToPaintGeometry(NewSize, FSlateLayoutTransform(-InflateAmount));
}
void FDesignTimeUtils::GetArrangedWidgetRelativeToParent(FWidgetPath& WidgetPath, TSharedRef<const SWidget> Widget, TSharedRef<const SWidget> Parent, FArrangedWidget& ArrangedWidget)
{
	FArrangedWidget ArrangedDesigner = WidgetPath.FindArrangedWidget(Parent).Get(FArrangedWidget::NullWidget);

	ArrangedWidget = WidgetPath.FindArrangedWidget(Widget).Get(FArrangedWidget::NullWidget);
	// !!! WRH 2014/08/26 - This code tries to mutate an FGeometry in an unsupported way. This code should be refactored.
	ArrangedWidget.Geometry.AppendTransform(FSlateLayoutTransform(Inverse(ArrangedDesigner.Geometry.AbsolutePosition)));
}
Beispiel #4
0
int32 FHittestGrid::InsertWidget( const int32 ParentHittestIndex, const EVisibility& Visibility, const FArrangedWidget& Widget, const FVector2D InWindowOffset, const FSlateRect& InClippingRect )
{
	check( ParentHittestIndex < WidgetsCachedThisFrame->Num() );

	// Update the FGeometry to transform into desktop space.
	FArrangedWidget WindowAdjustedWidget(Widget);
	WindowAdjustedWidget.Geometry.AppendTransform(FSlateLayoutTransform(InWindowOffset));
	const FSlateRect WindowAdjustedRect = InClippingRect.OffsetBy(InWindowOffset);

	// Remember this widget, its geometry, and its place in the logical hierarchy.
	const int32 WidgetIndex = WidgetsCachedThisFrame->Add( FCachedWidget( ParentHittestIndex, WindowAdjustedWidget, WindowAdjustedRect ) );
	check( WidgetIndex < WidgetsCachedThisFrame->Num() ); 
	if (ParentHittestIndex != INDEX_NONE)
	{
		(*WidgetsCachedThisFrame)[ParentHittestIndex].AddChild( WidgetIndex );
	}
	
	if (Visibility.IsHitTestVisible())
	{
		// Mark any cell that is overlapped by this widget.

		// Compute the render space clipping rect, and compute it's aligned bounds so we can insert conservatively into the hit test grid.
		FSlateRect GridRelativeBoundingClipRect = 
			TransformRect(
				Concatenate(
					Inverse(WindowAdjustedWidget.Geometry.GetAccumulatedLayoutTransform()),
					WindowAdjustedWidget.Geometry.GetAccumulatedRenderTransform()
				),
				FSlateRotatedRect(WindowAdjustedWidget.Geometry.GetClippingRect().IntersectionWith(WindowAdjustedRect))
			)
			.ToBoundingRect()
			.OffsetBy(-GridOrigin);

		// Starting and ending cells covered by this widget.	
		const FIntPoint UpperLeftCell = FIntPoint(
			FMath::Max(0, FMath::FloorToInt(GridRelativeBoundingClipRect.Left / CellSize.X)),
			FMath::Max(0, FMath::FloorToInt(GridRelativeBoundingClipRect.Top / CellSize.Y)));

		const FIntPoint LowerRightCell = FIntPoint(
			FMath::Min( NumCells.X-1, FMath::FloorToInt(GridRelativeBoundingClipRect.Right / CellSize.X)),
			FMath::Min( NumCells.Y-1, FMath::FloorToInt(GridRelativeBoundingClipRect.Bottom / CellSize.Y)));

		for (int32 XIndex=UpperLeftCell.X; XIndex <= LowerRightCell.X; ++ XIndex )
		{
			for(int32 YIndex=UpperLeftCell.Y; YIndex <= LowerRightCell.Y; ++YIndex)
			{
				CellAt(XIndex, YIndex).CachedWidgetIndexes.Add( WidgetIndex );
			}
		}
	}
	
	return WidgetIndex;

}
Beispiel #5
0
FGeometry ComputeMenuPlacement(const FGeometry& AllottedGeometry, const FVector2D& PopupDesiredSize, EMenuPlacement PlacementMode)
{
	// Compute the popup size, offset, and anchor rect  in local space
	const FPopupPlacement Placement(AllottedGeometry, PopupDesiredSize, PlacementMode);

	// ask the application to compute the proper desktop offset for the anchor. This requires the offsets to be in desktop space.
	const FVector2D NewPositionDesktopSpace = FSlateApplication::Get().CalculatePopupWindowPosition(
		TransformRect(AllottedGeometry.GetAccumulatedLayoutTransform(), Placement.AnchorLocalSpace),
		TransformVector(AllottedGeometry.GetAccumulatedLayoutTransform(), Placement.LocalPopupSize),
		Placement.Orientation);

	// transform the desktop offset into local space and use that as the layout transform for the child content.
	return AllottedGeometry.MakeChild(
		Placement.LocalPopupSize,
		FSlateLayoutTransform(TransformPoint(Inverse(AllottedGeometry.GetAccumulatedLayoutTransform()), NewPositionDesktopSpace)));
}
void SClippingHorizontalBox::OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const
{
	// If WrapButton hasn't been initialized, that means AddWrapButton() hasn't 
	// been called and this method isn't going to behave properly
	check(WrapButton.IsValid());

	SHorizontalBox::OnArrangeChildren(AllottedGeometry, ArrangedChildren);

	// Remove children that are clipped by the allotted geometry
	const int32 NumChildren = ArrangedChildren.Num(); 
	int32 IndexClippedAt = NumChildren;
	for (int32 ChildIdx = NumChildren - 2; ChildIdx >= 0; --ChildIdx)
	{
		const FArrangedWidget& CurWidget = ArrangedChildren[ChildIdx];
		if (FMath::TruncToInt(CurWidget.Geometry.AbsolutePosition.X + CurWidget.Geometry.Size.X * CurWidget.Geometry.Scale) > FMath::TruncToInt(AllottedGeometry.AbsolutePosition.X + AllottedGeometry.Size.X * CurWidget.Geometry.Scale))
		{
			ArrangedChildren.Remove(ChildIdx);
			IndexClippedAt = ChildIdx;
		}
	}

	if (IndexClippedAt == NumChildren)
	{
		// None of the children are being clipped, so remove the wrap button
		ArrangedChildren.Remove(ArrangedChildren.Num() - 1);
	}
	else
	{
		// Right align the wrap button
		FArrangedWidget& ArrangedButton = ArrangedChildren[ArrangedChildren.Num() - 1];
		ArrangedButton.Geometry = AllottedGeometry.MakeChild(ArrangedButton.Geometry.Size, FSlateLayoutTransform(AllottedGeometry.Size - ArrangedButton.Geometry.Size));
		const int32 WrapButtonXPosition = FMath::TruncToInt(ArrangedButton.Geometry.AbsolutePosition.X);

		// Further remove any children that the wrap button overlaps with
		for (int32 ChildIdx = IndexClippedAt - 1; ChildIdx >= 0; --ChildIdx)
		{
			const FArrangedWidget& CurWidget = ArrangedChildren[ChildIdx];
			if (FMath::TruncToInt(CurWidget.Geometry.AbsolutePosition.X + CurWidget.Geometry.Size.X * CurWidget.Geometry.Scale) > WrapButtonXPosition)
			{
				ArrangedChildren.Remove(ChildIdx);
			}
		}
	}
}
void FWidgetRenderer::DrawWindow(
	UTextureRenderTarget2D* RenderTarget,
	TSharedRef<FHittestGrid> HitTestGrid,
	TSharedRef<SWindow> Window,
	float Scale,
	FVector2D DrawSize,
	float DeltaTime)
{
	FGeometry WindowGeometry = FGeometry::MakeRoot(DrawSize * ( 1 / Scale ), FSlateLayoutTransform(Scale));

	DrawWindow(
		RenderTarget,
		HitTestGrid,
		Window,
		WindowGeometry,
		WindowGeometry.GetClippingRect(),
		DeltaTime
		);
}
bool FDesignTimeUtils::GetArrangedWidgetRelativeToWindow(TSharedRef<SWidget> Widget, FArrangedWidget& ArrangedWidget)
{
	TSharedPtr<SWindow> WidgetWindow = FSlateApplication::Get().FindWidgetWindow(Widget);
	if ( !WidgetWindow.IsValid() )
	{
		return false;
	}

	TSharedRef<SWindow> CurrentWindowRef = WidgetWindow.ToSharedRef();

	FWidgetPath WidgetPath;
	if ( FSlateApplication::Get().GeneratePathToWidgetUnchecked(Widget, WidgetPath) )
	{
		ArrangedWidget = WidgetPath.FindArrangedWidget(Widget).Get(FArrangedWidget::NullWidget);
		ArrangedWidget.Geometry.AppendTransform(FSlateLayoutTransform(Inverse(CurrentWindowRef->GetPositionInScreen())));
		return true;
	}

	return false;
}
Beispiel #9
0
TArray<FLayoutGeometry> SSplitter2x2::ArrangeChildrenForLayout( const FGeometry& AllottedGeometry ) const
{
	check( Children.Num() == 4 );

	TArray<FLayoutGeometry> Result;
	Result.Empty(Children.Num());

	int32 NumNonCollapsedChildren = 0;
	FVector2D CoefficientTotal(0,0);

	// The allotted space for our children is our geometry minus a little space to show splitter handles
	const FVector2D SpaceAllottedForChildren = AllottedGeometry.Size - FVector2D(SplitterHandleSize,SplitterHandleSize);

	// The current offset that the next child should be positioned at.
	FVector2D Offset(0,0);

	for (int32 ChildIndex=0; ChildIndex < Children.Num(); ++ChildIndex)
	{
		const FSlot& CurSlot = Children[ChildIndex];

		// Calculate the amount of space that this child should take up.  
		// It is based on the current percentage of space it should take up which is defined by a user moving the splitters
		const FVector2D ChildSpace = SpaceAllottedForChildren * CurSlot.PercentageAttribute.Get();

		// put them in their spot
		Result.Emplace(FSlateLayoutTransform(Offset), ChildSpace);

		// Advance to the next slot. If the child is collapsed, it takes up no room and does not need a splitter
		if( ChildIndex == 1 )
		{
			// ChildIndex of 1 means we are starting the next column so reset the Y offset.
			Offset.Y = 0.0f;
			Offset += FVector2D( ChildSpace.X + SplitterHandleSize, 0);
		}
		else
		{
			Offset += FVector2D( 0, ChildSpace.Y + SplitterHandleSize );
		}
	}
	return Result;
}
Beispiel #10
0
FGeometry SMenuAnchor::ComputeMenuPlacement( const FGeometry& AllottedGeometry, const FVector2D& PopupDesiredSize, EMenuPlacement PlacementMode )
{
	// Compute the popup size, offset, and anchor rect  in local space
	const FVector2D PopupSizeLocalSpace = (PlacementMode == MenuPlacement_ComboBox || PlacementMode == MenuPlacement_ComboBoxRight)
		? FVector2D( FMath::Max( AllottedGeometry.Size.X, PopupDesiredSize.X ), PopupDesiredSize.Y )
		: PopupDesiredSize;
	const FVector2D OffsetLocalSpace = GetMenuOffsetForPlacement(AllottedGeometry, PlacementMode, PopupSizeLocalSpace);
	const FSlateRect AnchorLocalSpace = FSlateRect::FromPointAndExtent(OffsetLocalSpace, AllottedGeometry.Size);
	const EOrientation Orientation = (PlacementMode == MenuPlacement_MenuRight || PlacementMode == MenuPlacement_MenuLeft) ? Orient_Horizontal : Orient_Vertical;
	
	// ask the application to compute the proper desktop offset for the anchor. This requires the offsets to be in desktop space.
	const FVector2D NewPositionDesktopSpace = FSlateApplication::Get().CalculatePopupWindowPosition( 
		TransformRect(AllottedGeometry.GetAccumulatedLayoutTransform(), AnchorLocalSpace), 
		TransformVector(AllottedGeometry.GetAccumulatedLayoutTransform(), PopupSizeLocalSpace), 
		Orientation );

	// transform the desktop offset into local space and use that as the layout transform for the child content.
	return AllottedGeometry.MakeChild(
		PopupSizeLocalSpace,
		FSlateLayoutTransform(TransformPoint(Inverse(AllottedGeometry.GetAccumulatedLayoutTransform()), NewPositionDesktopSpace)));
}
Beispiel #11
0
int32 FSlateImageRun::OnPaint( const FPaintArgs& Args, const FTextLayout::FLineView& Line, const TSharedRef< ILayoutBlock >& Block, const FTextBlockStyle& DefaultStyle, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const 
{
	// 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);

	if ( Image->DrawAs != ESlateBrushDrawType::NoDrawType )
	{
		const FColor FinalColorAndOpacity( InWidgetStyle.GetColorAndOpacityTint() * Image->GetTint( InWidgetStyle ) );
		const uint32 DrawEffects = bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
		FSlateDrawElement::MakeBox(
			OutDrawElements, 
			++LayerId, 
			AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset()))), 
			Image, 
			MyClippingRect, 
			DrawEffects, 
			FinalColorAndOpacity
			);
	}

	return LayerId;
}
Beispiel #12
0
TArray<FLayoutGeometry> SSplitter::ArrangeChildrenForLayout(const FGeometry& AllottedGeometry) const
{
	TArray<FLayoutGeometry> Result; 
	Result.Empty(Children.Num());

	const int32 AxisIndex = (Orientation == Orient_Horizontal) ? 0 : 1;

	// Splitters divide the space between their children proportionately based on size coefficients.
	// The size coefficients are usually determined by a user, who grabs the handled between the child elements
	// and moves them to resize the space available to the children.
	// Some children are sized automatically based on their content; those children cannot be resized.
	//
	// e.g.   _____________________________________ Children
	//       /              /                  /
	//      v              v                  v
	//   + - - - - - + + - - - + + - - - - - - - - - - - - - - +
	//   |           | |       | |                             |
	//   | Child 0   | |Child1 | |  Child2                     |
	//   + - - - - - + + - - - + + - - - - - - - - - - - - - - +
	//                ^         ^
	//                 \_________\___________ Resize handles.

	int32 NumNonCollapsedChildren = 0;
	int32 NumResizeableChildren = 0;
	float CoefficientTotal = 0;
	// Some space is claimed by non-resizeable elements (auto-sized elements)
	float NonResizeableSpace = 0;
	{
		for (int32 ChildIndex=0; ChildIndex < Children.Num(); ++ChildIndex)	
		{
			if (Children[ChildIndex].GetWidget()->GetVisibility() != EVisibility::Collapsed)
			{
				++NumNonCollapsedChildren;

				if ( Children[ChildIndex].SizingRule == SSplitter::SizeToContent )
				{
					NonResizeableSpace += Children[ChildIndex].GetWidget()->GetDesiredSize()[AxisIndex];
				}
				else // SizingRule == SSplitter::FractionOfParent
				{
					CoefficientTotal += Children[ChildIndex].SizeValue.Get();
				}
			}			
		}
	}



	// The user-sizeable children must make room for the resize handles and for auto-sized children.
	const float SpaceNeededForHandles = FMath::Max(0, NumNonCollapsedChildren-1) * PhysicalSplitterHandleSize;
	const float ResizeableSpace = AllottedGeometry.Size.Component(AxisIndex) - SpaceNeededForHandles - NonResizeableSpace;

	// Arrange the children horizontally or vertically.
	float XOffset = 0;
	for (int32 ChildIndex=0; ChildIndex < Children.Num(); ++ChildIndex)
	{
		const FSlot& CurSlot = Children[ChildIndex];

		const float ChildSpace = ( CurSlot.SizingRule == SSplitter::SizeToContent )
			? CurSlot.GetWidget()->GetDesiredSize()[AxisIndex]
		: ResizeableSpace * CurSlot.SizeValue.Get() / CoefficientTotal;

		const EVisibility ChildVisibility = CurSlot.GetWidget()->GetVisibility();

		FVector2D ChildOffset = Orientation == Orient_Horizontal ? FVector2D(XOffset, 0) : FVector2D(0, XOffset);
		FVector2D ChildSize = Orientation == Orient_Horizontal ? FVector2D(ChildSpace, AllottedGeometry.Size.Y) : FVector2D(AllottedGeometry.Size.X, ChildSpace);
		Result.Emplace(FSlateLayoutTransform(ChildOffset), ChildSize);

		// Advance to the next slot. If the child is collapsed, it takes up no room and does not need a splitter
		if ( ChildVisibility != EVisibility::Collapsed)
		{
			XOffset += ChildSpace + PhysicalSplitterHandleSize;
		}
	}
	return Result;
}
Beispiel #13
0
FGeometry FGeometry::MakeRoot(const FVector2D& LocalSize, const FSlateLayoutTransform& LayoutTransform)
{
	return FGeometry(LocalSize, LayoutTransform, FSlateLayoutTransform(), FSlateRenderTransform());
}
int32 SSlider::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
	// we draw the slider like a horizontal slider regardless of the orientation, and apply a render transform to make it display correctly.
	// However, the AllottedGeometry is computed as it will be rendered, so we have to use the "horizontal orientation" when doing drawing computations.
	const float AllottedWidth = Orientation == Orient_Horizontal ? AllottedGeometry.GetLocalSize().X : AllottedGeometry.GetLocalSize().Y;
	const float AllottedHeight = Orientation == Orient_Horizontal ? AllottedGeometry.GetLocalSize().Y : AllottedGeometry.GetLocalSize().X;

	float HandleRotation;
	FVector2D HandleTopLeftPoint;
	FVector2D SliderStartPoint;
	FVector2D SliderEndPoint;

	// calculate slider geometry as if it's a horizontal slider (we'll rotate it later if it's vertical)
	const FVector2D HandleSize = Style->NormalThumbImage.ImageSize;
	const FVector2D HalfHandleSize = 0.5f * HandleSize;
	const float Indentation = IndentHandle.Get() ? HandleSize.X : 0.0f;

	const float SliderLength = AllottedWidth - Indentation;
	const float SliderPercent = ValueAttribute.Get();
	const float SliderHandleOffset = SliderPercent * SliderLength;
	const float SliderY = 0.5f * AllottedHeight;

	HandleRotation = 0.0f;
	HandleTopLeftPoint = FVector2D(SliderHandleOffset - ( HandleSize.X * SliderPercent ) + 0.5f * Indentation, SliderY - HalfHandleSize.Y);

	SliderStartPoint = FVector2D(HalfHandleSize.X, SliderY);
	SliderEndPoint = FVector2D(AllottedWidth - HalfHandleSize.X, SliderY);

	FSlateRect RotatedClippingRect = MyClippingRect;
	FGeometry SliderGeometry = AllottedGeometry;
	
	// rotate the slider 90deg if it's vertical. The 0 side goes on the bottom, the 1 side on the top.
	if (Orientation == Orient_Vertical)
	{
		// Do this by translating along -X by the width of the geometry, then rotating 90 degreess CCW (left-hand coords)
		FSlateRenderTransform SlateRenderTransform = TransformCast<FSlateRenderTransform>(Concatenate(Inverse(FVector2D(AllottedWidth, 0)), FQuat2D(FMath::DegreesToRadians(-90.0f))));
		// create a child geometry matching this one, but with the render transform.
		SliderGeometry = AllottedGeometry.MakeChild(
			FVector2D(AllottedWidth, AllottedHeight), 
			FSlateLayoutTransform(), 
			SlateRenderTransform, FVector2D::ZeroVector);
		// The clipping rect is already given properly in window space. But we do not support layout rotations, so our local space rendering cannot
		// get the clipping rect into local space properly for the local space clipping we do in the shader.
		// Thus, we transform the clip coords into local space manually, UNDO the render transform so it will clip properly,
		// and then bring the clip coords back into window space where DrawElements expect them.
		RotatedClippingRect = TransformRect(
			Concatenate(
				Inverse(SliderGeometry.GetAccumulatedLayoutTransform()), 
				Inverse(SlateRenderTransform),
				SliderGeometry.GetAccumulatedLayoutTransform()), 
			MyClippingRect);
	}

	const bool bEnabled = ShouldBeEnabled(bParentEnabled);
	const ESlateDrawEffect::Type DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;

	// draw slider bar
	auto BarTopLeft = FVector2D(SliderStartPoint.X, SliderStartPoint.Y - Style->BarThickness * 0.5f);
	auto BarSize = FVector2D(SliderEndPoint.X - SliderStartPoint.X, Style->BarThickness);
	FSlateDrawElement::MakeBox(
		OutDrawElements,
		LayerId,
		SliderGeometry.ToPaintGeometry(BarTopLeft, BarSize),
		LockedAttribute.Get() ? &Style->DisabledBarImage : &Style->NormalBarImage,
		RotatedClippingRect,
		DrawEffects,
		SliderBarColor.Get().GetColor(InWidgetStyle) * InWidgetStyle.GetColorAndOpacityTint()
		);

	++LayerId;

	// draw slider thumb
	FSlateDrawElement::MakeBox( 
		OutDrawElements,
		LayerId,
		SliderGeometry.ToPaintGeometry(HandleTopLeftPoint, Style->NormalThumbImage.ImageSize),
		LockedAttribute.Get() ? &Style->DisabledThumbImage : &Style->NormalThumbImage,
		RotatedClippingRect,
		DrawEffects,
		SliderHandleColor.Get().GetColor(InWidgetStyle) * InWidgetStyle.GetColorAndOpacityTint()
	);

	return LayerId;
}
Beispiel #15
0
FPaintGeometry FGeometry::ToOffsetPaintGeometry(const FVector2D& LocalOffset) const
{
	return ToPaintGeometry(FSlateLayoutTransform(LocalOffset));
}
Beispiel #16
0
FPaintGeometry FGeometry::ToPaintGeometry(const FVector2D& LocalOffset, const FVector2D& LocalSize, float LocalScale) const
{
	// Since ChildOffset is given as a LocalSpaceOffset, we MUST convert this offset into the space of the parent to construct a valid layout transform.
	// The extra TransformPoint below does this by converting the local offset to an offset in parent space.
	return ToPaintGeometry(LocalSize, FSlateLayoutTransform(LocalScale, TransformPoint(LocalScale, LocalOffset)));
}
Beispiel #17
0
FArrangedWidget FGeometry::MakeChild( const TSharedRef<SWidget>& ChildWidget, const FVector2D& ChildOffset, const FVector2D& LocalSize, float ChildScale) const
{
	// Since ChildOffset is given as a LocalSpaceOffset, we MUST convert this offset into the space of the parent to construct a valid layout transform.
	// The extra TransformPoint below does this by converting the local offset to an offset in parent space.
	return MakeChild(ChildWidget, LocalSize, FSlateLayoutTransform(ChildScale, TransformPoint(ChildScale, ChildOffset)));
}
int32 FSlateTextHighlightRunRenderer::OnPaint( const FPaintArgs& Args, const FTextLayout::FLineView& Line, const TSharedRef< ISlateRun >& Run, const TSharedRef< ILayoutBlock >& Block, const FTextBlockStyle& DefaultStyle, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const 
{
	FVector2D Location( Block->GetLocationOffset() );
	Location.Y = Line.Offset.Y;

	// 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);

	// Draw the actual highlight rectangle
	FSlateDrawElement::MakeBox(
		OutDrawElements,
		++LayerId,
		AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, FVector2D( Block->GetSize().X, Line.Size.Y )), FSlateLayoutTransform(TransformPoint(InverseScale, Location))), 
		&DefaultStyle.HighlightShape,
		MyClippingRect,
		bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect,
		InWidgetStyle.GetColorAndOpacityTint() * DefaultStyle.HighlightColor
		);

	FLinearColor InvertedHighlightColor = FLinearColor::White - DefaultStyle.HighlightColor;
	InvertedHighlightColor.A = InWidgetStyle.GetForegroundColor().A;

	FWidgetStyle WidgetStyle( InWidgetStyle );
	WidgetStyle.SetForegroundColor( InvertedHighlightColor );

	return Run->OnPaint( Args, Line, Block, DefaultStyle, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, WidgetStyle, bParentEnabled );
}
int32 SWidget::Paint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	INC_DWORD_STAT(STAT_SlateNumPaintedWidgets);
	SLATE_CYCLE_COUNTER_SCOPE_CUSTOM_DETAILED(SLATE_STATS_DETAIL_LEVEL_MED, GSlateOnPaint, GetType());

	// Save the current layout cache we're associated with (if any)
	LayoutCache = Args.GetLayoutCache();

	// Record if we're part of a volatility pass, this is critical for ensuring we don't report a child
	// of a volatile widget as non-volatile, causing the invalidation panel to do work that's not required.
	bInheritedVolatility = Args.IsVolatilityPass();

	// If this paint pass is to cache off our geometry, but we're a volatile widget,
	// record this widget as volatile in the draw elements so that we get our own tick/paint 
	// pass later when the layout cache draws.
	if ( Args.IsCaching() && IsVolatile() )
	{
		const int32 VolatileLayerId = LayerId + 1;
		OutDrawElements.QueueVolatilePainting(
			FSlateWindowElementList::FVolatilePaint(SharedThis(this), Args, AllottedGeometry, MyClippingRect, VolatileLayerId, InWidgetStyle, bParentEnabled));

		return VolatileLayerId;
	}

	if ( bFoldTick && bCanTick )
	{
		FGeometry TickGeometry = AllottedGeometry;
		TickGeometry.AppendTransform( FSlateLayoutTransform(Args.GetWindowToDesktopTransform()) );

		SWidget* MutableThis = const_cast<SWidget*>(this);
		MutableThis->ExecuteActiveTimers( Args.GetCurrentTime(), Args.GetDeltaTime() );
		MutableThis->Tick( TickGeometry, Args.GetCurrentTime(), Args.GetDeltaTime() );
	}

	// Record hit test geometry, but only if we're not caching.
	const FPaintArgs UpdatedArgs = Args.RecordHittestGeometry(this, AllottedGeometry, MyClippingRect);

	// Paint the geometry of this widget.
	int32 NewLayerID = OnPaint(UpdatedArgs, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);

	// Check if we need to show the keyboard focus ring, this is only necessary if the widget could be focused.
	if ( bCanSupportFocus && SupportsKeyboardFocus() )
	{
		bool bShowUserFocus = FSlateApplicationBase::Get().ShowUserFocus(SharedThis(this));
		if (bShowUserFocus)
		{
			const FSlateBrush* BrushResource = GetFocusBrush();

			if (BrushResource != nullptr)
			{
				FSlateDrawElement::MakeBox(
					OutDrawElements,
					NewLayerID,
					AllottedGeometry.ToPaintGeometry(),
					BrushResource,
					MyClippingRect,
					ESlateDrawEffect::None,
					BrushResource->GetTint(InWidgetStyle)
					);
			}
		}
	}

	return NewLayerID;
}
int32 SClippingHorizontalBox::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
	// Get the clipped children info
	FArrangedChildren ClippedArrangedChildren(EVisibility::Visible);
	ArrangeChildren(AllottedGeometry, ClippedArrangedChildren);
	
	// Get the non-clipped children info
	// @todo umg: One should not call the virtual OnArrangeChildren, one should only call ArrangeChildren.
	FArrangedChildren ArrangedChildren(EVisibility::Visible);
	SBoxPanel::OnArrangeChildren(AllottedGeometry, ArrangedChildren);
	
	if ((ClippedArrangedChildren.Num() != 0) && (ArrangedChildren.Num() != 0))
	{
		int32 IndexClippedAt = ClippedArrangedChildren.Num() - 1;
		const FArrangedWidget& LastCippedChild = ClippedArrangedChildren[IndexClippedAt];
		const FArrangedWidget& FirstChild = ArrangedChildren[0];
		const FArrangedWidget& LastChild = ArrangedChildren[ArrangedChildren.Num() - 1];
		float BorderLocalWidth = AllottedGeometry.Size.X;
	
		// If only the last child/block, which is the wrap button, is being clipped
		if (IndexClippedAt == ArrangedChildren.Num() - 2)
		{
			// Only recalculate the alloted geometry size if said size is fitted to the toolbar/menubar
			if (FMath::TruncToInt(AllottedGeometry.AbsolutePosition.X + AllottedGeometry.Size.X * AllottedGeometry.Scale) <= FMath::TruncToInt(LastChild.Geometry.AbsolutePosition.X + LastChild.Geometry.Size.X * LastChild.Geometry.Scale))
			{
				// Calculate the size of the custom border
				BorderLocalWidth = (LastCippedChild.Geometry.AbsolutePosition.X + LastCippedChild.Geometry.Size.X * LastCippedChild.Geometry.Scale - FirstChild.Geometry.AbsolutePosition.X) / AllottedGeometry.Scale;
			}
		}
		else
		{
			// Children/blocks are being clipped, calculate the size of the custom border
			const FArrangedWidget& NextChild = (IndexClippedAt + 1 < ClippedArrangedChildren.Num())? ClippedArrangedChildren[IndexClippedAt + 1]: LastCippedChild;
			BorderLocalWidth = (NextChild.Geometry.AbsolutePosition.X + NextChild.Geometry.Size.X * NextChild.Geometry.Scale - FirstChild.Geometry.AbsolutePosition.X) / AllottedGeometry.Scale;
		}
	
		bool bEnabled = ShouldBeEnabled( bParentEnabled );
		ESlateDrawEffect::Type DrawEffects = !bEnabled ? ESlateDrawEffect::DisabledEffect : ESlateDrawEffect::None;
		FSlateColor BorderBackgroundColor = FLinearColor::White;

		// Draw the custom border
		FSlateDrawElement::MakeBox(
			OutDrawElements,
			LayerId,
			AllottedGeometry.ToPaintGeometry(FVector2D(BorderLocalWidth, AllottedGeometry.Size.Y), FSlateLayoutTransform()),
			BackgroundBrush,
			MyClippingRect,
			DrawEffects,
			BackgroundBrush->GetTint( InWidgetStyle ) * InWidgetStyle.GetColorAndOpacityTint() * BorderBackgroundColor.GetColor(InWidgetStyle)
			);
	}

	return SHorizontalBox::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
}
int32 FSlateHyperlinkRun::OnPaint( const FPaintArgs& Args, const FTextLayout::FLineView& Line, const TSharedRef< ILayoutBlock >& Block, const FTextBlockStyle& DefaultStyle, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const 
{
	const TSharedRef< FWidgetLayoutBlock > WidgetBlock = StaticCastSharedRef< FWidgetLayoutBlock >( Block );

	// 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);

	const FGeometry WidgetGeometry = AllottedGeometry.MakeChild(TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset())));
	return WidgetBlock->GetWidget()->Paint( Args, WidgetGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled );
}
Beispiel #22
0
void SMenuAnchor::OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const
{
	ArrangeSingleChild( AllottedGeometry, ArrangedChildren, Children[0], FVector2D::UnitVector );
	const TSharedPtr<SWindow> PresentingWindow = PopupWindowPtr.Pin();
	if (IsOpenAndReusingWindow() && PresentingWindow.IsValid())
	{
		const FPopupPlacement LocalPlacement(AllottedGeometry, Children[1].GetWidget()->GetDesiredSize(), Placement.Get());
		ArrangedChildren.AddWidget(AllottedGeometry.MakeChild(Children[1].GetWidget(), LocalPlacement.LocalPopupSize, FSlateLayoutTransform(LocalPopupPosition)));
	}
}
void FSlateWidgetRun::ArrangeChildren( const TSharedRef< ILayoutBlock >& Block, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const
{
	// 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);

	ArrangedChildren.AddWidget(
		AllottedGeometry.MakeChild(Info.Widget, TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset())))
		);
}
int32 FSlateWidgetRun::OnPaint( const FPaintArgs& Args, const FTextLayout::FLineView& Line, const TSharedRef< ILayoutBlock >& Block, const FTextBlockStyle& DefaultStyle, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const 
{
	// 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);

	const FVector2D DesiredWidgetSize = Info.Widget->GetDesiredSize();
	if (DesiredWidgetSize != WidgetSize)
	{
		WidgetSize = DesiredWidgetSize;

		const TSharedPtr<FTextLayout> TextLayoutPtr = TextLayout.Pin();
		if (TextLayoutPtr.IsValid())
		{
			TextLayoutPtr->DirtyRunLayout(SharedThis(this));
		}
	}

	const FGeometry WidgetGeometry = AllottedGeometry.MakeChild(TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset())));
	return Info.Widget->Paint( Args, WidgetGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled );
}
int32 SColorGradientEditor::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
	const TSharedRef< FSlateFontMeasure > FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();

	if( CurveOwner )
	{
		// Split the geometry into areas for stops and the gradient
		FGeometry ColorMarkAreaGeometry = GetColorMarkAreaGeometry( AllottedGeometry );
		FGeometry AlphaMarkAreaGeometry = GetAlphaMarkAreaGeometry( AllottedGeometry );

		FGeometry GradientAreaGeometry = AllottedGeometry.MakeChild( FVector2D(0.0f, 16.0f), FVector2D( AllottedGeometry.Size.X, AllottedGeometry.Size.Y - 30.0f ) );

		bool bEnabled = ShouldBeEnabled( bParentEnabled );
		ESlateDrawEffect::Type DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;

		// Pixel to value input converter
		FTrackScaleInfo ScaleInfo(ViewMinInput.Get(),  ViewMaxInput.Get(), 0.0f, 1.0f, GradientAreaGeometry.Size);

		// The start and end location in slate units of the area to draw
		int32 Start = 0;
		int32 Finish = FMath::TruncToInt( AllottedGeometry.Size.X );

		TArray<FSlateGradientStop> Stops;

		// If no alpha keys are available, treat the curve as being completely opaque for drawing purposes
		bool bHasAnyAlphaKeys = CurveOwner->HasAnyAlphaKeys(); 

		// If any transpareny (A < 1) is found, we'll draw a checkerboard to visualize the color with alpha
		bool bHasTransparency = false;

		// Sample the curve every 2 units.  THe curve could be non-linear so sampling at each stop would display an incorrect gradient
		for( int32 CurrentStep = Start; CurrentStep < Finish; CurrentStep+=2 )
		{
			// Figure out the time from the current screen unit
			float Time = ScaleInfo.LocalXToInput(CurrentStep);

			// Sample the curve
			FLinearColor Color = CurveOwner->GetLinearColorValue( Time );
			if( !bHasAnyAlphaKeys )
			{
				// Only show alpha if there is at least one key.  For some curves, alpha may not be important
				Color.A = 1.0f;
				bHasTransparency = false;
			}
			else
			{
				bHasTransparency |= (Color.A < 1.0f);
			}
	
			Stops.Add( FSlateGradientStop( FVector2D( CurrentStep, 0.0f ), Color ) );
		}

		if( Stops.Num() > 0 )
		{
			if( bHasTransparency )
			{
				// Draw a checkerboard behind there is any transparency visible
				FSlateDrawElement::MakeBox
				( 
					OutDrawElements,
					LayerId,
					GradientAreaGeometry.ToPaintGeometry(),
					FEditorStyle::GetBrush("Checkerboard"),
					MyClippingRect,
					DrawEffects 
				);
			}

			// Draw the color gradient
			FSlateDrawElement::MakeGradient
			( 
				OutDrawElements, 
				LayerId,
				GradientAreaGeometry.ToPaintGeometry(),
				Stops,
				Orient_Vertical,
				MyClippingRect,
				DrawEffects,
				false
			);	
		}

		// Get actual editable stop marks
		TArray<FGradientStopMark> ColorMarks;
		TArray<FGradientStopMark> AlphaMarks;
		GetGradientStopMarks( ColorMarks, AlphaMarks );

		// Draw each color stop
		for( int32 ColorIndex = 0; ColorIndex < ColorMarks.Num(); ++ColorIndex )
		{
			const FGradientStopMark& Mark = ColorMarks[ColorIndex];

			float XVal = ScaleInfo.InputToLocalX( Mark.Time );

			// Dont draw stops which are not visible
			if( XVal >= 0 && XVal <= ColorMarkAreaGeometry.Size.X )
			{
				FLinearColor Color = CurveOwner->GetLinearColorValue( Mark.Time );
				Color.A = 1.0f;
				DrawGradientStopMark( Mark, ColorMarkAreaGeometry, XVal, Color, OutDrawElements, LayerId, MyClippingRect, DrawEffects, true, InWidgetStyle );
			}

		}

		// Draw each alpha stop
		for( int32 ColorIndex = 0; ColorIndex < AlphaMarks.Num(); ++ColorIndex )
		{
			const FGradientStopMark& Mark = AlphaMarks[ColorIndex];

			float XVal = ScaleInfo.InputToLocalX( Mark.Time );
		
			// Dont draw stops which are not visible
			if( XVal >= 0 && XVal <= AlphaMarkAreaGeometry.Size.X )
			{
				float Alpha = CurveOwner->GetLinearColorValue( Mark.Time ).A;
				DrawGradientStopMark( Mark, AlphaMarkAreaGeometry, XVal, FLinearColor( Alpha, Alpha, Alpha, 1.0f ), OutDrawElements, LayerId, MyClippingRect, DrawEffects, false, InWidgetStyle );
			}

		}

		// Draw some hint messages about how to add stops if no stops exist
		if( ColorMarks.Num() == 0 && AlphaMarks.Num() == 0 && IsEditingEnabled.Get() == true )
		{
			static FString GradientColorMessage( LOCTEXT("ClickToAddColorStop", "Click in this area add color stops").ToString() );
			static FString GradientAlphaMessage( LOCTEXT("ClickToAddAlphaStop", "Click in this area add opacity stops").ToString() );
				
			// Draw the text centered in the color region
			{
				FVector2D StringSize = FontMeasureService->Measure( GradientColorMessage, FSlateFontInfo( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 8 ) );
				FPaintGeometry PaintGeom = ColorMarkAreaGeometry.ToPaintGeometry(FSlateLayoutTransform(FVector2D((ColorMarkAreaGeometry.Size.X - StringSize.X) * 0.5f, 1.0f)));

				FSlateDrawElement::MakeText
				( 
					OutDrawElements, 
					LayerId,
					PaintGeom,
					GradientColorMessage,
					FSlateFontInfo( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 8 ),
					MyClippingRect,
					DrawEffects,
					FLinearColor( .5f, .5f, .5f, .85f )
				);	
			}

			// Draw the text centered in the alpha region
			{
				FVector2D StringSize = FontMeasureService->Measure( GradientAlphaMessage, FSlateFontInfo( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 8 ) );
				FPaintGeometry PaintGeom = AlphaMarkAreaGeometry.ToPaintGeometry(FSlateLayoutTransform(FVector2D((AlphaMarkAreaGeometry.Size.X - StringSize.X) * 0.5f, 1.0f)));

				FSlateDrawElement::MakeText
				( 
					OutDrawElements, 
					LayerId,
					PaintGeom,
					GradientAlphaMessage,
					FSlateFontInfo( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 8 ),
					MyClippingRect,
					DrawEffects,
					FLinearColor( .5f, .5f, .5f, .85f )
				);	
			}
		}
		
	}

	return LayerId;
}
Beispiel #26
0
int32 FSlateTextLayout::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
	const FSlateRect ClippingRect = AllottedGeometry.GetClippingRect().IntersectionWith(MyClippingRect);
	const ESlateDrawEffect::Type DrawEffects = bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;

	static bool ShowDebug = false;
	FLinearColor BlockHue( 0, 1.0f, 1.0f, 0.5 );
	int32 HighestLayerId = LayerId;

	for (const FTextLayout::FLineView& LineView : LineViews)
	{
		// Is this line visible?
		const FSlateRect LineViewRect(AllottedGeometry.AbsolutePosition + LineView.Offset, AllottedGeometry.AbsolutePosition + LineView.Offset + LineView.Size);
		const FSlateRect VisibleLineView = ClippingRect.IntersectionWith(LineViewRect);
		if (VisibleLineView.IsEmpty())
		{
			continue;
		}

		// Render any underlays for this line
		const int32 HighestUnderlayLayerId = OnPaintHighlights( Args, LineView, LineView.UnderlayHighlights, DefaultTextStyle, AllottedGeometry, ClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled );

		const int32 BlockDebugLayer = HighestUnderlayLayerId;
		const int32 TextLayer = BlockDebugLayer + 1;
		int32 HighestBlockLayerId = TextLayer;

		// Render every block for this line
		for (const TSharedRef< ILayoutBlock >& Block : LineView.Blocks)
		{
			if ( ShowDebug )
			{
				BlockHue.R += 50.0f;

				// 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);

				FSlateDrawElement::MakeBox(
					OutDrawElements, 
					BlockDebugLayer,
					AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset()))),
					&DefaultTextStyle.HighlightShape,
					ClippingRect,
					DrawEffects,
					InWidgetStyle.GetColorAndOpacityTint() * BlockHue.HSVToLinearRGB()
					);
			}

			const TSharedRef< ISlateRun > Run = StaticCastSharedRef< ISlateRun >( Block->GetRun() );

			int32 HighestRunLayerId = TextLayer;
			const TSharedPtr< ISlateRunRenderer > RunRenderer = StaticCastSharedPtr< ISlateRunRenderer >( Block->GetRenderer() );
			if ( RunRenderer.IsValid() )
			{
				HighestRunLayerId = RunRenderer->OnPaint( Args, LineView, Run, Block, DefaultTextStyle, AllottedGeometry, ClippingRect, OutDrawElements, TextLayer, InWidgetStyle, bParentEnabled );
			}
			else
			{
				HighestRunLayerId = Run->OnPaint( Args, LineView, Block, DefaultTextStyle, AllottedGeometry, ClippingRect, OutDrawElements, TextLayer, InWidgetStyle, bParentEnabled );
			}

			HighestBlockLayerId = FMath::Max( HighestBlockLayerId, HighestRunLayerId );
		}

		// Render any overlays for this line
		const int32 HighestOverlayLayerId = OnPaintHighlights( Args, LineView, LineView.OverlayHighlights, DefaultTextStyle, AllottedGeometry, ClippingRect, OutDrawElements, HighestBlockLayerId, InWidgetStyle, bParentEnabled );
		HighestLayerId = FMath::Max( HighestLayerId, HighestOverlayLayerId );
	}

	return HighestLayerId;
}
int32 SInvalidationPanel::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
	if ( GetCanCache() )
	{
		const bool bWasCachingNeeded = bNeedsCaching;

		if ( bNeedsCaching )
		{
			SInvalidationPanel* MutableThis = const_cast<SInvalidationPanel*>( this );

			// Always set the caching flag to false first, during the paint / tick pass we may change something
			// to volatile and need to re-cache.
			bNeedsCaching = false;

			bIsInvalidating = true;

			if ( !CachedWindowElements.IsValid() || CachedWindowElements->GetWindow() != OutDrawElements.GetWindow() )
			{
				CachedWindowElements = MakeShareable(new FSlateWindowElementList(OutDrawElements.GetWindow()));
			}
			else
			{
				CachedWindowElements->Reset();
			}

			// Reset the cached node pool index so that we effectively reset the pool.
			LastUsedCachedNodeIndex = 0;

			RootCacheNode = CreateCacheNode();
			RootCacheNode->Initialize(Args, SharedThis(MutableThis), AllottedGeometry, MyClippingRect);

			//TODO: When SWidget::Paint is called don't drag self if volatile, and we're doing a cache pass.
			CachedMaxChildLayer = SCompoundWidget::OnPaint(
				Args.EnableCaching(SharedThis(MutableThis), RootCacheNode, true, false),
				AllottedGeometry,
				MyClippingRect,
				*CachedWindowElements.Get(),
				LayerId,
				InWidgetStyle,
				bParentEnabled);

			if ( bCacheRelativeTransforms )
			{
				CachedAbsolutePosition = AllottedGeometry.Position;
			}

			LastLayerId = LayerId;
			LastHitTestIndex = Args.GetLastHitTestIndex();

			bIsInvalidating = false;
		}

		// The hit test grid is actually populated during the initial cache phase, so don't bother
		// recording the hit test geometry on the same frame that we regenerate the cache.
		if ( bWasCachingNeeded == false )
		{
			RootCacheNode->RecordHittestGeometry(Args.GetGrid(), Args.GetLastHitTestIndex());
		}

		if ( bCacheRelativeTransforms )
		{
			FVector2D DeltaPosition = AllottedGeometry.Position - CachedAbsolutePosition;

			const TArray<FSlateDrawElement>& CachedElements = CachedWindowElements->GetDrawElements();
			const int32 CachedElementCount = CachedElements.Num();
			for ( int32 Index = 0; Index < CachedElementCount; Index++ )
			{
				const FSlateDrawElement& LocalElement = CachedElements[Index];
				FSlateDrawElement AbsElement = LocalElement;

				AbsElement.SetPosition(LocalElement.GetPosition() + DeltaPosition);
				AbsElement.SetClippingRect(LocalElement.GetClippingRect().OffsetBy(DeltaPosition));

				OutDrawElements.AddItem(AbsElement);
			}
		}
		else
		{
			OutDrawElements.AppendDrawElements(CachedWindowElements->GetDrawElements());
		}

		int32 OutMaxChildLayer = CachedMaxChildLayer;

		// Paint the volatile elements
		if ( CachedWindowElements.IsValid() )
		{
			OutMaxChildLayer = FMath::Max(CachedMaxChildLayer, CachedWindowElements->PaintVolatile(OutDrawElements));
		}

#if !UE_BUILD_SHIPPING

		if ( IsInvalidationDebuggingEnabled() )
		{
			// Draw a green or red border depending on if we were invalidated this frame.
			{
				check(Args.IsCaching() == false);
				//const bool bShowOutlineAsCached = Args.IsCaching() || bWasCachingNeeded == false;
				const FLinearColor DebugTint = bWasCachingNeeded ? FLinearColor::Red : FLinearColor::Green;

				FGeometry ScaledOutline = AllottedGeometry.MakeChild(FVector2D(0, 0), AllottedGeometry.GetLocalSize() * AllottedGeometry.Scale, Inverse(AllottedGeometry.Scale));

				FSlateDrawElement::MakeBox(
					OutDrawElements,
					++OutMaxChildLayer,
					ScaledOutline.ToPaintGeometry(),
					FCoreStyle::Get().GetBrush(TEXT("Debug.Border")),
					MyClippingRect,
					ESlateDrawEffect::None,
					DebugTint
				);
			}

			// Draw a yellow outline around any volatile elements.
			const TArray< TSharedRef<FSlateWindowElementList::FVolatilePaint> >& VolatileElements = CachedWindowElements->GetVolatileElements();
			for ( const TSharedRef<FSlateWindowElementList::FVolatilePaint>& VolatileElement : VolatileElements )
			{
				FSlateDrawElement::MakeBox(
					OutDrawElements,
					++OutMaxChildLayer,
					VolatileElement->GetGeometry().ToPaintGeometry(),
					FCoreStyle::Get().GetBrush(TEXT("FocusRectangle")),
					MyClippingRect,
					ESlateDrawEffect::None,
					FLinearColor::Yellow
				);
			}

			// Draw a white flash for any widget that invalidated us this frame.
			for ( TWeakPtr<SWidget> Invalidator : InvalidatorWidgets )
			{
				TSharedPtr<SWidget> SafeInvalidator = Invalidator.Pin();
				if ( SafeInvalidator.IsValid() )
				{
					FWidgetPath WidgetPath;
					if ( FSlateApplication::Get().GeneratePathToWidgetUnchecked(SafeInvalidator.ToSharedRef(), WidgetPath, EVisibility::All) )
					{
						FArrangedWidget ArrangedWidget = WidgetPath.FindArrangedWidget(SafeInvalidator.ToSharedRef()).Get(FArrangedWidget::NullWidget);
						ArrangedWidget.Geometry.AppendTransform( FSlateLayoutTransform(Inverse(Args.GetWindowToDesktopTransform())) );

						FSlateDrawElement::MakeBox(
							OutDrawElements,
							++OutMaxChildLayer,
							ArrangedWidget.Geometry.ToPaintGeometry(),
							FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
							MyClippingRect,
							ESlateDrawEffect::None,
							FLinearColor::White.CopyWithNewOpacity(0.6f)
						);
					}
				}
			}

			InvalidatorWidgets.Reset();
		}

#endif

		return OutMaxChildLayer;
	}
	else
	{
		return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
	}
}
int32 FThumbnailSection::OnPaintSection( FSequencerSectionPainter& InPainter ) const
{
	if (!GetDefault<UMovieSceneUserThumbnailSettings>()->bDrawThumbnails)
	{
		return InPainter.LayerId;
	}

	static const float SectionThumbnailPadding = 4.f;

	const ESlateDrawEffect::Type DrawEffects = InPainter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;

	int32 LayerId = InPainter.LayerId;

	const FGeometry& SectionGeometry = InPainter.SectionGeometry;

	// @todo Sequencer: Need a way to visualize the key here

	const TRange<float> VisibleRange = SequencerPtr.Pin()->GetViewRange();
	const TRange<float> SectionRange = Section->IsInfinite() ? VisibleRange : Section->GetRange();

	const float TimePerPx = SectionRange.Size<float>() / InPainter.SectionGeometry.GetLocalSize().X;
	
	FSlateRect ThumbnailClipRect = SectionGeometry.GetClippingRect().InsetBy(FMargin(SectionThumbnailPadding, 0.f)).IntersectionWith(InPainter.SectionClippingRect);

	for (const TSharedPtr<FTrackEditorThumbnail>& Thumbnail : ThumbnailCache.GetThumbnails())
	{
		const float Fade = Thumbnail->bHasFinishedDrawing ? Thumbnail->GetFadeInCurve() : 1.f;
		FIntPoint ThumbnailSize = Thumbnail->GetSize();
		
		// Calculate the paint geometry for this thumbnail
		TOptional<float> SingleReferenceFrame = ThumbnailCache.GetSingleReferenceFrame();

		// Single thumbnails are always draw at the start of the section, clamped to the visible range
		// Thumbnail sequences draw relative to their actual position in the sequence
		const int32 Offset = SingleReferenceFrame.IsSet() ?
			FMath::Max((VisibleRange.GetLowerBoundValue() - SectionRange.GetLowerBoundValue()) / TimePerPx, 0.f) + SectionThumbnailPadding :
			(Thumbnail->GetTimeRange().GetLowerBoundValue() - SectionRange.GetLowerBoundValue()) / TimePerPx;

		FPaintGeometry PaintGeometry = SectionGeometry.ToPaintGeometry(
			ThumbnailSize,
			FSlateLayoutTransform(
				SectionGeometry.Scale,
				FVector2D( Offset, (SectionGeometry.GetLocalSize().Y - ThumbnailSize.Y)*.5f)
			)
		);

		if (Fade <= 1.f)
		{
			FSlateDrawElement::MakeViewport(
				InPainter.DrawElements,
				LayerId,
				PaintGeometry,
				Thumbnail,
				ThumbnailClipRect,
				DrawEffects | ESlateDrawEffect::NoGamma,
				FLinearColor(1.f, 1.f, 1.f, 1.f - Fade)
				);
		}
	}

	return LayerId + 2;
}
int32 FSlateTextUnderlineLineHighlighter::OnPaint(const FPaintArgs& Args, const FTextLayout::FLineView& Line, const float OffsetX, const float Width, const FTextBlockStyle& DefaultStyle, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	TSharedRef<FSlateFontCache> FontCache = FSlateApplication::Get().GetRenderer()->GetFontCache();

	const uint16 MaxHeight = FontCache->GetMaxCharacterHeight(FontInfo, AllottedGeometry.Scale);
	const int16 Baseline = FontCache->GetBaseline(FontInfo, AllottedGeometry.Scale);

	int16 UnderlinePos, UnderlineThickness;
	FontCache->GetUnderlineMetrics(FontInfo, AllottedGeometry.Scale, UnderlinePos, UnderlineThickness);

	const FVector2D Location(Line.Offset.X + OffsetX, Line.Offset.Y + MaxHeight + Baseline - (UnderlinePos * 0.5f));
	const FVector2D Size(Width, FMath::Max<int16>(1, UnderlineThickness));

	// 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);

	if (Size.X)
	{
		const FLinearColor LineColorAndOpacity = ColorAndOpacity.GetColor(InWidgetStyle);

		const bool ShouldDropShadow = ShadowColorAndOpacity.A > 0.f && ShadowOffset.SizeSquared() > 0.f;

		// A negative shadow offset should be applied as a positive offset to the underline to avoid clipping issues
		const FVector2D DrawShadowOffset(
			(ShadowOffset.X > 0.0f) ? ShadowOffset.X * AllottedGeometry.Scale : 0.0f,
			(ShadowOffset.Y > 0.0f) ? ShadowOffset.Y * AllottedGeometry.Scale : 0.0f
			);
		const FVector2D DrawUnderlineOffset(
			(ShadowOffset.X < 0.0f) ? -ShadowOffset.X * AllottedGeometry.Scale : 0.0f,
			(ShadowOffset.Y < 0.0f) ? -ShadowOffset.Y * AllottedGeometry.Scale : 0.0f
			);

		// Draw the optional shadow
		if (ShouldDropShadow)
		{
			FSlateDrawElement::MakeBox(
				OutDrawElements,
				++LayerId,
				AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, Size), FSlateLayoutTransform(TransformPoint(InverseScale, Location + DrawShadowOffset))),
				&UnderlineBrush,
				MyClippingRect,
				bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect,
				ShadowColorAndOpacity * InWidgetStyle.GetColorAndOpacityTint()
				);
		}

		// Draw underline
		FSlateDrawElement::MakeBox(
			OutDrawElements,
			++LayerId,
			AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, Size), FSlateLayoutTransform(TransformPoint(InverseScale, Location + DrawUnderlineOffset))),
			&UnderlineBrush,
			MyClippingRect,
			bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect,
			LineColorAndOpacity * InWidgetStyle.GetColorAndOpacityTint()
			);
	}

	return LayerId;
}