Closes https://github.com/ppy/osu/issues/28741.
Regressed in a7b066f3ee.
The intent of the original change there was to ensure that addition
banks being set will put the ternary state toggles in indeterminate
state (to at least provide a visual indication that the selection does
not use a single bank). This would previously not be the case due to
the use of `.All()` in the original condition (a single object/node
was considered to have a bank enabled if and only if *all* samples
within it used it). However the attempt to fix that via switching
to `Any()` was not correct.
The logic used in the offending commit operates on extracted `Samples`
and `NodeSamples` from the selection, and would consider the ternary
toggle:
- fully off if none of the samples/node samples contained a sample with
the given bank,
- indeterminate if the some of the samples/node samples contained a
sample with the given bank,
- fully on if at least one sample from every samples/node samples
contained a sample with the given bank.
This is a *two-tiered* process, as in first a *binary* on/off state is
extracted from each object's samples/node samples, and *then* a ternary
state is extracted from all objects/nodes. This is insufficient to
express the *desired* behaviour, which is that the toggle should be:
- fully off if *none of the individual samples in the selection* use
the given bank,
- indeterminate if *at least one individual sample in the selection*
uses the given bank,
- fully on if *all individual samples in the selection* use the given
bank.
The second wording is flattened, and no longer tries to consider "nodes"
or "objects", it just looks at all of the samples in the selection
without concern as to whether they're from separate objects/nodes
or not.
To explain why this discrepancy caused the bug, consider a single object
with a `soft` normal bank and `drum` addition bank. Selecting the object
would cause a ternary button state update; as per the incorrect logic,
there were two samples on the object and each had its own separate
banks, so two ternary toggles would have their state set to `True`
(rather than the correct `Indeterminate`), thus triggering a bindable
feedback loop that would cause one of these banks to win and actually
overwrite the other.
Note that the addition indeterminate state computation *still* needs
to do the two-tiered process, because there it actually makes sense (for
a selection to have an addition fully on rather than indeterminate,
*every* object/node *must* contain that addition).
- Actually shows scores rather than playlist aggregates (which are
useful... in playlists, where there is more than one item)
- Actually allows scores to be shown by clicking on them
- Doesn't completely break down visually on smaller window sizes
The general appearance is not as polished as the old one in details but
I wanted something quick that we can get out by next weekend.
Also includes the naive method of refetching scores once a new top 50
score is detected. I can add a stagger if required.
At this point its primary usage is the daily challenge event feed, but
the leaderboard will be using this too shortly.
Because the playlists results screen that exists in `master` is
hard-coupled to showing the *local user's* best result on a given
playlist by way of hard-coupling itself to the relevant API request,
allowing show of *arbitrary* score by ID requires a whole bunch of
subclassery as things stand. Oh well.
Class naming is... best effort, due to the above.
The content is already padded enough to have the scrollbar sit on top.
Having the content change padding when the scrollbar appears gives an
unpleasent experience (especially when the scrollbar is hidden at first
but the user increases the content's height by clicking on a dropdown or
something)
With this new order, the logo can be easily moved to display in front of the footer in `SongSelectV2` without breaking experience when footer-based overlays are present. Such overlays (i.e. mod select overlay) will also be dimmed alongside the current screen when a game-wide overlay is open (e.g. settings).
Basically moves `PopoverContainer` to cover both the columns and the customisation panel, so that if the customisation panel is clicked on, the popover container will notice that and hide popovers like the mod preset popover.
I've also removed inheritance from `SearchTextBox` because it contains
logic that might interfere with the internal implementation of dropdown
search bars (focus logic and stuff).
Closes https://github.com/ppy/osu/issues/28587.
As outlined in the issue thread, the tail volume wasn't saving because
it wasn't actually attached to a hitobject properly, and as such the
`LegacyBeatmapEncoder` logic, which is based on hitobjects, did not
pick them up on save.
To fix that, switch to using `NodeSamples` for objects that are
`IHasRepeats`. That has one added complication in that having it work
properly requires changes to the decode side too. That is because the
intent is to allow the user to change the sample settings for each node
(which are specified via `NodeSamples`), as well as "the rest of the
object", which generally means ticks or auxiliary samples like
`sliderslide` (which are specified by `Samples`).
However, up until now, `Samples` always queried the control point
which was _active at the end time of the slider_. This obviously can't
work anymore when converting `NodeSamples` to legacy control points,
because the last node's sample is _also_ at the end time of the slider.
To bypass that, add extra sample points after each node (just out of
reach of the 5ms leniency), which are supposed to control volume of
ticks and/or slides.
Upon testing, this *sort of* has the intended effect in stable, with
the exception of `sliderslide`, which seems to either respect or _not_
respect the relevant volume spec dependent on... not sure what, and not
sure I want to be debugging that. It might be frame alignment, or it
might be the phase of the moon.
I thought this was already being handled, but it turns out that changing
sort mode (and potentially other operations) could break the depth of
display of panels due to pooling and what not.
This ensures consistency and also employs @bdach's suggestion of
reversing the depth above and below the current selection for a better
visual effect.
As discussed in https://github.com/ppy/osu/discussions/28599.
I think this feels better overall, and would like to apply the change
before other design changes to the carousel.
This is the secondary cause of https://github.com/ppy/osu/issues/28577,
because you could do the following:
- Have a break autogenerate itself
- Adjust either end of it to make it mark itself as manually-adjusted
- Remove all objects before or after said break
to end up in a state wherein there are no objects before or after a
break.
The direct fix is still correct because it is still technically possible
to end up in a state wherein a break is before or after all objects
(obvious one is manual `.osu` editing), but this behaviour is also
undesirable for the autogeneration logic.
Regressed in https://github.com/ppy/osu/pull/28399.
To reproduce, enter a playlist that has an item with a rate-changing mod
(rather than create it yourself).
This is happening because `APIRuleset` has `CreateInstance()`
unimplemented:
b4cefe0cc2/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs (L159)
and only triggers when the playlist items in question originate from
web.
This is why it is bad to have interface implementations throw outside of
maybe mock implementations for tests. `CreateInstance()` is a scourge
elsewhere in general, we need way less of it in the codebase (because
while convenient, it's also problematic to implement in online contexts,
and also expensive because reflection).
Normally I would just block keyboard input from going past `ModCustomisationPanel`, but it's a little complicated here since I'm dealing with global action key binding presses, and I also still want actions like `GlobalAction.Back` to get past the customisation panel even if it's expanded.