use crate::ui::{Model, theme};
mod foot;
mod rows;
pub use rows::{EMPTY, NOTHING_AIMED};
pub const LINGER: f64 = 1.5;
pub const SHUT: f32 = 80.0;
pub const DESK: f32 = 1400.0;
pub const CHARS: usize = 3;
pub const PIN: &str = "pin";
pub const UNPIN: &str = "unpin";
pub const PLUS: &str = "+";
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Bar {
pub pinned: bool,
pub left_at: Option<f64>,
}
impl Bar {
pub fn open(&self, now: f64) -> bool {
self.pinned || self.left_at.is_some_and(|at| now - at < LINGER)
}
pub fn heard(&mut self, now: f64, over: bool) -> Option<f64> {
if over {
self.left_at = Some(now);
return None;
}
let at = self.left_at?;
let left = LINGER - (now - at);
if left <= 0.0 {
self.left_at = None;
return None;
}
(!self.pinned).then_some(left)
}
}
pub fn open_width(window: f32) -> f32 {
(DESK / 4.0).min(window * 2.0 / 5.0)
}
pub fn cut(text: &str) -> String {
text.chars().take(CHARS).collect()
}
pub fn render(ctx: &egui::Context, model: &mut Model) {
if model.enroll.is_some() {
return;
}
let now = ctx.input(|i| i.time);
let screen = ctx.screen_rect();
let wide = open_width(screen.width());
let held = crate::ui::status::dropup::footed(model);
let reach = if held || model.bar.pinned || model.bar.left_at.is_some() {
wide
} else {
SHUT
};
let over = ctx
.input(|i| i.pointer.hover_pos())
.is_some_and(|at| at.x < screen.left() + reach);
if let Some(left) = model.bar.heard(now, over) {
ctx.request_repaint_after(std::time::Duration::from_secs_f64(left));
}
let open = held || model.bar.open(now);
egui::SidePanel::left("bar")
.resizable(false)
.exact_width(if open { wide } else { SHUT })
.frame(
egui::Frame::none()
.fill(theme::GROUND)
.inner_margin(egui::Margin::same(theme::space::XS)),
)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::bottom_up(egui::Align::Min), |ui| {
let seat = foot::render(ui, model, open);
ui.with_layout(egui::Layout::top_down(egui::Align::Min), |ui| {
head(ui, model, open);
rows::render(ui, model, open);
});
foot::list(ui, model, seat);
});
});
}
fn head(ui: &mut egui::Ui, model: &mut Model, open: bool) {
ui.horizontal(|ui| {
let word = if model.bar.pinned { UNPIN } else { PIN };
let said = if open {
theme::worded(theme::glyph::PIN, word)
} else {
theme::glyph::PIN.to_owned()
};
if theme::paint::link(ui, &said, theme::INK_WEAK).clicked() {
model.bar.pinned = !model.bar.pinned;
}
let Some(aim) = model.aim.clone() else {
return;
};
if model.covered() && !model.switching {
return;
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let said = if open {
crate::ui::roster::wall::NEW
} else {
PLUS
};
let seat = theme::paint::link(ui, said, theme::INK_WEAK);
crate::ui::act::tag(&seat, &[crate::verbs::PREPARE, crate::verbs::PROMPT]);
if seat.clicked() {
model.begin_at(&aim);
model.chose();
}
});
});
}
#[cfg(test)]
mod tests;