first commit
This commit is contained in:
22
Plugins/CommonTime/CommonTime.uplugin
Normal file
22
Plugins/CommonTime/CommonTime.uplugin
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "0.1.0",
|
||||
"FriendlyName": "CommonTime",
|
||||
"Description": "Provides an editor view for editing DateTime and Timespan values.",
|
||||
"Category": "Editor",
|
||||
"CreatedBy": "MrRobinOfficial",
|
||||
"CreatedByURL": "https://github.com/MrRobinOfficial",
|
||||
"DocsURL": "https://github.com/MrRobinOfficial/Unreal-CommonTime",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "https://github.com/MrRobinOfficial/Unreal-CommonTime/issues",
|
||||
"CanContainContent": true,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CommonTime",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit"
|
||||
}
|
||||
]
|
||||
}
|
||||
8
Plugins/CommonTime/Config/FilterPlugin.ini
Normal file
8
Plugins/CommonTime/Config/FilterPlugin.ini
Normal file
@ -0,0 +1,8 @@
|
||||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
21
Plugins/CommonTime/LICENSE.txt
Normal file
21
Plugins/CommonTime/LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 MrRobin
|
||||
|
||||
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.
|
||||
50
Plugins/CommonTime/README.md
Normal file
50
Plugins/CommonTime/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
<!-- markdownlint-disable-next-line -->
|
||||
<p align="center">
|
||||
<a href="#" rel="noopener" target="_blank"><img width="150" src="/Resources/Icon128.png" alt="CommonTime logo"></a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">CommonTime [Unreal Engine]</h1>
|
||||
|
||||
<div align="center">
|
||||
|
||||
*Provides an editor view for editing DateTime and Timespan values.*
|
||||
|
||||
[](https://github.com/mrrobinofficial/unreal-commontime/blob/HEAD/LICENSE.txt)
|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
#
|
||||
|
||||
## 📋 TODO
|
||||
|
||||
* Fix padding issue (not prioritize)
|
||||
|
||||
## ⚙️ Supported Platforms
|
||||
This plug-in was last built against Unreal Engine 5.3. But should be able to support any version of the engine.
|
||||
|
||||
## ⚒️ Installation
|
||||
|
||||
You can install from the <a href="https://github.com/MrRobinOfficial/Unreal-CommonTime/releases/latest">release section</a>.
|
||||
|
||||
Alternatively, you can install this plugin via terminal with [*git*](https://git-scm.com/). **Here is the command for installing it**.
|
||||
|
||||
```console
|
||||
git clone git@github.com:MrRobinOfficial/Unreal-CommonTime.git CommonTime
|
||||
```
|
||||
|
||||
#
|
||||
|
||||
<details open>
|
||||
<summary><h2>🖼️ Screenshots</h2></summary>
|
||||
<p><b>From this:</b></p>
|
||||
<img src="Resources/Screenshot_01.png" alt="Regular editor view for DateTime and Timespan">
|
||||
<p><b>To this:</b></p>
|
||||
<img src="Resources/Screenshot_02.png" alt="Custom editor view for DateTime and Timespan">
|
||||
</details>
|
||||
|
||||
#
|
||||
|
||||
## 🆘 Support
|
||||
If you have any questions or issue, just write either to my [YouTube channel](https://www.youtube.com/@mrrobinofficial), [Email](mailto:mrrobin123mail@gmail.com) or [Twitter DM](https://twitter.com/MrRobinOfficial).
|
||||
BIN
Plugins/CommonTime/Resources/Icon128.png
Normal file
BIN
Plugins/CommonTime/Resources/Icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
BIN
Plugins/CommonTime/Resources/Screenshot_01.png
Normal file
BIN
Plugins/CommonTime/Resources/Screenshot_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
Plugins/CommonTime/Resources/Screenshot_02.png
Normal file
BIN
Plugins/CommonTime/Resources/Screenshot_02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
30
Plugins/CommonTime/Source/CommonTime/CommonTime.Build.cs
Normal file
30
Plugins/CommonTime/Source/CommonTime/CommonTime.Build.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2023 MrRobin. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class CommonTime : ModuleRules
|
||||
{
|
||||
public CommonTime(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core",
|
||||
});
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"DeveloperSettings",
|
||||
"UnrealEd",
|
||||
"PropertyEditor",
|
||||
"ClassViewer",
|
||||
"InputCore",
|
||||
"BlueprintGraph",
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
// Copyright 2023 MrRobin. All Rights Reserved.
|
||||
|
||||
#include "CommonTimeModule.h"
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#include "DetailCustomizations/MyDateTimeDetailCustomization.h"
|
||||
#include "DetailCustomizations/MyTimespanDetailCustomization.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogCommonTime);
|
||||
|
||||
class FCommonTimeModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
void StartupModule() override
|
||||
{
|
||||
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));
|
||||
|
||||
PropertyModule.RegisterCustomPropertyTypeLayout(
|
||||
TEXT("Timespan"),
|
||||
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyTimespanDetailCustomization::MakeInstance)
|
||||
);
|
||||
|
||||
PropertyModule.RegisterCustomPropertyTypeLayout(
|
||||
TEXT("DateTime"),
|
||||
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyDateTimeDetailCustomization::MakeInstance)
|
||||
);
|
||||
|
||||
PropertyModule.NotifyCustomizationModuleChanged();
|
||||
}
|
||||
|
||||
void ShutdownModule() override
|
||||
{
|
||||
if (FModuleManager::Get().IsModuleLoaded(TEXT("PropertyEditor")))
|
||||
{
|
||||
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
|
||||
PropertyModule.UnregisterCustomPropertyTypeLayout(TEXT("Timespan"));
|
||||
PropertyModule.UnregisterCustomPropertyTypeLayout(TEXT("DateTime"));
|
||||
|
||||
PropertyModule.NotifyCustomizationModuleChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_MODULE(FCommonTimeModule, CommonTime);
|
||||
@ -0,0 +1,395 @@
|
||||
// Copyright 2023 MrRobin. All Rights Reserved.
|
||||
|
||||
#include "DetailCustomizations/MyDateTimeDetailCustomization.h"
|
||||
|
||||
#include "Containers/Array.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "DetailWidgetRow.h"
|
||||
#include "Fonts/SlateFontInfo.h"
|
||||
#include "HAL/PlatformCrt.h"
|
||||
#include "Internationalization/Internationalization.h"
|
||||
#include "Misc/Attribute.h"
|
||||
#include "Misc/DateTime.h"
|
||||
#include "PropertyHandle.h"
|
||||
#include "Styling/AppStyle.h"
|
||||
#include "Styling/ISlateStyle.h"
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "Widgets/Input/SNumericEntryBox.h"
|
||||
#include "Widgets/Input/SVectorInputBox.h"
|
||||
#include "Widgets/Input/NumericTypeInterface.h"
|
||||
#include "Widgets/Input/NumericUnitTypeInterface.inl"
|
||||
#include "Math/UnitConversion.h"
|
||||
|
||||
// SExpanderArrow
|
||||
|
||||
#define LOCTEXT_NAMESPACE "MyDateTimeDetailCustomization"
|
||||
|
||||
/* IDetailCustomization interface
|
||||
*****************************************************************************/
|
||||
|
||||
void FMyDateTimeDetailCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
void FMyDateTimeDetailCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
PropertyHandle = StructPropertyHandle;
|
||||
|
||||
SAssignNew(YearEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(1)
|
||||
.MaxValue(9999)
|
||||
.MinSliderValue(1)
|
||||
.MaxSliderValue(9999)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 3)
|
||||
.OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 3)
|
||||
.OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 3)
|
||||
.OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Years)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
SAssignNew(MonthEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(1)
|
||||
.MaxValue(12)
|
||||
.MinSliderValue(1)
|
||||
.MaxSliderValue(12)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 4)
|
||||
.OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 4)
|
||||
.OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 4)
|
||||
.OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Months)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
SAssignNew(DayEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(1)
|
||||
.MaxValue(31)
|
||||
.MinSliderValue(1)
|
||||
.MaxSliderValue(31)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 5)
|
||||
.OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 5)
|
||||
.OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 5)
|
||||
.OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Days)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
SAssignNew(HourEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(0)
|
||||
.MaxValue(23)
|
||||
.MinSliderValue(0)
|
||||
.MaxSliderValue(23)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 0)
|
||||
.OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 0)
|
||||
.OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 0)
|
||||
.OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Hours)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
SAssignNew(MinuteEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(0)
|
||||
.MaxValue(59)
|
||||
.MinSliderValue(0)
|
||||
.MaxSliderValue(59)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 1)
|
||||
.OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 1)
|
||||
.OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 1)
|
||||
.OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Minutes)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
SAssignNew(SecondEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(0)
|
||||
.MaxValue(59)
|
||||
.MinSliderValue(0)
|
||||
.MaxSliderValue(59)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 2)
|
||||
.OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 2)
|
||||
.OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 2)
|
||||
.OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Seconds)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
HeaderRow
|
||||
.NameContent()
|
||||
[
|
||||
StructPropertyHandle->CreatePropertyNameWidget()
|
||||
]
|
||||
.ValueContent()
|
||||
.MinDesiredWidth(125.0f * 3.0f)
|
||||
.MaxDesiredWidth(125.0f * 3.0f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(0.0f, 0.0f, 2.0f, 0.0f)
|
||||
[
|
||||
YearEntryBox.ToSharedRef()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(2.0f, 0.0f)
|
||||
[
|
||||
MonthEntryBox.ToSharedRef()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(2.0f, 0.0f, 0.0f, 0.0f)
|
||||
[
|
||||
DayEntryBox.ToSharedRef()
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(SSpacer)
|
||||
.Size(FVector2D(8.0f))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(0.0f, 0.0f, 2.0f, 0.0f)
|
||||
[
|
||||
HourEntryBox.ToSharedRef()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(2.0f, 0.0f)
|
||||
[
|
||||
MinuteEntryBox.ToSharedRef()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(2.0f, 0.0f, 0.0f, 0.0f)
|
||||
[
|
||||
SecondEntryBox.ToSharedRef()
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/* FMyDateTimeDetailCustomization callbacks
|
||||
*****************************************************************************/
|
||||
|
||||
TOptional<double> FMyDateTimeDetailCustomization::OnGetValue(int32 Index) const
|
||||
{
|
||||
TArray<void*> RawData;
|
||||
PropertyHandle->AccessRawData(RawData);
|
||||
|
||||
if (RawData.Num() != 1)
|
||||
return TOptional<double>();
|
||||
|
||||
if (RawData[0] == nullptr)
|
||||
return TOptional<double>();
|
||||
|
||||
auto* DateTime = ((FDateTime*)RawData[0]);
|
||||
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
return TOptional<double>(DateTime->GetHour());
|
||||
|
||||
case 1:
|
||||
return TOptional<double>(DateTime->GetMinute());
|
||||
|
||||
case 2:
|
||||
return TOptional<double>(DateTime->GetSecond());
|
||||
|
||||
case 3:
|
||||
return TOptional<double>(DateTime->GetYear());
|
||||
|
||||
case 4:
|
||||
return TOptional<double>(DateTime->GetMonth());
|
||||
|
||||
case 5:
|
||||
return TOptional<double>(DateTime->GetDay());
|
||||
|
||||
default:
|
||||
return TOptional<double>();
|
||||
}
|
||||
}
|
||||
|
||||
void FMyDateTimeDetailCustomization::OnBeginSliderMovement()
|
||||
{
|
||||
bIsUsingSlider = true;
|
||||
|
||||
GEditor->BeginTransaction(
|
||||
FText::Format(
|
||||
NSLOCTEXT("MyDateTimeDetailCustomization", "SetDateTimeProperty", "Edit {0}"),
|
||||
PropertyHandle->GetPropertyDisplayName())
|
||||
);
|
||||
}
|
||||
|
||||
void FMyDateTimeDetailCustomization::OnEndSliderMovement(double NewValue)
|
||||
{
|
||||
bIsUsingSlider = false;
|
||||
|
||||
GEditor->EndTransaction();
|
||||
}
|
||||
|
||||
void FMyDateTimeDetailCustomization::OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index)
|
||||
{
|
||||
NewValue = FMath::CeilToDouble(NewValue);
|
||||
|
||||
TArray<void*> RawData;
|
||||
|
||||
PropertyHandle->AccessRawData(RawData);
|
||||
PropertyHandle->NotifyPreChange();
|
||||
|
||||
for (auto RawDataInstance : RawData)
|
||||
{
|
||||
auto* DateTime = (FDateTime*)RawDataInstance;
|
||||
|
||||
int32 Year = DateTime->GetYear();
|
||||
int32 Month = DateTime->GetMonth();
|
||||
int32 Day = DateTime->GetDay();
|
||||
|
||||
int32 Hour = DateTime->GetHour();
|
||||
int32 Minute = DateTime->GetMinute();
|
||||
int32 Second = DateTime->GetSecond();
|
||||
|
||||
int32 MaxDays = FDateTime::DaysInMonth(Year, Month);
|
||||
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
*DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, NewValue, Minute, Second);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, NewValue, Second);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, NewValue);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
MaxDays = FDateTime::DaysInMonth(NewValue, Month);
|
||||
*DateTime = FDateTime(NewValue, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
MaxDays = FDateTime::DaysInMonth(Year, NewValue);
|
||||
*DateTime = FDateTime(Year, NewValue, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
*DateTime = FDateTime(Year, Month, NewValue > MaxDays ? MaxDays : NewValue, Hour, Minute, Second);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
||||
PropertyHandle->NotifyFinishedChangingProperties();
|
||||
}
|
||||
|
||||
void FMyDateTimeDetailCustomization::OnValueChanged(double NewValue, int32 Index)
|
||||
{
|
||||
if (!bIsUsingSlider)
|
||||
return;
|
||||
|
||||
NewValue = FMath::CeilToDouble(NewValue);
|
||||
|
||||
TArray<void*> RawData;
|
||||
|
||||
PropertyHandle->AccessRawData(RawData);
|
||||
PropertyHandle->NotifyPreChange();
|
||||
|
||||
for (auto RawDataInstance : RawData)
|
||||
{
|
||||
auto* DateTime = (FDateTime*)RawDataInstance;
|
||||
|
||||
int32 Year = DateTime->GetYear();
|
||||
int32 Month = DateTime->GetMonth();
|
||||
int32 Day = DateTime->GetDay();
|
||||
|
||||
int32 Hour = DateTime->GetHour();
|
||||
int32 Minute = DateTime->GetMinute();
|
||||
int32 Second = DateTime->GetSecond();
|
||||
|
||||
int32 MaxDays = FDateTime::DaysInMonth(Year, Month);
|
||||
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
*DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, NewValue, Minute, Second);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, NewValue, Second);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, NewValue);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
MaxDays = FDateTime::DaysInMonth(NewValue, Month);
|
||||
*DateTime = FDateTime(NewValue, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
MaxDays = FDateTime::DaysInMonth(Year, NewValue);
|
||||
*DateTime = FDateTime(Year, NewValue, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
*DateTime = FDateTime(Year, Month, NewValue > MaxDays ? MaxDays : NewValue, Hour, Minute, Second);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
||||
PropertyHandle->NotifyFinishedChangingProperties();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@ -0,0 +1,256 @@
|
||||
// Copyright 2023 MrRobin. All Rights Reserved.
|
||||
|
||||
#include "DetailCustomizations/MyTimespanDetailCustomization.h"
|
||||
|
||||
#include "Containers/Array.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "DetailWidgetRow.h"
|
||||
#include "Fonts/SlateFontInfo.h"
|
||||
#include "HAL/PlatformCrt.h"
|
||||
#include "Internationalization/Internationalization.h"
|
||||
#include "Misc/Attribute.h"
|
||||
#include "Misc/Timespan.h"
|
||||
#include "PropertyHandle.h"
|
||||
#include "Styling/AppStyle.h"
|
||||
#include "Styling/ISlateStyle.h"
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "Widgets/Input/SNumericEntryBox.h"
|
||||
#include "Widgets/Input/SVectorInputBox.h"
|
||||
#include "Widgets/Input/NumericTypeInterface.h"
|
||||
#include "Widgets/Input/NumericUnitTypeInterface.inl"
|
||||
#include "Math/UnitConversion.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "MyTimespanDetailCustomization"
|
||||
|
||||
/* IDetailCustomization interface
|
||||
*****************************************************************************/
|
||||
|
||||
void FMyTimespanDetailCustomization::CustomizeChildren(
|
||||
TSharedRef<IPropertyHandle> StructPropertyHandle,
|
||||
class IDetailChildrenBuilder& StructBuilder,
|
||||
IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
void FMyTimespanDetailCustomization::CustomizeHeader(
|
||||
TSharedRef<IPropertyHandle> StructPropertyHandle,
|
||||
class FDetailWidgetRow& HeaderRow,
|
||||
IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
PropertyHandle = StructPropertyHandle;
|
||||
|
||||
SAssignNew(HourEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(0)
|
||||
.MaxValue(23)
|
||||
.MinSliderValue(0)
|
||||
.MaxSliderValue(23)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyTimespanDetailCustomization::OnGetValue, 0)
|
||||
.OnValueChanged(this, &FMyTimespanDetailCustomization::OnValueChanged, 0)
|
||||
.OnValueCommitted(this, &FMyTimespanDetailCustomization::OnValueCommitted, 0)
|
||||
.OnBeginSliderMovement(this, &FMyTimespanDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyTimespanDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Hours)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
SAssignNew(MinuteEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(0)
|
||||
.MaxValue(59)
|
||||
.MinSliderValue(0)
|
||||
.MaxSliderValue(59)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyTimespanDetailCustomization::OnGetValue, 1)
|
||||
.OnValueChanged(this, &FMyTimespanDetailCustomization::OnValueChanged, 1)
|
||||
.OnValueCommitted(this, &FMyTimespanDetailCustomization::OnValueCommitted, 1)
|
||||
.OnBeginSliderMovement(this, &FMyTimespanDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyTimespanDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Minutes)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
SAssignNew(SecondEntryBox, SNumericEntryBox<double>)
|
||||
.AllowSpin(true)
|
||||
.MinValue(0)
|
||||
.MaxValue(59)
|
||||
.MinSliderValue(0)
|
||||
.MaxSliderValue(59)
|
||||
.MinFractionalDigits(0)
|
||||
.MaxFractionalDigits(0)
|
||||
.Font(IDetailLayoutBuilder::GetDetailFont())
|
||||
.Value(this, &FMyTimespanDetailCustomization::OnGetValue, 2)
|
||||
.OnValueChanged(this, &FMyTimespanDetailCustomization::OnValueChanged, 2)
|
||||
.OnValueCommitted(this, &FMyTimespanDetailCustomization::OnValueCommitted, 2)
|
||||
.OnBeginSliderMovement(this, &FMyTimespanDetailCustomization::OnBeginSliderMovement)
|
||||
.OnEndSliderMovement(this, &FMyTimespanDetailCustomization::OnEndSliderMovement)
|
||||
.TypeInterface(MakeShareable(new TNumericUnitTypeInterface<double>(EUnit::Seconds)))
|
||||
.LinearDeltaSensitivity(1);
|
||||
|
||||
HeaderRow
|
||||
.NameContent()
|
||||
[
|
||||
StructPropertyHandle->CreatePropertyNameWidget()
|
||||
]
|
||||
.ValueContent()
|
||||
.MinDesiredWidth(125.0f * 3.0f)
|
||||
.MaxDesiredWidth(125.0f * 3.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(0.0f)
|
||||
[
|
||||
HourEntryBox.ToSharedRef()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(2.5f, 0.0f)
|
||||
[
|
||||
MinuteEntryBox.ToSharedRef()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(0.0f)
|
||||
[
|
||||
SecondEntryBox.ToSharedRef()
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/* FMyTimespanDetailCustomization callbacks
|
||||
*****************************************************************************/
|
||||
|
||||
TOptional<double> FMyTimespanDetailCustomization::OnGetValue(int32 Index) const
|
||||
{
|
||||
TArray<void*> RawData;
|
||||
PropertyHandle->AccessRawData(RawData);
|
||||
|
||||
if (RawData.Num() != 1)
|
||||
return TOptional<double>();
|
||||
|
||||
if (RawData[0] == nullptr)
|
||||
return TOptional<double>();
|
||||
|
||||
auto* Timespan = ((FTimespan*)RawData[0]);
|
||||
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
return TOptional<double>(Timespan->GetHours());
|
||||
|
||||
case 1:
|
||||
return TOptional<double>(Timespan->GetMinutes());
|
||||
|
||||
case 2:
|
||||
return TOptional<double>(Timespan->GetSeconds());
|
||||
|
||||
default:
|
||||
return TOptional<double>();
|
||||
}
|
||||
}
|
||||
|
||||
void FMyTimespanDetailCustomization::OnBeginSliderMovement()
|
||||
{
|
||||
bIsUsingSlider = true;
|
||||
|
||||
GEditor->BeginTransaction(FText::Format(NSLOCTEXT("MyTimespanDetailCustomization", "SetTimespanProperty", "Edit {0}"), PropertyHandle->GetPropertyDisplayName()));
|
||||
}
|
||||
|
||||
void FMyTimespanDetailCustomization::OnEndSliderMovement(double NewValue)
|
||||
{
|
||||
bIsUsingSlider = false;
|
||||
|
||||
GEditor->EndTransaction();
|
||||
}
|
||||
|
||||
void FMyTimespanDetailCustomization::OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index)
|
||||
{
|
||||
NewValue = FMath::CeilToDouble(NewValue);
|
||||
|
||||
TArray<void*> RawData;
|
||||
|
||||
PropertyHandle->AccessRawData(RawData);
|
||||
PropertyHandle->NotifyPreChange();
|
||||
|
||||
for (auto RawDataInstance : RawData)
|
||||
{
|
||||
auto* Timespan = (FTimespan*)RawDataInstance;
|
||||
|
||||
int32 Hours = Timespan->GetHours();
|
||||
int32 Minutes = Timespan->GetMinutes();
|
||||
int32 Seconds = Timespan->GetSeconds();
|
||||
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
*Timespan = FTimespan(NewValue, Minutes, Seconds);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*Timespan = FTimespan(Hours, NewValue, Seconds);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*Timespan = FTimespan(Hours, Minutes, NewValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
||||
PropertyHandle->NotifyFinishedChangingProperties();
|
||||
}
|
||||
|
||||
void FMyTimespanDetailCustomization::OnValueChanged(double NewValue, int32 Index)
|
||||
{
|
||||
if (!bIsUsingSlider)
|
||||
return;
|
||||
|
||||
NewValue = FMath::CeilToDouble(NewValue);
|
||||
|
||||
TArray<void*> RawData;
|
||||
|
||||
PropertyHandle->AccessRawData(RawData);
|
||||
PropertyHandle->NotifyPreChange();
|
||||
|
||||
for (auto RawDataInstance : RawData)
|
||||
{
|
||||
auto* Timespan = (FTimespan*)RawDataInstance;
|
||||
|
||||
int32 Hours = Timespan->GetHours();
|
||||
int32 Minutes = Timespan->GetMinutes();
|
||||
int32 Seconds = Timespan->GetSeconds();
|
||||
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
*Timespan = FTimespan(NewValue, Minutes, Seconds);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*Timespan = FTimespan(Hours, NewValue, Seconds);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*Timespan = FTimespan(Hours, Minutes, NewValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
||||
PropertyHandle->NotifyFinishedChangingProperties();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@ -0,0 +1,7 @@
|
||||
// Copyright 2023 MrRobin. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCommonTime, Log, All);
|
||||
@ -0,0 +1,58 @@
|
||||
// Copyright 2023 MrRobin. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "Internationalization/Text.h"
|
||||
#include "Styling/SlateColor.h"
|
||||
#include "Templates/SharedPointer.h"
|
||||
#include "Types/SlateEnums.h"
|
||||
|
||||
class IPropertyHandle;
|
||||
class SEditableTextBox;
|
||||
|
||||
/**
|
||||
* Implements a details view customization for the FDateTime structure.
|
||||
*/
|
||||
class FMyDateTimeDetailCustomization
|
||||
: public IPropertyTypeCustomization
|
||||
{
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @return The new instance.
|
||||
*/
|
||||
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FMyDateTimeDetailCustomization());
|
||||
}
|
||||
|
||||
public:
|
||||
// IPropertyTypeCustomization interface
|
||||
void CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
|
||||
void CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
|
||||
private:
|
||||
TOptional<double> OnGetValue(int32 Index) const;
|
||||
void OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index);
|
||||
void OnValueChanged(double NewValue, int32 Index);
|
||||
void OnBeginSliderMovement();
|
||||
void OnEndSliderMovement(double NewValue);
|
||||
|
||||
private:
|
||||
/** Holds a handle to the property being edited. */
|
||||
TSharedPtr<IPropertyHandle> PropertyHandle;
|
||||
|
||||
/** True if a value is being changed by dragging a slider */
|
||||
bool bIsUsingSlider;
|
||||
|
||||
TSharedPtr<SWidget> YearEntryBox;
|
||||
TSharedPtr<SWidget> MonthEntryBox;
|
||||
TSharedPtr<SWidget> DayEntryBox;
|
||||
TSharedPtr<SWidget> HourEntryBox;
|
||||
TSharedPtr<SWidget> MinuteEntryBox;
|
||||
TSharedPtr<SWidget> SecondEntryBox;
|
||||
};
|
||||
@ -0,0 +1,54 @@
|
||||
// Copyright 2023 MrRobin. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "Internationalization/Text.h"
|
||||
#include "Styling/SlateColor.h"
|
||||
#include "Templates/SharedPointer.h"
|
||||
#include "Types/SlateEnums.h"
|
||||
|
||||
class IPropertyHandle;
|
||||
class SEditableTextBox;
|
||||
|
||||
/**
|
||||
* Implements a details view customization for the FTimespan structure.
|
||||
*/
|
||||
class FMyTimespanDetailCustomization
|
||||
: public IPropertyTypeCustomization
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @return The new instance.
|
||||
*/
|
||||
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FMyTimespanDetailCustomization());
|
||||
}
|
||||
|
||||
public:
|
||||
// IPropertyTypeCustomization interface
|
||||
void CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
|
||||
void CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
|
||||
private:
|
||||
TOptional<double> OnGetValue(int32 Index) const;
|
||||
void OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index);
|
||||
void OnValueChanged(double NewValue, int32 Index);
|
||||
void OnBeginSliderMovement();
|
||||
void OnEndSliderMovement(double NewValue);
|
||||
|
||||
private:
|
||||
/** Holds a handle to the property being edited. */
|
||||
TSharedPtr<IPropertyHandle> PropertyHandle;
|
||||
|
||||
/** True if a value is being changed by dragging a slider */
|
||||
bool bIsUsingSlider;
|
||||
|
||||
TSharedPtr<SWidget> HourEntryBox;
|
||||
TSharedPtr<SWidget> MinuteEntryBox;
|
||||
TSharedPtr<SWidget> SecondEntryBox;
|
||||
};
|
||||
Reference in New Issue
Block a user