Files
UnrealEngineUWP/Engine/Source/Developer/SourceControl/Public/SourceControlFileStatusMonitor.h
patrick laflamme 068b6058ab Implemented the 'Unsaved' Editor status bar button that displays the number of unsaved assets (dirty packages) the user and potential warnings with respect to the source control those asset may have.
- Implemented the UnsavedAssetTrackers plugin and enabled it by default.
  - Added the 'Unsaved' button to the Editor taskbars to display the number of unsaved packages along with a warning icon if warning are associated with one of the unsaved files.
  - Updated the 'Save Content' package to display warnings if one of the packages has warnings.
  - Added a class to monitor and periodically update the source controlled status of a collection of files/packages.
  - Changed the default value of PromptForCheckoutOnAssetModification settings to False because the Unsaved button flow is complementary (and the popup was noisy for OFPA users).
  - Updated the changelist window to use the soure control file status monitor.

#jira UE-163734 - Implement the 'Unsaved' Editor Taskbar Button
#rb Patrick.Enfedaque
#preflight 633ae915b12b8af5fde80f7c

[CL 22322224 by patrick laflamme in ue5-main branch]
2022-10-03 20:38:09 -04:00

185 lines
8.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Containers/Ticker.h"
#include "Delegates/DelegateCombinations.h"
#include "ISourceControlOperation.h"
#include "ISourceControlProvider.h"
#include "ISourceControlState.h"
#include "Misc/Timespan.h"
#include "Templates/SharedPointer.h"
class ISourceControlState;
/**
* Periodically monitors the source controlled status of a collection of files and notifies the latest status, including
* potential warnings such as 'out of date', 'locked by other', etc. This service is meant to throttle and batch non-time
* sensitive FUpdateStatus requests to the source control provider because those requests are expensive and can add an
* undesired load on the provider server.
*/
class SOURCECONTROL_API FSourceControlFileStatusMonitor : public TSharedFromThis<FSourceControlFileStatusMonitor>
{
public:
/** Invoked when the status of a source controlled files is updated. The state can be null when the source control provider is changed. */
DECLARE_DELEGATE_TwoParams(FOnSourceControlFileStatus, const FString& /*Pathname*/, const ISourceControlState* /*State*/);
public:
FSourceControlFileStatusMonitor();
virtual ~FSourceControlFileStatusMonitor();
/**
* Starts monitoring the source control status of the specified file. If the status is already known (the file is already monitored
* by another party), the know status is returned right away, otherwise a request is sent to the source control provider to get the
* status. The request is subject to the probation period policy to allow batching requests. The file status is refreshed according to
* the refresh period policy.
*
* The caller must be sure to call StopMonitoringFile() when the files status is not desired anymore. It is ok to call StopMonitoringFile()
* within the callback OnSourceControlledFileStatus().
*
* @param Owner The unique Id of the caller, typically the caller memory address.
* @param Pathname The absolute path and filname of the file to monitor.
* @param OnSourceControlledFileStatus Delegate invoked whe the status of the file is updated.
*
* @see SetNewRequestProbationPeriod()
* @see SetUpdateStatusPeriod()
* @see SetSuspendMonitoringPolicy()
* @see GetStatusAge()
*/
void StartMonitoringFile(uintptr_t OwnerId, const FString& Pathname, FOnSourceControlFileStatus OnSourceControlledFileStatus);
void StartMonitoringFiles(uintptr_t OwnerId, const TArray<FString>& Pathnames, FOnSourceControlFileStatus OnSourceControlledFileStatus);
void StartMonitoringFiles(uintptr_t OwnerId, const TSet<FString>& Pathnames, FOnSourceControlFileStatus OnSourceControlledFileStatus);
/**
* Stops monitoring the source control status of the specified file. If the specified file is not monitored, the function
* returns successfully.
*
* @param OwnerId The unique Id of the caller, typically the caller memory address.
* @param Pathname The absolute path and filname of the file to stop monitoring.
*
* @note This can be called fron the FOnSourceControlFileStatus callback passed to StartMonitoringFile().
*/
void StopMonitoringFile(uintptr_t OwnerId, const FString& Pathname);
void StopMonitoringFiles(uintptr_t OwnerId, const TArray<FString>& Pathnames);
void StopMonitoringFiles(uintptr_t OwnerId, const TSet<FString>& Pathnames);
/**
* Stops monitoring all the files that were registered by the specified owner Id.
*/
void StopMonitoringFiles(uintptr_t OwnerId);
/**
* Returns the set of files being monitored by the specifed owner.
*/
TSet<FString> GetMonitoredFiles(uintptr_t OwnerId);
/**
* Returns the file status age, i.e. (now - last_status_update) if the file is
* monitored. If the initial status request hasn't been received yet, returns
* zero.
*/
TOptional<FTimespan> GetStatusAge(const FString& Pathname) const;
/**
* Set the times waited before a newly added file status request is sent to the source control provider. This is used batch several
* status for file added closed in time into a single server request, for example if StartMonitoringFile() is called in a loop or
* when the user scroll a long list of files for which the status needs to be updated. Setting a value of 0 remove the probation period.
* By default, the propbation period is 1 second.
*/
void SetNewRequestProbationPeriodPolicy(const FTimespan& InPropbationPeriod) { ProbationPeriodPolicy = InPropbationPeriod; }
FTimespan GetNewRequestProbationPeriodPolicy() const { return ProbationPeriodPolicy; }
/**
* Set the status refresh period. By default, the monitored files are refreshed every 5 minutes, providing the monitor
* is it suspended according to the suspend monitor policy. The refresh period has an impact on the load of the source control
* provider. A short period may overload the provider service.
*
* @see SetSuspendMonitoringPolicy
*/
void SetUpdateStatusPeriodPolicy(const FTimespan& InRefreshPeriod) { RefreshPeriodPolicy = InRefreshPeriod; }
FTimespan GetUpdateStatusPeriodPolicy() const { return RefreshPeriodPolicy; }
/**
* Sets the maximum number of files to batch into a single request. By default, a maximum of 100 files status
* is requested.
*/
void SetMaxFilePerRequestPolicy(int32 MaxNum) { MaxFileNumPerRequestPolicy = MaxNum; }
int32 GetMaxFilePerRequestPolicy() const { return MaxFileNumPerRequestPolicy; }
/**
* Sets a function invoked to detect if the monitor should suspend its activities, for example, if no user
* is currently interacting with the application, it may not worth checking the file status periodically.
* By default, the monitor will suspends if there are not Slate interaction within 5 minutes and resume
* at the next user interaction.
*/
void SetSuspendMonitoringPolicy(TFunction<bool()> IsMonitoringSuspended) { SuspendMonitoringPolicy = MoveTemp(IsMonitoringSuspended); }
private:
struct FSourceControlFileStatus
{
FSourceControlFileStatus(uintptr_t OriginalOwnerId, FOnSourceControlFileStatus InFileStatusUpdateDelegate)
{
OwnerDelegateMap.Emplace(OriginalOwnerId, MoveTemp(InFileStatusUpdateDelegate));
}
TSharedPtr<ISourceControlState> FileState;
double LastStatusCheckTimestampSecs = 0.0;
TMap<uintptr_t, FOnSourceControlFileStatus> OwnerDelegateMap;
};
private:
/** Invoked when the source control has updated the status for the requested files. */
void OnSourceControlStatusUpdate(const TSharedRef<ISourceControlOperation>& InOperation, ECommandResult::Type InType);
/** Invoked when the source control provider changes. */
void OnSourceControlProviderChanged(ISourceControlProvider& OldProvider, ISourceControlProvider& NewProvider);
/** Invoked by the main loop ticker to fire source control requests. */
bool Tick(float DeltaTime);
/** Returns the specified files status or null if not found. */
TSharedPtr<FSourceControlFileStatus> FindFileStatus(const FString& Pathname) const;
/** Whethers the monitor has an in-flight request with the source control provider. */
bool HasOngoingRequest() const { return RequestedStatusFiles.Num() > 0; }
private:
/** The files currently monitored. */
TMap<FString, TSharedPtr<FSourceControlFileStatus>> MonitoredFiles;
/** The list of files for which a request to the source control provider is currently in-flight/ongoing. */
TArray<FString> RequestedStatusFiles;
/** The newly added files probation period. Time left to batch several files into the same requests. */
FTimespan ProbationPeriodPolicy;
/** The status refresh period. Time left until we re-request a file status. */
FTimespan RefreshPeriodPolicy;
/** Invoked to detect wheter the monitoring is paused/suspended (no request to source control provider).*/
TFunction<bool()> SuspendMonitoringPolicy;
/** The maximum number of files allowed to be batched together for a request to the provider. */
int32 MaxFileNumPerRequestPolicy = 100;
/** Last time a request to the source control provider was issued. */
double LastSourceControlCheckSecs = 0.0;
/** Last time a file was assed to the list of monitored files. */
double LastAddedFileTimeSecs = 0.0;
/** The oldest status update time. */
double OldestFileStatusTimeSecs = 0.0;
/** Number of files newly added that are in probation. */
int32 NewAddedFileCount = 0;
/** Handle for the tick. */
FTSTicker::FDelegateHandle TickerHandle;
/** Handle of the registered source provider change delegate. */
FDelegateHandle SourceControlProviderChangedDelegateHandle;
};