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) {
let mut fs = ct::FontSystem::new();
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 &[
("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]);
buffer
.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);

View file

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

View file

@ -102,7 +102,7 @@ fn main() {
// Set scroll to view scroll
buffer.set_scroll(*scroll);
// 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
//TODO: ability to prune with multiple views?
buffer.shape_until_scroll(true);
@ -154,10 +154,10 @@ fn main() {
scroll.vertical -= buffer.metrics().line_height;
}
Key::Named(NamedKey::PageDown) => {
scroll.vertical += buffer.size().1;
scroll.vertical += buffer.size().1.unwrap_or(0.0);
}
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);
editor.with_buffer_mut(|buffer| {
buffer.set_size(
window.inner_size().width as f32,
window.inner_size().height as f32,
Some(window.inner_size().width as f32),
Some(window.inner_size().height as f32),
)
});
editor.with_buffer_mut(|buffer| set_buffer_text(buffer));
@ -182,7 +182,7 @@ fn main() {
pixmap.fill(bg_color);
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();

View file

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

View file

@ -167,7 +167,7 @@ impl<'buffer> Editor<'buffer> {
if run.glyphs.is_empty() && end.line > line_i {
// 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() {
@ -176,7 +176,7 @@ impl<'buffer> Editor<'buffer> {
if run.rtl {
min = 0;
} else {
max = buffer.size().0 as i32;
max = buffer.size().0.unwrap_or(0.0) as i32;
}
}
f(

View file

@ -201,7 +201,11 @@ impl<'syntax_system, 'buffer> SyntaxEditor<'syntax_system, 'buffer> {
F: FnMut(i32, i32, u32, u32, Color),
{
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(
font_system,
cache,
@ -263,7 +267,7 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for SyntaxEditor<'syntax_system, 'bu
self.editor.with_buffer_mut(|buffer| {
let metrics = buffer.metrics();
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 highlighted = 0;
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();
self.with_buffer(|buffer| {
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;
for run in buffer.layout_runs() {
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 {
// 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() {
@ -402,7 +406,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
if run.rtl {
min = 0;
} else {
max = buffer.size().0 as i32;
max = buffer.size().0.unwrap_or(0.0) as i32;
}
}
f(

View file

@ -30,7 +30,7 @@
//! let mut buffer = buffer.borrow_with(&mut font_system);
//!
//! // 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
//! let attrs = Attrs::new();

View file

@ -852,7 +852,7 @@ impl ShapeLine {
for span in spans.iter_mut() {
for word in span.words.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
let space_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 margins = 5;
buffer.set_size(
(self.canvas_width - margins * 2) as f32,
(self.canvas_height - margins * 2) as f32,
Some((self.canvas_width - margins * 2) as f32),
Some((self.canvas_height - margins * 2) as f32),
);
buffer.set_text(&self.text, self.font.as_attrs(), Shaping::Advanced);
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);
// 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
buffer.shape_until_scroll(false);

View file

@ -16,17 +16,17 @@ fn wrap_word_fallback() {
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_size(50.0, 1000.0);
buffer.set_size(Some(50.0), Some(1000.0));
buffer.shape_until_scroll(false);
let measured_size = measure(&buffer);
assert!(
measured_size <= buffer.size().0,
measured_size <= buffer.size().0.unwrap_or(0.0),
"Measured width is larger than buffer width\n{} <= {}",
measured_size,
buffer.size().0
buffer.size().0.unwrap_or(0.0)
);
}