Implement eventing for widgets

Widgets need to be able to define events that users of Gooey can register callbacks to be triggered for.

Transmogrifiers need to be able to forward messages to these callbacks.

I looked into creating an eventing mechanism that didn’t rely on channels that allowed the layers to communicate, but each attempt is a dead end. The ability for that pathway to create an infinite loop of calling back and forth means that the borrow checker can’t be satisfied since mutable references are involved.

Sadly, the alternative approach I considered – using channels – also won’t work with the current ownership structure. I’m going to need to move ownership of child widgets into a centralized location to make it work – there’s no way for an external event to trigger into a widget that is owned somewhere else unless the widget storage is managed by the thing that receives the trigger.

Up until this point, I was trying to avoid an “arena” structure, but I already ended up with one for Transmogrifier state. Tomorrow’s task is likely going to be to add widget storage into Gooey.

The differentiation between TransmogrifierStorage and Gooey became clearer to me – TransmogrifierStorage operates without knowing the Frontend type. Any types that require the Frontend generic type can’t be stored at this level. Thankfully, Widgets are at the level that can go into this “TransmogrifierStorage”. It’s going to be renamed in the process.

All in all, while I didn’t make much progress, I have a pretty good idea of how to make meaningful headway tomorrow.

I’ve been doing updates on the pull request here:

Today I reached a meaningful milestone: Clicky Buttons!

What you see on the left is a native executable using Kludgine as a renderer for a rasterized user interface. On the right is a Firefox window with a <button> element. Both use the same example code. Technically, it’s not exactly this code, but it’s very close. The current example is needlessly complex because of some APIs I haven’t cleaned up yet.

I’m really excited at how things are coming together. I was questioning my sanity this past weekend while trying to get event handling through the frontend working. The hardest problem is that I was insistent that the cross-platform code must not know about the frontend. This means that the Counter example should be truly cross-platform code, with all the platform-specific code being implemented elsewhere (the gooey-widgets crate for this example).

Ultimately, the code behind the scenes must know about the frontend. When the cross-platform code wants to send a message to another widget, that process inherently requires types behind the scenes that need to know the frontend to function properly. This isn’t a problem I’m unfamiliar with, but it was made more complicated by the complex dependencies between types.

After I was able to get the browser version of the example functioning, I was a mess mentally. I was sure that the code would be horrible due to all the flailing about I had done while trying to tame the type system. The next morning, I spoke with @dAxpeDDa about what I had pushed. In the end, we both agreed it was in a pretty good state as-is, which was a relief after such a stressful weekend of coding.

The next step to make these widgets feel real on the Rasterizer level is implementing actual theming support. I’ve been thinking about how I wanted to solve it differently than I last approached it in Kludgine, and I have some ideas I am ready to explore. That being said, it’s been two weeks since my last commit on PliantDb, and I was thinking that I could get a start on the basic “To Do” example by building a CLI-based todo app. So, I may divert from this task for a day or two soon to give some love to PliantDb.