Set Transform properties on Actor read only while not in custom Editor Mode

Hello,

We have designed a custom FEdMode that the user can Enter/Exit like the similar modes Foliage, Landscape, etc…

In that mode, some special actors of ours are allowed to be moved/rotated/scaled.

Outside that mode, we would like to prevent the users to alter the transform of these actors (only base Transform) by making it read only. We don’t want the user to alter the actor in any way, and then revert the change immediately.

After a few hours of research, I found a solution but it is not ideal and I wanted to know if there were some hooks I didn’t find, or a just simpler way to do it.

What I tried:

  • GEditor->OnBeginObjectMovement() : by binding it with a method that instantly unselect the actor, it works, but only for the gyzmo. Changing values in the details panel still modifies the transform
  • Any delegates for actor movement or property changed : I didn’t find one that happens before it changes, or there are some, but it is impossible to prevent the change (they only carry informative value)
  • DetailsCustomization for the specific actors : could have worked, but Transform component has already its own customization (FComponentTransformDetails) with its own logic. It is private and not exported so not really customizable/extendable/overridable.
  • CanEditChange() on these actors : doesn’t seem to apply to Transform either

So far my solution works but is not ideal because it still modifies the Engine and requires the actor to inherit an additional interface. I would like to prevent as much as possible to alter the Engine, and also the Actor class. Here is what I’ve done:

Created a class where a static delegate is defined in UnrealEd plugin:

DECLARE_DELEGATE_RetVal_OneParam(bool, FCanEditTransformDelegate, const USceneComponent*)

class UNREALED_API FEditorTransformLock
{
public :
       static FCanEditTransformDelegate& Get()
       {
          return GDelegateInstance;
       }
private : 
    static FCanEditTransformDelegate GDelegateInstance;
};

//.cpp
FCanEditTransformDelegate FEditorTransformLock::GDelegateInstance;

in FComponentTransformDetails I have added a modification to the GetIsEnabled() method:

bool FComponentTransformDetails::GetIsEnabled() const
{
// new
    for (const TWeakObjectPtr<UObject>& Obj : SelectedObjects)
    {
       const USceneComponent* Component = Cast<USceneComponent>(Obj.Get());
       if (!Component)
       {
          if (const AActor* Actor = Cast<AActor>(Obj.Get()))
          {
             Component = Actor->GetRootComponent();
          }
       }
       if (Component && FEditorTransformLock::Get().IsBound() && !FEditorTransformLock::Get().Execute(Component))
       {
          return false;
       }
    }
//!new
    
    return bIsEnabledCache;
}

In one of my editor module startup, I have bound the delegate

FEditorTransformLock::Get().BindStatic(&FMyEditorModeModule::CanEditTransformStatic);

and written the method associated:

bool FMyEditorModeModule::CanEditTransformStatic(const USceneComponent* Component)
{
    if (!Component)
    {
       return true;
    }
    if (Component != Component->GetOwner()->GetRootComponent())
    {
       return true;
    }
    
    if (const AActor* Owner = Component->GetOwner())
    {
       if (Owner->GetClass()->ImplementsInterface(UTransformLockedInterface::StaticClass()))
       {
          const ITransformLockedInterface* Lockable = Cast<ITransformLockedInterface>(Owner);
          return !Lockable->IsRootTransformedLocked();
       }
    }
    
    return true;
}

Then I created a UTransformLockedInterface for my actor to use that only defines an abstract method bool IsRootTransformedLocked():

and then added it to the actor parent list. And the override:

bool AMyBaseActor::IsRootTransformedLocked() const
{
    if (IsSpecialActor())
    {
       if (!GLevelEditorModeTools().IsModeActive("EM_MyCustomEditorMode"))
       {
          return true;
       }
    }
    
    return false;
}

Steps to Reproduce
/

Hi Stephen,

Are you primarily concerned with the details panel only (ie. modification of transform via the controls exposed in `FComponentTransformDetails`)? Presumably the gizmo is another area that would need to disable transform modifications in the modes you’ve described.

Have you considered overriding `AActor::IsLockLocation()` on your special actors that internally will only return false if in the current editor mode? This _should_ notify the engine in relevant places that the transform is not to be modified. The challenge you might hit though is how to access the current ed mode from this callback - but before we go down that route, let me know if this is something you’ve already considered. Happy to help with some ideas on how to make that happen.

Logan