mirror of https://github.com/sgoudham/neovide.git
somewhat working with druid
commit
6ca852386f
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "rust_druid_test"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["keith <keith@the-simmons.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
itertools = "0.8.2"
|
||||||
|
druid-shell = { git = "https://github.com/xi-editor/druid.git", version = "0.4" }
|
||||||
|
derive-new = "0.5"
|
||||||
|
derivative = "1.0.3"
|
||||||
|
env_logger = "0.7.1"
|
||||||
|
neovim-lib = { git = "https://github.com/daa84/neovim-lib", version = "0.6" }
|
||||||
|
rmpv = "0.4.2"
|
@ -0,0 +1,51 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use druid_shell::piet::PietTextLayout;
|
||||||
|
|
||||||
|
use neovim_lib::Neovim;
|
||||||
|
|
||||||
|
#[derive(Derivative, new)]
|
||||||
|
#[derivative(PartialEq)]
|
||||||
|
pub struct DrawCommand {
|
||||||
|
pub text: String,
|
||||||
|
pub row: u64,
|
||||||
|
pub col_start: u64,
|
||||||
|
#[new(default)]
|
||||||
|
#[derivative(PartialEq="ignore")]
|
||||||
|
pub layout: Mutex<Option<PietTextLayout>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Editor {
|
||||||
|
pub nvim: Neovim,
|
||||||
|
pub draw_commands: Vec<Vec<Arc<Option<DrawCommand>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Editor {
|
||||||
|
pub fn new(nvim: Neovim, width: usize, height: usize) -> Editor {
|
||||||
|
let mut draw_commands = Vec::with_capacity(height);
|
||||||
|
for _ in 0..width {
|
||||||
|
draw_commands.push(vec![Arc::new(None); width]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Editor {
|
||||||
|
nvim,
|
||||||
|
draw_commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self, command: DrawCommand) {
|
||||||
|
let length = command.text.chars().count();
|
||||||
|
let row_index = command.row as usize;
|
||||||
|
let col_start = command.col_start as usize;
|
||||||
|
let pointer = Arc::new(Some(command));
|
||||||
|
|
||||||
|
let row = self.draw_commands.get_mut(row_index).expect("Draw command out of bounds");
|
||||||
|
|
||||||
|
for x in 0..length {
|
||||||
|
let pointer_index = x + col_start;
|
||||||
|
if pointer_index < row.len() {
|
||||||
|
row[pointer_index] = pointer.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
mod editor;
|
||||||
|
mod window;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate derive_new;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate derivative;
|
||||||
|
|
||||||
|
use std::sync::mpsc::Receiver;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use env_logger::Env as LoggerEnv;
|
||||||
|
use neovim_lib::{Neovim, UiAttachOptions, Session};
|
||||||
|
use rmpv::Value;
|
||||||
|
|
||||||
|
use window::ui_loop;
|
||||||
|
use editor::{Editor, DrawCommand};
|
||||||
|
|
||||||
|
fn handle_grid_line(grid_line_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
|
||||||
|
match grid_line_arguments.as_slice() {
|
||||||
|
[Value::Integer(grid_id), Value::Integer(row), Value::Integer(col_start), Value::Array(cells)] => {
|
||||||
|
let mut col_pos = col_start.as_u64().unwrap();
|
||||||
|
for cell in cells.into_iter() {
|
||||||
|
match cell {
|
||||||
|
Value::Array(cell_data) => {
|
||||||
|
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(),
|
||||||
|
_ => panic!("Cell text was not a string")
|
||||||
|
};
|
||||||
|
match cell_data.get(2) {
|
||||||
|
Some(Value::Integer(repeat_count)) => {
|
||||||
|
text = text.repeat(repeat_count.as_u64().unwrap_or(1) as usize);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut editor = editor.lock().unwrap();
|
||||||
|
editor.draw(DrawCommand::new(text.to_string(), row.as_u64().unwrap(), col_pos));
|
||||||
|
}
|
||||||
|
col_pos = col_pos + text.chars().count() as u64;
|
||||||
|
},
|
||||||
|
_ => println!("Invalid cell shape")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => println!("Invalid grid_line format")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_redraw_event(event_value: Value, editor: &Arc<Mutex<Editor>>) {
|
||||||
|
match event_value {
|
||||||
|
Value::Array(event_contents) => {
|
||||||
|
let name_value = &event_contents[0];
|
||||||
|
let arguments_value = &event_contents[1];
|
||||||
|
match (name_value, arguments_value) {
|
||||||
|
(Value::String(event_name), Value::Array(arguments)) => {
|
||||||
|
match event_name.as_str().expect("Invalid redraw command name format.") {
|
||||||
|
"grid_line" => handle_grid_line(arguments, &editor),
|
||||||
|
other => println!("Unhandled redraw command {}", other)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
println!("Unrecognized redraw event structure.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => println!("Event is not an array...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nvim_event_loop(receiver: Receiver<(String, Vec<Value>)>, editor: &Arc<Mutex<Editor>>) {
|
||||||
|
println!("UI thread spawned");
|
||||||
|
loop {
|
||||||
|
let (event_name, event_args) = receiver.recv().expect("Could not receive event.");
|
||||||
|
match event_name.as_ref() {
|
||||||
|
"redraw" => {
|
||||||
|
for event in event_args {
|
||||||
|
handle_redraw_event(event, &editor);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => println!("Unrecognized Event: {}", event_name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::from_env(LoggerEnv::default().default_filter_or("warn")).init();
|
||||||
|
|
||||||
|
let mut session = Session::new_child().unwrap();
|
||||||
|
let receiver = session.start_event_loop_channel();
|
||||||
|
let mut nvim = Neovim::new(session);
|
||||||
|
let mut options = UiAttachOptions::new();
|
||||||
|
options.set_cmdline_external(false);
|
||||||
|
options.set_messages_external(false);
|
||||||
|
options.set_linegrid_external(true);
|
||||||
|
options.set_rgb(true);
|
||||||
|
nvim.ui_attach(100, 50, &options).unwrap();
|
||||||
|
|
||||||
|
let editor = Arc::new(Mutex::new(Editor::new(nvim, 100, 50)));
|
||||||
|
|
||||||
|
let nvim_editor = editor.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
nvim_event_loop(receiver, &nvim_editor);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui_loop(editor);
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::any::Any;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use druid_shell::{Application, WinHandler, WindowHandle, WinCtx, KeyEvent, WindowBuilder, RunLoop, HotKey, KeyCode};
|
||||||
|
use druid_shell::piet::{
|
||||||
|
Piet, PietFont, Color, FontBuilder, RenderContext, Text, TextLayoutBuilder
|
||||||
|
};
|
||||||
|
use druid_shell::kurbo::Rect;
|
||||||
|
use neovim_lib::NeovimApi;
|
||||||
|
|
||||||
|
use crate::editor::{DrawCommand, Editor};
|
||||||
|
|
||||||
|
#[derive(new)]
|
||||||
|
struct WindowState {
|
||||||
|
editor: Arc<Mutex<Editor>>,
|
||||||
|
|
||||||
|
#[new(default)]
|
||||||
|
pub size: (f64, f64),
|
||||||
|
#[new(default)]
|
||||||
|
pub handle: WindowHandle,
|
||||||
|
#[new(default)]
|
||||||
|
pub font: Option<PietFont>
|
||||||
|
}
|
||||||
|
|
||||||
|
const FONT_NAME: &str = "Delugia Nerd Font";
|
||||||
|
const FONT_SIZE: f64 = 14.0;
|
||||||
|
const FONT_WIDTH: f64 = 10.0;
|
||||||
|
const FONT_HEIGHT: f64 = 20.0;
|
||||||
|
|
||||||
|
fn process_draw_commands(draw_commands: &Vec<Vec<Arc<Option<DrawCommand>>>>, piet: &mut Piet, font: &PietFont) {
|
||||||
|
for row in draw_commands {
|
||||||
|
for possible_command in row.iter().dedup() {
|
||||||
|
if let Some(ref command) = **possible_command {
|
||||||
|
let x = command.col_start as f64 * FONT_WIDTH;
|
||||||
|
let y = command.row as f64 * FONT_HEIGHT + FONT_HEIGHT;
|
||||||
|
let top = y - FONT_HEIGHT * 0.8;
|
||||||
|
let top_left = (x, top);
|
||||||
|
let width = command.text.chars().count() as f64 * FONT_WIDTH;
|
||||||
|
let height = FONT_HEIGHT;
|
||||||
|
let bottom_right = (x + width, top + height);
|
||||||
|
let region = Rect::from_points(top_left, bottom_right);
|
||||||
|
piet.fill(region, &Color::rgb8(0x11, 0x11, 0x11));
|
||||||
|
|
||||||
|
let layout_ref = command.layout.lock().unwrap();
|
||||||
|
|
||||||
|
let owned;
|
||||||
|
let text_layout = if layout_ref.is_some() {
|
||||||
|
layout_ref.as_ref().unwrap()
|
||||||
|
} else {
|
||||||
|
let piet_text = piet.text();
|
||||||
|
owned = piet_text.new_text_layout(&font, &command.text).build().unwrap();
|
||||||
|
&owned
|
||||||
|
};
|
||||||
|
piet.draw_text(&text_layout, (x, y), &Color::rgb8(0xff, 0xff, 0xff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WinHandler for WindowState {
|
||||||
|
fn connect(&mut self, handle: &WindowHandle) {
|
||||||
|
self.handle = handle.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self, piet: &mut Piet, _ctx: &mut dyn WinCtx) -> bool {
|
||||||
|
let text = piet.text();
|
||||||
|
if self.font.is_none() {
|
||||||
|
self.font = Some(text.new_font_by_name(FONT_NAME, FONT_SIZE).build().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let font = self.font.as_ref().unwrap();
|
||||||
|
|
||||||
|
piet.clear(Color::rgb8(0x00, 0x00, 0x00));
|
||||||
|
let editor = self.editor.lock().unwrap();
|
||||||
|
process_draw_commands(&editor.draw_commands, piet, font);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_down(&mut self, key_event: KeyEvent, _ctx: &mut dyn WinCtx) -> bool {
|
||||||
|
let mut editor = self.editor.lock().unwrap();
|
||||||
|
match key_event {
|
||||||
|
k_e if k_e.key_code.is_printable() => {
|
||||||
|
let incoming_text = k_e.unmod_text().unwrap_or("");
|
||||||
|
editor.nvim.input(incoming_text).expect("Input call failed...");
|
||||||
|
},
|
||||||
|
k_e if (HotKey::new(None, KeyCode::Escape)).matches(k_e) => {
|
||||||
|
editor.nvim.input("<Esc>").expect("Input call failed...");
|
||||||
|
},
|
||||||
|
k_e if (HotKey::new(None, KeyCode::Backspace)).matches(k_e) => {
|
||||||
|
editor.nvim.input("<BS>").expect("Input call failed...");
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
};
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self, width: u32, height: u32, _ctx: &mut dyn WinCtx) {
|
||||||
|
let dpi = self.handle.get_dpi();
|
||||||
|
let dpi_scale = dpi as f64 / 96.0;
|
||||||
|
let width_f = (width as f64) / dpi_scale;
|
||||||
|
let height_f = (height as f64) / dpi_scale;
|
||||||
|
self.size = (width_f, height_f);
|
||||||
|
|
||||||
|
let mut editor = self.editor.lock().unwrap();
|
||||||
|
editor.nvim.ui_try_resize((width_f / FONT_WIDTH) as i64, (height_f / FONT_HEIGHT) as i64).expect("Resize failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui_loop(editor: Arc<Mutex<Editor>>) {
|
||||||
|
let window_state = WindowState::new(editor);
|
||||||
|
|
||||||
|
Application::init();
|
||||||
|
let mut run_loop = RunLoop::new();
|
||||||
|
let mut builder = WindowBuilder::new();
|
||||||
|
builder.set_handler(Box::new(window_state));
|
||||||
|
builder.set_title("Nvim editor");
|
||||||
|
let window = builder.build().unwrap();
|
||||||
|
window.show();
|
||||||
|
run_loop.run();
|
||||||
|
}
|
Loading…
Reference in New Issue