Progress continues toward minigui 2.0

Posted 2021-05-17

A Vulkan renderer based on SDL was released for Android and desktops from the community, the PixelPerfectEngine got a new announcement, and in my world, I kept working on minigui 2.0.

Core D Development Statistics

In the community

Community announcements

See more at the announce forum.

What Adam is working on - minigui 2.0

New features, new documentation

To see the list of changes, see my beta documentation website at:

http://dpldocs.info/experimental-docs/arsd.minigui.html#history

This is still subject to change until it is officially released - I expect to freeze new feature development next Monday and tag this for release shortly thereafter, no later than June 1st - but I've been writing new documentation as I go both to help people transition to the new version and to simply explain how things work for new users.

The Widget page: http://dpldocs.info/experimental-docs/arsd.minigui.Widget.html now describes how you can make your own and the top level page finally (FINALLY) has a hello world example on it!

But since you can read about the changes there, in this blog, I instead want to talk about some of the implementation details and design decisions.

D signals and slots?

One of the goals with the new event system was to provide as much type checking as possible. On the listening side, this is relatively easy: providing concrete types in the listener indicates what you want and what fields it has. (The only real challenge was making an easy deprecation path so people can transition, and that proved not too hard thanks to the deprecated keyword on property accessors.)

It is possible to listen for an event that is never sent... but that's kinda by design, because you can add child widgets at runtime and handle their events in the parent, so limiting it to only statically declared types would limit the utility. That said, I did provide an alternative mechanism for this too, but I'll come back to that.

What about on the sending side? On the sending side, it is reasonable to have to statically declare what you can emit. It is even reasonably to make this protected, since a custom widget's custom events should only come from itself. (Though of course users can always construct synthetic events from outside, managing that is their responsibility.) Besides, it is nice to document your events as part of the interface.

My first thought was to do this as a mixin template which provided an emit function, but this doesn't work well when inheritance is involved due to the hijack rules. My next thought was to use a simple struct, whose type indicated the type of event, but I didn't like this either - the field as a whole was either private or protected, instead of a public subscribe with protected send. (Note that the phobos std.signals module has similar troubles.)

I ended up separating the pieces and using a bit of both. Widget.emit is a protected final method that is always present. There's no overloading involved, so it is easy to use with inheritance.

But then, how does it provide type checks? The answer is fairly simple: static assert. It uses a template this parameter to get the static type the caller knows then checks for the presence of the Emits annotation:

class Widget {
	final protected bool emit(EventType, this This, Args...)(Args args) {
		static assert(classStaticallyEmits!(This, EventType),
			"The " ~ This.stringof ~ " class is not declared to emit " ~ EventType.stringof);
		auto event = new EventType(this, args);
		event.dispatch();
		// snip
	}

	/// I can declare and document that I can send SomeEvent here
	mixin Emits!SomeEvent;
}

// then the usage is like:

class MyWidget : Widget {
	void example() {
		this.emit!SomeEvent(args, to, event, constructor);
	}
}

(btw many of you know I hate stringof, but one of the acceptable uses of it in my eyes is for messages to users so I'll allow it)

Template this params are often considered and rejected for various uses because they do not work with the dynamic type of the object; if they are called from the base class, it always sees the base class. So you cannot use them to add reflection information or similar tasks.

That's perfectly fine here though, since the goal is simply to add a static check! If you access it through the base class, it is supposed to fail here, since the base class does not necessarily use the same events as the children. Children are allowed to extend the capabilities of the object.

What does that Emits!SomeEvent thing do then? Well, it actually just adds a private zero-length array member - private SomeEvent[0] _emits_some_event;. That static assert thus just checks if it has that member and if the type checks out. Being a zero length array, this has size of = 0; it doesn't take up any actual space in the runtime object, and it works fine with inheritance since it is private (the reflection check if it exists simply bypasses the visibility restriction).

Of course, another option would have been to use a user-defined annotation, like:

@Emits!SomeEvent
class MyWidget : Widget {}

Or even

@SomeEvent
class .....

But I preferred the mixin because then I can attach an adrdox comment directly to the declaration to elaborate when and why it is sent in the documentation. I'm not sure if ddoc supports this, but adrdox does allow you to attach doc comments to the mixin approach, and neither do to UDAs at this time.

I don't think this emits thing is terribly important in the grand scheme of things, but I am pretty happy with how I managed to make it work.

That template pattern curiously recurs again with style

About a year ago, I wrote about using CRTP for reflection in cgi.d. CRTP stands for the "curiously recurring template pattern" and looks something like this:

class Base(Child) {}

class Child : Base!Child {}

In the previous section, I said template this params are often considered for some kind of child reflection in a base class but they aren't really suitable for that task. The CRTP, on the other hand, does allow this - for at least one level of depth - because the base class uses the child class as a template argument and thus is aware of it. (Indeed, technically there's a different base class for each child; they're intertwined.)

The minigui 2.0 visual theme class uses this to give user code a hook to completely override paint methods of specific widget classes, including basic ones inside minigui itself as well as user-defined classes, in addition to traditional overrides for simpler global properties.

A possible VisualTheme class would look something like this:

class MyVisualTheme : VisualTheme!MyVisualTheme {
        static if(__VERSION__ >= 2094) // the implementation uses a newish feature
        void paint(Checkbox c, WidgetPainter painter) {
		// I can do a *complete* replacement of the look here
                painter.fillColor = c.isChecked ? Color.green : Color.red;
                painter.drawRectangle(Point(0, 0), Size(c.width, c.height));
        }

	// and change various properties one-by-one
        override OperatingSystemFont defaultFont() {
                return new OperatingSystemFont("DejaVu Sans");
        }

        override Color selectionColor() {
                return Color.red;
        }

        override Color foregroundColor() {
                return Color.white;
        }

        override Color windowBackgroundColor() {
                return Color(64, 64, 64);
        }

        override Color widgetBackgroundColor() {
                return Color.black;
        }
}

The implementation uses that CRTP to find all the paint overloads there and see what parameter type they need. It then does a loop and dynamic cast inside - similar to the visitor pattern - to find matching items and call the methods. It uses the most-derived overload similar to arsd.mvd's implementation, counting the number of base classes.

I think this is a nicer way to offer this kind of deep hooks than a traditional visitor. I'm surely going to change this again before the release though, since the details of paint are hard to get right in a complete override like this. But I am pretty happy with what the CRTP offers - I might use it to override Widget.Style moreso than paint though. We'll see.

Webasm some day

I'm tempted to try to run this on webasm sometime soon too, it might not be too hard to port over (at least the custom_widgets version) and that isn't super practical, but it would be nice to have a "try it online" for the documentation. I don't have time for that in the short term but I'll probably play with it eventually.