use eframe::egui;
use crate::app;
pub const PAD: f32 = 12.0;
const GAP: f32 = 10.0;
const HEAD_H: f32 = 20.0;
const HEAD_TEXT: f32 = 9.0;
pub const NAME_TEXT: f32 = 11.5;
#[derive(Clone, Copy)]
pub enum Width {
Fixed(f32),
Share(f32),
}
pub fn columns<const N: usize>(rect: egui::Rect, widths: [Width; N]) -> [(f32, f32); N] {
let fixed: f32 = widths
.iter()
.map(|width| match width {
Width::Fixed(width) => *width,
Width::Share(_) => 0.0,
})
.sum();
let shares: f32 = widths
.iter()
.map(|width| match width {
Width::Share(share) => *share,
Width::Fixed(_) => 0.0,
})
.sum();
let taken = fixed + GAP * N.saturating_sub(1) as f32 + PAD * 2.0;
let free = (rect.width() - taken).max(0.0);
let mut left = rect.left() + PAD;
let mut out = [(0.0, 0.0); N];
for (cell, width) in out.iter_mut().zip(widths) {
let width = match width {
Width::Fixed(width) => width,
Width::Share(share) if shares > 0.0 => free * share / shares,
Width::Share(_) => 0.0,
};
*cell = (left, width);
left += width + GAP;
}
out
}
pub fn heads<const N: usize>(
ui: &mut egui::Ui,
widths: [Width; N],
labels: [&str; N],
right: &[usize],
) {
let visuals = ui.visuals().clone();
let quiet = app::caption(&visuals);
let hairline = egui::Stroke::new(1.0_f32, visuals.widgets.noninteractive.bg_stroke.color);
let (rect, _) = ui.allocate_exact_size(
egui::vec2(ui.available_width(), HEAD_H),
egui::Sense::hover(),
);
let painter = ui.painter();
painter.hline(rect.x_range(), rect.top() + 0.5, hairline);
painter.hline(rect.x_range(), rect.bottom() - 0.5, hairline);
for (column, ((left, width), text)) in columns(rect, widths).into_iter().zip(labels).enumerate()
{
if text.is_empty() {
continue;
}
let galley = painter.layout_no_wrap(
text.to_uppercase(),
egui::FontId::proportional(HEAD_TEXT),
quiet,
);
let left = match right.contains(&column) {
true => left + width - galley.size().x,
false => left,
};
painter.galley(
egui::pos2(left, rect.center().y - galley.size().y / 2.0),
galley,
quiet,
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_shared_columns_divide_what_the_fixed_ones_leave() {
let rect = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(500.0, 20.0));
let cells = columns(
rect,
[
Width::Fixed(56.0),
Width::Share(1.1),
Width::Share(1.5),
Width::Fixed(74.0),
],
);
let free = 500.0 - 56.0 - 74.0 - GAP * 3.0 - PAD * 2.0;
assert_eq!(cells[0], (PAD, 56.0));
assert_eq!(cells[1].1, free * 1.1 / 2.6);
assert_eq!(cells[2].1, free * 1.5 / 2.6);
assert_eq!(cells[3].1, 74.0);
assert_eq!(cells[3].0 + cells[3].1, 500.0 - PAD);
let tight = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(40.0, 20.0));
let cells = columns(tight, [Width::Fixed(56.0), Width::Share(1.0)]);
assert_eq!(cells[1].1, 0.0);
}
}