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 SInvalidationPanel::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) { if ( GetCanCache() ) { const bool bWasCachingNeeded = bNeedsCaching; if ( bNeedsCaching == false ) { if ( bCacheRelativeTransforms ) { // If the container we're in has changed in either scale or the rotation matrix has changed, if ( AllottedGeometry.GetAccumulatedLayoutTransform().GetScale() != LastAllottedGeometry.GetAccumulatedLayoutTransform().GetScale() || AllottedGeometry.GetAccumulatedRenderTransform().GetMatrix() != LastAllottedGeometry.GetAccumulatedRenderTransform().GetMatrix() ) { InvalidateCache(); } } else { // If the container we're in has changed in any way we need to invalidate for sure. if ( AllottedGeometry.GetAccumulatedLayoutTransform() != LastAllottedGeometry.GetAccumulatedLayoutTransform() || AllottedGeometry.GetAccumulatedRenderTransform() != LastAllottedGeometry.GetAccumulatedRenderTransform() ) { InvalidateCache(); } } if ( AllottedGeometry.GetLocalSize() != LastAllottedGeometry.GetLocalSize() ) { InvalidateCache(); } } LastAllottedGeometry = AllottedGeometry; // TODO We may be double pre-passing here, if the invalidation happened at the end of last frame, // we'll have already done one pre-pass before getting here. if ( bNeedsCaching ) { SlatePrepass(AllottedGeometry.Scale); CachePrepass(SharedThis(this)); } } }
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))); }
void SMenuAnchor::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) { TSharedPtr<SWindow> PopupWindow = PopupWindowPtr.Pin(); if ( PopupWindow.IsValid() && IsOpenViaCreatedWindow() ) { // Figure out where our attached pop-up window should be placed. const FVector2D PopupContentDesiredSize = PopupWindow->GetContent()->GetDesiredSize(); FGeometry PopupGeometry = ComputeMenuPlacement( AllottedGeometry, PopupContentDesiredSize, Placement.Get() ); const FVector2D NewPosition = PopupGeometry.LocalToAbsolute(FVector2D::ZeroVector); // For the CreateWindow case, don't transform the size; it will always use the ApplicationScale const FVector2D NewSize = PopupGeometry.GetLocalSize(); const FSlateRect NewShape = FSlateRect( NewPosition.X, NewPosition.Y, NewPosition.X + NewSize.X, NewPosition.Y + NewSize.Y ); // We made a window for showing the popup. // Update the window's position! if (false /*PopupWindow->IsMorphing()*/ ) { if( NewShape != PopupWindow->GetMorphTargetShape() ) { // Update the target shape PopupWindow->UpdateMorphTargetShape( NewShape ); // Set size immediately if not morphing size if(!PopupWindow->IsMorphingSize()) { PopupWindow->ReshapeWindow( PopupWindow->GetPositionInScreen(), NewSize ); } } } else { const FVector2D WindowPosition = PopupWindow->GetPositionInScreen(); const FVector2D WindowSize = PopupWindow->GetSizeInScreen(); if ( NewPosition != WindowPosition || NewSize != WindowSize ) { #if PLATFORM_LINUX // @FIXME: for some reason, popups reshaped here do not trigger OnWindowMoved() callback, // so we manually set cached position to where we expect it to be. Note the order of operations - (before Reshape) - // still giving the callback a chance to change it. // This needs to be investigated (tracked as TTP #347674). PopupWindow->SetCachedScreenPosition( NewPosition ); #endif // PLATFORM_LINUX PopupWindow->ReshapeWindow( NewShape ); } } } else if (PopupWindow.IsValid() && IsOpenAndReusingWindow()) { // Ideally, do this in OnArrangeChildren(); currently not possible because OnArrangeChildren() // can be called in DesktopSpace or WindowSpace, and we will not know which version of the Window // geometry to use. Tick() is always in DesktopSpace, so cache the solution here and just use // it in OnArrangeChildren(). const FPopupPlacement LocalPlacement(AllottedGeometry, Children[1].GetWidget()->GetDesiredSize(), Placement.Get()); const FSlateRect WindowRectLocalSpace = TransformRect(Inverse(AllottedGeometry.GetAccumulatedLayoutTransform()), PopupWindow->GetClientRectInScreen()); const FVector2D FittedPlacement = ComputePopupFitInRect( LocalPlacement.AnchorLocalSpace, FSlateRect(LocalPlacement.LocalPopupOffset, LocalPlacement.LocalPopupOffset + LocalPlacement.LocalPopupSize), LocalPlacement.Orientation, WindowRectLocalSpace); LocalPopupPosition = FittedPlacement; ScreenPopupPosition = AllottedGeometry.GetAccumulatedLayoutTransform().TransformPoint(LocalPopupPosition); } /** The tick is ending, so the window was not dismissed this tick. */ bDismissedThisTick = false; }
/** SWidget interface */ virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override { // First paint the background { LayerId = PaintBackground(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); LayerId++; } FArrangedChildren ArrangedChildren(EVisibility::Visible); ArrangeChildren(AllottedGeometry, ArrangedChildren); // Draw the child nodes // When drawing a marquee, need a preview of what the selection will be. const auto* SelectionToVisualize = &(SelectionManager.SelectedNodes); FGraphPanelSelectionSet SelectionPreview; if (Marquee.IsValid()) { ApplyMarqueeSelection(Marquee, SelectionManager.SelectedNodes, SelectionPreview); SelectionToVisualize = &SelectionPreview; } int32 NodesLayerId = LayerId; for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SWorldTileItem> ChildNode = StaticCastSharedRef<SWorldTileItem>(CurWidget.Widget); ChildNode->bAffectedByMarquee = SelectionToVisualize->Contains(ChildNode->GetObjectBeingDisplayed()); LayerId = CurWidget.Widget->Paint(Args.WithNewParent(this), CurWidget.Geometry, MyClippingRect, OutDrawElements, NodesLayerId, InWidgetStyle, ShouldBeEnabled(bParentEnabled)); ChildNode->bAffectedByMarquee = false; } // Draw editable world bounds if (!WorldModel->IsSimulating()) { float ScreenSpaceSize = FLevelCollectionModel::EditableAxisLength()*GetZoomAmount()*2.f; FVector2D PaintSize = FVector2D(ScreenSpaceSize, ScreenSpaceSize); FVector2D PaintPosition = GraphCoordToPanelCoord(FVector2D::ZeroVector) - (PaintSize*0.5f); float Scale = 0.2f; // Scale down drawing border FSlateLayoutTransform LayoutTransform(Scale, AllottedGeometry.GetAccumulatedLayoutTransform().GetTranslation() + PaintPosition); FSlateRenderTransform RenderTransform(Scale, AllottedGeometry.GetAccumulatedRenderTransform().GetTranslation() + PaintPosition); FPaintGeometry EditableArea(LayoutTransform, RenderTransform, PaintSize/Scale); FLinearColor PaintColor = FLinearColor::Yellow; PaintColor.A = 0.4f; FSlateDrawElement::MakeBox( OutDrawElements, ++LayerId, EditableArea, FEditorStyle::GetBrush(TEXT("Graph.CompactNode.ShadowSelected")), MyClippingRect, ESlateDrawEffect::None, PaintColor ); } // Draw the marquee selection rectangle PaintMarquee(AllottedGeometry, MyClippingRect, OutDrawElements, ++LayerId); // Draw the software cursor PaintSoftwareCursor(AllottedGeometry, MyClippingRect, OutDrawElements, ++LayerId); if(WorldModel->IsSimulating()) { // Draw a surrounding indicator when PIE is active, to make it clear that the graph is read-only, etc... FSlateDrawElement::MakeBox( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), FEditorStyle::GetBrush(TEXT("Graph.PlayInEditor")), MyClippingRect ); } // Draw observer location { FVector ObserverPosition; FRotator ObserverRotation; if (WorldModel->GetObserverView(ObserverPosition, ObserverRotation)) { FVector2D ObserverPositionScreen = GraphCoordToPanelCoord(FVector2D(ObserverPosition.X, ObserverPosition.Y)); const FSlateBrush* CameraImage = FEditorStyle::GetBrush(TEXT("WorldBrowser.SimulationViewPositon")); FPaintGeometry PaintGeometry = AllottedGeometry.ToPaintGeometry( ObserverPositionScreen - CameraImage->ImageSize*0.5f, CameraImage->ImageSize ); FSlateDrawElement::MakeRotatedBox( OutDrawElements, ++LayerId, PaintGeometry, CameraImage, MyClippingRect, ESlateDrawEffect::None, FMath::DegreesToRadians(ObserverRotation.Yaw), CameraImage->ImageSize*0.5f, FSlateDrawElement::RelativeToElement ); } FVector PlayerPosition; FRotator PlayerRotation; if (WorldModel->GetPlayerView(PlayerPosition, PlayerRotation)) { FVector2D PlayerPositionScreen = GraphCoordToPanelCoord(FVector2D(PlayerPosition.X, PlayerPosition.Y)); const FSlateBrush* CameraImage = FEditorStyle::GetBrush(TEXT("WorldBrowser.SimulationViewPositon")); FPaintGeometry PaintGeometry = AllottedGeometry.ToPaintGeometry( PlayerPositionScreen - CameraImage->ImageSize*0.5f, CameraImage->ImageSize ); FSlateDrawElement::MakeRotatedBox( OutDrawElements, ++LayerId, PaintGeometry, CameraImage, MyClippingRect, ESlateDrawEffect::None, FMath::DegreesToRadians(PlayerRotation.Yaw), CameraImage->ImageSize*0.5f, FSlateDrawElement::RelativeToElement, FLinearColor(FColorList::Orange) ); } } LayerId = PaintScaleRuler(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); return LayerId; }
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; }