Skip to content

Commit

Permalink
RedEditorIconWidget and Customization
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan-DowlingSoka committed Jun 1, 2022
1 parent 56d0688 commit 9087034
Show file tree
Hide file tree
Showing 4 changed files with 352 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// MIT License
//
// Copyright (c) 2022 Ryan DowlingSoka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "Customization/RedEditorIconPathCustomization.h"
#include "DetailWidgetRow.h"
#include "RedEditorIconWidget.h"
#include "RedDeveloperSettings.h"
#include "SSearchableComboBox.h"
#include "Brushes/SlateImageBrush.h"

TSharedRef<IPropertyTypeCustomization> FRedEditorIconPathCustomization::MakeInstance()
{
// Create the instance and returned a SharedRef
return MakeShareable(new FRedEditorIconPathCustomization());
}

void FRedEditorIconPathCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle,
class FDetailWidgetRow& HeaderRow,
IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
void* StructData = nullptr;
const FPropertyAccess::Result Result = StructPropertyHandle.Get().GetValueData(StructData);
if (Result == FPropertyAccess::Success)
{
check(StructData);
FRedEditorIconPath* CurrentValue = static_cast<FRedEditorIconPath*>(StructData);
const FString CurrentValuePath = CurrentValue->Path;
const FString ItemName = FPaths::GetCleanFilename(*CurrentValuePath);
const auto CurrentSelectionText = SNew(STextBlock)
.Text_Lambda([=]() { return FText::FromString(FPaths::GetCleanFilename(CurrentValue->Path)); });
HeaderRow.NameContent()[StructPropertyHandle->CreatePropertyNameWidget()]
.ValueContent()
[
SNew(SSearchableComboBox)
.OptionsSource(GetIconOptionsPointer())
.OnGenerateWidget(this, &FRedEditorIconPathCustomization::HandleGenerateWidget)
.OnSelectionChanged_Lambda([=](TSharedPtr<FString> NewChoice, ESelectInfo::Type SelectType)
{
if (NewChoice.IsValid())
{
StructPropertyHandle.Get().NotifyPreChange();
CurrentValue->Path = *NewChoice;
const FString ChoiceItemName = FPaths::GetCleanFilename(*NewChoice);
StructPropertyHandle.Get().NotifyPostChange(
EPropertyChangeType::ValueSet);
}
})
.ToolTip(SNew(SToolTip).Text_Lambda([=]()
{
return FText::FromString(
FPaths::ConvertRelativePathToFull(CurrentValue->Path));
}))
.Content()
[
CurrentSelectionText
]
];
}
}

TSharedRef<SWidget> FRedEditorIconPathCustomization::HandleGenerateWidget(TSharedPtr<FString> InItem)
{
const int EngineIndex = InItem->Find(TEXT("/Engine/"));
const int EditorIndex = InItem->Find(TEXT("/Editor/"));
int RightChopIndex = 0;
if (EngineIndex >= 0 || EditorIndex >= 0)
{
RightChopIndex = FMath::Max(EngineIndex + 7, EditorIndex + 7);
}

static FVector2D IconSize = FVector2D(24, 24);
const FString AbsolutePath = FPaths::ConvertRelativePathToFull(*InItem);
const FString ItemName = FPaths::GetCleanFilename(*InItem);
const FString Category = InItem->RightChop(RightChopIndex).LeftChop(ItemName.Len() + 1).
Replace(TEXT("/Content/"),TEXT("/")).
Replace(TEXT("/Slate/"),TEXT("/")).
Replace(TEXT("/Plugins/"),TEXT("/")).
Replace(TEXT("/Experimental/"),TEXT("/")).RightChop(1);

if (!InItem->IsEmpty() && IFileManager::Get().FileExists(*AbsolutePath))
{
TUniquePtr<FSlateBrush>& Item = GeneratedBrushes.FindOrAdd(*InItem);
if (!Item.IsValid())
{
if (const FString Ext = FPaths::GetExtension(*InItem); Ext == "svg")
{
Item.Reset(new FSlateVectorImageBrush(*InItem, IconSize));
}
else if (Ext == "png")
{
Item.Reset(new FSlateDynamicImageBrush(FName(**InItem), IconSize));
}
else
{
return SNew(STextBlock).Text(FText::FromString(FString("Path is not a .png or .svg file.")));
}
}
return SNew(SHorizontalBox)
.ToolTip(SNew(SToolTip).Text(FText::FromString(*AbsolutePath)))
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(EVerticalAlignment::VAlign_Center)
[
SNew(SImage)
.Image(Item.Get())
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(16.0f, 1.0f)
.VAlign(EVerticalAlignment::VAlign_Center)
[
SNew(STextBlock)
.Text(FText::FromString(ItemName))
.MinDesiredWidth(200.0f)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.Padding(16.0f, 1.0f, 0.0f, 1.0f)
.VAlign(EVerticalAlignment::VAlign_Center)
.HAlign(EHorizontalAlignment::HAlign_Right)
[
SNew(STextBlock)
.Text(FText::FromString(Category))
.MinDesiredWidth(300.0f)
.ColorAndOpacity(FLinearColor(0.5, 0.5, 0.5, 0.5))
.Justification(ETextJustify::Right)
];
}

return SNew(STextBlock).Text(FText::FromString(FString("Path doesn't exist.")));
}

void FRedEditorIconPathCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle,
class IDetailChildrenBuilder& StructBuilder,
IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
}

TArray<FString> FRedEditorIconPathCustomization::GetIconOptionsFromPath()
{
TArray<FString> OutFoundIcons;
const URedDeveloperSettings* RedSettings = GetDefault<URedDeveloperSettings>();
for (const FString& Path : RedSettings->EditorIconWidgetSearchPaths)
{
GetIconsFromPath(Path, OutFoundIcons);
}
return OutFoundIcons;
}

TArray<TSharedPtr<FString>>* FRedEditorIconPathCustomization::GetIconOptionsPointer()
{
if (CachedIconOptions.Num() == 0)
{
Algo::Transform(GetIconOptionsFromPath(), CachedIconOptions, [](const FString& InString)
{
return MakeShared<FString>(InString);
});
}

return &CachedIconOptions;
}


void FRedEditorIconPathCustomization::GetIconsFromPath(const FString& InPath, TArray<FString>& OutFoundIcons)
{
const FString SearchDirectory = FPaths::EngineDir() / InPath;
IFileManager::Get().FindFilesRecursive(OutFoundIcons, *SearchDirectory, TEXT("*.png"), true, false, false);
IFileManager::Get().FindFilesRecursive(OutFoundIcons, *SearchDirectory, TEXT("*.svg"), true, false, false);
}
49 changes: 49 additions & 0 deletions Source/RedTechArtToolsEditor/Private/RedEditorIconWidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// MIT License
//
// Copyright (c) 2022 Ryan DowlingSoka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "RedEditorIconWidget.h"

#include "Brushes/SlateImageBrush.h"
#include "Components/Image.h"


void URedEditorIconWidget::SynchronizeProperties()
{
Super::SynchronizeProperties();
if(!IconPath.Path.IsEmpty() && IFileManager::Get().FileExists(*FPaths::ConvertRelativePathToFull(*IconPath.Path)))
{
if(const FString Ext = FPaths::GetExtension(IconPath.Path); Ext == "svg")
{
IconBrush.Reset(new FSlateVectorImageBrush( IconPath.Path, IconSize, Brush.TintColor, Brush.Tiling ));
}
else if(Ext == "png")
{
IconBrush.Reset(new FSlateDynamicImageBrush(FName(*IconPath.Path), IconSize, Brush.TintColor.GetSpecifiedColor(), Brush.Tiling ));
}
if(IconBrush != nullptr)
{
SetBrush(*IconBrush.Get());
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// MIT License
//
// Copyright (c) 2022 Ryan DowlingSoka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

class FRedEditorIconPathCustomization : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance();

// BEGIN IPropertyTypeCustomization interface
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle,
class FDetailWidgetRow& HeaderRow,
IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle,
class IDetailChildrenBuilder& StructBuilder,
IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
// END IPropertyTypeCustomization interface

private:
TSharedRef<SWidget> HandleGenerateWidget(TSharedPtr<FString> InItem);

TArray<TSharedPtr<FString>>* GetIconOptionsPointer();
TArray<TSharedPtr<FString>> CachedIconOptions;

static TArray<FString> GetIconOptionsFromPath();
static void GetIconsFromPath(const FString& InPath, TArray<FString>& OutFoundIcons);

TMap<FString, TUniquePtr<FSlateBrush>> GeneratedBrushes;
};
66 changes: 66 additions & 0 deletions Source/RedTechArtToolsEditor/Public/RedEditorIconWidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// MIT License
//
// Copyright (c) 2022 Ryan DowlingSoka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Components/Image.h"
#include "Styling/SlateBrush.h"
#include "RedEditorIconWidget.generated.h"

class UImage;

USTRUCT()
struct FRedEditorIconPath
{
GENERATED_BODY()

UPROPERTY(EditAnywhere)
FString Path;
};


/**
* Convenience class for Editor Utility Widgets to access the Editor Slate Icons.
* Not available at runtime.
*/
UCLASS(DisplayName="RED Editor Icon Widget")
class REDTECHARTTOOLSEDITOR_API URedEditorIconWidget : public UImage
{
GENERATED_BODY()

public:
UPROPERTY(EditAnywhere, Category = "Editor Icon Widget")
FVector2D IconSize = FVector2D(20.f, 20.f);

UPROPERTY(EditAnywhere, Category = "Editor Icon Widget")
FRedEditorIconPath IconPath;

protected:
virtual void SynchronizeProperties() override;

private:
TUniquePtr<FSlateBrush> IconBrush;


};

0 comments on commit 9087034

Please sign in to comment.