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;
}
Example #2
0
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 );
}
Example #4
0
/**
 * 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();
			}
		}
	}
}