parent
e1df09a82b
commit
e201a64763
|
|
@ -179,8 +179,16 @@ impl From<ffi::GhosttyColorRgb> for RgbColor {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CellColor {
|
||||
Palette(u8),
|
||||
Rgb(RgbColor),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct CellStyle {
|
||||
pub fg_color: Option<CellColor>,
|
||||
pub bg_color: Option<CellColor>,
|
||||
pub bold: bool,
|
||||
pub italic: bool,
|
||||
pub faint: bool,
|
||||
|
|
@ -195,6 +203,8 @@ pub struct CellStyle {
|
|||
impl From<ffi::GhosttyStyle> for CellStyle {
|
||||
fn from(value: ffi::GhosttyStyle) -> Self {
|
||||
Self {
|
||||
fg_color: cell_color_from_style_color(value.fg_color),
|
||||
bg_color: cell_color_from_style_color(value.bg_color),
|
||||
bold: value.bold,
|
||||
italic: value.italic,
|
||||
faint: value.faint,
|
||||
|
|
@ -208,6 +218,20 @@ impl From<ffi::GhosttyStyle> for CellStyle {
|
|||
}
|
||||
}
|
||||
|
||||
fn cell_color_from_style_color(color: ffi::GhosttyStyleColor) -> Option<CellColor> {
|
||||
match color.tag {
|
||||
ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_PALETTE => {
|
||||
// SAFETY: Ghostty's tagged union stores `palette` when the tag is PALETTE.
|
||||
Some(CellColor::Palette(unsafe { color.value.palette }))
|
||||
}
|
||||
ffi::GhosttyStyleColorTag_GHOSTTY_STYLE_COLOR_RGB => {
|
||||
// SAFETY: Ghostty's tagged union stores `rgb` when the tag is RGB.
|
||||
Some(CellColor::Rgb(unsafe { color.value.rgb }.into()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RenderColors {
|
||||
pub background: RgbColor,
|
||||
|
|
@ -1198,6 +1222,47 @@ impl<'a> RowCellIter<'a> {
|
|||
Ok(style.into())
|
||||
}
|
||||
|
||||
pub fn content_bg_color(&self) -> Result<Option<CellColor>, Error> {
|
||||
let raw = self.raw_cell()?;
|
||||
let mut tag = ffi::GhosttyCellContentTag_GHOSTTY_CELL_CONTENT_CODEPOINT;
|
||||
unsafe {
|
||||
ffi::ghostty_cell_get(
|
||||
raw,
|
||||
ffi::GhosttyCellData_GHOSTTY_CELL_DATA_CONTENT_TAG,
|
||||
(&mut tag as *mut ffi::GhosttyCellContentTag).cast(),
|
||||
)
|
||||
.into_result()?;
|
||||
}
|
||||
|
||||
match tag {
|
||||
ffi::GhosttyCellContentTag_GHOSTTY_CELL_CONTENT_BG_COLOR_PALETTE => {
|
||||
let mut index = 0u8;
|
||||
unsafe {
|
||||
ffi::ghostty_cell_get(
|
||||
raw,
|
||||
ffi::GhosttyCellData_GHOSTTY_CELL_DATA_COLOR_PALETTE,
|
||||
(&mut index as *mut u8).cast(),
|
||||
)
|
||||
.into_result()?;
|
||||
}
|
||||
Ok(Some(CellColor::Palette(index)))
|
||||
}
|
||||
ffi::GhosttyCellContentTag_GHOSTTY_CELL_CONTENT_BG_COLOR_RGB => {
|
||||
let mut color = ffi::GhosttyColorRgb::default();
|
||||
unsafe {
|
||||
ffi::ghostty_cell_get(
|
||||
raw,
|
||||
ffi::GhosttyCellData_GHOSTTY_CELL_DATA_COLOR_RGB,
|
||||
(&mut color as *mut ffi::GhosttyColorRgb).cast(),
|
||||
)
|
||||
.into_result()?;
|
||||
}
|
||||
Ok(Some(CellColor::Rgb(color.into())))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fg_color(&self) -> Result<Option<RgbColor>, Error> {
|
||||
let mut color = ffi::GhosttyColorRgb::default();
|
||||
let result = unsafe {
|
||||
|
|
|
|||
|
|
@ -987,17 +987,18 @@ fn ghostty_cell_style(
|
|||
resolved_bg: Option<Color>,
|
||||
) -> Style {
|
||||
let style_data = cells.style().unwrap_or_default();
|
||||
let mut fg = cells
|
||||
.fg_color()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(ghostty_color)
|
||||
let mut fg = style_data
|
||||
.fg_color
|
||||
.map(ghostty_cell_color)
|
||||
.or_else(|| cells.fg_color().ok().flatten().map(ghostty_color))
|
||||
.or(default_fg);
|
||||
let mut bg = cells
|
||||
.bg_color()
|
||||
.content_bg_color()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(ghostty_color)
|
||||
.or(style_data.bg_color)
|
||||
.map(ghostty_cell_color)
|
||||
.or_else(|| cells.bg_color().ok().flatten().map(ghostty_color))
|
||||
.or(default_bg);
|
||||
if style_data.invisible {
|
||||
fg = bg.or(default_bg);
|
||||
|
|
@ -1073,6 +1074,13 @@ fn terminal_theme_color(color: crate::ghostty::RgbColor) -> crate::terminal_them
|
|||
}
|
||||
}
|
||||
|
||||
fn ghostty_cell_color(color: crate::ghostty::CellColor) -> Color {
|
||||
match color {
|
||||
crate::ghostty::CellColor::Palette(index) => Color::Indexed(index),
|
||||
crate::ghostty::CellColor::Rgb(color) => ghostty_color(color),
|
||||
}
|
||||
}
|
||||
|
||||
fn ghostty_color(color: crate::ghostty::RgbColor) -> Color {
|
||||
Color::Rgb(color.r, color.g, color.b)
|
||||
}
|
||||
|
|
@ -1643,6 +1651,81 @@ mod tests {
|
|||
assert_eq!(buffer[(2, 0)].style().bg, Some(Color::Reset));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_preserves_palette_colors_instead_of_flattening_to_rgb() {
|
||||
let (tx, _rx) = mpsc::channel(4);
|
||||
let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();
|
||||
let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
|
||||
{
|
||||
let mut core = pane.core.lock().unwrap();
|
||||
core.terminal.write(
|
||||
b"\x1b[31mR\x1b[0m \x1b[38;5;171mI\x1b[0m \x1b[48;5;4mB\x1b[0m \x1b[38;2;1;2;3mT",
|
||||
);
|
||||
}
|
||||
|
||||
let backend = ratatui::backend::TestBackend::new(20, 5);
|
||||
let mut terminal = ratatui::Terminal::new(backend).unwrap();
|
||||
terminal
|
||||
.draw(|frame| pane.render(frame, Rect::new(0, 0, 20, 5), false))
|
||||
.unwrap();
|
||||
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(0, 0)].symbol(), "R");
|
||||
assert_eq!(buffer[(0, 0)].style().fg, Some(Color::Indexed(1)));
|
||||
assert_eq!(buffer[(2, 0)].symbol(), "I");
|
||||
assert_eq!(buffer[(2, 0)].style().fg, Some(Color::Indexed(171)));
|
||||
assert_eq!(buffer[(4, 0)].symbol(), "B");
|
||||
assert_eq!(buffer[(4, 0)].style().bg, Some(Color::Indexed(4)));
|
||||
assert_eq!(buffer[(6, 0)].symbol(), "T");
|
||||
assert_eq!(buffer[(6, 0)].style().fg, Some(Color::Rgb(1, 2, 3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_preserves_palette_background_fill_cells() {
|
||||
let (tx, _rx) = mpsc::channel(4);
|
||||
let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();
|
||||
let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
|
||||
{
|
||||
let mut core = pane.core.lock().unwrap();
|
||||
core.terminal.write(b"\x1b[48;5;4m\x1b[K");
|
||||
}
|
||||
|
||||
let backend = ratatui::backend::TestBackend::new(20, 5);
|
||||
let mut terminal = ratatui::Terminal::new(backend).unwrap();
|
||||
terminal
|
||||
.draw(|frame| pane.render(frame, Rect::new(0, 0, 20, 5), false))
|
||||
.unwrap();
|
||||
|
||||
let buffer = terminal.backend().buffer();
|
||||
for x in 0..20 {
|
||||
assert_eq!(buffer[(x, 0)].symbol(), " ");
|
||||
assert_eq!(buffer[(x, 0)].style().bg, Some(Color::Indexed(4)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_preserves_rgb_background_fill_cells() {
|
||||
let (tx, _rx) = mpsc::channel(4);
|
||||
let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();
|
||||
let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
|
||||
{
|
||||
let mut core = pane.core.lock().unwrap();
|
||||
core.terminal.write(b"\x1b[48;2;17;34;51m\x1b[K");
|
||||
}
|
||||
|
||||
let backend = ratatui::backend::TestBackend::new(20, 5);
|
||||
let mut terminal = ratatui::Terminal::new(backend).unwrap();
|
||||
terminal
|
||||
.draw(|frame| pane.render(frame, Rect::new(0, 0, 20, 5), false))
|
||||
.unwrap();
|
||||
|
||||
let buffer = terminal.backend().buffer();
|
||||
for x in 0..20 {
|
||||
assert_eq!(buffer[(x, 0)].symbol(), " ");
|
||||
assert_eq!(buffer[(x, 0)].style().bg, Some(Color::Rgb(17, 34, 51)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_leaves_host_default_background_transparent() {
|
||||
let (tx, _rx) = mpsc::channel(4);
|
||||
|
|
|
|||
Loading…
Reference in New Issue