merge integration
This commit is contained in:
commit
e2dadf1044
|
|
@ -64,13 +64,18 @@ pub enum Message {
|
|||
|
||||
pub struct App {
|
||||
pub state: AppState,
|
||||
pub global_config: crate::config::AudioOxideConfig,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
state: AppState::ProjectView(ProjectViewState::Splash),
|
||||
}
|
||||
let config = crate::first_run::load_or_initialize_config();
|
||||
let state = if config.first_run {
|
||||
AppState::FirstRun { project_dir: config.project_dir.clone() }
|
||||
} else {
|
||||
AppState::ProjectView(ProjectViewState::Splash)
|
||||
};
|
||||
Self { state, global_config: config }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,12 +84,267 @@ pub fn main() -> iced::Result {
|
|||
.run()
|
||||
}
|
||||
|
||||
fn scan_recent_projects(project_dir: &PathBuf) -> Vec<ProjectInfo> {
|
||||
let mut projects = Vec::new();
|
||||
let entries = match std::fs::read_dir(project_dir) {
|
||||
Ok(e) => e,
|
||||
Err(_) => return projects,
|
||||
};
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if !path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
let config_path = path.join("project.toml");
|
||||
if !config_path.exists() {
|
||||
continue;
|
||||
}
|
||||
let name = path.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("Untitled")
|
||||
.to_string();
|
||||
let modified = std::fs::metadata(&config_path)
|
||||
.and_then(|m| m.modified())
|
||||
.ok()
|
||||
.and_then(|t| {
|
||||
let duration = t.duration_since(std::time::UNIX_EPOCH).ok()?;
|
||||
chrono::DateTime::from_timestamp(duration.as_secs() as i64, 0)
|
||||
.map(|dt| dt.naive_local())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
projects.push(ProjectInfo { name, path, modified });
|
||||
}
|
||||
projects.sort_by(|a, b| b.modified.cmp(&a.modified));
|
||||
projects
|
||||
}
|
||||
|
||||
fn parse_time_signature(s: &str) -> Option<(u8, u8)> {
|
||||
let parts: Vec<&str> = s.split('/').collect();
|
||||
if parts.len() == 2 {
|
||||
let num = parts[0].trim().parse::<u8>().ok()?;
|
||||
let den = parts[1].trim().parse::<u8>().ok()?;
|
||||
Some((num, den))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_tempo_to_state(state: &mut AppState, bpm: f32) {
|
||||
if let AppState::NewProject(np) = state {
|
||||
np.config.tempo = bpm;
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_time_signature_to_state(state: &mut AppState, num: u8, den: u8) {
|
||||
if let AppState::NewProject(np) = state {
|
||||
np.config.time_signature_numerator = num;
|
||||
np.config.time_signature_denominator = den;
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn update(&mut self, _message: Message) -> Task<Message> {
|
||||
fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match message {
|
||||
Message::ViewRecentProjects => {
|
||||
let projects = scan_recent_projects(&self.global_config.project_dir);
|
||||
self.state = AppState::ProjectView(ProjectViewState::Recent { projects });
|
||||
}
|
||||
Message::ViewFindProject => {
|
||||
self.state = AppState::ProjectView(ProjectViewState::Find {
|
||||
path_input: String::new(),
|
||||
});
|
||||
}
|
||||
Message::ViewNewProject => {
|
||||
self.state = AppState::NewProject(crate::gui::new_project::State::default());
|
||||
}
|
||||
Message::ViewTimeUtility => {
|
||||
let return_state = std::mem::replace(
|
||||
&mut self.state,
|
||||
AppState::ProjectView(ProjectViewState::Splash),
|
||||
);
|
||||
self.state = AppState::TimeUtility {
|
||||
tapper_state: time_utility::State::default(),
|
||||
return_state: Box::new(return_state),
|
||||
};
|
||||
}
|
||||
|
||||
Message::FindPathChanged(s) => {
|
||||
if let AppState::ProjectView(ProjectViewState::Find { ref mut path_input }) = self.state {
|
||||
*path_input = s;
|
||||
}
|
||||
}
|
||||
|
||||
Message::OpenProject(path) => {
|
||||
let (editor, task) = crate::editor::Editor::new(path);
|
||||
self.state = AppState::Editor(editor);
|
||||
return task.map(Message::EditorMessage);
|
||||
}
|
||||
Message::CreateProject => {
|
||||
if let AppState::NewProject(ref np_state) = self.state {
|
||||
let config = &np_state.config;
|
||||
let project_dir = self.global_config.project_dir.join(&config.name);
|
||||
let _ = std::fs::create_dir_all(&project_dir);
|
||||
let config_path = project_dir.join("project.toml");
|
||||
if let Ok(toml_str) = toml::to_string_pretty(config) {
|
||||
let _ = std::fs::write(&config_path, toml_str);
|
||||
}
|
||||
let (editor, task) = crate::editor::Editor::new(project_dir);
|
||||
self.state = AppState::Editor(editor);
|
||||
return task.map(Message::EditorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Message::ProjectNameChanged(s) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
np.config.name = s;
|
||||
}
|
||||
}
|
||||
Message::SampleRateSelected(sr) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
np.config.sample_rate = sr;
|
||||
}
|
||||
}
|
||||
Message::OutputBufferSizeSelected(bs) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
np.config.output_buffer_size = bs;
|
||||
}
|
||||
}
|
||||
Message::InputBufferSizeSelected(bs) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
np.config.input_buffer_size = bs;
|
||||
}
|
||||
}
|
||||
Message::AudioDeviceSelected(d) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
np.config.audio_device = d;
|
||||
}
|
||||
}
|
||||
Message::InputDeviceSelected(d) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
np.config.audio_input_device = d;
|
||||
}
|
||||
}
|
||||
Message::TempoChanged(t) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
np.config.tempo = t;
|
||||
}
|
||||
}
|
||||
Message::TimeSignatureNumeratorChanged(s) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
if let Ok(v) = s.parse::<u8>() {
|
||||
np.config.time_signature_numerator = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::TimeSignatureDenominatorChanged(s) => {
|
||||
if let AppState::NewProject(ref mut np) = self.state {
|
||||
if let Ok(v) = s.parse::<u8>() {
|
||||
np.config.time_signature_denominator = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message::FirstRunProjectDirChanged(s) => {
|
||||
if let AppState::FirstRun { ref mut project_dir } = self.state {
|
||||
*project_dir = PathBuf::from(s);
|
||||
}
|
||||
}
|
||||
Message::FirstRunComplete => {
|
||||
if let AppState::FirstRun { ref project_dir } = self.state {
|
||||
self.global_config.first_run = false;
|
||||
self.global_config.project_dir = project_dir.clone();
|
||||
let _ = std::fs::create_dir_all(&self.global_config.project_dir);
|
||||
crate::first_run::save_config(&self.global_config);
|
||||
}
|
||||
self.state = AppState::ProjectView(ProjectViewState::Splash);
|
||||
}
|
||||
|
||||
Message::TimeUtilityTapPressed => {
|
||||
if let AppState::TimeUtility { ref mut tapper_state, .. } = self.state {
|
||||
time_utility::handle_tap_pressed(tapper_state);
|
||||
}
|
||||
}
|
||||
Message::TimeUtilityTapReleased => {
|
||||
if let AppState::TimeUtility { ref mut tapper_state, .. } = self.state {
|
||||
return time_utility::handle_tap_released(tapper_state);
|
||||
}
|
||||
}
|
||||
Message::RunTimeUtilityAnalysis => {
|
||||
if let AppState::TimeUtility { ref mut tapper_state, .. } = self.state {
|
||||
tapper_state.result = time_utility::run_analysis(&tapper_state.tap_events);
|
||||
}
|
||||
}
|
||||
Message::TimeUtilitySet(bpm) => {
|
||||
if let Some(mut rs) = self.take_time_utility_return() {
|
||||
apply_tempo_to_state(&mut rs, bpm as f32);
|
||||
self.state = rs;
|
||||
}
|
||||
}
|
||||
Message::TimeUtilitySetTimeSignature(sig) => {
|
||||
if let Some(mut rs) = self.take_time_utility_return() {
|
||||
if let Some((num, den)) = parse_time_signature(&sig) {
|
||||
apply_time_signature_to_state(&mut rs, num, den);
|
||||
}
|
||||
self.state = rs;
|
||||
}
|
||||
}
|
||||
Message::TimeUtilitySetBoth(bpm, sig) => {
|
||||
if let Some(mut rs) = self.take_time_utility_return() {
|
||||
apply_tempo_to_state(&mut rs, bpm as f32);
|
||||
if let Some((num, den)) = parse_time_signature(&sig) {
|
||||
apply_time_signature_to_state(&mut rs, num, den);
|
||||
}
|
||||
self.state = rs;
|
||||
}
|
||||
}
|
||||
Message::TimeUtilityCancel => {
|
||||
if let Some(rs) = self.take_time_utility_return() {
|
||||
self.state = rs;
|
||||
} else {
|
||||
self.state = AppState::ProjectView(ProjectViewState::Splash);
|
||||
}
|
||||
}
|
||||
|
||||
Message::EditorMessage(editor_msg) => {
|
||||
if let AppState::Editor(ref mut editor) = self.state {
|
||||
return editor.update(editor_msg).map(Message::EditorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<'_, Message> {
|
||||
crate::gui::splash::view()
|
||||
match &self.state {
|
||||
AppState::ProjectView(ProjectViewState::Splash) => {
|
||||
crate::gui::splash::view()
|
||||
}
|
||||
AppState::ProjectView(pv_state) => {
|
||||
crate::gui::project_viewer::view(pv_state)
|
||||
}
|
||||
AppState::FirstRun { project_dir } => {
|
||||
crate::gui::first_run_wizard::view(project_dir)
|
||||
}
|
||||
AppState::NewProject(np_state) => {
|
||||
crate::gui::new_project::view(np_state)
|
||||
}
|
||||
AppState::TimeUtility { tapper_state, .. } => {
|
||||
time_utility::view(tapper_state)
|
||||
}
|
||||
AppState::Editor(editor) => {
|
||||
editor.view().map(Message::EditorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn take_time_utility_return(&mut self) -> Option<AppState> {
|
||||
let placeholder = AppState::ProjectView(ProjectViewState::Splash);
|
||||
let old = std::mem::replace(&mut self.state, placeholder);
|
||||
if let AppState::TimeUtility { return_state, .. } = old {
|
||||
Some(*return_state)
|
||||
} else {
|
||||
self.state = old;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue