use crate::config::ProjectConfig; use crate::entry::Message; use cpal::traits::{DeviceTrait, HostTrait}; use iced::widget::{button, column, container, pick_list, row, slider, text, text_input}; use iced::{Alignment, Element}; use std::collections::BTreeSet; #[derive(Debug, Clone)] pub struct State { pub config: ProjectConfig, pub available_output_devices: BTreeSet, pub available_input_devices: BTreeSet, } impl Default for State { fn default() -> Self { let host = cpal::default_host(); let output_devices = host .output_devices() .ok() .into_iter() .flatten() .filter_map(|d| d.name().ok()) .collect(); let input_devices = host .input_devices() .ok() .into_iter() .flatten() .filter_map(|d| d.name().ok()) .collect(); Self { config: ProjectConfig { name: "New Project".to_string(), sample_rate: 48000, output_buffer_size: 512, input_buffer_size: 512, audio_device: "Default".to_string(), audio_input_device: "Default".to_string(), auto_oversample: true, auto_undersample: true, tempo: 120.0, time_signature_numerator: 4, time_signature_denominator: 4, tracks: Vec::new(), }, available_output_devices: output_devices, available_input_devices: input_devices, } } } pub fn view(state: &State) -> Element<'static, Message> { let config = &state.config; let sample_rates = vec![44100, 48000, 96000]; let buffer_sizes = vec![256, 512, 1024]; let output_devices: Vec = state.available_output_devices.iter().cloned().collect(); let input_devices: Vec = state.available_input_devices.iter().cloned().collect(); let controls = column![ row![ text("Project Name:").width(150), text_input("My Awesome Track", &config.name).on_input(Message::ProjectNameChanged) ] .spacing(10), row![ text("Sample Rate:").width(150), pick_list( sample_rates, Some(config.sample_rate), Message::SampleRateSelected ) ] .spacing(10), row![ text("Output Buffer:").width(150), pick_list( buffer_sizes.clone(), Some(config.output_buffer_size), Message::OutputBufferSizeSelected ) ] .spacing(10), row![ text("Input Buffer:").width(150), pick_list( buffer_sizes, Some(config.input_buffer_size), Message::InputBufferSizeSelected ) ] .spacing(10), row![ text("Output Device:").width(150), pick_list( output_devices, Some(config.audio_device.clone()), Message::AudioDeviceSelected ) ] .spacing(10), row![ text("Input Device:").width(150), pick_list( input_devices, Some(config.audio_input_device.clone()), Message::InputDeviceSelected ) ] .spacing(10), row![ text("Time Signature:").width(150), text_input("4", &config.time_signature_numerator.to_string()) .on_input(Message::TimeSignatureNumeratorChanged) .width(50), text("/").width(20).align_x(Alignment::Center), text_input("4", &config.time_signature_denominator.to_string()) .on_input(Message::TimeSignatureDenominatorChanged) .width(50), ] .spacing(10) .align_y(Alignment::Center), row![ text("Tempo (BPM):").width(150), slider(40.0..=240.0, config.tempo, Message::TempoChanged).step(0.1), text(format!("{:.1}", config.tempo)) ] .spacing(10), container(row![ button("Time Utility").on_press(Message::ViewTimeUtility) ]) .align_x(Alignment::End), ] .spacing(10); column![ text("Create New Project").size(30), controls, row![ button("Create Project").on_press(Message::CreateProject), button("Cancel").on_press(Message::ViewRecentProjects) ] .spacing(10) ] .spacing(20) .align_x(Alignment::Center) .into() }