Skip to content

Instantly share code, notes, and snippets.

@iDevelopThings
Created November 15, 2024 06:36
Show Gist options
  • Save iDevelopThings/9f0b3b213fe4ac63de128fe499709210 to your computer and use it in GitHub Desktop.
Save iDevelopThings/9f0b3b213fe4ac63de128fe499709210 to your computer and use it in GitHub Desktop.
Slates SSplitter as a UMG Widget
#include "UI/UMGSplitter.h"
#include "Widgets/SWeakWidget.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(UMGSplitter)
#pragma region "Splitter Slot"
UUMGSplitterSlot::UUMGSplitterSlot(const FObjectInitializer& Oi): Super(Oi) {
Slot = nullptr;
}
void UUMGSplitterSlot::ReleaseSlateResources(bool bReleaseChildren) {
Super::ReleaseSlateResources(bReleaseChildren);
Slot = nullptr;
}
void UUMGSplitterSlot::BuildSlot(TSharedRef<SSplitter> Splitter) {
Splitter->AddSlot()
.Expose(Slot)
.SizeRule(SizeRule)
.Value(Value)
.MinSize(MinSize)
.Resizable(Resizable)
.OnSlotResized_UObject(this, &UUMGSplitterSlot::OnSlotResized)
[
Content == nullptr ? SNullWidget::NullWidget : Content->TakeWidget()
];
}
void UUMGSplitterSlot::ReplaceContent(UWidget* NewContent) {
if (Content != NewContent) {
if (NewContent) {
NewContent->RemoveFromParent();
}
if (UWidget* PreviousWidget = Content) {
// Setting Slot=null before RemoveFromParent to prevent destroying this slot
PreviousWidget->Slot = nullptr;
PreviousWidget->RemoveFromParent();
}
Content = NewContent;
if (Content) {
Content->Slot = this;
}
if (Slot) {
Slot->AttachWidget(Content == nullptr ? SNullWidget::NullWidget : Content->TakeWidget());
}
}
}
void UUMGSplitterSlot::SynchronizeProperties() {
Super::SynchronizeProperties();
SetSizeRule(SizeRule);
SetValue(Value);
SetMinSize(MinSize);
SetResizable(Resizable);
}
void UUMGSplitterSlot::SetSizeRule(EUMGSplitterSizeRule InSizeRule) {
SizeRule = InSizeRule;
if (Slot) {
Slot->SetSizingRule(GetSlateSizeRule(SizeRule));
}
}
EUMGSplitterSizeRule UUMGSplitterSlot::GetSizeRule() const { return SizeRule; }
void UUMGSplitterSlot::SetValue(float InValue) {
Value = InValue;
if (Slot) {
Slot->SetSizeValue(Value);
}
}
float UUMGSplitterSlot::GetValue() const { return Value; }
void UUMGSplitterSlot::SetMinSize(float InMinSize) {
MinSize = InMinSize;
if (Slot) {
Slot->SetMinSize(MinSize);
}
}
float UUMGSplitterSlot::GetMinSize() const { return MinSize; }
void UUMGSplitterSlot::SetResizable(bool bInIsResizable) {
Resizable = bInIsResizable;
if (Slot) {
Slot->SetResizable(Resizable);
}
}
bool UUMGSplitterSlot::GetResizable() const { return Resizable; }
void UUMGSplitterSlot::OnSlotResized(float Size) {
UE_LOG(LogTemp, Warning, TEXT("Slot resized: %f"), Size);
Value = Size;
if (Slot) {
Slot->SetSizeValue(Size);
}
if (UUMGSplitter* Splitter = Cast<UUMGSplitter>(Parent)) {
Splitter->OnSlotResized(this, Size);
}
}
#pragma endregion
#pragma region "Splitter"
UUMGSplitter::UUMGSplitter(const FObjectInitializer& OI): Super(OI) {
Style = FAppStyle::Get().GetWidgetStyle<FSplitterStyle>("DetailsView.Splitter");
}
void UUMGSplitter::OnSlotResized(UUMGSplitterSlot* InSlot, float Size) {
OnSlotResizedEvent.Broadcast(InSlot, Size);
}
void UUMGSplitter::SynchronizeProperties() {
Super::SynchronizeProperties();
if (Splitter.IsValid()) {
Splitter->SetOrientation(Orientation);
}
}
TSharedRef<SWidget> UUMGSplitter::RebuildWidget() {
Splitter = SNew(SSplitter)
.Style(&Style)
.Orientation(Orientation)
.ResizeMode(GetSlateResizeMode(ResizeMode))
.PhysicalSplitterHandleSize(PhysicalSplitterHandleSize)
.HitDetectionSplitterHandleSize(HitDetectionSplitterHandleSize)
.MinimumSlotHeight(MinimumSlotHeight)
// .PhysicalSplitterHandleSize(1.0f)
// .HitDetectionSplitterHandleSize(5.0f);
;
for (UPanelSlot* PanelSlot : Slots) {
if (UUMGSplitterSlot* TypedSlot = Cast<UUMGSplitterSlot>(PanelSlot)) {
TypedSlot->Parent = this;
TypedSlot->BuildSlot(Splitter.ToSharedRef());
}
}
return Splitter.ToSharedRef();
}
void UUMGSplitter::ReleaseSlateResources(bool bReleaseChildren) {
Super::ReleaseSlateResources(bReleaseChildren);
Splitter.Reset();
}
void UUMGSplitter::OnSlotAdded(UPanelSlot* InSlot) {
if (Splitter.IsValid()) {
CastChecked<UUMGSplitterSlot>(InSlot)->BuildSlot(Splitter.ToSharedRef());
}
}
void UUMGSplitter::OnSlotRemoved(UPanelSlot* InSlot) {
if (Splitter.IsValid() && InSlot->Content) {
TSharedPtr<SWidget> Widget = InSlot->Content->GetCachedWidget();
if (Widget.IsValid()) {
TPanelChildren<SSplitter::FSlot>* SplitterChildren = static_cast<TPanelChildren<SSplitter::FSlot>*>(Splitter->GetChildren());
SplitterChildren->Remove(Widget.ToSharedRef());
}
}
}
#pragma endregion
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Components/PanelWidget.h"
#include "Widgets/Layout/SSplitter.h"
#include "UMGSplitter.generated.h"
#define LOCTEXT_NAMESPACE "FontsAndIcons"
UENUM(BlueprintType)
enum class EUMGSplitterSizeRule: uint8
{
/** Get the DesiredSize() of the content */
SizeToContent,
/** Use a fraction of the parent's size */
FractionOfParent
};
inline GFONTSANDICONSEDITOR_API SSplitter::ESizeRule GetSlateSizeRule(EUMGSplitterSizeRule InSizeRule) {
return static_cast<SSplitter::ESizeRule>(static_cast<uint8>(InSizeRule));
}
UENUM(BlueprintType)
enum class EUMGSplitterResizeMode : uint8
{
/** Resize the selected slot. If space is needed, then resize the next resizable slot. */
FixedPosition,
/** Resize the selected slot. If space is needed, then resize the last resizable slot. */
FixedSize,
/** Resize the selected slot by redistributing the available space with the following resizable slots. */
Fill,
};
inline GFONTSANDICONSEDITOR_API ESplitterResizeMode::Type GetSlateResizeMode(EUMGSplitterResizeMode InResizeMode) {
return static_cast<ESplitterResizeMode::Type>(static_cast<uint8>(InResizeMode));
}
UCLASS(BlueprintType)
class GFONTSANDICONSEDITOR_API UUMGSplitterSlot : public UPanelSlot
{
GENERATED_BODY()
protected:
SSplitter::FSlot* Slot;
/** The size rule used by the slot. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetSizeRule, Category="Layout|Splitter Slot")
EUMGSplitterSizeRule SizeRule = EUMGSplitterSizeRule::FractionOfParent;
/** When the RuleSize is set to FractionOfParent, the size of the slot is the Value percentage of its parent size. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetValue, Category="Layout|Splitter Slot")
float Value = 1.0f;
/** Minimum slot size when resizing. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetMinSize, Category="Layout|Splitter Slot")
float MinSize = 0.0f;
/** Can the slot be resize by the user. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetResizable, Category="Layout|Splitter Slot")
bool Resizable = true;
public:
UUMGSplitterSlot(const FObjectInitializer& Oi = FObjectInitializer::Get());
protected:
virtual void ReleaseSlateResources(bool bReleaseChildren) override;
public:
virtual void BuildSlot(TSharedRef<SSplitter> Splitter);
virtual void ReplaceContent(UWidget* NewContent);
virtual void SynchronizeProperties() override;
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
void SetSizeRule(EUMGSplitterSizeRule InSizeRule);
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
EUMGSplitterSizeRule GetSizeRule() const;
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
void SetValue(float InValue);
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
float GetValue() const;
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
void SetMinSize(float InMinSize);
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
float GetMinSize() const;
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
void SetResizable(bool bInIsResizable);
UFUNCTION(BlueprintCallable, Category="Layout|Splitter Slot")
bool GetResizable() const;
void OnSlotResized(float Size);
};
UCLASS(ClassGroup = UI, meta = (Category = "Editor|FontsAndIcons", DisableNativeTick))
class GFONTSANDICONSEDITOR_API UUMGSplitter : public UPanelWidget
{
GENERATED_BODY()
public:
TSharedPtr<SSplitter> Splitter;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetStyle, Category="Appearance")
FSplitterStyle Style;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetOrientation, Category="Appearance")
TEnumAsByte<EOrientation> Orientation = Orient_Horizontal;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetResizeMode, Category="Appearance")
EUMGSplitterResizeMode ResizeMode = EUMGSplitterResizeMode::FixedPosition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetPhysicalSplitterHandleSize, Category="Appearance")
float PhysicalSplitterHandleSize = 5.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetHitDetectionSplitterHandleSize, Category="Appearance")
float HitDetectionSplitterHandleSize = 5.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, BlueprintSetter=SetMinimumSlotHeight, Category="Appearance")
float MinimumSlotHeight = 20.f;
UUMGSplitter(const FObjectInitializer& OI = FObjectInitializer::Get());
#if WITH_EDITOR
virtual const FText GetPaletteCategory() override {
static const FText Category = LOCTEXT("FontsAndIcons", "Fonts And Icons");
return Category;
}
#endif
void OnSlotResized(UUMGSplitterSlot* InSlot, float Size);
UUMGSplitterSlot* SlotAt(int32 SlotIndex) {
return Cast<UUMGSplitterSlot>(GetSlots()[SlotIndex]);
}
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnSlotResized, UUMGSplitterSlot*, float);
FOnSlotResized OnSlotResizedEvent;
virtual void SynchronizeProperties() override;
protected:
virtual TSharedRef<SWidget> RebuildWidget() override;
virtual void ReleaseSlateResources(bool bReleaseChildren) override;
virtual UClass* GetSlotClass() const override {
return UUMGSplitterSlot::StaticClass();
}
virtual void OnSlotAdded(UPanelSlot* InSlot) override;
virtual void OnSlotRemoved(UPanelSlot* InSlot) override;
public:
UFUNCTION(BlueprintCallable, Category="Appearance")
FSplitterStyle GetStyle() const { return Style; }
UFUNCTION(BlueprintCallable, Category="Appearance")
void SetStyle(FSplitterStyle InStyle) {
Style = InStyle;
// Slate doesn't have setter... :|
}
UFUNCTION(BlueprintCallable, Category="Appearance")
EOrientation GetOrientation() const { return Orientation; }
UFUNCTION(BlueprintCallable, Category="Appearance")
void SetOrientation(EOrientation InOrientation) {
Orientation = InOrientation;
if (Splitter.IsValid()) {
Splitter->SetOrientation(Orientation);
}
}
UFUNCTION(BlueprintCallable, Category="Appearance")
EUMGSplitterResizeMode GetResizeMode() const { return ResizeMode; }
UFUNCTION(BlueprintCallable, Category="Appearance")
void SetResizeMode(EUMGSplitterResizeMode InResizeMode) {
ResizeMode = InResizeMode;
// Slate doesn't have setter... :|
}
UFUNCTION(BlueprintCallable, Category="Appearance")
float GetPhysicalSplitterHandleSize() const { return PhysicalSplitterHandleSize; }
UFUNCTION(BlueprintCallable, Category="Appearance")
void SetPhysicalSplitterHandleSize(float InPhysicalSplitterHandleSize) {
PhysicalSplitterHandleSize = InPhysicalSplitterHandleSize;
// Slate doesn't have setter... :|
}
UFUNCTION(BlueprintCallable, Category="Appearance")
float GetHitDetectionSplitterHandleSize() const { return HitDetectionSplitterHandleSize; }
UFUNCTION(BlueprintCallable, Category="Appearance")
void SetHitDetectionSplitterHandleSize(float InHitDetectionSplitterHandleSize) {
HitDetectionSplitterHandleSize = InHitDetectionSplitterHandleSize;
// Slate doesn't have setter... :|
}
UFUNCTION(BlueprintCallable, Category="Appearance")
float GetMinimumSlotHeight() const { return MinimumSlotHeight; }
UFUNCTION(BlueprintCallable, Category="Appearance")
void SetMinimumSlotHeight(float InMinimumSlotHeight) {
MinimumSlotHeight = InMinimumSlotHeight;
// Slate doesn't have setter... :|
}
};
#undef LOCTEXT_NAMESPACE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment