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

Introduce common structure for key counters #22654

Merged
merged 41 commits into from
Apr 5, 2023

Conversation

ItsShamed
Copy link
Contributor

@ItsShamed ItsShamed commented Feb 15, 2023

Supersedes #22640

I decided to split the abstraction and the new design implementation as suggested in #22640 (comment)

This PR abstracts the KeyCounter and KeyCounterDisplay in order to simplfy the implementation of more designs.
This also simplifies the creation of more input triggers for the KeyCounters to react to.

Some commits have extended messages, in case the diff is too messy to understand anything.

Most of these commits were just cherry-picked from #22640

To implement different different sources of input for KeyCounter, it
is now possible to create a Trigger class (to inherit) instead of
inheriting KeyCounter. This eases the creation of more input sources
(like for tests) while allowing to implement different UI variants.

That way, if another variant of the key counter needs to implemented
(for whathever reason), this can be done by only inheriting KeyCounter
and changing how things are arranged visually.
This allows for different layouts of display. Idk, maybe someone would
want to mix both variants? (don't do this please). This commit is mostly
prep for further changes.
This caused the Keyboard inputs to register twice, which is not what we
want.
Makes it better to understand their purpose
@peppy
Copy link
Member

peppy commented Feb 16, 2023

I'm not too sure I understand why you've added non-action based key triggers. Before I begin to review, could you explain that?

@ItsShamed ItsShamed closed this Feb 16, 2023
@ItsShamed ItsShamed reopened this Feb 16, 2023
@ItsShamed
Copy link
Contributor Author

miss click was about to answer smh

@ItsShamed
Copy link
Contributor Author

ItsShamed commented Feb 16, 2023

I'm not too sure I understand why you've added non-action based key triggers. Before I begin to review, could you explain that?

If you're talking about KeyCounterKeyboardTrigger and KeyCounterMouseTrigger they basically were KeyCounters themselves before the PR. So in this PR, instead of being KeyCounters, they are now Triggers. The only place they were used is in the tests:

  • master

testCounter = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right),

  • this PR

testCounter = new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)),
new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)),

Some other tests also had custom KeyCounters for custom actions, so they were also converted to Triggers.

I hope I was clear enough, even though I thought this would be obvious to think about at first

74a58fb message is also relevant to look at.

@bdach bdach changed the title Abstracting Key Counter Introduce common structure for key counters Feb 16, 2023
Copy link
Collaborator

@bdach bdach left a comment

Choose a reason for hiding this comment

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

First pass. Mostly examination of structure.

}

protected virtual bool CheckType(KeyCounter key) => true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is very weird. What is this for? As best I can tell it's to avoid mixing up key counters of different drawable implementations?

Hesitantly I'd suggest to make the class a generic (as in KeyCounterDisplay<TKeyCounter> where TKeyCounter : KeyCounter) or something. I'm not sure it's ever going to be valid to mix these?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know what to do for this one, as I feel like there could be a lot of directions, but I don't know which one is the best.

From what I can learn from other generics in the codebase, I could separate the class into generic and non-generic base classes.
However, forcing the new implementations to inherit the generic base only is pretty impossible for this class, as making the constructor internal on the non-generic has pretty much no effect since new implementations will essentially be created in the same assembly.

I could also just make the base class generic without splitting anything, but then it would mean that every occurrence of KeyCounterDisplay would have to be replaced with KeyCounterDisplay<KeyCounter>, and I don't know if it's okay to do that (especially for variable declarations).

Copy link
Member

Choose a reason for hiding this comment

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

Why does the check even need to exist?

`IsCounting` is back being an auto-property.
`countPresses` is now encapsulated and being exposed as an
`IBindable<int>` via `CountPresses`
As for the second suggestion in
ppy#22654 (comment),
I went with the first one as only one Trigger actually uses this
argument for rewinding.
@ItsShamed
Copy link
Contributor Author

All issues have been addressed (hopefully).

peppy
peppy previously approved these changes Mar 7, 2023
Comment on lines 52 to 63
public abstract void AddTrigger(InputTrigger trigger);

public void AddTriggerRange(IEnumerable<InputTrigger> triggers) => triggers.ForEach(AddTrigger);

public override void Add(KeyCounter counter)
{
if (!checkType(counter))
throw new InvalidOperationException($"{counter.GetType()} is not a supported counter type. (hint: you may want to use {nameof(AddTrigger)} instead.)");

base.Add(counter);
counter.IsCounting.BindTo(IsCounting);
}
Copy link
Collaborator

@bdach bdach Mar 7, 2023

Choose a reason for hiding this comment

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

I started to apply some final cleanups to this pull but this is where I get stuck. Why is this here still? As in, why is any subclass still allowed to arbitrarily try and add any KeyCounter if the type isn't matching?

Like this is so weird for me, because KeyCounterDisplay is apparently a Container, but then DefaultKeyCounterDisplay is setting InternalChild anyway, and thus is just nullifying the fact that its base class is a container.

I would like to see the process of creating the individual key counters and placing them in the key counter display's layout entirely encapsulated within the key counter display, because it shouldn't ever make sense to mix and match key counter implementations. It should be fundamentally impossible to misuse the component. This would be done by making KeyCounter a plain CompositeDrawable, with the expectation that implementors of AddTrigger() should in response create the KeyCounter they want and place it in the layout of the key counter display according to expectation.

Copy link
Member

Choose a reason for hiding this comment

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

I did consider guarding against this further but wasn't sure it was worth the effort. Don't disagree with direction though.

Copy link
Member

Choose a reason for hiding this comment

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

That said, I did initially try to refactor to a CompositeDrawable but it gets messy since CompositeDrawable doesn't support Content overrides.

As can be seen by 44297a7.

So I'm not 100%, see what you think I guess.

/// <summary>
/// The <see cref="KeyCounter"/>s contained in this <see cref="KeyCounterDisplay"/>.
/// </summary>
public abstract Container<KeyCounter> Counters { get; }
Copy link
Collaborator

@bdach bdach Mar 8, 2023

Choose a reason for hiding this comment

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

This being a Container<KeyCounter> again means the last 3 commits actually changed nothing in the state of affairs, because if it's exposed as a container, then you can add anything you like again through it.

Can we revert 5d15426? I was honestly quite fine with the IEnumerable<KeyCounter>. I don't find it at all messy. The only consumers of this property are: test scenes, and RulesetInputManager. It's very contained.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The purpose of that commit was to fix this
https://github.com/ppy/osu/actions/runs/4360048022
Does that mean that I need to remove the checks for the counter flows visibility in TestSceneHUDOverlay? This is the only way I see to solve the failures (plus I think that this check will most likely be irrelevant as new variants get implemented)

Copy link
Collaborator

Choose a reason for hiding this comment

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

How does changing that from IEnumerable to Container fix test failures? That's an alarm bell if you ask me. Just changing the returned type fix it? Can you explain why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because of this??

private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();

Copy link
Contributor Author

@ItsShamed ItsShamed Mar 8, 2023

Choose a reason for hiding this comment

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

Actually the more I look into it, the more the failure feels weird to me [the failures]

Copy link
Collaborator

Choose a reason for hiding this comment

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

Well this is where I would just fix the test to not use .ChildrenOfType<>(). But maybe it's just me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll get into this later, I'm really not focused right now.

Copy link
Contributor Author

@ItsShamed ItsShamed Mar 9, 2023

Choose a reason for hiding this comment

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

I've pushed a potential fix. Tbh I'm a little bit lost rn so I don't know if this fix is heading towards a right direction, sorry.

As suggested by bdach as this would make the last two commits useless

Refs: 5d15426
@peppy peppy self-requested a review March 15, 2023 08:38
@@ -43,7 +42,7 @@ public partial class TestSceneSkinnableHUDOverlay : SkinnableTestScene

// best way to check without exposing.
private Drawable hideTarget => hudOverlay.KeyCounter;
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
private Drawable keyCounterFlow => (Drawable)hudOverlay.KeyCounter.Counters;
Copy link
Member

Choose a reason for hiding this comment

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

This is the dodgiest code I've seen in a long time. To the point my brain has trouble comprehending how it even compiles..

@peppy
Copy link
Member

peppy commented Mar 15, 2023

I've pushed remaining menial fixes. Kinda done with this PR at this point so going to go ahead with the merge. Any further changes can be applied in a new PR.

@peppy peppy self-requested a review April 3, 2023 06:33
peppy
peppy previously approved these changes Apr 3, 2023
peppy
peppy previously approved these changes Apr 3, 2023
@bdach bdach enabled auto-merge April 5, 2023 18:25
@bdach bdach merged commit a4e68e1 into ppy:master Apr 5, 2023
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.

3 participants