Add Renderer trait for more flexible rendering of buffers and editors
This commit is contained in:
parent
8a7bc790e5
commit
9339446cfa
7 changed files with 135 additions and 79 deletions
|
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Add `Renderer` trait for more flexible rendering of buffers and editors
|
||||
|
||||
## [0.15.0] - 2025-10-30
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -9,13 +9,10 @@ use core::{cmp, fmt};
|
|||
use core_maths::CoreFloat;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
use crate::Color;
|
||||
|
||||
use crate::{
|
||||
Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Cursor,
|
||||
FontSystem, LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Scroll,
|
||||
ShapeLine, Shaping, Wrap,
|
||||
Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Color,
|
||||
Cursor, FontSystem, LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion,
|
||||
Renderer, Scroll, ShapeLine, Shaping, Wrap,
|
||||
};
|
||||
|
||||
/// A line of visible text for rendering
|
||||
|
|
@ -1350,29 +1347,24 @@ impl Buffer {
|
|||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
mut f: F,
|
||||
callback: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let mut renderer = crate::LegacyRenderer {
|
||||
font_system,
|
||||
cache,
|
||||
callback,
|
||||
};
|
||||
self.render(&mut renderer, color);
|
||||
}
|
||||
|
||||
pub fn render<R: Renderer>(&self, renderer: &mut R, color: Color) {
|
||||
for run in self.layout_runs() {
|
||||
for glyph in run.glyphs {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
let physical_glyph = glyph.physical((0., run.line_y), 1.0);
|
||||
let glyph_color = glyph.color_opt.map_or(color, |some| some);
|
||||
|
||||
cache.with_pixels(
|
||||
font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
physical_glyph.x + x,
|
||||
run.line_y as i32 + physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
renderer.glyph(physical_glyph, glyph_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,13 @@ use alloc::{
|
|||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
use std::cmp;
|
||||
use core::cmp;
|
||||
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
use crate::Color;
|
||||
use crate::{
|
||||
Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem,
|
||||
Cursor, Edit, FontSystem, LayoutRun, LineEnding, LineIter, Selection, Shaping,
|
||||
Color, Cursor, Edit, FontSystem, LayoutRun, LineEnding, LineIter, Renderer, Selection, Shaping,
|
||||
};
|
||||
|
||||
/// A wrapper of [`Buffer`] for easy editing
|
||||
|
|
@ -115,10 +111,32 @@ impl<'buffer> Editor<'buffer> {
|
|||
cursor_color: Color,
|
||||
selection_color: Color,
|
||||
selected_text_color: Color,
|
||||
mut f: F,
|
||||
callback: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let mut renderer = crate::LegacyRenderer {
|
||||
font_system,
|
||||
cache,
|
||||
callback,
|
||||
};
|
||||
self.render(
|
||||
&mut renderer,
|
||||
text_color,
|
||||
cursor_color,
|
||||
selection_color,
|
||||
selected_text_color,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn render<R: Renderer>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
text_color: Color,
|
||||
cursor_color: Color,
|
||||
selection_color: Color,
|
||||
selected_text_color: Color,
|
||||
) {
|
||||
let selection_bounds = self.selection_bounds();
|
||||
self.with_buffer(|buffer| {
|
||||
for run in buffer.layout_runs() {
|
||||
|
|
@ -151,7 +169,7 @@ impl<'buffer> Editor<'buffer> {
|
|||
None => Some((c_x as i32, (c_x + c_w) as i32)),
|
||||
};
|
||||
} else if let Some((min, max)) = range_opt.take() {
|
||||
f(
|
||||
renderer.rectangle(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
|
|
@ -177,7 +195,7 @@ impl<'buffer> Editor<'buffer> {
|
|||
max = buffer.size().0.unwrap_or(0.0) as i32;
|
||||
}
|
||||
}
|
||||
f(
|
||||
renderer.rectangle(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
|
|
@ -190,11 +208,11 @@ impl<'buffer> Editor<'buffer> {
|
|||
|
||||
// Draw cursor
|
||||
if let Some((x, y)) = cursor_position(&self.cursor, &run) {
|
||||
f(x, y, 1, line_height as u32, cursor_color);
|
||||
renderer.rectangle(x, y, 1, line_height as u32, cursor_color);
|
||||
}
|
||||
|
||||
for glyph in run.glyphs {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
let physical_glyph = glyph.physical((0., line_y), 1.0);
|
||||
|
||||
let mut glyph_color = glyph.color_opt.map_or(text_color, |some| some);
|
||||
if text_color != selected_text_color {
|
||||
|
|
@ -209,20 +227,7 @@ impl<'buffer> Editor<'buffer> {
|
|||
}
|
||||
}
|
||||
|
||||
cache.with_pixels(
|
||||
font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
physical_glyph.x + x,
|
||||
line_y as i32 + physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
renderer.glyph(physical_glyph, glyph_color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use syntect::parsing::{ParseState, ScopeStack, SyntaxReference, SyntaxSet};
|
|||
|
||||
use crate::{
|
||||
Action, AttrsList, BorrowedWithFontSystem, BufferRef, Change, Color, Cursor, Edit, Editor,
|
||||
FontSystem, Selection, Shaping, Style, Weight,
|
||||
FontSystem, Renderer, Selection, Shaping, Style, Weight,
|
||||
};
|
||||
|
||||
pub use syntect::highlighting::Theme as SyntaxTheme;
|
||||
|
|
@ -219,24 +219,31 @@ impl<'syntax_system, 'buffer> SyntaxEditor<'syntax_system, 'buffer> {
|
|||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&self, font_system: &mut FontSystem, cache: &mut crate::SwashCache, mut f: F)
|
||||
pub fn draw<F>(&self, font_system: &mut FontSystem, cache: &mut crate::SwashCache, callback: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let mut renderer = crate::LegacyRenderer {
|
||||
font_system,
|
||||
cache,
|
||||
callback,
|
||||
};
|
||||
self.render(&mut renderer);
|
||||
}
|
||||
|
||||
pub fn render<R: Renderer>(&self, renderer: &mut R) {
|
||||
let size = self.with_buffer(|buffer| buffer.size());
|
||||
if let Some(width) = size.0 {
|
||||
if let Some(height) = size.1 {
|
||||
f(0, 0, width as u32, height as u32, self.background_color());
|
||||
renderer.rectangle(0, 0, width as u32, height as u32, self.background_color());
|
||||
}
|
||||
}
|
||||
self.editor.draw(
|
||||
font_system,
|
||||
cache,
|
||||
self.editor.render(
|
||||
renderer,
|
||||
self.foreground_color(),
|
||||
self.cursor_color(),
|
||||
self.selection_color(),
|
||||
self.foreground_color(),
|
||||
f,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
use alloc::{collections::BTreeMap, string::String};
|
||||
#[cfg(feature = "swash")]
|
||||
use core::cmp;
|
||||
use modit::{Event, Key, Parser, TextObject, WordIter};
|
||||
|
||||
use crate::{
|
||||
Action, AttrsList, BorrowedWithFontSystem, BufferRef, Change, Color, Cursor, Edit, FontSystem,
|
||||
Motion, Selection, SyntaxEditor, SyntaxTheme,
|
||||
Motion, Renderer, Selection, SyntaxEditor, SyntaxTheme,
|
||||
};
|
||||
|
||||
pub use modit::{ViMode, ViParser};
|
||||
#[cfg(feature = "swash")]
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
fn undo_2_action<'buffer, E: Edit<'buffer>>(
|
||||
|
|
@ -305,10 +303,19 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&self, font_system: &mut FontSystem, cache: &mut crate::SwashCache, mut f: F)
|
||||
pub fn draw<F>(&self, font_system: &mut FontSystem, cache: &mut crate::SwashCache, callback: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let mut renderer = crate::LegacyRenderer {
|
||||
font_system,
|
||||
cache,
|
||||
callback,
|
||||
};
|
||||
self.render(&mut renderer);
|
||||
}
|
||||
|
||||
pub fn render<R: Renderer>(&self, renderer: &mut R) {
|
||||
let background_color = self.background_color();
|
||||
let foreground_color = self.foreground_color();
|
||||
let cursor_color = self.cursor_color();
|
||||
|
|
@ -317,7 +324,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
|
|||
let size = buffer.size();
|
||||
if let Some(width) = size.0 {
|
||||
if let Some(height) = size.1 {
|
||||
f(0, 0, width as u32, height as u32, background_color);
|
||||
renderer.rectangle(0, 0, width as u32, height as u32, background_color);
|
||||
}
|
||||
}
|
||||
let font_size = buffer.metrics().font_size;
|
||||
|
|
@ -388,7 +395,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
|
|||
None => Some((c_x as i32, (c_x + c_w) as i32)),
|
||||
};
|
||||
} else if let Some((min, max)) = range_opt.take() {
|
||||
f(
|
||||
renderer.rectangle(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
|
|
@ -414,7 +421,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
|
|||
max = buffer.size().0.unwrap_or(0.0) as i32;
|
||||
}
|
||||
}
|
||||
f(
|
||||
renderer.rectangle(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
|
|
@ -476,7 +483,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
|
|||
if block_cursor {
|
||||
let left_x = cmp::min(start_x, end_x);
|
||||
let right_x = cmp::max(start_x, end_x);
|
||||
f(
|
||||
renderer.rectangle(
|
||||
left_x,
|
||||
line_top as i32,
|
||||
(right_x - left_x) as u32,
|
||||
|
|
@ -484,7 +491,7 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
|
|||
selection_color,
|
||||
);
|
||||
} else {
|
||||
f(
|
||||
renderer.rectangle(
|
||||
start_x,
|
||||
line_top as i32,
|
||||
1,
|
||||
|
|
@ -495,27 +502,14 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
|
|||
}
|
||||
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
let physical_glyph = glyph.physical((0., line_y), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => foreground_color,
|
||||
};
|
||||
|
||||
cache.with_pixels(
|
||||
font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
physical_glyph.x + x,
|
||||
line_y as i32 + physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
renderer.glyph(physical_glyph, glyph_color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -129,6 +129,9 @@ mod layout;
|
|||
pub use self::line_ending::*;
|
||||
mod line_ending;
|
||||
|
||||
pub use self::render::*;
|
||||
mod render;
|
||||
|
||||
pub use self::shape::*;
|
||||
mod shape;
|
||||
|
||||
|
|
|
|||
49
src/render.rs
Normal file
49
src/render.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//! Helpers for rendering buffers and editors
|
||||
|
||||
use crate::{Color, PhysicalGlyph};
|
||||
#[cfg(feature = "swash")]
|
||||
use crate::{FontSystem, SwashCache};
|
||||
|
||||
/// Custom renderer for buffers and editors
|
||||
pub trait Renderer {
|
||||
/// Render a rectangle at x, y with size w, h and the provided [`Color`].
|
||||
fn rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color);
|
||||
|
||||
/// Render a [`PhysicalGlyph`] with the provided [`Color`].
|
||||
/// For performance, consider using [`SwashCache`].
|
||||
fn glyph(&mut self, physical_glyph: PhysicalGlyph, color: Color);
|
||||
}
|
||||
|
||||
/// Helper to migrate from old renderer
|
||||
//TODO: remove in future version
|
||||
#[cfg(feature = "swash")]
|
||||
#[derive(Debug)]
|
||||
pub struct LegacyRenderer<'a, F: FnMut(i32, i32, u32, u32, Color)> {
|
||||
pub font_system: &'a mut FontSystem,
|
||||
pub cache: &'a mut SwashCache,
|
||||
pub callback: F,
|
||||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
impl<'a, F: FnMut(i32, i32, u32, u32, Color)> Renderer for LegacyRenderer<'a, F> {
|
||||
fn rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
|
||||
(self.callback)(x, y, w, h, color);
|
||||
}
|
||||
|
||||
fn glyph(&mut self, physical_glyph: PhysicalGlyph, color: Color) {
|
||||
self.cache.with_pixels(
|
||||
self.font_system,
|
||||
physical_glyph.cache_key,
|
||||
color,
|
||||
|x, y, pixel_color| {
|
||||
(self.callback)(
|
||||
physical_glyph.x + x,
|
||||
physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
pixel_color,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue