use egui::{Color32, Pos2, Rect, Response, Sense, Stroke, vec2};
use super::{BRAND, HAIRLINE, INK, INK_FAINT, INK_WEAK, RADIUS, RULE, space, type_scale};
mod field;
mod row;
pub use row::{Row, marked, row, row_width};
fn rule_at(ui: &egui::Ui, top: Pos2, height: f32, accent: Color32) {
ui.painter()
.rect_filled(Rect::from_min_size(top, vec2(RULE, height)), 0.0, accent);
}
pub fn rail(ui: &egui::Ui, x: f32, top: f32, bottom: f32) {
ui.painter()
.vline(x, top..=bottom, Stroke::new(1.0, INK_FAINT));
}
pub fn elbow(ui: &egui::Ui, x: f32, top: f32, middle: f32, to: f32) {
let stroke = Stroke::new(1.0, INK_FAINT);
ui.painter().vline(x, top..=middle, stroke);
ui.painter().hline(x..=to, middle, stroke);
}
pub fn ruled(ui: &mut egui::Ui, ink: Color32, body: impl FnOnce(&mut egui::Ui)) {
let top = ui.cursor().min;
ui.horizontal_top(|ui| {
ui.add_space(RULE + space::S);
ui.vertical(body);
});
let bottom = ui.min_rect().max.y;
rule_at(ui, top, bottom - top.y, ink);
}
pub fn empty(ui: &mut egui::Ui, sentence: &str) -> Response {
ui.add(egui::Label::new(egui::RichText::new(sentence).color(INK_WEAK)).wrap())
}
pub fn section(ui: &mut egui::Ui, word: &str) {
ui.add_space(space::L);
let width = ui.available_width();
let (rect, _) = ui.allocate_exact_size(vec2(width, 1.0), Sense::hover());
ui.painter().hline(
rect.min.x..=rect.max.x,
rect.center().y,
Stroke::new(1.0, HAIRLINE),
);
ui.add_space(space::XS);
ui.label(egui::RichText::new(word).color(INK_WEAK));
}
pub fn link(ui: &mut egui::Ui, words: &str, ink: Color32) -> Response {
door(ui, egui::TextStyle::Small, &[(words, ink)], words)
}
pub fn link_marked(
ui: &mut egui::Ui,
words: &str,
ink: Color32,
(mark, marked): (&str, Color32),
) -> Response {
let mark = format!(" {mark}");
door(
ui,
egui::TextStyle::Small,
&[(words, ink), (&mark, marked)],
words,
)
}
pub fn tab(ui: &mut egui::Ui, words: &str, ink: Color32) -> Response {
door(ui, egui::TextStyle::Body, &[(words, ink)], words)
}
fn door(
ui: &mut egui::Ui,
style: egui::TextStyle,
parts: &[(&str, Color32)],
name: &str,
) -> Response {
let font = style.resolve(ui.style());
let mut job = egui::text::LayoutJob::default();
for (text, ink) in parts {
job.append(text, 0.0, egui::TextFormat::simple(font.clone(), *ink));
}
let galley = ui.fonts(|fonts| fonts.layout_job(job));
let (rect, seat) = ui.allocate_exact_size(galley.size(), Sense::click());
seat.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Link, ui.is_enabled(), name));
if seat.hovered() {
ui.painter()
.rect_filled(rect.expand(space::XS), RADIUS, super::hover());
}
if seat.has_focus() {
ui.painter()
.rect_stroke(rect.expand(space::XS), RADIUS, Stroke::new(1.0, BRAND));
}
ui.painter().galley(rect.min, galley, INK);
seat.on_hover_cursor(egui::CursorIcon::PointingHand)
}
pub use field::{field, prompt};
pub fn prose(ui: &mut egui::Ui, words: &str, ink: Color32) -> Response {
let font = egui::TextStyle::Body.resolve(ui.style());
let format = egui::TextFormat {
font_id: font,
color: ink,
line_height: Some(type_scale::LEADING),
..egui::TextFormat::default()
};
let mut job = egui::text::LayoutJob::default();
job.append(words, 0.0, format);
job.wrap.max_width = ui.available_width();
ui.add(egui::Label::new(job).wrap())
}
#[cfg(test)]
mod tests;