part way to multi grid rendering

macos-click-through
keith 4 years ago
parent 655339bbf8
commit 4ef6adcfef

721
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -33,6 +33,7 @@ cfg-if = "0.1.10"
which = "4" which = "4"
dirs = "2" dirs = "2"
rand = "0.7" rand = "0.7"
skia-safe = "0.32.1"
[dev-dependencies] [dev-dependencies]
mockall = "0.7.0" mockall = "0.7.0"

@ -160,6 +160,7 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
.unwrap_or_explained_panic("Could not communicate with neovim process"); .unwrap_or_explained_panic("Could not communicate with neovim process");
let mut options = UiAttachOptions::new(); let mut options = UiAttachOptions::new();
options.set_linegrid_external(true); options.set_linegrid_external(true);
options.set_multigrid_external(true);
options.set_rgb(true); options.set_rgb(true);
if let Err(command_error) = nvim.command("runtime! ginit.vim").await { if let Err(command_error) = nvim.command("runtime! ginit.vim").await {
nvim.command(&format!( nvim.command(&format!(

@ -43,6 +43,8 @@ pub struct Cursor {
pub blinkoff: Option<u64>, pub blinkoff: Option<u64>,
pub style: Option<Arc<Style>>, pub style: Option<Arc<Style>>,
pub enabled: bool, pub enabled: bool,
pub double_width: bool,
pub character: String
} }
impl Cursor { impl Cursor {
@ -56,6 +58,8 @@ impl Cursor {
blinkon: None, blinkon: None,
blinkoff: None, blinkoff: None,
enabled: true, enabled: true,
double_width: false,
character: " ".to_string()
} }
} }

@ -1,18 +1,18 @@
mod cursor; mod cursor;
mod grid; mod grid;
mod style; mod style;
mod window;
use std::collections::HashMap; use std::collections::{HashSet, HashMap};
use std::sync::Arc; use std::sync::Arc;
use log::trace; use log::{trace, error};
use parking_lot::Mutex; use parking_lot::Mutex;
use skulpin::skia_safe::colors; use skulpin::skia_safe::colors;
use unicode_segmentation::UnicodeSegmentation;
use crate::bridge::{EditorMode, GridLineCell, GuiOption, RedrawEvent}; use crate::bridge::{EditorMode, GuiOption, RedrawEvent, WindowAnchor};
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::window::window_geometry_or_default; pub use window::*;
pub use cursor::{Cursor, CursorMode, CursorShape}; pub use cursor::{Cursor, CursorMode, CursorShape};
pub use grid::CharacterGrid; pub use grid::CharacterGrid;
pub use style::{Colors, Style}; pub use style::{Colors, Style};
@ -21,17 +21,25 @@ 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()));
} }
#[derive(new, Debug, Clone)] pub struct RenderInfo {
pub struct DrawCommand { windows: Vec<WindowRenderInfo>,
pub text: String, closed_window_ids: Vec<u64>,
pub cell_width: u64, }
pub struct WindowRenderInfo {
pub grid_id: u64,
pub grid_position: (u64, u64), pub grid_position: (u64, u64),
pub style: Option<Arc<Style>>, pub width: u64,
pub height: u64,
pub should_clear: bool,
pub draw_commands: Vec<DrawCommand>,
pub child_windows: Vec<WindowRenderInfo>
} }
pub struct Editor { pub struct Editor {
pub grid: CharacterGrid,
pub title: String, pub title: String,
pub windows: HashMap<u64, Window>,
pub closed_window_ids: HashSet<u64>,
pub mouse_enabled: bool, pub mouse_enabled: bool,
pub guifont: Option<String>, pub guifont: Option<String>,
pub cursor: Cursor, pub cursor: Cursor,
@ -45,8 +53,9 @@ pub struct Editor {
impl Editor { impl Editor {
pub fn new() -> Editor { pub fn new() -> Editor {
Editor { Editor {
grid: CharacterGrid::new(window_geometry_or_default()),
title: "Neovide".to_string(), title: "Neovide".to_string(),
windows: HashMap::new(),
closed_window_ids: HashSet::new(),
mouse_enabled: true, mouse_enabled: true,
guifont: None, guifont: None,
cursor: Cursor::new(), cursor: Cursor::new(),
@ -91,218 +100,180 @@ impl Editor {
trace!("Image flushed"); trace!("Image flushed");
REDRAW_SCHEDULER.queue_next_frame(); REDRAW_SCHEDULER.queue_next_frame();
} }
RedrawEvent::Resize { width, height, .. } => self.grid.resize(width, height),
RedrawEvent::DefaultColorsSet { colors } => { RedrawEvent::DefaultColorsSet { colors } => {
self.default_style = Arc::new(Style::new(colors)) self.default_style = Arc::new(Style::new(colors))
} }
RedrawEvent::HighlightAttributesDefine { id, style } => { RedrawEvent::HighlightAttributesDefine { id, style } => {
self.defined_styles.insert(id, Arc::new(style)); self.defined_styles.insert(id, Arc::new(style));
} }
RedrawEvent::CursorGoto { grid, row, column } => self.set_cursor_position(grid, row, column),
RedrawEvent::Resize { grid, width, height } => {
self.windows.get_mut(&grid).map(|window| window.resize(width, height));
},
RedrawEvent::GridLine { RedrawEvent::GridLine {
grid,
row, row,
column_start, column_start,
cells, cells
.. } => {
} => self.draw_grid_line(row, column_start, cells), self.windows.get_mut(&grid).map(|window| window.draw_grid_line(row, column_start, cells, &self.defined_styles, &mut self.previous_style));
RedrawEvent::Clear { .. } => self.grid.clear(), },
RedrawEvent::CursorGoto { row, column, .. } => self.cursor.position = (row, column), RedrawEvent::Clear { grid } => {
self.windows.get_mut(&grid).map(|window| window.grid.clear());
},
RedrawEvent::Scroll { RedrawEvent::Scroll {
grid,
top, top,
bottom, bottom,
left, left,
right, right,
rows, rows,
columns, columns
.. } => {
} => self.scroll_region(top, bottom, left, right, rows, columns), self.windows.get_mut(&grid).map(|window| window.scroll_region(top, bottom, left, right, rows, columns));
},
RedrawEvent::WindowPosition { grid, window, start_row, start_column, width, height } => self.set_window_position(grid, window, start_row, start_column, width, height),
RedrawEvent::WindowFloatPosition { grid, window, anchor, anchor_grid, anchor_row, anchor_column, .. } => self.set_window_float_position(grid, window, anchor_grid, anchor, anchor_row, anchor_column),
RedrawEvent::WindowHide { grid } => {
self.windows.get_mut(&grid).map(|window| window.hidden = true);
},
RedrawEvent::WindowClose { grid } => self.close_window(grid),
_ => {} _ => {}
}; };
} }
pub fn build_draw_commands(&mut self) -> (Vec<DrawCommand>, bool) { fn close_window(&mut self, grid: u64) {
let mut draw_commands = Vec::new(); self.windows.remove(&grid);
self.closed_window_ids.insert(grid);
for (row_index, row) in self.grid.rows().enumerate() { }
let mut command = None;
fn add_command(commands_list: &mut Vec<DrawCommand>, command: Option<DrawCommand>) {
if let Some(command) = command {
commands_list.push(command);
}
}
fn command_matches(command: &Option<DrawCommand>, style: &Option<Arc<Style>>) -> bool { fn set_window_position(&mut self, grid: u64, window_id: u64, start_row: u64, start_column: u64, width: u64, height: u64) {
match command { match self.windows.get_mut(&grid) {
Some(command) => &command.style == style, Some(window) => {
None => true, window.hidden = false;
} window.anchor_grid_id = None;
window.anchor_type = WindowAnchor::NorthWest;
window.anchor_row = start_row;
window.anchor_column = start_column;
window.resize(width, height);
},
None => {
let new_window = Window::new(window_id, grid, width, height, None, WindowAnchor::NorthWest, start_row, start_column);
self.windows.insert(grid, new_window);
} }
}
}
fn add_character( fn set_window_float_position(&mut self, grid: u64, window_id: u64, anchor_grid: u64, anchor_type: WindowAnchor, anchor_row: u64, anchor_column: u64) {
command: &mut Option<DrawCommand>, if let Some(window) = self.windows.get_mut(&grid) {
character: &str, window.hidden = false;
row_index: u64, window.anchor_grid_id = Some(anchor_grid);
col_index: u64, window.anchor_type = anchor_type;
style: Option<Arc<Style>>, window.anchor_row = anchor_row;
) { window.anchor_column = anchor_column;
match command { } else {
Some(command) => { error!("Attempted to float window that does not exist.");
command.text.push_str(character); }
command.cell_width += 1;
}
None => {
command.replace(DrawCommand::new(
character.to_string(),
1,
(col_index, row_index),
style,
));
}
}
}
for (col_index, cell) in row.iter().enumerate() { if let Some(anchor_window) = self.windows.get_mut(&anchor_grid) {
if let Some((character, style)) = cell { anchor_window.children.insert(grid);
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) {
add_command(&mut draw_commands, command);
command = None;
}
add_character(
&mut command,
&character,
row_index as u64,
col_index as u64,
style.clone(),
);
}
} else {
if !command_matches(&command, &None) {
add_command(&mut draw_commands, command);
command = None;
}
add_character(&mut command, " ", row_index as u64, col_index as u64, None);
}
}
add_command(&mut draw_commands, command);
} }
}
let should_clear = self.grid.should_clear; fn get_window_top_left(&self, grid: u64) -> Option<(u64, u64)> {
let draw_commands = draw_commands let window = self.windows.get(&grid)?;
.into_iter()
.filter(|command| {
let (x, y) = command.grid_position;
let min = (x as i64 - 1).max(0) as u64;
let max = (x + command.cell_width + 1).min(self.grid.width);
for char_index in min..max { match window.anchor_grid_id {
if self.grid.is_dirty_cell(char_index, y) { Some(anchor_grid) => {
return true; let (parent_anchor_row, parent_anchor_column) = self.get_window_top_left(anchor_grid)?;
} match window.anchor_type {
WindowAnchor::NorthWest => {
Some((parent_anchor_row + window.anchor_row, parent_anchor_column + window.anchor_column))
},
WindowAnchor::NorthEast => {
Some((parent_anchor_row + window.anchor_row, parent_anchor_column + window.anchor_column - window.grid.width))
},
WindowAnchor::SouthWest => {
Some((parent_anchor_row + window.anchor_row - window.grid.height, parent_anchor_column + window.anchor_column))
},
WindowAnchor::SouthEast => {
Some((parent_anchor_row + window.anchor_row - window.grid.height, parent_anchor_column + window.anchor_column - window.grid.width))
},
} }
false },
}) None => Some((window.anchor_row, window.anchor_column))
.collect::<Vec<DrawCommand>>(); }
self.grid.set_dirty_all(false);
self.grid.should_clear = false;
trace!("Draw commands sent");
(draw_commands, should_clear)
} }
fn draw_grid_line_cell(&mut self, row_index: u64, column_pos: &mut u64, cell: GridLineCell) { fn set_cursor_position(&self, grid: u64, row: u64, column: u64) {
let style = match cell.highlight_id { match self.get_window_top_left(grid) {
Some(0) => None, Some((window_row, window_column)) => {
Some(style_id) => self.defined_styles.get(&style_id).cloned(), self.cursor.position = (window_row + row, window_column + column);
None => self.previous_style.clone(),
};
let mut text = cell.text;
if let Some(times) = cell.repeat {
text = text.repeat(times as usize);
}
if text.is_empty() { if let Some(window) = self.windows.get(&grid) {
if let Some(cell) = self.grid.get_cell_mut(*column_pos, row_index) { self.cursor.character = match window.grid.get_cell(column, row) {
*cell = Some(("".to_string(), style.clone())); Some(Some((character, _))) => character.clone(),
} _ => ' '.to_string(),
};
self.grid.set_dirty_cell(*column_pos, row_index); self.cursor.double_width = match window.grid.get_cell(column + 1, row) {
*column_pos += 1; Some(Some((character, _))) => character.is_empty(),
} else { _ => false,
for (i, character) in text.graphemes(true).enumerate() { };
if let Some(cell) = self.grid.get_cell_mut(i as u64 + *column_pos, row_index) {
*cell = Some((character.to_string(), style.clone()));
self.grid.set_dirty_cell(*column_pos, row_index);
} }
},
None => {
self.cursor.position = (row, column);
self.cursor.double_width = false;
self.cursor.character = " ".to_string();
} }
*column_pos += text.graphemes(true).count() as u64;
} }
self.previous_style = style;
} }
fn draw_grid_line(&mut self, row: u64, column_start: u64, cells: Vec<GridLineCell>) { fn set_option(&mut self, gui_option: GuiOption) {
if row < self.grid.height { trace!("Option set {:?}", &gui_option);
let mut column_pos = column_start; if let GuiOption::GuiFont(guifont) = gui_option {
for cell in cells { self.guifont = Some(guifont);
self.draw_grid_line_cell(row, &mut column_pos, cell);
}
} else {
println!("Draw command out of bounds");
} }
} }
fn scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64, rows: i64, cols: i64) { fn build_window_render_info(&mut self, grid: u64) -> Option<WindowRenderInfo> {
let y_iter: Box<dyn Iterator<Item = i64>> = if rows > 0 { let grid_position = self.get_window_top_left(grid)?;
Box::new((top as i64 + rows)..bot as i64) let (draw_commands, should_clear) = {
} else { let mut window = self.windows.get_mut(&grid)?;
Box::new((top as i64..(bot as i64 + rows)).rev()) window.build_draw_commands()
}; };
for y in y_iter { let window = self.windows.get(&grid)?;
let dest_y = y - rows; let child_windows = window.children.iter().filter_map(|child_id| self.build_window_render_info(*child_id)).collect();
if dest_y >= 0 && dest_y < self.grid.height as i64 {
let x_iter: Box<dyn Iterator<Item = i64>> = if cols > 0 {
Box::new((left as i64 + cols)..right as i64)
} else {
Box::new((left as i64..(right as i64 + cols)).rev())
};
for x in x_iter { Some(WindowRenderInfo {
let dest_x = x - cols; grid_id: grid,
let cell_data = self.grid.get_cell(x as u64, y as u64).cloned(); grid_position,
width: window.grid.width,
height: window.grid.height,
should_clear,
draw_commands,
child_windows
})
}
if let Some(cell_data) = cell_data { pub fn build_render_info(&mut self) -> RenderInfo {
if let Some(dest_cell) = let mut windows = Vec::new();
self.grid.get_cell_mut(dest_x as u64, dest_y as u64)
{ for window in self.windows.values() {
*dest_cell = cell_data; if !window.hidden && window.anchor_grid_id.is_none() {
self.grid.set_dirty_cell(dest_x as u64, dest_y as u64); if let Some(window_render_info) = self.build_window_render_info(window.grid_id) {
} windows.push(window_render_info);
}
} }
} }
} }
trace!("Region scrolled");
}
fn set_option(&mut self, gui_option: GuiOption) { let closed_window_ids = self.closed_window_ids.iter().copied().collect();
trace!("Option set {:?}", &gui_option); self.closed_window_ids.clear();
if let GuiOption::GuiFont(guifont) = gui_option {
self.guifont = Some(guifont); RenderInfo {
windows, closed_window_ids
} }
} }
} }

@ -0,0 +1,224 @@
use std::collections::{HashSet, HashMap};
use std::sync::Arc;
use log::trace;
use unicode_segmentation::UnicodeSegmentation;
use crate::bridge::{GridLineCell, WindowAnchor};
use super::grid::CharacterGrid;
use super::style::Style;
#[derive(new, Debug, Clone)]
pub struct DrawCommand {
pub text: String,
pub cell_width: u64,
pub grid_position: (u64, u64),
pub style: Option<Arc<Style>>,
}
pub struct Window {
pub id: u64,
pub grid_id: u64,
pub grid: CharacterGrid,
pub hidden: bool,
pub anchor_grid_id: Option<u64>,
pub anchor_type: WindowAnchor,
pub anchor_row: u64,
pub anchor_column: u64,
pub children: HashSet<u64>,
}
impl Window {
pub fn new(id: u64, grid_id: u64, width: u64, height: u64, anchor_grid_id: Option<u64>, anchor_type: WindowAnchor, anchor_row: u64, anchor_column: u64) -> Window {
Window {
id, grid_id, anchor_grid_id, anchor_type, anchor_row, anchor_column,
grid: CharacterGrid::new((width, height)),
hidden: false,
children: HashSet::new()
}
}
pub fn resize(&mut self, width: u64, height: u64) {
self.grid.resize(width, height);
}
fn draw_grid_line_cell(&mut self, row_index: u64, column_pos: &mut u64, cell: GridLineCell, defined_styles: &HashMap<u64, Arc<Style>>, previous_style: &mut Option<Arc<Style>>) {
let style = match cell.highlight_id {
Some(0) => None,
Some(style_id) => defined_styles.get(&style_id).cloned(),
None => previous_style.clone(),
};
let mut text = cell.text;
if let Some(times) = cell.repeat {
text = text.repeat(times as usize);
}
if text.is_empty() {
if let Some(cell) = self.grid.get_cell_mut(*column_pos, row_index) {
*cell = Some(("".to_string(), style.clone()));
}
self.grid.set_dirty_cell(*column_pos, row_index);
*column_pos += 1;
} else {
for (i, character) in text.graphemes(true).enumerate() {
if let Some(cell) = self.grid.get_cell_mut(i as u64 + *column_pos, row_index) {
*cell = Some((character.to_string(), style.clone()));
self.grid.set_dirty_cell(*column_pos, row_index);
}
}
*column_pos += text.graphemes(true).count() as u64;
}
*previous_style = style;
}
pub fn draw_grid_line(&mut self, row: u64, column_start: u64, cells: Vec<GridLineCell>, defined_styles: &HashMap<u64, Arc<Style>>, previous_style: &mut Option<Arc<Style>>) {
if row < self.grid.height {
let mut column_pos = column_start;
for cell in cells {
self.draw_grid_line_cell(row, &mut column_pos, cell, defined_styles, previous_style);
}
} else {
println!("Draw command out of bounds");
}
}
pub fn scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64, rows: i64, cols: i64) {
let y_iter: Box<dyn Iterator<Item = i64>> = if rows > 0 {
Box::new((top as i64 + rows)..bot as i64)
} else {
Box::new((top as i64..(bot as i64 + rows)).rev())
};
for y in y_iter {
let dest_y = y - rows;
if dest_y >= 0 && dest_y < self.grid.height as i64 {
let x_iter: Box<dyn Iterator<Item = i64>> = if cols > 0 {
Box::new((left as i64 + cols)..right as i64)
} else {
Box::new((left as i64..(right as i64 + cols)).rev())
};
for x in x_iter {
let dest_x = x - cols;
let cell_data = self.grid.get_cell(x as u64, y as u64).cloned();
if let Some(cell_data) = cell_data {
if let Some(dest_cell) =
self.grid.get_cell_mut(dest_x as u64, dest_y as u64)
{
*dest_cell = cell_data;
self.grid.set_dirty_cell(dest_x as u64, dest_y as u64);
}
}
}
}
}
trace!("Region scrolled");
}
pub fn build_draw_commands(&mut self) -> (Vec<DrawCommand>, bool) {
let mut draw_commands = Vec::new();
for (row_index, row) in self.grid.rows().enumerate() {
let mut command = None;
fn add_command(commands_list: &mut Vec<DrawCommand>, command: Option<DrawCommand>) {
if let Some(command) = command {
commands_list.push(command);
}
}
fn command_matches(command: &Option<DrawCommand>, style: &Option<Arc<Style>>) -> bool {
match command {
Some(command) => &command.style == style,
None => true,
}
}
fn add_character(
command: &mut Option<DrawCommand>,
character: &str,
row_index: u64,
col_index: u64,
style: Option<Arc<Style>>,
) {
match command {
Some(command) => {
command.text.push_str(character);
command.cell_width += 1;
}
None => {
command.replace(DrawCommand::new(
character.to_string(),
1,
(col_index, row_index),
style,
));
}
}
}
for (col_index, cell) in row.iter().enumerate() {
if let Some((character, style)) = cell {
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) {
add_command(&mut draw_commands, command);
command = None;
}
add_character(
&mut command,
&character,
row_index as u64,
col_index as u64,
style.clone(),
);
}
} else {
if !command_matches(&command, &None) {
add_command(&mut draw_commands, command);
command = None;
}
add_character(&mut command, " ", row_index as u64, col_index as u64, None);
}
}
add_command(&mut draw_commands, command);
}
let should_clear = self.grid.should_clear;
let draw_commands = draw_commands
.into_iter()
.filter(|command| {
let (x, y) = command.grid_position;
let min = (x as i64 - 1).max(0) as u64;
let max = (x + command.cell_width + 1).min(self.grid.width);
for char_index in min..max {
if self.grid.is_dirty_cell(char_index, y) {
return true;
}
}
false
})
.collect::<Vec<DrawCommand>>();
self.grid.set_dirty_all(false);
self.grid.should_clear = false;
trace!("Draw commands sent");
(draw_commands, should_clear)
}
}

@ -276,26 +276,18 @@ impl CursorRenderer {
}; };
let (grid_x, grid_y) = self.previous_position; let (grid_x, grid_y) = self.previous_position;
let (character, font_dimensions, in_insert_mode): (String, Point, bool) = { let character = cursor.text;
let editor = EDITOR.lock();
let character = match editor.grid.get_cell(grid_x, grid_y) {
Some(Some((character, _))) => character.clone(),
_ => ' '.to_string(),
};
let is_double = match editor.grid.get_cell(grid_x + 1, grid_y) {
Some(Some((character, _))) => character.is_empty(),
_ => false,
};
let font_width = match (is_double, &cursor.shape) { let font_width = match (cursor.double_width, &cursor.shape) {
(true, CursorShape::Block) => font_width * 2.0, (true, CursorShape::Block) => font_width * 2.0,
_ => font_width, _ => font_width
}; };
let in_insert_mode = matches!(editor.current_mode, EditorMode::Insert); let font_dimensions: Point = (font_width, font_height).into();
(character, (font_width, font_height).into(), in_insert_mode) let in_insert_mode = {
let editor = EDITOR.lock();
matches!(editor.current_mode, EditorMode::Insert);
}; };
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();

@ -1,8 +1,9 @@
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use log::trace; use log::trace;
use skulpin::skia_safe::gpu::SurfaceOrigin; use skulpin::skia_safe::gpu::SurfaceOrigin;
use skulpin::skia_safe::{colors, dash_path_effect, Budgeted, Canvas, Paint, Rect, Surface}; use skulpin::skia_safe::{colors, dash_path_effect, Budgeted, Canvas, Paint, Rect, Surface, ImageInfo};
use skulpin::CoordinateSystemHelper; use skulpin::CoordinateSystemHelper;
mod caching_shaper; mod caching_shaper;
@ -16,7 +17,7 @@ use crate::editor::{Style, EDITOR};
use cursor_renderer::CursorRenderer; use cursor_renderer::CursorRenderer;
pub struct Renderer { pub struct Renderer {
surface: Option<Surface>, window_surfaces: HashMap<u64, Surface>,
paint: Paint, paint: Paint,
shaper: CachingShaper, shaper: CachingShaper,
@ -27,7 +28,6 @@ pub struct Renderer {
impl Renderer { impl Renderer {
pub fn new() -> Renderer { pub fn new() -> Renderer {
let surface = None;
let mut paint = Paint::new(colors::WHITE, None); let mut paint = Paint::new(colors::WHITE, None);
paint.set_anti_alias(false); paint.set_anti_alias(false);
@ -37,7 +37,7 @@ impl Renderer {
let cursor_renderer = CursorRenderer::new(); let cursor_renderer = CursorRenderer::new();
Renderer { Renderer {
surface, window_surfaces: HashMap::new(),
paint, paint,
shaper, shaper,
font_width, font_width,
@ -149,56 +149,47 @@ impl Renderer {
canvas.restore(); canvas.restore();
} }
pub fn draw( pub fn build_window_surface(&self, gpu_canvas: &mut Canvas, default_style: &Arc<Style>, dimensions: (u64, u64)) -> Surface {
&mut self, let mut context = gpu_canvas.gpu_context().unwrap();
gpu_canvas: &mut Canvas, let budgeted = Budgeted::YES;
coordinate_system_helper: &CoordinateSystemHelper, let parent_image_info = gpu_canvas.image_info();
dt: f32, let image_info = ImageInfo::new(dimensions, parent_image_info.color_type(), parent_image_info.alpha_type(), parent_image_info.color_space());
) -> bool { let surface_origin = SurfaceOrigin::TopLeft;
trace!("Rendering"); let mut surface = Surface::new_render_target(
&mut context,
budgeted,
&image_info,
None,
surface_origin,
None,
None,
)
.expect("Could not create surface");
let canvas = surface.canvas();
canvas.clear(default_style.colors.background.clone().unwrap().to_color());
surface
}
let ((draw_commands, should_clear), default_style, cursor, guifont_setting) = { pub fn draw_window(&mut self, root_canvas: &mut Canvas, window_render_info: &WindowRenderInfo, coordinate_system_helper: &CoordinateSystemHelper, default_style: Arc<Style>) {
let mut editor = EDITOR.lock(); let image_width = (window_render_info.width * self.font_width) as i32;
( let image_height = (window_render_info.height * self.font_height) as i32;
editor.build_draw_commands(),
editor.default_style.clone(),
editor.cursor.clone(),
editor.guifont.clone(),
)
};
let font_changed = guifont_setting let mut surface_entry = self.window_surfaces.entry(&window_render_info.grid_id);
.map(|guifont| self.update_font(&guifont))
.unwrap_or(false); let build_surface = || build_window_surface(gpu_canvas, default_style, (image_width, image_height));
if should_clear { if window_render_info.should_clear {
self.surface = None; surface_entry = surface_entry.insert(build_surface());
} }
let mut surface = self.surface.take().unwrap_or_else(|| { let surface = self.window_surfaces
let mut context = gpu_canvas.gpu_context().unwrap(); .entry(&window_render_info.grid_id)
let budgeted = Budgeted::YES; .or_insert_with(build_surface());
let image_info = gpu_canvas.image_info();
let surface_origin = SurfaceOrigin::TopLeft;
let mut surface = Surface::new_render_target(
&mut context,
budgeted,
&image_info,
None,
surface_origin,
None,
None,
)
.expect("Could not create surface");
let canvas = surface.canvas();
canvas.clear(default_style.colors.background.clone().unwrap().to_color());
surface
});
let mut canvas = surface.canvas(); let mut canvas = surface.canvas();
coordinate_system_helper.use_logical_coordinates(&mut canvas); coordinate_system_helper.use_logical_coordinates(&mut canvas);
for command in draw_commands.iter() { for command in window_render_info.draw_commands.iter() {
self.draw_background( self.draw_background(
&mut canvas, &mut canvas,
command.grid_position, command.grid_position,
@ -208,7 +199,7 @@ impl Renderer {
); );
} }
for command in draw_commands.iter() { for command in window_render_info.draw_commands.iter() {
self.draw_foreground( self.draw_foreground(
&mut canvas, &mut canvas,
&command.text, &command.text,
@ -220,15 +211,43 @@ impl Renderer {
} }
let image = surface.image_snapshot(); let image = surface.image_snapshot();
let window_size = coordinate_system_helper.window_logical_size();
let (grid_left, grid_right) = window_render_info.grid_position;
let image_destination = Rect::new( let image_destination = Rect::new(
0.0, grid_left * self.font_width,
0.0, grid_height * self.font_height,
window_size.width as f32, image_width as f32,
window_size.height as f32, image_height as f32,
); );
gpu_canvas.draw_image_rect(image, None, &image_destination, &self.paint); root_canvas.draw_image_rect(image, None, &image_destination, &self.paint);
for child_window_render_info in window_render_info.child_windows.iter() {
self.draw_window(root_canvas, child_window_render_info, coordinate_system_helper, default_style);
}
}
pub fn draw(
&mut self,
gpu_canvas: &mut Canvas,
coordinate_system_helper: &CoordinateSystemHelper,
dt: f32,
) -> bool {
trace!("Rendering");
let (render_info, default_style, cursor, guifont_setting) = {
let mut editor = EDITOR.lock();
(
editor.build_render_info(),
editor.default_style.clone(),
editor.cursor.clone(),
editor.guifont.clone(),
)
};
let font_changed = guifont_setting
.map(|guifont| self.update_font(&guifont))
.unwrap_or(false);
self.surface = Some(surface); self.surface = Some(surface);
self.cursor_renderer.draw( self.cursor_renderer.draw(

Loading…
Cancel
Save