// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Async/AsyncWork.h" #include "CoreMinimal.h" #include #include "ISourceControlProvider.h" #include "Misc/EnumClassFlags.h" #include "Misc/TextFilter.h" #include "SSourceControlCommon.h" #include "Stats/Stats.h" #include "UObject/ObjectSaveContext.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/Views/SListView.h" #include "Widgets/Views/STreeView.h" class FChangelistGroupTreeItem; class IPlugin; class SExpandableArea; class SExpandableChangelistArea; class SWidgetSwitcher; class SSearchBox; class USourceControlSettings; /** * Displays the user source control change lists. */ class SSourceControlChangelistsWidget : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SSourceControlChangelistsWidget) {} SLATE_END_ARGS() /** Constructs the widget */ void Construct(const FArguments& InArgs); virtual ~SSourceControlChangelistsWidget(); /** Set selected files */ void SetSelectedFiles(const TArray& Filenames); private: enum class ERefreshFlags { SourceControlChangelists = 1 << 0, UnsavedAssets = 1 << 1, UncontrolledChangelists = 1 << 2, All = SourceControlChangelists | UnsavedAssets | UncontrolledChangelists, }; FRIEND_ENUM_CLASS_FLAGS(ERefreshFlags) /** Queries files timestamp asynchronously. */ class FAsyncTimestampUpdater { public: void DoWork(); bool CanAbandon() { return true; } void Abandon() { bAbandon = true; } TStatId GetStatId() const { return TStatId(); } static const TCHAR* Name() { return TEXT("SourceControlChangelistsTimestampUpdater"); } public: std::atomic bAbandon; TSet RequestedFileTimestamps; TMap QueriedFileTimestamps; }; private: TSharedRef MakeToolBar(); TSharedRef> CreateChangelistTreeView(TArray>& ItemSources); TSharedRef> CreateChangelistFilesView(); TSharedRef> CreateUnsavedAssetsFilesView(); TSharedRef OnGenerateRow(FChangelistTreeItemPtr InTreeItem, const TSharedRef& OwnerTable); void OnGetChangelistChildren(FChangelistTreeItemPtr InParent, TArray& OutChildren); void OnFileViewHiddenColumnsListChanged(); EColumnSortPriority::Type GetColumnSortPriority(const FName ColumnId) const; EColumnSortMode::Type GetColumnSortMode(const FName ColumnId) const; void OnColumnSortModeChanged(const EColumnSortPriority::Type SortPriority, const FName& ColumnId, const EColumnSortMode::Type InSortMode); bool IsFileViewSortedByFileStatusIcon() const; bool IsFileViewSortedByFileShelveIcon() const; bool IsFileViewSortedByLastModifiedTimestamp() const; void SortFileView(); void OnChangelistSearchTextChanged(const FText& InFilterText); void OnUncontrolledChangelistSearchTextChanged(const FText& InFilterText); void OnFileSearchTextChanged(const FText& InFilterText); void PopulateItemSearchStrings(const IChangelistTreeItem& Item, TArray& OutStrings); void OnUnsavedAssetChanged(const FString& Filepath); FReply OnFilesDragged(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent); FReply OnUnsavedAssetsDragged(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent); void RequestChangelistsRefresh(); void RequestFileStatusRefresh(const IChangelistTreeItem& Changelist); void RequestFileStatusRefresh(TSet&& Pathnames); void OnRefreshUI(ERefreshFlags RefreshFlags); void OnRefreshUnsavedAssetsWidgets(int64 CurrUpdateNum, const TFunction&)>& AddItemToFileView); void OnRefreshSourceControlWidgets(int64 CurreUpdateNum, const TFunction&)>& AddItemToFileView); void OnRefreshUncontrolledChangelistWidgets(int64 CurreUpdateNum, const TFunction&)>& AddItemToFileView); void ClearChangelistsTree(); TSharedPtr OnOpenContextMenu(); /** Returns the currently selected changelist state ptr or null in invalid cases */ FSourceControlChangelistStatePtr GetCurrentChangelistState(); FUncontrolledChangelistStatePtr GetCurrentUncontrolledChangelistState() const; FSourceControlChangelistPtr GetCurrentChangelist(); TOptional GetCurrentUncontrolledChangelist() const; FSourceControlChangelistStatePtr GetChangelistStateFromSelection(); FSourceControlChangelistPtr GetChangelistFromSelection(); /** Returns list of currently selected files */ TArray GetSelectedFiles(); bool HasFilesSelected() const; /** Returns list of currently selected shelved files */ TArray GetSelectedShelvedFiles(); bool HasShelvedFilesSelected() const; /** * Splits selected files between Controlled and Uncontrolled files. * @param OutControlledFiles Selected source controlled files will be added to this array. * @param OutUncontrolledFiles Selected uncontrolled files will be added to this array. */ void GetSelectedFiles(TArray& OutControlledFiles, TArray& OutUncontrolledFiles); /** * Splits selected files between Controlled and Uncontrolled files. * @param OutControlledFileStates Selected source controlled file states will be added to this array. * @param OutUncontrolledFileStates Selected uncontrolled file states will be added to this array. */ void GetSelectedFileStates(TArray& OutControlledFileStates, TArray& OutUncontrolledFileStates); /** Intercept Enter and Delete key presses to Submit or Delete the selected changelist (if conditions are met) */ virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; /** Changelist operations */ void OnNewChangelist(); void OnDeleteChangelist(); bool CanDeleteChangelist(); bool CanDeleteChangelist(FText* OutFailureMessage); void OnEditChangelist(); void OnSubmitChangelist(); bool CanSubmitChangelist(); bool CanSubmitChangelist(FText* OutFailureMessage); void OnValidateChangelist(); bool CanValidateChangelist(); /** Uncontrolled Changelist operations */ void OnNewUncontrolledChangelist(); void OnEditUncontrolledChangelist(); bool CanEditUncontrolledChangelist(); void OnDeleteUncontrolledChangelist(); bool CanDeleteUncontrolledChangelist(); /** Changelist & File operations */ void OnRevertUnchanged(); bool CanRevertUnchanged(); void OnRevert(); bool CanRevert(); void OnShelve(); /** Changelist & shelved files operations */ void OnUnshelve(); void OnDeleteShelvedFiles(); /** Files operations */ TValueOrError TryMoveFiles(); void OnShowHistory(); void OnDiffAgainstDepot(); bool CanDiffAgainstDepot(); /** Shelved files operations */ void OnDiffAgainstWorkspace(); bool CanDiffAgainstWorkspace(); /** Uncontrolled changelist module callback. */ void OnUncontrolledChangelistStateChanged(); /** Source control callbacks */ void OnSourceControlProviderChanged(ISourceControlProvider& OldProvider, ISourceControlProvider& NewProvider); void OnSourceControlStateChanged(); void OnItemDoubleClicked(TSharedPtr Item); void OnChangelistSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); void OnChangelistsStatusUpdated(const TSharedRef& InOperation, ECommandResult::Type InType); void OnStartSourceControlOperation(TSharedRef Operation, const FText& Message); void OnEndSourceControlOperation(const TSharedRef& Operation, ECommandResult::Type InType); // Wrapper functions on top of the source control ones to display slow tasks for synchronous operations or toast notifications for async ones. void Execute(const FText& Message, const TSharedRef& InOperation, TSharedPtr InChangelist, const TArray& InFiles, EConcurrency::Type InConcurrency, const FSourceControlOperationComplete& InOperationCompleteDelegate); void Execute(const FText& Message, const TSharedRef& InOperation, TSharedPtr InChangelist, const EConcurrency::Type InConcurrency, const FSourceControlOperationComplete& InOperationCompleteDelegate); void Execute(const FText& Message, const TSharedRef& InOperation, const EConcurrency::Type InConcurrency, const FSourceControlOperationComplete& InOperationCompleteDelegate); void Execute(const FText& Message, const TSharedRef& InOperation, const TArray& InFiles, EConcurrency::Type InConcurrency, const FSourceControlOperationComplete& InOperationCompleteDelegate); void ExecuteUncontrolledChangelistOperation(const FText& Message, const TFunction& UncontrolledChangelistTask); virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; /** Executes an operation to updates the changelist description of the provided changelist with a new description. */ void EditChangelistDescription(const FText& InNewChangelistDescription, const FSourceControlChangelistStatePtr& InChangelistState); /** Invoked when a package is saved, to refresh the last saved timestamp. */ void OnPackageSaved(const FString& PackageFilename, UPackage* Package, FObjectPostSaveContext ObjectSaveContext); void OnPluginEdited(IPlugin& InPlugin); private: TSharedPtr UnsavedAssetsExpandableArea; TSharedPtr> UnsavedAssetsTreeView; TArray> UnsavedAssetsTreeNode; TSharedPtr ChangelistExpandableArea; TSharedPtr> ChangelistTreeView; TArray> ChangelistTreeNodes; TSharedPtr UncontrolledChangelistExpandableArea; TSharedPtr> UncontrolledChangelistTreeView; TArray> UncontrolledChangelistTreeNodes; TSharedPtr> FileListView; TArray> FileListNodes; TArray FileViewHiddenColumnsList; TSharedPtr> UnsavedAssetsFileListView; TSharedPtr FileListViewSwitcher; SListView& GetActiveFileListView() const; TMap, TSharedPtr> SourceControlItemCache; TMap, TSharedPtr> UncontrolledChangelistItemCache; TMap> OfflineFileItemCache; TUniquePtr> TimestampUpdateTask; TSet OutdatedTimestampFiles; /** Source control state changed delegate handle */ FDelegateHandle SourceControlStateChangedDelegateHandle; int64 UpdateRequestNum = 0; bool bInitialRefreshDone = false; bool bShouldRefresh = false; bool bShouldRefreshVersePaths = false; bool bShowingContentVersePath = false; bool bSourceControlAvailable = false; bool bUpdateMonitoredFileStatusList = false; /** Files to select after refresh */ TArray FilesToSelect; FName PrimarySortedColumn; FName SecondarySortedColumn; EColumnSortMode::Type PrimarySortMode = EColumnSortMode::Ascending; EColumnSortMode::Type SecondarySortMode = EColumnSortMode::None; TSharedPtr> ChangelistTextFilter; TSharedPtr> UncontrolledChangelistTextFilter; TSharedPtr> FileTextFilter; TSharedPtr FileSearchBox; void StartRefreshStatus(); void TickRefreshStatus(double InDeltaTime); void EndRefreshStatus(); FText RefreshStatus; bool bIsRefreshing = false; double RefreshStatusStartSecs; float ChangelistAreaSize = 0.3; // [0.0f, 1.0f] float FileAreaSize = 0.7; };