use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use crate::style::{BorderStyle, Style};
use super::{EdgeArrow, EdgeStyle};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NodeStyleSet {
pub default: Style,
pub classes: Arc<HashMap<Arc<str>, Style>>,
pub border_style: BorderStyle,
}
impl Hash for NodeStyleSet {
fn hash<H: Hasher>(&self, state: &mut H) {
self.default.hash(state);
self.border_style.hash(state);
let mut entries: Vec<_> = self.classes.iter().collect();
entries.sort_by_key(|(key, _)| *key);
entries.len().hash(state);
for (key, value) in entries {
key.hash(state);
value.hash(state);
}
}
}
impl Default for NodeStyleSet {
fn default() -> Self {
Self {
default: Style::default(),
classes: Arc::new(HashMap::new()),
border_style: BorderStyle::Rounded,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EdgeGlyphs {
pub horizontal: char,
pub vertical: char,
pub border_style: BorderStyle,
}
impl EdgeGlyphs {
const fn solid() -> Self {
Self {
horizontal: '─',
vertical: '│',
border_style: BorderStyle::Plain,
}
}
const fn dashed() -> Self {
Self {
horizontal: '╌',
vertical: '╎',
border_style: BorderStyle::Plain,
}
}
const fn thick() -> Self {
Self {
horizontal: '━',
vertical: '┃',
border_style: BorderStyle::Plain,
}
}
const fn invisible() -> Self {
Self {
horizontal: ' ',
vertical: ' ',
border_style: BorderStyle::Plain,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ArrowGlyphs {
pub up: char,
pub down: char,
pub left: char,
pub right: char,
}
impl ArrowGlyphs {
const fn new(up: char, down: char, left: char, right: char) -> Self {
Self {
up,
down,
left,
right,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SubgraphTheme {
pub style: Style,
pub header_style: Style,
pub border_style: BorderStyle,
}
impl Default for SubgraphTheme {
fn default() -> Self {
Self {
style: Style::default(),
header_style: Style::default(),
border_style: BorderStyle::Rounded,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct FlowchartTheme {
pub node_styles: NodeStyleSet,
pub edge_glyphs: [EdgeGlyphs; 4],
pub arrow_heads: [ArrowGlyphs; 5],
pub subgraph: SubgraphTheme,
pub label_style: Style,
pub item_hover_style: Style,
}
impl FlowchartTheme {
pub fn classic() -> Self {
Self {
node_styles: NodeStyleSet::default(),
edge_glyphs: [
EdgeGlyphs::solid(),
EdgeGlyphs::dashed(),
EdgeGlyphs::thick(),
EdgeGlyphs::invisible(),
],
arrow_heads: [
ArrowGlyphs::new(' ', ' ', ' ', ' '),
ArrowGlyphs::new('△', '▽', '◁', '▷'),
ArrowGlyphs::new('▲', '▼', '◀', '▶'),
ArrowGlyphs::new('×', '×', '×', '×'),
ArrowGlyphs::new('○', '○', '○', '○'),
],
subgraph: SubgraphTheme::default(),
label_style: Style::default(),
item_hover_style: Style::default(),
}
}
pub fn minimal() -> Self {
let mut theme = Self::classic();
theme.node_styles.border_style = BorderStyle::Plain;
theme.arrow_heads[EdgeArrow::Filled as usize] = ArrowGlyphs::new('△', '▽', '◁', '▷');
theme
}
pub fn ascii() -> Self {
Self {
node_styles: NodeStyleSet {
border_style: BorderStyle::Plain,
..NodeStyleSet::default()
},
edge_glyphs: [
EdgeGlyphs {
horizontal: '-',
vertical: '|',
border_style: BorderStyle::Plain,
},
EdgeGlyphs {
horizontal: '-',
vertical: ':',
border_style: BorderStyle::Plain,
},
EdgeGlyphs {
horizontal: '=',
vertical: '#',
border_style: BorderStyle::Plain,
},
EdgeGlyphs::invisible(),
],
arrow_heads: [
ArrowGlyphs::new(' ', ' ', ' ', ' '),
ArrowGlyphs::new('^', 'v', '<', '>'),
ArrowGlyphs::new('^', 'v', '<', '>'),
ArrowGlyphs::new('x', 'x', 'x', 'x'),
ArrowGlyphs::new('o', 'o', 'o', 'o'),
],
subgraph: SubgraphTheme {
border_style: BorderStyle::Plain,
..SubgraphTheme::default()
},
label_style: Style::default(),
item_hover_style: Style::default(),
}
}
}
impl Default for FlowchartTheme {
fn default() -> Self {
Self::classic()
}
}
impl EdgeStyle {
pub(crate) const fn theme_index(self) -> usize {
match self {
EdgeStyle::Solid => 0,
EdgeStyle::Dashed => 1,
EdgeStyle::Thick => 2,
EdgeStyle::Invisible => 3,
}
}
}
impl EdgeArrow {
pub(crate) const fn theme_index(self) -> usize {
match self {
EdgeArrow::None => 0,
EdgeArrow::Open => 1,
EdgeArrow::Filled => 2,
EdgeArrow::Cross => 3,
EdgeArrow::Circle => 4,
}
}
}