diff --git a/crates/rnote-compose/src/builders/arrowbuilder.rs b/crates/rnote-compose/src/builders/arrowbuilder.rs index fc52d23458..6a727698e8 100644 --- a/crates/rnote-compose/src/builders/arrowbuilder.rs +++ b/crates/rnote-compose/src/builders/arrowbuilder.rs @@ -58,6 +58,7 @@ impl Buildable for ArrowBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/coordsystem2dbuilder.rs b/crates/rnote-compose/src/builders/coordsystem2dbuilder.rs index ed89f6adb1..7ba855c3c8 100644 --- a/crates/rnote-compose/src/builders/coordsystem2dbuilder.rs +++ b/crates/rnote-compose/src/builders/coordsystem2dbuilder.rs @@ -56,6 +56,7 @@ impl Buildable for CoordSystem2DBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/coordsystem3dbuilder.rs b/crates/rnote-compose/src/builders/coordsystem3dbuilder.rs index f80095b2a4..688c8b9fda 100644 --- a/crates/rnote-compose/src/builders/coordsystem3dbuilder.rs +++ b/crates/rnote-compose/src/builders/coordsystem3dbuilder.rs @@ -56,6 +56,7 @@ impl Buildable for CoordSystem3DBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/cubbezbuilder.rs b/crates/rnote-compose/src/builders/cubbezbuilder.rs index 962b7ae0c9..3da3417558 100644 --- a/crates/rnote-compose/src/builders/cubbezbuilder.rs +++ b/crates/rnote-compose/src/builders/cubbezbuilder.rs @@ -143,6 +143,7 @@ impl Buildable for CubBezBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/ellipsebuilder.rs b/crates/rnote-compose/src/builders/ellipsebuilder.rs index 9181826760..5b96d40ad1 100644 --- a/crates/rnote-compose/src/builders/ellipsebuilder.rs +++ b/crates/rnote-compose/src/builders/ellipsebuilder.rs @@ -53,6 +53,7 @@ impl Buildable for EllipseBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/fociellipsebuilder.rs b/crates/rnote-compose/src/builders/fociellipsebuilder.rs index 8345edf2bc..0eb3abf4f7 100644 --- a/crates/rnote-compose/src/builders/fociellipsebuilder.rs +++ b/crates/rnote-compose/src/builders/fociellipsebuilder.rs @@ -114,6 +114,7 @@ impl Buildable for FociEllipseBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/gridbuilder.rs b/crates/rnote-compose/src/builders/gridbuilder.rs index 729193c166..6f8451accf 100644 --- a/crates/rnote-compose/src/builders/gridbuilder.rs +++ b/crates/rnote-compose/src/builders/gridbuilder.rs @@ -103,6 +103,7 @@ impl Buildable for GridBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/linebuilder.rs b/crates/rnote-compose/src/builders/linebuilder.rs index da077ec7e5..eaea6a4293 100644 --- a/crates/rnote-compose/src/builders/linebuilder.rs +++ b/crates/rnote-compose/src/builders/linebuilder.rs @@ -58,6 +58,7 @@ impl Buildable for LineBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/penpathcurvedbuilder.rs b/crates/rnote-compose/src/builders/penpathcurvedbuilder.rs index 1f4c5e2ebb..ae89540215 100644 --- a/crates/rnote-compose/src/builders/penpathcurvedbuilder.rs +++ b/crates/rnote-compose/src/builders/penpathcurvedbuilder.rs @@ -75,7 +75,8 @@ impl Buildable for PenPathCurvedBuilder { } (_, PenEvent::Proximity { .. }) | (_, PenEvent::KeyPressed { .. }) - | (_, PenEvent::Text { .. }) => BuilderProgress::InProgress, + | (_, PenEvent::Text { .. }) + | (_, PenEvent::AnimationFrame) => BuilderProgress::InProgress, (_, PenEvent::Cancel) => { self.reset(); @@ -87,6 +88,7 @@ impl Buildable for PenPathCurvedBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/penpathmodeledbuilder.rs b/crates/rnote-compose/src/builders/penpathmodeledbuilder.rs index 074b389342..95bdfca5e4 100644 --- a/crates/rnote-compose/src/builders/penpathmodeledbuilder.rs +++ b/crates/rnote-compose/src/builders/penpathmodeledbuilder.rs @@ -86,9 +86,10 @@ impl Buildable for PenPathModeledBuilder { BuilderProgress::Finished(segments) } - PenEvent::Proximity { .. } | PenEvent::KeyPressed { .. } | PenEvent::Text { .. } => { - BuilderProgress::InProgress - } + PenEvent::Proximity { .. } + | PenEvent::KeyPressed { .. } + | PenEvent::Text { .. } + | PenEvent::AnimationFrame => BuilderProgress::InProgress, PenEvent::Cancel => BuilderProgress::Finished(vec![]), }; @@ -96,6 +97,7 @@ impl Buildable for PenPathModeledBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/penpathsimplebuilder.rs b/crates/rnote-compose/src/builders/penpathsimplebuilder.rs index f47a24277a..6dcd1f72dc 100644 --- a/crates/rnote-compose/src/builders/penpathsimplebuilder.rs +++ b/crates/rnote-compose/src/builders/penpathsimplebuilder.rs @@ -49,9 +49,10 @@ impl Buildable for PenPathSimpleBuilder { BuilderProgress::Finished(segments) } - PenEvent::Proximity { .. } | PenEvent::KeyPressed { .. } | PenEvent::Text { .. } => { - BuilderProgress::InProgress - } + PenEvent::Proximity { .. } + | PenEvent::KeyPressed { .. } + | PenEvent::Text { .. } + | PenEvent::AnimationFrame => BuilderProgress::InProgress, PenEvent::Cancel => { self.reset(); BuilderProgress::Finished(vec![]) @@ -62,6 +63,7 @@ impl Buildable for PenPathSimpleBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/polygonbuilder.rs b/crates/rnote-compose/src/builders/polygonbuilder.rs index 967a2935ea..c611fe4c2f 100644 --- a/crates/rnote-compose/src/builders/polygonbuilder.rs +++ b/crates/rnote-compose/src/builders/polygonbuilder.rs @@ -91,7 +91,7 @@ impl Buildable for PolygonBuilder { } _ => BuilderProgress::InProgress, }, - PenEvent::Text { .. } => BuilderProgress::InProgress, + PenEvent::Text { .. } | PenEvent::AnimationFrame => BuilderProgress::InProgress, PenEvent::Cancel => { self.pen_state = PenState::Up; self.finish = false; @@ -103,6 +103,7 @@ impl Buildable for PolygonBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/polylinebuilder.rs b/crates/rnote-compose/src/builders/polylinebuilder.rs index 88f917ad60..698f56b720 100644 --- a/crates/rnote-compose/src/builders/polylinebuilder.rs +++ b/crates/rnote-compose/src/builders/polylinebuilder.rs @@ -91,7 +91,7 @@ impl Buildable for PolylineBuilder { } _ => BuilderProgress::InProgress, }, - PenEvent::Text { .. } => BuilderProgress::InProgress, + PenEvent::Text { .. } | PenEvent::AnimationFrame => BuilderProgress::InProgress, PenEvent::Cancel => { self.pen_state = PenState::Up; self.finish = false; @@ -103,6 +103,7 @@ impl Buildable for PolylineBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/quadbezbuilder.rs b/crates/rnote-compose/src/builders/quadbezbuilder.rs index 440c2ba60d..9b98ee51fd 100644 --- a/crates/rnote-compose/src/builders/quadbezbuilder.rs +++ b/crates/rnote-compose/src/builders/quadbezbuilder.rs @@ -99,6 +99,7 @@ impl Buildable for QuadBezBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/quadrantcoordsystem2dbuilder.rs b/crates/rnote-compose/src/builders/quadrantcoordsystem2dbuilder.rs index 0ea3f6dbb9..5cb19d5fb3 100644 --- a/crates/rnote-compose/src/builders/quadrantcoordsystem2dbuilder.rs +++ b/crates/rnote-compose/src/builders/quadrantcoordsystem2dbuilder.rs @@ -56,6 +56,7 @@ impl Buildable for QuadrantCoordSystem2DBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/builders/rectanglebuilder.rs b/crates/rnote-compose/src/builders/rectanglebuilder.rs index 74996782ce..0116c79306 100644 --- a/crates/rnote-compose/src/builders/rectanglebuilder.rs +++ b/crates/rnote-compose/src/builders/rectanglebuilder.rs @@ -54,6 +54,7 @@ impl Buildable for RectangleBuilder { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } diff --git a/crates/rnote-compose/src/eventresult.rs b/crates/rnote-compose/src/eventresult.rs index fd097a96dd..95abe14599 100644 --- a/crates/rnote-compose/src/eventresult.rs +++ b/crates/rnote-compose/src/eventresult.rs @@ -13,6 +13,8 @@ where pub propagate: EventPropagation, /// The pen progress. pub progress: T, + /// Whether an animated frame should be requested. + pub request_animation_frame: bool, } /// Whether the event should be propagated further. diff --git a/crates/rnote-compose/src/penevent.rs b/crates/rnote-compose/src/penevent.rs index 9683e44566..04f0d41855 100644 --- a/crates/rnote-compose/src/penevent.rs +++ b/crates/rnote-compose/src/penevent.rs @@ -44,6 +44,8 @@ pub enum PenEvent { /// The committed text. text: String, }, + /// Animation frame event. + AnimationFrame, /// Cancel event when the pen vanishes unexpected. /// /// Should finish all current actions and reset all state. diff --git a/crates/rnote-engine/src/engine/mod.rs b/crates/rnote-engine/src/engine/mod.rs index 6a3c661753..41feb4b96d 100644 --- a/crates/rnote-engine/src/engine/mod.rs +++ b/crates/rnote-engine/src/engine/mod.rs @@ -34,7 +34,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::sync::Arc; use std::time::Instant; -use tracing::error; +use tracing::{debug, error}; /// An immutable view into the engine, excluding the penholder. #[derive(Debug)] @@ -45,6 +45,7 @@ pub struct EngineView<'a> { pub store: &'a StrokeStore, pub camera: &'a Camera, pub audioplayer: &'a Option, + pub animation: &'a Animation, } /// Constructs an `EngineView` from an identifier containing an `Engine` instance. @@ -58,6 +59,7 @@ macro_rules! engine_view { store: &$engine.store, camera: &$engine.camera, audioplayer: &$engine.audioplayer, + animation: &$engine.animation, } }; } @@ -71,6 +73,7 @@ pub struct EngineViewMut<'a> { pub store: &'a mut StrokeStore, pub camera: &'a mut Camera, pub audioplayer: &'a mut Option, + pub animation: &'a mut Animation, } /// Constructs an `EngineViewMut` from an identifier containing an `Engine` instance. @@ -84,6 +87,7 @@ macro_rules! engine_view_mut { store: &mut $engine.store, camera: &mut $engine.camera, audioplayer: &mut $engine.audioplayer, + animation: &mut $engine.animation, } }; } @@ -98,6 +102,7 @@ impl<'a> EngineViewMut<'a> { store: self.store, camera: self.camera, audioplayer: self.audioplayer, + animation: self.animation, } } } @@ -129,6 +134,8 @@ pub enum EngineTask { }, /// Requests that the typewriter cursor should be blinked/toggled BlinkTypewriterCursor, + /// Emits an animation frame event. + // EmitAnimationFrame, /// Change the permanent zoom to the given value Zoom(f64), /// Indicates that the application is quitting. Sent to quit the handler which receives the tasks. @@ -179,6 +186,27 @@ impl EngineTaskReceiver { } } +#[derive(Debug, Clone, Default)] +pub struct Animation { + frame_in_flight: bool, +} + +impl Animation { + pub fn claim_frame(&mut self) -> bool { + if self.frame_in_flight { + debug!("Animation frame already in flight, skipping"); + false + } else { + self.frame_in_flight = true; + true + } + } + + pub fn reset(&mut self) { + self.frame_in_flight = false; + } +} + /// The engine. #[derive(Debug, Serialize, Deserialize)] #[serde(default, rename = "engine")] @@ -205,6 +233,8 @@ pub struct Engine { #[serde(skip)] audioplayer: Option, #[serde(skip)] + pub animation: Animation, + #[serde(skip)] visual_debug: bool, // the task sender. Must not be modified, only cloned. #[serde(skip)] @@ -241,6 +271,7 @@ impl Default for Engine { optimize_epd: false, audioplayer: None, + animation: Animation::default(), visual_debug: false, tasks_tx: EngineTaskSender(tasks_tx), tasks_rx: Some(EngineTaskReceiver(tasks_rx)), @@ -457,6 +488,13 @@ impl Engine { widget_flags.redraw = true; } } + // EngineTask::EmitAnimationFrame => { + // self.animation.reset(); + + // widget_flags |= self + // .handle_pen_event(PenEvent::AnimationFrame, None, Instant::now()) + // .1; + // } EngineTask::Zoom(zoom) => { widget_flags |= self.camera.zoom_temporarily_to(1.0) | self.camera.zoom_to(zoom); @@ -481,7 +519,7 @@ impl Engine { event: PenEvent, pen_mode: Option, now: Instant, - ) -> (EventPropagation, WidgetFlags) { + ) -> (EventPropagation, WidgetFlags, bool) { self.penholder .handle_pen_event(event, pen_mode, now, &mut engine_view_mut!(self)) } diff --git a/crates/rnote-engine/src/pens/brush.rs b/crates/rnote-engine/src/pens/brush.rs index d993bb898a..a1453166e1 100644 --- a/crates/rnote-engine/src/pens/brush.rs +++ b/crates/rnote-engine/src/pens/brush.rs @@ -118,12 +118,14 @@ impl PenBehaviour for Brush { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } else { EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, } } } @@ -131,6 +133,7 @@ impl PenBehaviour for Brush { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, ( BrushState::Drawing { @@ -161,6 +164,7 @@ impl PenBehaviour for Brush { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } ( @@ -256,6 +260,7 @@ impl PenBehaviour for Brush { handled, propagate, progress, + request_animation_frame: false, } } }; diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index 821ef5e92c..a0c2c0866a 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -66,6 +66,7 @@ impl PenBehaviour for Eraser { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } (EraserState::Up | EraserState::Down { .. }, PenEvent::Proximity { element, .. }) => { @@ -74,6 +75,7 @@ impl PenBehaviour for Eraser { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, } } ( @@ -83,6 +85,7 @@ impl PenBehaviour for Eraser { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, (EraserState::Down(current_element), PenEvent::Down { element, .. }) => { widget_flags |= erase(element, engine_view); @@ -91,6 +94,7 @@ impl PenBehaviour for Eraser { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } (EraserState::Down { .. }, PenEvent::Up { element, .. }) => { @@ -101,12 +105,14 @@ impl PenBehaviour for Eraser { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } (EraserState::Down(_), PenEvent::KeyPressed { .. }) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, (EraserState::Proximity(_), PenEvent::Up { .. }) => { self.state = EraserState::Up; @@ -114,6 +120,7 @@ impl PenBehaviour for Eraser { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, } } (EraserState::Proximity(current_element), PenEvent::Proximity { element, .. }) => { @@ -122,6 +129,7 @@ impl PenBehaviour for Eraser { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, } } (EraserState::Proximity { .. } | EraserState::Down { .. }, PenEvent::Cancel) => { @@ -131,28 +139,37 @@ impl PenBehaviour for Eraser { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } (EraserState::Proximity(_), PenEvent::KeyPressed { .. }) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, - (EraserState::Up, PenEvent::Text { .. }) => EventResult { + (EraserState::Up, PenEvent::Text { .. } | PenEvent::AnimationFrame) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, - (EraserState::Proximity(_), PenEvent::Text { .. }) => EventResult { - handled: false, - propagate: EventPropagation::Proceed, - progress: PenProgress::Idle, - }, - (EraserState::Down(_), PenEvent::Text { .. }) => EventResult { - handled: false, - propagate: EventPropagation::Proceed, - progress: PenProgress::InProgress, - }, + (EraserState::Proximity(_), PenEvent::Text { .. } | PenEvent::AnimationFrame) => { + EventResult { + handled: false, + propagate: EventPropagation::Proceed, + progress: PenProgress::Idle, + request_animation_frame: false, + } + } + (EraserState::Down(_), PenEvent::Text { .. } | PenEvent::AnimationFrame) => { + EventResult { + handled: false, + propagate: EventPropagation::Proceed, + progress: PenProgress::InProgress, + request_animation_frame: false, + } + } }; (event_result, widget_flags) diff --git a/crates/rnote-engine/src/pens/penholder.rs b/crates/rnote-engine/src/pens/penholder.rs index 66952fbe25..b2686265a8 100644 --- a/crates/rnote-engine/src/pens/penholder.rs +++ b/crates/rnote-engine/src/pens/penholder.rs @@ -234,7 +234,7 @@ impl PenHolder { pen_mode: Option, now: Instant, engine_view: &mut EngineViewMut, - ) -> (EventPropagation, WidgetFlags) { + ) -> (EventPropagation, WidgetFlags, bool) { let mut widget_flags = WidgetFlags::default(); if let Some(pen_mode) = pen_mode { @@ -248,15 +248,22 @@ impl PenHolder { widget_flags |= wf | self.handle_pen_progress(event_result.progress, engine_view); if !event_result.handled { - let (propagate, wf) = self.handle_pen_event_global(event, now, engine_view); + let (propagate, wf, request_animation_frame) = + self.handle_pen_event_global(event, now, engine_view); + event_result.propagate |= propagate; + event_result.request_animation_frame |= request_animation_frame; widget_flags |= wf; } // Always redraw after handling a pen event widget_flags.redraw = true; - (event_result.propagate, widget_flags) + ( + event_result.propagate, + widget_flags, + event_result.request_animation_frame, + ) } /// Handle a pressed shortcut key. @@ -360,7 +367,7 @@ impl PenHolder { event: PenEvent, _now: Instant, engine_view: &mut EngineViewMut, - ) -> (EventPropagation, WidgetFlags) { + ) -> (EventPropagation, WidgetFlags, bool) { const MOVE_VIEW_FACTOR: f64 = 0.33; let mut widget_flags = WidgetFlags::default(); @@ -369,6 +376,7 @@ impl PenHolder { | PenEvent::Up { .. } | PenEvent::Proximity { .. } | PenEvent::Text { .. } + | PenEvent::AnimationFrame | PenEvent::Cancel => EventPropagation::Proceed, PenEvent::KeyPressed { keyboard_key, @@ -458,7 +466,7 @@ impl PenHolder { }, }; - (propagate, widget_flags) + (propagate, widget_flags, false) } fn handle_pen_progress( diff --git a/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs b/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs index 578ef7780e..9fc40e407a 100644 --- a/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs +++ b/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs @@ -22,6 +22,8 @@ pub enum ToolStyle { OffsetCamera, #[serde(rename = "zoom")] Zoom, + #[serde(rename = "laser")] + Laser, } impl Default for ToolStyle { diff --git a/crates/rnote-engine/src/pens/selector/mod.rs b/crates/rnote-engine/src/pens/selector/mod.rs index da849b0a91..4ee4f69a34 100644 --- a/crates/rnote-engine/src/pens/selector/mod.rs +++ b/crates/rnote-engine/src/pens/selector/mod.rs @@ -152,6 +152,7 @@ impl PenBehaviour for Selector { modifier_keys, } => self.handle_pen_event_keypressed(keyboard_key, modifier_keys, now, engine_view), PenEvent::Text { text } => self.handle_pen_event_text(text, now, engine_view), + PenEvent::AnimationFrame => self.handle_pen_event_animation_frame(now, engine_view), PenEvent::Cancel => self.handle_pen_event_cancel(now, engine_view), } } diff --git a/crates/rnote-engine/src/pens/selector/penevents.rs b/crates/rnote-engine/src/pens/selector/penevents.rs index 23063bb8e3..0bd82b4bf4 100644 --- a/crates/rnote-engine/src/pens/selector/penevents.rs +++ b/crates/rnote-engine/src/pens/selector/penevents.rs @@ -41,6 +41,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } SelectorState::Selecting { path } => { @@ -67,6 +68,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } SelectorState::ModifySelection { @@ -399,6 +401,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } }; @@ -421,6 +424,7 @@ impl Selector { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, SelectorState::Selecting { path } => { let mut progress = PenProgress::Finished; @@ -495,6 +499,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } SelectorState::ModifySelection { @@ -542,6 +547,7 @@ impl Selector { handled: true, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, } } }; @@ -564,11 +570,13 @@ impl Selector { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, SelectorState::Selecting { .. } => EventResult { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, }, SelectorState::ModifySelection { modify_state, .. } => { *modify_state = if selector_bounds @@ -583,6 +591,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } }; @@ -607,12 +616,14 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, }, SelectorState::Selecting { .. } => match keyboard_key { @@ -622,12 +633,14 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, }, SelectorState::ModifySelection { selection, .. } => { @@ -638,6 +651,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::Unicode('d') => { @@ -660,6 +674,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } KeyboardKey::Delete | KeyboardKey::BackSpace => { @@ -670,6 +685,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } KeyboardKey::Escape => { @@ -679,12 +695,14 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, } } @@ -706,16 +724,38 @@ impl Selector { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, - SelectorState::Selecting { .. } => EventResult { + _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, + }, + }; + + (event_result, widget_flags) + } + + pub(super) fn handle_pen_event_animation_frame( + &mut self, + _now: Instant, + _engine_view: &mut EngineViewMut, + ) -> (EventResult, WidgetFlags) { + let widget_flags = WidgetFlags::default(); + + let event_result = match &mut self.state { + SelectorState::Idle => EventResult { + handled: false, + propagate: EventPropagation::Proceed, + progress: PenProgress::Idle, + request_animation_frame: false, }, - SelectorState::ModifySelection { .. } => EventResult { + _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, }; @@ -734,6 +774,7 @@ impl Selector { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, SelectorState::Selecting { .. } => { self.state = SelectorState::Idle; @@ -741,6 +782,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } SelectorState::ModifySelection { selection, .. } => { @@ -750,6 +792,7 @@ impl Selector { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } }; diff --git a/crates/rnote-engine/src/pens/shaper.rs b/crates/rnote-engine/src/pens/shaper.rs index c8834e7562..042eb9aa6d 100644 --- a/crates/rnote-engine/src/pens/shaper.rs +++ b/crates/rnote-engine/src/pens/shaper.rs @@ -81,12 +81,14 @@ impl PenBehaviour for Shaper { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } (ShaperState::Idle, _) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, (ShaperState::BuildShape { .. }, PenEvent::Cancel) => { self.state = ShaperState::Idle; @@ -95,6 +97,7 @@ impl PenBehaviour for Shaper { handled: false, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } (ShaperState::BuildShape { builder }, event) => { @@ -113,7 +116,7 @@ impl PenBehaviour for Shaper { | PenEvent::KeyPressed { ref modifier_keys, .. } => constraints.enabled ^ modifier_keys.contains(&ModifierKey::KeyboardCtrl), - PenEvent::Text { .. } | PenEvent::Cancel => false, + PenEvent::Text { .. } | PenEvent::AnimationFrame | PenEvent::Cancel => false, }; let builder_result = builder.handle_event(event.clone(), now, constraints); let handled = builder_result.handled; @@ -196,6 +199,7 @@ impl PenBehaviour for Shaper { handled, propagate, progress, + request_animation_frame: false, } } }; diff --git a/crates/rnote-engine/src/pens/tools.rs b/crates/rnote-engine/src/pens/tools.rs index 4bebf49830..7c1ba7098f 100644 --- a/crates/rnote-engine/src/pens/tools.rs +++ b/crates/rnote-engine/src/pens/tools.rs @@ -6,13 +6,139 @@ use crate::engine::{EngineView, EngineViewMut}; use crate::store::StrokeKey; use crate::{Camera, DrawableOnDoc, WidgetFlags}; use p2d::bounding_volume::Aabb; +use p2d::bounding_volume::BoundingVolume; use piet::RenderContext; +use rnote_compose::builders::buildable::Buildable; +use rnote_compose::builders::buildable::BuilderCreator; +use rnote_compose::builders::buildable::BuilderProgress; +use rnote_compose::builders::PenPathBuilderType; +use rnote_compose::builders::PenPathCurvedBuilder; +use rnote_compose::builders::PenPathModeledBuilder; +use rnote_compose::builders::PenPathSimpleBuilder; use rnote_compose::color; use rnote_compose::eventresult::{EventPropagation, EventResult}; use rnote_compose::ext::{AabbExt, Vector2Ext}; use rnote_compose::penevent::{PenEvent, PenProgress}; +use rnote_compose::penpath::Element; +use rnote_compose::penpath::Segment; +use rnote_compose::shapes::Shapeable; +use rnote_compose::Constraints; +use rnote_compose::PenPath; +use std::time::Duration; use std::time::Instant; +#[derive(Debug)] +pub struct LaserTool { + path_builder: Option>>, + stroke_paths: Vec, + stroke_update_time: Instant, +} + +impl LaserTool { + pub fn new_stroke(&mut self, element: Element, now: Instant) { + self.stroke_paths.push(PenPath::new(element)); + self.stroke_update_time = now; + } + + pub fn update_stroke(&mut self, progress: BuilderProgress, now: Instant) { + if let Some(last_stroke) = self.stroke_paths.last_mut() { + match progress { + BuilderProgress::InProgress => {} + BuilderProgress::EmitContinue(segments) | BuilderProgress::Finished(segments) => { + last_stroke.extend(segments); + } + }; + } + + self.stroke_update_time = now; + } + + pub fn is_faded(&self) -> bool { + self.stroke_update_time.elapsed() >= LaserTool::FULL_FADE_DURATION + } +} + +impl Default for LaserTool { + fn default() -> Self { + Self { + path_builder: None, + stroke_paths: Vec::new(), + stroke_update_time: Instant::now(), + } + } +} + +impl DrawableOnDoc for LaserTool { + fn bounds_on_doc(&self, engine_view: &EngineView) -> Option { + if self.is_faded() { + return None; + } + + let strokes = self.stroke_paths.iter(); + + strokes + .map(|path| path.bounds()) + .reduce(|acc, path| acc.merged(&path)) + .map(|bounds| { + bounds.extend_by(na::Vector2::repeat( + Self::OUTER_STROKE_WIDTH / engine_view.camera.total_zoom(), + )) + }) + } + + fn draw_on_doc( + &self, + cx: &mut piet_cairo::CairoRenderContext, + engine_view: &EngineView, + ) -> anyhow::Result<()> { + cx.save().map_err(|e| anyhow::anyhow!("{e:?}"))?; + + let transparency = self + .stroke_update_time + .elapsed() + .div_duration_f64(Self::FULL_FADE_DURATION) + .clamp(0.0, 1.0); + + let opacity: u8 = ((1.0 - transparency) * 255.0) as u8; + let total_zoom = engine_view.camera.total_zoom(); + + for pen_path in &self.stroke_paths { + let bez_path = pen_path.to_kurbo_flattened(0.5); + + cx.stroke_styled( + &bez_path, + &Self::OUTER_STROKE_COLOR.with_a8(opacity), + Self::OUTER_STROKE_WIDTH / total_zoom, + &LaserTool::STYLE, + ); + + cx.stroke_styled( + &bez_path, + &Self::INNER_STROKE_COLOR.with_a8(opacity), + Self::INNER_STROKE_WIDTH / total_zoom, + &LaserTool::STYLE, + ); + } + + cx.restore().map_err(|e| anyhow::anyhow!("{e:?}"))?; + Ok(()) + } +} + +impl LaserTool { + const FULL_FADE_DURATION: Duration = Duration::from_secs(1); + + const OUTER_STROKE_WIDTH: f64 = 6.0; + const INNER_STROKE_WIDTH: f64 = 1.0; + + const INNER_STROKE_COLOR: piet::Color = color::GNOME_BRIGHTS[1]; + const OUTER_STROKE_COLOR: piet::Color = color::GNOME_REDS[1]; + + const STYLE: piet::StrokeStyle = piet::StrokeStyle::new() + .line_join(piet::LineJoin::Round) + .line_cap(piet::LineCap::Round); +} + #[derive(Clone, Debug)] pub struct VerticalSpaceTool { start_pos_y: f64, @@ -269,11 +395,12 @@ impl Default for ToolsState { } } -#[derive(Clone, Debug, Default)] +#[derive(Debug, Default)] pub struct Tools { pub verticalspace_tool: VerticalSpaceTool, pub offsetcamera_tool: OffsetCameraTool, pub zoom_tool: ZoomTool, + pub laser_tool: LaserTool, state: ToolsState, } @@ -297,12 +424,12 @@ impl PenBehaviour for Tools { fn handle_event( &mut self, event: PenEvent, - _now: Instant, + now: Instant, engine_view: &mut EngineViewMut, ) -> (EventResult, WidgetFlags) { let mut widget_flags = WidgetFlags::default(); - let event_result = match (&mut self.state, event) { + let event_result = match (&mut self.state, &event) { (ToolsState::Idle, PenEvent::Down { element, .. }) => { match engine_view.pens_config.tools_config.style { ToolStyle::VerticalSpace => { @@ -366,6 +493,22 @@ impl PenBehaviour for Tools { .transform_point(&element.pos.into()) .coords; } + ToolStyle::Laser => { + self.laser_tool.new_stroke(*element, now); + + self.laser_tool.path_builder = + Some(match engine_view.pens_config.brush_config.builder_type { + PenPathBuilderType::Simple => { + Box::new(PenPathSimpleBuilder::start(*element, now)) + } + PenPathBuilderType::Curved => { + Box::new(PenPathCurvedBuilder::start(*element, now)) + } + PenPathBuilderType::Modeled => { + Box::new(PenPathModeledBuilder::start(*element, now)) + } + }); + } } widget_flags |= engine_view .document @@ -377,12 +520,42 @@ impl PenBehaviour for Tools { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, + } + } + (ToolsState::Idle, PenEvent::AnimationFrame) => { + match engine_view.pens_config.tools_config.style { + ToolStyle::Laser => { + let mut request_animation_frame = false; + let mut progress = PenProgress::Finished; + + if self.laser_tool.is_faded() { + self.reset(engine_view, true); + } else { + request_animation_frame = true; + progress = PenProgress::InProgress; + } + + EventResult { + handled: true, + propagate: EventPropagation::Stop, + progress, + request_animation_frame, + } + } + _ => EventResult { + handled: false, + propagate: EventPropagation::Proceed, + progress: PenProgress::Idle, + request_animation_frame: false, + }, } } (ToolsState::Idle, _) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, (ToolsState::Active, PenEvent::Down { element, .. }) => { match engine_view.pens_config.tools_config.style { @@ -483,15 +656,27 @@ impl PenBehaviour for Tools { } self.zoom_tool.current_surface_coord = new_surface_coord; } + ToolStyle::Laser => { + if let Some(builder) = &mut self.laser_tool.path_builder { + let builder_result = + builder.handle_event(event, now, Constraints::default()); + + self.laser_tool.update_stroke(builder_result.progress, now); + } + } } EventResult { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } (ToolsState::Active, PenEvent::Up { .. }) => { + let mut request_animation_frame = false; + let mut progress = PenProgress::Finished; + match engine_view.pens_config.tools_config.style { ToolStyle::VerticalSpace => { engine_view @@ -501,6 +686,17 @@ impl PenBehaviour for Tools { widget_flags |= engine_view.store.record(Instant::now()); widget_flags.store_modified = true; } + ToolStyle::Laser => { + if let Some(builder) = &mut self.laser_tool.path_builder { + let builder_result = + builder.handle_event(event, now, Constraints::default()); + + self.laser_tool.update_stroke(builder_result.progress, now); + + request_animation_frame = true; + progress = PenProgress::InProgress; + } + } ToolStyle::OffsetCamera | ToolStyle::Zoom => {} } @@ -514,23 +710,23 @@ impl PenBehaviour for Tools { engine_view.camera.image_scale(), ); - self.reset(engine_view); + self.reset(engine_view, false); EventResult { handled: true, propagate: EventPropagation::Stop, - progress: PenProgress::Finished, + progress, + request_animation_frame, } } - (ToolsState::Active, PenEvent::Proximity { .. }) => EventResult { - handled: false, - propagate: EventPropagation::Proceed, - progress: PenProgress::InProgress, - }, - (ToolsState::Active, PenEvent::KeyPressed { .. }) => EventResult { + ( + ToolsState::Active, + PenEvent::Proximity { .. } | PenEvent::KeyPressed { .. } | PenEvent::AnimationFrame, + ) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, (ToolsState::Active, PenEvent::Cancel) => { widget_flags |= engine_view @@ -543,18 +739,20 @@ impl PenBehaviour for Tools { engine_view.camera.image_scale(), ); - self.reset(engine_view); + self.reset(engine_view, true); EventResult { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } (ToolsState::Active, PenEvent::Text { .. }) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, }; @@ -564,13 +762,18 @@ impl PenBehaviour for Tools { impl DrawableOnDoc for Tools { fn bounds_on_doc(&self, engine_view: &EngineView) -> Option { - match self.state { - ToolsState::Active => match engine_view.pens_config.tools_config.style { - ToolStyle::VerticalSpace => self.verticalspace_tool.bounds_on_doc(engine_view), - ToolStyle::OffsetCamera => self.offsetcamera_tool.bounds_on_doc(engine_view), - ToolStyle::Zoom => self.zoom_tool.bounds_on_doc(engine_view), - }, - ToolsState::Idle => None, + if let ToolStyle::Laser = engine_view.pens_config.tools_config.style { + self.laser_tool.bounds_on_doc(engine_view) + } else { + match self.state { + ToolsState::Active => match engine_view.pens_config.tools_config.style { + ToolStyle::VerticalSpace => self.verticalspace_tool.bounds_on_doc(engine_view), + ToolStyle::OffsetCamera => self.offsetcamera_tool.bounds_on_doc(engine_view), + ToolStyle::Zoom => self.zoom_tool.bounds_on_doc(engine_view), + ToolStyle::Laser => self.laser_tool.bounds_on_doc(engine_view), + }, + ToolsState::Idle => None, + } } } @@ -591,6 +794,9 @@ impl DrawableOnDoc for Tools { ToolStyle::Zoom => { self.zoom_tool.draw_on_doc(cx, engine_view)?; } + ToolStyle::Laser => { + self.laser_tool.draw_on_doc(cx, engine_view)?; + } } cx.restore().map_err(|e| anyhow::anyhow!("{e:?}"))?; @@ -599,7 +805,9 @@ impl DrawableOnDoc for Tools { } impl Tools { - fn reset(&mut self, engine_view: &mut EngineViewMut) { + /// Reset the tool to its idle state. + /// `hard` indicates whether the tool should be reset to its initial state, e.g. due to a cancel pen event. + fn reset(&mut self, engine_view: &mut EngineViewMut, hard: bool) { match engine_view.pens_config.tools_config.style { ToolStyle::VerticalSpace => { self.verticalspace_tool.start_pos_y = 0.0; @@ -612,6 +820,14 @@ impl Tools { self.zoom_tool.start_surface_coord = na::Vector2::zeros(); self.zoom_tool.current_surface_coord = na::Vector2::zeros(); } + ToolStyle::Laser => { + self.laser_tool.path_builder = None; + + if hard { + self.laser_tool.stroke_paths.clear(); + self.laser_tool.stroke_update_time = Instant::now(); + } + } } self.state = ToolsState::Idle; } diff --git a/crates/rnote-engine/src/pens/typewriter/mod.rs b/crates/rnote-engine/src/pens/typewriter/mod.rs index f4bd028e7e..0e83fd0ffc 100644 --- a/crates/rnote-engine/src/pens/typewriter/mod.rs +++ b/crates/rnote-engine/src/pens/typewriter/mod.rs @@ -374,6 +374,7 @@ impl PenBehaviour for Typewriter { modifier_keys, } => self.handle_pen_event_keypressed(keyboard_key, modifier_keys, now, engine_view), PenEvent::Text { text } => self.handle_pen_event_text(text, now, engine_view), + PenEvent::AnimationFrame => self.handle_pen_event_animation_frame(now, engine_view), PenEvent::Cancel => self.handle_pen_event_cancel(now, engine_view), }; diff --git a/crates/rnote-engine/src/pens/typewriter/penevents.rs b/crates/rnote-engine/src/pens/typewriter/penevents.rs index 3fc6de8b4f..bcec9a35b0 100644 --- a/crates/rnote-engine/src/pens/typewriter/penevents.rs +++ b/crates/rnote-engine/src/pens/typewriter/penevents.rs @@ -89,6 +89,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } TypewriterState::Modifying { @@ -174,6 +175,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } ModifyState::Selecting { finished, .. } => { @@ -230,6 +232,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress, + request_animation_frame: false, } } ModifyState::Translating { current_pos, .. } => { @@ -276,6 +279,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } ModifyState::AdjustTextWidth { @@ -314,6 +318,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } } @@ -338,11 +343,13 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, TypewriterState::Start(_) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, TypewriterState::Modifying { modify_state, @@ -421,6 +428,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } }; @@ -443,11 +451,13 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, TypewriterState::Start(_) => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, TypewriterState::Modifying { modify_state, @@ -469,6 +479,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } }; @@ -493,6 +504,7 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, TypewriterState::Start(pos) => { super::play_sound(Some(keyboard_key), engine_view.audioplayer); @@ -531,12 +543,14 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, } } @@ -604,6 +618,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::BackSpace => { @@ -618,6 +633,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::HorizontalTab => { @@ -628,6 +644,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::CarriageReturn | KeyboardKey::Linefeed => { @@ -638,6 +655,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::Delete => { @@ -652,6 +670,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavLeft => { @@ -680,6 +699,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavRight => { @@ -708,6 +728,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavUp => { @@ -727,6 +748,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavDown => { @@ -746,6 +768,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::Home => { @@ -774,6 +797,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::End => { @@ -802,12 +826,14 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, } } else { @@ -815,6 +841,7 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, } } } @@ -864,6 +891,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavLeft => { @@ -883,6 +911,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavRight => { @@ -902,6 +931,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavUp => { @@ -913,6 +943,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::NavDown => { @@ -924,6 +955,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::Home => { @@ -939,6 +971,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::End => { @@ -954,6 +987,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::CarriageReturn | KeyboardKey::Linefeed => { @@ -968,6 +1002,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::BackSpace | KeyboardKey::Delete => { @@ -982,6 +1017,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::HorizontalTab => { @@ -996,6 +1032,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } KeyboardKey::CtrlLeft @@ -1005,6 +1042,7 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, _ => { quit_selecting = true; @@ -1012,6 +1050,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } }; @@ -1031,6 +1070,7 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, } } } @@ -1038,6 +1078,7 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, } } @@ -1066,6 +1107,7 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, TypewriterState::Start(pos) => { super::play_sound(None, engine_view.audioplayer); @@ -1100,6 +1142,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } TypewriterState::Modifying { @@ -1145,6 +1188,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } ModifyState::Selecting { @@ -1189,12 +1233,14 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::InProgress, + request_animation_frame: false, } } _ => EventResult { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::InProgress, + request_animation_frame: false, }, } } @@ -1203,6 +1249,31 @@ impl Typewriter { (event_result, widget_flags) } + pub(super) fn handle_pen_event_animation_frame( + &mut self, + _now: Instant, + _engine_view: &mut EngineViewMut, + ) -> (EventResult, WidgetFlags) { + let widget_flags = WidgetFlags::default(); + + let event_result = match &mut self.state { + TypewriterState::Idle => EventResult { + handled: false, + propagate: EventPropagation::Proceed, + progress: PenProgress::Idle, + request_animation_frame: false, + }, + _ => EventResult { + handled: true, + propagate: EventPropagation::Stop, + progress: PenProgress::Finished, + request_animation_frame: false, + }, + }; + + (event_result, widget_flags) + } + pub(super) fn handle_pen_event_cancel( &mut self, _now: Instant, @@ -1215,6 +1286,7 @@ impl Typewriter { handled: false, propagate: EventPropagation::Proceed, progress: PenProgress::Idle, + request_animation_frame: false, }, _ => { self.state = TypewriterState::Idle; @@ -1223,6 +1295,7 @@ impl Typewriter { handled: true, propagate: EventPropagation::Stop, progress: PenProgress::Finished, + request_animation_frame: false, } } }; diff --git a/crates/rnote-engine/src/store/mod.rs b/crates/rnote-engine/src/store/mod.rs index 1e0931d7eb..d71960aabe 100644 --- a/crates/rnote-engine/src/store/mod.rs +++ b/crates/rnote-engine/src/store/mod.rs @@ -8,7 +8,6 @@ pub mod trash_comp; // Re-exports pub use chrono_comp::ChronoComponent; -use keytree::KeyTree; pub use render_comp::RenderComponent; pub use selection_comp::SelectionComponent; pub use trash_comp::TrashComponent; @@ -18,6 +17,7 @@ use self::chrono_comp::StrokeLayer; use crate::engine::EngineSnapshot; use crate::strokes::Stroke; use crate::WidgetFlags; +use keytree::KeyTree; use rnote_compose::shapes::Shapeable; use serde::{Deserialize, Serialize}; use slotmap::{HopSlotMap, SecondaryMap}; diff --git a/crates/rnote-ui/data/icons/scalable/actions/pen-tools-laser-symbolic.svg b/crates/rnote-ui/data/icons/scalable/actions/pen-tools-laser-symbolic.svg new file mode 100644 index 0000000000..a1d2aabe85 --- /dev/null +++ b/crates/rnote-ui/data/icons/scalable/actions/pen-tools-laser-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/crates/rnote-ui/data/meson.build b/crates/rnote-ui/data/meson.build index b1ae9b76b9..a187fba6f8 100644 --- a/crates/rnote-ui/data/meson.build +++ b/crates/rnote-ui/data/meson.build @@ -230,6 +230,7 @@ rnote_ui_gresources_icons_files = files( 'icons/scalable/actions/pen-shaper-style-rough-symbolic.svg', 'icons/scalable/actions/pen-shaper-style-smooth-symbolic.svg', 'icons/scalable/actions/pen-shaper-symbolic.svg', + 'icons/scalable/actions/pen-tools-laser-symbolic.svg', 'icons/scalable/actions/pen-tools-offsetcameratool-symbolic.svg', 'icons/scalable/actions/pen-tools-symbolic.svg', 'icons/scalable/actions/pen-tools-verticalspacetool-symbolic.svg', diff --git a/crates/rnote-ui/data/resources.gresource.xml b/crates/rnote-ui/data/resources.gresource.xml index a1deb7af1f..356b29244b 100644 --- a/crates/rnote-ui/data/resources.gresource.xml +++ b/crates/rnote-ui/data/resources.gresource.xml @@ -100,6 +100,7 @@ icons/scalable/actions/pen-shaper-style-rough-symbolic.svg icons/scalable/actions/pen-shaper-style-smooth-symbolic.svg icons/scalable/actions/pen-shaper-symbolic.svg + icons/scalable/actions/pen-tools-laser-symbolic.svg icons/scalable/actions/pen-tools-offsetcameratool-symbolic.svg icons/scalable/actions/pen-tools-symbolic.svg icons/scalable/actions/pen-tools-verticalspacetool-symbolic.svg diff --git a/crates/rnote-ui/data/ui/penssidebar/toolspage.ui b/crates/rnote-ui/data/ui/penssidebar/toolspage.ui index cf7b9d1648..7db845c062 100644 --- a/crates/rnote-ui/data/ui/penssidebar/toolspage.ui +++ b/crates/rnote-ui/data/ui/penssidebar/toolspage.ui @@ -71,6 +71,22 @@ + + + vertical + + + + + Laser + pen-tools-laser-symbolic + toolstyle_verticalspace_toggle + + + diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index 8775ec852b..76efa2c34d 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -182,7 +182,7 @@ pub(crate) fn handle_pointer_controller_event( PenState::Up => { canvas.enable_drawing_cursor(false); - let (ep, wf) = canvas.engine_mut().handle_pen_event( + let (ep, wf) = canvas.handle_pen_event( PenEvent::Up { element, modifier_keys: modifier_keys.clone(), @@ -196,7 +196,7 @@ pub(crate) fn handle_pointer_controller_event( PenState::Proximity => { canvas.enable_drawing_cursor(false); - let (ep, wf) = canvas.engine_mut().handle_pen_event( + let (ep, wf) = canvas.handle_pen_event( PenEvent::Proximity { element, modifier_keys: modifier_keys.clone(), @@ -211,7 +211,7 @@ pub(crate) fn handle_pointer_controller_event( canvas.grab_focus(); canvas.enable_drawing_cursor(true); - let (ep, wf) = canvas.engine_mut().handle_pen_event( + let (ep, wf) = canvas.handle_pen_event( PenEvent::Down { element, modifier_keys: modifier_keys.clone(), @@ -248,7 +248,7 @@ pub(crate) fn handle_key_controller_key_pressed( .engine_mut() .handle_pressed_shortcut_key(shortcut_key, now) } else { - canvas.engine_mut().handle_pen_event( + canvas.handle_pen_event( PenEvent::KeyPressed { keyboard_key, modifier_keys, @@ -273,7 +273,7 @@ pub(crate) fn handle_key_controller_key_released( pub(crate) fn handle_imcontext_text_commit(canvas: &RnCanvas, text: &str) { let now = Instant::now(); - let (_ep, widget_flags) = canvas.engine_mut().handle_pen_event( + let (_ep, widget_flags) = canvas.handle_pen_event( PenEvent::Text { text: text.to_string(), }, diff --git a/crates/rnote-ui/src/canvas/mod.rs b/crates/rnote-ui/src/canvas/mod.rs index 11bee39b2b..c2d3e1079d 100644 --- a/crates/rnote-ui/src/canvas/mod.rs +++ b/crates/rnote-ui/src/canvas/mod.rs @@ -22,15 +22,18 @@ use notify::EventKind; use notify_debouncer_full::notify; use once_cell::sync::Lazy; use p2d::bounding_volume::Aabb; +use rnote_compose::eventresult::EventPropagation; use rnote_compose::ext::AabbExt; use rnote_compose::penevent::PenState; +use rnote_compose::PenEvent; use rnote_engine::ext::GraphenePointExt; use rnote_engine::ext::GrapheneRectExt; +use rnote_engine::pens::PenMode; use rnote_engine::Camera; use rnote_engine::{Engine, WidgetFlags}; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::path::Path; -use std::time::Duration; +use std::time::{Duration, Instant}; use tracing::{debug, error, warn}; #[derive(Debug, Default)] @@ -1473,6 +1476,33 @@ impl RnCanvas { } } + pub(crate) fn handle_pen_event( + &self, + event: PenEvent, + pen_mode: Option, + now: Instant, + ) -> (EventPropagation, WidgetFlags) { + let (propagation, widget_flags, request_animation_frame) = + self.engine_mut().handle_pen_event(event, pen_mode, now); + + if request_animation_frame && self.engine_mut().animation.claim_frame() { + glib::source::idle_add_local_once(clone!( + #[weak(rename_to = canvas)] + self, + move || { + canvas.engine_mut().animation.reset(); + + let (_, widget_flags) = + canvas.handle_pen_event(PenEvent::AnimationFrame, None, Instant::now()); + + canvas.emit_handle_widget_flags(widget_flags); + } + )); + } + + (propagation, widget_flags) + } + pub(crate) fn bounds(&self) -> Aabb { Aabb::new_positive( na::point![0.0, 0.0], diff --git a/crates/rnote-ui/src/penssidebar/toolspage.rs b/crates/rnote-ui/src/penssidebar/toolspage.rs index cdaac6004a..845c2ad64a 100644 --- a/crates/rnote-ui/src/penssidebar/toolspage.rs +++ b/crates/rnote-ui/src/penssidebar/toolspage.rs @@ -19,6 +19,8 @@ mod imp { #[template_child] pub(crate) toolstyle_zoom_toggle: TemplateChild, #[template_child] + pub(crate) toolstyle_laser_toggle: TemplateChild, + #[template_child] pub(crate) verticalspace_menubutton: TemplateChild, #[template_child] pub(crate) verticalspace_popover: TemplateChild, @@ -88,6 +90,8 @@ impl RnToolsPage { Some(ToolStyle::OffsetCamera) } else if imp.toolstyle_zoom_toggle.is_active() { Some(ToolStyle::Zoom) + } else if imp.toolstyle_laser_toggle.is_active() { + Some(ToolStyle::Laser) } else { None } @@ -106,6 +110,7 @@ impl RnToolsPage { ToolStyle::VerticalSpace => imp.toolstyle_verticalspace_toggle.set_active(true), ToolStyle::OffsetCamera => imp.toolstyle_offsetcamera_toggle.set_active(true), ToolStyle::Zoom => imp.toolstyle_zoom_toggle.set_active(true), + ToolStyle::Laser => imp.toolstyle_laser_toggle.set_active(true), } } @@ -156,6 +161,20 @@ impl RnToolsPage { } )); + imp.toolstyle_laser_toggle.connect_toggled(clone!( + #[weak] + appwindow, + move |toggle| { + let Some(canvas) = appwindow.active_tab_canvas() else { + return; + }; + + if toggle.is_active() { + canvas.engine_mut().pens_config.tools_config.style = ToolStyle::Laser; + } + } + )); + imp.verticalspace_menubutton.connect_active_notify(clone!( #[weak(rename_to=toolspage)] self,