refactor(gui): split app.rs into models and panel modules

Extract types (models.rs), font settings, session controls, and send
panel into separate modules. Reduces app.rs from 1488 to 848 lines.
Zero behavior change.

- models.rs: ConnectionStatus, View, SendMode, LineEnding,
  DisplayOptions, SearchState, SessionTab
- panels/font_settings.rs: font selector and settings window
- panels/session_controls.rs: session connect/disconnect UI
- panels/send_panel.rs: send input and payload building
This commit is contained in:
2026-06-22 15:33:25 +08:00
parent cd3f778502
commit 5923ad0143
7 changed files with 691 additions and 669 deletions

View File

@@ -0,0 +1,133 @@
use crate::buffers::{HexBuffer, PlotBuffer, TextBuffer};
use crate::logging::LogWriter;
use crate::panels::plot_view;
use pipeview_client::config::SessionConfig;
#[derive(Clone)]
pub enum ConnectionStatus {
Connected,
Disconnected,
Connecting,
Error(String),
}
impl ConnectionStatus {
pub fn badge(&self) -> (&'static str, &'static str) {
match self {
Self::Connected => ("[connected]", "Connected"),
Self::Disconnected => ("[disconnected]", "Disconnected"),
Self::Connecting => ("[connecting]", "Connecting"),
Self::Error(_) => ("[error]", "Error"),
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum View {
Text,
Hex,
Plot,
}
#[derive(Clone, Copy, PartialEq)]
pub enum SendMode {
Text,
Hex,
}
#[derive(Clone, Copy, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
pub enum LineEnding {
None,
LF,
CR,
CRLF,
}
impl std::fmt::Display for LineEnding {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => write!(f, "None"),
Self::LF => write!(f, "LF (\\n)"),
Self::CR => write!(f, "CR (\\r)"),
Self::CRLF => write!(f, "CRLF (\\r\\n)"),
}
}
}
#[derive(Clone, Copy)]
pub struct DisplayOptions {
pub show_timestamp: bool,
pub show_direction: bool,
pub show_pipeline: bool,
}
#[derive(Clone, Default)]
pub struct SearchState {
pub query: String,
pub matches: Vec<usize>,
pub current_match: usize,
pub case_sensitive: bool,
pub active: bool,
pub just_opened: bool,
}
impl SearchState {
pub fn clear(&mut self) {
self.query.clear();
self.matches.clear();
self.current_match = 0;
self.active = false;
self.just_opened = false;
}
pub fn next(&mut self) {
if !self.matches.is_empty() {
self.current_match = (self.current_match + 1) % self.matches.len();
}
}
pub fn prev(&mut self) {
if !self.matches.is_empty() {
self.current_match = if self.current_match == 0 {
self.matches.len() - 1
} else {
self.current_match - 1
};
}
}
pub fn match_count(&self) -> usize {
self.matches.len()
}
pub fn current_display(&self) -> usize {
if self.matches.is_empty() {
0
} else {
self.current_match + 1
}
}
}
pub struct SessionTab {
pub id: u64,
pub session_config: SessionConfig,
pub status: ConnectionStatus,
pub console: TextBuffer,
pub hex: HexBuffer,
pub plot: PlotBuffer,
pub plot_view: plot_view::PlotViewState,
pub view: View,
pub auto_reconnect: bool,
pub dtr: bool,
pub rts: bool,
pub send_input: String,
pub send_mode: SendMode,
pub line_ending: LineEnding,
pub send_status: Option<String>,
pub search: SearchState,
pub log_enabled: bool,
pub log_path: String,
pub log_writer: Option<LogWriter>,
}