Skip to main content

truce_loader/
traits.rs

1//! The PluginLogic trait — the single trait plugin developers implement.
2
3use truce_core::buffer::AudioBuffer;
4use truce_core::events::EventList;
5use truce_core::process::{ProcessContext, ProcessStatus};
6use truce_gui::interaction::WidgetRegion;
7use truce_gui::render::RenderBackend;
8use truce_gui::widgets::WidgetType;
9
10/// The trait for hot-reloadable plugin logic.
11///
12/// Implement this in your logic dylib. The shell loads it via
13/// `Box<dyn PluginLogic>` and delegates audio processing and GUI
14/// rendering to it.
15///
16/// All methods use safe Rust types. No `unsafe`, no `#[repr(C)]`,
17/// no raw pointers.
18pub trait PluginLogic: Send + 'static {
19    /// Create a new instance with default state.
20    fn new() -> Self where Self: Sized;
21
22    /// Reset for a new sample rate / block size.
23    fn reset(&mut self, sample_rate: f64, max_block_size: usize);
24
25    /// Return a mutable reference to the plugin's Params, if it owns one.
26    ///
27    /// If this returns Some, the shell automatically syncs parameter
28    /// values from host automation events and advances smoothers before
29    /// each process() call. The developer never calls sync manually.
30    ///
31    /// Return None if the plugin reads params via `context.param(id)` instead.
32    fn params_mut(&mut self) -> Option<&mut dyn truce_params::Params> { None }
33
34    /// Process one block of audio.
35    fn process(
36        &mut self,
37        buffer: &mut AudioBuffer,
38        events: &EventList,
39        context: &mut ProcessContext,
40    ) -> ProcessStatus;
41
42    /// Render the GUI into the backend.
43    ///
44    /// Default: no-op. The shell uses BuiltinEditor with the layout
45    /// from `layout()` to draw standard widgets automatically.
46    /// Override only for custom visuals.
47    fn render(&self, _backend: &mut dyn RenderBackend) {}
48
49    /// Whether this plugin uses a custom render() implementation.
50    /// If false (default), the shell uses BuiltinEditor with
51    /// standard widget drawing from layout().
52    fn uses_custom_render(&self) -> bool { false }
53
54    /// Return the widget layout.
55    ///
56    /// Use `GridLayout::build()` for the layout. Widgets auto-flow
57    /// left-to-right. Use `.cols(n)` and `.rows(n)` for spanning.
58    fn layout(&self) -> truce_gui::layout::GridLayout {
59        truce_gui::layout::GridLayout::build("", "", 1, 80.0, vec![], vec![])
60    }
61
62    /// Hit test: which widget (if any) is at (x, y)?
63    fn hit_test(&self, widgets: &[WidgetRegion], x: f32, y: f32) -> Option<usize> {
64        default_hit_test(widgets, x, y)
65    }
66
67    /// Serialize plugin-specific state (DSP state, not params).
68    fn save_state(&self) -> Vec<u8> { Vec::new() }
69
70    /// Restore plugin-specific state.
71    fn load_state(&mut self, _data: &[u8]) {}
72
73    /// Report latency in samples.
74    fn latency(&self) -> u32 { 0 }
75
76    /// Report tail time in samples.
77    fn tail(&self) -> u32 { 0 }
78
79    /// Provide a custom editor instead of the built-in widget layout.
80    ///
81    /// Return `Some(editor)` to use a custom `Editor` implementation
82    /// (e.g., `truce_egui::EguiEditor`). The shell calls this first;
83    /// if it returns `None`, the shell falls back to creating a
84    /// `BuiltinEditor` from `layout()`.
85    fn custom_editor(&self) -> Option<Box<dyn truce_core::editor::Editor>> { None }
86}
87
88/// Default hit test: circular for knobs, rectangular for others,
89/// skip meters.
90pub fn default_hit_test(widgets: &[WidgetRegion], x: f32, y: f32) -> Option<usize> {
91    for (i, w) in widgets.iter().enumerate() {
92        if w.widget_type == WidgetType::Meter { continue; }
93        if w.widget_type == WidgetType::Knob {
94            let dx = x - w.cx;
95            let dy = y - w.cy;
96            if dx * dx + dy * dy <= w.radius * w.radius {
97                return Some(i);
98            }
99        } else if x >= w.x && x <= w.x + w.w && y >= w.y && y <= w.y + w.h {
100            return Some(i);
101        }
102    }
103    None
104}