use egui::{Color32, Pos2, Rect, Response, Sense, Stroke, vec2};
use super::{
BRAND, HAIRLINE, INK_FAINT, INK_WEAK, RADIUS, RAISED, ROW, RULE, SURFACE, space, type_scale,
};
mod field;
pub fn row(
ui: &mut egui::Ui,
words: &str,
ink: Color32,
state: Option<Color32>,
chosen: bool,
indent: f32,
) -> Response {
let (rect, seat) = ui.allocate_exact_size(vec2(ui.available_width(), ROW), Sense::click());
seat.widget_info(|| {
egui::WidgetInfo::selected(
egui::WidgetType::SelectableLabel,
ui.is_enabled(),
chosen,
words,
)
});
if !ui.is_rect_visible(rect) {
return seat;
}
let fill = if chosen {
RAISED
} else if seat.hovered() {
SURFACE
} else {
Color32::TRANSPARENT
};
ui.painter().rect_filled(rect, RADIUS, fill);
if let Some(accent) = chosen.then_some(BRAND).or(state) {
rule_at(ui, rect.left_top(), rect.height(), accent);
}
if seat.has_focus() {
ui.painter()
.rect_stroke(rect, RADIUS, Stroke::new(1.0, BRAND));
}
let left = rect.min.x + RULE + indent + space::M;
let galley = elided(ui, words, ink, rect.max.x - left - space::S);
let at = Pos2::new(left, rect.center().y - galley.size().y / 2.0);
ui.painter().galley(at, galley, ink);
seat
}
fn elided(ui: &egui::Ui, words: &str, ink: Color32, width: f32) -> std::sync::Arc<egui::Galley> {
let font = egui::TextStyle::Body.resolve(ui.style());
let mut job = egui::text::LayoutJob::simple_singleline(words.to_owned(), font, ink);
job.wrap = egui::text::TextWrapping::truncate_at_width(width.max(0.0));
ui.fonts(|fonts| fonts.layout_job(job))
}
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 use field::{composer, field};
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;