Merge branch 'mut-font-system' of https://github.com/geieredgar/cosmic-text into geieredgar-mut-font-system
This commit is contained in:
commit
9ebbc33792
23 changed files with 650 additions and 453 deletions
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/pop-os/cosmic-text"
|
|||
fontdb = { version = "0.13.0", default-features = false }
|
||||
libm = "0.2.6"
|
||||
log = "0.4.17"
|
||||
ouroboros = "0.15.5"
|
||||
ouroboros = { version = "0.15.5", default-features = false }
|
||||
rustybuzz = { version = "0.7.0", default-features = false, features = ["libm"] }
|
||||
swash = { version = "0.1.6", optional = true }
|
||||
syntect = { version = "5.0.0", optional = true }
|
||||
|
|
@ -35,6 +35,7 @@ no_std = [
|
|||
std = [
|
||||
"fontdb/memmap",
|
||||
"fontdb/std",
|
||||
"ouroboros/std",
|
||||
"rustybuzz/std",
|
||||
"sys-locale",
|
||||
"unicode-bidi/std",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use self::text_box::text_box;
|
|||
mod text_box;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref FONT_SYSTEM: FontSystem = FontSystem::new();
|
||||
static ref FONT_SYSTEM: Mutex<FontSystem> = Mutex::new(FontSystem::new());
|
||||
static ref SYNTAX_SYSTEM: SyntaxSystem = SyntaxSystem::new();
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +112,8 @@ pub enum Message {
|
|||
impl Window {
|
||||
pub fn open(&mut self, path: PathBuf) {
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
let mut font_system = FONT_SYSTEM.lock().unwrap();
|
||||
let mut editor = editor.borrow_with(&mut font_system);
|
||||
match editor.load_text(&path, self.attrs) {
|
||||
Ok(()) => {
|
||||
log::info!("opened '{}'", path.display());
|
||||
|
|
@ -137,7 +139,10 @@ impl Application for Window {
|
|||
.family(cosmic_text::Family::Monospace);
|
||||
|
||||
let mut editor = SyntaxEditor::new(
|
||||
Buffer::new(&FONT_SYSTEM, FontSize::Body.to_metrics()),
|
||||
Buffer::new(
|
||||
&mut FONT_SYSTEM.lock().unwrap(),
|
||||
FontSize::Body.to_metrics(),
|
||||
),
|
||||
&SYNTAX_SYSTEM,
|
||||
"base16-eighties.dark",
|
||||
)
|
||||
|
|
@ -169,11 +174,11 @@ impl Application for Window {
|
|||
if let Some(path) = &self.path_opt {
|
||||
format!(
|
||||
"COSMIC Text - {} - {}",
|
||||
FONT_SYSTEM.locale(),
|
||||
FONT_SYSTEM.lock().unwrap().locale(),
|
||||
path.display()
|
||||
)
|
||||
} else {
|
||||
format!("COSMIC Text - {}", FONT_SYSTEM.locale())
|
||||
format!("COSMIC Text - {}", FONT_SYSTEM.lock().unwrap().locale())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,13 +242,18 @@ impl Application for Window {
|
|||
}
|
||||
Message::FontSizeChanged(font_size) => {
|
||||
self.font_size = font_size;
|
||||
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
editor.buffer_mut().set_metrics(font_size.to_metrics());
|
||||
editor
|
||||
.borrow_with(&mut FONT_SYSTEM.lock().unwrap())
|
||||
.buffer_mut()
|
||||
.set_metrics(font_size.to_metrics());
|
||||
}
|
||||
Message::WrapChanged(wrap) => {
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
editor.buffer_mut().set_wrap(wrap);
|
||||
editor
|
||||
.borrow_with(&mut FONT_SYSTEM.lock().unwrap())
|
||||
.buffer_mut()
|
||||
.set_wrap(wrap);
|
||||
}
|
||||
Message::AlignmentChanged(align) => {
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
|
|
@ -360,13 +370,13 @@ impl Application for Window {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_attrs<'a, T: Edit<'a>>(editor: &mut T, attrs: Attrs<'a>) {
|
||||
fn update_attrs<T: Edit>(editor: &mut T, attrs: Attrs) {
|
||||
editor.buffer_mut().lines.iter_mut().for_each(|line| {
|
||||
line.set_attrs_list(AttrsList::new(attrs));
|
||||
});
|
||||
}
|
||||
|
||||
fn update_alignment<'a, T: Edit<'a>>(editor: &mut T, align: Align) {
|
||||
fn update_alignment<T: Edit>(editor: &mut T, align: Align) {
|
||||
let current_line = editor.cursor().line;
|
||||
if let Some(select) = editor.select_opt() {
|
||||
let (start, end) = match select.line.cmp(¤t_line) {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ impl Text {
|
|||
let mut line = BufferLine::new(string, AttrsList::new(Attrs::new()));
|
||||
|
||||
//TODO: do we have to immediately shape?
|
||||
line.shape(&crate::FONT_SYSTEM);
|
||||
line.shape(&mut FONT_SYSTEM.lock().unwrap());
|
||||
|
||||
let text = Self {
|
||||
line,
|
||||
|
|
@ -186,7 +186,7 @@ where
|
|||
};
|
||||
|
||||
cache.with_pixels(
|
||||
&FONT_SYSTEM,
|
||||
&mut FONT_SYSTEM.lock().unwrap(),
|
||||
cache_key,
|
||||
glyph_color,
|
||||
|pixel_x, pixel_y, color| {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use crate::FONT_SYSTEM;
|
||||
|
||||
use super::text;
|
||||
use cosmic::{
|
||||
iced_native::{
|
||||
|
|
@ -66,11 +68,11 @@ pub fn text_box<Editor>(editor: &Mutex<Editor>) -> TextBox<Editor> {
|
|||
TextBox::new(editor)
|
||||
}
|
||||
|
||||
impl<'a, 'editor, Editor, Message, Renderer> Widget<Message, Renderer> for TextBox<'a, Editor>
|
||||
impl<'a, Editor, Message, Renderer> Widget<Message, Renderer> for TextBox<'a, Editor>
|
||||
where
|
||||
Renderer: renderer::Renderer + image::Renderer<Handle = image::Handle>,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Editor: Edit<'editor>,
|
||||
Editor: Edit,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
|
|
@ -93,7 +95,10 @@ where
|
|||
|
||||
//TODO: allow lazy shape
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
editor.buffer_mut().shape_until(i32::max_value());
|
||||
editor
|
||||
.borrow_with(&mut FONT_SYSTEM.lock().unwrap())
|
||||
.buffer_mut()
|
||||
.shape_until(i32::max_value());
|
||||
|
||||
let mut layout_lines = 0;
|
||||
for line in editor.buffer().lines.iter() {
|
||||
|
|
@ -162,6 +167,10 @@ where
|
|||
|
||||
let view_w = viewport.width.min(layout.bounds().width) - self.padding.horizontal() as f32;
|
||||
let view_h = viewport.height.min(layout.bounds().height) - self.padding.vertical() as f32;
|
||||
|
||||
let mut font_system = FONT_SYSTEM.lock().unwrap();
|
||||
let mut editor = editor.borrow_with(&mut font_system);
|
||||
|
||||
editor.buffer_mut().set_size(view_w, view_h);
|
||||
|
||||
editor.shape_as_needed();
|
||||
|
|
@ -232,6 +241,8 @@ where
|
|||
) -> Status {
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
let mut font_system = FONT_SYSTEM.lock().unwrap();
|
||||
let mut editor = editor.borrow_with(&mut font_system);
|
||||
|
||||
let mut status = Status::Ignored;
|
||||
match event {
|
||||
|
|
@ -330,12 +341,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'editor, Editor, Message, Renderer> From<TextBox<'a, Editor>>
|
||||
for Element<'a, Message, Renderer>
|
||||
impl<'a, Editor, Message, Renderer> From<TextBox<'a, Editor>> for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: renderer::Renderer + image::Renderer<Handle = image::Handle>,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Editor: Edit<'editor>,
|
||||
Editor: Edit,
|
||||
{
|
||||
fn from(text_box: TextBox<'a, Editor>) -> Self {
|
||||
Self::new(text_box)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ fn main() {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let font_system = FontSystem::new();
|
||||
let mut font_system = FontSystem::new();
|
||||
|
||||
let syntax_system = SyntaxSystem::new();
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ fn main() {
|
|||
let line_x = 8.0 * display_scale;
|
||||
|
||||
let mut editor = SyntaxEditor::new(
|
||||
Buffer::new(&font_system, font_sizes[font_size_i]),
|
||||
Buffer::new(&mut font_system, font_sizes[font_size_i]),
|
||||
&syntax_system,
|
||||
"base16-eighties.dark",
|
||||
)
|
||||
|
|
@ -67,6 +67,8 @@ fn main() {
|
|||
#[cfg(feature = "vi")]
|
||||
let mut editor = cosmic_text::ViEditor::new(editor);
|
||||
|
||||
let mut editor = editor.borrow_with(&mut font_system);
|
||||
|
||||
editor
|
||||
.buffer_mut()
|
||||
.set_size(window.width() as f32 - line_x * 2.0, window.height() as f32);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use cosmic_text::{Action, Buffer, Color, Edit, Editor, FontSystem, Metrics, SwashCache};
|
||||
use cosmic_text::{
|
||||
Action, BorrowedWithFontSystem, Buffer, Color, Edit, Editor, FontSystem, Metrics, SwashCache,
|
||||
};
|
||||
use orbclient::{EventOption, Renderer, Window, WindowFlag};
|
||||
use std::{env, fs, process, time::Instant};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
fn redraw(window: &mut Window, editor: &mut Editor<'_>, swash_cache: &mut SwashCache) {
|
||||
fn redraw(
|
||||
window: &mut Window,
|
||||
editor: &mut BorrowedWithFontSystem<Editor>,
|
||||
swash_cache: &mut SwashCache,
|
||||
) {
|
||||
let bg_color = orbclient::Color::rgb(0x34, 0x34, 0x34);
|
||||
let font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
||||
|
||||
|
|
@ -32,7 +38,7 @@ fn main() {
|
|||
env_logger::init();
|
||||
|
||||
let display_scale = 1.0;
|
||||
let font_system = FontSystem::new();
|
||||
let mut font_system = FontSystem::new();
|
||||
|
||||
let mut window = Window::new_flags(
|
||||
-1,
|
||||
|
|
@ -54,11 +60,15 @@ fn main() {
|
|||
];
|
||||
let font_size_default = 1; // Body
|
||||
|
||||
let mut buffer = Buffer::new(&font_system, font_sizes[font_size_default]);
|
||||
buffer.set_size(window.width() as f32, window.height() as f32);
|
||||
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);
|
||||
|
||||
let mut editor = Editor::new(buffer);
|
||||
|
||||
let mut editor = editor.borrow_with(&mut font_system);
|
||||
|
||||
let mut swash_cache = SwashCache::new();
|
||||
|
||||
let text = if let Some(arg) = env::args().nth(1) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use std::{
|
|||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let font_system = FontSystem::new();
|
||||
let mut font_system = FontSystem::new();
|
||||
|
||||
let display_scale = match orbclient::get_display_size() {
|
||||
Ok((w, h)) => {
|
||||
|
|
@ -37,10 +37,12 @@ fn main() {
|
|||
.unwrap();
|
||||
|
||||
let mut editor = Editor::new(Buffer::new(
|
||||
&font_system,
|
||||
&mut font_system,
|
||||
Metrics::new(32.0, 44.0).scale(display_scale),
|
||||
));
|
||||
|
||||
let mut editor = editor.borrow_with(&mut font_system);
|
||||
|
||||
editor
|
||||
.buffer_mut()
|
||||
.set_size(window.width() as f32, window.height() as f32);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use termion::{color, cursor};
|
|||
|
||||
fn main() {
|
||||
// A FontSystem provides access to detected system fonts, create one per application
|
||||
let font_system = FontSystem::new();
|
||||
let mut font_system = FontSystem::new();
|
||||
|
||||
// A SwashCache stores rasterized glyphs, create one per application
|
||||
let mut swash_cache = SwashCache::new();
|
||||
|
|
@ -15,7 +15,9 @@ fn main() {
|
|||
let metrics = Metrics::new(14.0, 20.0);
|
||||
|
||||
// A Buffer provides shaping and layout for a UTF-8 string, create one per text widget
|
||||
let mut buffer = Buffer::new(&font_system, metrics);
|
||||
let mut buffer = Buffer::new(&mut font_system, metrics);
|
||||
|
||||
let mut buffer = buffer.borrow_with(&mut font_system);
|
||||
|
||||
// Set a size for the text buffer, in pixels
|
||||
let width = 80u16;
|
||||
|
|
|
|||
192
src/buffer.rs
192
src/buffer.rs
|
|
@ -10,7 +10,10 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||
|
||||
#[cfg(feature = "swash")]
|
||||
use crate::Color;
|
||||
use crate::{Attrs, AttrsList, BufferLine, FontSystem, LayoutGlyph, LayoutLine, ShapeLine, Wrap};
|
||||
use crate::{
|
||||
Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, FontSystem, LayoutGlyph, LayoutLine,
|
||||
ShapeLine, Wrap,
|
||||
};
|
||||
|
||||
/// Current cursor location
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
|
||||
|
|
@ -169,8 +172,8 @@ impl<'a> LayoutRun<'a> {
|
|||
}
|
||||
|
||||
/// An iterator of visible text lines, see [`LayoutRun`]
|
||||
pub struct LayoutRunIter<'a, 'b> {
|
||||
buffer: &'b Buffer<'a>,
|
||||
pub struct LayoutRunIter<'b> {
|
||||
buffer: &'b Buffer,
|
||||
line_i: usize,
|
||||
layout_i: usize,
|
||||
remaining_len: usize,
|
||||
|
|
@ -178,8 +181,8 @@ pub struct LayoutRunIter<'a, 'b> {
|
|||
total_layout: i32,
|
||||
}
|
||||
|
||||
impl<'a, 'b> LayoutRunIter<'a, 'b> {
|
||||
pub fn new(buffer: &'b Buffer<'a>) -> Self {
|
||||
impl<'b> LayoutRunIter<'b> {
|
||||
pub fn new(buffer: &'b Buffer) -> Self {
|
||||
let total_layout_lines: usize = buffer
|
||||
.lines
|
||||
.iter()
|
||||
|
|
@ -215,7 +218,7 @@ impl<'a, 'b> LayoutRunIter<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Iterator for LayoutRunIter<'a, 'b> {
|
||||
impl<'b> Iterator for LayoutRunIter<'b> {
|
||||
type Item = LayoutRun<'b>;
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
|
|
@ -258,7 +261,7 @@ impl<'a, 'b> Iterator for LayoutRunIter<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> ExactSizeIterator for LayoutRunIter<'a, 'b> {}
|
||||
impl<'b> ExactSizeIterator for LayoutRunIter<'b> {}
|
||||
|
||||
/// Metrics of text
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
|
|
@ -296,8 +299,7 @@ impl fmt::Display for Metrics {
|
|||
}
|
||||
|
||||
/// A buffer of text that is shaped and laid out
|
||||
pub struct Buffer<'a> {
|
||||
font_system: &'a FontSystem,
|
||||
pub struct Buffer {
|
||||
/// [BufferLine]s (or paragraphs) of text in the buffer
|
||||
pub lines: Vec<BufferLine>,
|
||||
metrics: Metrics,
|
||||
|
|
@ -309,17 +311,16 @@ pub struct Buffer<'a> {
|
|||
wrap: Wrap,
|
||||
}
|
||||
|
||||
impl<'a> Buffer<'a> {
|
||||
impl Buffer {
|
||||
/// Create a new [`Buffer`] with the provided [`FontSystem`] and [`Metrics`]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `metrics.line_height` is zero.
|
||||
pub fn new(font_system: &'a FontSystem, metrics: Metrics) -> Self {
|
||||
pub fn new(font_system: &mut FontSystem, metrics: Metrics) -> Self {
|
||||
assert_ne!(metrics.line_height, 0.0, "line height cannot be 0");
|
||||
|
||||
let mut buffer = Self {
|
||||
font_system,
|
||||
lines: Vec::new(),
|
||||
metrics,
|
||||
width: 0.0,
|
||||
|
|
@ -328,23 +329,29 @@ impl<'a> Buffer<'a> {
|
|||
redraw: false,
|
||||
wrap: Wrap::Word,
|
||||
};
|
||||
buffer.set_text("", Attrs::new());
|
||||
buffer.set_text(font_system, "", Attrs::new());
|
||||
buffer
|
||||
}
|
||||
|
||||
fn relayout(&mut self) {
|
||||
/// Mutably borrows the buffer together with an [`FontSystem`] for more convenient methods
|
||||
pub fn borrow_with<'a>(
|
||||
&'a mut self,
|
||||
font_system: &'a mut FontSystem,
|
||||
) -> BorrowedWithFontSystem<'a, Buffer> {
|
||||
BorrowedWithFontSystem {
|
||||
inner: self,
|
||||
font_system,
|
||||
}
|
||||
}
|
||||
|
||||
fn relayout(&mut self, font_system: &mut FontSystem) {
|
||||
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
||||
let instant = std::time::Instant::now();
|
||||
|
||||
for line in &mut self.lines {
|
||||
if line.shape_opt().is_some() {
|
||||
line.reset_layout();
|
||||
line.layout(
|
||||
self.font_system,
|
||||
self.metrics.font_size,
|
||||
self.width,
|
||||
self.wrap,
|
||||
);
|
||||
line.layout(font_system, self.metrics.font_size, self.width, self.wrap);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +362,7 @@ impl<'a> Buffer<'a> {
|
|||
}
|
||||
|
||||
/// Pre-shape lines in the buffer, up to `lines`, return actual number of layout lines
|
||||
pub fn shape_until(&mut self, lines: i32) -> i32 {
|
||||
pub fn shape_until(&mut self, font_system: &mut FontSystem, lines: i32) -> i32 {
|
||||
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
||||
let instant = std::time::Instant::now();
|
||||
|
||||
|
|
@ -369,12 +376,7 @@ impl<'a> Buffer<'a> {
|
|||
if line.shape_opt().is_none() {
|
||||
reshaped += 1;
|
||||
}
|
||||
let layout = line.layout(
|
||||
self.font_system,
|
||||
self.metrics.font_size,
|
||||
self.width,
|
||||
self.wrap,
|
||||
);
|
||||
let layout = line.layout(font_system, self.metrics.font_size, self.width, self.wrap);
|
||||
total_layout += layout.len() as i32;
|
||||
}
|
||||
|
||||
|
|
@ -388,7 +390,7 @@ impl<'a> Buffer<'a> {
|
|||
}
|
||||
|
||||
/// Shape lines until cursor, also scrolling to include cursor in view
|
||||
pub fn shape_until_cursor(&mut self, cursor: Cursor) {
|
||||
pub fn shape_until_cursor(&mut self, font_system: &mut FontSystem, cursor: Cursor) {
|
||||
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
||||
let instant = std::time::Instant::now();
|
||||
|
||||
|
|
@ -402,12 +404,7 @@ impl<'a> Buffer<'a> {
|
|||
if line.shape_opt().is_none() {
|
||||
reshaped += 1;
|
||||
}
|
||||
let layout = line.layout(
|
||||
self.font_system,
|
||||
self.metrics.font_size,
|
||||
self.width,
|
||||
self.wrap,
|
||||
);
|
||||
let layout = line.layout(font_system, self.metrics.font_size, self.width, self.wrap);
|
||||
if line_i == cursor.line {
|
||||
let layout_cursor = self.layout_cursor(&cursor);
|
||||
layout_i += layout_cursor.layout as i32;
|
||||
|
|
@ -430,15 +427,15 @@ impl<'a> Buffer<'a> {
|
|||
self.scroll = layout_i - (lines - 1);
|
||||
}
|
||||
|
||||
self.shape_until_scroll();
|
||||
self.shape_until_scroll(font_system);
|
||||
}
|
||||
|
||||
/// Shape lines until scroll
|
||||
pub fn shape_until_scroll(&mut self) {
|
||||
pub fn shape_until_scroll(&mut self, font_system: &mut FontSystem) {
|
||||
let lines = self.visible_lines();
|
||||
|
||||
let scroll_end = self.scroll + lines;
|
||||
let total_layout = self.shape_until(scroll_end);
|
||||
let total_layout = self.shape_until(font_system, scroll_end);
|
||||
|
||||
self.scroll = cmp::max(0, cmp::min(total_layout - (lines - 1), self.scroll));
|
||||
}
|
||||
|
|
@ -473,26 +470,24 @@ impl<'a> Buffer<'a> {
|
|||
LayoutCursor::new(cursor.line, 0, 0)
|
||||
}
|
||||
|
||||
/// Get [`FontSystem`] used by this [`Buffer`]
|
||||
pub fn font_system(&self) -> &'a FontSystem {
|
||||
self.font_system
|
||||
}
|
||||
|
||||
/// Shape the provided line index and return the result
|
||||
pub fn line_shape(&mut self, line_i: usize) -> Option<&ShapeLine> {
|
||||
pub fn line_shape(
|
||||
&mut self,
|
||||
font_system: &mut FontSystem,
|
||||
line_i: usize,
|
||||
) -> Option<&ShapeLine> {
|
||||
let line = self.lines.get_mut(line_i)?;
|
||||
Some(line.shape(self.font_system))
|
||||
Some(line.shape(font_system))
|
||||
}
|
||||
|
||||
/// Lay out the provided line index and return the result
|
||||
pub fn line_layout(&mut self, line_i: usize) -> Option<&[LayoutLine]> {
|
||||
pub fn line_layout(
|
||||
&mut self,
|
||||
font_system: &mut FontSystem,
|
||||
line_i: usize,
|
||||
) -> Option<&[LayoutLine]> {
|
||||
let line = self.lines.get_mut(line_i)?;
|
||||
Some(line.layout(
|
||||
self.font_system,
|
||||
self.metrics.font_size,
|
||||
self.width,
|
||||
self.wrap,
|
||||
))
|
||||
Some(line.layout(font_system, self.metrics.font_size, self.width, self.wrap))
|
||||
}
|
||||
|
||||
/// Get the current [`Metrics`]
|
||||
|
|
@ -505,12 +500,12 @@ impl<'a> Buffer<'a> {
|
|||
/// # Panics
|
||||
///
|
||||
/// Will panic if `metrics.font_size` is zero.
|
||||
pub fn set_metrics(&mut self, metrics: Metrics) {
|
||||
pub fn set_metrics(&mut self, font_system: &mut FontSystem, metrics: Metrics) {
|
||||
if metrics != self.metrics {
|
||||
assert_ne!(metrics.font_size, 0.0, "font size cannot be 0");
|
||||
self.metrics = metrics;
|
||||
self.relayout();
|
||||
self.shape_until_scroll();
|
||||
self.relayout(font_system);
|
||||
self.shape_until_scroll(font_system);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -520,11 +515,11 @@ impl<'a> Buffer<'a> {
|
|||
}
|
||||
|
||||
/// Set the current [`Wrap`]
|
||||
pub fn set_wrap(&mut self, wrap: Wrap) {
|
||||
pub fn set_wrap(&mut self, font_system: &mut FontSystem, wrap: Wrap) {
|
||||
if wrap != self.wrap {
|
||||
self.wrap = wrap;
|
||||
self.relayout();
|
||||
self.shape_until_scroll();
|
||||
self.relayout(font_system);
|
||||
self.shape_until_scroll(font_system);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -534,15 +529,15 @@ impl<'a> Buffer<'a> {
|
|||
}
|
||||
|
||||
/// Set the current buffer dimensions
|
||||
pub fn set_size(&mut self, width: f32, height: f32) {
|
||||
pub fn set_size(&mut self, font_system: &mut FontSystem, width: f32, height: f32) {
|
||||
let clamped_width = width.max(0.0);
|
||||
let clamped_height = height.max(0.0);
|
||||
|
||||
if clamped_width != self.width || clamped_height != self.height {
|
||||
self.width = clamped_width;
|
||||
self.height = clamped_height;
|
||||
self.relayout();
|
||||
self.shape_until_scroll();
|
||||
self.relayout(font_system);
|
||||
self.shape_until_scroll(font_system);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -565,7 +560,7 @@ impl<'a> Buffer<'a> {
|
|||
}
|
||||
|
||||
/// Set text of buffer, using provided attributes for each line by default
|
||||
pub fn set_text(&mut self, text: &str, attrs: Attrs<'a>) {
|
||||
pub fn set_text(&mut self, font_system: &mut FontSystem, text: &str, attrs: Attrs) {
|
||||
self.lines.clear();
|
||||
for line in text.lines() {
|
||||
self.lines
|
||||
|
|
@ -579,7 +574,7 @@ impl<'a> Buffer<'a> {
|
|||
|
||||
self.scroll = 0;
|
||||
|
||||
self.shape_until_scroll();
|
||||
self.shape_until_scroll(font_system);
|
||||
}
|
||||
|
||||
/// True if a redraw is needed
|
||||
|
|
@ -593,7 +588,7 @@ impl<'a> Buffer<'a> {
|
|||
}
|
||||
|
||||
/// Get the visible layout runs for rendering and other tasks
|
||||
pub fn layout_runs<'b>(&'b self) -> LayoutRunIter<'a, 'b> {
|
||||
pub fn layout_runs(&self) -> LayoutRunIter {
|
||||
LayoutRunIter::new(self)
|
||||
}
|
||||
|
||||
|
|
@ -700,8 +695,13 @@ impl<'a> Buffer<'a> {
|
|||
|
||||
/// Draw the buffer
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&self, cache: &mut crate::SwashCache, color: Color, mut f: F)
|
||||
where
|
||||
pub fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
for run in self.layout_runs() {
|
||||
|
|
@ -713,10 +713,70 @@ impl<'a> Buffer<'a> {
|
|||
None => color,
|
||||
};
|
||||
|
||||
cache.with_pixels(self.font_system, cache_key, glyph_color, |x, y, color| {
|
||||
cache.with_pixels(font_system, cache_key, glyph_color, |x, y, color| {
|
||||
f(x_int + x, run.line_y as i32 + y_int + y, 1, 1, color);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BorrowedWithFontSystem<'a, Buffer> {
|
||||
/// Pre-shape lines in the buffer, up to `lines`, return actual number of layout lines
|
||||
pub fn shape_until(&mut self, lines: i32) -> i32 {
|
||||
self.inner.shape_until(self.font_system, lines)
|
||||
}
|
||||
|
||||
/// Shape lines until cursor, also scrolling to include cursor in view
|
||||
pub fn shape_until_cursor(&mut self, cursor: Cursor) {
|
||||
self.inner.shape_until_cursor(self.font_system, cursor);
|
||||
}
|
||||
|
||||
/// Shape lines until scroll
|
||||
pub fn shape_until_scroll(&mut self) {
|
||||
self.inner.shape_until_scroll(self.font_system);
|
||||
}
|
||||
|
||||
/// Shape the provided line index and return the result
|
||||
pub fn line_shape(&mut self, line_i: usize) -> Option<&ShapeLine> {
|
||||
self.inner.line_shape(self.font_system, line_i)
|
||||
}
|
||||
|
||||
/// Lay out the provided line index and return the result
|
||||
pub fn line_layout(&mut self, line_i: usize) -> Option<&[LayoutLine]> {
|
||||
self.inner.line_layout(self.font_system, line_i)
|
||||
}
|
||||
|
||||
/// Set the current [`Metrics`]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `metrics.font_size` is zero.
|
||||
pub fn set_metrics(&mut self, metrics: Metrics) {
|
||||
self.inner.set_metrics(self.font_system, metrics);
|
||||
}
|
||||
|
||||
/// Set the current [`Wrap`]
|
||||
pub fn set_wrap(&mut self, wrap: Wrap) {
|
||||
self.inner.set_wrap(self.font_system, wrap);
|
||||
}
|
||||
|
||||
/// Set the current buffer dimensions
|
||||
pub fn set_size(&mut self, width: f32, height: f32) {
|
||||
self.inner.set_size(self.font_system, width, height);
|
||||
}
|
||||
|
||||
/// Set text of buffer, using provided attributes for each line by default
|
||||
pub fn set_text(&mut self, text: &str, attrs: Attrs) {
|
||||
self.inner.set_text(self.font_system, text, attrs);
|
||||
}
|
||||
|
||||
/// Draw the buffer
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, color: Color, f: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
self.inner.draw(self.font_system, cache, color, f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ impl BufferLine {
|
|||
}
|
||||
|
||||
/// Shape line, will cache results
|
||||
pub fn shape(&mut self, font_system: &FontSystem) -> &ShapeLine {
|
||||
pub fn shape(&mut self, font_system: &mut FontSystem) -> &ShapeLine {
|
||||
if self.shape_opt.is_none() {
|
||||
self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_list));
|
||||
self.layout_opt = None;
|
||||
|
|
@ -183,7 +183,7 @@ impl BufferLine {
|
|||
/// Layout line, will cache results
|
||||
pub fn layout(
|
||||
&mut self,
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
font_size: f32,
|
||||
width: f32,
|
||||
wrap: Wrap,
|
||||
|
|
|
|||
|
|
@ -10,20 +10,22 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||
|
||||
#[cfg(feature = "swash")]
|
||||
use crate::Color;
|
||||
use crate::{Action, Affinity, AttrsList, Buffer, BufferLine, Cursor, Edit, LayoutCursor};
|
||||
use crate::{
|
||||
Action, Affinity, AttrsList, Buffer, BufferLine, Cursor, Edit, FontSystem, LayoutCursor,
|
||||
};
|
||||
|
||||
/// A wrapper of [`Buffer`] for easy editing
|
||||
pub struct Editor<'a> {
|
||||
buffer: Buffer<'a>,
|
||||
pub struct Editor {
|
||||
buffer: Buffer,
|
||||
cursor: Cursor,
|
||||
cursor_x_opt: Option<i32>,
|
||||
select_opt: Option<Cursor>,
|
||||
cursor_moved: bool,
|
||||
}
|
||||
|
||||
impl<'a> Editor<'a> {
|
||||
impl Editor {
|
||||
/// Create a new [`Editor`] with the provided [`Buffer`]
|
||||
pub fn new(buffer: Buffer<'a>) -> Self {
|
||||
pub fn new(buffer: Buffer) -> Self {
|
||||
Self {
|
||||
buffer,
|
||||
cursor: Cursor::default(),
|
||||
|
|
@ -33,10 +35,10 @@ impl<'a> Editor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_layout_cursor(&mut self, cursor: LayoutCursor) {
|
||||
fn set_layout_cursor(&mut self, font_system: &mut FontSystem, cursor: LayoutCursor) {
|
||||
let layout = self
|
||||
.buffer
|
||||
.line_layout(cursor.line)
|
||||
.line_layout(font_system, cursor.line)
|
||||
.expect("layout not found");
|
||||
|
||||
let layout_line = match layout.get(cursor.layout) {
|
||||
|
|
@ -68,12 +70,12 @@ impl<'a> Editor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Edit<'a> for Editor<'a> {
|
||||
fn buffer(&self) -> &Buffer<'a> {
|
||||
impl Edit for Editor {
|
||||
fn buffer(&self) -> &Buffer {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
fn buffer_mut(&mut self) -> &mut Buffer<'a> {
|
||||
fn buffer_mut(&mut self) -> &mut Buffer {
|
||||
&mut self.buffer
|
||||
}
|
||||
|
||||
|
|
@ -92,12 +94,12 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn shape_as_needed(&mut self) {
|
||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem) {
|
||||
if self.cursor_moved {
|
||||
self.buffer.shape_until_cursor(self.cursor);
|
||||
self.buffer.shape_until_cursor(font_system, self.cursor);
|
||||
self.cursor_moved = false;
|
||||
} else {
|
||||
self.buffer.shape_until_scroll();
|
||||
self.buffer.shape_until_scroll(font_system);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +281,7 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
self.cursor.index = self.buffer.lines[self.cursor.line].text().len() - after_len;
|
||||
}
|
||||
|
||||
fn action(&mut self, action: Action) {
|
||||
fn action(&mut self, font_system: &mut FontSystem, action: Action) {
|
||||
let old_cursor = self.cursor;
|
||||
|
||||
match action {
|
||||
|
|
@ -333,9 +335,9 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
self.action(Action::Next);
|
||||
self.action(font_system, Action::Next);
|
||||
} else {
|
||||
self.action(Action::Previous);
|
||||
self.action(font_system, Action::Previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -346,9 +348,9 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
self.action(Action::Previous);
|
||||
self.action(font_system, Action::Previous);
|
||||
} else {
|
||||
self.action(Action::Next);
|
||||
self.action(font_system, Action::Next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -373,7 +375,7 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
cursor.glyph = cursor_x as usize; //TODO: glyph x position
|
||||
}
|
||||
|
||||
self.set_layout_cursor(cursor);
|
||||
self.set_layout_cursor(font_system, cursor);
|
||||
}
|
||||
Action::Down => {
|
||||
//TODO: make this preserve X as best as possible!
|
||||
|
|
@ -381,7 +383,7 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
|
||||
let layout_len = self
|
||||
.buffer
|
||||
.line_layout(cursor.line)
|
||||
.line_layout(font_system, cursor.line)
|
||||
.expect("layout not found")
|
||||
.len();
|
||||
|
||||
|
|
@ -402,18 +404,18 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
cursor.glyph = cursor_x as usize; //TODO: glyph x position
|
||||
}
|
||||
|
||||
self.set_layout_cursor(cursor);
|
||||
self.set_layout_cursor(font_system, cursor);
|
||||
}
|
||||
Action::Home => {
|
||||
let mut cursor = self.buffer.layout_cursor(&self.cursor);
|
||||
cursor.glyph = 0;
|
||||
self.set_layout_cursor(cursor);
|
||||
self.set_layout_cursor(font_system, cursor);
|
||||
self.cursor_x_opt = None;
|
||||
}
|
||||
Action::End => {
|
||||
let mut cursor = self.buffer.layout_cursor(&self.cursor);
|
||||
cursor.glyph = usize::max_value();
|
||||
self.set_layout_cursor(cursor);
|
||||
self.set_layout_cursor(font_system, cursor);
|
||||
self.cursor_x_opt = None;
|
||||
}
|
||||
Action::ParagraphStart => {
|
||||
|
|
@ -427,10 +429,10 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
self.buffer.set_redraw(true);
|
||||
}
|
||||
Action::PageUp => {
|
||||
self.action(Action::Vertical(-self.buffer.size().1 as i32));
|
||||
self.action(font_system, Action::Vertical(-self.buffer.size().1 as i32));
|
||||
}
|
||||
Action::PageDown => {
|
||||
self.action(Action::Vertical(self.buffer.size().1 as i32));
|
||||
self.action(font_system, Action::Vertical(self.buffer.size().1 as i32));
|
||||
}
|
||||
Action::Vertical(px) => {
|
||||
// TODO more efficient
|
||||
|
|
@ -438,12 +440,12 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
match lines.cmp(&0) {
|
||||
Ordering::Less => {
|
||||
for _ in 0..-lines {
|
||||
self.action(Action::Up);
|
||||
self.action(font_system, Action::Up);
|
||||
}
|
||||
}
|
||||
Ordering::Greater => {
|
||||
for _ in 0..lines {
|
||||
self.action(Action::Down);
|
||||
self.action(font_system, Action::Down);
|
||||
}
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
|
|
@ -459,7 +461,7 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
// Filter out special chars (except for tab), use Action instead
|
||||
log::debug!("Refusing to insert control character {:?}", character);
|
||||
} else if character == '\n' {
|
||||
self.action(Action::Enter);
|
||||
self.action(font_system, Action::Enter);
|
||||
} else {
|
||||
let mut str_buf = [0u8; 8];
|
||||
let str_ref = character.encode_utf8(&mut str_buf);
|
||||
|
|
@ -619,9 +621,9 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
self.action(Action::NextWord);
|
||||
self.action(font_system, Action::NextWord);
|
||||
} else {
|
||||
self.action(Action::PreviousWord);
|
||||
self.action(font_system, Action::PreviousWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -632,9 +634,9 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
self.action(Action::PreviousWord);
|
||||
self.action(font_system, Action::PreviousWord);
|
||||
} else {
|
||||
self.action(Action::NextWord);
|
||||
self.action(font_system, Action::NextWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -673,8 +675,13 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(&self, cache: &mut crate::SwashCache, color: Color, mut f: F)
|
||||
where
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let font_size = self.buffer.metrics().font_size;
|
||||
|
|
@ -833,14 +840,9 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
None => color,
|
||||
};
|
||||
|
||||
cache.with_pixels(
|
||||
self.buffer.font_system(),
|
||||
cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(x_int + x, line_y as i32 + y_int + y, 1, 1, color);
|
||||
},
|
||||
);
|
||||
cache.with_pixels(font_system, cache_key, glyph_color, |x, y, color| {
|
||||
f(x_int + x, line_y as i32 + y_int + y, 1, 1, color);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use alloc::string::String;
|
|||
|
||||
#[cfg(feature = "swash")]
|
||||
use crate::Color;
|
||||
use crate::{AttrsList, Buffer, Cursor};
|
||||
use crate::{AttrsList, BorrowedWithFontSystem, Buffer, Cursor, FontSystem};
|
||||
|
||||
pub use self::editor::*;
|
||||
mod editor;
|
||||
|
|
@ -78,12 +78,26 @@ pub enum Action {
|
|||
}
|
||||
|
||||
/// A trait to allow easy replacements of [`Editor`], like `SyntaxEditor`
|
||||
pub trait Edit<'a> {
|
||||
pub trait Edit {
|
||||
/// Mutably borrows `self` together with an [`FontSystem`] for more convenient methods
|
||||
fn borrow_with<'a>(
|
||||
&'a mut self,
|
||||
font_system: &'a mut FontSystem,
|
||||
) -> BorrowedWithFontSystem<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
BorrowedWithFontSystem {
|
||||
inner: self,
|
||||
font_system,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the internal [`Buffer`]
|
||||
fn buffer(&self) -> &Buffer<'a>;
|
||||
fn buffer(&self) -> &Buffer;
|
||||
|
||||
/// Get the internal [`Buffer`], mutably
|
||||
fn buffer_mut(&mut self) -> &mut Buffer<'a>;
|
||||
fn buffer_mut(&mut self) -> &mut Buffer;
|
||||
|
||||
/// Get the current cursor position
|
||||
fn cursor(&self) -> Cursor;
|
||||
|
|
@ -95,7 +109,7 @@ pub trait Edit<'a> {
|
|||
fn set_select_opt(&mut self, select_opt: Option<Cursor>);
|
||||
|
||||
/// Shape lines until scroll, after adjusting scroll if the cursor moved
|
||||
fn shape_as_needed(&mut self);
|
||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem);
|
||||
|
||||
/// Copy selection
|
||||
fn copy_selection(&mut self) -> Option<String>;
|
||||
|
|
@ -109,11 +123,45 @@ pub trait Edit<'a> {
|
|||
fn insert_string(&mut self, data: &str, attrs_list: Option<AttrsList>);
|
||||
|
||||
/// Perform an [Action] on the editor
|
||||
fn action(&mut self, action: Action);
|
||||
fn action(&mut self, font_system: &mut FontSystem, action: Action);
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(&self, cache: &mut crate::SwashCache, color: Color, f: F)
|
||||
where
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color);
|
||||
}
|
||||
|
||||
impl<'a, T: Edit> BorrowedWithFontSystem<'a, T> {
|
||||
/// Get the internal [`Buffer`], mutably
|
||||
pub fn buffer_mut(&mut self) -> BorrowedWithFontSystem<Buffer> {
|
||||
BorrowedWithFontSystem {
|
||||
inner: self.inner.buffer_mut(),
|
||||
font_system: self.font_system,
|
||||
}
|
||||
}
|
||||
|
||||
/// Shape lines until scroll, after adjusting scroll if the cursor moved
|
||||
pub fn shape_as_needed(&mut self) {
|
||||
self.inner.shape_as_needed(self.font_system);
|
||||
}
|
||||
|
||||
/// Perform an [Action] on the editor
|
||||
pub fn action(&mut self, action: Action) {
|
||||
self.inner.action(self.font_system, action);
|
||||
}
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, color: Color, f: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
self.inner.draw(self.font_system, cache, color, f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ use syntect::highlighting::{
|
|||
};
|
||||
use syntect::parsing::{ParseState, ScopeStack, SyntaxReference, SyntaxSet};
|
||||
|
||||
use crate::{Action, AttrsList, Buffer, Color, Cursor, Edit, Editor, Style, Weight, Wrap};
|
||||
use crate::{
|
||||
Action, AttrsList, BorrowedWithFontSystem, Buffer, Color, Cursor, Edit, Editor, FontSystem,
|
||||
Style, Weight, Wrap,
|
||||
};
|
||||
|
||||
pub struct SyntaxSystem {
|
||||
pub syntax_set: SyntaxSet,
|
||||
|
|
@ -27,7 +30,7 @@ impl SyntaxSystem {
|
|||
|
||||
/// A wrapper of [`Editor`] with syntax highlighting provided by [`SyntaxSystem`]
|
||||
pub struct SyntaxEditor<'a> {
|
||||
editor: Editor<'a>,
|
||||
editor: Editor,
|
||||
syntax_system: &'a SyntaxSystem,
|
||||
syntax: &'a SyntaxReference,
|
||||
theme: &'a Theme,
|
||||
|
|
@ -41,11 +44,7 @@ impl<'a> SyntaxEditor<'a> {
|
|||
/// A good default theme name is "base16-eighties.dark".
|
||||
///
|
||||
/// Returns None if theme not found
|
||||
pub fn new(
|
||||
buffer: Buffer<'a>,
|
||||
syntax_system: &'a SyntaxSystem,
|
||||
theme_name: &str,
|
||||
) -> Option<Self> {
|
||||
pub fn new(buffer: Buffer, syntax_system: &'a SyntaxSystem, theme_name: &str) -> Option<Self> {
|
||||
let editor = Editor::new(buffer);
|
||||
let syntax = syntax_system.syntax_set.find_syntax_plain_text();
|
||||
let theme = syntax_system.theme_set.themes.get(theme_name)?;
|
||||
|
|
@ -69,13 +68,14 @@ impl<'a> SyntaxEditor<'a> {
|
|||
#[cfg(feature = "std")]
|
||||
pub fn load_text<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
font_system: &mut FontSystem,
|
||||
path: P,
|
||||
attrs: crate::Attrs<'a>,
|
||||
attrs: crate::Attrs,
|
||||
) -> io::Result<()> {
|
||||
let path = path.as_ref();
|
||||
|
||||
let text = fs::read_to_string(path)?;
|
||||
self.editor.buffer_mut().set_text(&text, attrs);
|
||||
self.editor.buffer_mut().set_text(font_system, &text, attrs);
|
||||
|
||||
//TODO: re-use text
|
||||
self.syntax = match self.syntax_system.syntax_set.find_syntax_for_file(path) {
|
||||
|
|
@ -115,12 +115,12 @@ impl<'a> SyntaxEditor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Edit<'a> for SyntaxEditor<'a> {
|
||||
fn buffer(&self) -> &Buffer<'a> {
|
||||
impl<'a> Edit for SyntaxEditor<'a> {
|
||||
fn buffer(&self) -> &Buffer {
|
||||
self.editor.buffer()
|
||||
}
|
||||
|
||||
fn buffer_mut(&mut self) -> &mut Buffer<'a> {
|
||||
fn buffer_mut(&mut self) -> &mut Buffer {
|
||||
self.editor.buffer_mut()
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ impl<'a> Edit<'a> for SyntaxEditor<'a> {
|
|||
self.editor.set_select_opt(select_opt);
|
||||
}
|
||||
|
||||
fn shape_as_needed(&mut self) {
|
||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem) {
|
||||
#[cfg(feature = "std")]
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ impl<'a> Edit<'a> for SyntaxEditor<'a> {
|
|||
line.set_wrap(Wrap::Word);
|
||||
|
||||
//TODO: efficiently do syntax highlighting without having to shape whole buffer
|
||||
buffer.line_shape(line_i);
|
||||
buffer.line_shape(font_system, line_i);
|
||||
|
||||
let cache_item = (parse_state.clone(), highlight_state.clone());
|
||||
if line_i < self.syntax_cache.len() {
|
||||
|
|
@ -226,7 +226,7 @@ impl<'a> Edit<'a> for SyntaxEditor<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
self.editor.shape_as_needed();
|
||||
self.editor.shape_as_needed(font_system);
|
||||
}
|
||||
|
||||
fn copy_selection(&mut self) -> Option<String> {
|
||||
|
|
@ -241,18 +241,36 @@ impl<'a> Edit<'a> for SyntaxEditor<'a> {
|
|||
self.editor.insert_string(data, attrs_list);
|
||||
}
|
||||
|
||||
fn action(&mut self, action: Action) {
|
||||
self.editor.action(action);
|
||||
fn action(&mut self, font_system: &mut FontSystem, action: Action) {
|
||||
self.editor.action(font_system, action);
|
||||
}
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(&self, cache: &mut crate::SwashCache, _color: Color, mut f: F)
|
||||
where
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
_color: Color,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let size = self.buffer().size();
|
||||
f(0, 0, size.0 as u32, size.1 as u32, self.background_color());
|
||||
self.editor.draw(cache, self.foreground_color(), f);
|
||||
self.editor
|
||||
.draw(font_system, cache, self.foreground_color(), f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> BorrowedWithFontSystem<'b, SyntaxEditor<'a>> {
|
||||
/// Load text from a file, and also set syntax to the best option
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// Returns an [`io::Error`] if reading the file fails
|
||||
#[cfg(feature = "std")]
|
||||
pub fn load_text<P: AsRef<Path>>(&mut self, path: P, attrs: crate::Attrs) -> io::Result<()> {
|
||||
self.inner.load_text(self.font_system, path, attrs)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
104
src/edit/vi.rs
104
src/edit/vi.rs
|
|
@ -2,7 +2,10 @@ use alloc::string::String;
|
|||
use core::cmp;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use crate::{Action, AttrsList, Buffer, Color, Cursor, Edit, SyntaxEditor};
|
||||
use crate::{
|
||||
Action, AttrsList, BorrowedWithFontSystem, Buffer, Color, Cursor, Edit, FontSystem,
|
||||
SyntaxEditor,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum Mode {
|
||||
|
|
@ -30,10 +33,11 @@ impl<'a> ViEditor<'a> {
|
|||
#[cfg(feature = "std")]
|
||||
pub fn load_text<P: AsRef<std::path::Path>>(
|
||||
&mut self,
|
||||
font_system: &mut FontSystem,
|
||||
path: P,
|
||||
attrs: crate::Attrs<'a>,
|
||||
attrs: crate::Attrs,
|
||||
) -> std::io::Result<()> {
|
||||
self.editor.load_text(path, attrs)
|
||||
self.editor.load_text(font_system, path, attrs)
|
||||
}
|
||||
|
||||
/// Get the default background color
|
||||
|
|
@ -47,12 +51,12 @@ impl<'a> ViEditor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Edit<'a> for ViEditor<'a> {
|
||||
fn buffer(&self) -> &Buffer<'a> {
|
||||
impl<'a> Edit for ViEditor<'a> {
|
||||
fn buffer(&self) -> &Buffer {
|
||||
self.editor.buffer()
|
||||
}
|
||||
|
||||
fn buffer_mut(&mut self) -> &mut Buffer<'a> {
|
||||
fn buffer_mut(&mut self) -> &mut Buffer {
|
||||
self.editor.buffer_mut()
|
||||
}
|
||||
|
||||
|
|
@ -68,8 +72,8 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
self.editor.set_select_opt(select_opt);
|
||||
}
|
||||
|
||||
fn shape_as_needed(&mut self) {
|
||||
self.editor.shape_as_needed()
|
||||
fn shape_as_needed(&mut self, font_system: &mut FontSystem) {
|
||||
self.editor.shape_as_needed(font_system);
|
||||
}
|
||||
|
||||
fn copy_selection(&mut self) -> Option<String> {
|
||||
|
|
@ -84,7 +88,7 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
self.editor.insert_string(data, attrs_list);
|
||||
}
|
||||
|
||||
fn action(&mut self, action: Action) {
|
||||
fn action(&mut self, font_system: &mut FontSystem, action: Action) {
|
||||
let old_mode = self.mode;
|
||||
|
||||
match self.mode {
|
||||
|
|
@ -92,18 +96,18 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
Action::Insert(c) => match c {
|
||||
// Enter insert mode after cursor
|
||||
'a' => {
|
||||
self.editor.action(Action::Right);
|
||||
self.editor.action(font_system, Action::Right);
|
||||
self.mode = Mode::Insert;
|
||||
}
|
||||
// Enter insert mode at end of line
|
||||
'A' => {
|
||||
self.editor.action(Action::End);
|
||||
self.editor.action(font_system, Action::End);
|
||||
self.mode = Mode::Insert;
|
||||
}
|
||||
// Change mode
|
||||
'c' => {
|
||||
if self.editor.select_opt().is_some() {
|
||||
self.editor.action(Action::Delete);
|
||||
self.editor.action(font_system, Action::Delete);
|
||||
self.mode = Mode::Insert;
|
||||
} else {
|
||||
//TODO: change to next cursor movement
|
||||
|
|
@ -112,7 +116,7 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
// Delete mode
|
||||
'd' => {
|
||||
if self.editor.select_opt().is_some() {
|
||||
self.editor.action(Action::Delete);
|
||||
self.editor.action(font_system, Action::Delete);
|
||||
} else {
|
||||
//TODO: delete to next cursor movement
|
||||
}
|
||||
|
|
@ -124,33 +128,33 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
// Enter insert mode at start of line
|
||||
'I' => {
|
||||
//TODO: soft home, skip whitespace
|
||||
self.editor.action(Action::Home);
|
||||
self.editor.action(font_system, Action::Home);
|
||||
self.mode = Mode::Insert;
|
||||
}
|
||||
// Create line after and enter insert mode
|
||||
'o' => {
|
||||
self.editor.action(Action::End);
|
||||
self.editor.action(Action::Enter);
|
||||
self.editor.action(font_system, Action::End);
|
||||
self.editor.action(font_system, Action::Enter);
|
||||
self.mode = Mode::Insert;
|
||||
}
|
||||
// Create line before and enter insert mode
|
||||
'O' => {
|
||||
self.editor.action(Action::Home);
|
||||
self.editor.action(Action::Enter);
|
||||
self.editor.shape_as_needed(); // TODO: do not require this?
|
||||
self.editor.action(Action::Up);
|
||||
self.editor.action(font_system, Action::Home);
|
||||
self.editor.action(font_system, Action::Enter);
|
||||
self.editor.shape_as_needed(font_system); // TODO: do not require this?
|
||||
self.editor.action(font_system, Action::Up);
|
||||
self.mode = Mode::Insert;
|
||||
}
|
||||
// Left
|
||||
'h' => self.editor.action(Action::Left),
|
||||
'h' => self.editor.action(font_system, Action::Left),
|
||||
// Top of screen
|
||||
//TODO: 'H' => self.editor.action(Action::ScreenHigh),
|
||||
// Down
|
||||
'j' => self.editor.action(Action::Down),
|
||||
'j' => self.editor.action(font_system, Action::Down),
|
||||
// Up
|
||||
'k' => self.editor.action(Action::Up),
|
||||
'k' => self.editor.action(font_system, Action::Up),
|
||||
// Right
|
||||
'l' => self.editor.action(Action::Right),
|
||||
'l' => self.editor.action(font_system, Action::Right),
|
||||
// Bottom of screen
|
||||
//TODO: 'L' => self.editor.action(Action::ScreenLow),
|
||||
// Middle of screen
|
||||
|
|
@ -168,23 +172,23 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
if self.editor.select_opt().is_some() {
|
||||
self.editor.set_select_opt(None);
|
||||
} else {
|
||||
self.editor.action(Action::Home);
|
||||
self.editor.action(font_system, Action::Home);
|
||||
self.editor.set_select_opt(Some(self.editor.cursor()));
|
||||
//TODO: set cursor_x_opt to max
|
||||
self.editor.action(Action::End);
|
||||
self.editor.action(font_system, Action::End);
|
||||
}
|
||||
}
|
||||
// Remove character at cursor
|
||||
'x' => self.editor.action(Action::Delete),
|
||||
'x' => self.editor.action(font_system, Action::Delete),
|
||||
// Remove character before cursor
|
||||
'X' => self.editor.action(Action::Backspace),
|
||||
'X' => self.editor.action(font_system, Action::Backspace),
|
||||
// Go to start of line
|
||||
'0' => self.editor.action(Action::Home),
|
||||
'0' => self.editor.action(font_system, Action::Home),
|
||||
// Go to end of line
|
||||
'$' => self.editor.action(Action::End),
|
||||
'$' => self.editor.action(font_system, Action::End),
|
||||
// Go to start of line after whitespace
|
||||
//TODO: implement this
|
||||
'^' => self.editor.action(Action::Home),
|
||||
'^' => self.editor.action(font_system, Action::Home),
|
||||
// Enter command mode
|
||||
':' => {
|
||||
self.mode = Mode::Command;
|
||||
|
|
@ -199,18 +203,18 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => self.editor.action(action),
|
||||
_ => self.editor.action(font_system, action),
|
||||
},
|
||||
Mode::Insert => match action {
|
||||
Action::Escape => {
|
||||
let cursor = self.cursor();
|
||||
let layout_cursor = self.buffer().layout_cursor(&cursor);
|
||||
if layout_cursor.glyph > 0 {
|
||||
self.editor.action(Action::Left);
|
||||
self.editor.action(font_system, Action::Left);
|
||||
}
|
||||
self.mode = Mode::Normal;
|
||||
}
|
||||
_ => self.editor.action(action),
|
||||
_ => self.editor.action(font_system, action),
|
||||
},
|
||||
_ => {
|
||||
//TODO: other modes
|
||||
|
|
@ -224,8 +228,13 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(&self, cache: &mut crate::SwashCache, color: Color, mut f: F)
|
||||
where
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let font_size = self.buffer().metrics().font_size;
|
||||
|
|
@ -421,15 +430,22 @@ impl<'a> Edit<'a> for ViEditor<'a> {
|
|||
None => color,
|
||||
};
|
||||
|
||||
cache.with_pixels(
|
||||
self.buffer().font_system(),
|
||||
cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(x_int + x, line_y as i32 + y_int + y, 1, 1, color);
|
||||
},
|
||||
);
|
||||
cache.with_pixels(font_system, cache_key, glyph_color, |x, y, color| {
|
||||
f(x_int + x, line_y as i32 + y_int + y, 1, 1, color);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> BorrowedWithFontSystem<'b, ViEditor<'a>> {
|
||||
/// Load text from a file, and also set syntax to the best option
|
||||
#[cfg(feature = "std")]
|
||||
pub fn load_text<P: AsRef<std::path::Path>>(
|
||||
&mut self,
|
||||
path: P,
|
||||
attrs: crate::Attrs,
|
||||
) -> std::io::Result<()> {
|
||||
self.inner.load_text(self.font_system, path, attrs)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
use alloc::sync::Arc;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::vec::Vec;
|
||||
use fontdb::Family;
|
||||
use unicode_script::Script;
|
||||
|
||||
use crate::Font;
|
||||
use crate::{Font, FontSystem};
|
||||
|
||||
use self::platform::*;
|
||||
|
||||
|
|
@ -26,11 +27,11 @@ mod platform;
|
|||
mod platform;
|
||||
|
||||
pub struct FontFallbackIter<'a> {
|
||||
fonts: &'a [Arc<Font<'a>>],
|
||||
default_families: &'a [&'a str],
|
||||
font_system: &'a mut FontSystem,
|
||||
font_ids: &'a [fontdb::ID],
|
||||
default_families: &'a [&'a Family<'a>],
|
||||
default_i: usize,
|
||||
scripts: Vec<Script>,
|
||||
locale: &'a str,
|
||||
script_i: (usize, usize),
|
||||
common_i: usize,
|
||||
other_i: usize,
|
||||
|
|
@ -39,17 +40,17 @@ pub struct FontFallbackIter<'a> {
|
|||
|
||||
impl<'a> FontFallbackIter<'a> {
|
||||
pub fn new(
|
||||
fonts: &'a [Arc<Font<'a>>],
|
||||
default_families: &'a [&'a str],
|
||||
font_system: &'a mut FontSystem,
|
||||
font_ids: &'a [fontdb::ID],
|
||||
default_families: &'a [&'a Family<'a>],
|
||||
scripts: Vec<Script>,
|
||||
locale: &'a str,
|
||||
) -> Self {
|
||||
Self {
|
||||
fonts,
|
||||
font_system,
|
||||
font_ids,
|
||||
default_families,
|
||||
default_i: 0,
|
||||
scripts,
|
||||
locale,
|
||||
script_i: (0, 0),
|
||||
common_i: 0,
|
||||
other_i: 0,
|
||||
|
|
@ -57,21 +58,20 @@ impl<'a> FontFallbackIter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_missing(&self, word: &str) {
|
||||
pub fn check_missing(&mut self, word: &str) {
|
||||
if self.end {
|
||||
log::debug!(
|
||||
"Failed to find any fallback for {:?} locale '{}': '{}'",
|
||||
self.scripts,
|
||||
self.locale,
|
||||
self.font_system.locale(),
|
||||
word
|
||||
);
|
||||
} else if self.other_i > 0 {
|
||||
let font = &self.fonts[self.other_i - 1];
|
||||
log::debug!(
|
||||
"Failed to find preset fallback for {:?} locale '{}', used '{}': '{}'",
|
||||
self.scripts,
|
||||
self.locale,
|
||||
font.name(),
|
||||
self.font_system.locale(),
|
||||
self.face_name(self.font_ids[self.other_i - 1]),
|
||||
word
|
||||
);
|
||||
} else if !self.scripts.is_empty() && self.common_i > 0 {
|
||||
|
|
@ -79,24 +79,48 @@ impl<'a> FontFallbackIter<'a> {
|
|||
log::debug!(
|
||||
"Failed to find script fallback for {:?} locale '{}', used '{}': '{}'",
|
||||
self.scripts,
|
||||
self.locale,
|
||||
self.font_system.locale(),
|
||||
family,
|
||||
word
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn face_name(&self, id: fontdb::ID) -> &str {
|
||||
if let Some(face) = self.font_system.db().face(id) {
|
||||
if let Some((name, _)) = face.families.first() {
|
||||
name
|
||||
} else {
|
||||
&face.post_script_name
|
||||
}
|
||||
} else {
|
||||
"invalid font id"
|
||||
}
|
||||
}
|
||||
|
||||
fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool {
|
||||
if let Some(face) = self.font_system.db().face(id) {
|
||||
face.families.iter().any(|(name, _)| name == family_name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FontFallbackIter<'a> {
|
||||
type Item = &'a Arc<Font<'a>>;
|
||||
type Item = Arc<Font>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while self.default_i < self.default_families.len() {
|
||||
let default_family = self.default_families[self.default_i];
|
||||
self.default_i += 1;
|
||||
|
||||
for font in self.fonts.iter() {
|
||||
if font.contains_family(default_family) {
|
||||
return Some(font);
|
||||
for id in self.font_ids.iter() {
|
||||
let default_family = self
|
||||
.font_system
|
||||
.db()
|
||||
.family_name(self.default_families[self.default_i - 1]);
|
||||
if self.face_contains_family(*id, default_family) {
|
||||
if let Some(font) = self.font_system.get_font(*id) {
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,20 +128,22 @@ impl<'a> Iterator for FontFallbackIter<'a> {
|
|||
while self.script_i.0 < self.scripts.len() {
|
||||
let script = self.scripts[self.script_i.0];
|
||||
|
||||
let script_families = script_fallback(script, self.locale);
|
||||
let script_families = script_fallback(script, self.font_system.locale());
|
||||
while self.script_i.1 < script_families.len() {
|
||||
let script_family = script_families[self.script_i.1];
|
||||
self.script_i.1 += 1;
|
||||
for font in self.fonts.iter() {
|
||||
if font.contains_family(script_family) {
|
||||
return Some(font);
|
||||
for id in self.font_ids.iter() {
|
||||
if self.face_contains_family(*id, script_family) {
|
||||
if let Some(font) = self.font_system.get_font(*id) {
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
log::debug!(
|
||||
"failed to find family '{}' for script {:?} and locale '{}'",
|
||||
script_family,
|
||||
script,
|
||||
self.locale
|
||||
self.font_system.locale(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -129,9 +155,11 @@ impl<'a> Iterator for FontFallbackIter<'a> {
|
|||
while self.common_i < common_families.len() {
|
||||
let common_family = common_families[self.common_i];
|
||||
self.common_i += 1;
|
||||
for font in self.fonts.iter() {
|
||||
if font.contains_family(common_family) {
|
||||
return Some(font);
|
||||
for id in self.font_ids.iter() {
|
||||
if self.face_contains_family(*id, common_family) {
|
||||
if let Some(font) = self.font_system.get_font(*id) {
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
log::debug!("failed to find family '{}'", common_family);
|
||||
|
|
@ -140,14 +168,16 @@ impl<'a> Iterator for FontFallbackIter<'a> {
|
|||
//TODO: do we need to do this?
|
||||
//TODO: do not evaluate fonts more than once!
|
||||
let forbidden_families = forbidden_fallback();
|
||||
while self.other_i < self.fonts.len() {
|
||||
let font = &self.fonts[self.other_i];
|
||||
while self.other_i < self.font_ids.len() {
|
||||
let id = self.font_ids[self.other_i];
|
||||
self.other_i += 1;
|
||||
if forbidden_families
|
||||
.iter()
|
||||
.all(|family| !font.contains_family(family))
|
||||
.all(|family_name| !self.face_contains_family(id, family_name))
|
||||
{
|
||||
return Some(font);
|
||||
if let Some(font) = self.font_system.get_font(id) {
|
||||
return Some(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use alloc::sync::Arc;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{string::String, vec::Vec};
|
||||
|
||||
use crate::Font;
|
||||
|
||||
/// Fonts that match a pattern
|
||||
pub struct FontMatches<'a> {
|
||||
pub locale: &'a str,
|
||||
pub default_family: String,
|
||||
pub fonts: Vec<Arc<Font<'a>>>,
|
||||
}
|
||||
102
src/font/mod.rs
102
src/font/mod.rs
|
|
@ -1,67 +1,95 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use core::ops::Deref;
|
||||
|
||||
pub(crate) mod fallback;
|
||||
|
||||
pub use self::matches::*;
|
||||
mod matches;
|
||||
use alloc::sync::Arc;
|
||||
|
||||
pub use self::system::*;
|
||||
mod system;
|
||||
|
||||
/// A font
|
||||
pub struct Font<'a> {
|
||||
pub info: &'a fontdb::FaceInfo,
|
||||
pub data: &'a [u8],
|
||||
pub rustybuzz: rustybuzz::Face<'a>,
|
||||
#[cfg(feature = "swash")]
|
||||
pub swash: (u32, swash::CacheKey),
|
||||
pub struct Font(FontInner);
|
||||
|
||||
#[ouroboros::self_referencing]
|
||||
#[allow(dead_code)]
|
||||
struct FontInner {
|
||||
id: fontdb::ID,
|
||||
data: Arc<dyn AsRef<[u8]> + Send + Sync>,
|
||||
#[borrows(data)]
|
||||
#[covariant]
|
||||
rustybuzz: rustybuzz::Face<'this>,
|
||||
// workaround, since ouroboros does not work with #[cfg(feature = "swash")]
|
||||
swash: SwashKey,
|
||||
}
|
||||
|
||||
impl<'a> Font<'a> {
|
||||
pub fn new(info: &'a fontdb::FaceInfo) -> Option<Self> {
|
||||
#[cfg(feature = "swash")]
|
||||
pub type SwashKey = (u32, swash::CacheKey);
|
||||
|
||||
#[cfg(not(feature = "swash"))]
|
||||
pub type SwashKey = ();
|
||||
|
||||
impl Font {
|
||||
pub fn new(info: &fontdb::FaceInfo) -> Option<Self> {
|
||||
#[allow(unused_variables)]
|
||||
let data = match &info.source {
|
||||
fontdb::Source::Binary(data) => data.deref().as_ref(),
|
||||
fontdb::Source::Binary(data) => Arc::clone(data),
|
||||
#[cfg(feature = "std")]
|
||||
fontdb::Source::File(path) => {
|
||||
log::warn!("Unsupported fontdb Source::File('{}')", path.display());
|
||||
return None;
|
||||
}
|
||||
#[cfg(feature = "std")]
|
||||
fontdb::Source::SharedFile(_path, data) => data.deref().as_ref(),
|
||||
fontdb::Source::SharedFile(_path, data) => Arc::clone(data),
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
info,
|
||||
data,
|
||||
rustybuzz: rustybuzz::Face::from_slice(data, info.index)?,
|
||||
#[cfg(feature = "swash")]
|
||||
swash: {
|
||||
let swash = swash::FontRef::from_index(data, info.index as usize)?;
|
||||
(swash.offset, swash.key)
|
||||
},
|
||||
})
|
||||
Some(Self(
|
||||
FontInnerTryBuilder {
|
||||
id: info.id,
|
||||
swash: {
|
||||
#[cfg(feature = "swash")]
|
||||
let swash = {
|
||||
let swash =
|
||||
swash::FontRef::from_index((*data).as_ref(), info.index as usize)?;
|
||||
(swash.offset, swash.key)
|
||||
};
|
||||
#[cfg(not(feature = "swash"))]
|
||||
let swash = ();
|
||||
swash
|
||||
},
|
||||
data,
|
||||
rustybuzz_builder: |data| {
|
||||
rustybuzz::Face::from_slice((**data).as_ref(), info.index).ok_or(())
|
||||
},
|
||||
}
|
||||
.try_build()
|
||||
.ok()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
if let Some((name, _)) = self.info.families.first() {
|
||||
name
|
||||
} else {
|
||||
&self.info.post_script_name
|
||||
}
|
||||
pub fn id(&self) -> fontdb::ID {
|
||||
*self.0.borrow_id()
|
||||
}
|
||||
|
||||
pub fn contains_family(&self, family: &str) -> bool {
|
||||
self.info.families.iter().any(|(name, _)| name == family)
|
||||
pub fn data(&self) -> &[u8] {
|
||||
(**self.0.borrow_data()).as_ref()
|
||||
}
|
||||
|
||||
pub fn rustybuzz(&self) -> &rustybuzz::Face {
|
||||
self.0.borrow_rustybuzz()
|
||||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn as_swash(&self) -> swash::FontRef {
|
||||
let swash = self.0.borrow_swash();
|
||||
swash::FontRef {
|
||||
data: self.data,
|
||||
offset: self.swash.0,
|
||||
key: self.swash.1,
|
||||
data: self.data(),
|
||||
offset: swash.0,
|
||||
key: swash.1,
|
||||
}
|
||||
}
|
||||
|
||||
// This is used to prevent warnings due to the swash field being unused.
|
||||
#[cfg(not(feature = "swash"))]
|
||||
#[allow(dead_code)]
|
||||
fn as_swash(&self) {
|
||||
self.0.borrow_swash();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub use self::no_std::*;
|
||||
#[cfg(not(feature = "std"))]
|
||||
|
|
@ -10,3 +12,23 @@ mod std;
|
|||
|
||||
// re-export fontdb
|
||||
pub use fontdb;
|
||||
|
||||
/// A value borrowed together with an [`FontSystem`]
|
||||
pub struct BorrowedWithFontSystem<'a, T> {
|
||||
pub(crate) inner: &'a mut T,
|
||||
pub(crate) font_system: &'a mut FontSystem,
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for BorrowedWithFontSystem<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for BorrowedWithFontSystem<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use alloc::{
|
|||
vec::Vec,
|
||||
};
|
||||
|
||||
use crate::{Attrs, Font, FontMatches};
|
||||
use crate::{Attrs, Font};
|
||||
|
||||
/// Access system fonts
|
||||
pub struct FontSystem {
|
||||
|
|
@ -43,35 +43,33 @@ impl FontSystem {
|
|||
&self.db
|
||||
}
|
||||
|
||||
// Clippy false positive
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
pub fn get_font<'a>(&'a self, id: fontdb::ID) -> Option<Arc<Font<'a>>> {
|
||||
let face = self.db.face(id)?;
|
||||
match Font::new(face) {
|
||||
Some(font) => Some(Arc::new(font)),
|
||||
None => {
|
||||
log::warn!("failed to load font '{}'", face.post_script_name);
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn db_mut(&mut self) -> &mut fontdb::Database {
|
||||
&mut self.db
|
||||
}
|
||||
|
||||
pub fn get_font_matches<'a>(&'a self, attrs: Attrs) -> Arc<FontMatches<'a>> {
|
||||
let mut fonts = Vec::new();
|
||||
for face in self.db.faces() {
|
||||
if !attrs.matches(face) {
|
||||
continue;
|
||||
}
|
||||
pub fn get_font(&self, id: fontdb::ID) -> Option<Arc<Font>> {
|
||||
get_font(&self.db, id)
|
||||
}
|
||||
|
||||
if let Some(font) = self.get_font(face.id) {
|
||||
fonts.push(font);
|
||||
}
|
||||
}
|
||||
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<fontdb::ID>> {
|
||||
let ids = self
|
||||
.db
|
||||
.faces()
|
||||
.filter(|face| attrs.matches(face))
|
||||
.map(|face| face.id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Arc::new(FontMatches {
|
||||
locale: &self.locale,
|
||||
default_family: self.db.family_name(&attrs.family).to_string(),
|
||||
fonts,
|
||||
})
|
||||
Arc::new(ids)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_font(db: &fontdb::Database, id: fontdb::ID) -> Option<Arc<Font>> {
|
||||
let face = db.face(id)?;
|
||||
match Font::new(face) {
|
||||
Some(font) => Some(Arc::new(font)),
|
||||
None => {
|
||||
log::warn!("failed to load font '{}'", face.post_script_name);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,16 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use crate::{Attrs, AttrsOwned, Font, FontMatches};
|
||||
|
||||
#[ouroboros::self_referencing]
|
||||
struct FontSystemInner {
|
||||
locale: String,
|
||||
db: fontdb::Database,
|
||||
#[borrows(db)]
|
||||
#[not_covariant]
|
||||
font_cache: Mutex<HashMap<fontdb::ID, Option<Arc<Font<'this>>>>>,
|
||||
#[borrows(locale, db)]
|
||||
#[not_covariant]
|
||||
font_matches_cache: Mutex<HashMap<AttrsOwned, Arc<FontMatches<'this>>>>,
|
||||
}
|
||||
use crate::{Attrs, AttrsOwned, Font};
|
||||
|
||||
/// Access system fonts
|
||||
pub struct FontSystem(FontSystemInner);
|
||||
pub struct FontSystem {
|
||||
locale: String,
|
||||
db: fontdb::Database,
|
||||
font_cache: HashMap<fontdb::ID, Option<Arc<Font>>>,
|
||||
font_matches_cache: HashMap<AttrsOwned, Arc<Vec<fontdb::ID>>>,
|
||||
}
|
||||
|
||||
impl FontSystem {
|
||||
/// Create a new [`FontSystem`], that allows access to any installed system fonts
|
||||
|
|
@ -72,110 +62,75 @@ impl FontSystem {
|
|||
}
|
||||
|
||||
/// Create a new [`FontSystem`], manually specifying the current locale and font database.
|
||||
pub fn new_with_locale_and_db(locale: String, mut db: fontdb::Database) -> Self {
|
||||
{
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
//TODO only do this on demand!
|
||||
for id in db.faces().map(|face| face.id).collect::<Vec<_>>() {
|
||||
unsafe {
|
||||
db.make_shared_face_data(id);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
log::info!(
|
||||
"Mapped {} font faces in {}ms.",
|
||||
db.len(),
|
||||
now.elapsed().as_millis()
|
||||
);
|
||||
pub fn new_with_locale_and_db(locale: String, db: fontdb::Database) -> Self {
|
||||
Self {
|
||||
locale,
|
||||
db,
|
||||
font_cache: HashMap::new(),
|
||||
font_matches_cache: HashMap::new(),
|
||||
}
|
||||
|
||||
Self(
|
||||
FontSystemInnerBuilder {
|
||||
locale,
|
||||
db,
|
||||
font_cache_builder: |_| Mutex::new(HashMap::new()),
|
||||
font_matches_cache_builder: |_, _| Mutex::new(HashMap::new()),
|
||||
}
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn locale(&self) -> &str {
|
||||
self.0.borrow_locale()
|
||||
&self.locale
|
||||
}
|
||||
|
||||
pub fn db(&self) -> &fontdb::Database {
|
||||
self.0.borrow_db()
|
||||
&self.db
|
||||
}
|
||||
|
||||
pub fn db_mut(&mut self) -> &mut fontdb::Database {
|
||||
self.font_matches_cache.clear();
|
||||
&mut self.db
|
||||
}
|
||||
|
||||
pub fn into_locale_and_db(self) -> (String, fontdb::Database) {
|
||||
let heads = self.0.into_heads();
|
||||
(heads.locale, heads.db)
|
||||
(self.locale, self.db)
|
||||
}
|
||||
|
||||
// Clippy false positive
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
pub fn get_font<'a>(&'a self, id: fontdb::ID) -> Option<Arc<Font<'a>>> {
|
||||
self.0.with(|fields| get_font(&fields, id))
|
||||
pub fn get_font(&mut self, id: fontdb::ID) -> Option<Arc<Font>> {
|
||||
get_font(&mut self.font_cache, &mut self.db, id)
|
||||
}
|
||||
|
||||
pub fn get_font_matches<'a>(&'a self, attrs: Attrs) -> Arc<FontMatches<'a>> {
|
||||
self.0.with(|fields| {
|
||||
let mut font_matches_cache = fields
|
||||
.font_matches_cache
|
||||
.lock()
|
||||
.expect("failed to lock font matches cache");
|
||||
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<fontdb::ID>> {
|
||||
self.font_matches_cache
|
||||
//TODO: do not create AttrsOwned unless entry does not already exist
|
||||
font_matches_cache
|
||||
.entry(AttrsOwned::new(attrs))
|
||||
.or_insert_with(|| {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let now = std::time::Instant::now();
|
||||
.entry(AttrsOwned::new(attrs))
|
||||
.or_insert_with(|| {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
let mut fonts = Vec::new();
|
||||
for face in fields.db.faces() {
|
||||
if !attrs.matches(face) {
|
||||
continue;
|
||||
}
|
||||
let ids = self
|
||||
.db
|
||||
.faces()
|
||||
.filter(|face| attrs.matches(face))
|
||||
.map(|face| face.id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Some(font) = get_font(&fields, face.id) {
|
||||
fonts.push(font);
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let elapsed = now.elapsed();
|
||||
log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
|
||||
}
|
||||
|
||||
let font_matches = Arc::new(FontMatches {
|
||||
locale: fields.locale,
|
||||
default_family: fields.db.family_name(&attrs.family).to_string(),
|
||||
fonts,
|
||||
});
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let elapsed = now.elapsed();
|
||||
log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
|
||||
}
|
||||
|
||||
font_matches
|
||||
})
|
||||
.clone()
|
||||
})
|
||||
Arc::new(ids)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_font<'b>(
|
||||
fields: &ouroboros_impl_font_system_inner::BorrowedFields<'_, 'b>,
|
||||
fn get_font(
|
||||
font_cache: &mut HashMap<fontdb::ID, Option<Arc<Font>>>,
|
||||
db: &mut fontdb::Database,
|
||||
id: fontdb::ID,
|
||||
) -> Option<Arc<Font<'b>>> {
|
||||
fields
|
||||
.font_cache
|
||||
.lock()
|
||||
.expect("failed to lock font cache")
|
||||
) -> Option<Arc<Font>> {
|
||||
font_cache
|
||||
.entry(id)
|
||||
.or_insert_with(|| {
|
||||
let face = fields.db.face(id)?;
|
||||
unsafe {
|
||||
db.make_shared_face_data(id);
|
||||
}
|
||||
let face = db.face(id)?;
|
||||
match Font::new(face) {
|
||||
Some(font) => Some(Arc::new(font)),
|
||||
None => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
//! use cosmic_text::{Attrs, Color, FontSystem, SwashCache, Buffer, Metrics};
|
||||
//!
|
||||
//! // A FontSystem provides access to detected system fonts, create one per application
|
||||
//! let font_system = FontSystem::new();
|
||||
//! let mut font_system = FontSystem::new();
|
||||
//!
|
||||
//! // A SwashCache stores rasterized glyphs, create one per application
|
||||
//! let mut swash_cache = SwashCache::new();
|
||||
|
|
@ -24,7 +24,10 @@
|
|||
//! let metrics = Metrics::new(14.0, 20.0);
|
||||
//!
|
||||
//! // A Buffer provides shaping and layout for a UTF-8 string, create one per text widget
|
||||
//! let mut buffer = Buffer::new(&font_system, metrics);
|
||||
//! let mut buffer = Buffer::new(&mut font_system, metrics);
|
||||
//!
|
||||
//! // Borrow buffer together with the font system for more convenient method calls
|
||||
//! 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);
|
||||
|
|
|
|||
44
src/shape.rs
44
src/shape.rs
|
|
@ -21,7 +21,7 @@ fn shape_fallback(
|
|||
) -> (Vec<ShapeGlyph>, Vec<usize>) {
|
||||
let run = &line[start_run..end_run];
|
||||
|
||||
let font_scale = font.rustybuzz.units_per_em() as f32;
|
||||
let font_scale = font.rustybuzz().units_per_em() as f32;
|
||||
|
||||
let mut buffer = rustybuzz::UnicodeBuffer::new();
|
||||
buffer.set_direction(if span_rtl {
|
||||
|
|
@ -35,7 +35,7 @@ fn shape_fallback(
|
|||
let rtl = matches!(buffer.direction(), rustybuzz::Direction::RightToLeft);
|
||||
assert_eq!(rtl, span_rtl);
|
||||
|
||||
let glyph_buffer = rustybuzz::shape(&font.rustybuzz, &[], buffer);
|
||||
let glyph_buffer = rustybuzz::shape(font.rustybuzz(), &[], buffer);
|
||||
let glyph_infos = glyph_buffer.glyph_infos();
|
||||
let glyph_positions = glyph_buffer.glyph_positions();
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ fn shape_fallback(
|
|||
y_advance,
|
||||
x_offset,
|
||||
y_offset,
|
||||
font_id: font.info.id,
|
||||
font_id: font.id(),
|
||||
glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"),
|
||||
//TODO: color should not be related to shaping
|
||||
color_opt: attrs.color_opt,
|
||||
|
|
@ -98,7 +98,7 @@ fn shape_fallback(
|
|||
}
|
||||
|
||||
fn shape_run(
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
line: &str,
|
||||
attrs_list: &AttrsList,
|
||||
start_run: usize,
|
||||
|
|
@ -122,24 +122,15 @@ fn shape_run(
|
|||
|
||||
let attrs = attrs_list.get_span(start_run);
|
||||
|
||||
let font_matches = font_system.get_font_matches(attrs);
|
||||
let fonts = font_system.get_font_matches(attrs);
|
||||
|
||||
let default_families = [font_matches.default_family.as_str()];
|
||||
let mut font_iter = FontFallbackIter::new(
|
||||
&font_matches.fonts,
|
||||
&default_families,
|
||||
scripts,
|
||||
font_matches.locale,
|
||||
);
|
||||
let default_families = [&attrs.family];
|
||||
let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, scripts);
|
||||
|
||||
let (mut glyphs, mut missing) = shape_fallback(
|
||||
font_iter.next().expect("no default font found"),
|
||||
line,
|
||||
attrs_list,
|
||||
start_run,
|
||||
end_run,
|
||||
span_rtl,
|
||||
);
|
||||
let font = font_iter.next().expect("no default font found");
|
||||
|
||||
let (mut glyphs, mut missing) =
|
||||
shape_fallback(&font, line, attrs_list, start_run, end_run, span_rtl);
|
||||
|
||||
//TODO: improve performance!
|
||||
while !missing.is_empty() {
|
||||
|
|
@ -148,9 +139,12 @@ fn shape_run(
|
|||
None => break,
|
||||
};
|
||||
|
||||
log::trace!("Evaluating fallback with font '{}'", font.name());
|
||||
log::trace!(
|
||||
"Evaluating fallback with font '{}'",
|
||||
font_iter.face_name(font.id())
|
||||
);
|
||||
let (mut fb_glyphs, fb_missing) =
|
||||
shape_fallback(font, line, attrs_list, start_run, end_run, span_rtl);
|
||||
shape_fallback(&font, line, attrs_list, start_run, end_run, span_rtl);
|
||||
|
||||
// Insert all matching glyphs
|
||||
let mut fb_i = 0;
|
||||
|
|
@ -278,7 +272,7 @@ pub struct ShapeWord {
|
|||
|
||||
impl ShapeWord {
|
||||
pub fn new(
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
line: &str,
|
||||
attrs_list: &AttrsList,
|
||||
word_range: Range<usize>,
|
||||
|
|
@ -352,7 +346,7 @@ pub struct ShapeSpan {
|
|||
|
||||
impl ShapeSpan {
|
||||
pub fn new(
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
line: &str,
|
||||
attrs_list: &AttrsList,
|
||||
span_range: Range<usize>,
|
||||
|
|
@ -443,7 +437,7 @@ impl ShapeLine {
|
|||
/// # Panics
|
||||
///
|
||||
/// Will panic if `line` contains more than one paragraph.
|
||||
pub fn new(font_system: &FontSystem, line: &str, attrs_list: &AttrsList) -> Self {
|
||||
pub fn new(font_system: &mut FontSystem, line: &str, attrs_list: &AttrsList) -> Self {
|
||||
let mut spans = Vec::new();
|
||||
|
||||
let bidi = unicode_bidi::BidiInfo::new(line, None);
|
||||
|
|
|
|||
12
src/swash.rs
12
src/swash.rs
|
|
@ -16,7 +16,7 @@ pub use swash::scale::image::{Content as SwashContent, Image as SwashImage};
|
|||
pub use swash::zeno::{Command, Placement};
|
||||
|
||||
fn swash_image(
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
context: &mut ScaleContext,
|
||||
cache_key: CacheKey,
|
||||
) -> Option<SwashImage> {
|
||||
|
|
@ -57,7 +57,7 @@ fn swash_image(
|
|||
}
|
||||
|
||||
fn swash_outline_commands(
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
context: &mut ScaleContext,
|
||||
cache_key: CacheKey,
|
||||
) -> Option<Vec<swash::zeno::Command>> {
|
||||
|
|
@ -109,7 +109,7 @@ impl SwashCache {
|
|||
/// Create a swash Image from a cache key, without caching results
|
||||
pub fn get_image_uncached(
|
||||
&mut self,
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
cache_key: CacheKey,
|
||||
) -> Option<SwashImage> {
|
||||
swash_image(font_system, &mut self.context, cache_key)
|
||||
|
|
@ -118,7 +118,7 @@ impl SwashCache {
|
|||
/// Create a swash Image from a cache key, caching results
|
||||
pub fn get_image(
|
||||
&mut self,
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
cache_key: CacheKey,
|
||||
) -> &Option<SwashImage> {
|
||||
self.image_cache
|
||||
|
|
@ -128,7 +128,7 @@ impl SwashCache {
|
|||
|
||||
pub fn get_outline_commands(
|
||||
&mut self,
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
cache_key: CacheKey,
|
||||
) -> Option<&[swash::zeno::Command]> {
|
||||
self.outline_command_cache
|
||||
|
|
@ -140,7 +140,7 @@ impl SwashCache {
|
|||
/// Enumerate pixels in an Image, use `with_image` for better performance
|
||||
pub fn with_pixels<F: FnMut(i32, i32, Color)>(
|
||||
&mut self,
|
||||
font_system: &FontSystem,
|
||||
font_system: &mut FontSystem,
|
||||
cache_key: CacheKey,
|
||||
base: Color,
|
||||
mut f: F,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue