How to dynamically resize Slate windows based on content

EDIT: See “Steps to reproduce” below first:

Then, the function that builds the Main SBox, which contains two buttons on top, an SListView in the middle, and two buttons below.

`MainEditableBox = SNew(SBox)
[
SNew(SVerticalBox)

  • SVerticalBox::Slot()
    .VAlign(VAlign_Top)
    [
    SNew(SBorder)
    .Padding(10)
    .Content()
    [
    SNew(SButton)
    .Text(FText::FromString(“Add New Line”))
    .OnClicked(FOnClicked::CreateRaw(this, &MyClass::OnAddNewRowButtonPressed))
    ]
    ]
    +SVerticalBox::Slot()
    .VAlign(VAlign_Top)
    [
    SNew(SBorder)
    .Padding(10)
    .Content()
    [
    SNew(SButton)
    .Text(FText::FromString(“Duplicate Selected Line(s)”))
    .OnClicked(FOnClicked::CreateRaw(this, &MyClass::OnDuplicateSelectedRowButtonPressed))
    ]
    ]
  • SVerticalBox::Slot()
    .AutoHeight()
    [
    BuildSelectedRowsWidget()
    ]
  • SVerticalBox::Slot()
    .AutoHeight()
    [
    SNew(SBorder)
    .Padding(10)
    .Content()
    [
    SNew(SHorizontalBox)
  • SHorizontalBox::Slot()
    [
    SNew(SButton)
    .Text(FText::FromString(“Submit Change(s)”))
    .OnClicked(FOnClicked::CreateRaw(this, &MyClass::OnSubmitRowChangeButtonClicked))

]

  • SHorizontalBox::Slot()
    [
    SNew(SButton)
    .Text(FText::FromString(“Cancel Change(s)”))
    .OnClicked(FOnClicked::CreateRaw(this, &MyClass::OnCancelRowChangeButtonClicked))

]
]
]
];
}
return MainEditableBox.ToSharedRef();`

And finally, the function that builds the SListView

`SelectedRowsBox = SNew(SBox)
[
SNew(SBorder)
.Padding(10)
.Content()
[
SNew(SVerticalBox)

  • SVerticalBox::Slot()
    .VAlign(VAlign_Top)
    [
    SAssignNew(LamsAddedRowsWidget, SListView<TSharedPtr>)
    .ItemHeight(20)
    .ListItemsSource(&DisplayArray)
    .SelectionMode(ESelectionMode::Multi)
    .OnGenerateRow_Raw(this, &SLAMSDataEntryWidget::MakeRowDataListViewWidget)
    .OnSelectionChanged_Raw(this, &SLAMSDataEntryWidget::EntrySelectionChanged)
    .OnKeyDownHandler_Raw(this, &SLAMSDataEntryWidget::OnKeyDown)
    .HeaderRow(
    SNew(SHeaderRow)
  • SHeaderRow::Column(“Select”).DefaultLabel(NSLOCTEXT(“LAMSData”, “SelectColumn”, “Select”))
  • SHeaderRow::Column(“Section”).DefaultLabel(NSLOCTEXT(“LAMSData”, “SectionColumn”, “Section”))
  • SHeaderRow::Column(“SubSection”).DefaultLabel(NSLOCTEXT(“LAMSData”, “SubSectionColumn”, “SubSection”))
  • SHeaderRow::Column(“Character”).DefaultLabel(NSLOCTEXT(“LAMSData”, “CharacterColumn”, “Character”))
  • SHeaderRow::Column(“UniqueName”).DefaultLabel(NSLOCTEXT(“LAMSData”, “UniqueNameColumn”, “Unique Name”))
  • SHeaderRow::Column(“Text”).DefaultLabel(NSLOCTEXT(“LAMSData”, “TextColumn”, “Text”))
  • SHeaderRow::Column(“Remove”).DefaultLabel(NSLOCTEXT(“LAMSData”, “RemoveColumn”, “Remove”)))
    ]
    ]
    ];`

Now, when OnAddNewButtonPressed() is called, we add a row to the SListView widget, which means I need to resize the window vertically.

Here’s what I’ve tried:

`SelectedRowsBox->SetContent(SNullWidget::NullWidget);
SelectedRowsBox->SetPadding(0.f);
SelectedRowsBox->SetContent(BuildSelectedRowsWidget());

MainEditableBox->SetContent(SNullWidget::NullWidget);
MainEditableBox->SetPadding(0.f);
MainEditableBox->SetContent(BuildEditableBox());

SelectedRowsBox->SlatePrepass();
MainEditableBox->SlatePrepass();

// GetDesiredSize always seems wrong
RowWindow->Resize(MainEditableBox->GetDesiredSize())`

Please let me know if there’s anything I can do to reliably resize my SWindow vertically each time a row is added to my SListView widget.

Thank you!

Steps to Reproduce
When creating an SWindow, you can either set sizing rule to ESizingRule::AutoSized, which will let the Editor calculate the window size - but this is a fixed size and users cannot make the window bigger or smaller, OR you can set to ESizingRule::UserSized, which will let you resize the window, but requires manual sizing.

I have been struggling to accurately size my window when sizing rule is set to user sized. My SWindow contains two SBox components:

Here’s my SWindow:

`RowWindow = SNew(SWindow)
.AutoCenter(EAutoCenter::PreferredWorkArea)
.Title(FText::FromString(TEXT(“Add New Lines”)))
.IsInitiallyMaximized(false)
.ClientSize(TextSize)
.AdjustInitialSizeAndPositionForDPIScale(true)
.CreateTitleBar(true)
.SizingRule(ESizingRule::UserSized)
.SupportsMaximize(false)
.SupportsMinimize(true)
.HasCloseButton(true)
[
BuildEditableBox()
];

if (!IsSlowTask)
{
RowWindow->SetOnWindowClosed(FOnWindowClosed::CreateRaw(this, &SLAMSDataEntryWidget::OnWindowClosed));
}

TSharedPtr RootWindow = FGlobalTabmanager::Get()->GetRootWindow();
FSlateApplication::Get().AddModalWindow(RowWindow.ToSharedRef(), RootWindow, IsSlowTask);

if (IsSlowTask)
{
RowWindow->ShowWindow();
}`I’ve noticed ALOT of SWindow widgets in the engine just use hard-coded values for .ClientSize when creating windows.

This seems problematic because we have users that are opening these windows on different monitors/laptops, so we need to take DPI into consideration.

Hi,

The ListView widget tends to need a tick or two before it’s fully refreshed after the underlying data changes; most changes to the source result in a call to STableViewBase::RequestLayoutRefresh, which schedules the refresh for the next tick. If you want to react to the actual size of the ListView, you’ll likely also need to schedule the resize for a frame (or two) in advance to be sure you’re getting an accurate DesiredSize.

Best,

Cody