Skip to main content

ChannelHandle

Struct ChannelHandle 

Source
pub struct ChannelHandle<T> { /* private fields */ }

Implementations§

Source§

impl<T: 'static> ChannelHandle<T>

Source

pub fn new(wake: Arc<AtomicBool>) -> Self

Create a new channel handle with an event-loop wake flag.

Source

pub fn tx(&self) -> WakingSender<T>

Get a cloneable sender that wakes the event loop on send.

Examples found in repository?
examples/34_channels_and_intervals.rs (line 51)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        let ticks = state!(cx, || 0u64);
33        let messages: State<Vec<String>> = state!(cx, Vec::new);
34        let tasks_running = state!(cx, || 0u32);
35
36        // Tick every second
37        interval!(cx, Duration::from_secs(1), with!(ticks => move || {
38            ticks.update(|n| *n += 1);
39        }));
40
41        // Channel for worker thread results
42        let ch = channel!(cx, String);
43
44        // Process incoming messages
45        for msg in ch.get() {
46            messages.update(|v| v.push(msg));
47            tasks_running.update(|n| *n = n.saturating_sub(1));
48        }
49
50        let spawn_task = {
51            let tx = ch.tx();
52            let task_num = messages.get().len() + tasks_running.get() as usize + 1;
53            with!(tasks_running => move || {
54                tasks_running.update(|n| *n += 1);
55                let tx = tx.clone();
56                std::thread::spawn(move || {
57                    std::thread::sleep(Duration::from_secs(2));
58                    tx.send(format!("Task {} complete!", task_num)).ok();
59                });
60            })
61        };
62
63        let elapsed = ticks.get();
64        let mins = elapsed / 60;
65        let secs = elapsed % 60;
66
67        View::vstack()
68            .spacing(1)
69            .child(View::styled_text("Channels & Intervals").bold().build())
70            .child(
71                View::hstack()
72                    .spacing(1)
73                    .child(View::styled_text("Elapsed:").dim().build())
74                    .child(View::styled_text(format!("{:02}:{:02}", mins, secs)).color(Color::Cyan).bold().build())
75                    .child(View::styled_text(format!("({} ticks)", elapsed)).dim().build())
76                    .build(),
77            )
78            .child(
79                View::hstack()
80                    .spacing(1)
81                    .child(
82                        View::button()
83                            .label("[ Spawn Background Task ]")
84                            .on_press(spawn_task)
85                            .build(),
86                    )
87                    .child(if tasks_running.get() > 0 {
88                        View::styled_text(format!("{} running...", tasks_running.get()))
89                            .color(Color::Yellow)
90                            .build()
91                    } else {
92                        View::styled_text("idle").dim().build()
93                    })
94                    .build(),
95            )
96            .child(View::styled_text("─── Messages ───").dim().build())
97            .child({
98                let msgs = messages.get();
99                if msgs.is_empty() {
100                    View::styled_text("(no messages yet — spawn a task!)").dim().build()
101                } else {
102                    let max_visible = 8;
103                    let len = msgs.len();
104                    let skip = len.saturating_sub(max_visible);
105                    let mut stack = View::vstack();
106                    if skip > 0 {
107                        stack = stack.child(
108                            View::styled_text(format!("  ... {} earlier messages", skip))
109                                .dim()
110                                .build(),
111                        );
112                    }
113                    for (i, m) in msgs.iter().enumerate().skip(skip) {
114                        let is_last = i == len - 1;
115                        stack = stack.child(
116                            View::styled_text(format!("  {} {}", if is_last { "→" } else { " " }, m))
117                                .color(if is_last { Color::Green } else { Color::Reset })
118                                .build(),
119                        );
120                    }
121                    stack.build()
122                }
123            })
124            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
125            .child(
126                View::modal()
127                    .visible(show_help.get())
128                    .title("Example 34: Channels & Intervals")
129                    .on_dismiss(with!(show_help => move || show_help.set(false)))
130                    .child(
131                        View::vstack()
132                            .child(View::styled_text("What you're seeing").bold().build())
133                            .child(View::text("• interval! ticks every second"))
134                            .child(View::text("• channel! receives from threads"))
135                            .child(View::text("• Workers sleep 2s then send a message"))
136                            .child(View::gap(1))
137                            .child(View::styled_text("Key concepts").bold().build())
138                            .child(View::text("• interval!(cx, dur, || callback)"))
139                            .child(View::text("• channel!(cx, Type) for inbound msgs"))
140                            .child(View::text("• ch.tx() gives a WakingSender"))
141                            .child(View::text("• ch.get() reads this frame's messages"))
142                            .child(View::gap(1))
143                            .child(View::styled_text("Try this").bold().build())
144                            .child(View::text("• Spawn several tasks at once"))
145                            .child(View::text("• Watch messages arrive after 2s"))
146                            .child(View::text("• Notice the tick counter keeps going"))
147                            .child(View::gap(1))
148                            .child(View::styled_text("Next up").bold().build())
149                            .child(View::text("-> 35_slider: bounded numeric input"))
150                            .child(View::gap(1))
151                            .child(View::styled_text("Press Escape to close").dim().build())
152                            .build(),
153                    )
154                    .build(),
155            )
156            .build()
157    }
More examples
Hide additional examples
examples/39_port.rs (line 82)
36    fn render(&self, cx: Scope) -> View {
37        let show_help = state!(cx, || false);
38
39        cx.use_command(
40            KeyBinding::key(KeyCode::F(1)),
41            with!(show_help => move || show_help.update(|v| *v = !*v)),
42        );
43
44        let status = state!(cx, || "Idle".to_string());
45        let progress = state!(cx, || 0u8);
46        let result: State<Option<String>> = state!(cx, || None);
47        let running = state!(cx, || false);
48
49        // Bidirectional port: inbound TaskProgress, outbound TaskCommand
50        let port = port!(cx, TaskProgress, TaskCommand);
51
52        // Process incoming progress messages
53        for msg in port.rx.get() {
54            match msg {
55                TaskProgress::Started => {
56                    status.set("Working...".to_string());
57                    progress.set(0);
58                    result.set(None);
59                    running.set(true);
60                }
61                TaskProgress::Progress(pct) => {
62                    status.set(format!("Progress: {}%", pct));
63                    progress.set(pct);
64                }
65                TaskProgress::Done(data) => {
66                    status.set("Done!".to_string());
67                    progress.set(100);
68                    result.set(Some(data));
69                    running.set(false);
70                }
71                TaskProgress::Cancelled => {
72                    status.set("Cancelled".to_string());
73                    running.set(false);
74                }
75            }
76        }
77
78        // Spawn the worker thread on first render
79        let worker_started = state!(cx, || false);
80        if !worker_started.get() {
81            worker_started.set(true);
82            let tx_progress = port.rx.tx();
83            if let Some(rx_commands) = port.take_outbound_rx() {
84                std::thread::spawn(move || {
85                    worker_loop(tx_progress, rx_commands);
86                });
87            }
88        }
89
90        let start_task = {
91            let tx = port.tx();
92            with!(running => move || {
93                if !running.get() {
94                    let _ = tx.send(TaskCommand::Start);
95                }
96            })
97        };
98
99        let cancel_task = {
100            let tx = port.tx();
101            with!(running => move || {
102                if running.get() {
103                    let _ = tx.send(TaskCommand::Cancel);
104                }
105            })
106        };
107
108        let pct = progress.get();
109        let bar_width = 30usize;
110        let filled = (pct as usize * bar_width) / 100;
111        let bar = format!(
112            "[{}{}] {}%",
113            "█".repeat(filled),
114            "░".repeat(bar_width - filled),
115            pct
116        );
117
118        View::vstack()
119            .spacing(1)
120            .child(View::styled_text("Port: Background Task Runner").bold().build())
121            .child(
122                View::hstack()
123                    .spacing(1)
124                    .child(View::styled_text("Status:").dim().build())
125                    .child(View::styled_text(status.get())
126                        .color(if running.get() { Color::Yellow } else if pct == 100 { Color::Green } else { Color::Reset })
127                        .bold()
128                        .build())
129                    .build(),
130            )
131            .child(View::styled_text(&bar).color(
132                if pct == 100 { Color::Green }
133                else if pct > 50 { Color::Yellow }
134                else { Color::Cyan }
135            ).build())
136            .child(if let Some(data) = result.get() {
137                View::vstack()
138                    .child(View::styled_text("Result:").dim().build())
139                    .child(View::styled_text(format!("  {}", data)).color(Color::Green).build())
140                    .build()
141            } else {
142                View::empty()
143            })
144            .child(
145                View::hstack()
146                    .spacing(1)
147                    .child(
148                        View::button()
149                            .label(if running.get() { "[ Running... ]" } else { "[ Start Task ]" })
150                            .on_press(start_task)
151                            .build(),
152                    )
153                    .child(
154                        View::button()
155                            .label("[ Cancel ]")
156                            .on_press(cancel_task)
157                            .build(),
158                    )
159                    .build(),
160            )
161            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
162            .child(
163                View::modal()
164                    .visible(show_help.get())
165                    .title("Example 39: Port")
166                    .on_dismiss(with!(show_help => move || show_help.set(false)))
167                    .child(
168                        View::vstack()
169                            .child(View::styled_text("What you're seeing").bold().build())
170                            .child(View::text("• Background task with progress"))
171                            .child(View::text("• Bidirectional communication"))
172                            .child(View::text("• Start and cancel controls"))
173                            .child(View::gap(1))
174                            .child(View::styled_text("Key concepts").bold().build())
175                            .child(View::text("• port!(cx, InType, OutType)"))
176                            .child(View::text("• port.rx.tx() sends to UI"))
177                            .child(View::text("• port.tx() sends to worker"))
178                            .child(View::text("• port.take_outbound_rx() for worker"))
179                            .child(View::text("• port.rx.get() reads this frame"))
180                            .child(View::gap(1))
181                            .child(View::styled_text("Try this").bold().build())
182                            .child(View::text("• Start a task, watch progress"))
183                            .child(View::text("• Cancel mid-way"))
184                            .child(View::text("• Start another after completion"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Press Escape to close").dim().build())
187                            .build(),
188                    )
189                    .build(),
190            )
191            .build()
192    }
Source

pub fn get(&self) -> Vec<T>
where T: Clone,

Get this frame’s messages (populated by the run loop’s drain pass).

Examples found in repository?
examples/34_channels_and_intervals.rs (line 45)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        let ticks = state!(cx, || 0u64);
33        let messages: State<Vec<String>> = state!(cx, Vec::new);
34        let tasks_running = state!(cx, || 0u32);
35
36        // Tick every second
37        interval!(cx, Duration::from_secs(1), with!(ticks => move || {
38            ticks.update(|n| *n += 1);
39        }));
40
41        // Channel for worker thread results
42        let ch = channel!(cx, String);
43
44        // Process incoming messages
45        for msg in ch.get() {
46            messages.update(|v| v.push(msg));
47            tasks_running.update(|n| *n = n.saturating_sub(1));
48        }
49
50        let spawn_task = {
51            let tx = ch.tx();
52            let task_num = messages.get().len() + tasks_running.get() as usize + 1;
53            with!(tasks_running => move || {
54                tasks_running.update(|n| *n += 1);
55                let tx = tx.clone();
56                std::thread::spawn(move || {
57                    std::thread::sleep(Duration::from_secs(2));
58                    tx.send(format!("Task {} complete!", task_num)).ok();
59                });
60            })
61        };
62
63        let elapsed = ticks.get();
64        let mins = elapsed / 60;
65        let secs = elapsed % 60;
66
67        View::vstack()
68            .spacing(1)
69            .child(View::styled_text("Channels & Intervals").bold().build())
70            .child(
71                View::hstack()
72                    .spacing(1)
73                    .child(View::styled_text("Elapsed:").dim().build())
74                    .child(View::styled_text(format!("{:02}:{:02}", mins, secs)).color(Color::Cyan).bold().build())
75                    .child(View::styled_text(format!("({} ticks)", elapsed)).dim().build())
76                    .build(),
77            )
78            .child(
79                View::hstack()
80                    .spacing(1)
81                    .child(
82                        View::button()
83                            .label("[ Spawn Background Task ]")
84                            .on_press(spawn_task)
85                            .build(),
86                    )
87                    .child(if tasks_running.get() > 0 {
88                        View::styled_text(format!("{} running...", tasks_running.get()))
89                            .color(Color::Yellow)
90                            .build()
91                    } else {
92                        View::styled_text("idle").dim().build()
93                    })
94                    .build(),
95            )
96            .child(View::styled_text("─── Messages ───").dim().build())
97            .child({
98                let msgs = messages.get();
99                if msgs.is_empty() {
100                    View::styled_text("(no messages yet — spawn a task!)").dim().build()
101                } else {
102                    let max_visible = 8;
103                    let len = msgs.len();
104                    let skip = len.saturating_sub(max_visible);
105                    let mut stack = View::vstack();
106                    if skip > 0 {
107                        stack = stack.child(
108                            View::styled_text(format!("  ... {} earlier messages", skip))
109                                .dim()
110                                .build(),
111                        );
112                    }
113                    for (i, m) in msgs.iter().enumerate().skip(skip) {
114                        let is_last = i == len - 1;
115                        stack = stack.child(
116                            View::styled_text(format!("  {} {}", if is_last { "→" } else { " " }, m))
117                                .color(if is_last { Color::Green } else { Color::Reset })
118                                .build(),
119                        );
120                    }
121                    stack.build()
122                }
123            })
124            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
125            .child(
126                View::modal()
127                    .visible(show_help.get())
128                    .title("Example 34: Channels & Intervals")
129                    .on_dismiss(with!(show_help => move || show_help.set(false)))
130                    .child(
131                        View::vstack()
132                            .child(View::styled_text("What you're seeing").bold().build())
133                            .child(View::text("• interval! ticks every second"))
134                            .child(View::text("• channel! receives from threads"))
135                            .child(View::text("• Workers sleep 2s then send a message"))
136                            .child(View::gap(1))
137                            .child(View::styled_text("Key concepts").bold().build())
138                            .child(View::text("• interval!(cx, dur, || callback)"))
139                            .child(View::text("• channel!(cx, Type) for inbound msgs"))
140                            .child(View::text("• ch.tx() gives a WakingSender"))
141                            .child(View::text("• ch.get() reads this frame's messages"))
142                            .child(View::gap(1))
143                            .child(View::styled_text("Try this").bold().build())
144                            .child(View::text("• Spawn several tasks at once"))
145                            .child(View::text("• Watch messages arrive after 2s"))
146                            .child(View::text("• Notice the tick counter keeps going"))
147                            .child(View::gap(1))
148                            .child(View::styled_text("Next up").bold().build())
149                            .child(View::text("-> 35_slider: bounded numeric input"))
150                            .child(View::gap(1))
151                            .child(View::styled_text("Press Escape to close").dim().build())
152                            .build(),
153                    )
154                    .build(),
155            )
156            .build()
157    }
More examples
Hide additional examples
examples/39_port.rs (line 53)
36    fn render(&self, cx: Scope) -> View {
37        let show_help = state!(cx, || false);
38
39        cx.use_command(
40            KeyBinding::key(KeyCode::F(1)),
41            with!(show_help => move || show_help.update(|v| *v = !*v)),
42        );
43
44        let status = state!(cx, || "Idle".to_string());
45        let progress = state!(cx, || 0u8);
46        let result: State<Option<String>> = state!(cx, || None);
47        let running = state!(cx, || false);
48
49        // Bidirectional port: inbound TaskProgress, outbound TaskCommand
50        let port = port!(cx, TaskProgress, TaskCommand);
51
52        // Process incoming progress messages
53        for msg in port.rx.get() {
54            match msg {
55                TaskProgress::Started => {
56                    status.set("Working...".to_string());
57                    progress.set(0);
58                    result.set(None);
59                    running.set(true);
60                }
61                TaskProgress::Progress(pct) => {
62                    status.set(format!("Progress: {}%", pct));
63                    progress.set(pct);
64                }
65                TaskProgress::Done(data) => {
66                    status.set("Done!".to_string());
67                    progress.set(100);
68                    result.set(Some(data));
69                    running.set(false);
70                }
71                TaskProgress::Cancelled => {
72                    status.set("Cancelled".to_string());
73                    running.set(false);
74                }
75            }
76        }
77
78        // Spawn the worker thread on first render
79        let worker_started = state!(cx, || false);
80        if !worker_started.get() {
81            worker_started.set(true);
82            let tx_progress = port.rx.tx();
83            if let Some(rx_commands) = port.take_outbound_rx() {
84                std::thread::spawn(move || {
85                    worker_loop(tx_progress, rx_commands);
86                });
87            }
88        }
89
90        let start_task = {
91            let tx = port.tx();
92            with!(running => move || {
93                if !running.get() {
94                    let _ = tx.send(TaskCommand::Start);
95                }
96            })
97        };
98
99        let cancel_task = {
100            let tx = port.tx();
101            with!(running => move || {
102                if running.get() {
103                    let _ = tx.send(TaskCommand::Cancel);
104                }
105            })
106        };
107
108        let pct = progress.get();
109        let bar_width = 30usize;
110        let filled = (pct as usize * bar_width) / 100;
111        let bar = format!(
112            "[{}{}] {}%",
113            "█".repeat(filled),
114            "░".repeat(bar_width - filled),
115            pct
116        );
117
118        View::vstack()
119            .spacing(1)
120            .child(View::styled_text("Port: Background Task Runner").bold().build())
121            .child(
122                View::hstack()
123                    .spacing(1)
124                    .child(View::styled_text("Status:").dim().build())
125                    .child(View::styled_text(status.get())
126                        .color(if running.get() { Color::Yellow } else if pct == 100 { Color::Green } else { Color::Reset })
127                        .bold()
128                        .build())
129                    .build(),
130            )
131            .child(View::styled_text(&bar).color(
132                if pct == 100 { Color::Green }
133                else if pct > 50 { Color::Yellow }
134                else { Color::Cyan }
135            ).build())
136            .child(if let Some(data) = result.get() {
137                View::vstack()
138                    .child(View::styled_text("Result:").dim().build())
139                    .child(View::styled_text(format!("  {}", data)).color(Color::Green).build())
140                    .build()
141            } else {
142                View::empty()
143            })
144            .child(
145                View::hstack()
146                    .spacing(1)
147                    .child(
148                        View::button()
149                            .label(if running.get() { "[ Running... ]" } else { "[ Start Task ]" })
150                            .on_press(start_task)
151                            .build(),
152                    )
153                    .child(
154                        View::button()
155                            .label("[ Cancel ]")
156                            .on_press(cancel_task)
157                            .build(),
158                    )
159                    .build(),
160            )
161            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
162            .child(
163                View::modal()
164                    .visible(show_help.get())
165                    .title("Example 39: Port")
166                    .on_dismiss(with!(show_help => move || show_help.set(false)))
167                    .child(
168                        View::vstack()
169                            .child(View::styled_text("What you're seeing").bold().build())
170                            .child(View::text("• Background task with progress"))
171                            .child(View::text("• Bidirectional communication"))
172                            .child(View::text("• Start and cancel controls"))
173                            .child(View::gap(1))
174                            .child(View::styled_text("Key concepts").bold().build())
175                            .child(View::text("• port!(cx, InType, OutType)"))
176                            .child(View::text("• port.rx.tx() sends to UI"))
177                            .child(View::text("• port.tx() sends to worker"))
178                            .child(View::text("• port.take_outbound_rx() for worker"))
179                            .child(View::text("• port.rx.get() reads this frame"))
180                            .child(View::gap(1))
181                            .child(View::styled_text("Try this").bold().build())
182                            .child(View::text("• Start a task, watch progress"))
183                            .child(View::text("• Cancel mid-way"))
184                            .child(View::text("• Start another after completion"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Press Escape to close").dim().build())
187                            .build(),
188                    )
189                    .build(),
190            )
191            .build()
192    }
Source

pub fn len(&self) -> usize

Get the number of messages this frame.

Source

pub fn is_empty(&self) -> bool

Check if there are no messages this frame.

Trait Implementations§

Source§

impl<T: 'static> ChannelDrain for ChannelHandle<T>

Source§

fn drain(&self)

Move pending messages from the mpsc receiver into the frame buffer.
Source§

fn clear(&self)

Clear the frame buffer (called at the start of each frame).
Source§

fn has_messages(&self) -> bool

Returns true if there are messages in the current frame buffer.
Source§

impl<T> Clone for ChannelHandle<T>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl<T> Freeze for ChannelHandle<T>

§

impl<T> !RefUnwindSafe for ChannelHandle<T>

§

impl<T> !Send for ChannelHandle<T>

§

impl<T> !Sync for ChannelHandle<T>

§

impl<T> Unpin for ChannelHandle<T>

§

impl<T> !UnwindSafe for ChannelHandle<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.