mirror of https://github.com/sgoudham/neovide.git
commit
2815a28e68
@ -1,54 +1,72 @@
|
|||||||
use nvim_rs::Neovim;
|
use log::trace;
|
||||||
use nvim_rs::compat::tokio::Compat;
|
use nvim_rs::compat::tokio::Compat;
|
||||||
|
use nvim_rs::Neovim;
|
||||||
use tokio::process::ChildStdin;
|
use tokio::process::ChildStdin;
|
||||||
use log::trace;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum UiCommand {
|
pub enum UiCommand {
|
||||||
Resize { width: u32, height: u32 },
|
Resize {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
},
|
||||||
Keyboard(String),
|
Keyboard(String),
|
||||||
MouseButton { action: String, position: (u32, u32) },
|
MouseButton {
|
||||||
Scroll { direction: String, position: (u32, u32) },
|
action: String,
|
||||||
|
position: (u32, u32),
|
||||||
|
},
|
||||||
|
Scroll {
|
||||||
|
direction: String,
|
||||||
|
position: (u32, u32),
|
||||||
|
},
|
||||||
Drag(u32, u32),
|
Drag(u32, u32),
|
||||||
FocusLost,
|
FocusLost,
|
||||||
FocusGained
|
FocusGained,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiCommand {
|
impl UiCommand {
|
||||||
pub async fn execute(self, nvim: &Neovim<Compat<ChildStdin>>) {
|
pub async fn execute(self, nvim: &Neovim<Compat<ChildStdin>>) {
|
||||||
match self {
|
match self {
|
||||||
UiCommand::Resize { width, height } =>
|
UiCommand::Resize { width, height } => nvim
|
||||||
nvim.ui_try_resize(width.max(10) as i64, height.max(3) as i64).await
|
.ui_try_resize(width.max(10) as i64, height.max(3) as i64)
|
||||||
.expect("Resize failed"),
|
.await
|
||||||
UiCommand::Keyboard(input_command) => {
|
.expect("Resize failed"),
|
||||||
|
UiCommand::Keyboard(input_command) => {
|
||||||
trace!("Keyboard Input Sent: {}", input_command);
|
trace!("Keyboard Input Sent: {}", input_command);
|
||||||
nvim.input(&input_command).await
|
nvim.input(&input_command).await.expect("Input failed");
|
||||||
.expect("Input failed");
|
}
|
||||||
},
|
UiCommand::MouseButton {
|
||||||
UiCommand::MouseButton { action, position: (grid_x, grid_y) } =>
|
action,
|
||||||
nvim.input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64).await
|
position: (grid_x, grid_y),
|
||||||
.expect("Mouse Input Failed"),
|
} => nvim
|
||||||
UiCommand::Scroll { direction, position: (grid_x, grid_y) } =>
|
.input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64)
|
||||||
nvim.input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64).await
|
.await
|
||||||
.expect("Mouse Scroll Failed"),
|
.expect("Mouse Input Failed"),
|
||||||
UiCommand::Drag(grid_x, grid_y) =>
|
UiCommand::Scroll {
|
||||||
nvim.input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64).await
|
direction,
|
||||||
.expect("Mouse Drag Failed"),
|
position: (grid_x, grid_y),
|
||||||
UiCommand::FocusLost => {
|
} => nvim
|
||||||
nvim.command("if exists('#FocusLost') | doautocmd <nomodeline> FocusLost | endif").await
|
.input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64)
|
||||||
.expect("Focus Lost Failed")
|
.await
|
||||||
},
|
.expect("Mouse Scroll Failed"),
|
||||||
UiCommand::FocusGained => {
|
UiCommand::Drag(grid_x, grid_y) => nvim
|
||||||
nvim.command("if exists('#FocusGained') | doautocmd <nomodeline> FocusGained | endif").await
|
.input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64)
|
||||||
.expect("Focus Gained Failed")
|
.await
|
||||||
},
|
.expect("Mouse Drag Failed"),
|
||||||
|
UiCommand::FocusLost => nvim
|
||||||
|
.command("if exists('#FocusLost') | doautocmd <nomodeline> FocusLost | endif")
|
||||||
|
.await
|
||||||
|
.expect("Focus Lost Failed"),
|
||||||
|
UiCommand::FocusGained => nvim
|
||||||
|
.command("if exists('#FocusGained') | doautocmd <nomodeline> FocusGained | endif")
|
||||||
|
.await
|
||||||
|
.expect("Focus Gained Failed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_resize(&self) -> bool {
|
pub fn is_resize(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
UiCommand::Resize { .. } => true,
|
UiCommand::Resize { .. } => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,98 +1,112 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use skulpin::skia_safe::Color4f;
|
use skulpin::skia_safe::Color4f;
|
||||||
|
|
||||||
use super::style::{Style, Colors};
|
use super::style::{Colors, Style};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum CursorShape {
|
pub enum CursorShape {
|
||||||
Block,
|
Block,
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical
|
Vertical,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CursorShape {
|
impl CursorShape {
|
||||||
pub fn from_type_name(name: &str) -> Option<CursorShape> {
|
pub fn from_type_name(name: &str) -> Option<CursorShape> {
|
||||||
match name {
|
match name {
|
||||||
"block" => Some(CursorShape::Block),
|
"block" => Some(CursorShape::Block),
|
||||||
"horizontal" => Some(CursorShape::Horizontal),
|
"horizontal" => Some(CursorShape::Horizontal),
|
||||||
"vertical" => Some(CursorShape::Vertical),
|
"vertical" => Some(CursorShape::Vertical),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq)]
|
#[derive(Default, Debug, Clone, PartialEq)]
|
||||||
pub struct CursorMode {
|
pub struct CursorMode {
|
||||||
pub shape: Option<CursorShape>,
|
pub shape: Option<CursorShape>,
|
||||||
pub style_id: Option<u64>,
|
pub style_id: Option<u64>,
|
||||||
pub cell_percentage: Option<f32>,
|
pub cell_percentage: Option<f32>,
|
||||||
pub blinkwait: Option<u64>,
|
pub blinkwait: Option<u64>,
|
||||||
pub blinkon: Option<u64>,
|
pub blinkon: Option<u64>,
|
||||||
pub blinkoff: Option<u64>,
|
pub blinkoff: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
pub position: (u64, u64),
|
pub position: (u64, u64),
|
||||||
pub shape: CursorShape,
|
pub shape: CursorShape,
|
||||||
pub cell_percentage: Option<f32>,
|
pub cell_percentage: Option<f32>,
|
||||||
pub blinkwait: Option<u64>,
|
pub blinkwait: Option<u64>,
|
||||||
pub blinkon: Option<u64>,
|
pub blinkon: Option<u64>,
|
||||||
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 mode_list: Vec<CursorMode>
|
pub mode_list: Vec<CursorMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor {
|
impl Cursor {
|
||||||
pub fn new() -> Cursor {
|
pub fn new() -> Cursor {
|
||||||
Cursor {
|
Cursor {
|
||||||
position: (0, 0),
|
position: (0, 0),
|
||||||
shape: CursorShape::Block,
|
shape: CursorShape::Block,
|
||||||
style: None,
|
style: None,
|
||||||
cell_percentage: None,
|
cell_percentage: None,
|
||||||
blinkwait: None,
|
blinkwait: None,
|
||||||
blinkon: None,
|
blinkon: None,
|
||||||
blinkoff: None,
|
blinkoff: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
mode_list: Vec::new()
|
mode_list: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
|
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
|
||||||
if let Some(style) = &self.style {
|
if let Some(style) = &self.style {
|
||||||
style.colors.foreground.clone().unwrap_or_else(||default_colors.background.clone().unwrap())
|
style
|
||||||
} else {
|
.colors
|
||||||
default_colors.background.clone().unwrap()
|
.foreground
|
||||||
}
|
.clone()
|
||||||
}
|
.unwrap_or_else(|| default_colors.background.clone().unwrap())
|
||||||
|
} else {
|
||||||
pub fn background(&self, default_colors: &Colors) -> Color4f {
|
default_colors.background.clone().unwrap()
|
||||||
if let Some(style) = &self.style {
|
}
|
||||||
style.colors.background.clone().unwrap_or_else(||default_colors.foreground.clone().unwrap())
|
}
|
||||||
} else {
|
|
||||||
default_colors.foreground.clone().unwrap()
|
pub fn background(&self, default_colors: &Colors) -> Color4f {
|
||||||
}
|
if let Some(style) = &self.style {
|
||||||
}
|
style
|
||||||
|
.colors
|
||||||
pub fn change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Arc<Style>>) {
|
.background
|
||||||
if let Some(CursorMode { shape, style_id, cell_percentage, blinkwait, blinkon, blinkoff }) = self.mode_list.get(mode_index as usize) {
|
.clone()
|
||||||
if let Some(shape) = shape {
|
.unwrap_or_else(|| default_colors.foreground.clone().unwrap())
|
||||||
self.shape = shape.clone();
|
} else {
|
||||||
}
|
default_colors.foreground.clone().unwrap()
|
||||||
|
}
|
||||||
if let Some(style_id) = style_id {
|
}
|
||||||
self.style = styles
|
|
||||||
.get(style_id)
|
pub fn change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Arc<Style>>) {
|
||||||
.cloned();
|
if let Some(CursorMode {
|
||||||
}
|
shape,
|
||||||
|
style_id,
|
||||||
self.cell_percentage = *cell_percentage;
|
cell_percentage,
|
||||||
self.blinkwait = *blinkwait;
|
blinkwait,
|
||||||
self.blinkon = *blinkon;
|
blinkon,
|
||||||
self.blinkoff = *blinkoff;
|
blinkoff,
|
||||||
}
|
}) = self.mode_list.get(mode_index as usize)
|
||||||
}
|
{
|
||||||
}
|
if let Some(shape) = shape {
|
||||||
|
self.shape = shape.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(style_id) = style_id {
|
||||||
|
self.style = styles.get(style_id).cloned();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cell_percentage = *cell_percentage;
|
||||||
|
self.blinkwait = *blinkwait;
|
||||||
|
self.blinkon = *blinkon;
|
||||||
|
self.blinkoff = *blinkoff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,49 +1,64 @@
|
|||||||
use skulpin::skia_safe::Color4f;
|
use skulpin::skia_safe::Color4f;
|
||||||
|
|
||||||
#[derive(new, PartialEq, Debug, Clone)]
|
#[derive(new, PartialEq, Debug, Clone)]
|
||||||
pub struct Colors {
|
pub struct Colors {
|
||||||
pub foreground: Option<Color4f>,
|
pub foreground: Option<Color4f>,
|
||||||
pub background: Option<Color4f>,
|
pub background: Option<Color4f>,
|
||||||
pub special: Option<Color4f>
|
pub special: Option<Color4f>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(new, Debug, Clone, PartialEq)]
|
#[derive(new, Debug, Clone, PartialEq)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
pub colors: Colors,
|
pub colors: Colors,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub reverse: bool,
|
pub reverse: bool,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub italic: bool,
|
pub italic: bool,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub bold: bool,
|
pub bold: bool,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub strikethrough: bool,
|
pub strikethrough: bool,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub underline: bool,
|
pub underline: bool,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub undercurl: bool,
|
pub undercurl: bool,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub blend: u8
|
pub blend: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
|
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
|
||||||
if self.reverse {
|
if self.reverse {
|
||||||
self.colors.background.clone().unwrap_or_else(||default_colors.background.clone().unwrap())
|
self.colors
|
||||||
} else {
|
.background
|
||||||
self.colors.foreground.clone().unwrap_or_else(||default_colors.foreground.clone().unwrap())
|
.clone()
|
||||||
}
|
.unwrap_or_else(|| default_colors.background.clone().unwrap())
|
||||||
}
|
} else {
|
||||||
|
self.colors
|
||||||
pub fn background(&self, default_colors: &Colors) -> Color4f {
|
.foreground
|
||||||
if self.reverse {
|
.clone()
|
||||||
self.colors.foreground.clone().unwrap_or_else(||default_colors.foreground.clone().unwrap())
|
.unwrap_or_else(|| default_colors.foreground.clone().unwrap())
|
||||||
} else {
|
}
|
||||||
self.colors.background.clone().unwrap_or_else(||default_colors.background.clone().unwrap())
|
}
|
||||||
}
|
|
||||||
}
|
pub fn background(&self, default_colors: &Colors) -> Color4f {
|
||||||
|
if self.reverse {
|
||||||
pub fn special(&self, default_colors: &Colors) -> Color4f {
|
self.colors
|
||||||
self.colors.special.clone().unwrap_or_else(||default_colors.special.clone().unwrap())
|
.foreground
|
||||||
}
|
.clone()
|
||||||
}
|
.unwrap_or_else(|| default_colors.foreground.clone().unwrap())
|
||||||
|
} else {
|
||||||
|
self.colors
|
||||||
|
.background
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| default_colors.background.clone().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn special(&self, default_colors: &Colors) -> Color4f {
|
||||||
|
self.colors
|
||||||
|
.special
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| default_colors.special.clone().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
fn show_error(explanation: &str) -> ! {
|
fn show_error(explanation: &str) -> ! {
|
||||||
error!("{}", explanation);
|
error!("{}", explanation);
|
||||||
panic!(explanation.to_string());
|
panic!(explanation.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ResultPanicExplanation<T, E: ToString> {
|
pub trait ResultPanicExplanation<T, E: ToString> {
|
||||||
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
|
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> {
|
impl<T, E: ToString> ResultPanicExplanation<T, E> for Result<T, E> {
|
||||||
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
|
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
|
||||||
match self {
|
match self {
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let explanation = format!("{}: {}", explanation, error.to_string());
|
let explanation = format!("{}: {}", explanation, error.to_string());
|
||||||
show_error(&explanation);
|
show_error(&explanation);
|
||||||
},
|
}
|
||||||
Ok(content) => content
|
Ok(content) => content,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait OptionPanicExplanation<T> {
|
pub trait OptionPanicExplanation<T> {
|
||||||
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
|
fn unwrap_or_explained_panic(self, explanation: &str) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OptionPanicExplanation<T> for Option<T> {
|
impl<T> OptionPanicExplanation<T> for Option<T> {
|
||||||
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
|
fn unwrap_or_explained_panic(self, explanation: &str) -> T {
|
||||||
match self {
|
match self {
|
||||||
None => {
|
None => {
|
||||||
show_error(explanation);
|
show_error(explanation);
|
||||||
},
|
}
|
||||||
Some(content) => content
|
Some(content) => content,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,83 +1,87 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::atomic::{AtomicU16, Ordering};
|
||||||
use std::sync::atomic::{AtomicU16, Ordering};
|
use std::sync::Mutex;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use crate::settings::*;
|
use crate::settings::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new();
|
pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct RedrawSettings {
|
struct RedrawSettings {
|
||||||
extra_buffer_frames: u64,
|
extra_buffer_frames: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize_settings() {
|
pub fn initialize_settings() {
|
||||||
|
let buffer_frames = if SETTINGS
|
||||||
let buffer_frames = if SETTINGS.neovim_arguments.contains(&String::from("--extraBufferFrames")) {
|
.neovim_arguments
|
||||||
60
|
.contains(&String::from("--extraBufferFrames"))
|
||||||
}else{
|
{
|
||||||
1
|
60
|
||||||
};
|
} else {
|
||||||
|
1
|
||||||
SETTINGS.set(&RedrawSettings {
|
};
|
||||||
extra_buffer_frames: buffer_frames,
|
|
||||||
});
|
SETTINGS.set(&RedrawSettings {
|
||||||
|
extra_buffer_frames: buffer_frames,
|
||||||
register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames);
|
});
|
||||||
}
|
|
||||||
|
register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames);
|
||||||
pub struct RedrawScheduler {
|
}
|
||||||
frames_queued: AtomicU16,
|
|
||||||
scheduled_frame: Mutex<Option<Instant>>
|
pub struct RedrawScheduler {
|
||||||
}
|
frames_queued: AtomicU16,
|
||||||
|
scheduled_frame: Mutex<Option<Instant>>,
|
||||||
impl RedrawScheduler {
|
}
|
||||||
pub fn new() -> RedrawScheduler {
|
|
||||||
RedrawScheduler {
|
impl RedrawScheduler {
|
||||||
frames_queued: AtomicU16::new(1),
|
pub fn new() -> RedrawScheduler {
|
||||||
scheduled_frame: Mutex::new(None)
|
RedrawScheduler {
|
||||||
}
|
frames_queued: AtomicU16::new(1),
|
||||||
}
|
scheduled_frame: Mutex::new(None),
|
||||||
|
}
|
||||||
pub fn schedule(&self, new_scheduled: Instant) {
|
}
|
||||||
trace!("Redraw scheduled for {:?}", new_scheduled);
|
|
||||||
let mut scheduled_frame = self.scheduled_frame.lock().unwrap();
|
pub fn schedule(&self, new_scheduled: Instant) {
|
||||||
if let Some(previous_scheduled) = *scheduled_frame {
|
trace!("Redraw scheduled for {:?}", new_scheduled);
|
||||||
if new_scheduled < previous_scheduled {
|
let mut scheduled_frame = self.scheduled_frame.lock().unwrap();
|
||||||
*scheduled_frame = Some(new_scheduled);
|
if let Some(previous_scheduled) = *scheduled_frame {
|
||||||
}
|
if new_scheduled < previous_scheduled {
|
||||||
} else {
|
*scheduled_frame = Some(new_scheduled);
|
||||||
*scheduled_frame = Some(new_scheduled);
|
}
|
||||||
}
|
} else {
|
||||||
}
|
*scheduled_frame = Some(new_scheduled);
|
||||||
|
}
|
||||||
pub fn queue_next_frame(&self) {
|
}
|
||||||
trace!("Next frame queued");
|
|
||||||
let buffer_frames = SETTINGS.get::<RedrawSettings>().extra_buffer_frames;
|
pub fn queue_next_frame(&self) {
|
||||||
self.frames_queued.store(buffer_frames as u16, Ordering::Relaxed);
|
trace!("Next frame queued");
|
||||||
}
|
let buffer_frames = SETTINGS.get::<RedrawSettings>().extra_buffer_frames;
|
||||||
|
self.frames_queued
|
||||||
pub fn should_draw(&self) -> bool {
|
.store(buffer_frames as u16, Ordering::Relaxed);
|
||||||
let frames_queued = self.frames_queued.load(Ordering::Relaxed);
|
}
|
||||||
if frames_queued > 0 {
|
|
||||||
self.frames_queued.store(frames_queued - 1, Ordering::Relaxed);
|
pub fn should_draw(&self) -> bool {
|
||||||
true
|
let frames_queued = self.frames_queued.load(Ordering::Relaxed);
|
||||||
} else {
|
if frames_queued > 0 {
|
||||||
let mut next_scheduled_frame = self.scheduled_frame.lock().unwrap();
|
self.frames_queued
|
||||||
if let Some(scheduled_frame) = *next_scheduled_frame {
|
.store(frames_queued - 1, Ordering::Relaxed);
|
||||||
if scheduled_frame < Instant::now() {
|
true
|
||||||
*next_scheduled_frame = None;
|
} else {
|
||||||
true
|
let mut next_scheduled_frame = self.scheduled_frame.lock().unwrap();
|
||||||
} else {
|
if let Some(scheduled_frame) = *next_scheduled_frame {
|
||||||
false
|
if scheduled_frame < Instant::now() {
|
||||||
}
|
*next_scheduled_frame = None;
|
||||||
} else {
|
true
|
||||||
false
|
} else {
|
||||||
}
|
false
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
}
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,322 +1,362 @@
|
|||||||
mod animation_utils;
|
mod animation_utils;
|
||||||
mod cursor_vfx;
|
mod blink;
|
||||||
mod blink;
|
mod cursor_vfx;
|
||||||
|
|
||||||
use skulpin::skia_safe::{Canvas, Paint, Path, Point};
|
use skulpin::skia_safe::{Canvas, Paint, Path, Point};
|
||||||
|
|
||||||
use crate::settings::*;
|
use crate::editor::{Colors, Cursor, CursorShape, EDITOR};
|
||||||
use crate::renderer::CachingShaper;
|
use crate::redraw_scheduler::REDRAW_SCHEDULER;
|
||||||
use crate::editor::{EDITOR, Colors, Cursor, CursorShape};
|
use crate::renderer::CachingShaper;
|
||||||
use crate::redraw_scheduler::REDRAW_SCHEDULER;
|
use crate::settings::*;
|
||||||
|
|
||||||
use animation_utils::*;
|
use animation_utils::*;
|
||||||
use blink::*;
|
use blink::*;
|
||||||
|
|
||||||
|
const COMMAND_LINE_DELAY_FRAMES: u64 = 5;
|
||||||
const COMMAND_LINE_DELAY_FRAMES: u64 = 5;
|
const DEFAULT_CELL_PERCENTAGE: f32 = 1.0 / 8.0;
|
||||||
const DEFAULT_CELL_PERCENTAGE: f32 = 1.0 / 8.0;
|
|
||||||
|
const STANDARD_CORNERS: &[(f32, f32); 4] = &[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)];
|
||||||
const STANDARD_CORNERS: &[(f32, f32); 4] = &[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)];
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
#[derive(Clone)]
|
||||||
#[derive(Clone)]
|
pub struct CursorSettings {
|
||||||
pub struct CursorSettings {
|
animation_length: f32,
|
||||||
animation_length: f32,
|
trail_size: f32,
|
||||||
trail_size: f32,
|
vfx_mode: cursor_vfx::VfxMode,
|
||||||
vfx_mode: cursor_vfx::VfxMode,
|
vfx_opacity: f32,
|
||||||
vfx_opacity: f32,
|
vfx_particle_lifetime: f32,
|
||||||
vfx_particle_lifetime: f32,
|
vfx_particle_density: f32,
|
||||||
vfx_particle_density: f32,
|
vfx_particle_speed: f32,
|
||||||
vfx_particle_speed: f32,
|
vfx_particle_phase: f32,
|
||||||
vfx_particle_phase: f32,
|
vfx_particle_curl: f32,
|
||||||
vfx_particle_curl: f32,
|
}
|
||||||
}
|
|
||||||
|
pub fn initialize_settings() {
|
||||||
pub fn initialize_settings() {
|
SETTINGS.set(&CursorSettings {
|
||||||
SETTINGS.set(&CursorSettings {
|
animation_length: 0.13,
|
||||||
animation_length: 0.13,
|
trail_size: 0.7,
|
||||||
trail_size: 0.7,
|
vfx_mode: cursor_vfx::VfxMode::Disabled,
|
||||||
vfx_mode: cursor_vfx::VfxMode::Disabled,
|
vfx_opacity: 200.0,
|
||||||
vfx_opacity: 200.0,
|
vfx_particle_lifetime: 1.2,
|
||||||
vfx_particle_lifetime: 1.2,
|
vfx_particle_density: 7.0,
|
||||||
vfx_particle_density: 7.0,
|
vfx_particle_speed: 10.0,
|
||||||
vfx_particle_speed: 10.0,
|
vfx_particle_phase: 1.5,
|
||||||
vfx_particle_phase: 1.5,
|
vfx_particle_curl: 1.0,
|
||||||
vfx_particle_curl: 1.0,
|
});
|
||||||
});
|
|
||||||
|
register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length);
|
||||||
register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length);
|
register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size);
|
||||||
register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size);
|
register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode);
|
||||||
register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode);
|
register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity);
|
||||||
register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity);
|
register_nvim_setting!(
|
||||||
register_nvim_setting!("cursor_vfx_particle_lifetime", CursorSettings::vfx_particle_lifetime);
|
"cursor_vfx_particle_lifetime",
|
||||||
register_nvim_setting!("cursor_vfx_particle_density", CursorSettings::vfx_particle_density);
|
CursorSettings::vfx_particle_lifetime
|
||||||
register_nvim_setting!("cursor_vfx_particle_speed", CursorSettings::vfx_particle_speed);
|
);
|
||||||
register_nvim_setting!("cursor_vfx_particle_phase", CursorSettings::vfx_particle_phase);
|
register_nvim_setting!(
|
||||||
register_nvim_setting!("cursor_vfx_particle_curl", CursorSettings::vfx_particle_curl);
|
"cursor_vfx_particle_density",
|
||||||
}
|
CursorSettings::vfx_particle_density
|
||||||
|
);
|
||||||
// ----------------------------------------------------------------------------
|
register_nvim_setting!(
|
||||||
|
"cursor_vfx_particle_speed",
|
||||||
#[derive(Debug, Clone)]
|
CursorSettings::vfx_particle_speed
|
||||||
pub struct Corner {
|
);
|
||||||
start_position: Point,
|
register_nvim_setting!(
|
||||||
current_position: Point,
|
"cursor_vfx_particle_phase",
|
||||||
relative_position: Point,
|
CursorSettings::vfx_particle_phase
|
||||||
previous_destination: Point,
|
);
|
||||||
t: f32,
|
register_nvim_setting!(
|
||||||
}
|
"cursor_vfx_particle_curl",
|
||||||
|
CursorSettings::vfx_particle_curl
|
||||||
impl Corner {
|
);
|
||||||
pub fn new() -> Corner {
|
}
|
||||||
Corner {
|
|
||||||
start_position: Point::new(0.0, 0.0),
|
// ----------------------------------------------------------------------------
|
||||||
current_position: Point::new(0.0, 0.0),
|
|
||||||
relative_position: Point::new(0.0, 0.0),
|
#[derive(Debug, Clone)]
|
||||||
previous_destination: Point::new(-1000.0, -1000.0),
|
pub struct Corner {
|
||||||
t: 0.0,
|
start_position: Point,
|
||||||
}
|
current_position: Point,
|
||||||
}
|
relative_position: Point,
|
||||||
|
previous_destination: Point,
|
||||||
pub fn update(&mut self, settings: &CursorSettings, font_dimensions: Point, destination: Point, dt: f32) -> bool {
|
t: f32,
|
||||||
// Update destination if needed
|
}
|
||||||
let mut immediate_movement = false;
|
|
||||||
if destination != self.previous_destination {
|
impl Corner {
|
||||||
let travel_distance = destination - self.previous_destination;
|
pub fn new() -> Corner {
|
||||||
let chars_travel_x = travel_distance.x / font_dimensions.x;
|
Corner {
|
||||||
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 {
|
start_position: Point::new(0.0, 0.0),
|
||||||
// We're moving one character to the right. Make movement immediate to avoid lag
|
current_position: Point::new(0.0, 0.0),
|
||||||
// while typing
|
relative_position: Point::new(0.0, 0.0),
|
||||||
immediate_movement = true;
|
previous_destination: Point::new(-1000.0, -1000.0),
|
||||||
}
|
t: 0.0,
|
||||||
self.t = 0.0;
|
}
|
||||||
self.start_position = self.current_position;
|
}
|
||||||
self.previous_destination = destination;
|
|
||||||
}
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
// Check first if animation's over
|
settings: &CursorSettings,
|
||||||
if self.t > 1.0 {
|
font_dimensions: Point,
|
||||||
return false;
|
destination: Point,
|
||||||
}
|
dt: f32,
|
||||||
|
) -> bool {
|
||||||
// Calculate window-space destination for corner
|
// Update destination if needed
|
||||||
let relative_scaled_position: Point = (
|
let mut immediate_movement = false;
|
||||||
self.relative_position.x * font_dimensions.x,
|
if destination != self.previous_destination {
|
||||||
self.relative_position.y * font_dimensions.y,
|
let travel_distance = destination - self.previous_destination;
|
||||||
).into();
|
let chars_travel_x = travel_distance.x / font_dimensions.x;
|
||||||
|
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 {
|
||||||
let corner_destination = destination + relative_scaled_position;
|
// We're moving one character to the right. Make movement immediate to avoid lag
|
||||||
|
// while typing
|
||||||
if immediate_movement {
|
immediate_movement = true;
|
||||||
self.t = 1.0;
|
}
|
||||||
self.current_position = corner_destination;
|
self.t = 0.0;
|
||||||
return true;
|
self.start_position = self.current_position;
|
||||||
}
|
self.previous_destination = destination;
|
||||||
|
}
|
||||||
// Calculate how much a corner will be lagging behind based on how much it's aligned
|
|
||||||
// with the direction of motion. Corners in front will move faster than corners in the
|
// Check first if animation's over
|
||||||
// back
|
if self.t > 1.0 {
|
||||||
let travel_direction = {
|
return false;
|
||||||
let mut d = destination - self.current_position;
|
}
|
||||||
d.normalize();
|
|
||||||
d
|
// Calculate window-space destination for corner
|
||||||
};
|
let relative_scaled_position: Point = (
|
||||||
|
self.relative_position.x * font_dimensions.x,
|
||||||
let corner_direction = {
|
self.relative_position.y * font_dimensions.y,
|
||||||
let mut d = self.relative_position;
|
)
|
||||||
d.normalize();
|
.into();
|
||||||
d
|
|
||||||
};
|
let corner_destination = destination + relative_scaled_position;
|
||||||
|
|
||||||
let direction_alignment = travel_direction.dot(corner_direction);
|
if immediate_movement {
|
||||||
|
self.t = 1.0;
|
||||||
|
self.current_position = corner_destination;
|
||||||
if self.t == 1.0 {
|
return true;
|
||||||
// We are at destination, move t out of 0-1 range to stop the animation
|
}
|
||||||
self.t = 2.0;
|
|
||||||
} else {
|
// Calculate how much a corner will be lagging behind based on how much it's aligned
|
||||||
let corner_dt = dt * lerp(1.0, 1.0 - settings.trail_size, -direction_alignment);
|
// with the direction of motion. Corners in front will move faster than corners in the
|
||||||
self.t = (self.t + corner_dt / settings.animation_length).min(1.0)
|
// back
|
||||||
}
|
let travel_direction = {
|
||||||
|
let mut d = destination - self.current_position;
|
||||||
self.current_position =
|
d.normalize();
|
||||||
ease_point(ease_out_expo, self.start_position, corner_destination, self.t);
|
d
|
||||||
|
};
|
||||||
true
|
|
||||||
}
|
let corner_direction = {
|
||||||
}
|
let mut d = self.relative_position;
|
||||||
|
d.normalize();
|
||||||
pub struct CursorRenderer {
|
d
|
||||||
pub corners: Vec<Corner>,
|
};
|
||||||
pub previous_position: (u64, u64),
|
|
||||||
pub command_line_delay: u64,
|
let direction_alignment = travel_direction.dot(corner_direction);
|
||||||
blink_status: BlinkStatus,
|
|
||||||
previous_cursor_shape: Option<CursorShape>,
|
if self.t == 1.0 {
|
||||||
cursor_vfx: Option<Box<dyn cursor_vfx::CursorVfx>>,
|
// We are at destination, move t out of 0-1 range to stop the animation
|
||||||
previous_vfx_mode: cursor_vfx::VfxMode,
|
self.t = 2.0;
|
||||||
}
|
} else {
|
||||||
|
let corner_dt = dt * lerp(1.0, 1.0 - settings.trail_size, -direction_alignment);
|
||||||
impl CursorRenderer {
|
self.t = (self.t + corner_dt / settings.animation_length).min(1.0)
|
||||||
pub fn new() -> CursorRenderer {
|
}
|
||||||
let mut renderer = CursorRenderer {
|
|
||||||
corners: vec![Corner::new(); 4],
|
self.current_position = ease_point(
|
||||||
previous_position: (0, 0),
|
ease_out_expo,
|
||||||
command_line_delay: 0,
|
self.start_position,
|
||||||
blink_status: BlinkStatus::new(),
|
corner_destination,
|
||||||
previous_cursor_shape: None,
|
self.t,
|
||||||
//cursor_vfx: Box::new(PointHighlight::new(Point{x:0.0, y:0.0}, HighlightMode::Ripple)),
|
);
|
||||||
cursor_vfx: None,
|
|
||||||
previous_vfx_mode: cursor_vfx::VfxMode::Disabled,
|
true
|
||||||
};
|
}
|
||||||
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE);
|
}
|
||||||
renderer
|
|
||||||
}
|
pub struct CursorRenderer {
|
||||||
|
pub corners: Vec<Corner>,
|
||||||
fn set_cursor_shape(&mut self, cursor_shape: &CursorShape, cell_percentage: f32) {
|
pub previous_position: (u64, u64),
|
||||||
self.corners = self.corners
|
pub command_line_delay: u64,
|
||||||
.clone()
|
blink_status: BlinkStatus,
|
||||||
.into_iter().enumerate()
|
previous_cursor_shape: Option<CursorShape>,
|
||||||
.map(|(i, corner)| {
|
cursor_vfx: Option<Box<dyn cursor_vfx::CursorVfx>>,
|
||||||
let (x, y) = STANDARD_CORNERS[i];
|
previous_vfx_mode: cursor_vfx::VfxMode,
|
||||||
Corner {
|
}
|
||||||
relative_position: match cursor_shape {
|
|
||||||
CursorShape::Block => (x, y).into(),
|
impl CursorRenderer {
|
||||||
// Transform the x position so that the right side is translated over to
|
pub fn new() -> CursorRenderer {
|
||||||
// the BAR_WIDTH position
|
let mut renderer = CursorRenderer {
|
||||||
CursorShape::Vertical => ((x + 0.5) * cell_percentage - 0.5, y).into(),
|
corners: vec![Corner::new(); 4],
|
||||||
// Do the same as above, but flip the y coordinate and then flip the result
|
previous_position: (0, 0),
|
||||||
// so that the horizontal bar is at the bottom of the character space
|
command_line_delay: 0,
|
||||||
// instead of the top.
|
blink_status: BlinkStatus::new(),
|
||||||
CursorShape::Horizontal => (x, -((-y + 0.5) * cell_percentage - 0.5)).into()
|
previous_cursor_shape: None,
|
||||||
},
|
//cursor_vfx: Box::new(PointHighlight::new(Point{x:0.0, y:0.0}, HighlightMode::Ripple)),
|
||||||
t: 0.0,
|
cursor_vfx: None,
|
||||||
start_position: corner.current_position,
|
previous_vfx_mode: cursor_vfx::VfxMode::Disabled,
|
||||||
.. corner
|
};
|
||||||
|
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE);
|
||||||
}
|
renderer
|
||||||
})
|
}
|
||||||
.collect::<Vec<Corner>>();
|
|
||||||
}
|
fn set_cursor_shape(&mut self, cursor_shape: &CursorShape, cell_percentage: f32) {
|
||||||
|
self.corners = self
|
||||||
pub fn draw(&mut self,
|
.corners
|
||||||
cursor: Cursor, default_colors: &Colors,
|
.clone()
|
||||||
font_width: f32, font_height: f32,
|
.into_iter()
|
||||||
shaper: &mut CachingShaper, canvas: &mut Canvas,
|
.enumerate()
|
||||||
dt: f32) {
|
.map(|(i, corner)| {
|
||||||
let render = self.blink_status.update_status(&cursor);
|
let (x, y) = STANDARD_CORNERS[i];
|
||||||
|
Corner {
|
||||||
let settings = SETTINGS.get::<CursorSettings>();
|
relative_position: match cursor_shape {
|
||||||
|
CursorShape::Block => (x, y).into(),
|
||||||
if settings.vfx_mode != self.previous_vfx_mode {
|
// Transform the x position so that the right side is translated over to
|
||||||
self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode);
|
// the BAR_WIDTH position
|
||||||
self.previous_vfx_mode = settings.vfx_mode.clone();
|
CursorShape::Vertical => ((x + 0.5) * cell_percentage - 0.5, y).into(),
|
||||||
}
|
// Do the same as above, but flip the y coordinate and then flip the result
|
||||||
|
// so that the horizontal bar is at the bottom of the character space
|
||||||
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
|
// instead of the top.
|
||||||
paint.set_anti_alias(true);
|
CursorShape::Horizontal => {
|
||||||
|
(x, -((-y + 0.5) * cell_percentage - 0.5)).into()
|
||||||
self.previous_position = {
|
}
|
||||||
let editor = EDITOR.lock();
|
},
|
||||||
let (_, grid_y) = cursor.position;
|
t: 0.0,
|
||||||
let (_, previous_y) = self.previous_position;
|
start_position: corner.current_position,
|
||||||
if grid_y == editor.grid.height - 1 && previous_y != grid_y {
|
..corner
|
||||||
self.command_line_delay += 1;
|
}
|
||||||
if self.command_line_delay < COMMAND_LINE_DELAY_FRAMES {
|
})
|
||||||
self.previous_position
|
.collect::<Vec<Corner>>();
|
||||||
} else {
|
}
|
||||||
self.command_line_delay = 0;
|
|
||||||
cursor.position
|
pub fn draw(
|
||||||
}
|
&mut self,
|
||||||
} else {
|
cursor: Cursor,
|
||||||
self.command_line_delay = 0;
|
default_colors: &Colors,
|
||||||
cursor.position
|
font_width: f32,
|
||||||
}
|
font_height: f32,
|
||||||
};
|
shaper: &mut CachingShaper,
|
||||||
|
canvas: &mut Canvas,
|
||||||
|
dt: f32,
|
||||||
let (grid_x, grid_y) = self.previous_position;
|
) {
|
||||||
|
let render = self.blink_status.update_status(&cursor);
|
||||||
let (character, font_dimensions): (String, Point) = {
|
|
||||||
let editor = EDITOR.lock();
|
let settings = SETTINGS.get::<CursorSettings>();
|
||||||
let character = match editor.grid.get_cell(grid_x, grid_y) {
|
|
||||||
Some(Some((character, _))) => character.clone(),
|
if settings.vfx_mode != self.previous_vfx_mode {
|
||||||
_ => ' '.to_string(),
|
self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode);
|
||||||
};
|
self.previous_vfx_mode = settings.vfx_mode.clone();
|
||||||
|
}
|
||||||
let is_double = match editor.grid.get_cell(grid_x + 1, grid_y) {
|
|
||||||
Some(Some((character, _))) => character.is_empty(),
|
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
|
||||||
_ => false,
|
paint.set_anti_alias(true);
|
||||||
};
|
|
||||||
|
self.previous_position = {
|
||||||
let font_width = match (is_double, &cursor.shape) {
|
let editor = EDITOR.lock();
|
||||||
(true, CursorShape::Block) => font_width * 2.0,
|
let (_, grid_y) = cursor.position;
|
||||||
_ => font_width
|
let (_, previous_y) = self.previous_position;
|
||||||
};
|
if grid_y == editor.grid.height - 1 && previous_y != grid_y {
|
||||||
(character, (font_width, font_height).into())
|
self.command_line_delay += 1;
|
||||||
};
|
if self.command_line_delay < COMMAND_LINE_DELAY_FRAMES {
|
||||||
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
|
self.previous_position
|
||||||
let center_destination = destination + font_dimensions * 0.5;
|
} else {
|
||||||
|
self.command_line_delay = 0;
|
||||||
let new_cursor = Some(cursor.shape.clone());
|
cursor.position
|
||||||
|
}
|
||||||
if self.previous_cursor_shape != new_cursor {
|
} else {
|
||||||
self.previous_cursor_shape = new_cursor;
|
self.command_line_delay = 0;
|
||||||
self.set_cursor_shape(&cursor.shape, cursor.cell_percentage.unwrap_or(DEFAULT_CELL_PERCENTAGE));
|
cursor.position
|
||||||
|
}
|
||||||
if let Some(vfx) = self.cursor_vfx.as_mut() {
|
};
|
||||||
vfx.restart(center_destination);
|
|
||||||
}
|
let (grid_x, grid_y) = self.previous_position;
|
||||||
}
|
|
||||||
|
let (character, font_dimensions): (String, Point) = {
|
||||||
let mut animating = false;
|
let editor = EDITOR.lock();
|
||||||
if !center_destination.is_zero() {
|
let character = match editor.grid.get_cell(grid_x, grid_y) {
|
||||||
for corner in self.corners.iter_mut() {
|
Some(Some((character, _))) => character.clone(),
|
||||||
let corner_animating = corner.update(&settings, font_dimensions, center_destination, dt);
|
_ => ' '.to_string(),
|
||||||
animating |= corner_animating;
|
};
|
||||||
}
|
|
||||||
|
let is_double = match editor.grid.get_cell(grid_x + 1, grid_y) {
|
||||||
let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() {
|
Some(Some((character, _))) => character.is_empty(),
|
||||||
vfx.update(&settings, center_destination, (font_width, font_height), dt)
|
_ => false,
|
||||||
}else{
|
};
|
||||||
false
|
|
||||||
};
|
let font_width = match (is_double, &cursor.shape) {
|
||||||
|
(true, CursorShape::Block) => font_width * 2.0,
|
||||||
animating |= vfx_animating;
|
_ => font_width,
|
||||||
}
|
};
|
||||||
|
(character, (font_width, font_height).into())
|
||||||
if animating || self.command_line_delay != 0 {
|
};
|
||||||
REDRAW_SCHEDULER.queue_next_frame();
|
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
|
||||||
}
|
let center_destination = destination + font_dimensions * 0.5;
|
||||||
|
|
||||||
if cursor.enabled && render {
|
let new_cursor = Some(cursor.shape.clone());
|
||||||
// Draw Background
|
|
||||||
paint.set_color(cursor.background(&default_colors).to_color());
|
if self.previous_cursor_shape != new_cursor {
|
||||||
|
self.previous_cursor_shape = new_cursor;
|
||||||
// The cursor is made up of four points, so I create a path with each of the four
|
self.set_cursor_shape(
|
||||||
// corners.
|
&cursor.shape,
|
||||||
let mut path = Path::new();
|
cursor.cell_percentage.unwrap_or(DEFAULT_CELL_PERCENTAGE),
|
||||||
path.move_to(self.corners[0].current_position);
|
);
|
||||||
path.line_to(self.corners[1].current_position);
|
|
||||||
path.line_to(self.corners[2].current_position);
|
if let Some(vfx) = self.cursor_vfx.as_mut() {
|
||||||
path.line_to(self.corners[3].current_position);
|
vfx.restart(center_destination);
|
||||||
path.close();
|
}
|
||||||
canvas.draw_path(&path, &paint);
|
}
|
||||||
|
|
||||||
// Draw foreground
|
let mut animating = false;
|
||||||
paint.set_color(cursor.foreground(&default_colors).to_color());
|
if !center_destination.is_zero() {
|
||||||
canvas.save();
|
for corner in self.corners.iter_mut() {
|
||||||
canvas.clip_path(&path, None, Some(false));
|
let corner_animating =
|
||||||
|
corner.update(&settings, font_dimensions, center_destination, dt);
|
||||||
let blobs = &shaper.shape_cached(&character, false, false);
|
animating |= corner_animating;
|
||||||
for blob in blobs.iter() {
|
}
|
||||||
canvas.draw_text_blob(&blob, destination, &paint);
|
|
||||||
}
|
let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() {
|
||||||
canvas.restore();
|
vfx.update(&settings, center_destination, (font_width, font_height), dt)
|
||||||
if let Some(vfx) = self.cursor_vfx.as_ref() {
|
} else {
|
||||||
vfx.render(&settings, canvas, &cursor, &default_colors, (font_width, font_height));
|
false
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
animating |= vfx_animating;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if animating || self.command_line_delay != 0 {
|
||||||
|
REDRAW_SCHEDULER.queue_next_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursor.enabled && render {
|
||||||
|
// Draw Background
|
||||||
|
paint.set_color(cursor.background(&default_colors).to_color());
|
||||||
|
|
||||||
|
// The cursor is made up of four points, so I create a path with each of the four
|
||||||
|
// corners.
|
||||||
|
let mut path = Path::new();
|
||||||
|
path.move_to(self.corners[0].current_position);
|
||||||
|
path.line_to(self.corners[1].current_position);
|
||||||
|
path.line_to(self.corners[2].current_position);
|
||||||
|
path.line_to(self.corners[3].current_position);
|
||||||
|
path.close();
|
||||||
|
canvas.draw_path(&path, &paint);
|
||||||
|
|
||||||
|
// Draw foreground
|
||||||
|
paint.set_color(cursor.foreground(&default_colors).to_color());
|
||||||
|
canvas.save();
|
||||||
|
canvas.clip_path(&path, None, Some(false));
|
||||||
|
|
||||||
|
let blobs = &shaper.shape_cached(&character, false, false);
|
||||||
|
for blob in blobs.iter() {
|
||||||
|
canvas.draw_text_blob(&blob, destination, &paint);
|
||||||
|
}
|
||||||
|
canvas.restore();
|
||||||
|
if let Some(vfx) = self.cursor_vfx.as_ref() {
|
||||||
|
vfx.render(
|
||||||
|
&settings,
|
||||||
|
canvas,
|
||||||
|
&cursor,
|
||||||
|
&default_colors,
|
||||||
|
(font_width, font_height),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue