linebender / druid

A data-first Rust-native UI design toolkit.
https://linebender.org/druid/
Apache License 2.0
9.54k stars 569 forks source link

Sub-Window host/port design bug? #2143

Open maurerdietmar opened 2 years ago

maurerdietmar commented 2 years ago

Unfortunately, it is quite simple (and common) to remove the parent after creating a new window. After that, host/parent sync cannot work because the parent widget was removed (see SUB_WINDOW_HOST_TO_PARENT, SUB_WINDOW_PARENT_TO_HOST commands)

Attached is a simple example which triggers the bug.

window-problem.rs.txt

jneem commented 2 years ago

What would be the desired behavior? Would it be reasonable to close the subwindow when its parent goes away?

xarvic commented 2 years ago

Would it be reasonable to close the subwindow when its parent goes away?

I vote for yes

maurerdietmar commented 2 years ago

What would be the desired behavior? Would it be reasonable to close the subwindow when its parent goes away?

1.) I think that is also not reasonable. It would provide some feedback, but would be wrong most times. For example, you have some dialog with an "Open new window" button. Once you opened the window you would remove that dialog, but you don't want to remove the window. In more complex UIs you have this situation all the time..

2.) This is impossible to implement, because druid has no WidgetRemoved event.

maurerdietmar commented 2 years ago

Anyways, I wonder if it is useful to sync one global state across all windows? Why not simply have one state per Window? This would allow us to remove that clumsy host/parent communication. If an application want to share state between windows, it should do it manually by sending commands?

xarvic commented 2 years ago

1.) I think that is also not reasonable. It would provide some feedback, but would be wrong most times. For example, you have some dialog with an "Open new window" button. Once you opened the window you would remove that dialog, but you don't want to remove the window. In more complex UIs you have this situation all the time.

The point of subwindows is to have widgets which behave like children of the host (uses the same data and positioned relative to the host). To me it sounds like a normal windows would fit better in this case.

2.) This is impossible to implement, because druid has no WidgetRemoved event.

If think it should be possible using the Drop trait and an ExtEventSink.

If an application want to share state between windows, it should do it manually by sending commands?

To me sharing the state of windows sounds like a useful default since it complicated and error prone to do manually.

Also you can have windows which have their own state by wrapping them with this:

Scope::from_lens(
    |_|WindowData::new(), 
    lens::Unit, 
    root_widget(), 
).lens(lens::Unit)
maurerdietmar commented 2 years ago

The point of subwindows is to have widgets which behave like children of the host (uses the same data and positioned relative to the host). To me it sounds like a normal windows would fit better in this case.

Oh, I missed the fact that windows and subwindows are different - good to know.

I wonder what you would use to display a normal message dialog asking "Would you like to continue (Yes/No)". A window or a sub-window?

jneem commented 2 years ago

Usually, a window. But I can think of a situation where it could be a sub-window: if I have a tabbed interface and the message dialog is asking about whether I want to make changes to the document contained in one tab, then I'd have it as a subwindow of that tab. I think the ideal behavior in that case would be that the sub-window would be closed when switching tabs, and then opened again when the relevant tab gets re-opened.

maurerdietmar commented 2 years ago

Usually, a window.

How do you make that work inside a custom widget that has no access to the App state?

We usually structure the application by building reusable sub-widget, which do have access to the whole application state.

jneem commented 2 years ago

Ah, you're referring to the fact that EventCtx::new_window needs to know the global app data type? I can see how that would be a problem.

I guess temporary workaround would be to send a command and have some controller at the root that listens for it. But maybe it would be nicer to add a window creation API that takes a WindowDesc<()>? Our oldest open PR (#1039) has something like this...

maurerdietmar commented 2 years ago

2.) This is impossible to implement, because druid has no WidgetRemoved event.

If think it should be possible using the Drop trait and an ExtEventSink.

Ah yes, it is possible.

I am currently trying to implement sub-windows using the Stack widget, and it works there:

https://github.com/maurerdietmar/druid-widget-nursery/tree/implement-sub-window-manager

I simply use a (zero-sized) Dialog widget, which proxies data and handles create/destroy event.

The final goal is to have sub-windows on the web target, but I am unsure if this whole thing makes sense ...

xarvic commented 2 years ago

I am not an expert on druid_shell, but i think it should be possible to implement sub-windows on the Web. Currently druid draws to a Web-Canvas, maybe we just can create more of them :)

I am unsure if this whole thing makes sense ...

I took a look at your code. Its quite complex and i had not enough time to fully understand how it works, but without integration from the shell its probably a really hard problem. From what i saw its probably a good solution.