mtui 0.7.0

An extensive Modbus client (TCP, RTU & mock) for your terminal.
Documentation
use crate::app::App;
use crate::input::KeyCode;
use crate::state::{CustomField, CustomParams};
use crate::tui::hints::{self, Hint};
use crate::tui::theme::Theme;
use ratatui::layout::Rect;
use ratatui::text::{Line, Span};
use ratatui::Frame;

pub(super) fn draw(frame: &mut Frame, area: Rect, theme: &Theme, app: &App, c: &CustomParams) {
    let sel = c.current_field();

    let field_line = |label: &str, value: String, selected: bool| -> Line<'static> {
        let marker = if selected { "> " } else { "  " };
        let style = if selected {
            theme.selected_style()
        } else {
            theme.base()
        };
        Line::from(vec![
            Span::styled(format!("{marker}{label:<12} "), theme.dim_style()),
            Span::styled(value, style),
        ])
    };

    let mut lines: Vec<Line> = vec![];

    lines.push(match app.custom_preview(c) {
        Ok((input, output)) => Line::from(vec![
            Span::styled(" Preview  ", theme.dim_style()),
            Span::styled(input.to_string(), theme.base()),
            Span::styled(" \u{2192} ".to_string(), theme.dim_style()),
            Span::styled(output, theme.accent_style()),
        ]),
        Err(reason) => Line::from(vec![
            Span::styled(" Preview  ", theme.dim_style()),
            Span::styled(reason, theme.dim_style()),
        ]),
    });
    lines.push(Line::default());

    let repr_val = format!("{}  ({} reg)", c.repr.label(), c.repr.register_count());
    let repr_val = if sel == CustomField::Repr {
        format!("\u{2039} {repr_val} \u{203a}")
    } else {
        repr_val
    };
    lines.push(field_line("Type", repr_val, sel == CustomField::Repr));

    let ops_str = if c.ops.is_empty() {
        "(none)".to_string()
    } else {
        c.ops
            .iter()
            .map(|o| o.display())
            .collect::<Vec<_>>()
            .join(" ")
    };
    lines.push(field_line("Operations", ops_str, sel == CustomField::Ops));
    if sel == CustomField::Ops {
        lines.push(Line::from(Span::styled(
            "    (enter adds, backspace removes)".to_string(),
            theme.dim_style(),
        )));
        lines.push(Line::from(Span::styled(
            format!("    add: {}_   e.g. *0.1  +5  /10  ^2", c.op_buffer),
            theme.dim_style(),
        )));
    }

    let enum_str = if c.enum_map.is_empty() {
        "(none)".to_string()
    } else {
        c.enum_map
            .iter()
            .map(|e| format!("{}\u{2192}{}", e.value, e.text))
            .collect::<Vec<_>>()
            .join("  ")
    };
    lines.push(field_line("Enum", enum_str, sel == CustomField::Enum));
    if sel == CustomField::Enum {
        lines.push(Line::from(Span::styled(
            "    (enter adds, backspace removes)".to_string(),
            theme.dim_style(),
        )));
        lines.push(Line::from(Span::styled(
            format!("    add: {}_   e.g. 3=Running", c.enum_buffer),
            theme.dim_style(),
        )));
    }

    let dec = if c.decimals.is_empty() {
        "auto".to_string()
    } else {
        c.decimals.clone()
    };
    lines.push(field_line("Decimals", dec, sel == CustomField::Decimals));
    if sel == CustomField::Decimals {
        lines.push(Line::from(Span::styled(
            "    auto; 0 for none; numerical for amount".to_string(),
            theme.dim_style(),
        )));
    }

    let pfx = if sel == CustomField::Prefix {
        format!("{} ", c.prefix)
    } else {
        c.suffix.to_string()
    };
    lines.push(field_line("Prefix", pfx, sel == CustomField::Prefix));

    let sfx = if sel == CustomField::Suffix {
        format!("{} ", c.suffix)
    } else {
        c.suffix.to_string()
    };
    lines.push(field_line("Suffix", sfx, sel == CustomField::Suffix));

    lines.push(Line::default());
    let save_hint = if sel == CustomField::Save {
        "\u{2190} enter".to_string()
    } else {
        String::new()
    };
    lines.push(field_line("Save rule", save_hint, sel == CustomField::Save));
    let remove_hint = if sel == CustomField::Remove {
        "\u{2190} enter".to_string()
    } else {
        String::new()
    };
    lines.push(field_line(
        "Remove rule",
        remove_hint,
        sel == CustomField::Remove,
    ));

    if let Some(err) = &c.error {
        lines.push(Line::default());
        lines.push(Line::from(Span::styled(
            format!(" {}", err.clone()),
            theme.err_style(),
        )));
    }

    lines.push(Line::default());
    let kb = &app.config.keybinds;
    lines.push(hints::footer(
        theme,
        [
            Hint::pair(kb.move_up, kb.move_down, "Field"),
            Hint::pair(KeyCode::Left, KeyCode::Right, "Change"),
            Hint::key(kb.exit, "Close"),
        ],
    ));

    super::render(frame, area, theme, "Custom rule", 48, lines);
}