Skip to content

Commit ec41e8a

Browse files
committed
implemented project creation
1 parent eba1b5e commit ec41e8a

File tree

3 files changed

+147
-6
lines changed

3 files changed

+147
-6
lines changed

src/home.rs

+119-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
1+
use std::path::PathBuf;
2+
13
use bevy::prelude::*;
24
use bevy_egui::EguiContext;
35
use rfd::FileDialog;
46

57
use crate::{
6-
file::{pick_folder, PickingEvent, PickingKind},
7-
project::{project_not_loaded, LoadProjectEvent},
8+
file::{pick_file, pick_folder, PickingEvent, PickingKind},
9+
notification::{ToastsExt, ToastsStorage},
10+
project::{create_project, project_not_loaded, LoadProjectEvent, ProjectMeta},
811
};
912

13+
#[derive(Resource, Debug, Default)]
14+
pub struct CreateProjectForm {
15+
meta: ProjectMeta,
16+
music: Option<PathBuf>,
17+
illustration: Option<PathBuf>,
18+
}
19+
1020
pub struct HomePlugin;
1121

1222
impl Plugin for HomePlugin {
1323
fn build(&self, app: &mut App) {
14-
app.add_systems(Update, ui_system.run_if(project_not_loaded()))
15-
.add_systems(Update, load_project_system.run_if(project_not_loaded()));
24+
app.insert_resource(CreateProjectForm::default())
25+
.add_systems(Update, ui_system.run_if(project_not_loaded()))
26+
.add_systems(Update, load_project_system.run_if(project_not_loaded()))
27+
.add_systems(
28+
Update,
29+
(
30+
handle_select_illustration_system,
31+
handle_select_music_system,
32+
handle_create_project_system,
33+
)
34+
.run_if(project_not_loaded()),
35+
);
1636
}
1737
}
1838

@@ -27,6 +47,39 @@ fn ui_system(world: &mut World) {
2747
if ui.button("Load Project").clicked() {
2848
pick_folder(world, PickingKind::OpenProject, FileDialog::new());
2949
}
50+
51+
ui.separator();
52+
53+
ui.horizontal(|ui| {
54+
let form = world.resource_mut::<CreateProjectForm>();
55+
ui.label(format!("{:?}", form.music));
56+
if ui.button("Select Music").clicked() {
57+
pick_file(world, PickingKind::SelectMusic, FileDialog::new());
58+
}
59+
});
60+
ui.horizontal(|ui| {
61+
let form = world.resource_mut::<CreateProjectForm>();
62+
ui.label(format!("{:?}", form.illustration));
63+
if ui.button("Select Illustration").clicked() {
64+
pick_file(world, PickingKind::SelectIllustration, FileDialog::new());
65+
}
66+
});
67+
68+
let form = world.resource_mut::<CreateProjectForm>();
69+
if ui.button("Create Project").clicked() {
70+
if form.music.is_none() {
71+
let mut toasts = world.resource_mut::<ToastsStorage>();
72+
toasts.error("Music is not selected");
73+
return;
74+
};
75+
if form.illustration.is_none() {
76+
let mut toasts = world.resource_mut::<ToastsStorage>();
77+
toasts.error("Illustration is not selected");
78+
return;
79+
};
80+
81+
pick_folder(world, PickingKind::CreateProject, FileDialog::new());
82+
}
3083
});
3184
}
3285

@@ -43,3 +96,65 @@ fn load_project_system(
4396
}
4497
}
4598
}
99+
100+
fn handle_select_illustration_system(
101+
mut events: EventReader<PickingEvent>,
102+
mut form: ResMut<CreateProjectForm>,
103+
) {
104+
for PickingEvent { path, kind } in events.read() {
105+
if !matches!(kind, PickingKind::SelectIllustration) {
106+
continue;
107+
}
108+
form.illustration = path.clone();
109+
}
110+
}
111+
112+
fn handle_select_music_system(
113+
mut events: EventReader<PickingEvent>,
114+
mut form: ResMut<CreateProjectForm>,
115+
) {
116+
for PickingEvent { path, kind } in events.read() {
117+
if !matches!(kind, PickingKind::SelectMusic) {
118+
continue;
119+
}
120+
form.music = path.clone();
121+
}
122+
}
123+
124+
fn handle_create_project_system(
125+
mut events: EventReader<PickingEvent>,
126+
form: Res<CreateProjectForm>,
127+
mut load_project_events: EventWriter<LoadProjectEvent>,
128+
129+
mut toasts: ResMut<ToastsStorage>,
130+
) {
131+
for PickingEvent { path, kind } in events.read() {
132+
if !matches!(kind, PickingKind::CreateProject) {
133+
continue;
134+
}
135+
136+
let Some(root_path) = path else {
137+
return;
138+
};
139+
140+
let Some(ref music_path) = form.music else {
141+
return;
142+
};
143+
144+
let Some(ref illustration_path) = form.illustration else {
145+
return;
146+
};
147+
148+
match create_project(
149+
root_path.clone(),
150+
music_path.clone(),
151+
illustration_path.clone(),
152+
form.meta.clone(),
153+
) {
154+
Ok(_) => {
155+
load_project_events.send(LoadProjectEvent(root_path.clone()));
156+
},
157+
Err(error) => toasts.error(format!("{:?}", error)),
158+
}
159+
}
160+
}

src/project.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
serialzation::PhiChainChart,
1111
};
1212

13-
#[derive(Debug, Serialize, Deserialize, Default)]
13+
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1414
pub struct ProjectMeta {
1515
pub composer: String,
1616
pub charter: String,
@@ -124,3 +124,20 @@ fn load_project_system(
124124

125125
events.clear();
126126
}
127+
128+
pub fn create_project(
129+
root_path: PathBuf,
130+
music_path: PathBuf,
131+
illustration_path: PathBuf,
132+
project_meta: ProjectMeta,
133+
) -> anyhow::Result<()> {
134+
let project_path = ProjectPath(root_path);
135+
std::fs::copy(music_path, project_path.music_path()).context("Failed to copy music file")?;
136+
std::fs::copy(illustration_path, project_path.illustration_path()).context("Failed to copy illustration file")?;
137+
let meta_string = serde_json::to_string_pretty(&project_meta).unwrap();
138+
std::fs::write(project_path.meta_path(), meta_string).context("Failed to write meta")?;
139+
let chart_string = serde_json::to_string_pretty(&PhiChainChart::default()).unwrap();
140+
std::fs::write(project_path.chart_path(), chart_string).context("Failed to write meta")?;
141+
142+
Ok(())
143+
}

src/serialzation.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
timing::BpmList,
1010
};
1111

12-
#[derive(Serialize, Deserialize, Default)]
12+
#[derive(Serialize, Deserialize)]
1313
pub struct PhiChainChart {
1414
pub bpm_list: BpmList,
1515
pub lines: Vec<LineWrapper>,
@@ -21,6 +21,15 @@ impl PhiChainChart {
2121
}
2222
}
2323

24+
impl Default for PhiChainChart {
25+
fn default() -> Self {
26+
Self {
27+
bpm_list: Default::default(),
28+
lines: vec![Default::default()],
29+
}
30+
}
31+
}
32+
2433
/// A wrapper struct to handle line serialzation and deserialzation
2534
#[derive(Serialize, Deserialize)]
2635
pub struct LineWrapper(pub Vec<Note>, pub Vec<LineEvent>);

0 commit comments

Comments
 (0)