Created
April 8, 2023 16:18
-
-
Save moppius/06a68eed0c4f96fd49b322495d35f5d1 to your computer and use it in GitHub Desktop.
AssetStatus Angelscript Prototype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Settings for the Asset Status */ | |
UCLASS(Config = Editor, DefaultConfig) | |
class UAssetStatusSettings : UDeveloperSettings | |
{ | |
UPROPERTY(Config, EditDefaultsOnly, Category = "Asset Status", Meta = (TitleProperty = "Name")) | |
protected TArray<FAssetStatusColumnSchema> Schemas; | |
TArray<FAssetStatusColumnSchema> GetSchemas() const | |
{ | |
return Schemas; | |
} | |
}; | |
struct FAssetStatusColumnSchema | |
{ | |
UPROPERTY(Config) | |
FString Name; | |
UPROPERTY(Config, Meta = (TitleProperty = "CollectionName")) | |
TArray<FAssetStatusColumn> Columns; | |
}; | |
struct FAssetStatusColumn | |
{ | |
UPROPERTY(Config) | |
FName CollectionName; | |
UPROPERTY(Config) | |
FText ColumnTitle; | |
UPROPERTY(Config) | |
FLinearColor Color = FLinearColor(0.017642f, 0.017642f, 0.017642f, 0.f); | |
}; | |
/** Base class for the Asset Status Editor Utility Widget */ | |
UCLASS(Abstract) | |
class UAssetStatusEditorUtilityWidget : UEditorUtilityWidget | |
{ | |
UPROPERTY(BindWidget) | |
private UComboBoxString SchemaComboBox; | |
UPROPERTY(BindWidget) | |
private UButton SettingsButton; | |
UPROPERTY(BindWidget) | |
private UScrollBox ScrollBox; | |
UPROPERTY(BindWidget) | |
private UHorizontalBox ColumnContainer; | |
UPROPERTY(EditDefaultsOnly, Category = "Asset Status") | |
protected TSubclassOf<UAssetStatusColumnWidget> ColumnWidgetClass; | |
private TArray<UAssetStatusColumnWidget> ColumnWidgets; | |
private float LastWidth = 0.f; | |
UFUNCTION(BlueprintOverride) | |
void PreConstruct(bool bIsDesignTime) | |
{ | |
const auto Settings = Cast<UAssetStatusSettings>( | |
UAssetStatusSettings::StaticClass().GetDefaultObject() | |
); | |
const auto Schemas = Settings.GetSchemas(); | |
if (Schemas.Num() == 0) | |
{ | |
Warning("NO SCHEMAS! Add some in the settings"); | |
SchemaComboBox.SetVisibility(ESlateVisibility::Collapsed); | |
return; | |
} | |
SchemaComboBox.ClearOptions(); | |
for (const auto& Schema : Schemas) | |
{ | |
SchemaComboBox.AddOption(Schema.Name); | |
} | |
SchemaComboBox.SetSelectedIndex(0); | |
SetSelectedSchema(Schemas[0].Name, bIsDesignTime); | |
} | |
UFUNCTION(BlueprintOverride) | |
void Construct() | |
{ | |
SchemaComboBox.OnSelectionChanged.AddUFunction(this, n"SchemaChanged"); | |
SettingsButton.OnClicked.AddUFunction(this, n"OpenSettings"); | |
} | |
UFUNCTION(BlueprintOverride) | |
void Tick(FGeometry MyGeometry, float InDeltaTime) | |
{ | |
const int NumColumns = ColumnWidgets.Num(); | |
if (NumColumns > 0) | |
{ | |
const float CurrentWidth = MyGeometry.GetAbsoluteSize().X - 8.f; | |
if (!Math::IsNearlyEqual(CurrentWidth, LastWidth)) | |
{ | |
const float ColumnWidth = CurrentWidth / NumColumns; | |
for (auto ColumnWidget : ColumnWidgets) | |
{ | |
ColumnWidget.SetWidth(ColumnWidth); | |
} | |
LastWidth = CurrentWidth; | |
} | |
} | |
} | |
UFUNCTION() | |
private void SchemaChanged(FString SelectedItem, ESelectInfo SelectionType) | |
{ | |
SetSelectedSchema(SelectedItem); | |
} | |
private void SetSelectedSchema(FString SchemaName, bool bIsDesignTime = false) | |
{ | |
LastWidth = 0.f; | |
ColumnWidgets.Empty(); | |
ColumnContainer.ClearChildren(); | |
const auto Settings = Cast<UAssetStatusSettings>( | |
UAssetStatusSettings::StaticClass().GetDefaultObject() | |
); | |
const auto Schemas = Settings.GetSchemas(); | |
for (const auto& Schema : Schemas) | |
{ | |
if (Schema.Name == SchemaName) | |
{ | |
for (const auto& Column : Schema.Columns) | |
{ | |
auto ColumnWidget = Cast<UAssetStatusColumnWidget>( | |
NewObject(this, ColumnWidgetClass.Get(), bTransient = true) | |
); | |
auto ColumnSlot = ColumnContainer.AddChildToHorizontalBox(ColumnWidget); | |
FSlateChildSize ColumnSize; | |
ColumnSize.SizeRule = ESlateSizeRule::Fill; | |
ColumnSize.Value = 1.f; | |
ColumnSlot.Size = ColumnSize; | |
ColumnWidget.Setup(Column, bIsDesignTime); | |
ColumnWidgets.Add(ColumnWidget); | |
if (!bIsDesignTime) | |
{ | |
ColumnWidget.OnAssetDropped.AddUFunction(this, n"AssetChangedColumn"); | |
} | |
} | |
return; | |
} | |
} | |
Error(f"Failed to find Schema named '{SchemaName}'"); | |
} | |
UFUNCTION() | |
private void OpenSettings() | |
{ | |
Editor::OpenSettings(n"Project", n"Editor", n"AssetStatusSettings"); | |
} | |
UFUNCTION() | |
private void AssetChangedColumn(FName FromCollection, FName ToCollection, const FAssetData& AssetData) | |
{ | |
auto TagsSubsystem = UAssetTagsSubsystem::Get(); | |
const bool bToCollectionExists = TagsSubsystem.GetCollections().Contains(ToCollection); | |
if (!bToCollectionExists) | |
{ | |
const auto CreateCollectionType = ECollectionScriptingShareType::Local; | |
TagsSubsystem.CreateCollection(ToCollection, CreateCollectionType); | |
} | |
const bool bChangedColumn = FromCollection != ToCollection; | |
for (auto ColumnWidget : ColumnWidgets) | |
{ | |
const FName ColumnCollectionName = ColumnWidget.GetCollectionName(); | |
if (bChangedColumn) | |
{ | |
if (ColumnCollectionName == FromCollection) | |
{ | |
TagsSubsystem.RemoveAssetDataFromCollection(FromCollection, AssetData); | |
ColumnWidget.UpdateAssetList(); | |
} | |
else if (ColumnCollectionName == ToCollection) | |
{ | |
TagsSubsystem.AddAssetDataToCollection(ToCollection, AssetData); | |
ColumnWidget.UpdateAssetList(); | |
} | |
} | |
else | |
{ | |
if (ColumnCollectionName == FromCollection) | |
{ | |
ColumnWidget.UpdateAssetList(); | |
break; | |
} | |
} | |
} | |
} | |
}; | |
namespace AssetStatusEditor | |
{ | |
UAssetStatusDragDropPayload GetPayloadFromOperation(UDragDropOperation Operation) | |
{ | |
if (Operation.Payload.IsA(UAssetStatusDragDropPayload::StaticClass())) | |
{ | |
return Cast<UAssetStatusDragDropPayload>(Operation.Payload); | |
} | |
return nullptr; | |
} | |
} | |
event void FAssetStatusOnAssetDroppedSignature(FName FromCollection, FName ToCollection, const FAssetData& AssetData); | |
UCLASS(Abstract) | |
class UAssetStatusColumnWidget : UUserWidget | |
{ | |
UPROPERTY(EditDefaultsOnly, Category = "Asset Status") | |
protected FLinearColor ColumnColor = FLinearColor(0.05f, 0.05f, 0.05f, 0.5f); | |
UPROPERTY(Category = "Asset Status") | |
FAssetStatusOnAssetDroppedSignature OnAssetDropped; | |
UPROPERTY(BindWidget) | |
private USizeBox SizeBox; | |
UPROPERTY(BindWidget) | |
private UBorder ColumnBorder; | |
UPROPERTY(BindWidget) | |
private UTextBlock HeaderTextBlock; | |
UPROPERTY(BindWidget) | |
private UListView AssetList; | |
UPROPERTY(BindWidget) | |
private UAssetStatusRemoveDropAreaWidget RemoveDropArea; | |
private FName CollectionName = NAME_None; | |
UFUNCTION(BlueprintOverride) | |
void PreConstruct(bool bIsDesignTime) | |
{ | |
ColumnBorder.SetBrushColor(ColumnColor); | |
} | |
UFUNCTION(BlueprintOverride) | |
void Construct() | |
{ | |
RemoveDropArea.OnAssetRemoved.AddUFunction(this, n"AssetRemoved"); | |
} | |
void Setup(const FAssetStatusColumn& InColumn, bool bIsDesignTime) | |
{ | |
CollectionName = InColumn.CollectionName; | |
FText HeaderText = InColumn.ColumnTitle; | |
if (HeaderText.IsEmptyOrWhitespace()) | |
{ | |
HeaderText = FText::AsCultureInvariant(CollectionName.ToString().ToDisplayName()); | |
} | |
HeaderTextBlock.SetText(HeaderText); | |
ColumnColor = InColumn.Color; | |
ColumnBorder.SetBrushColor(ColumnColor); | |
AssetList.BP_OnItemDoubleClicked.AddUFunction(this, n"ItemDoubleClicked"); | |
if (!bIsDesignTime) | |
{ | |
UpdateAssetList(); | |
} | |
} | |
FName GetCollectionName() const | |
{ | |
return CollectionName; | |
} | |
void SetWidth(float InWidth) | |
{ | |
const float Width = Math::Max(SizeBox.GetMinDesiredWidth(), InWidth); | |
SizeBox.SetWidthOverride(Width); | |
} | |
UFUNCTION() | |
private void ItemDoubleClicked(UObject Item) | |
{ | |
Cast<UAssetStatusListEntryObject>(Item).OpenInContentBrowser(); | |
} | |
UFUNCTION() | |
private void AssetRemoved(FName FromCollection, const FAssetData& AssetData) | |
{ | |
if (FromCollection == CollectionName) | |
{ | |
UAssetTagsSubsystem::Get().RemoveAssetDataFromCollection(CollectionName, AssetData); | |
UpdateAssetList(); | |
} | |
} | |
UFUNCTION(BlueprintOverride) | |
bool OnDrop(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation Operation) | |
{ | |
const auto Payload = AssetStatusEditor::GetPayloadFromOperation(Operation); | |
if (Payload != nullptr) | |
{ | |
OnAssetDropped.Broadcast(Payload.FromCollectionName, CollectionName, Payload.AssetData); | |
ColumnBorder.SetBrushColor(ColumnColor); | |
SetRemoveDropAreaVisibility(false); | |
return true; | |
} | |
return false; | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnDragEnter(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation Operation) | |
{ | |
const auto Payload = AssetStatusEditor::GetPayloadFromOperation(Operation); | |
if (Payload != nullptr) | |
{ | |
if (Payload.FromCollectionName != CollectionName) | |
{ | |
const float LerpAlpha = 0.25f; | |
const FLinearColor HoverColor( | |
Math::Lerp(ColumnColor.R, 1.f, LerpAlpha), | |
Math::Lerp(ColumnColor.G, 1.f, LerpAlpha), | |
Math::Lerp(ColumnColor.B, 1.f, LerpAlpha), | |
Math::Lerp(ColumnColor.A, 1.f, LerpAlpha), | |
); | |
ColumnBorder.SetBrushColor(HoverColor); | |
} | |
else | |
{ | |
SetRemoveDropAreaVisibility(true); | |
} | |
} | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnDragLeave(FPointerEvent PointerEvent, UDragDropOperation Operation) | |
{ | |
const auto Payload = AssetStatusEditor::GetPayloadFromOperation(Operation); | |
if (Payload != nullptr && Payload.FromCollectionName != CollectionName) | |
{ | |
ColumnBorder.SetBrushColor(ColumnColor); | |
} | |
} | |
UFUNCTION() | |
void UpdateAssetList() | |
{ | |
if (AssetRegistry::IsLoadingAssets()) | |
{ | |
System::SetTimer(this, n"UpdateAssetList", 0.1f, false); | |
return; | |
} | |
auto TagsSubsystem = UAssetTagsSubsystem::Get(); | |
const auto Assets = TagsSubsystem.GetAssetsInCollection(CollectionName); | |
AssetList.ClearListItems(); | |
for (const auto& AssetData : Assets) | |
{ | |
auto ListObject = UAssetStatusListEntryObject::New(this, CollectionName, AssetData); | |
AssetList.AddItem(ListObject); | |
} | |
SetRemoveDropAreaVisibility(false); | |
} | |
private void SetRemoveDropAreaVisibility(bool bShowRemoveDropArea) | |
{ | |
const auto Vis = bShowRemoveDropArea ? ESlateVisibility::Visible : ESlateVisibility::Collapsed; | |
RemoveDropArea.SetVisibility(Vis); | |
} | |
}; | |
event void FAssetStatusOnAssetRemovedSignature(FName FromCollection, const FAssetData& AssetData); | |
UCLASS(Abstract) | |
class UAssetStatusRemoveDropAreaWidget : UUserWidget | |
{ | |
UPROPERTY(Category = "Asset Status") | |
FAssetStatusOnAssetRemovedSignature OnAssetRemoved; | |
UPROPERTY(BindWidget) | |
protected FLinearColor DefaultBackgroundColor = FLinearColor(0.1f, 0.0f, 0.0f); | |
UPROPERTY(BindWidget) | |
private UBorder BackgroundBorder; | |
UFUNCTION(BlueprintOverride) | |
void PreConstruct(bool bIsDesignTime) | |
{ | |
BackgroundBorder.SetBrushColor(DefaultBackgroundColor); | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnDragEnter(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation Operation) | |
{ | |
const FLinearColor HoverColor = FLinearColor::LerpUsingHSV(DefaultBackgroundColor, FLinearColor::White, 0.1f); | |
BackgroundBorder.SetBrushColor(HoverColor); | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnDragLeave(FPointerEvent PointerEvent, UDragDropOperation Operation) | |
{ | |
BackgroundBorder.SetBrushColor(DefaultBackgroundColor); | |
} | |
UFUNCTION(BlueprintOverride) | |
bool OnDrop(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation Operation) | |
{ | |
BackgroundBorder.SetBrushColor(DefaultBackgroundColor); | |
const auto Payload = AssetStatusEditor::GetPayloadFromOperation(Operation); | |
if (Payload != nullptr) | |
{ | |
OnAssetRemoved.Broadcast(Payload.FromCollectionName, Payload.AssetData); | |
return true; | |
} | |
return false; | |
} | |
}; | |
namespace UAssetStatusListEntryObject | |
{ | |
UAssetStatusListEntryObject New(UObject Outer, FName CollectionName, const FAssetData& AssetData) | |
{ | |
auto AssetObject = Cast<UAssetStatusListEntryObject>( | |
NewObject(Outer, UAssetStatusListEntryObject::StaticClass(), bTransient = true) | |
); | |
AssetObject.CollectionName = CollectionName; | |
AssetObject.AssetData = AssetData; | |
return AssetObject; | |
} | |
}; | |
class UAssetStatusDragDropPayload : UObject | |
{ | |
FName FromCollectionName; | |
FAssetData AssetData; | |
}; | |
class UAssetStatusListEntryObject : UObject | |
{ | |
FName CollectionName; | |
FAssetData AssetData; | |
void OpenInContentBrowser() const | |
{ | |
TArray<FString> AssetPaths; | |
AssetPaths.Add(AssetData.PackageName.ToString()); | |
EditorAsset::SyncBrowserToObjects(AssetPaths); | |
} | |
}; | |
UCLASS(Abstract) | |
class UAssetStatusListEntryWidget : UUserWidget | |
{ | |
UPROPERTY(BindWidget) | |
private UBorder BackgroundBorder; | |
UPROPERTY(BindWidget) | |
private UTextBlock AssetNameTextBlock; | |
UPROPERTY(EditDefaultsOnly, Category = "Asset Status") | |
protected const FLinearColor BackgroundDragColor = FLinearColor(0.f, 0.162029f, 0.745404f); | |
private FName CollectionName; | |
private FAssetData AssetData; | |
private bool bIsDragging = false; | |
UFUNCTION(BlueprintOverride) | |
void PreConstruct(bool bIsDesignTime) | |
{ | |
UpdateBackgroundColor(); | |
} | |
UFUNCTION(Category = "Asset Status") | |
protected void SetObject(UAssetStatusListEntryObject InObject) | |
{ | |
CollectionName = InObject.CollectionName; | |
SetAssetData(InObject.AssetData); | |
} | |
void SetAssetData(const FAssetData& InAssetData) | |
{ | |
AssetData = InAssetData; | |
UpdateText(); | |
} | |
void SetIsDragging(bool bInIsDragging) | |
{ | |
bIsDragging = bInIsDragging; | |
} | |
UFUNCTION(BlueprintOverride) | |
void Construct() | |
{ | |
UpdateText(); | |
UpdateBackgroundColor(); | |
} | |
private void UpdateText() | |
{ | |
if (AssetNameTextBlock != nullptr) | |
{ | |
AssetNameTextBlock.SetText(FText::AsCultureInvariant(AssetData.AssetName.ToString())); | |
SetToolTipText(FText::AsCultureInvariant(AssetData.PackageName.ToString())); | |
} | |
RenderOpacity = 1.f; | |
} | |
UFUNCTION(BlueprintOverride) | |
FEventReply OnMouseButtonDown(FGeometry MyGeometry, FPointerEvent MouseEvent) | |
{ | |
return Widget::DetectDragIfPressed(MouseEvent, this, EKeys::LeftMouseButton); | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnDragDetected(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation& Operation) | |
{ | |
Operation = Cast<UDragDropOperation>( | |
NewObject(this, UDragDropOperation::StaticClass(), bTransient = true) | |
); | |
auto DragWidget = Cast<UAssetStatusListEntryWidget>(NewObject(this, Class, bTransient = true)); | |
DragWidget.SetAssetData(AssetData); | |
DragWidget.SetIsDragging(true); | |
Operation.DefaultDragVisual = DragWidget; | |
auto Payload = Cast<UAssetStatusDragDropPayload>( | |
NewObject(this, UAssetStatusDragDropPayload::StaticClass(), bTransient = true) | |
); | |
Payload.FromCollectionName = CollectionName; | |
Payload.AssetData = AssetData; | |
Operation.Payload = Payload; | |
RenderOpacity = 0.25f; | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnDragCancelled(FPointerEvent PointerEvent, UDragDropOperation Operation) | |
{ | |
RenderOpacity = 1.f; | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnMouseEnter(FGeometry MyGeometry, FPointerEvent MouseEvent) | |
{ | |
UpdateBackgroundColor(); | |
} | |
UFUNCTION(BlueprintOverride) | |
void OnMouseLeave(FPointerEvent MouseEvent) | |
{ | |
UpdateBackgroundColor(); | |
} | |
private void UpdateBackgroundColor() | |
{ | |
if (bIsDragging) | |
{ | |
BackgroundBorder.SetBrushColor(BackgroundDragColor); | |
return; | |
} | |
const FLinearColor Color(1.f, 1.f, 1.f, IsHovered() ? 0.15f : 0.f); | |
BackgroundBorder.SetBrushColor(Color); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment