multithreadinguser-interfacerustownership

Can't bring mpsc receiver into iced.rs update logic


I'm really confused here. Context: I'm trying to use mpsc to communicate with a thread that is processing audio with fundsp, while using iced as the frontend:

use fundsp::thingbuf::mpsc::Sender;
use iced::{
    alignment,
    widget::{
        button, column, container, horizontal_rule, horizontal_space, row, scrollable, slider,
        text, text_input, vertical_slider, Column,
    },
    Element, Length, Padding, Settings,
};

use std::sync::*;
use std::thread::*;

mod audio;
use audio::*;

#[derive(Default, Debug)]
struct State {
    volume: f32,
    lowpass: f32,
    q: f32,
}

#[derive(Debug, Clone)]
enum Message {
    VolumeChanged(f32),
    LowPassChanged(f32),
    QChanged(f32),
}

impl State {
    fn update(&mut self, message: Message) {
        match message {
            Message::VolumeChanged(volume) => {
                self.volume = volume;
            }
            Message::LowPassChanged(pass) => {
                self.lowpass = pass;
            }
            Message::QChanged(q) => {
                self.q = q;
            }
        }
    }

    fn view(&self) -> Element<Message> {
        container(
            column!(
                text(self.volume),
                text(self.lowpass),
                text(self.q),
                row!(
                    vertical_slider(0.0..=1000.0, self.volume * 10.0, |value| {
                        Message::VolumeChanged(value / 10.0)
                    }),
                    horizontal_space(),
                    vertical_slider(0.0..=3000.0, self.lowpass, |value| Message::LowPassChanged(
                        value
                    )),
                    horizontal_space(),
                    vertical_slider(0.1..=100.0, self.q * 10.0, |value| Message::QChanged(
                        value / 10.0
                    )),
                )
            )
            .padding(100)
            .height(Length::Fill)
            .width(Length::Fill)
            .align_x(alignment::Horizontal::Center),
        )
        .into()
    }
}

fn main() {
    let (tx, rx) = mpsc::channel();
    audio::run(rx);
    iced::run("Brown Noise Player", State::update, State::view);
}

I need to bring tx into State::update somehow, but I can't see how. Including it in the state object itself doesn't work, because iced::run requires State to implement the Default trait, which Receiver<> doesn't.

I'm pretty new to rust and very new to iced, so apologies if the answer is obvious.


Solution

  • From the docs, iced::run is shorthand for application(...).run(). Application also has a run_with method for cases where State does not implement Default. So you would need something like

    iced::application("Brown Noise Player", State::update, State::view)
        run_with(|| (todo!("create initial state"), todo!("create first message")))