Allow for undefined buffer width and/or height, fixes #70

This commit is contained in:
Jeremy Soller 2024-06-12 09:04:04 -06:00
parent cd1cd0a337
commit 93a7df859a
15 changed files with 99 additions and 72 deletions

View file

@ -10,7 +10,7 @@ fn load_font_system(c: &mut Criterion) {
fn layout(c: &mut Criterion) { fn layout(c: &mut Criterion) {
let mut fs = ct::FontSystem::new(); let mut fs = ct::FontSystem::new();
let mut buffer = ct::Buffer::new(&mut fs, ct::Metrics::new(10.0, 10.0)); let mut buffer = ct::Buffer::new(&mut fs, ct::Metrics::new(10.0, 10.0));
buffer.set_size(&mut fs, 80.0, f32::MAX); buffer.set_size(&mut fs, Some(80.0), None);
for (wrap_name, wrap) in &[ for (wrap_name, wrap) in &[
("None", ct::Wrap::None), ("None", ct::Wrap::None),

View file

@ -74,7 +74,7 @@ fn main() {
let mut buffer = Buffer::new(&mut font_system, font_sizes[font_size_default]); let mut buffer = Buffer::new(&mut font_system, font_sizes[font_size_default]);
buffer buffer
.borrow_with(&mut font_system) .borrow_with(&mut font_system)
.set_size(window.width() as f32, window.height() as f32); .set_size(Some(window.width() as f32), Some(window.height() as f32));
let mut editor = Editor::new(buffer); let mut editor = Editor::new(buffer);

View file

@ -110,8 +110,8 @@ fn main() {
editor.with_buffer_mut(|buffer| { editor.with_buffer_mut(|buffer| {
buffer.set_size( buffer.set_size(
width as f32 - scrollbar_width * display_scale, Some(width as f32 - scrollbar_width * display_scale),
height as f32, Some(height as f32),
) )
}); });

View file

@ -102,7 +102,7 @@ fn main() {
// Set scroll to view scroll // Set scroll to view scroll
buffer.set_scroll(*scroll); buffer.set_scroll(*scroll);
// Set size, will relayout and shape until scroll if changed // Set size, will relayout and shape until scroll if changed
buffer.set_size(width as f32, height as f32); buffer.set_size(Some(width as f32), Some(height as f32));
// Shape until scroll, ensures scroll is clamped // Shape until scroll, ensures scroll is clamped
//TODO: ability to prune with multiple views? //TODO: ability to prune with multiple views?
buffer.shape_until_scroll(true); buffer.shape_until_scroll(true);
@ -154,10 +154,10 @@ fn main() {
scroll.vertical -= buffer.metrics().line_height; scroll.vertical -= buffer.metrics().line_height;
} }
Key::Named(NamedKey::PageDown) => { Key::Named(NamedKey::PageDown) => {
scroll.vertical += buffer.size().1; scroll.vertical += buffer.size().1.unwrap_or(0.0);
} }
Key::Named(NamedKey::PageUp) => { Key::Named(NamedKey::PageUp) => {
scroll.vertical -= buffer.size().1; scroll.vertical -= buffer.size().1.unwrap_or(0.0);
} }
_ => {} _ => {}
} }

View file

@ -122,8 +122,8 @@ fn main() {
let mut editor = editor.borrow_with(&mut font_system); let mut editor = editor.borrow_with(&mut font_system);
editor.with_buffer_mut(|buffer| { editor.with_buffer_mut(|buffer| {
buffer.set_size( buffer.set_size(
window.inner_size().width as f32, Some(window.inner_size().width as f32),
window.inner_size().height as f32, Some(window.inner_size().height as f32),
) )
}); });
editor.with_buffer_mut(|buffer| set_buffer_text(buffer)); editor.with_buffer_mut(|buffer| set_buffer_text(buffer));
@ -182,7 +182,7 @@ fn main() {
pixmap.fill(bg_color); pixmap.fill(bg_color);
editor.with_buffer_mut(|buffer| { editor.with_buffer_mut(|buffer| {
buffer.set_size(width as f32, height as f32) buffer.set_size(Some(width as f32), Some(height as f32))
}); });
let mut paint = Paint::default(); let mut paint = Paint::default();

View file

@ -26,8 +26,8 @@ fn main() {
// Set a size for the text buffer, in pixels // Set a size for the text buffer, in pixels
let width = 80.0; let width = 80.0;
let height = f32::MAX; // The height is unbounded // The height is unbounded
buffer.set_size(width, height); buffer.set_size(Some(width), None);
// Attributes indicate what font to choose // Attributes indicate what font to choose
let attrs = Attrs::new(); let attrs = Attrs::new();
@ -45,7 +45,6 @@ fn main() {
const TEXT_COLOR: Color = Color::rgb(0xFF, 0xFF, 0xFF); const TEXT_COLOR: Color = Color::rgb(0xFF, 0xFF, 0xFF);
// Set up the canvas // Set up the canvas
let width = buffer.size().0;
let height = LINE_HEIGHT * buffer.layout_runs().count() as f32; let height = LINE_HEIGHT * buffer.layout_runs().count() as f32;
let mut canvas = vec![vec![None; width as usize]; height as usize]; let mut canvas = vec![vec![None; width as usize]; height as usize];

View file

@ -129,8 +129,10 @@ impl<'b> Iterator for LayoutRunIter<'b> {
let glyph_height = layout_line.max_ascent + layout_line.max_descent; let glyph_height = layout_line.max_ascent + layout_line.max_descent;
let centering_offset = (line_height - glyph_height) / 2.0; let centering_offset = (line_height - glyph_height) / 2.0;
let line_y = line_top + centering_offset + layout_line.max_ascent; let line_y = line_top + centering_offset + layout_line.max_ascent;
if line_top + centering_offset > self.buffer.height { if let Some(height) = self.buffer.height_opt {
return None; if line_top + centering_offset > height {
return None;
}
} }
self.line_top += line_height; self.line_top += line_height;
if line_y < 0.0 { if line_y < 0.0 {
@ -203,8 +205,8 @@ pub struct Buffer {
/// [BufferLine]s (or paragraphs) of text in the buffer /// [BufferLine]s (or paragraphs) of text in the buffer
pub lines: Vec<BufferLine>, pub lines: Vec<BufferLine>,
metrics: Metrics, metrics: Metrics,
width: f32, width_opt: Option<f32>,
height: f32, height_opt: Option<f32>,
scroll: Scroll, scroll: Scroll,
/// True if a redraw is requires. Set to false after processing /// True if a redraw is requires. Set to false after processing
redraw: bool, redraw: bool,
@ -221,8 +223,8 @@ impl Clone for Buffer {
Self { Self {
lines: self.lines.clone(), lines: self.lines.clone(),
metrics: self.metrics, metrics: self.metrics,
width: self.width, width_opt: self.width_opt,
height: self.height, height_opt: self.height_opt,
scroll: self.scroll, scroll: self.scroll,
redraw: self.redraw, redraw: self.redraw,
wrap: self.wrap, wrap: self.wrap,
@ -250,8 +252,8 @@ impl Buffer {
Self { Self {
lines: Vec::new(), lines: Vec::new(),
metrics, metrics,
width: 0.0, width_opt: None,
height: 0.0, height_opt: None,
scroll: Scroll::default(), scroll: Scroll::default(),
redraw: false, redraw: false,
wrap: Wrap::WordOrGlyph, wrap: Wrap::WordOrGlyph,
@ -294,7 +296,7 @@ impl Buffer {
&mut self.scratch, &mut self.scratch,
font_system, font_system,
self.metrics.font_size, self.metrics.font_size,
Some(self.width), self.width_opt,
self.wrap, self.wrap,
self.monospace_width, self.monospace_width,
self.tab_width, self.tab_width,
@ -315,7 +317,6 @@ impl Buffer {
cursor: Cursor, cursor: Cursor,
prune: bool, prune: bool,
) { ) {
let height = self.height;
let metrics = self.metrics; let metrics = self.metrics;
let old_scroll = self.scroll; let old_scroll = self.scroll;
@ -345,7 +346,7 @@ impl Buffer {
// Adjust scroll backwards if cursor is before it // Adjust scroll backwards if cursor is before it
self.scroll.line = layout_cursor.line; self.scroll.line = layout_cursor.line;
self.scroll.vertical = layout_y; self.scroll.vertical = layout_y;
} else { } else if let Some(height) = self.height_opt {
// Adjust scroll forwards if cursor is after it // Adjust scroll forwards if cursor is after it
let mut line_i = layout_cursor.line; let mut line_i = layout_cursor.line;
while line_i > self.scroll.line { while line_i > self.scroll.line {
@ -389,9 +390,11 @@ impl Buffer {
self.scroll.horizontal = x_min; self.scroll.horizontal = x_min;
self.redraw = true; self.redraw = true;
} }
if x_max > self.scroll.horizontal + self.width { if let Some(width) = self.width_opt {
self.scroll.horizontal = x_max - self.width; if x_max > self.scroll.horizontal + width {
self.redraw = true; self.scroll.horizontal = x_max - width;
self.redraw = true;
}
} }
} }
} }
@ -424,7 +427,7 @@ impl Buffer {
} }
let scroll_start = self.scroll.vertical; let scroll_start = self.scroll.vertical;
let scroll_end = scroll_start + self.height; let scroll_end = scroll_start + self.height_opt.unwrap_or(f32::INFINITY);
let mut total_height = 0.0; let mut total_height = 0.0;
for line_i in 0..self.lines.len() { for line_i in 0..self.lines.len() {
@ -528,7 +531,7 @@ impl Buffer {
&mut self.scratch, &mut self.scratch,
font_system, font_system,
self.metrics.font_size, self.metrics.font_size,
Some(self.width), self.width_opt,
self.wrap, self.wrap,
self.monospace_width, self.monospace_width,
self.tab_width, self.tab_width,
@ -546,7 +549,7 @@ impl Buffer {
/// ///
/// Will panic if `metrics.font_size` is zero. /// Will panic if `metrics.font_size` is zero.
pub fn set_metrics(&mut self, font_system: &mut FontSystem, metrics: Metrics) { pub fn set_metrics(&mut self, font_system: &mut FontSystem, metrics: Metrics) {
self.set_metrics_and_size(font_system, metrics, self.width, self.height); self.set_metrics_and_size(font_system, metrics, self.width_opt, self.height_opt);
} }
/// Get the current [`Wrap`] /// Get the current [`Wrap`]
@ -608,13 +611,18 @@ impl Buffer {
} }
/// Get the current buffer dimensions (width, height) /// Get the current buffer dimensions (width, height)
pub fn size(&self) -> (f32, f32) { pub fn size(&self) -> (Option<f32>, Option<f32>) {
(self.width, self.height) (self.width_opt, self.height_opt)
} }
/// Set the current buffer dimensions /// Set the current buffer dimensions
pub fn set_size(&mut self, font_system: &mut FontSystem, width: f32, height: f32) { pub fn set_size(
self.set_metrics_and_size(font_system, self.metrics, width, height); &mut self,
font_system: &mut FontSystem,
width_opt: Option<f32>,
height_opt: Option<f32>,
) {
self.set_metrics_and_size(font_system, self.metrics, width_opt, height_opt);
} }
/// Set the current [`Metrics`] and buffer dimensions at the same time /// Set the current [`Metrics`] and buffer dimensions at the same time
@ -626,17 +634,20 @@ impl Buffer {
&mut self, &mut self,
font_system: &mut FontSystem, font_system: &mut FontSystem,
metrics: Metrics, metrics: Metrics,
width: f32, width_opt: Option<f32>,
height: f32, height_opt: Option<f32>,
) { ) {
let clamped_width = width.max(0.0); let clamped_width_opt = width_opt.map(|width| width.max(0.0));
let clamped_height = height.max(0.0); let clamped_height_opt = height_opt.map(|height| height.max(0.0));
if metrics != self.metrics || clamped_width != self.width || clamped_height != self.height { if metrics != self.metrics
|| clamped_width_opt != self.width_opt
|| clamped_height_opt != self.height_opt
{
assert_ne!(metrics.font_size, 0.0, "font size cannot be 0"); assert_ne!(metrics.font_size, 0.0, "font size cannot be 0");
self.metrics = metrics; self.metrics = metrics;
self.width = clamped_width; self.width_opt = clamped_width_opt;
self.height = clamped_height; self.height_opt = clamped_height_opt;
self.relayout(font_system); self.relayout(font_system);
self.shape_until_scroll(font_system, false); self.shape_until_scroll(font_system, false);
} }
@ -1128,20 +1139,24 @@ impl Buffer {
cursor_x_opt = None; cursor_x_opt = None;
} }
Motion::PageUp => { Motion::PageUp => {
(cursor, cursor_x_opt) = self.cursor_motion( if let Some(height) = self.height_opt {
font_system, (cursor, cursor_x_opt) = self.cursor_motion(
cursor, font_system,
cursor_x_opt, cursor,
Motion::Vertical(-self.size().1 as i32), cursor_x_opt,
)?; Motion::Vertical(-height as i32),
)?;
}
} }
Motion::PageDown => { Motion::PageDown => {
(cursor, cursor_x_opt) = self.cursor_motion( if let Some(height) = self.height_opt {
font_system, (cursor, cursor_x_opt) = self.cursor_motion(
cursor, font_system,
cursor_x_opt, cursor,
Motion::Vertical(self.size().1 as i32), cursor_x_opt,
)?; Motion::Vertical(height as i32),
)?;
}
} }
Motion::Vertical(px) => { Motion::Vertical(px) => {
// TODO more efficient, use layout run line height // TODO more efficient, use layout run line height
@ -1341,8 +1356,8 @@ impl<'a> BorrowedWithFontSystem<'a, Buffer> {
} }
/// Set the current buffer dimensions /// Set the current buffer dimensions
pub fn set_size(&mut self, width: f32, height: f32) { pub fn set_size(&mut self, width_opt: Option<f32>, height_opt: Option<f32>) {
self.inner.set_size(self.font_system, width, height); self.inner.set_size(self.font_system, width_opt, height_opt);
} }
/// Set the current [`Metrics`] and buffer dimensions at the same time /// Set the current [`Metrics`] and buffer dimensions at the same time
@ -1350,9 +1365,14 @@ impl<'a> BorrowedWithFontSystem<'a, Buffer> {
/// # Panics /// # Panics
/// ///
/// Will panic if `metrics.font_size` is zero. /// Will panic if `metrics.font_size` is zero.
pub fn set_metrics_and_size(&mut self, metrics: Metrics, width: f32, height: f32) { pub fn set_metrics_and_size(
&mut self,
metrics: Metrics,
width_opt: Option<f32>,
height_opt: Option<f32>,
) {
self.inner self.inner
.set_metrics_and_size(self.font_system, metrics, width, height); .set_metrics_and_size(self.font_system, metrics, width_opt, height_opt);
} }
/// Set text of buffer, using provided attributes for each line by default /// Set text of buffer, using provided attributes for each line by default

View file

@ -167,7 +167,7 @@ impl<'buffer> Editor<'buffer> {
if run.glyphs.is_empty() && end.line > line_i { if run.glyphs.is_empty() && end.line > line_i {
// Highlight all of internal empty lines // Highlight all of internal empty lines
range_opt = Some((0, buffer.size().0 as i32)); range_opt = Some((0, buffer.size().0.unwrap_or(0.0) as i32));
} }
if let Some((mut min, mut max)) = range_opt.take() { if let Some((mut min, mut max)) = range_opt.take() {
@ -176,7 +176,7 @@ impl<'buffer> Editor<'buffer> {
if run.rtl { if run.rtl {
min = 0; min = 0;
} else { } else {
max = buffer.size().0 as i32; max = buffer.size().0.unwrap_or(0.0) as i32;
} }
} }
f( f(

View file

@ -201,7 +201,11 @@ impl<'syntax_system, 'buffer> SyntaxEditor<'syntax_system, 'buffer> {
F: FnMut(i32, i32, u32, u32, Color), F: FnMut(i32, i32, u32, u32, Color),
{ {
let size = self.with_buffer(|buffer| buffer.size()); let size = self.with_buffer(|buffer| buffer.size());
f(0, 0, size.0 as u32, size.1 as u32, self.background_color()); if let Some(width) = size.0 {
if let Some(height) = size.1 {
f(0, 0, width as u32, height as u32, self.background_color());
}
}
self.editor.draw( self.editor.draw(
font_system, font_system,
cache, cache,
@ -263,7 +267,7 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for SyntaxEditor<'syntax_system, 'bu
self.editor.with_buffer_mut(|buffer| { self.editor.with_buffer_mut(|buffer| {
let metrics = buffer.metrics(); let metrics = buffer.metrics();
let scroll = buffer.scroll(); let scroll = buffer.scroll();
let scroll_end = scroll.vertical + buffer.size().1; let scroll_end = scroll.vertical + buffer.size().1.unwrap_or(f32::INFINITY);
let mut total_height = 0.0; let mut total_height = 0.0;
let mut highlighted = 0; let mut highlighted = 0;
for line_i in 0..buffer.lines.len() { for line_i in 0..buffer.lines.len() {

View file

@ -310,7 +310,11 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
let selection_color = self.selection_color(); let selection_color = self.selection_color();
self.with_buffer(|buffer| { self.with_buffer(|buffer| {
let size = buffer.size(); let size = buffer.size();
f(0, 0, size.0 as u32, size.1 as u32, background_color); if let Some(width) = size.0 {
if let Some(height) = size.1 {
f(0, 0, width as u32, height as u32, background_color);
}
}
let font_size = buffer.metrics().font_size; let font_size = buffer.metrics().font_size;
for run in buffer.layout_runs() { for run in buffer.layout_runs() {
let line_i = run.line_i; let line_i = run.line_i;
@ -393,7 +397,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
if run.glyphs.is_empty() && end.line > line_i { if run.glyphs.is_empty() && end.line > line_i {
// Highlight all of internal empty lines // Highlight all of internal empty lines
range_opt = Some((0, buffer.size().0 as i32)); range_opt = Some((0, buffer.size().0.unwrap_or(0.0) as i32));
} }
if let Some((mut min, mut max)) = range_opt.take() { if let Some((mut min, mut max)) = range_opt.take() {
@ -402,7 +406,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
if run.rtl { if run.rtl {
min = 0; min = 0;
} else { } else {
max = buffer.size().0 as i32; max = buffer.size().0.unwrap_or(0.0) as i32;
} }
} }
f( f(

View file

@ -30,7 +30,7 @@
//! let mut buffer = buffer.borrow_with(&mut font_system); //! let mut buffer = buffer.borrow_with(&mut font_system);
//! //!
//! // Set a size for the text buffer, in pixels //! // Set a size for the text buffer, in pixels
//! buffer.set_size(80.0, 25.0); //! buffer.set_size(Some(80.0), Some(25.0));
//! //!
//! // Attributes indicate what font to choose //! // Attributes indicate what font to choose
//! let attrs = Attrs::new(); //! let attrs = Attrs::new();

View file

@ -852,7 +852,7 @@ impl ShapeLine {
for span in spans.iter_mut() { for span in spans.iter_mut() {
for word in span.words.iter_mut() { for word in span.words.iter_mut() {
for glyph in word.glyphs.iter_mut() { for glyph in word.glyphs.iter_mut() {
if &line[glyph.start..glyph.end] == "\t" { if line.get(glyph.start..glyph.end) == Some("\t") {
//TODO: better fallback for width //TODO: better fallback for width
let space_x_advance = let space_x_advance =
glyph.font_monospace_em_width.unwrap_or(glyph.x_advance); glyph.font_monospace_em_width.unwrap_or(glyph.x_advance);

View file

@ -89,8 +89,8 @@ impl DrawTestCfg {
let mut buffer = buffer.borrow_with(&mut font_system); let mut buffer = buffer.borrow_with(&mut font_system);
let margins = 5; let margins = 5;
buffer.set_size( buffer.set_size(
(self.canvas_width - margins * 2) as f32, Some((self.canvas_width - margins * 2) as f32),
(self.canvas_height - margins * 2) as f32, Some((self.canvas_height - margins * 2) as f32),
); );
buffer.set_text(&self.text, self.font.as_attrs(), Shaping::Advanced); buffer.set_text(&self.text, self.font.as_attrs(), Shaping::Advanced);
buffer.shape_until_scroll(true); buffer.shape_until_scroll(true);

View file

@ -108,7 +108,7 @@ fn wrap_extra_line() {
buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing\n\nweeewoooo minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced); buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing\n\nweeewoooo minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced);
// Set a size for the text buffer, in pixels // Set a size for the text buffer, in pixels
buffer.set_size(50.0, 1000.0); buffer.set_size(Some(50.0), Some(1000.0));
// Perform shaping as desired // Perform shaping as desired
buffer.shape_until_scroll(false); buffer.shape_until_scroll(false);

View file

@ -16,17 +16,17 @@ fn wrap_word_fallback() {
buffer.set_wrap(Wrap::WordOrGlyph); buffer.set_wrap(Wrap::WordOrGlyph);
buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced); buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced);
buffer.set_size(50.0, 1000.0); buffer.set_size(Some(50.0), Some(1000.0));
buffer.shape_until_scroll(false); buffer.shape_until_scroll(false);
let measured_size = measure(&buffer); let measured_size = measure(&buffer);
assert!( assert!(
measured_size <= buffer.size().0, measured_size <= buffer.size().0.unwrap_or(0.0),
"Measured width is larger than buffer width\n{} <= {}", "Measured width is larger than buffer width\n{} <= {}",
measured_size, measured_size,
buffer.size().0 buffer.size().0.unwrap_or(0.0)
); );
} }