Trait-like styling for UI #1522
Replies: 7 comments 1 reply
-
Hi @alice-i-cecile, there's two important snippets from the same discord discussion that I wanted to extract (let me know if this isn't the right way to do it). Also apologies if I've left too much in and should have pared it down more, I've done my best but my brain struggles with this kind of task, so feel free to give me pointers on this or edit it down as you see fit. First snippetCooCooCaCha: One thing I liked about flutter is that it modularizes the layout algorithm in a clean way. For example, not every component has padding but if you want padding you can use the Padding component. That component contains the logic to compute padding. What's also nice about the modular layout approach is that users can implement their own mini-layout algorithms I'll keep using flutter as an example. So in traditional CSS you have a single layout algorithm that needs to take every possible CSS property into account. With flutter, the logic for padding is encapsulated in the Padding class. If you don't use the Padding class then that layout algorithm isn't executed. Same with flex style layouts Cagey: Alice's proposal is specifically about manually applying styles to things - we'd still have to build something to automatically choose what styles go where if that was something we wanted Cole Poirier: What about something like default labels Alice? Where there are certain default styles for UI components, but different from from how CSS cascading works. I would strongly advocate against implementing CSS in bevy. There's a reason the decision was made to do a custom from the ground up UI system for bevy and not use VueJS or React or something else from the web GUI sphere. I think CSS is fatally and fundamentally flawed and we shouldn't tarnish the promise of bevy by tying that boulder around its neck. There's a lot that can be learned from the web; things that were done right as well as an abundance of anti-patterns. Cagey: I'm not suggesting re-implementing CSS Alice: I think the more common thing that you would do is make your own custom WidgetBundles with specific components set to default values, ideally with some nice way to create new bundles based on "X but with the following style labels applied". Second snippetCole quoting Cagey:
Can you elaborate on this? Cagey Like i might want buttons inside popups to be automatically styled one way, but buttons outside popups to be styled another Alice: CooCooCaCha: An alternative is we could allow devs to define styling presets and provide a way to combine presets. Alice: I think the builder pattern I discussed above is a bit clearer CooCooCaCha: True, either way as an alternative to selectors Mdan: Is there a way to query based on parent/child component relationship though? S33n: You can query for Parent, Children and slap marker components based on their relationships ahead of time Mdan: that makes sense, as long as the style definitions aren't dynamic S33n: I'd think it should work even if they were? Cagey Then you need some logic for unapplying a style, which isn't super intuitive how to implement S33n: You'd need logic to re-build the style imo I settled on applying a default style (which effectively unsets everything) Mdan: First pass - just discard and re-create the components when style changes? |
Beta Was this translation helpful? Give feedback.
-
Cathartic notes from @plof27 on the problems with CSS and the trait-like styles proposal:
|
Beta Was this translation helpful? Give feedback.
-
A builder pattern snippet for defining styles: struct DefaultStyle;
impl Style for DefaultStyle{
fn appply(&self, widget: &mut Widget){
widget
.font("Times New Rome")
.background_color("Blue")
.text_weight("Medium")
}
}
struct BoldStyle;
impl Style for DefaultStyle{
fn apply(&self, widget: &mut Widget){
widget
.background_color("Red")
.text_weight("Bold")
}
}
// In some system
{
widget.apply::<DefaultStyle>().apply::<BoldStyle>();
} From me, on Discord. |
Beta Was this translation helpful? Give feedback.
-
Ideas from #ui on discord today CooCooCaCha: (https://discord.com/channels/691052431525675048/743663673393938453/818207087062155306) struct Style {
styles: Vec<&dyn Layoutable>
}
struct Padding(f32);
impl Layoutable for Padding {
// some api that I haven't thought through.
}
// Somewhere else
commands.spawn(TextBundle {
style: Style {
styles: vec![Padding(16.0)]
}
} geom3trik: (https://discord.com/channels/691052431525675048/743663673393938453/818206481215127552) geom3trik: (https://discord.com/channels/691052431525675048/743663673393938453/818208145158438932) geom3trik: (https://discord.com/channels/691052431525675048/743663673393938453/818209917583884338) |
Beta Was this translation helpful? Give feedback.
-
Experiments with styling based on the idea with traits above: https://gist.github.com/rust-play/383c2b96fa97633e26fa3412f38eb12b |
Beta Was this translation helpful? Give feedback.
-
A simple version of styling based on lenses https://gist.github.com/jihiggins/d14774276fe0af48ae0beaeaf1050a34 |
Beta Was this translation helpful? Give feedback.
-
So, proper notes of what I'm thinking of for UI styles:
|
Beta Was this translation helpful? Give feedback.
-
There are a few basic approaches I see to styling:
Approach 1 is clearly bad.
Approach 3 is very powerful, but tends to lead to horribly complex behavior and weird workarounds, just like other OOP.
Approach 2 avoids that, but then you need to write out a new style every single time you change a small detail.
Approach 4 seems like the way to go, but the devil is in the details: how do we handle conflicting fields?
This is the same phenomenon we see with the multiple labels approach proposed for the ECS (see #1375). You have several important fields, you want to set them in some reusable and composable way.
The most sensible approach, IMO, is to use the builder pattern. Apply the "style labels" sequentially, then overwrite other field values if they're already set. This gives you a really nice syntax and mental model, something like
MyWidget.base_style().specialized_style()
and lets you mix and match styles in a way that you can't with the inheritance approach.Under the hood, I think what you'd probably want to do in Bevy is something like make each of those fields a component, and then use commands to apply the styles in order. I'm not entirely confident on that point; there are some issues with that due to the command-processing delay.
(lifted from Discord)
Beta Was this translation helpful? Give feedback.
All reactions