Skip to content

Commit

Permalink
Impl scene sprite editor (#963)
Browse files Browse the repository at this point in the history
  • Loading branch information
bigfoodK authored Sep 28, 2024
1 parent caccec5 commit 71dc9ad
Show file tree
Hide file tree
Showing 19 changed files with 952 additions and 247 deletions.
12 changes: 9 additions & 3 deletions luda-editor/new-client/src/asset_manage_page.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{server_connection, simple_button, toast};
use luda_rpc::{asset::reserve_team_asset_upload, AssetKind};
use luda_rpc::{asset::reserve_team_asset_upload, AssetKind, AssetTag};
use namui::*;
use namui_prebuilt::table::*;
use network::http;
Expand Down Expand Up @@ -35,6 +35,9 @@ impl Component for AssetManagePage<'_> {
asset_name: &name,
byte_size: encoded_bytes.len() as u64,
asset_kind: &AssetKind::Sprite,
tags: &vec![AssetTag::System {
tag: luda_rpc::AssetSystemTag::SpriteCharacter,
}],
})
.await
{
Expand Down Expand Up @@ -116,11 +119,14 @@ async fn upload_asset(
headers: Vec<(String, String)>,
bytes: Vec<u8>,
) -> Result<()> {
let mut builder = http::Request::post(presigned_put_uri);
let mut builder = http::Request::put(presigned_put_uri);
for (key, value) in headers {
builder = builder.header(key, value);
}
let response = builder.body(bytes)?.send().await?;
response.ensure_status_code()?;
let status = response.ensure_status_code()?.status();
if !status.is_success() {
return Err(anyhow!("status code: {}", status));
}
Ok(())
}
102 changes: 71 additions & 31 deletions luda-editor/new-client/src/episode_editor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,86 @@
mod properties_panel;
mod scene_list;
mod scene_preview;
mod scene_sprite_editor;
mod speaker_selector;
mod text_editor;

use super::*;
use luda_rpc::{EpisodeEditAction, Scene};
use crate::rpc::asset::get_team_asset_docs;
use crate::rpc::episode_editor::join_episode_editor;
use luda_rpc::{AssetDoc, EpisodeEditAction, Scene};
use properties_panel::PropertiesPanel;
use std::{collections::HashMap, sync::Arc};

pub struct EpisodeEditor<'a> {
pub team_id: &'a String,
pub project_id: &'a String,
pub episode_id: &'a String,
}

impl Component for EpisodeEditor<'_> {
fn render(self, ctx: &RenderCtx) {
let Self {
team_id,
project_id,
episode_id,
} = self;

let wh = namui::screen::size().map(|x| x.into_px());

{
use crate::rpc::episode_editor::join_episode_editor::*;
let result = join_episode_editor(
ctx,
|episode_id| Some((RefRequest { episode_id }, ())),
episode_id,
);
let join_result = join_episode_editor::join_episode_editor(
ctx,
|episode_id| Some((join_episode_editor::RefRequest { episode_id }, ())),
episode_id,
);
let asset_result = get_team_asset_docs::get_team_asset_docs(
ctx,
|team_id| Some((get_team_asset_docs::RefRequest { team_id }, ())),
team_id,
);
let asset_docs = ctx.memo({
|| {
let Some(Ok((get_team_asset_docs::Response { asset_docs }, _))) =
asset_result.as_ref()
else {
return HashMap::new();
};
asset_docs
.iter()
.map(|asset_doc| (asset_doc.name.clone(), asset_doc.clone()))
.collect()
}
});

let Some(result) = result.as_ref() else {
let (Some(join_result), Some(asset_result)) = (join_result.as_ref(), asset_result.as_ref())
else {
ctx.add(typography::center_text(
wh,
"로딩중...",
Color::RED,
16.int_px(),
));
return;
};

match (join_result, asset_result) {
(Ok((join_episode_editor::Response { scenes, texts }, _)), Ok(_)) => {
ctx.add(LoadedEpisodeEditor {
project_id,
episode_id,
initial_scenes: scenes,
initial_texts: texts,
asset_docs,
});
}
(join_result, asset_result) => {
let errors = (join_result.as_ref().err(), asset_result.as_ref().err());
ctx.add(typography::center_text(
wh,
"로딩중...",
format!("에러: {:#?}", errors),
Color::RED,
16.int_px(),
));
return;
};

match result {
Ok((Response { scenes, texts }, _)) => {
ctx.add(LoadedEpisodeEditor {
project_id,
episode_id,
initial_scenes: scenes,
initial_texts: texts,
});
}
Err(err) => {
ctx.add(typography::center_text(
wh,
format!("에러: {:?}", err),
Color::RED,
16.int_px(),
));
}
}
}
}
Expand All @@ -67,6 +91,7 @@ struct LoadedEpisodeEditor<'a> {
episode_id: &'a String,
initial_scenes: &'a Vec<Scene>,
initial_texts: &'a HashMap<String, HashMap<String, String>>,
asset_docs: Sig<'a, HashMap<String, AssetDoc>>,
}

impl Component for LoadedEpisodeEditor<'_> {
Expand All @@ -76,6 +101,7 @@ impl Component for LoadedEpisodeEditor<'_> {
episode_id,
initial_scenes,
initial_texts,
asset_docs,
} = self;
let (scenes, set_scenes) = ctx.state(|| initial_scenes.clone());
let (texts, set_texts) = ctx.state(|| initial_texts.clone());
Expand Down Expand Up @@ -251,12 +277,18 @@ impl Component for LoadedEpisodeEditor<'_> {
});
};

let select_scene = &|scene_id: &str| {
set_selected_scene_id.set(Some(scene_id.to_string()));
};

let wh = namui::screen::size().map(|x| x.into_px());

let scene_list = table::fixed(160.px(), |wh, ctx| {
ctx.add(scene_list::SceneList {
wh,
scenes: &scenes,
select_scene,
add_new_scene: &add_new_scene,
});
});
let scene_editor = table::ratio(1, |wh, ctx| {
Expand Down Expand Up @@ -291,7 +323,15 @@ impl Component for LoadedEpisodeEditor<'_> {
])(wh, ctx);
});
});
let properties_panel = table::ratio(1, |wh, ctx| {});
let properties_panel = table::ratio(1, |wh, ctx| {
let Some(scene) = scene else { return };
ctx.add(PropertiesPanel {
wh,
scene,
edit_episode: &edit_episode,
asset_docs,
});
});

ctx.compose(|ctx| horizontal([scene_list, scene_editor, properties_panel])(wh, ctx));
}
Expand Down
110 changes: 110 additions & 0 deletions luda-editor/new-client/src/episode_editor/properties_panel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use super::scene_sprite_editor::SceneSpriteEditor;
use luda_rpc::{AssetDoc, EpisodeEditAction, Scene};
use namui::*;
use namui_prebuilt::{button, table::*};
use std::collections::HashMap;

static PROPERTIES_PANEL_TAB_ATOM: Atom<PropertiesPanelTab> = Atom::uninitialized();

pub struct PropertiesPanel<'a> {
pub wh: Wh<Px>,
pub scene: &'a Scene,
pub edit_episode: &'a dyn Fn(EpisodeEditAction),
pub asset_docs: Sig<'a, HashMap<String, AssetDoc>>,
}
impl Component for PropertiesPanel<'_> {
fn render(self, ctx: &RenderCtx) {
let Self {
wh,
scene,
asset_docs,
edit_episode,
} = self;

let (properties_panel_tab, set_properties_panel_tab) =
ctx.init_atom(&PROPERTIES_PANEL_TAB_ATOM, || PropertiesPanelTab::Standing);

let update_scene = &|scene: Scene| {
edit_episode(EpisodeEditAction::UpdateScene { scene });
};

ctx.compose(|ctx| {
vertical([
fixed(
48.px(),
horizontal([
render_tab_button(
"스탠딩",
matches!(*properties_panel_tab, PropertiesPanelTab::Standing),
|| {
set_properties_panel_tab.set(PropertiesPanelTab::Standing);
},
),
render_tab_button(
"배경",
matches!(*properties_panel_tab, PropertiesPanelTab::Background),
|| {
set_properties_panel_tab.set(PropertiesPanelTab::Background);
},
),
render_tab_button(
"오디오",
matches!(*properties_panel_tab, PropertiesPanelTab::Audio),
|| {
set_properties_panel_tab.set(PropertiesPanelTab::Audio);
},
),
]),
),
ratio(1, |wh, ctx| match properties_panel_tab.as_ref() {
PropertiesPanelTab::Standing => {
ctx.add(SceneSpriteEditor {
wh,
scene,
update_scene,
asset_docs,
});
}
PropertiesPanelTab::Background => {}
PropertiesPanelTab::Audio => {}
}),
])(wh, ctx);
});
}
}

pub enum PropertiesPanelTab {
Standing,
Background,
Audio,
}

fn render_tab_button<'a>(
text: &'a str,
selected: bool,
on_click: impl 'a + FnOnce(),
) -> TableCell<'a> {
TableCell::Some {
unit: Unit::Ratio(1.0),
render: Box::new(move |_direction, wh, ctx| {
let (text_color, fill_color) = match selected {
true => (Color::WHITE, Color::BLUE),
false => (Color::BLUE, Color::TRANSPARENT),
};

ctx.add(button::TextButton {
rect: wh.to_rect(),
text: text.to_string(),
text_color,
stroke_color: Color::BLUE,
stroke_width: 1.px(),
fill_color,
mouse_buttons: vec![MouseButton::Left],
on_mouse_up_in: move |_| {
on_click();
},
});
}),
need_clip: true,
}
}
Loading

0 comments on commit 71dc9ad

Please sign in to comment.