/** * See SWidget::OnMouseButtonUp. * * @param MyGeometry The Geometry of the widget receiving the event * @param MouseEvent Information about the input event * * @return Whether the event was handled along with possible requests for the system to take action. */ FReply SCheckBox::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { if ( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton ) { bIsPressed = false; if( ClickMethod == EButtonClickMethod::MouseDown ) { // NOTE: If we're configured to click on mouse-down, then we never capture the mouse thus // may never receive an OnMouseButtonUp() call. We make sure that our bIsPressed // state is reset by overriding OnMouseLeave(). } else { const bool IsUnderMouse = MyGeometry.IsUnderLocation( MouseEvent.GetScreenSpacePosition() ); if ( IsUnderMouse ) { // If we were asked to allow the button to be clicked on mouse up, regardless of whether the user // pressed the button down first, then we'll allow the click to proceed without an active capture if( ClickMethod == EButtonClickMethod::MouseUp || HasMouseCapture() ) { ToggleCheckedState(); const TAttribute<ESlateCheckBoxState::Type>& State = IsCheckboxChecked.Get(); if(State == ESlateCheckBoxState::Checked) { PlayCheckedSound(); } else if(State == ESlateCheckBoxState::Unchecked) { PlayUncheckedSound(); } } } } return FReply::Handled().ReleaseMouseCapture(); } return FReply::Unhandled(); }
FReply FVisualLoggerTimeSliderController::OnMouseButtonUp( TSharedRef<SWidget> WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { bool bHandleLeftMouseButton = MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && WidgetOwner->HasMouseCapture(); bool bHandleRightMouseButton = MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && WidgetOwner->HasMouseCapture() && TimeSliderArgs.AllowZoom ; if ( bHandleRightMouseButton ) { if (!bPanning) { // return unhandled in case our parent wants to use our right mouse button to open a context menu return FReply::Unhandled().ReleaseMouseCapture(); } bPanning = false; FReply::Handled().CaptureMouse(WidgetOwner).UseHighPrecisionMouseMovement(WidgetOwner); return FReply::Handled().ReleaseMouseCapture(); } else if ( bHandleLeftMouseButton ) { if( bDraggingScrubber ) { TimeSliderArgs.OnEndScrubberMovement.ExecuteIfBound(); } else { FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size ); FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition()); float NewValue = RangeToScreen.LocalXToInput(CursorPos.X); CommitScrubPosition( NewValue, /*bIsScrubbing=*/false ); } bDraggingScrubber = false; return FReply::Handled().ReleaseMouseCapture(); } return FReply::Unhandled(); }
/** * The system asks each widget under the mouse to provide a cursor. This event is bubbled. * * @return FCursorReply::Unhandled() if the event is not handled; return FCursorReply::Cursor() otherwise. */ FCursorReply SSplitter::OnCursorQuery( const FGeometry& MyGeometry, const FPointerEvent& CursorEvent ) const { const FVector2D LocalMousePosition = MyGeometry.AbsoluteToLocal( CursorEvent.GetScreenSpacePosition() ); TArray<FLayoutGeometry> LayoutChildren = ArrangeChildrenForLayout(MyGeometry); // Hit test which handle we are hovering over. const int32 CurrentHoveredHandleIndex = (Orientation == Orient_Horizontal) ? GetHandleBeingResizedFromMousePosition<Orient_Horizontal>( PhysicalSplitterHandleSize, HitDetectionSplitterHandleSize, LocalMousePosition, LayoutChildren ) : GetHandleBeingResizedFromMousePosition<Orient_Vertical>( PhysicalSplitterHandleSize, HitDetectionSplitterHandleSize, LocalMousePosition, LayoutChildren ); if (CurrentHoveredHandleIndex != INDEX_NONE) { return ( Orientation == Orient_Horizontal ) ? FCursorReply::Cursor( EMouseCursor::ResizeLeftRight ) : FCursorReply::Cursor( EMouseCursor::ResizeUpDown ); } else { return FCursorReply::Unhandled(); } }
float STrack::GetNodeDragDropDataPos( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ) { float DataPos = 0.f; TSharedPtr<FTrackNodeDragDropOp> DragDropOp = StaticCastSharedPtr<FTrackNodeDragDropOp>(DragDropEvent.GetOperation()); if(DragDropOp.IsValid()) { TSharedPtr<STrackNode> TrackNode = DragDropOp->OriginalTrackNode.Pin(); if(TrackNode.IsValid()) { FVector2D CursorPos = MyGeometry.AbsoluteToLocal(TrackNode->GetDragDropScreenSpacePosition(MyGeometry, DragDropEvent)); DataPos = LocalToDataX(CursorPos.X, MyGeometry); if(TrackNode->SnapToDragBars()) { float OriginalX = DataPos; DataPos = GetSnappedPosForLocalPos(MyGeometry, CursorPos.X); TrackNode->OnSnapNodeDataPosition(OriginalX, DataPos); } } } return DataPos; }
void SGraphPanel::OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const { SNodePanel::OnArrangeChildren(AllottedGeometry, ArrangedChildren); FArrangedChildren MyArrangedChildren(ArrangedChildren.GetFilter()); for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget); TArray<FOverlayWidgetInfo> OverlayWidgets = ChildNode->GetOverlayWidgets(false, CurWidget.Geometry.Size); for (int32 WidgetIndex = 0; WidgetIndex < OverlayWidgets.Num(); ++WidgetIndex) { FOverlayWidgetInfo& OverlayInfo = OverlayWidgets[WidgetIndex]; MyArrangedChildren.AddWidget(AllottedGeometry.MakeChild( OverlayInfo.Widget.ToSharedRef(), CurWidget.Geometry.Position + OverlayInfo.OverlayOffset, OverlayInfo.Widget->GetDesiredSize(), GetZoomAmount() )); } } ArrangedChildren.Append(MyArrangedChildren); }
int32 FSlateImageRun::OnPaint( const FPaintArgs& Args, const FTextLayout::FLineView& Line, const TSharedRef< ILayoutBlock >& Block, const FTextBlockStyle& DefaultStyle, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { // The block size and offset values are pre-scaled, so we need to account for that when converting the block offsets into paint geometry const float InverseScale = Inverse(AllottedGeometry.Scale); if ( Image->DrawAs != ESlateBrushDrawType::NoDrawType ) { const FColor FinalColorAndOpacity( InWidgetStyle.GetColorAndOpacityTint() * Image->GetTint( InWidgetStyle ) ); const uint32 DrawEffects = bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; FSlateDrawElement::MakeBox( OutDrawElements, ++LayerId, AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, Block->GetSize()), FSlateLayoutTransform(TransformPoint(InverseScale, Block->GetLocationOffset()))), Image, MyClippingRect, DrawEffects, FinalColorAndOpacity ); } return LayerId; }
void SSection::CheckForEdgeInteraction( const FPointerEvent& MouseEvent, const FGeometry& SectionGeometry ) { bLeftEdgeHovered = false; bRightEdgeHovered = false; bLeftEdgePressed = false; bRightEdgePressed = false; if (!SectionInterface->SectionIsResizable()) { return; } // Make areas to the left and right of the geometry. We will use these areas to determine if someone dragged the left or right edge of a section FGeometry SectionRectLeft = SectionGeometry.MakeChild( FVector2D::ZeroVector, FVector2D( SequencerSectionConstants::SectionGripSize, SectionGeometry.Size.Y ) ); FGeometry SectionRectRight = SectionGeometry.MakeChild( FVector2D( SectionGeometry.Size.X - SequencerSectionConstants::SectionGripSize, 0 ), SectionGeometry.Size ); if( SectionRectLeft.IsUnderLocation( MouseEvent.GetScreenSpacePosition() ) ) { if( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton ) { bLeftEdgePressed = true; } else { bLeftEdgeHovered = true; } } else if( SectionRectRight.IsUnderLocation( MouseEvent.GetScreenSpacePosition() ) ) { if( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton ) { bRightEdgePressed = true; } else { bRightEdgeHovered = true; } } }
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override { if (!SlideCurve.IsPlaying()) { StartSlideOffset = 0; } // Delete any widgets that are now offscreen if (Children.Num() != 0) { const float Alpha = 1.f - SlideCurve.GetLerp(); float PositionSoFar = AllottedGeometry.GetLocalSize().Y + Alpha * StartSlideOffset; for (int32 Index = 0; PositionSoFar > 0 && Index < NumSlots(); ++Index) { const SBoxPanel::FSlot& CurChild = Children[Index]; const EVisibility ChildVisibility = CurChild.GetWidget()->GetVisibility(); if (ChildVisibility != EVisibility::Collapsed) { const FVector2D ChildDesiredSize = CurChild.GetWidget()->GetDesiredSize(); PositionSoFar -= ChildDesiredSize.Y + CurChild.SlotPadding.Get().GetTotalSpaceAlong<Orient_Vertical>(); } } for (int32 Index = MaxNumVisible; Index < Children.Num(); ) { if (StaticCastSharedRef<SWidgetStackItem>(Children[Index].GetWidget())->bIsFinished) { Children.RemoveAt(Index); } else { ++Index; } } } }
FReply FScrollyZoomy::OnMouseMove( const TSharedRef<SWidget> MyWidget, IScrollableZoomable& ScrollableZoomable, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { if (MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton)) { // If scrolling with the right mouse button, we need to remember how much we scrolled. // If we did not scroll at all, we will bring up the context menu when the mouse is released. AmountScrolledWhileRightMouseDown += FMath::Abs( MouseEvent.GetCursorDelta().X ) + FMath::Abs( MouseEvent.GetCursorDelta().Y ); // Has the mouse moved far enough with the right mouse button held down to start capturing // the mouse and dragging the view? if (IsRightClickScrolling()) { this->HorizontalIntertia.AddScrollSample( MouseEvent.GetCursorDelta().X, FPlatformTime::Seconds() ); this->VerticalIntertia.AddScrollSample( MouseEvent.GetCursorDelta().Y, FPlatformTime::Seconds() ); const bool DidScroll = ScrollableZoomable.ScrollBy( MouseEvent.GetCursorDelta() ); FReply Reply = FReply::Handled(); // Capture the mouse if we need to if (MyWidget->HasMouseCapture() == false) { Reply.CaptureMouse( MyWidget ).UseHighPrecisionMouseMovement( MyWidget ); SoftwareCursorPosition = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); bShowSoftwareCursor = true; } // Check if the mouse has moved. if (DidScroll) { SoftwareCursorPosition += MouseEvent.GetCursorDelta(); } return Reply; } } return FReply::Unhandled(); }
int32 SGridPanel::LayoutDebugPaint(const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId ) const { float XOffset = 0; for (int32 Column=0; Column<Columns.Num(); ++Column) { float YOffset = 0; for (int32 Row=0; Row<Rows.Num(); ++Row) { FSlateDrawElement::MakeDebugQuad ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D(XOffset, YOffset), FVector2D( Columns[Column], Rows[Row] ) ), MyClippingRect ); YOffset += Rows[Row]; } XOffset += Columns[Column]; } return LayerId; }
int32 SBox::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { // An SBox just draws its only child FArrangedChildren ArrangedChildren(EVisibility::Visible); { #if SLATE_HD_STATS SCOPE_CYCLE_COUNTER( STAT_SlateOnPaint_SBox ); #endif this->ArrangeChildren(AllottedGeometry, ArrangedChildren); } // Maybe none of our children are visible if( ArrangedChildren.Num() > 0 ) { check( ArrangedChildren.Num() == 1 ); FArrangedWidget& TheChild = ArrangedChildren[0]; const FSlateRect ChildClippingRect = AllottedGeometry.GetClippingRect().InsetBy( ChildSlot.SlotPadding.Get() * AllottedGeometry.Scale ).IntersectionWith(MyClippingRect); return TheChild.Widget->Paint( Args.WithNewParent(this), TheChild.Geometry, ChildClippingRect, OutDrawElements, LayerId, InWidgetStyle, ShouldBeEnabled( bParentEnabled ) ); } return LayerId; }
FReply FSceneViewport::OnTouchEnded( const FGeometry& MyGeometry, const FPointerEvent& TouchEvent ) { // Start a new reply state CurrentReplyState = FReply::Handled(); UpdateCachedMousePos(MyGeometry, TouchEvent); UpdateCachedGeometry(MyGeometry); if( ViewportClient ) { // Switch to the viewport clients world before processing input FScopedConditionalWorldSwitcher WorldSwitcher( ViewportClient ); const FVector2D TouchPosition = MyGeometry.AbsoluteToLocal(TouchEvent.GetLastScreenSpacePosition()); if( !ViewportClient->InputTouch( this, TouchEvent.GetUserIndex(), TouchEvent.GetPointerIndex(), ETouchType::Ended, TouchPosition, FDateTime::Now(), TouchEvent.GetTouchpadIndex()) ) { CurrentReplyState = FReply::Unhandled(); } } return CurrentReplyState; }
FReply FSequencerTimeSliderController::OnMouseButtonDown( SWidget& WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { bool bHandleLeftMouseButton = MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton; bool bHandleRightMouseButton = MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && TimeSliderArgs.AllowZoom; DistanceDragged = 0; FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size ); MouseDownRange[0] = RangeToScreen.LocalXToInput(MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition()).X); MouseDownRange[1] = MouseDownRange[0]; if ( bHandleLeftMouseButton ) { return FReply::Handled().CaptureMouse( WidgetOwner.AsShared() ).PreventThrottling(); } else if ( bHandleRightMouseButton ) { // Always capture mouse if we left or right click on the widget return FReply::Handled().CaptureMouse( WidgetOwner.AsShared() ); } return FReply::Unhandled(); }
FReply SFlareKeyBind::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { if (bWaitingForKey) { SetKey(MouseEvent.GetEffectingButton()); return FReply::Handled(); } else if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) { // Get the center of the widget so we can lock our mouse there FSlateRect Rect = MyGeometry.GetClippingRect(); WaitingMousePos.X = (Rect.Left + Rect.Right) * 0.5f; WaitingMousePos.Y = (Rect.Top + Rect.Bottom) * 0.5f; FSlateApplication::Get().GetPlatformApplication().Get()->Cursor->SetPosition(WaitingMousePos.X, WaitingMousePos.Y); KeyText->SetText(LOCTEXT("SFlareKeyBindPressAnyKey", "Press a key...")); bWaitingForKey = true; FSlateApplication::Get().GetPlatformApplication().Get()->Cursor->Show(false); return FReply::Handled(); } return FReply::Unhandled(); }
FVector2D SPaperEditorViewport::ComputeEdgePanAmount(const FGeometry& MyGeometry, const FVector2D& TargetPosition) { // How quickly to ramp up the pan speed as the user moves the mouse further past the edge of the graph panel. static const float EdgePanSpeedCoefficient = 0.1f; // Never pan slower than this, it's just unpleasant. static const float MinPanSpeed = 5.0f; // Start panning before we reach the edge of the graph panel. static const float EdgePanForgivenessZone = 30.0f; const FVector2D LocalCursorPos = MyGeometry.AbsoluteToLocal( TargetPosition ); // If the mouse is outside of the graph area, then we want to pan in that direction. // The farther out the mouse is, the more we want to pan. FVector2D EdgePanThisTick(0,0); if ( LocalCursorPos.X <= EdgePanForgivenessZone ) { EdgePanThisTick.X += FMath::Min( -MinPanSpeed, EdgePanSpeedCoefficient * (EdgePanForgivenessZone - LocalCursorPos.X) ); } else if( LocalCursorPos.X >= MyGeometry.Size.X - EdgePanForgivenessZone ) { EdgePanThisTick.X = FMath::Max( MinPanSpeed, EdgePanSpeedCoefficient * (MyGeometry.Size.X - EdgePanForgivenessZone - LocalCursorPos.X) ); } if ( LocalCursorPos.Y <= EdgePanForgivenessZone ) { EdgePanThisTick.Y += FMath::Min( -MinPanSpeed, EdgePanSpeedCoefficient * (EdgePanForgivenessZone - LocalCursorPos.Y) ); } else if( LocalCursorPos.Y >= MyGeometry.Size.Y - EdgePanForgivenessZone ) { EdgePanThisTick.Y = FMath::Max( MinPanSpeed, EdgePanSpeedCoefficient * (MyGeometry.Size.Y - EdgePanForgivenessZone - LocalCursorPos.Y) ); } return EdgePanThisTick; }
FReply FVisualLoggerTimeSliderController::OnMouseButtonDown( TSharedRef<SWidget> WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { bool bHandleLeftMouseButton = MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton; bool bHandleRightMouseButton = MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && TimeSliderArgs.AllowZoom; DistanceDragged = 0; if ( bHandleLeftMouseButton ) { // Always capture mouse if we left or right click on the widget FScrubRangeToScreen RangeToScreen(TimeSliderArgs.ViewRange.Get(), MyGeometry.Size); FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition()); float NewValue = RangeToScreen.LocalXToInput(CursorPos.X); CommitScrubPosition(NewValue, /*bIsScrubbing=*/false); return FReply::Handled().CaptureMouse( WidgetOwner ).PreventThrottling(); } else if ( bHandleRightMouseButton ) { return FReply::Handled().CaptureMouse(WidgetOwner); } return FReply::Unhandled(); }
void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override { if ( AllottedGeometry.IsUnderLocation(FSlateApplication::Get().GetCursorPos()) ) { if ( !ResizeCurve.IsPlaying() ) { if ( ResizeCurve.IsAtStart() ) { ResizeCurve.Play(); } else if ( ResizeCurve.IsAtEnd() ) { ResizeCurve.PlayReverse(); } } } else { if ( !ResizeCurve.IsAtStart() && ((!ResizeCurve.IsInReverse() && ResizeCurve.IsPlaying()) || !ResizeCurve.IsPlaying()) ) { ResizeCurve.PlayReverse(); } } }
FReply SProfilerThreadView::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { FReply Reply = FReply::Unhandled(); if( IsReady() ) { MousePositionOnButtonDown = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); if( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton ) { bIsLeftMousePressed = true; DistanceDragged = PositionXMS; // Capture mouse, so we can move outside this widget. Reply = FReply::Handled().CaptureMouse( SharedThis( this ) ); } else if( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton ) { bIsRightMousePressed = true; } } return Reply; }
FReply SVirtualJoystick::OnTouchMoved(const FGeometry& MyGeometry, const FPointerEvent& Event) { FVector2D LocalCoord = MyGeometry.AbsoluteToLocal( Event.GetScreenSpacePosition() ); for (int32 ControlIndex = 0; ControlIndex < Controls.Num(); ControlIndex++) { FControlInfo& Control = Controls[ControlIndex]; // is this control the one captured to this pointer? if (Control.CapturedPointerIndex == Event.GetPointerIndex()) { if (Control.bNeedUpdatedCenter) { return FReply::Unhandled(); } else if (HandleTouch(ControlIndex, LocalCoord, MyGeometry.Size)) { return FReply::Handled(); } } } return FReply::Unhandled(); }
FReply SProfilerThreadView::OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { // SCOPE_LOG_TIME_FUNC(); FReply Reply = FReply::Unhandled(); if( IsReady() ) { const FVector2D LocalMousePosition = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); HoveredPositionX = 0.0;//PositionToFrameIndex( LocalMousePosition.X ); HoveredPositionY = 0.0; const float CursorPosXDelta = -MouseEvent.GetCursorDelta().X; const float ScrollSpeed = 1.0f / ZoomFactorX; if( MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton ) ) { if( HasMouseCapture() && !MouseEvent.GetCursorDelta().IsZero() ) { DistanceDragged += CursorPosXDelta*ScrollSpeed*0.1; // Inform other widgets that we have scrolled the thread-view. SetPositionX( FMath::Clamp( DistanceDragged, 0.0, TotalRangeXMS - RangeXMS ) ); CursorType = EThreadViewCursor::Hand; Reply = FReply::Handled(); } } else { CursorType = EThreadViewCursor::Default; } } return Reply; }
bool SColorWheel::ProcessMouseAction(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, bool bProcessWhenOutsideColorWheel) { const FVector2D LocalMouseCoordinate = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); const FVector2D Location = LocalMouseCoordinate / (MyGeometry.Size * 0.5f) - FVector2D(1.0f, 1.0f); const float Radius = Location.Size(); if (Radius <= 1.0f || bProcessWhenOutsideColorWheel) { float Angle = FMath::Atan2(Location.Y, Location.X); if (Angle < 0.0f) { Angle += 2.0f * PI; } FLinearColor NewColor = SelectedColor.Get(); NewColor.R = Angle * 180.0f * INV_PI; NewColor.G = FMath::Min(Radius, 1.0f); OnValueChanged.ExecuteIfBound(NewColor); } return (Radius <= 1.0f); }
int32 SScrubWidget::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { const bool bActiveFeedback = IsHovered() || bDragging; const FSlateBrush* BackgroundImage = bActiveFeedback ? FEditorStyle::GetBrush("SpinBox.Background.Hovered") : FEditorStyle::GetBrush("SpinBox.Background"); const FSlateBrush* FillImage = bActiveFeedback ? FEditorStyle::GetBrush("SpinBox.Fill.Hovered") : FEditorStyle::GetBrush("SpinBox.Fill"); const int32 BackgroundLayer = LayerId; const FSlateFontInfo SmallLayoutFont( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 10 ); const bool bEnabled = ShouldBeEnabled( bParentEnabled ); const ESlateDrawEffect::Type DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; const int32 TextLayer = BackgroundLayer + 1; const FSlateBrush* StyleInfo = FEditorStyle::GetBrush( TEXT( "ProgressBar.Background" ) ); const float GeomHeight = AllottedGeometry.Size.Y; if ( NumOfKeys.Get() > 0 && SequenceLength.Get() > 0) { const FTrackScaleInfo TimeScaleInfo(ViewInputMin.Get(), ViewInputMax.Get(), 0.f, 0.f, AllottedGeometry.Size); const int32 Divider = SScrubWidget::GetDivider( ViewInputMin.Get(), ViewInputMax.Get(), AllottedGeometry.Size, SequenceLength.Get(), NumOfKeys.Get() ); const float HalfDivider = Divider/2.f; const int32 TotalNumKeys = NumOfKeys.Get(); const float TimePerKey = (TotalNumKeys > 0) ? SequenceLength.Get() / (float)(TotalNumKeys) : 0.0f; for (float KeyVal = 0; KeyVal < TotalNumKeys; KeyVal += HalfDivider) { const float CurValue = KeyVal*TimePerKey; const float XPos = TimeScaleInfo.InputToLocalX(CurValue); if ( FGenericPlatformMath::Fmod(KeyVal, Divider) == 0.f ) { const FVector2D Offset(XPos, 0.f); const FVector2D Size(1, GeomHeight); // draw each box with key frame FSlateDrawElement::MakeBox( OutDrawElements, BackgroundLayer, AllottedGeometry.ToPaintGeometry(Offset, Size), StyleInfo, MyClippingRect, DrawEffects, InWidgetStyle.GetColorAndOpacityTint() ); const int32 FrameNumber = KeyVal; const FString FrameString = FString::Printf(TEXT("%d"), (FrameNumber)); const FVector2D TextOffset(XPos+2.f, 0.f); const TSharedRef< FSlateFontMeasure > FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); const FVector2D TextSize = FontMeasureService->Measure(FrameString, SmallLayoutFont); FSlateDrawElement::MakeText( OutDrawElements, TextLayer, AllottedGeometry.ToPaintGeometry(TextOffset, TextSize), FrameString, SmallLayoutFont, MyClippingRect, DrawEffects); } else if (HalfDivider > 1.f) { const float Height = GeomHeight; const FVector2D Offset(XPos, Height*0.25f); const FVector2D Size(1, Height*0.5f); // draw each box with key frame FSlateDrawElement::MakeBox( OutDrawElements, BackgroundLayer, AllottedGeometry.ToPaintGeometry(Offset, Size), StyleInfo, MyClippingRect, DrawEffects, InWidgetStyle.GetColorAndOpacityTint() ); } } const int32 ArrowLayer = TextLayer + 1; { const float XPos = TimeScaleInfo.InputToLocalX(ValueAttribute.Get()); const float Height = AllottedGeometry.Size.Y; const FVector2D Offset( XPos - Height*0.25f, 0.f ); FPaintGeometry MyGeometry = AllottedGeometry.ToPaintGeometry( Offset, FVector2D(Height*0.5f, Height) ); FLinearColor ScrubColor = InWidgetStyle.GetColorAndOpacityTint(); ScrubColor.A = ScrubColor.A*0.5f; ScrubColor.B *= 0.1f; ScrubColor.G *= 0.1f; FSlateDrawElement::MakeBox( OutDrawElements, ArrowLayer, MyGeometry, StyleInfo, MyClippingRect, DrawEffects, ScrubColor ); } // Draggable Bars if ( DraggableBars.IsBound() ) { for ( const float BarValue : DraggableBars.Get() ) { const float BarXPos = TimeScaleInfo.InputToLocalX(BarValue); const FVector2D BarOffset(BarXPos-2.f, 0.f); const FVector2D Size(4.f, GeomHeight); FLinearColor BarColor = InWidgetStyle.GetColorAndOpacityTint(); BarColor.R *= 0.1f; BarColor.G *= 0.1f; FSlateDrawElement::MakeBox( OutDrawElements, ArrowLayer+1, AllottedGeometry.ToPaintGeometry(BarOffset, Size), StyleInfo, MyClippingRect, DrawEffects, BarColor ); } } return FMath::Max( ArrowLayer, SCompoundWidget::OnPaint( Args, AllottedGeometry, MyClippingRect, OutDrawElements, ArrowLayer, InWidgetStyle, bEnabled ) ); } return SCompoundWidget::OnPaint( Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bEnabled ); }
FReply SScrubWidget::OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { // Bar Dragging if(DraggingBar) { // Update bar if we are dragging FVector2D CursorPos = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); FTrackScaleInfo ScaleInfo(ViewInputMin.Get(), ViewInputMax.Get(), 0.f, 0.f, MyGeometry.Size); float NewDataPos = FMath::Clamp( ScaleInfo.LocalXToInput(CursorPos.X), ViewInputMin.Get(), ViewInputMax.Get() ); OnBarDrag.ExecuteIfBound(DraggableBarIndex, NewDataPos); } else { // Update what bar we are hovering over FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); FTrackScaleInfo ScaleInfo(ViewInputMin.Get(), ViewInputMax.Get(), 0.f, 0.f, MyGeometry.Size); DraggableBarIndex = INDEX_NONE; if ( DraggableBars.IsBound() ) { const TArray<float>& DraggableBarsVal = DraggableBars.Get(); for ( int32 I=0; I < DraggableBarsVal.Num(); I++ ) { if( FMath::Abs( ScaleInfo.InputToLocalX(DraggableBarsVal[I]) - CursorPos.X ) < 10 ) { DraggableBarIndex = I; break; } } } } if ( this->HasMouseCapture() ) { if (MouseEvent.IsMouseButtonDown( EKeys::RightMouseButton ) && bPanning) { FTrackScaleInfo ScaleInfo(ViewInputMin.Get(), ViewInputMax.Get(), 0.f, 0.f, MyGeometry.Size); FVector2D ScreenDelta = MouseEvent.GetCursorDelta(); float InputDeltaX = ScreenDelta.X/ScaleInfo.PixelsPerInput; bMouseMovedDuringPanning |= !ScreenDelta.IsNearlyZero(0.001f); float NewViewInputMin = ViewInputMin.Get() - InputDeltaX; float NewViewInputMax = ViewInputMax.Get() - InputDeltaX; // we'd like to keep the range if outside when panning if ( NewViewInputMin < 0.f ) { NewViewInputMin = 0.f; NewViewInputMax = ScaleInfo.ViewInputRange; } else if ( NewViewInputMax > SequenceLength.Get() ) { NewViewInputMax = SequenceLength.Get(); NewViewInputMin = NewViewInputMax - ScaleInfo.ViewInputRange; } OnSetInputViewRange.ExecuteIfBound(NewViewInputMin, NewViewInputMax); } else if (!bDragging) { DistanceDragged += FMath::Abs(MouseEvent.GetCursorDelta().X); if ( DistanceDragged > FSlateApplication::Get().GetDragTriggerDistance() ) { bDragging = true; } if( bDragging ) { OnBeginSliderMovement.ExecuteIfBound(); } } else if (bDragging) { FTrackScaleInfo TimeScaleInfo(ViewInputMin.Get(), ViewInputMax.Get(), 0.f, 0.f, MyGeometry.Size); FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition()); float NewValue = TimeScaleInfo.LocalXToInput(CursorPos.X); CommitValue( NewValue, true, false ); } return FReply::Handled(); } return FReply::Unhandled(); }
int32 SSpineWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const { SSpineWidget* self = (SSpineWidget*)this; if (widget && widget->skeleton && widget->Atlas) { widget->skeleton->getColor().set(widget->Color.R, widget->Color.G, widget->Color.B, widget->Color.A); if (widget->atlasNormalBlendMaterials.Num() != widget->Atlas->atlasPages.Num()) { widget->atlasNormalBlendMaterials.SetNum(0); widget->pageToNormalBlendMaterial.Empty(); widget->atlasAdditiveBlendMaterials.SetNum(0); widget->pageToAdditiveBlendMaterial.Empty(); widget->atlasMultiplyBlendMaterials.SetNum(0); widget->pageToMultiplyBlendMaterial.Empty(); widget->atlasScreenBlendMaterials.SetNum(0); widget->pageToScreenBlendMaterial.Empty(); for (int i = 0; i < widget->Atlas->atlasPages.Num(); i++) { AtlasPage* currPage = widget->Atlas->GetAtlas()->getPages()[i]; UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(widget->NormalBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]); widget->atlasNormalBlendMaterials.Add(material); widget->pageToNormalBlendMaterial.Add(currPage, material); material = UMaterialInstanceDynamic::Create(widget->AdditiveBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]); widget->atlasAdditiveBlendMaterials.Add(material); widget->pageToAdditiveBlendMaterial.Add(currPage, material); material = UMaterialInstanceDynamic::Create(widget->MultiplyBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]); widget->atlasMultiplyBlendMaterials.Add(material); widget->pageToMultiplyBlendMaterial.Add(currPage, material); material = UMaterialInstanceDynamic::Create(widget->ScreenBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]); widget->atlasScreenBlendMaterials.Add(material); widget->pageToScreenBlendMaterial.Add(currPage, material); } } else { widget->pageToNormalBlendMaterial.Empty(); widget->pageToAdditiveBlendMaterial.Empty(); widget->pageToMultiplyBlendMaterial.Empty(); widget->pageToScreenBlendMaterial.Empty(); for (int i = 0; i < widget->Atlas->atlasPages.Num(); i++) { AtlasPage* currPage = widget->Atlas->GetAtlas()->getPages()[i]; UTexture2D* texture = widget->Atlas->atlasPages[i]; UTexture* oldTexture = nullptr; UMaterialInstanceDynamic* current = widget->atlasNormalBlendMaterials[i]; if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) { UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(widget->NormalBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, texture); widget->atlasNormalBlendMaterials[i] = material; } widget->pageToNormalBlendMaterial.Add(currPage, widget->atlasNormalBlendMaterials[i]); current = widget->atlasAdditiveBlendMaterials[i]; if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) { UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(widget->AdditiveBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, texture); widget->atlasAdditiveBlendMaterials[i] = material; } widget->pageToAdditiveBlendMaterial.Add(currPage, widget->atlasAdditiveBlendMaterials[i]); current = widget->atlasMultiplyBlendMaterials[i]; if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) { UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(widget->MultiplyBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, texture); widget->atlasMultiplyBlendMaterials[i] = material; } widget->pageToMultiplyBlendMaterial.Add(currPage, widget->atlasMultiplyBlendMaterials[i]); current = widget->atlasScreenBlendMaterials[i]; if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) { UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(widget->ScreenBlendMaterial, widget); material->SetTextureParameterValue(widget->TextureParameterName, texture); widget->atlasScreenBlendMaterials[i] = material; } widget->pageToScreenBlendMaterial.Add(currPage, widget->atlasScreenBlendMaterials[i]); } } // self->UpdateMesh(LayerId, OutDrawElements, AllottedGeometry, widget->skeleton); } //return LayerId; self->renderData.IndexData.SetNumUninitialized(6); uint32* indexData = (uint32*)renderData.IndexData.GetData(); indexData[0] = 0; indexData[1] = 1; indexData[2] = 2; indexData[3] = 2; indexData[4] = 3; indexData[5] = 0; self->renderData.VertexData.SetNumUninitialized(4); FSlateVertex* vertexData = (FSlateVertex*)renderData.VertexData.GetData(); FVector2D offset = AllottedGeometry.AbsolutePosition; FColor white = FColor(0xffffffff); float width = AllottedGeometry.GetAbsoluteSize().X; float height = AllottedGeometry.GetAbsoluteSize().Y; setVertex(&vertexData[0], 0, 0, 0, 0, white, offset); setVertex(&vertexData[1], width, 0, 1, 0, white, offset); setVertex(&vertexData[2], width, height, 1, 1, white, offset); setVertex(&vertexData[3], 0, height, 0, 1, white, offset); if (brush && renderData.VertexData.Num() > 0 && renderData.IndexData.Num() > 0) { FSlateShaderResourceProxy* shaderResource = FSlateDataPayload::ResourceManager->GetShaderResource(widget->Brush); FSlateResourceHandle resourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(widget->Brush); if (shaderResource) FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, resourceHandle, renderData.VertexData, renderData.IndexData, nullptr, 0, 0); } return LayerId; }
int32 FAnimationSection::OnPaintSection( const FGeometry& AllottedGeometry, const FSlateRect& SectionClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, bool bParentEnabled ) const { UMovieSceneAnimationSection* AnimSection = Cast<UMovieSceneAnimationSection>(&Section); FTimeToPixel TimeToPixelConverter( AllottedGeometry, TRange<float>( Section.GetStartTime(), Section.GetEndTime() ) ); // Add a box for the section FSlateDrawElement::MakeBox( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), FEditorStyle::GetBrush("Sequencer.GenericSection.Background"), SectionClippingRect, ESlateDrawEffect::None, FLinearColor(0.7f, 0.4f, 0.7f, 1.f) ); // Darken the part that doesn't have animation if (AnimSection->GetAnimationStartTime() > AnimSection->GetStartTime()) { float StartDarkening = AnimSection->GetStartTime(); float EndDarkening = FMath::Min(AnimSection->GetAnimationStartTime(), AnimSection->GetEndTime()); float StartPixels = TimeToPixelConverter.TimeToPixel(StartDarkening); float EndPixels = TimeToPixelConverter.TimeToPixel(EndDarkening); FSlateDrawElement::MakeBox( OutDrawElements, LayerId + 1, AllottedGeometry.ToPaintGeometry(FVector2D(StartPixels, 0), FVector2D(EndPixels - StartPixels, AllottedGeometry.Size.Y)), FEditorStyle::GetBrush("WhiteTexture"), SectionClippingRect, ESlateDrawEffect::None, FLinearColor(0.f, 0.f, 0.f, 0.3f) ); } // Add lines where the animation starts and ends/loops float CurrentTime = AnimSection->GetAnimationStartTime(); while (CurrentTime < AnimSection->GetEndTime()) { if (CurrentTime > AnimSection->GetStartTime()) { float CurrentPixels = TimeToPixelConverter.TimeToPixel(CurrentTime); TArray<FVector2D> Points; Points.Add(FVector2D(CurrentPixels, 0)); Points.Add(FVector2D(CurrentPixels, AllottedGeometry.Size.Y)); FSlateDrawElement::MakeLines( OutDrawElements, LayerId + 2, AllottedGeometry.ToPaintGeometry(), Points, SectionClippingRect ); } CurrentTime += AnimSection->GetAnimationDuration(); } return LayerId+3; }
int32 FSequencerTimeSliderController::OnPaintTimeSlider( bool bMirrorLabels, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { const bool bEnabled = bParentEnabled; const ESlateDrawEffect::Type DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get(); const float LocalViewRangeMin = LocalViewRange.GetLowerBoundValue(); const float LocalViewRangeMax = LocalViewRange.GetUpperBoundValue(); const float LocalSequenceLength = LocalViewRangeMax-LocalViewRangeMin; FVector2D Scale = FVector2D(1.0f,1.0f); if ( LocalSequenceLength > 0) { FScrubRangeToScreen RangeToScreen( LocalViewRange, AllottedGeometry.Size ); const float MajorTickHeight = 9.0f; FDrawTickArgs Args; Args.AllottedGeometry = AllottedGeometry; Args.bMirrorLabels = bMirrorLabels; Args.bOnlyDrawMajorTicks = false; Args.TickColor = FLinearColor::White; Args.ClippingRect = MyClippingRect; Args.DrawEffects = DrawEffects; Args.StartLayer = LayerId; Args.TickOffset = bMirrorLabels ? 0.0f : FMath::Abs( AllottedGeometry.Size.Y - MajorTickHeight ); Args.MajorTickHeight = MajorTickHeight; DrawTicks( OutDrawElements, RangeToScreen, Args ); FPaintPlaybackRangeArgs PlaybackRangeArgs( bMirrorLabels ? FEditorStyle::GetBrush("Sequencer.Timeline.PlayRange_Bottom_L") : FEditorStyle::GetBrush("Sequencer.Timeline.PlayRange_Top_L"), bMirrorLabels ? FEditorStyle::GetBrush("Sequencer.Timeline.PlayRange_Bottom_R") : FEditorStyle::GetBrush("Sequencer.Timeline.PlayRange_Top_R"), 6.f ); LayerId = DrawPlaybackRange(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, RangeToScreen, PlaybackRangeArgs); float HalfSize = FMath::CeilToFloat(ScrubHandleSize/2.0f); // Draw the scrub handle float XPos = RangeToScreen.InputToLocalX( TimeSliderArgs.ScrubPosition.Get() ); // Should draw above the text const int32 ArrowLayer = LayerId + 2; FPaintGeometry MyGeometry = AllottedGeometry.ToPaintGeometry( FVector2D( XPos-HalfSize, 0 ), FVector2D( ScrubHandleSize, AllottedGeometry.Size.Y ) ); FLinearColor ScrubColor = InWidgetStyle.GetColorAndOpacityTint(); // @todo Sequencer this color should be specified in the style ScrubColor.A = ScrubColor.A*0.75f; ScrubColor.B *= 0.1f; ScrubColor.G *= 0.2f; FSlateDrawElement::MakeBox( OutDrawElements, ArrowLayer, MyGeometry, bMirrorLabels ? ScrubHandleUp : ScrubHandleDown, MyClippingRect, DrawEffects, ScrubColor ); // Draw the current time next to the scrub handle float Time = TimeSliderArgs.ScrubPosition.Get(); FString FrameString; if (SequencerSnapValues::IsTimeSnapIntervalFrameRate(TimeSliderArgs.Settings->GetTimeSnapInterval()) && TimeSliderArgs.Settings->GetShowFrameNumbers()) { float FrameRate = 1.0f/TimeSliderArgs.Settings->GetTimeSnapInterval(); float FrameTime = Time * FrameRate; int32 Frame = SequencerHelpers::TimeToFrame(Time, FrameRate); const float FrameTolerance = 0.001f; if (FMath::IsNearlyEqual(FrameTime, (float)Frame, FrameTolerance)) { FrameString = FString::Printf( TEXT("%d"), TimeToFrame(Time)); } else { FrameString = FString::Printf( TEXT("%.3f"), FrameTime); } } else { FrameString = FString::Printf( TEXT("%.2f"), Time ); } FSlateFontInfo SmallLayoutFont( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 10 ); const TSharedRef< FSlateFontMeasure > FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); FVector2D TextSize = FontMeasureService->Measure(FrameString, SmallLayoutFont); // Flip the text position if getting near the end of the view range if ((AllottedGeometry.Size.X - XPos) < (TextSize.X + 14.f)) { XPos = XPos - TextSize.X - 12.f; } else { XPos = XPos + 10.f; } FVector2D TextOffset( XPos, Args.bMirrorLabels ? TextSize.Y-6.f : Args.AllottedGeometry.Size.Y - (Args.MajorTickHeight+TextSize.Y) ); FSlateDrawElement::MakeText( OutDrawElements, Args.StartLayer+1, Args.AllottedGeometry.ToPaintGeometry( TextOffset, TextSize ), FrameString, SmallLayoutFont, Args.ClippingRect, Args.DrawEffects, Args.TickColor ); if (MouseDragType == DRAG_SETTING_RANGE) { float MouseStartPosX = RangeToScreen.InputToLocalX(MouseDownRange[0]); float MouseEndPosX = RangeToScreen.InputToLocalX(MouseDownRange[1]); float RangePosX = MouseStartPosX < MouseEndPosX ? MouseStartPosX : MouseEndPosX; float RangeSizeX = FMath::Abs(MouseStartPosX - MouseEndPosX); FSlateDrawElement::MakeBox( OutDrawElements, LayerId+1, AllottedGeometry.ToPaintGeometry( FVector2D(RangePosX, 0.f), FVector2D(RangeSizeX, AllottedGeometry.Size.Y) ), bMirrorLabels ? ScrubHandleDown : ScrubHandleUp, MyClippingRect, DrawEffects, MouseStartPosX < MouseEndPosX ? FLinearColor(0.5f, 0.5f, 0.5f) : FLinearColor(0.25f, 0.3f, 0.3f) ); } return ArrowLayer; } return LayerId; }
int32 SGraphPanel::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { #if SLATE_HD_STATS SCOPE_CYCLE_COUNTER( STAT_SlateOnPaint_SGraphPanel ); #endif CachedAllottedGeometryScaledSize = AllottedGeometry.Size * AllottedGeometry.Scale; //Style used for objects that are the same between revisions FWidgetStyle FadedStyle = InWidgetStyle; FadedStyle.BlendColorAndOpacityTint(FLinearColor(0.45f,0.45f,0.45f,0.45f)); // First paint the background const UEditorExperimentalSettings& Options = *GetDefault<UEditorExperimentalSettings>(); const FSlateBrush* BackgroundImage = FEditorStyle::GetBrush(TEXT("Graph.Panel.SolidBackground")); PaintBackgroundAsLines(BackgroundImage, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); const float ZoomFactor = AllottedGeometry.Scale * GetZoomAmount(); FArrangedChildren ArrangedChildren(EVisibility::Visible); ArrangeChildNodes(AllottedGeometry, ArrangedChildren); // Determine some 'global' settings based on current LOD const bool bDrawShadowsThisFrame = GetCurrentLOD() > EGraphRenderingLOD::LowestDetail; // Because we paint multiple children, we must track the maximum layer id that they produced in case one of our parents // wants to an overlay for all of its contents. // Save LayerId for comment boxes to ensure they always appear below nodes & wires const int32 CommentNodeShadowLayerId = LayerId++; const int32 CommentNodeLayerId = LayerId++; // Save a LayerId for wires, which appear below nodes but above comments // We will draw them later, along with the arrows which appear above nodes. const int32 WireLayerId = LayerId++; const int32 NodeShadowsLayerId = LayerId; const int32 NodeLayerId = NodeShadowsLayerId + 1; int32 MaxLayerId = NodeLayerId; const FVector2D NodeShadowSize = GetDefault<UGraphEditorSettings>()->GetShadowDeltaSize(); const UEdGraphSchema* Schema = GraphObj->GetSchema(); // Draw the child nodes { // When drawing a marquee, need a preview of what the selection will be. const FGraphPanelSelectionSet* SelectionToVisualize = &(SelectionManager.SelectedNodes); FGraphPanelSelectionSet SelectionPreview; if ( Marquee.IsValid() ) { ApplyMarqueeSelection(Marquee, SelectionManager.SelectedNodes, SelectionPreview); SelectionToVisualize = &SelectionPreview; } // Context for rendering node infos FKismetNodeInfoContext Context(GraphObj); TArray<FGraphDiffControl::FNodeMatch> NodeMatches; for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget); // Examine node to see what layers we should be drawing in int32 ShadowLayerId = NodeShadowsLayerId; int32 ChildLayerId = NodeLayerId; // If a comment node, draw in the dedicated comment slots { UObject* NodeObj = ChildNode->GetObjectBeingDisplayed(); if (NodeObj && NodeObj->IsA(UEdGraphNode_Comment::StaticClass())) { ShadowLayerId = CommentNodeShadowLayerId; ChildLayerId = CommentNodeLayerId; } } const bool bNodeIsVisible = FSlateRect::DoRectanglesIntersect( CurWidget.Geometry.GetClippingRect(), MyClippingRect ); if (bNodeIsVisible) { const bool bSelected = SelectionToVisualize->Contains( StaticCastSharedRef<SNodePanel::SNode>(CurWidget.Widget)->GetObjectBeingDisplayed() ); // Handle Node renaming once the node is visible if( bSelected && ChildNode->IsRenamePending() ) { ChildNode->ApplyRename(); } // Draw the node's shadow. if (bDrawShadowsThisFrame || bSelected) { const FSlateBrush* ShadowBrush = ChildNode->GetShadowBrush(bSelected); FSlateDrawElement::MakeBox( OutDrawElements, ShadowLayerId, CurWidget.Geometry.ToInflatedPaintGeometry(NodeShadowSize), ShadowBrush, MyClippingRect ); } // Draw the comments and information popups for this node, if it has any. { const SNodePanel::SNode::FNodeSlot& CommentSlot = ChildNode->GetOrAddSlot( ENodeZone::TopCenter ); float CommentBubbleY = -CommentSlot.Offset.Get().Y; Context.bSelected = bSelected; TArray<FGraphInformationPopupInfo> Popups; { ChildNode->GetNodeInfoPopups(&Context, /*out*/ Popups); } for (int32 PopupIndex = 0; PopupIndex < Popups.Num(); ++PopupIndex) { FGraphInformationPopupInfo& Popup = Popups[PopupIndex]; PaintComment(Popup.Message, CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, Popup.BackgroundColor, /*inout*/ CommentBubbleY, InWidgetStyle); } } int32 CurWidgetsMaxLayerId; { UEdGraphNode* NodeObj = Cast<UEdGraphNode>(ChildNode->GetObjectBeingDisplayed()); /** When diffing nodes, nodes that are different between revisions are opaque, nodes that have not changed are faded */ FGraphDiffControl::FNodeMatch NodeMatch = FGraphDiffControl::FindNodeMatch(GraphObjToDiff, NodeObj, NodeMatches); if (NodeMatch.IsValid()) { NodeMatches.Add(NodeMatch); } const bool bNodeIsDifferent = (!GraphObjToDiff || NodeMatch.Diff()); /* When dragging off a pin, we want to duck the alpha of some nodes */ TSharedPtr< SGraphPin > OnlyStartPin = (1 == PreviewConnectorFromPins.Num()) ? PreviewConnectorFromPins[0].FindInGraphPanel(*this) : TSharedPtr< SGraphPin >(); const bool bNodeIsNotUsableInCurrentContext = Schema->FadeNodeWhenDraggingOffPin(NodeObj, OnlyStartPin.IsValid() ? OnlyStartPin.Get()->GetPinObj() : NULL); const FWidgetStyle& NodeStyleToUse = (bNodeIsDifferent && !bNodeIsNotUsableInCurrentContext)? InWidgetStyle : FadedStyle; // Draw the node.O CurWidgetsMaxLayerId = CurWidget.Widget->Paint( Args.WithNewParent(this), CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, NodeStyleToUse, ShouldBeEnabled( bParentEnabled ) ); } // Draw the node's overlay, if it has one. { // Get its size const FVector2D WidgetSize = CurWidget.Geometry.Size; { TArray<FOverlayBrushInfo> OverlayBrushes; ChildNode->GetOverlayBrushes(bSelected, WidgetSize, /*out*/ OverlayBrushes); for (int32 BrushIndex = 0; BrushIndex < OverlayBrushes.Num(); ++BrushIndex) { FOverlayBrushInfo& OverlayInfo = OverlayBrushes[BrushIndex]; const FSlateBrush* OverlayBrush = OverlayInfo.Brush; if(OverlayBrush != NULL) { FPaintGeometry BouncedGeometry = CurWidget.Geometry.ToPaintGeometry(OverlayInfo.OverlayOffset, OverlayBrush->ImageSize, 1.f); // Handle bouncing const float BounceValue = FMath::Sin(2.0f * PI * BounceCurve.GetLerpLooping()); BouncedGeometry.DrawPosition += (OverlayInfo.AnimationEnvelope * BounceValue * ZoomFactor); CurWidgetsMaxLayerId++; FSlateDrawElement::MakeBox( OutDrawElements, CurWidgetsMaxLayerId, BouncedGeometry, OverlayBrush, MyClippingRect ); } } } { TArray<FOverlayWidgetInfo> OverlayWidgets = ChildNode->GetOverlayWidgets(bSelected, WidgetSize); for (int32 WidgetIndex = 0; WidgetIndex < OverlayWidgets.Num(); ++WidgetIndex) { FOverlayWidgetInfo& OverlayInfo = OverlayWidgets[WidgetIndex]; if(OverlayInfo.Widget->GetVisibility() == EVisibility::Visible) { // call SlatePrepass as these widgets are not in the 'normal' child hierarchy OverlayInfo.Widget->SlatePrepass(); const FGeometry WidgetGeometry = CurWidget.Geometry.MakeChild(OverlayInfo.OverlayOffset, OverlayInfo.Widget->GetDesiredSize(), 1.f); OverlayInfo.Widget->Paint(Args.WithNewParent(this), WidgetGeometry, MyClippingRect, OutDrawElements, CurWidgetsMaxLayerId, InWidgetStyle, bParentEnabled); } } } } MaxLayerId = FMath::Max( MaxLayerId, CurWidgetsMaxLayerId + 1 ); } } } MaxLayerId += 1; // Draw connections between pins if (Children.Num() > 0 ) { //@TODO: Pull this into a factory like the pin and node ones FConnectionDrawingPolicy* ConnectionDrawingPolicy; { ConnectionDrawingPolicy = Schema->CreateConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); if (!ConnectionDrawingPolicy) { if (Schema->IsA(UAnimationGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FAnimGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UAnimationStateMachineSchema::StaticClass())) { ConnectionDrawingPolicy = new FStateMachineConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UEdGraphSchema_K2::StaticClass())) { ConnectionDrawingPolicy = new FKismetConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(USoundCueGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FSoundCueGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UMaterialGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FMaterialGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else { ConnectionDrawingPolicy = new FConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements); } } } TArray<TSharedPtr<SGraphPin>> OverridePins; for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr<SGraphPin> Pin = Handle.FindInGraphPanel(*this); if (Pin.IsValid()) { OverridePins.Add(Pin); } } ConnectionDrawingPolicy->SetHoveredPins(CurrentHoveredPins, OverridePins, TimeSinceMouseEnteredPin); ConnectionDrawingPolicy->SetMarkedPin(MarkedPin); // Get the set of pins for all children and synthesize geometry for culled out pins so lines can be drawn to them. TMap<TSharedRef<SWidget>, FArrangedWidget> PinGeometries; TSet< TSharedRef<SWidget> > VisiblePins; for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex) { TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(Children[ChildIndex]); // If this is a culled node, approximate the pin geometry to the corner of the node it is within if (IsNodeCulled(ChildNode, AllottedGeometry)) { TArray< TSharedRef<SWidget> > NodePins; ChildNode->GetPins(NodePins); const FVector2D NodeLoc = ChildNode->GetPosition(); const FGeometry SynthesizedNodeGeometry(GraphCoordToPanelCoord(NodeLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); for (TArray< TSharedRef<SWidget> >::TConstIterator NodePinIterator(NodePins); NodePinIterator; ++NodePinIterator) { const SGraphPin& PinWidget = static_cast<const SGraphPin&>((*NodePinIterator).Get()); FVector2D PinLoc = NodeLoc + PinWidget.GetNodeOffset(); const FGeometry SynthesizedPinGeometry(GraphCoordToPanelCoord(PinLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); PinGeometries.Add(*NodePinIterator, FArrangedWidget(*NodePinIterator, SynthesizedPinGeometry)); } // Also add synthesized geometries for culled nodes ArrangedChildren.AddWidget( FArrangedWidget(ChildNode, SynthesizedNodeGeometry) ); } else { ChildNode->GetPins(VisiblePins); } } // Now get the pin geometry for all visible children and append it to the PinGeometries map TMap<TSharedRef<SWidget>, FArrangedWidget> VisiblePinGeometries; { this->FindChildGeometries(AllottedGeometry, VisiblePins, VisiblePinGeometries); PinGeometries.Append(VisiblePinGeometries); } // Draw preview connections (only connected on one end) if (PreviewConnectorFromPins.Num() > 0) { for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr< SGraphPin > CurrentStartPin = Handle.FindInGraphPanel(*this); if (!CurrentStartPin.IsValid()) { continue; } const FArrangedWidget* PinGeometry = PinGeometries.Find( CurrentStartPin.ToSharedRef() ); if (PinGeometry != NULL) { FVector2D StartPoint; FVector2D EndPoint; if (CurrentStartPin->GetDirection() == EGPD_Input) { StartPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; EndPoint = FGeometryHelper::VerticalMiddleLeftOf( PinGeometry->Geometry ) - FVector2D(ConnectionDrawingPolicy->ArrowRadius.X, 0); } else { StartPoint = FGeometryHelper::VerticalMiddleRightOf( PinGeometry->Geometry ); EndPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; } ConnectionDrawingPolicy->DrawPreviewConnector(PinGeometry->Geometry, StartPoint, EndPoint, CurrentStartPin.Get()->GetPinObj()); } //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->SetIncompatiblePinDrawState(CurrentStartPin, VisiblePins); } } else { //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->ResetIncompatiblePinDrawState(VisiblePins); } // Draw all regular connections ConnectionDrawingPolicy->Draw(PinGeometries, ArrangedChildren); delete ConnectionDrawingPolicy; } // Draw a shadow overlay around the edges of the graph ++MaxLayerId; PaintSurroundSunkenShadow(FEditorStyle::GetBrush(TEXT("Graph.Shadow")), AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); if(ShowGraphStateOverlay.Get()) { const FSlateBrush* BorderBrush = nullptr; if((GEditor->bIsSimulatingInEditor || GEditor->PlayWorld != NULL)) { // Draw a surrounding indicator when PIE is active, to make it clear that the graph is read-only, etc... BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.PlayInEditor")); } else if(!IsEditable.Get()) { // Draw a different border when we're not simulating but the graph is read-only BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.ReadOnlyBorder")); } if(BorderBrush) { // Actually draw the border FSlateDrawElement::MakeBox( OutDrawElements, MaxLayerId, AllottedGeometry.ToPaintGeometry(), BorderBrush, MyClippingRect ); } } // Draw the marquee selection rectangle PaintMarquee(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); // Draw the software cursor ++MaxLayerId; PaintSoftwareCursor(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); return MaxLayerId; }
FReply FSequencerTimeSliderController::OnMouseButtonUp( SWidget& WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { bool bHandleLeftMouseButton = MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && WidgetOwner.HasMouseCapture(); bool bHandleRightMouseButton = MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && WidgetOwner.HasMouseCapture() && TimeSliderArgs.AllowZoom ; if ( bHandleRightMouseButton ) { if (!bPanning) { // Open a context menu if allowed if (ContextMenuSupression == 0 && TimeSliderArgs.PlaybackRange.IsSet()) { FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size ); FVector2D CursorPos = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); float MouseValue = RangeToScreen.LocalXToInput( CursorPos.X ); if (TimeSliderArgs.Settings->GetIsSnapEnabled()) { MouseValue = TimeSliderArgs.Settings->SnapTimeToInterval(MouseValue); } TSharedRef<SWidget> MenuContent = OpenSetPlaybackRangeMenu(MouseValue); FSlateApplication::Get().PushMenu( WidgetOwner.AsShared(), MouseEvent.GetEventPath() != nullptr ? *MouseEvent.GetEventPath() : FWidgetPath(), MenuContent, MouseEvent.GetScreenSpacePosition(), FPopupTransitionEffect( FPopupTransitionEffect::ContextMenu ) ); return FReply::Handled().SetUserFocus(MenuContent, EFocusCause::SetDirectly).ReleaseMouseCapture(); } // return unhandled in case our parent wants to use our right mouse button to open a context menu return FReply::Unhandled().ReleaseMouseCapture(); } bPanning = false; return FReply::Handled().ReleaseMouseCapture(); } else if ( bHandleLeftMouseButton ) { if (MouseDragType == DRAG_START_RANGE) { TimeSliderArgs.OnEndPlaybackRangeDrag.ExecuteIfBound(); } // Set the end range time? else if (MouseDragType == DRAG_END_RANGE) { TimeSliderArgs.OnEndPlaybackRangeDrag.ExecuteIfBound(); } else if (MouseDragType == DRAG_SETTING_RANGE) { FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size ); FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition()); float NewValue = RangeToScreen.LocalXToInput(CursorPos.X); if ( TimeSliderArgs.Settings->GetIsSnapEnabled() ) { NewValue = TimeSliderArgs.Settings->SnapTimeToInterval(NewValue); } float DownValue = MouseDownRange[0]; if ( TimeSliderArgs.Settings->GetIsSnapEnabled() ) { DownValue = TimeSliderArgs.Settings->SnapTimeToInterval(DownValue); } // Zoom in if (NewValue > DownValue) { // push the current value onto the stack RangeStack.Add(FVector2D(TimeSliderArgs.ViewRange.Get().GetLowerBoundValue(), TimeSliderArgs.ViewRange.Get().GetUpperBoundValue())); } // Zoom out else if (RangeStack.Num()) { // pop the stack FVector2D LastRange = RangeStack.Pop(); DownValue = LastRange[0]; NewValue = LastRange[1]; } TimeSliderArgs.OnViewRangeChanged.ExecuteIfBound(TRange<float>(DownValue, NewValue), EViewRangeInterpolation::Immediate); if( !TimeSliderArgs.ViewRange.IsBound() ) { // The output is not bound to a delegate so we'll manage the value ourselves TimeSliderArgs.ViewRange.Set( TRange<float>( DownValue, NewValue ) ); } } else { TimeSliderArgs.OnEndScrubberMovement.ExecuteIfBound(); FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size ); FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition()); float NewValue = RangeToScreen.LocalXToInput(CursorPos.X); if ( TimeSliderArgs.Settings->GetIsSnapEnabled() && TimeSliderArgs.Settings->GetSnapPlayTimeToInterval() ) { NewValue = TimeSliderArgs.Settings->SnapTimeToInterval(NewValue); } CommitScrubPosition( NewValue, /*bIsScrubbing=*/false ); } MouseDragType = DRAG_NONE; return FReply::Handled().ReleaseMouseCapture(); } return FReply::Unhandled(); }
int32 FSequencerTimeSliderController::OnPaintSectionView( const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, bool bEnabled, const FPaintSectionAreaViewArgs& Args ) const { const ESlateDrawEffect::Type DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get(); float LocalScrubPosition = TimeSliderArgs.ScrubPosition.Get(); float ViewRange = LocalViewRange.Size<float>(); float PixelsPerInput = ViewRange > 0 ? AllottedGeometry.Size.X / ViewRange : 0; float LinePos = (LocalScrubPosition - LocalViewRange.GetLowerBoundValue()) * PixelsPerInput; FScrubRangeToScreen RangeToScreen( LocalViewRange, AllottedGeometry.Size ); if (Args.PlaybackRangeArgs.IsSet()) { LayerId = DrawPlaybackRange(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, RangeToScreen, Args.PlaybackRangeArgs.GetValue()); } if( Args.bDisplayTickLines ) { static FLinearColor TickColor(0.f, 0.f, 0.f, 0.3f); // Draw major tick lines in the section area FDrawTickArgs DrawTickArgs; DrawTickArgs.AllottedGeometry = AllottedGeometry; DrawTickArgs.bMirrorLabels = false; DrawTickArgs.bOnlyDrawMajorTicks = true; DrawTickArgs.TickColor = TickColor; DrawTickArgs.ClippingRect = MyClippingRect; DrawTickArgs.DrawEffects = DrawEffects; // Draw major ticks under sections DrawTickArgs.StartLayer = LayerId-1; // Draw the tick the entire height of the section area DrawTickArgs.TickOffset = 0.0f; DrawTickArgs.MajorTickHeight = AllottedGeometry.Size.Y; DrawTicks( OutDrawElements, RangeToScreen, DrawTickArgs ); } if( Args.bDisplayScrubPosition ) { // Draw a line for the scrub position TArray<FVector2D> LinePoints; LinePoints.AddUninitialized(2); LinePoints[0] = FVector2D( 0.0f, 0.0f ); LinePoints[1] = FVector2D( 0.0f, FMath::RoundToFloat( AllottedGeometry.Size.Y ) ); FSlateDrawElement::MakeLines( OutDrawElements, LayerId+1, AllottedGeometry.ToPaintGeometry( FVector2D(LinePos, 0.0f ), FVector2D(1.0f,1.0f) ), LinePoints, MyClippingRect, DrawEffects, FLinearColor::White, false ); } return LayerId; }
FReply FSequencerTimeSliderController::OnMouseMove( SWidget& WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { if ( WidgetOwner.HasMouseCapture() ) { if (MouseEvent.IsMouseButtonDown( EKeys::RightMouseButton )) { if (!bPanning) { DistanceDragged += FMath::Abs( MouseEvent.GetCursorDelta().X ); if ( DistanceDragged > FSlateApplication::Get().GetDragTriggerDistance() ) { bPanning = true; } } else { TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get(); float LocalViewRangeMin = LocalViewRange.GetLowerBoundValue(); float LocalViewRangeMax = LocalViewRange.GetUpperBoundValue(); FScrubRangeToScreen ScaleInfo( LocalViewRange, MyGeometry.Size ); FVector2D ScreenDelta = MouseEvent.GetCursorDelta(); FVector2D InputDelta; InputDelta.X = ScreenDelta.X/ScaleInfo.PixelsPerInput; float NewViewOutputMin = LocalViewRangeMin - InputDelta.X; float NewViewOutputMax = LocalViewRangeMax - InputDelta.X; ClampViewRange(NewViewOutputMin, NewViewOutputMax); SetViewRange(NewViewOutputMin, NewViewOutputMax, EViewRangeInterpolation::Immediate); } } else if (MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton )) { TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get(); DistanceDragged += FMath::Abs( MouseEvent.GetCursorDelta().X ); if ( MouseDragType == DRAG_NONE ) { if ( DistanceDragged > FSlateApplication::Get().GetDragTriggerDistance() ) { FScrubRangeToScreen RangeToScreen(LocalViewRange, MyGeometry.Size); const float ScrubPosition = TimeSliderArgs.ScrubPosition.Get(); TRange<float> PlaybackRange = TimeSliderArgs.PlaybackRange.Get(); float LocalMouseDownPos = RangeToScreen.InputToLocalX(MouseDownRange[0]); // Favor dragging the end position if (HitTestPlaybackEnd(RangeToScreen, PlaybackRange, LocalMouseDownPos, ScrubPosition)) { MouseDragType = DRAG_END_RANGE; TimeSliderArgs.OnBeginPlaybackRangeDrag.ExecuteIfBound(); } else if (HitTestPlaybackStart(RangeToScreen, PlaybackRange, LocalMouseDownPos, ScrubPosition)) { MouseDragType = DRAG_START_RANGE; TimeSliderArgs.OnBeginPlaybackRangeDrag.ExecuteIfBound(); } else if (FSlateApplication::Get().GetModifierKeys().AreModifersDown(EModifierKey::Control)) { MouseDragType = DRAG_SETTING_RANGE; } else { MouseDragType = DRAG_SCRUBBING_TIME; TimeSliderArgs.OnBeginScrubberMovement.ExecuteIfBound(); } } } else { FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size ); FVector2D CursorPos = MyGeometry.AbsoluteToLocal( MouseEvent.GetLastScreenSpacePosition() ); float NewValue = RangeToScreen.LocalXToInput( CursorPos.X ); // Set the start range time? if (MouseDragType == DRAG_START_RANGE) { if (TimeSliderArgs.Settings->GetIsSnapEnabled()) { NewValue = TimeSliderArgs.Settings->SnapTimeToInterval(NewValue); } SetPlaybackRangeStart(NewValue); } // Set the end range time? else if(MouseDragType == DRAG_END_RANGE) { if (TimeSliderArgs.Settings->GetIsSnapEnabled()) { NewValue = TimeSliderArgs.Settings->SnapTimeToInterval(NewValue); } SetPlaybackRangeEnd(NewValue); } else if (MouseDragType == DRAG_SCRUBBING_TIME) { if ( TimeSliderArgs.Settings->GetIsSnapEnabled() && TimeSliderArgs.Settings->GetSnapPlayTimeToInterval() ) { NewValue = TimeSliderArgs.Settings->SnapTimeToInterval(NewValue); } // Delegate responsibility for clamping to the current viewrange to the client CommitScrubPosition( NewValue, /*bIsScrubbing=*/true ); } else if (MouseDragType == DRAG_SETTING_RANGE) { MouseDownRange[1] = NewValue; } } } return FReply::Handled(); } return FReply::Unhandled(); }