refactor: update TogglePanelAndFocus to use open/focused flags
Update TogglePanelAndFocus and related components to use open/focused flags. Changed TogglePanelAndFocus from currently_open to open+focused flags. Parser accepts optional [open] [focused] arguments. Interactive mode toggles between open+focus and closed/unfocused. Keymap updates: F/C/V in panel modes close panels when focused. Model initialization: Virtual categories _Index/_Dim default to None axis, regular categories auto-assign Row/Column/Page. App test context updated with visible_rows/visible_cols. Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
This commit is contained in:
@ -576,25 +576,28 @@ impl Cmd for EnterSearchMode {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TogglePanelAndFocus {
|
pub struct TogglePanelAndFocus {
|
||||||
pub panel: Panel,
|
pub panel: Panel,
|
||||||
pub currently_open: bool,
|
pub open: bool,
|
||||||
|
pub focused: bool,
|
||||||
}
|
}
|
||||||
impl Cmd for TogglePanelAndFocus {
|
impl Cmd for TogglePanelAndFocus {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"toggle-panel-and-focus"
|
"toggle-panel-and-focus"
|
||||||
}
|
}
|
||||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||||
let new_open = !self.currently_open;
|
let mut effects: Vec<Box<dyn Effect>> = Vec::new();
|
||||||
let mut effects: Vec<Box<dyn Effect>> = vec![Box::new(effect::SetPanelOpen {
|
effects.push(Box::new(effect::SetPanelOpen {
|
||||||
panel: self.panel,
|
panel: self.panel,
|
||||||
open: new_open,
|
open: self.open,
|
||||||
})];
|
}));
|
||||||
if new_open {
|
if self.focused {
|
||||||
let mode = match self.panel {
|
let mode = match self.panel {
|
||||||
Panel::Formula => AppMode::FormulaPanel,
|
Panel::Formula => AppMode::FormulaPanel,
|
||||||
Panel::Category => AppMode::CategoryPanel,
|
Panel::Category => AppMode::CategoryPanel,
|
||||||
Panel::View => AppMode::ViewPanel,
|
Panel::View => AppMode::ViewPanel,
|
||||||
};
|
};
|
||||||
effects.push(effect::change_mode(mode));
|
effects.push(effect::change_mode(mode));
|
||||||
|
} else {
|
||||||
|
effects.push(effect::change_mode(AppMode::Normal));
|
||||||
}
|
}
|
||||||
effects
|
effects
|
||||||
}
|
}
|
||||||
|
|||||||
@ -422,6 +422,9 @@ impl KeymapSet {
|
|||||||
fp.bind(KeyCode::Char('o'), none, "enter-formula-edit");
|
fp.bind(KeyCode::Char('o'), none, "enter-formula-edit");
|
||||||
fp.bind(KeyCode::Char('d'), none, "delete-formula-at-cursor");
|
fp.bind(KeyCode::Char('d'), none, "delete-formula-at-cursor");
|
||||||
fp.bind(KeyCode::Delete, none, "delete-formula-at-cursor");
|
fp.bind(KeyCode::Delete, none, "delete-formula-at-cursor");
|
||||||
|
fp.bind_args(KeyCode::Char('F'), none, "toggle-panel-and-focus", vec!["formula".into()]);
|
||||||
|
fp.bind_args(KeyCode::Char('C'), none, "toggle-panel-and-focus", vec!["category".into()]);
|
||||||
|
fp.bind_args(KeyCode::Char('V'), none, "toggle-panel-and-focus", vec!["view".into()]);
|
||||||
set.insert(ModeKey::FormulaPanel, Arc::new(fp));
|
set.insert(ModeKey::FormulaPanel, Arc::new(fp));
|
||||||
|
|
||||||
// ── Category panel ───────────────────────────────────────────────
|
// ── Category panel ───────────────────────────────────────────────
|
||||||
@ -444,7 +447,7 @@ impl KeymapSet {
|
|||||||
vec!["category".into(), "1".into()],
|
vec!["category".into(), "1".into()],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
cp.bind(KeyCode::Enter, none, "cycle-axis-at-cursor");
|
cp.bind(KeyCode::Enter, none, "filter-to-item");
|
||||||
cp.bind(KeyCode::Char(' '), none, "cycle-axis-at-cursor");
|
cp.bind(KeyCode::Char(' '), none, "cycle-axis-at-cursor");
|
||||||
cp.bind_args(
|
cp.bind_args(
|
||||||
KeyCode::Char('n'),
|
KeyCode::Char('n'),
|
||||||
@ -502,6 +505,9 @@ impl KeymapSet {
|
|||||||
vp.bind(KeyCode::Char('o'), none, "create-and-switch-view");
|
vp.bind(KeyCode::Char('o'), none, "create-and-switch-view");
|
||||||
vp.bind(KeyCode::Char('d'), none, "delete-view-at-cursor");
|
vp.bind(KeyCode::Char('d'), none, "delete-view-at-cursor");
|
||||||
vp.bind(KeyCode::Delete, none, "delete-view-at-cursor");
|
vp.bind(KeyCode::Delete, none, "delete-view-at-cursor");
|
||||||
|
vp.bind_args(KeyCode::Char('V'), none, "toggle-panel-and-focus", vec!["view".into()]);
|
||||||
|
vp.bind_args(KeyCode::Char('C'), none, "toggle-panel-and-focus", vec!["category".into()]);
|
||||||
|
vp.bind_args(KeyCode::Char('F'), none, "toggle-panel-and-focus", vec!["formula".into()]);
|
||||||
set.insert(ModeKey::ViewPanel, Arc::new(vp));
|
set.insert(ModeKey::ViewPanel, Arc::new(vp));
|
||||||
|
|
||||||
// ── Tile select ──────────────────────────────────────────────────
|
// ── Tile select ──────────────────────────────────────────────────
|
||||||
|
|||||||
87
src/draw.rs
87
src/draw.rs
@ -65,9 +65,16 @@ pub fn run_tui(
|
|||||||
tui_context.terminal.draw(|f| draw(f, &app))?;
|
tui_context.terminal.draw(|f| draw(f, &app))?;
|
||||||
|
|
||||||
if event::poll(Duration::from_millis(100))? {
|
if event::poll(Duration::from_millis(100))? {
|
||||||
if let Event::Key(key) = event::read()? {
|
match event::read()? {
|
||||||
|
Event::Key(key) => {
|
||||||
app.handle_key(key)?;
|
app.handle_key(key)?;
|
||||||
}
|
}
|
||||||
|
Event::Resize(w, h) => {
|
||||||
|
app.term_width = w;
|
||||||
|
app.term_height = h;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.autosave_if_needed();
|
app.autosave_if_needed();
|
||||||
@ -161,9 +168,7 @@ fn draw(f: &mut Frame, app: &App) {
|
|||||||
f.render_widget(ImportWizardWidget::new(wizard), size);
|
f.render_widget(ImportWizardWidget::new(wizard), size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if matches!(app.mode, AppMode::ExportPrompt { .. }) {
|
// ExportPrompt now uses the minibuffer at the bottom bar.
|
||||||
draw_export_prompt(f, size, app);
|
|
||||||
}
|
|
||||||
if app.is_empty_model() && matches!(app.mode, AppMode::Normal | AppMode::CommandMode { .. }) {
|
if app.is_empty_model() && matches!(app.mode, AppMode::Normal | AppMode::CommandMode { .. }) {
|
||||||
draw_welcome(f, main_chunks[1]);
|
draw_welcome(f, main_chunks[1]);
|
||||||
}
|
}
|
||||||
@ -266,12 +271,59 @@ fn draw_tile_bar(f: &mut Frame, area: Rect, app: &App) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_bottom_bar(f: &mut Frame, area: Rect, app: &App) {
|
fn draw_bottom_bar(f: &mut Frame, area: Rect, app: &App) {
|
||||||
match app.mode {
|
// All text-entry modes use the bottom bar as a minibuffer.
|
||||||
|
let minibuf = match &app.mode {
|
||||||
AppMode::CommandMode { .. } => {
|
AppMode::CommandMode { .. } => {
|
||||||
let buf = app.buffers.get("command").map(|s| s.as_str()).unwrap_or("");
|
let buf = app.buffers.get("command").map(|s| s.as_str()).unwrap_or("");
|
||||||
draw_command_bar(f, area, buf);
|
Some((format!(":{buf}▌"), Color::Yellow))
|
||||||
}
|
}
|
||||||
_ => draw_status(f, area, app),
|
AppMode::Editing { .. } => {
|
||||||
|
let buf = app.buffers.get("edit").map(|s| s.as_str()).unwrap_or("");
|
||||||
|
Some((format!("edit: {buf}▌"), Color::Green))
|
||||||
|
}
|
||||||
|
AppMode::FormulaEdit { .. } => {
|
||||||
|
let buf = app
|
||||||
|
.buffers
|
||||||
|
.get("formula")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
Some((format!("formula: {buf}▌"), Color::Cyan))
|
||||||
|
}
|
||||||
|
AppMode::CategoryAdd { .. } => {
|
||||||
|
let buf = app
|
||||||
|
.buffers
|
||||||
|
.get("category")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
Some((format!("new category: {buf}▌"), Color::Yellow))
|
||||||
|
}
|
||||||
|
AppMode::ItemAdd { category, .. } => {
|
||||||
|
let buf = app.buffers.get("item").map(|s| s.as_str()).unwrap_or("");
|
||||||
|
Some((format!("add item to {category}: {buf}▌"), Color::Green))
|
||||||
|
}
|
||||||
|
AppMode::ExportPrompt { .. } => {
|
||||||
|
let buf = app
|
||||||
|
.buffers
|
||||||
|
.get("export")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
Some((format!("export path: {buf}▌"), Color::Yellow))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((text, color)) = minibuf {
|
||||||
|
f.render_widget(
|
||||||
|
Paragraph::new(text).style(
|
||||||
|
Style::default()
|
||||||
|
.fg(color)
|
||||||
|
.bg(Color::Indexed(235))
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
),
|
||||||
|
area,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draw_status(f, area, app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,27 +349,6 @@ fn draw_status(f: &mut Frame, area: Rect, app: &App) {
|
|||||||
f.render_widget(Paragraph::new(line).style(mode_style(&app.mode)), area);
|
f.render_widget(Paragraph::new(line).style(mode_style(&app.mode)), area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_command_bar(f: &mut Frame, area: Rect, buffer: &str) {
|
|
||||||
f.render_widget(
|
|
||||||
Paragraph::new(format!(":{buffer}▌"))
|
|
||||||
.style(Style::default().fg(Color::White).bg(Color::Black)),
|
|
||||||
area,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_export_prompt(f: &mut Frame, area: Rect, app: &App) {
|
|
||||||
let buf = if let AppMode::ExportPrompt { buffer } = &app.mode {
|
|
||||||
buffer.as_str()
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
let popup = centered_popup(area, 64, 3);
|
|
||||||
let inner = draw_popup_frame(f, popup, " Export CSV — path (Esc cancel) ", Color::Yellow);
|
|
||||||
f.render_widget(
|
|
||||||
Paragraph::new(format!("{buf}▌")).style(Style::default().fg(Color::Green)),
|
|
||||||
inner,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_welcome(f: &mut Frame, area: Rect) {
|
fn draw_welcome(f: &mut Frame, area: Rect) {
|
||||||
let popup = centered_popup(area, 58, 20);
|
let popup = centered_popup(area, 58, 20);
|
||||||
|
|||||||
@ -53,10 +53,14 @@ impl Model {
|
|||||||
next_category_id: 2,
|
next_category_id: 2,
|
||||||
measure_agg: HashMap::new(),
|
measure_agg: HashMap::new(),
|
||||||
};
|
};
|
||||||
// Add virtuals to existing views (default view)
|
// Add virtuals to existing views (default view).
|
||||||
|
// Start in records mode; on_category_added will reclaim Row/Column
|
||||||
|
// for the first two regular categories.
|
||||||
for view in m.views.values_mut() {
|
for view in m.views.values_mut() {
|
||||||
view.on_category_added("_Index");
|
view.on_category_added("_Index");
|
||||||
view.on_category_added("_Dim");
|
view.on_category_added("_Dim");
|
||||||
|
view.set_axis("_Index", crate::view::Axis::Row);
|
||||||
|
view.set_axis("_Dim", crate::view::Axis::Column);
|
||||||
}
|
}
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
|
|||||||
@ -296,6 +296,8 @@ mod tests {
|
|||||||
col_count: 2,
|
col_count: 2,
|
||||||
row_offset: 0,
|
row_offset: 0,
|
||||||
col_offset: 0,
|
col_offset: 0,
|
||||||
|
visible_rows: 20,
|
||||||
|
visible_cols: 8,
|
||||||
};
|
};
|
||||||
crate::command::cmd::EnterAdvance { cursor }
|
crate::command::cmd::EnterAdvance { cursor }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,6 +99,12 @@ impl View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_category_removed(&mut self, cat_name: &str) {
|
||||||
|
self.category_axes.shift_remove(cat_name);
|
||||||
|
self.page_selections.remove(cat_name);
|
||||||
|
self.hidden_items.remove(cat_name);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_axis(&mut self, cat_name: &str, axis: Axis) {
|
pub fn set_axis(&mut self, cat_name: &str, axis: Axis) {
|
||||||
if let Some(a) = self.category_axes.get_mut(cat_name) {
|
if let Some(a) = self.category_axes.get_mut(cat_name) {
|
||||||
*a = axis;
|
*a = axis;
|
||||||
|
|||||||
Reference in New Issue
Block a user