Files
UnrealEngine/Engine/Plugins/Runtime/MeshModelingToolset/Source/ModelingComponents/Public/InteractiveToolActivity.h
2025-05-18 13:04:45 +08:00

169 lines
5.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "InteractionMechanic.h"
#include "InteractiveTool.h" //EToolShutdownType
#include "InteractiveToolActivity.generated.h"
class IToolsContextRenderAPI;
enum class EToolActivityStartResult
{
Running,
FailedStart,
// It's not clear whether we want this option. It's certainly the case that depending
// on settings, an activity may want to end immediately, or perhaps we may want to
// put single-action subtools on the same system. But the activity can just end on the
// next tick, and if we allow a third start option, the tool may need to be aware of
// it as a possibility, plus we have to decide whether or not NotifyActivitySelfEnded
// applies... Seems messy to have it, so let's keep it out until we really want it.
//ImmediatelyCompleted,
};
enum class EToolActivityEndResult
{
Completed,
Cancelled,
ErrorDuringEnd,
};
/**
* A tool activity is a sort of "sub-tool" used to break apart functionality in tools that
* provide support for different multi-interaction subtasks. It is meant to limit the sprawl
* of switch statements in the base tool, to allow subtasks to be designed similar to how a
* tool might be designed, and to ease extendability.
*
* An activity has the following expectations:
* - Setup() is called in host tool setup and Shutdown() is called in host tool Shutdown()
* - Start() is called to start the activity (such as when user clicks a button).
* - If the activity returns a result of EStartResult::ActivityRunning on Start(), it will expect
* Render() and Tick() calls from the host until either (a)- the host calls End() on the activity,
* or (b)- the activity reaches a stopping point itself and calls NotifyActivitySelfEnded() on the
* host. The activity should not require Render() and Tick() if it is not running.
*
* Compared to a UInteractionMechanic, a tool activity:
* - Expects that it is the main consumer of input, i.e. takes over the tool. Mechanics, by contrast,
* are currently often used together with other mechanics, or to support main tool functionality.
* - Should not require the hosting tool to have specific knowledge about it or be heavily involved.
* Mechanics, by contrast, currently often require tools to use various mechanic-specific getters/setters
* during the tool.
*
* Passing data back and forth can be done either by letting a tool activity use a specific
* context object that the tool can prep in the context store, or by requiring the host to
* implement specific interfaces (that the activity can check for in Setup()).
*/
UCLASS(MinimalAPI)
class UInteractiveToolActivity : public UInteractionMechanic
{
GENERATED_BODY()
public:
// Note about the below: we didn't really need to repeat Setup(), Tick(), and Render() here
// since these are staying the same as UInteractionMechanic and get inherited. But it is
// helpful to have the entire API that we need to implement described in one place
virtual ~UInteractiveToolActivity() {}
/**
* Should be called during a tool's Setup(). Does not start the activity.
*/
virtual void Setup(UInteractiveTool* ParentToolIn) override
{
Super::Setup(ParentToolIn);
}
/**
* Should be called during a tool's Shutdown()
*/
virtual void Shutdown(EToolShutdownType ShutdownType)
{
Super::Shutdown();
}
/**
* Check whether a Start() call will result in a success.
*/
virtual bool CanStart() const { return false; }
/**
* Attempt to start the activity.
*/
virtual EToolActivityStartResult Start() { return EToolActivityStartResult::FailedStart; }
/**
* Check whether the activity is running (though the tool can just check the result of
* Start() itself).
*/
virtual bool IsRunning() const
{
// We could keep a boolean here in the base class and update it in the base class Start() and End()
// functions so that derived classes don't have to remember to implement this. But it is probably better
// to have the inconvenience of having to implement IsRunning in the derived class than to force
// derived classes to remember to call base Start() and End() or risk giving an incorrect result here...
return ensure(false);
}
/**
* Similar to a tool, this determines whether the activity prefers an accept/cancel option to be
* displayed (if true) vs a "complete".
*/
virtual bool HasAccept() const { return false; };
/**
* If true, calling End with EToolShutdownType::Accept will result in a valid completion of
* the activity. It's up to the tool whether to respect this or not, since End() should still
* end the activity.
*/
virtual bool CanAccept() const { return false; };
/**
* Force an end to the activity.
*/
virtual EToolActivityEndResult End(EToolShutdownType) { return EToolActivityEndResult::ErrorDuringEnd; };
/**
* If the activity is running, should be called from the tool's OnTick()
*/
virtual void Tick(float DeltaTime) override {};
/**
* If the activity is running, should be called from the tool's Render()
*/
virtual void Render(IToolsContextRenderAPI* RenderAPI) override {};
protected:
private:
// We don't want to use this overload of Shutdown because an activity may need to know
// the ShutdownType if it is still running when Shutdown is called (to End properly).
virtual void Shutdown() override final
{
ensure(false);
Shutdown(EToolShutdownType::Cancel);
}
};
UINTERFACE(MinimalAPI)
class UToolActivityHost : public UInterface
{
GENERATED_BODY()
};
class IToolActivityHost
{
GENERATED_BODY()
public:
/**
* Notify the tool that the activity has ended.
*/
virtual void NotifyActivitySelfEnded(UInteractiveToolActivity* Activity) = 0;
};