int32 SSplitter::GetHandleBeingResizedFromMousePosition( float PhysicalSplitterHandleSize, float HitDetectionSplitterHandleSize, FVector2D LocalMousePos, const TArray<FLayoutGeometry>& ChildGeometries ) { const int32 AxisIndex = (SplitterOrientation == Orient_Horizontal) ? 0 : 1; const float HalfHitDetectionSplitterHandleSize = ( HitDetectionSplitterHandleSize / 2 ); const float HalfPhysicalSplitterHandleSize = ( PhysicalSplitterHandleSize / 2 ); // Search for the two widgets between which the cursor currently resides. for ( int32 ChildIndex = 1; ChildIndex < ChildGeometries.Num(); ++ChildIndex ) { FSlateRect PrevChildRect = ChildGeometries[ChildIndex - 1].GetRectInParentSpace(); FVector2D NextChildOffset = ChildGeometries[ChildIndex].GetOffsetInParentSpace(); float PrevBound = PrevChildRect.GetTopLeft().Component(AxisIndex) + PrevChildRect.GetSize().Component(AxisIndex) - HalfHitDetectionSplitterHandleSize + HalfPhysicalSplitterHandleSize; float NextBound = NextChildOffset.Component(AxisIndex) + HalfHitDetectionSplitterHandleSize - HalfPhysicalSplitterHandleSize; if ( LocalMousePos.Component(AxisIndex) > PrevBound && LocalMousePos.Component(AxisIndex) < NextBound ) { return ChildIndex - 1; } } return INDEX_NONE; }
void SSplitter::HandleResizing( const float PhysicalSplitterHandleSize, const ESplitterResizeMode::Type ResizeMode, int32 DraggedHandle, const FVector2D& LocalMousePos, TPanelChildren<FSlot>& Children, const TArray<FLayoutGeometry>& ChildGeometries ) { const int32 NumChildren = Children.Num(); const int32 AxisIndex = (SplitterOrientation == Orient_Horizontal) ? 0 : 1; // Note: // - Prev vs. Next refers to the widgets in the order they are laid out (left->right, top->bottom). // - New vs. Old refers to the Old values for width/height vs. the post-resize values. const float HandlePos = ChildGeometries[DraggedHandle+1].GetLocalToParentTransform().GetTranslation().Component(AxisIndex) - PhysicalSplitterHandleSize / 2; float Delta = LocalMousePos.Component(AxisIndex) - HandlePos; const int32 SlotBeforeDragHandle = FindResizeableSlotBeforeHandle( DraggedHandle, Children ); TArray< int32 > SlotsAfterDragHandleIndicies; if ( ResizeMode == ESplitterResizeMode::Fixed ) { const int32 SlotAfterDragHandle = FindResizeableSlotAfterHandle( DraggedHandle, Children ); if ( SlotAfterDragHandle < NumChildren ) { SlotsAfterDragHandleIndicies.Add( SlotAfterDragHandle ); } } else if ( ResizeMode == ESplitterResizeMode::Fill ) { FindAllResizeableSlotsAfterHandle( DraggedHandle, Children, /*OUT*/ SlotsAfterDragHandleIndicies ); } if ( SlotBeforeDragHandle >= 0 && SlotsAfterDragHandleIndicies.Num() > 0 ) { struct FSlotInfo { FSlot* Slot; const FLayoutGeometry* Geometry; float NewSize; }; TArray< FSlotInfo > SlotsAfterDragHandle; for (int SlotIndex = 0; SlotIndex < SlotsAfterDragHandleIndicies.Num(); SlotIndex++) { FSlotInfo SlotInfo; SlotInfo.Slot = &Children[ SlotsAfterDragHandleIndicies[ SlotIndex ] ]; SlotInfo.Geometry = &ChildGeometries[ SlotsAfterDragHandleIndicies[ SlotIndex ] ]; SlotInfo.NewSize = SlotInfo.Geometry->GetSizeInParentSpace().Component( AxisIndex ); SlotsAfterDragHandle.Add( SlotInfo ); } // Get references the prev and next children and their layout settings so that we can modify them. FSlot& PrevChild = Children[SlotBeforeDragHandle]; const FLayoutGeometry& PrevChildGeom = ChildGeometries[SlotBeforeDragHandle]; // Compute the new sizes of the children const float PrevChildLength = PrevChildGeom.GetSizeInParentSpace().Component(AxisIndex); float NewPrevChildLength = ClampChild( PrevChildLength + Delta ); Delta = NewPrevChildLength - PrevChildLength; // Distribute the Delta across the affected slots after the drag handle float UnusedDelta = Delta; for (int DistributionCount = 0; DistributionCount < SlotsAfterDragHandle.Num() && UnusedDelta != 0; DistributionCount++) { float DividedDelta = UnusedDelta / SlotsAfterDragHandle.Num(); UnusedDelta = 0; for (int SlotIndex = 0; SlotIndex < SlotsAfterDragHandle.Num(); SlotIndex++) { FSlotInfo& SlotInfo = SlotsAfterDragHandle[ SlotIndex ]; float CurrentSize = ClampChild( SlotInfo.Geometry->GetSizeInParentSpace().Component(AxisIndex) ); SlotInfo.NewSize = ClampChild( CurrentSize - DividedDelta ); // If one of the slots couldn't be fully adjusted by the delta due to min/max constraints then // the leftover delta needs to be evenly distributed to all of the other slots UnusedDelta += SlotInfo.NewSize - ( CurrentSize - DividedDelta ); } } Delta = Delta - UnusedDelta; // PrevChildLength needs to be updated: it's value has to take into account the next child's min/max restrictions NewPrevChildLength = ClampChild( PrevChildLength + Delta ); // Cells being resized are both stretch values -> redistribute the stretch coefficients proportionately // to match the new child sizes on the screen. { float TotalLength = NewPrevChildLength; float TotalStretchCoefficients = PrevChild.SizeValue.Get(); for (int SlotIndex = 0; SlotIndex < SlotsAfterDragHandle.Num(); SlotIndex++) { FSlotInfo SlotInfo = SlotsAfterDragHandle[ SlotIndex ]; TotalLength += SlotInfo.NewSize; TotalStretchCoefficients += SlotInfo.Slot->SizeValue.Get(); } const float NewPrevChildSize = ( TotalStretchCoefficients * NewPrevChildLength / TotalLength ); if (PrevChild.OnSlotResized_Handler.IsBound()) { PrevChild.OnSlotResized_Handler.Execute( NewPrevChildSize ); } else { PrevChild.SizeValue = NewPrevChildSize; } for (int SlotIndex = 0; SlotIndex < SlotsAfterDragHandle.Num(); SlotIndex++) { FSlotInfo SlotInfo = SlotsAfterDragHandle[ SlotIndex ]; const float NewNextChildSize = ( TotalStretchCoefficients * SlotInfo.NewSize / TotalLength ); if (SlotInfo.Slot->OnSlotResized_Handler.IsBound()) { SlotInfo.Slot->OnSlotResized_Handler.Execute(NewNextChildSize); } else { SlotInfo.Slot->SizeValue = NewNextChildSize; } } } } }