Add filtering functionality to UI constraints

As a Roblox developer, it is currently inefficient and disruptive to design the GUI around UI constraints.
A UI constraint applies to everything inside its parent without discrimination - but this means you need to create containers upon containers (and script them) if your design has some complexity.

An example of this is having a GUI setup like so:

Screenshot_259

I want the ImageLabel Back to be excluded from the constraint as it serves as the background for my GUI.

Why don't I make Holder an image?

The image I am using has a drop shadow so I need to give everything inside it a margin. This is also means I can easily size and move elements without accounting for extra size.

So what can I do? This:
Screenshot_260

Suddenly I have to account for another object and move everything into it. Not only does this slow down the design process, but it also slows down the scripting process.

This isn’t a problem when you’ve only got a few objects - but when you have more it becomes very frustrating and complex, slowing everything down.

If Roblox is able to address your issue, how would it improve your game and/or your development experience? Please be as specific as possible.

If this issue is addressed, scripting and designing GUIs will become more efficient and less complex to the user.
Thank you.

11 Likes

Yep. It’d be great if there was an ‘Adornee’ property for the new UI constraints so you could place them wherever and have them effect the specific frame

4 Likes

It’s not clear to me exactly what feature this is requesting, or what your pain point is. Could you provide an example of what “filtering functionality” would entail here, and what is “very frustrating and complex”? I’m assuming that having these folder-like frames nested very deep, and navigating into them (both in the explorer and via scripts) is what you are finding cumbersome. If that’s the case, well, the TLDR is that filtering has proven itself to not be viable solution to this problem.

In short, the container-based hierarchy is pretty standard for UI layout, and for good reasons, and this is unlikely to change.

The Long Explanation

Roblox’s Frames are just containers, equivalent to div blocks in HTML/CSS documents. If you’ve ever browsed the web with your browser’s developer tools open, you’ll have noticed that many of layers of containers is commonplace. I don’t know exactly what “filtering” would look like in this context, but suppose, for example, UIPageLayout had a blacklist, so you could exempt “Back” from its effects. OK, that solves one small organization problem or needing an extra container, but replaces it with a new and much hairier problem: how to resolve conflicts between children of a container who have mutually-exclusive constraints applied to them. This problem is precisely why document object models have lots of containers, because grouping and explicitly arranging things has proven to be the simplest way to achieve a specific, desired result. This is a problem that many have given a lot of thought to; everyone involved in the decades of development of markup languages for the web, for example.

So my first question to you would be: is there another solution you can think of that would address your pain points? For example, a different way of visualizing the structure of the UI that is easier to edit? Just because a data structure solves a problem well does not mean the interface and tools we have for working with it cannot be improved upon through feature requests!

As for scripting, a very complex and deeply-nested UI is not something a programmer should attempt to handle with equally deep references to things. Beyond maybe 2-3 levels deep, direct paths to elements should be abandoned as a practical solution. In other words, no one should have code that looks like this:

local frameBkgd = player.PlayerGui.Shop.Inventory.Frame.Frame.Hats.BlueHats.FuzzyBlueHats.Frame.Frame.HowDeepDoesThisEvenGo.Frame2.Products.Image.OMGAnotherFrame.Frame.BkgdImageFinally

So how is this solved then?

When you get into developing large, sophisticated UIs, new methods are required to keep things sane and scalable. It will quickly become apparent how incredibly painful it is to maintain deep, explicit paths to things within a large GUI, and to keep it all functional while UI designers are iterating on the design. This is not because they’ve used too many containers, or the organizational structure is flawed, it’s because this approach to coding for it is fragile and not suitable. Traversing a complex GUI’s tree and acquiring references to the things you need programmatic control of is a viable approach. Agree on naming conventions with your designer to identify elements you need references to, or use the new CollectionService. With a few simple rules, you can write flexible code that is tolerant of things like inserting new layers of containers, or changing the number of a certain type of element.

I understand what you’re suggesting. It would not be an Adornee exactly, but a similar A-applies-to-B relationship set up via reference. But I don’t believe that solves the OP’s problem, it does not change the number of containers required. If anything, it makes the whole UI more difficult to assess at a glance, since the constraints could be stored anywhere. Even though you can do this with things like welds, people generally parent welds to one of the things they are connecting (Part0 or Part1), or to a container that holds them both, because the alternative invites a loss of sanity.

3 Likes

I believe the solution to this problem is to fix GuiObjects placed inside Folders, which we have some scheduled time for. I personally would like to get that fully fixed within the next 6 months, but we’ll see. We currently are only tracking one bug related to this - please report issues so that we can fix them.

While you do still have the nested hierarchy, at least there isn’t a Frame that you have to go and manually make invisible + UDim2.new(1, 0, 1, 0) sized.

4 Likes

Yeah, you assumed right.

Can you expand on that ‘mutually exclusive’ problem?
I don’t understand how it could be caused.

For your second question I came up with the idea of somehow linking objects; this could either be still using the current structure or doing something else, but I haven’t really fleshed out the idea yet.
Since people have been working on answers for years it’s probably going to be for naught.

Thank you for the great post, too. It helped me understand the situation much better.

One thing that html/css has that we don’t really have, is IDs and classes. We only have name and DOM type. You can implement classes using instance tagging using this snippet:

local CollectionService = game:GetService("CollectionService")

function GetElementsByClass(instance, class)
    local out = {}
    for descendant in pairs(instance:GetDescendants()) do
        if CollectionService:HasTag(descendant, class) then
            out[#out+1] = descendant
        end
    end
    return out
end

You can then assign tags to your instances to give behavior to them in the scripts, like a buy button or something. You can assign the tags to your GUI objects using this handy plugin.

IDs are harder, because of the uniqueness constraint. We don’t have anything comparable. One reason we don’t have it is because it would break Clone().

2 Likes

Oh, cool!

Couldn’t you edit :Clone() to generate a new unique ID?

IDs in html/css are created and assigned by the author of the document, and are specifically designed to be looked up using Javascript. It would not be meaningful to assign a random ID to a cloned object. They’re also kind of bad because they’re essentially global variables. You can only ever have one thing with that ID. That’s bad even for GUIs, because there are multiple players in your server, whose GUIs are usually cloned from a prototype in StarterGui. In the end, you can just use the class system I described above to implement IDs. Just never create more than one object with the tag. An ID system built into Roblox would never be able to express something like “the ID namespace is unique to this ScreenGui object”, while doing it in Lua can.

element = document.getElementById(id); // e.g. "footer"
1 Like

Thanks for the help :wink:

If you had a filtering system, you could have 2 objects inside a frame with a UIPageLayout applied, and another 2 with a UIGridLayout. What then decides how these two groups position relative to each other? We have a zIndex for managing front-to-back order, but nothing like the “flow” of CSS (float, left/right align, etc) and the order things appear as children of a frame is arbitrary. There isn’t really a use case for letting groups of things just pile up, unaware of each other, since beyond the simple case of a background image this isn’t useful behavior. This relative layout issue is more of a problem than mutually-exclusive constraints, as you could argue that applying more than one layout constraint to a single element is simply user error.

Oh, I see what you mean.

I was only thinking of excluding certain objects from the effect of a constraint, not of allowing a number of constraints to be running in a single object :slight_smile: