iced icon indicating copy to clipboard operation
iced copied to clipboard

Layers

Open hecrj opened this issue 4 years ago • 8 comments

Currently, Iced assumes widgets cannot be laid out on top of each other. We should implement support for multiple layers of widgets.

This is a necessary feature to implement many kind of interactables, like dropdown menus, select fields, etc.

iced_native will need to group widgets to perform layouting and process some events first for widgets positioned on top.

iced_wgpu will also need to process the scene graph and sort draw calls based on the different layers.

hecrj avatar Oct 23 '19 18:10 hecrj

How would the depth of the widgets be determined? Maybe with something like this?

enum Depth {
    Above,
    Below,
    Topmost,
}

fn view(&mut self) -> Element<Message> {
    // ...
    Layer(SomeWidget::new(), Depth::Above)
    // ...
}

Then the current depth would have to be kept in the layouting state. Given a combobox for example, would it be best for the choice list to be part of the drawing code and the widget set to be on top (and ComboBox::new actually returns Layer(ComboBox {...}, Depth::Above) or the choice list be a separate widget, attached to the ComboBox's layout? Or maybe there's a better approach?

manokara avatar Nov 08 '19 15:11 manokara

I do not have a particular approach in mind yet. There is definitely an interesting amount of exploration ahead of us here.

I do know using something like absolute indices (like z-index in CSS) can be quite painful and does not compose at all.

A relative Layer widget sounds like a good idea to me, as I think it should compose no matter where you decide to nest it. elm-ui follows a similar approach and the author gave a great talk in elm-conf with many interesting ideas. I recommend watching it for inspiration!

Given a combobox for example, would it be best for the choice list to be part of the drawing code and the widget set to be on top (and ComboBox::new actually returns Layer(ComboBox {...}, Depth::Above) or the choice list be a separate widget, attached to the ComboBox's layout? Or maybe there's a better approach?

I think layers (or at least event layers) will need to exist as a first-class concept in iced_native. As of now, they cannot simply be another widget because the runtime needs to be aware of them. In other words, update needs to be aware that layers on top, although potentially nested deeply in the widget tree, should capture mouse events.

I am not yet sure about how we will end up implementing this. I do have the feeling that the different methods in the Widget trait are inherently coupled (node defines layout, which in turn affects on_event, draw, and hash_layout). Layers will make this coupling even more apparent.

I think the problem is telling us something here: we should define layout, update, and draw in a unified, declarative way. The runtime will then figure out how to process events and draw widgets. How could this API look like? I am not sure yet! :sweat_smile: This is where we should explore! I think the way iced_web works makes everything very composable and easy to extend.

hecrj avatar Nov 08 '19 21:11 hecrj

I would like to add my 2 cents. In other GUI libraries like GTK under X11, such overlays are actually separate windows, and can therefore extend outside the actual application window.

In my opinion, this is very useful for dropdowns and menus. I collected a couple of screenshots to illustrate my point.

Selectbox image

Menu image

In such cases or in cases of very small windows, it looks like it would be desirable to have a similar effect in Iced, too. I'm absolutely not sure how difficult something like that is to achieve...

frapa avatar Apr 10 '20 14:04 frapa

Isn't it better to achieve single instance window widget support @hecrj ? After we can improve it to have such functionality like gimp has. If such, in our app windows are crucial and we really want to use iced, so we can definetely contribute, but can you guide and write your general ideas about layers first naive implementaion, so I can submit draft for pull request?

krupitskas avatar Jul 09 '20 14:07 krupitskas

Separate windows would be the ideal way to go, but aren't really supported by winit yet (https://github.com/rust-windowing/winit/issues/403), and even if it were I don't know if it would be portable to all platforms (e.g. Android).

FYI, for KAS I solved the event handling via specific support for "pop-ups" (still a bit hacky, but required to support closing menus when clicking under them and things like accelerator key bindings on the menus). For drawing it uses the depth buffer with some specific offsets (not the only option, but otherwise you'd need indirect drawing for each layer I believe).

dhardy avatar Jul 09 '20 18:07 dhardy

This is a necessary feature to implement many kind of interactables, like dropdown menus, select fields, etc.

@hecrj isn't this already achieved through overlays? Could you explain the difference between a overlays and what you're suggesting? I'm a bit new to Iced, and I don't have a lot of experience with the API as yet.

AshfordN avatar Aug 10 '21 15:08 AshfordN

@AshfordN overlays do somewhat work but the issue become when you have a lot of overlays which ones should be rendered above the others. So in this case a lot of GUI libraries even Microsoft's UI stuff uses Z List rendering/ordering. All this means is they have a Arc connection point to a list and the list is just used for the rendering order. A list works the best for this method since you can Add children quickly above it and additions don't normally matter as much. To use a faster method for rendering like a Array/Vec you would need to pull in the top most first and then everything else starting from its children and then back in reverse. so the children or top most object is last/first in the array. and moving them around should be OK to do as well. In C we would use memmove() but if you use Vec or an actual list should be good enough since both support those methods safely.

This would be

  1. Need a connection point that can be added at view creation.
  2. Decide if you want a slower list or a Vec .
  3. Need to keep track of the current top most object and must place its children as the top most in the rendering list.
  4. Make it so the Top most is the last rendered, based on how you decide to order it in the list.

@dhardy: separate windows would need to be an enabled feature and would also require a separate list of widgets per window. this also means a different sandbox etc per window. for it to work optimally. I did some work a while back with GLFW and multiple window support is nice but they function as their own individual programs just with shared memory.

genusistimelord avatar May 31 '22 23:05 genusistimelord

Regarding child windows, there is now this PR for winit (X11 only): https://github.com/rust-windowing/winit/pull/2246

As @genusistimelord notes, having an application spawn multiple windows isn't exactly hard (KAS has had a "synchronised counter" example since very early on), but tying together event handling across windows for uses like menus is harder.

dhardy avatar Jun 01 '22 07:06 dhardy

Is there any kind of requirement list? What I could gather so far from the above discussions, other comments and some own ideas is that the layering system should allow:

  • Nested widgets (e.g. drop list) need to be able to display things above others even when higher in the widget tree
  • Layering should be able to be nested (like some subspace in the window where things are layered but don't overlap with the outside?)
  • Items on top of other items should get events first
  • There are "overlays" that belong to a widget like (drop list) and there are overlays that are independent like modals
    • This also means the layering system has to be accessible from inside a widget and probably also for a user in view
  • The ordering should be done rather relative instead of absolute (e.g. z-index, except for things like topmost) because users are mostly concerned with "I want this thing to go above/below that" (Relative to "that")
  • The layers probably need to support some kind of focus concept. When many dialogs stack the focused one should be on top (Iced would need a concept of focus for this, but one could try to keep it in mind)
    • This also implies that there is some kind of "baselayer" that is never on top of e.g. modals
  • There will be cases where the user wants to decide where things go but in many cases it should just work (you don't want to specify where the list of a drop-down goes or how to handle it)

These are just some ideas I gathered and a complete list would be helpful / would need to be discussed (maybe already exists and I just missed it).

Remmirad avatar Sep 27 '23 09:09 Remmirad

Currently hector has kindof made layering possible with the ability to create more layering structs which is helpful for fixing the overlay issues.
Event wise the Zlist/Focus would generally be the fastest method for this as you would mostly check if it is over the focused item first. if not then detect what new thing it is over/handling and attempt to delegate the events upon those. Some older GUI went as far as building a Image map and using it to determine where the event occurred and what Colored index existed there.

genusistimelord avatar Sep 27 '23 12:09 genusistimelord

Solved by #1719.

hecrj avatar Feb 07 '24 09:02 hecrj