kas-gui / kas

Another GUI toolkit
Apache License 2.0
904 stars 25 forks source link

Field types created by impl_singleton! are opaque #15

Open dhardy opened 4 years ago

dhardy commented 4 years ago

When creating a widget with make_widget!{ .. }, fields cannot be accessed from outside the defining macro due to the anonymous type.

I had hoped this could be fixed with some type of type alias feature, but alas, this appears limited to trait implementations.

To some extent this can be worked around with traits, e.g. from the stopwatch example:

#[widget] display: impl HasText = make_widget!{
    frame => EmptyMsg;
    struct {
        #[widget] display: Label = Label::from("0.000"),
    }
    impl HasText {
        fn get_text(&self) -> &str {
            self.display.get_text()
        }
        fn set_string(&mut self, tk: &mut dyn TkWindow, text: String) {
            self.display.set_text(tk, text);
        }
    }
},

allowing self.display.set_text(tk, &self.dur_buf); to be called from the outer widget later.

Ideally in the above, it would be possible to directly call self.display.display.set_text(tk, ..).


Proposal: build_widget! macro

Move most of the make_widget! code into a separate macro, defining the type, and allow direct use of this.

Issue: implicit typing within make_widget! uses generics, thus the resulting type requires parameterisation on use. Ideally, we would not have to use generics but could use some type of "magic type alias".

dhardy commented 4 years ago

RFC 2524 should fix this.

dhardy commented 2 years ago

Since this issue was created, the make_widget! macro has been separated from derive(Widget) and a few work-arounds added, however the fundamental issue remains: something like RFC 2524 is required to make it work properly (replacing use of generics with various hacky bounds, and allowing visibility outside of the generated code without explicit typing).

dhardy commented 2 years ago

Note: this is still an issue, but the macro in question has been renamed to impl_singleton!.

dhardy commented 1 year ago

As an experiment, tried using type_alias_impl_trait instead of type generics to declare inferred field types:

let ty = make_ident(format_args!("{singleton_ty}{}", &ty_name[1..]), span);
pre_items.append_all(quote! { type #ty = impl #bound; });

// or, for widgets with omitted type:
pre_items.append_all(quote! { type #ty = impl ::kas::Widget; });

The result appears to function identically (except where there are zero bounds, but such fields are only useful for extending the life of some object). It does not solve the issue of opaque types, nor offers any other advantage.