fix: place help below both panes and status in table border
This commit is contained in:
+61
-43
@@ -152,18 +152,17 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn draw(&mut self, frame: &mut Frame, area: Rect, focused: bool) {
|
pub(crate) fn draw(&mut self, frame: &mut Frame, area: Rect, focused: bool) {
|
||||||
if area.width < 38 || area.height < 12 {
|
if area.width < 38 || area.height < 10 {
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new("Enlarge terminal to at least 70×12. q to quit."),
|
Paragraph::new("Enlarge terminal to at least 70×12. q to quit."),
|
||||||
area,
|
area,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [header, body, status, help] = Layout::vertical([
|
let [header, body, error_area] = Layout::vertical([
|
||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
Constraint::Min(4),
|
Constraint::Min(4),
|
||||||
Constraint::Length(2),
|
Constraint::Length(1),
|
||||||
Constraint::Length(2),
|
|
||||||
])
|
])
|
||||||
.areas(area);
|
.areas(area);
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
@@ -181,7 +180,6 @@ impl App {
|
|||||||
} else {
|
} else {
|
||||||
" Rows · s for schema "
|
" Rows · s for schema "
|
||||||
};
|
};
|
||||||
let content_block = panel(title, focused);
|
|
||||||
let count = if self.show_schema {
|
let count = if self.show_schema {
|
||||||
5
|
5
|
||||||
} else {
|
} else {
|
||||||
@@ -197,6 +195,40 @@ impl App {
|
|||||||
Constraint::Length(20)
|
Constraint::Length(20)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let description = if let Some(index) = self.active {
|
||||||
|
let relation = &self.relations[index];
|
||||||
|
if self.show_schema {
|
||||||
|
format!(
|
||||||
|
" {} [{}] · {} columns · fields {}–{}/5",
|
||||||
|
relation.label,
|
||||||
|
relation.kind,
|
||||||
|
self.columns.len(),
|
||||||
|
self.column_offset + 1,
|
||||||
|
self.column_offset + visible
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let start = if self.page.rows.is_empty() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
self.page_index * PAGE_SIZE + 1
|
||||||
|
};
|
||||||
|
let end = self.page_index * PAGE_SIZE + self.page.rows.len();
|
||||||
|
format!(
|
||||||
|
" {} [{}] · rows {start}–{end} · page {}{} · columns {}–{}/{}",
|
||||||
|
relation.label,
|
||||||
|
relation.kind,
|
||||||
|
self.page_index + 1,
|
||||||
|
if self.page.has_next { " (more)" } else { "" },
|
||||||
|
if count > 0 { self.column_offset + 1 } else { 0 },
|
||||||
|
self.column_offset + visible,
|
||||||
|
count
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
" No readable relations. r to refresh.".into()
|
||||||
|
};
|
||||||
|
let content_block =
|
||||||
|
panel(title, focused).title_bottom(Line::from(description).left_aligned());
|
||||||
if self.active.is_none() {
|
if self.active.is_none() {
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new("Choose a relation and press Enter.").block(content_block),
|
Paragraph::new("Choose a relation and press Enter.").block(content_block),
|
||||||
@@ -266,50 +298,12 @@ impl App {
|
|||||||
.highlight_symbol("› ");
|
.highlight_symbol("› ");
|
||||||
frame.render_stateful_widget(table, content, &mut self.rows_state);
|
frame.render_stateful_widget(table, content, &mut self.rows_state);
|
||||||
}
|
}
|
||||||
let description = if let Some(index) = self.active {
|
|
||||||
let relation = &self.relations[index];
|
|
||||||
if self.show_schema {
|
|
||||||
format!(
|
|
||||||
" {} [{}] · {} columns · fields {}–{}/5",
|
|
||||||
relation.label,
|
|
||||||
relation.kind,
|
|
||||||
self.columns.len(),
|
|
||||||
self.column_offset + 1,
|
|
||||||
self.column_offset + visible
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let start = if self.page.rows.is_empty() {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.page_index * PAGE_SIZE + 1
|
|
||||||
};
|
|
||||||
let end = self.page_index * PAGE_SIZE + self.page.rows.len();
|
|
||||||
format!(
|
|
||||||
" {} [{}] · rows {start}–{end} · page {}{} · columns {}–{}/{}",
|
|
||||||
relation.label,
|
|
||||||
relation.kind,
|
|
||||||
self.page_index + 1,
|
|
||||||
if self.page.has_next { " (more)" } else { "" },
|
|
||||||
if count > 0 { self.column_offset + 1 } else { 0 },
|
|
||||||
self.column_offset + visible,
|
|
||||||
count
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
" No readable relations. r to refresh.".into()
|
|
||||||
};
|
|
||||||
let [description_area, error_area] =
|
|
||||||
Layout::vertical([Constraint::Length(1), Constraint::Length(1)]).areas(status);
|
|
||||||
frame.render_widget(Paragraph::new(description), description_area);
|
|
||||||
if let Some(error) = &self.error {
|
if let Some(error) = &self.error {
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new(error.as_str()).style(Style::default().fg(Color::Red)),
|
Paragraph::new(error.as_str()).style(Style::default().fg(Color::Red)),
|
||||||
error_area,
|
error_area,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
frame.render_widget(Paragraph::new(
|
|
||||||
" Tab focus · Esc explorer · ↑↓/jk move · Enter open · s schema/rows\n ←→/hl columns · n/p page · r refresh · c add database · q quit"
|
|
||||||
).style(Style::default().fg(Color::DarkGray)), help);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,4 +352,28 @@ mod tests {
|
|||||||
assert_eq!(moved_index(Some(1), 3, isize::MIN), Some(0));
|
assert_eq!(moved_index(Some(1), 3, isize::MIN), Some(0));
|
||||||
assert_eq!(moved_index(Some(1), 3, isize::MAX), Some(2));
|
assert_eq!(moved_index(Some(1), 3, isize::MAX), Some(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn relation_status_is_in_the_bottom_border() -> crate::Result<()> {
|
||||||
|
let file = tempfile::NamedTempFile::new()?;
|
||||||
|
rusqlite::Connection::open(file.path())?.execute_batch(
|
||||||
|
"CREATE TABLE comments (id INTEGER); INSERT INTO comments VALUES (1);",
|
||||||
|
)?;
|
||||||
|
let mut app = super::App::new(crate::db::Database::open_sqlite(file.path())?)?;
|
||||||
|
let mut terminal = ratatui::Terminal::new(ratatui::backend::TestBackend::new(100, 20))?;
|
||||||
|
for schema in [false, true] {
|
||||||
|
app.show_schema = schema;
|
||||||
|
terminal.draw(|frame| app.draw(frame, frame.area(), true))?;
|
||||||
|
let buffer = terminal.backend().buffer();
|
||||||
|
let border: String = (0..100).map(|x| buffer[(x, 18)].symbol()).collect();
|
||||||
|
assert!(border.starts_with("└ main.comments [table]"), "{border}");
|
||||||
|
assert!(border.ends_with('┘'), "{border}");
|
||||||
|
assert!(border.contains(if schema {
|
||||||
|
"fields 1–4/5"
|
||||||
|
} else {
|
||||||
|
"rows 1–1"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -34,8 +34,10 @@ pub fn run(terminal: &mut DefaultTerminal, initial: Option<Database>) -> Result<
|
|||||||
expanded,
|
expanded,
|
||||||
);
|
);
|
||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
|
let [main, help] =
|
||||||
|
Layout::vertical([Constraint::Min(1), Constraint::Length(2)]).areas(frame.area());
|
||||||
let [sidebar, content] = Layout::horizontal([Constraint::Length(32), Constraint::Min(1)])
|
let [sidebar, content] = Layout::horizontal([Constraint::Length(32), Constraint::Min(1)])
|
||||||
.areas(frame.area());
|
.areas(main);
|
||||||
let items = entries.iter().map(|entry| {
|
let items = entries.iter().map(|entry| {
|
||||||
ListItem::new(match *entry {
|
ListItem::new(match *entry {
|
||||||
ExplorerEntry::Database(index) => {
|
ExplorerEntry::Database(index) => {
|
||||||
@@ -62,6 +64,9 @@ pub fn run(terminal: &mut DefaultTerminal, initial: Option<Database>) -> Result<
|
|||||||
if let Some(error) = &error {
|
if let Some(error) = &error {
|
||||||
frame.render_widget(Paragraph::new(error.as_str()).style(Style::default().fg(Color::Red)), Rect::new(content.x, content.bottom().saturating_sub(1), content.width, 1));
|
frame.render_widget(Paragraph::new(error.as_str()).style(Style::default().fg(Color::Red)), Rect::new(content.x, content.bottom().saturating_sub(1), content.width, 1));
|
||||||
}
|
}
|
||||||
|
frame.render_widget(Paragraph::new(
|
||||||
|
" Tab focus · Esc explorer · ↑↓/jk move · Enter open · s schema/rows\n ←→/hl columns · n/p page · r refresh · c add database · q quit"
|
||||||
|
).style(Style::default().fg(Color::DarkGray)), help);
|
||||||
if show_form {
|
if show_form {
|
||||||
form.draw(frame);
|
form.draw(frame);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ Navigation:
|
|||||||
r Refresh relations and the active relation
|
r Refresh relations and the active relation
|
||||||
Connected databases show their tables and views beneath them in the explorer.
|
Connected databases show their tables and views beneath them in the explorer.
|
||||||
Enter on the connected database collapses/expands it without reconnecting.
|
Enter on the connected database collapses/expands it without reconnecting.
|
||||||
|
Navigation help spans the bottom of the terminal beneath both panes.
|
||||||
|
Relation, page, and column status appears in the rows/schema bottom border.
|
||||||
q / Ctrl-C Quit
|
q / Ctrl-C Quit
|
||||||
New database window: Up/Down chooses a provider; Enter starts editing.
|
New database window: Up/Down chooses a provider; Enter starts editing.
|
||||||
Tab/Shift-Tab changes fields; Left/Right changes TLS when that field is focused.
|
Tab/Shift-Tab changes fields; Left/Right changes TLS when that field is focused.
|
||||||
|
|||||||
Reference in New Issue
Block a user