Files
UnrealEngine/Engine/Source/Runtime/Slate/Private/Widgets/Docking/SDockTab.cpp
2025-05-18 13:04:45 +08:00

1079 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/SToolTip.h"
#include "Framework/Docking/SDockingTabStack.h"
#include "Framework/Application/SlateApplication.h"
#include "Widgets/Layout/SSpacer.h"
#include "Widgets/Input/SButton.h"
#include "Framework/Docking/SDockingTabWell.h"
#include "Framework/Docking/TabCommands.h"
#include "Framework/Docking/SDockingArea.h"
#include "Widgets/Colors/SComplexGradient.h"
#include "Widgets/Layout/SBox.h"
#include "Misc/NamePermissionList.h"
namespace SDockTabDefs
{
/** Tab flash rate. Flashes per second */
static const float TabFlashFrequency = 2.0f;
/** Tab flash duration. Seconds*/
static const float TabFlashDuration = 1.0f;
/** The amount of time to pass before we switch tabs due to drag event */
static const float DragTimerActivate = 0.75f;
/** Overrides the tab padding if color overlays are enabled */
static const float TabVerticalPaddingScaleOverride = 0.85f;
}
static float TotalDraggedDistance = 0;
FReply SDockTab::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( !HasMouseCapture() )
{
if ( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton )
{
TotalDraggedDistance = 0;
ActivateInParent(ETabActivationCause::UserClickedOnTab);
return FReply::Handled().DetectDrag( SharedThis(this), EKeys::LeftMouseButton );
}
else if ( MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton )
{
return FReply::Handled().CaptureMouse(SharedThis(this));
}
else if ( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton )
{
// We clicked on the tab, so it should be active
ActivateInParent(ETabActivationCause::UserClickedOnTab);
// ... but let the tab well bring up the context menu or whatever it wants to do with the right click.
return FReply::Unhandled();
}
}
return FReply::Unhandled();
}
FReply SDockTab::OnMouseButtonDoubleClick( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton )
{
return FReply::Handled().CaptureMouse(SharedThis(this));
}
else
{
return FReply::Unhandled();
}
}
FReply SDockTab::OnDragDetected( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
// Need to remember where within a tab we grabbed
const FVector2D TabGrabOffset = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() );
const FVector2D TabSize = MyGeometry.GetLocalSize();
const FVector2D TabGrabOffsetFraction = FVector2D(
FMath::Clamp(TabGrabOffset.X / TabSize.X, 0.0f, 1.0f),
FMath::Clamp(TabGrabOffset.Y / TabSize.Y, 0.0f, 1.0f) );
if (TSharedPtr<SDockingTabWell> PinnedParent = ParentPtr.Pin())
{
//See if we can drag tabs contain in this manager
TSharedPtr<FTabManager> TabManager = GetTabManagerPtr();
if (TabManager.IsValid() && TabManager->GetCanDoDragOperation())
{
return PinnedParent->StartDraggingTab(SharedThis(this), TabGrabOffsetFraction, MouseEvent);
}
else
{
return FReply::Handled();
}
}
else
{
// Should never get here (but sometimes does, under unknown circumstances)
// TODO: investigate how the parent pointer can become invalid
return FReply::Unhandled();
}
}
FReply SDockTab::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( HasMouseCapture() )
{
if ( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton )
{
return FReply::Handled().ReleaseMouseCapture();
}
else if ( MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton )
{
if ( MyGeometry.IsUnderLocation( MouseEvent.GetScreenSpacePosition() ) )
{
RequestCloseTab();
}
return FReply::Handled().ReleaseMouseCapture();
}
}
return FReply::Unhandled();
}
void SDockTab::OnDragEnter( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
// Register to activate the tab after a delay
if ( !DragDropTimerHandle.IsValid() )
{
DragDropTimerHandle = RegisterActiveTimer( SDockTabDefs::DragTimerActivate, FWidgetActiveTimerDelegate::CreateSP( this, &SDockTab::TriggerActivateTab ) );
}
UpdateTabStyle();
SBorder::OnDragEnter( MyGeometry, DragDropEvent );
}
void SDockTab::OnDragLeave( const FDragDropEvent& DragDropEvent )
{
// Unregister the activation timer if it hasn't fired yet
if ( DragDropTimerHandle.IsValid() )
{
UnRegisterActiveTimer( DragDropTimerHandle.Pin().ToSharedRef() );
}
SBorder::OnDragLeave( DragDropEvent );
}
FReply SDockTab::OnDrop( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
// Unregister the activation timer if it hasn't fired yet
if ( DragDropTimerHandle.IsValid() )
{
UnRegisterActiveTimer( DragDropTimerHandle.Pin().ToSharedRef() );
}
return SBorder::OnDrop( MyGeometry, DragDropEvent );
}
FReply SDockTab::OnTouchStarted( const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent )
{
if (!this->HasMouseCapture())
{
// We tapped on the tab, so it should be active
ActivateInParent(ETabActivationCause::UserClickedOnTab);
return FReply::Handled().CaptureMouse(SharedThis(this));
}
return FReply::Unhandled();
}
FReply SDockTab::OnTouchEnded( const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent )
{
if (this->HasMouseCapture())
{
return FReply::Handled().ReleaseMouseCapture();
}
return FReply::Unhandled();
}
void SDockTab::SetContent( TSharedRef<SWidget> InContent )
{
Content = InContent;
if (ParentPtr.IsValid())
{
// This is critical to do, otherwise the content might remain if currently active even if expected to be destroyed
ParentPtr.Pin()->RefreshParentContent();
}
}
void SDockTab::SetLeftContent( TSharedRef<SWidget> InContent )
{
this->TabWellContentLeft = InContent;
if (ParentPtr.IsValid())
{
// This is critical to do, otherwise the content might remain if currently active even if expected to be destroyed
ParentPtr.Pin()->RefreshParentContent();
}
}
void SDockTab::SetRightContent( TSharedRef<SWidget> InContent )
{
this->TabWellContentRight = InContent;
if (ParentPtr.IsValid())
{
// This is critical to do, otherwise the content might remain if currently active even if expected to be destroyed
ParentPtr.Pin()->RefreshParentContent();
}
}
void SDockTab::SetTitleBarRightContent(TSharedRef<SWidget> InContent)
{
this->TitleBarContentRight = InContent;
if (ParentPtr.IsValid())
{
// This is critical to do, otherwise the content might remain if currently active even if expected to be destroyed
ParentPtr.Pin()->RefreshParentContent();
}
}
bool SDockTab::IsActive() const
{
return FGlobalTabmanager::Get()->GetActiveTab() == SharedThis(this);
}
bool SDockTab::IsForeground() const
{
return ParentPtr.IsValid() ? (ParentPtr.Pin()->GetForegroundTab() == SharedThis(this)) : true;
}
FSlateColor SDockTab::GetForegroundColor() const
{
auto LocalForegroundColorAttribute = GetForegroundColorAttribute();
if (LocalForegroundColorAttribute.IsBound() || LocalForegroundColorAttribute.Get() != FSlateColor::UseStyle())
{
return LocalForegroundColorAttribute.Get();
}
else if (IsActive())
{
return GetCurrentStyle().ActiveForegroundColor;
}
else if (IsHovered())
{
return GetCurrentStyle().HoveredForegroundColor;
}
else if (IsForeground())
{
return GetCurrentStyle().ForegroundForegroundColor;
}
else
{
return GetCurrentStyle().NormalForegroundColor;
}
}
ETabRole SDockTab::GetTabRole() const
{
return TabRole;
}
ETabRole SDockTab::GetVisualTabRole() const
{
// If the tab role is NomadTab but is being visualized as a major tab
if (this->TabRole == ETabRole::NomadTab)
{
bool bNomadMajorStyle = false;
if (DraggedOverDockingArea.IsValid())
{
bNomadMajorStyle = DraggedOverDockingArea->GetTabManager() == FGlobalTabmanager::Get();
}
else if (GetParent().IsValid() && GetParent()->GetDockArea().IsValid())
{
bNomadMajorStyle = GetParent()->GetDockArea()->GetTabManager() == FGlobalTabmanager::Get();
}
else
{
// We are dragging or have no parent, but we are not dragging over anything, assume major
bNomadMajorStyle = true;
}
if (bNomadMajorStyle)
{
return ETabRole::MajorTab;
}
}
return GetTabRole();
}
const FSlateBrush* SDockTab::GetContentAreaBrush() const
{
return &GetCurrentStyle().ContentAreaBrush;
}
const FSlateBrush* SDockTab::GetTabWellBrush() const
{
return &GetCurrentStyle().TabWellBrush;
}
TSharedRef<SWidget> SDockTab::GetContent()
{
return Content;
}
TSharedRef<SWidget> SDockTab::GetLeftContent()
{
return TabWellContentLeft;
}
TSharedRef<SWidget> SDockTab::GetRightContent()
{
return TabWellContentRight;
}
TSharedRef<SWidget> SDockTab::GetTitleBarRightContent()
{
return TitleBarContentRight;
}
FMargin SDockTab::GetContentPadding() const
{
return ContentAreaPadding.Get();
}
void SDockTab::SetLayoutIdentifier( const FTabId& TabId )
{
LayoutIdentifier = TabId;
}
const FTabId SDockTab::GetLayoutIdentifier() const
{
return LayoutIdentifier;
}
void SDockTab::SetParent(TSharedPtr<SDockingTabWell> Parent)
{
ParentPtr = Parent;
OnParentSet();
}
TSharedPtr<SDockingTabWell> SDockTab::GetParent() const
{
return ParentPtr.IsValid() ? ParentPtr.Pin() : TSharedPtr<SDockingTabWell>();
}
TSharedPtr<SDockingTabStack> SDockTab::GetParentDockTabStack() const
{
TSharedPtr<SDockingTabWell> ParentTabWell = GetParent();
if( ParentTabWell.IsValid() )
{
return ParentTabWell->GetParentDockTabStack();
}
return NULL;
}
void SDockTab::RemoveTabFromParent()
{
OnTabClosed.ExecuteIfBound(SharedThis(this));
if (ParentPtr.IsValid())
{
ParentPtr.Pin()->RemoveAndDestroyTab(SharedThis(this), SDockingNode::TabRemoval_Closed);
}
}
TSharedPtr<SDockingArea> SDockTab::GetDockArea() const
{
return ParentPtr.IsValid() ? ParentPtr.Pin()->GetDockArea() : TSharedPtr<SDockingArea>();
}
TSharedPtr<SWindow> SDockTab::GetParentWindow() const
{
TSharedPtr<SDockingArea> DockingAreaPtr = this->GetDockArea();
return DockingAreaPtr.IsValid() ? DockingAreaPtr->GetParentWindow() : TSharedPtr<SWindow>();
}
SDockTab::SDockTab()
: Content(SNew(SSpacer))
, TabWellContentLeft(SNullWidget::NullWidget)
, TabWellContentRight(SNullWidget::NullWidget)
, TitleBarContentRight(SNullWidget::NullWidget)
, LayoutIdentifier(NAME_None)
, TabRole(ETabRole::PanelTab)
, ParentPtr()
, TabLabel(NSLOCTEXT("DockTab", "DefaultTabTitle", "UNNAMED"))
, TabLabelSuffix(FText::GetEmpty())
, OnTabClosed()
, OnTabActivated()
, OnCanCloseTab()
, ContentAreaPadding( 2.f )
, bShouldAutosize(false)
, TabColorScale(FLinearColor::Transparent)
, LastActivationTime(0.0)
{
}
void SDockTab::ActivateInParent(ETabActivationCause InActivationCause)
{
TSharedPtr<SDockingTabWell> ParentTabWell = ParentPtr.Pin();
if (ParentTabWell.IsValid())
{
ParentTabWell->BringTabToFront(SharedThis(this));
}
OnTabActivated.ExecuteIfBound(SharedThis(this), InActivationCause);
}
void SDockTab::SetTabManager( const TSharedPtr<FTabManager>& InTabManager)
{
{
TSharedPtr<FTabManager> Previous = MyTabManager.Pin();
if (Previous && Previous != InTabManager)
{
Previous->GetTabPermissionList()->OnFilterChanged().RemoveAll(this);
}
}
MyTabManager = InTabManager;
if (InTabManager)
{
if (!InTabManager->GetTabPermissionList()->OnFilterChanged().IsBoundToObject(this))
{
InTabManager->GetTabPermissionList()->OnFilterChanged().AddSP(this, &SDockTab::CheckTabAllowed);
}
}
}
void SDockTab::SetOnPersistVisualState( const FOnPersistVisualState& Handler )
{
OnPersistVisualState = Handler;
}
void SDockTab::SetOnExtendContextMenu( const FExtendContextMenu& Handler )
{
OnExtendContextMenu = Handler;
}
void SDockTab::SetCanCloseTab( const FCanCloseTab& InOnTabClosing )
{
OnCanCloseTab = InOnTabClosing;
}
void SDockTab::SetOnTabClosed( const FOnTabClosedCallback& InDelegate )
{
OnTabClosed = InDelegate;
}
void SDockTab::SetOnTabActivated( const FOnTabActivatedCallback& InDelegate )
{
OnTabActivated = InDelegate;
}
void SDockTab::SetOnTabRenamed(const FOnTabRenamed& InDelegate)
{
OnTabRenamed = InDelegate;
}
void SDockTab::SetOnTabDrawerOpened(const FSimpleDelegate InDelegate)
{
OnTabDrawerOpenedEvent = InDelegate;
}
void SDockTab::SetOnTabDrawerClosed(const FSimpleDelegate InDelegate)
{
OnTabDrawerClosedEvent = InDelegate;
}
void SDockTab::SetOnTabRelocated(const FSimpleDelegate InDelegate)
{
OnTabRelocated = InDelegate;
}
void SDockTab::SetOnTabDraggedOverDockArea(const FSimpleDelegate InDelegate)
{
OnTabDraggedOverDockArea = InDelegate;
}
TSharedRef<FTabManager> SDockTab::GetTabManager() const
{
return MyTabManager.Pin().ToSharedRef();
}
TSharedPtr<FTabManager> SDockTab::GetTabManagerPtr() const
{
return MyTabManager.Pin();
}
void SDockTab::DrawAttention()
{
TSharedPtr<FTabManager> TabManager = GetTabManagerPtr();
if (TabManager.IsValid())
{
TabManager->DrawAttention(SharedThis(this));
}
}
void SDockTab::ProvideDefaultLabel( const FText& InDefaultLabel )
{
const bool UserProvidedLabel = TabLabel.IsBound() || !TabLabel.Get().IsEmpty();
if ( !UserProvidedLabel )
{
TabLabel = InDefaultLabel;
}
}
void SDockTab::ProvideDefaultIcon( const FSlateBrush* InDefaultIcon )
{
const bool UserProvidedIcon = TabIcon.IsBound() || (TabIcon.Get() && TabIcon.Get() != FStyleDefaults::GetNoBrush());
if( !UserProvidedIcon )
{
TabIcon = InDefaultIcon;
}
}
void SDockTab::PlaySpawnAnim()
{
SpawnAnimCurve.Play( this->AsShared() );
}
void SDockTab::FlashTab()
{
FlashTabCurve = FCurveSequence(0, SDockTabDefs::TabFlashDuration, ECurveEaseFunction::Linear);
FlashTabCurve.Play( this->AsShared() );
}
float SDockTab::GetFlashValue() const
{
if(FlashTabCurve.IsPlaying())
{
const float Lerp = FlashTabCurve.GetLerp();
const float SinRateMultiplier = 2.0f * PI * SDockTabDefs::TabFlashDuration * SDockTabDefs::TabFlashFrequency;
const float SinTerm = 0.5f * (FMath::Sin(Lerp * SinRateMultiplier) + 1.0f);
const float FadeTerm = 1.0f - Lerp;
return SinTerm * FadeTerm;
}
return 0.0f;
}
void SDockTab::SetDraggedOverDockArea( const TSharedPtr<SDockingArea>& Area )
{
DraggedOverDockingArea = Area;
OnTabDraggedOverDockArea.ExecuteIfBound();
}
bool SDockTab::HasSiblingTab(const FTabId& SiblingTabId, const bool TreatIndexNoneAsWildcard) const
{
TSharedPtr<SDockingTabStack> ParentTabStack = GetParentDockTabStack();
return (ParentTabStack.IsValid()) ? ParentTabStack->HasTab(FTabMatcher(SiblingTabId, static_cast<ETabState::Type>(ETabState::ClosedTab | ETabState::OpenedTab | ETabState::SidebarTab), TreatIndexNoneAsWildcard)) : false;
}
void SDockTab::Construct( const FArguments& InArgs )
{
SpawnAnimCurve = FCurveSequence(0, 0.15f);
SpawnAnimCurve.JumpToEnd();
// We are just holding on to the content via a referece; not actually presenting it.
this->Content = InArgs._Content.Widget;
this->TabWellContentLeft = InArgs._TabWellContentLeft.Widget;
this->TabWellContentRight = InArgs._TabWellContentRight.Widget;
this->TabRole = InArgs._TabRole;
this->OnTabClosed = InArgs._OnTabClosed;
this->OnTabActivated = InArgs._OnTabActivated;
this->OnCanCloseTab = InArgs._OnCanCloseTab;
this->bCanEverClose = InArgs._CanEverClose;
this->OnPersistVisualState = InArgs._OnPersistVisualState;
this->OnExtendContextMenu = InArgs._OnExtendContextMenu;
this->TabLabel = InArgs._Label;
this->TabLabelSuffix = InArgs._LabelSuffix;
this->bShouldAutosize = InArgs._ShouldAutosize;
this->TabColorScale = InArgs._TabColorScale;
this->IconColor = InArgs._IconColor;
OnTabDrawerClosedEvent = InArgs._OnTabDrawerClosed;
OnTabRelocated = InArgs._OnTabRelocated;
OnTabDraggedOverDockArea = InArgs._OnTabDraggedOverDockArea;
MajorTabStyle = &FAppStyle::Get().GetWidgetStyle<FDockTabStyle>("Docking.MajorTab");
GenericTabStyle = &FAppStyle::Get().GetWidgetStyle<FDockTabStyle>("Docking.Tab");
ContentAreaPadding = InArgs._ContentPadding;
const FButtonStyle* const CloseButtonStyle = &GetCurrentStyle().CloseButtonStyle;
const FTextBlockStyle* const TabTextStyle = &GetCurrentStyle().TabTextStyle;
// This makes a gradient that displays whether or not a viewport is active
static FLinearColor ActiveBorderColor = FAppStyle::Get().GetSlateColor("Docking.Tab.ActiveTabIndicatorColor").GetSpecifiedColor();
static FLinearColor ActiveBorderColorTransparent = FLinearColor(ActiveBorderColor.R, ActiveBorderColor.G, ActiveBorderColor.B, 0.0f);
static TArray<FLinearColor> GradientStops{ ActiveBorderColorTransparent, ActiveBorderColor, ActiveBorderColorTransparent };
SBorder::Construct( SBorder::FArguments()
.BorderImage( FStyleDefaults::GetNoBrush() )
.VAlign(VAlign_Bottom)
.Padding( 0.0f )
.ForegroundColor(InArgs._ForegroundColor)
[
SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SImage)
.Image( this, &SDockTab::GetImageBrush )
]
// Overlay for active tab indication.
+ SOverlay::Slot()
.VAlign(VAlign_Top)
.HAlign(HAlign_Fill)
[
SNew(SComplexGradient)
.Visibility(this, &SDockTab::GetActiveTabIndicatorVisibility)
.DesiredSizeOverride(FVector2D(1.0f, 1.0f))
.GradientColors(GradientStops)
.Orientation(EOrientation::Orient_Vertical)
]
// Overlay for flashing a tab for attention
+ SOverlay::Slot()
[
SNew(SBorder)
// Don't allow flasher tab overlay to absorb mouse clicks
.Visibility( EVisibility::HitTestInvisible )
.Padding( this, &SDockTab::GetTabPadding )
.BorderImage( this, &SDockTab::GetFlashOverlayImageBrush )
.BorderBackgroundColor( this, &SDockTab::GetFlashColor )
]
+ SOverlay::Slot()
.Padding(TAttribute<FMargin>::Create(TAttribute<FMargin>::FGetter::CreateSP(this, &SDockTab::GetTabPadding)))
.VAlign(VAlign_Center)
.HAlign(HAlign_Fill)
[
SNew(SHorizontalBox)
.Visibility(EVisibility::Visible)
.ToolTip(InArgs._ToolTip)
.ToolTipText(InArgs._ToolTipText.IsSet() ? InArgs._ToolTipText : TAttribute<FText>(this, &SDockTab::GetTabLabel))
// Tab Icon
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(0, 0, 5, 0)
[
SNew(SBorder)
// Don't allow active tab overlay to absorb mouse clicks
.Padding(this, &SDockTab::GetTabIconBorderPadding)
.Visibility(EVisibility::HitTestInvisible)
// Overlay for color-coded tab effect
.BorderImage(this, &SDockTab::GetColorOverlayImageBrush)
.BorderBackgroundColor(this, &SDockTab::GetTabColor)
[
SAssignNew(IconWidget, SImage)
.ColorAndOpacity(this, &SDockTab::GetIconColor)
.Image(this, &SDockTab::GetTabIcon)
.DesiredSizeOverride(this, &SDockTab::GetTabIconSize)
]
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.Padding(0.0f, 1.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
// Label sub HBox
SNew(SHorizontalBox)
.ToolTip(InArgs._ToolTip)
// Tab Label
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.Padding(0.0f, 1.0f)
.VAlign(VAlign_Center)
[
SAssignNew(LabelWidget, STextBlock)
.TextStyle(&GetCurrentStyle().TabTextStyle)
.Text(this, &SDockTab::GetTabLabel)
.OverflowPolicy(InArgs._LabelOverflowPolicy)
]
// Tab Label Suffix
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 1.0f)
.VAlign(VAlign_Center)
[
SAssignNew(LabelSuffix, STextBlock)
.TextStyle(&GetCurrentStyle().TabTextStyle)
.Text(this, &SDockTab::GetTabLabelSuffix)
]
]
// @todo toolkit major: Could inject inline content here into tab for standalone asset editing dropdown/dirty state, etc.
+ SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
[
SNew(SButton)
.ButtonStyle( CloseButtonStyle )
.OnClicked( this, &SDockTab::OnCloseButtonClicked )
.ContentPadding(FMargin(0.0, 1.5, 0.0, 0.0))
.ToolTipText(this, &SDockTab::GetCloseButtonToolTipText)
.Visibility(this, &SDockTab::HandleIsCloseButtonVisible)
[
SNew(SSpacer)
.Size( CloseButtonStyle->Normal.ImageSize )
]
]
]
]
);
}
EActiveTimerReturnType SDockTab::TriggerActivateTab( double InCurrentTime, float InDeltaTime )
{
ActivateInParent( ETabActivationCause::UserClickedOnTab );
return EActiveTimerReturnType::Stop;
}
EActiveTimerReturnType SDockTab::OnHandleUpdateStyle(double InCurrentTime, float InDeltaTime)
{
if (GetParent().IsValid() && GetParent()->GetDockArea().IsValid())
{
UpdateTabStyle();
return EActiveTimerReturnType::Stop;
}
return EActiveTimerReturnType::Continue;
}
void SDockTab::OnParentSet()
{
// Update the style once the parent is set as it could change the way things look for certian tab types.
if (!UpdateStyleTimerHandle.IsValid())
{
UpdateStyleTimerHandle = RegisterActiveTimer(0, FWidgetActiveTimerDelegate::CreateSP(this, &SDockTab::OnHandleUpdateStyle));
}
}
void SDockTab::UpdateTabStyle()
{
const FTextBlockStyle& TabTextStyle = GetCurrentStyle().TabTextStyle;
LabelSuffix->SetTextStyle(&TabTextStyle);
LabelWidget->SetTextStyle(&TabTextStyle);
}
void SDockTab::OnTabDrawerOpened()
{
OnTabDrawerOpenedEvent.ExecuteIfBound();
}
void SDockTab::OnTabDrawerClosed()
{
OnTabDrawerClosedEvent.ExecuteIfBound();
}
void SDockTab::NotifyTabRelocated()
{
OnTabRelocated.ExecuteIfBound();
}
void SDockTab::CheckTabAllowed()
{
if (!LayoutIdentifier.TabType.IsNone())
{
TSharedPtr<FTabManager> Manager = MyTabManager.Pin();
if (Manager)
{
if (!Manager->GetTabPermissionList()->PassesFilter(LayoutIdentifier.TabType))
{
UE_LOG(LogSlate, Verbose, TEXT("Closing tab because type does not pass filter: %s"), *LayoutIdentifier.TabType.ToString());
RequestCloseTab();
}
}
}
}
const FDockTabStyle& SDockTab::GetCurrentStyle() const
{
if ( GetVisualTabRole() == ETabRole::MajorTab )
{
return *MajorTabStyle;
}
return *GenericTabStyle;
}
const FSlateBrush* SDockTab::GetImageBrush() const
{
const FDockTabStyle& CurrentStyle = GetCurrentStyle();
// Pick the right brush based on whether the tab is active or hovered.
if ( this->IsForeground() )
{
return &CurrentStyle.ForegroundBrush;
}
else if ( this->IsHovered() )
{
return &CurrentStyle.HoveredBrush;
}
return &CurrentStyle.NormalBrush;
}
FMargin SDockTab::GetTabPadding() const
{
FMargin NewPadding = GetCurrentStyle().TabPadding;
NewPadding.Top *= SDockTabDefs::TabVerticalPaddingScaleOverride;
NewPadding.Bottom *= SDockTabDefs::TabVerticalPaddingScaleOverride;
return NewPadding;
}
FMargin SDockTab::GetTabIconBorderPadding() const
{
return FMargin(GetCurrentStyle().IconBorderPadding);
}
const FSlateBrush* SDockTab::GetColorOverlayImageBrush() const
{
if (this->TabColorScale.Get().A > 0.0f)
{
return &GetCurrentStyle().ColorOverlayIconBrush;
}
return FStyleDefaults::GetNoBrush();
}
EVisibility SDockTab::GetActiveTabIndicatorVisibility() const
{
return IsActive() && GetVisualTabRole() != ETabRole::MajorTab ? EVisibility::HitTestInvisible : EVisibility::Collapsed;
}
FSlateColor SDockTab::GetIconColor() const
{
if (!IconColor.IsSet())
{
return FSlateColor::UseForeground();
}
if (this->IsForeground() || this->IsHovered())
{
return IconColor.Get();
}
else // Dim to 30% if not active
{
return IconColor.Get().CopyWithNewOpacity(0.7);
}
}
FSlateColor SDockTab::GetTabColor() const
{
return TabColorScale.Get();
}
const FSlateBrush* SDockTab::GetFlashOverlayImageBrush() const
{
if (FlashTabCurve.IsPlaying())
{
// Flashing is really just applying a color overlay, so we can re-use the color overlay brush and apply our flash tint to it
return &GetCurrentStyle().ColorOverlayTabBrush;
}
return FStyleDefaults::GetNoBrush();
}
void SDockTab::ExtendContextMenu(FMenuBuilder& MenuBuilder)
{
OnExtendContextMenu.ExecuteIfBound(MenuBuilder);
}
FSlateColor SDockTab::GetFlashColor() const
{
if ( GetCurrentStyle().FlashColor.IsColorSpecified() )
{
FLinearColor Color = GetCurrentStyle().FlashColor.GetSpecifiedColor();
Color.A = GetFlashValue();
return FSlateColor(Color);
}
return FLinearColor::White;
}
float SDockTab::GetOverlapWidth() const
{
return GetCurrentStyle().OverlapWidth;
}
FText SDockTab::GetTabLabel() const
{
return TabLabel.Get();
}
void SDockTab::SetLabel( const TAttribute<FText>& InTabLabel )
{
TabLabel = InTabLabel;
OnTabRenamed.ExecuteIfBound(SharedThis(this));
}
FText SDockTab::GetTabLabelSuffix() const
{
return TabLabelSuffix.Get();
}
void SDockTab::SetTabLabelSuffix(const TAttribute<FText>& InTabLabelSuffix)
{
TabLabelSuffix = InTabLabelSuffix;
}
void SDockTab::SetTabLabelOverflowPolicy(ETextOverflowPolicy InOverflowPolicy) const
{
if (LabelWidget.IsValid())
{
LabelWidget->SetOverflowPolicy(InOverflowPolicy);
}
}
const FSlateBrush* SDockTab::GetTabIcon() const
{
return TabIcon.Get();
}
void SDockTab::SetTabToolTipWidget(TSharedPtr<SToolTip> InTabToolTipWidget)
{
IconWidget->SetToolTip(InTabToolTipWidget);
LabelWidget->SetToolTip(InTabToolTipWidget);
}
void SDockTab::SetTabIcon( const TAttribute<const FSlateBrush*> InTabIcon )
{
TabIcon = InTabIcon;
}
bool SDockTab::ShouldAutosize() const
{
return bShouldAutosize;
}
void SDockTab::SetShouldAutosize(const bool bNewShouldAutosize)
{
bShouldAutosize = bNewShouldAutosize;
}
FReply SDockTab::OnCloseButtonClicked()
{
RequestCloseTab();
return FReply::Handled();
}
FText SDockTab::GetCloseButtonToolTipText() const
{
TSharedPtr<FUICommandInfo> CloseCommand =
GetVisualTabRole() == ETabRole::MajorTab ? FTabCommands::Get().CloseMajorTab : FTabCommands::Get().CloseMinorTab;
FFormatNamedArguments Arguments;
Arguments.Add(TEXT("Label"), NSLOCTEXT("DockTab", "CloseButtonToolTipLabel", "Close Tab"));
FText InputText = CloseCommand->GetInputText();
if (InputText.IsEmptyOrWhitespace())
{
Arguments.Add(TEXT("InputText"), InputText);
}
else
{
Arguments.Add(TEXT("InputText"), FText::Format(NSLOCTEXT("DockTab", "CloseButtonInputText", "({0})"), InputText));
}
return FText::Format(NSLOCTEXT("DockTab", "CloseButtonToolTipFormat", "{Label} {InputText}"), Arguments);
}
EVisibility SDockTab::HandleIsCloseButtonVisible() const
{
TSharedPtr<FTabManager> Manager = MyTabManager.Pin();
const bool bIsVisibled = IsHovered() || IsForeground();
const bool bIsTabCloseable = Manager == nullptr || Manager->IsTabCloseable(SharedThis(this));
return bCanEverClose && bIsVisibled && bIsTabCloseable ? EVisibility::Visible : EVisibility::Hidden;
}
TOptional<FVector2D> SDockTab::GetTabIconSize() const
{
return FVector2D(GetCurrentStyle().IconSize);
}
bool SDockTab::CanCloseTab() const
{
// Check the below conditions separately and early out to prevent the OnCanCloseTab delegate
// from being called if we already know we cannot close the tab.
if (!bCanEverClose)
{
return false;
}
if (TSharedPtr<FTabManager> Manager = MyTabManager.Pin();
Manager && !Manager->IsTabCloseable(SharedThis(this)))
{
return false;
}
if (OnCanCloseTab.IsBound() && !OnCanCloseTab.Execute())
{
return false;
}
return true;
}
bool SDockTab::RequestCloseTab()
{
this->PersistVisualState();
// The tab can be closed if the delegate is not bound or if the delegate call indicates we cannot close it
const bool bCanCloseTabNow = CanCloseTab();
if (bCanCloseTabNow)
{
if (GetParentDockTabStack() &&
GetParentDockTabStack()->GetDockArea() &&
GetParentDockTabStack()->GetDockArea()->IsTabInSidebar(SharedThis(this)))
{
OnTabClosed.ExecuteIfBound(SharedThis(this));
GetParentDockTabStack()->GetDockArea()->RemoveTabFromSidebar(SharedThis(this));
}
else
{
RemoveTabFromParent();
}
}
return bCanCloseTabNow;
}
void SDockTab::PersistVisualState()
{
OnPersistVisualState.ExecuteIfBound();
}
UE::Slate::FDeprecateVector2DResult SDockTab::GetAnimatedScale() const
{
static FVector2f FullyOpen = FVector2f::UnitVector;
static FVector2f FullyClosed = FVector2f(1.0f, 0.0f);
return FMath::Lerp(FullyClosed, FullyOpen, SpawnAnimCurve.GetLerp());
}
void SDockTab::UpdateActivationTime()
{
if (FSlateApplication::IsInitialized())
{
LastActivationTime = FSlateApplication::Get().GetCurrentTime();
}
}
void SDockTab::SetParentDockTabStackTabWellHidden(bool bIsTabWellHidden)
{
if ( const TSharedPtr<SDockingTabStack> ParentStack = GetParentDockTabStack() )
{
if ( bIsTabWellHidden )
{
if ( ParentStack->CanHideTabWell() )
{
ParentStack->SetTabWellHidden(true);
}
}
else
{
ParentStack->SetTabWellHidden(false);
}
}
}