1use std::ffi::c_void;
8use std::sync::Arc;
9
10use truce_core::editor::{Editor, EditorContext, RawWindowHandle};
11use truce_params::Params;
12
13use crate::backend_cpu::CpuBackend;
14use crate::interaction::InteractionState;
15use crate::layout::{GridLayout, Layout, PluginLayout, compute_section_offsets,
16 GRID_GAP, GRID_PADDING, GRID_HEADER_H, GRID_SECTION_H};
17use crate::platform::{PlatformView, ViewCallbacks};
18use crate::render::RenderBackend;
19use crate::theme::Theme;
20use crate::widgets;
21
22pub struct BuiltinEditor<P: Params> {
27 params: Arc<P>,
28 layout: Layout,
29 theme: Theme,
30 backend: Option<CpuBackend>,
31 interaction: InteractionState,
32 context: Option<EditorContext>,
33 view: Option<PlatformView>,
34 self_ptr: *mut c_void,
36}
37
38unsafe impl<P: Params> Send for BuiltinEditor<P> {}
40
41impl<P: Params + 'static> BuiltinEditor<P> {
42 pub fn new(params: Arc<P>, layout: PluginLayout) -> Self {
43 Self {
44 params,
45 layout: Layout::Rows(layout),
46 theme: Theme::dark(),
47 backend: None,
48 interaction: InteractionState::new(),
49 context: None,
50 view: None,
51 self_ptr: std::ptr::null_mut(),
52 }
53 }
54
55 pub fn new_with_layout(params: Arc<P>, layout: Layout) -> Self {
56 Self {
57 params,
58 layout,
59 theme: Theme::dark(),
60 backend: None,
61 interaction: InteractionState::new(),
62 context: None,
63 view: None,
64 self_ptr: std::ptr::null_mut(),
65 }
66 }
67
68 pub fn new_grid(params: Arc<P>, layout: GridLayout) -> Self {
69 Self {
70 params,
71 layout: Layout::Grid(layout),
72 theme: Theme::dark(),
73 backend: None,
74 interaction: InteractionState::new(),
75 context: None,
76 view: None,
77 self_ptr: std::ptr::null_mut(),
78 }
79 }
80
81 pub fn with_theme(mut self, theme: Theme) -> Self {
82 self.theme = theme;
83 self
84 }
85
86 pub fn render(&mut self) {
88 let (w, h) = (self.layout.width(), self.layout.height());
89 let backend = self
90 .backend
91 .get_or_insert_with(|| CpuBackend::new(w, h).expect("Failed to create backend"));
92 let backend_ptr = backend as *mut CpuBackend;
94 self.render_widgets(unsafe { &mut *backend_ptr });
95 }
96
97 fn render_widgets(&mut self, backend: &mut dyn RenderBackend) {
99 if matches!(self.layout, Layout::Grid(_)) {
100 self.render_grid_inner(backend);
101 } else {
102 self.render_rows_inner(backend);
103 }
104 }
105
106 fn render_rows_inner(&mut self, backend: &mut dyn RenderBackend) {
107 let pl = match &self.layout {
108 Layout::Rows(pl) => pl,
109 _ => return,
110 };
111 let w = pl.width;
112 let knob_size = pl.knob_size;
113 let title = pl.title;
114 let version = pl.version;
115
116 backend.clear(self.theme.background);
117 let theme = &self.theme;
118
119 widgets::draw_header(backend, 0.0, 0.0, w as f32, 30.0, title, version, theme);
120
121 let pl = match &self.layout {
122 Layout::Rows(pl) => pl,
123 _ => return,
124 };
125 let mut y = 35.0;
126 let mut render_widget_idx = 0usize;
127
128 for row in &pl.rows {
129 if let Some(label) = row.label {
130 widgets::draw_section_label(backend, 0.0, y, w as f32, label, theme);
131 y += 18.0;
132 }
133
134 let total_cols: u32 = row.knobs.iter().map(|k| k.span.max(1)).sum();
135 let total_w = total_cols as f32 * (knob_size + 10.0) - 10.0;
136 let start_x = (w as f32 - total_w) / 2.0;
137
138 let mut col = 0u32;
139 for knob_def in row.knobs.iter() {
140 let span = knob_def.span.max(1);
141 let x = start_x + col as f32 * (knob_size + 10.0);
142 let widget_w = span as f32 * (knob_size + 10.0) - 10.0;
143
144 let (normalized, value_text) = if let Some(ref ctx) = self.context {
145 let n = (ctx.get_param)(knob_def.param_id) as f32;
146 let t = (ctx.format_param)(knob_def.param_id);
147 (n, t)
148 } else {
149 let n = self.params.get_normalized(knob_def.param_id).unwrap_or(0.0) as f32;
150 let p = self.params.get_plain(knob_def.param_id).unwrap_or(0.0);
151 let t = self
152 .params
153 .format_value(knob_def.param_id, p)
154 .unwrap_or_else(|| format!("{:.1}", p));
155 (n, t)
156 };
157
158 let region_idx = render_widget_idx;
159 render_widget_idx += 1;
160 let is_hovered = self.interaction.hover_idx == Some(region_idx);
161
162 let wtype = resolve_widget_type(knob_def.widget, knob_def.param_id, &*self.params);
163
164 match wtype {
165 widgets::WidgetType::Toggle => widgets::draw_toggle(
166 backend, x, y, widget_w, knob_size,
167 normalized, knob_def.label, &value_text,
168 theme, is_hovered,
169 ),
170 widgets::WidgetType::Slider => widgets::draw_slider(
171 backend, x, y, widget_w, knob_size,
172 normalized, knob_def.label, &value_text,
173 theme, is_hovered,
174 ),
175 widgets::WidgetType::Selector => widgets::draw_selector(
176 backend, x, y, widget_w, knob_size,
177 normalized, knob_def.label, &value_text,
178 theme, is_hovered,
179 ),
180 widgets::WidgetType::Meter => {
181 let default_ids = vec![knob_def.param_id];
182 let ids = knob_def.meter_ids.as_deref()
183 .unwrap_or(&default_ids);
184 let levels: Vec<f32> = if let Some(ref ctx) = self.context {
185 ids.iter().map(|&id| (ctx.get_meter)(id)).collect()
186 } else {
187 vec![0.0; ids.len()]
188 };
189 widgets::draw_meter(
190 backend, x, y, widget_w, knob_size,
191 &levels, knob_def.label, theme,
192 );
193 },
194 widgets::WidgetType::XYPad => {
195 let val_y_id = knob_def.param_id_y.unwrap_or(knob_def.param_id);
196 let (vx, vy) = if let Some(ref ctx) = self.context {
197 ((ctx.get_param)(knob_def.param_id) as f32,
198 (ctx.get_param)(val_y_id) as f32)
199 } else {
200 (self.params.get_normalized(knob_def.param_id).unwrap_or(0.0) as f32,
201 self.params.get_normalized(val_y_id).unwrap_or(0.0) as f32)
202 };
203 let infos = self.params.param_infos();
204 let x_name = infos.iter().find(|i| i.id == knob_def.param_id)
205 .map(|i| i.name).unwrap_or(knob_def.label);
206 let y_name = infos.iter().find(|i| i.id == val_y_id)
207 .map(|i| i.name).unwrap_or("");
208 widgets::draw_xy_pad(
209 backend, x, y, widget_w, knob_size,
210 vx, vy, x_name, y_name, theme, is_hovered,
211 );
212 },
213 widgets::WidgetType::Knob => widgets::draw_knob(
214 backend, x, y, knob_size, normalized,
215 knob_def.label, &value_text, theme, is_hovered,
216 ),
217 }
218 col += span;
219 }
220
221 y += knob_size + 30.0;
222 }
223 }
224
225 fn render_grid_inner(&mut self, backend: &mut dyn RenderBackend) {
226 let grid = match &self.layout {
227 Layout::Grid(g) => g,
228 _ => return,
229 };
230 let w = grid.width;
231 let title = grid.title;
232 let version = grid.version;
233
234 backend.clear(self.theme.background);
235 let theme = &self.theme;
236
237 widgets::draw_header(backend, 0.0, 0.0, w as f32, 30.0, title, version, theme);
238
239 let grid = match &self.layout {
240 Layout::Grid(g) => g,
241 _ => return,
242 };
243
244 let section_offsets = compute_section_offsets(grid);
245
246 for &(row_idx, label) in &grid.sections {
248 let y = GRID_HEADER_H + GRID_PADDING
249 + row_idx as f32 * (grid.cell_size + GRID_GAP)
250 + section_offsets[row_idx as usize]
251 - GRID_SECTION_H;
252 widgets::draw_section_label(backend, 0.0, y, w as f32, label, theme);
253 }
254
255 for (idx, gw) in grid.widgets.iter().enumerate() {
257 let x = GRID_PADDING + gw.col as f32 * (grid.cell_size + GRID_GAP);
258 let y = GRID_HEADER_H + GRID_PADDING
259 + gw.row as f32 * (grid.cell_size + GRID_GAP)
260 + section_offsets[gw.row as usize];
261 let widget_w = gw.col_span as f32 * (grid.cell_size + GRID_GAP) - GRID_GAP;
262 let widget_h = gw.row_span as f32 * (grid.cell_size + GRID_GAP) - GRID_GAP;
263
264 let (normalized, value_text) = if let Some(ref ctx) = self.context {
265 let n = (ctx.get_param)(gw.param_id) as f32;
266 let t = (ctx.format_param)(gw.param_id);
267 (n, t)
268 } else {
269 let n = self.params.get_normalized(gw.param_id).unwrap_or(0.0) as f32;
270 let p = self.params.get_plain(gw.param_id).unwrap_or(0.0);
271 let t = self
272 .params
273 .format_value(gw.param_id, p)
274 .unwrap_or_else(|| format!("{:.1}", p));
275 (n, t)
276 };
277
278 let is_hovered = self.interaction.hover_idx == Some(idx);
279 let wtype = resolve_widget_type(gw.widget, gw.param_id, &*self.params);
280
281 match wtype {
282 widgets::WidgetType::Toggle => widgets::draw_toggle(
283 backend, x, y, widget_w, widget_h,
284 normalized, gw.label, &value_text, theme, is_hovered,
285 ),
286 widgets::WidgetType::Slider => widgets::draw_slider(
287 backend, x, y, widget_w, widget_h,
288 normalized, gw.label, &value_text, theme, is_hovered,
289 ),
290 widgets::WidgetType::Selector => widgets::draw_selector(
291 backend, x, y, widget_w, widget_h,
292 normalized, gw.label, &value_text, theme, is_hovered,
293 ),
294 widgets::WidgetType::Meter => {
295 let default_ids = vec![gw.param_id];
296 let ids = gw.meter_ids.as_deref().unwrap_or(&default_ids);
297 let levels: Vec<f32> = if let Some(ref ctx) = self.context {
298 ids.iter().map(|&id| (ctx.get_meter)(id)).collect()
299 } else {
300 vec![0.0; ids.len()]
301 };
302 widgets::draw_meter(
303 backend, x, y, widget_w, widget_h,
304 &levels, gw.label, theme,
305 );
306 },
307 widgets::WidgetType::XYPad => {
308 let val_y_id = gw.param_id_y.unwrap_or(gw.param_id);
309 let (vx, vy) = if let Some(ref ctx) = self.context {
310 ((ctx.get_param)(gw.param_id) as f32,
311 (ctx.get_param)(val_y_id) as f32)
312 } else {
313 (self.params.get_normalized(gw.param_id).unwrap_or(0.0) as f32,
314 self.params.get_normalized(val_y_id).unwrap_or(0.0) as f32)
315 };
316 let infos = self.params.param_infos();
317 let x_name = infos.iter().find(|i| i.id == gw.param_id)
318 .map(|i| i.name).unwrap_or(gw.label);
319 let y_name = infos.iter().find(|i| i.id == val_y_id)
320 .map(|i| i.name).unwrap_or("");
321 widgets::draw_xy_pad(
322 backend, x, y, widget_w, widget_h,
323 vx, vy, x_name, y_name, theme, is_hovered,
324 );
325 },
326 widgets::WidgetType::Knob => {
327 let knob_size = widget_w.min(widget_h);
328 let kx = x + (widget_w - knob_size) / 2.0;
329 let ky = y + (widget_h - knob_size) / 2.0;
330 widgets::draw_knob(
331 backend, kx, ky, knob_size, normalized,
332 gw.label, &value_text, theme, is_hovered,
333 );
334 },
335 }
336 }
337 }
338
339 pub fn pixel_data(&self) -> Option<&[u8]> {
341 self.backend.as_ref().map(|b| b.data())
342 }
343
344 fn knob_def_at(&self, idx: usize) -> Option<&crate::layout::KnobDef> {
346 if let Layout::Rows(pl) = &self.layout {
347 let mut i = 0;
348 for row in &pl.rows {
349 for kd in &row.knobs {
350 if i == idx { return Some(kd); }
351 i += 1;
352 }
353 }
354 }
355 None
356 }
357
358 fn param_id_y_at(&self, idx: usize) -> Option<u32> {
360 match &self.layout {
361 Layout::Rows(_) => self.knob_def_at(idx).and_then(|kd| kd.param_id_y),
362 Layout::Grid(g) => g.widgets.get(idx).and_then(|w| w.param_id_y),
363 }
364 }
365
366 pub fn set_context(&mut self, context: EditorContext) {
370 self.context = Some(context);
371 match &self.layout {
372 Layout::Rows(pl) => self.interaction.build_regions(pl),
373 Layout::Grid(gl) => self.interaction.build_regions_grid(gl),
374 }
375 }
376
377 pub fn render_to(&mut self, backend: &mut dyn RenderBackend) {
382 unsafe { update_interaction(self) };
383 self.render_widgets(backend);
384 }
385
386 pub fn on_mouse_down(&mut self, x: f32, y: f32) {
389 if let Some(idx) = self.interaction.hit_test(x, y) {
390 let param_id = self.interaction.knob_regions[idx].param_id;
391 let wtype = self.interaction.widget_type_at(idx);
392 if wtype == Some(crate::widgets::WidgetType::Toggle) {
393 let norm = self.params.get_normalized(param_id).unwrap_or(0.0);
394 let new_norm = if norm > 0.5 { 0.0 } else { 1.0 };
395 self.params.set_normalized(param_id, new_norm);
396 if let Some(ref ctx) = self.context {
397 (ctx.begin_edit)(param_id);
398 (ctx.set_param)(param_id, new_norm);
399 (ctx.end_edit)(param_id);
400 }
401 } else if wtype == Some(crate::widgets::WidgetType::Selector) {
402 if let Some(info) = self.params.param_infos().into_iter().find(|i| i.id == param_id) {
403 let plain = self.params.get_plain(param_id).unwrap_or(0.0);
404 let max = info.range.max();
405 let next = if plain >= max { 0.0 } else { plain + 1.0 };
406 let new_norm = info.range.normalize(next);
407 self.params.set_normalized(param_id, new_norm);
408 if let Some(ref ctx) = self.context {
409 (ctx.begin_edit)(param_id);
410 (ctx.set_param)(param_id, new_norm);
411 (ctx.end_edit)(param_id);
412 }
413 }
414 } else {
415 let norm = self.params.get_normalized(param_id).unwrap_or(0.0);
416 self.interaction.begin_drag(idx, norm, y);
417 if let Some(ref ctx) = self.context {
418 (ctx.begin_edit)(param_id);
419 if wtype == Some(crate::widgets::WidgetType::XYPad) {
420 if let Some(y_id) = self.param_id_y_at(idx) {
421 (ctx.begin_edit)(y_id);
422 }
423 }
424 }
425 }
426 }
427 }
428
429 pub fn on_mouse_dragged(&mut self, x: f32, y: f32) {
430 if let Some(drag) = &self.interaction.dragging {
431 if drag.widget_type == crate::widgets::WidgetType::XYPad {
432 let pad_margin = 4.0;
433 let label_h = 18.0;
434 let pad_x = drag.region_x + pad_margin;
435 let pad_w = drag.region_w - pad_margin * 2.0;
436 let pad_y_start = drag.region_y + pad_margin;
437 let pad_h = drag.region_h - pad_margin * 2.0 - label_h;
438
439 let norm_x = ((x - pad_x) / pad_w).clamp(0.0, 1.0) as f64;
440 let norm_y = (1.0 - (y - pad_y_start) / pad_h).clamp(0.0, 1.0) as f64;
441
442 let param_id = drag.param_id;
443 let region_idx = drag.region_idx;
444 self.params.set_normalized(param_id, norm_x);
445 if let Some(ref ctx) = self.context {
446 (ctx.set_param)(param_id, norm_x);
447 }
448
449 if let Some(y_id) = self.param_id_y_at(region_idx) {
450 self.params.set_normalized(y_id, norm_y);
451 if let Some(ref ctx) = self.context {
452 (ctx.set_param)(y_id, norm_y);
453 }
454 }
455 } else if drag.widget_type == crate::widgets::WidgetType::Slider {
456 if let Some((param_id, new_norm)) = self.interaction.update_slider_drag(x) {
457 self.params.set_normalized(param_id, new_norm);
458 if let Some(ref ctx) = self.context {
459 (ctx.set_param)(param_id, new_norm);
460 }
461 }
462 } else {
463 if let Some((param_id, new_norm)) = self.interaction.update_drag(y) {
464 self.params.set_normalized(param_id, new_norm);
465 if let Some(ref ctx) = self.context {
466 (ctx.set_param)(param_id, new_norm);
467 }
468 }
469 }
470 }
471 }
472
473 pub fn on_mouse_up(&mut self, _x: f32, _y: f32) {
474 if let Some(drag) = &self.interaction.dragging {
475 let param_id = drag.param_id;
476 let was_xy = drag.widget_type == crate::widgets::WidgetType::XYPad;
477 let region_idx = drag.region_idx;
478 self.interaction.end_drag();
479 if let Some(ref ctx) = self.context {
480 (ctx.end_edit)(param_id);
481 if was_xy {
482 if let Some(y_id) = self.param_id_y_at(region_idx) {
483 (ctx.end_edit)(y_id);
484 }
485 }
486 }
487 }
488 }
489
490 pub fn on_double_click(&mut self, x: f32, y: f32) {
491 if let Some(idx) = self.interaction.hit_test(x, y) {
492 let param_id = self.interaction.knob_regions[idx].param_id;
493 let infos = self.params.param_infos();
495 if let Some(info) = infos.iter().find(|i| i.id == param_id) {
496 let default_norm = info.range.normalize(info.default_plain);
497 self.params.set_normalized(param_id, default_norm);
498 if let Some(ref ctx) = self.context {
499 (ctx.begin_edit)(param_id);
500 (ctx.set_param)(param_id, default_norm);
501 (ctx.end_edit)(param_id);
502 }
503 }
504 }
505 }
506
507 pub fn on_scroll(&mut self, x: f32, y: f32, delta_y: f32) {
508 if let Some(idx) = self.interaction.hit_test(x, y) {
509 let param_id = self.interaction.knob_regions[idx].param_id;
510 let norm = self.params.get_normalized(param_id).unwrap_or(0.0);
511 let step = delta_y as f64 / 200.0; let new_norm = (norm + step).clamp(0.0, 1.0);
513 self.params.set_normalized(param_id, new_norm);
514 if let Some(ref ctx) = self.context {
515 (ctx.begin_edit)(param_id);
516 (ctx.set_param)(param_id, new_norm);
517 (ctx.end_edit)(param_id);
518 }
519 }
520 }
521
522 pub fn on_mouse_moved(&mut self, x: f32, y: f32) -> bool {
523 self.interaction.hover_idx = self.interaction.hit_test(x, y);
524 self.interaction.hover_idx.is_some()
525 }
526}
527
528pub unsafe fn update_interaction<P: Params + 'static>(editor: &mut BuiltinEditor<P>) {
537 match &editor.layout {
538 Layout::Rows(pl) => {
539 editor.interaction.build_regions(pl);
540 let mut flat_idx = 0usize;
541 for row in &pl.rows {
542 for knob_def in &row.knobs {
543 if let Some(region) = editor.interaction.knob_regions.get_mut(flat_idx) {
544 region.widget_type = resolve_widget_type(
545 knob_def.widget, knob_def.param_id, &*editor.params,
546 );
547 }
548 flat_idx += 1;
549 }
550 }
551 }
552 Layout::Grid(gl) => {
553 editor.interaction.build_regions_grid(gl);
554 for (idx, gw) in gl.widgets.iter().enumerate() {
555 if let Some(region) = editor.interaction.knob_regions.get_mut(idx) {
556 region.widget_type = resolve_widget_type(
557 gw.widget, gw.param_id, &*editor.params,
558 );
559 }
560 }
561 }
562 }
563 for region in &mut editor.interaction.knob_regions {
564 if let Some(ref ctx) = editor.context {
565 region.normalized_value = (ctx.get_param)(region.param_id) as f32;
566 } else {
567 region.normalized_value =
568 editor.params.get_normalized(region.param_id).unwrap_or(0.0) as f32;
569 }
570 }
571}
572
573unsafe extern "C" fn cb_render<P: Params + 'static>(
574 ctx: *mut c_void,
575 out_w: *mut u32,
576 out_h: *mut u32,
577) -> *const u8 {
578 let editor = &mut *(ctx as *mut BuiltinEditor<P>);
579 update_interaction(editor);
580 editor.render();
581 let backend = match editor.backend.as_ref() {
582 Some(b) => b,
583 None => return std::ptr::null(),
584 };
585 *out_w = backend.width();
586 *out_h = backend.height();
587 backend.data().as_ptr()
588}
589
590unsafe extern "C" fn cb_mouse_down<P: Params + 'static>(ctx: *mut c_void, x: f32, y: f32) {
591 let editor = &mut *(ctx as *mut BuiltinEditor<P>);
592 editor.on_mouse_down(x, y);
593}
594
595unsafe extern "C" fn cb_mouse_dragged<P: Params + 'static>(ctx: *mut c_void, x: f32, y: f32) {
596 let editor = &mut *(ctx as *mut BuiltinEditor<P>);
597 editor.on_mouse_dragged(x, y);
598}
599
600unsafe extern "C" fn cb_mouse_up<P: Params + 'static>(ctx: *mut c_void, x: f32, y: f32) {
601 let editor = &mut *(ctx as *mut BuiltinEditor<P>);
602 editor.on_mouse_up(x, y);
603}
604
605unsafe extern "C" fn cb_scroll<P: Params + 'static>(
606 ctx: *mut c_void,
607 x: f32,
608 y: f32,
609 delta_y: f32,
610) {
611 let editor = &mut *(ctx as *mut BuiltinEditor<P>);
612 editor.on_scroll(x, y, delta_y);
613}
614
615unsafe extern "C" fn cb_double_click<P: Params + 'static>(ctx: *mut c_void, x: f32, y: f32) {
616 let editor = &mut *(ctx as *mut BuiltinEditor<P>);
617 editor.on_double_click(x, y);
618}
619
620unsafe extern "C" fn cb_mouse_moved<P: Params + 'static>(ctx: *mut c_void, x: f32, y: f32) -> u8 {
621 let editor = &mut *(ctx as *mut BuiltinEditor<P>);
622 editor.on_mouse_moved(x, y) as u8
623}
624
625fn resolve_widget_type<P: Params>(
631 widget: Option<crate::layout::WidgetKind>,
632 param_id: u32,
633 params: &P,
634) -> widgets::WidgetType {
635 match widget {
636 Some(crate::layout::WidgetKind::Knob) => widgets::WidgetType::Knob,
637 Some(crate::layout::WidgetKind::Slider) => widgets::WidgetType::Slider,
638 Some(crate::layout::WidgetKind::Toggle) => widgets::WidgetType::Toggle,
639 Some(crate::layout::WidgetKind::Selector) => widgets::WidgetType::Selector,
640 Some(crate::layout::WidgetKind::Meter) => widgets::WidgetType::Meter,
641 Some(crate::layout::WidgetKind::XYPad) => widgets::WidgetType::XYPad,
642 None => {
643 let param_info = params.param_infos().into_iter()
644 .find(|i| i.id == param_id);
645 match param_info.as_ref().map(|i| &i.range) {
646 Some(truce_params::ParamRange::Discrete { min: 0, max: 1 }) => widgets::WidgetType::Toggle,
647 Some(truce_params::ParamRange::Enum { .. }) => widgets::WidgetType::Selector,
648 _ => widgets::WidgetType::Knob,
649 }
650 }
651 }
652}
653
654impl<P: Params + 'static> Editor for BuiltinEditor<P> {
655 fn size(&self) -> (u32, u32) {
656 (self.layout.width(), self.layout.height())
657 }
658
659 fn open(&mut self, parent: RawWindowHandle, context: EditorContext) {
660 let (w, h) = self.size();
661 self.backend = CpuBackend::new(w, h);
662 self.context = Some(context);
663
664 match &self.layout {
666 Layout::Rows(pl) => self.interaction.build_regions(pl),
667 Layout::Grid(gl) => self.interaction.build_regions_grid(gl),
668 }
669
670 self.render();
672
673 let parent_ptr = match parent {
675 RawWindowHandle::AppKit(ptr) => ptr,
676 #[allow(unused)]
677 _ => std::ptr::null_mut(),
678 };
679
680 if !parent_ptr.is_null() {
681 let self_ptr = self as *mut BuiltinEditor<P> as *mut c_void;
682 self.self_ptr = self_ptr;
683
684 let callbacks = ViewCallbacks {
685 render: Some(cb_render::<P>),
686 mouse_down: Some(cb_mouse_down::<P>),
687 mouse_dragged: Some(cb_mouse_dragged::<P>),
688 mouse_up: Some(cb_mouse_up::<P>),
689 scroll: Some(cb_scroll::<P>),
690 double_click: Some(cb_double_click::<P>),
691 mouse_moved: Some(cb_mouse_moved::<P>),
692 };
693
694 self.view = unsafe { PlatformView::new(parent_ptr, w, h, self_ptr, &callbacks) };
695 }
696 }
697
698 fn close(&mut self) {
699 self.view = None;
700 self.context = None;
701 self.backend = None;
702 self.self_ptr = std::ptr::null_mut();
703 }
704
705 fn idle(&mut self) {
706 if self.view.is_none() {
709 self.render();
710 }
711 }
712}