October3d55/M/LGUI/Source/LGUIEditor/Private/DetailCustomization/UIItemCustomization.cpp

2139 lines
67 KiB
C++

// Copyright 2019-Present LexLiu. All Rights Reserved.
#include "DetailCustomization/UIItemCustomization.h"
#include "Widgets/Layout/Anchors.h"
#include "Widgets/Layout/SConstraintCanvas.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/Input/SMultiLineEditableTextBox.h"
#include "IDetailGroup.h"
#include "LGUIEditorStyle.h"
#include "Editor.h"
#include "Widget/ComponentTransformDetails.h"
#include "Widget/AnchorPreviewWidget.h"
#include "PropertyCustomizationHelpers.h"
#include "HAL/PlatformApplicationMisc.h"
#include "LevelEditorViewport.h"
#include "LGUIEditorUtils.h"
#include "LGUIEditorTools.h"
#include "Layout/UILayoutBase.h"
#include "Layout/UILayoutElement.h"
#include "LGUIHeaders.h"
#include "PrefabEditor/LGUIPrefabEditor.h"
#include "PrefabSystem/LGUIPrefabManager.h"
#include "LGUIEditorModule.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "Widgets/Input/SNumericEntryBox.h"
#define LOCTEXT_NAMESPACE "UIItemComponentDetails"
UE_DISABLE_OPTIMIZATION
FUIItemCustomization::FUIItemCustomization()
{
}
FUIItemCustomization::~FUIItemCustomization()
{
}
TSharedRef<IDetailCustomization> FUIItemCustomization::MakeInstance()
{
return MakeShareable(new FUIItemCustomization);
}
void FUIItemCustomization::ForceUpdateUI()
{
for (auto item : TargetScriptArray)
{
if (item.IsValid())
{
item->EditorForceUpdate();
}
}
}
#include "Layout/UIPanelLayoutBase.h"
void FUIItemCustomization::AddSlotProperty(IDetailLayoutBuilder& DetailBuilder)
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return;
auto UIItem = TargetScriptArray[0];
auto ParentUIItem = UIItem->GetParentUIItem();
if (!IsValid(ParentUIItem))return;
auto PanelLayout = ParentUIItem->GetOwner()->FindComponentByClass<UUIPanelLayoutBase>();
if (!IsValid(PanelLayout))return;
auto Slot = PanelLayout->GetChildSlot(UIItem.Get());
if (!IsValid(Slot))return;
IDetailCategoryBuilder& PanelLayoutSlotCategory = DetailBuilder.EditCategory("LGUILayoutSlot"
, FText::Format(LOCTEXT("LGUIPanelLayoutSlotFormat", "LGUI-PanelLayout-Slot ({0})"), PanelLayout->GetCategoryDisplayName())
, ECategoryPriority::Transform);
PanelLayoutSlotCategory.AddExternalObjects({ Slot }, EPropertyLocation::Default,
FAddPropertyParams().CreateCategoryNodes(false).HideRootObjectNode(true));
}
void FUIItemCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
TArray<TWeakObjectPtr<UObject>> targetObjects;
DetailBuilder.GetObjectsBeingCustomized(targetObjects);
TargetScriptArray.Empty();
for (auto item : targetObjects)
{
if (auto validItem = Cast<UUIItem>(item.Get()))
{
TargetScriptArray.Add(validItem);
if (validItem->GetWorld() != nullptr)
{
if (validItem->GetWorld()->WorldType == EWorldType::Editor)
{
validItem->EditorForceUpdate();
}
}
}
}
if (TargetScriptArray.Num() == 0)
{
UE_LOG(LGUIEditor, Log, TEXT("[UIItemCustomization]Get TargetScript is null"));
return;
}
LGUIEditorUtils::ShowError_MultiComponentNotAllowed(&DetailBuilder, TargetScriptArray[0].Get());
AddSlotProperty(DetailBuilder);
IDetailCategoryBuilder& LGUICategory = DetailBuilder.EditCategory("LGUI");
DetailBuilder.HideCategory("TransformCommon");
IDetailCategoryBuilder& TransformCategory = DetailBuilder.EditCategory("LGUITransform", LOCTEXT("LGUI-Transform", "LGUI-Transform"), ECategoryPriority::Transform);
//base
{
auto uiActiveHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, bIsUIActive));
uiActiveHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda([this] {
ForceUpdateUI();
}));
}
DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData));
LGUICategory.AddProperty(GET_MEMBER_NAME_CHECKED(UUIItem, bIsUIActive));
//anchor, width, height
{
TMap<EAnchorControlledByLayoutType, TArray<UObject*>> MultipleLayoutControlMap;
if (IsAnchorControlledByMultipleLayout(MultipleLayoutControlMap))
{
FString ControlTypeString;
for (auto& KeyValue : MultipleLayoutControlMap)
{
if (KeyValue.Value.Num() <= 1)continue;
FString LayoutControlTypeString;
switch (KeyValue.Key)
{
case EAnchorControlledByLayoutType::HorizontalAnchor:
LayoutControlTypeString = "HorizontalAnchor";
break;
case EAnchorControlledByLayoutType::HorizontalAnchoredPosition:
LayoutControlTypeString = "HorizontalAnchoredPosition";
break;
case EAnchorControlledByLayoutType::HorizontalSizeDelta:
LayoutControlTypeString = "HorizontalSizeDelta";
break;
case EAnchorControlledByLayoutType::VerticalAnchor:
LayoutControlTypeString = "VerticalAnchor";
break;
case EAnchorControlledByLayoutType::VerticalAnchoredPosition:
LayoutControlTypeString = "VerticalAnchoredPosition";
break;
case EAnchorControlledByLayoutType::VerticalSizeDelta:
LayoutControlTypeString = "VerticalSizeDelta";
break;
}
ControlTypeString += FString::Printf(TEXT("Anchor '%s' is controlled by layout object:\n"), *LayoutControlTypeString);
for (auto& ObjectItem : KeyValue.Value)
{
FString LayoutObjectNameString;
if (auto Actor = Cast<AActor>(ObjectItem))
{
LayoutObjectNameString = Actor->GetActorLabel();
}
else if (auto Component = Cast<UActorComponent>(ObjectItem))
{
LayoutObjectNameString = FString::Printf(TEXT("%s.%s"), *Component->GetOwner()->GetActorLabel(), *Component->GetName());
}
else
{
LayoutObjectNameString = Component->GetName();
}
ControlTypeString += FString::Printf(TEXT(" '%s'\n"), *LayoutObjectNameString);
}
}
auto MessageText = FText::Format(LOCTEXT("MultiLayoutControlAnchorInfoText", "[{0}].{1} Detect multiple layout control this UI's anchor, this may cause issue!\n{2}")
, FText::FromString(ANSI_TO_TCHAR(__FUNCTION__)), __LINE__, FText::FromString(ControlTypeString));
TransformCategory.AddCustomRow(LOCTEXT("MultiLayoutControlAnchorError", "MultiLayoutControlAnchorError"))
.WholeRowContent()
[
SNew(STextBlock)
.Text(MessageText)
.ColorAndOpacity(FLinearColor(FColor::Yellow))
.AutoWrapText(true)
]
;
LGUIUtils::EditorNotification(MessageText, 8.0f);
}
auto AnchorHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData));
auto AnchorMinHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.AnchorMin));
auto AnchorMaxHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.AnchorMax));
auto AnchoredPositionHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.AnchoredPosition));
auto SizeDeltaHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.SizeDelta));
FVector2D AnchorMin, AnchorMax;
AnchorMinHandle->GetValue(AnchorMin);
AnchorMaxHandle->GetValue(AnchorMax);
//anchors preset menu
FVector2D anchorItemSize(42, 42);
float itemBasePadding = 8;
FMargin AnchorLabelMargin = FMargin(4, 2);
FMargin AnchorValueMargin = FMargin(2, 2);
auto MakeAnchorLabelWidget = [&](int AnchorLabelIndex) {
return
SNew(SBox)
.Padding(AnchorLabelMargin)
.VAlign(EVerticalAlignment::VAlign_Center)
[
SNew(STextBlock)
.Text(this, &FUIItemCustomization::GetAnchorLabelText, AnchorMinHandle, AnchorMaxHandle, AnchorLabelIndex)
.ToolTipText(this, &FUIItemCustomization::GetAnchorLabelTooltipText, AnchorMinHandle, AnchorMaxHandle, AnchorLabelIndex)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
;
};
auto MakeAnchorValueWidget = [=, this](int AnchorValueIndex) {
return
SNew(SBox)
.Padding(AnchorValueMargin)
.VAlign(EVerticalAlignment::VAlign_Center)
[
SNew(SNumericEntryBox<float>)
.AllowSpin(true)
.MinSliderValue(this, &FUIItemCustomization::GetMinMaxSliderValue, AnchorHandle, AnchorValueIndex, true)
.MaxSliderValue(this, &FUIItemCustomization::GetMinMaxSliderValue, AnchorHandle, AnchorValueIndex, false)
.Font(IDetailLayoutBuilder::GetDetailFont())
.UndeterminedString( NSLOCTEXT( "PropertyEditor", "MultipleValues", "Multiple Values") )
.Value(this, &FUIItemCustomization::GetAnchorValue, AnchorHandle, AnchorValueIndex)
.OnValueChanged(this, &FUIItemCustomization::OnAnchorValueChanged, AnchorHandle, AnchorValueIndex)
.OnValueCommitted(this, &FUIItemCustomization::OnAnchorValueCommitted, AnchorHandle, AnchorValueIndex)
.OnBeginSliderMovement(this, &FUIItemCustomization::OnAnchorValueSliderMovementBegin)
.OnEndSliderMovement(this, &FUIItemCustomization::OnAnchorValueSliderMovementEnd, AnchorHandle, AnchorValueIndex)
.IsEnabled(this, &FUIItemCustomization::IsAnchorValueEnable, AnchorHandle, AnchorValueIndex)
]
;
};
auto DetailBuilderPoiter = &DetailBuilder;
auto MakeAnchorPreviewWidget = [=, this](LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign HAlign, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign VAlign) {
return
SNew(LGUIAnchorPreviewWidget::SAnchorPreviewWidget, anchorItemSize)
.BasePadding(itemBasePadding)
.SelectedHAlign(this, &FUIItemCustomization::GetAnchorHAlign, AnchorMinHandle, AnchorMaxHandle)
.SelectedVAlign(this, &FUIItemCustomization::GetAnchorVAlign, AnchorMinHandle, AnchorMaxHandle)
.PersistentHAlign(HAlign)
.PersistentVAlign(VAlign)
.ButtonEnable(true)
.OnAnchorChange(this, &FUIItemCustomization::OnSelectAnchor, DetailBuilderPoiter)
;
};//@todo: auto refresh SAnchorPreviewWidget when change from AnchorMinMax
auto SplitLineColor = FLinearColor(0.5f, 0.5f, 0.5f);
TransformCategory.AddCustomRow(LOCTEXT("Anchor","Anchor"))
.CopyAction(FUIAction
(
FExecuteAction::CreateSP(this, &FUIItemCustomization::OnCopyAnchor),
FCanExecuteAction::CreateSP(this, &FUIItemCustomization::OnCanCopyAnchor)
))
.PasteAction(FUIAction
(
FExecuteAction::CreateSP(this, &FUIItemCustomization::OnPasteAnchor, &DetailBuilder),
FCanExecuteAction::CreateSP(this, &FUIItemCustomization::OnCanPasteAnchor)
))
.ValueContent()
.MinDesiredWidth(500)
[
SNew(SBox)
.IsEnabled(this, &FUIItemCustomization::IsAnchorEditable)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorLabelWidget(0)
]
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorLabelWidget(1)
]
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorValueWidget(0)
]
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorValueWidget(1)
]
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorLabelWidget(2)
]
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorLabelWidget(3)
]
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorValueWidget(2)
]
+SHorizontalBox::Slot()
.FillWidth(0.5f)
[
MakeAnchorValueWidget(3)
]
]
]
]
.NameContent()
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
[
SNew(SBox)
.Visibility(this, &FUIItemCustomization::GetAnchorPresetButtonVisibility)
[
SNew(SComboButton)
.ContentPadding(8)
.HasDownArrow(false)
.ToolTipText(this, &FUIItemCustomization::GetAnchorsTooltipText)
.ButtonStyle(FLGUIEditorStyle::Get(), "AnchorButton")
.ButtonContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.HAlign(EHorizontalAlignment::HAlign_Left)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.Padding(FMargin(0, 0))
[
SNew(SBox)
.Padding(FMargin(0, 0))
.HAlign(EHorizontalAlignment::HAlign_Center)
[
SNew(STextBlock)
.Text(this, &FUIItemCustomization::GetHAlignText, AnchorMinHandle, AnchorMaxHandle)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
]
+SVerticalBox::Slot()
.Padding(FMargin(0, 0))
.AutoHeight()
[
TargetScriptArray[0]->GetParentUIItem() != nullptr
?
SNew(SBox)
[
SNew(LGUIAnchorPreviewWidget::SAnchorPreviewWidget, FVector2D(40, 40))
.BasePadding(0)
.ButtonEnable(false)
.PersistentHAlign(this, &FUIItemCustomization::GetAnchorHAlign, AnchorMinHandle, AnchorMaxHandle)
.PersistentVAlign(this, &FUIItemCustomization::GetAnchorVAlign, AnchorMinHandle, AnchorMaxHandle)
//.SelectedHAlign(this, &FUIItemCustomization::GetAnchorHAlign, AnchorMinHandle, AnchorMaxHandle)
//.SelectedVAlign(this, &FUIItemCustomization::GetAnchorVAlign, AnchorMinHandle, AnchorMaxHandle)
]
:
SNew(SBox)
]
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SBox)
.Padding(FMargin(0, 0))
.HAlign(EHorizontalAlignment::HAlign_Center)
[
SNew(STextBlock)
.Text(this, &FUIItemCustomization::GetVAlignText, AnchorMinHandle, AnchorMaxHandle)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Justification(ETextJustify::Center)
.RenderTransformPivot(FVector2D(0, 0.5f))
.RenderTransform(FSlateRenderTransform(FQuat2D(FMath::DegreesToRadians(90)), FVector2D(-12, -10)))
]
]
]
.MenuContent()
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBorder)
.Padding(4)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
[
SNew(STextBlock)
.Text(LOCTEXT("AnchorPresets", "Anchor Presets"))
]
+SVerticalBox::Slot()
[
SNew(STextBlock)
.Text(LOCTEXT("AnchorPresetsHelperKeys", "Shift: Also set pivot Alt: Also set position"))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(4)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SOverlay)
+SOverlay::Slot()
[
SNew(SUniformGridPanel)
+SUniformGridPanel::Slot(1, 0)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::None)
]
+SUniformGridPanel::Slot(2, 0)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::None)
]
+SUniformGridPanel::Slot(3, 0)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::None)
]
+SUniformGridPanel::Slot(4, 0)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::None)
]
//Top
+SUniformGridPanel::Slot(0, 1)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::None, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top)
]
+SUniformGridPanel::Slot(1, 1)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top)
]
+SUniformGridPanel::Slot(2, 1)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top)
]
+SUniformGridPanel::Slot(3, 1)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top)
]
+SUniformGridPanel::Slot(4, 1)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top)
]
//Center
+SUniformGridPanel::Slot(0, 2)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::None, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle)
]
+SUniformGridPanel::Slot(1, 2)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle)
]
+SUniformGridPanel::Slot(2, 2)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle)
]
+SUniformGridPanel::Slot(3, 2)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle)
]
+SUniformGridPanel::Slot(4, 2)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle)
]
//Bottom
+SUniformGridPanel::Slot(0, 3)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::None, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom)
]
+SUniformGridPanel::Slot(1, 3)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom)
]
+SUniformGridPanel::Slot(2, 3)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom)
]
+SUniformGridPanel::Slot(3, 3)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom)
]
+SUniformGridPanel::Slot(4, 3)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom)
]
//Bottom stretch
+SUniformGridPanel::Slot(0, 4)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::None, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch)
]
+SUniformGridPanel::Slot(1, 4)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch)
]
+SUniformGridPanel::Slot(2, 4)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch)
]
+SUniformGridPanel::Slot(3, 4)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch)
]
+SUniformGridPanel::Slot(4, 4)
[
MakeAnchorPreviewWidget(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch)
]
]
//splite line
+ SOverlay::Slot()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(EHorizontalAlignment::HAlign_Left)
[
SNew(SBox)
.WidthOverride(anchorItemSize.X + 16)
[
SNew(SBox)
.HAlign(EHorizontalAlignment::HAlign_Right)
.WidthOverride(1)
[
SNew(SImage)
.Image(FLGUIEditorStyle::Get().GetBrush("LGUIEditor.WhiteDot"))
.ColorAndOpacity(SplitLineColor)
]
]
]
]
+ SOverlay::Slot()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(EHorizontalAlignment::HAlign_Right)
[
SNew(SBox)
.WidthOverride(anchorItemSize.X + 16)
[
SNew(SBox)
.HAlign(EHorizontalAlignment::HAlign_Left)
.WidthOverride(1)
[
SNew(SImage)
.Image(FLGUIEditorStyle::Get().GetBrush("LGUIEditor.WhiteDot"))
.ColorAndOpacity(SplitLineColor)
]
]
]
]
+ SOverlay::Slot()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(EVerticalAlignment::VAlign_Top)
[
SNew(SBox)
.HeightOverride(anchorItemSize.X + 16)
[
SNew(SBox)
.VAlign(EVerticalAlignment::VAlign_Bottom)
.HeightOverride(1)
[
SNew(SImage)
.Image(FLGUIEditorStyle::Get().GetBrush("LGUIEditor.WhiteDot"))
.ColorAndOpacity(SplitLineColor)
]
]
]
]
+ SOverlay::Slot()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(EVerticalAlignment::VAlign_Bottom)
[
SNew(SBox)
.HeightOverride(anchorItemSize.X + 16)
[
SNew(SBox)
.VAlign(EVerticalAlignment::VAlign_Top)
.HeightOverride(1)
[
SNew(SImage)
.Image(FLGUIEditorStyle::Get().GetBrush("LGUIEditor.WhiteDot"))
.ColorAndOpacity(SplitLineColor)
]
]
]
]
]
]
]
]
]
]
]
;
IDetailGroup& AnchorGroup = TransformCategory.AddGroup(FName("Anchors"), LOCTEXT("AnchorsGroup", "Anchors"));
auto AnchorControlledByLayout = GetLayoutControlHorizontalAnchor() || GetLayoutControlVerticalAnchor();
IDetailPropertyRow& AnchorMinProperty = AnchorGroup.AddPropertyRow(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.AnchorMin)));
if (!this->IsAnchorEditable())
{
AnchorMinProperty.IsEnabled(false);
AnchorMinProperty.ToolTip(LOCTEXT("ControlledByLayoutTip", "This property is controlled by layout"));
}
IDetailPropertyRow& AnchorMaxProperty = AnchorGroup.AddPropertyRow(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.AnchorMax)));
if (!this->IsAnchorEditable())
{
AnchorMaxProperty.IsEnabled(false);
AnchorMaxProperty.ToolTip(LOCTEXT("ControlledByLayoutTip", "This property is controlled by layout"));
}
auto& AnchorRawDataGroup = TransformCategory.AddGroup(FName("AnchorsRawData"), LOCTEXT("AnchorsRawData", "AnchorsRawData"), true);
AnchorRawDataGroup.AddWidgetRow()
.WholeRowContent()
[
SNew(STextBlock)
.Text(LOCTEXT("AnchorRawDataWarning", "Normally do not edit these!"))
.ColorAndOpacity(FLinearColor(FColor::Yellow))
.AutoWrapText(true)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
;
auto& AnchoredPositionProperty = AnchorRawDataGroup.AddPropertyRow(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.AnchoredPosition)));
auto& SizeDeltaProperty = AnchorRawDataGroup.AddPropertyRow(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.SizeDelta)));
AnchoredPositionProperty.IsEnabled(this->IsAnchorEditable());
SizeDeltaProperty.IsEnabled(this->IsAnchorEditable());
}
//pivot
auto PivotHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData.Pivot));
auto& PivotProperty = TransformCategory.AddProperty(PivotHandle);
PivotProperty.IsEnabled(this->IsAnchorEditable());
PivotHandle->SetOnPropertyValuePreChange(FSimpleDelegate::CreateLambda([=, this] {
this->OnPrePivotChange();
}));
PivotHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda([=, this] {
this->OnPivotChanged();
}));
PivotHandle->SetOnChildPropertyValuePreChange(FSimpleDelegate::CreateLambda([=, this] {
this->OnPrePivotChange();
}));
PivotHandle->SetOnChildPropertyValueChanged(FSimpleDelegate::CreateLambda([=, this] {
this->OnPivotChanged();
}));
//location rotation scale
const FSelectedActorInfo& selectedActorInfo = DetailBuilder.GetDetailsView()->GetSelectedActorInfo();
TSharedRef<FComponentTransformDetails> transformDetails = MakeShareable(new FComponentTransformDetails(TargetScriptArray, selectedActorInfo, DetailBuilder));
TransformCategory.AddCustomBuilder(transformDetails);
//HierarchyIndex
{
auto HierarchyIndexHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, hierarchyIndex));
DetailBuilder.HideProperty(HierarchyIndexHandle);
HierarchyIndexHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda([=, this] {
ForceUpdateUI();
ULGUIPrefabManagerObject::MarkBroadcastLevelActorListChanged();
}));
auto hierarchyIndexWidget =
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(2, 0)
[
HierarchyIndexHandle->CreatePropertyValueWidget()
]
+ SHorizontalBox::Slot()
.Padding(2, 0)
.AutoWidth()
[
SNew(SButton)
.ToolTipText(LOCTEXT("IncreaseHierarchyOrder_Tooltip", "Move order up"))
.HAlign(EHorizontalAlignment::HAlign_Center)
.VAlign(EVerticalAlignment::VAlign_Center)
.IsEnabled_Static(LGUIEditorUtils::IsEnabledOnProperty, HierarchyIndexHandle)
.OnClicked(this, &FUIItemCustomization::OnClickIncreaseOrDecreaseHierarchyIndex, true, HierarchyIndexHandle)
[
SNew(STextBlock)
.Text(LOCTEXT("IncreaseHierarchyOrder", "+"))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
]
+ SHorizontalBox::Slot()
.Padding(2, 0)
.AutoWidth()
[
SNew(SButton)
.ToolTipText(LOCTEXT("DecreaseHierarchyOrder_Tooltip", "Move order down"))
.HAlign(EHorizontalAlignment::HAlign_Center)
.VAlign(EVerticalAlignment::VAlign_Center)
.IsEnabled_Static(LGUIEditorUtils::IsEnabledOnProperty, HierarchyIndexHandle)
.OnClicked(this, &FUIItemCustomization::OnClickIncreaseOrDecreaseHierarchyIndex, false, HierarchyIndexHandle)
[
SNew(STextBlock)
.Text(LOCTEXT("DecreaseHierarchyOrder", "-"))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
];
TransformCategory.AddCustomRow(LOCTEXT("HierarchyIndexManager", "HierarchyIndexManager"))
.CopyAction(FUIAction(
FExecuteAction::CreateSP(this, &FUIItemCustomization::OnCopyHierarchyIndex)
))
.PasteAction(FUIAction(
FExecuteAction::CreateSP(this, &FUIItemCustomization::OnPasteHierarchyIndex, HierarchyIndexHandle)
))
.NameContent()
[
HierarchyIndexHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
hierarchyIndexWidget
]
.PropertyHandleList({ HierarchyIndexHandle })
;
TransformCategory.AddProperty(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, flattenHierarchyIndex)), EPropertyLocation::Advanced);
}
//displayName
auto displayNamePropertyHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UUIItem, displayName));
DetailBuilder.HideProperty(displayNamePropertyHandle);
LGUICategory.AddCustomRow(LOCTEXT("DisplayName", "Display Name"), true)
.NameContent()
[
displayNamePropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
.MinDesiredWidth(500)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[
displayNamePropertyHandle->CreatePropertyValueWidget(true)
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.Text(LOCTEXT("FixDisplayName", "Fix it"))
.OnClicked(this, &FUIItemCustomization::OnClickFixDisplayNameButton, true, displayNamePropertyHandle)
.HAlign(EHorizontalAlignment::HAlign_Center)
.Visibility(this, &FUIItemCustomization::GetDisplayNameWarningVisibility)
.ToolTipText(LOCTEXT("FixDisplayName_Tooltip", "DisplayName not equal to ActorLabel."))
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.Text(LOCTEXT("FixDisplayNameOnHierarchy", "Fix all hierarchy"))
.OnClicked(this, &FUIItemCustomization::OnClickFixDisplayNameButton, false, displayNamePropertyHandle)
.HAlign(EHorizontalAlignment::HAlign_Center)
.Visibility(this, &FUIItemCustomization::GetDisplayNameWarningVisibility)
.ToolTipText(LOCTEXT("FixDisplayNameOnHierarchy_Tooltip", "DisplayName not equal to ActorLabel."))
]
]
;
}
EVisibility FUIItemCustomization::GetAnchorPresetButtonVisibility()const
{
if (TargetScriptArray.Num() > 0 && TargetScriptArray[0].IsValid())
{
return TargetScriptArray[0]->GetParentUIItem() != nullptr ? EVisibility::Visible : EVisibility::Hidden;
}
return EVisibility::Hidden;
}
bool FUIItemCustomization::IsAnchorEditable()const
{
if (TargetScriptArray.Num() > 0 && TargetScriptArray[0].IsValid())
{
auto UIItem = TargetScriptArray[0];
if (FLGUIPrefabEditor::ActorIsRootAgent(UIItem->GetOwner()))return true;//special for PrefabEditor's agent root actor
if (UIItem->GetParentUIItem() != nullptr)return true;//not root
if (UIItem->IsCanvasUIItem() && UIItem->GetRenderCanvas() != nullptr && UIItem->GetRenderCanvas()->IsRenderToScreenSpace())//is root canvas, and is render to screen space
{
return false;
}
}
return true;
}
void FUIItemCustomization::OnPrePivotChange()
{
AnchorAsMarginArray.Empty();
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return;
for (auto& Item : TargetScriptArray)
{
FMargin AnchorAsMargin;
if (Item.IsValid())
{
AnchorAsMargin.Left = Item->GetAnchorLeft();
AnchorAsMargin.Right = Item->GetAnchorRight();
AnchorAsMargin.Bottom = Item->GetAnchorBottom();
AnchorAsMargin.Top = Item->GetAnchorTop();
}
AnchorAsMarginArray.Add(AnchorAsMargin);
}
}
void FUIItemCustomization::OnPivotChanged()
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return;
for (int i = 0; i < TargetScriptArray.Num(); i++)
{
auto& Item = TargetScriptArray[i];
auto& AnchorAsMargin = AnchorAsMarginArray[i];
if (Item.IsValid())
{
Item->MarkAllDirtyRecursive();
//set anchors to make it stay as relative to parent
Item->SetAnchorLeft(AnchorAsMargin.Left);
Item->SetAnchorRight(AnchorAsMargin.Right);
Item->SetAnchorBottom(AnchorAsMargin.Bottom);
Item->SetAnchorTop(AnchorAsMargin.Top);
}
}
}
EVisibility FUIItemCustomization::GetDisplayNameWarningVisibility()const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return EVisibility::Hidden;
if (auto actor = TargetScriptArray[0]->GetOwner())
{
if (TargetScriptArray[0] == actor->GetRootComponent())
{
auto actorLabel = actor->GetActorLabel();
if (actorLabel.StartsWith("//"))
{
actorLabel = actorLabel.Right(actorLabel.Len() - 2);
}
if (TargetScriptArray[0]->GetDisplayName() == actorLabel)
{
return EVisibility::Hidden;
}
else
{
return EVisibility::Visible;
}
}
else
{
if (TargetScriptArray[0]->GetName() == TargetScriptArray[0]->GetDisplayName())
{
return EVisibility::Hidden;
}
else
{
return EVisibility::Visible;
}
}
}
else
{
auto name = TargetScriptArray[0]->GetName();
auto genVarSuffix = FString(TEXT("_GEN_VARIABLE"));
if (name.EndsWith(genVarSuffix))
{
name.RemoveAt(name.Len() - genVarSuffix.Len(), genVarSuffix.Len());
}
if (TargetScriptArray[0]->GetDisplayName() == name)
{
return EVisibility::Hidden;
}
else
{
return EVisibility::Visible;
}
}
}
FReply FUIItemCustomization::OnClickIncreaseOrDecreaseHierarchyIndex(bool IncreaseOrDecrease, TSharedRef<IPropertyHandle> HierarchyIndexHandle)
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return FReply::Handled();
//hierarchy index could affect other items
GEditor->BeginTransaction(LOCTEXT("ChangeHierarchyIndex_Transaction", "Change LGUI Hierarchy Index"));
for (auto& Item : TargetScriptArray)
{
Item->Modify();
if (auto Parent = Item->GetParentUIItem())
{
for (auto Child : Parent->UIChildren)
{
Child->Modify();
}
}
}
for (auto& Item : TargetScriptArray)
{
HierarchyIndexHandle->SetValue(Item->hierarchyIndex + (IncreaseOrDecrease ? 1 : -1));
//notify others
if (auto Parent = Item->GetParentUIItem())
{
for (auto Child : Parent->UIChildren)
{
auto HierarchyIndexProperty = FindFProperty<FIntProperty>(UUIItem::StaticClass(), GET_MEMBER_NAME_CHECKED(UUIItem, hierarchyIndex));
check(HierarchyIndexProperty != nullptr);
LGUIUtils::NotifyPropertyChanged(Child, HierarchyIndexProperty);
}
}
}
GEditor->EndTransaction();
ULGUIPrefabManagerObject::MarkBroadcastLevelActorListChanged();
return FReply::Handled();
}
FText FUIItemCustomization::GetHAlignText(TSharedRef<IPropertyHandle> AnchorMinHandle, TSharedRef<IPropertyHandle> AnchorMaxHandle)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return FText();
FVector2D AnchorMinValue;
AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
AnchorMaxHandle->GetValue(AnchorMaxValue);
if (AnchorMinValue.X == AnchorMaxValue.X)
{
if (AnchorMinValue.X == 0)
{
return LOCTEXT("AnchorLeft", "Left");
}
else if (AnchorMinValue.X == 0.5f)
{
return LOCTEXT("AnchorCenter", "Center");
}
else if (AnchorMinValue.X == 1.0f)
{
return LOCTEXT("AnchorRight", "Right");
}
else
{
return LOCTEXT("AnchorCustom", "Custom");
}
}
else if (AnchorMinValue.X == 0.0f && AnchorMaxValue.X == 1.0f)
{
return LOCTEXT("AnchorStretch", "Stretch");
}
else
{
return LOCTEXT("AnchorCustom", "Custom");
}
}
FText FUIItemCustomization::GetVAlignText(TSharedRef<IPropertyHandle> AnchorMinHandle, TSharedRef<IPropertyHandle> AnchorMaxHandle)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return FText();
FVector2D AnchorMinValue;
AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
AnchorMaxHandle->GetValue(AnchorMaxValue);
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
if (AnchorMinValue.Y == 0)
{
return LOCTEXT("AnchorBottom", "Bottom");
}
else if (AnchorMinValue.Y == 0.5f)
{
return LOCTEXT("AnchorMiddle", "Middle");
}
else if (AnchorMinValue.Y == 1.0f)
{
return LOCTEXT("AnchorTop", "Top");
}
else
{
return LOCTEXT("AnchorCustom", "Custom");
}
}
else if (AnchorMinValue.Y == 0.0f && AnchorMaxValue.Y == 1.0f)
{
return LOCTEXT("AnchorStretch", "Stretch");
}
else
{
return LOCTEXT("AnchorCustom", "Custom");
}
}
FText FUIItemCustomization::GetAnchorLabelText(TSharedRef<IPropertyHandle> AnchorMinHandle, TSharedRef<IPropertyHandle> AnchorMaxHandle, int LabelIndex)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return FText();
FVector2D AnchorMinValue;
AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
AnchorMaxHandle->GetValue(AnchorMaxValue);
switch (LabelIndex)
{
case 0://anchored position y, stretch left
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return LOCTEXT("AnchoredPositionX", "PosY");
}
else
{
return LOCTEXT("AnchoredLeft", "Left");
}
}
break;
case 1://anchored position z, stretch top
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return LOCTEXT("AnchoredPositionY", "PosZ");
}
else
{
return LOCTEXT("AnchoredTop", "Top");
}
}
break;
case 2://width, stretch right
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return LOCTEXT("Width", "Width");
}
else
{
return LOCTEXT("AnchoredRight", "Right");
}
}
break;
case 3://height, stretch bottom
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return LOCTEXT("Height", "Height");
}
else
{
return LOCTEXT("AnchoredBottom", "Bottom");
}
}
break;
}
return LOCTEXT("AnchorEroor", "Error");
}
FText FUIItemCustomization::GetAnchorLabelTooltipText(TSharedRef<IPropertyHandle> AnchorMinHandle, TSharedRef<IPropertyHandle> AnchorMaxHandle, int LabelTooltipIndex)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return FText();
FVector2D AnchorMinValue;
AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
AnchorMaxHandle->GetValue(AnchorMaxValue);
switch (LabelTooltipIndex)
{
default:
case 0://anchored position x, stretch left
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return FText::Format(LOCTEXT("AnchoredPositionX_Tooltip", "Horizontal anchored position. Related function: {0} / {1}."), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetAnchoredPosition)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetAnchoredPosition)));
}
else
{
return FText::Format(LOCTEXT("AnchoredLeft_Tooltip", "Calculated distance to parent's left anchor point. Related function: {0} / {1}."), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetAnchorLeft)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetAnchorLeft)));
}
}
break;
case 1://anchored position y, stretch top
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return FText::Format(LOCTEXT("AnchoredPositionY_Tooltip", "Vertical anchored position. Related function: {0} / {1}."), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetAnchoredPosition)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetAnchoredPosition)));
}
else
{
return FText::Format(LOCTEXT("AnchoredTop_Tooltip", "Calculated distance to parent's top anchor point. Related function: {0} / {1}."), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetAnchorLeft)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetAnchorLeft)));
}
}
break;
case 2://width, stretch right
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return FText::Format(LOCTEXT("Width_Tooltip", "Horizontal size. Related function: {0} / {1}."), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetWidth)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetWidth)));
}
else
{
return FText::Format(LOCTEXT("AnchoredRight_Tooltip", "Calculated distance to parent's right anchor point. Related function: {0} / {1}."), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetAnchorLeft)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetAnchorLeft)));
}
}
break;
case 3://height, stretch bottom
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return FText::Format(LOCTEXT("Height_Tooltip", "Vertical size. Related function: {0} / {1}"), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetHeight)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetHeight)));
}
else
{
return FText::Format(LOCTEXT("AnchoredBottom_Tooltip", "Calculated distance to parent's bottom anchor point. Related function: {0} / {0}."), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, GetAnchorLeft)), FText::FromString(GET_FUNCTION_NAME_STRING_CHECKED(UUIItem, SetAnchorLeft)));
}
}
break;
}
return LOCTEXT("AnchorError", "Error");
}
TArray<float> FUIItemCustomization::ValueRangeArray = {
1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f
};
TOptional<float> FUIItemCustomization::GetMinMaxSliderValue(TSharedRef<IPropertyHandle> AnchorHandle, int AnchorValueIndex, bool MinOrMax)const
{
auto Value = GetAnchorValue(AnchorHandle, AnchorValueIndex).Get(0.0f);
Value = FMath::Abs(Value);
float MaxRangeValue = ValueRangeArray[ValueRangeArray.Num() - 1];
float RangeValue = MaxRangeValue;
for (int i = ValueRangeArray.Num() - 1; i >= 0; i--)
{
auto RangeValueItem = ValueRangeArray[i];
if (Value > RangeValueItem)
{
break;
}
else
{
RangeValue = RangeValueItem;
}
}
return RangeValue *
(RangeValue >= MaxRangeValue ? 1.0f : (FMath::Abs(Value - RangeValue) < KINDA_SMALL_NUMBER ? 2.0f : 1.0f))
* (MinOrMax ? -1.0f : 1.0f);
}
TOptional<float> FUIItemCustomization::GetAnchorValue(TSharedRef<IPropertyHandle> AnchorHandle, int AnchorValueIndex)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return TOptional<float>();
auto AnchorMinHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchorMin));
auto AnchorMaxHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchorMax));
auto AnchoredPositionHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchoredPosition));
auto SizeDeltaHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, SizeDelta));
FVector2D AnchorMinValue;
auto AnchorMinValueAccessResult = AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
auto AnchorMaxValueAccessResult = AnchorMaxHandle->GetValue(AnchorMaxValue);
FVector2D AnchoredPosition;
auto AnchoredPositionAccessResult = AnchoredPositionHandle->GetValue(AnchoredPosition);
FVector2D SizeDelta;
auto SizeDeltaAccessResult = SizeDeltaHandle->GetValue(SizeDelta);
switch (AnchorValueIndex)
{
default:
case 0://anchored position x, stretch left
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success)
{
auto GetValue = [=](TWeakObjectPtr<UUIItem> Item)->float {
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return Item->GetHorizontalAnchoredPosition();
}
else
{
return Item->GetAnchorLeft();
}
};
if (AnchoredPositionAccessResult == FPropertyAccess::Result::Success)
{
return GetValue(TargetScriptArray[0]);
}
else if (AnchoredPositionAccessResult == FPropertyAccess::Result::MultipleValues)
{
bool bIsSameValue = true;
float Value = 0;
bool bIsFirst = true;
for (auto& Item : TargetScriptArray)
{
if (bIsFirst)
{
Value = GetValue(Item);
bIsFirst = false;
}
else
{
if (FMath::Abs(GetValue(Item) - Value) > KINDA_SMALL_NUMBER)
{
bIsSameValue = false;
break;
}
}
}
if (bIsSameValue)
{
return Value;
}
}
}
return TOptional<float>();
}
break;
case 1://anchored position y, stretch top
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success)
{
auto GetValue = [=](TWeakObjectPtr<UUIItem> Item)->float {
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return Item->GetVerticalAnchoredPosition();
}
else
{
return Item->GetAnchorTop();
}
};
if (AnchoredPositionAccessResult == FPropertyAccess::Result::Success)
{
return GetValue(TargetScriptArray[0]);
}
else if (AnchoredPositionAccessResult == FPropertyAccess::Result::MultipleValues)
{
bool bIsSameValue = true;
float Value = 0;
bool bIsFirst = true;
for (auto& Item : TargetScriptArray)
{
if (bIsFirst)
{
Value = GetValue(Item);
bIsFirst = false;
}
else
{
if (FMath::Abs(GetValue(Item) - Value) > KINDA_SMALL_NUMBER)
{
bIsSameValue = false;
break;
}
}
}
if (bIsSameValue)
{
return Value;
}
}
}
return TOptional<float>();
}
break;
case 2://width, stretch right
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success)
{
auto GetValue = [=](TWeakObjectPtr<UUIItem> Item)->float {
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return Item->GetSizeDelta().X;
}
else
{
return Item->GetAnchorRight();
}
};
if (SizeDeltaAccessResult == FPropertyAccess::Result::Success)
{
return GetValue(TargetScriptArray[0]);
}
else if (SizeDeltaAccessResult == FPropertyAccess::Result::MultipleValues)
{
bool bIsSameValue = true;
float Value = 0;
bool bIsFirst = true;
for (auto& Item : TargetScriptArray)
{
if (bIsFirst)
{
Value = GetValue(Item);
bIsFirst = false;
}
else
{
if (FMath::Abs(GetValue(Item) - Value) > KINDA_SMALL_NUMBER)
{
bIsSameValue = false;
break;
}
}
}
if (bIsSameValue)
{
return Value;
}
}
}
return TOptional<float>();
}
break;
case 3://height, stretch bottom
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success)
{
auto GetValue = [=](TWeakObjectPtr<UUIItem> Item)->float {
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return Item->GetSizeDelta().Y;
}
else
{
return Item->GetAnchorBottom();
}
};
if (SizeDeltaAccessResult == FPropertyAccess::Result::Success)
{
return GetValue(TargetScriptArray[0]);
}
else if (SizeDeltaAccessResult == FPropertyAccess::Result::MultipleValues)
{
bool bIsSameValue = true;
float Value = 0;
bool bIsFirst = true;
for (auto& Item : TargetScriptArray)
{
if (bIsFirst)
{
Value = GetValue(Item);
bIsFirst = false;
}
else
{
if (FMath::Abs(GetValue(Item) - Value) > KINDA_SMALL_NUMBER)
{
bIsSameValue = false;
break;
}
}
}
if (bIsSameValue)
{
return Value;
}
}
}
return TOptional<float>();
}
break;
}
}
void FUIItemCustomization::ApplyValueChanged(float Value, TSharedRef<IPropertyHandle> AnchorHandle, int AnchorValueIndex)
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return;
auto AnchorMinHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchorMin));
auto AnchorMaxHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchorMax));
auto AnchoredPositionHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchoredPosition));
auto SizeDeltaHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, SizeDelta));
FVector2D AnchorMinValue;
AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
AnchorMaxHandle->GetValue(AnchorMaxValue);
switch (AnchorValueIndex)
{
case 0://anchored position x, stretch left
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
for (auto& Item : TargetScriptArray)
{
Item->SetHorizontalAnchoredPosition(Value);
}
}
else
{
for (auto& Item : TargetScriptArray)
{
Item->SetAnchorLeft(Value);
}
}
}
break;
case 1://anchored position y, stretch top
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
for (auto& Item : TargetScriptArray)
{
Item->SetVerticalAnchoredPosition(Value);
}
}
else
{
for (auto& Item : TargetScriptArray)
{
Item->SetAnchorTop(Value);
}
}
}
break;
case 2://width, stretch right
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
for (auto& Item : TargetScriptArray)
{
Item->SetWidth(Value);
}
}
else
{
for (auto& Item : TargetScriptArray)
{
Item->SetAnchorRight(Value);
}
}
}
break;
case 3://height, stretch bottom
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
for (auto& Item : TargetScriptArray)
{
Item->SetHeight(Value);
}
}
else
{
for (auto& Item : TargetScriptArray)
{
Item->SetAnchorBottom(Value);
}
}
}
break;
}
auto AnchorProperty = FindFProperty<FProperty>(UUIItem::StaticClass(), GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData));
auto RelativeLocationProperty = FindFProperty<FProperty>(USceneComponent::StaticClass(), FName(TEXT("RelativeLocation")));
for (auto& Item : TargetScriptArray)
{
LGUIUtils::NotifyPropertyChanged(Item.Get(), AnchorProperty);
LGUIUtils::NotifyPropertyChanged(Item.Get(), RelativeLocationProperty);
}
}
void FUIItemCustomization::OnAnchorValueChanged(float Value, TSharedRef<IPropertyHandle> AnchorHandle, int AnchorValueIndex)
{
GEditor->BeginTransaction(LOCTEXT("ChangeAnchorValue_Transaction", "Change LGUI Anchor Value"));
for (auto& Item : TargetScriptArray)
{
Item->Modify();
}
ApplyValueChanged(Value, AnchorHandle, AnchorValueIndex);
GEditor->EndTransaction();
}
void FUIItemCustomization::OnAnchorValueCommitted(float Value, ETextCommit::Type commitType, TSharedRef<IPropertyHandle> AnchorHandle, int AnchorValueIndex)
{
GEditor->BeginTransaction(LOCTEXT("CommitAnchorValue_Transaction", "Commit LGUI Anchor Value"));
for (auto& Item : TargetScriptArray)
{
Item->Modify();
}
ApplyValueChanged(Value, AnchorHandle, AnchorValueIndex);
GEditor->EndTransaction();
}
void FUIItemCustomization::OnAnchorValueSliderMovementBegin()
{
GEditor->BeginTransaction(LOCTEXT("SlideAnchorValue_Transaction", "Slide LGUI Anchor Value"));
for (auto& Item : TargetScriptArray)
{
Item->Modify();
}
}
void FUIItemCustomization::OnAnchorValueSliderMovementEnd(float Value, TSharedRef<IPropertyHandle> AnchorHandle, int AnchorValueIndex)
{
ApplyValueChanged(Value, AnchorHandle, AnchorValueIndex);
GEditor->EndTransaction();
}
LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign FUIItemCustomization::GetAnchorHAlign(TSharedRef<IPropertyHandle> AnchorMinHandle, TSharedRef<IPropertyHandle> AnchorMaxHandle)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::None;
FVector2D AnchorMinValue;
AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
AnchorMaxHandle->GetValue(AnchorMaxValue);
LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign AnchorHAlign = LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::None;
if (AnchorMinValue.X == AnchorMaxValue.X)
{
if (AnchorMinValue.X == 0)
{
AnchorHAlign = LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left;
}
else if (AnchorMinValue.X == 0.5f)
{
AnchorHAlign = LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center;
}
else if (AnchorMinValue.X == 1.0f)
{
AnchorHAlign = LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right;
}
}
else if (AnchorMinValue.X == 0.0f && AnchorMaxValue.X == 1.0f)
{
AnchorHAlign = LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch;
}
return AnchorHAlign;
}
LGUIAnchorPreviewWidget::UIAnchorVerticalAlign FUIItemCustomization::GetAnchorVAlign(TSharedRef<IPropertyHandle> AnchorMinHandle, TSharedRef<IPropertyHandle> AnchorMaxHandle)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::None;
FVector2D AnchorMinValue;
AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
AnchorMaxHandle->GetValue(AnchorMaxValue);
LGUIAnchorPreviewWidget::UIAnchorVerticalAlign AnchorVAlign = LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::None;
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
if (AnchorMinValue.Y == 0)
{
AnchorVAlign = LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom;
}
else if (AnchorMinValue.Y == 0.5f)
{
AnchorVAlign = LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle;
}
else if (AnchorMinValue.Y == 1.0f)
{
AnchorVAlign = LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top;
}
}
else if (AnchorMinValue.Y == 0.0f && AnchorMaxValue.Y == 1.0f)
{
AnchorVAlign = LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch;
}
return AnchorVAlign;
}
void FUIItemCustomization::OnSelectAnchor(LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign HorizontalAlign, LGUIAnchorPreviewWidget::UIAnchorVerticalAlign VerticalAlign, IDetailLayoutBuilder* DetailBuilder)
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return;
bool ShiftPressed = FSlateApplication::Get().GetModifierKeys().IsShiftDown();
bool AltPressed = FSlateApplication::Get().GetModifierKeys().IsAltDown();
GEditor->BeginTransaction(LOCTEXT("ChangeAnchor_Transaction", "Change LGUI Anchor"));
for (auto& UIItem : TargetScriptArray)
{
UIItem->Modify();
}
for (auto& UIItem : TargetScriptArray)
{
FVector2D DesiredPivot = UIItem->GetPivot();
auto AnchorMin = UIItem->GetAnchorMin();
auto AnchorMax = UIItem->GetAnchorMax();
switch (HorizontalAlign)
{
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::None:
break;
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left:
{
DesiredPivot.X = 0;
AnchorMin.X = AnchorMax.X = 0;
}
break;
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center:
{
DesiredPivot.X = 0.5f;
AnchorMin.X = AnchorMax.X = 0.5f;
}
break;
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right:
{
DesiredPivot.X = 1.0f;
AnchorMin.X = AnchorMax.X = 1.0f;
}
break;
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch:
{
DesiredPivot.X = 0.5f;
AnchorMin.X = 0;
AnchorMax.X = 1.0f;
}
break;
}
switch (VerticalAlign)
{
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::None:
break;
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top:
{
DesiredPivot.Y = 1.0f;
AnchorMin.Y = AnchorMax.Y = 1;
}
break;
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle:
{
DesiredPivot.Y = 0.5f;
AnchorMin.Y = AnchorMax.Y = 0.5f;
}
break;
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom:
{
DesiredPivot.Y = 0.0f;
AnchorMin.Y = AnchorMax.Y = 0.0f;
}
break;
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch:
{
DesiredPivot.Y = 0.5f;
AnchorMin.Y = 0;
AnchorMax.Y = 1.0f;
}
break;
}
auto PrevRelativeLocation = UIItem->GetRelativeLocation();
auto PrevWidth = UIItem->GetWidth();
auto PrevHeight = UIItem->GetHeight();
UIItem->SetAnchorMin(AnchorMin);
UIItem->SetAnchorMax(AnchorMax);
UIItem->MarkAllDirtyRecursive();
UIItem->SetWidth(PrevWidth);
UIItem->SetHeight(PrevHeight);
UIItem->SetRelativeLocation(PrevRelativeLocation);
if (AltPressed)
{
switch (HorizontalAlign)
{
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Left:
{
UIItem->SetHorizontalAnchoredPosition(-UIItem->GetLocalSpaceLeft());
}
break;
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Center:
{
UIItem->SetHorizontalAnchoredPosition(UIItem->GetWidth() * (UIItem->GetPivot().X - 0.5f));
}
break;
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Right:
{
UIItem->SetHorizontalAnchoredPosition(-UIItem->GetLocalSpaceRight());
}
break;
case LGUIAnchorPreviewWidget::UIAnchorHorizontalAlign::Stretch:
{
UIItem->SetAnchorLeft(0);
UIItem->SetAnchorRight(0);
}
break;
}
switch (VerticalAlign)
{
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Top:
{
UIItem->SetVerticalAnchoredPosition(-UIItem->GetLocalSpaceTop());
}
break;
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Middle:
{
UIItem->SetVerticalAnchoredPosition(UIItem->GetHeight() * (UIItem->GetPivot().Y - 0.5f));
}
break;
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Bottom:
{
UIItem->SetVerticalAnchoredPosition(-UIItem->GetLocalSpaceBottom());
}
break;
case LGUIAnchorPreviewWidget::UIAnchorVerticalAlign::Stretch:
{
UIItem->SetAnchorBottom(0);
UIItem->SetAnchorTop(0);
}
break;
}
}
if (ShiftPressed)
{
FMargin PrevAnchorAsMargin(UIItem->GetAnchorLeft(), UIItem->GetAnchorTop(), UIItem->GetAnchorRight(), UIItem->GetAnchorBottom());
UIItem->SetPivot(DesiredPivot);
UIItem->SetAnchorLeft(PrevAnchorAsMargin.Left);
UIItem->SetAnchorRight(PrevAnchorAsMargin.Right);
UIItem->SetAnchorBottom(PrevAnchorAsMargin.Bottom);
UIItem->SetAnchorTop(PrevAnchorAsMargin.Top);
}
LGUIUtils::NotifyPropertyChanged(UIItem.Get(), GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData));
}
TargetScriptArray[0]->EditorForceUpdate();
ForceRefreshEditor(DetailBuilder);
GEditor->EndTransaction();
}
bool FUIItemCustomization::IsAnchorValueEnable(TSharedRef<IPropertyHandle> AnchorHandle, int AnchorValueIndex)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return false;
auto AnchorMinHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchorMin));
auto AnchorMaxHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchorMax));
auto AnchoredPositionHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, AnchoredPosition));
auto SizeDeltaHandle = AnchorHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FUIAnchorData, SizeDelta));
FVector2D AnchorMinValue;
auto AnchorMinValueAccessResult = AnchorMinHandle->GetValue(AnchorMinValue);
FVector2D AnchorMaxValue;
auto AnchorMaxValueAccessResult = AnchorMaxHandle->GetValue(AnchorMaxValue);
FVector2D AnchoredPosition;
auto AnchoredPositionAccessResult = AnchoredPositionHandle->GetValue(AnchoredPosition);
FVector2D SizeDelta;
auto SizeDeltaAccessResult = SizeDeltaHandle->GetValue(SizeDelta);
switch (AnchorValueIndex)
{
default:
case 0://anchored position x, stretch left
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success
&& AnchoredPositionAccessResult == FPropertyAccess::Result::Success
)
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return !GetLayoutControlHorizontalAnchoredPosition();
}
else
{
return !GetLayoutControlHorizontalAnchoredPosition() && !GetLayoutControlHorizontalSizeDelta();
}
}
else
{
return true;
}
}
break;
case 1://anchored position y, stretch top
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success
&& AnchoredPositionAccessResult == FPropertyAccess::Result::Success
)
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return !GetLayoutControlVerticalAnchoredPosition();
}
else
{
return !GetLayoutControlVerticalAnchoredPosition() && !GetLayoutControlVerticalSizeDelta();
}
}
else
{
return true;
}
}
break;
case 2://width, stretch right
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success
&& SizeDeltaAccessResult == FPropertyAccess::Result::Success
)
{
if (AnchorMinValue.X == AnchorMaxValue.X)
{
return !GetLayoutControlHorizontalSizeDelta();
}
else
{
return !GetLayoutControlHorizontalAnchoredPosition() && !GetLayoutControlHorizontalSizeDelta();
}
}
else
{
return true;
}
}
break;
case 3://height, stretch bottom
{
if (AnchorMinValueAccessResult == FPropertyAccess::Result::Success && AnchorMaxValueAccessResult == FPropertyAccess::Result::Success
&& SizeDeltaAccessResult == FPropertyAccess::Result::Success
)
{
if (AnchorMinValue.Y == AnchorMaxValue.Y)
{
return !GetLayoutControlVerticalSizeDelta();
}
else
{
return !GetLayoutControlVerticalAnchoredPosition() && !GetLayoutControlVerticalSizeDelta();
}
}
else
{
return true;
}
}
break;
}
}
FReply FUIItemCustomization::OnClickFixDisplayNameButton(bool singleOrAll, TSharedRef<IPropertyHandle> DisplayNameHandle)
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return FReply::Handled();
TArray<TWeakObjectPtr<UUIItem>> UIItems;
if (singleOrAll)
{
UIItems = TargetScriptArray;
}
else
{
TArray<AActor*> SelectedActors;
for (auto& UIItem : TargetScriptArray)
{
if (!SelectedActors.Contains(UIItem->GetOwner()))
{
SelectedActors.Add(UIItem->GetOwner());
}
}
auto SelectedRootActors = LGUIEditorTools::GetRootActorListFromSelection(SelectedActors);
for (auto& RootActor : SelectedRootActors)
{
TArray<AActor*> ChildrenActors;
LGUIUtils::CollectChildrenActors(RootActor, ChildrenActors, true);
for (auto& Actor : ChildrenActors)
{
if (auto UIItem = Cast<UUIItem>(Actor->GetRootComponent()))
{
UIItems.Add(UIItem);
}
}
}
}
GEditor->BeginTransaction(LOCTEXT("FixDisplayName_Transaction", "Fix DisplayName"));
for (auto& UIItem : UIItems)
{
UIItem->Modify();
}
for (auto& UIItem : TargetScriptArray)
{
FString DisplayName;
if (auto actor = UIItem->GetOwner())
{
if (UIItem == actor->GetRootComponent())
{
auto actorLabel = UIItem->GetOwner()->GetActorLabel();
if (actorLabel.StartsWith("//"))
{
actorLabel = actorLabel.Right(actorLabel.Len() - 2);
}
DisplayName = actorLabel;
}
else
{
DisplayName = UIItem->GetName();
}
}
else
{
auto name = UIItem->GetName();
auto genVarSuffix = FString(TEXT("_GEN_VARIABLE"));
if (name.EndsWith(genVarSuffix))
{
name.RemoveAt(name.Len() - genVarSuffix.Len(), genVarSuffix.Len());
}
DisplayName = name;
}
DisplayNameHandle->SetValue(DisplayName);
LGUIUtils::NotifyPropertyChanged(UIItem.Get(), GET_MEMBER_NAME_CHECKED(UUIItem, displayName));
}
GEditor->EndTransaction();
return FReply::Handled();
}
void FUIItemCustomization::ForceRefreshEditor(IDetailLayoutBuilder* DetailBuilder)
{
if (DetailBuilder)
{
DetailBuilder->ForceRefreshDetails();
}
}
bool FUIItemCustomization::GetIsAnchorsEnabled()const
{
return !GetLayoutControlHorizontalAnchor() || !GetLayoutControlVerticalAnchor();
}
FText FUIItemCustomization::GetAnchorsTooltipText()const
{
return GetIsAnchorsEnabled() ? LOCTEXT("ChangeAnchor_Tooltip", "Change anchor") : LOCTEXT("AnchorIsControlledByLayout", "Anchor is controlled by layout");
}
bool FUIItemCustomization::OnCanCopyAnchor()const
{
return TargetScriptArray.Num() == 1;
}
#define BEGIN_LGUI_AnchorData_CLIPBOARD TEXT("Begin LGUI AnchorData")
bool FUIItemCustomization::OnCanPasteAnchor()const
{
FString PastedText;
FPlatformApplicationMisc::ClipboardPaste(PastedText);
return PastedText.StartsWith(BEGIN_LGUI_AnchorData_CLIPBOARD);
}
void FUIItemCustomization::OnCopyAnchor()
{
if (TargetScriptArray.Num() == 1)
{
auto script = TargetScriptArray[0];
if (script.IsValid())
{
auto AnchorData = script->GetAnchorData();
auto CopiedText = FString::Printf(TEXT("%s, PivotX=%f, PivotY=%f\
, AnchorMinX=%f, AnchorMinY=%f, AnchorMaxX=%f, AnchorMaxY=%f\
, AnchoredPositionX=%f, AnchoredPositionY=%f\
, SizeDeltaX=%f, SizeDeltaY=%f")
, BEGIN_LGUI_AnchorData_CLIPBOARD
, AnchorData.Pivot.X
, AnchorData.Pivot.Y
, AnchorData.AnchorMin.X
, AnchorData.AnchorMin.Y
, AnchorData.AnchorMax.X
, AnchorData.AnchorMax.Y
, AnchorData.AnchoredPosition.X
, AnchorData.AnchoredPosition.Y
, AnchorData.SizeDelta.X
, AnchorData.SizeDelta.Y
);
FPlatformApplicationMisc::ClipboardCopy(*CopiedText);
}
}
}
void FUIItemCustomization::OnPasteAnchor(IDetailLayoutBuilder* DetailBuilder)
{
FString PastedText;
FPlatformApplicationMisc::ClipboardPaste(PastedText);
if (PastedText.StartsWith(BEGIN_LGUI_AnchorData_CLIPBOARD))
{
FUIAnchorData AnchorData;
FParse::Value(*PastedText, TEXT("PivotX="), AnchorData.Pivot.X);
FParse::Value(*PastedText, TEXT("PivotY="), AnchorData.Pivot.Y);
FParse::Value(*PastedText, TEXT("AnchorMinX="), AnchorData.AnchorMin.X);
FParse::Value(*PastedText, TEXT("AnchorMinY="), AnchorData.AnchorMin.Y);
FParse::Value(*PastedText, TEXT("AnchorMaxX="), AnchorData.AnchorMax.X);
FParse::Value(*PastedText, TEXT("AnchorMaxY="), AnchorData.AnchorMax.Y);
FParse::Value(*PastedText, TEXT("AnchoredPositionX="), AnchorData.AnchoredPosition.X);
FParse::Value(*PastedText, TEXT("AnchoredPositionY="), AnchorData.AnchoredPosition.Y);
FParse::Value(*PastedText, TEXT("SizeDeltaX="), AnchorData.SizeDelta.X);
FParse::Value(*PastedText, TEXT("SizeDeltaY="), AnchorData.SizeDelta.Y);
for (auto item : TargetScriptArray)
{
if (item.IsValid())
{
auto itemWidget = item->GetAnchorData();
item->SetAnchorData(AnchorData);
LGUIUtils::NotifyPropertyChanged(item.Get(), GET_MEMBER_NAME_CHECKED(UUIItem, AnchorData));
item->MarkPackageDirty();
}
}
ForceUpdateUI();
ForceRefreshEditor(DetailBuilder);
}
}
void FUIItemCustomization::OnCopyHierarchyIndex()
{
if (TargetScriptArray.Num() > 0)
{
if (TargetScriptArray[0].IsValid())
{
FPlatformApplicationMisc::ClipboardCopy(*FString::Printf(TEXT("%d"), TargetScriptArray[0]->GetHierarchyIndex()));
}
}
}
void FUIItemCustomization::OnPasteHierarchyIndex(TSharedRef<IPropertyHandle> PropertyHandle)
{
FString PastedText;
FPlatformApplicationMisc::ClipboardPaste(PastedText);
if (PastedText.IsNumeric())
{
int value = FCString::Atoi(*PastedText);
PropertyHandle->SetValue(value);
}
}
FLGUICanLayoutControlAnchor FUIItemCustomization::GetLayoutControlAnchorValue()const
{
FLGUICanLayoutControlAnchor Result;
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return Result;
auto Actor = TargetScriptArray[0]->GetOwner();
auto World = TargetScriptArray[0]->GetWorld();
if (Actor && World)
{
if (auto Manager = ULGUIManagerWorldSubsystem::GetInstance(World))
{
auto& AllLayoutArray = Manager->GetAllLayoutArray();
if (AllLayoutArray.Num() > 0)
{
for (auto& Item : AllLayoutArray)
{
FLGUICanLayoutControlAnchor ItemResult;
if (ILGUILayoutInterface::Execute_GetCanLayoutControlAnchor(Item.Get(), TargetScriptArray[0].Get(), ItemResult))
{
Result.Or(ItemResult);
}
}
}
}
}
return Result;
}
bool FUIItemCustomization::IsAnchorControlledByMultipleLayout(TMap<EAnchorControlledByLayoutType, TArray<UObject*>>& Result)const
{
if (TargetScriptArray.Num() == 0 || !TargetScriptArray[0].IsValid())return false;
auto Actor = TargetScriptArray[0]->GetOwner();
auto World = TargetScriptArray[0]->GetWorld();
if (Actor && World)
{
if (auto Manager = ULGUIManagerWorldSubsystem::GetInstance(World))
{
auto AllLayoutArray = Manager->GetAllLayoutArray();
if (AllLayoutArray.Num() > 0)
{
for (auto& Item : AllLayoutArray)
{
FLGUICanLayoutControlAnchor ItemLayoutControl;
if (ILGUILayoutInterface::Execute_GetCanLayoutControlAnchor(Item.Get(), TargetScriptArray[0].Get(), ItemLayoutControl))
{
if (ItemLayoutControl.bCanControlHorizontalAnchor)
{
Result.FindOrAdd(EAnchorControlledByLayoutType::HorizontalAnchor).Add(Item.Get());
}
if (ItemLayoutControl.bCanControlHorizontalAnchoredPosition)
{
Result.FindOrAdd(EAnchorControlledByLayoutType::HorizontalAnchoredPosition).Add(Item.Get());
}
if (ItemLayoutControl.bCanControlHorizontalSizeDelta)
{
Result.FindOrAdd(EAnchorControlledByLayoutType::HorizontalSizeDelta).Add(Item.Get());
}
if (ItemLayoutControl.bCanControlVerticalAnchor)
{
Result.FindOrAdd(EAnchorControlledByLayoutType::VerticalAnchor).Add(Item.Get());
}
if (ItemLayoutControl.bCanControlVerticalAnchoredPosition)
{
Result.FindOrAdd(EAnchorControlledByLayoutType::VerticalAnchoredPosition).Add(Item.Get());
}
if (ItemLayoutControl.bCanControlVerticalSizeDelta)
{
Result.FindOrAdd(EAnchorControlledByLayoutType::VerticalSizeDelta).Add(Item.Get());
}
}
}
}
}
}
for (auto& KeyValue : Result)
{
if (KeyValue.Value.Num() > 1)return true;
}
return false;
}
bool FUIItemCustomization::GetLayoutControlHorizontalAnchor()const
{
return GetLayoutControlAnchorValue().bCanControlHorizontalAnchor;
}
bool FUIItemCustomization::GetLayoutControlVerticalAnchor()const
{
return GetLayoutControlAnchorValue().bCanControlVerticalAnchor;
}
bool FUIItemCustomization::GetLayoutControlHorizontalAnchoredPosition()const
{
return GetLayoutControlAnchorValue().bCanControlHorizontalAnchoredPosition;
}
bool FUIItemCustomization::GetLayoutControlVerticalAnchoredPosition()const
{
return GetLayoutControlAnchorValue().bCanControlVerticalAnchoredPosition;
}
bool FUIItemCustomization::GetLayoutControlHorizontalSizeDelta()const
{
return GetLayoutControlAnchorValue().bCanControlHorizontalSizeDelta;
}
bool FUIItemCustomization::GetLayoutControlVerticalSizeDelta()const
{
return GetLayoutControlAnchorValue().bCanControlVerticalSizeDelta;
}
UE_ENABLE_OPTIMIZATION
#undef LOCTEXT_NAMESPACE