diff --git a/crates/gpui2/src/text_system.rs b/crates/gpui2/src/text_system.rs index b3d7a96aff9d2eaed8b5c9113be6b8d5f36b8873..76e21e5f7339cf2ac69e8caf3773e3456a56f3f8 100644 --- a/crates/gpui2/src/text_system.rs +++ b/crates/gpui2/src/text_system.rs @@ -196,7 +196,10 @@ impl TextSystem { let mut decoration_runs = SmallVec::<[DecorationRun; 32]>::new(); for run in runs { if let Some(last_run) = decoration_runs.last_mut() { - if last_run.color == run.color && last_run.underline == run.underline { + if last_run.color == run.color + && last_run.underline == run.underline + && last_run.background_color == run.background_color + { last_run.len += run.len as u32; continue; } @@ -204,6 +207,7 @@ impl TextSystem { decoration_runs.push(DecorationRun { len: run.len as u32, color: run.color, + background_color: run.background_color, underline: run.underline.clone(), }); } @@ -254,13 +258,16 @@ impl TextSystem { } if decoration_runs.last().map_or(false, |last_run| { - last_run.color == run.color && last_run.underline == run.underline + last_run.color == run.color + && last_run.underline == run.underline + && last_run.background_color == run.background_color }) { decoration_runs.last_mut().unwrap().len += run_len_within_line as u32; } else { decoration_runs.push(DecorationRun { len: run_len_within_line as u32, color: run.color, + background_color: run.background_color, underline: run.underline.clone(), }); } diff --git a/crates/gpui2/src/text_system/line.rs b/crates/gpui2/src/text_system/line.rs index d05ae9468dae491ed2ed130e6773fc1e754a3cf2..045a985ce7c1fbcb44d4afc6df88d9eee52ee75c 100644 --- a/crates/gpui2/src/text_system/line.rs +++ b/crates/gpui2/src/text_system/line.rs @@ -1,6 +1,7 @@ use crate::{ - black, point, px, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString, - UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout, + black, point, px, size, transparent_black, BorrowWindow, Bounds, Corners, Edges, Hsla, + LineLayout, Pixels, Point, Result, SharedString, UnderlineStyle, WindowContext, WrapBoundary, + WrappedLineLayout, }; use derive_more::{Deref, DerefMut}; use smallvec::SmallVec; @@ -10,6 +11,7 @@ use std::sync::Arc; pub struct DecorationRun { pub len: u32, pub color: Hsla, + pub background_color: Option, pub underline: Option, } @@ -97,6 +99,7 @@ fn paint_line( let mut run_end = 0; let mut color = black(); let mut current_underline: Option<(Point, UnderlineStyle)> = None; + let mut current_background: Option<(Point, Hsla)> = None; let text_system = cx.text_system().clone(); let mut glyph_origin = origin; let mut prev_glyph_position = Point::default(); @@ -110,12 +113,24 @@ fn paint_line( if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) { wraps.next(); + if let Some((background_origin, background_color)) = current_background.take() { + cx.paint_quad( + Bounds { + origin: background_origin, + size: size(glyph_origin.x - background_origin.x, line_height), + }, + Corners::default(), + background_color, + Edges::default(), + transparent_black(), + ); + } if let Some((underline_origin, underline_style)) = current_underline.take() { cx.paint_underline( underline_origin, glyph_origin.x - underline_origin.x, &underline_style, - )?; + ); } glyph_origin.x = origin.x; @@ -123,9 +138,20 @@ fn paint_line( } prev_glyph_position = glyph.position; + let mut finished_background: Option<(Point, Hsla)> = None; let mut finished_underline: Option<(Point, UnderlineStyle)> = None; if glyph.index >= run_end { if let Some(style_run) = decoration_runs.next() { + if let Some((_, background_color)) = &mut current_background { + if style_run.background_color.as_ref() != Some(background_color) { + finished_background = current_background.take(); + } + } + if let Some(run_background) = style_run.background_color { + current_background + .get_or_insert((point(glyph_origin.x, origin.y), run_background)); + } + if let Some((_, underline_style)) = &mut current_underline { if style_run.underline.as_ref() != Some(underline_style) { finished_underline = current_underline.take(); @@ -149,16 +175,30 @@ fn paint_line( color = style_run.color; } else { run_end = layout.len; + finished_background = current_background.take(); finished_underline = current_underline.take(); } } + if let Some((background_origin, background_color)) = finished_background { + cx.paint_quad( + Bounds { + origin: background_origin, + size: size(glyph_origin.x - background_origin.x, line_height), + }, + Corners::default(), + background_color, + Edges::default(), + transparent_black(), + ); + } + if let Some((underline_origin, underline_style)) = finished_underline { cx.paint_underline( underline_origin, glyph_origin.x - underline_origin.x, &underline_style, - )?; + ); } let max_glyph_bounds = Bounds { @@ -188,13 +228,27 @@ fn paint_line( } } + if let Some((background_origin, background_color)) = current_background.take() { + let line_end_x = origin.x + wrap_width.unwrap_or(Pixels::MAX).min(layout.width); + cx.paint_quad( + Bounds { + origin: background_origin, + size: size(line_end_x - background_origin.x, line_height), + }, + Corners::default(), + background_color, + Edges::default(), + transparent_black(), + ); + } + if let Some((underline_start, underline_style)) = current_underline.take() { let line_end_x = origin.x + wrap_width.unwrap_or(Pixels::MAX).min(layout.width); cx.paint_underline( underline_start, line_end_x - underline_start.x, &underline_style, - )?; + ); } Ok(()) diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 1973aa14a98dc258ee81e0a823e7f1be515f91eb..7b39089ae0773295f8cf5d34090acdaa67f10003 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -881,7 +881,7 @@ impl<'a> WindowContext<'a> { origin: Point, width: Pixels, style: &UnderlineStyle, - ) -> Result<()> { + ) { let scale_factor = self.scale_factor(); let height = if style.wavy { style.thickness * 3. @@ -905,7 +905,6 @@ impl<'a> WindowContext<'a> { wavy: style.wavy, }, ); - Ok(()) } /// Paint a monochrome (non-emoji) glyph into the scene for the current frame at the current z-index. diff --git a/crates/storybook2/src/stories/text.rs b/crates/storybook2/src/stories/text.rs index 4c478c929892e7a3fb0fab8026274ecaa225a423..42009136c414750de416dd3272e82955cb17ef60 100644 --- a/crates/storybook2/src/stories/text.rs +++ b/crates/storybook2/src/stories/text.rs @@ -1,6 +1,6 @@ use gpui::{ - blue, div, red, white, Div, InteractiveText, ParentElement, Render, Styled, StyledText, View, - VisualContext, WindowContext, + blue, div, green, red, white, Div, InteractiveText, ParentElement, Render, Styled, StyledText, + TextRun, View, VisualContext, WindowContext, }; use ui::v_stack; @@ -56,6 +56,21 @@ impl Render for TextStory { "flex-row. width 96. The quick brown fox jumps over the lazy dog. ", "Meanwhile, the lazy dog decided it was time for a change. ", "He started daily workout routines, ate healthier and became the fastest dog in town.", - ))).child(InteractiveText::new("interactive", StyledText::new("Hello world, how is it going?")).on_click(vec![2..4], |event, cx| {dbg!(event);})) + ))).child( + InteractiveText::new( + "interactive", + StyledText::new("Hello world, how is it going?").with_runs(vec![ + cx.text_style().to_run(6), + TextRun { + background_color: Some(green()), + ..cx.text_style().to_run(5) + }, + cx.text_style().to_run(18), + ]), + ) + .on_click(vec![2..4, 1..3, 7..9], |range_ix, cx| { + println!("Clicked range {range_ix}"); + }) + ) } }