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