From f8047d6ab6d96bf9c7b87fcf50b93e2b084da2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 23 Jul 2023 19:48:39 +0200 Subject: [PATCH] Migrate skin element rotation handling to `SelectionRotationHandler` --- .../SkinEditor/SkinSelectionHandler.cs | 29 +----- .../SkinSelectionRotationHandler.cs | 94 +++++++++++++++++++ 2 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 4a1ddd9d69..bee973bea0 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -26,31 +26,11 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private SkinEditor skinEditor { get; set; } = null!; - public override bool HandleRotation(float angle) + public override SelectionRotationHandler CreateRotationHandler() => new SkinSelectionRotationHandler(ChangeHandler) { - if (SelectedBlueprints.Count == 1) - { - // for single items, rotate around the origin rather than the selection centre. - ((Drawable)SelectedBlueprints.First().Item).Rotation += angle; - } - else - { - var selectionQuad = getSelectionQuad(); - - foreach (var b in SelectedBlueprints) - { - var drawableItem = (Drawable)b.Item; - - var rotatedPosition = GeometryUtils.RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, selectionQuad.Centre, angle); - updateDrawablePosition(drawableItem, rotatedPosition); - - drawableItem.Rotation += angle; - } - } - - // this isn't always the case but let's be lenient for now. - return true; - } + SelectedItems = { BindTarget = SelectedItems }, + UpdatePosition = updateDrawablePosition + }; public override bool HandleScale(Vector2 scale, Anchor anchor) { @@ -172,7 +152,6 @@ namespace osu.Game.Overlays.SkinEditor { base.OnSelectionChanged(); - SelectionBox.CanRotate = true; SelectionBox.CanScaleX = true; SelectionBox.CanScaleY = true; SelectionBox.CanFlipX = true; diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs new file mode 100644 index 0000000000..e60e2b1e12 --- /dev/null +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Skinning; +using osu.Game.Utils; +using osuTK; + +namespace osu.Game.Overlays.SkinEditor +{ + public class SkinSelectionRotationHandler : SelectionRotationHandler + { + private readonly IEditorChangeHandler? changeHandler; + + public BindableList SelectedItems { get; } = new BindableList(); + public Action UpdatePosition { get; init; } = null!; + + public SkinSelectionRotationHandler(IEditorChangeHandler? changeHandler) + { + this.changeHandler = changeHandler; + + SelectedItems.CollectionChanged += (_, __) => updateState(); + } + + private void updateState() + { + CanRotate.Value = SelectedItems.Count > 0; + } + + private Drawable[]? objectsInRotation; + + private Vector2? defaultOrigin; + private Dictionary? originalRotations; + private Dictionary? originalPositions; + + public override void Begin() + { + if (objectsInRotation != null) + throw new InvalidOperationException($"Cannot {nameof(Begin)} a rotate operation while another is in progress!"); + + changeHandler?.BeginChange(); + + objectsInRotation = SelectedItems.Cast().ToArray(); + originalRotations = objectsInRotation.ToDictionary(d => d, d => d.Rotation); + originalPositions = objectsInRotation.ToDictionary(d => d, d => d.ToScreenSpace(d.OriginPosition)); + defaultOrigin = GeometryUtils.GetSurroundingQuad(objectsInRotation.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray())).Centre; + } + + public override void Update(float rotation, Vector2? origin = null) + { + if (objectsInRotation == null) + throw new InvalidOperationException($"Cannot {nameof(Update)} a rotate operation without calling {nameof(Begin)} first!"); + + Debug.Assert(originalRotations != null && originalPositions != null && defaultOrigin != null); + + if (objectsInRotation.Length == 1 && origin == null) + { + // for single items, rotate around the origin rather than the selection centre by default. + objectsInRotation[0].Rotation = originalRotations.Single().Value + rotation; + return; + } + + var actualOrigin = origin ?? defaultOrigin.Value; + + foreach (var drawableItem in objectsInRotation) + { + var rotatedPosition = GeometryUtils.RotatePointAroundOrigin(originalPositions[drawableItem], actualOrigin, rotation); + UpdatePosition(drawableItem, rotatedPosition); + + drawableItem.Rotation = originalRotations[drawableItem] + rotation; + } + } + + public override void Commit() + { + if (objectsInRotation == null) + throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!"); + + changeHandler?.EndChange(); + + objectsInRotation = null; + originalPositions = null; + originalRotations = null; + defaultOrigin = null; + } + } +}