From 6aff4e55a60351f0bcbdfae3d752d81713e2bd55 Mon Sep 17 00:00:00 2001 From: i do not exist Date: Wed, 23 Sep 2026 06:22:18 +0300 Subject: [PATCH] open editor --- src/midi_piano.rs | 6 +- src/proj_track.rs | 4 - src/tek.rs | 48 ++--- src/tek_ctrl.rs | 16 +- src/tek_view.rs | 439 +++++++++++++++++++--------------------------- tengri | 2 +- 6 files changed, 212 insertions(+), 303 deletions(-) diff --git a/src/midi_piano.rs b/src/midi_piano.rs index f9fc695e..79a26af8 100644 --- a/src/midi_piano.rs +++ b/src/midi_piano.rs @@ -5,7 +5,7 @@ use crate::*; /// ``` /// let piano = tek::PianoHorizontal::default(); /// ``` -#[derive(Clone)] pub struct PianoHorizontal { +#[derive(Clone, Default)] pub struct PianoHorizontal { pub clip: ClipSlot, /// Buffer where the whole clip is rerendered on change pub buffer: Arc>, @@ -23,8 +23,8 @@ use crate::*; impl_has!(Sizer: |self: PianoHorizontal| self.size); -impl Default for PianoHorizontal { - fn default () -> Self { +impl PianoHorizontal { + pub fn new () -> Self { let size = Sizer::default(); let mut range = MidiSelection::from((12, true)); range.time_axis = size.0.clone(); diff --git a/src/proj_track.rs b/src/proj_track.rs index ff637da3..d66d8e52 100644 --- a/src/proj_track.rs +++ b/src/proj_track.rs @@ -91,10 +91,6 @@ pub trait HasTrack: AsRefOpt + AsMutOpt { } } -pub(crate) fn track_width (_index: usize, track: &Track) -> u16 { - track.width as u16 -} - def_sizes_iter!(TracksSizes => Track); impl_has!(Vec: |self: Project| self.tracks); impl_as_ref!(Vec: |self: App| self.project.as_ref()); diff --git a/src/tek.rs b/src/tek.rs index 6dba8e47..cc0c0531 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -172,9 +172,8 @@ pub struct App { pub project: Project, /// Error, if any pub error: Arc>>>, - #[cfg(feature = "prof2")] /// Tracing guard - pub guard: Option>>, + #[cfg(feature = "prof2")] pub guard: Option>>, } impl App { @@ -359,18 +358,17 @@ fn get_vec_arc_str <'a> (_: &App, _: impl Language) -> Perhaps>> { todo!() } -impl_has!(Sizer: |self: App|self.size); -impl_has!(Dialog: |self: App|self.dialog); -impl_has!(Jack<'static>: |self: App|self.jack); - -impl_has!(Clock: |self: App|self.project.clock); +impl_has!(Clock: |self: App|self.project.clock); +impl_has!(Dialog: |self: App|self.dialog); +impl_has!(Jack<'static>: |self: App|self.jack); impl_has!(Pool: |self: App|self.project.clips); -impl_has!(Pool: |self: App|self.project.samples); -impl_has!(Selection: |self: App|self.project.selection); -impl_as_ref!(Option: |self: App|todo!()); -impl_as_mut!(Option: |self: App|todo!()); -impl_as_ref!(Option: |self: App|self.project.as_ref()); -impl_as_mut!(Option: |self: App|self.project.as_mut()); +impl_has!(Pool: |self: App|self.project.samples); +impl_has!(Selection: |self: App|self.project.selection); +impl_has!(Sizer: |self: App|self.size); +impl_as_ref!(Option: |self: App|todo!()); +impl_as_mut!(Option: |self: App|todo!()); +impl_as_ref!(Option: |self: App|self.project.as_ref()); +impl_as_mut!(Option: |self: App|self.project.as_mut()); impl HasClip for App { fn clip_slot (&self) -> Option> { self.project.clip_slot() @@ -548,26 +546,16 @@ impl Dialog { Self::Menu(1, Arc::new([ MenuItem::new(" Resume session ", [ - AppCmd::Project { - command: ProjectCmd::LoadLast - } + AppCmd::Project { command: ProjectCmd::LoadLast } ]), MenuItem::new(" Create session ", [ - AppCmd::Dialog { - dialog: Dialog::None - }, - AppCmd::Mode { - command: ModeCmd::Reset { - modes: vec!["Arranger".into()] - } - }, - AppCmd::Tracks { - command: TracksCmd::Add - }, - AppCmd::Scenes { - command: ScenesCmd::Add - } + AppCmd::Dialog { dialog: Dialog::None }, + AppCmd::Mode { command: ModeCmd::Reset { modes: vec!["Arranger".into()] } }, + AppCmd::Tracks { command: TracksCmd::Add }, + AppCmd::Scenes { command: ScenesCmd::Add }, + AppCmd::MidiClip { command: MidiClipCmd::Upsert }, + AppCmd::MidiEdit { command: MidiEditCmd::Toggle }, ]), MenuItem::new(" Load session ", [ diff --git a/src/tek_ctrl.rs b/src/tek_ctrl.rs index b0eece53..6618d7cf 100644 --- a/src/tek_ctrl.rs +++ b/src/tek_ctrl.rs @@ -400,7 +400,7 @@ impl + Namespace + Namespace @@ -409,7 +409,7 @@ impl > MidiEditControl for T {} -#[tek_proc::commands(MidiEditCmd)] pub trait MidiEditControl: HasEditor +#[tek_proc::commands(MidiEditCmd)] pub trait MidiEditControl: HasClip + HasEditor + Namespace + Namespace + Namespace @@ -420,8 +420,16 @@ impl Perhaps { if self.is_editing() { *self.editor_mut() = None; - } else { - todo!() + } else if let Some(clip) = self.clip() { + let editor = MidiEditor { + mode: Default::default(), + size: Sizer( + Arc::new(0usize.into()), + Arc::new(0usize.into()), + ), + }; + *editor.mode.clip.0.write() = clip.clone(); + *self.editor_mut() = Some(editor); } Ok(Some(MidiEditCmd::Toggle)) } diff --git a/src/tek_view.rs b/src/tek_view.rs index 4b3c846c..1e4c915d 100644 --- a/src/tek_view.rs +++ b/src/tek_view.rs @@ -385,7 +385,7 @@ impl View { Some("MetersIn") => draw!(|___|bg(Rgb(30, 30, 30), "Input Meters".align_s().full_h())), Some("MetersOut") => draw!(|___|bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h())), Some("ModeList") => draw!(|app|app.modes.read().get(0)), // TODO all - Some("ScenesClips") => draw!(|app|app.view_scenes_clips()), + Some("ScenesClips") => draw!(|app|view_scenes_clips(app)), Some("ScenesNames") => draw!(|app|app.view_scenes_names()), Some("Sessions") => draw!(|___|view_sessions()), Some("Templates") => draw!(|app|view_templates(app)), @@ -685,146 +685,69 @@ pub trait TracksView: }) } - /// Draw name of each track - fn view_track_names (&self, theme: ItemTheme) -> impl Draw { - view_track_row_section(theme, - self.view_track_names_count(), - self.view_track_names_add(), - self.view_track_names_cells() - ) - } - - fn view_track_names_add (&self) -> impl Draw { - south(self.view_track_add(), self.view_scene_add()) - } - - fn view_track_names_count (&self) -> impl Draw { - south(self.view_track_count(), self.view_scene_count()) - } - - fn view_track_names_cells <'a> (&'a self) -> impl Draw { - iter_east(||self.tracks_with_sizes().map(|(index, track, _x1, _x2)|{ - let b = if self.selection().track() == Some(index) { - track.color.light.term - } else { - track.color.base.term - }; - bg(b, south( - east!( - "·t", - index, - " ", - fg(Rgb(255, 255, 255), bold(true, &track.name)) - ) - .align_nw() - .full_w(), - "" - )) - .exact_w(track_width(index, track)) - .exact_h(2) - })) - } - - /// Draw outputs per track - fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw { - view_track_row_section(theme, - self.view_track_output_count(), - self.view_track_output_add(), - self.view_track_output_cells() - ) - } - - fn view_track_output_cells <'a> (&'a self) -> impl Draw { - iter_east(move||self.tracks_with_sizes().map(move|(index, track, _x1, _x2)|{ - let f = Rgb(255, 255, 255); - let b = track.color.dark.term; - iter_south(move||track.sequencer.midi_outs.iter().map(move|port: &MidiOutput|{ - fg(f, bg(b, east!("·o", index, " ", port.port_name()).full_w().align_w()).exact_h(1)) - })) - .full_h() - .align_nw() - .exact_w(track_width(index, track)) - })) - } - - fn view_track_device_count (&self) -> impl Draw { - button_3("d", "evice", self.track().map(|t|t.devices.len()).unwrap_or(0), false).min_h(2) - } - - fn view_track_devices (&self, theme: ItemTheme) -> impl Draw { - let height = self.tracks_devices_height(); - let btn1 = self.view_track_device_count(); - let btn2 = self.view_track_device_add(); - view_track_row_section(theme, btn1, btn2, self.view_track_device_cells(height)) - } - - fn view_track_device_cells (&self, height: u16) -> impl Draw { + fn view_track_column <'t, S, D, F> (&'t self, f: F) -> impl Draw where + S: Screen, + D: Draw + 't, + F: Fn(usize, &'t Track, &Selection, Option<&MidiEditor>)->D + Copy + 't + { + let select = self.selection(); + let editor = self.editor().as_ref(); iter_east(move||self.tracks_with_sizes() - .enumerate() - .map(move|(index, (_, track, _x1, _x2))|bg( - track.color.dark.term, - iter_south(move||(0..height).map(|_|fg_bg( - ItemTheme::G[32].lightest.term, - ItemTheme::G[32].dark.term, - " · --".align_nw() - ).exact_wh(track.width as u16, 2))) - .align_nw() + .map(move|(i, t, _x1, _x2)|f(i, t, select, editor))) + } + + fn view_track_names_cells (&self) -> impl Draw { + self.view_track_column(|i, t, select, editor|{ + let b = if select.track() == Some(i) { t.color.light } else { t.color.base }.term; + bg(b, south(east!( + "·t", i, " ", fg(Rgb(255, 255, 255), bold(true, &t.name)) + ).align_nw().full_w(), "")).exact_wh( + view_track_w(select, i, t, editor), + 2 ) - .exact_wh( - Some(track_width(index, track)), - Some(height + 1), - ))) + }) } - fn tracks_devices_height (&self) -> u16 { - let mut h = 2; - for track in self.tracks().iter() { - h = h.max(track.devices.len() * 2); - } - h as u16 + fn view_track_output_cells (&self) -> impl Draw { + self.view_track_column(|i, t, select, editor|{ + let f = Rgb(255, 255, 255); + let b = t.color.dark.term; + iter_south(move||t.sequencer.midi_outs.iter().map(move|port: &MidiOutput|{ + fg(f, bg(b, east!("·o", i, " ", port.port_name()).full_w().align_w()).exact_h(1)) + })).full_h().align_nw().exact_w( + view_track_w(select, i, t, editor) + ) + }) } - fn view_inputs (&self) -> impl Draw + '_ { - let title_1 = button_3("i", "nput ", self.midi_ins().len(), false).align_w().exact_wh(20, 1); - let title_2 = self.view_track_input_add().exact_wh(4, 1); - east(title_1, west(title_2, self.view_input_cells())) - } - - fn tracks_inputs_height (&self) -> u16 { - let mut height = 0u16; - for track in self.tracks().iter() { - height = height.max(track.sequencer.midi_ins.len() as u16); - } - height - } - - /// Draw inputs per track - fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw { - view_track_row_section(theme, - self.view_track_input_count(), - self.view_track_input_add(), - self.view_track_input_cells(self.tracks_inputs_height()) - ) + fn view_track_device_cells (&self, height: u16) -> impl Draw + '_ { + self.view_track_column(move|i, t, select, editor|bg( + t.color.dark.term, + iter_south(move||(0..height).map(|_|fg_bg( + ItemTheme::G[32].lightest.term, + ItemTheme::G[32].dark.term, + " · --".align_nw() + ).exact_wh(t.width as u16, 2))).align_nw() + ).exact_wh( + view_track_w(select, i, t, editor), + height + 1 + )) } fn view_track_input_cells (&self, height: u16) -> impl Draw { - iter_east(move||self - .tracks_with_sizes() - .map(move|(index, track, _x1, _x2)|{ - south( - bg(track.color.base.term, - east!( - either(track.sequencer.monitoring, fg(Green, "●mon "), "·mon "), - either(track.sequencer.recording, fg(Red, "●rec "), "·rec "), - either(track.sequencer.overdub, fg(Yellow, "●dub "), "·dub "), - ).align_w().full_w()), - iter_south(move||track.sequencer.midi_ins.iter().map(move|port|fg_bg( - Rgb(255, 255, 255), - track.color.dark.term, - east!("·i", index, " ", port.port_name()).align_w().full_w())))) - .align_nw() - .exact_wh(track_width(index, track), height + 1) - })).align_w() + self.view_track_column(move|i, t, select, editor|south( + bg(t.color.base.term, + east!( + either(t.sequencer.monitoring, fg(Green, "●mon "), "·mon "), + either(t.sequencer.recording, fg(Red, "●rec "), "·rec "), + either(t.sequencer.overdub, fg(Yellow, "●dub "), "·dub "), + ).align_w().full_w()), + iter_south(move||t.sequencer.midi_ins.iter().map(move|port|fg_bg( + Rgb(255, 255, 255), t.color.dark.term, east!( + "·i", i, " ", port.port_name() + ).align_w().full_w() + )))).align_nw().exact_wh(view_track_w(select, i, t, editor), height + 1) + ).align_w() } fn view_input_cells (&self) -> impl Draw { @@ -861,14 +784,6 @@ pub trait TracksView: }) } - fn outputs_height (&self) -> u16 { - let mut h = 1; - for output in self.midi_outs().iter() { - h += 1 + output.connections.len(); - } - h as u16 - } - fn view_outputs (&self, theme: ItemTheme) -> impl Draw { let height = self.outputs_height(); let list = south( @@ -921,54 +836,68 @@ pub trait TracksView: }).align_w().full_w())).exact_h(height) } - fn view_track_count (&self) -> impl Draw { - button_3("t", "rack ", east( - self.selection().track().map(|track|east(track, "/")), - self.tracks().len() - ), false) + fn tracks_devices_height (&self) -> u16 { self.tracks().iter().fold(2, |h, t|h.max(t.devices.len() * 2)) as u16 } + fn tracks_inputs_height (&self) -> u16 { self.tracks().iter().fold(0, |h, t|h.max(t.sequencer.midi_ins.len())) as u16 } + fn outputs_height (&self) -> u16 { self.midi_outs().iter().fold(1, |h, o|h + o.connections.len()) as u16 } + + fn view_track_add (&self) -> impl Draw { button_2("T", "+", false) } + fn view_scene_add (&self) -> impl Draw { button_2("S", "+", false) } + fn view_track_output_add (&self) -> impl Draw { button_2("O", "+", false) } + fn view_track_input_add (&self) -> impl Draw { button_2("I", "+", false) } + fn view_track_device_add (&self) -> impl Draw { button_2("D", "+", false) } + fn view_track_names_add (&self) -> impl Draw { south(self.view_track_add(), self.view_scene_add()) } + + fn view_track_count (&self) -> impl Draw { view_n_of_m("t", "rack", self.selection().track(), self.tracks().len()); } + fn view_scene_count (&self) -> impl Draw { view_n_of_m("s", "cene", self.selection().scene(), self.scenes().len()); } + fn view_track_input_count (&self) -> impl Draw { button_2("i", "nput", false) } + fn view_track_output_count (&self) -> impl Draw { button_3("o", "utput", 0, false) } + fn view_track_names_count (&self) -> impl Draw { south(self.view_track_count(), self.view_scene_count()) } + fn view_track_device_count (&self) -> impl Draw { button_3("d", "evice", self.track().map(|t|t.devices.len()).unwrap_or(0), false).min_h(2) } + + /// Draw outputs per track + fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw { + view_track_row_section(theme, + self.view_track_output_count(), + self.view_track_output_add(), + self.view_track_output_cells() + ) } - fn view_scene_count (&self) -> impl Draw { - button_3("s", "cene ", east( - self.selection().scene().map(|scene|east(scene, "/")), - self.scenes().len() - ), false) + /// Draw name of each track + fn view_track_names (&self, theme: ItemTheme) -> impl Draw { + view_track_row_section(theme, + self.view_track_names_count(), + self.view_track_names_add(), + self.view_track_names_cells() + ) } - fn view_track_add (&self) -> impl Draw { - button_2("T", "+", false) + /// Draw inputs per track + fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw { + view_track_row_section(theme, + self.view_track_input_count(), + self.view_track_input_add(), + self.view_track_input_cells(self.tracks_inputs_height()) + ) } - fn view_scene_add (&self) -> impl Draw { - button_2("S", "+", false) + fn view_track_devices (&self, theme: ItemTheme) -> impl Draw { + let height = self.tracks_devices_height(); + let btn1 = self.view_track_device_count(); + let btn2 = self.view_track_device_add(); + view_track_row_section(theme, btn1, btn2, self.view_track_device_cells(height)) } - fn view_track_input_count (&self) -> impl Draw { - button_2("i", "nput", false) + fn view_inputs (&self) -> impl Draw + '_ { + let title_1 = button_3("i", "nput ", self.midi_ins().len(), false).align_w().exact_wh(20, 1); + let title_2 = self.view_track_input_add().exact_wh(4, 1); + east(title_1, west(title_2, self.view_input_cells())) } - fn view_track_output_count (&self) -> impl Draw { - button_3("o", "utput", 0, false) - //draw(|to: &mut Tui|{ - //for port in self.midi_outs().iter() { - //let _ = port.port_name().align_w().full_w().draw(to)?; - //} - //Ok(Some(XYWH(0, 0, 0, 0))) - //})), - } - - fn view_track_output_add (&self) -> impl Draw { - button_2("O", "+", false) - } - - fn view_track_input_add (&self) -> impl Draw { - button_2("I", "+", false) - } - - fn view_track_device_add (&self) -> impl Draw { - button_2("D", "+", false) - } +} +fn view_n_of_m (key: &str, name: &str, index: Option, count: usize) -> impl Draw { + button_3(key, name, east(index, count), false) } pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + Send + Sync { @@ -1028,41 +957,78 @@ impl ScenesView for Project { } } -impl ClipsView for T {} - -pub trait ClipsView: TracksView + ScenesView { - - /// Draw clips per scene - fn view_scenes_clips (&self) -> impl Draw { - let select = self.selection(); - let editor = self.editor(); - let editing = self.is_editing(); - self.with_clips_size(true, iter_east(move||self.tracks_with_sizes() - .map(move|(track_index, track, _, _)|iter_south(move||self.scenes_with_sizes() - .map(move|(scene_index, scene, _, _)|{ - let (name, theme): (Arc, ItemTheme) = view_scene_name_theme(scene, track_index); - let f = theme.lightest.term; - let (b, o) = view_scene_bg(theme, select, track_index, scene_index); - let is_selected = view_scene_sel(select, track_index, scene_index, editing); +/// Draw clips per scene +fn view_scenes_clips (state: &S) -> impl Draw { + let select = state.selection(); + let editor = state.editor(); + let editing = state.is_editing(); + let sized = move|it|state.with_clips_size(true, it).align_c(); + let tracks = move|it|iter_east(move||state.tracks_with_sizes().map(it)); + let scenes = move|it|iter_south(move||state.scenes_with_sizes().map(it)); + sized(tracks(move|(track_index, track, _, _)|{ + let w = view_track_w(select, track_index, track, editor.as_ref()) as u16; + scenes(move|(scene_index, scene, _, _)|{ + let (name, theme): (Arc, ItemTheme) = view_scene_name_theme(scene, track_index); + let f = theme.lightest.term; + let (b, o) = view_scene_bg(theme, select, track_index, scene_index); + let is_sel = view_scene_sel(select, track_index, scene_index, editing); + below( + Outer(true, Style::default().fg(o)).full_wh(), + below( below( - Outer(true, Style::default().fg(o)).full_wh(), - below( - below( - fg_bg(o, b, "".full_wh()), - fg_bg(f, b, bold(true, name)).align_nw().full_wh(), - ), - when(is_selected, editor).full_wh() - ).full_wh() - ).exact_wh( - view_scene_w(track, select, track_index, editor.as_ref()), - view_scene_y(select, scene_index, editor.as_ref()), - ) - }) - ) - .exact_w(track.width as u16)) - )).align_c() - } + fg_bg(o, b, "".full_wh()), + fg_bg(f, b, bold(true, name)).align_nw().full_wh(), + ), + when(is_sel, editor).full_wh() + ).full_wh() + ).exact_wh(w, view_scene_y(select, scene_index, editor.as_ref()),) + }).exact_w(w) + })) +} +pub fn view_scene_bg ( + theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize +) -> (Color, Color) { + let mut outline = theme.base.term; + (if select.track() == Some(track_index) && select.scene() == Some(scene_index) { + outline = theme.lighter.term; + theme.light.term + } else if select.track() == Some(track_index) || select.scene() == Some(scene_index) { + outline = theme.darkest.term; + theme.base.term + } else { + theme.dark.term + }, outline) +} + +pub fn view_scene_sel ( + select: &Selection, track_index: usize, scene_index: usize, editing: bool +) -> bool { + editing && select.track() == Some(track_index) && select.scene() == Some(scene_index) +} + +pub fn view_track_w ( + select: &Selection, track_index: usize, track: &Track, editor: Option<&MidiEditor> +) -> u16 { + if select.track() == Some(track_index) && let Some(editor) = editor { + (editor.size.w() as usize).max(24).max(track.width) as u16 + } else { + track.width as u16 + } +} + +pub(crate) fn track_width (_index: usize, track: &Track) -> u16 { + track.width as u16 +} + +pub fn view_scene_y ( + select: &Selection, scene_index: usize, editor: Option<&MidiEditor> +) -> u16 { + if select.scene() == Some(scene_index) && let Some(editor) = editor { + editor.size.h().max(12) + } else { + Scene::DEFAULT_HEIGHT as u16 + } } pub fn draw_sample ( @@ -1277,45 +1243,6 @@ pub fn view_scene_name_theme (scene: &Scene, track_index: usize) -> (Arc, I }) } -pub fn view_scene_bg ( - theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize -) -> (Color, Color) { - let mut outline = theme.base.term; - (if select.track() == Some(track_index) && select.scene() == Some(scene_index) { - outline = theme.lighter.term; - theme.light.term - } else if select.track() == Some(track_index) || select.scene() == Some(scene_index) { - outline = theme.darkest.term; - theme.base.term - } else { - theme.dark.term - }, outline) -} - -pub fn view_scene_w ( - track: &Track, select: &Selection, track_index: usize, editor: Option<&MidiEditor> -) -> u16 { - if select.track() == Some(track_index) && let Some(editor) = editor { - (editor.size.w() as usize).max(24).max(track.width) as u16 - } else { - track.width as u16 - } -} - -pub fn view_scene_y ( - select: &Selection, scene_index: usize, editor: Option<&MidiEditor> -) -> u16 { - if select.scene() == Some(scene_index) && let Some(editor) = editor { - editor.size.h().max(12) - } else { - Scene::DEFAULT_HEIGHT as u16 - } -} - -pub fn view_scene_sel (select: &Selection, track_index: usize, scene_index: usize, editing: bool) -> bool { - editing && select.track() == Some(track_index) && select.scene() == Some(scene_index) -} - pub fn view_templates <'a> (state: &'a App) -> impl Draw + use<'a> { let height = (state.config.modes.len() * 2) as u16; draw(move |to: &mut Tui|{ @@ -1635,23 +1562,13 @@ impl PianoHorizontal { }; } -pub struct Memo { - value: T, - view: U, -} - -impl Memo { - pub fn new (value: T, view: U) -> Self { - Self { value, view } - } -} - +pub struct Memo { value: T, view: U, } +impl Memo { pub fn new (value: T, view: U) -> Self { Self { value, view } } } impl Debug for Memo { fn fmt (&self, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { todo!() } } - impl Memo { pub fn update (&mut self, newval: T, render: impl Fn(&mut U, &T, &T)->R) -> Option { if newval != self.value { diff --git a/tengri b/tengri index 7237121a..21d6674f 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit 7237121a508556ee98714a6af387e325a371e368 +Subproject commit 21d6674ffb81b78eb7efe281c43f0a085cbd94ad