Wrap game viewport with custom widget

Hi,

Right now the only way to add widgets to viewport is to use AddViewportWidget (or similar) function but it just adds a widget on top of the viewport. I was wondering if one could wrap game viewport with custom widget?

So what I would like to achieve is something like in Heroes of Might&Magic. https://lh3.ggpht.com/pVricu4a1S63c3fXk6hcU7s0ykd8pfpHYd5h2VSKi2vG1TqFVP7Fq2uT-vyXiHd1FQ=h900

I want game viewport to be a part of panel widget so I can surround it with other widgets. I dont want these other widgets to cover my viewport.

So for the testing purposes I managed to modify PlayLevel.cpp and change this line

PieWindow->SetContent( PieViewportWidget.ToSharedRef() );

so it looks like this:

PieWindow->SetContent( SNew(SHorizontalBox)
					+SHorizontalBox::Slot()[SNullWidget::NullWidget]
					+SHorizontalBox::Slot()[PieViewportWidget.ToSharedRef()] );

The result was just what I expected ( http://i.imgur.com/dK1N4o2.png ) but this will work only for Play in New Editor Window option. What I want is to have same effect in standalone game.

So I modified GameEngine.cpp and change GameViewportWindowPtr->SetContent( line like this

GameViewportWindowPtr->SetContent(SNew(SHorizontalBox)
			+ SHorizontalBox::Slot()[SNullWidget::NullWidget]
			+ SHorizontalBox::Slot()[GameViewportWidgetRef] ); 

But the result was quite different than expected: http://i.imgur.com/XcNppyf.png There are now 2 viewports, one on top of the other and I cant really figure out why is that.

I was thinking that maybe it could be possible to add a function like

virtual TSharedRef<SWidget> GetGameViewportWidget(TSharedRef<SWidget> ViewportWidget);

that would allow user to wrap viewport with any widget he/she wants. So if you would like to not wrap viewport with anything you would just return ViewportWidget; but if you would like to wrap viewport with horizontal box (like in the examples above) you would

return SNew(SHorizontalBox)
    + SHorizontalBox::Slot()[SNullWidget::NullWidget]
    + SHorizontalBox::Slot()[ViewportWidget];

How difficult would it be to implement such a functionality? GameWindow creation code is kinda scattered (between editor and game engine) and complex, so before I start doing anything I would like to ask if this kind of engine modification wont cause too much complications? How much code will I have to adjust to get this thing working?

I also have no idea where this kind of function should be implemented. GameEngine? EditorEngine? maybe GameInstance or GameMode?

Any tips and thoughts appreciated,
Thanks

Anyone ? ?

still looking for any tips

I have searched a long time to find a solution for this.
The only way I could come up to solve this is by abusing the splitscreen functionality. Splitscreens don’t have to be enabled in the settings for this to work. Use the following C++ code:

Imagine you have a widget on the left side and call the following function inside a ticking function (I put this inside the DrawHUD overridden function)

if (!bGameViewportAdjusted)
{
  AdjustGameViewport();
}

This ensures that the size of the viewport and its origin will be adjusted once the viewport actually finished initializing (this is usually done with a small delay, which I find very very unelegant and unreliable too but do this as you please).

void YourHUDClass::AdjustGameViewport() const
{
  if (GetWorld() && GetWorld()->WorldType == EWorldType::Game
&& GEngine && GEngine->GameViewport)
  {
    FVector2D ViewportSize;
    GEngine->GameViewport->GetViewportSize(ViewportSize);
    
    // The GameViewport takes some time to initialize
    if (ViewportSize.X > 0 && ViewportSize.Y > 0)
    {
      LeftSideUISize = LeftSideUIWidget->GetDesiredSize();

      // HACK: This "transforms" slate space to screen space
      LeftSideUISize = LeftSideUISize / (FVector2D(1920, 1080) / ViewportSize);

      FVector2D LeftSideUISizeRelativeToViewport = LeftSideUISize / ViewportSize;

      GEngine->GameViewport->SplitscreenInfo[0].PlayerData[0].OriginX = LeftSideUISizeRelativeToViewport.X;
      GEngine->GameViewport->SplitscreenInfo[0].PlayerData[0].OriginY = 0.0f;
      GEngine->GameViewport->SplitscreenInfo[0].PlayerData[0].SizeX   = 1.0f - LeftSideUISizeRelativeToViewport.X; 
      GEngine->GameViewport->SplitscreenInfo[0].PlayerData[0].SizeY   = 1.0f;

      bGameViewportAdjusted = true;
    }
  }
}

If you would like to add a slate widget to the top you can do this the same way with

GEngine->GameViewport->SplitscreenInfo[0].PlayerData[0].OriginY = TopSideUISizeRelativeToViewport.Y; 
GEngine->GameViewport->SplitscreenInfo[0].PlayerData[0].SizeY    = 1.0f - LeftSideUISizeRelativeToViewport.Y;

If you want to add a slate widget to the right you have to subtract the X size of that widget from SizeX, and if you want to add a slate widget to the bottom you have to subtract the Y size of that widget from SizeY (additionaly to what you already subtract for left and top).

If you’ve noticed I am only doing this when it is not PIE (GetWorld() && GetWorld()->WorldType == WorldType::Game). I couldn’t figure out yet how to calculate the Widget size correctly for PIE.

Hi, szyszek
I have same issue. PIE is ok, but standalone game not work.
Did you figured it out how to do?
Thanks.

Not sure it will work out but in GameEngine.cpp there is this line saying to render directly to the window buffer:
.RenderDirectlyToWindow(bRenderDirectlyToWindow)

Maybe by putting false there it could work.
Here an example for doing that somewhere in your code after the ViewportWidget initialization.

UGameEngine* GameEngine = Cast<UGameEngine>(GEngine);

if (GameEngine != nullptr)
{
	TSharedPtr<SViewport> ViewportWidget = GameEngine->GetGameViewportWidget();
	if (ViewportWidget.IsValid())
	{
		ViewportWidget->SetRenderDirectlyToWindow(false);
	}
}