Until now, the implementation of the overrides in `SelectionBlueprint`
have been confusing to the point where I would just implement by
trial-and-error (or copying from an existing implementation). This was
due to a combination of using "object" space coordinates
(ie. the thing the `Blueprint` is operating on) and screen-space coordinates.
This change switches all event related coordinates to screen-space,
which is how we already handle rotation/scale operations. With the
introduction of other editor types where the related objects are
drawables, this also makes a lot more sense.
Further separates them from `IBeatSnapProvider`'s `SnapTime`, and groups them together more, to prevent confusion between the two interfaces.
Also changes the xmldoc of the reference time to that of `IBeatSnapProvider` for consistency.
Seems `EditorBeatmap` already implements a different kind of `SnapTime` from `IBeatSnapProvider`, so method names here aren't great.
This is very similar to what https://github.com/ppy/osu/pull/12558 is doing, so may need to do some duplicate resolution later, especially surrounding `ClosestBeatSnapDivisor`.
Worth noting that this change makes 1/7, 1/5, etc unsupported for now, as we now rely on `BindableBeatDivisor.VALID_DIVISORS`.
`ensureSourceClockSet()` was intended to only run when the adjustable
source hasn't been set at all yet. As it turns out permitting it to run
unconditionally can break the state of the underlying interpolated
clock. This is caused by the following factors:
* While the decoupleable clock is running, its `CurrentTime` does not
come from either the source clock, or the internal stopwatch; it is
instead calculated using the base `InterpolatingFramedClock` logic.
* A source change of a decoupleable clock seeks the provided source
clock to the decoupleable's current time.
* When an interpolating clock is seeked (decoupleable clock is also
an interpolating one), its interpolation state
(`{Last,Current}InterpolatedTime`) are reset to 0.
* If the interpolating clock determines that its current time is too
far away from the source's time (which was set when the source is
changed), it will ignore the source and instead continue to use
its current time until the source clock has caught up.
Overall, the source change is not really necessary if a source is
already there. The only reason to ensure it was set was to make sure
the first seek of the gameplay clock wasn't performed in decoupled
mode. Therefore, add a guard to make sure the source is only set if
there isn't one already.
The UserFinishedPlaying event may trigger before the event is subscribed
to by SpectatorScreen. For such cases, an extra check is done to make
sure the user is _actually_ playing.