fix: keep explorer selections readable on light terminals
This commit is contained in:
@@ -131,6 +131,7 @@ Diff LSP integration checks reuse the real framed-protocol subprocess with test-
|
|||||||
|
|
||||||
Changed-file explorer checks cover reuse of the compact directory tree, removed/historical paths, file-to-directory transitions, remapped tree/selection bindings, filter/open/focus transitions, right-dock geometry, and wheel routing without edits to the underlying document.
|
Changed-file explorer checks cover reuse of the compact directory tree, removed/historical paths, file-to-directory transitions, remapped tree/selection bindings, filter/open/focus transitions, right-dock geometry, and wheel routing without edits to the underlying document.
|
||||||
Visibility checks preserve browsing and pane state across Ctrl+e, exclude hidden explorers from focus/wheel routing, and verify full-width review. Status checks preserve catalog associations through sorting, compaction, duplicate inputs, and file/directory collisions, and cover colored A/M/D/R markers, selected/unfocused rows, Tree/List filtering, and sanitized rename paths.
|
Visibility checks preserve browsing and pane state across Ctrl+e, exclude hidden explorers from focus/wheel routing, and verify full-width review. Status checks preserve catalog associations through sorting, compaction, duplicate inputs, and file/directory collisions, and cover colored A/M/D/R markers, selected/unfocused rows, Tree/List filtering, and sanitized rename paths.
|
||||||
|
Light-terminal explorer selection checks render focused/unfocused rows through theme changes, require readable filename/status/directory contrast on Pumpkin Light, and retain dark/unknown-terminal fallbacks. A live PTY check answers native OSC color queries, opens the explorer with Space e, and observes the pale truecolor selection without changing source bytes.
|
||||||
Diff styling checks cover independent snapshot language/shebang/injection detection, accepted/stale snapshot identity, real-parser cache freshness/reuse/release, syntax foreground over changed backgrounds, real empty rows versus missing counterparts, scrolling-stable diagonal hatching, and tab/wide/combining/control-character clipping. TestBackend omits wide-glyph continuation cells; terminal smoke verifies their actual styling.
|
Diff styling checks cover independent snapshot language/shebang/injection detection, accepted/stale snapshot identity, real-parser cache freshness/reuse/release, syntax foreground over changed backgrounds, real empty rows versus missing counterparts, scrolling-stable diagonal hatching, and tab/wide/combining/control-character clipping. TestBackend omits wide-glyph continuation cells; terminal smoke verifies their actual styling.
|
||||||
|
|
||||||
## Performance and process limits
|
## Performance and process limits
|
||||||
|
|||||||
@@ -297,6 +297,9 @@ Pane-focus bindings also work while the ordinary file explorer or picker has foc
|
|||||||
|
|
||||||
Floating windows overlay the entire editor, above all workspace panes. File pickers, revision selectors, hover documentation, and completion lists are not clipped to the focused pane; completion remains anchored to its caret. Docked exploration reserves space at the editor edge before the remaining workspace is split into panes. The changed-file explorer follows the same layout, on the editor's right edge.
|
Floating windows overlay the entire editor, above all workspace panes. File pickers, revision selectors, hover documentation, and completion lists are not clipped to the focused pane; completion remains anchored to its caret. Docked exploration reserves space at the editor edge before the remaining workspace is split into panes. The changed-file explorer follows the same layout, on the editor's right edge.
|
||||||
|
|
||||||
|
On light terminals that answer color queries, selected explorer rows use a pale shade of the terminal background, preserving filename, directory and repository-status foregrounds when focused or unfocused. Dark terminals and unavailable color reports retain the ANSI blue/dark-gray selection backgrounds. Theme changes update visible and parked panes.
|
||||||
|
|
||||||
|
|
||||||
Diff comparisons reuse the split geometry and directional-focus engine. Option/Alt navigation moves between Before, After, and Files first, then to a neighboring workspace pane when there is no internal target in that direction. Splitting a diff text view creates an ordinary sibling showing the same buffer; historical snapshots remain read-only, including after the comparison closes.
|
Diff comparisons reuse the split geometry and directional-focus engine. Option/Alt navigation moves between Before, After, and Files first, then to a neighboring workspace pane when there is no internal target in that direction. Splitting a diff text view creates an ordinary sibling showing the same buffer; historical snapshots remain read-only, including after the comparison closes.
|
||||||
|
|
||||||
## Tabs
|
## Tabs
|
||||||
|
|||||||
+19
-2
@@ -484,9 +484,22 @@ impl BufferViewports {
|
|||||||
}
|
}
|
||||||
self.theme = theme;
|
self.theme = theme;
|
||||||
let colors = DiffColors::new(theme);
|
let colors = DiffColors::new(theme);
|
||||||
|
let file_selection_background = theme.and_then(|theme| {
|
||||||
|
let [r, g, b] = theme.background.map(u32::from);
|
||||||
|
if r * 299 + g * 587 + b * 114 < 128_000 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// Keep ANSI status/directory foregrounds readable on light terminals.
|
||||||
|
let [r, g, b] = theme
|
||||||
|
.background
|
||||||
|
.map(|c| ((u16::from(c) * 95 + 50) / 100) as u8);
|
||||||
|
Some(Color::Rgb(r, g, b))
|
||||||
|
});
|
||||||
self.panels.diff_colors = colors;
|
self.panels.diff_colors = colors;
|
||||||
|
self.panels.file_selection_background = file_selection_background;
|
||||||
for pane in self.parked.values_mut() {
|
for pane in self.parked.values_mut() {
|
||||||
pane.panels.diff_colors = colors;
|
pane.panels.diff_colors = colors;
|
||||||
|
pane.panels.file_selection_background = file_selection_background;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,6 +555,7 @@ impl BufferViewports {
|
|||||||
})()
|
})()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
inherited.panels.diff_colors = self.panels.diff_colors;
|
inherited.panels.diff_colors = self.panels.diff_colors;
|
||||||
|
inherited.panels.file_selection_background = self.panels.file_selection_background;
|
||||||
self.parked.insert(id, inherited);
|
self.parked.insert(id, inherited);
|
||||||
}
|
}
|
||||||
let outgoing = self
|
let outgoing = self
|
||||||
@@ -5699,6 +5713,7 @@ struct WheelPanels {
|
|||||||
picker_scroll: PickerScroll,
|
picker_scroll: PickerScroll,
|
||||||
diff: Option<DiffGeometry>,
|
diff: Option<DiffGeometry>,
|
||||||
diff_colors: DiffColors,
|
diff_colors: DiffColors,
|
||||||
|
file_selection_background: Option<Color>,
|
||||||
diff_syntax: Option<DiffSyntaxCache>,
|
diff_syntax: Option<DiffSyntaxCache>,
|
||||||
previous_diff: Option<DiffGeometry>,
|
previous_diff: Option<DiffGeometry>,
|
||||||
diff_wrap: Option<DiffWrapViewport>,
|
diff_wrap: Option<DiffWrapViewport>,
|
||||||
@@ -6380,11 +6395,13 @@ fn draw_file_list(
|
|||||||
};
|
};
|
||||||
if selected {
|
if selected {
|
||||||
style = style
|
style = style
|
||||||
.bg(if picker.focused {
|
.bg(panels
|
||||||
|
.file_selection_background
|
||||||
|
.unwrap_or(if picker.focused {
|
||||||
Color::Blue
|
Color::Blue
|
||||||
} else {
|
} else {
|
||||||
Color::DarkGray
|
Color::DarkGray
|
||||||
})
|
}))
|
||||||
.add_modifier(Modifier::BOLD);
|
.add_modifier(Modifier::BOLD);
|
||||||
}
|
}
|
||||||
let changed = entry
|
let changed = entry
|
||||||
|
|||||||
@@ -32,6 +32,74 @@ impl Drop for Directory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explorer_selection_follows_light_background_and_restores_dark_styles() {
|
||||||
|
let directory = Directory::new();
|
||||||
|
directory.file("arguments.hon", "return 0\n");
|
||||||
|
let mut picker =
|
||||||
|
FilePicker::from_paths(directory.0.clone(), [(0, PathBuf::from("arguments.hon"))]);
|
||||||
|
let light = ThemeColors {
|
||||||
|
background: [255, 246, 230],
|
||||||
|
red: [177, 52, 37],
|
||||||
|
green: [82, 101, 31],
|
||||||
|
};
|
||||||
|
let dark = ThemeColors {
|
||||||
|
background: [31, 23, 20],
|
||||||
|
red: [224, 120, 100],
|
||||||
|
green: [169, 182, 101],
|
||||||
|
};
|
||||||
|
let mut views = BufferViewports::default();
|
||||||
|
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(70, 12)).unwrap();
|
||||||
|
for theme in [Some(light), Some(dark), None, Some(light)] {
|
||||||
|
views.set_theme(theme);
|
||||||
|
for focused in [false, true] {
|
||||||
|
picker.focused = focused;
|
||||||
|
terminal
|
||||||
|
.draw(|frame| {
|
||||||
|
draw_file_list(frame, &picker, frame.area(), "", &mut views.panels, None);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let buffer = terminal.backend().buffer();
|
||||||
|
let cell = buffer
|
||||||
|
.content
|
||||||
|
.iter()
|
||||||
|
.find(|cell| cell.symbol() == "a" && cell.modifier.contains(Modifier::BOLD))
|
||||||
|
.expect("selected arguments.hon row");
|
||||||
|
if theme == Some(light) {
|
||||||
|
let Color::Rgb(r, g, b) = cell.bg else {
|
||||||
|
panic!("light selection must not use a dark ANSI background");
|
||||||
|
};
|
||||||
|
let luminance = |rgb: [u8; 3]| {
|
||||||
|
rgb.into_iter()
|
||||||
|
.zip([0.2126, 0.7152, 0.0722])
|
||||||
|
.map(|(c, w)| {
|
||||||
|
let c = f64::from(c) / 255.0;
|
||||||
|
w * if c <= 0.04045 {
|
||||||
|
c / 12.92
|
||||||
|
} else {
|
||||||
|
((c + 0.055) / 1.055).powf(2.4)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum::<f64>()
|
||||||
|
};
|
||||||
|
for foreground in [[69, 49, 38], [138, 98, 28], [153, 91, 43]] {
|
||||||
|
assert!((luminance([r, g, b]) + 0.05) / (luminance(foreground) + 0.05) >= 4.5);
|
||||||
|
}
|
||||||
|
assert_ne!([r, g, b], light.background, "selection must remain visible");
|
||||||
|
} else {
|
||||||
|
assert_eq!(
|
||||||
|
cell.bg,
|
||||||
|
if focused {
|
||||||
|
Color::Blue
|
||||||
|
} else {
|
||||||
|
Color::DarkGray
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn rendered_whitespace_line(
|
fn rendered_whitespace_line(
|
||||||
source: &str,
|
source: &str,
|
||||||
|
|||||||
Reference in New Issue
Block a user