emojis working

macos-click-through
Keith Simmons 5 years ago
parent 5a880d06aa
commit f8bc1a2ce4

1
Cargo.lock generated

@ -1485,6 +1485,7 @@ dependencies = [
"skribo", "skribo",
"skulpin", "skulpin",
"tokio", "tokio",
"unicode-segmentation",
"winres", "winres",
] ]

@ -21,6 +21,7 @@ nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "futures",
tokio = { version = "0.2.9", features = [ "blocking", "process", "time" ] } tokio = { version = "0.2.9", features = [ "blocking", "process", "time" ] }
async-trait = "0.1.18" async-trait = "0.1.18"
lazy_static = "1.4.0" lazy_static = "1.4.0"
unicode-segmentation = "1.6.0"
[build-dependencies] [build-dependencies]
winres = "0.1.11" winres = "0.1.11"

@ -6,6 +6,7 @@ use std::sync::{Arc, Mutex};
use skulpin::skia_safe::colors; use skulpin::skia_safe::colors;
use skulpin::winit::window::Window; use skulpin::winit::window::Window;
use unicode_segmentation::UnicodeSegmentation;
pub use cursor::{Cursor, CursorShape, CursorMode}; pub use cursor::{Cursor, CursorShape, CursorMode};
pub use style::{Colors, Style}; pub use style::{Colors, Style};
@ -17,7 +18,7 @@ lazy_static! {
pub static ref EDITOR: Arc<Mutex<Editor>> = Arc::new(Mutex::new(Editor::new())); pub static ref EDITOR: Arc<Mutex<Editor>> = Arc::new(Mutex::new(Editor::new()));
} }
pub type GridCell = Option<(char, Option<Style>)>; pub type GridCell = Option<(String, Option<Style>)>;
#[derive(new, Debug, Clone)] #[derive(new, Debug, Clone)]
pub struct DrawCommand { pub struct DrawCommand {
@ -102,9 +103,9 @@ impl Editor {
} }
} }
fn add_character(command: &mut Option<DrawCommand>, character: &char, row_index: u64, col_index: u64, style: Option<Style>) { fn add_character(command: &mut Option<DrawCommand>, character: &str, row_index: u64, col_index: u64, style: Option<Style>) {
match command { match command {
Some(command) => command.text.push(character.clone()), Some(command) => command.text.push_str(character),
None => { None => {
command.replace(DrawCommand::new(character.to_string(), (col_index, row_index), style)); command.replace(DrawCommand::new(character.to_string(), (col_index, row_index), style));
} }
@ -112,13 +113,19 @@ impl Editor {
} }
for (col_index, cell) in row.iter().enumerate() { for (col_index, cell) in row.iter().enumerate() {
let (character, style) = cell.clone().unwrap_or_else(|| (' ', Some(Style::new(self.default_colors.clone())))); let (character, style) = cell.clone().unwrap_or_else(|| (' '.to_string(), Some(Style::new(self.default_colors.clone()))));
if character.is_empty() {
add_character(&mut command, &" ", row_index as u64, col_index as u64, style.clone());
add_command(&mut draw_commands, command);
command = None;
} else {
if !command_matches(&command, &style) { if !command_matches(&command, &style) {
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
command = None; command = None;
} }
add_character(&mut command, &character, row_index as u64, col_index as u64, style.clone()); add_character(&mut command, &character, row_index as u64, col_index as u64, style.clone());
} }
}
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
} }
let should_clear = self.should_clear; let should_clear = self.should_clear;
@ -127,7 +134,7 @@ impl Editor {
let (x, y) = command.grid_position; let (x, y) = command.grid_position;
let dirty_row = &self.dirty[y as usize]; let dirty_row = &self.dirty[y as usize];
for char_index in 0..command.text.chars().count() { for char_index in 0..command.text.graphemes(true).count() {
if dirty_row[x as usize + char_index] { if dirty_row[x as usize + char_index] {
return true; return true;
} }
@ -155,15 +162,21 @@ impl Editor {
let row = self.grid.get_mut(row_index as usize).expect("Grid must have size greater than row_index"); let row = self.grid.get_mut(row_index as usize).expect("Grid must have size greater than row_index");
let dirty_row = &mut self.dirty[row_index as usize]; let dirty_row = &mut self.dirty[row_index as usize];
for (i, character) in text.chars().enumerate() {
if text.is_empty() {
row[*column_pos as usize] = Some(("".to_string(), style.clone()));
dirty_row[*column_pos as usize] = true;
*column_pos = *column_pos + 1;
} else {
for (i, character) in text.graphemes(true).enumerate() {
let pointer_index = i + *column_pos as usize; let pointer_index = i + *column_pos as usize;
if pointer_index < row.len() { if pointer_index < row.len() {
row[pointer_index] = Some((character, style.clone())); row[pointer_index] = Some((character.to_string(), style.clone()));
dirty_row[pointer_index] = true; dirty_row[pointer_index] = true;
} }
} }
*column_pos = *column_pos + text.graphemes(true).count() as u64;
*column_pos = *column_pos + text.chars().count() as u64; }
self.previous_style = style; self.previous_style = style;
} }

@ -4,7 +4,7 @@ use std::collections::HashMap;
use lru::LruCache; use lru::LruCache;
use skulpin::skia_safe::{TextBlob, Font as SkiaFont, FontStyle, Typeface, TextBlobBuilder}; use skulpin::skia_safe::{TextBlob, Font as SkiaFont, FontStyle, Typeface, TextBlobBuilder};
use font_kit::source::SystemSource; use font_kit::source::SystemSource;
use skribo::{layout, layout_run, FontRef as SkriboFont, FontFamily, FontCollection, TextStyle}; use skribo::{layout_run, LayoutSession, FontRef as SkriboFont, FontFamily, FontCollection, TextStyle};
const STANDARD_CHARACTER_STRING: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; const STANDARD_CHARACTER_STRING: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
@ -103,39 +103,22 @@ impl CachingShaper {
let style = TextStyle { size: base_size * scale as f32 }; let style = TextStyle { size: base_size * scale as f32 };
let mut family = FontFamily::new();
family.add_font(font_pair.normal.1.clone());
family.add_font(font_pair.emoji.1.clone());
let mut collection = FontCollection::new(); let mut collection = FontCollection::new();
collection.add_family(family);
let layout = layout(&style, &collection, text); let mut normal_family = FontFamily::new();
normal_family.add_font(font_pair.normal.1.clone());
collection.add_family(normal_family);
let mut groups = Vec::new(); let mut emoji_family = FontFamily::new();
let mut group = Vec::new(); emoji_family.add_font(font_pair.emoji.1.clone());
let mut previous_font: Option<SkriboFont> = None; collection.add_family(emoji_family);
for glyph in layout.glyphs { let session = LayoutSession::create(text, &style, &collection);
if previous_font.clone().map(|previous_font| Arc::ptr_eq(&previous_font.font, &glyph.font.font)).unwrap_or(true) {
group.push(glyph);
} else {
groups.push(group);
previous_font = Some(glyph.font.clone());
group = vec![glyph];
}
}
if !group.is_empty() {
groups.push(group);
}
let mut blobs = Vec::new(); let mut blobs = Vec::new();
for group in groups {
if group.is_empty() {
continue;
}
let skribo_font = group[0].font.clone(); for layout_run in session.iter_all() {
let skribo_font = layout_run.font();
let skia_font = if Arc::ptr_eq(&skribo_font.font, &font_pair.normal.1.font) { let skia_font = if Arc::ptr_eq(&skribo_font.font, &font_pair.normal.1.font) {
&font_pair.normal.0 &font_pair.normal.0
} else { } else {
@ -144,16 +127,14 @@ impl CachingShaper {
let mut blob_builder = TextBlobBuilder::new(); let mut blob_builder = TextBlobBuilder::new();
let count = group.len(); let count = layout_run.glyphs().count();
let metrics = skribo_font.font.metrics(); let metrics = skribo_font.font.metrics();
let ascent = metrics.ascent * base_size / metrics.units_per_em as f32; let ascent = metrics.ascent * base_size / metrics.units_per_em as f32;
let (glyphs, positions) = blob_builder.alloc_run_pos_h(&skia_font, count, ascent, None); let (glyphs, positions) = blob_builder.alloc_run_pos_h(&skia_font, count, ascent, None);
for (i, glyph_id) in group.iter().map(|glyph| glyph.glyph_id as u16).enumerate() { for (i, glyph) in layout_run.glyphs().enumerate() {
glyphs[i] = glyph_id; glyphs[i] = glyph.glyph_id as u16;
} positions[i] = glyph.offset.x;
for (i, offset) in group.iter().map(|glyph| glyph.offset.x as f32).enumerate() {
positions[i] = offset;
} }
blobs.push(blob_builder.make().unwrap()); blobs.push(blob_builder.make().unwrap());
} }

@ -195,7 +195,22 @@ impl CursorRenderer {
let (grid_x, grid_y) = self.previous_position; let (grid_x, grid_y) = self.previous_position;
let font_dimensions: Point = (font_width, font_height).into(); let (character, font_dimensions): (String, Point) = {
let editor = EDITOR.lock().unwrap();
let character = editor.grid[grid_y as usize][grid_x as usize].clone()
.map(|(character, _)| character)
.unwrap_or(' '.to_string());
let is_double = editor.grid[grid_y as usize]
.get(grid_x as usize + 1)
.map(|cell| cell.as_ref().map(|(character, _)| character.is_empty()).unwrap_or(false))
.unwrap_or(false);
let font_width = match (is_double, &cursor.shape) {
(true, CursorShape::Block) => font_width * 2.0,
_ => font_width
};
(character, (font_width, font_height).into())
};
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into(); let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
let center_destination = destination + font_dimensions * 0.5; let center_destination = destination + font_dimensions * 0.5;
@ -226,9 +241,6 @@ impl CursorRenderer {
// Draw foreground // Draw foreground
paint.set_color(cursor.foreground(&default_colors).to_color()); paint.set_color(cursor.foreground(&default_colors).to_color());
let editor = EDITOR.lock().unwrap(); let editor = EDITOR.lock().unwrap();
let character = editor.grid[grid_y as usize][grid_x as usize].clone()
.map(|(character, _)| character)
.unwrap_or(' ');
canvas.save(); canvas.save();
canvas.clip_path(&path, None, Some(false)); canvas.clip_path(&path, None, Some(false));

@ -3,6 +3,7 @@ use std::time::Instant;
use skulpin::CoordinateSystemHelper; use skulpin::CoordinateSystemHelper;
use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors}; use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors};
use skulpin::skia_safe::gpu::SurfaceOrigin; use skulpin::skia_safe::gpu::SurfaceOrigin;
use unicode_segmentation::UnicodeSegmentation;
mod caching_shaper; mod caching_shaper;
mod cursor_renderer; mod cursor_renderer;
@ -54,7 +55,7 @@ impl Renderer {
let (grid_x, grid_y) = grid_pos; let (grid_x, grid_y) = grid_pos;
let x = grid_x as f32 * self.font_width; let x = grid_x as f32 * self.font_width;
let y = grid_y as f32 * self.font_height; let y = grid_y as f32 * self.font_height;
let width = text.chars().count() as f32 * self.font_width * size as f32; let width = text.graphemes(true).count() as f32 * self.font_width * size as f32;
let height = self.font_height * size as f32; let height = self.font_height * size as f32;
Rect::new(x, y, x + width, y + height) Rect::new(x, y, x + width, y + height)
} }
@ -71,13 +72,18 @@ impl Renderer {
let (grid_x, grid_y) = grid_pos; let (grid_x, grid_y) = grid_pos;
let x = grid_x as f32 * self.font_width; let x = grid_x as f32 * self.font_width;
let y = grid_y as f32 * self.font_height; let y = grid_y as f32 * self.font_height;
let width = text.chars().count() as f32 * self.font_width; let width = text.graphemes(true).count() as f32 * self.font_width;
let style = style.clone().unwrap_or_else(|| Style::new(default_colors.clone())); let style = style.clone().unwrap_or_else(|| Style::new(default_colors.clone()));
canvas.save(); canvas.save();
let region = self.compute_text_region(text, grid_pos, size); let region = self.compute_text_region(text, grid_pos, size);
let region = Rect::new(
region.x() - self.font_width,
region.y(),
region.x() + region.width() + self.font_width,
region.y() + region.height());
canvas.clip_rect(region, None, Some(false)); canvas.clip_rect(region, None, Some(false));

Loading…
Cancel
Save