1use crate::utils;
2use fluid_parser::ast::*;
3use std::fmt::Write;
4use std::sync::atomic;
5use std::sync::Mutex;
6
7static COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
8static I18N: atomic::AtomicBool = atomic::AtomicBool::new(false);
9static LAST_MENU: Mutex<String> = Mutex::new(String::new());
10
11pub const ALLOWS: &str = r#"// Automatically generated by fl2rust
12
13#![allow(unused_variables)]
14#![allow(unused_mut)]
15#![allow(unused_imports)]
16#![allow(dead_code)]
17#![allow(clippy::needless_update)]"#;
18
19const HEADER: &str = r#"
20use fltk::browser::*;
21use fltk::button::*;
22use fltk::dialog::*;
23use fltk::enums::*;
24use fltk::frame::*;
25use fltk::group::*;
26use fltk::image::*;
27use fltk::input::*;
28use fltk::menu::*;
29use fltk::misc::*;
30use fltk::output::*;
31use fltk::prelude::*;
32use fltk::table::*;
33use fltk::text::*;
34use fltk::tree::*;
35use fltk::valuator::*;
36use fltk::widget::*;
37use fltk::window::*;"#;
38
39fn i18nize(s: &str) -> String {
40 if I18N.load(atomic::Ordering::Relaxed) {
41 format!("&tr!(r#\"{}\"#)", s)
42 } else {
43 format!("r#\"{}\"#", s)
44 }
45}
46
47fn is_parent_type(typ: &str) -> bool {
48 matches!(
49 typ,
50 "Window"
51 | "Group"
52 | "Pack"
53 | "Tabs"
54 | "Scroll"
55 | "Table"
56 | "Tile"
57 | "Wizard"
58 | "MenuBar"
59 | "MenuButton"
60 | "Choice"
61 | "Flex"
62 )
63}
64
65fn is_menu_type(typ: &str) -> bool {
66 matches!(typ, "MenuBar" | "SysMenuBar" | "MenuButton" | "Choice")
67}
68
69fn add_menus(widgets: &[Widget], sub: &mut Vec<String>) -> String {
70 let mut wid = String::new();
71 let mut substyle = String::new();
72 for w in widgets {
73 if w.typ == "MenuItem" {
74 wid += "\tlet idx = ";
75 {
76 wid += &*LAST_MENU.lock().unwrap();
77 }
78 wid += ".add_choice(";
79 let mut temp = String::new();
80 temp += &sub.iter().map(|x| x.to_owned() + "/").collect::<String>();
81 temp += w.props.label.as_ref().unwrap_or(&String::new());
82 wid += &i18nize(&temp);
83 wid += ");\n";
84
85 let name = &format!("{}.at(idx).unwrap()", *LAST_MENU.lock().unwrap());
86 if let Some(v) = &w.props.shortcut {
87 writeln!(
88 wid,
89 "\t{}.set_shortcut(unsafe {{std::mem::transmute({})}});",
90 name, v
91 )
92 .unwrap();
93 }
94 if let Some(v) = &w.props.typ {
95 writeln!(wid, "\t{}.set_flag(MenuFlag::{});", name, v).unwrap();
96 } else if w.props.divider.is_some() {
97 writeln!(wid, "\t{}.set_flag(MenuFlag::MenuDivider);", name).unwrap();
98 }
99 if let Some(v) = &w.props.callback {
100 writeln!(wid, "\t{}.set_callback({});", name, v).unwrap();
101 }
102 if let Some(v) = &w.props.labeltype {
103 let temp = utils::global_to_pascal(v);
104 let temp = if temp == "No" { "None" } else { temp.as_str() };
105 writeln!(wid, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
106 }
107 if let Some(v) = &w.props.labelfont {
108 writeln!(wid, "\t{}.set_label_font(Font::by_index({}));", name, v).unwrap();
109 }
110 if let Some(v) = &w.props.labelsize {
111 writeln!(wid, "\t{}.set_label_size({});", name, v).unwrap();
112 }
113 if let Some(v) = &w.props.labelcolor {
114 writeln!(wid, "\t{}.set_label_color(Color::by_index({}));", name, v).unwrap();
115 }
116 } else {
117 sub.push(w.props.label.as_ref().unwrap_or(&String::new()).to_string());
118 let name = &format!(
119 "{}.find_item(\"{}\").unwrap()",
120 *LAST_MENU.lock().unwrap(),
121 {
122 let mut s = sub.iter().map(|x| x.to_owned() + "/").collect::<String>();
123 s.pop();
124 s
125 }
126 );
127 if let Some(v) = &w.props.labeltype {
128 let temp = utils::global_to_pascal(v);
129 let temp = if temp == "No" { "None" } else { temp.as_str() };
130 writeln!(substyle, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
131 }
132 if let Some(v) = &w.props.labelfont {
133 writeln!(
134 substyle,
135 "\t{}.set_label_font(Font::by_index({}));",
136 name, v
137 )
138 .unwrap();
139 }
140 if let Some(v) = &w.props.labelsize {
141 writeln!(substyle, "\t{}.set_label_size({});", name, v).unwrap();
142 }
143 if let Some(v) = &w.props.labelcolor {
144 writeln!(
145 substyle,
146 "\t{}.set_label_color(Color::by_index({}));",
147 name, v
148 )
149 .unwrap();
150 }
151 }
152 if !w.children.is_empty() {
153 wid += &add_menus(&w.children, sub);
154 }
155 if w.children.last().is_some() {
156 sub.pop();
157 }
158 }
159 wid += &substyle;
160 wid
161}
162
163fn add_widgets(
164 parent: Option<&str>,
165 widgets: &[Widget],
166 named: &mut Vec<(String, String)>,
167) -> String {
168 let mut wid = String::new();
169 let mut flex = String::new();
170 for w in widgets {
171 let mut name = String::new();
172 let mut refname = String::new();
173 let typ = if let Some(class) = &w.props.class {
174 class.to_owned()
175 } else {
176 utils::de_fl(&w.typ)
177 };
178 if typ != "MenuItem" && typ != "Submenu" {
179 if let Some(comment) = &w.props.comment {
180 wid += "\t// ";
181 wid += comment;
182 wid += "\n";
183 }
184 wid += "\tlet mut ";
185 if w.name.is_empty() {
186 let val = COUNTER.load(atomic::Ordering::Relaxed);
187 name += "fl2rust_widget_";
188 name += &val.to_string();
189 COUNTER.store(val + 1, atomic::Ordering::Relaxed);
190 } else {
191 name += &w.name;
192 named.push((name.clone(), typ.clone()));
193 }
194 if w.props.class.is_some() {
195 refname += "*";
196 refname += &name;
197 } else {
198 refname = name.clone();
199 }
200 wid += &name;
201 wid += " = ";
202 wid += &typ;
203 wid += "::new(";
204 for coord in w.props.xywh.split_ascii_whitespace() {
205 wid += coord;
206 wid += ", ";
207 }
208 wid += "None);\n";
209 if let Some(label) = &w.props.label {
210 wid += "\t";
211 wid += &name;
212 wid += ".set_label(";
213 wid += &i18nize(label);
214 wid += ");\n";
215 }
216
217 if let Some(v) = &w.props.typ {
218 let v = if typ == "Flex" {
219 if v == "HORIZONTAL" {
220 "Row"
221 } else {
222 "Column"
223 }
224 } else {
225 v
226 };
227 writeln!(
228 wid,
229 "\t{}.set_type({}Type::{});",
230 name,
231 utils::fix_type(&typ),
232 utils::global_to_pascal(v)
233 )
234 .unwrap();
235 } else if typ == "Flex" {
236 writeln!(wid, "\t{}.set_type(FlexType::Column);", name,).unwrap();
237 }
238 if let Some(v) = &w.props.align {
239 writeln!(
240 wid,
241 "\t{}.set_align(unsafe {{std::mem::transmute({})}});",
242 name, v
243 )
244 .unwrap();
245 }
246 if w.props.resizable.is_some() {
247 if parent.is_none() {
248 writeln!(wid, "\t{}.make_resizable(true);", name).unwrap();
249 } else {
250 writeln!(wid, "\t{}.resizable(&{});", parent.unwrap(), refname).unwrap();
251 }
252 }
253 if w.props.modal.is_some() {
254 writeln!(wid, "\t{}.make_modal(true);", name).unwrap();
255 }
256 if w.props.non_modal.is_some() {
257 writeln!(wid, "\t{}.make_modal(false);", name).unwrap();
258 }
259 if w.props.hide.is_some() {
260 writeln!(wid, "\t{}.hide();", name).unwrap();
261 }
262 if w.props.deactivate.is_some() {
263 writeln!(wid, "\t{}.deactivate();", name).unwrap();
264 }
265 if let Some(v) = &w.props.color {
266 writeln!(wid, "\t{}.set_color(Color::by_index({}));", name, v).unwrap();
267 }
268 if let Some(v) = &w.props.selection_color {
269 writeln!(
270 wid,
271 "\t{}.set_selection_color(Color::by_index({}));",
272 name, v
273 )
274 .unwrap();
275 }
276 if let Some(v) = &w.props.tooltip {
277 writeln!(wid, "\t{}.set_tooltip({});", name, i18nize(v)).unwrap();
278 }
279 if let Some(v) = &w.props.xclass {
280 writeln!(wid, "\t{}.set_xclass({});", name, i18nize(v)).unwrap();
281 }
282 if w.props.noborder.is_some() {
283 writeln!(wid, "\t{}.set_border(false);", name).unwrap();
284 }
285 if let Some(v) = &w.props.image {
286 writeln!(wid, "\tlet image_data = &{:?};", utils::gen_image(v)).unwrap();
287 writeln!(wid, "\t{0}.set_image(Some({1}::from_data(image_data).expect(\"Could not load image: {2}\")));", name, utils::get_image_type(v), v).unwrap();
288 }
289 if let Some(v) = &w.props.deimage {
290 writeln!(wid, "\tlet image_data = &{:?};", utils::gen_image(v)).unwrap();
291 writeln!(wid, "\t{0}.set_deimage(Some({1}::from_data(image_data).expect(\"Could not load image: {2}\")));", name, utils::get_image_type(v), v).unwrap();
292 }
293 if let Some(v) = &w.props.r#box {
294 let temp = utils::global_to_pascal(v);
295 let temp = match temp.as_str() {
296 "OflatBox" => "OFlatFrame",
297 "OshadowBox" => "OShadowBox",
298 "RflatBox" => "RFlatBox",
299 "RshadowBox" => "RShadowBox",
300 _ => temp.as_str(),
301 };
302 writeln!(wid, "\t{}.set_frame(FrameType::{});", name, temp).unwrap();
303 }
304 if let Some(v) = &w.props.down_box {
305 let temp = utils::global_to_pascal(v);
306 let temp = match temp.as_str() {
307 "OflatBox" => "OFlatFrame",
308 "OshadowBox" => "OShadowBox",
309 "RflatBox" => "RFlatBox",
310 "RshadowBox" => "RShadowBox",
311 _ => temp.as_str(),
312 };
313 writeln!(wid, "\t{}.set_down_frame(FrameType::{});", name, temp).unwrap();
314 }
315 if let Some(v) = &w.props.labeltype {
316 let temp = utils::global_to_pascal(v);
317 let temp = if temp == "No" { "None" } else { temp.as_str() };
318 writeln!(wid, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
319 }
320 if let Some(v) = &w.props.labelfont {
321 writeln!(wid, "\t{}.set_label_font(Font::by_index({}));", name, v).unwrap();
322 }
323 if let Some(v) = &w.props.labelsize {
324 writeln!(wid, "\t{}.set_label_size({});", name, v).unwrap();
325 }
326 if let Some(v) = &w.props.labelcolor {
327 writeln!(wid, "\t{}.set_label_color(Color::by_index({}));", name, v).unwrap();
328 }
329 if let Some(v) = &w.props.when {
330 writeln!(
331 wid,
332 "\t{}.set_trigger(unsafe {{std::mem::transmute({})}});",
333 name, v
334 )
335 .unwrap();
336 }
337 if let Some(v) = &w.props.textfont {
338 writeln!(wid, "\t{}.set_text_font(Font::by_index({}));", name, v).unwrap();
339 }
340 if let Some(v) = &w.props.textsize {
341 writeln!(wid, "\t{}.set_text_size({});", name, v).unwrap();
342 }
343 if let Some(v) = &w.props.textcolor {
344 writeln!(wid, "\t{}.set_text_color(Color::by_index({}));", name, v).unwrap();
345 }
346 if let Some(v) = &w.props.shortcut {
347 writeln!(
348 wid,
349 "\t{}.set_shortcut(unsafe {{std::mem::transmute({})}});",
350 name, v
351 )
352 .unwrap();
353 }
354 if let Some(v) = &w.props.gap {
355 if v.contains(' ') {
356 let count: Vec<_> = v.split_ascii_whitespace().collect();
357 write!(wid, "\t{}.set_gap(", name).unwrap();
358 for e in count {
359 wid += e;
360 wid += ", ";
361 }
362 wid += ");\n";
363 } else {
364 writeln!(wid, "\t{}.set_pad({});", name, v).unwrap();
365 }
366 }
367 if let Some(v) = &w.props.minimum {
368 writeln!(wid, "\t{}.set_minimum({} as _);", name, v).unwrap();
369 }
370 if let Some(v) = &w.props.maximum {
371 writeln!(wid, "\t{}.set_maximum({} as _);", name, v).unwrap();
372 }
373 if let Some(v) = &w.props.size {
374 writeln!(wid, "\t{}.set_size({} as _);", name, v).unwrap();
375 }
376 if let Some(v) = &w.props.slider_size {
377 writeln!(wid, "\t{}.set_slider_size({} as _);", name, v).unwrap();
378 }
379 if let Some(v) = &w.props.step {
380 writeln!(wid, "\t{}.set_step({} as _, 1);", name, v).unwrap();
381 }
382 if let Some(v) = &w.props.user_data {
383 if let Some(stripped) = v.strip_prefix("id:") {
384 writeln!(wid, "\t{}.set_id(\"{}\");", name, stripped).unwrap();
385 }
386 }
387 if let Some(v) = &w.props.value {
388 let val = if typ.contains("Button") {
389 let b = v
390 .parse::<i32>()
391 .expect("Buttons should have integral values");
392 if b != 0 {
393 "true".to_string()
394 } else {
395 "false".to_string()
396 }
397 } else if (typ.contains("Input") || typ.contains("Output"))
398 && !typ.contains("Value")
399 {
400 i18nize(v)
401 } else {
402 format!("{} as _", v)
403 };
404 writeln!(wid, "\t{}.set_value({});", name, val).unwrap();
405 }
406 if let Some(v) = &w.props.code0 {
407 wid += "\t";
408 wid += v;
409 wid += "\n";
410 }
411 if let Some(v) = &w.props.code1 {
412 wid += "\t";
413 wid += v;
414 wid += "\n";
415 }
416 if let Some(v) = &w.props.code2 {
417 wid += "\t";
418 wid += v;
419 wid += "\n";
420 }
421 if let Some(v) = &w.props.code3 {
422 wid += "\t";
423 wid += v;
424 wid += "\n";
425 }
426 if let Some(v) = &w.props.extra_code {
427 wid += "\t";
428 wid += v;
429 wid += "\n";
430 }
431 if let Some(v) = &w.props.callback {
432 writeln!(wid, "\t{}.set_callback({});", name, v).unwrap();
433 }
434
435 if let Some(sizes) = &w.props.size_tuple {
436 let count: Vec<_> = sizes.split_ascii_whitespace().collect();
437 let count: Vec<_> = count.iter().skip(1).collect();
438 for e in count.chunks_exact(2) {
439 let idx: usize = e[0].parse().unwrap();
440 writeln!(
441 flex,
442 "\t{0}.fixed(&{0}.child({1}).unwrap(), {2});",
443 name, idx, e[1]
444 )
445 .unwrap();
446 }
447 writeln!(flex, "\t{}.recalc();", name).unwrap();
448 }
449 if let Some(sizes) = &w.props.dimensions {
450 let count: Vec<_> = sizes.split_ascii_whitespace().collect();
451 write!(wid, "\t{0}.set_layout(", name).unwrap();
452 for e in count {
453 wid += e;
454 wid += ", ";
455 }
456 wid += ");\n";
457 }
458 if let Some(sizes) = &w.props.margins {
459 let count: Vec<_> = sizes.split_ascii_whitespace().collect();
460 write!(wid, "\t{0}.set_margins(", name).unwrap();
461 for e in count {
462 wid += e;
463 wid += ", ";
464 }
465 wid += ");\n";
466 }
467 if let Some(sizes) = &w.props.margin {
468 let count: Vec<_> = sizes.split_ascii_whitespace().collect();
469 if count.len() == 1 {
470 write!(wid, "\t{0}.set_margin(", name).unwrap();
471 for e in count {
472 wid += e;
473 }
474 wid += ");\n";
475 } else {
476 write!(wid, "\t{0}.set_margins(", name).unwrap();
477 for e in count {
478 wid += e;
479 wid += ", ";
480 }
481 wid += ");\n";
482 }
483 }
484 if let Some(sizes) = &w.props.size_range {
485 let count: Vec<_> = sizes.split_ascii_whitespace().collect();
486 write!(wid, "\t{0}.size_range(", name).unwrap();
487 for e in count {
488 wid += e;
489 wid += ", ";
490 }
491 wid += ");\n";
492 }
493 if let Some(parent_props) = &w.props.parent_properties {
494 if let Some(loc) = &parent_props.location {
495 let count: Vec<_> = loc.split_ascii_whitespace().collect();
496 write!(wid, "\tlet mut p_grid = Grid::from_dyn_widget(&{}.parent().unwrap()).unwrap();\n", name,).unwrap();
497 write!(wid, "\tp_grid.set_widget(&mut {}, ", name, ).unwrap();
498 for e in count {
499 wid += e;
500 wid += ", ";
501 }
502 wid += ");\n";
503 }
504 }
505 if is_menu_type(&typ) {
506 {
507 *LAST_MENU.lock().unwrap() = name.to_string();
508 }
509 let ch = add_menus(&w.children, &mut vec![]);
510 wid += &ch;
511 } else if !w.children.is_empty() {
512 let ch = add_widgets(Some(&name), &w.children, named);
513 wid += &ch;
514 }
515 if is_parent_type(&typ) {
516 wid += "\t";
517 wid += &name;
518 wid += ".end();\n";
519 }
520 if w.props.visible.is_some() {
521 writeln!(wid, "\t{}.show();", name).unwrap();
522 }
523 }
524 }
525 wid += &flex;
526 wid
527}
528
529fn add_funcs(functions: &[Function], free: bool, named: &mut Vec<(String, String)>) -> String {
530 let mut func = String::new();
531 for c in functions {
532 func += "\n pub fn ";
533 func += &c.name;
534 if let Some(ret) = &c.props.return_type {
535 func += " -> ";
536 func += ret;
537 } else if !free && !c.name.contains("self") {
538 func += " -> Self";
539 }
540 func += " {\n";
541 if let Some(code) = &c.code {
542 func += "\t";
543 func += code;
544 func += "\n";
545 }
546 if !c.widgets.is_empty() {
547 func += &add_widgets(None, &c.widgets, named);
548 }
549 if free && c.props.return_type.is_none() {
550 func += "\t(\n";
551 } else if !c.name.contains("self") && !free {
552 func += "\tSelf {\n";
553 }
554 if !named.is_empty() && named.len() > 1 {
555 for n in named.iter() {
556 func += "\t ";
557 func += &n.0;
558 func += ",\n";
559 }
560 } else if !named.is_empty() && named.len() == 1 {
561 func += "\t ";
562 func += &named[0].0;
563 func += "\n";
564 }
565 if free {
566 named.clear();
567 }
568 if free && c.props.return_type.is_none() {
569 func += "\t)";
570 } else if !c.name.contains("self") && !free {
571 func += "\t}";
572 }
573 func += "\n }";
574 }
575 func
576}
577
578fn add_widget_class_ctor(w: &Widget, named: &mut Vec<(String, String)>) -> String {
579 let mut wid = String::new();
580 wid += "\n pub fn new<L: Into<Option<&'static str>>>(x: i32, y: i32, w: i32, h: i32, label: L) -> Self {\n";
581 wid += "\tlet mut base_group = Group::new(0, 0, ";
582 for coord in w.props.xywh.split_ascii_whitespace().skip(2) {
583 wid += coord;
584 wid += ", ";
585 }
586 wid += "label);\n";
587 let name = "base_group";
588 if w.props.resizable.is_some() {
589 writeln!(wid, "\t{}.make_resizable(true);", name).unwrap();
590 }
591 if let Some(v) = &w.props.labeltype {
592 let temp = utils::global_to_pascal(v);
593 let temp = if temp == "No" { "None" } else { temp.as_str() };
594 writeln!(wid, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
595 }
596 if let Some(v) = &w.props.labelfont {
597 writeln!(wid, "\t{}.set_label_font(Font::by_index({}));", name, v).unwrap();
598 }
599 if let Some(v) = &w.props.labelsize {
600 writeln!(wid, "\t{}.set_label_size({});", name, v).unwrap();
601 }
602 if let Some(v) = &w.props.labelcolor {
603 writeln!(wid, "\t{}.set_label_color(Color::by_index({}));", name, v).unwrap();
604 }
605 if let Some(v) = &w.props.color {
606 writeln!(wid, "\t{}.set_color(Color::by_index({}));", name, v).unwrap();
607 }
608 if !w.children.is_empty() {
609 wid += &add_widgets(Some(name), &w.children, named);
610 }
611 wid += "\tbase_group.end();\n";
612 wid += "\tbase_group.resize(x, y, w, h);\n";
613 wid += "\tSelf {\n\t base_group,\n";
614 if !named.is_empty() {
615 for n in named.iter() {
616 wid += "\t ";
617 wid += &n.0;
618 wid += ",\n";
619 }
620 }
621 wid += "\t}";
622 wid += "\n }";
623 wid
624}
625
626fn generate_(ast: &Ast) -> String {
628 let mut s = String::new();
629 if let Some(i18n) = ast.i18n_type {
630 I18N.store(i18n, atomic::Ordering::Relaxed);
631 }
632 s += "\n";
633 let mut classes = vec![];
634 let mut widget_classes = vec![];
635 let mut funcs = vec![];
636 if !ast.decls.is_empty() {
637 for decl in &ast.decls {
638 s += &decl.decl;
639 s += "\n";
640 }
641 s += "\n";
642 }
643 if !ast.comments.is_empty() {
644 for comment in &ast.comments {
645 s += &comment.comment;
646 s += "\n";
647 }
648 }
649 if !ast.functions.is_empty() {
650 let mut local_named = vec![];
651 let func = add_funcs(&ast.functions, true, &mut local_named);
652 funcs.push(func);
653 }
654 if !ast.widget_classes.is_empty() {
655 let mut named: Vec<(String, String)> = vec![];
656 let mut class = String::new();
657 for c in &ast.widget_classes {
658 class += "#[derive(Debug, Clone)]\n";
659 class += "pub struct ";
660 class += &c.name;
661 class += " {\n";
662 class += " pub base_group: Group,\n";
663 let fns = add_widget_class_ctor(c, &mut named);
664 if !named.is_empty() {
665 for n in &named {
666 class += " pub ";
667 class += &n.0;
668 class += ": ";
669 class += &n.1;
670 class += ",\n";
671 }
672 }
673 named.clear();
674 class += "}\n\n";
675 class += "impl ";
676 class += &c.name;
677 class += " {";
678 class += &fns;
679 class += "\n}\n\n";
680 class += "fltk::widget_extends!(";
681 class += &c.name;
682 class += ", Group, base_group);\n\n";
683 }
684 widget_classes.push(class);
685 }
686 if !ast.classes.is_empty() {
687 let mut named: Vec<(String, String)> = vec![];
688 let mut class = String::new();
689 for c in &ast.classes {
690 class += "#[derive(Debug, Clone)]\n";
691 class += "pub struct ";
692 class += &c.name;
693 class += " {\n";
694 let fns = add_funcs(&c.functions, false, &mut named);
695 if !named.is_empty() {
696 for n in &named {
697 class += " pub ";
698 class += &n.0;
699 class += ": ";
700 class += &n.1;
701 class += ",\n";
702 }
703 }
704 named.clear();
705 class += "}\n\n";
706 if !c.functions.is_empty() {
707 class += "impl ";
708 class += &c.name;
709 class += " {";
710 class += &fns;
711 class += "\n}\n\n";
712 }
713 }
714 classes.push(class);
715 }
716 for f in funcs {
717 s += &f;
718 s += "\n";
719 }
720 for c in widget_classes {
721 s += &c;
722 s += "\n";
723 }
724 for c in classes {
725 s += &c;
726 s += "\n";
727 }
728 s
729}
730
731pub fn generate(ast: &Ast) -> String {
733 let s = generate_(ast);
734 format!("{}\n{}", HEADER, s)
735}
736
737pub fn generate_with_directives_preamble(ast: &Ast) -> String {
739 let s = generate_(ast);
740 format!("{}\n{}\n{}", ALLOWS, HEADER, s)
741}