bool FSpriteGeometryEditMode::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) { bool bHandled = false; FInputEventState InputState(Viewport, Key, Event); // Handle marquee tracking in source region edit mode if (IsEditingGeometry()) { if (SpriteGeometryHelper.IsAddingPolygon()) { if (Key == EKeys::LeftMouseButton) { const int32 HitX = Viewport->GetMouseX(); const int32 HitY = Viewport->GetMouseY(); // Calculate the texture space position of the mouse click FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(Viewport, ViewportClient->GetScene(), ViewportClient->EngineShowFlags)); FSceneView* View = ViewportClient->CalcSceneView(&ViewFamily); const FVector WorldPoint = View->PixelToWorld(HitX, HitY, 0); const FVector2D TexturePoint = SpriteGeometryHelper.GetEditorContext()->WorldSpaceToTextureSpace(WorldPoint); // Add or close the polygon (depending on where the click happened and how) const bool bMakeSubtractiveIfAllowed = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); SpriteGeometryHelper.HandleAddPolygonClick(TexturePoint, bMakeSubtractiveIfAllowed, *View, Event); } else if ((Key == EKeys::BackSpace) && (Event == IE_Pressed)) { SpriteGeometryHelper.DeleteLastVertexFromAddPolygonMode(); } else if (Key == EKeys::Enter) { SpriteGeometryHelper.ResetAddPolygonMode(); } else if (Key == EKeys::Escape) { SpriteGeometryHelper.AbandonAddPolygonMode(); } } else { if (ProcessMarquee(Viewport, Key, Event, true)) { const bool bAddingToSelection = InputState.IsShiftButtonPressed(); //@TODO: control button moves widget? Hopefully make this more consistent when that is changed SelectVerticesInMarquee(ViewportClient, Viewport, bAddingToSelection); } } } //@TODO: Support select-and-drag in a single operation (may involve InputAxis and StartTracking) // Pass keys to standard controls, if we didn't consume input return bHandled ? true : FEdMode::InputKey(ViewportClient, Viewport, Key, Event); }
void FSpriteGeometryEditingHelper::DeleteSelectedItems() { // Determine which vertices or entire shapes should be deleted TSet<FShapeVertexPair> CompositeIndicesSet; TSet<int32> ShapesToDeleteSet; if (IsEditingGeometry()) { FSpriteGeometryCollection& Geometry = GetGeometryChecked(); for (TSharedPtr<FSelectedItem> SelectionIt : GetSelectionSet()) { if (const FSpriteSelectedVertex* SelectedVertex = SelectionIt->CastTo<const FSpriteSelectedVertex>(FSelectionTypes::Vertex)) { CompositeIndicesSet.Add(FShapeVertexPair(SelectedVertex->ShapeIndex, SelectedVertex->VertexIndex)); if (SelectedVertex->IsA(FSelectionTypes::Edge)) // add the "next" point for the edge { const int32 NextIndex = (SelectedVertex->VertexIndex + 1) % Geometry.Shapes[SelectedVertex->ShapeIndex].Vertices.Num(); CompositeIndicesSet.Add(FShapeVertexPair(SelectedVertex->ShapeIndex, NextIndex)); } } else if (const FSpriteSelectedShape* SelectedShape = SelectionIt->CastTo<const FSpriteSelectedShape>(FSelectionTypes::GeometryShape)) { ShapesToDeleteSet.Add(SelectedShape->ShapeIndex); } } } // See if anything else can be deleted bool bCanDeleteNonGeometry = false; for (const TSharedPtr<FSelectedItem> SelectedItem : GetSelectionSet()) { if (SelectedItem->CanBeDeleted()) { bCanDeleteNonGeometry = true; break; } } // Now delete the stuff that was selected in the correct order so that indices aren't messed up const bool bDeletingGeometry = (CompositeIndicesSet.Num() > 0) || (ShapesToDeleteSet.Num() > 0); if (bDeletingGeometry || bCanDeleteNonGeometry) { EditorContext->BeginTransaction(LOCTEXT("DeleteSelectionTransaction", "Delete Selection")); EditorContext->MarkTransactionAsDirty(); if (bDeletingGeometry) { FSpriteGeometryCollection& Geometry = GetGeometryChecked(); // Delete the selected vertices first, as they may cause entire shapes to need to be deleted (sort so we delete from the back first) TArray<FShapeVertexPair> CompositeIndices = CompositeIndicesSet.Array(); CompositeIndices.Sort([](const FShapeVertexPair& A, const FShapeVertexPair& B) { return (A.VertexIndex > B.VertexIndex); }); for (const FShapeVertexPair& Composite : CompositeIndices) { const int32 ShapeIndex = Composite.ShapeIndex; const int32 VertexIndex = Composite.VertexIndex; if (DeleteVertexInPolygonInternal(Geometry, ShapeIndex, VertexIndex)) { ShapesToDeleteSet.Add(ShapeIndex); } } // Delete the selected shapes (plus any shapes that became empty due to selected vertices) if (ShapesToDeleteSet.Num() > 0) { // Sort so we delete from the back first TArray<int32> ShapesToDeleteIndicies = ShapesToDeleteSet.Array(); ShapesToDeleteIndicies.Sort([](const int32& A, const int32& B) { return (A > B); }); for (const int32 ShapeToDeleteIndex : ShapesToDeleteIndicies) { Geometry.Shapes.RemoveAt(ShapeToDeleteIndex); } } Geometry.GeometryType = ESpritePolygonMode::FullyCustom; } // Delete everything else if (bCanDeleteNonGeometry) { for (TSharedPtr<FSelectedItem> SelectedItem : GetSelectionSet()) { if (SelectedItem->CanBeDeleted()) { SelectedItem->DeleteThisItem(); } } } EditorContext->EndTransaction(); } ClearSelectionSet(); ResetAddPolygonMode(); }
bool FSpriteGeometryEditMode::HandleClick(FEditorViewportClient* InViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) { FViewport* Viewport = InViewportClient->Viewport; const bool bIsCtrlKeyDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); const bool bIsShiftKeyDown = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift); const bool bIsAltKeyDown = Viewport->KeyState(EKeys::LeftAlt) || Viewport->KeyState(EKeys::RightAlt); bool bHandled = false; const bool bAllowSelectVertex = !(IsEditingGeometry() && SpriteGeometryHelper.IsAddingPolygon()) && !bIsShiftKeyDown; const bool bClearSelectionModifier = bIsCtrlKeyDown; const bool bDeleteClickedVertex = bIsAltKeyDown; const bool bInsertVertexModifier = bIsShiftKeyDown; HSpriteSelectableObjectHitProxy* SelectedItemProxy = HitProxyCast<HSpriteSelectableObjectHitProxy>(HitProxy); if (bAllowSelectVertex && (SelectedItemProxy != nullptr)) { if (!bClearSelectionModifier) { SpriteGeometryHelper.ClearSelectionSet(); } if (bDeleteClickedVertex) { // Delete selection if (const FSpriteSelectedVertex* SelectedVertex = SelectedItemProxy->Data->CastTo<const FSpriteSelectedVertex>(FSelectionTypes::Vertex)) { SpriteGeometryHelper.ClearSelectionSet(); SpriteGeometryHelper.AddPolygonVertexToSelection(SelectedVertex->ShapeIndex, SelectedVertex->VertexIndex); SpriteGeometryHelper.DeleteSelectedItems(); } else if (const FSpriteSelectedShape* SelectedShape = SelectedItemProxy->Data->CastTo<const FSpriteSelectedShape>(FSelectionTypes::GeometryShape)) { SpriteGeometryHelper.ClearSelectionSet(); SpriteGeometryHelper.AddShapeToSelection(SelectedShape->ShapeIndex); SpriteGeometryHelper.DeleteSelectedItems(); } } else if (Click.GetEvent() == EInputEvent::IE_DoubleClick) { // Double click to select a polygon if (const FSpriteSelectedVertex* SelectedVertex = SelectedItemProxy->Data->CastTo<const FSpriteSelectedVertex>(FSelectionTypes::Vertex)) { SpriteGeometryHelper.ClearSelectionSet(); SpriteGeometryHelper.AddShapeToSelection(SelectedVertex->ShapeIndex); } } else { //@TODO: This needs to be generalized! if (const FSpriteSelectedEdge* SelectedEdge = SelectedItemProxy->Data->CastTo<const FSpriteSelectedEdge>(FSelectionTypes::Edge)) { // Add the next vertex defined by this edge SpriteGeometryHelper.AddPolygonEdgeToSelection(SelectedEdge->ShapeIndex, SelectedEdge->VertexIndex); } else if (const FSpriteSelectedVertex* SelectedVertex = SelectedItemProxy->Data->CastTo<const FSpriteSelectedVertex>(FSelectionTypes::Vertex)) { SpriteGeometryHelper.AddPolygonVertexToSelection(SelectedVertex->ShapeIndex, SelectedVertex->VertexIndex); } else if (const FSpriteSelectedShape* SelectedShape = SelectedItemProxy->Data->CastTo<const FSpriteSelectedShape>(FSelectionTypes::GeometryShape)) { SpriteGeometryHelper.AddShapeToSelection(SelectedShape->ShapeIndex); } else { SpriteGeometryHelper.SelectItem(SelectedItemProxy->Data); } } bHandled = true; } // else if (HWidgetUtilProxy* PivotProxy = HitProxyCast<HWidgetUtilProxy>(HitProxy)) // { // //const bool bUserWantsPaint = bIsLeftButtonDown && ( !GetDefault<ULevelEditorViewportSettings>()->bLeftMouseDragMovesCamera || bIsCtrlDown ); // //findme // WidgetAxis = WidgetProxy->Axis; // // // Calculate the screen-space directions for this drag. // FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues( Viewport, GetScene(), EngineShowFlags )); // FSceneView* View = CalcSceneView(&ViewFamily); // WidgetProxy->CalcVectors(View, FViewportClick(View, this, Key, Event, HitX, HitY), LocalManipulateDir, WorldManipulateDir, DragX, DragY); // bHandled = true; // } else { if (IsEditingGeometry() && !SpriteGeometryHelper.IsAddingPolygon()) { FSpriteGeometryCollection* Geometry = SpriteGeometryHelper.GetGeometryBeingEdited(); if (bInsertVertexModifier) { const FPlane SpritePlane(PaperAxisX, FVector::ZeroVector, PaperAxisY); const FVector WorldPoint = FMath::LinePlaneIntersection(Click.GetOrigin(), Click.GetOrigin() + Click.GetDirection(), SpritePlane); const FVector2D SpriteSpaceClickPoint = SpriteGeometryHelper.GetEditorContext()->WorldSpaceToTextureSpace(WorldPoint); // find a polygon to add vert to bool bFoundShapeToAddTo = false; for (TSharedPtr<FSelectedItem> SelectedItemPtr : SpriteGeometryHelper.GetSelectionSet()) { if (const FSpriteSelectedVertex* SelectedVertex = SelectedItemPtr->CastTo<const FSpriteSelectedVertex>(FSelectionTypes::Vertex)) //@TODO: Inflexible? { SpriteGeometryHelper.AddPointToGeometry(SpriteSpaceClickPoint, SelectedVertex->ShapeIndex); bFoundShapeToAddTo = true; break; } } if (!bFoundShapeToAddTo) { SpriteGeometryHelper.AddPointToGeometry(SpriteSpaceClickPoint); } bHandled = true; } } else if (!IsEditingGeometry()) { // Clicked on the background (missed any proxies), deselect the socket or whatever was selected SpriteGeometryHelper.ClearSelectionSet(); } } return bHandled ? true : FEdMode::HandleClick(InViewportClient, HitProxy, Click); }