diff --git a/luda-editor/client/Cargo.lock b/luda-editor/client/Cargo.lock index 3be57f735..e55ed8a57 100644 --- a/luda-editor/client/Cargo.lock +++ b/luda-editor/client/Cargo.lock @@ -550,9 +550,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "lock_api" @@ -596,6 +596,7 @@ dependencies = [ "rpc", "serde", "serde_json", + "tokio", "unicode-normalization", "wasm-bindgen", "wasm-bindgen-test", @@ -620,6 +621,14 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "migration" version = "0.1.0" +dependencies = [ + "bincode", + "migration-macro", +] + +[[package]] +name = "migration-macro" +version = "0.1.0" dependencies = [ "proc-macro2", "quote", @@ -1171,6 +1180,7 @@ dependencies = [ name = "rpc" version = "0.1.0" dependencies = [ + "bincode", "migration", "namui", "namui-type", @@ -1365,9 +1375,9 @@ checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1482,18 +1492,17 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.22.0" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "pin-project-lite", "socket2", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1826,21 +1835,51 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.0", "windows_aarch64_msvc 0.42.0", "windows_i686_gnu 0.42.0", "windows_i686_msvc 0.42.0", "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.0", "windows_x86_64_msvc 0.42.0", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -1853,6 +1892,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -1865,6 +1910,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -1877,6 +1928,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -1889,12 +1946,24 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -1907,6 +1976,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.10.1" diff --git a/luda-editor/client/Cargo.toml b/luda-editor/client/Cargo.toml index c30cc731e..f70fc6565 100644 --- a/luda-editor/client/Cargo.toml +++ b/luda-editor/client/Cargo.toml @@ -20,6 +20,7 @@ futures = "0.3.17" wasm-bindgen = "0.2.84" unicode-normalization = "0.1" crc32fast = "1.3" +tokio = { version = "1.28.2", features = ["sync"] } [dev-dependencies] wasm-bindgen-test = "0.3.13" diff --git a/luda-editor/client/src/atom/mod.rs b/luda-editor/client/src/atom/mod.rs new file mode 100644 index 000000000..241adf209 --- /dev/null +++ b/luda-editor/client/src/atom/mod.rs @@ -0,0 +1,78 @@ +use std::sync::{Arc, Mutex, MutexGuard}; + +#[allow(dead_code)] +pub struct Atom { + atom: Mutex, +} + +#[allow(dead_code)] +impl Atom { + pub fn get(&self) -> MutexGuard { + self.atom.lock().unwrap() + } + + pub fn update(&self, f: F) + where + F: FnOnce(&mut TAtom), + { + let mut atom = self.atom.lock().unwrap(); + f(&mut atom); + atom.on_update(); + namui::event::send(namui::NamuiEvent::NoUpdateJustRender); + } + + pub(crate) fn new(atom: TAtom) -> Self { + Self { + atom: Mutex::new(atom), + } + } +} + +pub struct OptionAtom { + inner: Mutex>>, +} + +impl OptionAtom { + pub fn get_unwrap(&self) -> Arc { + let inner = self.inner.lock().unwrap(); + inner.as_ref().unwrap().clone() + } + + pub fn set(&self, atom: TAtom) { + let mut inner = self.inner.lock().unwrap(); + *inner = Some(Arc::new(atom)); + namui::event::send(namui::NamuiEvent::NoUpdateJustRender); + } + + pub fn update(&self, f: F) + where + F: FnOnce(&mut TAtom), + { + let mut inner = self.inner.lock().unwrap(); + let Some(atom) = inner.as_mut() else { + return; + }; + let atom = Arc::get_mut(atom).unwrap(); + f(&mut *atom); + atom.on_update(); + namui::event::send(namui::NamuiEvent::NoUpdateJustRender); + } + + pub(crate) const fn new() -> Self { + Self { + inner: Mutex::new(None), + } + } +} + +pub trait Atomic { + fn on_update(&self); +} + +impl Atomic for Option { + fn on_update(&self) { + if let Some(value) = self { + value.on_update(); + } + } +} diff --git a/luda-editor/client/src/color.rs b/luda-editor/client/src/color.rs index 8ec7c602f..5761dedb9 100644 --- a/luda-editor/client/src/color.rs +++ b/luda-editor/client/src/color.rs @@ -1,13 +1,20 @@ use namui::Color; +#[allow(dead_code)] pub const BACKGROUND: Color = Color::grayscale_u8(32); +#[allow(dead_code)] pub const STROKE_NORMAL: Color = Color::grayscale_u8(204); +#[allow(dead_code)] pub const STROKE_DISABLED: Color = Color::grayscale_u8(128); +#[allow(dead_code)] pub const STROKE_HOVER: Color = Color::grayscale_u8(230); +#[allow(dead_code)] pub const STROKE_SELECTED: Color = Color::grayscale_u8(255); +#[allow(dead_code)] pub const STROKE_FOCUS: Color = Color::from_u8(155, 109, 255, 255); +#[allow(dead_code)] pub const fn stroke_color(is_selected: bool, is_focused: bool) -> Color { if is_selected && is_focused { STROKE_FOCUS diff --git a/luda-editor/client/src/components/cg_render.rs b/luda-editor/client/src/components/cg_render.rs new file mode 100644 index 000000000..71f7a3ed8 --- /dev/null +++ b/luda-editor/client/src/components/cg_render.rs @@ -0,0 +1,74 @@ +use crate::storage::get_project_cg_part_variant_image_url; +use namui::prelude::*; +use rpc::data::*; + +pub struct CgRenderProps { + pub rect: Rect, + pub project_id: Uuid, + pub cg_id: Uuid, +} + +pub fn render_cg(props: CgRenderProps, screen_cg: &ScreenCg, cg_file: &CgFile) -> RenderingTree { + render(screen_cg.parts.iter().map(|part| { + try_render(|| { + let cg_part = cg_file.parts.iter().find(|part| part.name == part.name)?; + Some(render_cg_part(&props, part, cg_part)) + }) + })) +} + +fn render_cg_part( + props: &CgRenderProps, + part: &rpc::data::ScreenCgPart, + cg_part: &CgPart, +) -> RenderingTree { + match part { + rpc::data::ScreenCgPart::Single { variant_name, .. } => try_render(|| { + let variant_name = variant_name.as_ref()?; + + let variant = cg_part + .variants + .iter() + .find(|variant| &variant.name == variant_name)?; + + Some(render_cg_variant(props, variant)) + }), + rpc::data::ScreenCgPart::Multi { variant_names, .. } => render( + cg_part + .variants + .iter() + .filter(|variant| variant_names.contains(&variant.name)) + .map(|variant| render_cg_variant(props, variant)), + ), + rpc::data::ScreenCgPart::AlwaysOn { .. } => render( + cg_part + .variants + .iter() + .map(|variant| render_cg_variant(props, variant)), + ), + } +} + +fn render_cg_variant(props: &CgRenderProps, variant: &rpc::data::CgPartVariant) -> RenderingTree { + try_render(|| { + let rect = Rect::Xywh { + x: props.rect.x() + props.rect.width() * variant.rect.x(), + y: props.rect.y() + props.rect.height() * variant.rect.y(), + width: props.rect.width() * variant.rect.width(), + height: props.rect.height() * variant.rect.height(), + }; + + let url = get_project_cg_part_variant_image_url(props.project_id, props.cg_id, variant.id) + .unwrap(); + + let image = namui::image::try_load_url(&url)?; + Some(namui::image(ImageParam { + rect, + source: ImageSource::Image(image), + style: ImageStyle { + fit: ImageFit::Fill, + paint_builder: None, + }, + })) + }) +} diff --git a/luda-editor/client/src/components/context_menu.rs b/luda-editor/client/src/components/context_menu.rs index 035073d6b..c61ea5e0f 100644 --- a/luda-editor/client/src/components/context_menu.rs +++ b/luda-editor/client/src/components/context_menu.rs @@ -8,6 +8,8 @@ pub enum Item { text: String, on_click: ClosurePtr<(), ()>, }, + + #[allow(dead_code)] Divider, } diff --git a/luda-editor/client/src/components/mod.rs b/luda-editor/client/src/components/mod.rs index bcf3bab49..5eb56abc7 100644 --- a/luda-editor/client/src/components/mod.rs +++ b/luda-editor/client/src/components/mod.rs @@ -1,3 +1,3 @@ +pub mod cg_render; pub mod context_menu; pub mod sequence_player; -pub mod sync; diff --git a/luda-editor/client/src/components/sequence_player.rs b/luda-editor/client/src/components/sequence_player.rs index 56a61f562..07bf55413 100644 --- a/luda-editor/client/src/components/sequence_player.rs +++ b/luda-editor/client/src/components/sequence_player.rs @@ -1,7 +1,5 @@ -use crate::storage::{ - get_project_cg_part_variant_image_url, get_project_cg_thumbnail_image_url, - get_project_image_url, -}; +use super::cg_render; +use crate::storage::{get_project_cg_thumbnail_image_url, get_project_image_url}; use namui::prelude::*; use namui_prebuilt::*; use rpc::data::*; @@ -10,6 +8,7 @@ pub struct SequencePlayer { sequence: Sequence, project_shared_data: ProjectSharedData, state: State, + cg_files: Option>, } pub struct Props { @@ -20,6 +19,7 @@ enum InternalEvent { OnClickScreen, GoToPrevCut, GoToNextCut, + CgFilesLoaded(Vec), } enum State { @@ -38,19 +38,34 @@ impl SequencePlayer { sequence: Sequence, project_shared_data: ProjectSharedData, start_cut_index: usize, + cg_files: Option>, ) -> Self { + let project_id = project_shared_data.id(); + if cg_files.is_none() { + spawn_local(async move { + let response = crate::RPC + .list_cg_files(rpc::list_cg_files::Request { project_id }) + .await + .unwrap(); + namui::event::send(InternalEvent::CgFilesLoaded(response.cg_files)); + }); + } Self { sequence, project_shared_data, state: State::ShowingCut { cut_index: start_cut_index, }, + cg_files, } } pub fn render(&self, props: Props) -> RenderingTree { if self.sequence.cuts.is_empty() { return RenderingTree::Empty; } + let Some(cg_files) = &self.cg_files else { + return RenderingTree::Empty; + }; let inner_content_rect = get_inner_content_rect(props.wh); @@ -66,6 +81,7 @@ impl SequencePlayer { inner_content_rect.wh(), cut, 1.0.one_zero(), + &cg_files, ), render_text_box(inner_content_rect.wh()), render_text( @@ -98,6 +114,7 @@ impl SequencePlayer { inner_content_rect.wh(), from_cut_index, transition_progress, + &cg_files, ), render_text_box(inner_content_rect.wh()), render_text( @@ -147,30 +164,11 @@ impl SequencePlayer { } InternalEvent::GoToPrevCut => self.go_to_prev_cut(), InternalEvent::GoToNextCut => self.go_to_next_cut(false), + InternalEvent::CgFilesLoaded(cg_files) => { + self.cg_files = Some(cg_files.clone()); + } }); } - fn get_image_urls(&self, cut: &Cut) -> Vec { - cut.screen_graphics - .iter() - .flat_map(|screen_graphic| match screen_graphic { - ScreenGraphic::Image(image) => { - vec![get_project_image_url(self.project_shared_data.id(), image.id).unwrap()] - } - ScreenGraphic::Cg(cg) => cg - .part_variants - .iter() - .map(|(variant_id, _)| { - get_project_cg_part_variant_image_url( - self.project_shared_data.id(), - cg.id, - *variant_id, - ) - .unwrap() - }) - .collect::>(), - }) - .collect::>() - } fn go_to_next_cut(&mut self, do_transition: bool) { if self.sequence.cuts.len() == 0 { @@ -227,23 +225,22 @@ impl SequencePlayer { wh: Wh, from_cut_index: usize, transition_progress: OneZero, + cg_files: &Vec, ) -> RenderingTree { let from_cut = self.sequence.cuts.get(from_cut_index).unwrap(); let to_cut = self.sequence.cuts.get(from_cut_index + 1).unwrap(); - let from_cut_image_urls = self.get_image_urls(from_cut); - let to_cut_image_urls = self.get_image_urls(to_cut); let project_id = self.project_shared_data.id(); - if from_cut_image_urls == to_cut_image_urls { - render_graphics(project_id, wh, from_cut, 1.0.one_zero()) + if from_cut.screen_graphics == to_cut.screen_graphics { + render_graphics(project_id, wh, from_cut, 1.0.one_zero(), cg_files) } else { let from_opacity = 1.0.one_zero() - transition_progress; let to_opacity = transition_progress; render([ - render_graphics(project_id, wh, from_cut, from_opacity), - render_graphics(project_id, wh, to_cut, to_opacity), + render_graphics(project_id, wh, from_cut, from_opacity, cg_files), + render_graphics(project_id, wh, to_cut, to_opacity, cg_files), ]) } } @@ -412,14 +409,26 @@ pub fn calculate_graphic_rect_on_screen( Rect::from_xy_wh(xy, wh) } -pub fn render_graphics(project_id: Uuid, wh: Wh, cut: &Cut, opacity: OneZero) -> RenderingTree { +pub fn render_graphics( + project_id: Uuid, + wh: Wh, + cut: &Cut, + opacity: OneZero, + cg_files: &Vec, +) -> RenderingTree { let paint_builder = namui::PaintBuilder::new().set_color_filter( Color::from_f01(1.0, 1.0, 1.0, opacity.as_f32()), BlendMode::DstIn, ); - let graphics = cut.screen_graphics.iter().map(|screen_graphic| { - render_graphic(project_id, wh, screen_graphic, Some(paint_builder.clone())) + let graphics = cut.screen_graphics.iter().map(|(_, screen_graphic)| { + render_graphic( + project_id, + wh, + screen_graphic, + Some(paint_builder.clone()), + cg_files, + ) }); render(graphics) } @@ -429,6 +438,7 @@ pub fn render_graphic( wh: Wh, graphic: &ScreenGraphic, paint_builder: Option, + cg_files: &Vec, ) -> RenderingTree { namui::try_render(|| match graphic { ScreenGraphic::Image(screen_image) => { @@ -453,31 +463,18 @@ pub fn render_graphic( let outer_rect = calculate_graphic_rect_on_screen(image.size(), wh, screen_cg.circumscribed); - Some(render(screen_cg.part_variants.iter().rev().filter_map( - |(variant_id, rect)| { - let rect = Rect::Xywh { - x: outer_rect.x() + outer_rect.width() * rect.x(), - y: outer_rect.y() + outer_rect.height() * rect.y(), - width: outer_rect.width() * rect.width(), - height: outer_rect.height() * rect.height(), - }; - let url = get_project_cg_part_variant_image_url( - project_id, - screen_cg.id, - *variant_id, - ) - .unwrap(); - let image = namui::image::try_load_url(&url)?; - Some(namui::image(ImageParam { - rect, - source: ImageSource::Image(image), - style: ImageStyle { - fit: ImageFit::Fill, - paint_builder: paint_builder.clone(), - }, - })) + let cg_file = cg_files + .iter() + .find(|cg_file| cg_file.name == screen_cg.name)?; + Some(cg_render::render_cg( + cg_render::CgRenderProps { + cg_id: screen_cg.id, + project_id, + rect: outer_rect, }, - ))) + screen_cg, + cg_file, + )) } }) } diff --git a/luda-editor/client/src/lib.rs b/luda-editor/client/src/lib.rs index 154a8e3f6..02e83443c 100644 --- a/luda-editor/client/src/lib.rs +++ b/luda-editor/client/src/lib.rs @@ -1,4 +1,5 @@ mod app; +mod atom; mod color; mod components; mod late_init; diff --git a/luda-editor/client/src/pages/project_list_page/mod.rs b/luda-editor/client/src/pages/project_list_page/mod.rs index 0d78ed736..0daec371f 100644 --- a/luda-editor/client/src/pages/project_list_page/mod.rs +++ b/luda-editor/client/src/pages/project_list_page/mod.rs @@ -140,7 +140,7 @@ impl ProjectListPage { [MouseButton::Left], move |event: MouseEvent| { if event.button == Some(MouseButton::Left) { - Router::move_to(super::router::RoutePath::SequenceList(project_id)); + Router::move_to(super::router::RoutePath::SequenceList { project_id }); } else if event.button == Some(MouseButton::Right) { // TODO // namui::event::send(Event::CellRightClick { diff --git a/luda-editor/client/src/pages/router.rs b/luda-editor/client/src/pages/router.rs index 91e67b47f..4d790ddcb 100644 --- a/luda-editor/client/src/pages/router.rs +++ b/luda-editor/client/src/pages/router.rs @@ -29,11 +29,15 @@ impl From for Route { RoutePath::ProjectList => { Self::ProjectListPage(project_list_page::ProjectListPage::new()) } - RoutePath::SequenceList(project_id) => { + RoutePath::SequenceList { project_id } => { Self::SequenceListPage(sequence_list_page::SequenceListPage::new(project_id)) } - RoutePath::SequenceEdit(sequence_id) => { + RoutePath::SequenceEdit { + project_id, + sequence_id, + } => { return Self::SequenceEditPage(sequence_edit_page::SequenceEditPage::new( + project_id, sequence_id, )) } @@ -103,22 +107,34 @@ fn get_path_from_hash() -> RoutePath { #[derive(Clone)] pub enum RoutePath { ProjectList, - SequenceList(Uuid), - SequenceEdit(Uuid), + SequenceList { project_id: Uuid }, + SequenceEdit { project_id: Uuid, sequence_id: Uuid }, } impl From for RoutePath { - fn from(path_string: String) -> Self { + fn from(mut path_string: String) -> Self { if path_string.starts_with("/sequence_list") { - let rest = path_string.clone().split_off("/sequence_list".len()); + let rest = path_string.split_off("/sequence_list".len()); if let Ok(project_id) = Uuid::parse_str(rest.trim_matches('/')) { - return Self::SequenceList(project_id); + return Self::SequenceList { project_id }; } } if path_string.starts_with("/sequence_edit") { - let rest = path_string.clone().split_off("/sequence_edit".len()); - if let Ok(sequence_id) = Uuid::parse_str(rest.trim_matches('/')) { - return Self::SequenceEdit(sequence_id); + let rest = path_string.split_off("/sequence_edit".len()); + let mut items = rest.split('/'); + items.next(); + let project_id = items.next(); + let sequence_id = items.next(); + + if let (Some(project_id), Some(sequence_id)) = (project_id, sequence_id) { + if let (Ok(project_id), Ok(sequence_id)) = + (Uuid::parse_str(project_id), Uuid::parse_str(sequence_id)) + { + return Self::SequenceEdit { + project_id, + sequence_id, + }; + } } } @@ -129,8 +145,11 @@ impl ToString for RoutePath { fn to_string(&self) -> String { match self { Self::ProjectList => "/".to_string(), - Self::SequenceList(project_id) => format!("/sequence_list/{project_id}"), - Self::SequenceEdit(sequence_id) => format!("/sequence_edit/{sequence_id}"), + Self::SequenceList { project_id } => format!("/sequence_list/{project_id}"), + Self::SequenceEdit { + project_id, + sequence_id, + } => format!("/sequence_edit/{project_id}/{sequence_id}"), } } } diff --git a/luda-editor/client/src/pages/sequence_edit_page/cg_files_atom.rs b/luda-editor/client/src/pages/sequence_edit_page/cg_files_atom.rs new file mode 100644 index 000000000..a27acdf14 --- /dev/null +++ b/luda-editor/client/src/pages/sequence_edit_page/cg_files_atom.rs @@ -0,0 +1,35 @@ +use crate::atom::{Atomic, OptionAtom}; +use rpc::data::CgFile; +use std::ops::Deref; + +impl Atomic for CgFilesAtom { + fn on_update(&self) {} +} + +pub struct CgFilesAtom { + cg_files: Vec, +} + +impl CgFilesAtom { + pub fn new(cg_files: Vec) -> Self { + Self { cg_files } + } + + pub(crate) fn update_file(&mut self, cg_file: CgFile) { + if let Some(index) = self.iter().position(|cg_file_| cg_file_.id == cg_file.id) { + self.cg_files[index] = cg_file; + } else { + self.cg_files.push(cg_file); + } + } +} + +impl Deref for CgFilesAtom { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.cg_files + } +} + +pub static CG_FILES_ATOM: OptionAtom = OptionAtom::new(); diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/auto_complete_text_input/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/auto_complete_text_input/mod.rs index 99c55ea54..c288a743b 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/auto_complete_text_input/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/auto_complete_text_input/mod.rs @@ -22,8 +22,6 @@ pub struct Props< pub on_key_down: OnKeyDown, } -pub enum Event {} - enum InternalEvent { ArrowUpDown { next_index: Option }, UpdateItemIndex { over_item_index: Option }, diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cg_upload/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cg_upload/mod.rs index 211411743..2fe387829 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cg_upload/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cg_upload/mod.rs @@ -1,11 +1,10 @@ -use namui::{uuid_from_hash, Uuid}; -use rpc::utils::retry_on_error; +use rpc::{data::CgFile, utils::retry_on_error}; pub async fn create_cg( project_id: namui::Uuid, psd_file_name: String, psd_file: Vec, -) -> Result> { +) -> Result> { let response = retry_on_error( { let psd_file_size = psd_file.len(); @@ -49,7 +48,7 @@ pub async fn create_cg( ) .await?; - retry_on_error( + let rpc::complete_put_psd::Response { cg_id } = retry_on_error( { let psd_file_name = psd_file_name.clone(); move || { @@ -65,5 +64,11 @@ pub async fn create_cg( ) .await?; - Ok(uuid_from_hash(psd_file_name.as_str())) + let rpc::get_cg_file::Response { cg_file } = retry_on_error( + move || crate::RPC.get_cg_file(rpc::get_cg_file::Request { cg_id, project_id }), + 10, + ) + .await?; + + Ok(cg_file) } diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/mod.rs index cd8d00c33..162a6cc9d 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/mod.rs @@ -2,13 +2,12 @@ mod render; mod update; use namui::prelude::*; use namui_prebuilt::scroll_view::ScrollView; -use rpc::data::{CgFile, Cut, ScreenGraphic}; +use rpc::data::{Cut, CutUpdateAction, ScreenGraphic}; pub struct CharacterEditor { tooltip: Option, scroll_view: ScrollView, edit_target: EditTarget, - cg_file_load_state: CgFileLoadState, } #[derive(Clone, Copy)] @@ -20,13 +19,7 @@ pub struct Props<'a> { pub enum Event { MouseDownOutsideCharacterEditor, - OpenCharacterEditor { - target: EditTarget, - }, - UpdateCutGraphics { - cut_id: Uuid, - callback: Box) -> () + 'static + Send + Sync>, - }, + OpenCharacterEditor { target: EditTarget }, } enum InternalEvent { @@ -36,51 +29,23 @@ enum InternalEvent { }, CloseTooltip, CgChangeButtonClicked, - CgFileLoadStateChanged(CgFileLoadState), CgThumbnailClicked { cg_id: Uuid, }, FocusCg { cut_id: Uuid, cg_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, }, } impl CharacterEditor { - pub fn new(project_id: Uuid, edit_target: EditTarget) -> Self { - let mut character_editor = Self { + pub fn new(edit_target: EditTarget) -> Self { + Self { tooltip: None, scroll_view: ScrollView::new(), edit_target, - cg_file_load_state: CgFileLoadState::Loading, - }; - character_editor.start_load_cg_files(project_id); - character_editor - } - - fn start_load_cg_files(&mut self, project_id: Uuid) { - self.cg_file_load_state = CgFileLoadState::Loading; - spawn_local(async move { - let response = crate::RPC - .list_cg_files(rpc::list_cg_files::Request { project_id }) - .await; - - match response { - Ok(response) => { - namui::event::send(InternalEvent::CgFileLoadStateChanged( - CgFileLoadState::Loaded(response.cg_files), - )); - } - Err(error) => { - namui::event::send(InternalEvent::CgFileLoadStateChanged( - CgFileLoadState::Failed { - error: error.to_string(), - }, - )); - } - } - }); + } } } @@ -96,18 +61,11 @@ pub enum EditTarget { }, ExistingCharacter { cut_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, }, ExistingCharacterPart { cut_id: Uuid, cg_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, }, } - -#[derive(Clone)] -enum CgFileLoadState { - Loading, - Loaded(Vec), - Failed { error: String }, -} diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/mod.rs index c8dfa3910..27f8b3c3d 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/mod.rs @@ -4,7 +4,7 @@ mod text_content; mod tooltip; use super::*; -use crate::color; +use crate::{color, pages::sequence_edit_page::cg_files_atom::CG_FILES_ATOM}; use namui_prebuilt::*; use tooltip::*; @@ -28,34 +28,43 @@ impl CharacterEditor { } fn render_content(&self, props: Props) -> namui::RenderingTree { - match &self.cg_file_load_state { - CgFileLoadState::Loading => self.render_text_content(props.wh, "Loading..."), - CgFileLoadState::Failed { error } => self.render_text_content(props.wh, error), - CgFileLoadState::Loaded(cg_file_list) => { - match self.edit_target { - EditTarget::NewCharacter { .. } | EditTarget::ExistingCharacter { .. } => { - self.render_cg_picker(props.wh, &cg_file_list, props.project_id) - } - EditTarget::ExistingCharacterPart { - cg_id, - cut_id, - graphic_index, - } => { - let selected_cg_file = - cg_file_list.iter().find(|cg_file| cg_file.id == cg_id); - let selected_screen_graphic = - props.cut.map(|cut| &cut.screen_graphics[graphic_index]); + let cg_file_list = CG_FILES_ATOM.get_unwrap(); + match self.edit_target { + EditTarget::NewCharacter { .. } | EditTarget::ExistingCharacter { .. } => { + self.render_cg_picker(props.wh, &cg_file_list, props.project_id) + } + EditTarget::ExistingCharacterPart { + cg_id, + cut_id, + graphic_index, + } => { + let selected_cg_file = cg_file_list.iter().find(|cg_file| cg_file.id == cg_id); + let selected_screen_graphic = props.cut.and_then(|cut| { + cut.screen_graphics + .iter() + .find_map(|(index, screen_graphic)| { + if index == &graphic_index { + Some(screen_graphic) + } else { + None + } + }) + }); - match (selected_cg_file, selected_screen_graphic) { - (Some(selected_cg_file), Some(ScreenGraphic::Cg(selected_screen_cg))) => { - self.render_part_picker(props.wh, selected_cg_file, props.project_id, cut_id, graphic_index, selected_screen_cg) - } - _ => self.render_text_content( + match (selected_cg_file, selected_screen_graphic) { + (Some(selected_cg_file), Some(ScreenGraphic::Cg(selected_screen_cg))) => self + .render_part_picker( props.wh, - "Selected resource not found. Close character picker and try again.", + selected_cg_file, + props.project_id, + cut_id, + graphic_index, + selected_screen_cg, ), - } - } + _ => self.render_text_content( + props.wh, + "Selected resource not found. Close character picker and try again.", + ), } } } diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/part_picker.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/part_picker.rs index f1d6421b9..a4acd04d5 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/part_picker.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/render/part_picker.rs @@ -1,12 +1,15 @@ use super::*; -use crate::{color, storage::get_project_cg_part_variant_image_url}; +use crate::{ + color, pages::sequence_edit_page::sequence_atom::SEQUENCE_ATOM, + storage::get_project_cg_part_variant_image_url, +}; use namui_prebuilt::{ table::TableCell, typography::{center_text, center_text_full_height}, *, }; use rpc::data::{CgFile, CgPart, CgPartVariant, PartSelectionType, ScreenCg}; -use std::{collections::HashMap, iter::once}; +use std::iter::once; use tooltip::*; const BUTTON_HEIGHT: Px = px(32.0); @@ -24,7 +27,7 @@ impl CharacterEditor { cg_file: &CgFile, project_id: Uuid, cut_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, screen_cg: &ScreenCg, ) -> namui::RenderingTree { table::padding(OUTER_PADDING, |wh| { @@ -71,7 +74,7 @@ fn render_cg_part_group_list( cg_file: &CgFile, project_id: Uuid, cut_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, screen_cg: &ScreenCg, ) -> RenderingTree { let cg_id = cg_file.id; @@ -89,7 +92,6 @@ fn render_cg_part_group_list( cut_id, graphic_index, screen_cg, - cg_file, ) }), )(wh) @@ -101,19 +103,10 @@ fn render_cg_part_group<'a>( project_id: Uuid, cg_id: Uuid, cut_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, screen_cg: &'a ScreenCg, - cg_file: &'a CgFile, ) -> Vec> { - let no_selection = !screen_cg - .part_variants - .iter() - .any(|(selected_variant_id, _)| { - cg_part - .variants - .iter() - .any(|variant| variant.id == *selected_variant_id) - }); + let no_selection = screen_cg.part(&cg_part.name).unwrap().is_not_selected(); let title_bar = render_title_bar(cg_part); @@ -129,7 +122,6 @@ fn render_cg_part_group<'a>( .iter() .map(|variant| { render_thumbnail( - cg_file, cg_part, variant, project_id, @@ -146,7 +138,6 @@ fn render_cg_part_group<'a>( table::fixed(THUMBNAIL_WH.height, move |wh| { table::horizontal(row.iter().map(|variant| { render_thumbnail( - cg_file, cg_part, variant, project_id, @@ -181,7 +172,7 @@ fn render_no_selection_button( no_selection: bool, cg_part: &CgPart, cut_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, ) -> TableCell { table::fixed(THUMBNAIL_WH.width, move |wh| { table::padding(INNER_PADDING, |wh| { @@ -198,25 +189,16 @@ fn render_no_selection_button( simple_rect(wh, color::STROKE_NORMAL, 1.px(), Color::TRANSPARENT) .with_mouse_cursor(MouseCursor::Pointer) .attach_event(move |builder| { - let cg_part_variant_ids = cg_part - .variants - .iter() - .map(|variant| variant.id) - .collect::>(); + let cg_part_name = cg_part.name.clone(); builder.on_mouse_down_in(move |_| { - namui::event::send(Event::UpdateCutGraphics { - cut_id, - callback: { - let cg_part_variant_ids = cg_part_variant_ids.clone(); - Box::new(move |graphics| { - if let ScreenGraphic::Cg(cg) = &mut graphics[graphic_index] - { - cg.part_variants.retain(|(variant_id, _)| { - !cg_part_variant_ids.contains(variant_id) - }); - }; - }) - }, + SEQUENCE_ATOM.update(|sequence| { + sequence.update_cut( + cut_id, + CutUpdateAction::UnselectCgPart { + graphic_index, + cg_part_name: cg_part_name.clone(), + }, + ) }); }); }), @@ -230,19 +212,18 @@ fn render_divider<'a>(height: Px) -> TableCell<'a> { } fn render_thumbnail<'a>( - cg_file: &'a CgFile, cg_part: &'a CgPart, cg_part_variant: &'a CgPartVariant, project_id: Uuid, cg_id: Uuid, cut_id: Uuid, - graphic_index: usize, + graphic_index: Uuid, screen_cg: &'a ScreenCg, ) -> TableCell<'a> { - let selected = screen_cg - .part_variants - .iter() - .any(|(cg_part_variant_id, _)| *cg_part_variant_id == cg_part_variant.id); + let variant_selected = screen_cg + .part(&cg_part.name) + .unwrap() + .is_variant_selected(&cg_part_variant.name); table::fixed(THUMBNAIL_WH.width, move |wh| { table::padding(INNER_PADDING, |wh| { @@ -262,7 +243,7 @@ fn render_thumbnail<'a>( }), simple_rect( wh, - match selected { + match variant_selected { true => color::STROKE_SELECTED, false => color::STROKE_NORMAL, }, @@ -272,86 +253,43 @@ fn render_thumbnail<'a>( .attach_event(|builder| { let selection_type = cg_part.selection_type; let cg_part_variant = cg_part_variant.clone(); - let cg_part_variant_ids = cg_part - .variants - .iter() - .map(|variant| variant.id) - .collect::>(); - let cg_variant_id_index_map = HashMap::from_iter( - cg_file - .parts - .iter() - .flat_map(|part| part.variants.iter().map(|variant| variant.id)) - .enumerate() - .map(|(index, variant_id)| (variant_id, index)), - ); - builder.on_mouse_down_in(move |_| { - namui::event::send(Event::UpdateCutGraphics { - cut_id, - callback: { - let cg_part_variant_ids = cg_part_variant_ids.clone(); - let cg_variant_id_index_map = cg_variant_id_index_map.clone(); - Box::new(move |graphics| { - if let ScreenGraphic::Cg(cg) = &mut graphics[graphic_index] { - match (selected, selection_type) { - (true, rpc::data::PartSelectionType::AlwaysOn) => {} - (true, _) => { - cg.part_variants.retain(|(variant_id, _)| { - variant_id != &cg_part_variant.id - }) - } - (false, rpc::data::PartSelectionType::Single) => { - cg.part_variants.retain(|(variant_id, _)| { - !cg_part_variant_ids.contains(variant_id) - }); - cg.part_variants.push(( - cg_part_variant.id, - cg_part_variant.rect, - )); - } - (false, rpc::data::PartSelectionType::Multi) => { - cg.part_variants.retain(|(variant_id, _)| { - variant_id != &cg_part_variant.id - }); - cg.part_variants.push(( - cg_part_variant.id, - cg_part_variant.rect, - )); - } - (false, rpc::data::PartSelectionType::AlwaysOn) => { - cg.part_variants.retain(|(variant_id, _)| { - !cg_part_variant_ids.contains(variant_id) - }); - cg.part_variants.push(( - cg_part_variant.id, - cg_part_variant.rect, - )); - } - } + let cg_part_name = cg_part.name.clone(); + let cg_part_variant_name = cg_part_variant.name.clone(); - sort_variations( - &mut cg.part_variants, - &cg_variant_id_index_map, - ); - }; - }) - }, - }) - }); + match (variant_selected, selection_type) { + (_, rpc::data::PartSelectionType::AlwaysOn) => {} + (true, _) => { + builder.on_mouse_down_in(move |_| { + SEQUENCE_ATOM.update(|sequence| { + sequence.update_cut( + cut_id, + CutUpdateAction::TurnOffCgPartVariant { + graphic_index, + cg_part_name: cg_part_name.clone(), + cg_part_variant_name: cg_part_variant_name.clone(), + }, + ) + }); + }); + } + (false, _) => { + builder.on_mouse_down_in(move |_| { + SEQUENCE_ATOM.update(|sequence| { + sequence.update_cut( + cut_id, + CutUpdateAction::TurnOnCgPartVariant { + graphic_index, + cg_part_name: cg_part_name.clone(), + cg_part_variant_name: cg_part_variant_name.clone(), + }, + ) + }); + }); + } + }; }), ]) .with_tooltip(cg_part_variant.name.clone()) })(wh) }) } - -fn sort_variations( - part_variants: &mut Vec<(Uuid, Rect)>, - cg_variant_id_index_map: &HashMap, -) { - part_variants.sort_by(|(a, _), (b, _)| { - let a_index = cg_variant_id_index_map.get(a).unwrap_or(&0); - let b_index = cg_variant_id_index_map.get(b).unwrap_or(&0); - a_index.cmp(b_index) - }); -} diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/update.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/update.rs index b27cf6494..8c95071e2 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/update.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/character_editor/update.rs @@ -1,4 +1,7 @@ use super::*; +use crate::pages::sequence_edit_page::{ + cg_files_atom::CG_FILES_ATOM, sequence_atom::SEQUENCE_ATOM, +}; use rpc::data::ScreenCg; impl CharacterEditor { @@ -27,41 +30,56 @@ impl CharacterEditor { }; } } - InternalEvent::CgFileLoadStateChanged(cg_file_load_state) => { - self.cg_file_load_state = cg_file_load_state.clone(); - } &InternalEvent::CgThumbnailClicked { cg_id } => match self.edit_target { EditTarget::NewCharacter { cut_id } => { - namui::event::send(Event::UpdateCutGraphics { - cut_id, - callback: Box::new(move |graphics| { - let graphic_index = graphics.len(); - graphics.push(ScreenGraphic::Cg(ScreenCg::new(cg_id, vec![]))); - namui::event::send(InternalEvent::FocusCg { - cut_id, - cg_id, + let cg_files = CG_FILES_ATOM.get_unwrap(); + let Some(cg_file) = cg_files + .iter() + .find(|file| file.id == cg_id) else { + return; + }; + + let graphic_index: Uuid = Uuid::new_v4(); + + SEQUENCE_ATOM.update(|sequence| { + sequence.update_cut( + cut_id, + CutUpdateAction::PushScreenGraphic { graphic_index, - }) - }), + screen_graphic: ScreenGraphic::Cg(ScreenCg::new(cg_file)), + }, + ) + }); + namui::event::send(InternalEvent::FocusCg { + cut_id, + cg_id, + graphic_index, }); } EditTarget::ExistingCharacter { cut_id, graphic_index, } => { - namui::event::send(Event::UpdateCutGraphics { + let cg_files = CG_FILES_ATOM.get_unwrap(); + let Some(cg_file) = cg_files + .iter() + .find(|file| file.id == cg_id) else { + return; + }; + + SEQUENCE_ATOM.update(|sequence| { + sequence.update_cut( + cut_id, + CutUpdateAction::ChangeCgKeepCircumscribed { + graphic_index, + cg: ScreenCg::new(cg_file), + }, + ) + }); + namui::event::send(InternalEvent::FocusCg { cut_id, - callback: Box::new(move |graphics| { - if let ScreenGraphic::Cg(cg) = &mut graphics[graphic_index] { - cg.id = cg_id; - cg.part_variants = vec![]; - namui::event::send(InternalEvent::FocusCg { - cut_id, - cg_id, - graphic_index, - }) - } - }), + cg_id, + graphic_index, }); } _ => {} diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/mod.rs index 453621f5f..e479c2422 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/mod.rs @@ -5,7 +5,7 @@ use crate::components::context_menu::ContextMenu; use super::*; use namui::prelude::*; -use rpc::data::{Cut, ScreenCg}; +use rpc::data::{CgFile, Cut, ScreenCg}; pub struct CutEditor { selected_target: Option, @@ -21,17 +21,10 @@ pub struct Props<'a> { pub cuts: &'a Vec, pub is_focused: bool, pub project_id: Uuid, + pub cg_files: &'a Vec, } pub enum Event { - ChangeCharacterName { - name: String, - cut_id: Uuid, - }, - ChangeCutLine { - text: String, - cut_id: Uuid, - }, MoveCutRequest { cut_id: Uuid, to_prev: bool, diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/background_with_event.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/background_with_event.rs index 5acd242af..ce149477e 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/background_with_event.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/background_with_event.rs @@ -3,7 +3,7 @@ use rpc::data::ScreenCg; impl CutEditor { pub fn background_with_event(&self, props: &Props, cut: &Cut) -> namui::RenderingTree { - let cut_id = cut.id(); + let cut_id = cut.id; let selected_target = self.selected_target; let prev_cut_id = prev_cut_id(&props, cut_id); let next_cut_id = next_cut_id(&props, cut_id); diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/character_name_side.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/character_name_side.rs index 91580d878..6c8f027cf 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/character_name_side.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/character_name_side.rs @@ -1,4 +1,6 @@ use super::*; +use crate::pages::sequence_edit_page::sequence_atom::SEQUENCE_ATOM; +use rpc::data::CutUpdateAction; impl CutEditor { pub fn render_character_name_side( @@ -12,7 +14,7 @@ impl CutEditor { render([ transparent_rect(wh), if props.is_focused && self.selected_target == Some(ClickTarget::CharacterName) { - let cut_id = cut.id(); + let cut_id = cut.id; let prev_cut_id = prev_cut_id(&props, cut_id); self.character_name_input @@ -21,7 +23,12 @@ impl CutEditor { wh, candidates: get_character_name_candidates(&props.cuts, &cut), on_text_change: move |text| { - namui::event::send(Event::ChangeCharacterName { name: text, cut_id }) + SEQUENCE_ATOM.update(|sequence| { + sequence.update_cut( + cut_id, + CutUpdateAction::ChangeCharacterName { name: text }, + ) + }); }, on_edit_done: move || { namui::event::send(Event::Click { @@ -92,7 +99,7 @@ fn get_character_name_candidates(cuts: &[Cut], current_cut: &Cut) -> Vec let cut_index = cuts .iter() - .position(|cut| cut.id() == current_cut.id()) + .position(|cut| cut.id == current_cut.id) .unwrap(); candidates.sort_by_key(|(index, _)| (cut_index as isize - *index as isize).abs()); diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/cut_text_side.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/cut_text_side.rs index 6b71bef5c..62b5d4a73 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/cut_text_side.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/cut_text_side.rs @@ -1,4 +1,6 @@ use super::*; +use crate::pages::sequence_edit_page::sequence_atom::SEQUENCE_ATOM; +use rpc::data::CutUpdateAction; impl CutEditor { pub fn render_cut_text_side( @@ -8,7 +10,7 @@ impl CutEditor { cut: &Cut, ) -> namui::RenderingTree { let line_text = cut.line.clone(); - let cut_id = cut.id(); + let cut_id = cut.id; render([ transparent_rect(wh), @@ -39,7 +41,12 @@ impl CutEditor { event_handler: Some( text_input::EventHandler::new() .on_text_updated(move |text: String| { - namui::event::send(Event::ChangeCutLine { text, cut_id }) + SEQUENCE_ATOM.update(move |sequence| { + sequence.update_cut( + cut_id, + CutUpdateAction::ChangeCutLine { line: text }, + ) + }); }) .on_key_down(move |event: KeyDownEvent| { if event.code == Code::Tab { diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/mod.rs index c1ed9071a..81e359d3c 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/render/mod.rs @@ -32,7 +32,8 @@ impl CutEditor { wh: content_rect.wh(), screen_graphics: &cut.screen_graphics, project_id: props.project_id, - cut_id: cut.id(), + cut_id: cut.id, + cg_files: props.cg_files, }) }), sequence_player::render_text_box(content_rect.wh()), @@ -52,11 +53,11 @@ impl CutEditor { fn prev_cut_id(props: &Props, cut_id: Uuid) -> Option { props.cuts.iter().enumerate().find_map(|(i, cut)| { - if cut.id() == cut_id { + if cut.id == cut_id { if i == 0 { None } else { - Some(props.cuts[i - 1].id()) + Some(props.cuts[i - 1].id) } } else { None @@ -66,11 +67,11 @@ fn prev_cut_id(props: &Props, cut_id: Uuid) -> Option { fn next_cut_id(props: &Props, cut_id: Uuid) -> Option { props.cuts.iter().enumerate().find_map(|(i, cut)| { - if cut.id() == cut_id { + if cut.id == cut_id { if i == props.cuts.len() - 1 { None } else { - Some(props.cuts[i + 1].id()) + Some(props.cuts[i + 1].id) } } else { None diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/update/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/update/mod.rs index 7557f4fe1..fa1f7f764 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/update/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_editor/update/mod.rs @@ -21,11 +21,7 @@ impl CutEditor { &Event::Click { target } => { self.focus(target); } - Event::ChangeCharacterName { .. } - | Event::ChangeCutLine { .. } - | Event::AddNewImage { .. } - | Event::AddNewCg { .. } - | Event::AddCg { .. } => {} + Event::AddNewImage { .. } | Event::AddNewCg { .. } | Event::AddCg { .. } => {} }) .is::(|event| match event { InternalEvent::EscapeKeyDown => { diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_list_view/render/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_list_view/render/mod.rs index 9ab20061b..a8c84585d 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_list_view/render/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/cut_list_view/render/mod.rs @@ -21,7 +21,7 @@ impl CutListView { props .cuts .iter() - .map(|cut| props.cut_id_memo_map.get(&cut.id())), + .map(|cut| props.cut_id_memo_map.get(&cut.id)), ) .enumerate(), item_render: |wh, (index, (cut, memos))| { @@ -67,7 +67,7 @@ impl CutListView { memo_count: usize, props: &Props, ) -> RenderingTree { - let cut_id = cut.id(); + let cut_id = cut.id; let is_focused = props.is_focused; let is_selected = props.selected_cut_id == Some(cut_id); let stroke_color = color::stroke_color(is_selected, is_focused); @@ -132,7 +132,7 @@ fn handle_move_key(event: &KeyboardEvent, cuts: &[Cut], selected_cut_id: Uuid) { let cut_index = cuts .iter() - .position(|cut| cut.id() == selected_cut_id) + .position(|cut| cut.id == selected_cut_id) .unwrap(); let next_cut_id = match direction { @@ -140,13 +140,13 @@ fn handle_move_key(event: &KeyboardEvent, cuts: &[Cut], selected_cut_id: Uuid) { if cut_index == 0 { return; } - cuts[cut_index - 1].id() + cuts[cut_index - 1].id } UpDown::Down => { if cut_index == cuts.len() - 1 { return; } - cuts[cut_index + 1].id() + cuts[cut_index + 1].id } }; diff --git a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/image_upload/mod.rs b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/image_upload/mod.rs index d8d2ae458..7f2ee412a 100644 --- a/luda-editor/client/src/pages/sequence_edit_page/loaded/components/image_upload/mod.rs +++ b/luda-editor/client/src/pages/sequence_edit_page/loaded/components/image_upload/mod.rs @@ -67,6 +67,7 @@ pub async fn create_image( Ok(image_id) } +#[allow(dead_code)] pub async fn update_image( _prev_label_list: Vec