1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned, ToTokens};
use syn::spanned::Spanned;
use syn::Expr;

use crate::widgets::{AssignProperty, AssignPropertyAttr, PropertyName};

use super::AssignInfo;

impl AssignProperty {
    pub(crate) fn conditional_assign_stream(
        &self,
        info: &mut AssignInfo<'_>,
        p_name: &PropertyName,
        init: bool,
    ) {
        // If the code gen path is behind a conditional widgets, handle `watch` and `track` later.
        // Normally, those would be initialized right away, but they might need access to
        // variables from a pattern, for example `Some(variable)` so they are moved inside the
        // match arm or if expression.
        if !info.is_conditional
            || !matches!(
                self.attr,
                AssignPropertyAttr::Track(_) | AssignPropertyAttr::Watch
            )
        {
            self.assign_stream(info, p_name, init);
        }
    }

    pub(crate) fn assign_stream(
        &self,
        info: &mut AssignInfo<'_>,
        p_name: &PropertyName,
        init: bool,
    ) {
        let assign_fn = p_name.assign_fn_stream(info.widget_name);
        let self_assign_args = p_name.assign_args_stream(info.widget_name);
        let span = p_name.span();

        let args = self.args.as_ref().map(|args| {
            quote! {
                , #args
            }
        });

        // Destructure tuples
        let assign = if let Expr::Tuple(tuple) = &self.expr {
            tuple.elems.to_token_stream()
        } else {
            self.expr.to_token_stream()
        };

        let chain = self.chain.as_ref().map(|chain| {
            quote_spanned! {
                chain.span() => .#chain
            }
        });

        let (block_stream, unblock_stream) = if init || self.block_signals.is_empty() {
            (None, None)
        } else {
            let mut block_stream = TokenStream2::default();
            let mut unblock_stream = TokenStream2::default();
            let gtk_import = crate::gtk_import();

            let w_name = info.widget_name;
            for signal_handler in &self.block_signals {
                block_stream.extend(quote_spanned! {
                    signal_handler.span() =>
                        {
                            use relm4::WidgetRef;
                            #[allow(clippy::needless_borrow)]
                            #gtk_import::prelude::ObjectExt::block_signal(#w_name.widget_ref(), &#signal_handler);
                        }
                });
                unblock_stream.extend(quote_spanned! {
                    signal_handler.span() =>
                        {
                            use relm4::WidgetRef;
                            #[allow(clippy::needless_borrow)]
                            #gtk_import::prelude::ObjectExt::unblock_signal(#w_name.widget_ref(), &#signal_handler);
                        }
                });
            }
            (Some(block_stream), Some(unblock_stream))
        };

        info.stream
            .extend(match (self.optional_assign, self.iterative) {
                (false, false) => {
                    quote_spanned! { span =>
                        #block_stream
                        #assign_fn(#self_assign_args #assign #args) #chain;
                        #unblock_stream
                    }
                }
                (true, false) => {
                    quote_spanned! {
                        span => if let Some(__p_assign) = #assign {
                            #block_stream
                            #assign_fn(#self_assign_args __p_assign #args) #chain;
                            #unblock_stream
                        }
                    }
                }
                (false, true) => {
                    quote_spanned! {
                        span =>
                            #block_stream
                            for __elem in #assign {
                                #assign_fn(#self_assign_args __elem #args) #chain;
                            }
                            #unblock_stream
                    }
                }
                (true, true) => {
                    quote_spanned! {
                        span =>
                            #block_stream
                            for __elem in #assign {
                                if let Some(__p_assign) = __elem {
                                    #assign_fn(#self_assign_args __p_assign #args) #chain;
                                }
                            }
                            #unblock_stream
                    }
                }
            });
    }
}