diff --git a/main/src/domain/main_processor.rs b/main/src/domain/main_processor.rs index a61999387..8ca717fb2 100644 --- a/main/src/domain/main_processor.rs +++ b/main/src/domain/main_processor.rs @@ -41,6 +41,7 @@ use crate::domain::ui_util::{ use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet, NonCryptoIndexSet}; use base::{hash_util, NamedChannelSender, SenderToNormalThread, SenderToRealTimeThread}; use helgoboss_midi::{ControlChange14BitMessage, ParameterNumberMessage, RawShortMessage}; +use playtime_api::runtime::ControlUnitId; use reaper_high::{ChangeEvent, Reaper}; use reaper_medium::ReaperNormalizedFxParamValue; use rosc::{OscMessage, OscPacket, OscType}; @@ -4137,6 +4138,12 @@ impl From for u32 { } } +impl From for UnitId { + fn from(value: ControlUnitId) -> Self { + UnitId(value.get()) + } +} + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Display)] pub enum MatchOutcome { /// Message didn't match at all, was not even consumed. diff --git a/main/src/domain/unit.rs b/main/src/domain/unit.rs index 7880b7247..201ce04cd 100644 --- a/main/src/domain/unit.rs +++ b/main/src/domain/unit.rs @@ -1,4 +1,6 @@ use std::cell::RefCell; +use std::cmp; +use std::cmp::min; use std::rc::{Rc, Weak}; use std::sync::Arc; @@ -14,6 +16,7 @@ use crate::domain::{ }; use base::hash_util::{NonCryptoHashMap, NonCryptoHashSet}; use base::{serde_json_util, NamedChannelSender, SenderToNormalThread}; +use playtime_api::persistence::SlotAddress; pub type SharedUnit = Rc>; pub type WeakUnit = Weak>; @@ -260,6 +263,26 @@ impl Unit { self.control_unit_top_left_corner } + pub fn invalidate_control_unit_scroll_pos(&mut self, column_count: usize, row_count: usize) { + let actual_column = self.control_unit_top_left_corner.column_index; + let fixed_column = fix_scroll_pos( + actual_column, + self.control_unit_column_count() as usize, + column_count, + ); + let actual_row = self.control_unit_top_left_corner.row_index; + let fixed_row = fix_scroll_pos( + actual_row, + self.control_unit_row_count() as usize, + row_count, + ); + if actual_column == fixed_column && actual_row == fixed_row { + return; + } + // Control unit scroll position is invalid + self.set_control_unit_top_left_corner(SlotAddress::new(fixed_column, fixed_row)); + } + pub fn set_control_unit_top_left_corner( &mut self, value: playtime_api::persistence::SlotAddress, @@ -620,3 +643,12 @@ impl UnitEvent { matches!(self, UnitEvent::MappingWhichLearnsTargetChanged { .. }) } } + +fn fix_scroll_pos(start_pos: usize, span: usize, count: usize) -> usize { + if count == 0 || span == 0 { + return 0; + } + let exclusive_end_pos = start_pos + span; + let fixed_exclusive_end_pos = min(count, exclusive_end_pos); + fixed_exclusive_end_pos.saturating_sub(span) +} diff --git a/main/src/infrastructure/plugin/clip_matrix_handler.rs b/main/src/infrastructure/plugin/clip_matrix_handler.rs index abe83930d..f97fa393f 100644 --- a/main/src/infrastructure/plugin/clip_matrix_handler.rs +++ b/main/src/infrastructure/plugin/clip_matrix_handler.rs @@ -5,13 +5,16 @@ use crate::domain::{ RealTimeInstanceTask, ReaperTarget, }; use crate::infrastructure::plugin::WeakInstanceShell; -use base::{Global, NamedChannelSender}; +use anyhow::Context; +use base::{spawn_in_main_thread, Global, NamedChannelSender}; use helgobox_api::persistence::{ PlaytimeColumnAction, PlaytimeMatrixAction, PlaytimeRowAction, PlaytimeSlotTransportAction, }; +use playtime_api::persistence::SlotAddress; use playtime_api::runtime::{ ControlUnit, ControlUnitId, SimpleMappingContainer, SimpleMappingTarget, }; +use playtime_clip_engine::base::ClipMatrixEvent; use reaper_high::Reaper; #[derive(Debug)] @@ -46,7 +49,28 @@ impl MatrixHandler { } } - fn do_async_with_session(&self, f: impl FnOnce(SharedUnitModel) + 'static) { + fn invalidate_control_units(&self) { + let weak_instance_shell = self.weak_instance_shell.clone(); + spawn_in_main_thread(async move { + let instance_shell = weak_instance_shell + .upgrade() + .context("instance shell gone")?; + let instance = instance_shell.instance().borrow(); + let matrix = instance.clip_matrix().context("no matrix")?; + let column_count = matrix.column_count(); + let row_count = matrix.row_count(); + for unit_model in instance_shell.additional_unit_models() { + unit_model + .borrow() + .unit() + .borrow_mut() + .invalidate_control_unit_scroll_pos(column_count, row_count); + } + Ok(()) + }); + } + + fn do_async_with_main_unit_model(&self, f: impl FnOnce(SharedUnitModel) + 'static) { let session = self.main_unit_model.clone(); Global::task_support() .do_later_in_main_thread_from_main_thread_asap(move || { @@ -79,6 +103,11 @@ impl playtime_clip_engine::base::ClipMatrixHandler for MatrixHandler { } fn emit_event(&self, event: playtime_clip_engine::base::ClipMatrixEvent) { + // Check if we should do something special with that event + if matches!(event, ClipMatrixEvent::EverythingChanged) { + self.invalidate_control_units(); + } + // Send event to receiver let event = QualifiedClipMatrixEvent { instance_id: self.instance_id, event, @@ -134,7 +163,7 @@ impl playtime_clip_engine::base::ClipMatrixHandler for MatrixHandler { } fn toggle_learn_source_by_target(&self, target: SimpleMappingTarget) { - self.do_async_with_session(move |shared_session| { + self.do_async_with_main_unit_model(move |shared_session| { let mut session = shared_session.borrow_mut(); let mapping_desc = SimpleMappingDesc::from_simple_target(target); session.toggle_learn_source_for_target( @@ -147,7 +176,7 @@ impl playtime_clip_engine::base::ClipMatrixHandler for MatrixHandler { } fn remove_mapping_by_target(&self, target: SimpleMappingTarget) { - self.do_async_with_session(move |shared_session| { + self.do_async_with_main_unit_model(move |shared_session| { let mut session = shared_session.borrow_mut(); let mapping_desc = SimpleMappingDesc::from_simple_target(target); session.remove_mapping_by_target(CompartmentKind::Main, &mapping_desc.reaper_target); diff --git a/playtime-clip-engine b/playtime-clip-engine index 7bc004654..da78f2642 160000 --- a/playtime-clip-engine +++ b/playtime-clip-engine @@ -1 +1 @@ -Subproject commit 7bc004654ebe4eeed06c5bd8024608e3074b4682 +Subproject commit da78f2642a6b352280f26242470b63211020f01b