FWidgetPath FWeakWidgetPath::ToNextFocusedPath(EUINavigation NavigationType, const FNavigationReply& NavigationReply, const FArrangedWidget& RuleWidget) { check(NavigationType == EUINavigation::Next || NavigationType == EUINavigation::Previous); // Make a copy of the focus path. We will mutate it until it meets the necessary requirements. FWidgetPath NewFocusPath = this->ToWidgetPath(); TSharedPtr<SWidget> CurrentlyFocusedWidget = this->Widgets.Last().Pin(); bool bMovedFocus = false; // Attempt to move the focus starting at the leafmost widget and bubbling up to the root (i.e. the window) for (int32 FocusNodeIndex=NewFocusPath.Widgets.Num()-1; !bMovedFocus && FocusNodeIndex >= 0; --FocusNodeIndex) { // We've reached the stop boundary and not yet moved focus, so don't advance. if ( NavigationReply.GetBoundaryRule() == EUINavigationRule::Stop && RuleWidget.Widget == NewFocusPath.Widgets[FocusNodeIndex].Widget ) { break; } //TODO Slate Navigation Handle Wrap. bMovedFocus = NewFocusPath.MoveFocus(FocusNodeIndex, NavigationType); } return NewFocusPath; }
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))); }
FReply FCurveColorCustomization::OnCurvePreviewDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent) { if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) { if (RuntimeCurve->ExternalCurve) { FAssetEditorManager::Get().OpenEditorForAsset(RuntimeCurve->ExternalCurve); } else { DestroyPopOutWindow(); // Determine the position of the window so that it will spawn near the mouse, but not go off the screen. const FVector2D CursorPos = FSlateApplication::Get().GetCursorPos(); FSlateRect Anchor(CursorPos.X, CursorPos.Y, CursorPos.X, CursorPos.Y); FVector2D AdjustedSummonLocation = FSlateApplication::Get().CalculatePopupWindowPosition( Anchor, FCurveColorCustomization::DEFAULT_WINDOW_SIZE, Orient_Horizontal ); TSharedPtr<SWindow> Window = SNew(SWindow) .Title( FText::Format( LOCTEXT("WindowHeader", "{0} - Internal Color Curve Editor"), StructPropertyHandle->GetPropertyDisplayName()) ) .ClientSize( FCurveColorCustomization::DEFAULT_WINDOW_SIZE ) .ScreenPosition(AdjustedSummonLocation) .AutoCenter(EAutoCenter::None) .SupportsMaximize(false) .SupportsMinimize(false) .SizingRule( ESizingRule::FixedSize ); // init the mini curve editor widget TSharedRef<SMiniCurveEditor> MiniCurveEditor = SNew(SMiniCurveEditor) .CurveOwner(this) .OwnerObject(Owner) .ParentWindow(Window); Window->SetContent( MiniCurveEditor ); // Find the window of the parent widget FWidgetPath WidgetPath; FSlateApplication::Get().GeneratePathToWidgetChecked( CurveWidget.ToSharedRef(), WidgetPath ); Window = FSlateApplication::Get().AddWindowAsNativeChild( Window.ToSharedRef(), WidgetPath.GetWindow() ); //hold on to the window created for external use... CurveEditorWindow = Window; } } return FReply::Handled(); }
bool FDesignTimeUtils::GetArrangedWidget(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); return true; } return false; }
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; }
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); } }
/** * Open or close the popup * * @param InIsOpen If true, open the popup. Otherwise close it. */ void SMenuAnchor::SetIsOpen( bool InIsOpen, const bool bFocusMenu ) { // Prevent redundant opens/closes if ( IsOpen() != InIsOpen ) { if ( InIsOpen ) { if ( OnGetMenuContent.IsBound() ) { SetMenuContent(OnGetMenuContent.Execute()); } if ( MenuContent.IsValid() ) { // OPEN POPUP if ( OnMenuOpenChanged.IsBound() ) { OnMenuOpenChanged.Execute(true); } // Figure out where the menu anchor is on the screen, so we can set the initial position of our pop-up window // This can be called at any time so we use the push menu override that explicitly allows us to specify our parent // NOTE: Careful, GeneratePathToWidget can be reentrant in that it can call visibility delegates and such FWidgetPath MyWidgetPath; FSlateApplication::Get().GeneratePathToWidgetUnchecked(AsShared(), MyWidgetPath); if (MyWidgetPath.IsValid()) { const FGeometry& MyGeometry = MyWidgetPath.Widgets.Last().Geometry; const float LayoutScaleMultiplier = MyGeometry.GetAccumulatedLayoutTransform().GetScale(); SlatePrepass(LayoutScaleMultiplier); // Figure out how big the content widget is so we can set the window's initial size properly TSharedRef< SWidget > MenuContentRef = MenuContent.ToSharedRef(); MenuContentRef->SlatePrepass(LayoutScaleMultiplier); // Combo-boxes never size down smaller than the widget that spawned them, but all // other pop-up menus are currently auto-sized const FVector2D DesiredContentSize = MenuContentRef->GetDesiredSize(); // @todo slate: This is ignoring any window border size! const EMenuPlacement PlacementMode = Placement.Get(); const FVector2D NewPosition = MyGeometry.AbsolutePosition; FVector2D NewWindowSize = DesiredContentSize; const FVector2D SummonLocationSize = MyGeometry.Size; FPopupTransitionEffect TransitionEffect( FPopupTransitionEffect::None ); if ( PlacementMode == MenuPlacement_ComboBox || PlacementMode == MenuPlacement_ComboBoxRight ) { TransitionEffect = FPopupTransitionEffect( FPopupTransitionEffect::ComboButton ); NewWindowSize = FVector2D( FMath::Max( MyGeometry.Size.X, DesiredContentSize.X ), DesiredContentSize.Y ); } else if ( PlacementMode == MenuPlacement_BelowAnchor ) { TransitionEffect = FPopupTransitionEffect( FPopupTransitionEffect::TopMenu ); } else if ( PlacementMode == MenuPlacement_MenuRight ) { TransitionEffect = FPopupTransitionEffect( FPopupTransitionEffect::SubMenu ); } MethodInUse = Method.IsSet() ? Method.GetValue() : QueryPopupMethod(MyWidgetPath); if (MethodInUse == EPopupMethod::CreateNewWindow) { // Open the pop-up const bool bIsCollapsedByParent = false; // don't auto-close child menus when the parent gets focus TSharedPtr<IMenu> NewMenu = FSlateApplication::Get().PushMenu(AsShared(), MyWidgetPath, MenuContentRef, NewPosition, TransitionEffect, bFocusMenu, SummonLocationSize, MethodInUse, bIsCollapsedByParent); PopupMenuPtr = NewMenu; check(NewMenu.IsValid() && NewMenu->GetOwnedWindow().IsValid()); NewMenu->GetOnMenuDismissed().AddSP(this, &SMenuAnchor::OnMenuClosed); PopupWindowPtr = NewMenu->GetOwnedWindow(); } else { // We are re-using the current window instead of creating a new one. // The popup will be presented via an overlay service. ensure(MethodInUse == EPopupMethod::UseCurrentWindow); TSharedRef<SWindow> PopupWindow = MyWidgetPath.GetWindow(); PopupWindowPtr = PopupWindow; if (bFocusMenu) { FSlateApplication::Get().ReleaseMouseCapture(); } TSharedRef<SMenuAnchor> SharedThis = StaticCastSharedRef<SMenuAnchor>(AsShared()); const bool bIsCollapsedByParent = false; // don't auto-close child menus when the parent gets focus TSharedPtr<IMenu> NewMenu = FSlateApplication::Get().PushHostedMenu( SharedThis, MyWidgetPath, SharedThis, MenuContentRef, WrappedContent, TransitionEffect, bIsCollapsedByParent); PopupMenuPtr = NewMenu; check(NewMenu.IsValid()); check(NewMenu->GetParentWindow().ToSharedRef() == PopupWindow); check(WrappedContent.IsValid()); Children[1] [ WrappedContent.ToSharedRef() ]; if (bFocusMenu) { FSlateApplication::Get().SetKeyboardFocus(MenuContentRef, EFocusCause::SetDirectly); } } } } } else { // CLOSE POPUP if (PopupMenuPtr.IsValid()) { PopupMenuPtr.Pin()->Dismiss(); } else { PopupWindowPtr.Reset(); MethodInUse.Reset(); } } } }