mirror of https://github.com/sgoudham/neovide.git
Smooth scrolling (#423)
* parse viewport events * progress toward pixel scrolling * ITS WORKING * fix basic scrolling gaps * add limit to number of old surfaces to store * set cursor destination based on rendered location not global gridmacos-click-through
parent
38c5538186
commit
1d981f3c2c
@ -1,240 +1,238 @@
|
|||||||
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::{Colors, Style};
|
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: (f64, f64),
|
pub grid_position: (u64, u64),
|
||||||
pub grid_position: (u64, u64),
|
pub parent_window_id: u64,
|
||||||
pub parent_window_id: 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 double_width: bool,
|
||||||
pub double_width: bool,
|
pub character: String,
|
||||||
pub character: String,
|
}
|
||||||
}
|
|
||||||
|
impl Cursor {
|
||||||
impl Cursor {
|
pub fn new() -> Cursor {
|
||||||
pub fn new() -> Cursor {
|
Cursor {
|
||||||
Cursor {
|
grid_position: (0, 0),
|
||||||
position: (0.0, 0.0),
|
parent_window_id: 0,
|
||||||
grid_position: (0, 0),
|
shape: CursorShape::Block,
|
||||||
parent_window_id: 0,
|
style: None,
|
||||||
shape: CursorShape::Block,
|
cell_percentage: None,
|
||||||
style: None,
|
blinkwait: None,
|
||||||
cell_percentage: None,
|
blinkon: None,
|
||||||
blinkwait: None,
|
blinkoff: None,
|
||||||
blinkon: None,
|
enabled: true,
|
||||||
blinkoff: None,
|
double_width: false,
|
||||||
enabled: true,
|
character: " ".to_string(),
|
||||||
double_width: false,
|
}
|
||||||
character: " ".to_string(),
|
}
|
||||||
}
|
|
||||||
}
|
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
|
||||||
|
if let Some(style) = &self.style {
|
||||||
pub fn foreground(&self, default_colors: &Colors) -> Color4f {
|
style
|
||||||
if let Some(style) = &self.style {
|
.colors
|
||||||
style
|
.foreground
|
||||||
.colors
|
.clone()
|
||||||
.foreground
|
.unwrap_or_else(|| default_colors.background.clone().unwrap())
|
||||||
.clone()
|
} else {
|
||||||
.unwrap_or_else(|| default_colors.background.clone().unwrap())
|
default_colors.background.clone().unwrap()
|
||||||
} else {
|
}
|
||||||
default_colors.background.clone().unwrap()
|
}
|
||||||
}
|
|
||||||
}
|
pub fn background(&self, default_colors: &Colors) -> Color4f {
|
||||||
|
if let Some(style) = &self.style {
|
||||||
pub fn background(&self, default_colors: &Colors) -> Color4f {
|
style
|
||||||
if let Some(style) = &self.style {
|
.colors
|
||||||
style
|
.background
|
||||||
.colors
|
.clone()
|
||||||
.background
|
.unwrap_or_else(|| default_colors.foreground.clone().unwrap())
|
||||||
.clone()
|
} else {
|
||||||
.unwrap_or_else(|| default_colors.foreground.clone().unwrap())
|
default_colors.foreground.clone().unwrap()
|
||||||
} else {
|
}
|
||||||
default_colors.foreground.clone().unwrap()
|
}
|
||||||
}
|
|
||||||
}
|
pub fn change_mode(&mut self, cursor_mode: &CursorMode, styles: &HashMap<u64, Arc<Style>>) {
|
||||||
|
let CursorMode {
|
||||||
pub fn change_mode(&mut self, cursor_mode: &CursorMode, styles: &HashMap<u64, Arc<Style>>) {
|
shape,
|
||||||
let CursorMode {
|
style_id,
|
||||||
shape,
|
cell_percentage,
|
||||||
style_id,
|
blinkwait,
|
||||||
cell_percentage,
|
blinkon,
|
||||||
blinkwait,
|
blinkoff,
|
||||||
blinkon,
|
} = cursor_mode;
|
||||||
blinkoff,
|
|
||||||
} = cursor_mode;
|
if let Some(shape) = shape {
|
||||||
|
self.shape = shape.clone();
|
||||||
if let Some(shape) = shape {
|
}
|
||||||
self.shape = shape.clone();
|
|
||||||
}
|
if let Some(style_id) = style_id {
|
||||||
|
self.style = styles.get(style_id).cloned();
|
||||||
if let Some(style_id) = style_id {
|
}
|
||||||
self.style = styles.get(style_id).cloned();
|
|
||||||
}
|
self.cell_percentage = *cell_percentage;
|
||||||
|
self.blinkwait = *blinkwait;
|
||||||
self.cell_percentage = *cell_percentage;
|
self.blinkon = *blinkon;
|
||||||
self.blinkwait = *blinkwait;
|
self.blinkoff = *blinkoff;
|
||||||
self.blinkon = *blinkon;
|
}
|
||||||
self.blinkoff = *blinkoff;
|
}
|
||||||
}
|
|
||||||
}
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
#[cfg(test)]
|
use super::*;
|
||||||
mod tests {
|
|
||||||
use super::*;
|
const COLORS: Colors = Colors {
|
||||||
|
foreground: Some(Color4f::new(0.1, 0.1, 0.1, 0.1)),
|
||||||
const COLORS: Colors = Colors {
|
background: Some(Color4f::new(0.2, 0.1, 0.1, 0.1)),
|
||||||
foreground: Some(Color4f::new(0.1, 0.1, 0.1, 0.1)),
|
special: Some(Color4f::new(0.3, 0.1, 0.1, 0.1)),
|
||||||
background: Some(Color4f::new(0.2, 0.1, 0.1, 0.1)),
|
};
|
||||||
special: Some(Color4f::new(0.3, 0.1, 0.1, 0.1)),
|
|
||||||
};
|
const DEFAULT_COLORS: Colors = Colors {
|
||||||
|
foreground: Some(Color4f::new(0.1, 0.2, 0.1, 0.1)),
|
||||||
const DEFAULT_COLORS: Colors = Colors {
|
background: Some(Color4f::new(0.2, 0.2, 0.1, 0.1)),
|
||||||
foreground: Some(Color4f::new(0.1, 0.2, 0.1, 0.1)),
|
special: Some(Color4f::new(0.3, 0.2, 0.1, 0.1)),
|
||||||
background: Some(Color4f::new(0.2, 0.2, 0.1, 0.1)),
|
};
|
||||||
special: Some(Color4f::new(0.3, 0.2, 0.1, 0.1)),
|
|
||||||
};
|
const NONE_COLORS: Colors = Colors {
|
||||||
|
foreground: None,
|
||||||
const NONE_COLORS: Colors = Colors {
|
background: None,
|
||||||
foreground: None,
|
special: None,
|
||||||
background: None,
|
};
|
||||||
special: None,
|
|
||||||
};
|
#[test]
|
||||||
|
fn test_from_type_name() {
|
||||||
#[test]
|
assert_eq!(
|
||||||
fn test_from_type_name() {
|
CursorShape::from_type_name("block"),
|
||||||
assert_eq!(
|
Some(CursorShape::Block)
|
||||||
CursorShape::from_type_name("block"),
|
);
|
||||||
Some(CursorShape::Block)
|
assert_eq!(
|
||||||
);
|
CursorShape::from_type_name("horizontal"),
|
||||||
assert_eq!(
|
Some(CursorShape::Horizontal)
|
||||||
CursorShape::from_type_name("horizontal"),
|
);
|
||||||
Some(CursorShape::Horizontal)
|
assert_eq!(
|
||||||
);
|
CursorShape::from_type_name("vertical"),
|
||||||
assert_eq!(
|
Some(CursorShape::Vertical)
|
||||||
CursorShape::from_type_name("vertical"),
|
);
|
||||||
Some(CursorShape::Vertical)
|
}
|
||||||
);
|
|
||||||
}
|
#[test]
|
||||||
|
fn test_foreground() {
|
||||||
#[test]
|
let mut cursor = Cursor::new();
|
||||||
fn test_foreground() {
|
let style = Some(Arc::new(Style::new(COLORS)));
|
||||||
let mut cursor = Cursor::new();
|
|
||||||
let style = Some(Arc::new(Style::new(COLORS)));
|
assert_eq!(
|
||||||
|
cursor.foreground(&DEFAULT_COLORS),
|
||||||
assert_eq!(
|
DEFAULT_COLORS.background.clone().unwrap()
|
||||||
cursor.foreground(&DEFAULT_COLORS),
|
);
|
||||||
DEFAULT_COLORS.background.clone().unwrap()
|
cursor.style = style.clone();
|
||||||
);
|
assert_eq!(
|
||||||
cursor.style = style.clone();
|
cursor.foreground(&DEFAULT_COLORS),
|
||||||
assert_eq!(
|
COLORS.foreground.clone().unwrap()
|
||||||
cursor.foreground(&DEFAULT_COLORS),
|
);
|
||||||
COLORS.foreground.clone().unwrap()
|
|
||||||
);
|
cursor.style = Some(Arc::new(Style::new(NONE_COLORS)));
|
||||||
|
assert_eq!(
|
||||||
cursor.style = Some(Arc::new(Style::new(NONE_COLORS)));
|
cursor.foreground(&DEFAULT_COLORS),
|
||||||
assert_eq!(
|
DEFAULT_COLORS.background.clone().unwrap()
|
||||||
cursor.foreground(&DEFAULT_COLORS),
|
);
|
||||||
DEFAULT_COLORS.background.clone().unwrap()
|
}
|
||||||
);
|
|
||||||
}
|
#[test]
|
||||||
|
fn test_background() {
|
||||||
#[test]
|
let mut cursor = Cursor::new();
|
||||||
fn test_background() {
|
let style = Some(Arc::new(Style::new(COLORS)));
|
||||||
let mut cursor = Cursor::new();
|
|
||||||
let style = Some(Arc::new(Style::new(COLORS)));
|
assert_eq!(
|
||||||
|
cursor.background(&DEFAULT_COLORS),
|
||||||
assert_eq!(
|
DEFAULT_COLORS.foreground.clone().unwrap()
|
||||||
cursor.background(&DEFAULT_COLORS),
|
);
|
||||||
DEFAULT_COLORS.foreground.clone().unwrap()
|
cursor.style = style.clone();
|
||||||
);
|
assert_eq!(
|
||||||
cursor.style = style.clone();
|
cursor.background(&DEFAULT_COLORS),
|
||||||
assert_eq!(
|
COLORS.background.clone().unwrap()
|
||||||
cursor.background(&DEFAULT_COLORS),
|
);
|
||||||
COLORS.background.clone().unwrap()
|
|
||||||
);
|
cursor.style = Some(Arc::new(Style::new(NONE_COLORS)));
|
||||||
|
assert_eq!(
|
||||||
cursor.style = Some(Arc::new(Style::new(NONE_COLORS)));
|
cursor.background(&DEFAULT_COLORS),
|
||||||
assert_eq!(
|
DEFAULT_COLORS.foreground.clone().unwrap()
|
||||||
cursor.background(&DEFAULT_COLORS),
|
);
|
||||||
DEFAULT_COLORS.foreground.clone().unwrap()
|
}
|
||||||
);
|
|
||||||
}
|
#[test]
|
||||||
|
fn test_change_mode() {
|
||||||
#[test]
|
let cursor_mode = CursorMode {
|
||||||
fn test_change_mode() {
|
shape: Some(CursorShape::Horizontal),
|
||||||
let cursor_mode = CursorMode {
|
style_id: Some(1),
|
||||||
shape: Some(CursorShape::Horizontal),
|
cell_percentage: Some(100.0),
|
||||||
style_id: Some(1),
|
blinkwait: Some(1),
|
||||||
cell_percentage: Some(100.0),
|
blinkon: Some(1),
|
||||||
blinkwait: Some(1),
|
blinkoff: Some(1),
|
||||||
blinkon: Some(1),
|
};
|
||||||
blinkoff: Some(1),
|
let mut styles = HashMap::new();
|
||||||
};
|
styles.insert(1, Arc::new(Style::new(COLORS)));
|
||||||
let mut styles = HashMap::new();
|
|
||||||
styles.insert(1, Arc::new(Style::new(COLORS)));
|
let mut cursor = Cursor::new();
|
||||||
|
|
||||||
let mut cursor = Cursor::new();
|
cursor.change_mode(&cursor_mode, &styles);
|
||||||
|
assert_eq!(cursor.shape, CursorShape::Horizontal);
|
||||||
cursor.change_mode(&cursor_mode, &styles);
|
assert_eq!(cursor.style, styles.get(&1).cloned());
|
||||||
assert_eq!(cursor.shape, CursorShape::Horizontal);
|
assert_eq!(cursor.cell_percentage, Some(100.0));
|
||||||
assert_eq!(cursor.style, styles.get(&1).cloned());
|
assert_eq!(cursor.blinkwait, Some(1));
|
||||||
assert_eq!(cursor.cell_percentage, Some(100.0));
|
assert_eq!(cursor.blinkon, Some(1));
|
||||||
assert_eq!(cursor.blinkwait, Some(1));
|
assert_eq!(cursor.blinkoff, Some(1));
|
||||||
assert_eq!(cursor.blinkon, Some(1));
|
|
||||||
assert_eq!(cursor.blinkoff, Some(1));
|
let cursor_mode_with_none = CursorMode {
|
||||||
|
shape: None,
|
||||||
let cursor_mode_with_none = CursorMode {
|
style_id: None,
|
||||||
shape: None,
|
cell_percentage: None,
|
||||||
style_id: None,
|
blinkwait: None,
|
||||||
cell_percentage: None,
|
blinkon: None,
|
||||||
blinkwait: None,
|
blinkoff: None,
|
||||||
blinkon: None,
|
};
|
||||||
blinkoff: None,
|
cursor.change_mode(&cursor_mode_with_none, &styles);
|
||||||
};
|
assert_eq!(cursor.shape, CursorShape::Horizontal);
|
||||||
cursor.change_mode(&cursor_mode_with_none, &styles);
|
assert_eq!(cursor.style, styles.get(&1).cloned());
|
||||||
assert_eq!(cursor.shape, CursorShape::Horizontal);
|
assert_eq!(cursor.cell_percentage, None);
|
||||||
assert_eq!(cursor.style, styles.get(&1).cloned());
|
assert_eq!(cursor.blinkwait, None);
|
||||||
assert_eq!(cursor.cell_percentage, None);
|
assert_eq!(cursor.blinkon, None);
|
||||||
assert_eq!(cursor.blinkwait, None);
|
assert_eq!(cursor.blinkoff, None);
|
||||||
assert_eq!(cursor.blinkon, None);
|
}
|
||||||
assert_eq!(cursor.blinkoff, None);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,335 +1,368 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use log::{error, trace, warn};
|
use log::{error, trace, warn};
|
||||||
use skulpin::skia_safe::{colors, dash_path_effect, BlendMode, Canvas, Color, Paint, Rect};
|
use skulpin::skia_safe::{colors, dash_path_effect, BlendMode, Canvas, Color, Paint, Rect};
|
||||||
use skulpin::CoordinateSystemHelper;
|
use skulpin::CoordinateSystemHelper;
|
||||||
|
|
||||||
pub mod animation_utils;
|
pub mod animation_utils;
|
||||||
mod caching_shaper;
|
mod caching_shaper;
|
||||||
pub mod cursor_renderer;
|
pub mod cursor_renderer;
|
||||||
pub mod font_options;
|
pub mod font_options;
|
||||||
mod rendered_window;
|
mod rendered_window;
|
||||||
|
|
||||||
pub use caching_shaper::CachingShaper;
|
pub use caching_shaper::CachingShaper;
|
||||||
pub use font_options::*;
|
pub use font_options::*;
|
||||||
pub use rendered_window::{RenderedWindow, WindowDrawDetails};
|
pub use rendered_window::{RenderedWindow, WindowDrawDetails};
|
||||||
|
|
||||||
use crate::editor::{Colors, DrawCommand, Style, WindowDrawCommand};
|
use crate::bridge::EditorMode;
|
||||||
use crate::settings::*;
|
use crate::editor::{Colors, DrawCommand, Style, WindowDrawCommand};
|
||||||
use cursor_renderer::CursorRenderer;
|
use crate::settings::*;
|
||||||
|
use cursor_renderer::CursorRenderer;
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RendererSettings {
|
#[derive(Clone)]
|
||||||
animation_length: f32,
|
pub struct RendererSettings {
|
||||||
floating_opacity: f32,
|
position_animation_length: f32,
|
||||||
floating_blur: bool,
|
scroll_animation_length: f32,
|
||||||
}
|
floating_opacity: f32,
|
||||||
|
floating_blur: bool,
|
||||||
pub fn initialize_settings() {
|
}
|
||||||
SETTINGS.set(&RendererSettings {
|
|
||||||
animation_length: 0.15,
|
pub fn initialize_settings() {
|
||||||
floating_opacity: 0.7,
|
SETTINGS.set(&RendererSettings {
|
||||||
floating_blur: true,
|
position_animation_length: 0.15,
|
||||||
});
|
scroll_animation_length: 0.3,
|
||||||
|
floating_opacity: 0.7,
|
||||||
register_nvim_setting!(
|
floating_blur: true,
|
||||||
"window_animation_length",
|
});
|
||||||
RendererSettings::animation_length
|
|
||||||
);
|
register_nvim_setting!(
|
||||||
register_nvim_setting!(
|
"window_position_animation_length",
|
||||||
"floating_window_opacity",
|
RendererSettings::position_animation_length
|
||||||
RendererSettings::floating_opacity
|
);
|
||||||
);
|
register_nvim_setting!(
|
||||||
register_nvim_setting!("floating_window_blur", RendererSettings::floating_blur);
|
"window_scroll_animation_length",
|
||||||
}
|
RendererSettings::scroll_animation_length
|
||||||
|
);
|
||||||
// ----------------------------------------------------------------------------
|
register_nvim_setting!(
|
||||||
|
"floating_window_opacity",
|
||||||
pub struct Renderer {
|
RendererSettings::floating_opacity
|
||||||
rendered_windows: HashMap<u64, RenderedWindow>,
|
);
|
||||||
cursor_renderer: CursorRenderer,
|
register_nvim_setting!("floating_window_blur", RendererSettings::floating_blur);
|
||||||
|
}
|
||||||
pub paint: Paint,
|
|
||||||
pub shaper: CachingShaper,
|
// ----------------------------------------------------------------------------
|
||||||
pub default_style: Arc<Style>,
|
|
||||||
pub font_width: f32,
|
pub struct Renderer {
|
||||||
pub font_height: f32,
|
rendered_windows: HashMap<u64, RenderedWindow>,
|
||||||
pub window_regions: Vec<WindowDrawDetails>,
|
cursor_renderer: CursorRenderer,
|
||||||
pub batched_draw_command_receiver: Receiver<Vec<DrawCommand>>,
|
|
||||||
}
|
pub current_mode: EditorMode,
|
||||||
|
pub paint: Paint,
|
||||||
impl Renderer {
|
pub shaper: CachingShaper,
|
||||||
pub fn new(batched_draw_command_receiver: Receiver<Vec<DrawCommand>>) -> Renderer {
|
pub default_style: Arc<Style>,
|
||||||
let rendered_windows = HashMap::new();
|
pub font_width: f32,
|
||||||
let cursor_renderer = CursorRenderer::new();
|
pub font_height: f32,
|
||||||
|
pub window_regions: Vec<WindowDrawDetails>,
|
||||||
let mut paint = Paint::new(colors::WHITE, None);
|
pub batched_draw_command_receiver: Receiver<Vec<DrawCommand>>,
|
||||||
paint.set_anti_alias(false);
|
}
|
||||||
let mut shaper = CachingShaper::new();
|
|
||||||
let (font_width, font_height) = shaper.font_base_dimensions();
|
impl Renderer {
|
||||||
let default_style = Arc::new(Style::new(Colors::new(
|
pub fn new(batched_draw_command_receiver: Receiver<Vec<DrawCommand>>) -> Renderer {
|
||||||
Some(colors::WHITE),
|
let rendered_windows = HashMap::new();
|
||||||
Some(colors::BLACK),
|
let cursor_renderer = CursorRenderer::new();
|
||||||
Some(colors::GREY),
|
|
||||||
)));
|
let current_mode = EditorMode::Unknown(String::from(""));
|
||||||
let window_regions = Vec::new();
|
let mut paint = Paint::new(colors::WHITE, None);
|
||||||
|
paint.set_anti_alias(false);
|
||||||
Renderer {
|
let mut shaper = CachingShaper::new();
|
||||||
rendered_windows,
|
let (font_width, font_height) = shaper.font_base_dimensions();
|
||||||
cursor_renderer,
|
let default_style = Arc::new(Style::new(Colors::new(
|
||||||
|
Some(colors::WHITE),
|
||||||
paint,
|
Some(colors::BLACK),
|
||||||
shaper,
|
Some(colors::GREY),
|
||||||
default_style,
|
)));
|
||||||
font_width,
|
let window_regions = Vec::new();
|
||||||
font_height,
|
|
||||||
window_regions,
|
Renderer {
|
||||||
batched_draw_command_receiver,
|
rendered_windows,
|
||||||
}
|
cursor_renderer,
|
||||||
}
|
|
||||||
|
current_mode,
|
||||||
fn update_font(&mut self, guifont_setting: &str) -> bool {
|
paint,
|
||||||
let updated = self.shaper.update_font(guifont_setting);
|
shaper,
|
||||||
if updated {
|
default_style,
|
||||||
let (font_width, font_height) = self.shaper.font_base_dimensions();
|
font_width,
|
||||||
self.font_width = font_width;
|
font_height,
|
||||||
self.font_height = font_height.ceil();
|
window_regions,
|
||||||
}
|
batched_draw_command_receiver,
|
||||||
updated
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_text_region(&self, grid_pos: (u64, u64), cell_width: u64) -> Rect {
|
fn update_font(&mut self, guifont_setting: &str) {
|
||||||
let (grid_x, grid_y) = grid_pos;
|
if self.shaper.update_font(guifont_setting) {
|
||||||
let x = grid_x as f32 * self.font_width;
|
let (font_width, font_height) = self.shaper.font_base_dimensions();
|
||||||
let y = grid_y as f32 * self.font_height;
|
self.font_width = font_width;
|
||||||
let width = cell_width as f32 * self.font_width as f32;
|
self.font_height = font_height.ceil();
|
||||||
let height = self.font_height as f32;
|
}
|
||||||
Rect::new(x, y, x + width, y + height)
|
}
|
||||||
}
|
|
||||||
|
fn compute_text_region(&self, grid_pos: (u64, u64), cell_width: u64) -> Rect {
|
||||||
fn draw_background(
|
let (grid_x, grid_y) = grid_pos;
|
||||||
&mut self,
|
let x = grid_x as f32 * self.font_width;
|
||||||
canvas: &mut Canvas,
|
let y = grid_y as f32 * self.font_height;
|
||||||
grid_pos: (u64, u64),
|
let width = cell_width as f32 * self.font_width as f32;
|
||||||
cell_width: u64,
|
let height = self.font_height as f32;
|
||||||
style: &Option<Arc<Style>>,
|
Rect::new(x, y, x + width, y + height)
|
||||||
) {
|
}
|
||||||
self.paint.set_blend_mode(BlendMode::Src);
|
|
||||||
|
fn get_default_background(&self) -> Color {
|
||||||
let region = self.compute_text_region(grid_pos, cell_width);
|
self.default_style
|
||||||
let style = style.as_ref().unwrap_or(&self.default_style);
|
.colors
|
||||||
|
.background
|
||||||
self.paint
|
.clone()
|
||||||
.set_color(style.background(&self.default_style.colors).to_color());
|
.unwrap()
|
||||||
canvas.draw_rect(region, &self.paint);
|
.to_color()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_foreground(
|
fn draw_background(
|
||||||
&mut self,
|
&mut self,
|
||||||
canvas: &mut Canvas,
|
canvas: &mut Canvas,
|
||||||
text: &str,
|
grid_pos: (u64, u64),
|
||||||
grid_pos: (u64, u64),
|
cell_width: u64,
|
||||||
cell_width: u64,
|
style: &Option<Arc<Style>>,
|
||||||
style: &Option<Arc<Style>>,
|
) {
|
||||||
) {
|
self.paint.set_blend_mode(BlendMode::Src);
|
||||||
let (grid_x, grid_y) = grid_pos;
|
|
||||||
let x = grid_x as f32 * self.font_width;
|
let region = self.compute_text_region(grid_pos, cell_width);
|
||||||
let y = grid_y as f32 * self.font_height;
|
let style = style.as_ref().unwrap_or(&self.default_style);
|
||||||
let width = cell_width as f32 * self.font_width;
|
|
||||||
|
self.paint
|
||||||
let style = style.as_ref().unwrap_or(&self.default_style);
|
.set_color(style.background(&self.default_style.colors).to_color());
|
||||||
|
canvas.draw_rect(region, &self.paint);
|
||||||
canvas.save();
|
}
|
||||||
|
|
||||||
let region = self.compute_text_region(grid_pos, cell_width);
|
fn draw_foreground(
|
||||||
|
&mut self,
|
||||||
canvas.clip_rect(region, None, Some(false));
|
canvas: &mut Canvas,
|
||||||
|
text: &str,
|
||||||
self.paint.set_blend_mode(BlendMode::Src);
|
grid_pos: (u64, u64),
|
||||||
let transparent = Color::from_argb(0, 0, 0, 0);
|
cell_width: u64,
|
||||||
self.paint.set_color(transparent);
|
style: &Option<Arc<Style>>,
|
||||||
canvas.draw_rect(region, &self.paint);
|
) {
|
||||||
|
let (grid_x, grid_y) = grid_pos;
|
||||||
if style.underline || style.undercurl {
|
let x = grid_x as f32 * self.font_width;
|
||||||
let line_position = self.shaper.underline_position();
|
let y = grid_y as f32 * self.font_height;
|
||||||
let stroke_width = self.shaper.options.size / 10.0;
|
let width = cell_width as f32 * self.font_width;
|
||||||
self.paint
|
|
||||||
.set_color(style.special(&self.default_style.colors).to_color());
|
let style = style.as_ref().unwrap_or(&self.default_style);
|
||||||
self.paint.set_stroke_width(stroke_width);
|
|
||||||
|
canvas.save();
|
||||||
if style.undercurl {
|
|
||||||
self.paint.set_path_effect(dash_path_effect::new(
|
let region = self.compute_text_region(grid_pos, cell_width);
|
||||||
&[stroke_width * 2.0, stroke_width * 2.0],
|
|
||||||
0.0,
|
canvas.clip_rect(region, None, Some(false));
|
||||||
));
|
|
||||||
} else {
|
self.paint.set_blend_mode(BlendMode::Src);
|
||||||
self.paint.set_path_effect(None);
|
let transparent = Color::from_argb(0, 0, 0, 0);
|
||||||
}
|
self.paint.set_color(transparent);
|
||||||
|
canvas.draw_rect(region, &self.paint);
|
||||||
canvas.draw_line(
|
|
||||||
(x, y - line_position + self.font_height),
|
if style.underline || style.undercurl {
|
||||||
(x + width, y - line_position + self.font_height),
|
let line_position = self.shaper.underline_position();
|
||||||
&self.paint,
|
let stroke_width = self.shaper.options.size / 10.0;
|
||||||
);
|
self.paint
|
||||||
}
|
.set_color(style.special(&self.default_style.colors).to_color());
|
||||||
|
self.paint.set_stroke_width(stroke_width);
|
||||||
self.paint
|
|
||||||
.set_color(style.foreground(&self.default_style.colors).to_color());
|
if style.undercurl {
|
||||||
let text = text.trim_end();
|
self.paint.set_path_effect(dash_path_effect::new(
|
||||||
if !text.is_empty() {
|
&[stroke_width * 2.0, stroke_width * 2.0],
|
||||||
for blob in self
|
0.0,
|
||||||
.shaper
|
));
|
||||||
.shape_cached(text, style.bold, style.italic)
|
} else {
|
||||||
.iter()
|
self.paint.set_path_effect(None);
|
||||||
{
|
}
|
||||||
canvas.draw_text_blob(blob, (x, y), &self.paint);
|
|
||||||
}
|
canvas.draw_line(
|
||||||
}
|
(x, y - line_position + self.font_height),
|
||||||
|
(x + width, y - line_position + self.font_height),
|
||||||
if style.strikethrough {
|
&self.paint,
|
||||||
let line_position = region.center_y();
|
);
|
||||||
self.paint
|
}
|
||||||
.set_color(style.special(&self.default_style.colors).to_color());
|
|
||||||
canvas.draw_line((x, line_position), (x + width, line_position), &self.paint);
|
self.paint
|
||||||
}
|
.set_color(style.foreground(&self.default_style.colors).to_color());
|
||||||
|
let text = text.trim_end();
|
||||||
canvas.restore();
|
if !text.is_empty() {
|
||||||
}
|
for blob in self
|
||||||
|
.shaper
|
||||||
pub fn handle_draw_command(&mut self, root_canvas: &mut Canvas, draw_command: DrawCommand) {
|
.shape_cached(text, style.bold, style.italic)
|
||||||
warn!("{:?}", &draw_command);
|
.iter()
|
||||||
match draw_command {
|
{
|
||||||
DrawCommand::Window {
|
canvas.draw_text_blob(blob, (x, y), &self.paint);
|
||||||
grid_id,
|
}
|
||||||
command: WindowDrawCommand::Close,
|
}
|
||||||
} => {
|
|
||||||
self.rendered_windows.remove(&grid_id);
|
if style.strikethrough {
|
||||||
}
|
let line_position = region.center_y();
|
||||||
DrawCommand::Window { grid_id, command } => {
|
self.paint
|
||||||
if let Some(rendered_window) = self.rendered_windows.remove(&grid_id) {
|
.set_color(style.special(&self.default_style.colors).to_color());
|
||||||
warn!("Window positioned {}", grid_id);
|
canvas.draw_line((x, line_position), (x + width, line_position), &self.paint);
|
||||||
let rendered_window = rendered_window.handle_window_draw_command(self, command);
|
}
|
||||||
self.rendered_windows.insert(grid_id, rendered_window);
|
|
||||||
} else if let WindowDrawCommand::Position {
|
canvas.restore();
|
||||||
grid_left,
|
}
|
||||||
grid_top,
|
|
||||||
width,
|
pub fn handle_draw_command(&mut self, root_canvas: &mut Canvas, draw_command: DrawCommand) {
|
||||||
height,
|
warn!("{:?}", &draw_command);
|
||||||
..
|
match draw_command {
|
||||||
} = command
|
DrawCommand::Window {
|
||||||
{
|
grid_id,
|
||||||
warn!("Created window {}", grid_id);
|
command: WindowDrawCommand::Close,
|
||||||
let new_window = RenderedWindow::new(
|
} => {
|
||||||
root_canvas,
|
self.rendered_windows.remove(&grid_id);
|
||||||
&self,
|
}
|
||||||
grid_id,
|
DrawCommand::Window { grid_id, command } => {
|
||||||
(grid_left as f32, grid_top as f32).into(),
|
if let Some(rendered_window) = self.rendered_windows.remove(&grid_id) {
|
||||||
width,
|
let rendered_window = rendered_window.handle_window_draw_command(self, command);
|
||||||
height,
|
self.rendered_windows.insert(grid_id, rendered_window);
|
||||||
);
|
} else if let WindowDrawCommand::Position {
|
||||||
self.rendered_windows.insert(grid_id, new_window);
|
grid_left,
|
||||||
} else {
|
grid_top,
|
||||||
error!("WindowDrawCommand sent for uninitialized grid {}", grid_id);
|
width,
|
||||||
}
|
height,
|
||||||
}
|
..
|
||||||
DrawCommand::UpdateCursor(new_cursor) => {
|
} = command
|
||||||
self.cursor_renderer.update_cursor(new_cursor);
|
{
|
||||||
}
|
warn!("Created window {}", grid_id);
|
||||||
DrawCommand::FontChanged(new_font) => {
|
let new_window = RenderedWindow::new(
|
||||||
if self.update_font(&new_font) {
|
root_canvas,
|
||||||
// Resize all the grids
|
&self,
|
||||||
}
|
grid_id,
|
||||||
}
|
(grid_left as f32, grid_top as f32).into(),
|
||||||
DrawCommand::DefaultStyleChanged(new_style) => {
|
width,
|
||||||
self.default_style = Arc::new(new_style);
|
height,
|
||||||
}
|
);
|
||||||
_ => {}
|
self.rendered_windows.insert(grid_id, new_window);
|
||||||
}
|
} else {
|
||||||
}
|
error!("WindowDrawCommand sent for uninitialized grid {}", grid_id);
|
||||||
|
}
|
||||||
pub fn draw_frame(
|
}
|
||||||
&mut self,
|
DrawCommand::UpdateCursor(new_cursor) => {
|
||||||
root_canvas: &mut Canvas,
|
self.cursor_renderer.update_cursor(new_cursor);
|
||||||
coordinate_system_helper: &CoordinateSystemHelper,
|
}
|
||||||
dt: f32,
|
DrawCommand::FontChanged(new_font) => {
|
||||||
) -> bool {
|
self.update_font(&new_font);
|
||||||
trace!("Rendering");
|
}
|
||||||
let mut font_changed = false;
|
DrawCommand::DefaultStyleChanged(new_style) => {
|
||||||
|
self.default_style = Arc::new(new_style);
|
||||||
let draw_commands: Vec<DrawCommand> = self
|
}
|
||||||
.batched_draw_command_receiver
|
DrawCommand::ModeChanged(new_mode) => {
|
||||||
.try_iter() // Iterator of Vec of DrawCommand
|
self.current_mode = new_mode;
|
||||||
.map(|batch| batch.into_iter()) // Iterator of Iterator of DrawCommand
|
}
|
||||||
.flatten() // Iterator of DrawCommand
|
_ => {}
|
||||||
.collect(); // Vec of DrawCommand
|
}
|
||||||
for draw_command in draw_commands.into_iter() {
|
}
|
||||||
if let DrawCommand::FontChanged(_) = draw_command {
|
|
||||||
font_changed = true;
|
pub fn draw_frame(
|
||||||
}
|
&mut self,
|
||||||
self.handle_draw_command(root_canvas, draw_command);
|
root_canvas: &mut Canvas,
|
||||||
}
|
coordinate_system_helper: &CoordinateSystemHelper,
|
||||||
|
dt: f32,
|
||||||
root_canvas.clear(
|
) -> bool {
|
||||||
self.default_style
|
trace!("Rendering");
|
||||||
.colors
|
let mut font_changed = false;
|
||||||
.background
|
|
||||||
.clone()
|
let draw_commands: Vec<DrawCommand> = self
|
||||||
.unwrap()
|
.batched_draw_command_receiver
|
||||||
.to_color(),
|
.try_iter() // Iterator of Vec of DrawCommand
|
||||||
);
|
.map(|batch| batch.into_iter()) // Iterator of Iterator of DrawCommand
|
||||||
|
.flatten() // Iterator of DrawCommand
|
||||||
root_canvas.save();
|
.collect(); // Vec of DrawCommand
|
||||||
|
for draw_command in draw_commands.into_iter() {
|
||||||
if let Some(root_window) = self.rendered_windows.get(&1) {
|
if let DrawCommand::FontChanged(_) = draw_command {
|
||||||
let clip_rect = root_window.pixel_region(self.font_width, self.font_height);
|
font_changed = true;
|
||||||
root_canvas.clip_rect(&clip_rect, None, Some(false));
|
}
|
||||||
}
|
self.handle_draw_command(root_canvas, draw_command);
|
||||||
|
}
|
||||||
coordinate_system_helper.use_logical_coordinates(root_canvas);
|
|
||||||
|
root_canvas.clear(
|
||||||
let windows: Vec<&mut RenderedWindow> = {
|
self.default_style
|
||||||
let (mut root_windows, mut floating_windows): (
|
.colors
|
||||||
Vec<&mut RenderedWindow>,
|
.background
|
||||||
Vec<&mut RenderedWindow>,
|
.clone()
|
||||||
) = self
|
.unwrap()
|
||||||
.rendered_windows
|
.to_color(),
|
||||||
.values_mut()
|
);
|
||||||
.filter(|window| !window.hidden)
|
|
||||||
.partition(|window| !window.floating);
|
root_canvas.save();
|
||||||
|
|
||||||
root_windows
|
if let Some(root_window) = self.rendered_windows.get(&1) {
|
||||||
.sort_by(|window_a, window_b| window_a.id.partial_cmp(&window_b.id).unwrap());
|
let clip_rect = root_window.pixel_region(self.font_width, self.font_height);
|
||||||
floating_windows
|
root_canvas.clip_rect(&clip_rect, None, Some(false));
|
||||||
.sort_by(|window_a, window_b| window_a.id.partial_cmp(&window_b.id).unwrap());
|
}
|
||||||
|
|
||||||
root_windows
|
coordinate_system_helper.use_logical_coordinates(root_canvas);
|
||||||
.into_iter()
|
|
||||||
.chain(floating_windows.into_iter())
|
let default_background = self.get_default_background();
|
||||||
.collect()
|
let font_width = self.font_width;
|
||||||
};
|
let font_height = self.font_height;
|
||||||
|
|
||||||
let settings = SETTINGS.get::<RendererSettings>();
|
let windows: Vec<&mut RenderedWindow> = {
|
||||||
let font_width = self.font_width;
|
let (mut root_windows, mut floating_windows): (
|
||||||
let font_height = self.font_height;
|
Vec<&mut RenderedWindow>,
|
||||||
self.window_regions = windows
|
Vec<&mut RenderedWindow>,
|
||||||
.into_iter()
|
) = self
|
||||||
.map(|window| window.draw(root_canvas, &settings, font_width, font_height, dt))
|
.rendered_windows
|
||||||
.collect();
|
.values_mut()
|
||||||
|
.filter(|window| !window.hidden)
|
||||||
self.cursor_renderer.draw(
|
.partition(|window| !window.floating);
|
||||||
&self.default_style.colors,
|
|
||||||
(self.font_width, self.font_height),
|
root_windows
|
||||||
&mut self.shaper,
|
.sort_by(|window_a, window_b| window_a.id.partial_cmp(&window_b.id).unwrap());
|
||||||
root_canvas,
|
floating_windows
|
||||||
dt,
|
.sort_by(|window_a, window_b| window_a.id.partial_cmp(&window_b.id).unwrap());
|
||||||
);
|
|
||||||
|
root_windows
|
||||||
root_canvas.restore();
|
.into_iter()
|
||||||
|
.chain(floating_windows.into_iter())
|
||||||
font_changed
|
.collect()
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
let settings = SETTINGS.get::<RendererSettings>();
|
||||||
|
self.window_regions = windows
|
||||||
|
.into_iter()
|
||||||
|
.map(|window| {
|
||||||
|
window.draw(
|
||||||
|
root_canvas,
|
||||||
|
&settings,
|
||||||
|
default_background,
|
||||||
|
font_width,
|
||||||
|
font_height,
|
||||||
|
dt,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let windows = &self.rendered_windows;
|
||||||
|
self.cursor_renderer
|
||||||
|
.update_cursor_destination(font_width, font_height, windows);
|
||||||
|
|
||||||
|
self.cursor_renderer.draw(
|
||||||
|
&self.default_style.colors,
|
||||||
|
(self.font_width, self.font_height),
|
||||||
|
&self.current_mode,
|
||||||
|
&mut self.shaper,
|
||||||
|
root_canvas,
|
||||||
|
dt,
|
||||||
|
);
|
||||||
|
|
||||||
|
root_canvas.restore();
|
||||||
|
|
||||||
|
font_changed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue