Skip to content
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

Fix placing objects via touch in editor not working sometimes #30411

Merged
merged 8 commits into from
Nov 13, 2024

Conversation

frenzibyte
Copy link
Member

Placing objects in editor requires the presence of a PlacementBlueprint that responds to the input of the user placing the object. But placement blueprints are programmed to be removed when the user's cursor is outside the compose area.

Similar to the symptoms in #30263, in the case of touch input, if the current position of the cursor (led by where the user has tapped the screen last time) is currently outside the compose area, then if the user tries to tap on the compose screen to place an object, nothing happens because there's no PlacementBlueprint present at the point of the user tapping the compose area.

Unlike #30263, this cannot be resolved by responding to mouse movement events and create a PlacementBlueprint right before the subsequent mouse click event is raised, because adding a drawable in the scene requires waiting a full update frame cycle in order for the drawable to become alive (which is a prerequisite for the drawable to be considered in the input queue).

Instead, this PR resolves the issue by making placement blueprints always exist, and change their visibility state instead. This required a minor refactor around the IPlacementHandler interface to better work with the new behaviour.

ScreenRecording_10-23-2024.16-55-59_1.MP4

…input

More specifically, this fixes placement blueprints not beginning placement when using touch input while the cursor was previously outside compose area, due to the placement blueprint not existing (removed from the scene by `ComposeBlueprintContainer`).
@@ -127,6 +129,16 @@ protected override bool Handle(UIEvent e)
}
}

protected override void PopIn()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want a fade.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought it may be a nice touch, no problem.

@peppy
Copy link
Member

peppy commented Oct 24, 2024

I think the direction seems okay. Will need some conflict resolution on tests (need to be moved to new class).

@peppy peppy self-requested a review October 25, 2024 07:17
@peppy
Copy link
Member

peppy commented Oct 25, 2024

In initial testing I can't find any edge cases that don't match previous behaviour. But reviewing this change is a bit arduous due to the amount of weird logic surrounding the placement object (and the change doesn't simplify but instead adds more new state).

Will need some time to work through this.

@peppy peppy self-requested a review November 13, 2024 05:58
@peppy peppy merged commit 78084e3 into ppy:master Nov 13, 2024
9 of 13 checks passed
@frenzibyte frenzibyte deleted the editor-slider-touch-support-2 branch November 13, 2024 06:43
bdach added a commit to bdach/osu that referenced this pull request Jan 8, 2025
- Closes ppy#31423.
- Regressed in ppy#30411.

Admittedly, I don't completely understand all of the pieces here,
because code quality of this placement blueprint code is ALL-CAPS
ATROCIOUS, but I believe the failure mode to be something along the
lines of:

- User activates juice stream tool, blueprint gets created in initial
  state. It reads in a mouse position far outside of the playfield, and
  sets internal positioning appropriately.
- When the user moves the mouse into the bounds of the playfield, some
  positions update (the ones inside `UpdateTimeAndPosition()`, but the
  fruit markers are for *nested* objects, and
  `updateHitObjectFromPath()` is responsible for updating those...
  however, it only fires if the `editablePath.PathId` changes, which it
  won't here, because there is only one path vertex until the user
  commits the starting point of the juice stream and it's always at
  (0,0).
- Therefore the position of the starting fruit marker remains bogus
  until left click, at which point the path changes and everything
  returns to *relative* sanity.

An alternative solution that shows this hypothesis to be true follows in
the diff below:

diff --cc osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs
index 7b57dac36e,7b57dac36e..21cc260462
--- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs
@@@ -88,10 -88,10 +88,9 @@@ namespace osu.Game.Rulesets.Catch.Edit.
              switch (PlacementActive)
              {
                  case PlacementState.Waiting:
--                    if (!(result.Time is double snappedTime)) return;
--
                      HitObject.OriginalX = ToLocalSpace(result.ScreenSpacePosition).X;
--                    HitObject.StartTime = snappedTime;
++                    if (result.Time is double snappedTime)
++                        HitObject.StartTime = snappedTime;
                      break;

                  case PlacementState.Active:
@@@ -107,21 -107,21 +106,13 @@@
              Vector2 startPosition = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject);
              editablePath.Position = nestedOutlineContainer.Position = scrollingPath.Position = startPosition;

--            updateHitObjectFromPath();
--        }
--
--        private void updateHitObjectFromPath()
--        {
--            if (lastEditablePathId == editablePath.PathId)
--                return;
++            if (lastEditablePathId != editablePath.PathId)
++                editablePath.UpdateHitObjectFromPath(HitObject);
++            lastEditablePathId = editablePath.PathId;

--            editablePath.UpdateHitObjectFromPath(HitObject);
              ApplyDefaultsToHitObject();
--
              scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject);
              nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject);
--
--            lastEditablePathId = editablePath.PathId;
          }

          private double positionToTime(float relativeYPosition)

The patch essentially relies on inlining the broken method and only
guarding the relevant part of processing behind the path version check
(which is actually updating the path). Everything else that can touch
positions of nesteds (like default application, and the drawable piece
updates) is allowed to happen unconditionally.

On one side, that patch looks more proper, but also does more
processing, and is less safe, so I kinda just went with the easier
option?
bdach added a commit to bdach/osu that referenced this pull request Jan 9, 2025
- Closes ppy#31423.
- Regressed in ppy#30411.

Admittedly, I don't completely understand all of the pieces here,
because code quality of this placement blueprint code is ALL-CAPS
ATROCIOUS, but I believe the failure mode to be something along the
lines of:

- User activates juice stream tool, blueprint gets created in initial
  state. It reads in a mouse position far outside of the playfield, and
  sets internal positioning appropriately.
- When the user moves the mouse into the bounds of the playfield, some
  positions update (the ones inside `UpdateTimeAndPosition()`, but the
  fruit markers are for *nested* objects, and
  `updateHitObjectFromPath()` is responsible for updating those...
  however, it only fires if the `editablePath.PathId` changes, which it
  won't here, because there is only one path vertex until the user
  commits the starting point of the juice stream and it's always at
  (0,0).
- Therefore the position of the starting fruit marker remains bogus
  until left click, at which point the path changes and everything
  returns to *relative* sanity.

The solution essentially relies on inlining the broken method and only
guarding the relevant part of processing behind the path version check
(which is actually updating the path). Everything else that can touch
positions of nesteds (like default application, and the drawable piece
updates) is allowed to happen unconditionally.
TheDark98 pushed a commit to TheDark98/osu that referenced this pull request Jan 14, 2025
- Closes ppy#31423.
- Regressed in ppy#30411.

Admittedly, I don't completely understand all of the pieces here,
because code quality of this placement blueprint code is ALL-CAPS
ATROCIOUS, but I believe the failure mode to be something along the
lines of:

- User activates juice stream tool, blueprint gets created in initial
  state. It reads in a mouse position far outside of the playfield, and
  sets internal positioning appropriately.
- When the user moves the mouse into the bounds of the playfield, some
  positions update (the ones inside `UpdateTimeAndPosition()`, but the
  fruit markers are for *nested* objects, and
  `updateHitObjectFromPath()` is responsible for updating those...
  however, it only fires if the `editablePath.PathId` changes, which it
  won't here, because there is only one path vertex until the user
  commits the starting point of the juice stream and it's always at
  (0,0).
- Therefore the position of the starting fruit marker remains bogus
  until left click, at which point the path changes and everything
  returns to *relative* sanity.

The solution essentially relies on inlining the broken method and only
guarding the relevant part of processing behind the path version check
(which is actually updating the path). Everything else that can touch
positions of nesteds (like default application, and the drawable piece
updates) is allowed to happen unconditionally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Drawing sliders when using a touch device doesn't work as expected
2 participants