guth 0.2.1

Native Rust desktop file manager for fast, bounded local file workflows.
Documentation
use eframe::egui::{self, Color32, Pos2, Rect, RichText, Rounding, Sense, Stroke, Vec2};

pub fn panel_heading(ui: &mut egui::Ui, tag: &str, title: &str) {
    let text = ui
        .visuals()
        .override_text_color
        .unwrap_or_else(|| Color32::from_rgb(244, 244, 244));
    let muted = ui.visuals().widgets.inactive.fg_stroke.color;
    let border = ui.visuals().widgets.inactive.bg_stroke.color;
    ui.vertical(|ui| {
        ui.label(RichText::new(tag).size(11.0).color(muted));
        ui.label(RichText::new(title).size(16.0).strong().color(text));
    });
    ui.add_space(2.0);
    let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 1.0), Sense::hover());
    ui.painter().line_segment(
        [rect.left_center(), rect.right_center()],
        Stroke::new(1.0, border),
    );
}

pub fn field_label(ui: &mut egui::Ui, label: &str) {
    let muted = ui.visuals().widgets.inactive.fg_stroke.color;
    ui.label(RichText::new(label).size(11.0).color(muted));
}

pub fn muted_label(ui: &mut egui::Ui, text: impl Into<String>) {
    let muted = ui.visuals().widgets.inactive.fg_stroke.color;
    ui.label(RichText::new(text.into()).size(10.0).color(muted));
}

pub fn paint_background(
    ui: &mut egui::Ui,
    _phase: f32,
    fill: Color32,
    grid: Color32,
    watermark: Color32,
    background_image: Option<&egui::TextureHandle>,
    background_opacity: f32,
) {
    let rect = ui.max_rect();
    let painter = ui.painter().clone();
    painter.rect_filled(rect, Rounding::ZERO, fill);

    if watermark.a() > 0 {
        let band_height = 24.0;
        for index in 0..8 {
            let fade = 1.0 - index as f32 / 8.0;
            let alpha = (watermark.a() as f32 * fade * 0.42).round() as u8;
            if alpha == 0 {
                continue;
            }
            let top = rect.top() + index as f32 * band_height;
            painter.rect_filled(
                Rect::from_min_max(
                    Pos2::new(rect.left(), top),
                    Pos2::new(rect.right(), top + band_height),
                ),
                Rounding::ZERO,
                Color32::from_rgba_unmultiplied(watermark.r(), watermark.g(), watermark.b(), alpha),
            );
        }
    }

    if grid.a() > 0 {
        let stroke = Stroke::new(1.0, grid);
        let mut y = rect.top() + 72.0;
        while y < rect.bottom() {
            painter.line_segment(
                [Pos2::new(rect.left(), y), Pos2::new(rect.right(), y)],
                stroke,
            );
            y += 72.0;
        }
    }

    if let Some(texture) = background_image {
        let alpha = (background_opacity.clamp(0.0, 0.9) * 255.0).round() as u8;
        if alpha > 0 {
            painter.image(
                texture.id(),
                cover_rect(rect, texture.size_vec2()),
                Rect::from_min_max(Pos2::ZERO, Pos2::new(1.0, 1.0)),
                Color32::from_white_alpha(alpha),
            );
        }
    }
}

fn cover_rect(target: Rect, image_size: Vec2) -> Rect {
    if image_size.x <= 0.0 || image_size.y <= 0.0 {
        return target;
    }
    let scale = (target.width() / image_size.x).max(target.height() / image_size.y);
    let size = image_size * scale;
    Rect::from_center_size(target.center(), size)
}