Closes https://github.com/ppy/osu/issues/29793.
I believe that the sequence of events that makes this happens is as
follows:
- User selects a range of objects. Some of those objects are off-screen,
and thus would be presumed to be not alive - except the blueprint
container forces them to remain alive, because they're part of the
selection.
- User moves the selection to another column, which is implemented by
temporarily removing the objects from the playfield, changing their
column, and re-adding them.
This sort of pattern is supposed to kick off the
`HitObjectUsageTransferred` flow in `HitObjectUsageEventBuffer` - and
it does... for objects that are *currently visible on screen* and thus
would be alive regardless of `SetKeepAlive()`. However, this does not
hold for objects that are off-screen - nothing ensures they are kept
alive again after re-adding, and thus they inadvertently become dead.
- Thus, this doesn't kick off the `BlueprintContainer` flows associated
with transferring objects to another column, and instead fires the
removal flows, which ensure that the off-screen objects that were
being moved are instead deselected.
I tried a few other options but found no better resolution than this -
calling `SetKeepAlive()` directly would require making it public, which
seems like a bad idea. There's really no good way to generically handle
this either, because it is the ruleset that decides that its way of
implementing this operation will be a removal and re-add of objects,
so...
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.
By moving this to a central location, we can avoid invoking the
EditorChangeHandler when there is no selection made. This helps
alleviate the issue pointed out in
https://github.com/ppy/osu/issues/11901, but not fix it completely.