pub struct ChannelHandle<T> { /* private fields */ }Implementations§
Source§impl<T: 'static> ChannelHandle<T>
impl<T: 'static> ChannelHandle<T>
Sourcepub fn new(wake: Arc<AtomicBool>) -> Self
pub fn new(wake: Arc<AtomicBool>) -> Self
Create a new channel handle with an event-loop wake flag.
Sourcepub fn tx(&self) -> WakingSender<T>
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
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 }Sourcepub fn get(&self) -> Vec<T>where
T: Clone,
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
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 }Trait Implementations§
Source§impl<T: 'static> ChannelDrain for ChannelHandle<T>
impl<T: 'static> ChannelDrain for ChannelHandle<T>
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
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>
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)
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)
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.