|
|
@ -1,6 +1,5 @@
|
|
|
|
use std::borrow::Cow;
|
|
|
|
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
use std::time::Instant;
|
|
|
|
|
|
|
|
|
|
|
|
use lru::LruCache;
|
|
|
|
use lru::LruCache;
|
|
|
|
|
|
|
|
|
|
|
@ -10,7 +9,7 @@ use skulpin::skia_safe::icu;
|
|
|
|
use skulpin::winit::dpi::LogicalSize;
|
|
|
|
use skulpin::winit::dpi::LogicalSize;
|
|
|
|
use skulpin::winit::event::{ElementState, Event, MouseScrollDelta, WindowEvent};
|
|
|
|
use skulpin::winit::event::{ElementState, Event, MouseScrollDelta, WindowEvent};
|
|
|
|
use skulpin::winit::event_loop::{ControlFlow, EventLoop};
|
|
|
|
use skulpin::winit::event_loop::{ControlFlow, EventLoop};
|
|
|
|
use skulpin::winit::window::WindowBuilder;
|
|
|
|
use skulpin::winit::window::{Window, WindowBuilder};
|
|
|
|
|
|
|
|
|
|
|
|
use neovim_lib::{Neovim, NeovimApi};
|
|
|
|
use neovim_lib::{Neovim, NeovimApi};
|
|
|
|
|
|
|
|
|
|
|
@ -34,7 +33,7 @@ impl CachingShaper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn shape(&self, text: &str, font: &Font) -> TextBlob {
|
|
|
|
pub fn shape(&self, text: &str, font: &Font) -> TextBlob {
|
|
|
|
let (blob, _) = self.shaper.shape_text_blob(text, font, true, 1000.0, Point::default()).unwrap();
|
|
|
|
let (blob, _) = self.shaper.shape_text_blob(text, font, true, 1000000.0, Point::default()).unwrap();
|
|
|
|
blob
|
|
|
|
blob
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -47,9 +46,38 @@ impl CachingShaper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct FpsTracker {
|
|
|
|
|
|
|
|
last_record_time: Instant,
|
|
|
|
|
|
|
|
frame_count: usize,
|
|
|
|
|
|
|
|
fps: usize
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl FpsTracker {
|
|
|
|
|
|
|
|
pub fn new() -> FpsTracker {
|
|
|
|
|
|
|
|
FpsTracker {
|
|
|
|
|
|
|
|
fps: 0,
|
|
|
|
|
|
|
|
last_record_time: Instant::now(),
|
|
|
|
|
|
|
|
frame_count: 0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn record_frame(&mut self) {
|
|
|
|
|
|
|
|
self.frame_count = self.frame_count + 1;
|
|
|
|
|
|
|
|
let now = Instant::now();
|
|
|
|
|
|
|
|
let time_since = (now - self.last_record_time).as_secs_f32();
|
|
|
|
|
|
|
|
if time_since > 1.0 {
|
|
|
|
|
|
|
|
self.fps = self.frame_count;
|
|
|
|
|
|
|
|
self.last_record_time = now;
|
|
|
|
|
|
|
|
self.frame_count = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Renderer {
|
|
|
|
struct Renderer {
|
|
|
|
editor: Arc<Mutex<Editor>>,
|
|
|
|
editor: Arc<Mutex<Editor>>,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
title: String,
|
|
|
|
|
|
|
|
|
|
|
|
paint: Paint,
|
|
|
|
paint: Paint,
|
|
|
|
font: Font,
|
|
|
|
font: Font,
|
|
|
|
shaper: CachingShaper,
|
|
|
|
shaper: CachingShaper,
|
|
|
@ -58,11 +86,13 @@ struct Renderer {
|
|
|
|
font_height: f32,
|
|
|
|
font_height: f32,
|
|
|
|
cursor_pos: (f32, f32),
|
|
|
|
cursor_pos: (f32, f32),
|
|
|
|
|
|
|
|
|
|
|
|
previous_frame_instant: Instant
|
|
|
|
fps_tracker: FpsTracker
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Renderer {
|
|
|
|
impl Renderer {
|
|
|
|
pub fn new(editor: Arc<Mutex<Editor>>) -> Renderer {
|
|
|
|
pub fn new(editor: Arc<Mutex<Editor>>) -> Renderer {
|
|
|
|
|
|
|
|
let title = "".to_string();
|
|
|
|
|
|
|
|
|
|
|
|
let paint = Paint::new(colors::WHITE, None);
|
|
|
|
let paint = Paint::new(colors::WHITE, None);
|
|
|
|
let typeface = Typeface::new(FONT_NAME, FontStyle::default()).expect("Could not load font file.");
|
|
|
|
let typeface = Typeface::new(FONT_NAME, FontStyle::default()).expect("Could not load font file.");
|
|
|
|
let font = Font::from_typeface(typeface, FONT_SIZE);
|
|
|
|
let font = Font::from_typeface(typeface, FONT_SIZE);
|
|
|
@ -70,40 +100,35 @@ impl Renderer {
|
|
|
|
|
|
|
|
|
|
|
|
let (_, bounds) = font.measure_str("0", Some(&paint));
|
|
|
|
let (_, bounds) = font.measure_str("0", Some(&paint));
|
|
|
|
let font_width = bounds.width();
|
|
|
|
let font_width = bounds.width();
|
|
|
|
|
|
|
|
|
|
|
|
let (_, metrics) = font.metrics();
|
|
|
|
let (_, metrics) = font.metrics();
|
|
|
|
let font_height = metrics.descent - metrics.ascent; // bounds.height() * 1.68;
|
|
|
|
let font_height = metrics.descent - metrics.ascent;
|
|
|
|
let cursor_pos = (0.0, 0.0);
|
|
|
|
let cursor_pos = (0.0, 0.0);
|
|
|
|
|
|
|
|
|
|
|
|
let previous_frame_instant = Instant::now();
|
|
|
|
let fps_tracker = FpsTracker::new();
|
|
|
|
|
|
|
|
|
|
|
|
Renderer { editor, paint, font, shaper, font_width, font_height, cursor_pos, previous_frame_instant }
|
|
|
|
Renderer { editor, title, paint, font, shaper, font_width, font_height, cursor_pos, fps_tracker }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn draw_text(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), style: &Style, default_colors: &Colors, update_cache: bool) {
|
|
|
|
fn draw_text(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), style: &Style, default_colors: &Colors, update_cache: bool) {
|
|
|
|
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 + self.font_height - self.font_height * 0.2;
|
|
|
|
let y = grid_y as f32 * self.font_height;
|
|
|
|
let top = y - self.font_height * 0.8;
|
|
|
|
|
|
|
|
let width = text.chars().count() as f32 * self.font_width;
|
|
|
|
let width = text.chars().count() as f32 * self.font_width;
|
|
|
|
let height = self.font_height;
|
|
|
|
let height = self.font_height;
|
|
|
|
let region = Rect::new(x, top, x + width, top + height);
|
|
|
|
let region = Rect::new(x, y, x + width, y + height);
|
|
|
|
self.paint.set_color(style.background(default_colors).to_color());
|
|
|
|
self.paint.set_color(style.background(default_colors).to_color());
|
|
|
|
canvas.draw_rect(region, &self.paint);
|
|
|
|
canvas.draw_rect(region, &self.paint);
|
|
|
|
|
|
|
|
|
|
|
|
if style.underline || style.undercurl {
|
|
|
|
if style.underline || style.undercurl {
|
|
|
|
let (_, metrics) = self.font.metrics();
|
|
|
|
let (_, metrics) = self.font.metrics();
|
|
|
|
let width = text.chars().count() as f32 * self.font_width;
|
|
|
|
let line_position = metrics.underline_position().unwrap();
|
|
|
|
let underline_position = metrics.underline_position().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.paint.set_color(style.special(&default_colors).to_color());
|
|
|
|
self.paint.set_color(style.special(&default_colors).to_color());
|
|
|
|
canvas.draw_line((x, y + underline_position), (x + width, y + underline_position), &self.paint);
|
|
|
|
canvas.draw_line((x, y - line_position + self.font_height), (x + width, y - line_position + self.font_height), &self.paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.paint.set_color(style.foreground(&default_colors).to_color());
|
|
|
|
self.paint.set_color(style.foreground(&default_colors).to_color());
|
|
|
|
let text = text.trim_end();
|
|
|
|
let text = text.trim_end();
|
|
|
|
|
|
|
|
|
|
|
|
//canvas.draw_str(text, (x, y), &font, &paint);
|
|
|
|
|
|
|
|
if text.len() > 0 {
|
|
|
|
if text.len() > 0 {
|
|
|
|
let reference;
|
|
|
|
let reference;
|
|
|
|
let blob = if update_cache {
|
|
|
|
let blob = if update_cache {
|
|
|
@ -112,16 +137,18 @@ impl Renderer {
|
|
|
|
reference = self.shaper.shape(text, &self.font);
|
|
|
|
reference = self.shaper.shape(text, &self.font);
|
|
|
|
&reference
|
|
|
|
&reference
|
|
|
|
};
|
|
|
|
};
|
|
|
|
canvas.draw_text_blob(blob, (x, top), &self.paint);
|
|
|
|
canvas.draw_text_blob(blob, (x, y), &self.paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn draw(&mut self, canvas: &mut Canvas) {
|
|
|
|
pub fn draw(&mut self, window: &Window, canvas: &mut Canvas) {
|
|
|
|
let (draw_commands, default_colors, cursor_grid_pos, cursor_type, cursor_foreground, cursor_background, cursor_enabled) = {
|
|
|
|
let (draw_commands, title, default_colors, (width, height), cursor_grid_pos, cursor_type, cursor_foreground, cursor_background, cursor_enabled) = {
|
|
|
|
let editor = self.editor.lock().unwrap();
|
|
|
|
let editor = self.editor.lock().unwrap();
|
|
|
|
(
|
|
|
|
(
|
|
|
|
editor.build_draw_commands().clone(),
|
|
|
|
editor.build_draw_commands().clone(),
|
|
|
|
|
|
|
|
editor.title.clone(),
|
|
|
|
editor.default_colors.clone(),
|
|
|
|
editor.default_colors.clone(),
|
|
|
|
|
|
|
|
editor.size.clone(),
|
|
|
|
editor.cursor_pos.clone(),
|
|
|
|
editor.cursor_pos.clone(),
|
|
|
|
editor.cursor_type.clone(),
|
|
|
|
editor.cursor_type.clone(),
|
|
|
|
editor.cursor_foreground(),
|
|
|
|
editor.cursor_foreground(),
|
|
|
@ -136,6 +163,9 @@ impl Renderer {
|
|
|
|
self.draw_text(canvas, &command.text, command.grid_position, &command.style, &default_colors, true);
|
|
|
|
self.draw_text(canvas, &command.text, command.grid_position, &command.style, &default_colors, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.fps_tracker.record_frame();
|
|
|
|
|
|
|
|
self.draw_text(canvas, &self.fps_tracker.fps.to_string(), (width - 2, height - 1), &Style::new(default_colors.clone()), &default_colors, false);
|
|
|
|
|
|
|
|
|
|
|
|
let (cursor_grid_x, cursor_grid_y) = cursor_grid_pos;
|
|
|
|
let (cursor_grid_x, cursor_grid_y) = cursor_grid_pos;
|
|
|
|
let target_cursor_x = cursor_grid_x as f32 * self.font_width;
|
|
|
|
let target_cursor_x = cursor_grid_x as f32 * self.font_width;
|
|
|
|
let target_cursor_y = cursor_grid_y as f32 * self.font_height;
|
|
|
|
let target_cursor_y = cursor_grid_y as f32 * self.font_height;
|
|
|
@ -164,12 +194,16 @@ impl Renderer {
|
|
|
|
let character = editor.grid[cursor_grid_y as usize][cursor_grid_x as usize].clone()
|
|
|
|
let character = editor.grid[cursor_grid_y as usize][cursor_grid_x as usize].clone()
|
|
|
|
.map(|(character, _)| character)
|
|
|
|
.map(|(character, _)| character)
|
|
|
|
.unwrap_or(' ');
|
|
|
|
.unwrap_or(' ');
|
|
|
|
let text_y = cursor_y + self.font_height - self.font_height * 0.2;
|
|
|
|
|
|
|
|
canvas.draw_text_blob(
|
|
|
|
canvas.draw_text_blob(
|
|
|
|
self.shaper.shape_cached(character.to_string(), &self.font),
|
|
|
|
self.shaper.shape_cached(character.to_string(), &self.font),
|
|
|
|
(cursor_x, text_y), &self.paint);
|
|
|
|
(cursor_x, cursor_y), &self.paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.title != title {
|
|
|
|
|
|
|
|
window.set_title(&title);
|
|
|
|
|
|
|
|
self.title = title;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -181,7 +215,6 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>, nvim: Neovim, initial_size: (u64, u64
|
|
|
|
let (width, height) = initial_size;
|
|
|
|
let (width, height) = initial_size;
|
|
|
|
let logical_size = LogicalSize::new(
|
|
|
|
let logical_size = LogicalSize::new(
|
|
|
|
(width as f32 * renderer.font_width) as f64,
|
|
|
|
(width as f32 * renderer.font_width) as f64,
|
|
|
|
// Add 1.0 here to make sure resizing horizontally doesn't change the grid height
|
|
|
|
|
|
|
|
(height as f32 * renderer.font_height + 1.0) as f64
|
|
|
|
(height as f32 * renderer.font_height + 1.0) as f64
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
@ -213,8 +246,9 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>, nvim: Neovim, initial_size: (u64, u64
|
|
|
|
..
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
} => {
|
|
|
|
if new_size.width > 0.0 && new_size.height > 0.0 {
|
|
|
|
if new_size.width > 0.0 && new_size.height > 0.0 {
|
|
|
|
let new_width = (new_size.width as f32 / renderer.font_width) as u64;
|
|
|
|
let new_width = ((new_size.width + 1.0) as f32 / renderer.font_width) as u64;
|
|
|
|
let new_height = (new_size.height as f32 / renderer.font_height) as u64;
|
|
|
|
let new_height = ((new_size.height + 1.0) as f32 / renderer.font_height) as u64;
|
|
|
|
|
|
|
|
// Add 1 here to make sure resizing doesn't change the grid size on startup
|
|
|
|
nvim.ui_try_resize(new_width as i64, new_height as i64).expect("Resize failed");
|
|
|
|
nvim.ui_try_resize(new_width as i64, new_height as i64).expect("Resize failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -292,7 +326,7 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>, nvim: Neovim, initial_size: (u64, u64
|
|
|
|
..
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
} => {
|
|
|
|
if let Err(e) = skulpin_renderer.draw(&window, |canvas, _coordinate_system_helper| {
|
|
|
|
if let Err(e) = skulpin_renderer.draw(&window, |canvas, _coordinate_system_helper| {
|
|
|
|
renderer.draw(canvas);
|
|
|
|
renderer.draw(&window, canvas);
|
|
|
|
}) {
|
|
|
|
}) {
|
|
|
|
println!("Error during draw: {:?}", e);
|
|
|
|
println!("Error during draw: {:?}", e);
|
|
|
|
*control_flow = ControlFlow::Exit
|
|
|
|
*control_flow = ControlFlow::Exit
|
|
|
|