W8a: Editor charset indicator + multi-segment status bar

Add 'Charset: UTF-8' to the editor status bar (TLC is UTF-8 native).
Upgrade status bar from a single-styled Span to a multi-segment Line
with per-segment accent colours so the status reads at a glance:
  - modified flag: warning colour (bold)
  - filename: accent colour
  - position (Ln/Col): info colour
  - tab/charset/eol labels: dim shadow colour
  - mode tag: warning colour

Tests: 1381 pass, zero warnings.
This commit is contained in:
2026-07-06 10:13:54 +03:00
parent 11d3665d7d
commit 5ad5e86e5c
@@ -90,7 +90,6 @@ impl Editor {
let marked_fg = editor_marked.map(|p| p.fg).unwrap_or(theme.marked_fg);
let marked_bg = editor_marked.map(|p| p.bg).unwrap_or(theme.marked_bg);
let linestate_fg = editor_linestate.map(|p| p.fg).unwrap_or(theme.cursor_fg);
let linestate_bg = editor_linestate.map(|p| p.bg).unwrap_or(theme.title_bg);
let frame_fg = editor_frameactive.map(|p| p.fg).unwrap_or(theme.title_fg);
let margin_fg = editor_rightmargin.map(|p| p.fg).unwrap_or(frame_fg);
let margin_bg = editor_rightmargin.map(|p| p.bg).unwrap_or(body_bg);
@@ -627,10 +626,7 @@ impl Editor {
// Status line + buttonbar.
if area.height >= 3 {
let status_y = area.y + area.height - 2;
let status = Paragraph::new(Line::from(Span::styled(
self.status_string(),
Style::default().fg(linestate_fg).bg(linestate_bg),
)));
let status = Paragraph::new(self.status_spans(theme));
frame.render_widget(status, Rect::new(area.x, status_y, area.width, 1));
let bar_y = area.y + area.height - 1;
@@ -643,10 +639,7 @@ impl Editor {
crate::widget::buttonbar::render_buttonbar(frame, bar_area, theme, &labels);
} else if area.height >= 2 {
let status_y = area.y + area.height - 1;
let status = Paragraph::new(Line::from(Span::styled(
self.status_string(),
Style::default().fg(linestate_fg).bg(linestate_bg),
)));
let status = Paragraph::new(self.status_spans(theme));
frame.render_widget(status, Rect::new(area.x, status_y, area.width, 1));
}
@@ -754,10 +747,19 @@ impl Editor {
line
}
pub(crate) fn status_string(&self) -> String {
/// Status line as a styled multi-segment `Line`. Each segment
/// uses an accent colour so the status reads at a glance:
/// modified flag (warning), filename (normal), position (info),
/// tab/charset/eol (dim), mode tag (accent).
pub(crate) fn status_spans(&self, theme: &Theme) -> Line<'static> {
let line = self.buffer_line_of(self.cursor.position()) + 1;
let col = self.cursor.visual_column() + 1;
let modified = if self.modified { "[+]" } else { " " };
let filename = self
.path
.as_ref()
.and_then(|p| p.file_name())
.map(|n| n.to_string_lossy().into_owned())
.unwrap_or_else(|| "(unnamed)".to_string());
let eol = self.buffer.eol();
let mode_tag = match self.mode {
Mode::Insert => if self.overwrite { " [OVR]" } else { "" },
@@ -776,21 +778,46 @@ impl Editor {
PromptKind::Sort => " [Sort]",
},
};
format!(
" {} Ln {}/{} Col {} Tab:{} EOL: {} Bytes: {} Path: {}{}",
modified,
line,
self.buffer.line_count(),
col,
self.tab_width,
eol,
self.buffer.len(),
self.path
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "<untitled>".to_string()),
mode_tag,
)
let linestate_fg = mc_skin::color_pair(theme.name, "editor", "editlinestate")
.map(|p| p.fg)
.unwrap_or(theme.foreground);
let linestate_bg = mc_skin::color_pair(theme.name, "editor", "editlinestate")
.map(|p| p.bg)
.unwrap_or(theme.background);
let base = Style::default().fg(linestate_fg).bg(linestate_bg);
let warn = Style::default()
.fg(theme.warning)
.bg(linestate_bg)
.add_modifier(Modifier::BOLD);
let info = Style::default().fg(theme.info).bg(linestate_bg);
let accent = Style::default().fg(theme.accent).bg(linestate_bg);
let dim = Style::default()
.fg(theme.shadow)
.bg(linestate_bg);
let modified_marker = if self.modified { "[+]" } else { " " };
let modified_style = if self.modified { warn } else { base };
Line::from(vec![
Span::styled(" ", base),
Span::styled(modified_marker, modified_style),
Span::styled(" ", base),
Span::styled(filename, accent),
Span::styled(" Ln ", base),
Span::styled(
format!("{}/{}", line, self.buffer.line_count()),
info,
),
Span::styled(" Col ", base),
Span::styled(format!("{col}"), info),
Span::styled(" Tab:", dim),
Span::styled(format!("{}", self.tab_width), dim),
Span::styled(" Charset:", dim),
Span::styled(" UTF-8", info),
Span::styled(" EOL:", dim),
Span::styled(format!(" {eol}"), info),
Span::styled(" Bytes:", dim),
Span::styled(format!(" {}", self.buffer.len()), info),
Span::styled(mode_tag, warn),
])
}
pub(crate) fn has_bookmark_on_line(&self, line: usize) -> bool {