more progress toward externalized command bar

macos-click-through
Keith Simmons 5 years ago
parent 1a12766861
commit 22ddb545a9

@ -1,27 +1,101 @@
use crate::events::{GridLineCell, RedrawEvent, StyledContent}; use std::collections::HashMap;
use crate::events::{RedrawEvent, StyledContent};
use crate::editor::{DrawCommand, Style};
pub struct CommandLine { pub struct CommandLine {
visible: bool, visible: bool,
prefix: String, prefix: String,
content: StyledContent, content: StyledContent,
cursor_position: u64, cursor_position: u64,
special_char: (String, bool), special_char: Option<(String, bool)>,
block: Vec<StyledContent> block: Vec<StyledContent>
} }
impl CommandLine { impl CommandLine {
pub fn new() -> CommandLine {
CommandLine {
visible: false,
prefix: String::new(),
content: Vec::new(),
cursor_position: 0,
special_char: None,
block: Vec::new()
}
}
pub fn draw(&self, window_size: (u64, u64), defined_styles: &HashMap<u64, Style>) -> Vec<DrawCommand> {
let mut draw_commands = Vec::new();
if self.content.len() > 0 {
let (width, height) = window_size;
let text_length: usize = self.content.iter().map(|(_, text)| text.len()).sum();
let x = (width / 2) - (text_length as u64 / 2);
let y = height / 2;
let mut start_x = x;
let mut commands = self.content.iter().map(|(style_id, text)| {
let command_width = text.len();
let style = defined_styles.get(style_id).map(|style| style.clone());
let command = DrawCommand::new(text.clone(), (start_x, y), style);
start_x = start_x + command_width as u64;
command
}).collect::<Vec<DrawCommand>>();
draw_commands.append(&mut commands);
}
draw_commands
}
pub fn handle_command_events(&mut self, event: RedrawEvent) { pub fn handle_command_events(&mut self, event: RedrawEvent) {
match event { match event {
RedrawEvent::CommandLineShow { content, position, first_character, prompt, indent, level } => {}, RedrawEvent::CommandLineShow { content, position, first_character, prompt, indent, level } => self.show(content, position, first_character, prompt, indent, level),
RedrawEvent::CommandLinePosition { position, level } => {}, RedrawEvent::CommandLinePosition { position, level } => self.set_position(position, level),
RedrawEvent::CommandLineSpecialCharacter { character, shift, level } => {}, RedrawEvent::CommandLineSpecialCharacter { character, shift, level } => self.set_special_character(character, shift, level),
RedrawEvent::CommandLineHide => {}, RedrawEvent::CommandLineHide => self.hide(),
RedrawEvent::CommandLineBlockShow { lines } => {}, RedrawEvent::CommandLineBlockShow { lines } => self.show_block(lines),
RedrawEvent::CommandLineBlockAppend { line } => {}, RedrawEvent::CommandLineBlockAppend { line } => self.append_line_to_block(line),
RedrawEvent::CommandLineBlockHide => {} RedrawEvent::CommandLineBlockHide => self.hide_block(),
_ => {} _ => {}
} }
} }
// fn show() fn show(&mut self, content: StyledContent, position: u64, first_character: String, prompt: String, _indent: u64, _level: u64) {
let prefix;
if first_character.len() > 0 {
prefix = first_character;
} else {
prefix = prompt;
}
self.visible = true;
self.prefix = prefix;
self.content = content;
self.cursor_position = position;
self.block = Vec::new();
}
fn set_position(&mut self, position: u64, level: u64) {
self.cursor_position = position;
}
fn set_special_character(&mut self, character: String, shift: bool, _level: u64) {
self.special_char = Some((character, shift));
}
fn hide(&mut self) {
self.visible = false;
self.special_char = None;
}
fn show_block(&mut self, lines: Vec<StyledContent>) {
self.block = lines;
}
fn append_line_to_block(&mut self, line: StyledContent) {
self.block.push(line);
}
fn hide_block(&mut self) {
self.block.clear();
}
} }

@ -9,15 +9,18 @@ mod command_line;
pub use cursor::{Cursor, CursorShape, CursorMode}; pub use cursor::{Cursor, CursorShape, CursorMode};
pub use style::{Colors, Style}; pub use style::{Colors, Style};
use command_line::CommandLine;
use crate::events::{GridLineCell, RedrawEvent}; use crate::events::{GridLineCell, RedrawEvent};
pub type GridCell = Option<(char, Style)>; pub type GridCell = Option<(char, Option<Style>)>;
#[derive(new, Debug, Clone)] #[derive(new, Debug, Clone)]
pub struct DrawCommand { pub struct DrawCommand {
pub text: String, pub text: String,
pub grid_position: (u64, u64), pub grid_position: (u64, u64),
pub style: Style pub style: Option<Style>,
#[new(value = "1")]
pub scale: u16
} }
pub struct Editor { pub struct Editor {
@ -27,6 +30,7 @@ pub struct Editor {
pub window: Option<Arc<Window>>, pub window: Option<Arc<Window>>,
pub command_line: CommandLine,
pub title: String, pub title: String,
pub size: (u64, u64), pub size: (u64, u64),
pub cursor: Cursor, pub cursor: Cursor,
@ -44,6 +48,7 @@ impl Editor {
window: None, window: None,
command_line: CommandLine::new(),
title: "Neovide".to_string(), title: "Neovide".to_string(),
cursor: Cursor::new(), cursor: Cursor::new(),
size: (width, height), size: (width, height),
@ -71,7 +76,7 @@ impl Editor {
RedrawEvent::Clear { .. } => self.clear(), RedrawEvent::Clear { .. } => self.clear(),
RedrawEvent::CursorGoto { row, column, .. } => self.cursor.position = (row, column), RedrawEvent::CursorGoto { row, column, .. } => self.cursor.position = (row, column),
RedrawEvent::Scroll { top, bottom, left, right, rows, columns, .. } => self.scroll_region(top, bottom, left, right, rows, columns), RedrawEvent::Scroll { top, bottom, left, right, rows, columns, .. } => self.scroll_region(top, bottom, left, right, rows, columns),
_ => {} event => self.command_line.handle_command_events(event)
}; };
} }
@ -86,14 +91,14 @@ impl Editor {
} }
} }
fn command_matches(command: &Option<DrawCommand>, style: &Style) -> bool { fn command_matches(command: &Option<DrawCommand>, style: &Option<Style>) -> bool {
match command { match command {
Some(command) => &command.style == style, Some(command) => &command.style == style,
None => true None => true
} }
} }
fn add_character(command: &mut Option<DrawCommand>, character: &char, row_index: u64, col_index: u64, style: Style) { fn add_character(command: &mut Option<DrawCommand>, character: &char, 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(character.clone()),
None => { None => {
@ -118,7 +123,7 @@ impl Editor {
} }
let should_clear = self.should_clear; let should_clear = self.should_clear;
let draw_commands = draw_commands.into_iter().filter(|command| { let mut draw_commands = draw_commands.into_iter().filter(|command| {
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];
@ -130,6 +135,9 @@ impl Editor {
return false; return false;
}).collect::<Vec<DrawCommand>>(); }).collect::<Vec<DrawCommand>>();
let mut command_line_draw_commands = self.command_line.draw(self.size, &self.defined_styles);
draw_commands.append(&mut command_line_draw_commands);
let (width, height) = self.size; let (width, height) = self.size;
self.dirty = vec![vec![false; width as usize]; height as usize]; self.dirty = vec![vec![false; width as usize]; height as usize];
self.should_clear = false; self.should_clear = false;
@ -137,11 +145,10 @@ impl Editor {
} }
fn draw_grid_line_cell(&mut self, row_index: u64, column_pos: &mut u64, cell: GridLineCell) { fn draw_grid_line_cell(&mut self, row_index: u64, column_pos: &mut u64, cell: GridLineCell) {
let style = match (cell.highlight_id, self.previous_style.clone()) { let style = match cell.highlight_id {
(Some(0), _) => Style::new(self.default_colors.clone()), Some(0) => None,
(Some(style_id), _) => self.defined_styles.get(&style_id).expect("GridLineCell must use defined color").clone(), Some(style_id) => self.defined_styles.get(&style_id).map(|style| style.clone()),
(None, Some(previous_style)) => previous_style, None => self.previous_style.clone()
(None, None) => Style::new(self.default_colors.clone())
}; };
let mut text = cell.text; let mut text = cell.text;
@ -160,7 +167,7 @@ impl Editor {
} }
*column_pos = *column_pos + text.chars().count() as u64; *column_pos = *column_pos + text.chars().count() as u64;
self.previous_style = Some(style); self.previous_style = style;
} }
fn draw_grid_line(&mut self, row: u64, column_start: u64, cells: Vec<GridLineCell>) { fn draw_grid_line(&mut self, row: u64, column_start: u64, cells: Vec<GridLineCell>) {

@ -45,7 +45,7 @@ pub struct GridLineCell {
pub repeat: Option<u64> pub repeat: Option<u64>
} }
pub type StyledContent = Vec<(Style, String)>; pub type StyledContent = Vec<(u64, String)>;
#[derive(Debug)] #[derive(Debug)]
pub enum MessageKind { pub enum MessageKind {
@ -347,8 +347,8 @@ fn parse_grid_scroll(grid_scroll_arguments: Vec<Value>) -> Result<RedrawEvent> {
fn parse_styled_content(line: &Value) -> Result<StyledContent> { fn parse_styled_content(line: &Value) -> Result<StyledContent> {
parse_array(line)?.iter().map(|tuple| { parse_array(line)?.iter().map(|tuple| {
if let [attributes, text] = parse_array(tuple)?.as_slice() { if let [style_id, text] = parse_array(tuple)?.as_slice() {
Ok((parse_style(attributes)?, parse_string(text)?)) Ok((parse_u64(style_id)?, parse_string(text)?))
} else { } else {
Err(EventParseError::InvalidEventFormat) Err(EventParseError::InvalidEventFormat)
} }
@ -525,8 +525,6 @@ pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
if let Some(parsed_event) = possible_parsed_event { if let Some(parsed_event) = possible_parsed_event {
parsed_events.push(parsed_event); parsed_events.push(parsed_event);
} else {
println!("Did not parse {}", event_name);
} }
} }

@ -1,4 +1,4 @@
#![windows_subsystem = "windows"] // #![windows_subsystem = "windows"]
mod editor; mod editor;
mod events; mod events;
@ -47,7 +47,7 @@ fn start_nvim(editor: Arc<Mutex<Editor>>) -> Neovim {
let join_handle = session.take_dispatch_guard(); let join_handle = session.take_dispatch_guard();
let mut nvim = Neovim::new(session); let mut nvim = Neovim::new(session);
let mut options = UiAttachOptions::new(); let mut options = UiAttachOptions::new();
options.set_cmdline_external(false); options.set_cmdline_external(true);
options.set_messages_external(false); options.set_messages_external(false);
options.set_linegrid_external(true); options.set_linegrid_external(true);
options.set_rgb(true); options.set_rgb(true);

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use skulpin::CoordinateSystemHelper; use skulpin::CoordinateSystemHelper;
use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, Typeface, Font, FontStyle, colors}; use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, Typeface, Font, FontStyle, colors};
@ -51,12 +52,47 @@ impl Fonts {
} }
} }
struct FontLookup {
pub name: String,
pub base_size: f32,
pub loaded_fonts: HashMap<u16, Fonts>
}
impl FontLookup {
pub fn new(name: &str, base_size: f32) -> FontLookup {
let lookup = FontLookup {
name: name.to_string(),
base_size,
loaded_fonts: HashMap::new()
};
lookup.size(1);
lookup.size(2);
lookup.size(3);
lookup
}
fn size(&mut self, size_multiplier: u16) -> &Fonts {
match self.loaded_fonts.get(&size_multiplier) {
Some(fonts) => fonts,
None => {
let fonts = Fonts::new(
&self.name,
self.base_size * size_multiplier as f32);
self.loaded_fonts.insert(size_multiplier, fonts);
self.loaded_fonts.get(&size_multiplier).unwrap()
}
}
}
}
pub struct Renderer { pub struct Renderer {
editor: Arc<Mutex<Editor>>, editor: Arc<Mutex<Editor>>,
surface: Option<Surface>, surface: Option<Surface>,
paint: Paint, paint: Paint,
fonts: Fonts, fonts_lookup: FontLookup,
shaper: CachingShaper, shaper: CachingShaper,
pub font_width: f32, pub font_width: f32,
@ -69,37 +105,43 @@ impl Renderer {
let surface = None; 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);
let fonts = Fonts::new(FONT_NAME, FONT_SIZE); let fonts_lookup = FontLookup::new(FONT_NAME, FONT_SIZE);
let shaper = CachingShaper::new(); let shaper = CachingShaper::new();
let (_, bounds) = fonts.normal.measure_str("_", Some(&paint)); let base_fonts = fonts_lookup.size(1);
let (_, bounds) = base_fonts.normal.measure_str("_", Some(&paint));
let font_width = bounds.width(); let font_width = bounds.width();
let (_, metrics) = fonts.normal.metrics(); let (_, metrics) = base_fonts.normal.metrics();
let font_height = metrics.descent - metrics.ascent; let font_height = metrics.descent - metrics.ascent;
let cursor_pos = (0.0, 0.0); let cursor_pos = (0.0, 0.0);
Renderer { editor, surface, paint, fonts, shaper, font_width, font_height, cursor_pos } Renderer { editor, surface, paint, fonts_lookup, shaper, font_width, font_height, cursor_pos }
} }
fn draw_background(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), style: &Style, default_colors: &Colors) { fn draw_background(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), size: u16, style: &Option<Style>, default_colors: &Colors) {
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.chars().count() as f32 * self.font_width;
let height = self.font_height; let height = self.font_height;
let region = Rect::new(x, y, x + width, y + height); let region = Rect::new(x, y, x + width, y + height);
let style = style.clone().unwrap_or(Style::new(default_colors.clone()));
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);
} }
fn draw_foreground(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), style: &Style, default_colors: &Colors) { fn draw_foreground(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), size: u16, style: &Option<Style>, default_colors: &Colors) {
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.chars().count() as f32 * self.font_width;
let style = style.clone().unwrap_or(Style::new(default_colors.clone()));
if style.underline || style.undercurl { if style.underline || style.undercurl {
let (_, metrics) = self.fonts.get(style).metrics(); let (_, metrics) = self.fonts_lookup.size(scale).get(&style).metrics();
let line_position = metrics.underline_position().unwrap(); let line_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());
@ -109,7 +151,7 @@ impl Renderer {
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();
if text.len() > 0 { if text.len() > 0 {
let blob = self.shaper.shape_cached(text.to_string(), self.fonts.get(style)); let blob = self.shaper.shape_cached(text.to_string(), self.fonts.get(&style));
canvas.draw_text_blob(blob, (x, y), &self.paint); canvas.draw_text_blob(blob, (x, y), &self.paint);
} }
} }

@ -30,6 +30,8 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>, nvim: Neovim, initial_size: (u64, u64
.expect("Failed to create window")); .expect("Failed to create window"));
let mut skulpin_renderer = RendererBuilder::new() let mut skulpin_renderer = RendererBuilder::new()
.prefer_integrated_gpu()
.use_vulkan_debug_layer(true)
.coordinate_system(CoordinateSystem::Logical) .coordinate_system(CoordinateSystem::Logical)
.build(&window) .build(&window)
.expect("Failed to create renderer"); .expect("Failed to create renderer");

Loading…
Cancel
Save