// Copyright Epic Games, Inc. All Rights Reserved. #include "DetailGroup.h" #include "Widgets/Input/SButton.h" #include "PropertyHandleImpl.h" #include "DetailPropertyRow.h" #include "Widgets/Images/SImage.h" #define LOCTEXT_NAMESPACE "FDetailGroup" FDetailGroup::FDetailGroup( const FName InGroupName, TSharedRef InParentCategory, const FText& InLocalizedDisplayName, const bool bInStartExpanded ) : ParentCategory( InParentCategory ) , LocalizedDisplayName( InLocalizedDisplayName ) , GroupName( InGroupName ) , bStartExpanded( bInStartExpanded ) , bResetEnabled( false ) , DisplayMode( EDetailGroupDisplayMode::Group ) , PasteFromTextDelegate(MakeShared()) { } FDetailWidgetRow& FDetailGroup::HeaderRow() { HeaderCustomization = MakeShareable( new FDetailLayoutCustomization ); HeaderCustomization->WidgetDecl = MakeShareable( new FDetailWidgetRow ); return *HeaderCustomization->WidgetDecl; } IDetailPropertyRow& FDetailGroup::HeaderProperty( TSharedRef PropertyHandle ) { check( PropertyHandle->IsValidHandle() ); PropertyHandle->MarkHiddenByCustomization(); HeaderCustomization = MakeShareable( new FDetailLayoutCustomization ); HeaderCustomization->PropertyRow = MakeShareable( new FDetailPropertyRow( StaticCastSharedRef( PropertyHandle )->GetPropertyNode(), ParentCategory.Pin().ToSharedRef() ) ); return *HeaderCustomization->PropertyRow; } FDetailWidgetRow& FDetailGroup::AddWidgetRow() { FDetailLayoutCustomization NewCustomization; NewCustomization.WidgetDecl = MakeShareable( new FDetailWidgetRow ); GroupChildren.Add( NewCustomization ); return *NewCustomization.WidgetDecl; } IDetailPropertyRow& FDetailGroup::AddPropertyRow( TSharedRef PropertyHandle ) { check( PropertyHandle->IsValidHandle() ); PropertyHandle->MarkHiddenByCustomization(); FDetailLayoutCustomization NewCustomization; NewCustomization.PropertyRow = MakeShareable( new FDetailPropertyRow( StaticCastSharedRef( PropertyHandle )->GetPropertyNode(), ParentCategory.Pin().ToSharedRef() ) ); GroupChildren.Add( NewCustomization ); return *NewCustomization.PropertyRow; } IDetailPropertyRow& FDetailGroup::AddExternalObjectProperty(const TArray& Objects, FName PropertyName, EPropertyLocation::Type Location, const FAddPropertyParams& Params) { FDetailLayoutCustomization NewCustomization; NewCustomization.bCustom = true; NewCustomization.bAdvanced = Location == EPropertyLocation::Advanced; FDetailPropertyRow::MakeExternalPropertyRowCustomization(Objects, PropertyName, ParentCategory.Pin().ToSharedRef(), NewCustomization, Params); GroupChildren.Add( NewCustomization ); return *NewCustomization.PropertyRow; } TSharedPtr FDetailGroup::FindPropertyRow(TSharedRef PropertyHandle) const { for (const FDetailLayoutCustomization& Customization : GroupChildren) { if (Customization.PropertyRow.IsValid() && Customization.PropertyRow->GetPropertyHandle() == PropertyHandle) { return Customization.PropertyRow; } } return nullptr; } IDetailGroup& FDetailGroup::AddGroup(FName NewGroupName, const FText& InLocalizedDisplayName, bool bInStartExpanded) { FDetailLayoutCustomization NewCustomization; NewCustomization.DetailGroup = MakeShareable(new FDetailGroup(NewGroupName, ParentCategory.Pin().ToSharedRef(), InLocalizedDisplayName, bStartExpanded)); GroupChildren.Add(NewCustomization); return *NewCustomization.DetailGroup; } const TOptional& FDetailGroup::GetToolTip() const { return LocalizedToolTip; } void FDetailGroup::SetToolTip(const FText& ToolTip) { LocalizedToolTip = ToolTip; } void FDetailGroup::ToggleExpansion( bool bExpand ) { if( ParentCategory.IsValid() && OwnerTreeNode.IsValid() ) { ParentCategory.Pin()->RequestItemExpanded( OwnerTreeNode.Pin().ToSharedRef(), bExpand ); } } bool FDetailGroup::GetExpansionState() const { if (ParentCategory.IsValid() && OwnerTreeNode.IsValid()) { return ParentCategory.Pin()->GetSavedExpansionState(*OwnerTreeNode.Pin().Get()); } return false; } TOptional FDetailGroup::GetCustomResetToDefault() const { if (HeaderCustomization.IsValid()) { return HeaderCustomization->PropertyRow->GetCustomResetToDefault(); } return TOptional(); } void FDetailGroup::SetDisplayMode(EDetailGroupDisplayMode Mode) { DisplayMode = Mode; } TSharedPtr FDetailGroup::GetHeaderPropertyRow() const { return HeaderCustomization.IsValid() ? HeaderCustomization->PropertyRow : nullptr; } TSharedPtr FDetailGroup::GetHeaderPropertyNode() const { return HeaderCustomization.IsValid() ? HeaderCustomization->GetPropertyNode() : nullptr; } bool FDetailGroup::HasColumns() const { if( HeaderCustomization.IsValid() && HeaderCustomization->HasPropertyNode() && HeaderCustomization->PropertyRow->HasColumns() ) { return HeaderCustomization->PropertyRow->HasColumns(); } else if( HeaderCustomization.IsValid() && HeaderCustomization->HasCustomWidget() ) { return HeaderCustomization->WidgetDecl->HasColumns(); } return true; } bool FDetailGroup::RequiresTick() const { bool bRequiresTick = false; if( HeaderCustomization.IsValid() && HeaderCustomization->HasPropertyNode() ) { bRequiresTick = HeaderCustomization->PropertyRow->RequiresTick(); } else if ( HeaderCustomization.IsValid() && HeaderCustomization->HasCustomWidget() ) { bRequiresTick = HeaderCustomization->WidgetDecl->VisibilityAttr.IsBound(); } return bRequiresTick; } EVisibility FDetailGroup::GetGroupVisibility() const { EVisibility Visibility = EVisibility::Visible; if( HeaderCustomization.IsValid() && HeaderCustomization->HasPropertyNode() ) { Visibility = HeaderCustomization->PropertyRow->GetPropertyVisibility(); } else if ( HeaderCustomization.IsValid() && HeaderCustomization->HasCustomWidget() ) { Visibility = HeaderCustomization->WidgetDecl->VisibilityAttr.Get(); } return Visibility; } FDetailWidgetRow FDetailGroup::GetWidgetRow() { if( HeaderCustomization.IsValid() && HeaderCustomization->HasPropertyNode() ) { return HeaderCustomization->PropertyRow->GetWidgetRow(); } else if ( HeaderCustomization.IsValid() && HeaderCustomization->HasCustomWidget() ) { return *HeaderCustomization->WidgetDecl; } else { FDetailWidgetRow Row; Row.NameContent() [ MakeNameWidget() ]; Row.OverrideResetToDefault(FResetToDefaultOverride::Create(TAttribute(this, &FDetailGroup::IsResetVisible), FSimpleDelegate::CreateSP(this, &FDetailGroup::OnResetClicked))); return Row; } } void FDetailGroup::OnItemNodeInitialized( TSharedRef InTreeNode, TSharedRef InParentCategory, const TAttribute& InIsParentEnabled ) { OwnerTreeNode = InTreeNode; ParentCategory = InParentCategory; IsParentEnabled = InIsParentEnabled; if( HeaderCustomization.IsValid() && HeaderCustomization->HasPropertyNode() ) { HeaderCustomization->PropertyRow->OnItemNodeInitialized( InParentCategory, InIsParentEnabled, AsShared() ); } } void FDetailGroup::OnGenerateChildren( FDetailNodeList& OutChildren ) { for( int32 ChildIndex = 0; ChildIndex < GroupChildren.Num(); ++ChildIndex ) { TSharedRef NewNode = MakeShareable( new FDetailItemNode( GroupChildren[ChildIndex], ParentCategory.Pin().ToSharedRef(), IsParentEnabled, AsShared()) ); NewNode->Initialize(); OutChildren.Add( NewNode ); } } FReply FDetailGroup::OnNameClicked() { OwnerTreeNode.Pin()->ToggleExpansion(); return FReply::Handled(); } TSharedRef FDetailGroup::MakeNameWidget() { TSharedRef TextBlock = SNew(STextBlock) .Font(IDetailLayoutBuilder::GetDetailFont()) .Text(LocalizedDisplayName); if (LocalizedToolTip.IsSet() && !LocalizedToolTip->IsEmpty()) { TextBlock->SetToolTipText(LocalizedToolTip.GetValue()); } return SNew( SButton ) .ButtonStyle( FAppStyle::Get(), "NoBorder" ) .ContentPadding(FMargin(0,2,0,2)) .OnClicked( this, &FDetailGroup::OnNameClicked ) .ForegroundColor( FSlateColor::UseForeground() ) .Content() [ TextBlock ]; } void FDetailGroup::OnResetClicked() { if (bResetEnabled) { TArray> PropertyHandles; if (GetAllChildrenPropertyHandles(PropertyHandles)) { for (TSharedPtr PropertyHandle : PropertyHandles) { PropertyHandle->ResetToDefault(); } OnDetailGroupReset.Broadcast(); } } } bool FDetailGroup::IsResetVisible() const { if (bResetEnabled) { TArray> PropertyHandles; if (GetAllChildrenPropertyHandles(PropertyHandles)) { for (TSharedPtr PropertyHandle : PropertyHandles) { if (PropertyHandle->DiffersFromDefault()) { return true; } } } } return false; } void FDetailGroup::EnableReset(bool InValue) { bResetEnabled = InValue; } bool FDetailGroup::GetAllChildrenPropertyHandles(TArray>& PropertyHandles) const { PropertyHandles.Reserve(GroupChildren.Num()); return GetAllChildrenPropertyHandlesRecursive(this, PropertyHandles); } bool FDetailGroup::GetAllChildrenPropertyHandlesRecursive(const FDetailGroup* CurrentDetailGroup, TArray>& PropertyHandles) const { bool Result = false; for (const FDetailLayoutCustomization& Customization : CurrentDetailGroup->GroupChildren) { if (Customization.HasPropertyNode()) { PropertyHandles.Add(Customization.PropertyRow->GetPropertyHandle()); Result = true; } else if (Customization.HasGroup()) { Result &= GetAllChildrenPropertyHandlesRecursive(Customization.DetailGroup.Get(), PropertyHandles); } else if (Customization.HasCustomWidget()) { PropertyHandles.Append(Customization.WidgetDecl->GetPropertyHandles()); Result = true; } } return Result; } #undef LOCTEXT_NAMESPACE