int32 SPanel::PaintArrangedChildren( const FPaintArgs& Args, const FArrangedChildren& ArrangedChildren, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { // Because we paint multiple children, we must track the maximum layer id that they produced in case one of our parents // wants to an overlay for all of its contents. int32 MaxLayerId = LayerId; const FPaintArgs NewArgs = Args.WithNewParent(this); for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { const FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; bool bWereOverlapping; FSlateRect ChildClipRect = MyClippingRect.IntersectionWith(CurWidget.Geometry.GetClippingRect(), bWereOverlapping); if ( bWereOverlapping ) { const int32 CurWidgetsMaxLayerId = CurWidget.Widget->Paint(NewArgs, CurWidget.Geometry, ChildClipRect, OutDrawElements, LayerId, InWidgetStyle, ShouldBeEnabled(bParentEnabled)); MaxLayerId = FMath::Max(MaxLayerId, CurWidgetsMaxLayerId); } } return MaxLayerId; }
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 FBehaviorTreeConnectionDrawingPolicy::Draw(TMap<TSharedRef<SWidget>, FArrangedWidget>& PinGeometries, FArrangedChildren& ArrangedNodes) { // Build an acceleration structure to quickly find geometry for the nodes NodeWidgetMap.Empty(); for (int32 NodeIndex = 0; NodeIndex < ArrangedNodes.Num(); ++NodeIndex) { FArrangedWidget& CurWidget = ArrangedNodes[NodeIndex]; TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget); NodeWidgetMap.Add(ChildNode->GetNodeObj(), NodeIndex); } // Now draw FConnectionDrawingPolicy::Draw(PinGeometries, ArrangedNodes); }
int32 SWidget::FindChildUnderPosition( const FArrangedChildren& Children, const FVector2D& ArrangedSpacePosition ) { const int32 NumChildren = Children.Num(); for( int32 ChildIndex=NumChildren-1; ChildIndex >= 0; --ChildIndex ) { const FArrangedWidget& Candidate = Children[ChildIndex]; const bool bCandidateUnderCursor = // Candidate is physically under the cursor Candidate.Geometry.IsUnderLocation( ArrangedSpacePosition ); if (bCandidateUnderCursor) { return ChildIndex; } } return INDEX_NONE; }
void SGraphPanel::OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const { SNodePanel::OnArrangeChildren(AllottedGeometry, ArrangedChildren); FArrangedChildren MyArrangedChildren(ArrangedChildren.GetFilter()); for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget); TArray<FOverlayWidgetInfo> OverlayWidgets = ChildNode->GetOverlayWidgets(false, CurWidget.Geometry.Size); for (int32 WidgetIndex = 0; WidgetIndex < OverlayWidgets.Num(); ++WidgetIndex) { FOverlayWidgetInfo& OverlayInfo = OverlayWidgets[WidgetIndex]; MyArrangedChildren.AddWidget(AllottedGeometry.MakeChild( OverlayInfo.Widget.ToSharedRef(), CurWidget.Geometry.Position + OverlayInfo.OverlayOffset, OverlayInfo.Widget->GetDesiredSize(), GetZoomAmount() )); } } ArrangedChildren.Append(MyArrangedChildren); }
/** * Move focus either forward on backward in the path level specified by PathLevel. * That is, this movement of focus will modify the subtree under Widgets(PathLevel). * * @param PathLevel The level in this WidgetPath whose focus to move. * @param MoveDirectin Move focus forward or backward? * * @return true if the focus moved successfully, false if we were unable to move focus */ bool FWidgetPath::MoveFocus(int32 PathLevel, EUINavigation NavigationType) { check(NavigationType == EUINavigation::Next || NavigationType == EUINavigation::Previous); const int32 MoveDirectionAsInt = (NavigationType == EUINavigation::Next) ? +1 : -1; if ( PathLevel == Widgets.Num()-1 ) { // We are the currently focused widget because we are at the very bottom of focus path. if (NavigationType == EUINavigation::Next) { // EFocusMoveDirection::Next implies descend, so try to find a focusable descendant. return ExtendPathTo( FFocusableWidgetMatcher() ); } else { // EFocusMoveDirection::Previous implies move focus up a level. return false; } } else if ( Widgets.Num() > 1 ) { // We are not the last widget in the path. // GOAL: Look for a focusable descendant to the left or right of the currently focused path. // Arrange the children so we can iterate through them regardless of widget type. FArrangedChildren ArrangedChildren(EVisibility::Visible); Widgets[PathLevel].Widget->ArrangeChildren( Widgets[PathLevel].Geometry, ArrangedChildren ); // Find the currently focused child among the children. int32 FocusedChildIndex = ArrangedChildren.FindItemIndex( Widgets[PathLevel+1] ); FocusedChildIndex = (FocusedChildIndex) % ArrangedChildren.Num() + MoveDirectionAsInt; // Now actually search for the widget. for( ; FocusedChildIndex < ArrangedChildren.Num() && FocusedChildIndex >= 0; FocusedChildIndex += MoveDirectionAsInt ) { // Neither disabled widgets nor their children can be focused. if ( ArrangedChildren[FocusedChildIndex].Widget->IsEnabled() ) { // Look for a focusable descendant. FArrangedChildren PathToFocusableChild = GeneratePathToWidget(FFocusableWidgetMatcher(), ArrangedChildren[FocusedChildIndex], NavigationType); // Either we found a focusable descendant, or an immediate child that is focusable. const bool bFoundNextFocusable = ( PathToFocusableChild.Num() > 0 ) || ArrangedChildren[FocusedChildIndex].Widget->SupportsKeyboardFocus(); if ( bFoundNextFocusable ) { // We found the next focusable widget, so make this path point at the new widget by: // First, truncating the FocusPath up to the current level (i.e. PathLevel). Widgets.Remove( PathLevel+1, Widgets.Num()-PathLevel-1 ); // Second, add the immediate child that is focus or whose descendant is focused. Widgets.AddWidget( ArrangedChildren[FocusedChildIndex] ); // Add path to focused descendants if any. Widgets.Append( PathToFocusableChild ); // We successfully moved focus! return true; } } } } return false; }