use concinnity_core::ecs::World;
use super::shaders_state::RowsKey;
use crate::debug::hot_reload::ReloadReports;
use crate::editor::asset_handle::AssetHandle;
use crate::editor::hook::{EditorHook, FormTarget};
use crate::editor::panels::registry::PanelKey;
use crate::editor::panels::sdf_field_list::{self, FieldDecl, SDF_VOLUME};
use crate::editor::panels::shader_list::{self, MenuItem, Row, RowKind, ShaderDecl};
use crate::editor::panels::shader_list_panel::{self, ShadersAction, ShadersView};
use crate::editor::panels::shader_source::SourceKey;
impl EditorHook {
pub(crate) fn reload_reports(&self) -> ReloadReports {
self.shaders.reports.clone()
}
pub(in crate::editor::hook) fn declared_shaders(&mut self) -> Vec<ShaderDecl> {
let dir = crate::project::assets_dir();
let state = &mut self.shaders;
shader_list::declared(&self.entries, |declared| {
state.resolved(declared, dir.as_deref())
})
}
pub(in crate::editor::hook) fn declared_fields(&mut self) -> Vec<FieldDecl> {
let dir = crate::project::assets_dir();
let state = &mut self.shaders;
sdf_field_list::declared_fields(&self.entries, |declared| {
state.resolved_field(declared, dir.as_deref())
})
}
pub(in crate::editor::hook) fn resolved_field(&mut self, declared: &str) -> String {
let dir = crate::project::assets_dir();
self.shaders.resolved_field(declared, dir.as_deref())
}
pub(in crate::editor::hook) fn shader_rows(&mut self) -> &[Row] {
let key = RowsKey {
shaders: self.declared_shaders(),
fields: self.declared_fields(),
seq: self.shaders.reports.seq(),
open: self.shaders.source.as_ref().map(|s| s.key.clone()),
};
let state = &mut self.shaders;
if state.rows_key.as_ref() != Some(&key) {
state.rows = shader_list::rows(
&key.shaders,
&key.fields,
&state.reports.snapshot(),
key.open.as_ref(),
);
state.rows_key = Some(key);
}
&state.rows
}
pub(in crate::editor::hook) fn make_shaders_view<'a>(
&self,
rows: &'a [Row],
mouse: [f32; 2],
) -> ShadersView<'a> {
let menu = self.shaders.menu.as_ref();
ShadersView {
rows,
scroll: self.shaders.scroll,
mouse,
menu: menu.and_then(|kind| rows.iter().position(|r| &r.kind == kind)),
}
}
pub(in crate::editor::hook) fn scroll_shaders(&mut self, delta: f32) {
let window = shader_list_panel::rows_for_height(self.effective_size(PanelKey::Shaders)[1]);
let max = self.shaders.rows.len().saturating_sub(window);
self.shaders.scroll = crate::editor::hook::scroll_step(self.shaders.scroll, delta, max);
self.shaders.menu = None;
}
pub(in crate::editor::hook) fn apply_shaders_action(
&mut self,
action: ShadersAction,
rows: &[Row],
world: &mut World,
) {
let i = match action {
ShadersAction::Row(i) => i,
ShadersAction::OpenMenu(i) => {
self.shaders.menu = rows.get(i).map(|r| r.kind.clone());
return;
}
ShadersAction::Menu(item) => {
if let Some(kind) = self.shaders.menu.take() {
self.apply_menu_item(item, kind, world);
}
return;
}
ShadersAction::CloseMenu => {
self.shaders.menu = None;
return;
}
ShadersAction::Consume => return,
};
match rows.get(i).map(|r| &r.kind) {
Some(RowKind::File(key)) => self.open_shader_file(key.clone()),
Some(&RowKind::Materials(shader)) => self.select_shader_materials(shader, world),
Some(&RowKind::AddVertex(shader)) => self.add_vertex_file(shader),
Some(RowKind::New) => self.open_new_form(world),
Some(&RowKind::Header(shader)) => self.open_shader_form(Some(shader), world),
Some(&RowKind::Field(field)) => self.open_field(field),
Some(RowKind::FieldVolume(name)) => self.open_volume_form(&name.clone(), world),
Some(RowKind::Note | RowKind::Section) | None => {}
}
}
fn apply_menu_item(&mut self, item: MenuItem, kind: RowKind, world: &mut World) {
match (item, kind) {
(MenuItem::Edit, RowKind::Header(i)) => self.open_shader_form(Some(i), world),
(MenuItem::Duplicate, RowKind::Header(i)) => self.duplicate_shader(i),
(MenuItem::Delete, RowKind::Header(i)) => self.confirm_delete_shader(i),
(MenuItem::RemoveVertex, RowKind::File(SourceKey::Shader { name, .. })) => {
self.remove_vertex_file(&name)
}
(MenuItem::Open, RowKind::Field(i)) => self.open_field(i),
(MenuItem::SelectVolumes, RowKind::Field(i)) => self.select_field_volumes(i, world),
(MenuItem::Delete, RowKind::Field(i)) => self.confirm_delete_field(i),
_ => {}
}
}
fn open_new_form(&mut self, world: &mut World) {
let shaders = shader_list::shader_count(&self.entries);
if shader_list::can_add_shader(shaders) {
self.open_shader_form(None, world);
return;
}
self.open_form(world, SDF_VOLUME.to_string(), FormTarget::New);
self.form.host = PanelKey::Shaders;
self.place_form_at_view(world);
}
fn open_field(&mut self, i: usize) {
if let Some(field) = self.declared_fields().into_iter().nth(i) {
self.open_shader_file(SourceKey::Field { path: field.path });
}
}
fn open_volume_form(&mut self, name: &str, world: &mut World) {
let found = concinnity_cook::authoring::world::find_entry(&self.entries, name);
let Some(key) = found.and_then(|idx| self.entries.key_at(idx)) else {
return;
};
self.open_form(world, SDF_VOLUME.to_string(), FormTarget::Entry(key));
self.form.host = PanelKey::Shaders;
}
fn select_field_volumes(&mut self, i: usize, world: &mut World) {
let Some(field) = self.declared_fields().into_iter().nth(i) else {
return;
};
let positions = field
.names()
.into_iter()
.filter_map(|n| concinnity_cook::authoring::world::find_entry(&self.entries, n))
.collect();
let handles = self.entry_handles(positions);
if handles.is_empty() {
return;
}
self.selection.set(handles);
self.follow_active(world);
}
fn open_shader_form(&mut self, i: Option<usize>, world: &mut World) {
let target = match i {
None => FormTarget::New,
Some(i) => {
let Some(shader) = self.declared_shaders().into_iter().nth(i) else {
return;
};
let found =
concinnity_cook::authoring::world::find_entry(&self.entries, &shader.name);
let Some(key) = found.and_then(|idx| self.entries.key_at(idx)) else {
return;
};
FormTarget::Entry(key)
}
};
self.open_form(world, "Shader".to_string(), target);
self.form.host = PanelKey::Shaders;
}
fn duplicate_shader(&mut self, i: usize) {
let Some(shader) = self.declared_shaders().into_iter().nth(i) else {
return;
};
let found = concinnity_cook::authoring::world::find_entry(&self.entries, &shader.name);
if let Some(key) = found.and_then(|idx| self.entries.key_at(idx)) {
self.duplicate(vec![AssetHandle::Entry(key)]);
}
}
fn select_shader_materials(&mut self, i: usize, world: &mut World) {
let Some(shader) = self.declared_shaders().into_iter().nth(i) else {
return;
};
let positions = shader.materials.iter().map(|&(m, _)| m).collect();
let handles = self.entry_handles(positions);
if handles.is_empty() {
return;
}
self.selection.set(handles);
self.follow_active(world);
}
}