Merge pull request #159 from Kethku/framerate-independent-cursor

Framerate independent cursor
macos-click-through
Keith Simmons 5 years ago committed by GitHub
commit 2b76e0e665
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -55,7 +55,7 @@ jobs:
run: |
curl -sL "http://packages.lunarg.com/lunarg-signing-key-pub.asc" | sudo apt-key add -
sudo curl -sLo "/etc/apt/sources.list.d/lunarg-vulkan-1.2.131-bionic.list" "http://packages.lunarg.com/vulkan/1.2.131/lunarg-vulkan-1.2.131-bionic.list"
sudo apt-get update -y
sudo apt-get update -y --ignore-missing
sudo apt-get install -y vulkan-sdk
- name: Install dependencies

@ -25,6 +25,7 @@ pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50);
fn main() {
window::initialize_settings();
redraw_scheduler::initialize_settings();
renderer::cursor_renderer::initialize_settings();
initialize(&BRIDGE);
ui_loop();

@ -0,0 +1,81 @@
use skulpin::skia_safe::Point;
#[allow(dead_code)]
pub fn ease_linear(t: f32) -> f32 {
t
}
#[allow(dead_code)]
pub fn ease_in_quad(t: f32) -> f32 {
t * t
}
#[allow(dead_code)]
pub fn ease_out_quad(t: f32) -> f32 {
-t * (t - 2.0)
}
#[allow(dead_code)]
pub fn ease_in_out_quad(t: f32) -> f32 {
if t < 0.5 {
2.0 * t * t
} else {
let n = t * 2.0 - 1.0;
-0.5 * (n * (n - 2.0) - 1.0)
}
}
#[allow(dead_code)]
pub fn ease_in_cubic(t: f32) -> f32 {
t * t * t
}
#[allow(dead_code)]
pub fn ease_out_cubic(t: f32) -> f32 {
let n = t - 1.0;
n * n * n + 1.0
}
#[allow(dead_code)]
pub fn ease_in_out_cubic(t: f32) -> f32 {
let n = 2.0 * t;
if n < 1.0 {
0.5 * n * n * n
} else {
let n = n - 2.0;
0.5 * (n * n * n + 2.0)
}
}
#[allow(dead_code)]
pub fn ease_in_expo(t: f32) -> f32 {
if t == 0.0 {
0.0
} else {
2.0f32.powf(10.0 * (t - 1.0))
}
}
#[allow(dead_code)]
pub fn ease_out_expo(t: f32) -> f32 {
if t == 1.0 {
1.0
} else {
1.0 - 2.0f32.powf(-10.0 * t)
}
}
pub fn lerp(start: f32, end: f32, t: f32) -> f32 {
start + (end - start) * t
}
pub fn ease(ease_func: fn(f32) -> f32, start: f32, end: f32, t: f32) -> f32 {
lerp(start, end, ease_func(t))
}
pub fn ease_point(ease_func: fn(f32) -> f32, start: Point, end: Point, t: f32) -> Point {
Point {
x: ease(ease_func, start.x, end.x, t),
y: ease(ease_func, start.y, end.y, t),
}
}

@ -0,0 +1,401 @@
use log::error;
use skulpin::skia_safe::{paint::Style, BlendMode, Canvas, Color, Paint, Point, Rect};
use super::animation_utils::*;
use super::CursorSettings;
use crate::editor::{Colors, Cursor};
use crate::settings::*;
pub trait CursorVfx {
fn update(
&mut self,
settings: &CursorSettings,
current_cursor_destination: Point,
font_size: (f32, f32),
dt: f32,
) -> bool;
fn restart(&mut self, position: Point);
fn render(
&self,
settings: &CursorSettings,
canvas: &mut Canvas,
cursor: &Cursor,
colors: &Colors,
font_size: (f32, f32),
);
}
#[derive(Clone, PartialEq)]
pub enum HighlightMode {
SonicBoom,
Ripple,
Wireframe,
}
#[derive(Clone, PartialEq)]
pub enum TrailMode {
Railgun,
Torpedo,
PixieDust,
}
#[derive(Clone, PartialEq)]
pub enum VfxMode {
Highlight(HighlightMode),
Trail(TrailMode),
Disabled,
}
impl FromValue for VfxMode {
fn from_value(&mut self, value: Value) {
if value.is_str() {
*self = match value.as_str().unwrap() {
"sonicboom" => VfxMode::Highlight(HighlightMode::SonicBoom),
"ripple" => VfxMode::Highlight(HighlightMode::Ripple),
"wireframe" => VfxMode::Highlight(HighlightMode::Wireframe),
"railgun" => VfxMode::Trail(TrailMode::Railgun),
"torpedo" => VfxMode::Trail(TrailMode::Torpedo),
"pixiedust" => VfxMode::Trail(TrailMode::PixieDust),
"" => VfxMode::Disabled,
value => {
error!("Expected a VfxMode name, but received {:?}", value);
return;
}
};
} else {
error!("Expected a VfxMode string, but received {:?}", value);
}
}
}
impl From<VfxMode> for Value {
fn from(mode: VfxMode) -> Self {
match mode {
VfxMode::Highlight(HighlightMode::SonicBoom) => Value::from("sonicboom"),
VfxMode::Highlight(HighlightMode::Ripple) => Value::from("ripple"),
VfxMode::Highlight(HighlightMode::Wireframe) => Value::from("wireframe"),
VfxMode::Trail(TrailMode::Railgun) => Value::from("railgun"),
VfxMode::Trail(TrailMode::Torpedo) => Value::from("torpedo"),
VfxMode::Trail(TrailMode::PixieDust) => Value::from("pixiedust"),
VfxMode::Disabled => Value::from(""),
}
}
}
pub fn new_cursor_vfx(mode: &VfxMode) -> Option<Box<dyn CursorVfx>> {
match mode {
VfxMode::Highlight(mode) => Some(Box::new(PointHighlight::new(mode))),
VfxMode::Trail(mode) => Some(Box::new(ParticleTrail::new(mode))),
VfxMode::Disabled => None,
}
}
pub struct PointHighlight {
t: f32,
center_position: Point,
mode: HighlightMode,
}
impl PointHighlight {
pub fn new(mode: &HighlightMode) -> PointHighlight {
PointHighlight {
t: 0.0,
center_position: Point::new(0.0, 0.0),
mode: mode.clone(),
}
}
}
impl CursorVfx for PointHighlight {
fn update(
&mut self,
_settings: &CursorSettings,
_current_cursor_destination: Point,
_font_size: (f32, f32),
dt: f32,
) -> bool {
self.t = (self.t + dt * 5.0).min(1.0); // TODO - speed config
self.t < 1.0
}
fn restart(&mut self, position: Point) {
self.t = 0.0;
self.center_position = position;
}
fn render(
&self,
settings: &CursorSettings,
canvas: &mut Canvas,
cursor: &Cursor,
colors: &Colors,
font_size: (f32, f32),
) {
if self.t == 1.0 {
return;
}
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
paint.set_blend_mode(BlendMode::SrcOver);
let base_color: Color = cursor.background(&colors).to_color();
let alpha = ease(ease_in_quad, settings.vfx_opacity, 0.0, self.t) as u8;
let color = Color::from_argb(alpha, base_color.r(), base_color.g(), base_color.b());
paint.set_color(color);
let size = 3.0 * font_size.1;
let radius = self.t * size;
let hr = radius * 0.5;
let rect = Rect::from_xywh(
self.center_position.x - hr,
self.center_position.y - hr,
radius,
radius,
);
match self.mode {
HighlightMode::SonicBoom => {
canvas.draw_oval(&rect, &paint);
}
HighlightMode::Ripple => {
paint.set_style(Style::Stroke);
paint.set_stroke_width(font_size.1 * 0.2);
canvas.draw_oval(&rect, &paint);
}
HighlightMode::Wireframe => {
paint.set_style(Style::Stroke);
paint.set_stroke_width(font_size.1 * 0.2);
canvas.draw_rect(&rect, &paint);
}
}
}
}
#[derive(Clone)]
struct ParticleData {
pos: Point,
speed: Point,
lifetime: f32,
}
pub struct ParticleTrail {
particles: Vec<ParticleData>,
previous_cursor_dest: Point,
trail_mode: TrailMode,
rng: RngState,
}
impl ParticleTrail {
pub fn new(trail_mode: &TrailMode) -> ParticleTrail {
ParticleTrail {
particles: vec![],
previous_cursor_dest: Point::new(0.0, 0.0),
trail_mode: trail_mode.clone(),
rng: RngState::new(),
}
}
fn add_particle(&mut self, pos: Point, speed: Point, lifetime: f32) {
self.particles.push(ParticleData {
pos,
speed,
lifetime,
});
}
// Note this method doesn't keep particles in order
fn remove_particle(&mut self, idx: usize) {
self.particles[idx] = self.particles[self.particles.len() - 1].clone();
self.particles.pop();
}
}
impl CursorVfx for ParticleTrail {
fn update(
&mut self,
settings: &CursorSettings,
current_cursor_dest: Point,
font_size: (f32, f32),
dt: f32) -> bool {
// Update lifetimes and remove dead particles
let mut i = 0;
while i < self.particles.len() {
let particle: &mut ParticleData = &mut self.particles[i];
particle.lifetime -= dt;
if particle.lifetime <= 0.0 {
self.remove_particle(i);
} else {
i += 1;
}
}
// Update particle positions
for i in 0..self.particles.len() {
let particle = &mut self.particles[i];
particle.pos += particle.speed * dt;
}
// Spawn new particles
if current_cursor_dest != self.previous_cursor_dest {
let travel = current_cursor_dest - self.previous_cursor_dest;
let travel_distance = travel.length();
// Increase amount of particles when cursor travels further
let particle_count =
((travel_distance / font_size.0).powf(1.5) * settings.vfx_particle_density * 0.01) as usize;
let prev_p = self.previous_cursor_dest;
for i in 0..particle_count {
let t = i as f32 / (particle_count as f32);
let speed = match self.trail_mode {
TrailMode::Railgun => {
let phase = t * 60.0; // TODO -- Hardcoded spiral curl
Point::new(phase.sin(), phase.cos()) * 20.0 // TODO -- Hardcoded spiral outward speed
}
TrailMode::Torpedo => {
self.rng.rand_dir_normalized() * 10.0 // TODO -- Hardcoded particle speed
}
TrailMode::PixieDust => {
let base_dir = self.rng.rand_dir_normalized();
let dir = Point::new(base_dir.x * 0.5, 0.4 + base_dir.y.abs());
dir * 30.0 // TODO -- hardcoded particle speed
}
};
// Distribute particles along the travel distance, with a random offset to make it
// look random
let pos = match self.trail_mode {
TrailMode::Railgun => prev_p + travel * t,
TrailMode::PixieDust | TrailMode::Torpedo => {
prev_p + travel * self.rng.next_f32() + Point::new(0.0, font_size.1 * 0.5)
}
};
self.add_particle(pos, speed, t * settings.vfx_particle_lifetime);
}
self.previous_cursor_dest = current_cursor_dest;
}
// Keep animating as long as there are particles alive
!self.particles.is_empty()
}
fn restart(&mut self, _position: Point) {}
fn render(
&self,
settings: &CursorSettings,
canvas: &mut Canvas,
cursor: &Cursor,
colors: &Colors,
font_size: (f32, f32),
) {
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
match self.trail_mode {
TrailMode::Torpedo | TrailMode::Railgun => {
paint.set_style(Style::Stroke);
paint.set_stroke_width(font_size.1 * 0.2);
}
_ => {}
}
let base_color: Color = cursor.background(&colors).to_color();
paint.set_blend_mode(BlendMode::SrcOver);
self.particles.iter().for_each(|particle| {
let l = particle.lifetime / settings.vfx_particle_lifetime;
let alpha = (l * settings.vfx_opacity) as u8;
let color = Color::from_argb(alpha, base_color.r(), base_color.g(), base_color.b());
paint.set_color(color);
let radius = match self.trail_mode {
TrailMode::Torpedo | TrailMode::Railgun => font_size.0 * 0.5 * l,
TrailMode::PixieDust => font_size.0 * 0.2,
};
let hr = radius * 0.5;
let rect = Rect::from_xywh(particle.pos.x - hr, particle.pos.y - hr, radius, radius);
match self.trail_mode {
TrailMode::Torpedo | TrailMode::Railgun => {
canvas.draw_oval(&rect, &paint);
}
TrailMode::PixieDust => {
canvas.draw_rect(&rect, &paint);
}
}
});
}
}
// Random number generator based on http://www.pcg-random.org/
struct RngState {
state: u64,
inc: u64,
}
impl RngState {
fn new() -> RngState {
RngState {
state: 0x853C49E6748FEA9Bu64,
inc: (0xDA3E39CB94B95BDBu64 << 1) | 1,
}
}
fn next(&mut self) -> u32 {
let old_state = self.state;
// Implementation copied from:
// https://rust-random.github.io/rand/src/rand_pcg/pcg64.rs.html#103
let new_state = old_state
.wrapping_mul(6_364_136_223_846_793_005u64)
.wrapping_add(self.inc);
self.state = new_state;
const ROTATE: u32 = 59; // 64 - 5
const XSHIFT: u32 = 18; // (5 + 32) / 2
const SPARE: u32 = 27; // 64 - 32 - 5
let rot = (old_state >> ROTATE) as u32;
let xsh = (((old_state >> XSHIFT) ^ old_state) >> SPARE) as u32;
xsh.rotate_right(rot)
}
fn next_f32(&mut self) -> f32 {
let v = self.next();
// In C we'd do ldexp(v, -32) to bring a number in the range [0,2^32) down to [0,1) range.
// But as we don't have ldexp in Rust, we're implementing the same idea (subtracting 32
// from the floating point exponent) manually.
// First, extract exponent bits
let float_bits = (v as f64).to_bits();
let exponent = (float_bits >> 52) & ((1 << 11) - 1);
// Set exponent for [0-1) range
let new_exponent = exponent.max(32) - 32;
// Build the new f64 value from the old mantissa and sign, and the new exponent
let new_bits = (new_exponent << 52) | (float_bits & 0x801F_FFFF_FFFF_FFFFu64);
f64::from_bits(new_bits) as f32
}
// Produces a random vector with x and y in the [-1,1) range
// Note: Vector is not normalized.
fn rand_dir(&mut self) -> Point {
let x = self.next_f32();
let y = self.next_f32();
Point::new(x * 2.0 - 1.0, y * 2.0 - 1.0)
}
fn rand_dir_normalized(&mut self) -> Point {
let mut v = self.rand_dir();
v.normalize();
v
}
}

@ -2,17 +2,54 @@ use std::time::{Duration, Instant};
use skulpin::skia_safe::{Canvas, Paint, Path, Point};
use crate::settings::*;
use crate::renderer::CachingShaper;
use crate::editor::{EDITOR, Colors, Cursor, CursorShape};
use crate::redraw_scheduler::REDRAW_SCHEDULER;
const AVERAGE_MOTION_PERCENTAGE: f32 = 0.7;
const MOTION_PERCENTAGE_SPREAD: f32 = 0.5;
mod animation_utils;
use animation_utils::*;
mod cursor_vfx;
const COMMAND_LINE_DELAY_FRAMES: u64 = 5;
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)];
// ----------------------------------------------------------------------------
#[derive(Clone)]
pub struct CursorSettings {
animation_length: f32,
trail_size: f32,
vfx_mode: cursor_vfx::VfxMode,
vfx_opacity: f32,
vfx_particle_lifetime: f32,
vfx_particle_density: f32,
}
pub fn initialize_settings() {
SETTINGS.set(&CursorSettings {
animation_length: 0.13,
trail_size: 0.7,
vfx_mode: cursor_vfx::VfxMode::Disabled,
vfx_opacity: 200.0,
vfx_particle_lifetime: 1.2,
vfx_particle_density: 7.0,
});
register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length);
register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size);
register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode);
register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity);
register_nvim_setting!("cursor_vfx_particle_lifetime", CursorSettings::vfx_particle_lifetime);
register_nvim_setting!("cursor_vfx_particle_density", CursorSettings::vfx_particle_density);
}
// ----------------------------------------------------------------------------
enum BlinkState {
Waiting,
On,
@ -85,47 +122,89 @@ impl BlinkStatus {
#[derive(Debug, Clone)]
pub struct Corner {
pub current_position: Point,
pub relative_position: Point,
start_position: Point,
current_position: Point,
relative_position: Point,
previous_destination: Point,
t: f32,
}
impl Corner {
pub fn new(relative_position: Point) -> Corner {
pub fn new() -> Corner {
Corner {
start_position: Point::new(0.0, 0.0),
current_position: Point::new(0.0, 0.0),
relative_position
relative_position: Point::new(0.0, 0.0),
previous_destination: Point::new(-1000.0, -1000.0),
t: 0.0,
}
}
pub fn update(&mut self, font_dimensions: Point, destination: Point) -> bool {
let relative_scaled_position: Point =
(self.relative_position.x * font_dimensions.x, self.relative_position.y * font_dimensions.y).into();
pub fn update(&mut self, settings: &CursorSettings, font_dimensions: Point, destination: Point, dt: f32) -> bool {
// Update destination if needed
let mut immediate_movement = false;
if destination != self.previous_destination {
let travel_distance = destination - self.previous_destination;
let chars_travel_x = travel_distance.x / font_dimensions.x;
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 {
// We're moving one character to the right. Make movement immediate to avoid lag
// while typing
immediate_movement = true;
}
self.t = 0.0;
self.start_position = self.current_position;
self.previous_destination = destination;
}
// Check first if animation's over
if self.t > 1.0 {
return false;
}
// Calculate window-space destination for corner
let relative_scaled_position: Point = (
self.relative_position.x * font_dimensions.x,
self.relative_position.y * font_dimensions.y,
).into();
let corner_destination = destination + relative_scaled_position;
let delta = corner_destination - self.current_position;
if immediate_movement {
self.t = 1.0;
self.current_position = corner_destination;
return true;
}
// 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
// back
let travel_direction = {
let mut d = destination - self.current_position;
d.normalize();
d
};
if delta.length() > 0.0 {
// Project relative_scaled_position (actual possition of the corner relative to the
// center of the cursor) onto the remaining distance vector. This gives us the relative
// distance to the destination along the delta vector which we can then use to scale the
// motion_percentage.
let motion_scale = delta.dot(relative_scaled_position) / delta.length() / font_dimensions.length();
let corner_direction = {
let mut d = self.relative_position;
d.normalize();
d
};
// The motion_percentage is then equal to the motion_scale factor times the
// MOTION_PERCENTAGE_SPREAD and added to the AVERAGE_MOTION_PERCENTAGE. This way all of
// the percentages are positive and spread out by the spread constant.
let motion_percentage = motion_scale * MOTION_PERCENTAGE_SPREAD + AVERAGE_MOTION_PERCENTAGE;
let direction_alignment = travel_direction.dot(corner_direction);
// Then the current_position is animated by taking the delta vector, multiplying it by
// the motion_percentage and adding the resulting value to the current position causing
// the cursor to "jump" toward the target destination. Since further away corners jump
// slower, the cursor appears to smear toward the destination in a satisfying and
// visually trackable way.
let delta = corner_destination - self.current_position;
self.current_position += delta * motion_percentage;
if self.t == 1.0 {
// We are at destination, move t out of 0-1 range to stop the animation
self.t = 2.0;
} else {
let corner_dt = dt * lerp(1.0, 1.0 - settings.trail_size, -direction_alignment);
self.t = (self.t + corner_dt / settings.animation_length).min(1.0)
}
delta.length() > 0.001
self.current_position =
ease_point(ease_out_expo, self.start_position, corner_destination, self.t);
true
}
}
@ -133,16 +212,23 @@ pub struct CursorRenderer {
pub corners: Vec<Corner>,
pub previous_position: (u64, u64),
pub command_line_delay: u64,
blink_status: BlinkStatus
blink_status: BlinkStatus,
previous_cursor_shape: Option<CursorShape>,
cursor_vfx: Option<Box<dyn cursor_vfx::CursorVfx>>,
previous_vfx_mode: cursor_vfx::VfxMode,
}
impl CursorRenderer {
pub fn new() -> CursorRenderer {
let mut renderer = CursorRenderer {
corners: vec![Corner::new((0.0, 0.0).into()); 4],
corners: vec![Corner::new(); 4],
previous_position: (0, 0),
command_line_delay: 0,
blink_status: BlinkStatus::new()
blink_status: BlinkStatus::new(),
previous_cursor_shape: None,
//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,
};
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE);
renderer
@ -165,7 +251,10 @@ impl CursorRenderer {
// instead of the top.
CursorShape::Horizontal => (x, -((-y + 0.5) * cell_percentage - 0.5)).into()
},
t: 0.0,
start_position: corner.current_position,
.. corner
}
})
.collect::<Vec<Corner>>();
@ -174,10 +263,20 @@ impl CursorRenderer {
pub fn draw(&mut self,
cursor: Cursor, default_colors: &Colors,
font_width: f32, font_height: f32,
paint: &mut Paint, shaper: &mut CachingShaper,
canvas: &mut Canvas) {
shaper: &mut CachingShaper, canvas: &mut Canvas,
dt: f32) {
let render = self.blink_status.update_status(&cursor);
let settings = SETTINGS.get::<CursorSettings>();
if settings.vfx_mode != self.previous_vfx_mode {
self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode);
self.previous_vfx_mode = settings.vfx_mode.clone();
}
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
paint.set_anti_alias(true);
self.previous_position = {
let editor = EDITOR.lock();
let (_, grid_y) = cursor.position;
@ -196,6 +295,7 @@ impl CursorRenderer {
}
};
let (grid_x, grid_y) = self.previous_position;
let (character, font_dimensions): (String, Point) = {
@ -219,14 +319,31 @@ impl CursorRenderer {
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
let center_destination = destination + font_dimensions * 0.5;
let new_cursor = Some(cursor.shape.clone());
if self.previous_cursor_shape != new_cursor {
self.previous_cursor_shape = new_cursor;
self.set_cursor_shape(&cursor.shape, cursor.cell_percentage.unwrap_or(DEFAULT_CELL_PERCENTAGE));
if let Some(vfx) = self.cursor_vfx.as_mut() {
vfx.restart(center_destination);
}
}
let mut animating = false;
if !center_destination.is_zero() {
for corner in self.corners.iter_mut() {
let corner_animating = corner.update(font_dimensions, center_destination);
animating = animating || corner_animating;
let corner_animating = corner.update(&settings, font_dimensions, center_destination, dt);
animating |= corner_animating;
}
let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() {
vfx.update(&settings, center_destination, (font_width, font_height), dt)
}else{
false
};
animating |= vfx_animating;
}
if animating || self.command_line_delay != 0 {
@ -257,6 +374,10 @@ impl CursorRenderer {
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));
}
}
}
}

@ -6,13 +6,14 @@ use skulpin::skia_safe::gpu::SurfaceOrigin;
use log::trace;
mod caching_shaper;
mod cursor_renderer;
pub mod cursor_renderer;
pub use caching_shaper::CachingShaper;
use cursor_renderer::CursorRenderer;
use crate::editor::{EDITOR, Style};
pub struct Renderer {
surface: Option<Surface>,
paint: Paint,
@ -107,7 +108,9 @@ impl Renderer {
canvas.restore();
}
pub fn draw(&mut self, gpu_canvas: &mut Canvas, coordinate_system_helper: &CoordinateSystemHelper) -> bool {
pub fn draw(&mut self, gpu_canvas: &mut Canvas,
coordinate_system_helper: &CoordinateSystemHelper,
dt: f32) -> bool {
trace!("Rendering");
let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = {
let mut editor = EDITOR.lock();
@ -162,8 +165,7 @@ impl Renderer {
self.cursor_renderer.draw(
cursor, &default_style.colors,
self.font_width, self.font_height,
&mut self.paint, &mut self.shaper,
gpu_canvas);
&mut self.shaper, gpu_canvas, dt);
font_changed
}

@ -26,12 +26,15 @@ pub trait FromValue {
// FromValue implementations for most typical types
impl FromValue for f32 {
fn from_value(&mut self, value: Value) {
if value.is_f32() {
if value.is_f64() {
*self = value.as_f64().unwrap() as f32;
}else if value.is_i64() {
*self = value.as_i64().unwrap() as f32;
}else if value.is_u64() {
*self = value.as_u64().unwrap() as f32;
}else{
error!("Setting expected an f32, but received {:?}", value);
}
}
}
@ -81,6 +84,8 @@ impl FromValue for bool {
fn from_value(&mut self, value: Value) {
if value.is_bool() {
*self = value.as_bool().unwrap();
}else if value.is_u64() {
*self = value.as_u64().unwrap() != 0;
}else{
error!("Setting expected a string, but received {:?}", value);
}

@ -244,7 +244,9 @@ impl WindowWrapper {
if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::<WindowSettings>().no_idle {
let renderer = &mut self.renderer;
if self.skulpin_renderer.draw(&self.window, |canvas, coordinate_system_helper| {
if renderer.draw(canvas, coordinate_system_helper) {
let dt = 1.0 / (SETTINGS.get::<WindowSettings>().refresh_rate as f32);
if renderer.draw(canvas, coordinate_system_helper, dt) {
handle_new_grid_size(current_size, &renderer)
}
}).is_err() {

Loading…
Cancel
Save