// Copyright Epic Games, Inc. All Rights Reserved. #if SOURCE_CONTROL_WITH_SLATE #include "SSourceControlControls.h" #include "ISourceControlProvider.h" #include "ISourceControlModule.h" #include "Misc/ConfigCacheIni.h" #include "RevisionControlStyle/RevisionControlStyle.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Layout/SSeparator.h" #define LOCTEXT_NAMESPACE "SSkeinSourceControlWidgets" bool SSourceControlControls::bRewoundMode = false; bool SSourceControlControls::bStaticDisableSyncLatestOverride = false; FOnClicked SSourceControlControls::OnSyncLatestClickedStaticOverride; FOnClicked SSourceControlControls::OnRestoreAsLatestClickedStaticOverride; /** * Construct this widget * * @param InArgs The declaration data for this widget */ void SSourceControlControls::Construct(const FArguments& InArgs) { OnSyncLatestClicked = InArgs._OnClickedSyncLatest; OnCheckInChangesClicked = InArgs._OnClickedCheckInChanges; OnRestoreAsLatestClicked = InArgs._OnClickedRestoreAsLatest; IsSyncLatestEnabled = InArgs._IsEnabledSyncLatest; IsCheckInChangesEnabled = InArgs._IsEnabledCheckInChanges; IsSyncLatestSeparatorEnabled = InArgs._IsEnabledSyncLatestSeparator; IsCheckInChangesSeparatorEnabled = InArgs._IsEnabledCheckInChangesSeparator; ChildSlot [ SNew(SHorizontalBox) + SHorizontalBox::Slot() // Check In Changes Button .VAlign(VAlign_Center) .Padding(FMargin(0.0f, 0.0f, 4.0f, 0.0f)) .AutoWidth() [ SNew(SButton) .ButtonStyle(&FAppStyle::Get().GetWidgetStyle("StatusBar.StatusBarButton")) .ToolTipText(this, &SSourceControlControls::GetSourceControlCheckInStatusTooltipText) .Visibility(this, &SSourceControlControls::GetSourceControlCheckInStatusVisibility) .IsEnabled(this, &SSourceControlControls::IsSourceControlCheckInEnabled) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Center) [ SNew(SImage) .Image(this, &SSourceControlControls::GetSourceControlCheckInStatusIcon) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(FMargin(5, 0, 0, 0)) [ SNew(STextBlock) .TextStyle(&FAppStyle::Get().GetWidgetStyle("NormalText")) .Text(this, &SSourceControlControls::GetSourceControlCheckInStatusText) ] ] .OnClicked(this, &SSourceControlControls::OnSourceControlCheckInChangesClicked) ] + SHorizontalBox::Slot() // Restore as Latest button .VAlign(VAlign_Center) .Padding(FMargin(0.0f, 0.0f, 4.0f, 0.0f)) .AutoWidth() [ SNew(SButton) .ButtonStyle(&FAppStyle::Get().GetWidgetStyle("StatusBar.StatusBarButton")) .ToolTipText(LOCTEXT("RestoreAsLatestTooltipText", "Restore this snapshot to be the latest version of the project for all team members.")) .Visibility(this, &SSourceControlControls::GetSourceControlRestoreAsLatestVisibility) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Center) [ SNew(SImage) .Image(FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.Promote")) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(FMargin(5, 0, 0, 0)) [ SNew(STextBlock) .TextStyle(&FAppStyle::Get().GetWidgetStyle("NormalText")) .Text(LOCTEXT("RestoreAsLatestButtonText", "Restore as Latest")) ] ] .OnClicked(this, &SSourceControlControls::OnSourceControlRestoreAsLatestClicked) ] +SHorizontalBox::Slot() // Check In Kebab Combo button .VAlign(VAlign_Center) .AutoWidth() [ SNew(SComboButton) .ContentPadding(FMargin(7.f, 0.f)) .ComboButtonStyle(&FAppStyle::Get().GetWidgetStyle("StatusBar.StatusBarEllipsisComboButton")) .MenuPlacement(MenuPlacement_AboveAnchor) .Visibility(this, &SSourceControlControls::GetSourceControlCheckInStatusVisibility) .OnGetMenuContent(InArgs._OnGenerateKebabMenu) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SSeparator) .Visibility(this, &SSourceControlControls::GetSourceControlCheckInSeparatorVisibility) .Thickness(1.0) .Orientation(EOrientation::Orient_Vertical) ] + SHorizontalBox::Slot() // Sync Latest Button .VAlign(VAlign_Center) .AutoWidth() [ SNew(SButton) .ButtonStyle(&FAppStyle::Get().GetWidgetStyle("StatusBar.StatusBarButton")) .ToolTipText(this, &SSourceControlControls::GetSourceControlSyncStatusTooltipText) .Visibility(this, &SSourceControlControls::GetSourceControlSyncStatusVisibility) .IsEnabled(this, &SSourceControlControls::IsSourceControlSyncEnabled) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Center) [ SNew(SImage) .Image(this, &SSourceControlControls::GetSourceControlSyncStatusIcon) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(FMargin(5, 0, 0, 0)) [ SNew(STextBlock) .TextStyle(&FAppStyle::Get().GetWidgetStyle("NormalText")) .Text(this, &SSourceControlControls::GetSourceControlSyncStatusText) ] ] .OnClicked(this, &SSourceControlControls::OnSourceControlSyncClicked) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SSeparator) .Visibility(this, &SSourceControlControls::GetSourceControlSyncSeparatorVisibility) .Thickness(1.0) .Orientation(EOrientation::Orient_Vertical) ] ]; CheckSourceControlStatus(); } void SSourceControlControls::CheckSourceControlStatus() { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); if (!SourceControlProviderChangedHandle.IsValid()) { SourceControlProviderChangedHandle = SourceControlModule.RegisterProviderChanged( FSourceControlProviderChanged::FDelegate::CreateSP(this, &SSourceControlControls::OnSourceControlProviderChanged) ); SourceControlStateChangedHandle = SourceControlModule.GetProvider().RegisterSourceControlStateChanged_Handle( FSourceControlStateChanged::FDelegate::CreateSP(this, &SSourceControlControls::OnSourceControlStateChanged) ); } } void SSourceControlControls::OnSourceControlProviderChanged(ISourceControlProvider& OldProvider, ISourceControlProvider& NewProvider) { if (SourceControlStateChangedHandle.IsValid()) { OldProvider.UnregisterSourceControlStateChanged_Handle(SourceControlStateChangedHandle); SourceControlStateChangedHandle.Reset(); } if (!IsEngineExitRequested()) { SourceControlStateChangedHandle = NewProvider.RegisterSourceControlStateChanged_Handle( FSourceControlStateChanged::FDelegate::CreateSP(this, &SSourceControlControls::OnSourceControlStateChanged) ); } } void SSourceControlControls::OnSourceControlStateChanged() { ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); TArray Conflicts = SourceControlProvider.GetCachedStateByPredicate( [](const FSourceControlStateRef& State) { return State->IsConflicted(); } ); NumConflictsRemaining = Conflicts.Num(); bConflictsRemaining = (NumConflictsRemaining > 0); } bool SSourceControlControls::AreConflictsRemaining() const { const bool bExiting = IsEngineExitRequested(); if (!bExiting) { return bConflictsRemaining; } return false; } int32 SSourceControlControls::GetNumConflictsRemaining() const { return NumConflictsRemaining; } /** Sync Status */ bool SSourceControlControls::IsAtLatestRevision() const { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); return SourceControlModule.IsEnabled() && SourceControlModule.GetProvider().IsAvailable() && SourceControlModule.GetProvider().IsAtLatestRevision().IsSet() && SourceControlModule.GetProvider().IsAtLatestRevision().GetValue(); } bool SSourceControlControls::IsSourceControlSyncEnabled() const { if (!HasSourceControlChangesToSync()) { return false; } return IsSyncLatestEnabled.Get(true) && !bStaticDisableSyncLatestOverride; } bool SSourceControlControls::HasSourceControlChangesToSync() const { return !IsAtLatestRevision(); } EVisibility SSourceControlControls::GetSourceControlSyncStatusVisibility() const { if (!GIsEditor) { // Always visible in the Slate Viewer return EVisibility::Visible; } bool bDisplaySourceControlSyncStatus = false; GConfig->GetBool(TEXT("SourceControlSettings"), TEXT("DisplaySourceControlSyncStatus"), bDisplaySourceControlSyncStatus, GEditorIni); if (bDisplaySourceControlSyncStatus) { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); if (SourceControlModule.IsEnabled() && SourceControlModule.GetProvider().IsAvailable() && SourceControlModule.GetProvider().IsAtLatestRevision().IsSet()) // Only providers that implement IsAtLatestRevision are supported. { return EVisibility::Visible; } } return EVisibility::Collapsed; } EVisibility SSourceControlControls::GetSourceControlSyncSeparatorVisibility() const { EVisibility StatusVisibility = GetSourceControlSyncStatusVisibility(); if (StatusVisibility != EVisibility::Visible) { return StatusVisibility; } return IsSyncLatestSeparatorEnabled.Get(true) ? EVisibility::Visible : EVisibility::Collapsed; } FText SSourceControlControls::GetSourceControlSyncStatusText() const { if (HasSourceControlChangesToSync()) { return LOCTEXT("SyncLatestButtonNotAtHeadText", "Sync Latest"); } return LOCTEXT("SyncLatestButtonAtHeadText", "At Latest"); } FText SSourceControlControls::GetSourceControlSyncStatusTooltipText() const { if (AreConflictsRemaining()) { return LOCTEXT("SyncLatestButtonNotAtHeadTooltipTextConflict", "Some of your local changes conflict with the latest snapshot of the project. Click here to review these conflicts."); } if (HasSourceControlChangesToSync()) { return LOCTEXT("SyncLatestButtonNotAtHeadTooltipText", "Sync to the latest Snapshot for this project"); } return LOCTEXT("SyncLatestButtonAtHeadTooltipText", "Currently at the latest Snapshot for this project"); } const FSlateBrush* SSourceControlControls::GetSourceControlSyncStatusIcon() const { static const FSlateBrush* ConflictBrush = FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.Conflicted"); static const FSlateBrush* AtHeadBrush = FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.AtLatestRevision"); static const FSlateBrush* NotAtHeadBrush = FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.NotAtLatestRevision"); if (AreConflictsRemaining()) { return ConflictBrush; } if (HasSourceControlChangesToSync()) { return NotAtHeadBrush; } return AtHeadBrush; } FReply SSourceControlControls::OnSourceControlSyncClicked() const { if (AreConflictsRemaining()) { if (IConsoleObject* CObj = IConsoleManager::Get().FindConsoleObject(TEXT("UnrealRevisionControl.FocusConflictResolution"))) { CObj->AsCommand()->Execute(/*Args=*/TArray(), /*InWorld=*/nullptr, *GLog); } } else if (HasSourceControlChangesToSync()) { if (OnSyncLatestClickedStaticOverride.IsBound()) { OnSyncLatestClickedStaticOverride.Execute(); } else if (OnSyncLatestClicked.IsBound()) { OnSyncLatestClicked.Execute(); } } return FReply::Handled(); } /** Check-in Status */ int SSourceControlControls::GetNumLocalChanges() const { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); if (SourceControlModule.IsEnabled() && SourceControlModule.GetProvider().IsAvailable() && SourceControlModule.GetProvider().GetNumLocalChanges().IsSet()) { return SourceControlModule.GetProvider().GetNumLocalChanges().GetValue(); } return 0; } bool SSourceControlControls::IsSourceControlCheckInEnabled() const { if (!HasSourceControlChangesToCheckIn()) { return false; } return IsCheckInChangesEnabled.Get(true); } bool SSourceControlControls::HasSourceControlChangesToCheckIn() const { return (GetNumLocalChanges() > 0); } EVisibility SSourceControlControls::GetSourceControlCheckInStatusVisibility() const { if (!GIsEditor) { // Always visible in the Slate Viewer return EVisibility::Visible; } if (bRewoundMode) { return EVisibility::Collapsed; } bool bDisplaySourceControlCheckInStatus = false; GConfig->GetBool(TEXT("SourceControlSettings"), TEXT("DisplaySourceControlCheckInStatus"), bDisplaySourceControlCheckInStatus, GEditorIni); if (bDisplaySourceControlCheckInStatus) { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); if (SourceControlModule.IsEnabled() && SourceControlModule.GetProvider().IsAvailable() && SourceControlModule.GetProvider().GetNumLocalChanges().IsSet()) // Only providers that implement GetNumLocalChanges are supported. { return EVisibility::Visible; } } return EVisibility::Collapsed; } EVisibility SSourceControlControls::GetSourceControlCheckInSeparatorVisibility() const { EVisibility StatusVisibility = GetSourceControlCheckInStatusVisibility(); if (StatusVisibility != EVisibility::Visible) { return StatusVisibility; } return IsCheckInChangesSeparatorEnabled.Get(true) ? EVisibility::Visible : EVisibility::Collapsed; } FText SSourceControlControls::GetSourceControlCheckInStatusText() const { if (HasSourceControlChangesToCheckIn()) { return LOCTEXT("CheckInButtonChangesText", "Check-in Changes"); } return LOCTEXT("CheckInButtonNoChangesText", "No Changes"); } FText SSourceControlControls::GetSourceControlCheckInStatusTooltipText() const { if (AreConflictsRemaining()) { return LOCTEXT("CheckInButtonChangesTooltipTextConflict", "Some of your local changes conflict with the latest snapshot of the project. Click here to review these conflicts."); } if (HasSourceControlChangesToCheckIn()) { return FText::Format(LOCTEXT("CheckInButtonChangesTooltipText", "Check-in {0} change(s) to this project"), GetNumLocalChanges()); } return LOCTEXT("CheckInButtonNoChangesTooltipText", "No Changes to check in for this project"); } const FSlateBrush* SSourceControlControls::GetSourceControlCheckInStatusIcon() const { static const FSlateBrush* ConflictBrush = FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.Conflicted"); static const FSlateBrush* NoLocalChangesBrush = FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.NoLocalChanges"); static const FSlateBrush* HasLocalChangesBrush = FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.HasLocalChanges"); if (AreConflictsRemaining()) { return ConflictBrush; } if (HasSourceControlChangesToCheckIn()) { return HasLocalChangesBrush; } return NoLocalChangesBrush; } FReply SSourceControlControls::OnSourceControlCheckInChangesClicked() const { if (AreConflictsRemaining()) { if (IConsoleObject* CObj = IConsoleManager::Get().FindConsoleObject(TEXT("UnrealRevisionControl.FocusConflictResolution"))) { CObj->AsCommand()->Execute(/*Args=*/TArray(), /*InWorld=*/nullptr, *GLog); } } else if (HasSourceControlChangesToCheckIn()) { if (OnCheckInChangesClicked.IsBound()) { OnCheckInChangesClicked.Execute(); } } return FReply::Handled(); } /** Restore as Latest */ EVisibility SSourceControlControls::GetSourceControlRestoreAsLatestVisibility() const { if (bRewoundMode) { return EVisibility::Visible; } return EVisibility::Collapsed; } FReply SSourceControlControls::OnSourceControlRestoreAsLatestClicked() const { if (OnRestoreAsLatestClickedStaticOverride.IsBound()) { OnRestoreAsLatestClickedStaticOverride.Execute(); } else if (OnRestoreAsLatestClicked.IsBound()) { OnRestoreAsLatestClicked.Execute(); } return FReply::Handled(); } #undef LOCTEXT_NAMESPACE #endif // SOURCE_CONTROL_WITH_SLATE