1use fltk::{image::*, prelude::*, utils::is_ptr_of, *};
167use std::collections::HashMap;
168use std::fmt;
169use std::path::Path;
170
171pub mod utils;
172
173pub fn make_image_frame<P: AsRef<Path>>(filename: P) -> frame::Frame {
174 let mut frame = frame::Frame::default();
175 let img = SharedImage::load(&filename).ok();
176 if let Some(ref img) = img {
177 let w = img.width();
178 let h = img.height();
179 frame.set_size(w, h);
180 }
181 frame.set_image(img);
182 frame.set_tooltip(filename.as_ref().to_str().unwrap());
183 frame
184}
185
186#[derive(Debug, Clone)]
187pub struct FlImage(pub String);
188
189impl fmt::Display for FlImage {
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191 write!(f, "{}", self.0)
192 }
193}
194#[derive(Debug)]
195#[non_exhaustive]
196pub enum FltkFormError {
197 FltkError(FltkErrorKind),
198 Internal(FltkFormErrorKind),
199 Unknown(String),
200}
201
202unsafe impl Send for FltkFormError {}
203unsafe impl Sync for FltkFormError {}
204
205#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
206pub enum FltkFormErrorKind {
207 PropertyInexistent,
208 FailedToChangeData,
209}
210
211impl std::error::Error for FltkFormError {
212 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
213 None
214 }
215}
216
217impl fmt::Display for FltkFormError {
218 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 match *self {
220 FltkFormError::Internal(ref err) => write!(f, "An internal error occured {:?}", err),
221 FltkFormError::Unknown(ref err) => write!(f, "An unknown error occurred {:?}", err),
222 FltkFormError::FltkError(ref err) => write!(f, "an fltk error occured {:?}", err),
223 }
224 }
225}
226
227#[derive(Clone, Debug)]
228pub struct Form {
229 grp: group::Group,
230}
231
232impl Default for Form {
233 fn default() -> Self {
234 Form::new(0, 0, 0, 0, None)
235 }
236}
237
238impl Form {
239 pub fn new<S: Into<Option<&'static str>>>(x: i32, y: i32, w: i32, h: i32, label: S) -> Self {
240 let grp = group::Group::new(x, y, w, h, label);
241 grp.end();
242 Self { grp }
243 }
244
245 pub fn default_fill() -> Self {
246 Form::default().size_of_parent().center_of_parent()
247 }
248
249 pub fn set_data<T: FltkForm>(&mut self, data: T) {
250 self.begin();
251 let mut w = data.generate();
252 w.resize(self.x(), self.y(), self.w(), self.h());
253 self.end();
254 }
255
256 pub fn from_data<T: FltkForm>(mut self, data: T) -> Self {
257 self.set_data(data);
258 self
259 }
260
261 pub fn set_data_view<T: FltkForm>(&mut self, data: T) {
262 self.begin();
263 let mut w = data.view();
264 w.resize(self.x(), self.y(), self.w(), self.h());
265 self.end();
266 }
267
268 pub fn from_data_view<T: FltkForm>(mut self, data: T) -> Self {
269 self.set_data_view(data);
270 self
271 }
272
273 pub fn get_prop(&self, prop: &str) -> Option<String> {
274 if let Some(child) = self.grp.child(0) {
275 if let Some(grp) = child.as_group() {
276 for child in grp.into_iter() {
277 if child.label() == prop {
278 let ptr = child.as_widget_ptr();
279 let val = if is_ptr_of::<input::Input>(ptr) {
280 1
281 } else if is_ptr_of::<button::CheckButton>(ptr) {
282 2
283 } else if is_ptr_of::<menu::Choice>(ptr) {
284 3
285 } else {
286 4
287 };
288 match val {
289 1 => {
290 let inp = input::Input::from_dyn_widget_ptr(ptr as _).unwrap();
291 return Some(inp.value());
292 }
293 2 => {
294 let inp =
295 button::CheckButton::from_dyn_widget_ptr(ptr as _).unwrap();
296 return Some(inp.value().to_string());
297 }
298 3 => {
299 let choice = menu::Choice::from_dyn_widget_ptr(ptr as _).unwrap();
300 return choice.choice();
301 }
302 _ => {
303 let wid = widget::Widget::from_dyn_widget_ptr(ptr as _).unwrap();
304 return Some(wid.label());
305 }
306 }
307 }
308 }
309 None
310 } else {
311 None
312 }
313 } else {
314 None
315 }
316 }
317
318 pub fn set_prop(&mut self, prop: &str, value: &str) -> Result<(), FltkFormError> {
319 let mut found = false;
320 if let Some(child) = self.grp.child(0) {
321 if let Some(grp) = child.as_group() {
322 for child in grp.into_iter() {
323 if child.label() == prop {
324 found = true;
325 let ptr = child.as_widget_ptr();
326 let val = if is_ptr_of::<input::Input>(ptr) {
327 1
328 } else if is_ptr_of::<button::CheckButton>(ptr) {
329 2
330 } else if is_ptr_of::<menu::Choice>(ptr) {
331 3
332 } else {
333 4
334 };
335 match val {
336 1 => {
337 let mut inp =
338 input::Input::from_dyn_widget_ptr(ptr as _).unwrap();
339 inp.set_value(value);
340 }
341 2 => {
342 let mut inp =
343 button::CheckButton::from_dyn_widget_ptr(ptr as _).unwrap();
344 let v = value == "true";
345 inp.set_value(v);
346 }
347 3 => {
348 let mut choice =
349 menu::Choice::from_dyn_widget_ptr(ptr as _).unwrap();
350 let idx = choice.find_index(value);
351 choice.set_value(idx);
352 }
353 _ => (),
354 }
355 }
356 }
357 }
358 }
359 if !found {
360 return Err(FltkFormError::Internal(
361 FltkFormErrorKind::PropertyInexistent,
362 ));
363 }
364 Ok(())
365 }
366
367 pub fn get_props(&self) -> HashMap<String, String> {
368 let mut temp = HashMap::new();
369 if let Some(c) = self.grp.child(0) {
370 if let Some(grp) = c.as_group() {
371 for child in grp.into_iter() {
372 if !child.label().is_empty() {
373 if let Some(prop) = self.get_prop(&child.label()) {
374 temp.insert(child.label().clone(), prop);
375 }
376 }
377 }
378 }
379 }
380 temp
381 }
382
383 pub fn rename_prop(&self, prop: &str, new_name: &str) {
384 if let Some(child) = self.grp.child(0) {
385 if let Some(grp) = child.as_group() {
386 for mut child in grp.into_iter() {
387 if child.label() == prop {
388 child.set_label(new_name);
389 }
390 }
391 }
392 }
393 }
394
395 pub fn get_widget(&self, prop: &str) -> Option<Box<dyn WidgetExt>> {
396 if let Some(grp) = self.as_group() {
397 for child in grp.into_iter() {
398 if child.label() == prop {
399 return Some(Box::new(child));
400 }
401 }
402 None
403 } else if self.label() == prop {
404 let wid: widget::Widget = widget::Widget::from_dyn_widget_ptr(self.as_widget_ptr()).unwrap();
405 Some(Box::new(wid))
406 } else {
407 None
408 }
409 }
410}
411
412fltk::widget_extends!(Form, group::Group, grp);
413
414pub trait FltkForm {
415 fn generate(&self) -> Box<dyn WidgetExt>;
416 fn view(&self) -> Box<dyn WidgetExt>;
417}
418
419impl FltkForm for FlImage {
420 fn generate(&self) -> Box<dyn WidgetExt> {
421 let val = format!("{}", *self);
422 let i = make_image_frame(val.as_str());
423 Box::new(i)
424 }
425 fn view(&self) -> Box<dyn WidgetExt> {
426 let val = format!("{}", *self);
427 let i = make_image_frame(val.as_str());
428 Box::new(i)
429 }
430}
431
432impl FltkForm for f64 {
433 fn generate(&self) -> Box<dyn WidgetExt> {
434 let mut i = input::FloatInput::default();
435 let val = format!("{:?}", *self);
436 i.set_value(&val);
437 Box::new(i)
438 }
439 fn view(&self) -> Box<dyn WidgetExt> {
440 let mut i = output::Output::default();
441 let val = format!("{:?}", *self);
442 i.set_value(&val);
443 Box::new(i)
444 }
445}
446
447impl FltkForm for f32 {
448 fn generate(&self) -> Box<dyn WidgetExt> {
449 let mut i = input::FloatInput::default();
450 let val = format!("{:?}", *self);
451 i.set_value(&val);
452 Box::new(i)
453 }
454 fn view(&self) -> Box<dyn WidgetExt> {
455 let mut i = output::Output::default();
456 let val = format!("{:?}", *self);
457 i.set_value(&val);
458 Box::new(i)
459 }
460}
461
462impl FltkForm for i32 {
463 fn generate(&self) -> Box<dyn WidgetExt> {
464 let mut i = input::IntInput::default();
465 let val = format!("{:?}", *self);
466 i.set_value(&val);
467 Box::new(i)
468 }
469 fn view(&self) -> Box<dyn WidgetExt> {
470 let mut i = output::Output::default();
471 let val = format!("{:?}", *self);
472 i.set_value(&val);
473 Box::new(i)
474 }
475}
476
477impl FltkForm for u32 {
478 fn generate(&self) -> Box<dyn WidgetExt> {
479 let mut i = input::IntInput::default();
480 let val = format!("{:?}", *self);
481 i.set_value(&val);
482 Box::new(i)
483 }
484 fn view(&self) -> Box<dyn WidgetExt> {
485 let mut i = output::Output::default();
486 let val = format!("{:?}", *self);
487 i.set_value(&val);
488 Box::new(i)
489 }
490}
491
492impl FltkForm for i64 {
493 fn generate(&self) -> Box<dyn WidgetExt> {
494 let mut i = input::IntInput::default();
495 let val = format!("{:?}", *self);
496 i.set_value(&val);
497 Box::new(i)
498 }
499 fn view(&self) -> Box<dyn WidgetExt> {
500 let mut i = output::Output::default();
501 let val = format!("{:?}", *self);
502 i.set_value(&val);
503 Box::new(i)
504 }
505}
506
507impl FltkForm for u64 {
508 fn generate(&self) -> Box<dyn WidgetExt> {
509 let mut i = input::IntInput::default();
510 let val = format!("{:?}", *self);
511 i.set_value(&val);
512 Box::new(i)
513 }
514 fn view(&self) -> Box<dyn WidgetExt> {
515 let mut i = output::Output::default();
516 let val = format!("{:?}", *self);
517 i.set_value(&val);
518 Box::new(i)
519 }
520}
521
522impl FltkForm for isize {
523 fn generate(&self) -> Box<dyn WidgetExt> {
524 let mut i = input::IntInput::default();
525 let val = format!("{:?}", *self);
526 i.set_value(&val);
527 Box::new(i)
528 }
529 fn view(&self) -> Box<dyn WidgetExt> {
530 let mut i = output::Output::default();
531 let val = format!("{:?}", *self);
532 i.set_value(&val);
533 Box::new(i)
534 }
535}
536
537impl FltkForm for usize {
538 fn generate(&self) -> Box<dyn WidgetExt> {
539 let mut i = input::IntInput::default();
540 let val = format!("{:?}", *self);
541 i.set_value(&val);
542 Box::new(i)
543 }
544 fn view(&self) -> Box<dyn WidgetExt> {
545 let mut i = output::Output::default();
546 let val = format!("{:?}", *self);
547 i.set_value(&val);
548 Box::new(i)
549 }
550}
551
552impl FltkForm for i8 {
553 fn generate(&self) -> Box<dyn WidgetExt> {
554 let mut i = input::IntInput::default();
555 let val = format!("{:?}", *self);
556 i.set_value(&val);
557 Box::new(i)
558 }
559 fn view(&self) -> Box<dyn WidgetExt> {
560 let mut i = output::Output::default();
561 let val = format!("{:?}", *self);
562 i.set_value(&val);
563 Box::new(i)
564 }
565}
566
567impl FltkForm for u8 {
568 fn generate(&self) -> Box<dyn WidgetExt> {
569 let mut i = input::IntInput::default();
570 let val = format!("{:?}", *self);
571 i.set_value(&val);
572 Box::new(i)
573 }
574 fn view(&self) -> Box<dyn WidgetExt> {
575 let mut i = output::Output::default();
576 let val = format!("{:?}", *self);
577 i.set_value(&val);
578 Box::new(i)
579 }
580}
581
582impl FltkForm for i16 {
583 fn generate(&self) -> Box<dyn WidgetExt> {
584 let mut i = input::IntInput::default();
585 let val = format!("{:?}", *self);
586 i.set_value(&val);
587 Box::new(i)
588 }
589 fn view(&self) -> Box<dyn WidgetExt> {
590 let mut i = output::Output::default();
591 let val = format!("{:?}", *self);
592 i.set_value(&val);
593 Box::new(i)
594 }
595}
596
597impl FltkForm for u16 {
598 fn generate(&self) -> Box<dyn WidgetExt> {
599 let mut i = input::IntInput::default();
600 let val = format!("{:?}", *self);
601 i.set_value(&val);
602 Box::new(i)
603 }
604 fn view(&self) -> Box<dyn WidgetExt> {
605 let mut i = output::Output::default();
606 let val = format!("{:?}", *self);
607 i.set_value(&val);
608 Box::new(i)
609 }
610}
611
612impl FltkForm for String {
613 fn generate(&self) -> Box<dyn WidgetExt> {
614 let mut i = input::Input::default();
615 i.set_value(self);
616 Box::new(i)
617 }
618 fn view(&self) -> Box<dyn WidgetExt> {
619 let mut i = output::Output::default();
620 i.set_value(self);
621 Box::new(i)
622 }
623}
624
625impl FltkForm for &str {
626 fn generate(&self) -> Box<dyn WidgetExt> {
627 let i = frame::Frame::default().with_label(self);
628 Box::new(i)
629 }
630 fn view(&self) -> Box<dyn WidgetExt> {
631 let i = frame::Frame::default().with_label(self);
632 Box::new(i)
633 }
634}
635
636impl FltkForm for bool {
637 fn generate(&self) -> Box<dyn WidgetExt> {
638 let mut i = button::CheckButton::default().with_align(enums::Align::Left);
639 i.set_value(*self);
640 i.clear_visible_focus();
641 Box::new(i)
642 }
643 fn view(&self) -> Box<dyn WidgetExt> {
644 let mut i = output::Output::default().with_align(enums::Align::Left);
645 i.set_value(&format!("{}", *self));
646 i.clear_visible_focus();
647 Box::new(i)
648 }
649}
650
651impl<T> FltkForm for Vec<T>
652where
653 T: FltkForm,
654{
655 fn generate(&self) -> Box<dyn WidgetExt> {
656 let mut g = group::Pack::default();
657 g.set_spacing(5);
658 for v in self.iter() {
659 let mut w = v.generate();
660 w.set_align(enums::Align::Left);
661 w.set_size(w.w(), 30);
662 }
663 g.end();
664 Box::new(g)
665 }
666 fn view(&self) -> Box<dyn WidgetExt> {
667 let mut g = group::Pack::default();
668 g.set_spacing(5);
669 for v in self.iter() {
670 let mut w = v.view();
671 w.set_align(enums::Align::Left);
672 w.set_size(w.w(), 30);
673 }
674 g.end();
675 Box::new(g)
676 }
677}
678
679#[allow(clippy::borrowed_box)]
680fn rename_prop_(wid: &Box<dyn WidgetExt>, prop: &str, new_name: &str) {
681 if let Some(grp) = wid.as_group() {
682 for mut child in grp.into_iter() {
683 if child.label() == prop {
684 child.set_label(new_name);
685 }
686 }
687 }
688}
689
690#[allow(clippy::borrowed_box)]
691fn get_prop_(wid: &Box<dyn WidgetExt>, prop: &str) -> Option<String> {
692 if let Some(grp) = wid.as_group() {
693 for child in grp.into_iter() {
694 if child.label() == prop {
695 let ptr = child.as_widget_ptr();
696 let val = if is_ptr_of::<input::Input>(ptr) {
697 1
698 } else if is_ptr_of::<button::CheckButton>(ptr) {
699 2
700 } else if is_ptr_of::<menu::Choice>(ptr) {
701 3
702 } else {
703 4
704 };
705 match val {
706 1 => {
707 let inp = input::Input::from_dyn_widget_ptr(ptr as _).unwrap();
708 return Some(inp.value());
709 }
710 2 => {
711 let inp = button::CheckButton::from_dyn_widget_ptr(ptr as _).unwrap();
712 return Some(format!("{}", inp.value()));
713 }
714 3 => {
715 let choice = menu::Choice::from_dyn_widget_ptr(ptr as _).unwrap();
716 return choice.choice();
717 }
718 _ => {
719 let wid = widget::Widget::from_dyn_widget_ptr(ptr as _).unwrap();
720 return Some(wid.label());
721 }
722 }
723 }
724 }
725 None
726 } else {
727 None
728 }
729}
730
731#[allow(clippy::borrowed_box)]
732fn set_prop_(wid: &Box<dyn WidgetExt>, prop: &str, value: &str) -> Result<(), FltkFormError> {
733 let mut found = false;
734 if let Some(grp) = wid.as_group() {
735 for child in grp.into_iter() {
736 if child.label() == prop {
737 found = true;
738 let ptr = child.as_widget_ptr();
739 let val = if is_ptr_of::<input::Input>(ptr) {
740 1
741 } else if is_ptr_of::<button::CheckButton>(ptr) {
742 2
743 } else if is_ptr_of::<menu::Choice>(ptr) {
744 3
745 } else {
746 4
747 };
748 match val {
749 1 => {
750 let mut inp = input::Input::from_dyn_widget_ptr(ptr as _).unwrap();
751 inp.set_value(value);
752 }
753 2 => {
754 let mut inp = button::CheckButton::from_dyn_widget_ptr(ptr as _).unwrap();
755 let v = value == "true";
756 inp.set_value(v);
757 }
758 3 => {
759 let mut choice = menu::Choice::from_dyn_widget_ptr(ptr as _).unwrap();
760 let idx = choice.find_index(value);
761 choice.set_value(idx);
762 }
763 _ => (),
764 }
765 }
766 }
767 }
768 if !found {
769 return Err(FltkFormError::Internal(
770 FltkFormErrorKind::PropertyInexistent,
771 ));
772 }
773 Ok(())
774}
775
776#[allow(clippy::borrowed_box)]
777fn get_props_(wid: &Box<dyn WidgetExt>) -> HashMap<String, String> {
778 let mut temp = HashMap::new();
779 if let Some(grp) = wid.as_group() {
780 for child in grp.into_iter() {
781 if !child.label().is_empty() {
782 if let Some(prop) = get_prop_(wid, &child.label()) {
783 temp.insert(child.label().clone(), prop);
784 }
785 }
786 }
787 }
788 temp
789}
790
791#[allow(clippy::borrowed_box)]
792fn get_widget_(wid: &Box<dyn WidgetExt>, prop: &str) -> Option<Box<dyn WidgetExt>> {
793 if let Some(grp) = wid.as_group() {
794 for child in grp.into_iter() {
795 if child.label() == prop {
796 return Some(Box::new(child));
797 }
798 }
799 None
800 } else if wid.label() == prop {
801 let wid: widget::Widget = widget::Widget::from_dyn_widget_ptr(wid.as_widget_ptr()).unwrap();
802 Some(Box::new(wid))
803 } else {
804 None
805 }
806}
807
808pub trait HasProps {
809 fn get_prop(&self, prop: &str) -> Option<String>;
810 fn set_prop(&mut self, prop: &str, value: &str) -> Result<(), FltkFormError>;
811 fn get_props(&self) -> HashMap<String, String>;
812 fn rename_prop(&mut self, prop: &str, new_name: &str);
813 fn get_widget(&self, prop: &str) -> Option<Box<dyn WidgetExt>>;
814}
815
816impl HasProps for Box<dyn WidgetExt> {
817 fn get_prop(&self, prop: &str) -> Option<String> {
818 get_prop_(self, prop)
819 }
820 fn set_prop(&mut self, prop: &str, value: &str) -> Result<(), FltkFormError> {
821 set_prop_(self, prop, value)
822 }
823 fn get_props(&self) -> HashMap<String, String> {
824 get_props_(self)
825 }
826 fn rename_prop(&mut self, prop: &str, new_name: &str) {
827 rename_prop_(self, prop, new_name);
828 }
829 fn get_widget(&self, prop: &str) -> Option<Box<dyn WidgetExt>> {
830 get_widget_(self, prop)
831 }
832}