• Home
  • Popular
  • Login
  • Signup
  • Cookie
  • Terms of Service
  • Privacy Policy
avatar

Posted by John Dev


08 Jan, 2025

Updated at 20 Jan, 2025

Trait objects with data-driven / ECS style?

I recently learned about this design pattern where you have an integer ID for your "things" and then various arrays containing the "components" that make up those things. The pattern is sometimes called "struct of arrays" or "entity component system" (ECS). I'm trying to use this idea in a fledgling UI library I'm tinkering with. I'm using the slotmap crate for the "arrays".

Maybe I need to forget about trait objects here, but they seem very nice for some things. I had a UI Widget trait that had methods related to layout, rendering, and event handling. Eg:

trait Widget {
    fn try_layout(&mut self, w: f32, h: f32) -> LayoutTry;
    fn layout_decision(&mut self, w: f32, h: f32);
    fn render(&self, rc: &RenderContext, ...) 
    ...
}

Those layout methods implement a kind of "layout protocol". The VStack struct, for example, implemented them to layout children widgets in a vertical stack. I'm tempted to keep these dynamic pieces around, even in the data oriented design. Something like:

widget_geometry: SlotMap<WidgetGeometry>,                // location, size, 
widget_tree:     SecondaryMap<WidgetId, WidgetTreeInfo>, // parents, children
widget_layout:   SecondaryMap<WidgetId, Box<dyn WidgetLayout>>

In effect the widget_layout array would contain vtables for the layout functions. The weird thing is, what is self for those functions? I supposed I could create a kind of proxy struct that contained only the WidgetId (the slot map integer ID).

struct VStackId { id: WidgetId }
impl WidgetLayout for VStackId { .... }

So I'm wondering if anything has been written about this already. So I don't re-invent wheels here.

1 post - 1 participant

Read full topic