-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
refactor and test ui_focus_system
#4264
Conversation
Ok, apparently the ci fails:
It seems this will depend on koute/stdweb#418 |
For the CI, the workaround is to whitelist the duplicate dependencies. That said, I'm reluctant to pull a unit testing framework into our dependency tree just for this. What benefit does it offer here? |
*interaction = Interaction::None; | ||
} | ||
} | ||
focus_ui( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's useful to have a system whose contents are a single function call: that seems like needless layering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to make it testable. Or do you have other suggestions how to mock Windows
?
I.e. ui_focus_system
is actually meant as a public export of focus_ui::<Windows>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. I think we should try to tackle that directly: I don't want to have to replicate this strategy everywhere.
touches_input: Res<Touches>, | ||
mut node_query: Query<NodeQuery>, | ||
) { | ||
reset_interactions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this living in a separate system at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I extract it? This system currently does two things:
- resetting Interactions to None
- checking the input to set some Interactions to Hovered / Clicked
The coupling between those two things is quite hard as they rely on each other. This becomes quite obvious from the Local<State>
. Actually I wanted to get rid of it, but I did not have a good idea without changing the logic.
|
||
let mouse_clicked = | ||
mouse_button_input.just_pressed(MouseButton::Left) || touches_input.just_released(0); | ||
let cursor_position = match windows.get_cursor_position() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should be able to use the ? operator here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler says: the ?
operator can only be used in a function that returns Result
or Option
(or another type that implements FromResidual
)
Or do you mean I should return an Option here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah my bad; I'd misread what the function does.
) | ||
} | ||
|
||
fn contains_cursor( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like it should be a method on Node or something to that effect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also thought that this function looks a bit odd as a standalone. But is Node
the right place as Node
is actually indifferent to positions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah 🤔 TBH, this feels like functionality that should be pub that we reuse internally, but I can't immediately figure out the best design for that.
*interaction = Interaction::None; | ||
} | ||
|
||
trait CursorResource: Resource { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really dislike the use of the generic plus trait here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see: it's to work around the difficulty mocking Windows
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my other comment - how else would I make Windows
do what I want in a unit test? Everything I tried ended up with a dependency on hardware.
crates/bevy_ui/src/focus.rs
Outdated
|
||
#[cfg(test)] | ||
mod tests { | ||
use rstest::*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that this adds enough value to pull in a testing library on a one-off basis. These tests should be feasibel to rewrite with a helper method instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kinda handcoded it myself now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent, thanks!
} | ||
|
||
#[cfg(test)] | ||
mod tests { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests look good, and we definitely need tests for this piece of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there actually a common practice how to test systems? This TestApp approach is something I came up with in one of my own projects from the Cheatbook suggesting Direct World Access. But I didn't see many tests around in bevy itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see how I did it in one of my external crates here: https://github.com/Leafwing-Studios/leafwing_input_manager/blob/main/tests/integration.rs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I think that this region could use code quality improvements to break up the mega-function, and it definitely needs tests!
I pretty strongly dislike rstest here (I don't think it's clearer, and it pulls in a new dependency). The refactor for the code itself needs a bit more iteration, but it feels like breaking this out into smaller pieces is the right path.
So, WRT the testing strategy: #3835 is the blocker here. IMO we should hold off on this until that's merged, then use the headless windows there to enable us to write proper tests for this code. |
54d4d59
to
99a737e
Compare
} | ||
*interaction = Interaction::Clicked; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I might need to be careful here concerning change detection. Double check.
You might be interested in bevyengine/rfcs#41 and https://github.com/nicopap/ui-navigation. I'm planning to start the bevy PR next week (I still need to update the RFC with the second round of changes). I'm not sold on The |
…ewer spots: - reset everything that should be reset - find hovered nodes - write Hovered/Clicked states
I guess after adding a test that checks change detection, unfortunately most of my changes won't work. At least there are a couple more tests. The " Anyway, I'll leave this PR around. Ping me once headless windows are there. I believe the tests can easily be adapted to work without the extra trait then. |
This looks like it has gone stale, and we are now planning to deprecate |
Objective
While digging through the bevy code, I found that
ui_focus_system
overly complex and quite hard to understand. -> make it easier to understand.Solution