* add husky dev dep

* clippy suggestions

* bump version

* save cargo lock

* fix epsilon issue for linux

* forgot to save

* my mistake
macos-click-through
j4qfrost 5 years ago committed by GitHub
parent 92757ea70c
commit 1892073c8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

83
Cargo.lock generated

@ -103,7 +103,7 @@ checksum = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
"syn 1.0.19",
]
[[package]]
@ -237,6 +237,12 @@ dependencies = [
"nix",
]
[[package]]
name = "cargo-husky"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b02b629252fe8ef6460461409564e2c21d0c8e77e0944f3d189ff06c4e932ad"
[[package]]
name = "cc"
version = "1.0.52"
@ -442,13 +448,13 @@ dependencies = [
[[package]]
name = "core-video-sys"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dc065219542086f72d1e9f7aadbbab0989e980263695d129d502082d063a9d0"
checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828"
dependencies = [
"cfg-if",
"core-foundation-sys 0.6.2",
"core-graphics 0.17.3",
"core-foundation-sys 0.7.0",
"core-graphics 0.19.0",
"libc",
"objc",
]
@ -557,7 +563,7 @@ checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
"syn 1.0.19",
]
[[package]]
@ -657,9 +663,9 @@ dependencies = [
[[package]]
name = "euclid"
version = "0.20.10"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a5b0c779cd0b744c73a1d2083faf181080d696903cdad99a3b03d015d7030"
checksum = "667703ececa1ac04d1d40ae0c0fd6401b91d8caccfda3a65458ca8ee5dfedf1c"
dependencies = [
"num-traits",
]
@ -875,7 +881,7 @@ dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
"syn 1.0.19",
]
[[package]]
@ -1289,9 +1295,9 @@ dependencies = [
[[package]]
name = "mockall"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c9eefc7768ee7a28a09d64e40203d57ad20af8525b7428d5f2f55d8c621984"
checksum = "256489d4d106cd2bc9e98ed0337402db0044de0621745d5d9eb70a14295ff77b"
dependencies = [
"cfg-if",
"downcast",
@ -1304,14 +1310,14 @@ dependencies = [
[[package]]
name = "mockall_derive"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447326d4e6d99ea272b6e5599cbbfc1e3407c23a856ccf1eb9427ad73267376f"
checksum = "826e14e8643cb12103b56efb963e5f9640b69b0f7bdcc460002092df4b0e959f"
dependencies = [
"cfg-if",
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
"syn 1.0.19",
]
[[package]]
@ -1320,6 +1326,7 @@ version = "0.6.0"
dependencies = [
"anyhow",
"async-trait",
"cargo-husky",
"cfg-if",
"derive-new",
"euclid",
@ -1565,29 +1572,29 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project"
version = "0.4.9"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2"
checksum = "82c3bfbfb5bb42f99498c7234bbd768c220eb0cea6818259d0d18a1aa3d2595d"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.9"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a"
checksum = "ccbf6449dcfb18562c015526b085b8df1aa3cdab180af8ec2ebd300a3bd28f63"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
"syn 1.0.19",
]
[[package]]
name = "pin-project-lite"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f"
[[package]]
name = "pin-utils"
@ -1862,7 +1869,7 @@ checksum = "60cacc306d294556771c6e92737ba7e6be0264144bc46dd713a14ef384b0d6b8"
dependencies = [
"quote 1.0.4",
"rust-embed-utils",
"syn 1.0.18",
"syn 1.0.19",
"walkdir",
]
@ -2060,9 +2067,9 @@ dependencies = [
[[package]]
name = "skia-bindings"
version = "0.27.2"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef181a694c8e2961ae2d08acbd814eba9e8840b1523d6c4f8c1fd8881b38394"
checksum = "bfa4cc006be07441ba7129e9ac39c750af5d5120759e95a3ba121d9c6ca94c0e"
dependencies = [
"bindgen",
"cc",
@ -2077,9 +2084,9 @@ dependencies = [
[[package]]
name = "skia-safe"
version = "0.27.2"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f903a58575ccbe0c24c375c58bb0e5a1ff553f813b01a4baf9521682de87e77d"
checksum = "59cfb4ded0c38514114de4b0835e9aea335382f0e318a365be62af40dd904faa"
dependencies = [
"bitflags",
"lazy_static",
@ -2101,8 +2108,8 @@ dependencies = [
[[package]]
name = "skulpin"
version = "0.9.2"
source = "git+https://github.com/aclysma/skulpin#9ab2c72fef9b903b70faad369c6fd60ffea82727"
version = "0.9.4"
source = "git+https://github.com/aclysma/skulpin#6139a1f158080b5007b81c60110162bbd4a9d3a9"
dependencies = [
"log",
"skulpin-app-winit",
@ -2114,7 +2121,7 @@ dependencies = [
[[package]]
name = "skulpin-app-winit"
version = "0.3.0"
source = "git+https://github.com/aclysma/skulpin#9ab2c72fef9b903b70faad369c6fd60ffea82727"
source = "git+https://github.com/aclysma/skulpin#6139a1f158080b5007b81c60110162bbd4a9d3a9"
dependencies = [
"log",
"skulpin-renderer",
@ -2124,7 +2131,7 @@ dependencies = [
[[package]]
name = "skulpin-renderer"
version = "0.3.1"
source = "git+https://github.com/aclysma/skulpin#9ab2c72fef9b903b70faad369c6fd60ffea82727"
source = "git+https://github.com/aclysma/skulpin#6139a1f158080b5007b81c60110162bbd4a9d3a9"
dependencies = [
"ash",
"log",
@ -2134,8 +2141,8 @@ dependencies = [
[[package]]
name = "skulpin-renderer-sdl2"
version = "0.3.1"
source = "git+https://github.com/aclysma/skulpin#9ab2c72fef9b903b70faad369c6fd60ffea82727"
version = "0.3.3"
source = "git+https://github.com/aclysma/skulpin#6139a1f158080b5007b81c60110162bbd4a9d3a9"
dependencies = [
"log",
"sdl2",
@ -2145,7 +2152,7 @@ dependencies = [
[[package]]
name = "skulpin-renderer-winit"
version = "0.3.1"
source = "git+https://github.com/aclysma/skulpin#9ab2c72fef9b903b70faad369c6fd60ffea82727"
source = "git+https://github.com/aclysma/skulpin#6139a1f158080b5007b81c60110162bbd4a9d3a9"
dependencies = [
"cocoa",
"log",
@ -2225,9 +2232,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.18"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213"
checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
@ -2337,7 +2344,7 @@ checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
"syn 1.0.19",
]
[[package]]
@ -2407,9 +2414,9 @@ checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
[[package]]
name = "vec_map"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"

@ -36,6 +36,11 @@ which = "3.1"
mockall = "0.7.0"
rand = "0.7"
[dev-dependencies.cargo-husky]
version = "1"
default-features = false
features = ["precommit-hook", "run-cargo-test", "run-cargo-clippy", "run-cargo-fmt"]
[target.'cfg(windows)'.dependencies]
winapi = "0.3.8"

@ -59,12 +59,12 @@ fn append_modifiers(
gui: bool,
) -> String {
let mut result = keycode_text.to_string();
let mut special = special;
if result == "<" {
let mut special = if result == "<" {
result = "lt".to_string();
special = true;
}
true
} else {
special
};
if shift {
special = true;

@ -42,7 +42,7 @@ impl UiCommand {
action,
position: (grid_x, grid_y),
} => {
if { EDITOR.lock().mouse_enabled } {
if EDITOR.lock().mouse_enabled {
nvim.input_mouse("left", &action, "", 0, grid_y as i64, grid_x as i64)
.await
.expect("Mouse Input Failed");
@ -52,14 +52,14 @@ impl UiCommand {
direction,
position: (grid_x, grid_y),
} => {
if { EDITOR.lock().mouse_enabled } {
if EDITOR.lock().mouse_enabled {
nvim.input_mouse("wheel", &direction, "", 0, grid_y as i64, grid_x as i64)
.await
.expect("Mouse Scroll Failed");
}
}
UiCommand::Drag(grid_x, grid_y) => {
if { EDITOR.lock().mouse_enabled } {
if EDITOR.lock().mouse_enabled {
nvim.input_mouse("left", "drag", "", 0, grid_y as i64, grid_x as i64)
.await
.expect("Mouse Drag Failed");

@ -79,11 +79,10 @@ impl CharacterGrid {
}
pub fn set_characters_all(&mut self, value: GridCell) {
let cloned_value = value.clone();
self.characters.clear();
self.characters
.resize_with((self.width * self.height) as usize, || {
cloned_value.as_ref().cloned()
value.as_ref().cloned()
});
}

@ -301,11 +301,8 @@ impl Editor {
fn set_option(&mut self, gui_option: GuiOption) {
trace!("Option set {:?}", &gui_option);
match gui_option {
GuiOption::GuiFont(guifont) => {
self.guifont = Some(guifont);
}
_ => {}
if let GuiOption::GuiFont(guifont) = gui_option {
self.guifont = Some(guifont);
}
}
}

@ -79,7 +79,7 @@ impl ExtendedFontFamily {
pub fn from_normal_font_family(fonts: &[Handle]) -> ExtendedFontFamily {
let mut family = ExtendedFontFamily::new();
for font in fonts.into_iter() {
for font in fonts.iter() {
if let Ok(font) = font.load() {
family.add_font(SkriboFont::new(font));
}
@ -88,11 +88,11 @@ impl ExtendedFontFamily {
family
}
pub fn to_normal_font_family(self) -> FontFamily {
pub fn to_normal_font_family(&self) -> FontFamily {
let mut new_family = FontFamily::new();
for font in self.fonts {
new_family.add_font(font);
for font in &self.fonts {
new_family.add_font(font.clone());
}
new_family
@ -113,17 +113,18 @@ impl FontLoader {
}
fn get(&mut self, font_name: &str) -> Option<ExtendedFontFamily> {
return self.cache.get(&String::from(font_name)).cloned();
self.cache.get(&String::from(font_name)).cloned()
}
#[cfg(feature = "embed-fonts")]
fn load_from_asset(&mut self, font_name: &str) -> Option<ExtendedFontFamily> {
let mut family = ExtendedFontFamily::new();
Asset::get(font_name)
if let Some(font) = Asset::get(font_name)
.and_then(|font_data| Font::from_bytes(font_data.to_vec().into(), 0).ok())
.map(|font| family.add_font(SkriboFont::new(font)));
{
family.add_font(SkriboFont::new(font))
}
self.cache.put(String::from(font_name), family);
self.get(font_name)
}
@ -162,7 +163,7 @@ struct ShapeKey {
pub fn build_collection_by_font_name(
loader: &mut FontLoader,
fallback_list: &Vec<String>,
fallback_list: &[String],
bold: bool,
italic: bool,
) -> FontCollection {
@ -212,7 +213,7 @@ struct FontSet {
}
impl FontSet {
fn new(fallback_list: &Vec<String>, mut loader: &mut FontLoader) -> FontSet {
fn new(fallback_list: &[String], mut loader: &mut FontLoader) -> FontSet {
FontSet {
normal: build_collection_by_font_name(&mut loader, fallback_list, false, false),
bold: build_collection_by_font_name(&mut loader, fallback_list, true, false),

@ -58,7 +58,7 @@ pub fn ease_in_expo(t: f32) -> f32 {
#[allow(dead_code)]
pub fn ease_out_expo(t: f32) -> f32 {
if t == 1.0 {
if (t - 1.0).abs() < std::f32::EPSILON {
1.0
} else {
1.0 - 2.0f32.powf(-10.0 * t)

@ -1,434 +1,434 @@
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,
rotation_speed: f32,
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, rotation_speed: f32, lifetime: f32) {
self.particles.push(ParticleData {
pos,
speed,
rotation_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;
particle.speed = rotate_vec(particle.speed, dt * particle.rotation_speed);
}
// 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 / 3.141592
* settings.vfx_particle_phase
* (travel_distance / font_size.0);
Point::new(phase.sin(), phase.cos()) * 2.0 * settings.vfx_particle_speed
}
TrailMode::Torpedo => {
let mut travel_dir = travel;
travel_dir.normalize();
let mut particle_dir = self.rng.rand_dir_normalized() - travel_dir * 1.5;
particle_dir.normalize();
particle_dir * settings.vfx_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 * 3.0 * settings.vfx_particle_speed
}
};
// Distribute particles along the travel distance
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)
}
};
let rotation_speed = match self.trail_mode {
TrailMode::Railgun => std::f32::consts::PI * settings.vfx_particle_curl,
TrailMode::PixieDust | TrailMode::Torpedo => {
(self.rng.next_f32() - 0.5)
* std::f32::consts::FRAC_PI_2
* settings.vfx_particle_curl
}
};
self.add_particle(
pos,
speed,
rotation_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
}
}
fn rotate_vec(v: Point, rot: f32) -> Point {
let sin = rot.sin();
let cos = rot.cos();
Point::new(v.x * cos - v.y * sin, v.x * sin + v.y * cos)
}
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).abs() < std::f32::EPSILON {
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,
rotation_speed: f32,
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, rotation_speed: f32, lifetime: f32) {
self.particles.push(ParticleData {
pos,
speed,
rotation_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;
particle.speed = rotate_vec(particle.speed, dt * particle.rotation_speed);
}
// 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 / std::f32::consts::PI
* settings.vfx_particle_phase
* (travel_distance / font_size.0);
Point::new(phase.sin(), phase.cos()) * 2.0 * settings.vfx_particle_speed
}
TrailMode::Torpedo => {
let mut travel_dir = travel;
travel_dir.normalize();
let mut particle_dir = self.rng.rand_dir_normalized() - travel_dir * 1.5;
particle_dir.normalize();
particle_dir * settings.vfx_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 * 3.0 * settings.vfx_particle_speed
}
};
// Distribute particles along the travel distance
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)
}
};
let rotation_speed = match self.trail_mode {
TrailMode::Railgun => std::f32::consts::PI * settings.vfx_particle_curl,
TrailMode::PixieDust | TrailMode::Torpedo => {
(self.rng.next_f32() - 0.5)
* std::f32::consts::FRAC_PI_2
* settings.vfx_particle_curl
}
};
self.add_particle(
pos,
speed,
rotation_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
}
}
fn rotate_vec(v: Point, rot: f32) -> Point {
let sin = rot.sin();
let cos = rot.cos();
Point::new(v.x * cos - v.y * sin, v.x * sin + v.y * cos)
}

@ -118,7 +118,7 @@ impl Corner {
}
// Check first if animation's over
if self.t > 1.0 {
if (self.t - 1.0).abs() < std::f32::EPSILON {
return false;
}
@ -154,7 +154,7 @@ impl Corner {
let direction_alignment = travel_direction.dot(corner_direction);
if self.t == 1.0 {
if (self.t - 1.0).abs() < std::f32::EPSILON {
// We are at destination, move t out of 0-1 range to stop the animation
self.t = 2.0;
} else {
@ -238,12 +238,12 @@ impl CursorRenderer {
&mut self,
cursor: Cursor,
default_colors: &Colors,
font_width: f32,
font_height: f32,
font_size: (f32, f32),
shaper: &mut CachingShaper,
canvas: &mut Canvas,
dt: f32,
) {
let (font_width, font_height) = font_size;
let render = self.blink_status.update_status(&cursor);
let settings = SETTINGS.get::<CursorSettings>();

@ -32,7 +32,7 @@ impl FontOptions {
.map(|fallback| fallback.to_string())
.collect();
if parsed_fallback_list.len() > 0 && self.fallback_list != parsed_fallback_list {
if parsed_fallback_list.is_empty() && self.fallback_list != parsed_fallback_list {
self.fallback_list = parsed_fallback_list;
updated = true;
}
@ -40,7 +40,7 @@ impl FontOptions {
for part in parts {
if part.starts_with('h') && part.len() > 1 {
if let Some(size) = part[1..].parse::<f32>().ok() {
if let Ok(size) = part[1..].parse::<f32>() {
if (self.size - size).abs() > std::f32::EPSILON {
self.size = size;
updated = true;

@ -201,7 +201,7 @@ impl Renderer {
for command in draw_commands.iter() {
self.draw_background(
&mut canvas,
command.grid_position.clone(),
command.grid_position,
command.cell_width,
&command.style,
&default_style,
@ -212,7 +212,7 @@ impl Renderer {
self.draw_foreground(
&mut canvas,
&command.text,
command.grid_position.clone(),
command.grid_position,
command.cell_width,
&command.style,
&default_style,
@ -234,8 +234,7 @@ impl Renderer {
self.cursor_renderer.draw(
cursor,
&default_style.colors,
self.font_width,
self.font_height,
(self.font_width, self.font_height),
&mut self.shaper,
gpu_canvas,
dt,

@ -68,12 +68,8 @@ impl Settings {
if arg == "--log" {
log_to_file = true;
false
} else if arg.starts_with("--geometry=") {
false
} else if arg == "--wsl" {
false
} else {
true
!(arg.starts_with("--geometry=") || arg == "--wsl")
}
})
.collect::<Vec<String>>();
@ -133,7 +129,7 @@ impl Settings {
write_lock.insert(type_id, Box::new(t));
}
pub fn get<'a, T: Clone + Send + Sync + 'static>(&'a self) -> T {
pub fn get<T: Clone + Send + Sync + 'static>(&'_ self) -> T {
let read_lock = self.settings.read();
let boxed = &read_lock
.get(&TypeId::of::<T>())

@ -64,8 +64,7 @@ pub fn window_geometry() -> Result<(u64, u64), String> {
let prefix = "--geometry=";
std::env::args()
.filter(|arg| arg.starts_with(prefix))
.next()
.find(|arg| arg.starts_with(prefix))
.map_or(Ok(INITIAL_DIMENSIONS), |arg| {
let input = &arg[prefix.len()..];
let invalid_parse_err = format!(
@ -78,7 +77,7 @@ pub fn window_geometry() -> Result<(u64, u64), String> {
.map(|dimension| {
dimension
.parse::<u64>()
.or(Err(invalid_parse_err.as_str()))
.or_else(|_| Err(invalid_parse_err.as_str()))
.and_then(|dimension| {
if dimension > 0 {
Ok(dimension)
@ -242,7 +241,7 @@ impl WindowWrapper {
let transparency = { SETTINGS.get::<WindowSettings>().transparency };
if let Ok(opacity) = self.window.opacity() {
if opacity != transparency {
if (opacity - transparency).abs() > std::f32::EPSILON {
self.window.set_opacity(transparency).ok();
self.transparency = transparency;
}
@ -311,12 +310,10 @@ impl WindowWrapper {
}
pub fn handle_mouse_wheel(&mut self, x: i32, y: i32) {
let vertical_input_type = if y > 0 {
Some("up")
} else if y < 0 {
Some("down")
} else {
None
let vertical_input_type = match y {
_ if y > 0 => Some("up"),
_ if y < 0 => Some("down"),
_ => None,
};
if let Some(input_type) = vertical_input_type {
@ -326,12 +323,10 @@ impl WindowWrapper {
});
}
let horizontal_input_type = if x > 0 {
Some("right")
} else if x < 0 {
Some("left")
} else {
None
let horizontal_input_type = match y {
_ if x > 0 => Some("right"),
_ if x < 0 => Some("left"),
_ => None,
};
if let Some(input_type) = horizontal_input_type {
@ -369,8 +364,7 @@ impl WindowWrapper {
if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::<WindowSettings>().no_idle {
let renderer = &mut self.renderer;
if self
let error = self
.skulpin_renderer
.draw(&sdl_window_wrapper, |canvas, coordinate_system_helper| {
let dt = 1.0 / (SETTINGS.get::<WindowSettings>().refresh_rate as f32);
@ -379,14 +373,14 @@ impl WindowWrapper {
handle_new_grid_size(current_size, &renderer)
}
})
.is_err()
{
.is_err();
if error {
error!("Render failed. Closing");
return false;
}
}
return true;
true
}
}

Loading…
Cancel
Save