Detailed changes
@@ -82,11 +82,11 @@ impl gpui::Element for TextElement {
text,
font_size,
&[
- (0..1, normal),
- (1..2, bold),
- (2..3, normal),
- (3..4, bold),
- (4..text.len(), normal),
+ (1, normal, ColorU::default()),
+ (1, bold, ColorU::default()),
+ (1, normal, ColorU::default()),
+ (1, bold, ColorU::default()),
+ (text.len() - 4, normal, ColorU::default()),
],
);
@@ -95,12 +95,7 @@ impl gpui::Element for TextElement {
background: Some(ColorU::white()),
..Default::default()
});
- line.paint(
- bounds.origin(),
- bounds,
- &[(0..text.len(), ColorU::black())],
- ctx,
- );
+ line.paint(bounds.origin(), bounds, ctx);
}
fn dispatch_event(
@@ -1,4 +1,5 @@
use serde_json::json;
+use smallvec::{smallvec, SmallVec};
use crate::{
color::ColorU,
@@ -13,7 +14,6 @@ use crate::{
AfterLayoutContext, DebugContext, Element, Event, EventContext, FontCache, LayoutContext,
PaintContext, SizeConstraint,
};
-use std::{ops::Range, sync::Arc};
pub struct Label {
text: String,
@@ -23,11 +23,6 @@ pub struct Label {
highlights: Option<Highlights>,
}
-pub struct LayoutState {
- line: Arc<Line>,
- colors: Vec<(Range<usize>, ColorU)>,
-}
-
pub struct Highlights {
color: ColorU,
indices: Vec<usize>,
@@ -59,60 +54,56 @@ impl Label {
self
}
- fn layout_text(
+ fn compute_runs(
&self,
font_cache: &FontCache,
font_id: FontId,
- ) -> (Vec<(Range<usize>, FontId)>, Vec<(Range<usize>, ColorU)>) {
- let text_len = self.text.len();
- let mut styles;
- let mut colors;
+ ) -> SmallVec<[(usize, FontId, ColorU); 8]> {
if let Some(highlights) = self.highlights.as_ref() {
- styles = Vec::new();
- colors = Vec::new();
let highlight_font_id = font_cache
.select_font(self.family_id, &highlights.font_properties)
.unwrap_or(font_id);
- let mut pending_highlight: Option<Range<usize>> = None;
- for ix in &highlights.indices {
- if let Some(pending_highlight) = pending_highlight.as_mut() {
- if *ix == pending_highlight.end {
- pending_highlight.end += 1;
- } else {
- styles.push((pending_highlight.clone(), highlight_font_id));
- colors.push((pending_highlight.clone(), highlights.color));
- styles.push((pending_highlight.end..*ix, font_id));
- colors.push((pending_highlight.end..*ix, ColorU::black()));
- *pending_highlight = *ix..*ix + 1;
+
+ let mut highlight_indices = highlights.indices.iter().copied().peekable();
+ let mut runs = SmallVec::new();
+
+ for (char_ix, c) in self.text.char_indices() {
+ let mut font_id = font_id;
+ let mut color = ColorU::black();
+ if let Some(highlight_ix) = highlight_indices.peek() {
+ if char_ix == *highlight_ix {
+ font_id = highlight_font_id;
+ color = highlights.color;
+ highlight_indices.next();
}
- } else {
- styles.push((0..*ix, font_id));
- colors.push((0..*ix, ColorU::black()));
- pending_highlight = Some(*ix..*ix + 1);
}
- }
- if let Some(pending_highlight) = pending_highlight.as_mut() {
- styles.push((pending_highlight.clone(), highlight_font_id));
- colors.push((pending_highlight.clone(), highlights.color));
- if text_len > pending_highlight.end {
- styles.push((pending_highlight.end..text_len, font_id));
- colors.push((pending_highlight.end..text_len, ColorU::black()));
+
+ let push_new_run =
+ if let Some((last_len, last_font_id, last_color)) = runs.last_mut() {
+ if font_id == *last_font_id && color == *last_color {
+ *last_len += c.len_utf8();
+ false
+ } else {
+ true
+ }
+ } else {
+ true
+ };
+
+ if push_new_run {
+ runs.push((c.len_utf8(), font_id, color));
}
- } else {
- styles.push((0..text_len, font_id));
- colors.push((0..text_len, ColorU::black()));
}
+
+ runs
} else {
- styles = vec![(0..text_len, font_id)];
- colors = vec![(0..text_len, ColorU::black())];
+ smallvec![(self.text.len(), font_id, ColorU::black())]
}
-
- (styles, colors)
}
}
impl Element for Label {
- type LayoutState = LayoutState;
+ type LayoutState = Line;
type PaintState = ();
fn layout(
@@ -124,17 +115,17 @@ impl Element for Label {
.font_cache
.select_font(self.family_id, &self.font_properties)
.unwrap();
- let (styles, colors) = self.layout_text(&ctx.font_cache, font_id);
+ let runs = self.compute_runs(&ctx.font_cache, font_id);
let line =
ctx.text_layout_cache
- .layout_str(self.text.as_str(), self.font_size, styles.as_slice());
+ .layout_str(self.text.as_str(), self.font_size, runs.as_slice());
let size = vec2f(
- line.width.max(constraint.min.x()).min(constraint.max.x()),
+ line.width().max(constraint.min.x()).min(constraint.max.x()),
ctx.font_cache.line_height(font_id, self.font_size).ceil(),
);
- (size, LayoutState { line, colors })
+ (size, line)
}
fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
@@ -143,13 +134,12 @@ impl Element for Label {
fn paint(
&mut self,
bounds: RectF,
- layout: &mut Self::LayoutState,
+ line: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
- layout.line.paint(
+ line.paint(
bounds.origin(),
RectF::new(vec2f(0., 0.), bounds.size()),
- layout.colors.as_slice(),
ctx,
)
}
@@ -226,28 +216,21 @@ mod tests {
],
);
- let (styles, colors) = label.layout_text(app.font_cache().as_ref(), menlo_regular);
- assert_eq!(styles.len(), colors.len());
-
- let mut spans = Vec::new();
- for ((style_range, font_id), (color_range, color)) in styles.into_iter().zip(colors) {
- assert_eq!(style_range, color_range);
- spans.push((style_range, font_id, color));
- }
+ let runs = label.compute_runs(app.font_cache().as_ref(), menlo_regular);
assert_eq!(
- spans,
+ runs.as_slice(),
&[
- (0..3, menlo_regular, black),
- (3..4, menlo_bold, red),
- (4..5, menlo_regular, black),
- (5..6, menlo_bold, red),
- (6..9, menlo_regular, black),
- (9..10, menlo_bold, red),
- (10..15, menlo_regular, black),
- (15..16, menlo_bold, red),
- (16..18, menlo_regular, black),
- (18..19, menlo_bold, red),
- (19..34, menlo_regular, black)
+ (3, menlo_regular, black),
+ (1, menlo_bold, red),
+ (1, menlo_regular, black),
+ (1, menlo_bold, red),
+ (3, menlo_regular, black),
+ (1, menlo_bold, red),
+ (5, menlo_regular, black),
+ (1, menlo_bold, red),
+ (2, menlo_regular, black),
+ (1, menlo_bold, red),
+ (15, menlo_regular, black),
]
);
}
@@ -1,4 +1,5 @@
use crate::{
+ color::ColorU,
fonts::{FontId, GlyphId, Metrics, Properties},
geometry::{
rect::{RectF, RectI},
@@ -6,7 +7,7 @@ use crate::{
vector::{vec2f, vec2i, Vector2F},
},
platform,
- text_layout::{Glyph, Line, Run},
+ text_layout::{Glyph, LineLayout, Run},
};
use cocoa::appkit::{CGFloat, CGPoint};
use core_foundation::{
@@ -21,7 +22,7 @@ use core_graphics::{
use core_text::{line::CTLine, string_attributes::kCTFontAttributeName};
use font_kit::{canvas::RasterizationOptions, hinting::HintingOptions, source::SystemSource};
use parking_lot::RwLock;
-use std::{char, convert::TryFrom};
+use std::{cell::RefCell, char, convert::TryFrom};
#[allow(non_upper_case_globals)]
const kCGImageAlphaOnly: u32 = 7;
@@ -80,8 +81,8 @@ impl platform::FontSystem for FontSystem {
&self,
text: &str,
font_size: f32,
- runs: &[(std::ops::Range<usize>, FontId)],
- ) -> Line {
+ runs: &[(usize, FontId, ColorU)],
+ ) -> LineLayout {
self.0.read().layout_str(text, font_size, runs)
}
}
@@ -185,8 +186,8 @@ impl FontSystemState {
&self,
text: &str,
font_size: f32,
- runs: &[(std::ops::Range<usize>, FontId)],
- ) -> Line {
+ runs: &[(usize, FontId, ColorU)],
+ ) -> LineLayout {
let font_id_attr_name = CFString::from_static_string("zed_font_id");
let len = text.len();
@@ -205,16 +206,34 @@ impl FontSystemState {
let mut utf8_and_utf16_ixs = utf8_and_utf16_ixs.clone();
string.replace_str(&CFString::new(text), CFRange::init(0, 0));
+ let last_run: RefCell<Option<(usize, FontId)>> = Default::default();
+ let font_runs = runs
+ .iter()
+ .filter_map(|(len, font_id, _)| {
+ let mut last_run = last_run.borrow_mut();
+ if let Some((last_len, last_font_id)) = last_run.as_mut() {
+ if font_id == last_font_id {
+ *last_len += *len;
+ None
+ } else {
+ let result = (*last_len, *last_font_id);
+ *last_len = *len;
+ *last_font_id = *font_id;
+ Some(result)
+ }
+ } else {
+ *last_run = Some((*len, *font_id));
+ None
+ }
+ })
+ .chain(std::iter::from_fn(|| last_run.borrow_mut().take()));
+
let mut utf8_ix = 0;
let mut utf16_ix = 0;
- for (range, font_id) in runs {
- while utf8_ix < range.start {
- let (next_utf8_ix, next_utf16_ix) = utf8_and_utf16_ixs.next().unwrap();
- utf8_ix = next_utf8_ix;
- utf16_ix = next_utf16_ix;
- }
+ for (run_len, font_id) in font_runs {
+ let utf8_end = utf16_ix + run_len;
let utf16_start = utf16_ix;
- while utf8_ix < range.end {
+ while utf8_ix < utf8_end {
let (next_utf8_ix, next_utf16_ix) = utf8_and_utf16_ixs.next().unwrap();
utf8_ix = next_utf8_ix;
utf16_ix = next_utf16_ix;
@@ -279,7 +298,7 @@ impl FontSystemState {
}
let typographic_bounds = line.get_typographic_bounds();
- Line {
+ LineLayout {
width: typographic_bounds.width as f32,
ascent: typographic_bounds.ascent as f32,
descent: typographic_bounds.descent as f32,
@@ -308,9 +327,9 @@ mod tests {
"hello world",
16.0,
&[
- (0..2, menlo_bold),
- (2..6, menlo_italic),
- (6..11, menlo_regular),
+ (2, menlo_bold, Default::default()),
+ (4, menlo_italic, Default::default()),
+ (5, menlo_regular, Default::default()),
],
);
assert_eq!(line.runs.len(), 3);
@@ -336,9 +355,9 @@ mod tests {
text,
16.0,
&[
- (0..9, zapfino_regular),
- (9..22, menlo_regular),
- (22..text.len(), zapfino_regular),
+ (9, zapfino_regular, ColorU::default()),
+ (13, menlo_regular, ColorU::default()),
+ (text.len() - 22, zapfino_regular, ColorU::default()),
],
);
assert_eq!(
@@ -8,20 +8,20 @@ pub mod current {
}
use crate::{
+ color::ColorU,
executor,
fonts::{FontId, GlyphId, Metrics as FontMetrics, Properties as FontProperties},
geometry::{
rect::{RectF, RectI},
vector::Vector2F,
},
- text_layout::Line,
+ text_layout::LineLayout,
ClipboardItem, Menu, Scene,
};
use async_task::Runnable;
pub use event::Event;
use std::{
any::Any,
- ops::Range,
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
@@ -122,5 +122,10 @@ pub trait FontSystem: Send + Sync {
subpixel_shift: Vector2F,
scale_factor: f32,
) -> Option<(RectI, Vec<u8>)>;
- fn layout_str(&self, text: &str, font_size: f32, runs: &[(Range<usize>, FontId)]) -> Line;
+ fn layout_str(
+ &self,
+ text: &str,
+ font_size: f32,
+ runs: &[(usize, FontId, ColorU)],
+ ) -> LineLayout;
}
@@ -14,13 +14,12 @@ use std::{
borrow::Borrow,
collections::HashMap,
hash::{Hash, Hasher},
- ops::Range,
sync::Arc,
};
pub struct TextLayoutCache {
- prev_frame: Mutex<HashMap<CacheKeyValue, Arc<Line>>>,
- curr_frame: RwLock<HashMap<CacheKeyValue, Arc<Line>>>,
+ prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
+ curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
fonts: Arc<dyn platform::FontSystem>,
}
@@ -44,31 +43,31 @@ impl TextLayoutCache {
&'a self,
text: &'a str,
font_size: f32,
- runs: &'a [(Range<usize>, FontId)],
- ) -> Arc<Line> {
+ runs: &'a [(usize, FontId, ColorU)],
+ ) -> Line {
let key = &CacheKeyRef {
text,
font_size: OrderedFloat(font_size),
runs,
} as &dyn CacheKey;
let curr_frame = self.curr_frame.upgradable_read();
- if let Some(line) = curr_frame.get(key) {
- return line.clone();
+ if let Some(layout) = curr_frame.get(key) {
+ return Line::new(layout.clone(), runs);
}
let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
- if let Some((key, line)) = self.prev_frame.lock().remove_entry(key) {
- curr_frame.insert(key, line.clone());
- line.clone()
+ if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
+ curr_frame.insert(key, layout.clone());
+ Line::new(layout.clone(), runs)
} else {
- let line = Arc::new(self.fonts.layout_str(text, font_size, runs));
+ let layout = Arc::new(self.fonts.layout_str(text, font_size, runs));
let key = CacheKeyValue {
text: text.into(),
font_size: OrderedFloat(font_size),
runs: SmallVec::from(runs),
};
- curr_frame.insert(key, line.clone());
- line
+ curr_frame.insert(key, layout.clone());
+ Line::new(layout, runs)
}
}
}
@@ -95,7 +94,7 @@ impl<'a> Hash for (dyn CacheKey + 'a) {
struct CacheKeyValue {
text: String,
font_size: OrderedFloat<f32>,
- runs: SmallVec<[(Range<usize>, FontId); 1]>,
+ runs: SmallVec<[(usize, FontId, ColorU); 1]>,
}
impl CacheKey for CacheKeyValue {
@@ -124,7 +123,7 @@ impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
struct CacheKeyRef<'a> {
text: &'a str,
font_size: OrderedFloat<f32>,
- runs: &'a [(Range<usize>, FontId)],
+ runs: &'a [(usize, FontId, ColorU)],
}
impl<'a> CacheKey for CacheKeyRef<'a> {
@@ -135,6 +134,12 @@ impl<'a> CacheKey for CacheKeyRef<'a> {
#[derive(Default, Debug)]
pub struct Line {
+ layout: Arc<LineLayout>,
+ color_runs: SmallVec<[(u32, ColorU); 32]>,
+}
+
+#[derive(Default, Debug)]
+pub struct LineLayout {
pub width: f32,
pub ascent: f32,
pub descent: f32,
@@ -157,22 +162,34 @@ pub struct Glyph {
}
impl Line {
+ fn new(layout: Arc<LineLayout>, runs: &[(usize, FontId, ColorU)]) -> Self {
+ let mut color_runs = SmallVec::new();
+ for (len, _, color) in runs {
+ color_runs.push((*len as u32, *color));
+ }
+ Self { layout, color_runs }
+ }
+
+ pub fn width(&self) -> f32 {
+ self.layout.width
+ }
+
pub fn x_for_index(&self, index: usize) -> f32 {
- for run in &self.runs {
+ for run in &self.layout.runs {
for glyph in &run.glyphs {
if glyph.index == index {
return glyph.position.x();
}
}
}
- self.width
+ self.layout.width
}
pub fn index_for_x(&self, x: f32) -> Option<usize> {
- if x >= self.width {
+ if x >= self.layout.width {
None
} else {
- for run in self.runs.iter().rev() {
+ for run in self.layout.runs.iter().rev() {
for glyph in run.glyphs.iter().rev() {
if glyph.position.x() <= x {
return Some(glyph.index);
@@ -183,21 +200,19 @@ impl Line {
}
}
- pub fn paint(
- &self,
- origin: Vector2F,
- bounds: RectF,
- colors: &[(Range<usize>, ColorU)],
- ctx: &mut PaintContext,
- ) {
- let mut colors = colors.iter().peekable();
- let mut color = ColorU::black();
+ pub fn paint(&self, origin: Vector2F, bounds: RectF, ctx: &mut PaintContext) {
+ let padding_top = (bounds.height() - self.layout.ascent - self.layout.descent) / 2.;
+ let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
- let padding_top = (bounds.height() - self.ascent - self.descent) / 2.;
- let baseline_origin = vec2f(0., padding_top + self.ascent);
+ let mut color_runs = self.color_runs.iter();
+ let mut color_end = 0;
+ let mut color = ColorU::black();
- for run in &self.runs {
- let max_glyph_width = ctx.font_cache.bounding_box(run.font_id, self.font_size).x();
+ for run in &self.layout.runs {
+ let max_glyph_width = ctx
+ .font_cache
+ .bounding_box(run.font_id, self.layout.font_size)
+ .x();
for glyph in &run.glyphs {
let glyph_origin = baseline_origin + glyph.position;
@@ -209,18 +224,19 @@ impl Line {
break;
}
- while let Some((range, next_color)) = colors.peek() {
- if glyph.index >= range.end {
- colors.next();
+ if glyph.index >= color_end {
+ if let Some(next_run) = color_runs.next() {
+ color_end += next_run.0 as usize;
+ color = next_run.1;
} else {
- color = *next_color;
- break;
+ color_end = self.layout.len;
+ color = ColorU::black();
}
}
ctx.scene.push_glyph(scene::Glyph {
font_id: run.font_id,
- font_size: self.font_size,
+ font_size: self.layout.font_size,
id: glyph.id,
origin: origin + glyph_origin,
color,
@@ -14,10 +14,7 @@ use gpui::{
use json::json;
use smallvec::SmallVec;
use std::cmp::Ordering;
-use std::{
- cmp::{self},
- sync::Arc,
-};
+use std::cmp::{self};
pub struct BufferElement {
view: WeakViewHandle<BufferView>,
@@ -188,13 +185,12 @@ impl BufferElement {
for (ix, line) in layout.line_number_layouts.iter().enumerate() {
let line_origin = rect.origin()
+ vec2f(
- rect.width() - line.width - layout.gutter_padding,
+ rect.width() - line.width() - layout.gutter_padding,
ix as f32 * line_height - (scroll_top % line_height),
);
line.paint(
line_origin,
- RectF::new(vec2f(0., 0.), vec2f(line.width, line_height)),
- &[(0..line.len, ColorU::black())],
+ RectF::new(vec2f(0., 0.), vec2f(line.width(), line_height)),
ctx,
);
}
@@ -259,7 +255,7 @@ impl BufferElement {
+ line_layout.x_for_index(range_end.column() as usize)
- scroll_left
} else {
- content_origin.x() + line_layout.width + corner_radius * 2.0
+ content_origin.x() + line_layout.width() + corner_radius * 2.0
- scroll_left
},
}
@@ -292,7 +288,6 @@ impl BufferElement {
line.paint(
content_origin + vec2f(-scroll_left, row as f32 * line_height - scroll_top),
RectF::new(vec2f(scroll_left, 0.), vec2f(bounds.width(), line_height)),
- &[(0..line.len, ColorU::black())],
ctx,
);
}
@@ -377,8 +372,8 @@ impl Element for BufferElement {
}
Ok(layouts) => {
for line in &layouts {
- if line.width > max_visible_line_width {
- max_visible_line_width = line.width;
+ if line.width() > max_visible_line_width {
+ max_visible_line_width = line.width();
}
}
@@ -506,8 +501,8 @@ pub struct LayoutState {
gutter_size: Vector2F,
gutter_padding: f32,
text_size: Vector2F,
- line_layouts: Vec<Arc<text_layout::Line>>,
- line_number_layouts: Vec<Arc<text_layout::Line>>,
+ line_layouts: Vec<text_layout::Line>,
+ line_number_layouts: Vec<text_layout::Line>,
max_visible_line_width: f32,
autoscroll_horizontally: bool,
}
@@ -524,7 +519,7 @@ impl LayoutState {
let longest_line_width = view
.layout_line(row, font_cache, layout_cache, app)
.unwrap()
- .width;
+ .width();
longest_line_width.max(self.max_visible_line_width) + view.em_width(font_cache)
}
@@ -5,9 +5,10 @@ use super::{
use crate::{settings::Settings, util::post_inc, workspace, worktree::FileHandle};
use anyhow::Result;
use gpui::{
- fonts::Properties as FontProperties, geometry::vector::Vector2F, keymap::Binding, text_layout,
- AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle,
- MutableAppContext, Task, TextLayoutCache, View, ViewContext, WeakViewHandle,
+ color::ColorU, fonts::Properties as FontProperties, geometry::vector::Vector2F,
+ keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity,
+ FontCache, ModelHandle, MutableAppContext, Task, TextLayoutCache, View, ViewContext,
+ WeakViewHandle,
};
use parking_lot::Mutex;
use postage::watch;
@@ -448,7 +449,7 @@ impl BufferView {
viewport_width: f32,
scroll_width: f32,
max_glyph_width: f32,
- layouts: &[Arc<text_layout::Line>],
+ layouts: &[text_layout::Line],
ctx: &AppContext,
) {
let mut target_left = std::f32::INFINITY;
@@ -2077,9 +2078,9 @@ impl BufferView {
.layout_str(
"1".repeat(digit_count).as_str(),
font_size,
- &[(0..digit_count, font_id)],
+ &[(digit_count, font_id, ColorU::black())],
)
- .width)
+ .width())
}
pub fn layout_line_numbers(
@@ -2088,7 +2089,7 @@ impl BufferView {
font_cache: &FontCache,
layout_cache: &TextLayoutCache,
ctx: &AppContext,
- ) -> Result<Vec<Arc<text_layout::Line>>> {
+ ) -> Result<Vec<text_layout::Line>> {
let settings = self.settings.borrow();
let font_size = settings.buffer_font_size;
let font_id =
@@ -2114,7 +2115,7 @@ impl BufferView {
layouts.push(layout_cache.layout_str(
&line_number,
font_size,
- &[(0..line_number.len(), font_id)],
+ &[(line_number.len(), font_id, ColorU::black())],
));
}
@@ -2127,7 +2128,7 @@ impl BufferView {
font_cache: &FontCache,
layout_cache: &TextLayoutCache,
ctx: &AppContext,
- ) -> Result<Vec<Arc<text_layout::Line>>> {
+ ) -> Result<Vec<text_layout::Line>> {
rows.end = cmp::min(rows.end, self.display_map.max_point(ctx).row() + 1);
if rows.start >= rows.end {
return Ok(Vec::new());
@@ -2151,7 +2152,7 @@ impl BufferView {
layouts.push(layout_cache.layout_str(
&line,
font_size,
- &[(0..line.len(), font_id)],
+ &[(line.len(), font_id, ColorU::black())],
));
line.clear();
row += 1;
@@ -2171,7 +2172,7 @@ impl BufferView {
font_cache: &FontCache,
layout_cache: &TextLayoutCache,
app: &AppContext,
- ) -> Result<Arc<text_layout::Line>> {
+ ) -> Result<text_layout::Line> {
let settings = self.settings.borrow();
let font_id =
font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
@@ -2181,7 +2182,7 @@ impl BufferView {
Ok(layout_cache.layout_str(
&line,
settings.buffer_font_size,
- &[(0..self.line_len(row, app) as usize, font_id)],
+ &[(self.line_len(row, app) as usize, font_id, ColorU::black())],
))
}