nanogui icon indicating copy to clipboard operation
nanogui copied to clipboard

Dynamic Forms: Flexibility of FormHelper/Memory Leak with AdvancedGridLayout

Open chrisguiney opened this issue 4 years ago • 0 comments

I'm trying to have composable widgets, which may get added or removed based on the underlying data model -- e.g., user adds or removes a shape to a 3d scene, and the form dynamically adds/removes a set of widgets accordingly.

The difficulty I'm having with the FormHelper/AdvancedGridLayout is two fold:

  • FormHelper uses specifically a Window as the parent widget for all sub widgets
  • AdvancedGridLayout will leak memory by maintaining a dangling pointer to any added widgets in it's m_anchor field -- there's no way to deregister a widget, and when used in combination withFormHelper, there's no (ergonomic) way to get the address of any label added by FormHelper

I'm going to attempt to create a similar class to FormHelper that allows adding widgets to an arbitrary parent widget, rather than a specific window. My hope is that by having each composite widget maintain it's own AdvancedGridLayout, I can avoid the memory leak because when the composite widget is removed it will destroy the AdvancedGridLayout that has all of the children widgets registered.

I'm curious about the following:

  • something that has been encountered/considered before
  • are the maintainers interested in any contributions to provide more flexibility? I could imagine the api I describe below being selected via sfinae based on if the parent is a Window* or not to select the existing FormHelper or a new implementation. I'm open to input on what would make it suitable for a contribution if there's interest.
  • is what I'm thinking reasonable/am i doing something totally wrong/not considering something? -- I'm fairly new to using nanogui, so I could very well be overlooking some well understood usage pattern.

My widget tree would look something like:

  • Screen
    • Window:
      • Scene Objects:
        • SphereForm:
          • Label: Min Phi
          • FormWidget
          • Label: Max Phi
          • FormWidget
          • FloatBox
          • ColorForm:
            • Label: Primary Color
            • ColorPicker
          • ColorForm
            • Label: Primary Color
            • ColorPicker
        • SphereForm:
          • Label: Min Phi
          • FormWidget
          • Label: Max Phi
          • FormWidget
          • FloatBox
          • ColorForm:
            • Label: Primary Color
            • ColorPicker
          • ColorForm
            • Label: Primary Color
            • ColorPicker

Ideally my api would look something like:

class FormBuilder {
    ref<Widget> m_parent;
public:
    FormBuilder(Widget* parent) m_parent(parent) {
        parent->set_layout(new AdvancedGridLayout{...})
    }
    add_widget(const std::string&label, Widget *child);
    ...misc helpers to mimic the FormHelper api...
};

class SphereForm : public nanogui::Widget {
    SphereForm(Widget* parent) : nanogui::Widget(parent) {
        auto f = FormBuilder(this);
        f->add_widget("Min Phi", new nanogui::detail::FormWidget<float>{});
        f->add_widget("Max Phi", new nanogui::detail::FormWidget<float>{});
        f->add_widget("Primary Color", new ColorForm{...});
    };
};

class SceneWindow : nanogui::Window {
    SceneWindow(nanogui::Screen* s) : nanogui::Widnow(s, "Scene Objects") {
        add_child(new SphereForm(this));
        add_child(new SphereForm(this));
    }
}

chrisguiney avatar Oct 29 '21 17:10 chrisguiney