Skip to content

Commit 2e6db62

Browse files
authored
Add dynamic menu support. (#16)
1 parent 22fb42c commit 2e6db62

File tree

27 files changed

+315
-21
lines changed

27 files changed

+315
-21
lines changed

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ members = [
2020
"plugins/chop/python",
2121
"plugins/chop/wasm",
2222
"plugins/dat/filter",
23+
"plugins/dat/dynamic_menu",
2324
"plugins/sop/generator-sop",
2425
"plugins/top/cpu-memory-top",
2526
"plugins/top/stable-diffusion",

CrashAutoSave.NewProject.1.toe

Whitespace-only changes.

plugins/chop/monome-grid/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use monome::{KeyDirection, Monome, MonomeDevice, MonomeEvent};
2+
use td_rs_chop::cxx::OP_Inputs;
23
use td_rs_chop::*;
34
use td_rs_derive::{Param, Params};
45

@@ -70,7 +71,7 @@ impl Chop for MonomeGrid {
7071
if let Some(ref mut device) = &mut self.device {
7172
while let Some(event) = device.poll() {
7273
match event {
73-
MonomeEvent::GridKey { x, y, direction, } => {
74+
MonomeEvent::GridKey { x, y, direction } => {
7475
let index = (y * 16 + x) as usize;
7576
if self.params.hold {
7677
if matches!(direction, KeyDirection::Down) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "dynamic-menu"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[package.metadata.td-rs]
7+
type = "dat"
8+
9+
[lib]
10+
name = "dynamic_menu"
11+
crate-type = ["staticlib"]
12+
13+
[dependencies]
14+
td-rs-dat = { path = "../../../td-rs-dat" }
15+
td-rs-derive = { path = "../../../td-rs-derive" }
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use td_rs_dat::chop::ChopInput;
2+
use td_rs_dat::*;
3+
use td_rs_derive::{Param, Params};
4+
5+
#[derive(Params, Default, Clone, Debug)]
6+
struct DynamicMenuDatParams {
7+
#[param(label = "Menu")]
8+
menu: DynamicMenuParam,
9+
}
10+
11+
/// Struct representing our DAT's state
12+
pub struct DynamicMenuDat {
13+
params: DynamicMenuDatParams,
14+
}
15+
16+
impl OpNew for DynamicMenuDat {
17+
fn new(_info: NodeInfo) -> Self {
18+
Self {
19+
params: Default::default(),
20+
}
21+
}
22+
}
23+
24+
impl OpInfo for DynamicMenuDat {
25+
const OPERATOR_TYPE: &'static str = "Dynamicmenu";
26+
const OPERATOR_LABEL: &'static str = "Dynamic Menu";
27+
const MIN_INPUTS: usize = 1;
28+
// This Dat takes no input
29+
const MAX_INPUTS: usize = 1;
30+
}
31+
32+
impl Op for DynamicMenuDat {
33+
fn params_mut(&mut self) -> Option<Box<&mut dyn OperatorParams>> {
34+
Some(Box::new(&mut self.params))
35+
}
36+
}
37+
38+
impl Dat for DynamicMenuDat {
39+
fn general_info(&self, _inputs: &OperatorInputs<DatInput>) -> DatGeneralInfo {
40+
DatGeneralInfo {
41+
cook_every_frame: false,
42+
cook_every_frame_if_asked: false,
43+
}
44+
}
45+
46+
fn execute(&mut self, output: DatOutput, inputs: &OperatorInputs<DatInput>) {
47+
if let Some(input) = inputs.input(0) {
48+
match input.dat_type() {
49+
DatType::Text => {
50+
if let Some(output_text) = &self.params.menu.0 {
51+
output
52+
.text()
53+
.set_text(&format!("Selected: {}", output_text));
54+
} else {
55+
output.text().set_text("");
56+
}
57+
}
58+
_ => self.set_warning("Input must be a text DAT"),
59+
}
60+
}
61+
}
62+
63+
fn build_dynamic_menu(
64+
&mut self,
65+
inputs: &OperatorInputs<DatInput>,
66+
menu_info: &mut DynamicMenuInfo,
67+
) {
68+
if menu_info.param_name() == "Menu" {
69+
if let Some(input) = inputs.input(0) {
70+
match input.dat_type() {
71+
DatType::Text => {
72+
let text = input.text();
73+
let labels = text
74+
.split('\n')
75+
.map(|s| s.to_string())
76+
.collect::<Vec<String>>();
77+
for label in labels {
78+
let name = label.replace(" ", "");
79+
menu_info.add_menu_entry(&name, &label);
80+
}
81+
}
82+
_ => self.set_warning("Input must be a text DAT"),
83+
}
84+
}
85+
}
86+
}
87+
}
88+
89+
impl DynamicMenuDat {}
90+
91+
dat_plugin!(DynamicMenuDat);

td-rs-base/src/RustBase.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ void releaseDownloadResult(TD::OP_SmartRef<TD::OP_TOPDownloadResult> &result) {
2727
result.release();
2828
}
2929

30+
const char* getBuildDynamicMenuInfoNames(TD::OP_BuildDynamicMenuInfo &info) {
31+
return info.name;
32+
}
33+
3034
#endif // TD_RS_RUSTBASE_H

td-rs-base/src/RustPy.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@ TD::PY_Context* getPyContext(TD::PY_Struct *pyStruct) {
1616

1717
void setPyInfo(TD::OP_CustomOPInfo &opInfo, void *pymethods, size_t size, void *pygetsets, size_t getsetsize) {
1818
if (size == 0) {
19-
std::cout << "No methods" << std::endl;
2019
opInfo.pythonMethods = nullptr;
2120
} else {
2221
opInfo.pythonMethods = static_cast<PyMethodDef*>(pymethods);
2322
}
2423

2524
if (getsetsize == 0) {
26-
std::cout << "No getsets" << std::endl;
2725
opInfo.pythonGetSets = nullptr;
2826
} else {
2927
opInfo.pythonGetSets = static_cast<PyGetSetDef*>(pygetsets);

td-rs-base/src/cxx.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ include_cpp! {
3636
generate!("TD::OP_SmartRef")
3737
generate_pod!("TD::OP_TextureDesc")
3838
generate_pod!("TD::OP_TexDim")
39+
generate!("TD::OP_BuildDynamicMenuInfo")
3940

4041
// util fns
4142
generate!("setString")
@@ -44,6 +45,7 @@ include_cpp! {
4445
generate!("getDownloadData")
4546
generate!("getDownloadTextureDesc")
4647
generate!("releaseDownloadResult")
48+
generate!("getBuildDynamicMenuInfoNames")
4749

4850
// Custom ops
4951
generate!("TD::OP_CustomOPInstance")

td-rs-base/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![feature(associated_type_defaults)]
22
#![feature(min_specialization)]
33

4+
use crate::cxx::OP_Inputs;
45
pub use param::*;
56
#[cfg(feature = "python")]
67
pub use py::*;
@@ -243,6 +244,30 @@ where
243244
}
244245
}
245246

247+
pub struct DynamicMenuInfo<'cook> {
248+
pub menu_info: Pin<&'cook mut cxx::OP_BuildDynamicMenuInfo>,
249+
}
250+
251+
impl<'cook> DynamicMenuInfo<'cook> {
252+
pub fn new(menu_info: Pin<&'cook mut cxx::OP_BuildDynamicMenuInfo>) -> Self {
253+
Self { menu_info }
254+
}
255+
256+
pub fn param_name(&mut self) -> &str {
257+
let name = cxx::getBuildDynamicMenuInfoNames(self.menu_info.as_mut());
258+
unsafe { ffi::CStr::from_ptr(name).to_str().unwrap() }
259+
}
260+
261+
pub fn add_menu_entry(&mut self, name: &str, label: &str) -> bool {
262+
unsafe {
263+
self.menu_info.as_mut().addMenuEntry(
264+
ffi::CString::new(name).unwrap().into_raw(),
265+
ffi::CString::new(label).unwrap().into_raw(),
266+
)
267+
}
268+
}
269+
}
270+
246271
/// Parameter inputs to an operator.
247272
pub struct ParamInputs<'cook> {
248273
inputs: &'cook crate::cxx::OP_Inputs,
@@ -276,6 +301,11 @@ impl<'cook> ParamInputs<'cook> {
276301
let res = self
277302
.inputs
278303
.getParString(ffi::CString::new(name).unwrap().into_raw());
304+
305+
if res.is_null() {
306+
return "";
307+
}
308+
279309
ffi::CStr::from_ptr(res).to_str().unwrap()
280310
}
281311
}

0 commit comments

Comments
 (0)