better keybindings and animated cursor

macos-click-through
keith 5 years ago
parent 66d9b05657
commit 44b545fa28

@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use skulpin::skia_safe::{colors, Color4f}; use skulpin::skia_safe::{colors, Color4f};
use neovim_lib::{Neovim, NeovimApi}; use neovim_lib::{Neovim, NeovimApi};
@ -31,10 +32,10 @@ pub struct Style {
#[derive(new)] #[derive(new)]
pub struct GridLineCell { pub struct GridLineCell {
pub grid: u16, pub grid: usize,
pub text: String, pub text: String,
pub row: u16, pub row: usize,
pub col_start: u16, pub col_start: usize,
pub style_id: Option<u64> pub style_id: Option<u64>
} }
@ -43,27 +44,36 @@ pub type GridCell = Option<(char, Style)>;
#[derive(new, Debug, Clone)] #[derive(new, Debug, Clone)]
pub struct DrawCommand { pub struct DrawCommand {
pub text: String, pub text: String,
pub row: u16, pub row: usize,
pub col_start: u16, pub col_start: usize,
pub style: Style pub style: Style
} }
#[derive(Clone)]
pub enum CursorType {
Block,
Horizontal,
Vertical
}
pub struct Editor { pub struct Editor {
pub nvim: Neovim, pub nvim: Neovim,
pub grid: Vec<Vec<GridCell>>, pub grid: Vec<Vec<GridCell>>,
pub cursor_pos: (u16, u16), pub cursor_pos: (usize, usize),
pub size: (u16, u16), pub cursor_type: CursorType,
pub size: (usize, usize),
pub default_colors: Colors, pub default_colors: Colors,
pub defined_styles: HashMap<u64, Style>, pub defined_styles: HashMap<u64, Style>,
pub previous_style: Option<Style> pub previous_style: Option<Style>
} }
impl Editor { impl Editor {
pub fn new(nvim: Neovim, width: u16, height: u16) -> Editor { pub fn new(nvim: Neovim, width: usize, height: usize) -> Editor {
let mut editor = Editor { let mut editor = Editor {
nvim, nvim,
grid: Vec::new(), grid: Vec::new(),
cursor_pos: (0, 0), cursor_pos: (0, 0),
cursor_type: CursorType::Block,
size: (width, height), size: (width, height),
default_colors: Colors::new(Some(colors::WHITE), Some(colors::BLACK), Some(colors::GREY)), default_colors: Colors::new(Some(colors::WHITE), Some(colors::BLACK), Some(colors::GREY)),
defined_styles: HashMap::new(), defined_styles: HashMap::new(),
@ -91,7 +101,7 @@ impl Editor {
} }
} }
fn add_character(command: &mut Option<DrawCommand>, character: &char, row_index: u16, col_index: u16, style: Style) { fn add_character(command: &mut Option<DrawCommand>, character: &char, row_index: usize, col_index: usize, style: Style) {
match command { match command {
Some(command) => command.text.push(character.clone()), Some(command) => command.text.push(character.clone()),
None => { None => {
@ -106,7 +116,7 @@ impl Editor {
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
command = None; command = None;
} }
add_character(&mut command, &character, row_index as u16, col_index as u16, new_style.clone()); add_character(&mut command, &character, row_index as usize, col_index as usize, new_style.clone());
} else { } else {
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
command = None; command = None;
@ -144,12 +154,62 @@ impl Editor {
self.previous_style = Some(style); self.previous_style = Some(style);
} }
pub fn scroll_region(&mut self, top: isize, bot: isize, left: isize, right: isize, rows: isize, cols: isize) {
let (top, bot) = if rows > 0 {
(top + rows, bot)
} else if rows < 0 {
(top, bot + rows)
} else {
(top, bot)
};
let (left, right) = if cols > 0 {
(left + cols, right)
} else if rows < 0 {
(left, right + cols)
} else {
(left, right)
};
let width = right - left;
let height = bot - top;
let mut region = Vec::new();
for y in top..bot {
let row = &self.grid[y as usize];
let mut copied_section = Vec::new();
for x in left..right {
copied_section.push(row[x as usize].clone());
}
region.push(copied_section);
}
let new_top = top as isize - rows;
let new_left = left as isize - cols;
dbg!(top, bot, left, right, rows, cols, new_top, new_left);
for (y, row_section) in region.into_iter().enumerate() {
for (x, cell) in row_section.into_iter().enumerate() {
let y = new_top + y as isize;
if y >= 0 && y < self.grid.len() as isize {
let mut row = &mut self.grid[y as usize];
let x = new_left + x as isize;
if x >= 0 && x < row.len() as isize {
row[x as usize] = cell;
}
}
}
}
}
pub fn clear(&mut self) { pub fn clear(&mut self) {
let (width, height) = self.size; let (width, height) = self.size;
self.grid = vec![vec![None; width as usize]; height as usize]; self.grid = vec![vec![None; width as usize]; height as usize];
} }
pub fn resize(&mut self, new_width: u16, new_height: u16) { pub fn resize(&mut self, new_width: usize, new_height: usize) {
self.nvim.ui_try_resize(new_width as i64, new_height as i64).expect("Resize failed"); self.nvim.ui_try_resize(new_width as i64, new_height as i64).expect("Resize failed");
self.size = (new_width, new_height); self.size = (new_width, new_height);
} }
@ -162,7 +222,7 @@ impl Editor {
self.default_colors = Colors::new(Some(foreground), Some(background), Some(special)); self.default_colors = Colors::new(Some(foreground), Some(background), Some(special));
} }
pub fn jump_cursor_to(&mut self, row: u16, col: u16) { pub fn jump_cursor_to(&mut self, row: usize, col: usize) {
self.cursor_pos = (row, col); self.cursor_pos = (row, col);
} }
} }

@ -80,13 +80,15 @@ fn parse_keycode(keycode: VirtualKeyCode) -> Option<(String, bool)> {
VirtualKeyCode::Space => Some(("Space".to_string(), true)), VirtualKeyCode::Space => Some(("Space".to_string(), true)),
VirtualKeyCode::Apostrophe => Some(("'".to_string(), false)), VirtualKeyCode::Apostrophe => Some(("'".to_string(), false)),
VirtualKeyCode::Backslash => Some(("Bslash".to_string(), true)), VirtualKeyCode::Backslash => Some(("Bslash".to_string(), true)),
VirtualKeyCode::Colon => Some((":".to_string(), false)),
VirtualKeyCode::Comma => Some((",".to_string(), false)), VirtualKeyCode::Comma => Some((",".to_string(), false)),
VirtualKeyCode::Decimal => Some((".".to_string(), false)),
VirtualKeyCode::Divide => Some(("/".to_string(), false)),
VirtualKeyCode::Equals => Some(("=".to_string(), false)), VirtualKeyCode::Equals => Some(("=".to_string(), false)),
VirtualKeyCode::LBracket => Some(("[".to_string(), false)),
VirtualKeyCode::Minus => Some(("-".to_string(), false)), VirtualKeyCode::Minus => Some(("-".to_string(), false)),
VirtualKeyCode::Period => Some((".".to_string(), false)),
VirtualKeyCode::RBracket => Some(("]".to_string(), false)),
VirtualKeyCode::Semicolon => Some((";".to_string(), false)), VirtualKeyCode::Semicolon => Some((";".to_string(), false)),
VirtualKeyCode::Slash => Some(("/".to_string(), false)),
VirtualKeyCode::Tab => Some(("<Tab>".to_string(), false)),
_ => None _ => None
} }
} }
@ -97,11 +99,6 @@ fn append_modifiers(modifiers: ModifiersState, keycode_text: String, special: bo
if modifiers.shift { if modifiers.shift {
result = match result.as_ref() { result = match result.as_ref() {
"," => "<".to_string(),
"." => ">".to_string(),
";" => ":".to_string(),
"=" => "+".to_string(),
"-" => "_".to_string(),
"1" => "!".to_string(), "1" => "!".to_string(),
"2" => "@".to_string(), "2" => "@".to_string(),
"3" => "#".to_string(), "3" => "#".to_string(),
@ -112,6 +109,16 @@ fn append_modifiers(modifiers: ModifiersState, keycode_text: String, special: bo
"8" => "*".to_string(), "8" => "*".to_string(),
"9" => "(".to_string(), "9" => "(".to_string(),
"0" => ")".to_string(), "0" => ")".to_string(),
"'" => "\"".to_string(),
"Bslash" => "|".to_string(),
"," => "<".to_string(),
"=" => "+".to_string(),
"[" => "{".to_string(),
"-" => "_".to_string(),
"." => ">".to_string(),
"]" => "}".to_string(),
";" => ":".to_string(),
"/" => "?".to_string(),
other => { other => {
special = true; special = true;
format!("S-{}", result) format!("S-{}", result)

@ -1,4 +1,4 @@
//#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
mod editor; mod editor;
mod window; mod window;
@ -21,16 +21,16 @@ use editor::{Colors, Editor, GridLineCell, Style};
use skulpin::skia_safe::Color4f; use skulpin::skia_safe::Color4f;
const INITIAL_WIDTH: u16 = 100; const INITIAL_WIDTH: usize = 100;
const INITIAL_HEIGHT: u16 = 50; const INITIAL_HEIGHT: usize = 50;
fn handle_grid_line(grid_line_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) { fn handle_grid_line(grid_line_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [Value::Integer(grid_id), Value::Integer(row), Value::Integer(col_start), Value::Array(cells)] = grid_line_arguments.as_slice() { if let [Value::Integer(grid_id), Value::Integer(row), Value::Integer(col_start), Value::Array(cells)] = grid_line_arguments.as_slice() {
let mut col_pos = col_start.as_u64().unwrap() as u16; let mut col_pos = col_start.as_u64().unwrap() as usize;
for cell in cells.into_iter() { for cell in cells.into_iter() {
if let Value::Array(cell_data) = cell { if let Value::Array(cell_data) = cell {
let grid_id = grid_id.as_u64().unwrap() as u16; let grid_id = grid_id.as_u64().unwrap() as usize;
let row = row.as_u64().unwrap() as u16; let row = row.as_u64().unwrap() as usize;
let mut text = match cell_data.get(0).expect("Cell must have non zero size") { let mut text = match cell_data.get(0).expect("Cell must have non zero size") {
Value::String(cell_text) => cell_text.as_str().expect("Could not process string").to_string(), Value::String(cell_text) => cell_text.as_str().expect("Could not process string").to_string(),
@ -47,7 +47,7 @@ fn handle_grid_line(grid_line_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>
} }
let mut editor = editor.lock().unwrap(); let mut editor = editor.lock().unwrap();
let length = text.chars().count() as u16; let length = text.chars().count();
editor.draw(GridLineCell::new(grid_id, text, row, col_pos, style_id)); editor.draw(GridLineCell::new(grid_id, text, row, col_pos, style_id));
col_pos = col_pos + length; col_pos = col_pos + length;
} else { } else {
@ -67,7 +67,7 @@ fn handle_clear(_clear_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
fn handle_cursor_goto(cursor_goto_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) { fn handle_cursor_goto(cursor_goto_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [Value::Integer(_grid_id), Value::Integer(row), Value::Integer(column)] = cursor_goto_arguments.as_slice() { if let [Value::Integer(_grid_id), Value::Integer(row), Value::Integer(column)] = cursor_goto_arguments.as_slice() {
let mut editor = editor.lock().unwrap(); let mut editor = editor.lock().unwrap();
editor.jump_cursor_to(column.as_u64().unwrap() as u16, row.as_u64().unwrap() as u16); editor.jump_cursor_to(column.as_u64().unwrap() as usize, row.as_u64().unwrap() as usize);
} else { } else {
println!("Invalid cursor_goto format: {:?}", cursor_goto_arguments); println!("Invalid cursor_goto format: {:?}", cursor_goto_arguments);
} }
@ -107,7 +107,6 @@ fn handle_hl_attr_define(hl_attr_define_arguments: &Vec<Value>, editor: &Arc<Mut
Value::Integer(id), Value::Map(attributes), Value::Map(_terminal_attributes), Value::Array(_info) Value::Integer(id), Value::Map(attributes), Value::Map(_terminal_attributes), Value::Array(_info)
] = hl_attr_define_arguments.as_slice() { ] = hl_attr_define_arguments.as_slice() {
let id = id.as_u64().unwrap(); let id = id.as_u64().unwrap();
let mut editor = editor.lock().unwrap();
let mut style = Style::new(Colors::new(None, None, None)); let mut style = Style::new(Colors::new(None, None, None));
for attribute in attributes { for attribute in attributes {
if let (Value::String(name), value) = attribute { if let (Value::String(name), value) = attribute {
@ -121,10 +120,28 @@ fn handle_hl_attr_define(hl_attr_define_arguments: &Vec<Value>, editor: &Arc<Mut
println!("Invalid attribute format"); println!("Invalid attribute format");
} }
} }
let mut editor = editor.lock().unwrap();
editor.define_style(id, style); editor.define_style(id, style);
} }
} }
fn handle_grid_scroll(grid_scroll_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [
Value::Integer(_grid_id), Value::Integer(top), Value::Integer(bot), Value::Integer(left),
Value::Integer(right), Value::Integer(rows), Value::Integer(cols)
] = grid_scroll_arguments.as_slice() {
let top = top.as_u64().unwrap() as isize;
let bot = bot.as_u64().unwrap() as isize;
let left = left.as_u64().unwrap() as isize;
let right = right.as_u64().unwrap() as isize;
let rows = rows.as_i64().unwrap() as isize;
let cols = cols.as_i64().unwrap() as isize;
let mut editor = editor.lock().unwrap();
editor.scroll_region(top, bot, left, right, rows, cols);
}
}
fn handle_redraw_event(event_value: Value, editor: &Arc<Mutex<Editor>>) { fn handle_redraw_event(event_value: Value, editor: &Arc<Mutex<Editor>>) {
match event_value { match event_value {
Value::Array(event_contents) => { Value::Array(event_contents) => {
@ -137,10 +154,11 @@ fn handle_redraw_event(event_value: Value, editor: &Arc<Mutex<Editor>>) {
"grid_resize" => println!("grid_resize event ignored"), "grid_resize" => println!("grid_resize event ignored"),
"default_colors_set" => handle_default_colors(arguments, editor), "default_colors_set" => handle_default_colors(arguments, editor),
"hl_attr_define" => handle_hl_attr_define(arguments, editor), "hl_attr_define" => handle_hl_attr_define(arguments, editor),
"hl_group_set" => println!("hl_group_set event ignored"),
"grid_line" => handle_grid_line(arguments, &editor), "grid_line" => handle_grid_line(arguments, &editor),
"grid_clear" => handle_clear(arguments, &editor), "grid_clear" => handle_clear(arguments, &editor),
"grid_cursor_goto" => handle_cursor_goto(arguments, &editor), "grid_cursor_goto" => handle_cursor_goto(arguments, &editor),
"hl_group_set" => println!("hl_group_set event ignored"), "grid_scroll" => handle_grid_scroll(arguments, &editor),
other => println!("Unhandled redraw command {}", other) other => println!("Unhandled redraw command {}", other)
} }
}, },

@ -5,14 +5,14 @@ use skulpin::{CoordinateSystem, CoordinateSystemHelper, RendererBuilder};
use skulpin::skia_safe::{Canvas, Color4f, Font, FontStyle, Point, Paint, Rect, Typeface}; use skulpin::skia_safe::{Canvas, Color4f, Font, FontStyle, Point, Paint, Rect, Typeface};
use skulpin::skia_safe::paint::Style; use skulpin::skia_safe::paint::Style;
use skulpin::skia_safe::matrix::ScaleToFit; use skulpin::skia_safe::matrix::ScaleToFit;
use skulpin::winit::dpi::LogicalSize; use skulpin::winit::dpi::{LogicalSize, LogicalPosition};
use skulpin::winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use skulpin::winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use skulpin::winit::event_loop::{ControlFlow, EventLoop}; use skulpin::winit::event_loop::{ControlFlow, EventLoop};
use skulpin::winit::window::WindowBuilder; use skulpin::winit::window::WindowBuilder;
use neovim_lib::NeovimApi; use neovim_lib::NeovimApi;
use crate::editor::{DrawCommand, Editor, Colors}; use crate::editor::{DrawCommand, Editor, Colors, CursorType};
use crate::keybindings::construct_keybinding_string; use crate::keybindings::construct_keybinding_string;
const FONT_NAME: &str = "Delugia Nerd Font"; const FONT_NAME: &str = "Delugia Nerd Font";
@ -24,19 +24,24 @@ const FONT_SIZE: f32 = 14.0;
fn draw( fn draw(
editor: &Arc<Mutex<Editor>>, editor: &Arc<Mutex<Editor>>,
canvas: &mut Canvas, canvas: &mut Canvas,
cursor_pos: &mut (f32, f32),
font: &Font, font: &Font,
font_width: f32, font_width: f32,
font_height: f32 font_height: f32
) { ) {
// let shaper = Shaper::new(None); // let shaper = Shaper::new(None);
// if let Some((blob, _)) = shaper.shape_text_blob("This is a test ~==", font, false, 10000.0, Point::default()) { // if let Some((blob, _)) = shaper.shape_text_blob("This is a test ~==", font, false, 10000.0, Point::default()) {
// canvas.draw_text_blob(&blob, (50, 50), &paint); // canvas.draw_text_blob(&blob, (50, 50), &paint);
// } // }
let (draw_commands, default_colors, cursor_pos) = { let (draw_commands, default_colors, cursor_grid_pos, cursor_type) = {
let editor = editor.lock().unwrap(); let editor = editor.lock().unwrap();
(editor.build_draw_commands().clone(), editor.default_colors.clone(), editor.cursor_pos.clone()) (
editor.build_draw_commands().clone(),
editor.default_colors.clone(),
editor.cursor_pos.clone(),
editor.cursor_type.clone()
)
}; };
canvas.clear(default_colors.background.clone().unwrap().to_color()); canvas.clear(default_colors.background.clone().unwrap().to_color());
@ -55,14 +60,37 @@ fn draw(
canvas.draw_str(&command.text, (x, y), &font, &foreground_paint); canvas.draw_str(&command.text, (x, y), &font, &foreground_paint);
} }
let (cursor_grid_x, cursor_grid_y) = cursor_pos; let (cursor_grid_x, cursor_grid_y) = cursor_grid_pos;
let cursor_x = cursor_grid_x as f32 * font_width; let target_cursor_x = cursor_grid_x as f32 * font_width;
let cursor_width = font_width / 8.0; let target_cursor_y = cursor_grid_y as f32 * font_height;
let cursor_y = cursor_grid_y as f32 * font_height; let (previous_cursor_x, previous_cursor_y) = cursor_pos;
let cursor_height = font_height;
let cursor_x = (target_cursor_x - *previous_cursor_x) * 0.05 + *previous_cursor_x;
let cursor_y = (target_cursor_y - *previous_cursor_y) * 0.05 + *previous_cursor_y;
*cursor_pos = (cursor_x, cursor_y);
let cursor_width = match cursor_type {
CursorType::Vertical => font_width / 8.0,
CursorType::Horizontal | CursorType::Block => font_width
};
let cursor_height = match cursor_type {
CursorType::Horizontal => font_width / 8.0,
CursorType::Vertical | CursorType::Block => font_height
};
let cursor = Rect::new(cursor_x, cursor_y, cursor_x + cursor_width, cursor_y + cursor_height); let cursor = Rect::new(cursor_x, cursor_y, cursor_x + cursor_width, cursor_y + cursor_height);
let cursor_paint = Paint::new(default_colors.foreground.unwrap(), None); let cursor_paint = Paint::new(default_colors.foreground.unwrap(), None);
canvas.draw_rect(cursor, &cursor_paint); canvas.draw_rect(cursor, &cursor_paint);
if let CursorType::Block = cursor_type {
let text_paint = Paint::new(default_colors.background.unwrap(), None);
let editor = editor.lock().unwrap();
let character = editor.grid[cursor_grid_y][cursor_grid_x].clone()
.map(|(character, _)| character)
.unwrap_or(' ');
let text_y = cursor_y + font_height - font_height * 0.2;
canvas.draw_str(character.to_string(), (cursor_x, text_y), &font, &text_paint);
}
} }
pub fn ui_loop(editor: Arc<Mutex<Editor>>) { pub fn ui_loop(editor: Arc<Mutex<Editor>>) {
@ -95,6 +123,8 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>) {
.build(&window) .build(&window)
.expect("Failed to create renderer"); .expect("Failed to create renderer");
let mut cursor_pos = (0.0, 0.0);
// icu::init(); // icu::init();
event_loop.run(move |event, _window_target, control_flow| { event_loop.run(move |event, _window_target, control_flow| {
@ -110,13 +140,13 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>) {
} => { } => {
if new_size.width > 0.0 && new_size.height > 0.0 { if new_size.width > 0.0 && new_size.height > 0.0 {
editor.lock().unwrap().resize( editor.lock().unwrap().resize(
(new_size.width as f32 / font_width) as u16, (new_size.width as f32 / font_width) as usize,
(new_size.height as f32 / font_height) as u16 (new_size.height as f32 / font_height) as usize
) )
} }
}, },
Event:: WindowEvent { Event::WindowEvent {
event: WindowEvent::KeyboardInput { event: WindowEvent::KeyboardInput {
input, input,
.. ..
@ -138,7 +168,7 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>) {
.. ..
} => { } => {
if let Err(e) = renderer.draw(&window, |canvas, _coordinate_system_helper| { if let Err(e) = renderer.draw(&window, |canvas, _coordinate_system_helper| {
draw(&editor, canvas, &font, font_width, font_height); draw(&editor, canvas, &mut cursor_pos, &font, font_width, font_height);
}) { }) {
println!("Error during draw: {:?}", e); println!("Error during draw: {:?}", e);
*control_flow = ControlFlow::Exit *control_flow = ControlFlow::Exit

Loading…
Cancel
Save