FReply SComboButton::OnButtonClicked() { TSharedPtr<SWidget> Content = NULL; if( OnGetMenuContent.IsBound() ) { Content = OnGetMenuContent.Execute(); SetMenuContent( Content.ToSharedRef() ); } // Button was clicked; show the popup. // Do nothing if clicking on the button also dismissed the menu, because we will end up doing the same thing twice. this->SetIsOpen( ShouldOpenDueToClick() ); // If the menu is open, execute the related delegate. if( IsOpen() && OnComboBoxOpened.IsBound() ) { OnComboBoxOpened.Execute(); } // Focusing any newly-created widgets must occur after they have been added to the UI root. FReply ButtonClickedReply = FReply::Handled(); TSharedPtr<SWidget> WidgetToFocus = WidgetToFocusPtr.Pin(); if (!WidgetToFocus.IsValid()) { // no explicitly focused widget, try to focus the content WidgetToFocus = Content; } if (!WidgetToFocus.IsValid()) { // no content, so try to focus the original widget set on construction WidgetToFocus = ContentWidgetPtr.Pin(); } if (WidgetToFocus.IsValid()) { ButtonClickedReply.SetKeyboardFocus( WidgetToFocus.ToSharedRef(), EKeyboardFocusCause::SetDirectly ); } return ButtonClickedReply; }
void SComboButton::Construct( const FArguments& InArgs ) { check(InArgs._ComboButtonStyle); // Work out which values we should use based on whether we were given an override, or should use the style's version const FButtonStyle* const OurButtonStyle = InArgs._ButtonStyle ? InArgs._ButtonStyle : &InArgs._ComboButtonStyle->ButtonStyle; MenuBorderBrush = &InArgs._ComboButtonStyle->MenuBorderBrush; MenuBorderPadding = InArgs._ComboButtonStyle->MenuBorderPadding; OnComboBoxOpened = InArgs._OnComboBoxOpened; ContentWidgetPtr = InArgs._MenuContent.Widget; bIsFocusable = InArgs._IsFocusable; TSharedPtr<SHorizontalBox> HBox; SMenuAnchor::Construct( SMenuAnchor::FArguments() .Placement(InArgs._MenuPlacement) .Method(InArgs._Method) .OnMenuOpenChanged(InArgs._OnMenuOpenChanged) .OnGetMenuContent(InArgs._OnGetMenuContent) [ SNew( SButton ) .ButtonStyle( OurButtonStyle ) .ClickMethod( EButtonClickMethod::MouseDown ) .OnClicked( this, &SComboButton::OnButtonClicked ) .ContentPadding( InArgs._ContentPadding ) .ForegroundColor( InArgs._ForegroundColor ) .ButtonColorAndOpacity( InArgs._ButtonColorAndOpacity ) .IsFocusable( InArgs._IsFocusable ) [ // Button and down arrow on the right // +-------------------+---+ // | Button Content | v | // +-------------------+---+ SAssignNew( HBox, SHorizontalBox ) + SHorizontalBox::Slot() .Expose( ButtonContentSlot ) .FillWidth( 1 ) .HAlign( InArgs._HAlign ) .VAlign( InArgs._VAlign ) [ InArgs._ButtonContent.Widget ] + SHorizontalBox::Slot() .AutoWidth() .HAlign( HAlign_Center ) .VAlign( VAlign_Center ) .Padding( InArgs._HasDownArrow ? 2 : 0 ) [ SNew( SImage ) .Visibility( InArgs._HasDownArrow ? EVisibility::Visible : EVisibility::Collapsed ) .Image( &InArgs._ComboButtonStyle->DownArrowImage ) // Inherit tinting from parent . ColorAndOpacity( FSlateColor::UseForeground() ) ] ] ] ); // The menu that pops up when we press the button. // We keep this content around, and then put it into a new window when we need to pop // it up. SetMenuContent( InArgs._MenuContent.Widget ); }
void SComboButton::Construct( const FArguments& InArgs ) { check(InArgs._ComboButtonStyle); // Work out which values we should use based on whether we were given an override, or should use the style's version const FButtonStyle* const OurButtonStyle = InArgs._ButtonStyle ? InArgs._ButtonStyle : &InArgs._ComboButtonStyle->ButtonStyle; ContentScale = InArgs._ContentScale.IsSet() ? InArgs._ContentScale : InArgs._ComboButtonStyle->ContentScale; TAttribute<FMargin> OurContentPadding = InArgs._ContentPadding.IsSet() ? InArgs._ContentPadding : InArgs._ComboButtonStyle->ContentPadding; const bool OurHasDownArrow = InArgs._HasDownArrow.Get(InArgs._ComboButtonStyle->HasDownArrow); const EHorizontalAlignment OurHAlign = InArgs._HAlign.Get(InArgs._ComboButtonStyle->HAlign); const EVerticalAlignment OurVAlign = InArgs._VAlign.Get(InArgs._ComboButtonStyle->VAlign); TAttribute<FSlateColor> OurForegroundColor = InArgs._ForegroundColor.IsSet() ? InArgs._ForegroundColor : InArgs._ComboButtonStyle->ForegroundColor; TAttribute<FSlateColor> OurButtonColorAndOpacity = InArgs._ButtonColorAndOpacity.IsSet() ? InArgs._ButtonColorAndOpacity : InArgs._ComboButtonStyle->ButtonColorAndOpacity; MenuBorderBrush = InArgs._MenuBorderBrush ? InArgs._MenuBorderBrush : &InArgs._ComboButtonStyle->MenuBorderBrush; MenuBorderPadding = InArgs._MenuBorderPadding.Get(InArgs._ComboButtonStyle->MenuBorderPadding); Placement = InArgs._MenuPlacement.IsSet() ? InArgs._MenuPlacement : InArgs._ComboButtonStyle->MenuPlacement; OnGetMenuContent = InArgs._OnGetMenuContent; OnComboBoxOpened = InArgs._OnComboBoxOpened; MenuHeight = InArgs._MenuHeight; MenuWidth = InArgs._MenuWidth; Method = InArgs._Method; ContentWidgetPtr = InArgs._MenuContent.Widget; TSharedPtr<SHorizontalBox> HBox; this->ChildSlot .HAlign( OurHAlign ) .VAlign( OurVAlign ) [ SNew(SButton) .ContentPadding(FMargin(1,0)) .ButtonStyle(OurButtonStyle) .ClickMethod( EButtonClickMethod::MouseDown ) .OnClicked( this, &SComboButton::OnButtonClicked ) .ContentPadding( OurContentPadding ) .ForegroundColor( OurForegroundColor ) .ButtonColorAndOpacity( OurButtonColorAndOpacity ) .IsFocusable( InArgs._IsFocusable ) [ // Button and down arrow on the right // +-------------------+---+ // | Button Content | v | // +-------------------+---+ SAssignNew(HBox, SHorizontalBox) + SHorizontalBox::Slot() . Expose( ButtonContentSlot ) . FillWidth(1) . VAlign(VAlign_Center) [ InArgs._ButtonContent.Widget ] +SHorizontalBox::Slot() . AutoWidth() . HAlign(HAlign_Center) . VAlign(VAlign_Center) . Padding( OurHasDownArrow ? 2 : 0) [ SNew(SImage) . Visibility( OurHasDownArrow ? EVisibility::Visible : EVisibility::Collapsed ) . Image(&InArgs._ComboButtonStyle->DownArrowImage) // Inherit tinting from parent . ColorAndOpacity( FSlateColor::UseForeground() ) ] ] ]; // The menu that pops up when we press the button. // We keep this content around, and then put it into a new window when we need to pop // it up. SetMenuContent( InArgs._MenuContent.Widget ); }
/** * 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(); } } } }