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

[RFC] Layout helper classes #1610

Open
falbrechtskirchinger opened this issue Jul 19, 2023 · 2 comments
Open

[RFC] Layout helper classes #1610

falbrechtskirchinger opened this issue Jul 19, 2023 · 2 comments

Comments

@falbrechtskirchinger
Copy link
Contributor

Working on the Addon Settings window, I'm really fed up with the task of manually laying out controls. Is there interest in a set of helper classes to simplify building GUI layouts?

I envision a two-step process, first, defining the layout using a builder pattern API, that computes a bunch of rectangles, then adding the controls using those rectangles in order. The builder API would require some redundancy, to spot certain errors early in the build phase and not later in the use phase, where they may be more difficult to locate.
Every node in the layout would be named, auto-generated from the associated control ID using a macro, if applicable. Again, this is to provide meaningful error messages.

Here's what I've come up with so far (this is based on iwAddons.cpp and uses the IDs defined there):

// gives us a string of the ID for helpful error messages
#define LAYOUT_ID(x) static_cast<unsigned>(x), STRINGIZE(x)
#define LAYOUT_NAME(x) STRINGIZE(x)

// clang-format off
auto lyt = LayoutBuilder(/* Window* */ this) // Window* for GetSize()
    .Vertical(/* name for error message */ "root", /* num items */ 3, layout::Spacing(10))
        .Item(LAYOUT_ID(ID_txtAddFeatures), layout::Height(NormalFont.getHeight()))
        .Horizontal(LAYOUT_NAME(ID_grpAddonGroup), 5, layout::Spacing(10), layout::Height(22))
            .Item(LAYOUT_ID(AddonGroup::All))
            // the other buttons
            .Done() // report an error if number of added layout items != 5
        .Horizontal("scrollView", 2, layout::Spacing(10))
            .Item(LAYOUT_ID(ID_grpAddonsStart))
            .Item(LAYOUT_ID(ID_scroll), layout::Width(SCROLLBAR_WIDTH))
            .Done()
        .Done()
    .Done(); // compute rectangles for each leaf node; easy, right?
// clang-format on

// calls lyt(id) -> Rect, forwards to AddText() overload with DrawPoint
// lyt(id) makes sure id matches the expected ID and generates an error otherwise
AddText(ID_txtAddFeatures, lyt, _("Additional features:"), COLOR_YELLOW, FontStyle{}, NormalFont);

ctrlOptionGroup* optiongroup = AddOptionGroup(ID_grpAddonGroup, GroupSelectType::Check);
// could also do the static_cast in the overload and SFINAE on std::is_unsigned_v(std::underlying_type_t<>)
optiongroup->AddTextButton(static_cast<unsigned>(AddonGroup::All), lyt, TextureColor::Green2, _("All"), NormalFont);

// ...

I'm using strong typing for things like widths, heights, etc. to provide more concise overloads. Would probably rely on some TMP to not repeat myself.

No strings would be stored in Release builds and error checking would only be active in Debug builds.

This is just a start. I've put more thought into some aspects and implementation details. Just let me know if I should pursue this or spend my time elsewhere. (I really want this – or something like it – for more UI work I'd like to do.)

@Flow86
Copy link
Member

Flow86 commented Jul 19, 2023

Would be cool to have something like that. That would ease up a lot.

@Flamefire
Copy link
Member

I also like that especially with the similar structure to other GUI frameworks.

What bothers me a bit is the duplication (of the IDs) but I guess splitting the layout from the control-adding also has its benefits.

I'm not sure about the num_items param though: What do we gain by that? I think being able to just add items as you go is a feature. Especially for changes: Adding a new item would then be a single line change instead of 2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants