Unreal 细节窗口 自定义属性 扩展按钮 修改属性 Details Window IDetailCustomization PropertyEditor

Unreal 细节窗口 自定义属性 扩展按钮 修改属性 Details Window IDetailCustomization PropertyEditor

需求

  • UI设计有一套规范 例如
    • 标题:粗体 32 大小
    • 内容:细体 28 大小
    • 按钮:粗体 30 大小
    • 金黄:#FFD700
    • 褐色:#D2B48C
  • 还原UI效果的时候经常需要设置这些数据
  • 为了提高效率和规范风格
  • 所以打算扩展文本组件细节窗口 点击按钮就可以设置 并且这些需要可配置化

实现

  • 使用插件扩展 不需要修改源码和组件

  • XXX.uplugin 插件需要设置 方便存放配置DataTabel

    • "CanContainContent": true,
      
  • 定义配置结构体

    • #pragma once
      
      #include "CoreMinimal.h"
      
      #include "CustomStyle.generated.h"
      
      USTRUCT(BlueprintType)
      struct FCustomColor: public FTableRowBase
      {
          GENERATED_BODY()
          UPROPERTY(EditAnywhere)
          FString Name;
          
          UPROPERTY(EditAnywhere)
          FSlateColor Color;
      };
      
      USTRUCT(BlueprintType)
      struct FCustomFont: public FTableRowBase
      {
          GENERATED_BODY()
          UPROPERTY(EditAnywhere)
          FString Name;
          
          UPROPERTY(EditAnywhere)
          FSlateFontInfo Font;
      };
      
  • 插件启动时注册扩展类型

    • void FMyAssetToolsModule::StartupModule()
      {
          FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
          PropertyModule.RegisterCustomClassLayout(UTextLayoutWidget::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FTextLayoutWidgetDetailCustomization::MakeInstance));
      }
      
      void FMyAssetToolsModule::ShutdownModule()
      {
          if (UObjectInitialized() && FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
          {
             FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
             PropertyModule.UnregisterCustomClassLayout(UTextLayoutWidget::StaticClass()->GetFName());
          }
      }
      
  • 具体的实现逻辑

    • FTextLayoutWidgetDetailCustomization.h

    • #pragma once
      
      #include "CustomStyle.h"
      #include "IDetailCustomization.h"
      
      
      /**
       * 
       */
      class FTextLayoutWidgetDetailCustomization : public IDetailCustomization
      {
      public:
          static TSharedRef<IDetailCustomization> MakeInstance();
      
          virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
      private:
      
          void AddSyncBrowserToObjectsButton(const TSharedPtr<SHorizontalBox>& HorizontalBox, const FAssetData& AssetData) const;
          
          bool IsCustomColorEnabled(const FCustomColor Value) const;
          FReply OnSetColorAndOpacity(const FCustomColor Value) const;
      
          bool IsCustomFontEnabled(const FCustomFont Value) const;
          FReply OnSetFont(const FCustomFont Value) const;
          static void OnSetFontPropertyHandle(const TSharedRef<IPropertyHandle>& PropertyHandle,const FCustomFont Value);
          
          IDetailLayoutBuilder* SavedLayoutBuilder;
      };
      
    • FTextLayoutWidgetDetailCustomization.cpp

    • #include "FTextLayoutWidgetDetailCustomization.h"
      #include "CustomStyle.h"
      #include "DetailCategoryBuilder.h"
      #include "DetailLayoutBuilder.h"
      #include "DetailWidgetRow.h"
      #include "Components/MultiLineEditableText.h"
      #include "Components/MultiLineEditableTextBox.h"
      #include "Components/RichTextBlock.h"
      #include "Components/TextBlock.h"
      
      
      TSharedRef<IDetailCustomization> FTextLayoutWidgetDetailCustomization::MakeInstance()
      {
          return MakeShareable(new FTextLayoutWidgetDetailCustomization());
      }
      
      void FTextLayoutWidgetDetailCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
      {
          SavedLayoutBuilder = &DetailBuilder;
      
      
          const TSharedPtr<SHorizontalBox> HorizontalBoxCustomColor = SNew(SHorizontalBox);
      
          const UDataTable* DataTableCustomColor = LoadObject<UDataTable>(nullptr,TEXT("/MyAssetTools/CustomColor.CustomColor"));
          DataTableCustomColor->ForeachRow<FCustomColor>("FCustomColor", [&](const FName& Key, const FCustomColor& Value)
          {
             HorizontalBoxCustomColor->AddSlot()
                                     .AutoWidth()
                                     .HAlign(HAlign_Fill)
             [
                SNew(SButton)
                .Text(FText::FromString(Value.Name))
                .IsEnabled(this, &FTextLayoutWidgetDetailCustomization::IsCustomColorEnabled, Value)
                .OnClicked(this, &FTextLayoutWidgetDetailCustomization::OnSetColorAndOpacity, Value)
             ];
          });
          AddSyncBrowserToObjectsButton(HorizontalBoxCustomColor, FAssetData(DataTableCustomColor));
      
          const TSharedPtr<SHorizontalBox> HorizontalBoxCustomFont = SNew(SHorizontalBox);
          const UDataTable* DataTableCustomFont = LoadObject<UDataTable>(nullptr,TEXT("/MyAssetTools/CustomFont.CustomFont"));
          DataTableCustomFont->ForeachRow<FCustomFont>("FCustomFont", [&](const FName& Key, const FCustomFont& Value)
          {
             HorizontalBoxCustomFont->AddSlot()
                                    .AutoWidth()
                                    .HAlign(HAlign_Fill)
             [
                SNew(SButton)
                .Text(FText::FromString(Value.Name))
                .IsEnabled(this, &FTextLayoutWidgetDetailCustomization::IsCustomFontEnabled, Value)
                .OnClicked(this, &FTextLayoutWidgetDetailCustomization::OnSetFont, Value)
             ];
          });
          AddSyncBrowserToObjectsButton(HorizontalBoxCustomFont, FAssetData(DataTableCustomFont));
      
          auto& DefaultCategory = DetailBuilder.EditCategory(TEXT("Appearance"));
          DefaultCategory.AddCustomRow(INVTEXT("CustomColor"))
                         .NameContent()
             [
                SNew(STextBlock)
                .Text(INVTEXT("Custom Color"))
                .Font(FCoreStyle::GetDefaultFontStyle("Regular", 8))
             ]
             .ValueContent()
             [
                HorizontalBoxCustomColor.ToSharedRef()
             ];
          DefaultCategory.AddCustomRow(INVTEXT("CustomFont"))
                         .NameContent()
             [
                SNew(STextBlock)
                .Text(INVTEXT("Custom Font"))
                .Font(FCoreStyle::GetDefaultFontStyle("Regular", 8))
             ]
             .ValueContent()
             [
                HorizontalBoxCustomFont.ToSharedRef()
             ];
      }
      
      void FTextLayoutWidgetDetailCustomization::AddSyncBrowserToObjectsButton(const TSharedPtr<SHorizontalBox>& HorizontalBox, const FAssetData& AssetData) const
      {
          HorizontalBox->AddSlot()
                       .AutoWidth()
                       .HAlign(HAlign_Fill)
                       .VAlign(VAlign_Center)
          [
             SNew(SButton)
             .ButtonStyle(FAppStyle::Get(), "SimpleButton")
             .OnClicked_Lambda([=, this]
             {
                TArray<FAssetData> Objects;
                Objects.Add(AssetData);
      
                GEditor->SyncBrowserToObjects(Objects);
                return FReply::Handled();
             })
             [
                SNew(SImage)
                .Image(FAppStyle::GetBrush("Icons.BrowseContent"))
                .ColorAndOpacity(FSlateColor::UseForeground())
             ]
          ];
      }
      
      bool FTextLayoutWidgetDetailCustomization::IsCustomColorEnabled(const FCustomColor Value) const
      {
          TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
          SavedLayoutBuilder->GetObjectsBeingCustomized(ObjectsBeingCustomized);
          for (const TWeakObjectPtr<UObject>& Object : ObjectsBeingCustomized)
          {
             if (const UTextBlock* CustomObject = Cast<UTextBlock>(Object.Get()))
             {
                if (CustomObject->GetColorAndOpacity() != Value.Color)
                {
                   return true;
                }
             }
             if (const URichTextBlock* CustomObject = Cast<URichTextBlock>(Object.Get()))
             {
                if (CustomObject->GetDefaultTextStyle().ColorAndOpacity != Value.Color)
                {
                   return true;
                }
             }
             if (const UMultiLineEditableText* CustomObject = Cast<UMultiLineEditableText>(Object.Get()))
             {
                if (CustomObject->WidgetStyle.ColorAndOpacity != Value.Color)
                {
                   return true;
                }
             }
             if (const UMultiLineEditableTextBox* CustomObject = Cast<UMultiLineEditableTextBox>(Object.Get()))
             {
                if (CustomObject->WidgetStyle.TextStyle.ColorAndOpacity != Value.Color)
                {
                   return true;
                }
             }
          }
          return false;
      }
      
      FReply FTextLayoutWidgetDetailCustomization::OnSetColorAndOpacity(const FCustomColor Value) const
      {
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("ColorAndOpacity", UTextBlock::StaticClass()); PropertyHandle->IsValidHandle())
          {
             PropertyHandle->SetValueFromFormattedString(Value.Color.GetSpecifiedColor().ToString());
          }
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("bOverrideDefaultStyle", URichTextBlock::StaticClass()); PropertyHandle->IsValidHandle())
          {
             PropertyHandle->SetValue(true);
          }
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("DefaultTextStyleOverride.ColorAndOpacity", URichTextBlock::StaticClass()); PropertyHandle->IsValidHandle())
          {
             PropertyHandle->SetValueFromFormattedString(Value.Color.GetSpecifiedColor().ToString());
          }
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("WidgetStyle.ColorAndOpacity", UMultiLineEditableText::StaticClass()); PropertyHandle->IsValidHandle())
          {
             PropertyHandle->SetValueFromFormattedString(Value.Color.GetSpecifiedColor().ToString());
          }
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("WidgetStyle.TextStyle.ColorAndOpacity", UMultiLineEditableTextBox::StaticClass()); PropertyHandle->IsValidHandle())
          {
             PropertyHandle->SetValueFromFormattedString(Value.Color.GetSpecifiedColor().ToString());
          }
          return FReply::Handled();
      }
      
      bool FTextLayoutWidgetDetailCustomization::IsCustomFontEnabled(const FCustomFont Value) const
      {
          TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
          SavedLayoutBuilder->GetObjectsBeingCustomized(ObjectsBeingCustomized);
          for (const TWeakObjectPtr<UObject>& Object : ObjectsBeingCustomized)
          {
             if (const UTextBlock* CustomObject = Cast<UTextBlock>(Object.Get()))
             {
                if (CustomObject->GetFont().FontObject != Value.Font.FontObject || CustomObject->GetFont().Size != Value.Font.Size)
                {
                   return true;
                }
             }
             if (const URichTextBlock* CustomObject = Cast<URichTextBlock>(Object.Get()))
             {
                if (CustomObject->GetDefaultTextStyle().Font.FontObject != Value.Font.FontObject || CustomObject->GetDefaultTextStyle().Font.Size != Value.Font.Size)
                {
                   return true;
                }
             }
             if (const UMultiLineEditableText* CustomObject = Cast<UMultiLineEditableText>(Object.Get()))
             {
                if (CustomObject->WidgetStyle.Font.FontObject != Value.Font.FontObject || CustomObject->WidgetStyle.Font.Size != Value.Font.Size)
                {
                   return true;
                }
             }
             if (const UMultiLineEditableTextBox* CustomObject = Cast<UMultiLineEditableTextBox>(Object.Get()))
             {
                if (CustomObject->WidgetStyle.TextStyle.Font != Value.Font || CustomObject->WidgetStyle.TextStyle.Font.Size != Value.Font.Size)
                {
                   return true;
                }
             }
          }
          return false;
      }
      
      FReply FTextLayoutWidgetDetailCustomization::OnSetFont(const FCustomFont Value) const
      {
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("Font", UTextBlock::StaticClass()); PropertyHandle->IsValidHandle())
          {
             OnSetFontPropertyHandle(PropertyHandle, Value);
          }
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("DefaultTextStyleOverride.Font", URichTextBlock::StaticClass()); PropertyHandle->IsValidHandle())
          {
             OnSetFontPropertyHandle(PropertyHandle, Value);
          }
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("WidgetStyle.Font", UMultiLineEditableText::StaticClass()); PropertyHandle->IsValidHandle())
          {
             OnSetFontPropertyHandle(PropertyHandle, Value);
          }
          if (const TSharedRef<IPropertyHandle> PropertyHandle = SavedLayoutBuilder->GetProperty("WidgetStyle.TextStyle.Font", UMultiLineEditableTextBox::StaticClass()); PropertyHandle->IsValidHandle())
          {
             OnSetFontPropertyHandle(PropertyHandle, Value);
          }
          return FReply::Handled();
      }
      
      void FTextLayoutWidgetDetailCustomization::OnSetFontPropertyHandle(const TSharedRef<IPropertyHandle>& PropertyHandle, const FCustomFont Value)
      {
          if (const TSharedPtr<IPropertyHandle> ChildPropertyHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSlateFontInfo, FontObject)))
          {
             ChildPropertyHandle->SetValue(Value.Font.FontObject);
          }
          if (const TSharedPtr<IPropertyHandle> ChildPropertyHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSlateFontInfo, Size)))
          {
             ChildPropertyHandle->SetValue(Value.Font.Size);
          }
      }
      
posted @ 2025-10-30 20:18  邹强  阅读(11)  评论(0)    收藏  举报