dwfv 0.5.0

A simple digital waveform viewer with vi-like key bindings
Documentation
// SPDX-License-Identifier: MIT
use super::symbols::block;
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::{Color, Modifier, Style};
use tui::symbols::line;
use tui::widgets::Widget;

#[derive(Clone, PartialEq, Eq)]
pub enum WaveformElement {
    Low,
    High,
    Value(String),
    Transition,
    RisingEdge,
    FallingEdge,
    Invalid,
    LowDensity,
    MediumDensity,
    HighDensity,
}

impl WaveformElement {
    pub fn to_symbols(&self) -> (&str, &str, &str) {
        match self {
            WaveformElement::Low => (" ", " ", line::HORIZONTAL),
            WaveformElement::High => (line::HORIZONTAL, " ", " "),
            WaveformElement::Value(_) => (line::HORIZONTAL, " ", line::HORIZONTAL),
            WaveformElement::RisingEdge => (line::TOP_LEFT, line::VERTICAL, line::BOTTOM_RIGHT),
            WaveformElement::FallingEdge => (line::TOP_RIGHT, line::VERTICAL, line::BOTTOM_LEFT),
            WaveformElement::Transition => {
                (line::HORIZONTAL_DOWN, line::VERTICAL, line::HORIZONTAL_UP)
            }
            WaveformElement::Invalid => (block::FULL_LOWER, block::FULL, block::FULL_UPPER),
            WaveformElement::LowDensity => (block::LIGHT_LOWER, block::LIGHT, block::LIGHT_UPPER),
            WaveformElement::MediumDensity => {
                (block::MEDIUM_LOWER, block::MEDIUM, block::MEDIUM_UPPER)
            }
            WaveformElement::HighDensity => (block::FULL_LOWER, block::FULL, block::FULL_UPPER),
        }
    }
}

pub struct Waveform<'a> {
    data: &'a [WaveformElement],
    name: String,
    selected: bool,
    cursor: usize,
    visual_cursor: Option<usize>,
    odd: bool,
}

impl<'a> Waveform<'a> {
    pub fn new(
        name: String,
        data: &'a [WaveformElement],
        selected: bool,
        cursor: usize,
        visual_cursor: Option<usize>,
        odd: bool,
    ) -> Waveform<'a> {
        Waveform {
            data,
            name,
            selected,
            cursor,
            visual_cursor,
            odd,
        }
    }
}

impl Widget for Waveform<'_> {
    fn render(self, area: Rect, buf: &mut Buffer) {
        for (i, elmt) in self.data.iter().enumerate() {
            let fg = if i == self.cursor {
                if self.selected {
                    Color::White
                } else {
                    Color::Black
                }
            } else if *elmt == WaveformElement::Invalid {
                if self.selected {
                    Color::LightRed
                } else {
                    Color::Red
                }
            } else if self.odd {
                if self.selected {
                    Color::LightCyan
                } else {
                    Color::Cyan
                }
            } else if self.selected {
                Color::LightGreen
            } else {
                Color::Green
            };
            let bg = if i == self.cursor {
                Color::Gray
            } else if let Some(visual_cursor) = self.visual_cursor {
                if (visual_cursor <= i && i <= self.cursor)
                    || (self.cursor <= i && i <= visual_cursor)
                {
                    Color::Blue
                } else {
                    Color::Reset
                }
            } else {
                Color::Reset
            };
            let style = Style::default().fg(fg).bg(bg);

            let (top, middle, bottom) = elmt.to_symbols();
            buf.get_mut(area.left() + i as u16, area.top())
                .set_symbol(top)
                .set_style(style);
            buf.get_mut(area.left() + i as u16, area.top() + 1)
                .set_symbol(middle)
                .set_style(style);
            buf.get_mut(area.left() + i as u16, area.top() + 2)
                .set_symbol(bottom)
                .set_style(style);
        }

        let mut elmts = self.data[1..].iter().enumerate();
        loop {
            let mut free_space = 0;
            let mut value = "";
            let mut elmt = elmts.next();
            let offset = if let Some((i, _)) = elmt { i } else { break };

            while let Some((_, WaveformElement::Value(v))) = elmt {
                free_space += 1;
                value = v;
                elmt = elmts.next();
            }

            for (i, c) in value.chars().enumerate() {
                if i >= free_space {
                    break;
                }

                let r = &c.to_string();
                let symbol = if i >= free_space - 1 && i + 1 < value.len() {
                    ""
                } else {
                    r
                };

                buf.get_mut(area.left() + (offset + i + 1) as u16, area.top() + 1)
                    .set_symbol(symbol);
            }
        }

        let annot_style = Style::default()
            .bg(Color::DarkGray)
            .fg(if self.selected {
                Color::White
            } else {
                Color::Black
            })
            .add_modifier(if self.selected {
                Modifier::BOLD
            } else {
                Modifier::empty()
            });

        buf.set_stringn(
            area.left(),
            area.top() + 1,
            block::FULL,
            area.width as usize,
            Style::default().fg(Color::DarkGray),
        );
        buf.set_stringn(
            area.left(),
            area.top() + 2,
            block::FULL_UPPER,
            area.width as usize,
            Style::default().fg(Color::DarkGray),
        );
        buf.set_stringn(
            area.left(),
            area.top(),
            &self.name,
            area.width as usize,
            annot_style,
        );
    }
}