buffer as a feature

This commit is contained in:
Priec
2025-08-22 13:47:34 +02:00
parent f49899e66d
commit f127298e5a
21 changed files with 40 additions and 39 deletions

View File

@@ -0,0 +1,35 @@
// src/buffer/functions/buffer.rs
use crate::buffer::state::BufferState;
use crate::buffer::state::AppView;
pub fn get_view_layer(view: &AppView) -> u8 {
match view {
AppView::Intro => 1,
AppView::Login | AppView::Register | AppView::Admin | AppView::AddTable | AppView::AddLogic => 2,
AppView::Form | AppView::Scratch => 3,
}
}
/// Switches the active buffer index.
pub fn switch_buffer(buffer_state: &mut BufferState, next: bool) -> bool {
if buffer_state.history.len() <= 1 {
return false;
}
let len = buffer_state.history.len();
let current_index = buffer_state.active_index;
let new_index = if next {
(current_index + 1) % len
} else {
(current_index + len - 1) % len
};
if new_index != current_index {
buffer_state.active_index = new_index;
true
} else {
false
}
}

9
client/src/buffer/mod.rs Normal file
View File

@@ -0,0 +1,9 @@
// src/buffer/mod.rs
pub mod state;
pub mod functions;
pub mod ui;
pub use state::{AppView, BufferState};
pub use functions::*;
pub use ui::render_buffer_list;

119
client/src/buffer/state.rs Normal file
View File

@@ -0,0 +1,119 @@
// src/buffer/state/buffer.rs
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AppView {
Intro,
Login,
Register,
Admin,
AddTable,
AddLogic,
Form,
Scratch,
}
impl AppView {
/// Returns the display name for the view.
/// For Form, pass the current table name to get dynamic naming.
pub fn display_name(&self) -> &str {
match self {
AppView::Intro => "Intro",
AppView::Login => "Login",
AppView::Register => "Register",
AppView::Admin => "Admin_Panel",
AppView::AddTable => "Add_Table",
AppView::AddLogic => "Add_Logic",
AppView::Form => "Form",
AppView::Scratch => "*scratch*",
}
}
/// Returns the display name with dynamic context (for Form buffers)
pub fn display_name_with_context(&self, current_table_name: Option<&str>) -> String {
match self {
AppView::Form => {
current_table_name
.unwrap_or("Data Form")
.to_string()
}
_ => self.display_name().to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct BufferState {
pub history: Vec<AppView>,
pub active_index: usize,
}
impl Default for BufferState {
fn default() -> Self {
Self {
history: vec![AppView::Intro],
active_index: 0,
}
}
}
impl BufferState {
pub fn update_history(&mut self, view: AppView) {
let existing_pos = self.history.iter().position(|v| v == &view);
match existing_pos {
Some(pos) => self.active_index = pos,
None => {
self.history.push(view.clone());
self.active_index = self.history.len() - 1;
}
}
}
pub fn get_active_view(&self) -> Option<&AppView> {
self.history.get(self.active_index)
}
pub fn close_active_buffer(&mut self) -> bool {
if self.history.is_empty() {
self.history.push(AppView::Intro);
self.active_index = 0;
return false;
}
let current_index = self.active_index;
self.history.remove(current_index);
if self.history.is_empty() {
self.history.push(AppView::Intro);
self.active_index = 0;
} else if self.active_index >= self.history.len() {
self.active_index = self.history.len() - 1;
}
true
}
pub fn close_buffer_with_intro_fallback(&mut self, current_table_name: Option<&str>) -> String {
let current_view_cloned = self.get_active_view().cloned();
if let Some(AppView::Intro) = current_view_cloned {
if self.history.len() == 1 {
self.close_active_buffer();
return "Intro buffer reset".to_string();
}
}
let closed_name = current_view_cloned
.as_ref()
.map(|v| v.display_name_with_context(current_table_name))
.unwrap_or_else(|| "Unknown".to_string());
if self.close_active_buffer() {
if self.history.len() == 1 && matches!(self.history.get(0), Some(AppView::Intro)) {
format!("Closed '{}' - returned to Intro", closed_name)
} else {
format!("Closed '{}'", closed_name)
}
} else {
format!("Buffer '{}' could not be closed", closed_name)
}
}
}

80
client/src/buffer/ui.rs Normal file
View File

@@ -0,0 +1,80 @@
// src/buffer/ui.rs
use crate::config::colors::themes::Theme;
use crate::buffer::state::BufferState;
use crate::state::app::state::AppState; // Add this import
use ratatui::{
layout::{Alignment, Rect},
style::Style,
text::{Line, Span},
widgets::Paragraph,
Frame,
};
use unicode_width::UnicodeWidthStr;
use crate::buffer::functions::get_view_layer;
pub fn render_buffer_list(
f: &mut Frame,
area: Rect,
theme: &Theme,
buffer_state: &BufferState,
app_state: &AppState,
) {
// --- Style Definitions ---
let active_style = Style::default()
.fg(theme.bg)
.bg(theme.highlight);
let inactive_style = Style::default()
.fg(theme.fg)
.bg(theme.bg);
// --- Determine Active Layer ---
let active_layer = match buffer_state.history.get(buffer_state.active_index) {
Some(view) => get_view_layer(view),
None => 1,
};
// --- Create Spans ---
let mut spans = Vec::new();
let mut current_width = 0;
let current_table_name = app_state.current_view_table_name.as_deref();
for (original_index, view) in buffer_state.history.iter().enumerate() {
// Filter: Only process views matching the active layer
if get_view_layer(view) != active_layer {
continue;
}
let is_active = original_index == buffer_state.active_index;
let buffer_name = view.display_name_with_context(current_table_name);
let buffer_text = format!(" {} ", buffer_name);
let text_width = UnicodeWidthStr::width(buffer_text.as_str());
// Calculate width needed for this buffer (separator + text)
let needed_width = text_width;
if current_width + needed_width > area.width as usize {
break;
}
// Add the buffer text itself
let text_style = if is_active { active_style } else { inactive_style };
spans.push(Span::styled(buffer_text, text_style));
current_width += text_width;
}
// --- Filler Span ---
let remaining_width = area.width.saturating_sub(current_width as u16);
if !spans.is_empty() || remaining_width > 0 {
spans.push(Span::styled(
" ".repeat(remaining_width as usize),
inactive_style,
));
}
// --- Render ---
let buffer_line = Line::from(spans);
let paragraph = Paragraph::new(buffer_line).alignment(Alignment::Left);
f.render_widget(paragraph, area);
}