From 35ea4db981ad3f54279ab8cb6ee7f1d274d1cf39 Mon Sep 17 00:00:00 2001 From: Lucas Peter Date: Tue, 17 Sep 2024 17:50:37 +0200 Subject: [PATCH] Initial --- .gitignore | 2 + Resources/Icon128.png | Bin 0 -> 25930 bytes Resources/PlaceholderButtonIcon.svg | 3 + SkyPortal.uplugin | 25 ++++ Source/SkyPortal/Private/SkyPortal.cpp | 109 ++++++++++++++++++ .../SkyPortal/Private/SkyPortalCommands.cpp | 12 ++ Source/SkyPortal/Private/SkyPortalStyle.cpp | 60 ++++++++++ Source/SkyPortal/Public/SkyPortal.h | 30 +++++ Source/SkyPortal/Public/SkyPortalCommands.h | 23 ++++ Source/SkyPortal/Public/SkyPortalStyle.h | 32 +++++ Source/SkyPortal/SkyPortal.Build.cs | 58 ++++++++++ 11 files changed, 354 insertions(+) create mode 100644 .gitignore create mode 100644 Resources/Icon128.png create mode 100644 Resources/PlaceholderButtonIcon.svg create mode 100644 SkyPortal.uplugin create mode 100644 Source/SkyPortal/Private/SkyPortal.cpp create mode 100644 Source/SkyPortal/Private/SkyPortalCommands.cpp create mode 100644 Source/SkyPortal/Private/SkyPortalStyle.cpp create mode 100644 Source/SkyPortal/Public/SkyPortal.h create mode 100644 Source/SkyPortal/Public/SkyPortalCommands.h create mode 100644 Source/SkyPortal/Public/SkyPortalStyle.h create mode 100644 Source/SkyPortal/SkyPortal.Build.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ef6465 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Intermediate +Binaries diff --git a/Resources/Icon128.png b/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..fc6eae42ff4d5df9ebbff5444ba084def942dc3b GIT binary patch literal 25930 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 literal 0 HcmV?d00001 diff --git a/Resources/PlaceholderButtonIcon.svg b/Resources/PlaceholderButtonIcon.svg new file mode 100644 index 0000000..7302447 --- /dev/null +++ b/Resources/PlaceholderButtonIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/SkyPortal.uplugin b/SkyPortal.uplugin new file mode 100644 index 0000000..03b10d3 --- /dev/null +++ b/SkyPortal.uplugin @@ -0,0 +1,25 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "Skylanders Portal", + "Description": "Enable skylanders portal input.", + "Category": "Input Devices", + "CreatedBy": "lucastucious", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": false, + "IsBetaVersion": true, + "Installed": false, + "Modules": [ + { + "Name": "SkyPortal", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "IsExperimentalVersion": false, + "Sealed": true +} \ No newline at end of file diff --git a/Source/SkyPortal/Private/SkyPortal.cpp b/Source/SkyPortal/Private/SkyPortal.cpp new file mode 100644 index 0000000..4aadb3e --- /dev/null +++ b/Source/SkyPortal/Private/SkyPortal.cpp @@ -0,0 +1,109 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SkyPortal.h" +#include "SkyPortalStyle.h" +#include "SkyPortalCommands.h" +#include "LevelEditor.h" +#include "Widgets/Docking/SDockTab.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Text/STextBlock.h" +#include "ToolMenus.h" + +static const FName SkyPortalTabName("SkyPortal"); + +#define LOCTEXT_NAMESPACE "FSkyPortalModule" + +void FSkyPortalModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + + FSkyPortalStyle::Initialize(); + FSkyPortalStyle::ReloadTextures(); + + FSkyPortalCommands::Register(); + + PluginCommands = MakeShareable(new FUICommandList); + + PluginCommands->MapAction( + FSkyPortalCommands::Get().OpenPluginWindow, + FExecuteAction::CreateRaw(this, &FSkyPortalModule::PluginButtonClicked), + FCanExecuteAction()); + + UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FSkyPortalModule::RegisterMenus)); + + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(SkyPortalTabName, FOnSpawnTab::CreateRaw(this, &FSkyPortalModule::OnSpawnPluginTab)) + .SetDisplayName(LOCTEXT("FSkyPortalTabTitle", "SkyPortal")) + .SetMenuType(ETabSpawnerMenuType::Hidden); +} + +void FSkyPortalModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. + + UToolMenus::UnRegisterStartupCallback(this); + + UToolMenus::UnregisterOwner(this); + + FSkyPortalStyle::Shutdown(); + + FSkyPortalCommands::Unregister(); + + FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(SkyPortalTabName); +} + +TSharedRef FSkyPortalModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) +{ + FText WidgetText = FText::Format( + LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"), + FText::FromString(TEXT("FSkyPortalModule::OnSpawnPluginTab")), + FText::FromString(TEXT("SkyPortal.cpp")) + ); + + return SNew(SDockTab) + .TabRole(ETabRole::NomadTab) + [ + // Put your tab content here! + SNew(SBox) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(WidgetText) + ] + ]; +} + +void FSkyPortalModule::PluginButtonClicked() +{ + FGlobalTabmanager::Get()->TryInvokeTab(SkyPortalTabName); +} + +void FSkyPortalModule::RegisterMenus() +{ + // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner + FToolMenuOwnerScoped OwnerScoped(this); + + { + UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window"); + { + FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout"); + Section.AddMenuEntryWithCommandList(FSkyPortalCommands::Get().OpenPluginWindow, PluginCommands); + } + } + + { + UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); + { + FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings"); + { + FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FSkyPortalCommands::Get().OpenPluginWindow)); + Entry.SetCommandList(PluginCommands); + } + } + } +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FSkyPortalModule, SkyPortal) diff --git a/Source/SkyPortal/Private/SkyPortalCommands.cpp b/Source/SkyPortal/Private/SkyPortalCommands.cpp new file mode 100644 index 0000000..e9fc874 --- /dev/null +++ b/Source/SkyPortal/Private/SkyPortalCommands.cpp @@ -0,0 +1,12 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SkyPortalCommands.h" + +#define LOCTEXT_NAMESPACE "FSkyPortalModule" + +void FSkyPortalCommands::RegisterCommands() +{ + UI_COMMAND(OpenPluginWindow, "SkyPortal", "Bring up SkyPortal window", EUserInterfaceActionType::Button, FInputChord()); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/SkyPortal/Private/SkyPortalStyle.cpp b/Source/SkyPortal/Private/SkyPortalStyle.cpp new file mode 100644 index 0000000..095d525 --- /dev/null +++ b/Source/SkyPortal/Private/SkyPortalStyle.cpp @@ -0,0 +1,60 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SkyPortalStyle.h" +#include "Styling/SlateStyleRegistry.h" +#include "Framework/Application/SlateApplication.h" +#include "Slate/SlateGameResources.h" +#include "Interfaces/IPluginManager.h" +#include "Styling/SlateStyleMacros.h" + +#define RootToContentDir Style->RootToContentDir + +TSharedPtr FSkyPortalStyle::StyleInstance = nullptr; + +void FSkyPortalStyle::Initialize() +{ + if (!StyleInstance.IsValid()) + { + StyleInstance = Create(); + FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); + } +} + +void FSkyPortalStyle::Shutdown() +{ + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); + ensure(StyleInstance.IsUnique()); + StyleInstance.Reset(); +} + +FName FSkyPortalStyle::GetStyleSetName() +{ + static FName StyleSetName(TEXT("SkyPortalStyle")); + return StyleSetName; +} + +const FVector2D Icon16x16(16.0f, 16.0f); +const FVector2D Icon20x20(20.0f, 20.0f); + +TSharedRef< FSlateStyleSet > FSkyPortalStyle::Create() +{ + TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("SkyPortalStyle")); + Style->SetContentRoot(IPluginManager::Get().FindPlugin("SkyPortal")->GetBaseDir() / TEXT("Resources")); + + Style->Set("SkyPortal.OpenPluginWindow", new IMAGE_BRUSH_SVG(TEXT("PlaceholderButtonIcon"), Icon20x20)); + + return Style; +} + +void FSkyPortalStyle::ReloadTextures() +{ + if (FSlateApplication::IsInitialized()) + { + FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); + } +} + +const ISlateStyle& FSkyPortalStyle::Get() +{ + return *StyleInstance; +} diff --git a/Source/SkyPortal/Public/SkyPortal.h b/Source/SkyPortal/Public/SkyPortal.h new file mode 100644 index 0000000..9405618 --- /dev/null +++ b/Source/SkyPortal/Public/SkyPortal.h @@ -0,0 +1,30 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FToolBarBuilder; +class FMenuBuilder; + +class FSkyPortalModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + /** This function will be bound to Command (by default it will bring up plugin window) */ + void PluginButtonClicked(); + +private: + + void RegisterMenus(); + + TSharedRef OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs); + +private: + TSharedPtr PluginCommands; +}; diff --git a/Source/SkyPortal/Public/SkyPortalCommands.h b/Source/SkyPortal/Public/SkyPortalCommands.h new file mode 100644 index 0000000..4d054c3 --- /dev/null +++ b/Source/SkyPortal/Public/SkyPortalCommands.h @@ -0,0 +1,23 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Framework/Commands/Commands.h" +#include "SkyPortalStyle.h" + +class FSkyPortalCommands : public TCommands +{ +public: + + FSkyPortalCommands() + : TCommands(TEXT("SkyPortal"), NSLOCTEXT("Contexts", "SkyPortal", "SkyPortal Plugin"), NAME_None, FSkyPortalStyle::GetStyleSetName()) + { + } + + // TCommands<> interface + virtual void RegisterCommands() override; + +public: + TSharedPtr< FUICommandInfo > OpenPluginWindow; +}; diff --git a/Source/SkyPortal/Public/SkyPortalStyle.h b/Source/SkyPortal/Public/SkyPortalStyle.h new file mode 100644 index 0000000..52e3194 --- /dev/null +++ b/Source/SkyPortal/Public/SkyPortalStyle.h @@ -0,0 +1,32 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Styling/SlateStyle.h" + +/** */ +class FSkyPortalStyle +{ +public: + + static void Initialize(); + + static void Shutdown(); + + /** reloads textures used by slate renderer */ + static void ReloadTextures(); + + /** @return The Slate style set for the Shooter game */ + static const ISlateStyle& Get(); + + static FName GetStyleSetName(); + +private: + + static TSharedRef< class FSlateStyleSet > Create(); + +private: + + static TSharedPtr< class FSlateStyleSet > StyleInstance; +}; diff --git a/Source/SkyPortal/SkyPortal.Build.cs b/Source/SkyPortal/SkyPortal.Build.cs new file mode 100644 index 0000000..84a0dce --- /dev/null +++ b/Source/SkyPortal/SkyPortal.Build.cs @@ -0,0 +1,58 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class SkyPortal : ModuleRules +{ + public SkyPortal(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Projects", + "InputCore", + "EditorFramework", + "UnrealEd", + "ToolMenus", + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +}