// 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" FNumConflicts SSourceControlControls::NumConflictsRemaining; FNumConflicts SSourceControlControls::NumConflictsUpcoming; FIsEnabled SSourceControlControls::IsSyncLatestEnabled; FIsEnabled SSourceControlControls::IsCheckInChangesEnabled; FIsEnabled SSourceControlControls::IsRestoreAsLatestEnabled; FIsVisible SSourceControlControls::IsSyncLatestVisible; FIsVisible SSourceControlControls::IsCheckInChangesVisible; FIsVisible SSourceControlControls::IsRestoreAsLatestVisible; FOnClicked SSourceControlControls::OnSyncLatestClicked; FOnClicked SSourceControlControls::OnCheckInChangesClicked; FOnClicked SSourceControlControls::OnRestoreAsLatestClicked; static bool DisplaySyncStatus() { 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 true; } } return false; } static bool DisplayCheckInStatus() { 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 true; } } return false; } static bool DisplayRestoreAsLatestStatus() { // The 'Restore as Latest' button is a replacement for the 'Check in Changes' button. return DisplayCheckInStatus(); } /** * Construct this widget * * @param InArgs The declaration data for this widget */ void SSourceControlControls::Construct(const FArguments& InArgs) { IsMiddleSeparatorEnabled = InArgs._IsEnabledMiddleSeparator; IsRightSeparatorEnabled = InArgs._IsEnabledRightSeparator; 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_Static(&SSourceControlControls::GetSourceControlCheckInStatusToolTipText) .Visibility_Static(&SSourceControlControls::GetSourceControlCheckInStatusVisibility) .IsEnabled_Static(&SSourceControlControls::IsSourceControlCheckInEnabled) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Center) [ SNew(SImage) .Image_Static(&SSourceControlControls::GetSourceControlCheckInStatusIcon) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(FMargin(5, 0, 0, 0)) [ SNew(STextBlock) .TextStyle(&FAppStyle::Get().GetWidgetStyle("NormalText")) .Text_Static(&SSourceControlControls::GetSourceControlCheckInStatusText) ] ] .OnClicked_Static(&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_Static(&SSourceControlControls::GetSourceControlRestoreAsLatestToolTipText) .Visibility_Static(&SSourceControlControls::GetSourceControlRestoreAsLatestVisibility) .IsEnabled_Static(&SSourceControlControls::IsSourceControlRestoreAsLatestEnabled) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Center) [ SNew(SImage) .Image_Static(&SSourceControlControls::GetSourceControlRestoreAsLatestStatusIcon) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(FMargin(5, 0, 0, 0)) [ SNew(STextBlock) .TextStyle(&FAppStyle::Get().GetWidgetStyle("NormalText")) .Text_Static(&SSourceControlControls::GetSourceControlRestoreAsLatestText) ] ] .OnClicked_Static(&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_Static(&SSourceControlControls::GetSourceControlCheckInStatusVisibility) .OnGetMenuContent(InArgs._OnGenerateKebabMenu) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SSeparator) .Visibility(this, &SSourceControlControls::GetSourceControlMiddleSeparatorVisibility) .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_Static(&SSourceControlControls::GetSourceControlSyncStatusToolTipText) .Visibility_Static(&SSourceControlControls::GetSourceControlSyncStatusVisibility) .IsEnabled_Static(&SSourceControlControls::IsSourceControlSyncEnabled) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Center) [ SNew(SImage) .Image_Static(&SSourceControlControls::GetSourceControlSyncStatusIcon) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(FMargin(5, 0, 0, 0)) [ SNew(STextBlock) .TextStyle(&FAppStyle::Get().GetWidgetStyle("NormalText")) .Text_Static(&SSourceControlControls::GetSourceControlSyncStatusText) ] ] .OnClicked_Static(&SSourceControlControls::OnSourceControlSyncClicked) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SSeparator) .Visibility(this, &SSourceControlControls::GetSourceControlRightSeparatorVisibility) .Thickness(1.0) .Orientation(EOrientation::Orient_Vertical) ] ]; } int32 SSourceControlControls::GetNumConflictsRemaining() { return NumConflictsRemaining.IsBound() ? NumConflictsRemaining.Execute() : 0; } int32 SSourceControlControls::GetNumConflictsUpcoming() { return NumConflictsUpcoming.IsBound() ? NumConflictsUpcoming.Execute() : 0; } /** Sync Status */ bool SSourceControlControls::IsAtLatestRevision() { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); return SourceControlModule.IsEnabled() && SourceControlModule.GetProvider().IsAvailable() && SourceControlModule.GetProvider().IsAtLatestRevision().IsSet() && SourceControlModule.GetProvider().IsAtLatestRevision().GetValue(); } bool SSourceControlControls::IsSourceControlSyncEnabled() { if (!HasSourceControlChangesToSync()) { return false; } if (IsSyncLatestEnabled.IsBound()) { return IsSyncLatestEnabled.Execute(); } return false; } bool SSourceControlControls::HasSourceControlChangesToSync() { return !IsAtLatestRevision(); } EVisibility SSourceControlControls::GetSourceControlSyncStatusVisibility() { if (!GIsEditor) { // Always visible in the Slate Viewer return EVisibility::Visible; } bool bVisibleSourceControlSyncStatus = false; if (IsSyncLatestVisible.IsBound()) { bVisibleSourceControlSyncStatus = IsSyncLatestVisible.Execute(); } bool bDisplaySourceControlSyncStatus = DisplaySyncStatus(); if (bVisibleSourceControlSyncStatus && bDisplaySourceControlSyncStatus) { return EVisibility::Visible; } return EVisibility::Collapsed; } EVisibility SSourceControlControls::GetSourceControlRightSeparatorVisibility() const { EVisibility StatusVisibility = GetSourceControlSyncStatusVisibility(); if (StatusVisibility != EVisibility::Visible) { return StatusVisibility; } return IsRightSeparatorEnabled.Get(true) ? EVisibility::Visible : EVisibility::Collapsed; } FText SSourceControlControls::GetSourceControlSyncStatusText() { if (HasSourceControlChangesToSync()) { return LOCTEXT("SyncLatestButtonNotAtHeadText", "Sync Latest"); } return LOCTEXT("SyncLatestButtonAtHeadText", "At Latest"); } FText SSourceControlControls::GetSourceControlSyncStatusToolTipText() { if (GetNumConflictsRemaining() > 0) { return LOCTEXT("SyncLatestButtonNotAtHeadTooltipTextConflict", "Some of your local changes conflict with the latest snapshot of the project. Click here to review these conflicts."); } if (GetNumConflictsUpcoming() > 0) { return LOCTEXT("SyncLatestButtonNotAtHeadTooltipTextConflictUpcoming", "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() { 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 (GetNumConflictsRemaining() > 0) { return ConflictBrush; } if (GetNumConflictsUpcoming() > 0) { return ConflictBrush; } if (HasSourceControlChangesToSync()) { return NotAtHeadBrush; } return AtHeadBrush; } FReply SSourceControlControls::OnSourceControlSyncClicked() { if (GetNumConflictsRemaining() > 0) { if (IConsoleObject* CObj = IConsoleManager::Get().FindConsoleObject(TEXT("UnrealRevisionControl.FocusConflictResolution"))) { CObj->AsCommand()->Execute(/*Args=*/TArray(), /*InWorld=*/nullptr, *GLog); } } else if (HasSourceControlChangesToSync()) { if (OnSyncLatestClicked.IsBound()) { OnSyncLatestClicked.Execute(); } } return FReply::Handled(); } /** Check-in Status */ int SSourceControlControls::GetNumLocalChanges() { ISourceControlModule& SourceControlModule = ISourceControlModule::Get(); if (SourceControlModule.IsEnabled() && SourceControlModule.GetProvider().IsAvailable() && SourceControlModule.GetProvider().GetNumLocalChanges().IsSet()) { return SourceControlModule.GetProvider().GetNumLocalChanges().GetValue(); } return 0; } bool SSourceControlControls::IsSourceControlCheckInEnabled() { if (!HasSourceControlChangesToCheckIn()) { return false; } if (IsCheckInChangesEnabled.IsBound()) { return IsCheckInChangesEnabled.Execute(); } return false; } bool SSourceControlControls::HasSourceControlChangesToCheckIn() { return (GetNumLocalChanges() > 0); } EVisibility SSourceControlControls::GetSourceControlCheckInStatusVisibility() { if (!GIsEditor) { // Always visible in the Slate Viewer return EVisibility::Visible; } bool bVisibleSourceControlCheckInStatus = false; if (IsCheckInChangesVisible.IsBound()) { bVisibleSourceControlCheckInStatus = IsCheckInChangesVisible.Execute(); } bool bDisplaySourceControlCheckInStatus = DisplayCheckInStatus(); if (bVisibleSourceControlCheckInStatus && bDisplaySourceControlCheckInStatus) { return EVisibility::Visible; } return EVisibility::Collapsed; } EVisibility SSourceControlControls::GetSourceControlMiddleSeparatorVisibility() const { EVisibility StatusVisibility = GetSourceControlCheckInStatusVisibility(); if (StatusVisibility != EVisibility::Visible) { return StatusVisibility; } return IsMiddleSeparatorEnabled.Get(true) ? EVisibility::Visible : EVisibility::Collapsed; } FText SSourceControlControls::GetSourceControlCheckInStatusText() { if (HasSourceControlChangesToCheckIn()) { return LOCTEXT("CheckInButtonChangesText", "Check-in Changes"); } return LOCTEXT("CheckInButtonNoChangesText", "No Changes"); } FText SSourceControlControls::GetSourceControlCheckInStatusToolTipText() { if (GetNumConflictsRemaining() > 0) { return LOCTEXT("CheckInButtonChangesTooltipTextConflict", "Some of your local changes conflict with the latest snapshot of the project. Click here to review these conflicts."); } if (GetNumConflictsUpcoming() > 0) { return LOCTEXT("CheckInButtonChangesTooltipTextConflictUpcoming", "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() { 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 (GetNumConflictsRemaining() > 0) { return ConflictBrush; } if (GetNumConflictsUpcoming() > 0) { return ConflictBrush; } if (HasSourceControlChangesToCheckIn()) { return HasLocalChangesBrush; } return NoLocalChangesBrush; } FReply SSourceControlControls::OnSourceControlCheckInChangesClicked() { if (GetNumConflictsRemaining() > 0) { 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 */ bool SSourceControlControls::IsSourceControlRestoreAsLatestEnabled() { if (IsRestoreAsLatestEnabled.IsBound()) { return IsRestoreAsLatestEnabled.Execute(); } return false; } EVisibility SSourceControlControls::GetSourceControlRestoreAsLatestVisibility() { bool bVisibleSourceControlRestoreAsLatestStatus = false; if (IsRestoreAsLatestVisible.IsBound()) { bVisibleSourceControlRestoreAsLatestStatus = IsRestoreAsLatestVisible.Execute(); } bool bDisplaySourceControlRestoreAsLatestStatus = DisplayRestoreAsLatestStatus(); if (bVisibleSourceControlRestoreAsLatestStatus && bDisplaySourceControlRestoreAsLatestStatus) { return EVisibility::Visible; } return EVisibility::Collapsed; } FText SSourceControlControls::GetSourceControlRestoreAsLatestText() { return LOCTEXT("RestoreAsLatestButtonText", "Restore as Latest"); } FText SSourceControlControls::GetSourceControlRestoreAsLatestToolTipText() { return LOCTEXT("RestoreAsLatestTooltipText", "Restore this snapshot to be the latest version of the project for all team members."); } const FSlateBrush* SSourceControlControls::GetSourceControlRestoreAsLatestStatusIcon() { return FRevisionControlStyleManager::Get().GetBrush("RevisionControl.StatusBar.Promote"); } FReply SSourceControlControls::OnSourceControlRestoreAsLatestClicked() { if (OnRestoreAsLatestClicked.IsBound()) { OnRestoreAsLatestClicked.Execute(); } return FReply::Handled(); } #undef LOCTEXT_NAMESPACE #endif // SOURCE_CONTROL_WITH_SLATE