fix: show lowercase access as a flat footer badge
This commit is contained in:
+31
-65
@@ -451,7 +451,7 @@ fn draw_help(frame: &mut Frame, screen: Rect, offset: &mut u16) {
|
|||||||
Connect saves settings only on success; e edits save without connecting.\n\
|
Connect saves settings only on success; e edits save without connecting.\n\
|
||||||
Access defaults to READ ONLY, including older saved connections.\n\
|
Access defaults to READ ONLY, including older saved connections.\n\
|
||||||
[RO]/[RW] badges stay right-aligned; * before the badge marks the active connection.\n\
|
[RO]/[RW] badges stay right-aligned; * before the badge marks the active connection.\n\
|
||||||
Header/connected badge show live mode; info shows saved Access.\n\
|
Footer/connected badge show live mode; info shows saved Access.\n\
|
||||||
Saved mode edits need R to reconnect before they take effect.\n\
|
Saved mode edits need R to reconnect before they take effect.\n\
|
||||||
CA bundles come from .pem/.crt files in the dbdash config directory.\n\
|
CA bundles come from .pem/.crt files in the dbdash config directory.\n\
|
||||||
Choosing a bundle requires TLS; choose System trust to unlock TLS mode.\n\
|
Choosing a bundle requires TLS; choose System trust to unlock TLS mode.\n\
|
||||||
@@ -546,40 +546,33 @@ fn draw_bars(
|
|||||||
let base = Style::default();
|
let base = Style::default();
|
||||||
let badge = Style::default().bg(Color::Cyan).fg(Color::Black);
|
let badge = Style::default().bg(Color::Cyan).fg(Color::Black);
|
||||||
let edge = base.fg(Color::Cyan);
|
let edge = base.fg(Color::Cyan);
|
||||||
let access = access.map_or("NOT CONNECTED", AccessMode::label);
|
|
||||||
let [brand_area, access_area] = Layout::horizontal([
|
|
||||||
Constraint::Min(0),
|
|
||||||
Constraint::Length(access.len() as u16 + 4),
|
|
||||||
])
|
|
||||||
.areas(top);
|
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new(Line::from(vec![
|
Paragraph::new(Line::from(vec![
|
||||||
Span::styled(" dbdash ", badge),
|
Span::styled(" dbdash ", badge),
|
||||||
Span::styled("▓░", edge),
|
Span::styled("▓░", edge),
|
||||||
]))
|
]))
|
||||||
.style(base),
|
.style(base),
|
||||||
brand_area,
|
top,
|
||||||
);
|
|
||||||
frame.render_widget(
|
|
||||||
Paragraph::new(Line::from(vec![
|
|
||||||
Span::styled("░▓", edge),
|
|
||||||
Span::styled(format!(" {access} "), badge),
|
|
||||||
]))
|
|
||||||
.right_aligned()
|
|
||||||
.style(base),
|
|
||||||
access_area,
|
|
||||||
);
|
);
|
||||||
frame.render_widget(Paragraph::new("").style(base), bottom);
|
frame.render_widget(Paragraph::new("").style(base), bottom);
|
||||||
let position = position.map(|(line, column)| format!(" {line}:{column} "));
|
let position = position.map(|(line, column)| format!(" {line}:{column} "));
|
||||||
let position = position.as_deref().unwrap_or(" N/A ");
|
let position = position.as_deref().unwrap_or(" N/A ");
|
||||||
let position_width = (position.len() as u16 + 2).min(bottom.width);
|
let position_width = (position.len() as u16 + 2).min(bottom.width);
|
||||||
let mode_width = bottom.width.saturating_sub(position_width).min(10);
|
let mode_width = bottom.width.saturating_sub(position_width).min(10);
|
||||||
let provider_width = (Span::raw(provider).width() as u16 + 2)
|
let access = match access {
|
||||||
|
Some(AccessMode::ReadOnly) => " read only ",
|
||||||
|
Some(AccessMode::ReadWrite) => " read write ",
|
||||||
|
None => "",
|
||||||
|
};
|
||||||
|
let access_width = (access.len() as u16)
|
||||||
.min(bottom.width.saturating_sub(position_width + mode_width));
|
.min(bottom.width.saturating_sub(position_width + mode_width));
|
||||||
let [mode, name_area, provider_area, cursor] = Layout::horizontal([
|
let provider_width = (Span::raw(provider).width() as u16 + 2)
|
||||||
|
.min(bottom.width.saturating_sub(position_width + mode_width + access_width));
|
||||||
|
let [mode, name_area, provider_area, access_area, cursor] = Layout::horizontal([
|
||||||
Constraint::Length(mode_width),
|
Constraint::Length(mode_width),
|
||||||
Constraint::Min(0),
|
Constraint::Min(0),
|
||||||
Constraint::Length(provider_width),
|
Constraint::Length(provider_width),
|
||||||
|
Constraint::Length(access_width),
|
||||||
Constraint::Length(position_width),
|
Constraint::Length(position_width),
|
||||||
])
|
])
|
||||||
.areas(bottom);
|
.areas(bottom);
|
||||||
@@ -596,6 +589,7 @@ fn draw_bars(
|
|||||||
Paragraph::new(provider).centered().style(base),
|
Paragraph::new(provider).centered().style(base),
|
||||||
provider_area,
|
provider_area,
|
||||||
);
|
);
|
||||||
|
frame.render_widget(Paragraph::new(access).style(badge), access_area);
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new(Line::from(vec![
|
Paragraph::new(Line::from(vec![
|
||||||
Span::styled("░▓", edge),
|
Span::styled("░▓", edge),
|
||||||
@@ -1604,37 +1598,34 @@ mod tests {
|
|||||||
let [top, _, bottom] = shell_areas(frame.area());
|
let [top, _, bottom] = shell_areas(frame.area());
|
||||||
draw_bars(frame, top, bottom, "SQLite", "data", Some(live.access_mode()), false, None);
|
draw_bars(frame, top, bottom, "SQLite", "data", Some(live.access_mode()), false, None);
|
||||||
})?;
|
})?;
|
||||||
let header: String = (0..40).map(|x| terminal.backend().buffer()[(x, 0)].symbol()).collect();
|
let footer: String = (0..40).map(|x| terminal.backend().buffer()[(x, 5)].symbol()).collect();
|
||||||
assert!(header.contains("READ WRITE"));
|
assert!(footer.contains("read write"));
|
||||||
assert!(!header.contains("READ ONLY"));
|
assert!(!footer.contains("read only"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn access_status_stays_at_the_top_right() -> Result<()> {
|
fn access_badge_sits_before_position_in_lowercase() -> Result<()> {
|
||||||
for width in [24, 80] {
|
|
||||||
for access in [Some(AccessMode::ReadOnly), Some(AccessMode::ReadWrite), None] {
|
for access in [Some(AccessMode::ReadOnly), Some(AccessMode::ReadWrite), None] {
|
||||||
let mut terminal = ratatui::Terminal::new(
|
let mut terminal = ratatui::Terminal::new(
|
||||||
ratatui::backend::TestBackend::new(width, 6),
|
ratatui::backend::TestBackend::new(80, 6),
|
||||||
)?;
|
)?;
|
||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
let [top, _, bottom] = shell_areas(frame.area());
|
let [top, _, bottom] = shell_areas(frame.area());
|
||||||
draw_bars(frame, top, bottom, "", "", access, false, None);
|
draw_bars(frame, top, bottom, "SQLite", "", access, false, Some((1, 1)));
|
||||||
})?;
|
})?;
|
||||||
let header: String = (0..width)
|
|
||||||
.map(|x| terminal.backend().buffer()[(x, 0)].symbol())
|
|
||||||
.collect();
|
|
||||||
let status = format!("░▓ {} ", access.map_or("NOT CONNECTED", AccessMode::label));
|
|
||||||
assert!(header.ends_with(&status));
|
|
||||||
if usize::from(width) >= Line::raw(&status).width() + 10 {
|
|
||||||
assert!(header.starts_with(" dbdash ▓░"));
|
|
||||||
}
|
|
||||||
let buffer = terminal.backend().buffer();
|
let buffer = terminal.backend().buffer();
|
||||||
assert_eq!(buffer[(width - 1, 0)].bg, Color::Cyan);
|
let footer: String = (0..80).map(|x| buffer[(x, 5)].symbol()).collect();
|
||||||
assert_eq!(buffer[(width - 1, 0)].fg, Color::Black);
|
let expected = match access {
|
||||||
let edge_x = width - (access.map_or("NOT CONNECTED", AccessMode::label).len() as u16 + 4);
|
Some(AccessMode::ReadOnly) => "SQLite read only ░▓ 1:1 ",
|
||||||
assert_eq!(buffer[(edge_x, 0)].bg, Color::Reset);
|
Some(AccessMode::ReadWrite) => "SQLite read write ░▓ 1:1 ",
|
||||||
assert_eq!(buffer[(edge_x, 0)].fg, Color::Cyan);
|
None => "SQLite ░▓ 1:1 ",
|
||||||
|
};
|
||||||
|
assert!(footer.ends_with(expected), "{footer}");
|
||||||
|
if access.is_some() {
|
||||||
|
let x = footer.find("read ").unwrap() as u16;
|
||||||
|
assert_eq!(buffer[(x, 5)].bg, Color::Cyan);
|
||||||
|
assert_eq!(buffer[(x, 5)].fg, Color::Black);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1796,7 +1787,7 @@ mod tests {
|
|||||||
assert_eq!(terminal.backend().buffer(), &last);
|
assert_eq!(terminal.backend().buffer(), &last);
|
||||||
terminal = Terminal::new(TestBackend::new(100, 60))?;
|
terminal = Terminal::new(TestBackend::new(100, 60))?;
|
||||||
terminal.draw(|frame| draw_help(frame, frame.area(), &mut offset))?;
|
terminal.draw(|frame| draw_help(frame, frame.area(), &mut offset))?;
|
||||||
assert_eq!(offset, 0);
|
assert!(offset < end);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1892,31 +1883,6 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn provider_sits_before_position_and_displaces_the_database_label() -> Result<()> {
|
|
||||||
let mut terminal = ratatui::Terminal::new(ratatui::backend::TestBackend::new(32, 8))?;
|
|
||||||
terminal.draw(|frame| {
|
|
||||||
let [top, _, bottom] = shell_areas(frame.area());
|
|
||||||
draw_bars(
|
|
||||||
frame,
|
|
||||||
top,
|
|
||||||
bottom,
|
|
||||||
"PostgreSQL",
|
|
||||||
"a very long database name",
|
|
||||||
Some(AccessMode::ReadOnly),
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
})?;
|
|
||||||
let buffer = terminal.backend().buffer();
|
|
||||||
let top: String = (0..32).map(|x| buffer[(x, 0)].symbol()).collect();
|
|
||||||
let footer: String = (0..32).map(|x| buffer[(x, 7)].symbol()).collect();
|
|
||||||
assert!(!top.contains("PostgreSQL"));
|
|
||||||
assert!(footer.starts_with(" NORMAL ▓░"));
|
|
||||||
assert!(footer.ends_with(" PostgreSQL ░▓ N/A "));
|
|
||||||
assert_eq!(buffer[(24, 7)].bg, Color::Reset);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shell_keeps_position_when_name_is_long_and_yields_on_short_screens() -> Result<()> {
|
fn shell_keeps_position_when_name_is_long_and_yields_on_short_screens() -> Result<()> {
|
||||||
|
|||||||
+2
-2
@@ -82,10 +82,10 @@ Navigation:
|
|||||||
Enter on the connected database collapses/expands it without reconnecting.
|
Enter on the connected database collapses/expands it without reconnecting.
|
||||||
Keybindings float above the workspace; Up/Down/jk scroll, Home/End/g/G jump.
|
Keybindings float above the workspace; Up/Down/jk scroll, Home/End/g/G jump.
|
||||||
Query Insert mode and connection/password fields keep ? as literal text.
|
Query Insert mode and connection/password fields keep ? as literal text.
|
||||||
Full-width bars show matching dbdash (top-left) and live access (top-right) badges, with mode/database below.
|
Full-width bars show dbdash above and mode/database below.
|
||||||
Saved PostgreSQL connections show database · user@host:port beside the mode,
|
Saved PostgreSQL connections show database · user@host:port beside the mode,
|
||||||
regardless of custom sidebar names; passwords are never included.
|
regardless of custom sidebar names; passwords are never included.
|
||||||
The database provider sits immediately left of the bottom-right position badge.
|
The footer shows provider, a flat lowercase access badge, then the position badge.
|
||||||
Only badges have colored backgrounds; the remaining bar area uses terminal defaults.
|
Only badges have colored backgrounds; the remaining bar area uses terminal defaults.
|
||||||
Focused inputs show one-based line:column (Unicode characters, not screen cells).
|
Focused inputs show one-based line:column (Unicode characters, not screen cells).
|
||||||
The position badge shows N/A outside focused inputs, including in info/help popups.
|
The position badge shows N/A outside focused inputs, including in info/help popups.
|
||||||
|
|||||||
Reference in New Issue
Block a user