Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve view macro syntax for 0.5 #162

Closed
AaronErhardt opened this issue Apr 10, 2022 · 6 comments
Closed

Improve view macro syntax for 0.5 #162

AaronErhardt opened this issue Apr 10, 2022 · 6 comments
Milestone

Comments

@AaronErhardt
Copy link
Member

AaronErhardt commented Apr 10, 2022

This is a list of suggestion for improving the macro syntax. Comments and suggestions are highly appreciated.

Multiple "roots"

Motivation and source

view! {
    window = adw::ApplicationWindow { ... },
    style_man = adw::StyleManager::for_display(window.display()) {
        set_color_scheme: track! { &model.theme_variant.to_color_scheme() },
    },
}

Attributes

Allow using local variables

let root = gtk::Box::default();
let label = gtk::Box::default();
...
view! {
    #[local]
    root {
        set_spacing: 5,
        #[local]
        append: label {
            set_label: "test",
            ...
        }
    }
}

[local_ref] could be used for variables passed by reference.

Make watch! and track! attributes

// Old
view! {
    gtk::Box {
        set_spacing: watch!(model.spacing),
        set_margin_all: track!(model.margin),
    }
}

// New
view! {
    gtk::Box {
        #[watch]
        set_spacing: model.spacing,
        #[track]
        set_margin_all: model.margin,
    }
}

Block signals

view! {
    gtk::Entry {
        connect_changed(sender) => move |_| {
            ...
        } -> changed_id,

        #[block_signal(changed_id)]
        set_text: "test",
    }
}

Multiple arguments

Remove args!

// Old
view! {
    gtk::UnknownWidget {
        set_two_args: arg!(1, 2),
        set_tuple: (1, 2),
    }
}

// New: interpret parentheses as multiple args
view! {
    gtk::UnknownWidget {
        set_two_args: (1, 2),
        set_tuple: ((1, 2)),
    }
}

Other

  • Deprecate send!
@AaronErhardt AaronErhardt added this to the 0.5 milestone Apr 10, 2022
@MaksymShcherbak
Copy link
Member

Great suggestions! By the way, the args! change allows to use methods with no arguments:

window = gtk::Window {
    ...
    present: (),
}

I've got a few suggestions, too:

Unify clone syntax

To clone a variable into closure you just write it in parentheses:

connect_name(cloned_var1, cloned_var2, ...) => move |arg1, arg2, ...| { ... }

To clone a field or a method, you write it inside square brackets:

connect_name[sender1 = components.name1.sender(), 
    sender2 = components.name2.sender(), ...] => move |arg1, arg2, ...|

But I think the syntax could be unified.

connect_name(cloned_var1, sender1 = components.name1.sender(), 
    sender2 = components.name2.sender(), cloned_var2, ...) => move |arg1, arg2, ...|

Allow property / method chains in widget init

append = app.components.my_component.widget() {
	...
}

@etrombly
Copy link

Everything looks really useful to me. I'd be a little concerned with the new multiple arguments parsing being a little easy to miss. The new way looks like a single item tuple of tuples. But it might be better when you get more familiar with it.

@ruifengx
Copy link

Now after pondering about the "multiple root" idea for a while, I realised it might also be useful to allow arbitrary ordering of the "roots", instead of hard-coding the first one as the root widget. Perhaps for example some fields must be initialised before the main widget tree (previously covered by pre_init), so it would be helpful to allow fields to appear before the widget tree. Maybe in such circumstances we can specify the root widget explicitly as #[relm4::widget(root = "...")], and otherwise infer the root to be the first one (it might also be desirable to have an warn-by-default/allow-by-default lint for having multiple roots and not specifying the root widget, but I believe it is not possible currently with stable Rust).

@your-sudden-death
Copy link

For conditional rendering, maybe something like this would be sufficient:

set_child: some_child = || {
    match model.mode {
        AppMode::SingleUser => view! {
            gtk::Label::new("hi")
        }
        AppMode::Batch => view! {
            gtk::Label::new("ho")
        }
    }
}

The closure can be used for anything and will run each time the UI gets updated. The type of the return value can be inferred via the set_child parameter type

@your-sudden-death
Copy link

For a more pragmatic approach that extends the property?: Option<T> approach, the ?: could be extended to support not only Option but also fn -> bool and then adding the additional syntax for widgets:

set_child ?= (model.mode == AppMode::SingleUser) = {

}
set_child: a_child ?= (model.mode == AppMode::SingleUser) = {

}

@AaronErhardt AaronErhardt mentioned this issue Apr 18, 2022
14 tasks
@AaronErhardt
Copy link
Member Author

The PR is merged 🎉

Thanks for your feedback, I think most of it should have been implemented by the PR. Let me know if there's something broken or missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants