source: https://dev.epicgames.com/documentation/en-us/unreal-engine/slate-editor-window-quickstart-guide-for-unreal-engine?application_version=5.8
version: Unreal Engine 5.8 Documentation
fetched: 2026-10-11(curl 抓取页面文本,去掉 HTML;原文全文,供 wiki 页面引用)
Slate Editor Window Quickstart Guide for Unreal Engine | Unreal Engine 5.8 Documentation | Epic Developer Community Table of Contents Developer Slate Editor Window Quickstart Guide Slate Editor Window Quickstart Guide Build a simple editor window using Slate.
On this page This is a step-by-step guide on how to create a simple editor window that responds to user input using Slate , Unreal Engine's UI framework.
This page does not provide a comprehensive guide on how Slate interacts with other systems.
Rather, it focuses on the fundamentals of how Slate's syntax relates to the layout of your UI, and the basics of making your UI respond to events. 1.
Project Setup This guide uses a new project called SlateQuickstartGame with the following settings: Template: Third-Person Project C++ Project You can also follow this guide using an existing project.
This project does not depend on any specific target platform, as it focuses on editor functionality. 2.
Creating a Window Plugin To get started on your new editor window quickly, open the Edit > Plugins window, then click the + Add button to create a new plugin.
Choose Editor Standalone Window as the plugin type, then call it SlateQuickstartWindow .
Click Create Plugin to finish.
Click to enlarge image.
This automatically creates the Plugin module with the necessary C++ classes to support a basic editor window.
Close the editor and recompile your project, then open the project again.
You can find your new editor window under the Window dropdown.
Click to enlarge image.
When you initially view this window, it will display placeholder text but no interactive elements.
Understanding the Window's Code Before continuing to build your menu, you should review the code in SlateQuickstartWindowModule.cpp for a quick preview of how to use Slate's declarative syntax.
The Slate code is contained in the OnSpawnPluginTab function.
When you initially view it, the function reads as follows: TSharedRef FSlateQuickstartWindowModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) { FText WidgetText = FText::Format( LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"), FText::FromString(TEXT("FSlateQuickstartWindowModule::OnSpawnPluginTab")), FText::FromString(TEXT("SlateQuickstartWindow.cpp")) ); return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ // Put your tab content here!
SNew(SBox) .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(WidgetText) ] ]; } Copy full snippet TSharedRef FSlateQuickstartWindowModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) { FText WidgetText = FText::Format( LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"), FText::FromString(TEXT("FSlateQuickstartWindowModule::OnSpawnPluginTab")), FText::FromString(TEXT("SlateQuickstartWindow.cpp")) ); return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ // Put your tab content here!
SNew(SBox) .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(WidgetText) ] ]; } This function performs the following steps: Builds a string called WidgetText using FText::Format to combine several strings.
Defines a new SDockTab using the SNew function.
This represents the dockable window.
Fills the SDockTab with an SBox element which can contain content.
This box has HAlign and VAlign parameters that align it to the center of the window.
Adds a new STextBlock to the SBox and sets its text to the value of WidgetText .
Returns the SDockTab containing all these UI elements.
The best way to understand how this works is to think of how you would create a similar user interface in UMG.
To re-create this in UMG, you would drag a box widget onto the stage as the root UI element, then place a Text widget inside of it and change its alignment to center.
The stage and hierarchy would resemble the following image.
Click to enlarge image.
In the case of the SlateQuickstartWindow , the viewport stage is replaced with the SDockTab widget.
When you review the code, the square brackets define what elements are nested inside a given widget, while periods set the value of the parameters you would see in the Details panel.
For more information about Slate's declarative syntax, refer to the Slate Overview . 3.
Creating a Slate Widget For the Menu You can add Slate widgets directly to the FSlateQuickstartWindowModule class generated by the Plugin wizard.
However, there are some limitations to how you can handle your widgets' callbacks if you do this.
Therefore, you should create a dedicated Slate widget to hold the menu's contents, then add that widget to the FSlateQuickstartWindowModule class.
In the Content Drawer, click the C++ Classes folder, then right-click and click New C++ Class .
Click to enlarge image.
In the Add C++ Class window, select Slate Widget , then click Next .
Change your new C++ class's settings as follows: Click to enlarge image.
Class Type: Public Name: QuickStartWindowMenu Module: SlateQuickstartWindow (Editor) Click Create Class to finish.
Make sure you set the Module drop-down to the SlateQuickstartWindow module before you click Create Class.
This adds the class to your plugin's code.
If you don't do this, the wizard will put it in your game's code, and your Slate widget will not be visible to the plugin.
The wizard creates a new C++ class called SQuickStartWindowMenu in your plugin's Source folder, then regenerates your project's Visual Studio solution. 4.
Populating Your Menu With Widgets To add Slate widgets to your menu, open SQuickStartWindowMenu.cpp and add them to the Construct function.
When you initially view this function, it reads as follows: void SQuickStartWindowMenu::Construct(const FArguments& InArgs) { /* ChildSlot [ // Populate the widget ]; / } Copy full snippet void SQuickStartWindowMenu::Construct(const FArguments& InArgs) { / ChildSlot [ // Populate the widget ]; */ } Follow these steps to populate the window: Delete the commented-out code inside the Construct function.
Inside the Construct function, add a new SVerticalBox with the SNew function.
Follow that with two +SVerticalBox::Slot() elements.
Give both slots the .AutoHeight() property.
SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ ] +SVerticalBox::Slot() .AutoHeight() [ ] Copy full snippet SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ ] +SVerticalBox::Slot() .AutoHeight() [ ] Inside the first Vertical Box slot, add a SHorizontalBox with two slots.
Give both slots the .VAlign(VAlign_Top) property. +SVerticalBox::Slot() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ ] +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ ] ] Copy full snippet +SVerticalBox::Slot() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ ] +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ ] ] This will nest the horizontal box inside the vertical box, effectively dividing this first slot into two columns.
In the first horizontal slot, add a new STextBlock widget.
Set the .Text to FText::FromString("Test Button") . +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Button")) ] Copy full snippet +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Button")) ] Whenever you display user-facing text, you should use FText instead of FString or other methods of displaying text, as it provides support for localization.
For more information, refer to Text Localization .
In the second horizontal slot, add a new SButton widget.
Set its .Text to FText::FromString("Press Me") . +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SButton) .Text(FText::FromString("Press Me")) ] Copy full snippet +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SButton) .Text(FText::FromString("Press Me")) ] In the second Vertical Box slot, create another Horizontal Box with two slots.
Give the first horizontal slot a STextBlock widget that reads "Test Checkbox" , then add a SCheckBox widget to the second horizontal slot. +SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Checkbox")) ] +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SCheckBox) ] ] Copy full snippet +SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Checkbox")) ] +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SCheckBox) ] ] The full definition of your Slate window should now read as follows: return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Button")) ] +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SButton) .Text(FText::FromString("Press Me")) ] ] +SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Checkbox")) ] + SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SCheckBox) ] ] ]; Copy full snippet return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Button")) ] +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SButton) .Text(FText::FromString("Press Me")) ] ] +SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(STextBlock) .Text(FText::FromString("Test Checkbox")) ] + SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SCheckBox) ] ] ]; Save and compile your code, then open your project in Unreal Editor and open your window.
You can't compile Slate windows while Live Coding is active and your project is open in Unreal Editor.
Make sure to close Unreal Editor before trying to compile your code.
When you open your window, it should look like this: Neither the button or the checkbox do anything yet, but you will add functionality to them in the next section. 5.
Binding Events to Interactive Widgets Interactive Slate widgets use events to respond to interactions like clicking or toggling.
In this section, you will create several delegates to drive the interaction for the checkbox and button from the previous section.
Open SQuickStartWindowMenu.h and declare the following functions and variables: public: FReply OnTestButtonClicked(); void OnTestCheckboxStateChanged(ECheckBoxState NewState); ECheckBoxState IsTestBoxChecked() const; protected: bool bIsTestBoxChecked; Copy full snippet public: FReply OnTestButtonClicked(); void OnTestCheckboxStateChanged(ECheckBoxState NewState); ECheckBoxState IsTestBoxChecked() const; protected: bool bIsTestBoxChecked; The functions will contain the logic that runs when you click the test button and test checkbox.
The checkbox will use the IsTestBoxChecked function to determine what state it should display, and bIsTestBoxChecked will cache that state.
In SQuickStartWindowMenu.cpp , provide the following implementations for the checkbox's functions: void SQuickStartWindowMenu::OnTestCheckboxStateChanged(ECheckBoxState NewState) { bIsTestBoxChecked = NewState == ECheckBoxState::Checked ? true : false; } ECheckBoxState SQuickStartWindowMenu::IsTestBoxChecked() const { return bIsTestBoxChecked ?
ECheckBoxState::Checked : ECheckBoxState::Unchecked; } Copy full snippet void SQuickStartWindowMenu::OnTestCheckboxStateChanged(ECheckBoxState NewState) { bIsTestBoxChecked = NewState == ECheckBoxState::Checked ? true : false; } ECheckBoxState SQuickStartWindowMenu::IsTestBoxChecked() const { return bIsTestBoxChecked ?
ECheckBoxState::Checked : ECheckBoxState::Unchecked; } These provide a getter and setter for the checkbox's current state.
The OnTestCheckboxStateChanged function will set the value of bIsTestBoxChecked depending on what the user changes the checkbox's state to.
Meanwhile, the IsTestBoxChecked function will read that variable and translate it back into an ECheckBoxState .
Add the .OnCheckStateChanged parameter to the SCheckBox widget, and provide a reference to the FSlateQuickstartModule::OnTestCheckboxStateChanged function.
Then, add the IsChecked parameter, and provide a reference to the SQuickStartWindowMenu::IsTestBoxChecked function. +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SCheckBox) .OnCheckStateChanged(FOnCheckStateChanged::CreateSP(this, &SQuickStartWindowMenu::OnTestCheckboxStateChanged)) .IsChecked(FIsChecked::CreateSP(this, &SQuickStartWindowMenu::IsTestBoxChecked)) ] Copy full snippet +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SCheckBox) .OnCheckStateChanged(FOnCheckStateChanged::CreateSP(this, &SQuickStartWindowMenu::OnTestCheckboxStateChanged)) .IsChecked(FIsChecked::CreateSP(this, &SQuickStartWindowMenu::IsTestBoxChecked)) ] You now have a mechanism to track when the user checks and unchecks the SCheckBox widget.
Provide the following implementation for the OnTestButtonClicked function: FReply SQuickStartWindowMenu::OnTestButtonClicked() { UE_LOG(LogTemp, Warning, TEXT("Hello, world!
The checkbox is %s."), (bIsTestBoxChecked ?
TEXT("checked") : TEXT("unchecked"))); return FReply::Handled(); } Copy full snippet FReply SQuickStartWindowMenu::OnTestButtonClicked() { UE_LOG(LogTemp, Warning, TEXT("Hello, world!
The checkbox is %s."), (bIsTestBoxChecked ?
TEXT("checked") : TEXT("unchecked"))); return FReply::Handled(); } This function outputs a short message to the output log stating whether the checkbox is checked.
Add the .OnClicked parameter to the SButton , then provide a reference to the SQuickStartWindowMenu::OnTestButtonClicked function. +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SButton) .Text(FText::FromString("Press Me")) .OnClicked(FOnClicked::CreateSP(this, &SQuickStartWindowMenu::OnTestButtonClicked)) ] Copy full snippet +SHorizontalBox::Slot() .VAlign(VAlign_Top) [ SNew(SButton) .Text(FText::FromString("Press Me")) .OnClicked(FOnClicked::CreateSP(this, &SQuickStartWindowMenu::OnTestButtonClicked)) ] 6.
Adding Your Menu to the Window Finally, you need to add the Slate widget you created to the menu tab's code.
Open SlateQuickstartWindowModule.cpp and delete the contents of the SDockTab.
Its code should now read as follows: return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ ]; Copy full snippet return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ ]; Add your SQuickStartWindowMenu widget inside the SDockTab . return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ SNew(SQuickStartWindowMenu) ]; Copy full snippet return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ SNew(SQuickStartWindowMenu) ]; Save and compile your code, then open Unreal Editor and test your window.
Final Result When you click the test button, your console log should print the message "Hello, world!
The checkbox is unchecked." If you check the check box, it should print the message "Hello, world!
The checkbox is checked." This provides a basic working example of a Slate window which you can expand on to create custom editor windows.
On Your Own For examples of different Slate widgets and the many ways they can bind delegates, refer to STestSuite.cpp .
You can find this in your engine's install directory under Source\Runtime\AppFramework\Private\Widgets\Testing .
You can experiment with these widgets to create different layouts and interactive elements.
You can also experiment with using Slate UI elements to interact with assets or data in your project, or the World in the level editor. ui editor slate Ask questions and help your peers Developer Forums Write your own tutorials or read those from others Learning Library On this page 1.
Project Setup 2.
Creating a Window Plugin Understanding the Window's Code 3.
Creating a Slate Widget For the Menu 4.
Populating Your Menu With Widgets 5.
Binding Events to Interactive Widgets 6.
Adding Your Menu to the Window Final Result On Your Own