nebulus 0.1.28

Low-latency native OpenIPC FPV ground station built with egui
use eframe::egui;

use crate::{app::NebulusApp, settings::GuiTheme};

pub(crate) fn show(app: &mut NebulusApp, ui: &mut egui::Ui) {
    ui.heading("Interface");
    ui.label(
        egui::RichText::new("Appearance settings apply immediately and persist across launches.")
            .small()
            .color(ui.visuals().weak_text_color()),
    );
    ui.add_space(12.0);

    ui.strong("Theme");
    ui.add_space(4.0);
    for themes in GuiTheme::ALL.chunks_exact(2) {
        ui.columns(2, |columns| {
            for (column, theme) in columns.iter_mut().zip(themes.iter().copied()) {
                if theme_button(column, theme, app.settings.gui_theme).clicked() {
                    app.settings.gui_theme = theme;
                    super::theme::apply(column.ctx(), theme);
                }
            }
        });
        ui.add_space(4.0);
    }

    ui.add_space(14.0);
    ui.strong("Scale");
    ui.horizontal(|ui| {
        let changed = ui
            .add(
                egui::Slider::new(&mut app.settings.interface_scale_percent, 75..=150)
                    .suffix("%")
                    .step_by(5.0),
            )
            .changed();
        if changed {
            ui.ctx()
                .set_zoom_factor(f32::from(app.settings.interface_scale_percent) / 100.0);
        }
        if ui.small_button("Default").clicked() {
            app.settings.interface_scale_percent = 100;
            ui.ctx().set_zoom_factor(1.0);
        }
    });

    ui.add_space(14.0);
    ui.strong("Display");
    ui.checkbox(&mut app.settings.show_osd, "Link telemetry overlay");
    if ui
        .checkbox(&mut app.settings.show_sidebar, "Controls panel visible")
        .changed()
        && !app.settings.show_sidebar
    {
        ui.ctx().request_repaint();
    }
    ui.label(
        egui::RichText::new("Use the Controls button in the header to restore a hidden panel.")
            .small()
            .color(ui.visuals().weak_text_color()),
    );

    ui.add_space(14.0);
    if ui.button("Reset GUI settings").clicked() {
        app.settings.gui_theme = GuiTheme::Macchiato;
        app.settings.interface_scale_percent = 100;
        app.settings.show_osd = true;
        app.settings.show_sidebar = true;
        super::theme::apply(ui.ctx(), app.settings.gui_theme);
        ui.ctx().set_zoom_factor(1.0);
    }
}

fn theme_button(ui: &mut egui::Ui, theme: GuiTheme, selected: GuiTheme) -> egui::Response {
    let palette = palette(theme);
    let desired = egui::vec2(ui.available_width().max(110.0), 44.0);
    let response = ui.add_sized(
        desired,
        egui::Button::new(egui::RichText::new(theme.label()).strong()).selected(theme == selected),
    );
    let swatch_size = 6.0;
    let start = egui::pos2(response.rect.left() + 10.0, response.rect.bottom() - 12.0);
    for (index, color) in palette.into_iter().enumerate() {
        let rect = egui::Rect::from_min_size(
            egui::pos2(start.x + index as f32 * (swatch_size + 3.0), start.y),
            egui::vec2(swatch_size, swatch_size),
        );
        ui.painter().rect_filled(rect, 2.0, color);
    }
    response
}

fn palette(theme: GuiTheme) -> [egui::Color32; 4] {
    let theme = match theme {
        GuiTheme::Latte => catppuccin_egui::LATTE,
        GuiTheme::Frappe => catppuccin_egui::FRAPPE,
        GuiTheme::Macchiato => catppuccin_egui::MACCHIATO,
        GuiTheme::Mocha => catppuccin_egui::MOCHA,
    };
    [theme.base, theme.blue, theme.green, theme.mauve].map(|color| {
        let [red, green, blue, alpha] = color.to_array();
        egui::Color32::from_rgba_unmultiplied(red, green, blue, alpha)
    })
}