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)); } } }
void Add(const TSharedRef<SWidget>& InWidget) { TSharedPtr<SWidgetStackItem> NewItem; InsertSlot(0) .AutoHeight() [ SAssignNew(NewItem, SWidgetStackItem) [ InWidget ] ]; { auto Widget = Children[0].GetWidget(); Widget->SlatePrepass(); const float WidgetHeight = Widget->GetDesiredSize().Y; StartSlideOffset += WidgetHeight; // Fade in time is 1 second x the proportion of the slide amount that this widget takes up NewItem->FadeIn(WidgetHeight / StartSlideOffset); if (!SlideCurve.IsPlaying()) { SlideCurve.Play(AsShared()); } } const FVector2D NewSize = ComputeTotalSize(); if (NewSize != StartSizeOffset) { StartSizeOffset = NewSize; if (!SizeCurve.IsPlaying()) { SizeCurve.Play(AsShared()); } } }
/** * 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 ) { TSharedPtr< SWidget > MenuContentPtr = OnGetMenuContent.IsBound() ? OnGetMenuContent.Execute() : MenuContent; if ( MenuContentPtr.IsValid() ) { // OPEN POPUP if ( OnMenuOpenChanged.IsBound() ) { OnMenuOpenChanged.Execute(true); } // This can be called at any time so we use the push menu override that explicitly allows us to specify our parent // Figure out where the menu anchor is on the screen, so we can set the initial position of our pop-up window SlatePrepass(); // NOTE: Careful, GeneratePathToWidget can be reentrant in that it can call visibility delegates and such FWidgetPath MyWidgetPath; FSlateApplication::Get().GeneratePathToWidgetChecked(AsShared(), MyWidgetPath); const FGeometry& MyGeometry = MyWidgetPath.Widgets.Last().Geometry; // @todo Slate: This code does not properly propagate the layout scale of the widget we are creating the popup for. // The popup instead has a scale of one, but a computed size as if the contents were scaled. This code should ideally // wrap the contents with an SFxWidget that applies the necessary layout scale. This is a very rare case right now. // Figure out how big the content widget is so we can set the window's initial size properly TSharedRef< SWidget > MenuContentRef = MenuContentPtr.ToSharedRef(); MenuContentRef->SlatePrepass(); // 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: 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); } if ( Method == CreateNewWindow ) { // Open the pop-up TSharedRef<SWindow> PopupWindow = FSlateApplication::Get().PushMenu(AsShared(), MenuContentRef, NewPosition, TransitionEffect, bFocusMenu, false, NewWindowSize, SummonLocationSize); PopupWindow->SetRequestDestroyWindowOverride(FRequestDestroyWindowOverride::CreateSP(this, &SMenuAnchor::RequestClosePopupWindow)); PopupWindowPtr = PopupWindow; } else { // We are re-using the current window instead of creating a new one. // The popup will be presented via an overlay service. ensure(Method == UseCurrentWindow); Children[1] [ MenuContentRef ]; // We want to support dismissing the popup widget when the user clicks outside it. FSlateApplication::Get().GetPopupSupport().RegisterClickNotification(MenuContentRef, FOnClickedOutside::CreateSP(this, &SMenuAnchor::OnClickedOutsidePopup)); } if ( bFocusMenu ) { FSlateApplication::Get().SetKeyboardFocus(MenuContentRef, EKeyboardFocusCause::SetDirectly); } } } else { // CLOSE POPUP if (Method == CreateNewWindow) { // Close the Popup window. TSharedPtr<SWindow> PopupWindow = PopupWindowPtr.Pin(); if ( PopupWindow.IsValid() ) { // Request that the popup be closed. PopupWindow->RequestDestroyWindow(); } } else { // Close the popup overlay ensure(Method==UseCurrentWindow); Children[1].DetachWidget(); } if (OnMenuOpenChanged.IsBound()) { OnMenuOpenChanged.Execute(false); } } } }
/** * 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(); } } } }
void SWidget::SlatePrepass() { SlatePrepass( FSlateApplicationBase::Get().GetApplicationScale() ); }