tui_markup_renderer/
markup_parser.rs

1use crossterm::{
2    event::{self, Event as CEvent, KeyCode, KeyEvent},
3    terminal::{disable_raw_mode, enable_raw_mode},
4};
5use log::{info, warn};
6#[allow(unused_imports)]
7use std::borrow::Borrow;
8use std::{
9    collections::HashMap,
10    fmt,
11    fs::File,
12    io::BufReader,
13    panic,
14    path::Path,
15    rc::Rc,
16    sync::mpsc,
17    thread,
18    time::{Duration, Instant},
19    vec::Vec,
20    {borrow::BorrowMut, cell::RefCell},
21};
22use tui::{
23    backend::Backend,
24    layout::{Alignment, Constraint, Direction, Layout, Rect},
25    style::{Modifier, Style},
26    text::{Span, Spans},
27    widgets::{Block, BorderType, Borders, Clear, Paragraph, Wrap},
28    Frame, Terminal,
29};
30use xml::reader::{EventReader, XmlEvent};
31
32use crate::{
33    actions::{ActionsStorage, IActionsStorage},
34    event_response::EventResponse,
35    markup_element::MarkupElement,
36    storage::{IRendererStorage, RendererStorage},
37    styles::{IStylesStorage, StylesStorage},
38    utils::{color_from_str, extract_attribute, modifier_from_str, modifiers_from_str},
39};
40
41////////////// END LIBS //////////////
42
43type ActionCallback = fn(HashMap<String, String>, Option<MarkupElement>) -> EventResponse;
44
45pub enum Event<I> {
46    Input(I),
47    Tick,
48}
49
50const WIDGET_NAMES: &[&str] = &["p", "button"];
51
52/**
53 * To use specific features you can use the macro:
54 *   - #[cfg(feature = "test")]
55 * also you can negate something:
56 *   - #[cfg(not(test))]
57 * To enable something only for test use:
58 *   - #[cfg(test)]
59 * To allow make a struct printable for debug use:
60 *   - #[derive(Debug)]
61 */
62
63pub struct MarkupParser<B: Backend> {
64    pub path: String,
65    pub failed: bool,
66    pub error: Option<String>,
67    pub root: Option<Rc<RefCell<MarkupElement>>>,
68    pub storage: Option<Rc<RefCell<RendererStorage<B>>>>,
69    pub current: i32,
70    pub indexed_elements: Vec<MarkupElement>,
71    pub contexts: Vec<(String, Vec<MarkupElement>)>,
72    pub state: HashMap<String, String>,
73    pub actions: ActionsStorage,
74    pub global_styles: StylesStorage,
75    fingerprint: String,
76}
77
78impl<B: Backend> fmt::Debug for MarkupParser<B> {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        let mut r = f.debug_struct("MarkupParser");
81        r.field("failed", &self.failed);
82        r.field("root", &self.root);
83        r.finish()
84    }
85}
86
87impl<B: Backend> MarkupParser<B> {
88    // Constructor
89    pub fn new(
90        path: String,
91        optional_storage: Option<RendererStorage<B>>,
92        initial_state: Option<HashMap<String, String>>,
93    ) -> MarkupParser<B> {
94        // env_logger::init();
95        if !Path::new(&path).exists() {
96            panic!("Markup file does not exist at {}", &path);
97        }
98        let file = File::open(&path).unwrap();
99        let buffer = BufReader::new(file);
100        let parser = EventReader::new(buffer);
101        let storage = optional_storage.unwrap_or(RendererStorage::new());
102        let mut root_node: Option<Rc<RefCell<MarkupElement>>> = None;
103        let mut current_node: Option<Rc<RefCell<MarkupElement>>> = None;
104        let mut parent_node: Option<Rc<RefCell<MarkupElement>>> = None;
105        let mut global_styles = StylesStorage::new();
106        let mut indexed_elements = vec![];
107        let mut cntr = 0;
108        let mut parent_count = 0;
109        let mut actions = ActionsStorage::new();
110        for e in parser {
111            cntr += 1;
112            match e {
113                Ok(XmlEvent::StartElement {
114                    name, attributes, ..
115                }) => {
116                    let valid_name = name.local_name.clone();
117                    let mut attrs = HashMap::new();
118                    for attr in attributes {
119                        attrs.insert(attr.name.local_name, attr.value);
120                    }
121
122                    // TO DO: prepare default attributes depending on the node type
123                    if valid_name.eq("tab-item") {
124                        if !attrs.contains_key("action") {
125                            attrs.insert("action".to_string(), "__change_tab".to_string());
126                        }
127                        if !attrs.contains_key("index") {
128                            attrs.insert("index".to_string(), format!("{}", cntr));
129                        }
130                        if !attrs.contains_key("tabs-id") && parent_node.is_some() {
131                            let pn = MarkupParser::<B>::get_element(parent_node.clone());
132                            let gpn = MarkupParser::<B>::get_element(pn.parent_node);
133                            attrs.insert("tabs-id".to_string(), gpn.id);
134                        }
135                    }
136                    if valid_name.eq("tab-content")
137                        && !attrs.contains_key("tabs-id")
138                        && parent_node.is_some()
139                    {
140                        let pn = MarkupParser::<B>::get_element(parent_node.clone());
141                        let gpn = MarkupParser::<B>::get_element(pn.parent_node);
142                        attrs.insert("tabs-id".to_string(), gpn.id);
143                    }
144
145                    let unknown_id = format!("unknown_elm_{}", cntr);
146                    let _id = attrs.get("id").unwrap_or(&unknown_id);
147                    let unknown_idx = "-1".to_owned();
148
149                    let posible_elm_idx = if valid_name.eq(&"tab-item") {
150                        parent_count * 10 + cntr
151                    } else {
152                        -1
153                    };
154                    let elm_idx = if posible_elm_idx > -1 {
155                        posible_elm_idx
156                    } else {
157                        attrs
158                            .get("index")
159                            .unwrap_or(&unknown_idx)
160                            .parse::<i32>()
161                            .unwrap_or(posible_elm_idx)
162                    };
163
164                    let partial = MarkupElement {
165                        deep: if parent_node.is_some() {
166                            MarkupParser::<B>::get_element(parent_node.clone()).deep + 1
167                        } else {
168                            0
169                        },
170                        id: String::from(_id),
171                        text: None,
172                        order: elm_idx,
173                        name: name.local_name.to_lowercase(),
174                        attributes: attrs,
175                        children: vec![],
176                        parent_node: parent_node.clone(),
177                        dependencies: vec![],
178                    };
179
180                    current_node = Some(Rc::new(RefCell::new(partial.clone())));
181
182                    let is_root_defined = root_node.clone().as_ref().is_some();
183                    if !is_root_defined {
184                        root_node = current_node.clone();
185                    }
186
187                    if parent_node.is_some() {
188                        let parent = parent_node.clone();
189                        let parent = parent.unwrap();
190                        let parent = parent.as_ref();
191                        let mut parent = parent.borrow_mut();
192                        let son = current_node.clone().unwrap();
193                        parent.children.push(son);
194                    }
195
196                    if elm_idx != -1 {
197                        indexed_elements.push(partial);
198                    }
199
200                    parent_node = current_node.clone();
201                    parent_count = elm_idx;
202                }
203                Ok(XmlEvent::Characters(ref r)) => {
204                    let node = current_node.clone();
205                    let node = node.unwrap();
206                    let node = node.as_ref();
207                    let mut node = node.borrow_mut();
208                    node.text = Some(String::from(r.trim()));
209                }
210                Ok(XmlEvent::EndElement { .. }) => {
211                    let p = MarkupParser::<B>::get_element(parent_node.clone());
212                    let q = p.clone();
213                    if q.name.eq("styles") {
214                        global_styles = MarkupParser::<B>::process_styles(q);
215                    }
216                    parent_node = p.parent_node;
217                }
218                Ok(XmlEvent::EndDocument { .. }) => {}
219                Err(e) => {
220                    return MarkupParser {
221                        path,
222                        failed: true,
223                        error: Some(e.msg().to_string()),
224                        root: None,
225                        storage: None,
226                        current: -1,
227                        indexed_elements: vec![],
228                        contexts: vec![],
229                        actions: ActionsStorage::new(),
230                        state: HashMap::new(),
231                        global_styles: StylesStorage::new(),
232                        fingerprint: String::from("<empty>"),
233                    };
234                }
235                _ => {}
236            };
237        }
238        indexed_elements.sort_by(|e1, e2| e1.order.cmp(&e2.order));
239        let state = initial_state.unwrap_or(HashMap::new());
240        actions.add_action("__change_tab".to_string(), |old_state, node_wrapper| {
241            let mut state = old_state;
242            if let Some(node) = node_wrapper {
243                let key = node.attributes.get("tabs-id").unwrap();
244                state.insert(format!("{}:index", key), node.id.clone());
245            }
246            EventResponse::CLEANFOCUS(state)
247        });
248        MarkupParser {
249            path,
250            failed: false,
251            error: None,
252            root: root_node,
253            storage: Some(Rc::new(RefCell::new(storage))),
254            current: -1,
255            indexed_elements,
256            contexts: vec![],
257            actions,
258            state,
259            global_styles,
260            fingerprint: String::from("<empty>"),
261        }
262    }
263
264    // Instance methods
265    fn draw_block(
266        &self,
267        child: &MarkupElement,
268        _area: Rect,
269        focus: bool,
270        active: bool,
271        base_styles: Style,
272    ) -> Block {
273        let styles = MarkupParser::<B>::get_styles(&child.clone(), focus, active);
274        let styles = base_styles.patch(styles);
275        let title = extract_attribute(child.attributes.clone(), "title");
276        let border = extract_attribute(child.attributes.clone(), "border");
277        let border = MarkupParser::<B>::get_border(border.as_str());
278        let block = Block::default().title(title).style(styles).borders(border);
279        block
280    }
281
282    fn draw_paragraph(
283        &self,
284        child: &MarkupElement,
285        area: Rect,
286        focus: bool,
287        active: bool,
288        base_styles: Style,
289    ) -> Paragraph {
290        let styles = MarkupParser::<B>::get_styles(&child.clone(), focus, active);
291        let styles = base_styles.patch(styles);
292        let alignment = MarkupParser::<B>::get_alignment(&child.clone());
293        let block = self.draw_block(&child.clone(), area, focus, active, base_styles);
294        let p = Paragraph::new(child.text.clone().unwrap_or(String::from("")))
295            .style(styles)
296            .alignment(alignment)
297            .wrap(Wrap { trim: true })
298            .block(block);
299        p
300    }
301
302    fn draw_button(
303        &self,
304        child: &MarkupElement,
305        area: Rect,
306        focus: bool,
307        active: bool,
308        base_styles: Style,
309    ) -> Paragraph {
310        let styles = MarkupParser::<B>::get_styles(&child.clone(), focus, active);
311        let styles = base_styles.patch(styles);
312        let mut elcnt = usize::from(area.height);
313        if area.height > 0 {
314            elcnt = usize::from(area.height / 2 - 1);
315        }
316        let text = child.text.clone().unwrap_or(String::from(""));
317        let mut lns_cntt = vec![];
318        for _i in 0..elcnt {
319            lns_cntt.push(Spans::from(""));
320        }
321        lns_cntt.push(Spans::from(Span::styled(
322            text,
323            if focus {
324                styles.add_modifier(Modifier::UNDERLINED)
325            } else {
326                styles
327            },
328        )));
329        let block = Block::default()
330            .style(styles)
331            .borders(Borders::ALL)
332            .border_type(BorderType::Rounded);
333        let p = Paragraph::new(lns_cntt)
334            .style(styles)
335            .alignment(Alignment::Center)
336            .block(block);
337        p
338    }
339
340    fn draw_dialog(
341        &self,
342        child: &MarkupElement,
343        _area: Rect,
344        focus: bool,
345        active: bool,
346        base_styles: Style,
347    ) -> Block {
348        let styles = MarkupParser::<B>::get_styles(&child.clone(), focus, active);
349        let styles = base_styles.patch(styles);
350        let block = Block::default()
351            .style(styles)
352            .borders(Borders::ALL)
353            .border_type(BorderType::Double);
354        block
355    }
356
357    fn draw_tab_borders(
358        &self,
359        _child: &MarkupElement,
360        _area: Rect,
361        _focus: bool,
362        _active: bool,
363        _base_styles: Style,
364    ) -> Block {
365        let block = Block::default()
366            .borders(Borders::BOTTOM)
367            .border_type(BorderType::Rounded);
368        block
369    }
370
371    fn draw_tab_item(
372        &self,
373        child: &MarkupElement,
374        _area: Rect,
375        _focus: bool,
376        _active: bool,
377        base_styles: Style,
378    ) -> Paragraph {
379        /*
380        let styles = if focus {
381            Style::default()
382                .fg(Color::Gray)
383                .add_modifier(Modifier::DIM)
384        } else {
385            Style::default()
386                .fg(Color::DarkGray)
387        };
388        let styles = if active {
389            styles.add_modifier(Modifier::BOLD)
390        } else {
391            styles
392        };
393
394        let styles = styles.patch(base_styles);
395        */
396        let styles = base_styles;
397        let text = child.text.clone();
398        let text = text.unwrap_or("Tab".to_string());
399        let block = Block::default()
400            .style(styles)
401            .borders(Borders::TOP | Borders::RIGHT | Borders::LEFT)
402            .border_type(BorderType::Rounded);
403        let p = Paragraph::new(text)
404            .style(styles)
405            .alignment(Alignment::Center)
406            .block(block);
407        p
408    }
409
410    fn go_next(&mut self) -> i32 {
411        let size = i32::try_from(self.indexed_elements.len()).unwrap() - 2;
412        if self.current > size {
413            self.current = -1;
414        } else {
415            self.current += 1;
416        }
417        self.current
418    }
419
420    fn go_prev(&mut self) -> i32 {
421        let size = i32::try_from(self.indexed_elements.len()).unwrap() - 1;
422        if self.current < 0 {
423            self.current = size;
424        } else {
425            self.current -= 1;
426        }
427        self.current
428    }
429
430    fn do_action(&mut self) -> EventResponse {
431        if self.current > -1 {
432            let current = self.indexed_elements[self.current as usize].clone();
433            let action = extract_attribute(current.attributes.clone(), "action");
434            if self.actions.has_action(action.clone()) {
435                info!("Executing {}", action);
436                let new_state = self
437                    .actions
438                    .execute(action, self.state.clone(), Some(current));
439                if let Some(event_response) = new_state {
440                    return event_response;
441                }
442            }
443        }
444        EventResponse::NOOP
445    }
446
447    fn get_element_styles(&self, node: &MarkupElement, focus: bool, active: bool) -> Style {
448        let name = node.name.clone();
449        let parent = node.parent_node.clone();
450        let parent_styles = if let Some(nref) = parent {
451            let parent = MarkupParser::<B>::extract_element(&nref);
452            self.get_element_styles(&parent, focus, active)
453        } else {
454            Style::default()
455        };
456        let rulename = if focus {
457            format!("{}{}", name, if focus { ":focus" } else { "" })
458        } else if active {
459            format!("{}{}", name, if active { ":active" } else { "" })
460        } else {
461            name
462        };
463        let base_styles = parent_styles.patch(self.global_styles.get_rule(rulename));
464        let rulename = format!("#{}", node.id);
465        let elm_styles = self.global_styles.get_rule(rulename);
466
467        base_styles.patch(elm_styles)
468    }
469
470    fn draw_element(&mut self, frame: &mut Frame<B>, area: Rect, node: &MarkupElement) -> bool {
471        let name = node.name.clone();
472        let name = name.as_str();
473        let storage = self.storage.clone();
474        let storage = storage.unwrap();
475        let storage = storage.as_ref();
476        let storage = storage.borrow_mut();
477        if storage.has_component(name) {
478            storage.render(name, frame);
479            true
480        } else {
481            let mut cid = "".to_owned();
482            if self.current > -1 {
483                cid = self.indexed_elements[self.current as usize].id.clone();
484            }
485            let is_focused_node = node.id.eq(&cid);
486            let is_active_tab = if node.parent_node.is_some() {
487                let parent_node: MarkupElement =
488                    node.parent_node.clone().unwrap().as_ref().borrow().clone();
489                let parent_id = parent_node.id;
490                let state_elm = format!("{}:index", parent_id);
491                let current = self.state.get(&state_elm);
492                if let Some(current) = current {
493                    let currval = current;
494                    currval.eq(&node.id)
495                } else {
496                    false
497                }
498            } else {
499                false
500            };
501            let base_styles = self.get_element_styles(node, is_focused_node, is_active_tab);
502            match name {
503                "container" | "block" => {
504                    let widget = self.draw_block(node, area, is_focused_node, false, base_styles);
505                    frame.render_widget(Clear, area);
506                    frame.render_widget(widget, area);
507                    true
508                }
509                "tabs-borders" => {
510                    let widget =
511                        self.draw_tab_borders(node, area, is_focused_node, false, base_styles);
512                    frame.render_widget(widget, area);
513                    true
514                }
515                "p" => {
516                    let widget = self.draw_paragraph(node, area, is_focused_node, false, base_styles);
517                    frame.render_widget(Clear, area);
518                    frame.render_widget(widget, area);
519                    true
520                }
521                "tabs" => {
522                    let id = format!("{}:index", node.id.clone());
523                    if self.state.get(&id).is_none() {
524                        let mut state = self.state.clone();
525                        let thdr = node.children.first();
526                        if let Some(wrapped_value) = thdr {
527                            let plain_elm = MarkupParser::<B>::extract_element(wrapped_value);
528                            let frst = plain_elm.children.first();
529                            if let Some(first) = frst {
530                                let chld = MarkupParser::<B>::extract_element(first);
531                                state.insert(id, chld.id);
532                            }
533                        }
534                        self.state = state;
535                    }
536                    true
537                }
538                "tab-item" => {
539                    let widget =
540                        self.draw_tab_item(node, area, is_focused_node, is_active_tab, base_styles);
541                    frame.render_widget(Clear, area);
542                    frame.render_widget(widget, area);
543                    true
544                }
545                "tab-content" => {
546                    let default_val = "unknown".to_string();
547                    let show_flag = node.attributes.get("tabs-id").unwrap_or(&default_val);
548                    let show_flag = format!("{}:index", show_flag);
549                    let state_value = self.state.get(&show_flag).unwrap_or(&default_val);
550                    let me = node.attributes.get("for").unwrap_or(&default_val);
551                    if state_value.eq(me) {
552                        let widget = self.draw_block(node, area, is_focused_node, false, base_styles);
553                        frame.render_widget(Clear, area);
554                        frame.render_widget(widget, area);
555                        return true;
556                    }
557                    false
558                }
559                "dialog" => {
560                    let new_node = node.clone();
561                    let show_flag = extract_attribute(new_node.clone().attributes, "show");
562                    let default_val = "false".to_string();
563                    let state_value = self.state.get(&show_flag).unwrap_or(&default_val);
564                    if state_value.eq(&"true".to_string()) {
565                        self.add_context(node);
566                        let widget =
567                            self.draw_dialog(&new_node, area, is_focused_node, false, base_styles);
568                        frame.render_widget(Clear, area);
569                        frame.render_widget(widget, area);
570                        return true;
571                    } else {
572                        self.remove_context(node);
573                    }
574                    false
575                }
576                "button" => {
577                    let mut new_area = area;
578                    new_area.height = if new_area.height > 3 {
579                        3
580                    } else {
581                        new_area.height
582                    };
583                    let widget = self.draw_button(node, new_area, is_focused_node, false, base_styles);
584                    frame.render_widget(Clear, area);
585                    frame.render_widget(widget, new_area);
586                    true
587                }
588                _ => {
589                    let widget = Block::default();
590                    frame.render_widget(Clear, area);
591                    frame.render_widget(widget, area);
592                    true
593                }
594            }
595        }
596    }
597
598    fn process_block(
599        &self,
600        frame: &mut Frame<B>,
601        node: &MarkupElement,
602        dependency: Option<MarkupElement>,
603        place: Option<Rect>,
604        _margin: Option<u16>, // remove or transform in padding?
605        count: usize,
606    ) -> Vec<(Rect, MarkupElement)> {
607        let current = node.clone();
608        let split_space = place.unwrap_or(frame.size());
609        let border_value = extract_attribute(current.attributes.clone(), "border");
610        let mut res: Vec<(Rect, MarkupElement)> = vec![];
611        let mut constraints: Vec<Constraint> = vec![];
612        let id = extract_attribute(current.attributes.clone(), "id");
613        let mut widgets_info: Vec<(usize, MarkupElement)> = vec![];
614        let mut children_nodes: Vec<(usize, MarkupElement)> = vec![];
615        res.push((place.unwrap_or(frame.size()), current));
616
617        info!(target: "MarkupParser",
618            "{}Container #{}[[{:?}]]",
619            "".repeat(count * 2),
620            id,
621            split_space.clone(),
622        );
623
624        // println!("\n\n==> {}[{:?}]: {:?}\n\n", id.clone(), current.attributes.clone(), split_space.clone());
625
626        for (position, base_child) in node.children.iter().enumerate() {
627            let child = base_child.as_ref().borrow();
628            let constraint = extract_attribute(child.clone().attributes, "constraint");
629            constraints.push(MarkupParser::<B>::get_constraint(constraint));
630            let child_name = child.clone().name;
631
632            if MarkupParser::<B>::is_widget(child_name.as_str()) {
633                widgets_info.push((position, child.clone()));
634            } else {
635                children_nodes.push((position, child.clone()));
636            }
637        }
638
639        let new_margin = if border_value.eq("") || border_value.eq("none") {
640            0 // margin.unwrap_or(0)
641        } else {
642            1
643        };
644        let layout = Layout::default()
645            .direction(Direction::Horizontal)
646            .margin(new_margin)
647            .constraints(constraints.clone().as_ref());
648        let chunks = layout.split(split_space);
649
650        for (cntr, base_child) in children_nodes.iter() {
651            let counter = *cntr;
652            let mut child = base_child.clone();
653            if dependency.is_some() {
654                child.dependencies.push(dependency.clone().unwrap().id);
655            }
656            let partial_res = self.process_node(
657                frame,
658                &child,
659                dependency.clone(),
660                Some(chunks[counter]),
661                None,
662                count + 1,
663            );
664            for pair in partial_res.iter() {
665                res.push((pair.0, pair.1.clone()));
666            }
667        }
668
669        for (cntr, widget_info) in widgets_info.iter() {
670            let counter = *cntr;
671            let mut mkp_elm = widget_info.clone();
672            if dependency.is_some() {
673                let did = dependency.clone().unwrap().id;
674                if !mkp_elm.dependencies.contains(&did) {
675                    mkp_elm.dependencies.push(did);
676                }
677            }
678            res.push((chunks[counter], mkp_elm));
679        }
680
681        res
682    }
683
684    fn process_layout(
685        &self,
686        frame: &mut Frame<B>,
687        node: &MarkupElement,
688        dependency: Option<MarkupElement>,
689        place: Option<Rect>,
690        margin: Option<u16>,
691        count: usize,
692    ) -> Vec<(Rect, MarkupElement)> {
693        let current = node.clone();
694        let split_space = place.unwrap_or(frame.size());
695        let direction = MarkupParser::<B>::get_direction(node);
696        let id = extract_attribute(current.attributes.clone(), "id");
697        info!(target: "MarkupParser",
698            "{}Layout #{}[{}]({} children) [[{:?}]]",
699            " ".repeat(count * 2),
700            id,
701            current.attributes.get("direction").unwrap(),
702            node.children.len(),
703            split_space.clone(),
704        );
705        let mut res: Vec<(Rect, MarkupElement)> = vec![];
706        let constraints: Vec<Constraint> = MarkupParser::<B>::get_constraints(node.clone());
707        info!(target: "MarkupParser", "{}  ::>{:?}", "".repeat(count * 2), constraints);
708
709        let layout = Layout::default()
710            .direction(direction)
711            .margin(margin.unwrap_or(0))
712            .constraints(constraints.as_ref());
713
714        let chunks = layout.split(split_space);
715
716        for (position, base_child) in node.children.iter().enumerate() {
717            let mut child = base_child.as_ref().borrow().clone();
718            if dependency.is_some() {
719                child.dependencies.push(dependency.clone().unwrap().id);
720            }
721            let partial_res = self.process_node(
722                frame,
723                &child,
724                dependency.clone(),
725                Some(chunks[position]),
726                Some(1),
727                count + 1,
728            );
729            for pair in partial_res.iter() {
730                let mut mkp_elm = pair.1.clone();
731                if dependency.is_some() {
732                    let did = dependency.clone().unwrap().id;
733                    if !mkp_elm.dependencies.contains(&did) {
734                        mkp_elm.dependencies.push(did);
735                    }
736                }
737                res.push((pair.0, mkp_elm));
738            }
739        }
740
741        res
742    }
743
744    fn process_other(
745        &self,
746        frame: &mut Frame<B>,
747        node: &MarkupElement,
748        depends_on: Option<MarkupElement>,
749        place: Option<Rect>,
750        margin: Option<u16>,
751        count: usize,
752    ) -> Option<Vec<(Rect, MarkupElement)>> {
753        let mut current = node.clone();
754        /*
755        let mut parent_id = String::new();
756        if current.parent_node.is_some() {
757            parent_id = current
758                .parent_node
759                .clone()
760                .unwrap()
761                .as_ref()
762                .borrow()
763                .id
764                .clone();
765        }
766        */
767        let id = extract_attribute(current.attributes.clone(), "id");
768        let mut split_space = place.unwrap_or(frame.size());
769        let mut child_space = split_space;
770        let mut res: Vec<(Rect, MarkupElement)> = vec![];
771        let mut subsequents: Vec<(Rect, MarkupElement)> = vec![];
772        let mut dependency = depends_on;
773        let mut process_children = true;
774
775        info!(target: "MarkupParser",
776            "{}Other #{}[[{:?}]]",
777            "".repeat(count * 2),
778            id,
779            split_space.clone(),
780        );
781
782        let cname = node.name.as_str();
783        match cname {
784            "tabs" => {
785                let header_size = 3;
786                let vertical_layout = Layout::default()
787                    .direction(Direction::Vertical)
788                    .margin(margin.unwrap_or(0))
789                    .constraints(
790                        vec![
791                            Constraint::Length(header_size),
792                            Constraint::Length(split_space.height - header_size),
793                        ]
794                        .as_ref(),
795                    );
796                let vertical_chunks = vertical_layout.split(split_space);
797                for (pos, chld) in node.children.iter().enumerate() {
798                    let elm = chld.as_ref().borrow().clone();
799                    let child_space = vertical_chunks[pos];
800                    if pos > 0 {
801                        let partial_res = self.process_node(
802                            frame,
803                            &elm,
804                            None,
805                            Some(child_space),
806                            Some(1),
807                            count + 1,
808                        );
809                        for pair in partial_res.iter() {
810                            subsequents.push(pair.clone());
811                        }
812                    } else {
813                        let elm = chld.as_ref().borrow().clone();
814                        let start_x = vertical_chunks[0].x + 1;
815                        let start_y = vertical_chunks[0].y;
816                        let line = MarkupElement {
817                            id: "line_unk".to_string(),
818                            attributes: HashMap::new(),
819                            parent_node: None,
820                            children: vec![],
821                            name: "tabs-borders".to_string(),
822                            text: None,
823                            deep: 0,
824                            dependencies: vec![],
825                            order: -1,
826                        };
827                        let tab_width: u16 = 8;
828                        subsequents.push((vertical_chunks[0], line));
829                        for (_idx, chld) in elm.children.iter().enumerate() {
830                            let idx: u16 = _idx as u16;
831                            let chldelm = chld.as_ref().clone().into_inner();
832                            let order = 10 + (idx as i32);
833                            let btn = MarkupElement {
834                                id: chldelm.id.clone(),
835                                attributes: chldelm.attributes.clone(),
836                                parent_node: elm.parent_node.clone(),
837                                children: vec![],
838                                name: chldelm.name,
839                                text: chldelm.text.clone(),
840                                deep: chldelm.deep + 1,
841                                dependencies: vec![],
842                                order,
843                            };
844                            let place = Rect::new(
845                                start_x + (idx * tab_width) + (idx),
846                                start_y,
847                                tab_width + 1,
848                                2,
849                            );
850                            subsequents.push((place, btn));
851                        }
852                    }
853                }
854                process_children = false;
855            }
856            "tab-content" => {
857                let vertical_layout = Layout::default()
858                    .direction(Direction::Vertical)
859                    .margin(margin.unwrap_or(0))
860                    .constraints(
861                        vec![Constraint::Percentage(10), Constraint::Percentage(90)].as_ref(),
862                    );
863                let vertical_chunks = vertical_layout.split(split_space);
864                split_space = vertical_chunks[1];
865                dependency = Some(node.clone());
866            }
867            "dialog" => {
868                let horizontal_layout = Layout::default()
869                    .direction(Direction::Horizontal)
870                    .margin(margin.unwrap_or(0))
871                    .constraints(
872                        vec![
873                            Constraint::Percentage(34),
874                            Constraint::Percentage(32),
875                            Constraint::Percentage(34),
876                        ]
877                        .as_ref(),
878                    );
879                let horizontal_chunks = horizontal_layout.split(frame.size());
880
881                let vertical_layout = Layout::default()
882                    .direction(Direction::Vertical)
883                    .margin(margin.unwrap_or(0))
884                    .constraints(
885                        vec![
886                            Constraint::Percentage(31),
887                            Constraint::Percentage(34),
888                            Constraint::Percentage(31),
889                        ]
890                        .as_ref(),
891                    );
892                let vertical_chunks = vertical_layout.split(horizontal_chunks[1]);
893
894                split_space = vertical_chunks[1];
895                let dialog_space = vertical_chunks[1];
896
897                let dialog_parts = Layout::default()
898                    .direction(Direction::Vertical)
899                    .margin(1)
900                    .constraints(
901                        vec![Constraint::Percentage(80), Constraint::Percentage(20)].as_ref(),
902                    );
903                let dialog_chunks = dialog_parts.split(dialog_space);
904
905                let action = extract_attribute(node.attributes.clone(), "action");
906                let btns = extract_attribute(node.attributes.clone(), "buttons");
907                let btns: Vec<String> = btns.split('|').map(String::from).collect();
908                let btn_constraints: Vec<Constraint> = btns
909                    .clone()
910                    .iter()
911                    .map(|_| Constraint::Percentage((100 / btns.len()) as u16))
912                    .collect();
913
914                let buttons_layout = Layout::default()
915                    .direction(Direction::Horizontal)
916                    .constraints(btn_constraints.as_ref());
917                child_space = dialog_chunks[0];
918                let button_chunks = buttons_layout.split(dialog_chunks[1]);
919
920                for (elm_idx, btn) in btns.iter().enumerate() {
921                    let btn_id = format!("{}_btn_{}", node.id, btn);
922                    let btn_action = if !action.is_empty() {
923                        action.clone()
924                    } else {
925                        format!("on_{}", btn_id)
926                    };
927                    let btn_elm = MarkupElement {
928                        deep: node.deep + 1,
929                        id: btn_id.clone(),
930                        text: Some(String::from(btn)),
931                        order: elm_idx as i32,
932                        name: String::from("button"),
933                        attributes: HashMap::from([
934                            ("id".to_string(), btn_id.clone()),
935                            ("action".to_string(), btn_action),
936                            ("index".to_string(), format!("{}", elm_idx)),
937                        ]),
938                        children: vec![],
939                        parent_node: Some(Rc::new(RefCell::new(node.clone()))),
940                        dependencies: vec![node.id.clone()],
941                    };
942                    let btn_desc = Rc::new(RefCell::new(btn_elm.clone()));
943                    current.children.push(btn_desc);
944                    subsequents.push((button_chunks[elm_idx], btn_elm));
945                }
946                dependency = Some(node.clone());
947            }
948            _ => {
949                let layout = Layout::default()
950                    .direction(Direction::Horizontal)
951                    .margin(margin.unwrap_or(0))
952                    .constraints(vec![Constraint::Percentage(100)].as_ref());
953                split_space = layout.split(place.unwrap_or(frame.size()))[0];
954            }
955        }
956        res.push((split_space, current));
957
958        if process_children {
959            for base_child in node.children.iter() {
960                let mut child = base_child.as_ref().borrow().clone();
961                if dependency.is_some() {
962                    child.dependencies.push(dependency.clone().unwrap().id);
963                }
964                let partial_res = self.process_node(
965                    frame,
966                    &child,
967                    dependency.clone(),
968                    Some(child_space),
969                    Some(1),
970                    count + 1,
971                );
972                for pair in partial_res.iter() {
973                    let mut mkp_elm = pair.1.clone();
974                    if dependency.is_some() {
975                        let did = dependency.clone().unwrap().id;
976                        if !mkp_elm.dependencies.contains(&did) {
977                            mkp_elm.dependencies.push(did);
978                        }
979                    }
980                    res.push((pair.0, mkp_elm));
981                }
982            }
983        }
984
985        for shld in subsequents {
986            res.push(shld);
987        }
988
989        Some(res)
990    }
991
992    fn process_node(
993        &self,
994        frame: &mut Frame<B>,
995        node: &MarkupElement,
996        depends_on: Option<MarkupElement>,
997        place: Option<Rect>,
998        margin: Option<u16>,
999        count: usize,
1000    ) -> Vec<(Rect, MarkupElement)> {
1001        let name = node.name.clone();
1002        let name = name.as_str();
1003        let values: Vec<(Rect, MarkupElement)> = match name {
1004            "styles" => vec![],
1005            "layout" => {
1006                self.process_layout(frame.borrow_mut(), node, depends_on, place, margin, count)
1007            }
1008            "container" => {
1009                self.process_block(frame.borrow_mut(), node, depends_on, place, margin, count)
1010            }
1011            "block" => {
1012                self.process_block(frame.borrow_mut(), node, depends_on, place, margin, count)
1013            }
1014            _ => {
1015                let res =
1016                    self.process_other(frame.borrow_mut(), node, depends_on, place, margin, count);
1017                if let Some(value) = res {
1018                    value
1019                } else {
1020                    warn!("Unknown node type \"{}\"", name);
1021                    vec![]
1022                }
1023            }
1024        };
1025        values
1026    }
1027
1028    pub fn add_action(&mut self, name: &str, action: ActionCallback) -> &mut Self {
1029        self.actions.add_action(String::from(name), action);
1030        self
1031    }
1032
1033    fn can_be_drawn(&self, node: MarkupElement, drawn: &[String]) -> bool {
1034        let others = node.dependencies;
1035        if others.is_empty() {
1036            return true;
1037        }
1038        let mut res = false;
1039        for eid in others {
1040            if drawn.contains(&eid) {
1041                res = true;
1042            }
1043        }
1044        res
1045    }
1046
1047    fn get_fingerprint(&self) -> String {
1048        let idxd: Vec<String> = self.indexed_elements.iter().map(|x| x.id.clone()).collect();
1049        let mut state_fngrprnt = format!(
1050            "{}:{}:{}:",
1051            self.current,
1052            self.contexts.len(),
1053            idxd.join("~")
1054        );
1055        for (key, value) in self.state.clone().iter() {
1056            state_fngrprnt = format!("{}-{}_{}", state_fngrprnt, key, value);
1057        }
1058        state_fngrprnt
1059    }
1060
1061    fn update_fingerprint(&mut self) {
1062        let state_fngrprnt = self.get_fingerprint();
1063        self.fingerprint = state_fngrprnt;
1064    }
1065
1066    /// Render the current state of the tree
1067    ///
1068    pub fn render_ui(&mut self, frame: &mut Frame<B>) -> Result<bool, String> {
1069        let elm = self.root.clone();
1070        if elm.is_some() {
1071            let root = MarkupParser::<B>::get_element(elm);
1072            let drawables = self.process_node(frame.borrow_mut(), &root, None, None, None, 0);
1073            let mut drawn: Vec<String> = vec![];
1074            drawables.iter().for_each(|pair| {
1075                let area = pair.0;
1076                let node = pair.1.clone();
1077                if self.can_be_drawn(node.clone(), &drawn) {
1078                    // println!("{} can be drawn...", &node.id);
1079                    let done = self.draw_element(frame, area, &node);
1080                    if done {
1081                        drawn.push(node.id);
1082                    }
1083                } else {
1084                    // println!("{} cant be drawn...", &node.id);
1085                }
1086            });
1087            Ok(true)
1088        } else {
1089            let err = "Critical error on render process.".to_string();
1090            Err(err)
1091        }
1092    }
1093
1094    pub fn add_context(&mut self, node: &MarkupElement) {
1095        let loc = self.contexts.len();
1096        let current = self.contexts.get(loc);
1097        let must_insert = current.is_some() && !current.unwrap().0.eq(&node.id);
1098        if loc == 0 || must_insert {
1099            self.contexts
1100                .push((node.id.clone(), self.indexed_elements.clone()));
1101            let chld: Vec<MarkupElement> = node
1102                .clone()
1103                .children
1104                .iter()
1105                .map(|x| x.as_ref().borrow().clone())
1106                .filter(|x| x.order > -1)
1107                .collect();
1108            self.indexed_elements = chld;
1109            self.current = -1;
1110        }
1111        self.fingerprint = String::from("<>");
1112    }
1113
1114    pub fn remove_context(&mut self, node: &MarkupElement) {
1115        let loc = self.contexts.len();
1116        if loc > 0 {
1117            let partial = self.contexts[loc - 1].clone();
1118            if partial.0.eq(&node.id) {
1119                self.indexed_elements = partial.1;
1120                self.contexts.pop();
1121                self.current = -1;
1122            }
1123        }
1124        self.fingerprint = String::from("<>");
1125    }
1126
1127    pub fn test_check(&self, backend: B) -> Result<(), Box<dyn std::error::Error>> {
1128        let elm = self.root.clone();
1129        if elm.is_some() {
1130            let mut terminal = Terminal::new(backend)?;
1131            let root = MarkupParser::<B>::get_element(elm);
1132            terminal.draw(|frame| {
1133                let drawables = self.process_node(frame.borrow_mut(), &root, None, None, None, 0);
1134                let ids: Vec<String> = drawables
1135                    .iter()
1136                    .map(|x| format!("{}#{}", x.1.name, x.1.id))
1137                    .collect();
1138                println!("{:#?}", drawables);
1139                println!("{:#?}", ids);
1140            })?;
1141        }
1142        println!("{:#?}", self.global_styles);
1143        Ok(())
1144    }
1145
1146    /// Starts a render loop. the loop receive a callback thar will return true
1147    /// if the loop must finish.
1148    ///
1149    /// - *on_event*: callback thar receive a key event.
1150    ///
1151    pub fn ui_loop(
1152        &mut self,
1153        backend: B,
1154        on_event: impl Fn(crossterm::event::KeyEvent, HashMap<String, String>) -> EventResponse,
1155        // on_event: impl Fn(crossterm::event::KeyEvent) -> bool,
1156    ) -> Result<(), Box<dyn std::error::Error>>
1157// pub fn ui_loop<Fut>(
1158//     on_event: impl Fn(crossterm::event::KeyEvent) -> Fut,
1159     // ) -> Result<(), Box<dyn std::error::Error>>
1160     // where
1161     //     Fut: Future<Output = bool>,
1162    {
1163        if self.error.is_some() {
1164            panic!("{}", self.error.clone().unwrap());
1165        }
1166
1167        let mut terminal = Terminal::new(backend)?;
1168
1169        enable_raw_mode().expect("Can't run in raw mode.");
1170        terminal.clear()?;
1171
1172        let (tx, rx) = mpsc::channel::<Event<KeyEvent>>();
1173        let tick_rate = Duration::from_millis(200);
1174
1175        thread::spawn(move || {
1176            let mut last_tick = Instant::now();
1177            loop {
1178                let timeout = tick_rate
1179                    .checked_sub(last_tick.elapsed())
1180                    .unwrap_or_else(|| Duration::from_secs(0));
1181
1182                if event::poll(timeout).expect("poll works") {
1183                    if let CEvent::Key(key) = event::read().expect("can read events") {
1184                        tx.send(Event::Input(key)).expect("can send events");
1185                    }
1186                }
1187
1188                if last_tick.elapsed() >= tick_rate && tx.send(Event::Tick).is_ok() {
1189                    last_tick = Instant::now();
1190                }
1191            }
1192        });
1193        let mut error_info: Option<String> = None;
1194        let mut should_quit: bool = false;
1195        loop {
1196            let new_fprnt = self.get_fingerprint();
1197            if !new_fprnt.eq(&self.fingerprint) {
1198                terminal.draw(|frame| {
1199                    let res = self.render_ui(frame);
1200                    if res.is_ok() {
1201                        self.update_fingerprint();
1202                    } else {
1203                        error_info = res.err();
1204                        should_quit = true;
1205                    }
1206                })?;
1207            }
1208            let evt: Event<crossterm::event::KeyEvent> = rx.recv()?;
1209            if let Event::Input(key_event) = evt {
1210                let event = key_event;
1211                match event.code {
1212                    KeyCode::Tab => {
1213                        self.go_next();
1214                    }
1215                    KeyCode::BackTab => {
1216                        self.go_prev();
1217                    }
1218                    KeyCode::Enter => {
1219                        let res = self.do_action();
1220                        match res {
1221                            EventResponse::QUIT => {
1222                                should_quit = true;
1223                            }
1224                            EventResponse::STATE(state) => {
1225                                self.state = state;
1226                            }
1227                            EventResponse::CLEANFOCUS(state) => {
1228                                self.state = state;
1229                                self.current = -1;
1230                            }
1231                            _ => {}
1232                        }
1233                    }
1234                    _ => {
1235                        info!("{:?}", key_event);
1236                    }
1237                }
1238                let response =
1239                    on_event(key_event as crossterm::event::KeyEvent, self.state.clone());
1240                match response {
1241                    EventResponse::QUIT => {
1242                        should_quit = true;
1243                    }
1244                    EventResponse::STATE(new_state) => {
1245                        self.state = new_state;
1246                    }
1247                    EventResponse::CLEANFOCUS(new_state) => {
1248                        self.state = new_state;
1249                        self.current = -1;
1250                    }
1251                    EventResponse::NOOP => {}
1252                }
1253                if should_quit {
1254                    break;
1255                }
1256            }
1257        }
1258
1259        disable_raw_mode()?;
1260        terminal.show_cursor()?;
1261        terminal.clear()?;
1262        if error_info.is_some() {
1263            panic!("{}", error_info.unwrap());
1264        }
1265        Ok(())
1266    }
1267
1268    // Static
1269
1270    fn get_constraints(node: MarkupElement) -> Vec<Constraint> {
1271        let mut constraints: Vec<Constraint> = vec![];
1272        if !node.children.is_empty() {
1273            for (_position, base_child) in node.children.iter().enumerate() {
1274                let child = base_child.as_ref().borrow().clone();
1275                let constraint = extract_attribute(child.attributes.clone(), "constraint");
1276                constraints.push(MarkupParser::<B>::get_constraint(constraint));
1277            }
1278        }
1279        constraints
1280    }
1281
1282    pub fn get_element(node: Option<Rc<RefCell<MarkupElement>>>) -> MarkupElement {
1283        let r = node.unwrap();
1284        let r = r.as_ref().borrow().to_owned();
1285        r
1286    }
1287
1288    pub fn extract_element(node: &Rc<RefCell<MarkupElement>>) -> MarkupElement {
1289        let r = node.as_ref().borrow().to_owned();
1290        r
1291    }
1292
1293    pub fn is_widget(node_name: &str) -> bool {
1294        WIDGET_NAMES.contains(&node_name)
1295    }
1296
1297    pub fn is_layout(node_name: &str) -> bool {
1298        node_name.eq("layout")
1299    }
1300
1301    pub fn get_border(border_value: &str) -> Borders {
1302        let border = String::from(border_value);
1303        if border.contains('|') {
1304            let borders = border
1305                .split('|')
1306                .map(String::from)
1307                .map(|s| MarkupParser::<B>::get_border(&s))
1308                .collect::<Vec<Borders>>();
1309            let size = borders.len();
1310            let mut res = borders[0];
1311            // for i in 1..size {
1312            //    res |= borders[i];
1313            for border in borders.iter().take(size).skip(1) {
1314                res |= *border;
1315            }
1316            return res;
1317        }
1318        let border = match border.to_lowercase().as_str() {
1319            "all" => Borders::ALL,
1320            "bottom" => Borders::BOTTOM,
1321            "top" => Borders::TOP,
1322            "left" => Borders::LEFT,
1323            "right" => Borders::RIGHT,
1324            _ => Borders::NONE,
1325        };
1326        border
1327    }
1328
1329    pub fn get_constraint(constraint: String) -> Constraint {
1330        let res = if constraint.ends_with('%') {
1331            let constraint_value = constraint.replace('%', "");
1332            let constraint_value = constraint_value.parse::<u16>().unwrap_or(1);
1333            Constraint::Percentage(constraint_value)
1334        } else if constraint.ends_with("min") {
1335            let constraint_value = constraint.replace("min", "");
1336            let constraint_value = constraint_value.parse::<u16>().unwrap_or(1);
1337            Constraint::Min(constraint_value)
1338        } else if constraint.ends_with("max") {
1339            let constraint_value = constraint.replace("max", "");
1340            let constraint_value = constraint_value.parse::<u16>().unwrap_or(1);
1341            Constraint::Max(constraint_value)
1342        } else if constraint.contains(':') {
1343            let parts = constraint.split(':');
1344            let parts: Vec<&str> = parts.collect();
1345            let x = String::from(parts[0]).parse::<u32>().unwrap_or(1);
1346            let y = String::from(parts[1]).parse::<u32>().unwrap_or(1);
1347            Constraint::Ratio(x, y)
1348        } else {
1349            let constraint_value = constraint.parse::<u16>().unwrap_or(1);
1350            Constraint::Length(constraint_value)
1351        };
1352        res
1353    }
1354
1355    pub fn get_direction(node: &MarkupElement) -> Direction {
1356        let direction = extract_attribute(node.attributes.clone(), "direction");
1357        if direction.eq("vertical") {
1358            Direction::Vertical
1359        } else {
1360            Direction::Horizontal
1361        }
1362    }
1363
1364    pub fn get_alignment(node: &MarkupElement) -> Alignment {
1365        let align_text = extract_attribute(node.attributes.clone(), "align");
1366        match align_text.as_str() {
1367            "center" => Alignment::Center,
1368            "left" => Alignment::Left,
1369            "right" => Alignment::Right,
1370            _ => Alignment::Left,
1371        }
1372    }
1373
1374    pub fn process_styles(node: MarkupElement) -> StylesStorage {
1375        let mut global_styles = StylesStorage::new();
1376        if node.text.is_some() {
1377            let text = node.text.unwrap();
1378            let text = text
1379                .replace(['\n', '\r', ' '], "")
1380                .replace('{', " {")
1381                .replace('}', "}\n");
1382            let rules: Vec<_> = text
1383                .split('\n')
1384                .filter(|x| !x.is_empty())
1385                .map(|text| {
1386                    let nt = String::from(text);
1387                    let rule_info = nt.replace('}', "");
1388                    let rule_info: Vec<String> = rule_info.split(" {").map(String::from).collect();
1389                    let rules = rule_info;
1390                    let rulename: String = rules.get(0).unwrap().to_string();
1391                    let properties: String = rules.get(1).unwrap().to_string();
1392                    (rulename, MarkupParser::<B>::generate_styles(properties))
1393                })
1394                .collect();
1395            for (rulename, styles) in rules.iter() {
1396                global_styles.add_rule(rulename.clone(), *styles);
1397            }
1398        }
1399        global_styles
1400    }
1401
1402    fn generate_styles(styles_text: String) -> Style {
1403        let mut res = Style::default();
1404        if styles_text.len() < 3 {
1405            return res;
1406        }
1407        let styles_vec = styles_text
1408            .split(';')
1409            .filter(|x| !x.is_empty())
1410            .map(|style| style.split(':').map(|word| word.trim()).collect())
1411            .map(|data: Vec<&str>| (data[0], data[1]))
1412            .collect::<Vec<(&str, &str)>>();
1413        let styles: HashMap<&str, &str> = styles_vec.into_iter().collect();
1414        if styles.contains_key("bg") {
1415            let color = styles.get("bg").unwrap();
1416            let color = color_from_str(color);
1417            res = res.bg(color);
1418        }
1419        if styles.contains_key("fg") {
1420            let color = styles.get("fg").unwrap();
1421            let color = color_from_str(color);
1422            res = res.fg(color);
1423        }
1424        if styles.contains_key("weight") {
1425            let weight = modifier_from_str(styles.get("weight").unwrap());
1426            res = res.add_modifier(weight);
1427        }
1428        if styles.contains_key("font-decoration") {
1429            let decorations = modifiers_from_str(styles.get("font-decoration").unwrap());
1430            res = res.patch(decorations);
1431        }
1432        // println!("-----------------\n{} \n\n {:#?}\n\n -----------------", &styles_text, res);
1433        res
1434    }
1435
1436    pub fn get_styles(node: &MarkupElement, focus: bool, active: bool) -> Style {
1437        let key = if focus { "focus_styles" } else { "styles" };
1438        let key = if active { "active_styles" } else { key };
1439        let styles_text = extract_attribute(node.attributes.clone(), key);
1440        MarkupParser::<B>::generate_styles(styles_text)
1441    }
1442}