xtap-core-lib 0.5.1

星TAP实验室通用 Rust 基座:跨平台路径/IO/硬件/MCP HTTP 服务/egui 主题与字体(features 按需裁剪)
Documentation
/*
 * Copyright (c) 2026 星TAP实验室
 * Skill: 通用 UI 布局与主题 (Common UI Layout & Theme)
 */

use chrono::Timelike;
use egui::{Color32, Margin, Visuals};

/// UI 布局大师常量
pub struct UiTheme;

/// 主题模式
#[derive(Debug, Clone, Copy, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum ThemeMode {
    Auto,
    Light,
    Dark,
}

impl UiTheme {
    // --- 间距与尺寸 ---
    pub const PADDING: f32 = 20.0;
    pub const SPACING: f32 = 12.0;
    pub const ROUNDING: u8 = 12; // 更加圆润
    pub const BUTTON_HEIGHT: f32 = 36.0;
    pub const ICON_SIZE: f32 = 18.0;

    // --- 品牌颜色 (星TAP实验室 风格) ---
    pub const PRIMARY: Color32 = Color32::from_rgb(0, 120, 215);
    pub const SUCCESS: Color32 = Color32::from_rgb(16, 124, 16);
    pub const ERROR: Color32 = Color32::from_rgb(209, 52, 56);

    /// 获取标准的外边距
    pub fn standard_margin() -> Margin {
        Margin {
            left: Self::PADDING as i8,
            right: Self::PADDING as i8,
            top: Self::PADDING as i8,
            bottom: Self::PADDING as i8,
        }
    }

    /// 应用星TAP实验室推荐的视觉风格
    pub fn apply_master_visuals(ctx: &egui::Context, mode: ThemeMode) {
        let is_dark = match mode {
            ThemeMode::Light => false,
            ThemeMode::Dark => true,
            ThemeMode::Auto => {
                let hour = chrono::Local::now().hour();
                !(6..18).contains(&hour) // 晚 6 点到早 6 点为深色模式
            }
        };

        let mut visuals = if is_dark {
            let mut v = Visuals::dark();
            // 深色:深蓝灰质感 (更高级的深色)
            v.panel_fill = Color32::from_rgb(18, 20, 24);
            v.widgets.noninteractive.bg_fill = Color32::from_rgb(30, 33, 37);
            v.widgets.noninteractive.weak_bg_fill = Color32::from_rgb(40, 44, 49);
            v.extreme_bg_color = Color32::from_rgb(12, 14, 16);
            v.widgets.noninteractive.fg_stroke.color = Color32::from_gray(200);
            v.widgets.noninteractive.bg_stroke.color = Color32::from_gray(50); // 统一边框

            // 按钮统一圆角
            v.widgets.inactive.corner_radius = 12.0.into();
            v.widgets.hovered.corner_radius = 12.0.into();
            v.widgets.active.corner_radius = 12.0.into();
            v
        } else {
            let mut v = Visuals::light();
            // 浅色:纯净乳白/浅灰
            v.panel_fill = Color32::from_rgb(248, 249, 250);
            v.widgets.noninteractive.bg_fill = Color32::from_rgb(240, 242, 245);
            v.widgets.noninteractive.weak_bg_fill = Color32::from_rgb(255, 255, 255);
            v.extreme_bg_color = Color32::from_rgb(230, 233, 237);
            v.widgets.noninteractive.fg_stroke.color = Color32::from_rgb(45, 45, 45); // 深灰黑文字
            v.widgets.noninteractive.bg_stroke.color = Color32::from_gray(200); // 统一边框

            // 按钮统一圆角
            v.widgets.inactive.corner_radius = 12.0.into();
            v.widgets.hovered.corner_radius = 12.0.into();
            v.widgets.active.corner_radius = 12.0.into();
            v
        };

        // 统一窗口组件圆角
        visuals.window_corner_radius = 15.0.into();

        // 强化主色调交互
        visuals.selection.bg_fill = Self::PRIMARY;

        ctx.set_visuals(visuals);
    }

    /// [大师技巧] 快速居中排版辅助
    pub fn center_content<R>(
        ui: &mut egui::Ui,
        add_contents: impl FnOnce(&mut egui::Ui) -> R,
    ) -> R {
        ui.vertical_centered(|ui| {
            ui.add_space(ui.available_height() / 3.0);
            add_contents(ui)
        })
        .inner
    }
}