-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Refactor rotation handling in editor to facilitate reuse #24341
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty happy with this direction, would say you should carry on.
// this isn't always the case but let's be lenient for now. | ||
return true; | ||
} | ||
SelectedItems = { BindTarget = SelectedItems } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing to note is that as SelectionRotationHandler
isn't a drawable, this binding won't be eagerly cleaned up. I think it's fine as long as only one RotationHandler
is created ever (seems to be the case) and the lifetime is generally the same as the source bindable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good point, and which has been in the back of my mind (as non-drawables binding to drawable bindables has been a direct cause of many ugly bugs in days past), but one I didn't want to spend too much attention on before getting any feedback on the overall direction. I will reconsider making SelectionRotationHandler
a Component
that lives in the hierarchy instead to remedy this; it may even open up opportunities for DI usage. I'll see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess two main questions from me as to how to proceed here:
|
|
I'm a big fan of this change. Not only does this allow changing rotation origin, it will also fix the scale operation losing precision when scaling to 0x and back in the same operation, and allow new scaling features such as preserving the original aspect ratio when pressing shift (like many drawing applications). |
Correct, all of those were among the underlying reasons to take this direction. Hopefully I'll get this out of draft today. No promises though. |
I don't have any more changes to make here for now I don't think, so I took the PR out of draft. Further changes will come in separate PRs. |
RFC. While everything was manually tested to work, opening as draft, as this is a long shot to me direction-wise.
The primary goal of this change is to build on top of it to ship #12045. A preview branch with the implementation of aforementioned issue can be seen here: https://github.com/ppy/osu/compare/master...bdach:osu:precise-rotation-2?expand=1
There are a few core hang-ups in the current editor selection handling flows that made me try this particular direction:
While
SelectionHandler
definesHandleMovement()
,HandleRotation()
,HandleScale()
, etc., it cannot by itself tell if it is even possible to perform those actions, because so far, the only singular component that knows this and uses this knowledge isSelectionBox
. This is an issue when wanting to reuse the rotation/movement availability & performance logic somewhere else, such as in the rotation control mentioned in the issue above.Delivering selection edit flows with continuous preview that the rotation control requires is difficult, because for complete correctness, it is required to store the initial state of the selection when a continuous edit flow is initiated, so that changes made to the rotation operation performed when the rotation popover is opened can be previewed correctly. The one particular problematic case is switching rotation origin from playfield centre to selection centre when the popover is open.
At this point you could say that
EditorChangeHandler
already can do this, via undo. Unfortunately the wayEditorChangeHandler
works, at least in beatmap editor, is that it will just remove and recreate objects which were moved, therefore discarding selection. This is also quite noticeable UI-wise; if you're unlucky, you can see the object disappear for a moment. Trying to leverage undo in the continuous edit flow makes that much more apparent.Fixing this so that undo preserves object instances feels like an objectively worse solution, since it's combining the worst aspects of the snapshot vs delta dichotomy of implementing undo (not only would we be storing lots of data, we would also have to remember to unapply everything correctly, too).
To this end, the proposed direction is probably best summed up by reading
SelectionRotationHandler
and its xmldocs. It offers a bindable to be able to tell if you can perform a rotation, as well as two different APIs to actually perform one: one simple, to perform an instant rotation, and a "continuous" one, which allows the parameter of the operation to be changed to the user's heart's content so long asCommit()
isn't called.Now, a few caveats:
This change is ruleset API breaking in a major way. There is probably a way to make it not so, but I decided not to worry about this that much yet, because I want to see if I will even get agreement on this direction.
If this direction takes, I will incrementally apply it onto all remaining editor operations across all rulesets. I just want to see the reaction this gets first.
Reviewers will likely notice lots of boilerplate in the implementation of
SelectionRotationHandler
, like the fact that 2 of 3 implementations acceptIEditorChangeHandler
, and they also have their ownBindableList
s with selected objects, and pretty much all manage slightly different pieces inBegin()
/Commit()
.Reviewers may also be poised to suggest an overarching interface for this, something similar to
I would ask to at least hold off on this until migration of all operations is completed, but am also relatively certain that doing this would be likely impractical, if not straight up impossible. Some of the constraints in place such as:
SelectionScaleHandler
, it will haveCanScaleX
andCanScaleY
SelectionFlipHandler
, it will not have the continuous interface, because it won't need itSelectionBox
, which now accepts theSelectionRotationHandler
, is generic-unaware and I'd like to keep it that waymean that I think there is likely no appropriate common abstraction.
The diff is huge, which I apologise for; if it helps any, 4622255 is menial, takes up ~300 lines of diffstat, and is trivially splittable into its own PR. I just don't want to overdo ceremony on a long shot PR that I may end up closing pretty much out of the gate if the direction doesn't take.