#![doc(html_root_url = "https://docs.rs/egui-dataframe/0.3.3")]
use eframe::{self, egui::*};
use egui_grid::GridBuilder;
use egui_extras::{Size, TableBuilder, Column};
use polars::prelude::{DataFrame, Schema};
#[derive(Debug, Clone)]
pub struct Decorator {
pub sz: Vec2,
pub sense: Sense,
pub cols: Vec<Option<Color32>>,
pub align: Align2,
pub ofs: Vec2,
pub fontid: FontId
}
impl Decorator {
pub fn opt(v: &[Color32]) -> Vec<Option<Color32>> {
v.iter().map(|c| Some(*c)).collect::<Vec<_>>()
}
pub fn new(sz: Vec2, sense: Sense, cols: Vec<Option<Color32>>,
align: Align2, ofs: Vec2, fontid: FontId) -> Self {
Decorator{sz, sense, cols, align, ofs, fontid}
}
pub fn disp(&self, ui: &mut Ui, txt: &str) -> Option<(Response, Painter)> {
let (bgr, bgc, fgc) = (self.cols[0], self.cols[1], self.cols[2]);
if let Some(fgc) = fgc {
let (resp, painter) = ui.allocate_painter(self.sz, self.sense);
let rc = resp.rect;
if let Some(bgr) = bgr {
if let Some(bgc) = bgc {
painter.rect(rc, 0.0, bgc, Stroke{width: 1.0, color: bgr});
}
}
painter.text(rc.min + self.ofs, self.align, txt,
self.fontid.clone(), fgc);
Some((resp, painter))
}else{
ui.label(RichText::new(txt)
.size(self.fontid.size).family(self.fontid.family.clone()));
None
}
}
}
pub type DecoFunc<'a> = &'a mut dyn FnMut(&Decorator, &mut Ui, &str,
usize, usize) -> bool;
pub struct DecoFs<'a> {
pub fncs: (DecoFunc<'a>, DecoFunc<'a>)
}
impl<'a> DecoFs<'a> {
pub fn default(d: &Decorator, ui: &mut Ui, tx: &str,
_ri: usize, _ci: usize) -> bool {
let _resp_painter = d.disp(ui, tx);
true
}
}
#[derive(Debug, Clone)]
pub struct DFDesc {
pub default_deco: (Decorator, Decorator),
pub deco: Vec<(Option<Decorator>, Option<Decorator>)>,
pub schema: Schema
}
impl DFDesc {
pub fn new(default_deco: (Decorator, Decorator), schema: Schema) -> Self {
let n = schema.len();
DFDesc{default_deco,
deco: Vec::<(Option<Decorator>, Option<Decorator>)>::with_capacity(n),
schema}
}
pub fn all_from(mut self,
deco: Vec<(Option<Decorator>, Option<Decorator>)>) -> Self {
self.deco = deco; self
}
pub fn all_default(mut self) -> Self {
for _i in 0..self.schema.len() { self.push((None, None)) }
self
}
pub fn push(&mut self, deco: (Option<Decorator>, Option<Decorator>)) {
self.deco.push(deco);
}
pub fn disp<'a>(&'a self, ui: &'a mut Ui, f: &mut DecoFs, df: &DataFrame,
height_head: f32, height_body: f32, resizable: bool, striped: bool,
vscroll: bool) {
let (nrows, ncols) = (df.height(), df.width());
let cols = df.get_column_names();
TableBuilder::new(ui).columns(Column::auto().resizable(resizable), ncols)
.resizable(resizable)
.striped(striped)
.vscroll(vscroll)
.header(height_head, |mut header| {
for (i, head) in cols.iter().enumerate() {
header.col(|ui| {
let tx = format!("{}", head);
let d = if self.deco.len() == 0 { &self.default_deco.0 } else {
match &self.deco[i] {
(None, _) => &self.default_deco.0,
(Some(deco_head), _) => deco_head
}
};
f.fncs.0(d, ui, &tx, 0, i);
});
}
})
.body(|body| {
body.rows(height_body, nrows, |ri, mut row| {
for (i, col) in cols.iter().enumerate() {
row.col(|ui| {
if let Ok(column) = &df.column(col) {
let value = column.get(ri);
let tx = format!("{}", value);
let d = if self.deco.len() == 0 { &self.default_deco.1 } else {
match &self.deco[i] {
(_, None) => &self.default_deco.1,
(_, Some(deco_body)) => deco_body
}
};
f.fncs.1(d, ui, &tx, ri, i);
}
});
}
});
});
}
pub fn grid<'a>(&'a self, ui: &'a mut Ui, f: &mut DecoFs, df: &DataFrame,
width: f32, height: f32, ts: &TextStyle,
sp: &(f32, f32), ma: &style::Margin) {
let (nrows, ncols) = (df.height(), df.width());
ui.style_mut().override_text_style = Some(ts.clone());
let mut gb = GridBuilder::new().spacing(sp.0, sp.1).clip(true);
gb = (0..nrows).into_iter().fold(gb, |gb, _i|
(0..ncols).into_iter().fold(gb.new_row(Size::exact(height)), |gb, _j|
gb.cell(Size::exact(width)).with_margin(ma.clone())
)
);
gb.show(ui, |mut grid| {
let cols = df.get_column_names();
for j in 0..nrows {
for (i, col) in cols.iter().enumerate() {
grid.cell(|ui| {
if let Ok(column) = &df.column(col) {
let value = column.get(j);
let tx = format!("{}", value);
let d = if self.deco.len() == 0 { &self.default_deco.1 } else {
match &self.deco[i] {
(_, None) => &self.default_deco.1,
(_, Some(deco_body)) => deco_body
}
};
f.fncs.1(d, ui, &tx, j, i);
}
});
}
}
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dataframe() {
assert_eq!(Decorator::opt(&[Color32::RED, Color32::GREEN]),
[Some(Color32::RED), Some(Color32::GREEN)]);
}
}