FORCEINLINE bool FCompareUObjectByPropertyAscending<UByteProperty>::ComparePropertyValue( const uint8& LhsValue, const uint8& RhsValue ) const { // Bytes are trivially sorted numerically UEnum* PropertyEnum = Property->GetIntPropertyEnum(); if( PropertyEnum == NULL ) { return LhsValue < RhsValue; } else { // But Enums are sorted alphabetically based on the full enum entry name - must be sure that values are within Enum bounds! bool bLhsEnumValid = LhsValue < PropertyEnum->NumEnums(); bool bRhsEnumValid = RhsValue < PropertyEnum->NumEnums(); if(bLhsEnumValid && bRhsEnumValid) { FName LhsEnumName( PropertyEnum->GetEnum( LhsValue ) ); FName RhsEnumName( PropertyEnum->GetEnum( RhsValue ) ); return LhsEnumName.Compare( RhsEnumName ) < 0; } else if(bLhsEnumValid) { return true; } else if(bRhsEnumValid) { return false; } else { return LhsValue < RhsValue; } } }
void UPhysicsSettings::LoadSurfaceType() { // read "SurfaceType" defines and set meta data for the enum // find the enum UEnum * Enum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EPhysicalSurface"), true); // we need this Enum check(Enum); const FString KeyName = TEXT("DisplayName"); const FString HiddenMeta = TEXT("Hidden"); const FString UnusedDisplayName = TEXT("Unused"); // remainders, set to be unused for(int32 EnumIndex=1; EnumIndex<Enum->NumEnums(); ++EnumIndex) { // if meta data isn't set yet, set name to "Unused" until we fix property window to handle this // make sure all hide and set unused first // if not hidden yet if(!Enum->HasMetaData(*HiddenMeta, EnumIndex)) { Enum->SetMetaData(*HiddenMeta, TEXT(""), EnumIndex); Enum->SetMetaData(*KeyName, *UnusedDisplayName, EnumIndex); } } for(auto Iter=PhysicalSurfaces.CreateConstIterator(); Iter; ++Iter) { // @todo only for editor Enum->SetMetaData(*KeyName, *Iter->Name.ToString(), Iter->Type); // also need to remove "Hidden" Enum->RemoveMetaData(*HiddenMeta, Iter->Type); } }
void UGatherTextFromMetaDataCommandlet::GatherTextFromUObject(UField* const Field) { const int32 MetaDataCount = 3; const FString MetaDataKeys[MetaDataCount] = { TEXT("ToolTip"), TEXT("DisplayName"), TEXT("Category") }; const FString AssociatedNamespaces[MetaDataCount] = { TEXT("UObjectToolTips"), TEXT("UObjectDisplayNames"), TEXT("UObjectCategories") }; // Gather for object. { for(int32 i = 0; i < MetaDataCount; ++i) { const FString& MetaDataValue = Field->GetMetaData(*MetaDataKeys[i]); if(!(MetaDataValue.IsEmpty())) { const FString Namespace = AssociatedNamespaces[i]; FLocItem LocItem(MetaDataValue); FContext Context; Context.Key = MetaDataKeys[i] == TEXT("Category") ? MetaDataValue : Field->GetFullGroupName(true) + TEXT(".") + Field->GetName(); Context.SourceLocation = TEXT("Run-time MetaData"); ManifestInfo->AddEntry(TEXT("EntryDescription"), Namespace, LocItem, Context); } } } // For enums, also gather for enum values. { UEnum* Enum = Cast<UEnum>(Field); if(Enum) { const int32 ValueCount = Enum->NumEnums(); for(int32 i = 0; i < ValueCount; ++i) { for(int32 j = 0; j < MetaDataCount; ++j) { const FString& MetaDataValue = Enum->GetMetaData(*MetaDataKeys[j], i); if(!(MetaDataValue.IsEmpty())) { const FString Namespace = AssociatedNamespaces[j]; FLocItem LocItem(MetaDataValue); FContext Context; Context.Key = MetaDataKeys[j] == TEXT("Category") ? MetaDataValue : Enum->GetFullGroupName(true) + TEXT(".") + Enum->GetName() + TEXT(".") + Enum->GetEnumName(i); Context.SourceLocation = TEXT("Run-time MetaData"); ManifestInfo->AddEntry(TEXT("EntryDescription"), Namespace, LocItem, Context); } } } } } }
void SPropertyEditorCombo::SendToObjects( const FString& NewValue ) { const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode(); UProperty* Property = PropertyNode->GetProperty(); FString Value; FString ToolTipValue; if ( bUsesAlternateDisplayValues && !Property->IsA(UStrProperty::StaticClass())) { // currently only enum properties can use alternate display values; this might change, so assert here so that if support is expanded to other property types // without updating this block of code, we'll catch it quickly UEnum* Enum = CastChecked<UByteProperty>(Property)->Enum; check(Enum); int32 Index = INDEX_NONE; for( int32 ItemIndex = 0; ItemIndex < Enum->NumEnums(); ++ItemIndex ) { const FString EnumName = Enum->GetEnumName(ItemIndex); const FString DisplayName = Enum->GetDisplayNameText(ItemIndex ).ToString(); if( DisplayName.Len() > 0 ) { if ( DisplayName == NewValue ) { Index = ItemIndex; break; } } else if (EnumName == NewValue) { Index = ItemIndex; break; } } check( Index != INDEX_NONE ); Value = Enum->GetEnumName(Index); ToolTipValue = Enum->GetMetaData( TEXT("ToolTip"), Index ); FString ToolTipText = Property->GetToolTipText().ToString(); if (ToolTipValue.Len() > 0) { ToolTipText = FString::Printf(TEXT("%s\n\n%s"), *ToolTipText, *ToolTipValue); } SetToolTipText(ToolTipText); } else { Value = NewValue; } const TSharedRef< IPropertyHandle > PropertyHandle = PropertyEditor->GetPropertyHandle(); PropertyHandle->SetValueFromFormattedString( Value ); }
FORCEINLINE bool FCompareRowByColumnAscending<UByteProperty>::ComparePropertyValue( const TSharedPtr< IPropertyHandle >& LhsPropertyHandle, const TSharedPtr< IPropertyHandle >& RhsPropertyHandle ) const { // Get the basic uint8 values uint8 LhsValue; LhsPropertyHandle->GetValue( LhsValue ); uint8 RhsValue; RhsPropertyHandle->GetValue( RhsValue ); // Bytes are trivially sorted numerically UEnum* PropertyEnum = Property->GetIntPropertyEnum(); if( PropertyEnum == NULL ) { return LhsValue < RhsValue; } else { // But Enums are sorted alphabetically based on the full enum entry name - must be sure that values are within Enum bounds! bool bLhsEnumValid = LhsValue < PropertyEnum->NumEnums(); bool bRhsEnumValid = RhsValue < PropertyEnum->NumEnums(); if(bLhsEnumValid && bRhsEnumValid) { FName LhsEnumName( PropertyEnum->GetEnum( LhsValue ) ); FName RhsEnumName( PropertyEnum->GetEnum( RhsValue ) ); return LhsEnumName.Compare( RhsEnumName ) < 0; } else if(bLhsEnumValid) { return true; } else if(bRhsEnumValid) { return false; } else { return LhsValue < RhsValue; } } }
void UCollisionProfile::LoadProfileConfig(bool bForceInit) { /** * This function loads all config data to memory * * 1. First it fixes the meta data for each custom channel name since those meta data is used for #2 * 2. Load Default Profile so that it can be used later * 3. Second it sets up Correct ResponseToChannel for all profiles * 4. It loads profile redirect data **/ // read "EngineTraceChanne" and "GameTraceChanne" and set meta data FConfigSection* Configs = GConfig->GetSectionPrivate( TEXT("/Script/Engine.CollisionProfile"), false, true, GEngineIni ); // before any op, verify if profiles contains invalid name - such as Custom profile name - remove all of them for (auto Iter=Profiles.CreateConstIterator(); Iter; ++Iter) { // make sure it doens't have any if (Iter->Name == CustomCollisionProfileName) { UE_LOG(LogCollisionProfile, Error, TEXT("Profiles contain invalid name : %s is reserved for internal use"), *CustomCollisionProfileName.ToString()); Profiles.RemoveAt(Iter.GetIndex()); } } // 1. First loads all meta data for custom channels // this can be used in #2, so had to fix up first // this is to replace ECollisionChannel's DisplayName to be defined by users FString EngineTraceChannel = TEXT("EngineTraceChannel"); FString GameTraceChannel = TEXT("GameTraceChannel"); // find the enum UEnum* Enum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ECollisionChannel"), true); // we need this Enum check (Enum); UStruct* Struct = FCollisionResponseContainer::StaticStruct(); check (Struct); const FString KeyName = TEXT("DisplayName"); const FString TraceType = TEXT("TraceQuery"); const FString TraceValue = TEXT("1"); const FString HiddenMeta = TEXT("Hidden"); // need to initialize displaynames separate int32 NumEnum = Enum->NumEnums(); ChannelDisplayNames.Empty(NumEnum); ChannelDisplayNames.AddZeroed(NumEnum); TraceTypeMapping.Empty(); ObjectTypeMapping.Empty(); // first go through enum entry, and add suffix to displaynames int32 PrefixLen = FString(TEXT("ECC_")).Len(); // need to have mapping table between ECollisionChannel and EObjectTypeQuery/ETraceTypeQuery UEnum* ObjectTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EObjectTypeQuery"), true); UEnum* TraceTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ETraceTypeQuery"), true); check (ObjectTypeEnum && TraceTypeEnum); for ( int32 EnumIndex=0; EnumIndex<NumEnum; ++EnumIndex ) { FString EnumName = Enum->GetEnumName(EnumIndex); EnumName = EnumName.RightChop(PrefixLen); FName DisplayName = FName(*EnumName); if ( IS_VALID_COLLISIONCHANNEL(EnumIndex) ) { // verify if the Struct name matches // this is to avoid situations where they mismatch and causes random bugs UField* Field = FindField<UField>(Struct, DisplayName); if (!Field) { // error - this is bad. This means somebody changed variable name without changing channel enum UE_LOG(LogCollisionProfile, Error, TEXT("Variable (%s) isn't found for Channel (%s). \nPlease make sure you name matches between ECollisionChannel and FCollisionResponseContainer."), *DisplayName.ToString(), *EnumName); } #if WITH_EDITOR // clear displayname since we're setting it in below Enum->RemoveMetaData(*KeyName, EnumIndex); if (Enum->HasMetaData(*HiddenMeta, EnumIndex) == false) { Enum->SetMetaData(*HiddenMeta, NULL, EnumIndex); } #endif } else { // fix up FCollisionQueryFlag AllObjects flag, if trace type=1 ECollisionChannel CollisionChannel = (ECollisionChannel)EnumIndex; // for any engine level collision profile, we hard coded here // meta data doesn't work in cooked build, so we'll have to manually handle them if ((CollisionChannel == ECC_Visibility) || (CollisionChannel == ECC_Camera)) { // remove from object query flags FCollisionQueryFlag::Get().RemoveFromAllObjectsQueryFlag(CollisionChannel); TraceTypeMapping.Add(CollisionChannel); } else if ( CollisionChannel < ECC_OverlapAll_Deprecated ) { ObjectTypeMapping.Add(CollisionChannel); } } ChannelDisplayNames[EnumIndex] = DisplayName; } // Now Load Channel setups, and set display names if customized // also initialize DefaultResposneContainer with default response for each channel FCollisionResponseContainer::DefaultResponseContainer.SetAllChannels(ECR_Block); for (auto Iter=DefaultChannelResponses.CreateConstIterator(); Iter; ++Iter) { const FCustomChannelSetup& CustomChannel = *Iter; int32 EnumIndex = CustomChannel.Channel; // make sure it is the range of channels we allow to change if ( IS_VALID_COLLISIONCHANNEL(EnumIndex) ) { if ( CustomChannel.Name != NAME_None ) { // before you set meta data, you'll have to save this value to modify // ECollisionResponseContainer variables meta data FString VariableName = ChannelDisplayNames[EnumIndex].ToString(); FString DisplayValue = CustomChannel.Name.ToString(); // also has to set this for internal use ChannelDisplayNames[EnumIndex] = FName(*DisplayValue); #if WITH_EDITOR // set displayvalue for this enum entry Enum->SetMetaData(*KeyName, *DisplayValue, EnumIndex); // also need to remove "Hidden" Enum->RemoveMetaData(*HiddenMeta, EnumIndex); #endif // now add MetaData type for trace type if it does if (CustomChannel.bTraceType) { #if WITH_EDITOR // add to enum Enum->SetMetaData(*TraceType, *TraceValue, EnumIndex); #endif // remove from all object queries FCollisionQueryFlag::Get().RemoveFromAllObjectsQueryFlag(CustomChannel.Channel); TraceTypeMapping.Add(CustomChannel.Channel); } // if it has display value else { #if WITH_EDITOR // add to enum Enum->RemoveMetaData(*TraceType, EnumIndex); #endif ObjectTypeMapping.Add(CustomChannel.Channel); if (CustomChannel.bStaticObject) { // add to static object FCollisionQueryFlag::Get().AddToAllStaticObjectsQueryFlag(CustomChannel.Channel); } } #if WITH_EDITOR // now enum is fixed, so find member variable for the field UField* Field = FindField<UField>(Struct, FName(*VariableName)); // I verified up in the class, this can't happen check (Field); Field->SetMetaData(*KeyName, *DisplayValue); #endif } else { // missing name UE_LOG(LogCollisionProfile, Warning, TEXT("Name can't be empty for Channel (%d) "), EnumIndex ); } // allow it to set default response FCollisionResponseContainer::DefaultResponseContainer.SetResponse((ECollisionChannel) EnumIndex, CustomChannel.DefaultResponse); } else { // you can't customize those channels UE_LOG(LogCollisionProfile, Warning, TEXT("Default Setup doesn't allow for predefined engine channels (%d) "), EnumIndex); } } #if WITH_EDITOR // now propagate all changes to the channels to EObjectTypeQuery and ETraceTypeQuery for blueprint accessibility // this code is to show in editor more friendly, so it doesn't have to be set if it's not for editor int32 ObjectTypeEnumIndex = 0; int32 TraceTypeEnumIndex = 0; // first go through fill up ObjectType Enum for ( int32 EnumIndex=0; EnumIndex<NumEnum; ++EnumIndex ) { // if not hidden const FString& Hidden = Enum->GetMetaData(*HiddenMeta, EnumIndex); if ( Hidden.IsEmpty() ) { const FString& DisplayName = Enum->GetMetaData(*KeyName, EnumIndex); if ( !DisplayName.IsEmpty() ) { // find out trace type or object type if (Enum->GetMetaData(*TraceType, EnumIndex) == TraceValue) { TraceTypeEnum->RemoveMetaData(*HiddenMeta, TraceTypeEnumIndex); TraceTypeEnum->SetMetaData(*KeyName, *DisplayName, TraceTypeEnumIndex); ++TraceTypeEnumIndex; } else { ObjectTypeEnum->RemoveMetaData(*HiddenMeta, ObjectTypeEnumIndex); ObjectTypeEnum->SetMetaData(*KeyName, *DisplayName, ObjectTypeEnumIndex); ++ObjectTypeEnumIndex; } } } } // make sure TraceTypeEnumIndex matches TraceTypeMapping check (TraceTypeMapping.Num() == TraceTypeEnumIndex); check (ObjectTypeMapping.Num() == ObjectTypeEnumIndex); #endif // collision redirector has to be loaded before profile CollisionChannelRedirectsMap.Empty(); for(auto Iter = CollisionChannelRedirects.CreateConstIterator(); Iter; ++Iter) { FName OldName = Iter->OldName; FName NewName = Iter->NewName; // at least we need to have OldName if(OldName!= NAME_None && NewName != NAME_None) { // add to pair CollisionChannelRedirectsMap.Add(OldName, NewName); } else { // print error UE_LOG(LogCollisionProfile, Warning, TEXT("CollisionChannel Redirects : Name Can't be none (%s: %s)"), *OldName.ToString(), *NewName.ToString()); } } // 2. Second loads all set up back to ResponseToChannels // this does a lot of iteration, but this only happens once loaded, so it's better to be convenient than efficient // fill up Profiles data FillProfileData(Profiles, Enum, KeyName, EditProfiles); // 3. It loads redirect data - now time to load profile redirect ProfileRedirectsMap.Empty(); // handle profile redirect here for(auto Iter = ProfileRedirects.CreateConstIterator(); Iter; ++Iter) { FName OldName = Iter->OldName; FName NewName = Iter->NewName; // at least we need to have OldName if(OldName!= NAME_None && NewName != NAME_None) { FCollisionResponseTemplate Template; // make sure the template exists if(FindProfileData(Profiles, NewName, Template)) { // add to pair ProfileRedirectsMap.Add(OldName, NewName); } else { // print error UE_LOG(LogCollisionProfile, Warning, TEXT("ProfileRedirect (%s : %s) - New Name (\'%s\') isn't found "), *OldName.ToString(), *NewName.ToString(), *NewName.ToString()); } } } #if WITH_EDITOR if (bForceInit) { // go through objects, and see if we can reinitialize profile for(TObjectIterator<UPrimitiveComponent> PrimIt; PrimIt; ++PrimIt) { PrimIt->UpdateCollisionProfile(); } } #endif }
void UGatherTextFromMetaDataCommandlet::GatherTextFromUObject(UField* const Field, const FGatherParameters& Arguments) { // Gather for object. { if( !Field->HasMetaData( TEXT("DisplayName") ) ) { Field->SetMetaData( TEXT("DisplayName"), *FName::NameToDisplayString( Field->GetName(), Field->IsA( UBoolProperty::StaticClass() ) ) ); } for(int32 i = 0; i < Arguments.InputKeys.Num(); ++i) { FFormatNamedArguments PatternArguments; PatternArguments.Add( TEXT("FieldPath"), FText::FromString( Field->GetFullGroupName(false) ) ); if( Field->HasMetaData( *Arguments.InputKeys[i] ) ) { const FString& MetaDataValue = Field->GetMetaData(*Arguments.InputKeys[i]); if( !MetaDataValue.IsEmpty() ) { PatternArguments.Add( TEXT("MetaDataValue"), FText::FromString(MetaDataValue) ); const FString Namespace = Arguments.OutputNamespaces[i]; FLocItem LocItem(MetaDataValue); FManifestContext Context; Context.Key = FText::Format(Arguments.OutputKeys[i], PatternArguments).ToString(); Context.SourceLocation = FString::Printf(TEXT("From metadata for key %s of member %s in %s"), *Arguments.InputKeys[i], *Field->GetName(), *Field->GetFullGroupName(true)); GatherManifestHelper->AddSourceText(Namespace, LocItem, Context); } } } } // For enums, also gather for enum values. { UEnum* Enum = Cast<UEnum>(Field); if(Enum) { const int32 ValueCount = Enum->NumEnums(); for(int32 i = 0; i < ValueCount; ++i) { if( !Enum->HasMetaData(TEXT("DisplayName"), i) ) { Enum->SetMetaData(TEXT("DisplayName"), *FName::NameToDisplayString(Enum->GetEnumName(i), false), i); } for(int32 j = 0; j < Arguments.InputKeys.Num(); ++j) { FFormatNamedArguments PatternArguments; PatternArguments.Add( TEXT("FieldPath"), FText::FromString( Enum->GetFullGroupName(false) + TEXT(".") + Enum->GetEnumName(i) ) ); if( Enum->HasMetaData(*Arguments.InputKeys[j], i) ) { const FString& MetaDataValue = Enum->GetMetaData(*Arguments.InputKeys[j], i); if( !MetaDataValue.IsEmpty() ) { PatternArguments.Add( TEXT("MetaDataValue"), FText::FromString(MetaDataValue) ); const FString Namespace = Arguments.OutputNamespaces[j]; FLocItem LocItem(MetaDataValue); FManifestContext Context; Context.Key = FText::Format(Arguments.OutputKeys[j], PatternArguments).ToString(); Context.SourceLocation = FString::Printf(TEXT("From metadata for key %s of enum value %s of enum %s in %s"), *Arguments.InputKeys[j], *Enum->GetEnumName(i), *Enum->GetName(), *Enum->GetFullGroupName(true)); GatherManifestHelper->AddSourceText(Namespace, LocItem, Context); } } } } } } }