feat: JE-Skin 功能迁移 — One Dark Pro 工业风 + 录制导出

## One Dark Pro 主题
- 替换 ENGINEERING_DARK → ONE_DARK_PRO (#282C34 背景, #C678DD 紫色强调)
- 新增 ACCENT_GREEN/RED/BLUE/CYAN/ORANGE 独立色彩常量

## 录制模块 (recording.rs — 新建)
- 全量录制 + 快照录制两种模式
- 暂停/恢复/停止状态管理
- CSV 导出 (channel1,...,channelN,timestamp_ms)
- CSV 导入回放
- 线程安全 Arc<Mutex<>>
- 4 个单元测试

## 新 UI 组件 (ui.rs — +343 行)
- draw_signal_chart: 实时信号火花图 (min/max/current)
- draw_recording_toolbar: 录制控制栏 (全量/快照/暂停/导出/导入)
- draw_export_panel: 浮动录制导出面板
- draw_matrix_config_panel: 矩阵配置 (行/列/色域/预设 12x7~64x32)
- 连接面板集成录制工具栏 (连接后自动显示)

## 应用集成 (app.rs)
- 集成 Recorder, 信号历史, 导出面板, 矩阵配置面板
- 每帧数据自动送入录制器
- 信号历史环形缓冲 (128 帧)
This commit is contained in:
2026-05-20 17:27:38 +08:00
parent 2f16c4762f
commit 174adb5366
5 changed files with 823 additions and 59 deletions

View File

@@ -2,15 +2,17 @@ use eframe::{egui, egui_wgpu};
use std::sync::Arc;
use crate::connection::ConnectionManager;
use crate::theme::{ENGINEERING_DARK, apply_fonts, apply_theme};
use crate::recording::Recorder;
use crate::theme::{ONE_DARK_PRO, apply_fonts, apply_theme};
use crate::{
matrix::{MATRIX_COLS, MATRIX_ROWS},
render::{
BackgroundRenderResources, PRESSURE_CELL_COUNT, PressureFrame, WgpuBackgroundCallback,
},
ui::{
ConfigPanelState, ConnectPanelState, FloatingPanelState, draw_config_panel,
draw_connect_panel, draw_scene_panel, draw_stats_panel,
ConfigPanelState, ConnectPanelState, FloatingPanelState, MatrixConfigState,
draw_config_panel, draw_connect_panel, draw_export_panel, draw_matrix_config_panel,
draw_scene_panel, draw_signal_chart, draw_stats_panel, draw_recording_toolbar,
},
};
@@ -26,13 +28,20 @@ pub struct EskinDesktopApp {
config_panel: FloatingPanelState,
config_state: ConfigPanelState,
stats_panel: FloatingPanelState,
// New: recording, export, matrix config, signal charts
recorder: Recorder,
export_panel: FloatingPanelState,
export_path: String,
matrix_config_panel: FloatingPanelState,
matrix_config: MatrixConfigState,
signal_history: Vec<f32>,
}
impl EskinDesktopApp {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
egui_extras::install_image_loaders(&cc.egui_ctx);
apply_fonts(&cc.egui_ctx);
apply_theme(&cc.egui_ctx, &ENGINEERING_DARK);
apply_theme(&cc.egui_ctx, &ONE_DARK_PRO);
let wgpu_state = cc
.wgpu_render_state
@@ -59,6 +68,12 @@ impl EskinDesktopApp {
config_panel: FloatingPanelState::new([840.0, 48.0], [128.0, 48.0]),
config_state: ConfigPanelState::default(),
stats_panel: FloatingPanelState::new([16.0, 520.0], [240.0, 48.0]),
recorder: Recorder::new(),
export_panel: FloatingPanelState::new([16.0, 280.0], [16.0, 280.0]),
export_path: String::new(),
matrix_config_panel: FloatingPanelState::new([840.0, 280.0], [400.0, 48.0]),
matrix_config: MatrixConfigState::default(),
signal_history: Vec::with_capacity(128),
}
}
@@ -87,6 +102,17 @@ impl EskinDesktopApp {
sample.cols,
&mut self.pressure_matrix,
);
// Feed data to recorder
self.recorder.add_frame(&sample.matrix);
// Update signal history (sum of all pressure values)
let total: f32 = sample.matrix.iter().map(|v| *v as f32).sum();
self.signal_history.push(total);
if self.signal_history.len() > 128 {
self.signal_history.remove(0);
}
self.data_log_frame += 1;
if self.data_log_frame % DATA_LOG_EVERY_FRAMES == 0 {
log_pressure_sample(&sample.matrix, sample.rows, sample.cols);
@@ -104,11 +130,11 @@ impl EskinDesktopApp {
ui.painter().rect_filled(
title_bar_rect,
egui::CornerRadius::ZERO,
ENGINEERING_DARK.panel_deep,
ONE_DARK_PRO.panel_deep,
);
ui.painter().line_segment(
[title_bar_rect.left_bottom(), title_bar_rect.right_bottom()],
egui::Stroke::new(1.0, ENGINEERING_DARK.border),
egui::Stroke::new(1.0, ONE_DARK_PRO.border),
);
// Drag-to-move: double-click to maximize, drag to move
@@ -211,10 +237,14 @@ impl EskinDesktopApp {
&mut self.connect_panel,
&mut self.connect_state,
&self.connection,
&self.recorder,
&mut self.export_path,
);
draw_scene_panel(ctx, &mut self.scene_panel);
draw_config_panel(ctx, &mut self.config_panel, &mut self.config_state);
draw_stats_panel(ctx, &mut self.stats_panel);
draw_export_panel(ctx, &mut self.export_panel, &self.recorder, &mut self.export_path);
draw_matrix_config_panel(ctx, &mut self.matrix_config_panel, &mut self.matrix_config);
}
}