[UE] Weapon Plugin 5: 창 내부 Header, Child 구분하기

목차
Plugins |
||||
Weapon |
||||
Resource |
||||
Icon128.png weapon_thumbnail_icon.png |
||||
Source |
||||
Weapon | ||||
SWeaponDetailsView.h .cpp 생성 SWeaponEquipmentData.h .cpp 생성 SWeaponLeftArea.h .cpp Weapon.Build.cs WeaponAssetEditor.h .cpp WeaponAssetFactory.h .cpp WeaponCommand.h .cpp WeaponContextMenu.h .cpp WeaponModule.h .cpp WeaponStyle.h .cpp |
||||
Weapon Plugin 5
WeaponAssetEditor
WeaponAssetEditor.h
더보기
#pragma once #include "CoreMinimal.h" #include "Toolkits/AssetEditorToolkit.h" class WEAPON_API FWeaponAssetEditor : public FAssetEditorToolkit { public: static void OpenWindow(FString InAssetName = ""); static void Shutdown(); private: static TSharedPtr<FWeaponAssetEditor> Instance; private: void Open(FString InAssetName); protected: bool OnRequestClose() override;//창이 닫힐 때 콜이 되게 설계. public: void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override; private: TSharedRef<SDockTab> Spawn_LeftAreaTab(const FSpawnTabArgs& InArgs); TSharedRef<SDockTab> Spawn_DetailsViewTab(const FSpawnTabArgs& InArgs); private: void OnListViewSelectedItem(FWeaponRowDataPtr InDataPtr);//LeftArea에서 선택한 FWeaponRowDataPtr public: FName GetToolkitFName() const override; FText GetBaseToolkitName() const override; FString GetWorldCentricTabPrefix() const override; FLinearColor GetWorldCentricTabColorScale() const override; private: TSharedPtr<class SWeaponLeftArea> LeftArea; TSharedPtr<class IDetailsView> DetailsView;//IDetailsView는 DetailView의 최상위 자료형. private: static const FName EditorName; static const FName LeftAreaTabId; static const FName DetailTabId; private: FReply OnClicked(); };
변동사항 없음.
WeaponAssetEditor.cpp
더보기
#include "WeaponAssetEditor.h" #include "SWeaponLeftArea.h" #include "SWeaponDetailsView.h" #include "SWeaponEquipmentData.h" #include "Weapons/CWeaponAsset.h" //설정한 이름들 const FName FWeaponAssetEditor::EditorName = "WeaponAssetEditor"; const FName FWeaponAssetEditor::LeftAreaTabId = "LeftArea"; const FName FWeaponAssetEditor::DetailTabId = "Details"; TSharedPtr<FWeaponAssetEditor> FWeaponAssetEditor::Instance = nullptr;//헤더에서 선언한 static 외부 초기화. void FWeaponAssetEditor::OpenWindow(FString InAssetName) { //아래의 코드 대신에 Shutdown()을 사용하면 안 된다! Shutdown()은 다른 곳에서도 콜이 되기 때문에 아래의 코드 대신에 사용하면 문제가 된다. if (Instance.IsValid()) //창이 만들어졌다면 { Instance->CloseWindow();//창을 닫는다. Instance.Reset(); Instance = nullptr; } Instance = MakeShareable(new FWeaponAssetEditor()); Instance->Open(InAssetName); } void FWeaponAssetEditor::Shutdown() { if (Instance.IsValid()) { Instance->CloseWindow(); Instance.Reset(); Instance = nullptr; } } void FWeaponAssetEditor::Open(FString InAssetName) { LeftArea = SNew(SWeaponLeftArea)//SWeaponLeftArea에서 받은 자료형을 생성하여 넣어준다. .OnSelectedItem(this, &FWeaponAssetEditor::OnListViewSelectedItem);//LeftArea에서 선택한 데이터 FPropertyEditorModule& prop = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor"); //DetailsView { FDetailsViewArgs args(false, false, true, FDetailsViewArgs::HideNameArea);//기본값 설정. ActorsUserNameArea, ObjectsUserNameArea, HideNameArea args.ViewIdentifier = "WeaponAssetEditorDetailsView";//식별자 설정. 게임 Editor쪽에서 DetailView 접근시 이 식별자로 찾을 수 있다. DetailsView = prop.CreateDetailView(args);//Detail 창 띄우기. FOnGetDetailCustomizationInstance detailView; detailView.BindStatic(&SWeaponDetailsView::MakeInstance);//Static은 객체가 필요없다. 그래서 함수 주소로 바로 연결한다. DetailsView->SetGenericLayoutDetailsDelegate(detailView);//Delegate를 연결해준다. } //EquipmentData { FOnGetPropertyTypeCustomizationInstance instance; instance.BindStatic(&SWeaponEquipmentData::MakeInstance); prop.RegisterCustomPropertyTypeLayout("EquipmentData", instance);//instance를 delegate 등록 } //Layout 설정 TSharedRef<FTabManager::FLayout> layout = FTabManager::NewLayout("WeaponAssetEditor_Layout") ->AddArea //전체화면의 메인 영역 ( FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.1f)//10%만 사용하겠다. ->AddTab(GetToolbarTabId(), ETabState::OpenedTab) ) ->Split ( FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.175f)//왼쪽 17.5% 사용 ->AddTab(LeftAreaTabId, ETabState::OpenedTab)//ListViewTabId ->SetHideTabWell(true) ) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.725f)//오른쪽 72.5% 사용 ->AddTab(DetailTabId, ETabState::OpenedTab)//DetailTabId ->SetHideTabWell(true) ) ) ); UCWeaponAsset* asset = nullptr; asset = LeftArea->GetFirstDataPtr()->Asset;//LeftArea의 맨 위 첫번째 데이터 선택 FAssetEditorToolkit::InitAssetEditor(EToolkitMode::Standalone, TSharedPtr<IToolkitHost>(), EditorName, layout, true, true, asset); //DetailsView->SetObject(asset);//어느 DetailView든 asset객체가 세팅된다. 창은 여러개지만 실제 관리는 내부적으로 하나로 관리한다. 그래서 창이 종료될 때 DetailsView가 해제 안 되어 있으면 터진다. LeftArea->SelectDataPtr(asset); } bool FWeaponAssetEditor::OnRequestClose() { if (!!DetailsView) { //AssetEditorSubsystem안에(=DetailView 안에) //GetEditingObject()가 등록되어 있었다면 해제하고 Editor에 알린다. if (!!GEditor && !!GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()) GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->NotifyAssetClosed(GetEditingObject(), this); //해당 모듈이 읽힌적이 있다면 해제. if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) { FPropertyEditorModule& prop = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");//해당모듈을 가져온다. prop.UnregisterCustomClassLayout("EquipmentData");//등록 해제 } } if (LeftArea.IsValid()) LeftArea.Reset(); if (DetailsView.IsValid()) DetailsView.Reset(); return true; //false 리턴인 경우 창이 닫힐 수 없다는 것을 의미한다. } void FWeaponAssetEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) { FAssetEditorToolkit::RegisterTabSpawners(InTabManager); FOnSpawnTab tab; tab.BindSP(this, &FWeaponAssetEditor::Spawn_LeftAreaTab); TabManager->RegisterTabSpawner(LeftAreaTabId, tab); FOnSpawnTab tab2; tab2.BindSP(this, &FWeaponAssetEditor::Spawn_DetailsViewTab); TabManager->RegisterTabSpawner(DetailTabId, tab2); } TSharedRef<SDockTab> FWeaponAssetEditor::Spawn_LeftAreaTab(const FSpawnTabArgs& InArgs) { //TSharedPtr<SDockTab> tab = SNew(SDockTab) //[ // SNew(SButton) // .OnClicked(this, &FWeaponAssetEditor::OnClicked)//OnClicked 함수 연결 // [ // SNew(STextBlock) // .Text(FText::FromString("Test")) // ] //]; //return tab.ToSharedRef(); return SNew(SDockTab) [ LeftArea.ToSharedRef() ]; } TSharedRef<SDockTab> FWeaponAssetEditor::Spawn_DetailsViewTab(const FSpawnTabArgs& InArgs) { return SNew(SDockTab) [ DetailsView.ToSharedRef() ]; } void FWeaponAssetEditor::OnListViewSelectedItem(FWeaponRowDataPtr InDataPtr) { if (InDataPtr == nullptr)//LeftArea에서 선택한게 없다면(또는 빈 공간을 선택했다면) return; if (!!GetEditingObject())//편집하는 객체가 있다면 RemoveEditingObject(GetEditingObject());//현재 창에서 편집중인 애들 제거한다. AddEditingObject(InDataPtr->Asset);//현재 창에 편집해줄 객체를 등록해준다. DetailsView->SetObject(InDataPtr->Asset);//창 안의 DetailsView도 변경해준다. } FName FWeaponAssetEditor::GetToolkitFName() const { return EditorName;//외부에서 어떤 에디터 이름을 쓸 지 정해준다. } FText FWeaponAssetEditor::GetBaseToolkitName() const { return FText::FromName(EditorName);//외부에서 어떤 에디터 이름을 쓸 지 정해준다. } FString FWeaponAssetEditor::GetWorldCentricTabPrefix() const { return EditorName.ToString();////외부에서 어떤 식별자를 쓸 지 정해준다. } FLinearColor FWeaponAssetEditor::GetWorldCentricTabColorScale() const { return FLinearColor(0, 0, 1);//파란색으로 설정. } FReply FWeaponAssetEditor::OnClicked() { GLog->Log("Test"); return FReply::Handled();//Handled()는 처리하고 끝낸다. }
void FWeaponAssetEditor::Open(FString InAssetName) 내의 수정사항
※ IDetailCustomization을 사용하는 DELEGATE
PropertyEditorDelegates.h 에 정의된 DECLARE_DELEGATE_RetVal

WeaponAssetEditor.cpp에 사용한 부분

SWeaponDetailsView 생성
새 C++ 클래스 - 없음 - SWeaponDetailsView 생성

SWeaponDetailsView.h
더보기
#pragma once #include "CoreMinimal.h" #include "IDetailCustomization.h" class WEAPON_API SWeaponDetailsView : public IDetailCustomization { public: static TSharedRef<IDetailCustomization> MakeInstance(); void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override; };
IDetailCustomization로 부터 상속받는다.
SWeaponDetailsView.cpp
더보기
#include "SWeaponDetailsView.h" #include "DetailLayoutBuilder.h" #include "DetailCategoryBuilder.h" #include "IDetailPropertyRow.h" #include "Weapons/CWeaponAsset.h" TSharedRef<IDetailCustomization> SWeaponDetailsView::MakeInstance() { //자신의 클래스 타입을 만들어서 return해준다. return MakeShareable(new SWeaponDetailsView()); } void SWeaponDetailsView::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { UClass* type = UCWeaponAsset::StaticClass();//UClass 타입 하나를 받아온다. DetailBuilder.HideCategory("CWeaponAsset");//CWeaponAsset 카테고리를 숨겨준다. 현재 작업의 편의를 위해 숨겨주고 추후에 보여주게 만들 예정이다. //Class Settings { //.EditCategory 해당 타입에 해당 카테고리가 있으면 그 카테고리를 return, 없으면 새로 만들어서 return //ClassSettings는 없으므로 새로 만들어서 return IDetailCategoryBuilder& category = DetailBuilder.EditCategory("ClassSettings", FText::FromString("Class Settings")); //CWeaponAsset에서 직렬화된 변수들을 카테고리에 추가한다. category.AddProperty("AttachmentClass", type);//CWeaponAsset의 AttachmentClass를 카테고리에 추가. category.AddProperty("EquipmentClass", type);//CWeaponAsset의 EquipmentClass를 카테고리에 추가. category.AddProperty("DoActionClass", type);//CWeaponAsset의 DoActionClass를 카테고리에 추가. } //EquipmentData { //.EditCategory 해당 타입에 해당 카테고리가 있으면 그 카테고리를 return, 없으면 새로 만들어서 return IDetailCategoryBuilder& category = DetailBuilder.EditCategory("EquipmentData", FText::FromString("Equipment Data")); IDetailPropertyRow& row = category.AddProperty("EquipmentData", type); } }
SWeaponEquipmentData 생성
새 C++ 클래스 - 없음 - SWeaponEquipmentData 생성

SWeaponEquipmentData.h
더보기
#pragma once #include "CoreMinimal.h" #include "IPropertyTypeCustomization.h" class WEAPON_API SWeaponEquipmentData : public IPropertyTypeCustomization { public: static TSharedRef<IPropertyTypeCustomization> MakeInstance(); void CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils) override; void CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils) override; };
IPropertyTypeCustomization로 부터 상속받는다.
SWeaponEquipmentData.cpp
더보기
#include "SWeaponEquipmentData.h" #include "IPropertyUtilities.h" #include "IDetailPropertyRow.h" #include "IDetailChildrenBuilder.h" #include "DetailWidgetRow.h" TSharedRef<IPropertyTypeCustomization> SWeaponEquipmentData::MakeInstance() { //자신의 클래스 타입을 만들어서 return해준다. return MakeShareable(new SWeaponEquipmentData); } void SWeaponEquipmentData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils) { InHeaderRow .NameContent() [ InPropertyHandle->CreatePropertyNameWidget() ] .ValueContent() .MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth")) .MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth")) [ SNew(STextBlock) .Text(FText::FromString("Value")) ]; } void SWeaponEquipmentData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils) { uint32 number = 0; InPropertyHandle->GetNumChildren(number); for(uint32 i = 0; i < number; i++) { TSharedPtr<IPropertyHandle> handle = InPropertyHandle->GetChildHandle(i); IDetailPropertyRow& row = InChildBuilder.AddProperty(handle.ToSharedRef());//handle를 가지고 기본모양을 추가하여 만들어준다. row.CustomWidget() .NameContent() [ handle->CreatePropertyNameWidget() ] //줄이거나 늘렸을 때 Min 이하로는 고정. Max 이상으로는 고정. .ValueContent() .MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth")) .MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth")) [ handle->CreatePropertyValueWidget() ]; } }
※ 참고 - Header, Children 위치

실행화면

'⭐ Unreal Engine > UE Plugin - Slate UI' 카테고리의 다른 글
[UE] Weapon Plugin 7: Check Box와 데이터 연결하기 (0) | 2023.06.12 |
---|---|
[UE] Weapon Plugin 6: 체크박스 만들기 (0) | 2023.06.08 |
[UE] Weapon Plugin 4: 창 내에 데이터 넣어주기, 검색 기능 넣기 (0) | 2023.06.01 |
[UE] Weapon Plugin 3: 창 내부 영역 구분하기(SWeaponLeftArea, WeaponAssetEditor) (0) | 2023.05.31 |
[UE] Weapon Plugin 2: 버튼 만들기, 창 띄우기 (0) | 2023.05.30 |
댓글
이 글 공유하기
다른 글
-
[UE] Weapon Plugin 7: Check Box와 데이터 연결하기
[UE] Weapon Plugin 7: Check Box와 데이터 연결하기
2023.06.12 -
[UE] Weapon Plugin 6: 체크박스 만들기
[UE] Weapon Plugin 6: 체크박스 만들기
2023.06.08 -
[UE] Weapon Plugin 4: 창 내에 데이터 넣어주기, 검색 기능 넣기
[UE] Weapon Plugin 4: 창 내에 데이터 넣어주기, 검색 기능 넣기
2023.06.01 -
[UE] Weapon Plugin 3: 창 내부 영역 구분하기(SWeaponLeftArea, WeaponAssetEditor)
[UE] Weapon Plugin 3: 창 내부 영역 구분하기(SWeaponLeftArea, WeaponAssetEditor)
2023.05.31
댓글을 사용할 수 없습니다.